Skip to main content

azul_css/
system.rs

1//! Discovers system-native styling for colors, fonts, and other metrics.
2//!
3//! This module provides a best-effort attempt to query the host operating system
4//! for its UI theme information. This is gated behind the **`io`** feature flag.
5//!
6//! **End-user customization (`AZ_RICING`):**
7//! By default (if the `io` feature is enabled), Azul looks for an
8//! application-specific stylesheet at `~/.config/azul/styles/<app_name>.css`
9//! (or `%APPDATA%\azul\styles\<app_name>.css` on Windows) and applies it as
10//! the last layer of the cascade, letting end-users "rice" any Azul app.
11//!
12//! The `AZ_RICING` env var has three modes (case-insensitive):
13//!
14//! - unset (default): load the user CSS if present; on Linux, the
15//!   detection chain is `KDE > GNOME > riced > defaults`.
16//! - `AZ_RICING=off` (aliases: `disabled`, `none`, `0`): skip the user
17//!   CSS file and the riced-desktop sources (Hyprland config, pywal
18//!   cache). Use for kiosk builds or CI runs that mustn't pick up local
19//!   customization.
20//! - `AZ_RICING=force` (aliases: `prefer`, `aggressive`, `1`): on Linux,
21//!   reorder the detection chain so riced-desktop sources win over
22//!   GNOME/KDE — useful for tiling-WM users whose `XDG_CURRENT_DESKTOP`
23//!   still says `gnome`. The user CSS file still loads.
24
25#![cfg(feature = "parser")]
26
27use alloc::{
28    boxed::Box,
29    string::{String, ToString},
30    vec::Vec,
31};
32use crate::{
33    corety::{AzString, OptionF32, OptionString, OptionU16},
34    css::Css,
35    parser2::{new_from_str, CssParseWarnMsg},
36    props::{
37        basic::{
38            color::{parse_css_color, ColorU, OptionColorU},
39            pixel::{PixelValue, OptionPixelValue},
40        },
41        style::scrollbar::{ComputedScrollbarStyle, OverscrollBehavior, ScrollBehavior, ScrollPhysics},
42    },
43};
44
45// --- End-user customization mode ---
46
47/// User-customization mode controlled by the `AZ_RICING` env var.
48///
49/// See the module-level documentation for the full description.
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51pub enum RicingMode {
52    /// `AZ_RICING=off` (or `disabled` / `none` / `0`). Skip the user
53    /// CSS file *and* the riced-desktop sources. Vanilla detection.
54    Off,
55    /// Unset. Load the user CSS if present; standard detection chain
56    /// (`KDE > GNOME > riced > defaults` on Linux).
57    Default,
58    /// `AZ_RICING=force` (or `prefer` / `aggressive` / `1`). Reorder
59    /// the Linux detection chain so riced-desktop sources win over
60    /// GNOME/KDE. The user CSS file still loads.
61    Force,
62}
63
64impl Default for RicingMode {
65    fn default() -> Self { RicingMode::Default }
66}
67
68/// Read the `AZ_RICING` env var and classify it. Case-insensitive.
69/// Anything we don't recognise falls through to `Default` so a typo
70/// degrades gracefully instead of disabling the feature silently.
71pub fn ricing_mode() -> RicingMode {
72    let raw = match std::env::var("AZ_RICING") {
73        Ok(s) => s,
74        Err(_) => return RicingMode::Default,
75    };
76    match raw.trim().to_ascii_lowercase().as_str() {
77        "off" | "disabled" | "none" | "0" | "false" => RicingMode::Off,
78        "force" | "prefer" | "aggressive" | "1" | "true" => RicingMode::Force,
79        _ => RicingMode::Default,
80    }
81}
82
83/// True when the user CSS file at `~/.config/azul/styles/<app>.css`
84/// should be read. False only when `AZ_RICING=off` is set.
85pub fn ricing_enabled() -> bool {
86    !matches!(ricing_mode(), RicingMode::Off)
87}
88
89// --- Public Data Structures ---
90
91/// Represents the detected platform.
92#[derive(Debug, Default, Clone, PartialEq, Eq)]
93#[repr(C, u8)]
94pub enum Platform {
95    Windows,
96    MacOs,
97    Linux(DesktopEnvironment),
98    Android,
99    Ios,
100    #[default]
101    Unknown,
102}
103
104impl Platform {
105    /// Get the current platform at compile time.
106    #[inline]
107    pub fn current() -> Self {
108        #[cfg(target_os = "macos")]
109        { Platform::MacOs }
110        #[cfg(target_os = "windows")]
111        { Platform::Windows }
112        #[cfg(target_os = "linux")]
113        { Platform::Linux(DesktopEnvironment::Other(AzString::from_const_str("unknown"))) }
114        #[cfg(target_os = "android")]
115        { Platform::Android }
116        #[cfg(target_os = "ios")]
117        { Platform::Ios }
118        #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux", target_os = "android", target_os = "ios")))]
119        { Platform::Unknown }
120    }
121}
122
123/// Represents the detected Linux Desktop Environment.
124#[derive(Debug, Clone, PartialEq, Eq)]
125#[repr(C, u8)]
126pub enum DesktopEnvironment {
127    Gnome,
128    Kde,
129    Other(AzString),
130}
131
132/// The overall theme type.
133#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
134#[repr(C)]
135pub enum Theme {
136    #[default]
137    Light,
138    Dark,
139}
140
141/// A unified collection of discovered system style properties.
142#[derive(Debug, Default, Clone, PartialEq)]
143#[repr(C)]
144pub struct SystemStyle {
145    pub fonts: SystemFonts,
146    pub metrics: SystemMetrics,
147    /// Linux-specific customisation (icon theme, cursor theme, GTK theme, ...)
148    pub linux: LinuxCustomization,
149    pub platform: Platform,
150    /// Focus ring / indicator visual style
151    pub focus_visuals: FocusVisuals,
152    /// System language/locale in BCP 47 format (e.g., "en-US", "de-DE")
153    /// Detected from OS settings at startup
154    pub language: AzString,
155    /// An optional, user-provided stylesheet loaded from a conventional
156    /// location (`~/.config/azul/styles/<app_name>.css`), allowing for
157    /// application-specific "ricing". Only loaded when the "io" feature
158    /// is enabled and `AZ_RICING` is not set to `off`.
159    pub app_specific_stylesheet: Option<Box<Css>>,
160    /// Scrollbar style information (boxed to ensure stable FFI size)
161    pub scrollbar: Option<Box<ComputedScrollbarStyle>>,
162    /// Global scroll physics configuration (momentum, friction, rubber-banding).
163    /// Platform-specific defaults are applied during system style discovery.
164    /// Applications can override this to change the "feel" of scrolling globally.
165    pub scroll_physics: ScrollPhysics,
166    pub theme: Theme,
167    /// Detected OS version (e.g., Windows 11 22H2, macOS Sonoma, etc.)
168    pub os_version: crate::dynamic_selector::OsVersion,
169    /// User prefers reduced motion (accessibility setting)
170    pub prefers_reduced_motion: crate::dynamic_selector::BoolCondition,
171    /// User prefers high contrast (accessibility setting)
172    pub prefers_high_contrast: crate::dynamic_selector::BoolCondition,
173    /// Detailed accessibility settings (superset of prefers_reduced_motion / prefers_high_contrast)
174    pub accessibility: AccessibilitySettings,
175    /// Input interaction timing / distance thresholds from the OS
176    pub input: InputMetrics,
177    /// Text rendering / anti-aliasing hints from the OS
178    pub text_rendering: TextRenderingHints,
179    /// OS-level scrollbar visibility / click-behaviour preferences
180    pub scrollbar_preferences: ScrollbarPreferences,
181    /// Visual hints: icons in menus/buttons, toolbar style, tooltips
182    pub visual_hints: VisualHints,
183    /// Animation enable/disable, speed factor, focus indicator behaviour
184    pub animation: AnimationMetrics,
185    pub colors: SystemColors,
186    /// Icon-specific styling options (grayscale, tinting, etc.)
187    pub icon_style: IconStyleOptions,
188    /// Audio feedback preferences (event sounds, input sounds)
189    pub audio: AudioMetrics,
190}
191
192/// Icon-specific styling options for accessibility and theming.
193///
194/// These settings affect how icons are rendered, supporting accessibility
195/// needs like reduced colors and high contrast modes.
196#[derive(Debug, Default, Clone, PartialEq)]
197#[repr(C)]
198pub struct IconStyleOptions {
199    /// If true, icons should be rendered in grayscale (for color-blind users
200    /// or reduced color preference). Applies a CSS grayscale filter.
201    pub prefer_grayscale: bool,
202    /// Optional tint color to apply to icons. Useful for matching icons
203    /// to the current theme or for high contrast modes.
204    pub tint_color: OptionColorU,
205    /// If true, icons should inherit the current text color instead of
206    /// using their original colors. Works well with font-based icons.
207    pub inherit_text_color: bool,
208}
209
210/// System font types that can be resolved at runtime based on OS settings.
211/// 
212/// This enum allows specifying semantic font roles that get resolved to
213/// actual font families based on the current platform and user preferences.
214/// For example, `Monospace` resolves to:
215/// - macOS: SF Mono or Menlo
216/// - Windows: Cascadia Mono or Consolas
217/// - Linux: Ubuntu Mono or DejaVu Sans Mono
218/// 
219/// Font variants (bold, italic) can be combined with the base type.
220#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
221#[repr(C)]
222#[derive(Default)]
223pub enum SystemFontType {
224    /// UI font for buttons, labels, menus (SF Pro, Segoe UI, Cantarell)
225    #[default]
226    Ui,
227    /// Bold variant of UI font
228    UiBold,
229    /// Monospace font for code (SF Mono, Consolas, Ubuntu Mono)
230    Monospace,
231    /// Bold variant of monospace font
232    MonospaceBold,
233    /// Italic variant of monospace font
234    MonospaceItalic,
235    /// Font for window titles
236    Title,
237    /// Bold variant of title font
238    TitleBold,
239    /// Font for menu items
240    Menu,
241    /// Small/caption font
242    Small,
243    /// Serif font for reading content (New York on macOS, Georgia on Windows)
244    Serif,
245    /// Bold variant of serif font
246    SerifBold,
247}
248
249
250impl SystemFontType {
251    /// Parse a SystemFontType from a CSS string.
252    /// 
253    /// Supported formats:
254    /// - `system:ui`, `system:ui:bold`
255    /// - `system:monospace`, `system:monospace:bold`, `system:monospace:italic`
256    /// - `system:title`, `system:title:bold`
257    /// - `system:menu`
258    /// - `system:small`
259    /// - `system:serif`, `system:serif:bold`
260    pub fn from_css_str(s: &str) -> Option<Self> {
261        let s = s.trim();
262        if !s.starts_with("system:") {
263            return None;
264        }
265        let rest = &s[7..]; // Skip "system:"
266        match rest {
267            "ui" => Some(SystemFontType::Ui),
268            "ui:bold" => Some(SystemFontType::UiBold),
269            "monospace" => Some(SystemFontType::Monospace),
270            "monospace:bold" => Some(SystemFontType::MonospaceBold),
271            "monospace:italic" => Some(SystemFontType::MonospaceItalic),
272            "title" => Some(SystemFontType::Title),
273            "title:bold" => Some(SystemFontType::TitleBold),
274            "menu" => Some(SystemFontType::Menu),
275            "small" => Some(SystemFontType::Small),
276            "serif" => Some(SystemFontType::Serif),
277            "serif:bold" => Some(SystemFontType::SerifBold),
278            _ => None,
279        }
280    }
281    
282    /// Get the CSS syntax for this system font type.
283    pub fn as_css_str(&self) -> &'static str {
284        match self {
285            SystemFontType::Ui => "system:ui",
286            SystemFontType::UiBold => "system:ui:bold",
287            SystemFontType::Monospace => "system:monospace",
288            SystemFontType::MonospaceBold => "system:monospace:bold",
289            SystemFontType::MonospaceItalic => "system:monospace:italic",
290            SystemFontType::Title => "system:title",
291            SystemFontType::TitleBold => "system:title:bold",
292            SystemFontType::Menu => "system:menu",
293            SystemFontType::Small => "system:small",
294            SystemFontType::Serif => "system:serif",
295            SystemFontType::SerifBold => "system:serif:bold",
296        }
297    }
298    
299    /// Returns true if this system font type implies bold weight.
300    /// Used when resolving system fonts to pass the correct weight to fontconfig.
301    pub fn is_bold(&self) -> bool {
302        matches!(
303            self,
304            SystemFontType::UiBold
305                | SystemFontType::MonospaceBold
306                | SystemFontType::TitleBold
307                | SystemFontType::SerifBold
308        )
309    }
310    
311    /// Returns true if this system font type implies italic style.
312    pub fn is_italic(&self) -> bool {
313        matches!(self, SystemFontType::MonospaceItalic)
314    }
315}
316
317/// Accessibility settings detected from the operating system.
318/// 
319/// These settings allow apps to adapt their UI for users with accessibility needs.
320/// Detection methods:
321/// - macOS: UIAccessibility APIs (isBoldTextEnabled, isReduceMotionEnabled, etc.)
322/// - Windows: SystemParametersInfo (SPI_GETHIGHCONTRAST, SPI_GETCLIENTAREAANIMATION)
323/// - Linux: gsettings (org.gnome.desktop.interface, org.gnome.desktop.a11y)
324#[derive(Debug, Default, Clone, PartialEq)]
325#[repr(C)]
326pub struct AccessibilitySettings {
327    /// Text scaling factor (1.0 = normal, 1.5 = 150%, etc.)
328    pub text_scale_factor: f32,
329    /// User prefers bold text for better readability
330    /// macOS: UIAccessibility.isBoldTextEnabled
331    /// Windows: N/A (font scaling)
332    /// Linux: org.gnome.desktop.interface text-scaling-factor
333    pub prefers_bold_text: bool,
334    /// User prefers larger text
335    /// macOS: preferredContentSizeCategory
336    /// Windows: SystemParametersInfo text scale factor
337    /// Linux: org.gnome.desktop.interface text-scaling-factor
338    pub prefers_larger_text: bool,
339    /// User prefers high contrast colors
340    /// macOS: UIAccessibility.isDarkerSystemColorsEnabled
341    /// Windows: SPI_GETHIGHCONTRAST
342    /// Linux: org.gnome.desktop.a11y.interface high-contrast
343    pub prefers_high_contrast: bool,
344    /// User prefers reduced motion/animations
345    /// macOS: UIAccessibility.isReduceMotionEnabled
346    /// Windows: SPI_GETCLIENTAREAANIMATION (inverted)
347    /// Linux: org.gnome.desktop.interface enable-animations (inverted)
348    pub prefers_reduced_motion: bool,
349    /// User prefers reduced transparency
350    /// macOS: UIAccessibility.isReduceTransparencyEnabled
351    /// Windows: N/A
352    /// Linux: N/A
353    pub prefers_reduced_transparency: bool,
354    /// Screen reader is active (VoiceOver, Narrator, Orca)
355    pub screen_reader_active: bool,
356    /// User prefers differentiate without color
357    /// macOS: UIAccessibility.shouldDifferentiateWithoutColor
358    pub differentiate_without_color: bool,
359}
360
361/// Common system colors used for UI elements.
362/// 
363/// These colors are queried from the operating system and automatically adapt
364/// to the current theme (light/dark mode) and accent color settings.
365/// 
366/// On macOS, these correspond to NSColor semantic colors.
367/// On Windows, these come from UISettings.
368/// On Linux/GTK, these come from the GTK theme.
369#[derive(Debug, Default, Clone, PartialEq)]
370#[repr(C)]
371pub struct SystemColors {
372    // === Primary semantic colors ===
373    /// Primary text color (NSColor.textColor on macOS)
374    pub text: OptionColorU,
375    /// Secondary text color for less prominent text (NSColor.secondaryLabelColor)
376    pub secondary_text: OptionColorU,
377    /// Tertiary text color for disabled/placeholder text (NSColor.tertiaryLabelColor)
378    pub tertiary_text: OptionColorU,
379    /// Background color for content areas (NSColor.textBackgroundColor)
380    pub background: OptionColorU,
381    
382    // === Accent colors ===
383    /// System accent color chosen by user (NSColor.controlAccentColor on macOS)
384    pub accent: OptionColorU,
385    /// Text color on accent backgrounds
386    pub accent_text: OptionColorU,
387    
388    // === Control colors ===
389    /// Button/control background (NSColor.controlColor)
390    pub button_face: OptionColorU,
391    /// Button/control text color (NSColor.controlTextColor)
392    pub button_text: OptionColorU,
393    /// Disabled control text color (NSColor.disabledControlTextColor)
394    pub disabled_text: OptionColorU,
395    
396    // === Window colors ===
397    /// Window background color (NSColor.windowBackgroundColor)
398    pub window_background: OptionColorU,
399    /// Under-page background color (NSColor.underPageBackgroundColor)
400    pub under_page_background: OptionColorU,
401    
402    // === Selection colors ===
403    /// Selection background when window is focused (NSColor.selectedContentBackgroundColor)
404    pub selection_background: OptionColorU,
405    /// Selection text color when window is focused
406    pub selection_text: OptionColorU,
407    /// Selection background when window is NOT focused (NSColor.unemphasizedSelectedContentBackgroundColor)
408    /// This is used for :backdrop state styling
409    pub selection_background_inactive: OptionColorU,
410    /// Selection text color when window is NOT focused
411    pub selection_text_inactive: OptionColorU,
412    
413    // === Additional semantic colors ===
414    /// Link color (NSColor.linkColor)
415    pub link: OptionColorU,
416    /// Separator/divider color (NSColor.separatorColor)
417    pub separator: OptionColorU,
418    /// Grid/table line color (NSColor.gridColor)
419    pub grid: OptionColorU,
420    /// Find/search highlight color (NSColor.findHighlightColor)
421    pub find_highlight: OptionColorU,
422    
423    // === Sidebar colors (macOS-specific) ===
424    /// Sidebar background color
425    pub sidebar_background: OptionColorU,
426    /// Selected row in sidebar
427    pub sidebar_selection: OptionColorU,
428}
429
430/// Common system font settings.
431/// 
432/// On macOS, these are queried from NSFont.
433/// On Windows, these come from SystemParametersInfo.
434/// On Linux, these come from GTK/gsettings.
435#[derive(Debug, Default, Clone, PartialEq)]
436#[repr(C)]
437pub struct SystemFonts {
438    /// The primary font used for UI elements like buttons and labels.
439    /// On macOS: SF Pro (system font)
440    /// On Windows: Segoe UI
441    /// On Linux: Cantarell, Ubuntu, or system default
442    pub ui_font: OptionString,
443    /// The default font size for UI elements, in points.
444    pub ui_font_size: OptionF32,
445    /// The font used for code or other monospaced text.
446    /// On macOS: SF Mono or Menlo
447    /// On Windows: Cascadia Mono or Consolas
448    /// On Linux: Ubuntu Mono or DejaVu Sans Mono
449    pub monospace_font: OptionString,
450    /// Monospace font size in points
451    pub monospace_font_size: OptionF32,
452    /// Bold variant of the UI font (if different)
453    pub ui_font_bold: OptionString,
454    /// Font for window titles
455    pub title_font: OptionString,
456    /// Title font size in points
457    pub title_font_size: OptionF32,
458    /// Font for menu items
459    pub menu_font: OptionString,
460    /// Menu font size in points
461    pub menu_font_size: OptionF32,
462    /// Small/caption font for less prominent text
463    pub small_font: OptionString,
464    /// Small font size in points
465    pub small_font_size: OptionF32,
466}
467
468/// Common system metrics for UI element sizing and spacing.
469#[derive(Debug, Default, Clone, PartialEq)]
470#[repr(C)]
471pub struct SystemMetrics {
472    /// The corner radius for standard elements like buttons.
473    pub corner_radius: OptionPixelValue,
474    /// The width of standard borders.
475    pub border_width: OptionPixelValue,
476    /// The horizontal (left/right) padding for buttons and similar controls.
477    pub button_padding_horizontal: OptionPixelValue,
478    /// The vertical (top/bottom) padding for buttons and similar controls.
479    pub button_padding_vertical: OptionPixelValue,
480    /// Titlebar layout information (button positions, safe areas, etc.)
481    pub titlebar: TitlebarMetrics,
482}
483
484/// Which side of the titlebar the window control buttons are on.
485#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
486#[repr(C)]
487pub enum TitlebarButtonSide {
488    /// Buttons are on the left (macOS default)
489    Left,
490    /// Buttons are on the right (Windows, most Linux DEs)
491    #[default]
492    Right,
493}
494
495/// Which window control buttons are available in the titlebar.
496#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
497#[repr(C)]
498pub struct TitlebarButtons {
499    /// Close button is available
500    pub has_close: bool,
501    /// Minimize button is available
502    pub has_minimize: bool,
503    /// Maximize/zoom button is available
504    pub has_maximize: bool,
505    /// Fullscreen button is available (macOS green button behavior)
506    pub has_fullscreen: bool,
507}
508
509impl Default for TitlebarButtons {
510    fn default() -> Self {
511        Self {
512            has_close: true,
513            has_minimize: true,
514            has_maximize: true,
515            has_fullscreen: false,
516        }
517    }
518}
519
520/// Safe area insets for devices with notches, rounded corners, or sensor housings.
521/// 
522/// On devices like iPhones with notches or Dynamic Island, the safe area
523/// indicates regions where content should not be placed to avoid being
524/// obscured by hardware features.
525#[derive(Debug, Default, Clone, Copy, PartialEq)]
526#[repr(C)]
527pub struct SafeAreaInsets {
528    /// Inset from the top edge (notch, camera housing, etc.)
529    pub top: OptionPixelValue,
530    /// Inset from the bottom edge (home indicator on iPhone)
531    pub bottom: OptionPixelValue,
532    /// Inset from the left edge (rounded corners)
533    pub left: OptionPixelValue,
534    /// Inset from the right edge (rounded corners)
535    pub right: OptionPixelValue,
536}
537
538/// Metrics for titlebar layout and window chrome.
539/// 
540/// This provides information needed to correctly position custom titlebar
541/// content when using `WindowDecorations::NoTitle` (expanded title mode).
542#[derive(Debug, Clone, PartialEq)]
543#[repr(C)]
544pub struct TitlebarMetrics {
545    /// Which side the window control buttons are on
546    pub button_side: TitlebarButtonSide,
547    /// Which buttons are available
548    pub buttons: TitlebarButtons,
549    /// Height of the titlebar in pixels
550    pub height: OptionPixelValue,
551    /// Width reserved for window control buttons (close/min/max)
552    /// This is the space to avoid when drawing custom title text
553    pub button_area_width: OptionPixelValue,
554    /// Horizontal padding inside the titlebar
555    pub padding_horizontal: OptionPixelValue,
556    /// Safe area insets for notched/rounded displays
557    pub safe_area: SafeAreaInsets,
558    /// Title text font (from SystemFonts::title_font)
559    pub title_font: OptionString,
560    /// Title text font size
561    pub title_font_size: OptionF32,
562    /// Title text font weight (400 = normal, 600 = semibold, 700 = bold)
563    pub title_font_weight: OptionU16,
564}
565
566impl Default for TitlebarMetrics {
567    fn default() -> Self {
568        Self {
569            button_side: TitlebarButtonSide::Right,
570            buttons: TitlebarButtons::default(),
571            height: OptionPixelValue::Some(PixelValue::px(32.0)),
572            button_area_width: OptionPixelValue::Some(PixelValue::px(100.0)),
573            padding_horizontal: OptionPixelValue::Some(PixelValue::px(8.0)),
574            safe_area: SafeAreaInsets::default(),
575            title_font: OptionString::None,
576            title_font_size: OptionF32::Some(13.0),
577            title_font_weight: OptionU16::Some(600), // Semibold
578        }
579    }
580}
581
582impl TitlebarMetrics {
583    /// Windows-style titlebar (buttons on right)
584    pub fn windows() -> Self {
585        Self {
586            button_side: TitlebarButtonSide::Right,
587            buttons: TitlebarButtons {
588                has_close: true,
589                has_minimize: true,
590                has_maximize: true,
591                has_fullscreen: false,
592            },
593            height: OptionPixelValue::Some(PixelValue::px(32.0)),
594            button_area_width: OptionPixelValue::Some(PixelValue::px(138.0)), // 3 buttons * 46px
595            padding_horizontal: OptionPixelValue::Some(PixelValue::px(8.0)),
596            safe_area: SafeAreaInsets::default(),
597            title_font: OptionString::Some("Segoe UI Variable Text".into()),
598            title_font_size: OptionF32::Some(12.0),
599            title_font_weight: OptionU16::Some(400), // Normal
600        }
601    }
602    
603    /// macOS-style titlebar (buttons on left, "traffic lights")
604    pub fn macos() -> Self {
605        Self {
606            button_side: TitlebarButtonSide::Left,
607            buttons: TitlebarButtons {
608                has_close: true,
609                has_minimize: true,
610                has_maximize: false, // macOS has fullscreen instead
611                has_fullscreen: true,
612            },
613            height: OptionPixelValue::Some(PixelValue::px(28.0)),
614            button_area_width: OptionPixelValue::Some(PixelValue::px(78.0)), // 3 buttons with gaps
615            padding_horizontal: OptionPixelValue::Some(PixelValue::px(8.0)),
616            safe_area: SafeAreaInsets::default(),
617            title_font: OptionString::Some(".SF NS".into()),
618            title_font_size: OptionF32::Some(13.0),
619            title_font_weight: OptionU16::Some(600), // Semibold
620        }
621    }
622    
623    /// Linux GNOME-style titlebar (buttons on right by default)
624    pub fn linux_gnome() -> Self {
625        Self {
626            button_side: TitlebarButtonSide::Right, // Default, can be changed in settings
627            buttons: TitlebarButtons {
628                has_close: true,
629                has_minimize: true,
630                has_maximize: true,
631                has_fullscreen: false,
632            },
633            height: OptionPixelValue::Some(PixelValue::px(35.0)),
634            button_area_width: OptionPixelValue::Some(PixelValue::px(100.0)),
635            padding_horizontal: OptionPixelValue::Some(PixelValue::px(12.0)),
636            safe_area: SafeAreaInsets::default(),
637            title_font: OptionString::Some("Cantarell".into()),
638            title_font_size: OptionF32::Some(11.0),
639            title_font_weight: OptionU16::Some(700), // Bold
640        }
641    }
642    
643    /// iOS-style safe area (for notched devices)
644    pub fn ios() -> Self {
645        Self {
646            button_side: TitlebarButtonSide::Left,
647            buttons: TitlebarButtons {
648                has_close: false, // iOS apps don't have close buttons
649                has_minimize: false,
650                has_maximize: false,
651                has_fullscreen: false,
652            },
653            height: OptionPixelValue::Some(PixelValue::px(44.0)),
654            button_area_width: OptionPixelValue::Some(PixelValue::px(0.0)),
655            padding_horizontal: OptionPixelValue::Some(PixelValue::px(16.0)),
656            safe_area: SafeAreaInsets {
657                // iPhone notch safe area
658                top: OptionPixelValue::Some(PixelValue::px(47.0)),
659                bottom: OptionPixelValue::Some(PixelValue::px(34.0)),
660                left: OptionPixelValue::None,
661                right: OptionPixelValue::None,
662            },
663            title_font: OptionString::Some(".SFUI-Semibold".into()),
664            title_font_size: OptionF32::Some(17.0),
665            title_font_weight: OptionU16::Some(600),
666        }
667    }
668    
669    /// Android-style titlebar (action bar)
670    pub fn android() -> Self {
671        Self {
672            button_side: TitlebarButtonSide::Left, // Back button on left
673            buttons: TitlebarButtons {
674                has_close: false,
675                has_minimize: false,
676                has_maximize: false,
677                has_fullscreen: false,
678            },
679            height: OptionPixelValue::Some(PixelValue::px(56.0)),
680            button_area_width: OptionPixelValue::Some(PixelValue::px(48.0)), // Back button
681            padding_horizontal: OptionPixelValue::Some(PixelValue::px(16.0)),
682            safe_area: SafeAreaInsets::default(),
683            title_font: OptionString::Some("Roboto Medium".into()),
684            title_font_size: OptionF32::Some(20.0),
685            title_font_weight: OptionU16::Some(500),
686        }
687    }
688}
689
690// ── Input interaction metrics ────────────────────────────────────────────
691
692/// Input interaction timing and distance thresholds from the OS.
693///
694/// These values are queried from the operating system to match the user's
695/// configured double-click speed, drag sensitivity, caret blink rate, etc.
696///
697/// # Platform APIs
698/// - **macOS:** `NSEvent.doubleClickInterval`
699/// - **Windows:** `GetDoubleClickTime()`, `GetSystemMetrics(SM_CXDOUBLECLK)`,
700///   `GetCaretBlinkTime()`, `SystemParametersInfo(SPI_GETWHEELSCROLLLINES)`
701/// - **Linux:** XDG Desktop Portal / gsettings
702#[derive(Debug, Clone, Copy, PartialEq)]
703#[repr(C)]
704pub struct InputMetrics {
705    /// Max milliseconds between clicks to register a double-click.
706    pub double_click_time_ms: u32,
707    /// Max pixels the mouse can move between clicks and still count.
708    pub double_click_distance_px: f32,
709    /// Pixels the mouse must move while held down before a drag starts.
710    pub drag_threshold_px: f32,
711    /// Caret blink rate in milliseconds (0 = no blink).
712    pub caret_blink_rate_ms: u32,
713    /// Width of the text caret/cursor in pixels (typically 1–2).
714    pub caret_width_px: f32,
715    /// Lines to scroll per mouse wheel notch.
716    pub wheel_scroll_lines: u32,
717    /// Milliseconds to wait before a hover triggers (e.g. tooltip delay).
718    /// Windows: `SystemParametersInfo(SPI_GETMOUSEHOVERTIME)` — default 400.
719    pub hover_time_ms: u32,
720}
721
722impl Default for InputMetrics {
723    fn default() -> Self {
724        Self {
725            double_click_time_ms: 500,
726            double_click_distance_px: 4.0,
727            drag_threshold_px: 5.0,
728            caret_blink_rate_ms: 530,
729            caret_width_px: 1.0,
730            wheel_scroll_lines: 3,
731            hover_time_ms: 400,
732        }
733    }
734}
735
736// ── Text rendering hints ─────────────────────────────────────────────────
737
738/// Subpixel rendering layout for font smoothing.
739#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
740#[repr(C)]
741pub enum SubpixelType {
742    /// No subpixel rendering (grayscale anti-aliasing only).
743    #[default]
744    None,
745    /// Horizontal RGB subpixel layout (most common for LCD monitors).
746    Rgb,
747    /// Horizontal BGR subpixel layout.
748    Bgr,
749    /// Vertical RGB subpixel layout.
750    VRgb,
751    /// Vertical BGR subpixel layout.
752    VBgr,
753}
754
755/// Text rendering configuration from the OS.
756///
757/// These hints allow the framework to match the host's font smoothing
758/// settings for crisp, consistent text rendering.
759#[derive(Debug, Clone, Copy, PartialEq)]
760#[repr(C)]
761pub struct TextRenderingHints {
762    /// Subpixel rendering type.
763    pub subpixel_type: SubpixelType,
764    /// Font smoothing gamma (1000 = default, higher = more contrast).
765    pub font_smoothing_gamma: u32,
766    /// Whether font smoothing (anti-aliasing) is enabled.
767    pub font_smoothing_enabled: bool,
768    /// User prefers increased text contrast.
769    pub increased_contrast: bool,
770}
771
772impl Default for TextRenderingHints {
773    fn default() -> Self {
774        Self {
775            subpixel_type: SubpixelType::None,
776            font_smoothing_gamma: 1000,
777            font_smoothing_enabled: true,
778            increased_contrast: false,
779        }
780    }
781}
782
783// ── Focus ring visuals ───────────────────────────────────────────────────
784
785/// Focus ring / indicator visual style.
786///
787/// When an element receives keyboard focus the OS typically draws a visible
788/// ring or border.  These values come from the OS preferences.
789#[derive(Debug, Default, Clone, Copy, PartialEq)]
790#[repr(C)]
791pub struct FocusVisuals {
792    /// Focus ring / indicator colour.
793    /// macOS: `NSColor.keyboardFocusIndicatorColor`
794    pub focus_ring_color: OptionColorU,
795    /// Width of focus border / ring.
796    /// Windows: `SystemParametersInfo(SPI_GETFOCUSBORDERWIDTH)`
797    pub focus_border_width: OptionPixelValue,
798    /// Height of focus border / ring.
799    pub focus_border_height: OptionPixelValue,
800}
801
802// ── Scrollbar preferences ────────────────────────────────────────────────
803
804/// When scrollbars should be shown (OS-level preference).
805#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
806#[repr(C)]
807pub enum ScrollbarVisibility {
808    /// Always show scrollbars.
809    Always,
810    /// Show only while scrolling, then fade out.
811    #[default]
812    WhenScrolling,
813    /// Automatic: depends on input device (trackpad → overlay, mouse → always).
814    Automatic,
815}
816
817/// What happens when clicking the scrollbar track area.
818#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
819#[repr(C)]
820pub enum ScrollbarTrackClick {
821    /// Jump to the clicked position.
822    JumpToPosition,
823    /// Scroll by one page.
824    #[default]
825    PageUpDown,
826}
827
828/// OS-level scrollbar behaviour preferences.
829///
830/// These are separate from the CSS scrollbar *appearance* (`ComputedScrollbarStyle`).
831/// They control *when* scrollbars appear and *how* clicking the track behaves.
832#[derive(Debug, Clone, Copy, PartialEq)]
833#[repr(C)]
834pub struct ScrollbarPreferences {
835    /// How scrollbars should be shown.
836    /// macOS: `NSScroller.preferredScrollerStyle`
837    pub visibility: ScrollbarVisibility,
838    /// What happens when clicking the scrollbar track.
839    pub track_click: ScrollbarTrackClick,
840}
841
842impl Default for ScrollbarPreferences {
843    fn default() -> Self {
844        Self {
845            visibility: ScrollbarVisibility::WhenScrolling,
846            track_click: ScrollbarTrackClick::PageUpDown,
847        }
848    }
849}
850
851// ── Linux-specific customisation ─────────────────────────────────────────
852
853/// Linux-specific customisation settings.
854///
855/// Read from GTK / KDE / XDG settings on Linux; `Default` (all `None` / 0)
856/// on other platforms.
857#[derive(Debug, Default, Clone, PartialEq)]
858#[repr(C)]
859pub struct LinuxCustomization {
860    /// GTK theme name (e.g. "Adwaita", "Breeze", "Numix").
861    pub gtk_theme: OptionString,
862    /// Icon theme name (e.g. "Papirus", "Numix", "Breeze").
863    pub icon_theme: OptionString,
864    /// Cursor theme name (e.g. "Breeze_Snow", "DMZ-Black").
865    pub cursor_theme: OptionString,
866    /// Cursor size in pixels (0 = unset / use OS default).
867    pub cursor_size: u32,
868    /// GTK button layout string (e.g. "close,minimize,maximize:menu").
869    /// Determines button side and order for CSD titlebars on Linux.
870    pub titlebar_button_layout: OptionString,
871}
872
873// ── Visual hints (icons in menus / buttons / toolbar style) ──────────────
874
875/// Toolbar display style (icons, text, or both).
876#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
877#[repr(C)]
878pub enum ToolbarStyle {
879    /// Show only icons in toolbars.
880    #[default]
881    IconsOnly,
882    /// Show only text labels in toolbars.
883    TextOnly,
884    /// Show text beside the icon (horizontal).
885    TextBesideIcon,
886    /// Show text below the icon (vertical).
887    TextBelowIcon,
888}
889
890/// Visual hints from the OS about how icons and decorations should be shown.
891///
892/// These preferences differ heavily between Linux desktops (KDE vs GNOME)
893/// and are less configurable on macOS / Windows where HIG rules apply.
894#[derive(Debug, Clone, Copy, PartialEq)]
895#[repr(C)]
896pub struct VisualHints {
897    /// Toolbar display style.
898    /// Linux: `org.gnome.desktop.interface toolbar-style`, KDE `ToolButtonStyle`.
899    pub toolbar_style: ToolbarStyle,
900    /// Show icons on push buttons?  (Common in KDE, rare in Win/Mac.)
901    /// Linux: `org.gnome.desktop.interface buttons-have-icons`, KDE ShowIconsOnPushButtons.
902    pub show_button_images: bool,
903    /// Show icons in context menus?  (GNOME defaults off since 3.x; Win/Mac/KDE usually on.)
904    /// Linux: `org.gnome.desktop.interface menus-have-icons`.
905    pub show_menu_images: bool,
906    /// Should tooltips be shown on hover?
907    pub show_tooltips: bool,
908    /// Flash the window taskbar entry on alert?
909    pub flash_on_alert: bool,
910}
911
912impl Default for VisualHints {
913    fn default() -> Self {
914        Self {
915            toolbar_style: ToolbarStyle::IconsOnly,
916            show_button_images: false,
917            show_menu_images: true,
918            show_tooltips: true,
919            flash_on_alert: true,
920        }
921    }
922}
923
924// ── Animation metrics ────────────────────────────────────────────────────
925
926/// Focus indicator behaviour (always visible vs keyboard-only).
927#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
928#[repr(C)]
929pub enum FocusBehavior {
930    /// Focus indicators are always visible when an element has focus.
931    #[default]
932    AlwaysVisible,
933    /// Focus indicators are hidden until the user presses a keyboard key
934    /// (Alt, Tab, arrow keys, etc.).  Windows: `SPI_GETKEYBOARDCUES`.
935    KeyboardOnly,
936}
937
938/// Animation-related preferences from the OS.
939///
940/// These control whether UI animations (transitions, fades, slides) should
941/// play and at what speed.
942///
943/// # Platform APIs
944/// - **Windows:** `SystemParametersInfo(SPI_GETCLIENTAREAANIMATION)`,
945///   `SPI_GETKEYBOARDCUES`
946/// - **macOS:** `NSWorkspace.accessibilityDisplayShouldReduceMotion`
947/// - **Linux:** `org.gnome.desktop.interface enable-animations`,
948///   KDE `AnimationDurationFactor`
949#[derive(Debug, Clone, Copy, PartialEq)]
950#[repr(C)]
951pub struct AnimationMetrics {
952    /// Global enable/disable for UI animations.
953    pub animations_enabled: bool,
954    /// Animation speed factor (1.0 = normal, 0.5 = 2× faster, 2.0 = 2× slower).
955    /// Primarily used in KDE.
956    pub animation_duration_factor: f32,
957    /// When to show focus rectangles / rings.
958    pub focus_indicator_behavior: FocusBehavior,
959}
960
961impl Default for AnimationMetrics {
962    fn default() -> Self {
963        Self {
964            animations_enabled: true,
965            animation_duration_factor: 1.0,
966            focus_indicator_behavior: FocusBehavior::AlwaysVisible,
967        }
968    }
969}
970
971// ── Audio metrics ────────────────────────────────────────────────────────
972
973/// Audio-feedback preferences from the OS.
974///
975/// Controls whether the app should make sounds on events (error pings,
976/// notifications) or on input (clicks, key presses).
977///
978/// # Platform APIs
979/// - **Windows:** `SystemParametersInfo(SPI_GETBEEP)`
980/// - **macOS:** `NSSound.soundEffectAudioVolume`
981/// - **Linux:** `org.gnome.desktop.sound event-sounds`,
982///   `org.gnome.desktop.sound input-feedback-sounds`
983#[derive(Debug, Clone, Copy, PartialEq, Eq)]
984#[repr(C)]
985pub struct AudioMetrics {
986    /// Should the app make sounds on events?  (Error ping, notification, etc.)
987    pub event_sounds_enabled: bool,
988    /// Should the app make sounds on input?  (Clicks, typing feedback.)
989    pub input_feedback_sounds_enabled: bool,
990}
991
992impl Default for AudioMetrics {
993    fn default() -> Self {
994        Self {
995            event_sounds_enabled: true,
996            input_feedback_sounds_enabled: false,
997        }
998    }
999}
1000
1001/// Apple system font family names for font fallback chains.
1002/// 
1003/// These are the canonical names for Apple's system fonts, which should
1004/// be used in font fallback chains for proper rendering on Apple platforms.
1005/// Note: The names here must match what rust-fontconfig indexes from the font metadata.
1006pub mod apple_fonts {
1007    /// System Font - Primary system font for macOS
1008    /// This is how rust-fontconfig indexes the SF Pro font family
1009    pub const SYSTEM_FONT: &str = "System Font";
1010    
1011    /// SF NS variants as indexed by rust-fontconfig
1012    pub const SF_NS_ROUNDED: &str = "SF NS Rounded";
1013    
1014    /// SF Compact - System font optimized for watchOS
1015    /// Optimized for small sizes and narrow columns
1016    pub const SF_COMPACT: &str = "SF Compact";
1017    
1018    /// SF Mono - Monospaced font used in Xcode
1019    /// Enables alignment between rows and columns of text
1020    pub const SF_MONO: &str = "SF NS Mono Light";
1021    
1022    /// New York - Serif font for reading
1023    /// Performs as traditional reading face at small sizes
1024    pub const NEW_YORK: &str = "New York";
1025    
1026    /// SF Arabic - Arabic system font
1027    pub const SF_ARABIC: &str = "SF Arabic";
1028    
1029    /// SF Armenian - Armenian system font
1030    pub const SF_ARMENIAN: &str = "SF Armenian";
1031    
1032    /// SF Georgian - Georgian system font
1033    pub const SF_GEORGIAN: &str = "SF Georgian";
1034    
1035    /// SF Hebrew - Hebrew system font with niqqud support
1036    pub const SF_HEBREW: &str = "SF Hebrew";
1037    
1038    /// Legacy macOS fonts for fallback
1039    pub const MENLO: &str = "Menlo";
1040    pub const MENLO_REGULAR: &str = "Menlo Regular";
1041    pub const MENLO_BOLD: &str = "Menlo Bold";
1042    pub const MONACO: &str = "Monaco";
1043    pub const LUCIDA_GRANDE: &str = "Lucida Grande";
1044    pub const LUCIDA_GRANDE_BOLD: &str = "Lucida Grande Bold";
1045    pub const HELVETICA_NEUE: &str = "Helvetica Neue";
1046    pub const HELVETICA_NEUE_BOLD: &str = "Helvetica Neue Bold";
1047}
1048
1049/// Windows system font family names.
1050pub mod windows_fonts {
1051    /// Modern Windows 11 fonts
1052    pub const SEGOE_UI_VARIABLE: &str = "Segoe UI Variable";
1053    pub const SEGOE_UI_VARIABLE_TEXT: &str = "Segoe UI Variable Text";
1054    pub const SEGOE_UI_VARIABLE_DISPLAY: &str = "Segoe UI Variable Display";
1055    
1056    /// Standard Windows fonts
1057    pub const SEGOE_UI: &str = "Segoe UI";
1058    pub const CONSOLAS: &str = "Consolas";
1059    pub const CASCADIA_CODE: &str = "Cascadia Code";
1060    pub const CASCADIA_MONO: &str = "Cascadia Mono";
1061    
1062    /// Legacy Windows fonts
1063    pub const TAHOMA: &str = "Tahoma";
1064    pub const MS_SANS_SERIF: &str = "MS Sans Serif";
1065    pub const LUCIDA_CONSOLE: &str = "Lucida Console";
1066    pub const COURIER_NEW: &str = "Courier New";
1067}
1068
1069/// Linux/GTK common font family names.
1070pub mod linux_fonts {
1071    /// GNOME default fonts
1072    pub const CANTARELL: &str = "Cantarell";
1073    pub const ADWAITA: &str = "Adwaita";
1074    
1075    /// Ubuntu fonts
1076    pub const UBUNTU: &str = "Ubuntu";
1077    pub const UBUNTU_MONO: &str = "Ubuntu Mono";
1078    
1079    /// DejaVu fonts (widely available)
1080    pub const DEJAVU_SANS: &str = "DejaVu Sans";
1081    pub const DEJAVU_SANS_MONO: &str = "DejaVu Sans Mono";
1082    pub const DEJAVU_SERIF: &str = "DejaVu Serif";
1083    
1084    /// Liberation fonts (metrically compatible with Windows fonts)
1085    pub const LIBERATION_SANS: &str = "Liberation Sans";
1086    pub const LIBERATION_MONO: &str = "Liberation Mono";
1087    pub const LIBERATION_SERIF: &str = "Liberation Serif";
1088    
1089    /// Noto fonts (broad Unicode coverage)
1090    pub const NOTO_SANS: &str = "Noto Sans";
1091    pub const NOTO_MONO: &str = "Noto Sans Mono";
1092    pub const NOTO_SERIF: &str = "Noto Serif";
1093    
1094    /// KDE default fonts
1095    pub const HACK: &str = "Hack";
1096    
1097    /// Generic fallback names
1098    pub const MONOSPACE: &str = "Monospace";
1099    pub const SANS_SERIF: &str = "Sans";
1100    pub const SERIF: &str = "Serif";
1101}
1102
1103impl SystemFontType {
1104    /// Returns the font fallback chain for this font type on the given platform.
1105    /// 
1106    /// The returned list contains font family names in order of preference.
1107    /// The first available font should be used.
1108    pub fn get_fallback_chain(&self, platform: &Platform) -> Vec<&'static str> {
1109        match platform {
1110            Platform::MacOs | Platform::Ios => self.macos_fallback_chain(),
1111            Platform::Windows => self.windows_fallback_chain(),
1112            Platform::Linux(_) => self.linux_fallback_chain(),
1113            Platform::Android => self.android_fallback_chain(),
1114            Platform::Unknown => self.generic_fallback_chain(),
1115        }
1116    }
1117    
1118    fn macos_fallback_chain(&self) -> Vec<&'static str> {
1119        match self {
1120            // For Normal weight, try System Font first, then Helvetica Neue
1121            SystemFontType::Ui => vec![
1122                apple_fonts::SYSTEM_FONT,
1123                apple_fonts::HELVETICA_NEUE,
1124                apple_fonts::LUCIDA_GRANDE,
1125            ],
1126            // For Bold weight, use Helvetica Neue first (System Font has no Bold variant in fontconfig)
1127            SystemFontType::UiBold => vec![
1128                apple_fonts::HELVETICA_NEUE, // Will be queried with weight=Bold -> "Helvetica Neue Bold"
1129                apple_fonts::LUCIDA_GRANDE,
1130            ],
1131            // Monospace fonts: Menlo has bold variant
1132            SystemFontType::Monospace => vec![
1133                apple_fonts::MENLO,
1134                apple_fonts::MONACO,
1135            ],
1136            SystemFontType::MonospaceBold | SystemFontType::MonospaceItalic => vec![
1137                apple_fonts::MENLO, // Menlo Bold exists
1138                apple_fonts::MONACO,
1139            ],
1140            // Title: same strategy - use Helvetica Neue for bold
1141            SystemFontType::Title => vec![
1142                apple_fonts::SYSTEM_FONT,
1143                apple_fonts::HELVETICA_NEUE,
1144            ],
1145            SystemFontType::TitleBold => vec![
1146                apple_fonts::HELVETICA_NEUE, // Will be queried with weight=Bold
1147                apple_fonts::LUCIDA_GRANDE,
1148            ],
1149            SystemFontType::Menu => vec![
1150                apple_fonts::SYSTEM_FONT,
1151                apple_fonts::HELVETICA_NEUE,
1152            ],
1153            SystemFontType::Small => vec![
1154                apple_fonts::SYSTEM_FONT,
1155                apple_fonts::HELVETICA_NEUE,
1156            ],
1157            // Serif fonts - Georgia has bold variant
1158            SystemFontType::Serif => vec![
1159                apple_fonts::NEW_YORK,
1160                "Georgia",
1161                "Times New Roman",
1162            ],
1163            SystemFontType::SerifBold => vec![
1164                "Georgia", // Georgia Bold exists
1165                "Times New Roman",
1166            ],
1167        }
1168    }
1169    
1170    fn windows_fallback_chain(&self) -> Vec<&'static str> {
1171        match self {
1172            SystemFontType::Ui | SystemFontType::UiBold => vec![
1173                windows_fonts::SEGOE_UI_VARIABLE_TEXT,
1174                windows_fonts::SEGOE_UI,
1175                windows_fonts::TAHOMA,
1176            ],
1177            SystemFontType::Monospace | SystemFontType::MonospaceBold | SystemFontType::MonospaceItalic => vec![
1178                windows_fonts::CASCADIA_MONO,
1179                windows_fonts::CASCADIA_CODE,
1180                windows_fonts::CONSOLAS,
1181                windows_fonts::LUCIDA_CONSOLE,
1182                windows_fonts::COURIER_NEW,
1183            ],
1184            SystemFontType::Title | SystemFontType::TitleBold => vec![
1185                windows_fonts::SEGOE_UI_VARIABLE_DISPLAY,
1186                windows_fonts::SEGOE_UI,
1187            ],
1188            SystemFontType::Menu => vec![
1189                windows_fonts::SEGOE_UI,
1190                windows_fonts::TAHOMA,
1191            ],
1192            SystemFontType::Small => vec![
1193                windows_fonts::SEGOE_UI,
1194            ],
1195            SystemFontType::Serif | SystemFontType::SerifBold => vec![
1196                "Cambria",
1197                "Georgia",
1198                "Times New Roman",
1199            ],
1200        }
1201    }
1202    
1203    fn linux_fallback_chain(&self) -> Vec<&'static str> {
1204        match self {
1205            SystemFontType::Ui | SystemFontType::UiBold => vec![
1206                linux_fonts::CANTARELL,
1207                linux_fonts::UBUNTU,
1208                linux_fonts::NOTO_SANS,
1209                linux_fonts::DEJAVU_SANS,
1210                linux_fonts::LIBERATION_SANS,
1211                linux_fonts::SANS_SERIF,
1212            ],
1213            SystemFontType::Monospace | SystemFontType::MonospaceBold | SystemFontType::MonospaceItalic => vec![
1214                linux_fonts::UBUNTU_MONO,
1215                linux_fonts::HACK,
1216                linux_fonts::NOTO_MONO,
1217                linux_fonts::DEJAVU_SANS_MONO,
1218                linux_fonts::LIBERATION_MONO,
1219                linux_fonts::MONOSPACE,
1220            ],
1221            SystemFontType::Title | SystemFontType::TitleBold => vec![
1222                linux_fonts::CANTARELL,
1223                linux_fonts::UBUNTU,
1224                linux_fonts::NOTO_SANS,
1225            ],
1226            SystemFontType::Menu => vec![
1227                linux_fonts::CANTARELL,
1228                linux_fonts::UBUNTU,
1229                linux_fonts::NOTO_SANS,
1230            ],
1231            SystemFontType::Small => vec![
1232                linux_fonts::CANTARELL,
1233                linux_fonts::UBUNTU,
1234                linux_fonts::NOTO_SANS,
1235            ],
1236            SystemFontType::Serif | SystemFontType::SerifBold => vec![
1237                linux_fonts::NOTO_SERIF,
1238                linux_fonts::DEJAVU_SERIF,
1239                linux_fonts::LIBERATION_SERIF,
1240                linux_fonts::SERIF,
1241            ],
1242        }
1243    }
1244    
1245    fn android_fallback_chain(&self) -> Vec<&'static str> {
1246        match self {
1247            SystemFontType::Ui | SystemFontType::UiBold => vec!["Roboto", "Noto Sans"],
1248            SystemFontType::Monospace | SystemFontType::MonospaceBold | SystemFontType::MonospaceItalic => {
1249                vec!["Roboto Mono", "Droid Sans Mono", "monospace"]
1250            }
1251            SystemFontType::Title | SystemFontType::TitleBold => vec!["Roboto", "Noto Sans"],
1252            SystemFontType::Menu => vec!["Roboto"],
1253            SystemFontType::Small => vec!["Roboto"],
1254            SystemFontType::Serif | SystemFontType::SerifBold => vec!["Noto Serif", "Droid Serif", "serif"],
1255        }
1256    }
1257    
1258    fn generic_fallback_chain(&self) -> Vec<&'static str> {
1259        match self {
1260            SystemFontType::Ui | SystemFontType::UiBold => vec!["sans-serif"],
1261            SystemFontType::Monospace | SystemFontType::MonospaceBold | SystemFontType::MonospaceItalic => {
1262                vec!["monospace"]
1263            }
1264            SystemFontType::Title | SystemFontType::TitleBold => vec!["sans-serif"],
1265            SystemFontType::Menu => vec!["sans-serif"],
1266            SystemFontType::Small => vec!["sans-serif"],
1267            SystemFontType::Serif | SystemFontType::SerifBold => vec!["serif"],
1268        }
1269    }
1270}
1271
1272impl SystemStyle {
1273
1274    /// Format the SystemStyle as a human-readable JSON string for debugging.
1275    ///
1276    /// This does NOT use serde — it manually formats the most important fields
1277    /// so that they can be verified against OS-reported values in a test script.
1278    pub fn to_json_string(&self) -> AzString {
1279        use alloc::format;
1280
1281        fn opt_color(c: &OptionColorU) -> alloc::string::String {
1282            match c.as_ref() {
1283                Some(c) => format!("\"#{:02x}{:02x}{:02x}{:02x}\"", c.r, c.g, c.b, c.a),
1284                None => "null".into(),
1285            }
1286        }
1287        fn opt_str(s: &OptionString) -> alloc::string::String {
1288            match s.as_ref() {
1289                Some(s) => format!("\"{}\"", s.as_str()),
1290                None => "null".into(),
1291            }
1292        }
1293        fn opt_f32(v: &OptionF32) -> alloc::string::String {
1294            match v.into_option() {
1295                Some(v) => format!("{:.2}", v),
1296                None => "null".into(),
1297            }
1298        }
1299        fn opt_u16(v: &OptionU16) -> alloc::string::String {
1300            match v.into_option() {
1301                Some(v) => format!("{}", v),
1302                None => "null".into(),
1303            }
1304        }
1305        fn opt_px(v: &OptionPixelValue) -> alloc::string::String {
1306            match v.as_ref() {
1307                Some(v) => format!("{:.1}", v.to_pixels_internal(0.0, 0.0, 0.0)),
1308                None => "null".into(),
1309            }
1310        }
1311
1312        let tm = &self.metrics.titlebar;
1313        let inp = &self.input;
1314        let tr = &self.text_rendering;
1315        let acc = &self.accessibility;
1316        let sp = &self.scrollbar_preferences;
1317        let lnx = &self.linux;
1318        let vh = &self.visual_hints;
1319        let anim = &self.animation;
1320        let audio = &self.audio;
1321
1322        let json = format!(
1323r#"{{
1324  "theme": "{:?}",
1325  "platform": "{:?}",
1326  "os_version": "{:?}:{}",
1327  "language": "{}",
1328  "prefers_reduced_motion": {:?},
1329  "prefers_high_contrast": {:?},
1330  "colors": {{
1331    "text": {},
1332    "secondary_text": {},
1333    "tertiary_text": {},
1334    "background": {},
1335    "accent": {},
1336    "accent_text": {},
1337    "button_face": {},
1338    "button_text": {},
1339    "disabled_text": {},
1340    "window_background": {},
1341    "under_page_background": {},
1342    "selection_background": {},
1343    "selection_text": {},
1344    "selection_background_inactive": {},
1345    "selection_text_inactive": {},
1346    "link": {},
1347    "separator": {},
1348    "grid": {},
1349    "find_highlight": {},
1350    "sidebar_background": {},
1351    "sidebar_selection": {}
1352  }},
1353  "fonts": {{
1354    "ui_font": {},
1355    "ui_font_size": {},
1356    "monospace_font": {},
1357    "title_font": {},
1358    "menu_font": {},
1359    "small_font": {}
1360  }},
1361  "titlebar": {{
1362    "button_side": "{:?}",
1363    "height": {},
1364    "button_area_width": {},
1365    "padding_horizontal": {},
1366    "title_font": {},
1367    "title_font_size": {},
1368    "title_font_weight": {},
1369    "has_close": {},
1370    "has_minimize": {},
1371    "has_maximize": {},
1372    "has_fullscreen": {}
1373  }},
1374  "input": {{
1375    "double_click_time_ms": {},
1376    "double_click_distance_px": {:.1},
1377    "drag_threshold_px": {:.1},
1378    "caret_blink_rate_ms": {},
1379    "caret_width_px": {:.1},
1380    "wheel_scroll_lines": {},
1381    "hover_time_ms": {}
1382  }},
1383  "text_rendering": {{
1384    "font_smoothing_enabled": {},
1385    "subpixel_type": "{:?}",
1386    "font_smoothing_gamma": {},
1387    "increased_contrast": {}
1388  }},
1389  "accessibility": {{
1390    "prefers_bold_text": {},
1391    "prefers_larger_text": {},
1392    "text_scale_factor": {:.2},
1393    "prefers_high_contrast": {},
1394    "prefers_reduced_motion": {},
1395    "prefers_reduced_transparency": {},
1396    "screen_reader_active": {},
1397    "differentiate_without_color": {}
1398  }},
1399  "scrollbar_preferences": {{
1400    "visibility": "{:?}",
1401    "track_click": "{:?}"
1402  }},
1403  "linux": {{
1404    "gtk_theme": {},
1405    "icon_theme": {},
1406    "cursor_theme": {},
1407    "cursor_size": {},
1408    "titlebar_button_layout": {}
1409  }},
1410  "visual_hints": {{
1411    "show_button_images": {},
1412    "show_menu_images": {},
1413    "toolbar_style": "{:?}",
1414    "show_tooltips": {}
1415  }},
1416  "animation": {{
1417    "animations_enabled": {},
1418    "animation_duration_factor": {:.2},
1419    "focus_indicator_behavior": "{:?}"
1420  }},
1421  "audio": {{
1422    "event_sounds_enabled": {},
1423    "input_feedback_sounds_enabled": {}
1424  }}
1425}}"#,
1426            // top-level
1427            self.theme,
1428            self.platform,
1429            self.os_version.os, self.os_version.version_id,
1430            self.language.as_str(),
1431            self.prefers_reduced_motion,
1432            self.prefers_high_contrast,
1433            // colors
1434            opt_color(&self.colors.text),
1435            opt_color(&self.colors.secondary_text),
1436            opt_color(&self.colors.tertiary_text),
1437            opt_color(&self.colors.background),
1438            opt_color(&self.colors.accent),
1439            opt_color(&self.colors.accent_text),
1440            opt_color(&self.colors.button_face),
1441            opt_color(&self.colors.button_text),
1442            opt_color(&self.colors.disabled_text),
1443            opt_color(&self.colors.window_background),
1444            opt_color(&self.colors.under_page_background),
1445            opt_color(&self.colors.selection_background),
1446            opt_color(&self.colors.selection_text),
1447            opt_color(&self.colors.selection_background_inactive),
1448            opt_color(&self.colors.selection_text_inactive),
1449            opt_color(&self.colors.link),
1450            opt_color(&self.colors.separator),
1451            opt_color(&self.colors.grid),
1452            opt_color(&self.colors.find_highlight),
1453            opt_color(&self.colors.sidebar_background),
1454            opt_color(&self.colors.sidebar_selection),
1455            // fonts
1456            opt_str(&self.fonts.ui_font),
1457            opt_f32(&self.fonts.ui_font_size),
1458            opt_str(&self.fonts.monospace_font),
1459            opt_str(&self.fonts.title_font),
1460            opt_str(&self.fonts.menu_font),
1461            opt_str(&self.fonts.small_font),
1462            // titlebar
1463            tm.button_side,
1464            opt_px(&tm.height),
1465            opt_px(&tm.button_area_width),
1466            opt_px(&tm.padding_horizontal),
1467            opt_str(&tm.title_font),
1468            opt_f32(&tm.title_font_size),
1469            opt_u16(&tm.title_font_weight),
1470            tm.buttons.has_close,
1471            tm.buttons.has_minimize,
1472            tm.buttons.has_maximize,
1473            tm.buttons.has_fullscreen,
1474            // input
1475            inp.double_click_time_ms,
1476            inp.double_click_distance_px,
1477            inp.drag_threshold_px,
1478            inp.caret_blink_rate_ms,
1479            inp.caret_width_px,
1480            inp.wheel_scroll_lines,
1481            inp.hover_time_ms,
1482            // text_rendering
1483            tr.font_smoothing_enabled,
1484            tr.subpixel_type,
1485            tr.font_smoothing_gamma,
1486            tr.increased_contrast,
1487            // accessibility
1488            acc.prefers_bold_text,
1489            acc.prefers_larger_text,
1490            acc.text_scale_factor,
1491            acc.prefers_high_contrast,
1492            acc.prefers_reduced_motion,
1493            acc.prefers_reduced_transparency,
1494            acc.screen_reader_active,
1495            acc.differentiate_without_color,
1496            // scrollbar_preferences
1497            sp.visibility,
1498            sp.track_click,
1499            // linux
1500            opt_str(&lnx.gtk_theme),
1501            opt_str(&lnx.icon_theme),
1502            opt_str(&lnx.cursor_theme),
1503            lnx.cursor_size,
1504            opt_str(&lnx.titlebar_button_layout),
1505            // visual_hints
1506            vh.show_button_images,
1507            vh.show_menu_images,
1508            vh.toolbar_style,
1509            vh.show_tooltips,
1510            // animation
1511            anim.animations_enabled,
1512            anim.animation_duration_factor,
1513            anim.focus_indicator_behavior,
1514            // audio
1515            audio.event_sounds_enabled,
1516            audio.input_feedback_sounds_enabled,
1517        );
1518
1519        AzString::from(json)
1520    }
1521
1522    /// Returns a platform-appropriate default system style.
1523    ///
1524    /// This returns hard-coded defaults based on the target OS. For actual
1525    /// runtime detection of the user's theme, colors, and fonts, use the
1526    /// platform discovery in `azul-dll` (called automatically by `App::create()`).
1527    pub fn detect() -> Self {
1528        Self::default_for_platform()
1529    }
1530
1531    /// Returns hard-coded defaults for the current compile-time platform.
1532    pub fn default_for_platform() -> Self {
1533        #[cfg(target_os = "windows")]
1534        { defaults::windows_11_light() }
1535        #[cfg(target_os = "macos")]
1536        { defaults::macos_modern_light() }
1537        #[cfg(target_os = "linux")]
1538        { defaults::gnome_adwaita_light() }
1539        #[cfg(target_os = "android")]
1540        { defaults::android_material_light() }
1541        #[cfg(target_os = "ios")]
1542        { defaults::ios_light() }
1543        #[cfg(not(any(
1544            target_os = "linux",
1545            target_os = "windows",
1546            target_os = "macos",
1547            target_os = "android",
1548            target_os = "ios"
1549        )))]
1550        { Self::default() }
1551    }
1552
1553    /// Alias for `detect` - kept for internal compatibility, not exposed in FFI.
1554    #[inline(always)]
1555    pub fn new() -> Self {
1556        Self::detect()
1557    }
1558
1559    /// Create a CSS stylesheet for CSD (Client-Side Decorations) titlebar
1560    ///
1561    /// This generates CSS rules for the CSD titlebar using system colors,
1562    /// fonts, and metrics to match the native platform look. Returned rules
1563    /// carry `rule_priority::SYSTEM`.
1564    pub fn create_csd_stylesheet(&self) -> Css {
1565        use alloc::format;
1566
1567        use crate::parser2::new_from_str;
1568
1569        // Build CSS string from SystemStyle
1570        let mut css = String::new();
1571
1572        // Get system colors with fallbacks
1573        let bg_color = self
1574            .colors
1575            .window_background
1576            .as_option()
1577            .copied()
1578            .unwrap_or(ColorU::new_rgb(240, 240, 240));
1579        let text_color = self
1580            .colors
1581            .text
1582            .as_option()
1583            .copied()
1584            .unwrap_or(ColorU::new_rgb(0, 0, 0));
1585        let accent_color = self
1586            .colors
1587            .accent
1588            .as_option()
1589            .copied()
1590            .unwrap_or(ColorU::new_rgb(0, 120, 215));
1591        let border_color = match self.theme {
1592            Theme::Dark => ColorU::new_rgb(60, 60, 60),
1593            Theme::Light => ColorU::new_rgb(200, 200, 200),
1594        };
1595
1596        // Get system metrics with fallbacks
1597        let corner_radius = self
1598            .metrics
1599            .corner_radius
1600            .map(|px| {
1601                use crate::props::basic::pixel::DEFAULT_FONT_SIZE;
1602                format!("{}px", px.to_pixels_internal(1.0, DEFAULT_FONT_SIZE, DEFAULT_FONT_SIZE))
1603            })
1604            .unwrap_or_else(|| "4px".to_string());
1605
1606        // Titlebar container
1607        css.push_str(&format!(
1608            ".csd-titlebar {{ width: 100%; height: 32px; background: rgb({}, {}, {}); \
1609             border-bottom: 1px solid rgb({}, {}, {}); display: flex; flex-direction: row; \
1610             align-items: center; justify-content: space-between; padding: 0 8px; \
1611             cursor: grab; user-select: none; }} ",
1612            bg_color.r, bg_color.g, bg_color.b, border_color.r, border_color.g, border_color.b,
1613        ));
1614
1615        // Title text
1616        css.push_str(&format!(
1617            ".csd-title {{ color: rgb({}, {}, {}); font-size: 13px; flex-grow: 1; text-align: \
1618             center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; \
1619             user-select: none; }} ",
1620            text_color.r, text_color.g, text_color.b,
1621        ));
1622
1623        // Button container
1624        css.push_str(".csd-buttons { display: flex; flex-direction: row; gap: 4px; } ");
1625
1626        // Buttons
1627        css.push_str(&format!(
1628            ".csd-button {{ width: 32px; height: 24px; border-radius: {}; background: \
1629             transparent; color: rgb({}, {}, {}); font-size: 16px; line-height: 24px; text-align: \
1630             center; cursor: pointer; user-select: none; }} ",
1631            corner_radius, text_color.r, text_color.g, text_color.b,
1632        ));
1633
1634        // Button hover state
1635        let hover_color = match self.theme {
1636            Theme::Dark => ColorU::new_rgb(60, 60, 60),
1637            Theme::Light => ColorU::new_rgb(220, 220, 220),
1638        };
1639        css.push_str(&format!(
1640            ".csd-button:hover {{ background: rgb({}, {}, {}); }} ",
1641            hover_color.r, hover_color.g, hover_color.b,
1642        ));
1643
1644        // Close button hover (red on all platforms)
1645        css.push_str(
1646            ".csd-close:hover { background: rgb(232, 17, 35); color: rgb(255, 255, 255); } ",
1647        );
1648
1649        // Platform-specific button styling
1650        match self.platform {
1651            Platform::MacOs => {
1652                // macOS traffic light buttons (left side)
1653                css.push_str(".csd-buttons { position: absolute; left: 8px; } ");
1654                css.push_str(
1655                    ".csd-close { background: rgb(255, 95, 86); width: 12px; height: 12px; \
1656                     border-radius: 50%; } ",
1657                );
1658                css.push_str(
1659                    ".csd-minimize { background: rgb(255, 189, 46); width: 12px; height: 12px; \
1660                     border-radius: 50%; } ",
1661                );
1662                css.push_str(
1663                    ".csd-maximize { background: rgb(40, 201, 64); width: 12px; height: 12px; \
1664                     border-radius: 50%; } ",
1665                );
1666            }
1667            Platform::Linux(_) => {
1668                // Linux - title on left, buttons on right
1669                css.push_str(".csd-title { text-align: left; } ");
1670            }
1671            _ => {
1672                // Windows and others - standard layout
1673            }
1674        }
1675
1676        // Parse CSS string into a Css.
1677        let (mut parsed_css, _warnings) = new_from_str(&css);
1678        // Tag every rule as system-level so author CSS overrides win.
1679        for rule in parsed_css.rules.as_mut() {
1680            rule.priority = crate::css::rule_priority::SYSTEM;
1681        }
1682        parsed_css
1683    }
1684}
1685
1686/// Detect the Linux desktop environment from environment variables.
1687///
1688/// Checks `XDG_CURRENT_DESKTOP`, `DESKTOP_SESSION`, and specific env markers
1689/// to identify GNOME, KDE, XFCE, Cinnamon, MATE, Hyprland, Sway, i3, etc.
1690pub fn detect_linux_desktop_env() -> DesktopEnvironment {
1691    // Check XDG_CURRENT_DESKTOP first (most reliable)
1692    if let Ok(desktop) = std::env::var("XDG_CURRENT_DESKTOP") {
1693        let desktop_lower = desktop.to_lowercase();
1694        if desktop_lower.contains("gnome") {
1695            return DesktopEnvironment::Gnome;
1696        }
1697        if desktop_lower.contains("kde") || desktop_lower.contains("plasma") {
1698            return DesktopEnvironment::Kde;
1699        }
1700        if desktop_lower.contains("xfce") {
1701            return DesktopEnvironment::Other(AzString::from_const_str("XFCE"));
1702        }
1703        if desktop_lower.contains("unity") {
1704            return DesktopEnvironment::Other(AzString::from_const_str("Unity"));
1705        }
1706        if desktop_lower.contains("cinnamon") {
1707            return DesktopEnvironment::Other(AzString::from_const_str("Cinnamon"));
1708        }
1709        if desktop_lower.contains("mate") {
1710            return DesktopEnvironment::Other(AzString::from_const_str("MATE"));
1711        }
1712        if desktop_lower.contains("lxde") || desktop_lower.contains("lxqt") {
1713            return DesktopEnvironment::Other(AzString::from(desktop.to_uppercase()));
1714        }
1715        if desktop_lower.contains("budgie") {
1716            return DesktopEnvironment::Other(AzString::from_const_str("Budgie"));
1717        }
1718        if desktop_lower.contains("pantheon") {
1719            return DesktopEnvironment::Other(AzString::from_const_str("Pantheon"));
1720        }
1721        if desktop_lower.contains("deepin") {
1722            return DesktopEnvironment::Other(AzString::from_const_str("Deepin"));
1723        }
1724        if desktop_lower.contains("hyprland") {
1725            return DesktopEnvironment::Other(AzString::from_const_str("Hyprland"));
1726        }
1727        if desktop_lower.contains("sway") {
1728            return DesktopEnvironment::Other(AzString::from_const_str("Sway"));
1729        }
1730        if desktop_lower.contains("i3") {
1731            return DesktopEnvironment::Other(AzString::from_const_str("i3"));
1732        }
1733        return DesktopEnvironment::Other(AzString::from(desktop));
1734    }
1735
1736    // Check DESKTOP_SESSION as fallback
1737    if let Ok(session) = std::env::var("DESKTOP_SESSION") {
1738        let session_lower = session.to_lowercase();
1739        if session_lower.contains("gnome") {
1740            return DesktopEnvironment::Gnome;
1741        }
1742        if session_lower.contains("plasma") || session_lower.contains("kde") {
1743            return DesktopEnvironment::Kde;
1744        }
1745        if session_lower.contains("xfce") {
1746            return DesktopEnvironment::Other(AzString::from_const_str("XFCE"));
1747        }
1748        if session_lower.contains("cinnamon") {
1749            return DesktopEnvironment::Other(AzString::from_const_str("Cinnamon"));
1750        }
1751        return DesktopEnvironment::Other(AzString::from(session));
1752    }
1753
1754    // Check for specific environment markers
1755    if std::env::var("GNOME_DESKTOP_SESSION_ID").is_ok() {
1756        return DesktopEnvironment::Gnome;
1757    }
1758    if std::env::var("KDE_FULL_SESSION").is_ok() {
1759        return DesktopEnvironment::Kde;
1760    }
1761    if std::env::var("HYPRLAND_INSTANCE_SIGNATURE").is_ok() {
1762        return DesktopEnvironment::Other(AzString::from_const_str("Hyprland"));
1763    }
1764    if std::env::var("SWAYSOCK").is_ok() {
1765        return DesktopEnvironment::Other(AzString::from_const_str("Sway"));
1766    }
1767    if std::env::var("I3SOCK").is_ok() {
1768        return DesktopEnvironment::Other(AzString::from_const_str("i3"));
1769    }
1770
1771    DesktopEnvironment::Other(AzString::from_const_str("Unknown"))
1772}
1773
1774/// Detect the system language as a BCP 47 tag.
1775///
1776/// Checks `LANGUAGE`, `LC_ALL`, `LC_MESSAGES`, and `LANG` in priority order.
1777/// Returns `"en-US"` if detection fails. For runtime detection via native
1778/// OS APIs, the platform discovery in `azul-dll` overrides this.
1779pub fn detect_system_language() -> AzString {
1780    let env_vars = ["LANGUAGE", "LC_ALL", "LC_MESSAGES", "LANG"];
1781    for var in &env_vars {
1782        if let Ok(value) = std::env::var(var) {
1783            let value = value.trim();
1784            if value.is_empty() || value == "C" || value == "POSIX" {
1785                continue;
1786            }
1787            // Parse locale format: "de_DE.UTF-8" or "de_DE" or "de"
1788            let lang = value
1789                .split('.')  // Remove .UTF-8 suffix
1790                .next()
1791                .unwrap_or(value)
1792                .split(':')  // LANGUAGE can be "de:en_US:en"
1793                .next()
1794                .unwrap_or(value);
1795            if !lang.is_empty() {
1796                return AzString::from(lang.replace('_', "-"));
1797            }
1798        }
1799    }
1800    AzString::from_const_str("en-US")
1801}
1802
1803pub mod defaults {
1804    //! A collection of hard-coded system style defaults that mimic the appearance
1805    //! of various operating systems and desktop environments. These are used as a
1806    //! fallback when the "io" feature is disabled, ensuring deterministic styles
1807    //! for testing and environments where system calls are not desired.
1808
1809    use crate::{
1810        corety::{AzString, OptionF32, OptionString},
1811        dynamic_selector::{BoolCondition, OsVersion},
1812        props::{
1813            basic::{
1814                color::{ColorU, OptionColorU},
1815                pixel::{PixelValue, OptionPixelValue},
1816            },
1817            layout::{
1818                dimensions::LayoutWidth,
1819                spacing::{LayoutPaddingLeft, LayoutPaddingRight},
1820            },
1821            style::{
1822                background::StyleBackgroundContent,
1823                scrollbar::{
1824                    ComputedScrollbarStyle, OverflowScrolling, OverscrollBehavior, ScrollBehavior,
1825                    ScrollPhysics, ScrollbarInfo,
1826                    SCROLLBAR_ANDROID_DARK, SCROLLBAR_ANDROID_LIGHT, SCROLLBAR_CLASSIC_DARK,
1827                    SCROLLBAR_CLASSIC_LIGHT, SCROLLBAR_IOS_DARK, SCROLLBAR_IOS_LIGHT,
1828                    SCROLLBAR_MACOS_DARK, SCROLLBAR_MACOS_LIGHT, SCROLLBAR_WINDOWS_DARK,
1829                    SCROLLBAR_WINDOWS_LIGHT,
1830                },
1831            },
1832        },
1833        system::{
1834            DesktopEnvironment, Platform, SystemColors, SystemFonts, SystemMetrics, SystemStyle,
1835            Theme, IconStyleOptions, TitlebarMetrics,
1836        },
1837    };
1838
1839    // --- Custom Scrollbar Style Constants for Nostalgia ---
1840
1841    /// A scrollbar style mimicking the classic Windows 95/98/2000/XP look.
1842    pub const SCROLLBAR_WINDOWS_CLASSIC: ScrollbarInfo = ScrollbarInfo {
1843        width: LayoutWidth::Px(crate::props::basic::pixel::PixelValue::const_px(17)),
1844        padding_left: LayoutPaddingLeft {
1845            inner: crate::props::basic::pixel::PixelValue::const_px(0),
1846        },
1847        padding_right: LayoutPaddingRight {
1848            inner: crate::props::basic::pixel::PixelValue::const_px(0),
1849        },
1850        track: StyleBackgroundContent::Color(ColorU {
1851            r: 223,
1852            g: 223,
1853            b: 223,
1854            a: 255,
1855        }), // Scrollbar trough color
1856        thumb: StyleBackgroundContent::Color(ColorU {
1857            r: 208,
1858            g: 208,
1859            b: 208,
1860            a: 255,
1861        }), // Button face color
1862        button: StyleBackgroundContent::Color(ColorU {
1863            r: 208,
1864            g: 208,
1865            b: 208,
1866            a: 255,
1867        }),
1868        corner: StyleBackgroundContent::Color(ColorU {
1869            r: 223,
1870            g: 223,
1871            b: 223,
1872            a: 255,
1873        }),
1874        resizer: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
1875        clip_to_container_border: false,
1876        scroll_behavior: ScrollBehavior::Auto,
1877        overscroll_behavior_x: OverscrollBehavior::None,
1878        overscroll_behavior_y: OverscrollBehavior::None,
1879        overflow_scrolling: OverflowScrolling::Auto,
1880    };
1881
1882    /// A scrollbar style mimicking the macOS "Aqua" theme from the early 2000s.
1883    pub const SCROLLBAR_MACOS_AQUA: ScrollbarInfo = ScrollbarInfo {
1884        width: LayoutWidth::Px(crate::props::basic::pixel::PixelValue::const_px(15)),
1885        padding_left: LayoutPaddingLeft {
1886            inner: crate::props::basic::pixel::PixelValue::const_px(0),
1887        },
1888        padding_right: LayoutPaddingRight {
1889            inner: crate::props::basic::pixel::PixelValue::const_px(0),
1890        },
1891        track: StyleBackgroundContent::Color(ColorU {
1892            r: 238,
1893            g: 238,
1894            b: 238,
1895            a: 128,
1896        }), // Translucent track
1897        thumb: StyleBackgroundContent::Color(ColorU {
1898            r: 105,
1899            g: 173,
1900            b: 255,
1901            a: 255,
1902        }), // "Gel" blue
1903        button: StyleBackgroundContent::Color(ColorU {
1904            r: 105,
1905            g: 173,
1906            b: 255,
1907            a: 255,
1908        }),
1909        corner: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
1910        resizer: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
1911        clip_to_container_border: true,
1912        scroll_behavior: ScrollBehavior::Smooth,
1913        overscroll_behavior_x: OverscrollBehavior::Auto,
1914        overscroll_behavior_y: OverscrollBehavior::Auto,
1915        overflow_scrolling: OverflowScrolling::Auto,
1916    };
1917
1918    /// A scrollbar style mimicking the KDE Oxygen theme.
1919    pub const SCROLLBAR_KDE_OXYGEN: ScrollbarInfo = ScrollbarInfo {
1920        width: LayoutWidth::Px(crate::props::basic::pixel::PixelValue::const_px(14)),
1921        padding_left: LayoutPaddingLeft {
1922            inner: crate::props::basic::pixel::PixelValue::const_px(2),
1923        },
1924        padding_right: LayoutPaddingRight {
1925            inner: crate::props::basic::pixel::PixelValue::const_px(2),
1926        },
1927        track: StyleBackgroundContent::Color(ColorU {
1928            r: 242,
1929            g: 242,
1930            b: 242,
1931            a: 255,
1932        }),
1933        thumb: StyleBackgroundContent::Color(ColorU {
1934            r: 177,
1935            g: 177,
1936            b: 177,
1937            a: 255,
1938        }),
1939        button: StyleBackgroundContent::Color(ColorU {
1940            r: 216,
1941            g: 216,
1942            b: 216,
1943            a: 255,
1944        }),
1945        corner: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
1946        resizer: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
1947        clip_to_container_border: false,
1948        scroll_behavior: ScrollBehavior::Auto,
1949        overscroll_behavior_x: OverscrollBehavior::Auto,
1950        overscroll_behavior_y: OverscrollBehavior::Auto,
1951        overflow_scrolling: OverflowScrolling::Auto,
1952    };
1953
1954    /// Helper to convert a detailed `ScrollbarInfo` into the simplified `ComputedScrollbarStyle`.
1955    fn scrollbar_info_to_computed(info: &ScrollbarInfo) -> ComputedScrollbarStyle {
1956        ComputedScrollbarStyle {
1957            width: Some(info.width.clone()),
1958            thumb_color: match info.thumb {
1959                StyleBackgroundContent::Color(c) => Some(c),
1960                _ => None,
1961            },
1962            track_color: match info.track {
1963                StyleBackgroundContent::Color(c) => Some(c),
1964                _ => None,
1965            },
1966        }
1967    }
1968
1969    // --- Windows Styles ---
1970
1971    /// Windows 11 light mode defaults (Segoe UI Variable, WinUI 3 colors).
1972    pub fn windows_11_light() -> SystemStyle {
1973        SystemStyle {
1974            theme: Theme::Light,
1975            platform: Platform::Windows,
1976            colors: SystemColors {
1977                text: OptionColorU::Some(ColorU::new_rgb(0, 0, 0)),
1978                background: OptionColorU::Some(ColorU::new_rgb(243, 243, 243)),
1979                accent: OptionColorU::Some(ColorU::new_rgb(0, 95, 184)),
1980                window_background: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
1981                selection_background: OptionColorU::Some(ColorU::new_rgb(0, 120, 215)),
1982                selection_text: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
1983                ..Default::default()
1984            },
1985            fonts: SystemFonts {
1986                ui_font: OptionString::Some("Segoe UI Variable Text".into()),
1987                ui_font_size: OptionF32::Some(9.0),
1988                monospace_font: OptionString::Some("Consolas".into()),
1989                ..Default::default()
1990            },
1991            metrics: SystemMetrics {
1992                corner_radius: OptionPixelValue::Some(PixelValue::px(4.0)),
1993                border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
1994                button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(12.0)),
1995                button_padding_vertical: OptionPixelValue::Some(PixelValue::px(6.0)),
1996                titlebar: TitlebarMetrics::windows(),
1997            },
1998            scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_WINDOWS_LIGHT))),
1999            app_specific_stylesheet: None,
2000            icon_style: IconStyleOptions::default(),
2001            language: AzString::from_const_str("en-US"),
2002            os_version: OsVersion::WIN_11,
2003            prefers_reduced_motion: BoolCondition::False,
2004            prefers_high_contrast: BoolCondition::False,
2005            scroll_physics: ScrollPhysics::windows(),
2006            ..Default::default()
2007        }
2008    }
2009
2010    /// Windows 11 dark mode defaults (Segoe UI Variable, WinUI 3 dark colors).
2011    pub fn windows_11_dark() -> SystemStyle {
2012        SystemStyle {
2013            theme: Theme::Dark,
2014            platform: Platform::Windows,
2015            colors: SystemColors {
2016                text: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
2017                background: OptionColorU::Some(ColorU::new_rgb(32, 32, 32)),
2018                accent: OptionColorU::Some(ColorU::new_rgb(0, 120, 215)),
2019                window_background: OptionColorU::Some(ColorU::new_rgb(25, 25, 25)),
2020                selection_background: OptionColorU::Some(ColorU::new_rgb(0, 120, 215)),
2021                selection_text: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
2022                ..Default::default()
2023            },
2024            fonts: SystemFonts {
2025                ui_font: OptionString::Some("Segoe UI Variable Text".into()),
2026                ui_font_size: OptionF32::Some(9.0),
2027                monospace_font: OptionString::Some("Consolas".into()),
2028                ..Default::default()
2029            },
2030            metrics: SystemMetrics {
2031                corner_radius: OptionPixelValue::Some(PixelValue::px(4.0)),
2032                border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
2033                button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(12.0)),
2034                button_padding_vertical: OptionPixelValue::Some(PixelValue::px(6.0)),
2035                titlebar: TitlebarMetrics::windows(),
2036            },
2037            scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_WINDOWS_DARK))),
2038            app_specific_stylesheet: None,
2039            icon_style: IconStyleOptions::default(),
2040            language: AzString::from_const_str("en-US"),
2041            os_version: OsVersion::WIN_11,
2042            prefers_reduced_motion: BoolCondition::False,
2043            prefers_high_contrast: BoolCondition::False,
2044            scroll_physics: ScrollPhysics::windows(),
2045            ..Default::default()
2046        }
2047    }
2048
2049    /// Windows 7 Aero theme defaults (Segoe UI, classic Aero colors).
2050    pub fn windows_7_aero() -> SystemStyle {
2051        SystemStyle {
2052            theme: Theme::Light,
2053            platform: Platform::Windows,
2054            colors: SystemColors {
2055                text: OptionColorU::Some(ColorU::new_rgb(0, 0, 0)),
2056                background: OptionColorU::Some(ColorU::new_rgb(240, 240, 240)),
2057                accent: OptionColorU::Some(ColorU::new_rgb(51, 153, 255)),
2058                window_background: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
2059                selection_background: OptionColorU::Some(ColorU::new_rgb(51, 153, 255)),
2060                selection_text: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
2061                ..Default::default()
2062            },
2063            fonts: SystemFonts {
2064                ui_font: OptionString::Some("Segoe UI".into()),
2065                ui_font_size: OptionF32::Some(9.0),
2066                monospace_font: OptionString::Some("Consolas".into()),
2067                ..Default::default()
2068            },
2069            metrics: SystemMetrics {
2070                corner_radius: OptionPixelValue::Some(PixelValue::px(6.0)),
2071                border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
2072                button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(10.0)),
2073                button_padding_vertical: OptionPixelValue::Some(PixelValue::px(5.0)),
2074                titlebar: TitlebarMetrics::windows(),
2075            },
2076            scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_CLASSIC_LIGHT))),
2077            app_specific_stylesheet: None,
2078            icon_style: IconStyleOptions::default(),
2079            language: AzString::from_const_str("en-US"),
2080            os_version: OsVersion::WIN_7,
2081            prefers_reduced_motion: BoolCondition::False,
2082            prefers_high_contrast: BoolCondition::False,
2083            scroll_physics: ScrollPhysics::windows(),
2084            ..Default::default()
2085        }
2086    }
2087
2088    /// Windows XP Luna theme defaults (Tahoma, classic Luna blue).
2089    pub fn windows_xp_luna() -> SystemStyle {
2090        SystemStyle {
2091            theme: Theme::Light,
2092            platform: Platform::Windows,
2093            colors: SystemColors {
2094                text: OptionColorU::Some(ColorU::new_rgb(0, 0, 0)),
2095                background: OptionColorU::Some(ColorU::new_rgb(236, 233, 216)),
2096                accent: OptionColorU::Some(ColorU::new_rgb(49, 106, 197)),
2097                window_background: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
2098                selection_background: OptionColorU::Some(ColorU::new_rgb(49, 106, 197)),
2099                selection_text: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
2100                ..Default::default()
2101            },
2102            fonts: SystemFonts {
2103                ui_font: OptionString::Some("Tahoma".into()),
2104                ui_font_size: OptionF32::Some(8.0),
2105                monospace_font: OptionString::Some("Lucida Console".into()),
2106                ..Default::default()
2107            },
2108            metrics: SystemMetrics {
2109                corner_radius: OptionPixelValue::Some(PixelValue::px(3.0)),
2110                border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
2111                button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(8.0)),
2112                button_padding_vertical: OptionPixelValue::Some(PixelValue::px(4.0)),
2113                titlebar: TitlebarMetrics::windows(),
2114            },
2115            scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_WINDOWS_CLASSIC))),
2116            app_specific_stylesheet: None,
2117            icon_style: IconStyleOptions::default(),
2118            language: AzString::from_const_str("en-US"),
2119            os_version: OsVersion::WIN_XP,
2120            prefers_reduced_motion: BoolCondition::False,
2121            prefers_high_contrast: BoolCondition::False,
2122            scroll_physics: ScrollPhysics::windows(),
2123            ..Default::default()
2124        }
2125    }
2126
2127    // --- macOS Styles ---
2128
2129    /// Modern macOS light mode defaults (SF Pro, rounded corners).
2130    pub fn macos_modern_light() -> SystemStyle {
2131        SystemStyle {
2132            platform: Platform::MacOs,
2133            theme: Theme::Light,
2134            colors: SystemColors {
2135                text: OptionColorU::Some(ColorU::new(0, 0, 0, 221)),
2136                background: OptionColorU::Some(ColorU::new_rgb(242, 242, 247)),
2137                accent: OptionColorU::Some(ColorU::new_rgb(0, 122, 255)),
2138                window_background: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
2139                // Default macOS selection uses accent color with transparency
2140                selection_background: OptionColorU::Some(ColorU::new(0, 122, 255, 128)),
2141                selection_text: OptionColorU::Some(ColorU::new_rgb(0, 0, 0)),
2142                ..Default::default()
2143            },
2144            fonts: SystemFonts {
2145                ui_font: OptionString::Some(".SF NS".into()),
2146                ui_font_size: OptionF32::Some(13.0),
2147                monospace_font: OptionString::Some("Menlo".into()),
2148                ..Default::default()
2149            },
2150            metrics: SystemMetrics {
2151                corner_radius: OptionPixelValue::Some(PixelValue::px(8.0)),
2152                border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
2153                button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(16.0)),
2154                button_padding_vertical: OptionPixelValue::Some(PixelValue::px(6.0)),
2155                titlebar: TitlebarMetrics::macos(),
2156            },
2157            scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_MACOS_LIGHT))),
2158            app_specific_stylesheet: None,
2159            icon_style: IconStyleOptions::default(),
2160            language: AzString::from_const_str("en-US"),
2161            os_version: OsVersion::MACOS_SONOMA,
2162            prefers_reduced_motion: BoolCondition::False,
2163            prefers_high_contrast: BoolCondition::False,
2164            scroll_physics: ScrollPhysics::macos(),
2165            ..Default::default()
2166        }
2167    }
2168
2169    /// Modern macOS dark mode defaults (SF Pro, dark background).
2170    pub fn macos_modern_dark() -> SystemStyle {
2171        SystemStyle {
2172            platform: Platform::MacOs,
2173            theme: Theme::Dark,
2174            colors: SystemColors {
2175                text: OptionColorU::Some(ColorU::new(255, 255, 255, 221)),
2176                background: OptionColorU::Some(ColorU::new_rgb(28, 28, 30)),
2177                accent: OptionColorU::Some(ColorU::new_rgb(10, 132, 255)),
2178                window_background: OptionColorU::Some(ColorU::new_rgb(44, 44, 46)),
2179                // Default macOS selection uses accent color with transparency
2180                selection_background: OptionColorU::Some(ColorU::new(10, 132, 255, 128)),
2181                selection_text: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
2182                ..Default::default()
2183            },
2184            fonts: SystemFonts {
2185                ui_font: OptionString::Some(".SF NS".into()),
2186                ui_font_size: OptionF32::Some(13.0),
2187                monospace_font: OptionString::Some("SF Mono".into()),
2188                monospace_font_size: OptionF32::Some(12.0),
2189                title_font: OptionString::Some(".SF NS".into()),
2190                title_font_size: OptionF32::Some(13.0),
2191                menu_font: OptionString::Some(".SF NS".into()),
2192                menu_font_size: OptionF32::Some(13.0),
2193                small_font: OptionString::Some(".SF NS".into()),
2194                small_font_size: OptionF32::Some(11.0),
2195                ..Default::default()
2196            },
2197            metrics: SystemMetrics {
2198                corner_radius: OptionPixelValue::Some(PixelValue::px(8.0)),
2199                border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
2200                button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(16.0)),
2201                button_padding_vertical: OptionPixelValue::Some(PixelValue::px(6.0)),
2202                titlebar: TitlebarMetrics::macos(),
2203            },
2204            scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_MACOS_DARK))),
2205            app_specific_stylesheet: None,
2206            icon_style: IconStyleOptions::default(),
2207            language: AzString::from_const_str("en-US"),
2208            os_version: OsVersion::MACOS_SONOMA,
2209            prefers_reduced_motion: BoolCondition::False,
2210            prefers_high_contrast: BoolCondition::False,
2211            scroll_physics: ScrollPhysics::macos(),
2212            ..Default::default()
2213        }
2214    }
2215
2216    /// Classic macOS Aqua theme defaults (Lucida Grande, gel scrollbars).
2217    pub fn macos_aqua() -> SystemStyle {
2218        SystemStyle {
2219            platform: Platform::MacOs,
2220            theme: Theme::Light,
2221            colors: SystemColors {
2222                text: OptionColorU::Some(ColorU::new_rgb(0, 0, 0)),
2223                background: OptionColorU::Some(ColorU::new_rgb(229, 229, 229)),
2224                accent: OptionColorU::Some(ColorU::new_rgb(63, 128, 234)),
2225                window_background: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
2226                ..Default::default()
2227            },
2228            fonts: SystemFonts {
2229                ui_font: OptionString::Some("Lucida Grande".into()),
2230                ui_font_size: OptionF32::Some(13.0),
2231                monospace_font: OptionString::Some("Monaco".into()),
2232                monospace_font_size: OptionF32::Some(12.0),
2233                ..Default::default()
2234            },
2235            metrics: SystemMetrics {
2236                corner_radius: OptionPixelValue::Some(PixelValue::px(12.0)),
2237                border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
2238                button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(16.0)),
2239                button_padding_vertical: OptionPixelValue::Some(PixelValue::px(6.0)),
2240                titlebar: TitlebarMetrics::macos(),
2241            },
2242            scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_MACOS_AQUA))),
2243            app_specific_stylesheet: None,
2244            icon_style: IconStyleOptions::default(),
2245            language: AzString::from_const_str("en-US"),
2246            os_version: OsVersion::MACOS_TIGER,
2247            prefers_reduced_motion: BoolCondition::False,
2248            prefers_high_contrast: BoolCondition::False,
2249            scroll_physics: ScrollPhysics::macos(),
2250            ..Default::default()
2251        }
2252    }
2253
2254    // --- Linux Styles ---
2255
2256    /// GNOME Adwaita light theme defaults (Cantarell font).
2257    pub fn gnome_adwaita_light() -> SystemStyle {
2258        SystemStyle {
2259            platform: Platform::Linux(DesktopEnvironment::Gnome),
2260            theme: Theme::Light,
2261            colors: SystemColors {
2262                text: OptionColorU::Some(ColorU::new_rgb(46, 52, 54)),
2263                background: OptionColorU::Some(ColorU::new_rgb(249, 249, 249)),
2264                accent: OptionColorU::Some(ColorU::new_rgb(53, 132, 228)),
2265                window_background: OptionColorU::Some(ColorU::new_rgb(237, 237, 237)),
2266                ..Default::default()
2267            },
2268            fonts: SystemFonts {
2269                ui_font: OptionString::Some("Cantarell".into()),
2270                ui_font_size: OptionF32::Some(11.0),
2271                monospace_font: OptionString::Some("Monospace".into()),
2272                ..Default::default()
2273            },
2274            metrics: SystemMetrics {
2275                corner_radius: OptionPixelValue::Some(PixelValue::px(4.0)),
2276                border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
2277                button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(12.0)),
2278                button_padding_vertical: OptionPixelValue::Some(PixelValue::px(8.0)),
2279                titlebar: TitlebarMetrics::linux_gnome(),
2280            },
2281            scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_CLASSIC_LIGHT))),
2282            app_specific_stylesheet: None,
2283            icon_style: IconStyleOptions::default(),
2284            language: AzString::from_const_str("en-US"),
2285            os_version: OsVersion::LINUX_6_0,
2286            prefers_reduced_motion: BoolCondition::False,
2287            prefers_high_contrast: BoolCondition::False,
2288            ..Default::default()
2289        }
2290    }
2291
2292    /// GNOME Adwaita dark theme defaults (Cantarell font, dark background).
2293    pub fn gnome_adwaita_dark() -> SystemStyle {
2294        SystemStyle {
2295            platform: Platform::Linux(DesktopEnvironment::Gnome),
2296            theme: Theme::Dark,
2297            colors: SystemColors {
2298                text: OptionColorU::Some(ColorU::new_rgb(238, 238, 236)),
2299                background: OptionColorU::Some(ColorU::new_rgb(36, 36, 36)),
2300                accent: OptionColorU::Some(ColorU::new_rgb(53, 132, 228)),
2301                window_background: OptionColorU::Some(ColorU::new_rgb(48, 48, 48)),
2302                ..Default::default()
2303            },
2304            fonts: SystemFonts {
2305                ui_font: OptionString::Some("Cantarell".into()),
2306                ui_font_size: OptionF32::Some(11.0),
2307                monospace_font: OptionString::Some("Monospace".into()),
2308                ..Default::default()
2309            },
2310            metrics: SystemMetrics {
2311                corner_radius: OptionPixelValue::Some(PixelValue::px(4.0)),
2312                border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
2313                button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(12.0)),
2314                button_padding_vertical: OptionPixelValue::Some(PixelValue::px(8.0)),
2315                titlebar: TitlebarMetrics::linux_gnome(),
2316            },
2317            scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_CLASSIC_DARK))),
2318            app_specific_stylesheet: None,
2319            icon_style: IconStyleOptions::default(),
2320            language: AzString::from_const_str("en-US"),
2321            os_version: OsVersion::LINUX_6_0,
2322            prefers_reduced_motion: BoolCondition::False,
2323            prefers_high_contrast: BoolCondition::False,
2324            ..Default::default()
2325        }
2326    }
2327
2328    /// GTK2 Clearlooks theme defaults (DejaVu Sans, orange accent).
2329    pub fn gtk2_clearlooks() -> SystemStyle {
2330        SystemStyle {
2331            platform: Platform::Linux(DesktopEnvironment::Gnome),
2332            theme: Theme::Light,
2333            colors: SystemColors {
2334                text: OptionColorU::Some(ColorU::new_rgb(0, 0, 0)),
2335                background: OptionColorU::Some(ColorU::new_rgb(239, 239, 239)),
2336                accent: OptionColorU::Some(ColorU::new_rgb(245, 121, 0)),
2337                ..Default::default()
2338            },
2339            fonts: SystemFonts {
2340                ui_font: OptionString::Some("DejaVu Sans".into()),
2341                ui_font_size: OptionF32::Some(10.0),
2342                monospace_font: OptionString::Some("DejaVu Sans Mono".into()),
2343                ..Default::default()
2344            },
2345            metrics: SystemMetrics {
2346                corner_radius: OptionPixelValue::Some(PixelValue::px(4.0)),
2347                border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
2348                button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(10.0)),
2349                button_padding_vertical: OptionPixelValue::Some(PixelValue::px(6.0)),
2350                titlebar: TitlebarMetrics::linux_gnome(),
2351            },
2352            scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_CLASSIC_LIGHT))),
2353            app_specific_stylesheet: None,
2354            icon_style: IconStyleOptions::default(),
2355            language: AzString::from_const_str("en-US"),
2356            os_version: OsVersion::LINUX_2_6,
2357            prefers_reduced_motion: BoolCondition::False,
2358            prefers_high_contrast: BoolCondition::False,
2359            ..Default::default()
2360        }
2361    }
2362
2363    /// KDE Breeze light theme defaults (Noto Sans, Oxygen scrollbars).
2364    pub fn kde_breeze_light() -> SystemStyle {
2365        SystemStyle {
2366            platform: Platform::Linux(DesktopEnvironment::Kde),
2367            theme: Theme::Light,
2368            colors: SystemColors {
2369                text: OptionColorU::Some(ColorU::new_rgb(31, 36, 39)),
2370                background: OptionColorU::Some(ColorU::new_rgb(239, 240, 241)),
2371                accent: OptionColorU::Some(ColorU::new_rgb(61, 174, 233)),
2372                ..Default::default()
2373            },
2374            fonts: SystemFonts {
2375                ui_font: OptionString::Some("Noto Sans".into()),
2376                ui_font_size: OptionF32::Some(10.0),
2377                monospace_font: OptionString::Some("Hack".into()),
2378                ..Default::default()
2379            },
2380            metrics: SystemMetrics {
2381                corner_radius: OptionPixelValue::Some(PixelValue::px(4.0)),
2382                border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
2383                button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(12.0)),
2384                button_padding_vertical: OptionPixelValue::Some(PixelValue::px(6.0)),
2385                titlebar: TitlebarMetrics::linux_gnome(),
2386            },
2387            scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_KDE_OXYGEN))),
2388            app_specific_stylesheet: None,
2389            icon_style: IconStyleOptions::default(),
2390            language: AzString::from_const_str("en-US"),
2391            os_version: OsVersion::LINUX_6_0,
2392            prefers_reduced_motion: BoolCondition::False,
2393            prefers_high_contrast: BoolCondition::False,
2394            ..Default::default()
2395        }
2396    }
2397
2398    // --- Mobile Styles ---
2399
2400    /// Android Material Design light theme defaults (Roboto font).
2401    pub fn android_material_light() -> SystemStyle {
2402        SystemStyle {
2403            platform: Platform::Android,
2404            theme: Theme::Light,
2405            colors: SystemColors {
2406                text: OptionColorU::Some(ColorU::new_rgb(0, 0, 0)),
2407                background: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
2408                accent: OptionColorU::Some(ColorU::new_rgb(98, 0, 238)),
2409                ..Default::default()
2410            },
2411            fonts: SystemFonts {
2412                ui_font: OptionString::Some("Roboto".into()),
2413                ui_font_size: OptionF32::Some(14.0),
2414                monospace_font: OptionString::Some("Droid Sans Mono".into()),
2415                ..Default::default()
2416            },
2417            metrics: SystemMetrics {
2418                corner_radius: OptionPixelValue::Some(PixelValue::px(12.0)),
2419                border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
2420                button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(16.0)),
2421                button_padding_vertical: OptionPixelValue::Some(PixelValue::px(10.0)),
2422                titlebar: TitlebarMetrics::android(),
2423            },
2424            scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_ANDROID_LIGHT))),
2425            app_specific_stylesheet: None,
2426            icon_style: IconStyleOptions::default(),
2427            language: AzString::from_const_str("en-US"),
2428            os_version: OsVersion::ANDROID_14,
2429            prefers_reduced_motion: BoolCondition::False,
2430            prefers_high_contrast: BoolCondition::False,
2431            scroll_physics: ScrollPhysics::android(),
2432            ..Default::default()
2433        }
2434    }
2435
2436    /// Android Holo dark theme defaults (Roboto font, dark background).
2437    pub fn android_holo_dark() -> SystemStyle {
2438        SystemStyle {
2439            platform: Platform::Android,
2440            theme: Theme::Dark,
2441            colors: SystemColors {
2442                text: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
2443                background: OptionColorU::Some(ColorU::new_rgb(0, 0, 0)),
2444                accent: OptionColorU::Some(ColorU::new_rgb(51, 181, 229)),
2445                ..Default::default()
2446            },
2447            fonts: SystemFonts {
2448                ui_font: OptionString::Some("Roboto".into()),
2449                ui_font_size: OptionF32::Some(14.0),
2450                monospace_font: OptionString::Some("Droid Sans Mono".into()),
2451                ..Default::default()
2452            },
2453            metrics: SystemMetrics {
2454                corner_radius: OptionPixelValue::Some(PixelValue::px(2.0)),
2455                border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
2456                button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(12.0)),
2457                button_padding_vertical: OptionPixelValue::Some(PixelValue::px(8.0)),
2458                titlebar: TitlebarMetrics::android(),
2459            },
2460            scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_ANDROID_DARK))),
2461            app_specific_stylesheet: None,
2462            icon_style: IconStyleOptions::default(),
2463            language: AzString::from_const_str("en-US"),
2464            os_version: OsVersion::ANDROID_ICE_CREAM_SANDWICH,
2465            prefers_reduced_motion: BoolCondition::False,
2466            prefers_high_contrast: BoolCondition::False,
2467            scroll_physics: ScrollPhysics::android(),
2468            ..Default::default()
2469        }
2470    }
2471
2472    /// iOS light theme defaults (SF UI font, rounded corners).
2473    pub fn ios_light() -> SystemStyle {
2474        SystemStyle {
2475            platform: Platform::Ios,
2476            theme: Theme::Light,
2477            colors: SystemColors {
2478                text: OptionColorU::Some(ColorU::new_rgb(0, 0, 0)),
2479                background: OptionColorU::Some(ColorU::new_rgb(242, 242, 247)),
2480                accent: OptionColorU::Some(ColorU::new_rgb(0, 122, 255)),
2481                ..Default::default()
2482            },
2483            fonts: SystemFonts {
2484                ui_font: OptionString::Some(".SFUI-Display-Regular".into()),
2485                ui_font_size: OptionF32::Some(17.0),
2486                monospace_font: OptionString::Some("Menlo".into()),
2487                ..Default::default()
2488            },
2489            metrics: SystemMetrics {
2490                corner_radius: OptionPixelValue::Some(PixelValue::px(10.0)),
2491                border_width: OptionPixelValue::Some(PixelValue::px(0.5)),
2492                button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(20.0)),
2493                button_padding_vertical: OptionPixelValue::Some(PixelValue::px(12.0)),
2494                titlebar: TitlebarMetrics::ios(),
2495            },
2496            scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_IOS_LIGHT))),
2497            app_specific_stylesheet: None,
2498            icon_style: IconStyleOptions::default(),
2499            language: AzString::from_const_str("en-US"),
2500            os_version: OsVersion::IOS_17,
2501            prefers_reduced_motion: BoolCondition::False,
2502            prefers_high_contrast: BoolCondition::False,
2503            scroll_physics: ScrollPhysics::ios(),
2504            ..Default::default()
2505        }
2506    }
2507}