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