feat: add support for multi-themes (#210)

* chore: update deps

* wip

* add themes

* add matrix theme

* add flexoki and spaceduck themes

* .

* simple theme change function

* .

* respect shadow and radius settings

* add rose pine themes

* toggle theme
This commit is contained in:
reya
2025-12-26 08:20:18 +07:00
committed by GitHub
parent 5b7780ec9b
commit 34e026751b
49 changed files with 4349 additions and 2743 deletions

View File

@@ -309,7 +309,7 @@ impl DockItem {
pub(crate) fn focus_tab_panel(&self, window: &mut Window, cx: &mut App) {
if let DockItem::Tabs { view, .. } = self {
window.focus(&view.read(cx).focus_handle(cx));
window.focus(&view.read(cx).focus_handle(cx), cx);
}
}
}

View File

@@ -53,9 +53,9 @@ impl Render for DragPanel {
.justify_center()
.overflow_hidden()
.whitespace_nowrap()
.rounded(cx.theme().radius)
.rounded(cx.theme().radius_lg)
.text_xs()
.shadow_lg()
.when(cx.theme().shadow, |this| this.shadow_lg())
.bg(cx.theme().background)
.text_color(cx.theme().text_accent)
.child(self.panel.title(cx))
@@ -651,8 +651,8 @@ impl TabPanel {
.child(
div()
.size_full()
.rounded_xl()
.shadow_sm()
.rounded(cx.theme().radius_lg)
.when(cx.theme().shadow, |this| this.shadow_sm())
.when(cx.theme().mode.is_dark(), |this| this.shadow_lg())
.bg(cx.theme().panel_background)
.overflow_hidden()
@@ -671,7 +671,7 @@ impl TabPanel {
.p_1()
.child(
div()
.rounded_xl()
.rounded(cx.theme().radius_lg)
.border_1()
.border_color(cx.theme().element_disabled)
.bg(cx.theme().drop_target_background)
@@ -898,7 +898,7 @@ impl TabPanel {
fn focus_active_panel(&self, window: &mut Window, cx: &mut Context<Self>) {
if let Some(active_panel) = self.active_panel(cx) {
window.focus(&active_panel.focus_handle(cx));
window.focus(&active_panel.focus_handle(cx), cx);
}
}

View File

@@ -426,8 +426,8 @@ where
self.selected_value.as_ref()
}
pub fn focus(&self, window: &mut Window, _: &mut App) {
self.focus_handle.focus(window);
pub fn focus(&self, window: &mut Window, cx: &mut App) {
self.focus_handle.focus(window, cx);
}
fn on_blur(&mut self, window: &mut Window, cx: &mut Context<Self>) {
@@ -445,7 +445,7 @@ where
return;
}
self.list.focus_handle(cx).focus(window);
self.list.focus_handle(cx).focus(window, cx);
cx.propagate();
}
@@ -454,7 +454,7 @@ where
self.open = true;
}
self.list.focus_handle(cx).focus(window);
self.list.focus_handle(cx).focus(window, cx);
cx.propagate();
}
@@ -466,7 +466,7 @@ where
self.open = true;
cx.notify();
} else {
self.list.focus_handle(cx).focus(window);
self.list.focus_handle(cx).focus(window, cx);
}
}
@@ -475,7 +475,7 @@ where
self.open = !self.open;
if self.open {
self.list.focus_handle(cx).focus(window);
self.list.focus_handle(cx).focus(window, cx);
}
cx.notify();
}
@@ -708,7 +708,7 @@ where
.border_1()
.border_color(cx.theme().border)
.rounded(cx.theme().radius)
.shadow_sm()
.when(cx.theme().shadow, |this| this.shadow_sm())
.overflow_hidden()
.input_font_size(self.size)
.map(|this| match self.width {
@@ -793,7 +793,7 @@ where
.border_1()
.border_color(cx.theme().border)
.rounded(popup_radius)
.shadow_md()
.when(cx.theme().shadow, |this| this.shadow_md())
.child(state.list.clone()),
)
.on_mouse_down_out(window.listener_for(

View File

@@ -33,7 +33,7 @@ pub trait FocusableCycle {
.nth(1)
.unwrap_or(fallback_handle);
target_focus_handle.focus(window);
target_focus_handle.focus(window, cx);
cx.stop_propagation();
}
}

View File

@@ -859,7 +859,7 @@ impl Element for TextElement {
let p = point(input_bounds.origin.x, origin.y + offset_y);
for line in lines {
_ = line.paint(p, line_height, window, cx);
_ = line.paint(p, line_height, TextAlign::Left, None, window, cx);
offset_y += line_height;
}
}

View File

@@ -766,7 +766,7 @@ impl InputState {
/// Focus the input field.
pub fn focus(&self, window: &mut Window, cx: &mut Context<Self>) {
self.focus_handle.focus(window);
self.focus_handle.focus(window, cx);
self.blink_cursor.update(cx, |cursor, cx| {
cursor.start(cx);
});

View File

@@ -45,7 +45,6 @@ mod window_border;
/// This must be called before using any of the UI components.
/// You can initialize the UI module at your application's entry point.
pub fn init(cx: &mut gpui::App) {
theme::init(cx);
dropdown::init(cx);
input::init(cx);
list::init(cx);

View File

@@ -244,7 +244,7 @@ where
}
pub fn focus(&mut self, window: &mut Window, cx: &mut App) {
self.focus_handle(cx).focus(window);
self.focus_handle(cx).focus(window, cx);
}
/// Set the selected index of the list, this will also scroll to the selected item.

View File

@@ -145,7 +145,7 @@ impl AppMenu {
})
.with_menu_items(items, window, cx)
});
popup_menu.read(cx).focus_handle(cx).focus(window);
popup_menu.read(cx).focus_handle(cx).focus(window, cx);
self._subscription =
Some(cx.subscribe_in(&popup_menu, window, Self::handle_dismiss));
self.popup_menu = Some(popup_menu.clone());
@@ -157,7 +157,7 @@ impl AppMenu {
let focus_handle = popup_menu.read(cx).focus_handle(cx);
if !focus_handle.contains_focused(window, cx) {
focus_handle.focus(window);
focus_handle.focus(window, cx);
}
popup_menu

View File

@@ -156,7 +156,7 @@ impl Element for ContextMenu {
.when_some(menu_view, |this, menu| {
// Focus the menu, so that can be handle the action.
if !menu.focus_handle(cx).contains_focused(window, cx) {
menu.focus_handle(cx).focus(window);
menu.focus_handle(cx).focus(window, cx);
}
this.child(div().occlude().child(menu.clone()))

View File

@@ -659,7 +659,7 @@ impl PopupMenu {
cx: &mut Context<Self>,
) {
if let Some(context) = self.action_context.as_ref() {
context.focus(window);
context.focus(window, cx);
}
window.dispatch_action(action.boxed_clone(), cx);
@@ -759,7 +759,7 @@ impl PopupMenu {
// Focus the submenu, so that can be handle the action.
active_submenu.update(cx, |view, cx| {
view.set_selected_index(0, cx);
view.focus_handle.focus(window);
view.focus_handle.focus(window, cx);
});
cx.notify();
return true;
@@ -790,7 +790,7 @@ impl PopupMenu {
self.selected_index = None;
parent.update(cx, |view, cx| {
view.focus_handle.focus(window);
view.focus_handle.focus(window, cx);
cx.notify();
});
}
@@ -819,7 +819,7 @@ impl PopupMenu {
// Focus back to the previous focused handle.
if let Some(action_context) = self.action_context.as_ref() {
window.focus(action_context);
window.focus(action_context, cx);
}
let Some(parent_menu) = self.parent_menu.clone() else {

View File

@@ -342,7 +342,7 @@ impl RenderOnce for Modal {
});
let window_paddings = crate::window_border::window_paddings(window, cx);
let radius = (cx.theme().radius * 2.).min(px(20.));
let radius = (cx.theme().radius_lg * 2.).min(px(20.));
let view_size = window.viewport_size()
- gpui::size(
@@ -407,7 +407,7 @@ impl RenderOnce for Modal {
.border_1()
.border_color(cx.theme().border.alpha(0.4))
.rounded(radius)
.shadow_xl()
.when(cx.theme().shadow, |this| this.shadow_xl())
.min_h_24()
.key_context(CONTEXT)
.track_focus(&self.focus_handle)

View File

@@ -295,8 +295,8 @@ impl Render for Notification {
.border_1()
.border_color(cx.theme().border)
.bg(cx.theme().surface_background)
.rounded(cx.theme().radius * 1.6)
.shadow_md()
.rounded(cx.theme().radius_lg)
.when(cx.theme().shadow, |this| this.shadow_md())
.p_2()
.gap_3()
.justify_start()

View File

@@ -445,7 +445,7 @@ impl<M: ManagedView> Element for Popover<M> {
if let Some(previous_focus_handle) =
previous_focus_handle.as_ref()
{
window.focus(previous_focus_handle);
window.focus(previous_focus_handle, cx);
}
}
*old_content_view1.borrow_mut() = None;
@@ -455,7 +455,7 @@ impl<M: ManagedView> Element for Popover<M> {
)
.detach();
window.focus(&new_content_view.focus_handle(cx));
window.focus(&new_content_view.focus_handle(cx), cx);
*old_content_view.borrow_mut() = Some(new_content_view);
window.refresh();
}

View File

@@ -60,7 +60,7 @@ impl ContextModal for Window {
}
let focus_handle = cx.focus_handle();
focus_handle.focus(window);
focus_handle.focus(window, cx);
root.active_modals.push(ActiveModal {
focus_handle,
@@ -81,7 +81,7 @@ impl ContextModal for Window {
if let Some(top_modal) = root.active_modals.last() {
// Focus the next modal.
top_modal.focus_handle.focus(window);
top_modal.focus_handle.focus(window, cx);
} else {
// Restore focus if there are no more modals.
root.focus_back(window, cx);
@@ -188,9 +188,9 @@ impl Root {
.read(cx)
}
fn focus_back(&mut self, window: &mut Window, _: &mut App) {
fn focus_back(&mut self, window: &mut Window, cx: &mut App) {
if let Some(handle) = self.previous_focus_handle.clone() {
window.focus(&handle);
window.focus(&handle, cx);
}
}

View File

@@ -9,7 +9,6 @@ use gpui::{
IntoElement, LayoutId, MouseDownEvent, MouseMoveEvent, MouseUpEvent, PaintQuad, Pixels, Point,
Position, ScrollHandle, ScrollWheelEvent, Size, UniformListScrollHandle, Window,
};
use theme::scrollbar_mode::ScrollBarMode;
use theme::ActiveTheme;
use crate::AxisExt;
@@ -355,9 +354,10 @@ impl Scrollbar {
}
fn style_for_idle(cx: &App) -> (Hsla, Hsla, Hsla, Pixels, Pixels, Pixels) {
let (width, inset, radius) = match cx.theme().scrollbar_mode {
ScrollBarMode::Scrolling => (THUMB_WIDTH, THUMB_INSET, THUMB_RADIUS),
_ => (THUMB_ACTIVE_WIDTH, THUMB_ACTIVE_INSET, THUMB_ACTIVE_RADIUS),
let (width, inset, radius) = if cx.theme().scrollbar_mode.is_scrolling() {
(THUMB_WIDTH, THUMB_INSET, THUMB_RADIUS)
} else {
(THUMB_ACTIVE_WIDTH, THUMB_ACTIVE_INSET, THUMB_ACTIVE_RADIUS)
};
(

View File

@@ -78,7 +78,7 @@ pub trait StyledExt: Styled + Sized {
.border_1()
.border_color(cx.theme().border)
.shadow_lg()
.rounded_lg()
.rounded(cx.theme().radius_lg)
}
}

View File

@@ -181,7 +181,7 @@ impl Element for Switch {
// Switch Toggle
div()
.rounded_full()
.shadow_sm()
.when(cx.theme().shadow, |this| this.shadow_sm())
.bg(toggle_bg)
.size(bar_width)
.map(|this| {

View File

@@ -115,7 +115,7 @@ impl RenderOnce for Tab {
.text_ellipsis()
.text_color(text_color)
.bg(bg_color)
.rounded(cx.theme().radius)
.rounded(cx.theme().radius_lg)
.hover(|this| this.bg(hover_bg_color))
.when_some(self.prefix, |this, prefix| {
this.child(prefix).text_color(text_color)

View File

@@ -1,3 +1,4 @@
use gpui::prelude::FluentBuilder;
use gpui::{
div, relative, App, AppContext, Context, Entity, IntoElement, ParentElement, Render,
SharedString, Styled, Window,
@@ -24,8 +25,8 @@ impl Render for Tooltip {
.border_1()
.border_color(cx.theme().border)
.bg(cx.theme().surface_background)
.shadow_md()
.rounded_lg()
.when(cx.theme().shadow, |this| this.shadow_md())
.rounded(cx.theme().radius_lg)
.text_sm()
.text_color(cx.theme().text_muted)
.line_height(relative(1.25))