use gpui::{ AnyElement, App, ClipboardItem, IntoElement, ParentElement, RenderOnce, Styled, Window, }; use crate::{Tooltip, prelude::*}; #[derive(IntoElement, RegisterComponent)] pub struct CopyButton { message: SharedString, icon_size: IconSize, disabled: bool, tooltip_label: SharedString, visible_on_hover: Option, custom_on_click: Option>, } impl CopyButton { pub fn new(message: impl Into) -> Self { Self { message: message.into(), icon_size: IconSize::Small, disabled: false, tooltip_label: "Copy".into(), visible_on_hover: None, custom_on_click: None, } } pub fn icon_size(mut self, icon_size: IconSize) -> Self { self.icon_size = icon_size; self } pub fn disabled(mut self, disabled: bool) -> Self { self.disabled = disabled; self } pub fn tooltip_label(mut self, tooltip_label: impl Into) -> Self { self.tooltip_label = tooltip_label.into(); self } pub fn visible_on_hover(mut self, visible_on_hover: impl Into) -> Self { self.visible_on_hover = Some(visible_on_hover.into()); self } pub fn custom_on_click( mut self, custom_on_click: impl Fn(&mut Window, &mut App) + 'static, ) -> Self { self.custom_on_click = Some(Box::new(custom_on_click)); self } } impl RenderOnce for CopyButton { fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement { let message = self.message; let message_clone = message.clone(); let id = format!("copy-button-{}", message_clone); let copied = cx .read_from_clipboard() .map(|item| item.text().as_ref() == Some(&message_clone.into())) .unwrap_or(false); let (icon, color, tooltip) = if copied { (IconName::Check, Color::Success, "Copied!".into()) } else { (IconName::Copy, Color::Muted, self.tooltip_label) }; let custom_on_click = self.custom_on_click; let visible_on_hover = self.visible_on_hover; let button = IconButton::new(id, icon) .icon_color(color) .icon_size(self.icon_size) .disabled(self.disabled) .tooltip(Tooltip::text(tooltip)) .on_click(move |_, window, cx| { if let Some(custom_on_click) = custom_on_click.as_ref() { (custom_on_click)(window, cx); } else { cx.stop_propagation(); cx.write_to_clipboard(ClipboardItem::new_string(message.clone().into())); } }); if let Some(visible_on_hover) = visible_on_hover { button.visible_on_hover(visible_on_hover) } else { button } } } impl Component for CopyButton { fn scope() -> ComponentScope { ComponentScope::Input } fn description() -> Option<&'static str> { Some("An icon button that encapsulates the logic to copy a string into the clipboard.") } fn preview(_window: &mut Window, _cx: &mut App) -> Option { let label_text = "Here's an example label"; let mut counter: usize = 0; let mut copy_b = || { counter += 1; CopyButton::new(format!( "Here's an example label (id for uniqueness: {} — ignore this)", counter )) }; let example = vec![ single_example( "Default", h_flex() .gap_1() .child(Label::new(label_text).size(LabelSize::Small)) .child(copy_b()) .into_any_element(), ), single_example( "Multiple Icon Sizes", h_flex() .gap_1() .child(Label::new(label_text).size(LabelSize::Small)) .child(copy_b().icon_size(IconSize::XSmall)) .child(copy_b().icon_size(IconSize::Medium)) .child(copy_b().icon_size(IconSize::XLarge)) .into_any_element(), ), single_example( "Custom Tooltip Label", h_flex() .gap_1() .child(Label::new(label_text).size(LabelSize::Small)) .child(copy_b().tooltip_label("Custom tooltip label")) .into_any_element(), ), single_example( "Visible On Hover", h_flex() .group("container") .gap_1() .child(Label::new(label_text).size(LabelSize::Small)) .child(copy_b().visible_on_hover("container")) .into_any_element(), ), ]; Some(example_group(example).vertical().into_any_element()) } }