add sizable trait

This commit is contained in:
2026-10-29 07:56:23 +07:00
parent 3808b1519f
commit 1b29c2c940
19 changed files with 442 additions and 69 deletions
+54 -1
View File
@@ -1,7 +1,7 @@
use crate::definition::ThemeDefinition;
use crate::ramps::{AccentShades, Oklch, contrast_foreground, derive_shades, mix};
use crate::system::SystemTheme;
use crate::{ColorTokens, Radius, ThemeVariant};
use crate::{ColorTokens, Radius, SizeTable, ThemeVariant};
use gpui::px;
/// Derived state shades for the colors that get pressed and hovered.
@@ -19,6 +19,9 @@ pub struct ResolvedTheme {
pub colors: ColorTokens,
pub ramps: Ramps,
pub radius: Radius,
/// The platform's native root font size; the fallback theme uses 16.
pub root_font_size: gpui::Pixels,
pub sizes: SizeTable,
pub accessibility: crate::system::AccessibilityPreferences,
}
@@ -132,6 +135,17 @@ pub(crate) fn resolve(
.to_hsla();
}
// The platform's root font size scales every control metric:
// a medium control's label is exactly the root font size.
//
// Fallback: 16 (the web default gpui also uses for rems).
let root_font_size = system.root_font_size.unwrap_or(px(16.));
let mut sizes = SizeTable::with_root_font_size(root_font_size);
if let Some(input_height) = system.input_height {
sizes.set_input_height(input_height);
}
let mut radius = Radius::default();
if let Some(control) = preset
.and_then(|def| def.control_radius)
@@ -158,6 +172,8 @@ pub(crate) fn resolve(
},
colors,
radius,
root_font_size,
sizes,
accessibility,
}
}
@@ -182,6 +198,8 @@ mod tests {
accent,
palette,
control_radius: None,
input_height: None,
root_font_size: None,
accessibility: AccessibilityPreferences::default(),
source: SourceKind::Macos,
}
@@ -218,9 +236,44 @@ mod tests {
assert_eq!(resolved.ramps.secondary.base, base.secondary);
assert_eq!(resolved.radius.sm, px(4.));
assert_eq!(resolved.radius.control, px(6.));
assert_eq!(resolved.root_font_size, px(16.));
assert_eq!(resolved.sizes.medium.control_height, px(28.));
assert_eq!(resolved.sizes.medium.input_height, px(30.));
assert_eq!(resolved.sizes.medium.text_size, px(16.));
assert_eq!(resolved.sizes.large.avatar_size, px(48.));
}
}
#[test]
fn a_stated_root_font_size_scales_the_size_table() {
// macOS states 13pt; the medium control shrinks to native proportions.
let mut stated = system(ThemeVariant::Light, PartialPalette::default(), None);
stated.root_font_size = Some(px(13.));
let resolved = resolve(&stated, None, None);
assert_eq!(resolved.root_font_size, px(13.));
assert_eq!(resolved.sizes.medium.control_height, px(22.75));
assert_eq!(resolved.sizes.medium.text_size, px(13.));
assert_eq!(resolved.sizes.small.text_size, px(11.05));
}
#[test]
fn a_stated_input_height_overrides_the_derived_one_and_scales() {
let mut stated = system(ThemeVariant::Light, PartialPalette::default(), None);
stated.input_height = Some(px(24.));
let resolved = resolve(&stated, None, None);
// The medium class takes the stated height verbatim, and the other
// classes keep their derived ratio to the medium class.
assert_eq!(resolved.sizes.medium.input_height, px(24.));
// Derive what small would be without the statement, scale it by the
// stated-to-derived ratio, and compare.
let derived = SizeTable::with_root_font_size(px(16.));
let scale = px(24.) / derived.medium.input_height;
let expected_small = derived.small.input_height * scale;
assert!((resolved.sizes.small.input_height - expected_small).abs() < px(0.01));
}
#[test]
fn system_colors_override_the_base_but_leave_the_rest_intact() {
let palette = PartialPalette {