sequoia_elements/
clipboard.rs1use std::any::{Any, TypeId};
2use std::cell::RefCell;
3use std::collections::HashMap;
4use std::rc::Rc;
5
6pub trait Clipboard {
7 fn get_text(&self) -> Option<String>;
8
9 fn set_text(&self, text: &str);
10}
11
12#[derive(Clone)]
13pub struct SharedClipboard(pub Rc<dyn Clipboard>);
14
15#[derive(Clone, Default)]
16pub struct MemClipboard {
17 text: Rc<RefCell<String>>,
18}
19
20impl MemClipboard {
21 pub fn new() -> Self {
22 Self::default()
23 }
24
25 pub fn content(&self) -> String {
26 self.text.borrow().clone()
27 }
28}
29
30impl Clipboard for MemClipboard {
31 fn get_text(&self) -> Option<String> {
32 let text = self.text.borrow();
33 if text.is_empty() {
34 None
35 } else {
36 Some(text.clone())
37 }
38 }
39
40 fn set_text(&self, text: &str) {
41 *self.text.borrow_mut() = text.to_owned();
42 }
43}
44
45#[derive(Clone, Default)]
46pub struct DataClipboard {
47 slots: Rc<RefCell<HashMap<TypeId, Rc<dyn Any>>>>,
48}
49
50impl DataClipboard {
51 pub fn new() -> Self {
52 Self::default()
53 }
54
55 pub fn set<T: Clone + 'static>(&self, value: T) {
56 self.slots
57 .borrow_mut()
58 .insert(TypeId::of::<T>(), Rc::new(value));
59 }
60
61 pub fn get<T: Clone + 'static>(&self) -> Option<T> {
62 self.slots
63 .borrow()
64 .get(&TypeId::of::<T>())?
65 .downcast_ref::<T>()
66 .cloned()
67 }
68
69 pub fn clear<T: 'static>(&self) -> bool {
70 self.slots.borrow_mut().remove(&TypeId::of::<T>()).is_some()
71 }
72}
73
74#[derive(Clone)]
75pub struct SharedDataClipboard(pub Rc<DataClipboard>);
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80
81 #[test]
82 fn mem_clipboard_roundtrip() {
83 let clip = MemClipboard::new();
84 assert_eq!(clip.get_text(), None, "empty at start");
85 clip.set_text("hello");
86 assert_eq!(clip.get_text().as_deref(), Some("hello"));
87 assert_eq!(clip.content(), "hello");
88 let shared = SharedClipboard(Rc::new(clip.clone()));
89 shared.0.set_text("shared");
90 assert_eq!(clip.get_text().as_deref(), Some("shared"), "same clipboard");
91 }
92
93 #[test]
94 fn data_clipboard_is_typed() {
95 #[derive(Clone, PartialEq, Debug)]
96 struct Layer(String);
97 #[derive(Clone, PartialEq, Debug)]
98 struct Other(i32);
99 let clip = DataClipboard::new();
100 assert_eq!(clip.get::<Layer>(), None, "empty at start");
101 clip.set(Layer("hero".into()));
102 clip.set(Other(7));
103 assert_eq!(clip.get::<Layer>(), Some(Layer("hero".into())));
104 assert_eq!(clip.get::<Other>(), Some(Other(7)));
105
106 clip.set(Layer("footer".into()));
107 assert_eq!(clip.get::<Layer>(), Some(Layer("footer".into())));
108 assert!(clip.clear::<Layer>());
109 assert_eq!(clip.get::<Layer>(), None);
110 assert!(!clip.clear::<Layer>());
111
112 let shared = SharedDataClipboard(Rc::new(clip.clone()));
113 shared.0.set(Other(9));
114 assert_eq!(clip.get::<Other>(), Some(Other(9)), "same slots");
115 }
116}