1use gpui::{
2 AnyElement, App, ClipboardItem, IntoElement, ParentElement, RenderOnce, Styled, Window,
3};
4
5use crate::{Tooltip, prelude::*};
6
7#[derive(IntoElement, RegisterComponent)]
8pub struct CopyButton {
9 message: SharedString,
10 icon_size: IconSize,
11 disabled: bool,
12 tooltip_label: SharedString,
13 visible_on_hover: Option<SharedString>,
14 custom_on_click: Option<Box<dyn Fn(&mut Window, &mut App) + 'static>>,
15}
16
17impl CopyButton {
18 pub fn new(message: impl Into<SharedString>) -> Self {
19 Self {
20 message: message.into(),
21 icon_size: IconSize::Small,
22 disabled: false,
23 tooltip_label: "Copy".into(),
24 visible_on_hover: None,
25 custom_on_click: None,
26 }
27 }
28
29 pub fn icon_size(mut self, icon_size: IconSize) -> Self {
30 self.icon_size = icon_size;
31 self
32 }
33
34 pub fn disabled(mut self, disabled: bool) -> Self {
35 self.disabled = disabled;
36 self
37 }
38
39 pub fn tooltip_label(mut self, tooltip_label: impl Into<SharedString>) -> Self {
40 self.tooltip_label = tooltip_label.into();
41 self
42 }
43
44 pub fn visible_on_hover(mut self, visible_on_hover: impl Into<SharedString>) -> Self {
45 self.visible_on_hover = Some(visible_on_hover.into());
46 self
47 }
48
49 pub fn custom_on_click(
50 mut self,
51 custom_on_click: impl Fn(&mut Window, &mut App) + 'static,
52 ) -> Self {
53 self.custom_on_click = Some(Box::new(custom_on_click));
54 self
55 }
56}
57
58impl RenderOnce for CopyButton {
59 fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
60 let message = self.message;
61 let message_clone = message.clone();
62
63 let id = format!("copy-button-{}", message_clone);
64
65 let copied = cx
66 .read_from_clipboard()
67 .map(|item| item.text().as_ref() == Some(&message_clone.into()))
68 .unwrap_or(false);
69
70 let (icon, color, tooltip) = if copied {
71 (IconName::Check, Color::Success, "Copied!".into())
72 } else {
73 (IconName::Copy, Color::Muted, self.tooltip_label)
74 };
75
76 let custom_on_click = self.custom_on_click;
77 let visible_on_hover = self.visible_on_hover;
78
79 let button = IconButton::new(id, icon)
80 .icon_color(color)
81 .icon_size(self.icon_size)
82 .disabled(self.disabled)
83 .tooltip(Tooltip::text(tooltip))
84 .on_click(move |_, window, cx| {
85 if let Some(custom_on_click) = custom_on_click.as_ref() {
86 (custom_on_click)(window, cx);
87 } else {
88 cx.stop_propagation();
89 cx.write_to_clipboard(ClipboardItem::new_string(message.clone().into()));
90 }
91 });
92
93 if let Some(visible_on_hover) = visible_on_hover {
94 button.visible_on_hover(visible_on_hover)
95 } else {
96 button
97 }
98 }
99}
100
101impl Component for CopyButton {
102 fn scope() -> ComponentScope {
103 ComponentScope::Input
104 }
105
106 fn description() -> Option<&'static str> {
107 Some("An icon button that encapsulates the logic to copy a string into the clipboard.")
108 }
109
110 fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
111 let label_text = "Here's an example label";
112 let mut counter: usize = 0;
113
114 let mut copy_b = || {
115 counter += 1;
116 CopyButton::new(format!(
117 "Here's an example label (id for uniqueness: {} — ignore this)",
118 counter
119 ))
120 };
121
122 let example = vec![
123 single_example(
124 "Default",
125 h_flex()
126 .gap_1()
127 .child(Label::new(label_text).size(LabelSize::Small))
128 .child(copy_b())
129 .into_any_element(),
130 ),
131 single_example(
132 "Multiple Icon Sizes",
133 h_flex()
134 .gap_1()
135 .child(Label::new(label_text).size(LabelSize::Small))
136 .child(copy_b().icon_size(IconSize::XSmall))
137 .child(copy_b().icon_size(IconSize::Medium))
138 .child(copy_b().icon_size(IconSize::XLarge))
139 .into_any_element(),
140 ),
141 single_example(
142 "Custom Tooltip Label",
143 h_flex()
144 .gap_1()
145 .child(Label::new(label_text).size(LabelSize::Small))
146 .child(copy_b().tooltip_label("Custom tooltip label"))
147 .into_any_element(),
148 ),
149 single_example(
150 "Visible On Hover",
151 h_flex()
152 .group("container")
153 .gap_1()
154 .child(Label::new(label_text).size(LabelSize::Small))
155 .child(copy_b().visible_on_hover("container"))
156 .into_any_element(),
157 ),
158 ];
159
160 Some(example_group(example).vertical().into_any_element())
161 }
162}