diff --git a/examples/clipboard/Cargo.toml b/examples/clipboard/Cargo.toml new file mode 100644 index 0000000..bdb2257 --- /dev/null +++ b/examples/clipboard/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "clipboard" +version = "0.1.0" +edition = "2021" + +[dependencies] +dioxus-std = { path="../../", features = ["clipboard"] } +dioxus = "0.4" +dioxus-desktop = "0.4" diff --git a/examples/clipboard/src/main.rs b/examples/clipboard/src/main.rs new file mode 100644 index 0000000..e3c7d68 --- /dev/null +++ b/examples/clipboard/src/main.rs @@ -0,0 +1,46 @@ +use dioxus::prelude::*; +use dioxus_std::clipboard::use_clipboard; + +fn main() { + dioxus_desktop::launch(app); +} + +fn app(cx: Scope) -> Element { + let clipboard = use_clipboard(cx); + let text = use_state(cx, String::new); + + let oninput = |e: FormEvent| { + text.set(e.data.value.clone()); + }; + + let oncopy = { + to_owned![clipboard]; + move |_| match clipboard.set(text.get().clone()) { + Ok(_) => println!("Copied to clipboard: {}", text.get()), + Err(err) => println!("Error on copy: {err:?}"), + } + }; + + let onpaste = move |_| match clipboard.get() { + Ok(contents) => { + println!("Pasted from clipboard: {contents}"); + text.set(contents); + } + Err(err) => println!("Error on paste: {err:?}"), + }; + + render!( + input { + oninput: oninput, + value: "{text}" + } + button { + onclick: oncopy, + "Copy" + } + button { + onclick: onpaste, + "Paste" + } + ) +} diff --git a/src/clipboard/mod.rs b/src/clipboard/mod.rs index 5bd98af..f51ee17 100644 --- a/src/clipboard/mod.rs +++ b/src/clipboard/mod.rs @@ -1,100 +1,3 @@ -//! Provides a clipboard abstraction to access the target system's clipboard. +mod use_clipboard; -use copypasta::{ClipboardContext, ClipboardProvider}; -use std::fmt; - -/// Contains the context for interacting with the clipboard. -/// -/// # Examples -/// -/// ``` -/// use dioxus_std; -/// -/// // Access the clipboard abstraction -/// let mut clipboard = dioxus_std::clipboard::Clipboard::new().unwrap(); -/// -/// // Get clipboard content -/// if let Ok(content) = clipboard.get_content() { -/// println!("{}", content); -/// } -/// -/// // Set clipboard content -/// clipboard.set_content("Hello, Dioxus!".to_string());; -/// -/// ``` -pub struct Clipboard { - ctx: ClipboardContext, -} - -impl Clipboard { - /// Creates a new struct to utilize the clipboard abstraction. - pub fn new() -> Result { - let ctx = match ClipboardContext::new() { - Ok(ctx) => ctx, - Err(e) => return Err(ClipboardError::FailedToInit(e.to_string())), - }; - - Ok(Self { ctx }) - } - - /// Provides a [`String`] of the target system's current clipboard content. - pub fn get_content(&mut self) -> Result { - match self.ctx.get_contents() { - Ok(content) => Ok(content), - Err(e) => Err(ClipboardError::FailedToFetchContent(e.to_string())), - } - } - - /// Set the clipboard's content to the provided [`String`] - pub fn set_content(&mut self, value: String) -> Result<(), ClipboardError> { - match self.ctx.set_contents(value) { - Ok(()) => Ok(()), - Err(e) => Err(ClipboardError::FailedToSetContent(e.to_string())), - } - } -} - -/// Represents errors when utilizing the clipboard abstraction. -#[derive(Debug)] -pub enum ClipboardError { - /// Failure when initializing the clipboard. - FailedToInit(String), - /// Failure to retrieve clipboard content. - FailedToFetchContent(String), - /// Failure to set clipboard content. - FailedToSetContent(String), -} - -impl std::error::Error for ClipboardError {} -impl fmt::Display for ClipboardError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - ClipboardError::FailedToInit(s) => write!(f, "{}", s), - ClipboardError::FailedToFetchContent(s) => write!(f, "{}", s), - ClipboardError::FailedToSetContent(s) => write!(f, "{}", s), - } - } -} - -// Tests -// This doesn't work in CI. -/*#[test] -fn test_clipboard() { - let mut clipboard = Clipboard::new().unwrap(); - - // Preserve user's clipboard contents when testing - let initial_content = clipboard.get_content().unwrap(); - - // Set the content - let new_content = String::from("Hello, Dioxus!"); - clipboard.set_content(new_content.clone()).unwrap(); - - // Get the new content - let content = clipboard.get_content().unwrap(); - - // Return previous content - For some reason this only works if the test panics..? - clipboard.set_content(initial_content).unwrap(); - - // Check if the abstraction worked - assert_eq!(new_content, content); -}*/ +pub use use_clipboard::*; diff --git a/src/clipboard/use_clipboard.rs b/src/clipboard/use_clipboard.rs new file mode 100644 index 0000000..e90d631 --- /dev/null +++ b/src/clipboard/use_clipboard.rs @@ -0,0 +1,65 @@ +//! Provides a clipboard abstraction to access the target system's clipboard. + +use copypasta::{ClipboardContext, ClipboardProvider}; +use dioxus::prelude::{RefCell, ScopeState}; +use std::rc::Rc; + +#[derive(Debug, PartialEq, Clone)] +pub enum ClipboardError { + FailedToRead, + FailedToSet, +} + +/// Handle to access the ClipboardContext. +#[derive(Clone)] +pub struct UseClipboard { + clipboard: Rc>, +} + +impl UseClipboard { + // Read from the clipboard + pub fn get(&self) -> Result { + self.clipboard + .borrow_mut() + .get_contents() + .map_err(|_| ClipboardError::FailedToRead) + } + + // Write to the clipboard + pub fn set(&self, contents: String) -> Result<(), ClipboardError> { + self.clipboard + .borrow_mut() + .set_contents(contents) + .map_err(|_| ClipboardError::FailedToSet) + } +} + +/// Access the clipboard. +/// +/// # Examples +/// +/// ```ignore +/// use dioxus_std::clipboard::use_clipboard; +/// +/// // Get a handle to the clipboard +/// let clipboard = use_clipboard(cx); +/// +/// // Read the clipboard content +/// if let Ok(content) = clipboard.get() { +/// println!("{}", content); +/// } +/// +/// // Write to the clipboard +/// clipboard.set("Hello, Dioxus!".to_string());; +/// +/// ``` +pub fn use_clipboard(cx: &ScopeState) -> UseClipboard { + let clipboard = match cx.consume_context() { + Some(rt) => rt, + None => { + let clipboard = ClipboardContext::new().expect("Cannot create Clipboard."); + cx.provide_root_context(Rc::new(RefCell::new(clipboard))) + } + }; + UseClipboard { clipboard } +}