Skip to content

Commit

Permalink
Add OutputCommand for copying text and opening URL:s
Browse files Browse the repository at this point in the history
* Part of #5424
* Adds `egui::OutputComm
* Part of https://github.com/emilk/egui/issues/5424and`
* Adds `PlatformOutput::commands`
* Deprecates `PlatformOutput::open_url`
* Deprecates `PlatformOutput::copied_text`
  • Loading branch information
emilk committed Dec 29, 2024
1 parent 6c22fee commit 6a98667
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 14 deletions.
15 changes: 15 additions & 0 deletions crates/eframe/src/web/app_runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,12 +292,15 @@ impl AppRunner {
}

fn handle_platform_output(&self, platform_output: egui::PlatformOutput) {
#![allow(deprecated)]

#[cfg(feature = "web_screen_reader")]
if self.egui_ctx.options(|o| o.screen_reader) {
super::screen_reader::speak(&platform_output.events_description());
}

let egui::PlatformOutput {
commands,
cursor_icon,
open_url,
copied_text,
Expand All @@ -310,7 +313,19 @@ impl AppRunner {
request_discard_reasons: _, // handled by `Context::run`
} = platform_output;

for command in commands {
match command {
egui::OutputCommand::CopyText(text) => {
super::set_clipboard_text(&text);
}
egui::OutputCommand::OpenUrl(open_url) => {
super::open_url(&open_url.url, open_url.new_tab);
}
}
}

super::set_cursor_icon(cursor_icon);

if let Some(open) = open_url {
super::open_url(&open.url, open.new_tab);
}
Expand Down
13 changes: 13 additions & 0 deletions crates/egui-winit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -820,9 +820,11 @@ impl State {
window: &Window,
platform_output: egui::PlatformOutput,
) {
#![allow(deprecated)]
profiling::function_scope!();

let egui::PlatformOutput {
commands,
cursor_icon,
open_url,
copied_text,
Expand All @@ -835,6 +837,17 @@ impl State {
request_discard_reasons: _, // `egui::Context::run` handles this
} = platform_output;

for command in commands {
match command {
egui::OutputCommand::CopyText(text) => {
self.clipboard.set(text);
}
egui::OutputCommand::OpenUrl(open_url) => {
open_url_in_browser(&open_url.url);
}
}
}

self.set_cursor_icon(window, cursor_icon);

if let Some(open_url) = open_url {
Expand Down
18 changes: 8 additions & 10 deletions crates/egui/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1419,6 +1419,12 @@ impl Context {
self.output_mut(|o| o.cursor_icon = cursor_icon);
}

/// Add a command to [`PlatformOutput::commands`],
/// for the integration to execute at the end of the frame.
pub fn send_cmd(&self, cmd: crate::OutputCommand) {
self.output_mut(|o| o.commands.push(cmd));
}

/// Open an URL in a browser.
///
/// Equivalent to:
Expand All @@ -1428,24 +1434,16 @@ impl Context {
/// ctx.output_mut(|o| o.open_url = Some(open_url));
/// ```
pub fn open_url(&self, open_url: crate::OpenUrl) {
self.output_mut(|o| o.open_url = Some(open_url));
self.send_cmd(crate::OutputCommand::OpenUrl(open_url));
}

/// Copy the given text to the system clipboard.
///
/// Empty strings are ignored.
///
/// Note that in wasm applications, the clipboard is only accessible in secure contexts (e.g.,
/// HTTPS or localhost). If this method is used outside of a secure context, it will log an
/// error and do nothing. See <https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts>.
///
/// Equivalent to:
/// ```
/// # let ctx = egui::Context::default();
/// ctx.output_mut(|o| o.copied_text = "Copy this".to_owned());
/// ```
pub fn copy_text(&self, text: String) {
self.output_mut(|o| o.copied_text = text);
self.send_cmd(crate::OutputCommand::CopyText(text));
}

/// Format the given shortcut in a human-readable way (e.g. `Ctrl+Shift+X`).
Expand Down
26 changes: 25 additions & 1 deletion crates/egui/src/data/output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,21 @@ pub struct IMEOutput {
pub cursor_rect: crate::Rect,
}

/// Commands that the egui integration should execute at the end of a frame.
///
/// Commands that are specific to a viewport should be put in [`crate::ViewportCommand`] instead.
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum OutputCommand {
/// Put this text in the system clipboard.
///
/// This is often a response to [`crate::Event::Copy`] or [`crate::Event::Cut`].
CopyText(String),

/// Open this url in a browser.
OpenUrl(OpenUrl),
}

/// The non-rendering part of what egui emits each frame.
///
/// You can access (and modify) this with [`crate::Context::output`].
Expand All @@ -87,10 +102,14 @@ pub struct IMEOutput {
#[derive(Default, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct PlatformOutput {
/// Commands that the egui integration should execute at the end of a frame.
pub commands: Vec<OutputCommand>,

/// Set the cursor to this icon.
pub cursor_icon: CursorIcon,

/// If set, open this url.
#[deprecated = "Use `Context::open_url` instead"]
pub open_url: Option<OpenUrl>,

/// If set, put this text in the system clipboard. Ignore if empty.
Expand All @@ -104,6 +123,7 @@ pub struct PlatformOutput {
/// }
/// # });
/// ```
#[deprecated = "Use `Context::copy_text` instead"]
pub copied_text: String,

/// Events that may be useful to e.g. a screen reader.
Expand Down Expand Up @@ -162,7 +182,10 @@ impl PlatformOutput {

/// Add on new output.
pub fn append(&mut self, newer: Self) {
#![allow(deprecated)]

let Self {
mut commands,
cursor_icon,
open_url,
copied_text,
Expand All @@ -175,6 +198,7 @@ impl PlatformOutput {
mut request_discard_reasons,
} = newer;

self.commands.append(&mut commands);
self.cursor_icon = cursor_icon;
if open_url.is_some() {
self.open_url = open_url;
Expand Down Expand Up @@ -213,7 +237,7 @@ impl PlatformOutput {
/// What URL to open, and how.
///
/// Use with [`crate::Context::open_url`].
#[derive(Clone, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct OpenUrl {
pub url: String,
Expand Down
3 changes: 2 additions & 1 deletion crates/egui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,8 @@ pub use self::{
data::{
input::*,
output::{
self, CursorIcon, FullOutput, OpenUrl, PlatformOutput, UserAttentionType, WidgetInfo,
self, CursorIcon, FullOutput, OpenUrl, OutputCommand, PlatformOutput,
UserAttentionType, WidgetInfo,
},
Key, UserData,
},
Expand Down
7 changes: 5 additions & 2 deletions crates/egui/src/viewport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -936,13 +936,16 @@ pub enum ResizeDirection {

/// An output [viewport](crate::viewport)-command from egui to the backend, e.g. to change the window title or size.
///
/// You can send a [`ViewportCommand`] to the viewport with [`Context::send_viewport_cmd`].
/// You can send a [`ViewportCommand`] to the viewport with [`Context::send_viewport_cmd`].
///
/// See [`crate::viewport`] for how to build new viewports (native windows).
///
/// All coordinates are in logical points.
///
/// This is essentially a way to diff [`ViewportBuilder`].
/// [`ViewportCommand`] is essentially a way to diff [`ViewportBuilder`]s.
///
/// Only commands specific to a viewport are part of [`ViewportCommand`].
/// Other commands should be put in [`crate::OutputCommand`].
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub enum ViewportCommand {
Expand Down

0 comments on commit 6a98667

Please sign in to comment.