diff --git a/crates/eframe/src/web/app_runner.rs b/crates/eframe/src/web/app_runner.rs index 13ad762874a..789e8e11d50 100644 --- a/crates/eframe/src/web/app_runner.rs +++ b/crates/eframe/src/web/app_runner.rs @@ -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, @@ -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); } diff --git a/crates/egui-winit/src/lib.rs b/crates/egui-winit/src/lib.rs index 50ff2d31b4b..d6166a57bed 100644 --- a/crates/egui-winit/src/lib.rs +++ b/crates/egui-winit/src/lib.rs @@ -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, @@ -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 { diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index 118cddb31fa..828f867e370 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -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: @@ -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 . - /// - /// 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`). diff --git a/crates/egui/src/data/output.rs b/crates/egui/src/data/output.rs index a878bd5fd70..61e87bf618c 100644 --- a/crates/egui/src/data/output.rs +++ b/crates/egui/src/data/output.rs @@ -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`]. @@ -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, + /// 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, /// If set, put this text in the system clipboard. Ignore if empty. @@ -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. @@ -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, @@ -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; @@ -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, diff --git a/crates/egui/src/lib.rs b/crates/egui/src/lib.rs index 1afaada95e1..b26f64d0b20 100644 --- a/crates/egui/src/lib.rs +++ b/crates/egui/src/lib.rs @@ -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, }, diff --git a/crates/egui/src/viewport.rs b/crates/egui/src/viewport.rs index 91cd12e2b60..d3f1c389d28 100644 --- a/crates/egui/src/viewport.rs +++ b/crates/egui/src/viewport.rs @@ -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 { diff --git a/scripts/setup_web.sh b/scripts/setup_web.sh index 245ff427ab0..d4166a3af2d 100755 --- a/scripts/setup_web.sh +++ b/scripts/setup_web.sh @@ -9,4 +9,6 @@ set -x rustup target add wasm32-unknown-unknown # For generating JS bindings: -cargo install --force --quiet wasm-bindgen-cli --version 0.2.95 +if ! cargo install --list | grep -q 'wasm-bindgen-cli v0.2.95'; then + cargo install --force --quiet wasm-bindgen-cli --version 0.2.95 +fi