Cargo.lock 🔗
@@ -18840,7 +18840,6 @@ dependencies = [
"story",
"strum 0.27.2",
"theme",
- "theme_settings",
"ui_macros",
"windows 0.61.3",
]
Piotr Osiewicz and Zed Zippy created
This will allow us to use UI crate on the web
Self-Review Checklist:
- [ ] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [ ] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [ ] Tests cover the new/changed behavior
- [ ] Performance impact has been considered and is acceptable
Closes #ISSUE
Release Notes:
- N/A
---------
Co-authored-by: Zed Zippy <234243425+zed-zippy[bot]@users.noreply.github.com>
Cargo.lock | 1
crates/theme/src/theme.rs | 4 +
crates/theme/src/theme_settings_provider.rs | 43 +++++++++++
crates/theme/src/ui_density.rs | 65 +++++++++++++++++
crates/theme_settings/src/settings.rs | 80 +--------------------
crates/theme_settings/src/theme_settings.rs | 42 +++++++++--
crates/ui/Cargo.toml | 1
crates/ui/src/components/context_menu.rs | 4
crates/ui/src/components/label/label_like.rs | 14 ---
crates/ui/src/components/list/list_header.rs | 7 -
crates/ui/src/components/tooltip.rs | 7 -
crates/ui/src/styles/spacing.rs | 5
crates/ui/src/styles/typography.rs | 26 +++---
crates/ui_macros/src/dynamic_spacing.rs | 18 ++--
14 files changed, 185 insertions(+), 132 deletions(-)
@@ -18840,7 +18840,6 @@ dependencies = [
"story",
"strum 0.27.2",
"theme",
- "theme_settings",
"ui_macros",
"windows 0.61.3",
]
@@ -17,6 +17,8 @@ mod registry;
mod scale;
mod schema;
mod styles;
+mod theme_settings_provider;
+mod ui_density;
use std::sync::Arc;
@@ -37,6 +39,8 @@ pub use crate::registry::*;
pub use crate::scale::*;
pub use crate::schema::*;
pub use crate::styles::*;
+pub use crate::theme_settings_provider::*;
+pub use crate::ui_density::*;
/// The name of the default dark theme.
pub const DEFAULT_DARK_THEME: &str = "One Dark";
@@ -0,0 +1,43 @@
+use gpui::{App, Font, Global, Pixels};
+
+use crate::UiDensity;
+
+/// Trait for providing theme-related settings (fonts, font sizes, UI density)
+/// without coupling to the concrete settings infrastructure.
+///
+/// A concrete implementation is registered as a global by the `theme_settings` crate.
+pub trait ThemeSettingsProvider: Send + Sync + 'static {
+ /// Returns the font used for UI elements.
+ fn ui_font<'a>(&'a self, cx: &'a App) -> &'a Font;
+
+ /// Returns the font used for buffers and the terminal.
+ fn buffer_font<'a>(&'a self, cx: &'a App) -> &'a Font;
+
+ /// Returns the UI font size in pixels.
+ fn ui_font_size(&self, cx: &App) -> Pixels;
+
+ /// Returns the buffer font size in pixels.
+ fn buffer_font_size(&self, cx: &App) -> Pixels;
+
+ /// Returns the current UI density setting.
+ fn ui_density(&self, cx: &App) -> UiDensity;
+}
+
+struct GlobalThemeSettingsProvider(Box<dyn ThemeSettingsProvider>);
+
+impl Global for GlobalThemeSettingsProvider {}
+
+/// Registers the global [`ThemeSettingsProvider`] implementation.
+///
+/// This should be called during application initialization by the crate
+/// that owns the concrete theme settings (e.g. `theme_settings`).
+pub fn set_theme_settings_provider(provider: Box<dyn ThemeSettingsProvider>, cx: &mut App) {
+ cx.set_global(GlobalThemeSettingsProvider(provider));
+}
+
+/// Returns the global [`ThemeSettingsProvider`].
+///
+/// Panics if no provider has been registered via [`set_theme_settings_provider`].
+pub fn theme_settings(cx: &App) -> &dyn ThemeSettingsProvider {
+ &*cx.global::<GlobalThemeSettingsProvider>().0
+}
@@ -0,0 +1,65 @@
+use schemars::JsonSchema;
+use serde::{Deserialize, Serialize};
+
+/// Specifies the density of the UI.
+/// Note: This setting is still experimental. See [this tracking issue](https://github.com/zed-industries/zed/issues/18078)
+#[derive(
+ Debug,
+ Default,
+ PartialEq,
+ Eq,
+ PartialOrd,
+ Ord,
+ Hash,
+ Clone,
+ Copy,
+ Serialize,
+ Deserialize,
+ JsonSchema,
+)]
+#[serde(rename_all = "snake_case")]
+pub enum UiDensity {
+ /// A denser UI with tighter spacing and smaller elements.
+ #[serde(alias = "compact")]
+ Compact,
+ #[default]
+ #[serde(alias = "default")]
+ /// The default UI density.
+ Default,
+ #[serde(alias = "comfortable")]
+ /// A looser UI with more spacing and larger elements.
+ Comfortable,
+}
+
+impl UiDensity {
+ /// The spacing ratio of a given density.
+ /// TODO: Standardize usage throughout the app or remove
+ pub fn spacing_ratio(self) -> f32 {
+ match self {
+ UiDensity::Compact => 0.75,
+ UiDensity::Default => 1.0,
+ UiDensity::Comfortable => 1.25,
+ }
+ }
+}
+
+impl From<String> for UiDensity {
+ fn from(s: String) -> Self {
+ match s.as_str() {
+ "compact" => Self::Compact,
+ "default" => Self::Default,
+ "comfortable" => Self::Comfortable,
+ _ => Self::default(),
+ }
+ }
+}
+
+impl From<UiDensity> for String {
+ fn from(val: UiDensity) -> Self {
+ match val {
+ UiDensity::Compact => "compact".to_string(),
+ UiDensity::Default => "default".to_string(),
+ UiDensity::Comfortable => "comfortable".to_string(),
+ }
+ }
+}
@@ -12,83 +12,17 @@ use serde::{Deserialize, Serialize};
pub use settings::{FontFamilyName, IconThemeName, ThemeAppearanceMode, ThemeName};
use settings::{IntoGpui, RegisterSetting, Settings, SettingsContent};
use std::sync::Arc;
-use theme::{Appearance, DEFAULT_ICON_THEME_NAME, SyntaxTheme, Theme};
+use theme::{Appearance, DEFAULT_ICON_THEME_NAME, SyntaxTheme, Theme, UiDensity};
const MIN_FONT_SIZE: Pixels = px(6.0);
const MAX_FONT_SIZE: Pixels = px(100.0);
const MIN_LINE_HEIGHT: f32 = 1.0;
-#[derive(
- Debug,
- Default,
- PartialEq,
- Eq,
- PartialOrd,
- Ord,
- Hash,
- Clone,
- Copy,
- Serialize,
- Deserialize,
- JsonSchema,
-)]
-
-/// Specifies the density of the UI.
-/// Note: This setting is still experimental. See [this tracking issue](https://github.com/zed-industries/zed/issues/18078)
-#[serde(rename_all = "snake_case")]
-pub enum UiDensity {
- /// A denser UI with tighter spacing and smaller elements.
- #[serde(alias = "compact")]
- Compact,
- #[default]
- #[serde(alias = "default")]
- /// The default UI density.
- Default,
- #[serde(alias = "comfortable")]
- /// A looser UI with more spacing and larger elements.
- Comfortable,
-}
-
-impl UiDensity {
- /// The spacing ratio of a given density.
- /// TODO: Standardize usage throughout the app or remove
- pub fn spacing_ratio(self) -> f32 {
- match self {
- UiDensity::Compact => 0.75,
- UiDensity::Default => 1.0,
- UiDensity::Comfortable => 1.25,
- }
- }
-}
-
-impl From<String> for UiDensity {
- fn from(s: String) -> Self {
- match s.as_str() {
- "compact" => Self::Compact,
- "default" => Self::Default,
- "comfortable" => Self::Comfortable,
- _ => Self::default(),
- }
- }
-}
-
-impl From<UiDensity> for String {
- fn from(val: UiDensity) -> Self {
- match val {
- UiDensity::Compact => "compact".to_string(),
- UiDensity::Default => "default".to_string(),
- UiDensity::Comfortable => "comfortable".to_string(),
- }
- }
-}
-
-impl From<settings::UiDensity> for UiDensity {
- fn from(val: settings::UiDensity) -> Self {
- match val {
- settings::UiDensity::Compact => Self::Compact,
- settings::UiDensity::Default => Self::Default,
- settings::UiDensity::Comfortable => Self::Comfortable,
- }
+pub(crate) fn ui_density_from_settings(val: settings::UiDensity) -> UiDensity {
+ match val {
+ settings::UiDensity::Compact => UiDensity::Compact,
+ settings::UiDensity::Default => UiDensity::Default,
+ settings::UiDensity::Comfortable => UiDensity::Comfortable,
}
}
@@ -693,7 +627,7 @@ impl settings::Settings for ThemeSettings {
experimental_theme_overrides: content.experimental_theme_overrides.clone(),
theme_overrides: content.theme_overrides.clone(),
icon_theme: icon_theme_selection,
- ui_density: content.ui_density.unwrap_or_default().into(),
+ ui_density: ui_density_from_settings(content.ui_density.unwrap_or_default()),
unnecessary_code_fade: content.unnecessary_code_fade.unwrap().0.clamp(0.0, 0.9),
}
}
@@ -12,13 +12,13 @@ use std::sync::Arc;
use ::settings::{IntoGpui, Settings, SettingsStore};
use anyhow::{Context as _, Result};
-use gpui::{App, HighlightStyle, Refineable};
+use gpui::{App, Font, HighlightStyle, Pixels, Refineable};
use gpui_util::ResultExt;
use theme::{
AccentColors, Appearance, AppearanceContent, DEFAULT_DARK_THEME, DEFAULT_ICON_THEME_NAME,
GlobalTheme, LoadThemes, PlayerColor, PlayerColors, StatusColors, SyntaxTheme,
- SystemAppearance, SystemColors, Theme, ThemeColors, ThemeFamily, ThemeRegistry, ThemeStyles,
- default_color_scales, try_parse_color,
+ SystemAppearance, SystemColors, Theme, ThemeColors, ThemeFamily, ThemeRegistry,
+ ThemeSettingsProvider, ThemeStyles, default_color_scales, try_parse_color,
};
pub use crate::schema::{
@@ -28,12 +28,37 @@ pub use crate::schema::{
};
pub use crate::settings::{
AgentFontSize, BufferLineHeight, FontFamilyName, IconThemeName, IconThemeSelection,
- ThemeAppearanceMode, ThemeName, ThemeSelection, ThemeSettings, UiDensity,
- adjust_agent_buffer_font_size, adjust_agent_ui_font_size, adjust_buffer_font_size,
- adjust_ui_font_size, adjusted_font_size, appearance_to_mode, clamp_font_size, default_theme,
- observe_buffer_font_size_adjustment, reset_agent_buffer_font_size, reset_agent_ui_font_size,
- reset_buffer_font_size, reset_ui_font_size, set_icon_theme, set_mode, set_theme, setup_ui_font,
+ ThemeAppearanceMode, ThemeName, ThemeSelection, ThemeSettings, adjust_agent_buffer_font_size,
+ adjust_agent_ui_font_size, adjust_buffer_font_size, adjust_ui_font_size, adjusted_font_size,
+ appearance_to_mode, clamp_font_size, default_theme, observe_buffer_font_size_adjustment,
+ reset_agent_buffer_font_size, reset_agent_ui_font_size, reset_buffer_font_size,
+ reset_ui_font_size, set_icon_theme, set_mode, set_theme, setup_ui_font,
};
+pub use theme::UiDensity;
+
+struct ThemeSettingsProviderImpl;
+
+impl ThemeSettingsProvider for ThemeSettingsProviderImpl {
+ fn ui_font<'a>(&'a self, cx: &'a App) -> &'a Font {
+ &ThemeSettings::get_global(cx).ui_font
+ }
+
+ fn buffer_font<'a>(&'a self, cx: &'a App) -> &'a Font {
+ &ThemeSettings::get_global(cx).buffer_font
+ }
+
+ fn ui_font_size(&self, cx: &App) -> Pixels {
+ ThemeSettings::get_global(cx).ui_font_size(cx)
+ }
+
+ fn buffer_font_size(&self, cx: &App) -> Pixels {
+ ThemeSettings::get_global(cx).buffer_font_size(cx)
+ }
+
+ fn ui_density(&self, cx: &App) -> UiDensity {
+ ThemeSettings::get_global(cx).ui_density
+ }
+}
/// Initialize the theme system with settings integration.
///
@@ -43,6 +68,7 @@ pub fn init(themes_to_load: LoadThemes, cx: &mut App) {
let load_user_themes = matches!(&themes_to_load, LoadThemes::All(_));
theme::init(themes_to_load, cx);
+ theme::set_theme_settings_provider(Box::new(ThemeSettingsProviderImpl), cx);
if load_user_themes {
let registry = ThemeRegistry::global(cx);
@@ -28,7 +28,6 @@ smallvec.workspace = true
story = { workspace = true, optional = true }
strum.workspace = true
theme.workspace = true
-theme_settings.workspace = true
ui_macros.workspace = true
gpui_util.workspace = true
@@ -8,14 +8,12 @@ use gpui::{
Subscription, anchored, canvas, prelude::*, px,
};
use menu::{SelectChild, SelectFirst, SelectLast, SelectNext, SelectParent, SelectPrevious};
-use settings::Settings;
use std::{
cell::{Cell, RefCell},
collections::HashMap,
rc::Rc,
time::{Duration, Instant},
};
-use theme_settings::ThemeSettings;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum SubmenuOpenTrigger {
@@ -2050,7 +2048,7 @@ impl ContextMenuItem {
impl Render for ContextMenu {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
- let ui_font_size = ThemeSettings::get_global(cx).ui_font_size(cx);
+ let ui_font_size = theme::theme_settings(cx).ui_font_size(cx);
let window_size = window.viewport_size();
let rem_size = window.rem_size();
let is_wide_window = window_size.width / rem_size > rems_from_px(800.).0;
@@ -1,8 +1,6 @@
use crate::prelude::*;
use gpui::{FontWeight, Rems, StyleRefinement, UnderlineStyle};
-use settings::Settings;
use smallvec::SmallVec;
-use theme_settings::ThemeSettings;
/// Sets the size of a label
#[derive(Debug, PartialEq, Clone, Copy, Default)]
@@ -191,9 +189,7 @@ impl LabelCommon for LabelLike {
}
fn buffer_font(mut self, cx: &App) -> Self {
- let font = theme_settings::ThemeSettings::get_global(cx)
- .buffer_font
- .clone();
+ let font = theme::theme_settings(cx).buffer_font(cx).clone();
self.weight = Some(font.weight);
self.base = self.base.font(font);
self
@@ -202,11 +198,7 @@ impl LabelCommon for LabelLike {
fn inline_code(mut self, cx: &App) -> Self {
self.base = self
.base
- .font(
- theme_settings::ThemeSettings::get_global(cx)
- .buffer_font
- .clone(),
- )
+ .font(theme::theme_settings(cx).buffer_font(cx).clone())
.bg(cx.theme().colors().element_background)
.rounded_sm()
.px_0p5();
@@ -264,7 +256,7 @@ impl RenderOnce for LabelLike {
.text_color(color)
.font_weight(
self.weight
- .unwrap_or(ThemeSettings::get_global(cx).ui_font.weight),
+ .unwrap_or(theme::theme_settings(cx).ui_font(cx).weight),
)
.children(self.children)
}
@@ -3,8 +3,7 @@ use std::sync::Arc;
use crate::{Disclosure, prelude::*};
use component::{Component, ComponentScope, example_group_with_title, single_example};
use gpui::{AnyElement, ClickEvent};
-use settings::Settings;
-use theme_settings::ThemeSettings;
+use theme::UiDensity;
#[derive(IntoElement, RegisterComponent)]
pub struct ListHeader {
@@ -81,7 +80,7 @@ impl Toggleable for ListHeader {
impl RenderOnce for ListHeader {
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
- let ui_density = ThemeSettings::get_global(cx).ui_density;
+ let ui_density = theme::theme_settings(cx).ui_density(cx);
h_flex()
.id(self.label.clone())
@@ -91,7 +90,7 @@ impl RenderOnce for ListHeader {
.child(
div()
.map(|this| match ui_density {
- theme_settings::UiDensity::Comfortable => this.h_5(),
+ UiDensity::Comfortable => this.h_5(),
_ => this.h_7(),
})
.when(self.inset, |this| this.px_2())
@@ -1,12 +1,9 @@
use std::borrow::Borrow;
use std::rc::Rc;
-use gpui::{Action, AnyElement, AnyView, AppContext, FocusHandle, IntoElement, Render};
-use settings::Settings;
-use theme_settings::ThemeSettings;
-
use crate::prelude::*;
use crate::{Color, KeyBinding, Label, LabelSize, StyledExt, h_flex, v_flex};
+use gpui::{Action, AnyElement, AnyView, AppContext, FocusHandle, IntoElement, Render};
#[derive(RegisterComponent)]
pub struct Tooltip {
@@ -221,7 +218,7 @@ where
C: AppContext + Borrow<App>,
{
let app = (*cx).borrow();
- let ui_font = ThemeSettings::get_global(app).ui_font.clone();
+ let ui_font = theme::theme_settings(app).ui_font(app).clone();
// padding to avoid tooltip appearing right below the mouse cursor
div().pl_2().pt_2p5().child(
@@ -1,6 +1,5 @@
use gpui::{App, Pixels, Rems, px, rems};
-use settings::Settings;
-use theme_settings::{ThemeSettings, UiDensity};
+use theme::UiDensity;
use ui_macros::derive_dynamic_spacing;
// Derives [DynamicSpacing]. See [ui_macros::derive_dynamic_spacing].
@@ -51,5 +50,5 @@ derive_dynamic_spacing![
///
/// Always use [DynamicSpacing] for spacing values.
pub fn ui_density(cx: &mut App) -> UiDensity {
- ThemeSettings::get_global(cx).ui_density
+ theme::theme_settings(cx).ui_density(cx)
}
@@ -3,9 +3,7 @@ use gpui::{
AnyElement, App, IntoElement, ParentElement, Rems, RenderOnce, SharedString, Styled, Window,
div, rems,
};
-use settings::Settings;
use theme::ActiveTheme;
-use theme_settings::ThemeSettings;
use crate::{Color, rems_from_px};
@@ -13,16 +11,16 @@ use crate::{Color, rems_from_px};
pub trait StyledTypography: Styled + Sized {
/// Sets the font family to the buffer font.
fn font_buffer(self, cx: &App) -> Self {
- let settings = ThemeSettings::get_global(cx);
- let buffer_font_family = settings.buffer_font.family.clone();
+ let settings = theme::theme_settings(cx);
+ let buffer_font_family = settings.buffer_font(cx).family.clone();
self.font_family(buffer_font_family)
}
/// Sets the font family to the UI font.
fn font_ui(self, cx: &App) -> Self {
- let settings = ThemeSettings::get_global(cx);
- let ui_font_family = settings.ui_font.family.clone();
+ let settings = theme::theme_settings(cx);
+ let ui_font_family = settings.ui_font(cx).family.clone();
self.font_family(ui_font_family)
}
@@ -83,7 +81,7 @@ pub trait StyledTypography: Styled + Sized {
/// This should only be used for text that is displayed in a buffer,
/// or other places that text needs to match the user's buffer font size.
fn text_buffer(self, cx: &App) -> Self {
- let settings = ThemeSettings::get_global(cx);
+ let settings = theme::theme_settings(cx);
self.text_size(settings.buffer_font_size(cx))
}
}
@@ -134,28 +132,28 @@ pub enum TextSize {
impl TextSize {
/// Returns the text size in rems.
pub fn rems(self, cx: &App) -> Rems {
- let theme_settings = ThemeSettings::get_global(cx);
+ let settings = theme::theme_settings(cx);
match self {
Self::Large => rems_from_px(16.),
Self::Default => rems_from_px(14.),
Self::Small => rems_from_px(12.),
Self::XSmall => rems_from_px(10.),
- Self::Ui => rems_from_px(theme_settings.ui_font_size(cx)),
- Self::Editor => rems_from_px(theme_settings.buffer_font_size(cx)),
+ Self::Ui => rems_from_px(settings.ui_font_size(cx)),
+ Self::Editor => rems_from_px(settings.buffer_font_size(cx)),
}
}
pub fn pixels(self, cx: &App) -> Pixels {
- let theme_settings = ThemeSettings::get_global(cx);
+ let settings = theme::theme_settings(cx);
match self {
Self::Large => px(16.),
Self::Default => px(14.),
Self::Small => px(12.),
Self::XSmall => px(10.),
- Self::Ui => theme_settings.ui_font_size(cx),
- Self::Editor => theme_settings.buffer_font_size(cx),
+ Self::Ui => settings.ui_font_size(cx),
+ Self::Editor => settings.buffer_font_size(cx),
}
}
}
@@ -213,7 +211,7 @@ pub struct Headline {
impl RenderOnce for Headline {
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
- let ui_font = ThemeSettings::get_global(cx).ui_font.clone();
+ let ui_font = theme::theme_settings(cx).ui_font(cx).clone();
div()
.font(ui_font)
@@ -65,10 +65,10 @@ pub fn derive_spacing(input: TokenStream) -> TokenStream {
DynamicSpacingValue::Single(n) => {
let n = n.base10_parse::<f32>().unwrap();
quote! {
- DynamicSpacing::#variant => match ThemeSettings::get_global(cx).ui_density {
- ::theme_settings::UiDensity::Compact => (#n - 4.0).max(0.0) / BASE_REM_SIZE_IN_PX,
- ::theme_settings::UiDensity::Default => #n / BASE_REM_SIZE_IN_PX,
- ::theme_settings::UiDensity::Comfortable => (#n + 4.0) / BASE_REM_SIZE_IN_PX,
+ DynamicSpacing::#variant => match ::theme::theme_settings(cx).ui_density(cx) {
+ ::theme::UiDensity::Compact => (#n - 4.0).max(0.0) / BASE_REM_SIZE_IN_PX,
+ ::theme::UiDensity::Default => #n / BASE_REM_SIZE_IN_PX,
+ ::theme::UiDensity::Comfortable => (#n + 4.0) / BASE_REM_SIZE_IN_PX,
}
}
}
@@ -77,10 +77,10 @@ pub fn derive_spacing(input: TokenStream) -> TokenStream {
let b = b.base10_parse::<f32>().unwrap();
let c = c.base10_parse::<f32>().unwrap();
quote! {
- DynamicSpacing::#variant => match ThemeSettings::get_global(cx).ui_density {
- ::theme_settings::UiDensity::Compact => #a / BASE_REM_SIZE_IN_PX,
- ::theme_settings::UiDensity::Default => #b / BASE_REM_SIZE_IN_PX,
- ::theme_settings::UiDensity::Comfortable => #c / BASE_REM_SIZE_IN_PX,
+ DynamicSpacing::#variant => match ::theme::theme_settings(cx).ui_density(cx) {
+ ::theme::UiDensity::Compact => #a / BASE_REM_SIZE_IN_PX,
+ ::theme::UiDensity::Default => #b / BASE_REM_SIZE_IN_PX,
+ ::theme::UiDensity::Comfortable => #c / BASE_REM_SIZE_IN_PX,
}
}
}
@@ -157,7 +157,7 @@ pub fn derive_spacing(input: TokenStream) -> TokenStream {
/// Returns the spacing value in pixels.
pub fn px(&self, cx: &App) -> Pixels {
- let ui_font_size_f32: f32 = ThemeSettings::get_global(cx).ui_font_size(cx).into();
+ let ui_font_size_f32: f32 = ::theme::theme_settings(cx).ui_font_size(cx).into();
px(ui_font_size_f32 * self.spacing_ratio(cx))
}
}