Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use_clipboard #18

Merged
merged 4 commits into from
Nov 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions examples/clipboard/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
46 changes: 46 additions & 0 deletions examples/clipboard/src/main.rs
Original file line number Diff line number Diff line change
@@ -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"
}
)
}
101 changes: 2 additions & 99 deletions src/clipboard/mod.rs
Original file line number Diff line number Diff line change
@@ -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<Self, ClipboardError> {
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<String, ClipboardError> {
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::*;
65 changes: 65 additions & 0 deletions src/clipboard/use_clipboard.rs
Original file line number Diff line number Diff line change
@@ -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<RefCell<ClipboardContext>>,
}

impl UseClipboard {
// Read from the clipboard
pub fn get(&self) -> Result<String, ClipboardError> {
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 }
}