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