clipboard.rs
raw
//! 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<String>),
/// 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<String>,
primary: Option<String>,
}
impl LocalClipboard {
fn slot(&mut self, selection: Selection) -> &mut Option<String> {
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<String> {
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());
}
}