Hold down a modifier key when clicking a link to open it in a new tab
This commit is contained in:
parent
c1ef81628b
commit
1c06622dbc
8 changed files with 69 additions and 15 deletions
|
@ -152,6 +152,16 @@ pub struct Modifiers {
|
||||||
pub command: bool,
|
pub command: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Modifiers {
|
||||||
|
pub fn is_none(&self) -> bool {
|
||||||
|
self == &Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn any(&self) -> bool {
|
||||||
|
!self.is_none()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Keyboard keys.
|
/// Keyboard keys.
|
||||||
///
|
///
|
||||||
/// Includes all keys egui is interested in (such as `Home` and `End`)
|
/// Includes all keys egui is interested in (such as `Home` and `End`)
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
|
|
||||||
/// What egui emits each frame.
|
/// What egui emits each frame.
|
||||||
/// The backend should use this.
|
/// The backend should use this.
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default, PartialEq)]
|
||||||
pub struct Output {
|
pub struct Output {
|
||||||
/// Set the cursor to this icon.
|
/// Set the cursor to this icon.
|
||||||
pub cursor_icon: CursorIcon,
|
pub cursor_icon: CursorIcon,
|
||||||
|
|
||||||
/// If set, open this url.
|
/// 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.
|
/// Response to [`crate::Event::Copy`] or [`crate::Event::Cut`]. Ignore if empty.
|
||||||
pub copied_text: String,
|
pub copied_text: String,
|
||||||
|
@ -25,10 +25,35 @@ pub struct Output {
|
||||||
pub events: Vec<OutputEvent>,
|
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.
|
/// A mouse cursor icon.
|
||||||
///
|
///
|
||||||
/// egui emits a [`CursorIcon`] in [`Output`] each frame as a request to the integration.
|
/// egui emits a [`CursorIcon`] in [`Output`] each frame as a request to the integration.
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
pub enum CursorIcon {
|
pub enum CursorIcon {
|
||||||
Default,
|
Default,
|
||||||
/// Pointing hand, used for e.g. web links
|
/// 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.
|
/// 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.
|
/// 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 {
|
pub enum OutputEvent {
|
||||||
/// A widget gained keyboard focus (by tab key).
|
/// A widget gained keyboard focus (by tab key).
|
||||||
///
|
///
|
||||||
|
@ -81,6 +106,12 @@ pub enum WidgetType {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Output {
|
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
|
/// Inform the backend integration that a widget gained focus
|
||||||
pub fn push_gained_focus_event(&mut self, widget_type: WidgetType, text: impl Into<String>) {
|
pub fn push_gained_focus_event(&mut self, widget_type: WidgetType, text: impl Into<String>) {
|
||||||
self.events
|
self.events
|
||||||
|
|
|
@ -307,7 +307,10 @@ pub use epaint::{
|
||||||
pub use {
|
pub use {
|
||||||
containers::*,
|
containers::*,
|
||||||
context::{Context, CtxRef},
|
context::{Context, CtxRef},
|
||||||
data::{input::*, output::*},
|
data::{
|
||||||
|
input::*,
|
||||||
|
output::{self, CursorIcon, Output, WidgetType},
|
||||||
|
},
|
||||||
grid::Grid,
|
grid::Grid,
|
||||||
id::Id,
|
id::Id,
|
||||||
input_state::{InputState, PointerState},
|
input_state::{InputState, PointerState},
|
||||||
|
|
|
@ -62,7 +62,11 @@ impl Widget for Hyperlink {
|
||||||
ui.ctx().output().cursor_icon = CursorIcon::PointingHand;
|
ui.ctx().output().cursor_icon = CursorIcon::PointingHand;
|
||||||
}
|
}
|
||||||
if response.clicked() {
|
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;
|
let color = ui.visuals().hyperlink_color;
|
||||||
|
|
|
@ -93,7 +93,7 @@ impl epi::App for WrapApp {
|
||||||
{
|
{
|
||||||
self.selected_anchor = anchor.to_owned();
|
self.selected_anchor = anchor.to_owned();
|
||||||
if frame.is_web() {
|
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() {
|
if clock_button(ui, seconds_since_midnight).clicked() {
|
||||||
self.selected_anchor = "clock".to_owned();
|
self.selected_anchor = "clock".to_owned();
|
||||||
if frame.is_web() {
|
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,
|
frame_history: crate::frame_history::FrameHistory,
|
||||||
|
|
||||||
#[cfg_attr(feature = "persistence", serde(skip))]
|
#[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 {
|
impl Default for BackendPanel {
|
||||||
|
|
|
@ -281,8 +281,8 @@ pub fn handle_output(
|
||||||
display: &glium::backend::glutin::Display,
|
display: &glium::backend::glutin::Display,
|
||||||
clipboard: Option<&mut ClipboardContext>,
|
clipboard: Option<&mut ClipboardContext>,
|
||||||
) {
|
) {
|
||||||
if let Some(url) = output.open_url {
|
if let Some(open) = output.open_url {
|
||||||
if let Err(err) = webbrowser::open(&url) {
|
if let Err(err) = webbrowser::open(&open.url) {
|
||||||
eprintln!("Failed to open url: {}", err);
|
eprintln!("Failed to open url: {}", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
### Added ⭐
|
||||||
|
|
||||||
|
* Hold down a modifier key when clicking a link to open it in a new tab.
|
||||||
|
|
||||||
|
|
||||||
## 0.10.0 - 2021-02-28
|
## 0.10.0 - 2021-02-28
|
||||||
|
|
||||||
|
|
|
@ -237,8 +237,8 @@ pub fn handle_output(output: &egui::Output) {
|
||||||
} = output;
|
} = output;
|
||||||
|
|
||||||
set_cursor_icon(*cursor_icon);
|
set_cursor_icon(*cursor_icon);
|
||||||
if let Some(url) = open_url {
|
if let Some(open) = open_url {
|
||||||
crate::open_url(url);
|
crate::open_url(&open.url, open.new_tab);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(web_sys_unstable_apis)]
|
#[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()?
|
web_sys::window()?
|
||||||
.open_with_url_and_target(url, "_self")
|
.open_with_url_and_target(url, name)
|
||||||
.ok()?;
|
.ok()?;
|
||||||
Some(())
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue