chore: revamp theme

This commit is contained in:
2025-05-07 14:12:31 +07:00
parent 97e66fbeb7
commit 2f83b5091e
57 changed files with 922 additions and 1494 deletions

View File

@@ -1,35 +1,23 @@
use crate::{
indicator::Indicator,
theme::{scale::ColorScaleStep, ActiveTheme},
tooltip::Tooltip,
Disableable, Icon, Selectable, Sizable, Size, StyledExt,
};
use gpui::{
div, prelude::FluentBuilder as _, px, relative, AnyElement, App, ClickEvent, Corners, Div,
Edges, ElementId, Hsla, InteractiveElement, IntoElement, MouseButton, ParentElement, Pixels,
RenderOnce, SharedString, StatefulInteractiveElement as _, Styled, Window,
div, prelude::FluentBuilder as _, relative, AnyElement, App, ClickEvent, Div, ElementId, Hsla,
InteractiveElement, IntoElement, MouseButton, ParentElement, RenderOnce, SharedString,
StatefulInteractiveElement as _, Styled, Window,
};
use theme::ActiveTheme;
use crate::{
indicator::Indicator, tooltip::Tooltip, Disableable, Icon, Selectable, Sizable, Size, StyledExt,
};
pub enum ButtonRounded {
None,
Small,
Medium,
Large,
Size(Pixels),
}
impl From<Pixels> for ButtonRounded {
fn from(px: Pixels) -> Self {
ButtonRounded::Size(px)
}
Normal,
Full,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct ButtonCustomVariant {
color: Hsla,
foreground: Hsla,
border: Hsla,
shadow: bool,
hover: Hsla,
active: Hsla,
}
@@ -66,12 +54,10 @@ pub trait ButtonVariants: Sized {
impl ButtonCustomVariant {
pub fn new(_window: &Window, cx: &App) -> Self {
Self {
color: cx.theme().accent.step(cx, ColorScaleStep::NINE),
foreground: cx.theme().accent.step(cx, ColorScaleStep::ONE),
border: cx.theme().accent.step(cx, ColorScaleStep::TEN),
hover: cx.theme().accent.step(cx, ColorScaleStep::TEN),
active: cx.theme().accent.step(cx, ColorScaleStep::ELEVEN),
shadow: true,
color: cx.theme().element_background,
foreground: cx.theme().element_foreground,
hover: cx.theme().element_hover,
active: cx.theme().element_active,
}
}
@@ -85,11 +71,6 @@ impl ButtonCustomVariant {
self
}
pub fn border(mut self, color: Hsla) -> Self {
self.border = color;
self
}
pub fn hover(mut self, color: Hsla) -> Self {
self.hover = color;
self
@@ -99,11 +80,6 @@ impl ButtonCustomVariant {
self.active = color;
self
}
pub fn shadow(mut self, shadow: bool) -> Self {
self.shadow = shadow;
self
}
}
/// The variant of the Button.
@@ -149,12 +125,9 @@ pub struct Button {
disabled: bool,
variant: ButtonVariant,
rounded: ButtonRounded,
border_corners: Corners<bool>,
border_edges: Edges<bool>,
size: Size,
reverse: bool,
bold: bool,
centered: bool,
tooltip: Option<SharedString>,
on_click: OnClick,
loading: bool,
@@ -179,9 +152,7 @@ impl Button {
disabled: false,
selected: false,
variant: ButtonVariant::default(),
rounded: ButtonRounded::Medium,
border_corners: Corners::all(true),
border_edges: Edges::all(true),
rounded: ButtonRounded::Normal,
size: Size::Medium,
tooltip: None,
on_click: None,
@@ -189,7 +160,6 @@ impl Button {
loading: false,
reverse: false,
bold: false,
centered: true,
children: Vec::new(),
loading_icon: None,
}
@@ -201,18 +171,6 @@ impl Button {
self
}
/// Set the border corners side of the Button.
pub(crate) fn border_corners(mut self, corners: impl Into<Corners<bool>>) -> Self {
self.border_corners = corners.into();
self
}
/// Set the border edges of the Button.
pub(crate) fn border_edges(mut self, edges: impl Into<Edges<bool>>) -> Self {
self.border_edges = edges.into();
self
}
/// Set label to the Button, if no label is set, the button will be in Icon Button mode.
pub fn label(mut self, label: impl Into<SharedString>) -> Self {
self.label = Some(label.into());
@@ -243,11 +201,6 @@ impl Button {
self
}
pub fn not_centered(mut self) -> Self {
self.centered = false;
self
}
pub fn bold(mut self) -> Self {
self.bold = true;
self
@@ -335,11 +288,12 @@ impl RenderOnce for Button {
.id(self.id)
.flex()
.items_center()
.when(self.centered, |this| this.justify_center())
.justify_center()
.cursor_pointer()
.overflow_hidden()
.when(cx.theme().shadow && normal_style.shadow, |this| {
this.shadow_sm()
.map(|this| match self.rounded {
ButtonRounded::Normal => this.rounded(cx.theme().radius),
ButtonRounded::Full => this.rounded_full(),
})
.when(!style.no_padding(), |this| {
if self.label.is_none() && self.children.is_empty() {
@@ -361,50 +315,20 @@ impl RenderOnce for Button {
}
}
})
.when(
self.border_corners.top_left && self.border_corners.bottom_left,
|this| match self.rounded {
ButtonRounded::Small => this.rounded_l(px(cx.theme().radius * 0.5)),
ButtonRounded::Medium => this.rounded_l(px(cx.theme().radius)),
ButtonRounded::Large => this.rounded_l(px(cx.theme().radius * 1.6)),
ButtonRounded::Size(px) => this.rounded_l(px),
ButtonRounded::None => this.rounded_none(),
},
)
.when(
self.border_corners.top_right && self.border_corners.bottom_right,
|this| match self.rounded {
ButtonRounded::Small => this.rounded_r(px(cx.theme().radius * 0.5)),
ButtonRounded::Medium => this.rounded_r(px(cx.theme().radius)),
ButtonRounded::Large => this.rounded_r(px(cx.theme().radius * 1.6)),
ButtonRounded::Size(px) => this.rounded_r(px),
ButtonRounded::None => this.rounded_none(),
},
)
.when(self.border_edges.left, |this| this.border_l_1())
.when(self.border_edges.right, |this| this.border_r_1())
.when(self.border_edges.top, |this| this.border_t_1())
.when(self.border_edges.bottom, |this| this.border_b_1())
.text_color(normal_style.fg)
.when(self.selected, |this| {
let selected_style = style.selected(window, cx);
this.bg(selected_style.bg)
.border_color(selected_style.border)
.text_color(selected_style.fg)
this.bg(selected_style.bg).text_color(selected_style.fg)
})
.when(!self.disabled && !self.selected, |this| {
this.border_color(normal_style.border)
.bg(normal_style.bg)
this.bg(normal_style.bg)
.when(normal_style.underline, |this| this.text_decoration_1())
.hover(|this| {
let hover_style = style.hovered(window, cx);
this.bg(hover_style.bg).border_color(hover_style.border)
this.bg(hover_style.bg)
})
.active(|this| {
let active_style = style.active(window, cx);
this.bg(active_style.bg)
.border_color(active_style.border)
.text_color(active_style.fg)
this.bg(active_style.bg).text_color(active_style.fg)
})
})
.when_some(
@@ -427,9 +351,9 @@ impl RenderOnce for Button {
this.cursor_not_allowed()
.bg(disabled_style.bg)
.text_color(disabled_style.fg)
.border_color(disabled_style.border)
.shadow_none()
})
.text_color(normal_style.fg)
.child({
div()
.flex()
@@ -474,45 +398,26 @@ impl RenderOnce for Button {
struct ButtonVariantStyle {
bg: Hsla,
border: Hsla,
fg: Hsla,
underline: bool,
shadow: bool,
}
impl ButtonVariant {
fn bg_color(&self, _window: &Window, cx: &App) -> Hsla {
match self {
ButtonVariant::Primary => cx.theme().accent.step(cx, ColorScaleStep::NINE),
ButtonVariant::Primary => cx.theme().element_background,
ButtonVariant::Custom(colors) => colors.color,
_ => cx.theme().transparent,
_ => cx.theme().ghost_element_background,
}
}
fn text_color(&self, _window: &Window, cx: &App) -> Hsla {
match self {
ButtonVariant::Primary => match cx.theme().accent.name().to_string().as_str() {
"Sky" => cx.theme().base.darken(cx),
"Mint" => cx.theme().base.darken(cx),
"Lime" => cx.theme().base.darken(cx),
"Amber" => cx.theme().base.darken(cx),
"Yellow" => cx.theme().base.darken(cx),
_ => cx.theme().accent.step(cx, ColorScaleStep::ONE),
},
ButtonVariant::Link => cx.theme().accent.step(cx, ColorScaleStep::NINE),
ButtonVariant::Ghost => cx.theme().base.step(cx, ColorScaleStep::TWELVE),
ButtonVariant::Primary => cx.theme().element_foreground,
ButtonVariant::Link => cx.theme().text_accent,
ButtonVariant::Ghost => cx.theme().text_muted,
ButtonVariant::Custom(colors) => colors.foreground,
_ => cx.theme().base.step(cx, ColorScaleStep::TWELVE),
}
}
fn border_color(&self, _window: &Window, cx: &App) -> Hsla {
match self {
ButtonVariant::Primary => cx.theme().accent.step(cx, ColorScaleStep::NINE),
ButtonVariant::Ghost | ButtonVariant::Link | ButtonVariant::Text => {
cx.theme().transparent
}
ButtonVariant::Custom(colors) => colors.border,
_ => cx.theme().text,
}
}
@@ -520,142 +425,79 @@ impl ButtonVariant {
matches!(self, ButtonVariant::Link)
}
fn shadow(&self, _window: &Window, _cx: &App) -> bool {
match self {
ButtonVariant::Primary => true,
ButtonVariant::Custom(c) => c.shadow,
_ => false,
}
}
fn normal(&self, window: &Window, cx: &App) -> ButtonVariantStyle {
let bg = self.bg_color(window, cx);
let border = self.border_color(window, cx);
let fg = self.text_color(window, cx);
let underline = self.underline(window, cx);
let shadow = self.shadow(window, cx);
ButtonVariantStyle {
bg,
border,
fg,
underline,
shadow,
}
ButtonVariantStyle { bg, fg, underline }
}
fn hovered(&self, window: &Window, cx: &App) -> ButtonVariantStyle {
let bg = match self {
ButtonVariant::Primary => cx.theme().accent.step(cx, ColorScaleStep::TEN),
ButtonVariant::Ghost => cx.theme().base.step(cx, ColorScaleStep::FOUR),
ButtonVariant::Link => cx.theme().transparent,
ButtonVariant::Text => cx.theme().transparent,
ButtonVariant::Primary => cx.theme().element_hover,
ButtonVariant::Ghost => cx.theme().ghost_element_hover,
ButtonVariant::Link => cx.theme().ghost_element_background,
ButtonVariant::Text => cx.theme().ghost_element_background,
ButtonVariant::Custom(colors) => colors.hover,
};
let border = self.border_color(window, cx);
let fg = match self {
ButtonVariant::Ghost => cx.theme().base.step(cx, ColorScaleStep::TWELVE),
ButtonVariant::Link => cx.theme().accent.step(cx, ColorScaleStep::TEN),
ButtonVariant::Ghost => cx.theme().text,
ButtonVariant::Link => cx.theme().text_accent,
_ => self.text_color(window, cx),
};
let underline = self.underline(window, cx);
let shadow = self.shadow(window, cx);
ButtonVariantStyle {
bg,
border,
fg,
underline,
shadow,
}
ButtonVariantStyle { bg, fg, underline }
}
fn active(&self, window: &Window, cx: &App) -> ButtonVariantStyle {
let bg = match self {
ButtonVariant::Primary => cx.theme().accent.step(cx, ColorScaleStep::TEN),
ButtonVariant::Ghost => cx.theme().base.step(cx, ColorScaleStep::THREE),
ButtonVariant::Link => cx.theme().transparent,
ButtonVariant::Text => cx.theme().transparent,
ButtonVariant::Primary => cx.theme().element_active,
ButtonVariant::Ghost => cx.theme().ghost_element_active,
ButtonVariant::Custom(colors) => colors.active,
_ => cx.theme().ghost_element_background,
};
let fg = match self {
ButtonVariant::Link => cx.theme().accent.step(cx, ColorScaleStep::NINE),
ButtonVariant::Text => cx.theme().base.step(cx, ColorScaleStep::ELEVEN),
ButtonVariant::Link => cx.theme().text_accent,
ButtonVariant::Text => cx.theme().text,
_ => self.text_color(window, cx),
};
let border = self.border_color(window, cx);
let underline = self.underline(window, cx);
let shadow = self.shadow(window, cx);
ButtonVariantStyle {
bg,
border,
fg,
underline,
shadow,
}
ButtonVariantStyle { bg, fg, underline }
}
fn selected(&self, window: &Window, cx: &App) -> ButtonVariantStyle {
let bg = match self {
ButtonVariant::Primary => cx.theme().accent.step(cx, ColorScaleStep::TEN),
ButtonVariant::Ghost => cx.theme().base.step(cx, ColorScaleStep::THREE),
ButtonVariant::Link => cx.theme().transparent,
ButtonVariant::Text => cx.theme().transparent,
ButtonVariant::Primary => cx.theme().element_selected,
ButtonVariant::Ghost => cx.theme().ghost_element_selected,
ButtonVariant::Custom(colors) => colors.active,
_ => cx.theme().ghost_element_background,
};
let fg = match self {
ButtonVariant::Link => cx.theme().accent.step(cx, ColorScaleStep::TEN),
ButtonVariant::Text => cx.theme().accent.step(cx, ColorScaleStep::TEN),
ButtonVariant::Link => cx.theme().text_accent,
ButtonVariant::Text => cx.theme().text,
_ => self.text_color(window, cx),
};
let border = self.border_color(window, cx);
let underline = self.underline(window, cx);
let shadow = self.shadow(window, cx);
ButtonVariantStyle {
bg,
border,
fg,
underline,
shadow,
}
ButtonVariantStyle { bg, fg, underline }
}
fn disabled(&self, window: &Window, cx: &App) -> ButtonVariantStyle {
let bg = match self {
ButtonVariant::Link | ButtonVariant::Ghost | ButtonVariant::Text => {
cx.theme().transparent
cx.theme().ghost_element_disabled
}
_ => cx.theme().base.step(cx, ColorScaleStep::THREE),
_ => cx.theme().element_disabled,
};
let fg = match self {
ButtonVariant::Primary => match cx.theme().accent.name().to_string().as_str() {
"Sky" => cx.theme().base.darken(cx),
"Mint" => cx.theme().base.darken(cx),
"Lime" => cx.theme().base.darken(cx),
"Amber" => cx.theme().base.darken(cx),
"Yellow" => cx.theme().base.darken(cx),
_ => cx.theme().accent.step(cx, ColorScaleStep::ONE),
},
_ => cx.theme().base.step(cx, ColorScaleStep::ELEVEN),
ButtonVariant::Primary => cx.theme().text_muted, // TODO: use a different color?
_ => cx.theme().text_muted,
};
let border = bg;
let underline = self.underline(window, cx);
let shadow = false;
ButtonVariantStyle {
bg,
border,
fg,
underline,
shadow,
}
ButtonVariantStyle { bg, fg, underline }
}
}