1use gpui::{
11 AnyView, App, AppContext as _, ClickEvent, ClipboardItem, Div, ElementId, InteractiveElement,
12 IntoElement, ParentElement, RenderOnce, SharedString, StatefulInteractiveElement as _, Styled,
13 Window, div, prelude::FluentBuilder as _, px, rgb,
14};
15use icons::IconName;
16
17#[derive(IntoElement)]
22pub struct Checkbox {
23 id: ElementId,
24 checked: bool,
25 disabled: bool,
26 on_click: Option<Box<dyn Fn(&bool, &mut Window, &mut App) + 'static>>,
27}
28
29impl Checkbox {
30 pub fn new(id: impl Into<ElementId>, checked: bool) -> Self {
31 Self {
32 id: id.into(),
33 checked,
34 disabled: false,
35 on_click: None,
36 }
37 }
38
39 pub fn disabled(mut self, disabled: bool) -> Self {
40 self.disabled = disabled;
41 self
42 }
43
44 pub fn on_click(mut self, on_click: impl Fn(&bool, &mut Window, &mut App) + 'static) -> Self {
46 self.on_click = Some(Box::new(on_click));
47 self
48 }
49}
50
51impl RenderOnce for Checkbox {
52 fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
53 let checked = self.checked;
54 let disabled = self.disabled;
55 let on_click = self.on_click;
56
57 div()
58 .id(self.id)
59 .size(px(14.))
60 .flex()
61 .items_center()
62 .justify_center()
63 .rounded_xs()
64 .border_1()
65 .border_color(rgb(0x4A5568))
66 .when(checked, |this| this.bg(rgb(0x3B82F6)))
67 .when(!disabled, |this| this.cursor_pointer())
68 .when_some(on_click.filter(|_| !disabled), |this, on_click| {
69 this.on_click(move |_: &ClickEvent, window, cx| {
70 on_click(&!checked, window, cx);
71 })
72 })
73 .when(checked, |this| {
74 this.child(
75 div()
76 .size(px(8.))
77 .text_color(rgb(0xFFFFFF))
78 .child(SharedString::from("\u{2713}")),
79 )
80 })
81 }
82}
83
84#[derive(IntoElement)]
89pub struct CopyButton {
90 id: ElementId,
91 text: SharedString,
92 icon: IconName,
93}
94
95impl CopyButton {
96 pub fn new(id: impl Into<ElementId>, text: impl Into<SharedString>) -> Self {
97 Self {
98 id: id.into(),
99 text: text.into(),
100 icon: IconName::Copy,
101 }
102 }
103
104 pub fn icon(mut self, icon: IconName) -> Self {
105 self.icon = icon;
106 self
107 }
108}
109
110impl RenderOnce for CopyButton {
111 fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
112 let text = self.text;
113
114 div()
115 .id(self.id)
116 .flex()
117 .items_center()
118 .justify_center()
119 .size(px(20.))
120 .rounded_xs()
121 .cursor_pointer()
122 .hover(|this| this.bg(rgb(0x2D3748)))
123 .child(icon_svg(self.icon, px(14.), rgb(0xA0AEC0)))
124 .on_click(move |_: &ClickEvent, _window, cx| {
125 cx.write_to_clipboard(ClipboardItem::new_string(text.to_string()));
126 })
127 }
128}
129
130pub fn icon_svg(
133 icon: IconName,
134 size: impl Into<gpui::Pixels>,
135 color: impl Into<gpui::Hsla>,
136) -> gpui::Svg {
137 gpui::svg()
138 .path(icon.path())
139 .size(size.into())
140 .text_color(color.into())
141}
142
143struct SimpleTooltip {
147 text: SharedString,
148}
149
150impl gpui::Render for SimpleTooltip {
151 fn render(&mut self, _window: &mut Window, _cx: &mut gpui::Context<Self>) -> impl IntoElement {
152 div()
153 .rounded_xs()
154 .bg(rgb(0x1A202C))
155 .text_color(rgb(0xE2E8F0))
156 .px_2()
157 .py_1()
158 .text_sm()
159 .child(self.text.clone())
160 }
161}
162
163pub fn simple_tooltip(text: impl Into<SharedString>) -> impl Fn(&mut Window, &mut App) -> AnyView {
166 let text = text.into();
167 move |_window, cx| cx.new(|_| SimpleTooltip { text: text.clone() }).into()
168}
169
170#[derive(Debug, Clone, Copy, PartialEq, Eq)]
177pub enum ScrollAxes {
178 Vertical,
179 Horizontal,
180 Both,
181}
182
183pub trait WithScrollbar {
190 fn with_scrollbar(self, handle: &gpui::ScrollHandle, axes: ScrollAxes) -> Self;
191}
192
193impl WithScrollbar for gpui::Stateful<Div> {
194 fn with_scrollbar(self, handle: &gpui::ScrollHandle, axes: ScrollAxes) -> Self {
195 let this = match axes {
196 ScrollAxes::Vertical => self.overflow_y_scroll(),
197 ScrollAxes::Horizontal => self.overflow_x_scroll(),
198 ScrollAxes::Both => self.overflow_scroll(),
199 };
200 this.track_scroll(handle)
201 }
202}