Hold down a modifier key when clicking a link to open it in a new tab

This commit is contained in:
Emil Ernerfeldt 2021-03-08 17:48:23 +01:00
parent c1ef81628b
commit 1c06622dbc
8 changed files with 69 additions and 15 deletions

View file

@ -152,6 +152,16 @@ pub struct Modifiers {
pub command: bool,
}
impl Modifiers {
pub fn is_none(&self) -> bool {
self == &Self::default()
}
pub fn any(&self) -> bool {
!self.is_none()
}
}
/// Keyboard keys.
///
/// Includes all keys egui is interested in (such as `Home` and `End`)

View file

@ -2,13 +2,13 @@
/// What egui emits each frame.
/// The backend should use this.
#[derive(Clone, Default)]
#[derive(Clone, Default, PartialEq)]
pub struct Output {
/// Set the cursor to this icon.
pub cursor_icon: CursorIcon,
/// If set, open this url.
pub open_url: Option<String>,
pub open_url: Option<OpenUrl>,
/// Response to [`crate::Event::Copy`] or [`crate::Event::Cut`]. Ignore if empty.
pub copied_text: String,
@ -25,10 +25,35 @@ pub struct Output {
pub events: Vec<OutputEvent>,
}
#[derive(Clone, PartialEq)]
pub struct OpenUrl {
pub url: String,
/// If `true`, open the url in a new tab.
/// If `false` open it in the same tab.
/// Only matters when in a web browser.
pub new_tab: bool,
}
impl OpenUrl {
pub fn same_tab(url: impl Into<String>) -> Self {
Self {
url: url.into(),
new_tab: false,
}
}
pub fn new_tab(url: impl Into<String>) -> Self {
Self {
url: url.into(),
new_tab: true,
}
}
}
/// A mouse cursor icon.
///
/// egui emits a [`CursorIcon`] in [`Output`] each frame as a request to the integration.
#[derive(Clone, Copy)]
#[derive(Clone, Copy, PartialEq)]
pub enum CursorIcon {
Default,
/// Pointing hand, used for e.g. web links
@ -52,7 +77,7 @@ impl Default for CursorIcon {
/// Things that happened during this frame that the integration may be interested in.
///
/// In particular, these events may be useful for accessability, i.e. for screen readers.
#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(Clone, Debug, PartialEq)]
pub enum OutputEvent {
/// A widget gained keyboard focus (by tab key).
///
@ -81,6 +106,12 @@ pub enum WidgetType {
}
impl Output {
/// Open the given url in a web browser.
/// If egui is running in a browser, the same tab will be reused.
pub fn open_url(&mut self, url: impl Into<String>) {
self.open_url = Some(OpenUrl::new_tab(url))
}
/// Inform the backend integration that a widget gained focus
pub fn push_gained_focus_event(&mut self, widget_type: WidgetType, text: impl Into<String>) {
self.events

View file

@ -307,7 +307,10 @@ pub use epaint::{
pub use {
containers::*,
context::{Context, CtxRef},
data::{input::*, output::*},
data::{
input::*,
output::{self, CursorIcon, Output, WidgetType},
},
grid::Grid,
id::Id,
input_state::{InputState, PointerState},

View file

@ -62,7 +62,11 @@ impl Widget for Hyperlink {
ui.ctx().output().cursor_icon = CursorIcon::PointingHand;
}
if response.clicked() {
ui.ctx().output().open_url = Some(url.clone());
let modifiers = ui.ctx().input().modifiers;
ui.ctx().output().open_url = Some(crate::output::OpenUrl {
url: url.clone(),
new_tab: modifiers.any(),
});
}
let color = ui.visuals().hyperlink_color;

View file

@ -93,7 +93,7 @@ impl epi::App for WrapApp {
{
self.selected_anchor = anchor.to_owned();
if frame.is_web() {
ui.output().open_url = Some(format!("#{}", anchor));
ui.output().open_url(format!("#{}", anchor));
}
}
}
@ -105,7 +105,7 @@ impl epi::App for WrapApp {
if clock_button(ui, seconds_since_midnight).clicked() {
self.selected_anchor = "clock".to_owned();
if frame.is_web() {
ui.output().open_url = Some("#clock".to_owned());
ui.output().open_url("#clock");
}
}
}
@ -221,7 +221,7 @@ struct BackendPanel {
frame_history: crate::frame_history::FrameHistory,
#[cfg_attr(feature = "persistence", serde(skip))]
output_event_history: std::collections::VecDeque<egui::OutputEvent>,
output_event_history: std::collections::VecDeque<egui::output::OutputEvent>,
}
impl Default for BackendPanel {

View file

@ -281,8 +281,8 @@ pub fn handle_output(
display: &glium::backend::glutin::Display,
clipboard: Option<&mut ClipboardContext>,
) {
if let Some(url) = output.open_url {
if let Err(err) = webbrowser::open(&url) {
if let Some(open) = output.open_url {
if let Err(err) = webbrowser::open(&open.url) {
eprintln!("Failed to open url: {}", err);
}
}

View file

@ -7,6 +7,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## Unreleased
### Added ⭐
* Hold down a modifier key when clicking a link to open it in a new tab.
## 0.10.0 - 2021-02-28

View file

@ -237,8 +237,8 @@ pub fn handle_output(output: &egui::Output) {
} = output;
set_cursor_icon(*cursor_icon);
if let Some(url) = open_url {
crate::open_url(url);
if let Some(open) = open_url {
crate::open_url(&open.url, open.new_tab);
}
#[cfg(web_sys_unstable_apis)]
@ -299,9 +299,11 @@ fn cursor_web_name(cursor: egui::CursorIcon) -> &'static str {
}
}
pub fn open_url(url: &str) -> Option<()> {
pub fn open_url(url: &str, new_tab: bool) -> Option<()> {
let name = if new_tab { "_blank" } else { "_self" };
web_sys::window()?
.open_with_url_and_target(url, "_self")
.open_with_url_and_target(url, name)
.ok()?;
Some(())
}