//! The clipboard seam: the platform shell provides the real implementation //! (window-system clipboards are a platform affair); widgets reach it //! through the key- and pointer-dispatch paths. //! //! Reading is a *request*, not a call that returns text: on Wayland the //! content comes from whatever client owns the selection, over a pipe that //! client writes at its own pace — or never, if it is wedged. A platform //! whose read must negotiate like that answers [`TextRequest::Pending`] and //! delivers the text later through `WidgetTree::dispatch_paste`, off the UI //! thread's critical path. A platform (or test double) that has the text in //! hand answers [`TextRequest::Ready`] and the paste happens synchronously. /// Which of the window system's selections to address. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Selection { /// The explicit copy/paste clipboard (ctrl-C / ctrl-V). Clipboard, /// The implicit selection: selecting text populates it, middle-click /// pastes it (the X11/Wayland "primary selection" convention). Primary, } /// The immediate answer to [`Clipboard::request_text`]. #[derive(Debug, PartialEq, Eq)] pub enum TextRequest { /// The text (or its definite absence) was known on the spot. Ready(Option), /// The platform is fetching. Whatever arrives — possibly nothing — /// comes back through `WidgetTree::dispatch_paste`, to whichever /// widget is focused when it does. Pending, } pub trait Clipboard { /// Begin reading `selection`'s text. Must not block on the selection's /// owner: answer [`TextRequest::Pending`] and deliver later instead. fn request_text(&mut self, selection: Selection) -> TextRequest; fn set_text(&mut self, selection: Selection, text: &str); } /// A clipboard that holds nothing and accepts nothing: the default for /// headless use. #[derive(Default)] pub struct NoClipboard; impl Clipboard for NoClipboard { fn request_text(&mut self, _selection: Selection) -> TextRequest { TextRequest::Ready(None) } fn set_text(&mut self, _selection: Selection, _text: &str) {} } /// An in-process clipboard, for tests. Always answers immediately, so a /// paste driven through it lands synchronously. #[derive(Default)] pub struct LocalClipboard { clipboard: Option, primary: Option, } impl LocalClipboard { fn slot(&mut self, selection: Selection) -> &mut Option { match selection { Selection::Clipboard => &mut self.clipboard, Selection::Primary => &mut self.primary, } } /// What the clipboard holds, for test assertions. pub fn get_text(&mut self, selection: Selection) -> Option { self.slot(selection).clone() } } impl Clipboard for LocalClipboard { fn request_text(&mut self, selection: Selection) -> TextRequest { TextRequest::Ready(self.slot(selection).clone()) } fn set_text(&mut self, selection: Selection, text: &str) { *self.slot(selection) = Some(text.to_owned()); } }