1use gpui::{
2 color::Color,
3 elements::{
4 ConstrainedBox, Container, ContainerStyle, Empty, Flex, KeystrokeLabel, Label,
5 MouseEventHandler, ParentElement, Svg,
6 },
7 Action, Element, EventContext, RenderContext, View,
8};
9use serde::Deserialize;
10
11use crate::ContainedText;
12
13#[derive(Clone, Deserialize, Default)]
14pub struct CheckboxStyle {
15 pub icon: IconStyle,
16 pub label: ContainedText,
17 pub default: ContainerStyle,
18 pub checked: ContainerStyle,
19 pub hovered: ContainerStyle,
20 pub hovered_and_checked: ContainerStyle,
21}
22
23pub fn checkbox<T: 'static, V: View>(
24 label: &'static str,
25 style: &CheckboxStyle,
26 checked: bool,
27 cx: &mut RenderContext<V>,
28 change: fn(checked: bool, cx: &mut EventContext) -> (),
29) -> MouseEventHandler<T> {
30 MouseEventHandler::<T>::new(0, cx, |state, _| {
31 let indicator = if checked {
32 icon(&style.icon)
33 } else {
34 Empty::new()
35 .constrained()
36 .with_width(style.icon.dimensions.width)
37 .with_height(style.icon.dimensions.height)
38 };
39
40 Flex::row()
41 .with_children([
42 indicator
43 .contained()
44 .with_style(if checked {
45 if state.hovered() {
46 style.hovered_and_checked
47 } else {
48 style.checked
49 }
50 } else {
51 if state.hovered() {
52 style.hovered
53 } else {
54 style.default
55 }
56 })
57 .boxed(),
58 Label::new(label, style.label.text.clone())
59 .contained()
60 .with_style(style.label.container)
61 .boxed(),
62 ])
63 .align_children_center()
64 .boxed()
65 })
66 .on_click(gpui::MouseButton::Left, move |_, cx| change(!checked, cx))
67 .with_cursor_style(gpui::CursorStyle::PointingHand)
68}
69
70#[derive(Clone, Deserialize, Default)]
71pub struct IconStyle {
72 pub color: Color,
73 pub icon: String,
74 pub dimensions: Dimensions,
75}
76
77#[derive(Clone, Deserialize, Default)]
78pub struct Dimensions {
79 pub width: f32,
80 pub height: f32,
81}
82
83pub fn icon(style: &IconStyle) -> ConstrainedBox {
84 Svg::new(style.icon.clone())
85 .with_color(style.color)
86 .constrained()
87 .with_width(style.dimensions.width)
88 .with_height(style.dimensions.height)
89}
90
91pub fn keystroke_label<V: View>(
92 label_text: &'static str,
93 label_style: &ContainedText,
94 keystroke_style: &ContainedText,
95 action: Box<dyn Action>,
96 cx: &mut RenderContext<V>,
97) -> Container {
98 // FIXME: Put the theme in it's own global so we can
99 // query the keystroke style on our own
100 keystroke_label_for(
101 cx.window_id(),
102 cx.handle().id(),
103 label_text,
104 label_style,
105 keystroke_style,
106 action,
107 )
108}
109
110pub fn keystroke_label_for(
111 window_id: usize,
112 view_id: usize,
113 label_text: &'static str,
114 label_style: &ContainedText,
115 keystroke_style: &ContainedText,
116 action: Box<dyn Action>,
117) -> Container {
118 Flex::row()
119 .with_child(
120 Label::new(label_text, label_style.text.clone())
121 .contained()
122 .boxed(),
123 )
124 .with_child({
125 KeystrokeLabel::new(
126 window_id,
127 view_id,
128 action,
129 keystroke_style.container,
130 keystroke_style.text.clone(),
131 )
132 .flex_float()
133 .boxed()
134 })
135 .contained()
136 .with_style(label_style.container)
137}