button.rs

  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 SelectableButton for Button {
 96    fn selected_style(mut self, style: ButtonStyle) -> Self {
 97        self.base = self.base.selected_style(style);
 98        self
 99    }
100}
101
102impl Disableable for Button {
103    fn disabled(mut self, disabled: bool) -> Self {
104        self.base = self.base.disabled(disabled);
105        self
106    }
107}
108
109impl Clickable for Button {
110    fn on_click(
111        mut self,
112        handler: impl Fn(&gpui::ClickEvent, &mut WindowContext) + 'static,
113    ) -> Self {
114        self.base = self.base.on_click(handler);
115        self
116    }
117}
118
119impl FixedWidth for Button {
120    fn width(mut self, width: DefiniteLength) -> Self {
121        self.base = self.base.width(width);
122        self
123    }
124
125    fn full_width(mut self) -> Self {
126        self.base = self.base.full_width();
127        self
128    }
129}
130
131impl ButtonCommon for Button {
132    fn id(&self) -> &ElementId {
133        self.base.id()
134    }
135
136    fn style(mut self, style: ButtonStyle) -> Self {
137        self.base = self.base.style(style);
138        self
139    }
140
141    fn size(mut self, size: ButtonSize) -> Self {
142        self.base = self.base.size(size);
143        self
144    }
145
146    fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
147        self.base = self.base.tooltip(tooltip);
148        self
149    }
150}
151
152impl RenderOnce for Button {
153    #[allow(refining_impl_trait)]
154    fn render(self, _cx: &mut WindowContext) -> ButtonLike {
155        let is_disabled = self.base.disabled;
156        let is_selected = self.base.selected;
157
158        let label = self
159            .selected_label
160            .filter(|_| is_selected)
161            .unwrap_or(self.label);
162
163        let label_color = if is_disabled {
164            Color::Disabled
165        } else if is_selected {
166            Color::Selected
167        } else {
168            self.label_color.unwrap_or_default()
169        };
170
171        self.base.child(
172            h_stack()
173                .gap_1()
174                .when(self.icon_position == Some(IconPosition::Start), |this| {
175                    this.children(self.icon.map(|icon| {
176                        ButtonIcon::new(icon)
177                            .disabled(is_disabled)
178                            .selected(is_selected)
179                            .selected_icon(self.selected_icon)
180                            .size(self.icon_size)
181                            .color(self.icon_color)
182                    }))
183                })
184                .child(
185                    h_stack()
186                        .gap_2()
187                        .justify_between()
188                        .child(
189                            Label::new(label)
190                                .color(label_color)
191                                .size(self.label_size.unwrap_or_default())
192                                .line_height_style(LineHeightStyle::UiLabel),
193                        )
194                        .children(self.key_binding),
195                )
196                .when(self.icon_position != Some(IconPosition::Start), |this| {
197                    this.children(self.icon.map(|icon| {
198                        ButtonIcon::new(icon)
199                            .disabled(is_disabled)
200                            .selected(is_selected)
201                            .selected_icon(self.selected_icon)
202                            .size(self.icon_size)
203                            .color(self.icon_color)
204                    }))
205                }),
206        )
207    }
208}