diff --git a/Cargo.lock b/Cargo.lock index f6b05ed1439..7e11391f1c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -240,11 +240,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df099ccb16cd014ff054ac1bf392c67feeef57164b05c42f037cd40f5d4357f4" dependencies = [ "clipboard-win", + "core-graphics", + "image", "log", "objc2", "objc2-app-kit", "objc2-foundation", "parking_lot", + "windows-sys 0.48.0", "x11rb", ] @@ -1292,6 +1295,7 @@ dependencies = [ "accesskit_winit", "ahash", "arboard", + "bytemuck", "document-features", "egui", "log", @@ -2205,6 +2209,7 @@ dependencies = [ "image-webp", "num-traits", "png", + "tiff", "zune-core", "zune-jpeg", ] @@ -2311,6 +2316,12 @@ dependencies = [ "libc", ] +[[package]] +name = "jpeg-decoder" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" + [[package]] name = "js-sys" version = "0.3.72" @@ -3882,6 +3893,17 @@ dependencies = [ "syn", ] +[[package]] +name = "tiff" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + [[package]] name = "time" version = "0.3.36" diff --git a/crates/egui-winit/Cargo.toml b/crates/egui-winit/Cargo.toml index c584db85e70..e4a83782302 100644 --- a/crates/egui-winit/Cargo.toml +++ b/crates/egui-winit/Cargo.toml @@ -36,11 +36,11 @@ android-game-activity = ["winit/android-game-activity"] android-native-activity = ["winit/android-native-activity"] ## [`bytemuck`](https://docs.rs/bytemuck) enables you to cast [`egui::epaint::Vertex`], [`egui::Vec2`] etc to `&[u8]`. -bytemuck = ["egui/bytemuck"] +bytemuck = ["egui/bytemuck", "dep:bytemuck"] ## Enable cut/copy/paste to OS clipboard. ## If disabled a clipboard will be simulated so you can still copy/paste within the egui app. -clipboard = ["arboard", "smithay-clipboard"] +clipboard = ["arboard", "bytemuck", "smithay-clipboard"] ## Enable opening links in a browser when an egui hyperlink is clicked. links = ["webbrowser"] @@ -69,6 +69,8 @@ winit = { workspace = true, default-features = false } # feature accesskit accesskit_winit = { version = "0.23", optional = true } +bytemuck = { workspace = true, optional = true } + ## Enable this when generating docs. document-features = { workspace = true, optional = true } @@ -84,4 +86,6 @@ smithay-clipboard = { version = "0.7.2", optional = true } wayland-cursor = { version = "0.31.1", default-features = false, optional = true } [target.'cfg(not(target_os = "android"))'.dependencies] -arboard = { version = "3.3", optional = true, default-features = false } +arboard = { version = "3.3", optional = true, default-features = false, features = [ + "image-data", +] } diff --git a/crates/egui-winit/src/clipboard.rs b/crates/egui-winit/src/clipboard.rs index c4192f78d55..007748792ee 100644 --- a/crates/egui-winit/src/clipboard.rs +++ b/crates/egui-winit/src/clipboard.rs @@ -82,7 +82,7 @@ impl Clipboard { Some(self.clipboard.clone()) } - pub fn set(&mut self, text: String) { + pub fn set_text(&mut self, text: String) { #[cfg(all( any( target_os = "linux", @@ -108,6 +108,23 @@ impl Clipboard { self.clipboard = text; } + + pub fn set_image(&mut self, image: &egui::ColorImage) { + #[cfg(all(feature = "arboard", not(target_os = "android")))] + if let Some(clipboard) = &mut self.arboard { + if let Err(err) = clipboard.set_image(arboard::ImageData { + width: image.width(), + height: image.height(), + bytes: std::borrow::Cow::Borrowed(bytemuck::cast_slice(&image.pixels)), + }) { + log::error!("arboard copy/cut error: {err}"); + } + log::debug!("Copied image to clipboard"); + return; + } + + log::error!("Copying images is not supported. Enable the 'clipboard' feature of `egui-winit` to enable it."); + } } #[cfg(all(feature = "arboard", not(target_os = "android")))] diff --git a/crates/egui-winit/src/lib.rs b/crates/egui-winit/src/lib.rs index d6166a57bed..ac396a01bb2 100644 --- a/crates/egui-winit/src/lib.rs +++ b/crates/egui-winit/src/lib.rs @@ -190,7 +190,7 @@ impl State { /// Places the text onto the clipboard. pub fn set_clipboard_text(&mut self, text: String) { - self.clipboard.set(text); + self.clipboard.set_text(text); } /// Returns [`false`] or the last value that [`Window::set_ime_allowed()`] was called with, used for debouncing. @@ -840,7 +840,10 @@ impl State { for command in commands { match command { egui::OutputCommand::CopyText(text) => { - self.clipboard.set(text); + self.clipboard.set_text(text); + } + egui::OutputCommand::CopyImage(image) => { + self.clipboard.set_image(&image); } egui::OutputCommand::OpenUrl(open_url) => { open_url_in_browser(&open_url.url); @@ -855,7 +858,7 @@ impl State { } if !copied_text.is_empty() { - self.clipboard.set(copied_text); + self.clipboard.set_text(copied_text); } let allow_ime = ime.is_some(); diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index 828f867e370..3ecf42ca46b 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -1439,13 +1439,22 @@ impl Context { /// Copy the given text to the system clipboard. /// - /// Note that in wasm applications, the clipboard is only accessible in secure contexts (e.g., + /// Note that in web 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 . pub fn copy_text(&self, text: String) { self.send_cmd(crate::OutputCommand::CopyText(text)); } + /// Copy the given image to the system clipboard. + /// + /// Note that in web 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 . + pub fn copy_image(&self, image: crate::ColorImage) { + self.send_cmd(crate::OutputCommand::CopyImage(image)); + } + /// Format the given shortcut in a human-readable way (e.g. `Ctrl+Shift+X`). /// /// Can be used to get the text for [`crate::Button::shortcut_text`]. diff --git a/crates/egui/src/data/output.rs b/crates/egui/src/data/output.rs index 61e87bf618c..32773fc5522 100644 --- a/crates/egui/src/data/output.rs +++ b/crates/egui/src/data/output.rs @@ -85,11 +85,14 @@ pub struct IMEOutput { #[derive(Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub enum OutputCommand { - /// Put this text in the system clipboard. + /// Put this text to the system clipboard. /// /// This is often a response to [`crate::Event::Copy`] or [`crate::Event::Cut`]. CopyText(String), + /// Put this image to the system clipboard. + CopyImage(crate::ColorImage), + /// Open this url in a browser. OpenUrl(OpenUrl), }