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