diff options
-rw-r--r-- | CHANGELOG.md | 4 | ||||
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | core/src/clipboard.rs | 19 | ||||
-rw-r--r-- | runtime/src/clipboard.rs | 43 | ||||
-rw-r--r-- | src/lib.rs | 4 | ||||
-rw-r--r-- | widget/src/text_editor.rs | 11 | ||||
-rw-r--r-- | widget/src/text_input.rs | 19 | ||||
-rw-r--r-- | winit/src/application.rs | 8 | ||||
-rw-r--r-- | winit/src/clipboard.rs | 38 | ||||
-rw-r--r-- | winit/src/multi_window.rs | 8 |
10 files changed, 110 insertions, 46 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 317bfc8f..816ea89a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `Themer` widget. [#2209](https://github.com/iced-rs/iced/pull/2209) - `Transform` primitive. [#2120](https://github.com/iced-rs/iced/pull/2120) - Cut functionality for `TextEditor`. [#2215](https://github.com/iced-rs/iced/pull/2215) -- Disabled support for `Checkbox`. [#2109](https://github.com/iced-rs/iced/pull/2109) +- Primary clipboard support. [#2240](https://github.com/iced-rs/iced/pull/2240) +- Disabled state for `Checkbox`. [#2109](https://github.com/iced-rs/iced/pull/2109) - `skip_taskbar` window setting for Windows. [#2211](https://github.com/iced-rs/iced/pull/2211) - `fetch_maximized` and `fetch_minimized` commands in `window`. [#2189](https://github.com/iced-rs/iced/pull/2189) - `run_with_handle` command in `window`. [#2200](https://github.com/iced-rs/iced/pull/2200) @@ -150,6 +151,7 @@ Many thanks to... - @nyurik - @Remmirad - @ripytide +- @snaggen - @Tahinli - @tarkah - @tzemanovic @@ -160,5 +160,5 @@ web-sys = "=0.3.67" web-time = "0.2" wgpu = "0.19" winapi = "0.3" -window_clipboard = "0.4" +window_clipboard = "0.4.1" winit = { git = "https://github.com/iced-rs/winit.git", rev = "b91e39ece2c0d378c3b80da7f3ab50e17bb798a5" } diff --git a/core/src/clipboard.rs b/core/src/clipboard.rs index 081b4004..5df3e267 100644 --- a/core/src/clipboard.rs +++ b/core/src/clipboard.rs @@ -4,10 +4,21 @@ /// applications. pub trait Clipboard { /// Reads the current content of the [`Clipboard`] as text. - fn read(&self) -> Option<String>; + fn read(&self, kind: Kind) -> Option<String>; /// Writes the given text contents to the [`Clipboard`]. - fn write(&mut self, contents: String); + fn write(&mut self, kind: Kind, contents: String); +} + +/// The kind of [`Clipboard`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Kind { + /// The standard clipboard. + Standard, + /// The primary clipboard. + /// + /// Normally only present in X11 and Wayland. + Primary, } /// A null implementation of the [`Clipboard`] trait. @@ -15,9 +26,9 @@ pub trait Clipboard { pub struct Null; impl Clipboard for Null { - fn read(&self) -> Option<String> { + fn read(&self, _kind: Kind) -> Option<String> { None } - fn write(&mut self, _contents: String) {} + fn write(&mut self, _kind: Kind, _contents: String) {} } diff --git a/runtime/src/clipboard.rs b/runtime/src/clipboard.rs index bc450912..dd47c47d 100644 --- a/runtime/src/clipboard.rs +++ b/runtime/src/clipboard.rs @@ -1,5 +1,6 @@ //! Access the clipboard. use crate::command::{self, Command}; +use crate::core::clipboard::Kind; use crate::futures::MaybeSend; use std::fmt; @@ -9,10 +10,10 @@ use std::fmt; /// [`Command`]: crate::Command pub enum Action<T> { /// Read the clipboard and produce `T` with the result. - Read(Box<dyn Fn(Option<String>) -> T>), + Read(Box<dyn Fn(Option<String>) -> T>, Kind), /// Write the given contents to the clipboard. - Write(String), + Write(String, Kind), } impl<T> Action<T> { @@ -25,8 +26,10 @@ impl<T> Action<T> { T: 'static, { match self { - Self::Read(o) => Action::Read(Box::new(move |s| f(o(s)))), - Self::Write(content) => Action::Write(content), + Self::Read(o, target) => { + Action::Read(Box::new(move |s| f(o(s))), target) + } + Self::Write(content, target) => Action::Write(content, target), } } } @@ -34,8 +37,8 @@ impl<T> Action<T> { impl<T> fmt::Debug for Action<T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::Read(_) => write!(f, "Action::Read"), - Self::Write(_) => write!(f, "Action::Write"), + Self::Read(_, target) => write!(f, "Action::Read{target:?}"), + Self::Write(_, target) => write!(f, "Action::Write({target:?})"), } } } @@ -44,10 +47,34 @@ impl<T> fmt::Debug for Action<T> { pub fn read<Message>( f: impl Fn(Option<String>) -> Message + 'static, ) -> Command<Message> { - Command::single(command::Action::Clipboard(Action::Read(Box::new(f)))) + Command::single(command::Action::Clipboard(Action::Read( + Box::new(f), + Kind::Standard, + ))) +} + +/// Read the current contents of the primary clipboard. +pub fn read_primary<Message>( + f: impl Fn(Option<String>) -> Message + 'static, +) -> Command<Message> { + Command::single(command::Action::Clipboard(Action::Read( + Box::new(f), + Kind::Primary, + ))) } /// Write the given contents to the clipboard. pub fn write<Message>(contents: String) -> Command<Message> { - Command::single(command::Action::Clipboard(Action::Write(contents))) + Command::single(command::Action::Clipboard(Action::Write( + contents, + Kind::Standard, + ))) +} + +/// Write the given contents to the primary clipboard. +pub fn write_primary<Message>(contents: String) -> Command<Message> { + Command::single(command::Action::Clipboard(Action::Write( + contents, + Kind::Primary, + ))) } @@ -200,7 +200,9 @@ pub use crate::core::{ pub mod clipboard { //! Access the clipboard. - pub use crate::runtime::clipboard::{read, write}; + pub use crate::runtime::clipboard::{ + read, read_primary, write, write_primary, + }; } pub mod executor { diff --git a/widget/src/text_editor.rs b/widget/src/text_editor.rs index cbcab1eb..50f7fabb 100644 --- a/widget/src/text_editor.rs +++ b/widget/src/text_editor.rs @@ -1,4 +1,5 @@ //! Display a multi-line text input for text editing. +use crate::core::clipboard::{self, Clipboard}; use crate::core::event::{self, Event}; use crate::core::keyboard; use crate::core::keyboard::key; @@ -10,7 +11,7 @@ use crate::core::text::highlighter::{self, Highlighter}; use crate::core::text::{self, LineHeight}; use crate::core::widget::{self, Widget}; use crate::core::{ - Clipboard, Element, Length, Padding, Pixels, Rectangle, Shell, Size, Vector, + Element, Length, Padding, Pixels, Rectangle, Shell, Size, Vector, }; use std::cell::RefCell; @@ -448,17 +449,19 @@ where } Update::Copy => { if let Some(selection) = self.content.selection() { - clipboard.write(selection); + clipboard.write(clipboard::Kind::Standard, selection); } } Update::Cut => { if let Some(selection) = self.content.selection() { - clipboard.write(selection.clone()); + clipboard.write(clipboard::Kind::Standard, selection); shell.publish(on_edit(Action::Edit(Edit::Delete))); } } Update::Paste => { - if let Some(contents) = clipboard.read() { + if let Some(contents) = + clipboard.read(clipboard::Kind::Standard) + { shell.publish(on_edit(Action::Edit(Edit::Paste( Arc::new(contents), )))); diff --git a/widget/src/text_input.rs b/widget/src/text_input.rs index 0a7ed014..f5b57422 100644 --- a/widget/src/text_input.rs +++ b/widget/src/text_input.rs @@ -12,6 +12,7 @@ pub use value::Value; use editor::Editor; use crate::core::alignment; +use crate::core::clipboard::{self, Clipboard}; use crate::core::event::{self, Event}; use crate::core::keyboard; use crate::core::keyboard::key; @@ -26,8 +27,8 @@ use crate::core::widget::operation::{self, Operation}; use crate::core::widget::tree::{self, Tree}; use crate::core::window; use crate::core::{ - Clipboard, Element, Layout, Length, Padding, Pixels, Point, Rectangle, - Shell, Size, Vector, Widget, + Element, Layout, Length, Padding, Pixels, Point, Rectangle, Shell, Size, + Vector, Widget, }; use crate::runtime::Command; @@ -864,8 +865,10 @@ where if let Some((start, end)) = state.cursor.selection(value) { - clipboard - .write(value.select(start, end).to_string()); + clipboard.write( + clipboard::Kind::Standard, + value.select(start, end).to_string(), + ); } } keyboard::Key::Character("x") @@ -874,8 +877,10 @@ where if let Some((start, end)) = state.cursor.selection(value) { - clipboard - .write(value.select(start, end).to_string()); + clipboard.write( + clipboard::Kind::Standard, + value.select(start, end).to_string(), + ); } let mut editor = Editor::new(value, &mut state.cursor); @@ -894,7 +899,7 @@ where Some(content) => content, None => { let content: String = clipboard - .read() + .read(clipboard::Kind::Standard) .unwrap_or_default() .chars() .filter(|c| !c.is_control()) diff --git a/winit/src/application.rs b/winit/src/application.rs index 77e2c83e..0fc67adc 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -704,15 +704,15 @@ pub fn run_command<A, C, E>( runtime.run(stream); } command::Action::Clipboard(action) => match action { - clipboard::Action::Read(tag) => { - let message = tag(clipboard.read()); + clipboard::Action::Read(tag, kind) => { + let message = tag(clipboard.read(kind)); proxy .send_event(message) .expect("Send message to event loop"); } - clipboard::Action::Write(contents) => { - clipboard.write(contents); + clipboard::Action::Write(contents, kind) => { + clipboard.write(kind, contents); } }, command::Action::Window(action) => match action { diff --git a/winit/src/clipboard.rs b/winit/src/clipboard.rs index 8f5c5e63..5237ca01 100644 --- a/winit/src/clipboard.rs +++ b/winit/src/clipboard.rs @@ -1,5 +1,7 @@ //! Access the clipboard. +use crate::core::clipboard::Kind; + /// A buffer for short-term storage and transfer within and between /// applications. #[allow(missing_debug_implementations)] @@ -33,33 +35,45 @@ impl Clipboard { } /// Reads the current content of the [`Clipboard`] as text. - pub fn read(&self) -> Option<String> { + pub fn read(&self, kind: Kind) -> Option<String> { match &self.state { - State::Connected(clipboard) => clipboard.read().ok(), + State::Connected(clipboard) => match kind { + Kind::Standard => clipboard.read().ok(), + Kind::Primary => clipboard.read_primary().and_then(Result::ok), + }, State::Unavailable => None, } } /// Writes the given text contents to the [`Clipboard`]. - pub fn write(&mut self, contents: String) { + pub fn write(&mut self, kind: Kind, contents: String) { match &mut self.state { - State::Connected(clipboard) => match clipboard.write(contents) { - Ok(()) => {} - Err(error) => { - log::warn!("error writing to clipboard: {error}"); + State::Connected(clipboard) => { + let result = match kind { + Kind::Standard => clipboard.write(contents), + Kind::Primary => { + clipboard.write_primary(contents).unwrap_or(Ok(())) + } + }; + + match result { + Ok(()) => {} + Err(error) => { + log::warn!("error writing to clipboard: {error}"); + } } - }, + } State::Unavailable => {} } } } impl crate::core::Clipboard for Clipboard { - fn read(&self) -> Option<String> { - self.read() + fn read(&self, kind: Kind) -> Option<String> { + self.read(kind) } - fn write(&mut self, contents: String) { - self.write(contents); + fn write(&mut self, kind: Kind, contents: String) { + self.write(kind, contents); } } diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 23b2f3c4..ed00abdf 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -876,15 +876,15 @@ fn run_command<A, C, E>( runtime.run(Box::pin(stream)); } command::Action::Clipboard(action) => match action { - clipboard::Action::Read(tag) => { - let message = tag(clipboard.read()); + clipboard::Action::Read(tag, kind) => { + let message = tag(clipboard.read(kind)); proxy .send_event(message) .expect("Send message to event loop"); } - clipboard::Action::Write(contents) => { - clipboard.write(contents); + clipboard::Action::Write(contents, kind) => { + clipboard.write(kind, contents); } }, command::Action::Window(action) => match action { |