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 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}