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 as _,
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}
22
23impl Clipboard {
24 pub fn new(id: impl Into<ElementId>) -> Self {
26 Self {
27 id: id.into(),
28 value: SharedString::default(),
29 value_fn: None,
30 on_copied: None,
31 tooltip_text: None,
32 }
33 }
34
35 pub fn tooltip(mut self, tooltip: impl Into<SharedString>) -> Self {
37 self.tooltip_text = Some(tooltip.into());
38 self
39 }
40
41 pub fn value(mut self, value: impl Into<SharedString>) -> Self {
43 self.value = value.into();
44 self
45 }
46
47 pub fn value_fn(
51 mut self,
52 value: impl Fn(&mut Window, &mut App) -> SharedString + 'static,
53 ) -> Self {
54 self.value_fn = Some(Rc::new(value));
55 self
56 }
57
58 pub fn on_copied<F>(mut self, handler: F) -> Self
60 where
61 F: Fn(SharedString, &mut Window, &mut App) + 'static,
62 {
63 self.on_copied = Some(Rc::new(handler));
64 self
65 }
66}
67
68impl RenderOnce for Clipboard {
69 fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
70 let state = window.use_keyed_state(self.id.clone(), cx, |_, _| ClipboardState::default());
71
72 let value = self.value.clone();
73 let clipboard_id = self.id.clone();
74 let copied = state.read(cx).copied;
75 let value_fn = self.value_fn.clone();
76
77 Button::new(clipboard_id)
78 .icon(if copied {
79 IconName::Check
80 } else {
81 IconName::Copy
82 })
83 .ghost()
84 .xsmall()
85 .when_some(self.tooltip_text, |this, text| this.tooltip(text))
86 .when(!copied, |this| {
87 this.on_click({
88 let state = state.clone();
89 let on_copied = self.on_copied.clone();
90 move |_, window, cx| {
91 cx.stop_propagation();
92 let value = value_fn
93 .as_ref()
94 .map(|f| f(window, cx))
95 .unwrap_or_else(|| value.clone());
96 cx.write_to_clipboard(ClipboardItem::new_string(value.to_string()));
97 state.update(cx, |state, cx| {
98 state.copied = true;
99 cx.notify();
100 });
101
102 let state = state.clone();
103 cx.spawn(async move |cx| {
104 cx.background_executor().timer(Duration::from_secs(2)).await;
105 _ = state.update(cx, |state, cx| {
106 state.copied = false;
107 cx.notify();
108 });
109 })
110 .detach();
111
112 if let Some(on_copied) = &on_copied {
113 on_copied(value.clone(), window, cx);
114 }
115 }
116 })
117 })
118 }
119}
120
121#[doc(hidden)]
122#[derive(Default)]
123struct ClipboardState {
124 copied: bool,
125}