feat: impl Styled for button & variant support

This commit is contained in:
2026-05-13 02:23:07 +08:00
parent 53993fed93
commit eae7b84f4b

View File

@@ -1,5 +1,5 @@
use gpui::{ use gpui::{
AnyElement, FontWeight, InteractiveElement, IntoElement, ParentElement, AnyElement, FontWeight, InteractiveElement, IntoElement, ParentElement, Refineable,
StatefulInteractiveElement, Styled, div, prelude::FluentBuilder, StatefulInteractiveElement, Styled, div, prelude::FluentBuilder,
}; };
@@ -13,6 +13,13 @@ pub struct Button {
trailing: Option<Box<dyn FnOnce(gpui::Rgba) -> gpui::AnyElement>>, trailing: Option<Box<dyn FnOnce(gpui::Rgba) -> gpui::AnyElement>>,
on_click: Option<Box<dyn Fn(&gpui::ClickEvent, &mut gpui::Window, &mut gpui::App)>>, on_click: Option<Box<dyn Fn(&gpui::ClickEvent, &mut gpui::Window, &mut gpui::App)>>,
enabled: bool, enabled: bool,
style: gpui::StyleRefinement,
variant: Variant,
}
pub enum Variant {
Primary,
Secondary,
} }
pub fn button(id: impl Into<gpui::ElementId>) -> Button { pub fn button(id: impl Into<gpui::ElementId>) -> Button {
@@ -23,10 +30,17 @@ pub fn button(id: impl Into<gpui::ElementId>) -> Button {
trailing: None, trailing: None,
on_click: None, on_click: None,
enabled: true, enabled: true,
style: gpui::StyleRefinement::default(),
variant: Variant::Primary,
} }
} }
impl Button { impl Button {
pub fn variant(mut self, variant: Variant) -> Self {
self.variant = variant;
self
}
pub fn label(mut self, s: impl TextContent) -> Self { pub fn label(mut self, s: impl TextContent) -> Self {
self.label = Some(s.into_any_element()); self.label = Some(s.into_any_element());
self self
@@ -62,38 +76,63 @@ impl Button {
} }
} }
impl gpui::Styled for Button {
fn style(&mut self) -> &mut gpui::StyleRefinement {
&mut self.style
}
}
impl gpui::RenderOnce for Button { impl gpui::RenderOnce for Button {
fn render(self, _window: &mut gpui::Window, cx: &mut gpui::App) -> impl gpui::IntoElement { fn render(self, _window: &mut gpui::Window, cx: &mut gpui::App) -> impl gpui::IntoElement {
let theme = app::current_theme(cx); let theme = app::current_theme(cx);
let icon_color = match self.variant {
| Variant::Primary => theme.colors.accent_text,
| Variant::Secondary => theme.colors.text,
};
let mut children: Vec<AnyElement> = Vec::with_capacity(3); let mut children: Vec<AnyElement> = Vec::with_capacity(3);
if let Some(leading) = self.leading { if let Some(leading) = self.leading {
children.push(leading(theme.colors.accent_text)); children.push(leading(icon_color));
} }
if let Some(label) = self.label { if let Some(label) = self.label {
children.push(label); children.push(label);
} }
if let Some(trailing) = self.trailing { if let Some(trailing) = self.trailing {
children.push(trailing(theme.colors.accent_text)); children.push(trailing(icon_color));
} }
div() let mut div = div()
.id(self.id) .id(self.id)
.flex() .flex()
.flex_row() .flex_row()
.gap_2() .gap_2()
.items_center() .items_center()
.rounded_sm() .rounded_sm()
.bg(theme.colors.accent)
.text_xs() .text_xs()
.text_color(theme.colors.accent_text)
.font_weight(FontWeight(500.)) .font_weight(FontWeight(500.))
.px_2p5() .px_2p5()
.py_0p5() .py_0p5()
.children(children) .children(children)
.when(matches!(self.variant, Variant::Primary), |div| {
div.bg(theme.colors.accent)
.text_color(theme.colors.accent_text)
.border_1()
.border_color(theme.colors.border.blend(theme.colors.accent))
})
.when(matches!(self.variant, Variant::Secondary), |div| {
div.bg(theme.colors.surface_elevated)
.text_color(theme.colors.text)
.border_1()
.border_color(theme.colors.border)
})
.when(self.on_click.is_some() && self.enabled, |div| { .when(self.on_click.is_some() && self.enabled, |div| {
div.on_click(self.on_click.unwrap()) div.on_click(self.on_click.unwrap())
}) })
.when(!self.enabled, |div| div.opacity(0.5)) .when(!self.enabled, |div| div.opacity(0.5));
div.style().refine(&self.style);
div
} }
} }