theme: Add apply_color to theme to support merge a new color into theme color. (#353)

- Fix #334 to keep selected theme color.

## Example

<img width="1472" alt="image"
src="https://github.com/user-attachments/assets/3a05f58c-2720-436c-8df9-c582871d87d5">
<img width="1472" alt="image"
src="https://github.com/user-attachments/assets/5b8db494-1402-4785-8d98-a87173b15284">
<img width="1472" alt="image"
src="https://github.com/user-attachments/assets/0305c05b-c2fb-4fea-9a50-5aa00de504fe">
<img width="1472" alt="image"
src="https://github.com/user-attachments/assets/91fbb7b6-7494-440d-95bf-2de37d6bbb44">
This commit is contained in:
Jason Lee 2024-10-16 18:38:21 +08:00 committed by GitHub
parent 41c64e2912
commit 1fdcff3b51
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 103 additions and 37 deletions

View file

@ -14,7 +14,7 @@ use ui::{
dock::{DockArea, DockAreaState, DockEvent, DockItem, PanelView},
h_flex,
popup_menu::PopupMenuExt,
theme::{ActiveTheme, Colorize as _, Theme},
theme::{ActiveTheme, Theme},
ContextModal, IconName, Root, Sizable, TitleBar,
};
@ -40,6 +40,7 @@ pub fn init(_app_state: Arc<AppState>, cx: &mut AppContext) {
}
pub struct StoryWorkspace {
theme_color: Option<Hsla>,
dock_area: View<DockArea>,
locale_selector: View<LocaleSelector>,
theme_color_picker: View<ColorPicker>,
@ -96,27 +97,22 @@ impl StoryWorkspace {
let mut picker = ColorPicker::new("theme-color-picker", cx)
.xsmall()
.anchor(AnchorCorner::TopRight)
.label("Primary Color");
.label("Theme Color");
picker.set_value(cx.theme().primary, cx);
picker
});
cx.subscribe(
&theme_color_picker,
|_, _, ev: &ColorPickerEvent, cx| match ev {
|this, _, ev: &ColorPickerEvent, cx| match ev {
ColorPickerEvent::Change(color) => {
if let Some(color) = color {
let theme = cx.global_mut::<Theme>();
theme.primary = *color;
theme.primary_hover = color.lighten(0.1);
theme.primary_active = color.darken(0.1);
cx.refresh();
}
this.set_theme_color(*color, cx);
}
},
)
.detach();
Self {
theme_color: None,
dock_area,
locale_selector,
theme_color_picker,
@ -125,6 +121,25 @@ impl StoryWorkspace {
}
}
fn set_theme_color(&mut self, color: Option<Hsla>, cx: &mut ViewContext<Self>) {
self.theme_color = color;
if let Some(color) = self.theme_color {
let theme = cx.global_mut::<Theme>();
theme.apply_color(color);
cx.refresh();
}
}
fn change_color_mode(&mut self, _: &ClickEvent, cx: &mut ViewContext<Self>) {
let mode = match cx.theme().mode.is_dark() {
true => ui::theme::ThemeMode::Light,
false => ui::theme::ThemeMode::Dark,
};
Theme::change(mode, cx);
self.set_theme_color(self.theme_color, cx);
}
fn save_layout(&mut self, dock_area: View<DockArea>, cx: &mut ViewContext<Self>) {
self._save_layout_task = Some(cx.spawn(|this, mut cx| async move {
Timer::after(Duration::from_secs(10)).await;
@ -336,14 +351,7 @@ impl Render for StoryWorkspace {
})
.small()
.ghost()
.on_click(move |_, cx| {
let mode = match cx.theme().mode.is_dark() {
true => ui::theme::ThemeMode::Light,
false => ui::theme::ThemeMode::Dark,
};
Theme::change(mode, cx);
}),
.on_click(cx.listener(Self::change_color_mode)),
)
.child(self.locale_selector.clone())
.child(

View file

@ -4,12 +4,11 @@ use gpui::{
};
use ui::{
button::{Button, ButtonStyled},
button_group::ButtonGroup,
checkbox::Checkbox,
dropdown::{Dropdown, DropdownEvent, DropdownItem, SearchableVec},
h_flex,
theme::ActiveTheme,
v_flex, FocusableCycle, IconName, Selectable, Sizable,
v_flex, FocusableCycle, IconName, Sizable,
};
actions!(dropdown_story, [Tab, TabPrev]);
@ -232,22 +231,12 @@ impl Render for DropdownStory {
.size_full()
.gap_4()
.child(
ButtonGroup::new("button-group")
.primary()
.small()
.on_click(cx.listener(|this, index: &Vec<usize>, cx| {
this.toggle_disabled(index.contains(&1), cx);
}))
.child(
Button::new("Enable")
.label("Enable")
.selected(!self.disabled),
)
.child(
Button::new("Disable")
.label("Disable")
.selected(self.disabled),
),
Checkbox::new("disable-dropdowns")
.label("Disabled")
.checked(self.disabled)
.on_click(cx.listener(|this, checked, cx| {
this.toggle_disabled(*checked, cx);
})),
)
.child(
h_flex()

View file

@ -74,6 +74,7 @@ pub trait Colorize {
fn invert_l(&self) -> Hsla;
fn lighten(&self, amount: f32) -> Hsla;
fn darken(&self, amount: f32) -> Hsla;
fn apply(&self, base_color: Hsla) -> Hsla;
}
impl Colorize for Hsla {
@ -128,6 +129,16 @@ impl Colorize for Hsla {
Hsla { l, ..*self }
}
/// Return a new color with the same lightness and alpha but different hue and saturation.
fn apply(&self, new_color: Hsla) -> Hsla {
Hsla {
h: new_color.h,
s: new_color.s,
l: self.l,
a: self.a,
}
}
}
#[derive(Debug, Clone, Copy)]
@ -331,6 +342,64 @@ impl Theme {
pub fn get_global(cx: &AppContext) -> &Self {
cx.global::<Self>()
}
pub fn apply_color(&mut self, mask_color: Hsla) {
self.title_bar_background = self.title_bar_background.apply(mask_color);
self.background = self.background.apply(mask_color);
self.foreground = self.foreground.apply(mask_color);
self.card = self.card.apply(mask_color);
self.card_foreground = self.card_foreground.apply(mask_color);
self.popover = self.popover.apply(mask_color);
self.popover_foreground = self.popover_foreground.apply(mask_color);
self.primary = self.primary.apply(mask_color);
self.primary_hover = self.primary_hover.apply(mask_color);
self.primary_active = self.primary_active.apply(mask_color);
self.primary_foreground = self.primary_foreground.apply(mask_color);
self.secondary = self.secondary.apply(mask_color);
self.secondary_hover = self.secondary_hover.apply(mask_color);
self.secondary_active = self.secondary_active.apply(mask_color);
self.secondary_foreground = self.secondary_foreground.apply(mask_color);
self.destructive = self.destructive.apply(mask_color);
self.destructive_hover = self.destructive_hover.apply(mask_color);
self.destructive_active = self.destructive_active.apply(mask_color);
self.destructive_foreground = self.destructive_foreground.apply(mask_color);
self.muted = self.muted.apply(mask_color);
self.muted_foreground = self.muted_foreground.apply(mask_color);
self.accent = self.accent.apply(mask_color);
self.accent_foreground = self.accent_foreground.apply(mask_color);
self.border = self.border.apply(mask_color);
self.input = self.input.apply(mask_color);
self.ring = self.ring.apply(mask_color);
self.selection = self.selection.apply(mask_color);
self.scrollbar = self.scrollbar.apply(mask_color);
self.scrollbar_thumb = self.scrollbar_thumb.apply(mask_color);
self.panel = self.panel.apply(mask_color);
self.drag_border = self.drag_border.apply(mask_color);
self.drop_target = self.drop_target.apply(mask_color);
self.tab_bar = self.tab_bar.apply(mask_color);
self.tab = self.tab.apply(mask_color);
self.tab_active = self.tab_active.apply(mask_color);
self.tab_foreground = self.tab_foreground.apply(mask_color);
self.tab_active_foreground = self.tab_active_foreground.apply(mask_color);
self.progress_bar = self.progress_bar.apply(mask_color);
self.slider_bar = self.slider_bar.apply(mask_color);
self.slider_thumb = self.slider_thumb.apply(mask_color);
self.list = self.list.apply(mask_color);
self.list_even = self.list_even.apply(mask_color);
self.list_head = self.list_head.apply(mask_color);
self.list_active = self.list_active.apply(mask_color);
self.list_hover = self.list_hover.apply(mask_color);
self.table = self.table.apply(mask_color);
self.table_even = self.table_even.apply(mask_color);
self.table_active = self.table_active.apply(mask_color);
self.table_hover = self.table_hover.apply(mask_color);
self.table_row_border = self.table_row_border.apply(mask_color);
self.table_head_foreground = self.table_head_foreground.apply(mask_color);
self.link = self.link.apply(mask_color);
self.link_hover = self.link_hover.apply(mask_color);
self.link_active = self.link_active.apply(mask_color);
self.skeleton = self.skeleton.apply(mask_color);
}
}
impl From<Colors> for Theme {