button.rs

  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    type Output = ButtonLike;
140
141    fn render(self, _cx: &mut WindowContext) -> Self::Output {
142        let is_disabled = self.base.disabled;
143        let is_selected = self.base.selected;
144
145        let label = self
146            .selected_label
147            .filter(|_| is_selected)
148            .unwrap_or(self.label);
149
150        let label_color = if is_disabled {
151            Color::Disabled
152        } else if is_selected {
153            Color::Selected
154        } else {
155            self.label_color.unwrap_or_default()
156        };
157
158        self.base.child(
159            h_stack()
160                .gap_1()
161                .when(self.icon_position.is_some(), |this| {
162                    this.children(self.icon.map(|icon| {
163                        ButtonIcon::new(icon)
164                            .disabled(is_disabled)
165                            .selected(is_selected)
166                            .selected_icon(self.selected_icon)
167                            .size(self.icon_size)
168                            .color(self.icon_color)
169                    }))
170                })
171                .child(
172                    Label::new(label)
173                        .color(label_color)
174                        .size(self.label_size.unwrap_or_default())
175                        .line_height_style(LineHeightStyle::UiLabel),
176                )
177                .when(!self.icon_position.is_some(), |this| {
178                    this.children(self.icon.map(|icon| {
179                        ButtonIcon::new(icon)
180                            .disabled(is_disabled)
181                            .selected(is_selected)
182                            .selected_icon(self.selected_icon)
183                            .size(self.icon_size)
184                            .color(self.icon_color)
185                    }))
186                }),
187        )
188    }
189}