1use gpui::{AnyView, DefiniteLength};
2
3use crate::{prelude::*, IconPosition};
4use crate::{
5 ButtonCommon, ButtonLike, ButtonSize, ButtonStyle, Icon, IconSize, Label, LineHeightStyle,
6};
7
8use super::button_icon::ButtonIcon;
9
10#[derive(IntoElement)]
11pub struct Button {
12 base: ButtonLike,
13 label: SharedString,
14 label_color: Option<Color>,
15 label_size: Option<LabelSize>,
16 selected_label: Option<SharedString>,
17 icon: Option<Icon>,
18 icon_position: Option<IconPosition>,
19 icon_size: Option<IconSize>,
20 icon_color: Option<Color>,
21 selected_icon: Option<Icon>,
22}
23
24impl Button {
25 pub fn new(id: impl Into<ElementId>, label: impl Into<SharedString>) -> Self {
26 Self {
27 base: ButtonLike::new(id),
28 label: label.into(),
29 label_color: None,
30 label_size: None,
31 selected_label: None,
32 icon: None,
33 icon_position: None,
34 icon_size: None,
35 icon_color: None,
36 selected_icon: None,
37 }
38 }
39
40 pub fn color(mut self, label_color: impl Into<Option<Color>>) -> Self {
41 self.label_color = label_color.into();
42 self
43 }
44
45 pub fn label_size(mut self, label_size: impl Into<Option<LabelSize>>) -> Self {
46 self.label_size = label_size.into();
47 self
48 }
49
50 pub fn selected_label<L: Into<SharedString>>(mut self, label: impl Into<Option<L>>) -> Self {
51 self.selected_label = label.into().map(Into::into);
52 self
53 }
54
55 pub fn icon(mut self, icon: impl Into<Option<Icon>>) -> Self {
56 self.icon = icon.into();
57 self
58 }
59
60 pub fn icon_position(mut self, icon_position: impl Into<Option<IconPosition>>) -> Self {
61 self.icon_position = icon_position.into();
62 self
63 }
64
65 pub fn icon_size(mut self, icon_size: impl Into<Option<IconSize>>) -> Self {
66 self.icon_size = icon_size.into();
67 self
68 }
69
70 pub fn icon_color(mut self, icon_color: impl Into<Option<Color>>) -> Self {
71 self.icon_color = icon_color.into();
72 self
73 }
74
75 pub fn selected_icon(mut self, icon: impl Into<Option<Icon>>) -> Self {
76 self.selected_icon = icon.into();
77 self
78 }
79}
80
81impl Selectable for Button {
82 fn selected(mut self, selected: bool) -> Self {
83 self.base = self.base.selected(selected);
84 self
85 }
86}
87
88impl Disableable for Button {
89 fn disabled(mut self, disabled: bool) -> Self {
90 self.base = self.base.disabled(disabled);
91 self
92 }
93}
94
95impl Clickable for Button {
96 fn on_click(
97 mut self,
98 handler: impl Fn(&gpui::ClickEvent, &mut WindowContext) + 'static,
99 ) -> Self {
100 self.base = self.base.on_click(handler);
101 self
102 }
103}
104
105impl FixedWidth for Button {
106 fn width(mut self, width: DefiniteLength) -> Self {
107 self.base = self.base.width(width);
108 self
109 }
110
111 fn full_width(mut self) -> Self {
112 self.base = self.base.full_width();
113 self
114 }
115}
116
117impl ButtonCommon for Button {
118 fn id(&self) -> &ElementId {
119 self.base.id()
120 }
121
122 fn style(mut self, style: ButtonStyle) -> Self {
123 self.base = self.base.style(style);
124 self
125 }
126
127 fn size(mut self, size: ButtonSize) -> Self {
128 self.base = self.base.size(size);
129 self
130 }
131
132 fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
133 self.base = self.base.tooltip(tooltip);
134 self
135 }
136}
137
138impl RenderOnce for Button {
139 #[allow(refining_impl_trait)]
140 fn render(self, _cx: &mut WindowContext) -> ButtonLike {
141 let is_disabled = self.base.disabled;
142 let is_selected = self.base.selected;
143
144 let label = self
145 .selected_label
146 .filter(|_| is_selected)
147 .unwrap_or(self.label);
148
149 let label_color = if is_disabled {
150 Color::Disabled
151 } else if is_selected {
152 Color::Selected
153 } else {
154 self.label_color.unwrap_or_default()
155 };
156
157 self.base.child(
158 h_stack()
159 .gap_1()
160 .when(self.icon_position.is_some(), |this| {
161 this.children(self.icon.map(|icon| {
162 ButtonIcon::new(icon)
163 .disabled(is_disabled)
164 .selected(is_selected)
165 .selected_icon(self.selected_icon)
166 .size(self.icon_size)
167 .color(self.icon_color)
168 }))
169 })
170 .child(
171 Label::new(label)
172 .color(label_color)
173 .size(self.label_size.unwrap_or_default())
174 .line_height_style(LineHeightStyle::UiLabel),
175 )
176 .when(!self.icon_position.is_some(), |this| {
177 this.children(self.icon.map(|icon| {
178 ButtonIcon::new(icon)
179 .disabled(is_disabled)
180 .selected(is_selected)
181 .selected_icon(self.selected_icon)
182 .size(self.icon_size)
183 .color(self.icon_color)
184 }))
185 }),
186 )
187 }
188}