Add Icon Button support. (#11)

When the Button have no label present, it will use the Icon mode.

<img width="891" alt="image"
src="https://github.com/huacnlee/gpui-component/assets/5518/ca6240d5-02c8-4201-9a79-4afdf9938dd8">
This commit is contained in:
Jason Lee 2024-07-01 21:32:03 +08:00 committed by GitHub
parent f9e075113e
commit cbbd20867f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 358 additions and 147 deletions

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-x"><circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/></svg>

After

Width:  |  Height:  |  Size: 291 B

1
assets/icons/delete.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-delete"><path d="M20 5H9l-7 7 7 7h11a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2Z"/><line x1="18" x2="12" y1="9" y2="15"/><line x1="12" x2="18" y1="9" y2="15"/></svg>delete

After

Width:  |  Height:  |  Size: 360 B

1
assets/icons/search.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-search"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg>

After

Width:  |  Height:  |  Size: 273 B

View file

@ -218,7 +218,7 @@ impl Render for StoryWorkspace {
.items_center() .items_center()
.justify_end() .justify_end()
.px_2() .px_2()
.on_mouse_move(|_, cx| cx.stop_propagation()) .mr_3()
.child( .child(
Switch::new("theme-mode") Switch::new("theme-mode")
.size(ButtonSize::Small) .size(ButtonSize::Small)

View file

@ -1,6 +1,6 @@
use gpui::{ use gpui::{
px, ClickEvent, IntoElement, ParentElement as _, Render, Styled as _, View, ViewContext, div, ClickEvent, Div, IntoElement, ParentElement as _, Render, SharedString, Styled as _, View,
VisualContext as _, WindowContext, ViewContext, VisualContext as _, WindowContext,
}; };
use ui::{ use ui::{
@ -21,117 +21,224 @@ impl ButtonStory {
} }
impl Render for ButtonStory { impl Render for ButtonStory {
fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement { fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
fn section(title: impl Into<SharedString>, cx: &ViewContext<ButtonStory>) -> Div {
use ui::theme::ActiveTheme;
let theme = cx.theme();
h_flex()
.items_center()
.gap_4()
.p_4()
.w_full()
.rounded_lg()
.border_1()
.border_color(theme.border)
.flex_wrap()
.justify_around()
.child(div().flex_none().w_full().child(title.into()))
}
v_flex() v_flex()
.w_full() .w_full()
.justify_start() .justify_start()
.gap_6() .gap_6()
.child( .child(
v_flex() h_flex()
.w(px(360.))
.gap_6() .gap_6()
.child( .child(
Button::new("button-1", "Primary Button") section("Normal Button", cx)
.style(ButtonStyle::Primary) .child(
.on_click(Self::on_click), Button::new("button-1")
.label("Primary Button")
.style(ButtonStyle::Primary)
.on_click(Self::on_click),
)
.child(
Button::new("button-2")
.label("Secondary Button")
.style(ButtonStyle::Secondary)
.on_click(Self::on_click),
)
.child(
Button::new("button-4")
.label("Danger Button")
.style(ButtonStyle::Danger)
.on_click(Self::on_click),
),
) )
.child( .child(
Button::new("button-2", "Secondary Button") section("Button with Icon", cx)
.style(ButtonStyle::Secondary) .child(
.on_click(Self::on_click), Button::new("button-icon-1")
) .label("Confirm")
.child( .icon(IconName::Check)
Button::new("button-4", "Danger Button") .style(ButtonStyle::Primary)
.style(ButtonStyle::Danger) .on_click(Self::on_click),
.on_click(Self::on_click), )
.child(
Button::new("button-icon-2")
.label("Abort")
.icon(IconName::Close)
.style(ButtonStyle::Secondary)
.on_click(Self::on_click),
)
.child(
Button::new("button-icon-3")
.label("Maximize")
.icon(Icon::new(IconName::Maximize))
.style(ButtonStyle::Secondary)
.on_click(Self::on_click),
),
), ),
) )
.child( .child(
h_flex() h_flex()
.gap_6() .gap_6()
.child( .child(
Button::new("button-icon-1", "Confirm") section("Small Size", cx)
.icon(IconName::Check) .child(
.style(ButtonStyle::Primary) Button::new("button-6")
.on_click(Self::on_click), .label("Primary Button")
.style(ButtonStyle::Primary)
.size(ButtonSize::Small)
.on_click(Self::on_click),
)
.child(
Button::new("button-7")
.label("Secondary Button")
.style(ButtonStyle::Secondary)
.size(ButtonSize::Small)
.on_click(Self::on_click),
)
.child(
Button::new("button-8")
.label("Danger Button")
.style(ButtonStyle::Danger)
.size(ButtonSize::Small)
.on_click(Self::on_click),
),
) )
.child( .child(
Button::new("button-icon-2", "Abort") section("XSmall Size", cx)
.child(
Button::new("button-xs-1")
.label("Primary Button")
.style(ButtonStyle::Primary)
.size(ButtonSize::XSmall)
.on_click(Self::on_click),
)
.child(
Button::new("button-xs-2")
.label("Secondary Button")
.style(ButtonStyle::Secondary)
.size(ButtonSize::XSmall)
.on_click(Self::on_click),
)
.child(
Button::new("button-xs-3")
.label("Danger Button")
.style(ButtonStyle::Danger)
.size(ButtonSize::XSmall)
.on_click(Self::on_click),
),
),
)
.child(
h_flex()
.gap_6()
.child(
section("Disabled Button", cx)
.child(
Button::new("button-disabled1")
.label("Disabled Button")
.style(ButtonStyle::Primary)
.on_click(Self::on_click)
.disabled(true),
)
.child(
Button::new("button-disabled1")
.label("Disabled Button")
.style(ButtonStyle::Secondary)
.on_click(Self::on_click)
.disabled(true),
)
.child(
Button::new("button-disabled1")
.label("Disabled Button")
.style(ButtonStyle::Danger)
.on_click(Self::on_click)
.disabled(true),
),
)
.child(
section("Selected Style", cx)
.child(
Button::new("button-selected-1")
.label("Selected Button")
.style(ButtonStyle::Primary)
.selected(true),
)
.child(
Button::new("button-selected-2")
.label("Selected Button")
.style(ButtonStyle::Secondary)
.selected(true),
)
.child(
Button::new("button-selected-3")
.label("Selected Button")
.style(ButtonStyle::Danger)
.selected(true),
),
),
)
.child(
section("Icon Button", cx)
.child(
Button::new("icon-button-0")
.icon(IconName::Search)
.style(ButtonStyle::Primary),
)
.child(Button::new("icon-button-1").icon(IconName::Info))
.child(
Button::new("icon-button-2")
.icon(IconName::Close) .icon(IconName::Close)
.style(ButtonStyle::Secondary) .style(ButtonStyle::Danger),
.on_click(Self::on_click),
) )
.child( .child(
Button::new("button-icon-3", "Maximize") Button::new("icon-button-3")
.icon(Icon::new(IconName::Maximize)) .icon(IconName::Search)
.style(ButtonStyle::Secondary)
.on_click(Self::on_click),
),
)
.child(
h_flex()
.items_center()
.gap_6()
.child(
Button::new("button-disabled1", "Disabled Button")
.style(ButtonStyle::Primary)
.on_click(Self::on_click)
.disabled(true),
)
.child(
Button::new("button-disabled1", "Disabled Button")
.style(ButtonStyle::Secondary)
.on_click(Self::on_click)
.disabled(true),
)
.child(
Button::new("button-disabled1", "Disabled Button")
.style(ButtonStyle::Danger)
.on_click(Self::on_click)
.disabled(true),
),
)
.child(
h_flex()
.items_center()
.gap_6()
.child(
Button::new("button-6", "Primary Button")
.style(ButtonStyle::Primary)
.size(ButtonSize::Small) .size(ButtonSize::Small)
.on_click(Self::on_click), .style(ButtonStyle::Primary),
) )
.child( .child(
Button::new("button-7", "Secondary Button") Button::new("icon-button-4")
.style(ButtonStyle::Secondary) .icon(IconName::Info)
.size(ButtonSize::Small),
)
.child(
Button::new("icon-button-5")
.icon(IconName::Close)
.size(ButtonSize::Small) .size(ButtonSize::Small)
.on_click(Self::on_click), .style(ButtonStyle::Danger),
) )
.child( .child(
Button::new("button-8", "Danger Button") Button::new("icon-button-6")
.style(ButtonStyle::Danger) .icon(IconName::Search)
.size(ButtonSize::Small) .size(ButtonSize::XSmall)
.on_click(Self::on_click), .style(ButtonStyle::Primary),
),
)
.child(
h_flex()
.items_center()
.gap_6()
.child(
Button::new("button-6", "Selected Button")
.style(ButtonStyle::Primary)
.selected(true),
) )
.child( .child(
Button::new("button-7", "Selected Button") Button::new("icon-button-7")
.style(ButtonStyle::Secondary) .icon(IconName::Info)
.selected(true), .size(ButtonSize::XSmall),
) )
.child( .child(
Button::new("button-8", "Selected Button") Button::new("icon-button-8")
.style(ButtonStyle::Danger) .icon(IconName::Close)
.selected(true), .size(ButtonSize::XSmall)
.style(ButtonStyle::Danger),
), ),
) )
} }

View file

@ -39,13 +39,15 @@ impl Render for CheckboxStory {
h_flex() h_flex()
.items_center() .items_center()
.gap_6() .gap_6()
.child(Checkbox::new("check1", cx).checked(self.check1).on_click(
cx.listener(|v, _, _| {
v.check1 = v.check1.inverse();
}),
))
.child( .child(
Checkbox::new("check2", cx) Checkbox::new("check1")
.checked(self.check1)
.on_click(cx.listener(|v, _, _| {
v.check1 = v.check1.inverse();
})),
)
.child(
Checkbox::new("check2")
.checked(self.check2) .checked(self.check2)
.label("Subscribe to newsletter") .label("Subscribe to newsletter")
.on_click(cx.listener(|v, _, _| { .on_click(cx.listener(|v, _, _| {
@ -53,7 +55,7 @@ impl Render for CheckboxStory {
})), })),
) )
.child( .child(
Checkbox::new("check3", cx) Checkbox::new("check3")
.checked(self.check3) .checked(self.check3)
.label("Remember me") .label("Remember me")
.on_click(cx.listener(|v, _, _| { .on_click(cx.listener(|v, _, _| {
@ -68,19 +70,19 @@ impl Render for CheckboxStory {
.items_center() .items_center()
.gap_6() .gap_6()
.child( .child(
Checkbox::new("check3", cx) Checkbox::new("check3")
.label("Disabled Checked") .label("Disabled Checked")
.checked(Selection::Selected) .checked(Selection::Selected)
.disabled(true), .disabled(true),
) )
.child( .child(
Checkbox::new("check3_1", cx) Checkbox::new("check3_1")
.label("Disabled Unchecked") .label("Disabled Unchecked")
.checked(Selection::Unselected) .checked(Selection::Unselected)
.disabled(true), .disabled(true),
) )
.child( .child(
Checkbox::new("check3_2", cx) Checkbox::new("check3_2")
.label("Disabled Indeterminate") .label("Disabled Indeterminate")
.checked(Selection::Indeterminate) .checked(Selection::Indeterminate)
.disabled(true), .disabled(true),

View file

@ -9,7 +9,7 @@ use ui::{
h_flex, h_flex,
list::ListItem, list::ListItem,
picker::{Picker, PickerDelegate}, picker::{Picker, PickerDelegate},
v_flex, Clickable as _, v_flex, Clickable as _, IconName,
}; };
actions!(picker_story, [DismissPicker]); actions!(picker_story, [DismissPicker]);
@ -197,7 +197,9 @@ impl Render for PickerStory {
.gap_6() .gap_6()
.child( .child(
v_flex().items_start().child( v_flex().items_start().child(
Button::new("show-picker", "Show Picker...") Button::new("show-picker")
.label("Show Picker...")
.icon(IconName::Search)
.style(ButtonStyle::Primary) .style(ButtonStyle::Primary)
.on_click(cx.listener(|this, _, cx| { .on_click(cx.listener(|this, _, cx| {
this.open = !this.open; this.open = !this.open;

View file

@ -75,7 +75,7 @@ impl Render for PopoverStory {
.child( .child(
v_flex().gap_4().child( v_flex().gap_4().child(
Popover::new("info-top-left") Popover::new("info-top-left")
.trigger(Button::new("info-top-left", "Top Left")) .trigger(Button::new("info-top-left").label("Top Left"))
.content(|cx| { .content(|cx| {
PopoverContent::new(cx, |_| { PopoverContent::new(cx, |_| {
v_flex() v_flex()
@ -83,7 +83,8 @@ impl Render for PopoverStory {
.child("Hello, this is a Popover.") .child("Hello, this is a Popover.")
.child(Divider::horizontal()) .child(Divider::horizontal())
.child( .child(
Button::new("info1", "Yes") Button::new("info1")
.label("Yes")
.width(px(80.)) .width(px(80.))
.size(ButtonSize::Small), .size(ButtonSize::Small),
) )
@ -95,7 +96,7 @@ impl Render for PopoverStory {
.child( .child(
Popover::new("info-top-right") Popover::new("info-top-right")
.anchor(AnchorCorner::TopRight) .anchor(AnchorCorner::TopRight)
.trigger(Button::new("info-top-right", "Top Right")) .trigger(Button::new("info-top-right").label("Top Right"))
.content(|cx| { .content(|cx| {
PopoverContent::new(cx, |_| { PopoverContent::new(cx, |_| {
v_flex() v_flex()
@ -103,7 +104,8 @@ impl Render for PopoverStory {
.child("Hello, this is a Popover on the Top Right.") .child("Hello, this is a Popover on the Top Right.")
.child(Divider::horizontal()) .child(Divider::horizontal())
.child( .child(
Button::new("info1", "Yes") Button::new("info1")
.label("Yes")
.width(px(80.)) .width(px(80.))
.size(ButtonSize::Small), .size(ButtonSize::Small),
) )
@ -120,14 +122,20 @@ impl Render for PopoverStory {
.child( .child(
Popover::new("info-bottom-left") Popover::new("info-bottom-left")
.anchor(AnchorCorner::BottomLeft) .anchor(AnchorCorner::BottomLeft)
.trigger(Button::new("pop", "Popup with Form").width(px(300.))) .trigger(
Button::new("pop").label("Popup with Form").width(px(300.)),
)
.content(move |_| form.clone()), .content(move |_| form.clone()),
) )
.child( .child(
Popover::new("info-bottom-right") Popover::new("info-bottom-right")
.anchor(AnchorCorner::BottomRight) .anchor(AnchorCorner::BottomRight)
.mouse_button(MouseButton::Right) .mouse_button(MouseButton::Right)
.trigger(Button::new("pop", "Mouse Right Click").width(px(300.))) .trigger(
Button::new("pop")
.label("Mouse Right Click")
.width(px(300.)),
)
.content(|cx| { .content(|cx| {
PopoverContent::new(cx, |_| { PopoverContent::new(cx, |_| {
v_flex() v_flex()
@ -135,7 +143,8 @@ impl Render for PopoverStory {
.child("Hello, this is a Popover on the Bottom Right.") .child("Hello, this is a Popover on the Bottom Right.")
.child(Divider::horizontal()) .child(Divider::horizontal())
.child( .child(
Button::new("info1", "Yes") Button::new("info1")
.label("Yes")
.width(px(80.)) .width(px(80.))
.size(ButtonSize::Small), .size(ButtonSize::Small),
) )

View file

@ -25,14 +25,18 @@ impl TooltipStory {
} }
impl Render for TooltipStory { impl Render for TooltipStory {
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> impl gpui::IntoElement { fn render(&mut self, _cx: &mut gpui::ViewContext<Self>) -> impl gpui::IntoElement {
v_flex() v_flex()
.w(px(360.)) .w(px(360.))
.gap_5() .gap_5()
.child( .child(
div() div()
.cursor(CursorStyle::PointingHand) .cursor(CursorStyle::PointingHand)
.child(Button::new("button", "Hover me").style(ButtonStyle::Primary)) .child(
Button::new("button")
.label("Hover me")
.style(ButtonStyle::Primary),
)
.id("tooltip-1") .id("tooltip-1")
.tooltip(|cx| Tooltip::text("This is a Button", cx)), .tooltip(|cx| Tooltip::text("This is a Button", cx)),
) )
@ -40,7 +44,8 @@ impl Render for TooltipStory {
div() div()
.cursor(CursorStyle::PointingHand) .cursor(CursorStyle::PointingHand)
.child( .child(
Button::new("button-meta", "With meta, Hover me") Button::new("button-meta")
.label("With meta, Hover me")
.style(ButtonStyle::Primary), .style(ButtonStyle::Primary),
) )
.id("tooltip-2") .id("tooltip-2")
@ -58,7 +63,7 @@ impl Render for TooltipStory {
div() div()
.cursor(CursorStyle::PointingHand) .cursor(CursorStyle::PointingHand)
.child( .child(
Checkbox::new("check", cx) Checkbox::new("check")
.label("Remember me") .label("Remember me")
.checked(Selection::Selected), .checked(Selection::Selected),
) )

View file

@ -18,6 +18,7 @@ pub enum ButtonRounded {
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub enum ButtonSize { pub enum ButtonSize {
XSmall,
Small, Small,
Medium, Medium,
} }
@ -34,7 +35,7 @@ pub struct Button {
pub base: Div, pub base: Div,
id: ElementId, id: ElementId,
icon: Option<Icon>, icon: Option<Icon>,
label: SharedString, label: Option<SharedString>,
disabled: bool, disabled: bool,
selected: bool, selected: bool,
width: Option<DefiniteLength>, width: Option<DefiniteLength>,
@ -47,12 +48,12 @@ pub struct Button {
} }
impl Button { impl Button {
pub fn new(id: impl Into<ElementId>, label: impl Into<SharedString>) -> Self { pub fn new(id: impl Into<ElementId>) -> Self {
Self { Self {
base: div(), base: div(),
id: id.into(), id: id.into(),
icon: None, icon: None,
label: label.into(), label: None,
disabled: false, disabled: false,
selected: false, selected: false,
style: ButtonStyle::Secondary, style: ButtonStyle::Secondary,
@ -66,15 +67,15 @@ impl Button {
} }
pub fn primary(id: impl Into<ElementId>, label: impl Into<SharedString>) -> Self { pub fn primary(id: impl Into<ElementId>, label: impl Into<SharedString>) -> Self {
Self::new(id, label).style(ButtonStyle::Primary) Self::new(id).label(label).style(ButtonStyle::Primary)
} }
pub fn danger(id: impl Into<ElementId>, label: impl Into<SharedString>) -> Self { pub fn danger(id: impl Into<ElementId>, label: impl Into<SharedString>) -> Self {
Self::new(id, label).style(ButtonStyle::Danger) Self::new(id).label(label).style(ButtonStyle::Danger)
} }
pub fn small(id: impl Into<ElementId>, label: impl Into<SharedString>) -> Self { pub fn small(id: impl Into<ElementId>, label: impl Into<SharedString>) -> Self {
Self::new(id, label).size(ButtonSize::Small) Self::new(id).label(label).size(ButtonSize::Small)
} }
pub fn width(mut self, width: impl Into<DefiniteLength>) -> Self { pub fn width(mut self, width: impl Into<DefiniteLength>) -> Self {
@ -97,6 +98,12 @@ impl Button {
self self
} }
pub fn label(mut self, label: impl Into<SharedString>) -> Self {
self.label = Some(label.into());
self
}
/// Set the icon of the button, if the Button have no label, the button well in Icon Button mode.
pub fn icon(mut self, icon: impl Into<Icon>) -> Self { pub fn icon(mut self, icon: impl Into<Icon>) -> Self {
self.icon = Some(icon.into()); self.icon = Some(icon.into());
self self
@ -147,9 +154,20 @@ impl RenderOnce for Button {
.justify_center() .justify_center()
.when_some(self.width, |this, width| this.w(width)) .when_some(self.width, |this, width| this.w(width))
.when_some(self.height, |this, height| this.h(height)) .when_some(self.height, |this, height| this.h(height))
.map(|this| match self.size { .map(|this| {
ButtonSize::Small => this.px_3().py_2().h_6(), if self.label.is_none() {
ButtonSize::Medium => this.px_4().py_2().h_8(), match self.size {
ButtonSize::XSmall => this.size_5(),
ButtonSize::Small => this.size_6(),
ButtonSize::Medium => this.size_8(),
}
} else {
match self.size {
ButtonSize::XSmall => this.px_1().py_1().h_5(),
ButtonSize::Small => this.px_3().py_2().h_6(),
ButtonSize::Medium => this.px_4().py_2().h_8(),
}
}
}) })
.map(|this| match self.rounded { .map(|this| match self.rounded {
ButtonRounded::Small => this.rounded(px(cx.theme().radius * 0.5)), ButtonRounded::Small => this.rounded(px(cx.theme().radius * 0.5)),
@ -214,10 +232,11 @@ impl RenderOnce for Button {
.gap_2() .gap_2()
.text_color(text_color) .text_color(text_color)
.when_some(self.icon, |this, icon| { .when_some(self.icon, |this, icon| {
this.child(icon.text_color(text_color)) this.child(icon.size(self.size).text_color(text_color))
}) })
.child(self.label) .when_some(self.label, |this, label| this.child(label))
.map(|this| match self.size { .map(|this| match self.size {
ButtonSize::XSmall => this.text_xs(),
ButtonSize::Small => this.text_sm(), ButtonSize::Small => this.text_sm(),
ButtonSize::Medium => this.text_base(), ButtonSize::Medium => this.text_base(),
}) })
@ -250,9 +269,9 @@ impl ButtonStyle {
fn border_color(&self, cx: &WindowContext) -> Hsla { fn border_color(&self, cx: &WindowContext) -> Hsla {
match self { match self {
ButtonStyle::Primary => cx.theme().primary.darken(0.05), ButtonStyle::Primary => cx.theme().primary,
ButtonStyle::Secondary => cx.theme().border, ButtonStyle::Secondary => cx.theme().border,
ButtonStyle::Danger => cx.theme().destructive.darken(0.05), ButtonStyle::Danger => cx.theme().destructive,
} }
} }
@ -265,8 +284,11 @@ impl ButtonStyle {
} }
fn hovered(&self, cx: &WindowContext) -> ButtonStyles { fn hovered(&self, cx: &WindowContext) -> ButtonStyles {
// Hover color = color/90 let bg = match self {
let bg = self.bg_color(cx); ButtonStyle::Primary => cx.theme().primary_hover,
ButtonStyle::Secondary => cx.theme().secondary_hover,
ButtonStyle::Danger => cx.theme().destructive_hover,
};
let border = self.border_color(cx); let border = self.border_color(cx);
let fg = self.text_color(cx); let fg = self.text_color(cx);
@ -274,17 +296,25 @@ impl ButtonStyle {
} }
fn active(&self, cx: &WindowContext) -> ButtonStyles { fn active(&self, cx: &WindowContext) -> ButtonStyles {
let bg = self.bg_color(cx).darken(0.05); let bg = match self {
ButtonStyle::Primary => cx.theme().primary_active,
ButtonStyle::Secondary => cx.theme().secondary_active,
ButtonStyle::Danger => cx.theme().destructive_active,
};
let border = self.border_color(cx); let border = self.border_color(cx);
let fg = self.text_color(cx).darken(0.05); let fg = self.text_color(cx);
ButtonStyles { bg, border, fg } ButtonStyles { bg, border, fg }
} }
fn selected(&self, cx: &WindowContext) -> ButtonStyles { fn selected(&self, cx: &WindowContext) -> ButtonStyles {
let bg = self.bg_color(cx).darken(0.07); let bg = match self {
ButtonStyle::Primary => cx.theme().primary_active,
ButtonStyle::Secondary => cx.theme().secondary_active,
ButtonStyle::Danger => cx.theme().destructive_active,
};
let border = self.border_color(cx); let border = self.border_color(cx);
let fg = self.text_color(cx).darken(0.07); let fg = self.text_color(cx);
ButtonStyles { bg, border, fg } ButtonStyles { bg, border, fg }
} }

View file

@ -23,7 +23,7 @@ pub struct Checkbox {
} }
impl Checkbox { impl Checkbox {
pub fn new(id: impl Into<ElementId>, _: &mut WindowContext) -> Self { pub fn new(id: impl Into<ElementId>) -> Self {
Self { Self {
id: id.into(), id: id.into(),
checked: Selection::Unselected, checked: Selection::Unselected,

View file

@ -1,7 +1,7 @@
use crate::theme::ActiveTheme; use crate::{button::ButtonSize, theme::ActiveTheme};
use gpui::{ use gpui::{
svg, AnyElement, Hsla, IntoElement, RenderOnce, SharedString, StyleRefinement, Styled, Svg, prelude::FluentBuilder as _, svg, AnyElement, Hsla, IntoElement, RenderOnce, SharedString,
WindowContext, StyleRefinement, Styled, Svg, WindowContext,
}; };
#[derive(IntoElement)] #[derive(IntoElement)]
@ -18,6 +18,9 @@ pub enum IconName {
Info, Info,
Ellipsis, Ellipsis,
EllipsisVertical, EllipsisVertical,
Search,
Delete,
CicleX,
} }
impl IconName { impl IconName {
@ -35,6 +38,9 @@ impl IconName {
IconName::Info => "icons/info.svg", IconName::Info => "icons/info.svg",
IconName::Ellipsis => "icons/ellipsis.svg", IconName::Ellipsis => "icons/ellipsis.svg",
IconName::EllipsisVertical => "icons/ellipsis-vertical.svg", IconName::EllipsisVertical => "icons/ellipsis-vertical.svg",
IconName::Search => "icons/search.svg",
IconName::Delete => "icons/delete.svg",
IconName::CicleX => "icons/circle-x.svg",
} }
.into() .into()
} }
@ -57,6 +63,7 @@ pub struct Icon {
base: Svg, base: Svg,
path: SharedString, path: SharedString,
text_color: Option<Hsla>, text_color: Option<Hsla>,
size: ButtonSize,
} }
impl Icon { impl Icon {
@ -65,6 +72,7 @@ impl Icon {
base: svg().flex_none().size_4(), base: svg().flex_none().size_4(),
path: name.path(), path: name.path(),
text_color: None, text_color: None,
size: ButtonSize::Medium,
} }
} }
@ -75,6 +83,12 @@ impl Icon {
self.path = path.into(); self.path = path.into();
self self
} }
pub fn size(mut self, size: ButtonSize) -> Self {
self.size = size;
self
}
} }
impl Styled for Icon { impl Styled for Icon {
@ -92,7 +106,14 @@ impl RenderOnce for Icon {
fn render(self, cx: &mut WindowContext) -> impl IntoElement { fn render(self, cx: &mut WindowContext) -> impl IntoElement {
let text_color = self.text_color.unwrap_or_else(|| cx.theme().foreground); let text_color = self.text_color.unwrap_or_else(|| cx.theme().foreground);
self.base.text_color(text_color).path(self.path) self.base
.text_color(text_color)
.map(|this| match self.size {
ButtonSize::XSmall => this.size_3(),
ButtonSize::Small => this.size_3p5(),
ButtonSize::Medium => this.size_4(),
})
.path(self.path)
} }
} }

View file

@ -109,7 +109,7 @@ impl RenderOnce for Switch {
self.base self.base
.map(|this| match self.size { .map(|this| match self.size {
ButtonSize::Medium => this.w_11().h_6().rounded_xl(), ButtonSize::Medium => this.w_11().h_6().rounded_xl(),
ButtonSize::Small => this.w_8().h_4().rounded_lg(), ButtonSize::XSmall | ButtonSize::Small => this.w_8().h_4().rounded_lg(),
}) })
.flex() .flex()
.items_center() .items_center()
@ -127,21 +127,24 @@ impl RenderOnce for Switch {
.bg(toggle_bg) .bg(toggle_bg)
.map(|this| match self.size { .map(|this| match self.size {
ButtonSize::Medium => this.w_5().h_5(), ButtonSize::Medium => this.w_5().h_5(),
ButtonSize::Small => this.w_3().h_3(), ButtonSize::XSmall | ButtonSize::Small => this.w_3().h_3(),
}), }),
), ),
) )
.when_some(self.label, |this, label| { .when_some(self.label, |this, label| {
this.child(div().child(label).map(|this| match self.size { this.child(div().child(label).map(|this| match self.size {
ButtonSize::Medium => this.text_base(), ButtonSize::Medium => this.text_base(),
ButtonSize::Small => this.text_sm(), ButtonSize::XSmall | ButtonSize::Small => this.text_sm(),
})) }))
}) })
.when_some( .when_some(
self.on_click.filter(|_| !self.disabled), self.on_click.filter(|_| !self.disabled),
|this, on_click| { |this, on_click| {
cx.stop_propagation(); this.on_click(move |ev, cx| {
this.on_click(move |ev, cx| on_click(ev, cx)) dbg!("---- switch clicked");
cx.stop_propagation();
on_click(ev, cx);
})
}, },
) )
} }

View file

@ -83,7 +83,6 @@ impl Colorize for Hsla {
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
struct Colors { struct Colors {
pub title_bar_background: Hsla, pub title_bar_background: Hsla,
pub background: Hsla, pub background: Hsla,
pub foreground: Hsla, pub foreground: Hsla,
pub card: Hsla, pub card: Hsla,
@ -91,15 +90,21 @@ struct Colors {
pub popover: Hsla, pub popover: Hsla,
pub popover_foreground: Hsla, pub popover_foreground: Hsla,
pub primary: Hsla, pub primary: Hsla,
pub primary_hover: Hsla,
pub primary_active: Hsla,
pub primary_foreground: Hsla, pub primary_foreground: Hsla,
pub secondary: Hsla, pub secondary: Hsla,
pub secondary_hover: Hsla,
pub secondary_active: Hsla,
pub secondary_foreground: Hsla, pub secondary_foreground: Hsla,
pub destructive: Hsla,
pub destructive_hover: Hsla,
pub destructive_active: Hsla,
pub destructive_foreground: Hsla,
pub muted: Hsla, pub muted: Hsla,
pub muted_foreground: Hsla, pub muted_foreground: Hsla,
pub accent: Hsla, pub accent: Hsla,
pub accent_foreground: Hsla, pub accent_foreground: Hsla,
pub destructive: Hsla,
pub destructive_foreground: Hsla,
pub border: Hsla, pub border: Hsla,
pub input: Hsla, pub input: Hsla,
pub ring: Hsla, pub ring: Hsla,
@ -145,15 +150,21 @@ impl Colors {
popover: hsl(0.0, 0.0, 100.0), popover: hsl(0.0, 0.0, 100.0),
popover_foreground: hsl(240.0, 10.0, 3.9), popover_foreground: hsl(240.0, 10.0, 3.9),
primary: hsl(240.0, 5.9, 10.0), primary: hsl(240.0, 5.9, 10.0),
primary_hover: hsl(240.0, 5.9, 30.0),
primary_active: hsl(240.0, 5.9, 45.0),
primary_foreground: hsl(0.0, 0.0, 98.0), primary_foreground: hsl(0.0, 0.0, 98.0),
secondary: hsl(240.0, 4.8, 95.9), secondary: hsl(240.0, 4.8, 95.9),
secondary_hover: hsl(240.0, 4.8, 99.),
secondary_active: hsl(240.0, 5.9, 94.0),
secondary_foreground: hsl(240.0, 59.0, 10.0), secondary_foreground: hsl(240.0, 59.0, 10.0),
destructive: hsl(0.0, 84.2, 60.2),
destructive_hover: hsl(0.0, 84.2, 65.0),
destructive_active: hsl(0.0, 84.2, 47.0),
destructive_foreground: hsl(0.0, 0.0, 98.0),
muted: hsl(240.0, 4.8, 95.9), muted: hsl(240.0, 4.8, 95.9),
muted_foreground: hsl(240.0, 3.8, 46.1), muted_foreground: hsl(240.0, 3.8, 46.1),
accent: hsl(240.0, 5.0, 96.0), accent: hsl(240.0, 5.0, 96.0),
accent_foreground: hsl(240.0, 5.9, 10.0), accent_foreground: hsl(240.0, 5.9, 10.0),
destructive: hsl(0.0, 84.2, 60.2),
destructive_foreground: hsl(0.0, 0.0, 98.0),
border: hsl(240.0, 5.9, 90.0), border: hsl(240.0, 5.9, 90.0),
input: hsl(240.0, 5.9, 90.0), input: hsl(240.0, 5.9, 90.0),
ring: hsl(240.0, 5.9, 10.0), ring: hsl(240.0, 5.9, 10.0),
@ -198,15 +209,21 @@ impl Colors {
popover: hsl(240.0, 10.0, 3.9), popover: hsl(240.0, 10.0, 3.9),
popover_foreground: hsl(0.0, 0.0, 98.0), popover_foreground: hsl(0.0, 0.0, 98.0),
primary: hsl(0.0, 0.0, 98.0), primary: hsl(0.0, 0.0, 98.0),
primary_hover: hsl(0.0, 0.0, 85.0),
primary_active: hsl(0.0, 0.0, 60.0),
primary_foreground: hsl(240.0, 5.9, 10.0), primary_foreground: hsl(240.0, 5.9, 10.0),
secondary: hsl(240.0, 3.7, 15.9), secondary: hsl(240.0, 3.7, 15.9),
secondary_hover: hsl(240.0, 3.7, 20.9),
secondary_active: hsl(240.0, 3.7, 8.9),
secondary_foreground: hsl(0.0, 0.0, 98.0), secondary_foreground: hsl(0.0, 0.0, 98.0),
destructive: hsl(0.0, 62.8, 30.6),
destructive_hover: hsl(0.0, 62.8, 35.6),
destructive_active: hsl(0.0, 62.8, 20.6),
destructive_foreground: hsl(0.0, 0.0, 98.0),
muted: hsl(240.0, 3.7, 15.9), muted: hsl(240.0, 3.7, 15.9),
muted_foreground: hsl(240.0, 5.0, 64.9), muted_foreground: hsl(240.0, 5.0, 64.9),
accent: hsl(240.0, 3.7, 15.9), accent: hsl(240.0, 3.7, 15.9),
accent_foreground: hsl(0.0, 0.0, 98.0), accent_foreground: hsl(0.0, 0.0, 98.0),
destructive: hsl(0.0, 62.8, 30.6),
destructive_foreground: hsl(0.0, 0.0, 98.0),
border: hsl(240.0, 3.7, 15.9), border: hsl(240.0, 3.7, 15.9),
input: hsl(240.0, 3.7, 15.9), input: hsl(240.0, 3.7, 15.9),
ring: hsl(240.0, 4.9, 83.9), ring: hsl(240.0, 4.9, 83.9),
@ -233,15 +250,21 @@ pub struct Theme {
pub popover: Hsla, pub popover: Hsla,
pub popover_foreground: Hsla, pub popover_foreground: Hsla,
pub primary: Hsla, pub primary: Hsla,
pub primary_hover: Hsla,
pub primary_active: Hsla,
pub primary_foreground: Hsla, pub primary_foreground: Hsla,
pub secondary: Hsla, pub secondary: Hsla,
pub secondary_hover: Hsla,
pub secondary_active: Hsla,
pub secondary_foreground: Hsla, pub secondary_foreground: Hsla,
pub destructive: Hsla,
pub destructive_hover: Hsla,
pub destructive_active: Hsla,
pub destructive_foreground: Hsla,
pub muted: Hsla, pub muted: Hsla,
pub muted_foreground: Hsla, pub muted_foreground: Hsla,
pub accent: Hsla, pub accent: Hsla,
pub accent_foreground: Hsla, pub accent_foreground: Hsla,
pub destructive: Hsla,
pub destructive_foreground: Hsla,
pub border: Hsla, pub border: Hsla,
pub input: Hsla, pub input: Hsla,
pub ring: Hsla, pub ring: Hsla,
@ -276,15 +299,21 @@ impl From<Colors> for Theme {
popover: colors.popover, popover: colors.popover,
popover_foreground: colors.popover_foreground, popover_foreground: colors.popover_foreground,
primary: colors.primary, primary: colors.primary,
primary_hover: colors.primary_hover,
primary_active: colors.primary_active,
primary_foreground: colors.primary_foreground, primary_foreground: colors.primary_foreground,
secondary: colors.secondary, secondary: colors.secondary,
secondary_hover: colors.secondary_hover,
secondary_active: colors.secondary_active,
secondary_foreground: colors.secondary_foreground, secondary_foreground: colors.secondary_foreground,
destructive: colors.destructive,
destructive_hover: colors.destructive_hover,
destructive_active: colors.destructive_active,
destructive_foreground: colors.destructive_foreground,
muted: colors.muted, muted: colors.muted,
muted_foreground: colors.muted_foreground, muted_foreground: colors.muted_foreground,
accent: colors.accent, accent: colors.accent,
accent_foreground: colors.accent_foreground, accent_foreground: colors.accent_foreground,
destructive: colors.destructive,
destructive_foreground: colors.destructive_foreground,
border: colors.border, border: colors.border,
input: colors.input, input: colors.input,
ring: colors.ring, ring: colors.ring,