From 6c4de54fa49553e76e009831f2df39a07fe3c8fe Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Mon, 17 Nov 2025 11:18:17 +0800 Subject: [PATCH] theme: Use `font_family` for all elements. (#1618) Close #1613 - And add `mono_font_family` and `mono_font_size` for code editor. --- .theme-schema.json | 19 ++++++++++++---- crates/story/examples/editor.rs | 4 ++-- crates/story/examples/html.rs | 5 +++-- crates/story/examples/markdown.rs | 5 +++-- crates/story/examples/tiles.rs | 2 +- crates/ui/src/input/popovers/context_menu.rs | 6 ++--- crates/ui/src/input/search.rs | 2 +- crates/ui/src/inspector.rs | 10 ++++----- crates/ui/src/root.rs | 5 ++--- crates/ui/src/text/node.rs | 4 ++-- crates/ui/src/theme/mod.rs | 23 ++++++++++++++++---- crates/ui/src/theme/schema.rs | 19 ++++++++++++++++ crates/ui/src/tooltip.rs | 2 +- 13 files changed, 76 insertions(+), 30 deletions(-) diff --git a/.theme-schema.json b/.theme-schema.json index fab8a069..a85b08f9 100644 --- a/.theme-schema.json +++ b/.theme-schema.json @@ -23,27 +23,38 @@ "description": "The base font size, default is 16.", "type": ["number", "null"], "format": "float", - "default": null + "default": 16 }, "font.family": { "description": "The base font family, default is system font: `.SystemUIFont`.", "type": ["string", "null"], + "default": ".SystemUIFont" + }, + "mono_font.size": { + "description": "The base monospace font size, default is 13.", + "type": ["number", "null"], + "format": "float", + "default": 13 + }, + "mono_font.family": { + "description": "The monospace font family, default is platform specific:\nmacOS: `Menlo`\n- Windows: `Consolas`\n- Linux: `DejaVu Sans Mono`", + "type": ["string", "null"], "default": null }, "radius": { "description": "The border radius for general elements, default is 6.", "type": ["number", "null"], - "default": null + "default": 6 }, "radius.lg": { "description": "The border radius for large elements like Dialogs and Notifications, default is 8.", "type": ["number", "null"], - "default": null + "default": 8 }, "shadow": { "description": "Set shadows in the theme, for example the Input and Button, default is true.", "type": ["boolean", "null"], - "default": null + "default": true }, "themes": { "type": "array", diff --git a/crates/story/examples/editor.rs b/crates/story/examples/editor.rs index 5da3221e..924a210b 100644 --- a/crates/story/examples/editor.rs +++ b/crates/story/examples/editor.rs @@ -1003,8 +1003,8 @@ impl Render for Example { .bordered(false) .p_0() .h_full() - .font_family("Monaco") - .text_size(px(12.)) + .font_family(cx.theme().mono_font_family.clone()) + .text_size(cx.theme().mono_font_size) .focus_bordered(false) .into_any_element(), ), diff --git a/crates/story/examples/html.rs b/crates/story/examples/html.rs index 132cfa8f..a571f9d0 100644 --- a/crates/story/examples/html.rs +++ b/crates/story/examples/html.rs @@ -1,5 +1,6 @@ use gpui::*; use gpui_component::{ + ActiveTheme as _, highlighter::Language, input::{Input, InputState, TabSize}, resizable::h_resizable, @@ -52,8 +53,8 @@ impl Render for Example { div() .id("source") .size_full() - .font_family("Menlo") - .text_size(px(13.)) + .font_family(cx.theme().mono_font_family.clone()) + .text_size(cx.theme().mono_font_size) .child( Input::new(&self.input_state) .h_full() diff --git a/crates/story/examples/markdown.rs b/crates/story/examples/markdown.rs index cb3a9713..76adbb18 100644 --- a/crates/story/examples/markdown.rs +++ b/crates/story/examples/markdown.rs @@ -1,5 +1,6 @@ use gpui::*; use gpui_component::{ + ActiveTheme as _, highlighter::Language, input::{Input, InputEvent, InputState, TabSize}, resizable::{h_resizable, resizable_panel}, @@ -83,8 +84,8 @@ impl Render for Example { div() .id("source") .size_full() - .font_family("Monaco") - .text_size(px(12.)) + .font_family(cx.theme().mono_font_family.clone()) + .text_size(cx.theme().mono_font_size) .child( Input::new(&self.input_state) .h_full() diff --git a/crates/story/examples/tiles.rs b/crates/story/examples/tiles.rs index 6afb36e5..4088e3e1 100644 --- a/crates/story/examples/tiles.rs +++ b/crates/story/examples/tiles.rs @@ -416,7 +416,7 @@ impl Render for StoryTiles { let notification_layer = Root::render_notification_layer(window, cx); div() - .font_family(".SystemUIFont") + .font_family(cx.theme().font_family.clone()) .relative() .size_full() .flex() diff --git a/crates/ui/src/input/popovers/context_menu.rs b/crates/ui/src/input/popovers/context_menu.rs index 7d05a41d..72ce7fa5 100644 --- a/crates/ui/src/input/popovers/context_menu.rs +++ b/crates/ui/src/input/popovers/context_menu.rs @@ -8,6 +8,7 @@ use rust_i18n::t; use crate::{ input::{self, popovers::ContextMenu, InputState}, menu::PopupMenu, + ActiveTheme as _, }; /// Context menu for mouse right clicks. @@ -125,7 +126,7 @@ impl MouseContextMenu { } impl Render for MouseContextMenu { - fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { + fn render(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { if !self.open { return div().into_any_element(); } @@ -137,8 +138,7 @@ impl Render for MouseContextMenu { .position(self.mouse_position) .child( div() - .font_family(".SystemUIFont") - .text_size(px(14.)) + .font_family(cx.theme().font_family.clone()) .cursor_default() .child(self.menu.clone()), ), diff --git a/crates/ui/src/input/search.rs b/crates/ui/src/input/search.rs index c11072fb..11ada752 100644 --- a/crates/ui/src/input/search.rs +++ b/crates/ui/src/input/search.rs @@ -414,7 +414,7 @@ impl Render for SearchPanel { .on_action(cx.listener(Self::on_action_next)) .on_action(cx.listener(Self::on_action_escape)) .on_action(cx.listener(Self::on_action_tab)) - .font_family(".SystemUIFont") + .font_family(cx.theme().font_family.clone()) .items_center() .py_2() .px_3() diff --git a/crates/ui/src/inspector.rs b/crates/ui/src/inspector.rs index a3831872..e12f27bf 100644 --- a/crates/ui/src/inspector.rs +++ b/crates/ui/src/inspector.rs @@ -434,8 +434,8 @@ impl Render for DivInspector { v_flex() .flex_1() .gap_y_1() - .font_family("Monaco") - .text_size(px(12.)) + .font_family(cx.theme().mono_font_family.clone()) + .text_size(cx.theme().mono_font_size) .child(Input::new(&self.rust_state.state).h_full()) .when_some(self.rust_state.error.clone(), |this, err| { this.child(Alert::error("rust-error", err).text_xs()) @@ -462,8 +462,8 @@ impl Render for DivInspector { v_flex() .flex_1() .gap_y_1() - .font_family("Monaco") - .text_size(px(12.)) + .font_family(cx.theme().mono_font_family.clone()) + .text_size(cx.theme().mono_font_size) .child(Input::new(&self.json_state.state).h_full()) .when_some(self.json_state.error.clone(), |this, err| { this.child(Alert::error("json-error", err).text_xs()) @@ -487,7 +487,7 @@ fn render_inspector( v_flex() .id("inspector") - .font_family(".SystemUIFont") + .font_family(cx.theme().font_family.clone()) .size_full() .bg(cx.theme().background) .border_l_1() diff --git a/crates/ui/src/root.rs b/crates/ui/src/root.rs index 015eca2f..fe48213d 100644 --- a/crates/ui/src/root.rs +++ b/crates/ui/src/root.rs @@ -388,8 +388,7 @@ impl Root { impl Render for Root { fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { - let base_font_size = cx.theme().font_size; - window.set_rem_size(base_font_size); + window.set_rem_size(cx.theme().font_size); window_border().child( div() @@ -399,7 +398,7 @@ impl Render for Root { .on_action(cx.listener(Self::on_action_tab_prev)) .relative() .size_full() - .font_family(".SystemUIFont") + .font_family(cx.theme().font_family.clone()) .bg(cx.theme().background) .text_color(cx.theme().foreground) .child(self.view.clone()), diff --git a/crates/ui/src/text/node.rs b/crates/ui/src/text/node.rs index b37d750c..b4bd81a2 100644 --- a/crates/ui/src/text/node.rs +++ b/crates/ui/src/text/node.rs @@ -371,8 +371,8 @@ impl CodeBlock { .p_3() .rounded(cx.theme().radius) .bg(cx.theme().secondary.opacity(0.85)) - .font_family("Menlo, Monaco, Consolas, monospace") - .text_size(rems(0.875)) + .font_family(cx.theme().mono_font_family.clone()) + .text_size(cx.theme().mono_font_size) .relative() .refine_style(&style.code_block) .child(Inline::new( diff --git a/crates/ui/src/theme/mod.rs b/crates/ui/src/theme/mod.rs index cf2fe448..2b96572e 100644 --- a/crates/ui/src/theme/mod.rs +++ b/crates/ui/src/theme/mod.rs @@ -45,8 +45,20 @@ pub struct Theme { pub dark_theme: Rc, pub mode: ThemeMode, + /// The font family for the application, default is `.SystemUIFont`. pub font_family: SharedString, + /// The base font size for the application, default is 16px. pub font_size: Pixels, + /// The monospace font family for the application. + /// + /// Defaults to: + /// + /// - macOS: `Menlo` + /// - Windows: `Consolas` + /// - Linux: `DejaVu Sans Mono` + pub mono_font_family: SharedString, + /// The monospace font size for the application, default is 13px. + pub mono_font_size: Pixels, /// Radius for the general elements. pub radius: Pixels, /// Radius for the large elements, e.g.: Dialog, Notification border radius. @@ -172,14 +184,17 @@ impl From<&ThemeColor> for Theme { Theme { mode: ThemeMode::default(), transparent: Hsla::transparent_black(), + font_family: ".SystemUIFont".into(), font_size: px(16.), - font_family: if cfg!(target_os = "macos") { - ".SystemUIFont".into() + mono_font_family: if cfg!(target_os = "macos") { + // https://en.wikipedia.org/wiki/Menlo_(typeface) + "Menlo".into() } else if cfg!(target_os = "windows") { - "Segoe UI".into() + "Consolas".into() } else { - "FreeMono".into() + "DejaVu Sans Mono".into() }, + mono_font_size: px(13.), radius: px(6.), radius_lg: px(8.), shadow: true, diff --git a/crates/ui/src/theme/schema.rs b/crates/ui/src/theme/schema.rs index 0b920757..48d613ca 100644 --- a/crates/ui/src/theme/schema.rs +++ b/crates/ui/src/theme/schema.rs @@ -41,6 +41,15 @@ pub struct ThemeConfig { /// The base font family, default is system font: `.SystemUIFont`. #[serde(rename = "font.family")] pub font_family: Option, + /// The monospace font family, default is platform specific: + /// - macOS: `Menlo` + /// - Windows: `Consolas` + /// - Linux: `DejaVu Sans Mono` + #[serde(rename = "mono_font.family")] + pub mono_font_family: Option, + /// The monospace font size, default is 13. + #[serde(rename = "mono_font.size")] + pub mono_font_size: Option, /// The border radius for general elements, default is 6. #[serde(rename = "radius")] @@ -652,6 +661,16 @@ impl Theme { } else { self.font_family = default_theme.font_family.clone(); } + if let Some(mono_font_family) = &config.mono_font_family { + self.mono_font_family = mono_font_family.clone(); + } else { + self.mono_font_family = default_theme.mono_font_family.clone(); + } + if let Some(mono_font_size) = config.mono_font_size { + self.mono_font_size = px(mono_font_size); + } else { + self.mono_font_size = default_theme.mono_font_size; + } if let Some(radius) = config.radius { self.radius = px(radius as f32); } else { diff --git a/crates/ui/src/tooltip.rs b/crates/ui/src/tooltip.rs index a9b8fd6d..352f8b5b 100644 --- a/crates/ui/src/tooltip.rs +++ b/crates/ui/src/tooltip.rs @@ -89,7 +89,7 @@ impl Render for Tooltip { div().child( // Wrap in a child, to ensure the left margin is applied to the tooltip h_flex() - .font_family(".SystemUIFont") + .font_family(cx.theme().font_family.clone()) .m_3() .bg(cx.theme().popover) .text_color(cx.theme().popover_foreground)