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