gpui_component/
clipboard.rs1use std::{rc::Rc, time::Duration};
2
3use gpui::{
4 prelude::FluentBuilder, App, ClipboardItem, ElementId, IntoElement, RenderOnce, SharedString,
5 Window,
6};
7
8use crate::{
9 button::{Button, ButtonVariants as _},
10 IconName, Sizable as _,
11};
12
13#[derive(IntoElement)]
15pub struct Clipboard {
16 id: ElementId,
17 value: SharedString,
18 value_fn: Option<Rc<dyn Fn(&mut Window, &mut App) -> SharedString>>,
19 on_copied: Option<Rc<dyn Fn(SharedString, &mut Window, &mut App)>>,
20}
21
22impl Clipboard {
23 pub fn new(id: impl Into<ElementId>) -> Self {
25 Self {
26 id: id.into(),
27 value: SharedString::default(),
28 value_fn: None,
29 on_copied: None,
30 }
31 }
32
33 pub fn value(mut self, value: impl Into<SharedString>) -> Self {
35 self.value = value.into();
36 self
37 }
38
39 pub fn value_fn(
43 mut self,
44 value: impl Fn(&mut Window, &mut App) -> SharedString + 'static,
45 ) -> Self {
46 self.value_fn = Some(Rc::new(value));
47 self
48 }
49
50 pub fn on_copied<F>(mut self, handler: F) -> Self
52 where
53 F: Fn(SharedString, &mut Window, &mut App) + 'static,
54 {
55 self.on_copied = Some(Rc::new(handler));
56 self
57 }
58}
59
60impl RenderOnce for Clipboard {
61 fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
62 let state = window.use_keyed_state(self.id.clone(), cx, |_, _| ClipboardState::default());
63
64 let value = self.value.clone();
65 let clipboard_id = self.id.clone();
66 let copied = state.read(cx).copied;
67 let value_fn = self.value_fn.clone();
68
69 Button::new(clipboard_id)
70 .icon(if copied {
71 IconName::Check
72 } else {
73 IconName::Copy
74 })
75 .ghost()
76 .xsmall()
77 .when(!copied, |this| {
78 this.on_click({
79 let state = state.clone();
80 let on_copied = self.on_copied.clone();
81 move |_, window, cx| {
82 cx.stop_propagation();
83 let value = value_fn
84 .as_ref()
85 .map(|f| f(window, cx))
86 .unwrap_or_else(|| value.clone());
87 cx.write_to_clipboard(ClipboardItem::new_string(value.to_string()));
88 state.update(cx, |state, cx| {
89 state.copied = true;
90 cx.notify();
91 });
92
93 let state = state.clone();
94 cx.spawn(async move |cx| {
95 cx.background_executor().timer(Duration::from_secs(2)).await;
96 _ = state.update(cx, |state, cx| {
97 state.copied = false;
98 cx.notify();
99 });
100 })
101 .detach();
102
103 if let Some(on_copied) = &on_copied {
104 on_copied(value.clone(), window, cx);
105 }
106 }
107 })
108 })
109 }
110}
111
112#[doc(hidden)]
113#[derive(Default)]
114struct ClipboardState {
115 copied: bool,
116}