Remove 2 suffix for ui, storybook, text
Co-authored-by: Mikayla <mikayla@zed.dev>
This commit is contained in:
parent
0cf65223ce
commit
4305c5fdbe
142 changed files with 106 additions and 5018 deletions
188
crates/ui/src/components/button/button.rs
Normal file
188
crates/ui/src/components/button/button.rs
Normal file
|
@ -0,0 +1,188 @@
|
|||
use gpui::{AnyView, DefiniteLength};
|
||||
|
||||
use crate::{prelude::*, IconPosition};
|
||||
use crate::{
|
||||
ButtonCommon, ButtonLike, ButtonSize, ButtonStyle, Icon, IconSize, Label, LineHeightStyle,
|
||||
};
|
||||
|
||||
use super::button_icon::ButtonIcon;
|
||||
|
||||
#[derive(IntoElement)]
|
||||
pub struct Button {
|
||||
base: ButtonLike,
|
||||
label: SharedString,
|
||||
label_color: Option<Color>,
|
||||
label_size: Option<LabelSize>,
|
||||
selected_label: Option<SharedString>,
|
||||
icon: Option<Icon>,
|
||||
icon_position: Option<IconPosition>,
|
||||
icon_size: Option<IconSize>,
|
||||
icon_color: Option<Color>,
|
||||
selected_icon: Option<Icon>,
|
||||
}
|
||||
|
||||
impl Button {
|
||||
pub fn new(id: impl Into<ElementId>, label: impl Into<SharedString>) -> Self {
|
||||
Self {
|
||||
base: ButtonLike::new(id),
|
||||
label: label.into(),
|
||||
label_color: None,
|
||||
label_size: None,
|
||||
selected_label: None,
|
||||
icon: None,
|
||||
icon_position: None,
|
||||
icon_size: None,
|
||||
icon_color: None,
|
||||
selected_icon: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn color(mut self, label_color: impl Into<Option<Color>>) -> Self {
|
||||
self.label_color = label_color.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn label_size(mut self, label_size: impl Into<Option<LabelSize>>) -> Self {
|
||||
self.label_size = label_size.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn selected_label<L: Into<SharedString>>(mut self, label: impl Into<Option<L>>) -> Self {
|
||||
self.selected_label = label.into().map(Into::into);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn icon(mut self, icon: impl Into<Option<Icon>>) -> Self {
|
||||
self.icon = icon.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn icon_position(mut self, icon_position: impl Into<Option<IconPosition>>) -> Self {
|
||||
self.icon_position = icon_position.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn icon_size(mut self, icon_size: impl Into<Option<IconSize>>) -> Self {
|
||||
self.icon_size = icon_size.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn icon_color(mut self, icon_color: impl Into<Option<Color>>) -> Self {
|
||||
self.icon_color = icon_color.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn selected_icon(mut self, icon: impl Into<Option<Icon>>) -> Self {
|
||||
self.selected_icon = icon.into();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Selectable for Button {
|
||||
fn selected(mut self, selected: bool) -> Self {
|
||||
self.base = self.base.selected(selected);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Disableable for Button {
|
||||
fn disabled(mut self, disabled: bool) -> Self {
|
||||
self.base = self.base.disabled(disabled);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Clickable for Button {
|
||||
fn on_click(
|
||||
mut self,
|
||||
handler: impl Fn(&gpui::ClickEvent, &mut WindowContext) + 'static,
|
||||
) -> Self {
|
||||
self.base = self.base.on_click(handler);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl FixedWidth for Button {
|
||||
fn width(mut self, width: DefiniteLength) -> Self {
|
||||
self.base = self.base.width(width);
|
||||
self
|
||||
}
|
||||
|
||||
fn full_width(mut self) -> Self {
|
||||
self.base = self.base.full_width();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ButtonCommon for Button {
|
||||
fn id(&self) -> &ElementId {
|
||||
self.base.id()
|
||||
}
|
||||
|
||||
fn style(mut self, style: ButtonStyle) -> Self {
|
||||
self.base = self.base.style(style);
|
||||
self
|
||||
}
|
||||
|
||||
fn size(mut self, size: ButtonSize) -> Self {
|
||||
self.base = self.base.size(size);
|
||||
self
|
||||
}
|
||||
|
||||
fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
|
||||
self.base = self.base.tooltip(tooltip);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for Button {
|
||||
#[allow(refining_impl_trait)]
|
||||
fn render(self, _cx: &mut WindowContext) -> ButtonLike {
|
||||
let is_disabled = self.base.disabled;
|
||||
let is_selected = self.base.selected;
|
||||
|
||||
let label = self
|
||||
.selected_label
|
||||
.filter(|_| is_selected)
|
||||
.unwrap_or(self.label);
|
||||
|
||||
let label_color = if is_disabled {
|
||||
Color::Disabled
|
||||
} else if is_selected {
|
||||
Color::Selected
|
||||
} else {
|
||||
self.label_color.unwrap_or_default()
|
||||
};
|
||||
|
||||
self.base.child(
|
||||
h_stack()
|
||||
.gap_1()
|
||||
.when(self.icon_position.is_some(), |this| {
|
||||
this.children(self.icon.map(|icon| {
|
||||
ButtonIcon::new(icon)
|
||||
.disabled(is_disabled)
|
||||
.selected(is_selected)
|
||||
.selected_icon(self.selected_icon)
|
||||
.size(self.icon_size)
|
||||
.color(self.icon_color)
|
||||
}))
|
||||
})
|
||||
.child(
|
||||
Label::new(label)
|
||||
.color(label_color)
|
||||
.size(self.label_size.unwrap_or_default())
|
||||
.line_height_style(LineHeightStyle::UiLabel),
|
||||
)
|
||||
.when(!self.icon_position.is_some(), |this| {
|
||||
this.children(self.icon.map(|icon| {
|
||||
ButtonIcon::new(icon)
|
||||
.disabled(is_disabled)
|
||||
.selected(is_selected)
|
||||
.selected_icon(self.selected_icon)
|
||||
.size(self.icon_size)
|
||||
.color(self.icon_color)
|
||||
}))
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
82
crates/ui/src/components/button/button_icon.rs
Normal file
82
crates/ui/src/components/button/button_icon.rs
Normal file
|
@ -0,0 +1,82 @@
|
|||
use crate::{prelude::*, Icon, IconElement, IconSize};
|
||||
|
||||
/// An icon that appears within a button.
|
||||
///
|
||||
/// Can be used as either an icon alongside a label, like in [`Button`](crate::Button),
|
||||
/// or as a standalone icon, like in [`IconButton`](crate::IconButton).
|
||||
#[derive(IntoElement)]
|
||||
pub(super) struct ButtonIcon {
|
||||
icon: Icon,
|
||||
size: IconSize,
|
||||
color: Color,
|
||||
disabled: bool,
|
||||
selected: bool,
|
||||
selected_icon: Option<Icon>,
|
||||
}
|
||||
|
||||
impl ButtonIcon {
|
||||
pub fn new(icon: Icon) -> Self {
|
||||
Self {
|
||||
icon,
|
||||
size: IconSize::default(),
|
||||
color: Color::default(),
|
||||
disabled: false,
|
||||
selected: false,
|
||||
selected_icon: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size(mut self, size: impl Into<Option<IconSize>>) -> Self {
|
||||
if let Some(size) = size.into() {
|
||||
self.size = size;
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn color(mut self, color: impl Into<Option<Color>>) -> Self {
|
||||
if let Some(color) = color.into() {
|
||||
self.color = color;
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn selected_icon(mut self, icon: impl Into<Option<Icon>>) -> Self {
|
||||
self.selected_icon = icon.into();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Disableable for ButtonIcon {
|
||||
fn disabled(mut self, disabled: bool) -> Self {
|
||||
self.disabled = disabled;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Selectable for ButtonIcon {
|
||||
fn selected(mut self, selected: bool) -> Self {
|
||||
self.selected = selected;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for ButtonIcon {
|
||||
fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
|
||||
let icon = self
|
||||
.selected_icon
|
||||
.filter(|_| self.selected)
|
||||
.unwrap_or(self.icon);
|
||||
|
||||
let icon_color = if self.disabled {
|
||||
Color::Disabled
|
||||
} else if self.selected {
|
||||
Color::Selected
|
||||
} else {
|
||||
self.color
|
||||
};
|
||||
|
||||
IconElement::new(icon).size(self.size).color(icon_color)
|
||||
}
|
||||
}
|
407
crates/ui/src/components/button/button_like.rs
Normal file
407
crates/ui/src/components/button/button_like.rs
Normal file
|
@ -0,0 +1,407 @@
|
|||
use gpui::{relative, DefiniteLength, MouseButton};
|
||||
use gpui::{rems, transparent_black, AnyElement, AnyView, ClickEvent, Hsla, Rems};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
pub trait ButtonCommon: Clickable + Disableable {
|
||||
/// A unique element ID to identify the button.
|
||||
fn id(&self) -> &ElementId;
|
||||
|
||||
/// The visual style of the button.
|
||||
///
|
||||
/// Most commonly will be [`ButtonStyle::Subtle`], or [`ButtonStyle::Filled`]
|
||||
/// for an emphasized button.
|
||||
fn style(self, style: ButtonStyle) -> Self;
|
||||
|
||||
/// The size of the button.
|
||||
///
|
||||
/// Most buttons will use the default size.
|
||||
///
|
||||
/// [`ButtonSize`] can also be used to help build non-button elements
|
||||
/// that are consistently sized with buttons.
|
||||
fn size(self, size: ButtonSize) -> Self;
|
||||
|
||||
/// The tooltip that shows when a user hovers over the button.
|
||||
///
|
||||
/// Nearly all interactable elements should have a tooltip. Some example
|
||||
/// exceptions might a scroll bar, or a slider.
|
||||
fn tooltip(self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self;
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)]
|
||||
pub enum IconPosition {
|
||||
#[default]
|
||||
Start,
|
||||
End,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)]
|
||||
pub enum ButtonStyle {
|
||||
/// A filled button with a solid background color. Provides emphasis versus
|
||||
/// the more common subtle button.
|
||||
Filled,
|
||||
|
||||
/// 🚧 Under construction 🚧
|
||||
///
|
||||
/// Used to emphasize a button in some way, like a selected state, or a semantic
|
||||
/// coloring like an error or success button.
|
||||
Tinted,
|
||||
|
||||
/// The default button style, used for most buttons. Has a transparent background,
|
||||
/// but has a background color to indicate states like hover and active.
|
||||
#[default]
|
||||
Subtle,
|
||||
|
||||
/// Used for buttons that only change forground color on hover and active states.
|
||||
///
|
||||
/// TODO: Better docs for this.
|
||||
Transparent,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
|
||||
pub(crate) enum ButtonLikeRounding {
|
||||
All,
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct ButtonLikeStyles {
|
||||
pub background: Hsla,
|
||||
#[allow(unused)]
|
||||
pub border_color: Hsla,
|
||||
#[allow(unused)]
|
||||
pub label_color: Hsla,
|
||||
#[allow(unused)]
|
||||
pub icon_color: Hsla,
|
||||
}
|
||||
|
||||
impl ButtonStyle {
|
||||
pub(crate) fn enabled(self, cx: &mut WindowContext) -> ButtonLikeStyles {
|
||||
match self {
|
||||
ButtonStyle::Filled => ButtonLikeStyles {
|
||||
background: cx.theme().colors().element_background,
|
||||
border_color: transparent_black(),
|
||||
label_color: Color::Default.color(cx),
|
||||
icon_color: Color::Default.color(cx),
|
||||
},
|
||||
ButtonStyle::Tinted => ButtonLikeStyles {
|
||||
background: gpui::red(),
|
||||
border_color: gpui::red(),
|
||||
label_color: gpui::red(),
|
||||
icon_color: gpui::red(),
|
||||
},
|
||||
ButtonStyle::Subtle => ButtonLikeStyles {
|
||||
background: cx.theme().colors().ghost_element_background,
|
||||
border_color: transparent_black(),
|
||||
label_color: Color::Default.color(cx),
|
||||
icon_color: Color::Default.color(cx),
|
||||
},
|
||||
ButtonStyle::Transparent => ButtonLikeStyles {
|
||||
background: transparent_black(),
|
||||
border_color: transparent_black(),
|
||||
label_color: Color::Default.color(cx),
|
||||
icon_color: Color::Default.color(cx),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn hovered(self, cx: &mut WindowContext) -> ButtonLikeStyles {
|
||||
match self {
|
||||
ButtonStyle::Filled => ButtonLikeStyles {
|
||||
background: cx.theme().colors().element_hover,
|
||||
border_color: transparent_black(),
|
||||
label_color: Color::Default.color(cx),
|
||||
icon_color: Color::Default.color(cx),
|
||||
},
|
||||
ButtonStyle::Tinted => ButtonLikeStyles {
|
||||
background: gpui::red(),
|
||||
border_color: gpui::red(),
|
||||
label_color: gpui::red(),
|
||||
icon_color: gpui::red(),
|
||||
},
|
||||
ButtonStyle::Subtle => ButtonLikeStyles {
|
||||
background: cx.theme().colors().ghost_element_hover,
|
||||
border_color: transparent_black(),
|
||||
label_color: Color::Default.color(cx),
|
||||
icon_color: Color::Default.color(cx),
|
||||
},
|
||||
ButtonStyle::Transparent => ButtonLikeStyles {
|
||||
background: transparent_black(),
|
||||
border_color: transparent_black(),
|
||||
// TODO: These are not great
|
||||
label_color: Color::Muted.color(cx),
|
||||
// TODO: These are not great
|
||||
icon_color: Color::Muted.color(cx),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn active(self, cx: &mut WindowContext) -> ButtonLikeStyles {
|
||||
match self {
|
||||
ButtonStyle::Filled => ButtonLikeStyles {
|
||||
background: cx.theme().colors().element_active,
|
||||
border_color: transparent_black(),
|
||||
label_color: Color::Default.color(cx),
|
||||
icon_color: Color::Default.color(cx),
|
||||
},
|
||||
ButtonStyle::Tinted => ButtonLikeStyles {
|
||||
background: gpui::red(),
|
||||
border_color: gpui::red(),
|
||||
label_color: gpui::red(),
|
||||
icon_color: gpui::red(),
|
||||
},
|
||||
ButtonStyle::Subtle => ButtonLikeStyles {
|
||||
background: cx.theme().colors().ghost_element_active,
|
||||
border_color: transparent_black(),
|
||||
label_color: Color::Default.color(cx),
|
||||
icon_color: Color::Default.color(cx),
|
||||
},
|
||||
ButtonStyle::Transparent => ButtonLikeStyles {
|
||||
background: transparent_black(),
|
||||
border_color: transparent_black(),
|
||||
// TODO: These are not great
|
||||
label_color: Color::Muted.color(cx),
|
||||
// TODO: These are not great
|
||||
icon_color: Color::Muted.color(cx),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) fn focused(self, cx: &mut WindowContext) -> ButtonLikeStyles {
|
||||
match self {
|
||||
ButtonStyle::Filled => ButtonLikeStyles {
|
||||
background: cx.theme().colors().element_background,
|
||||
border_color: cx.theme().colors().border_focused,
|
||||
label_color: Color::Default.color(cx),
|
||||
icon_color: Color::Default.color(cx),
|
||||
},
|
||||
ButtonStyle::Tinted => ButtonLikeStyles {
|
||||
background: gpui::red(),
|
||||
border_color: gpui::red(),
|
||||
label_color: gpui::red(),
|
||||
icon_color: gpui::red(),
|
||||
},
|
||||
ButtonStyle::Subtle => ButtonLikeStyles {
|
||||
background: cx.theme().colors().ghost_element_background,
|
||||
border_color: cx.theme().colors().border_focused,
|
||||
label_color: Color::Default.color(cx),
|
||||
icon_color: Color::Default.color(cx),
|
||||
},
|
||||
ButtonStyle::Transparent => ButtonLikeStyles {
|
||||
background: transparent_black(),
|
||||
border_color: cx.theme().colors().border_focused,
|
||||
label_color: Color::Accent.color(cx),
|
||||
icon_color: Color::Accent.color(cx),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) fn disabled(self, cx: &mut WindowContext) -> ButtonLikeStyles {
|
||||
match self {
|
||||
ButtonStyle::Filled => ButtonLikeStyles {
|
||||
background: cx.theme().colors().element_disabled,
|
||||
border_color: cx.theme().colors().border_disabled,
|
||||
label_color: Color::Disabled.color(cx),
|
||||
icon_color: Color::Disabled.color(cx),
|
||||
},
|
||||
ButtonStyle::Tinted => ButtonLikeStyles {
|
||||
background: gpui::red(),
|
||||
border_color: gpui::red(),
|
||||
label_color: gpui::red(),
|
||||
icon_color: gpui::red(),
|
||||
},
|
||||
ButtonStyle::Subtle => ButtonLikeStyles {
|
||||
background: cx.theme().colors().ghost_element_disabled,
|
||||
border_color: cx.theme().colors().border_disabled,
|
||||
label_color: Color::Disabled.color(cx),
|
||||
icon_color: Color::Disabled.color(cx),
|
||||
},
|
||||
ButtonStyle::Transparent => ButtonLikeStyles {
|
||||
background: transparent_black(),
|
||||
border_color: transparent_black(),
|
||||
label_color: Color::Disabled.color(cx),
|
||||
icon_color: Color::Disabled.color(cx),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// ButtonSize can also be used to help build non-button elements
|
||||
/// that are consistently sized with buttons.
|
||||
#[derive(Default, PartialEq, Clone, Copy)]
|
||||
pub enum ButtonSize {
|
||||
Large,
|
||||
#[default]
|
||||
Default,
|
||||
Compact,
|
||||
None,
|
||||
}
|
||||
|
||||
impl ButtonSize {
|
||||
fn height(self) -> Rems {
|
||||
match self {
|
||||
ButtonSize::Large => rems(32. / 16.),
|
||||
ButtonSize::Default => rems(22. / 16.),
|
||||
ButtonSize::Compact => rems(18. / 16.),
|
||||
ButtonSize::None => rems(16. / 16.),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A button-like element that can be used to create a custom button when
|
||||
/// prebuilt buttons are not sufficient. Use this sparingly, as it is
|
||||
/// unconstrained and may make the UI feel less consistent.
|
||||
///
|
||||
/// This is also used to build the prebuilt buttons.
|
||||
#[derive(IntoElement)]
|
||||
pub struct ButtonLike {
|
||||
base: Div,
|
||||
id: ElementId,
|
||||
pub(super) style: ButtonStyle,
|
||||
pub(super) disabled: bool,
|
||||
pub(super) selected: bool,
|
||||
pub(super) width: Option<DefiniteLength>,
|
||||
size: ButtonSize,
|
||||
rounding: Option<ButtonLikeRounding>,
|
||||
tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView>>,
|
||||
on_click: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
|
||||
children: SmallVec<[AnyElement; 2]>,
|
||||
}
|
||||
|
||||
impl ButtonLike {
|
||||
pub fn new(id: impl Into<ElementId>) -> Self {
|
||||
Self {
|
||||
base: div(),
|
||||
id: id.into(),
|
||||
style: ButtonStyle::default(),
|
||||
disabled: false,
|
||||
selected: false,
|
||||
width: None,
|
||||
size: ButtonSize::Default,
|
||||
rounding: Some(ButtonLikeRounding::All),
|
||||
tooltip: None,
|
||||
children: SmallVec::new(),
|
||||
on_click: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn rounding(mut self, rounding: impl Into<Option<ButtonLikeRounding>>) -> Self {
|
||||
self.rounding = rounding.into();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Disableable for ButtonLike {
|
||||
fn disabled(mut self, disabled: bool) -> Self {
|
||||
self.disabled = disabled;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Selectable for ButtonLike {
|
||||
fn selected(mut self, selected: bool) -> Self {
|
||||
self.selected = selected;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Clickable for ButtonLike {
|
||||
fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
|
||||
self.on_click = Some(Box::new(handler));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl FixedWidth for ButtonLike {
|
||||
fn width(mut self, width: DefiniteLength) -> Self {
|
||||
self.width = Some(width);
|
||||
self
|
||||
}
|
||||
|
||||
fn full_width(mut self) -> Self {
|
||||
self.width = Some(relative(1.));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ButtonCommon for ButtonLike {
|
||||
fn id(&self) -> &ElementId {
|
||||
&self.id
|
||||
}
|
||||
|
||||
fn style(mut self, style: ButtonStyle) -> Self {
|
||||
self.style = style;
|
||||
self
|
||||
}
|
||||
|
||||
fn size(mut self, size: ButtonSize) -> Self {
|
||||
self.size = size;
|
||||
self
|
||||
}
|
||||
|
||||
fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
|
||||
self.tooltip = Some(Box::new(tooltip));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl VisibleOnHover for ButtonLike {
|
||||
fn visible_on_hover(mut self, group_name: impl Into<SharedString>) -> Self {
|
||||
self.base = self.base.visible_on_hover(group_name);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ParentElement for ButtonLike {
|
||||
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
|
||||
&mut self.children
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for ButtonLike {
|
||||
fn render(self, cx: &mut WindowContext) -> impl IntoElement {
|
||||
self.base
|
||||
.h_flex()
|
||||
.id(self.id.clone())
|
||||
.group("")
|
||||
.flex_none()
|
||||
.h(self.size.height())
|
||||
.when_some(self.width, |this, width| this.w(width).justify_center())
|
||||
.when_some(self.rounding, |this, rounding| match rounding {
|
||||
ButtonLikeRounding::All => this.rounded_md(),
|
||||
ButtonLikeRounding::Left => this.rounded_l_md(),
|
||||
ButtonLikeRounding::Right => this.rounded_r_md(),
|
||||
})
|
||||
.gap_1()
|
||||
.map(|this| match self.size {
|
||||
ButtonSize::Large => this.px_2(),
|
||||
ButtonSize::Default | ButtonSize::Compact => this.px_1(),
|
||||
ButtonSize::None => this,
|
||||
})
|
||||
.bg(self.style.enabled(cx).background)
|
||||
.when(self.disabled, |this| this.cursor_not_allowed())
|
||||
.when(!self.disabled, |this| {
|
||||
this.cursor_pointer()
|
||||
.hover(|hover| hover.bg(self.style.hovered(cx).background))
|
||||
.active(|active| active.bg(self.style.active(cx).background))
|
||||
})
|
||||
.when_some(
|
||||
self.on_click.filter(|_| !self.disabled),
|
||||
|this, on_click| {
|
||||
this.on_mouse_down(MouseButton::Left, |_, cx| cx.prevent_default())
|
||||
.on_click(move |event, cx| {
|
||||
cx.stop_propagation();
|
||||
(on_click)(event, cx)
|
||||
})
|
||||
},
|
||||
)
|
||||
.when_some(self.tooltip, |this, tooltip| {
|
||||
this.tooltip(move |cx| tooltip(cx))
|
||||
})
|
||||
.children(self.children)
|
||||
}
|
||||
}
|
122
crates/ui/src/components/button/icon_button.rs
Normal file
122
crates/ui/src/components/button/icon_button.rs
Normal file
|
@ -0,0 +1,122 @@
|
|||
use gpui::{AnyView, DefiniteLength};
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::{ButtonCommon, ButtonLike, ButtonSize, ButtonStyle, Icon, IconSize};
|
||||
|
||||
use super::button_icon::ButtonIcon;
|
||||
|
||||
#[derive(IntoElement)]
|
||||
pub struct IconButton {
|
||||
base: ButtonLike,
|
||||
icon: Icon,
|
||||
icon_size: IconSize,
|
||||
icon_color: Color,
|
||||
selected_icon: Option<Icon>,
|
||||
}
|
||||
|
||||
impl IconButton {
|
||||
pub fn new(id: impl Into<ElementId>, icon: Icon) -> Self {
|
||||
Self {
|
||||
base: ButtonLike::new(id),
|
||||
icon,
|
||||
icon_size: IconSize::default(),
|
||||
icon_color: Color::Default,
|
||||
selected_icon: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn icon_size(mut self, icon_size: IconSize) -> Self {
|
||||
self.icon_size = icon_size;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn icon_color(mut self, icon_color: Color) -> Self {
|
||||
self.icon_color = icon_color;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn selected_icon(mut self, icon: impl Into<Option<Icon>>) -> Self {
|
||||
self.selected_icon = icon.into();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Disableable for IconButton {
|
||||
fn disabled(mut self, disabled: bool) -> Self {
|
||||
self.base = self.base.disabled(disabled);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Selectable for IconButton {
|
||||
fn selected(mut self, selected: bool) -> Self {
|
||||
self.base = self.base.selected(selected);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Clickable for IconButton {
|
||||
fn on_click(
|
||||
mut self,
|
||||
handler: impl Fn(&gpui::ClickEvent, &mut WindowContext) + 'static,
|
||||
) -> Self {
|
||||
self.base = self.base.on_click(handler);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl FixedWidth for IconButton {
|
||||
fn width(mut self, width: DefiniteLength) -> Self {
|
||||
self.base = self.base.width(width);
|
||||
self
|
||||
}
|
||||
|
||||
fn full_width(mut self) -> Self {
|
||||
self.base = self.base.full_width();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ButtonCommon for IconButton {
|
||||
fn id(&self) -> &ElementId {
|
||||
self.base.id()
|
||||
}
|
||||
|
||||
fn style(mut self, style: ButtonStyle) -> Self {
|
||||
self.base = self.base.style(style);
|
||||
self
|
||||
}
|
||||
|
||||
fn size(mut self, size: ButtonSize) -> Self {
|
||||
self.base = self.base.size(size);
|
||||
self
|
||||
}
|
||||
|
||||
fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
|
||||
self.base = self.base.tooltip(tooltip);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl VisibleOnHover for IconButton {
|
||||
fn visible_on_hover(mut self, group_name: impl Into<SharedString>) -> Self {
|
||||
self.base = self.base.visible_on_hover(group_name);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for IconButton {
|
||||
fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
|
||||
let is_disabled = self.base.disabled;
|
||||
let is_selected = self.base.selected;
|
||||
|
||||
self.base.child(
|
||||
ButtonIcon::new(self.icon)
|
||||
.disabled(is_disabled)
|
||||
.selected(is_selected)
|
||||
.selected_icon(self.selected_icon)
|
||||
.size(self.icon_size)
|
||||
.color(self.icon_color),
|
||||
)
|
||||
}
|
||||
}
|
126
crates/ui/src/components/button/toggle_button.rs
Normal file
126
crates/ui/src/components/button/toggle_button.rs
Normal file
|
@ -0,0 +1,126 @@
|
|||
use gpui::{AnyView, ClickEvent};
|
||||
|
||||
use crate::{prelude::*, ButtonLike, ButtonLikeRounding};
|
||||
|
||||
/// The position of a [`ToggleButton`] within a group of buttons.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum ToggleButtonPosition {
|
||||
/// The toggle button is first in the group.
|
||||
First,
|
||||
|
||||
/// The toggle button is in the middle of the group (i.e., it is not the first or last toggle button).
|
||||
Middle,
|
||||
|
||||
/// The toggle button is last in the group.
|
||||
Last,
|
||||
}
|
||||
|
||||
#[derive(IntoElement)]
|
||||
pub struct ToggleButton {
|
||||
base: ButtonLike,
|
||||
position_in_group: Option<ToggleButtonPosition>,
|
||||
label: SharedString,
|
||||
label_color: Option<Color>,
|
||||
}
|
||||
|
||||
impl ToggleButton {
|
||||
pub fn new(id: impl Into<ElementId>, label: impl Into<SharedString>) -> Self {
|
||||
Self {
|
||||
base: ButtonLike::new(id),
|
||||
position_in_group: None,
|
||||
label: label.into(),
|
||||
label_color: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn color(mut self, label_color: impl Into<Option<Color>>) -> Self {
|
||||
self.label_color = label_color.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn position_in_group(mut self, position: ToggleButtonPosition) -> Self {
|
||||
self.position_in_group = Some(position);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn first(self) -> Self {
|
||||
self.position_in_group(ToggleButtonPosition::First)
|
||||
}
|
||||
|
||||
pub fn middle(self) -> Self {
|
||||
self.position_in_group(ToggleButtonPosition::Middle)
|
||||
}
|
||||
|
||||
pub fn last(self) -> Self {
|
||||
self.position_in_group(ToggleButtonPosition::Last)
|
||||
}
|
||||
}
|
||||
|
||||
impl Selectable for ToggleButton {
|
||||
fn selected(mut self, selected: bool) -> Self {
|
||||
self.base = self.base.selected(selected);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Disableable for ToggleButton {
|
||||
fn disabled(mut self, disabled: bool) -> Self {
|
||||
self.base = self.base.disabled(disabled);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Clickable for ToggleButton {
|
||||
fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
|
||||
self.base = self.base.on_click(handler);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ButtonCommon for ToggleButton {
|
||||
fn id(&self) -> &ElementId {
|
||||
self.base.id()
|
||||
}
|
||||
|
||||
fn style(mut self, style: ButtonStyle) -> Self {
|
||||
self.base = self.base.style(style);
|
||||
self
|
||||
}
|
||||
|
||||
fn size(mut self, size: ButtonSize) -> Self {
|
||||
self.base = self.base.size(size);
|
||||
self
|
||||
}
|
||||
|
||||
fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
|
||||
self.base = self.base.tooltip(tooltip);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl RenderOnce for ToggleButton {
|
||||
fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
|
||||
let is_disabled = self.base.disabled;
|
||||
let is_selected = self.base.selected;
|
||||
|
||||
let label_color = if is_disabled {
|
||||
Color::Disabled
|
||||
} else if is_selected {
|
||||
Color::Selected
|
||||
} else {
|
||||
self.label_color.unwrap_or_default()
|
||||
};
|
||||
|
||||
self.base
|
||||
.when_some(self.position_in_group, |this, position| match position {
|
||||
ToggleButtonPosition::First => this.rounding(ButtonLikeRounding::Left),
|
||||
ToggleButtonPosition::Middle => this.rounding(None),
|
||||
ToggleButtonPosition::Last => this.rounding(ButtonLikeRounding::Right),
|
||||
})
|
||||
.child(
|
||||
Label::new(self.label)
|
||||
.color(label_color)
|
||||
.line_height_style(LineHeightStyle::UiLabel),
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue