1#![cfg(feature = "parser")]
19
20use alloc::{
21 boxed::Box,
22 string::{String, ToString},
23 vec::Vec,
24};
25#[cfg(feature = "io")]
26use core::time::Duration;
27
28use crate::{
29 corety::{AzString, OptionF32, OptionString, OptionU16},
30 css::Stylesheet,
31 parser2::{new_from_str, CssParseWarnMsg},
32 props::{
33 basic::{
34 color::{parse_css_color, ColorU, OptionColorU},
35 pixel::{PixelValue, OptionPixelValue},
36 },
37 style::scrollbar::{ComputedScrollbarStyle, OverscrollBehavior, ScrollBehavior, ScrollPhysics},
38 },
39};
40
41#[cfg(all(feature = "system", target_os = "macos"))]
44#[path = "system_native_macos.rs"]
45mod native_macos;
46
47#[cfg(all(feature = "system", target_os = "windows"))]
48#[path = "system_native_windows.rs"]
49mod native_windows;
50
51#[cfg(all(feature = "system", target_os = "linux"))]
52#[path = "system_native_linux.rs"]
53mod native_linux;
54
55#[derive(Debug, Default, Clone, PartialEq, Eq)]
59#[repr(C, u8)]
60pub enum Platform {
61 Windows,
62 MacOs,
63 Linux(DesktopEnvironment),
64 Android,
65 Ios,
66 #[default]
67 Unknown,
68}
69
70impl Platform {
71 #[inline]
73 pub fn current() -> Self {
74 #[cfg(target_os = "macos")]
75 { Platform::MacOs }
76 #[cfg(target_os = "windows")]
77 { Platform::Windows }
78 #[cfg(target_os = "linux")]
79 { Platform::Linux(DesktopEnvironment::Other(AzString::from_const_str("unknown"))) }
80 #[cfg(target_os = "android")]
81 { Platform::Android }
82 #[cfg(target_os = "ios")]
83 { Platform::Ios }
84 #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux", target_os = "android", target_os = "ios")))]
85 { Platform::Unknown }
86 }
87}
88
89#[derive(Debug, Clone, PartialEq, Eq)]
91#[repr(C, u8)]
92pub enum DesktopEnvironment {
93 Gnome,
94 Kde,
95 Other(AzString),
96}
97
98#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
100#[repr(C)]
101pub enum Theme {
102 #[default]
103 Light,
104 Dark,
105}
106
107#[derive(Debug, Default, Clone, PartialEq)]
109#[repr(C)]
110pub struct SystemStyle {
111 pub theme: Theme,
112 pub platform: Platform,
113 pub os_version: crate::dynamic_selector::OsVersion,
115 pub colors: SystemColors,
116 pub fonts: SystemFonts,
117 pub metrics: SystemMetrics,
118 pub language: AzString,
121 pub prefers_reduced_motion: crate::dynamic_selector::BoolCondition,
123 pub prefers_high_contrast: crate::dynamic_selector::BoolCondition,
125 pub app_specific_stylesheet: Option<Box<Stylesheet>>,
130 pub icon_style: IconStyleOptions,
132 pub scrollbar: Option<Box<ComputedScrollbarStyle>>,
134 pub accessibility: AccessibilitySettings,
136 pub input: InputMetrics,
138 pub text_rendering: TextRenderingHints,
140 pub focus_visuals: FocusVisuals,
142 pub scrollbar_preferences: ScrollbarPreferences,
144 pub linux: LinuxCustomization,
146 pub visual_hints: VisualHints,
148 pub animation: AnimationMetrics,
150 pub audio: AudioMetrics,
152 pub scroll_physics: ScrollPhysics,
156}
157
158#[derive(Debug, Default, Clone, PartialEq)]
163#[repr(C)]
164pub struct IconStyleOptions {
165 pub prefer_grayscale: bool,
168 pub tint_color: OptionColorU,
171 pub inherit_text_color: bool,
174}
175
176#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
187#[repr(C)]
188pub enum SystemFontType {
189 Ui,
191 UiBold,
193 Monospace,
195 MonospaceBold,
197 MonospaceItalic,
199 Title,
201 TitleBold,
203 Menu,
205 Small,
207 Serif,
209 SerifBold,
211}
212
213impl Default for SystemFontType {
214 fn default() -> Self {
215 SystemFontType::Ui
216 }
217}
218
219impl SystemFontType {
220 pub fn from_css_str(s: &str) -> Option<Self> {
230 let s = s.trim();
231 if !s.starts_with("system:") {
232 return None;
233 }
234 let rest = &s[7..]; match rest {
236 "ui" => Some(SystemFontType::Ui),
237 "ui:bold" => Some(SystemFontType::UiBold),
238 "monospace" => Some(SystemFontType::Monospace),
239 "monospace:bold" => Some(SystemFontType::MonospaceBold),
240 "monospace:italic" => Some(SystemFontType::MonospaceItalic),
241 "title" => Some(SystemFontType::Title),
242 "title:bold" => Some(SystemFontType::TitleBold),
243 "menu" => Some(SystemFontType::Menu),
244 "small" => Some(SystemFontType::Small),
245 "serif" => Some(SystemFontType::Serif),
246 "serif:bold" => Some(SystemFontType::SerifBold),
247 _ => None,
248 }
249 }
250
251 pub fn as_css_str(&self) -> &'static str {
253 match self {
254 SystemFontType::Ui => "system:ui",
255 SystemFontType::UiBold => "system:ui:bold",
256 SystemFontType::Monospace => "system:monospace",
257 SystemFontType::MonospaceBold => "system:monospace:bold",
258 SystemFontType::MonospaceItalic => "system:monospace:italic",
259 SystemFontType::Title => "system:title",
260 SystemFontType::TitleBold => "system:title:bold",
261 SystemFontType::Menu => "system:menu",
262 SystemFontType::Small => "system:small",
263 SystemFontType::Serif => "system:serif",
264 SystemFontType::SerifBold => "system:serif:bold",
265 }
266 }
267
268 pub fn is_bold(&self) -> bool {
271 matches!(
272 self,
273 SystemFontType::UiBold
274 | SystemFontType::MonospaceBold
275 | SystemFontType::TitleBold
276 | SystemFontType::SerifBold
277 )
278 }
279
280 pub fn is_italic(&self) -> bool {
282 matches!(self, SystemFontType::MonospaceItalic)
283 }
284}
285
286#[derive(Debug, Default, Clone, PartialEq)]
294#[repr(C)]
295pub struct AccessibilitySettings {
296 pub prefers_bold_text: bool,
301 pub prefers_larger_text: bool,
306 pub text_scale_factor: f32,
308 pub prefers_high_contrast: bool,
313 pub prefers_reduced_motion: bool,
318 pub prefers_reduced_transparency: bool,
323 pub screen_reader_active: bool,
325 pub differentiate_without_color: bool,
328}
329
330#[derive(Debug, Default, Clone, PartialEq)]
339#[repr(C)]
340pub struct SystemColors {
341 pub text: OptionColorU,
344 pub secondary_text: OptionColorU,
346 pub tertiary_text: OptionColorU,
348 pub background: OptionColorU,
350
351 pub accent: OptionColorU,
354 pub accent_text: OptionColorU,
356
357 pub button_face: OptionColorU,
360 pub button_text: OptionColorU,
362 pub disabled_text: OptionColorU,
364
365 pub window_background: OptionColorU,
368 pub under_page_background: OptionColorU,
370
371 pub selection_background: OptionColorU,
374 pub selection_text: OptionColorU,
376 pub selection_background_inactive: OptionColorU,
379 pub selection_text_inactive: OptionColorU,
381
382 pub link: OptionColorU,
385 pub separator: OptionColorU,
387 pub grid: OptionColorU,
389 pub find_highlight: OptionColorU,
391
392 pub sidebar_background: OptionColorU,
395 pub sidebar_selection: OptionColorU,
397}
398
399#[derive(Debug, Default, Clone, PartialEq)]
405#[repr(C)]
406pub struct SystemFonts {
407 pub ui_font: OptionString,
412 pub ui_font_size: OptionF32,
414 pub monospace_font: OptionString,
419 pub monospace_font_size: OptionF32,
421 pub ui_font_bold: OptionString,
423 pub title_font: OptionString,
425 pub title_font_size: OptionF32,
427 pub menu_font: OptionString,
429 pub menu_font_size: OptionF32,
431 pub small_font: OptionString,
433 pub small_font_size: OptionF32,
435}
436
437#[derive(Debug, Default, Clone, PartialEq)]
439#[repr(C)]
440pub struct SystemMetrics {
441 pub corner_radius: OptionPixelValue,
443 pub border_width: OptionPixelValue,
445 pub button_padding_horizontal: OptionPixelValue,
447 pub button_padding_vertical: OptionPixelValue,
449 pub titlebar: TitlebarMetrics,
451}
452
453#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
455#[repr(C)]
456pub enum TitlebarButtonSide {
457 Left,
459 #[default]
461 Right,
462}
463
464#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
466#[repr(C)]
467pub struct TitlebarButtons {
468 pub has_close: bool,
470 pub has_minimize: bool,
472 pub has_maximize: bool,
474 pub has_fullscreen: bool,
476}
477
478impl Default for TitlebarButtons {
479 fn default() -> Self {
480 Self {
481 has_close: true,
482 has_minimize: true,
483 has_maximize: true,
484 has_fullscreen: false,
485 }
486 }
487}
488
489#[derive(Debug, Default, Clone, Copy, PartialEq)]
495#[repr(C)]
496pub struct SafeAreaInsets {
497 pub top: OptionPixelValue,
499 pub bottom: OptionPixelValue,
501 pub left: OptionPixelValue,
503 pub right: OptionPixelValue,
505}
506
507#[derive(Debug, Clone, PartialEq)]
512#[repr(C)]
513pub struct TitlebarMetrics {
514 pub button_side: TitlebarButtonSide,
516 pub buttons: TitlebarButtons,
518 pub height: OptionPixelValue,
520 pub button_area_width: OptionPixelValue,
523 pub padding_horizontal: OptionPixelValue,
525 pub safe_area: SafeAreaInsets,
527 pub title_font: OptionString,
529 pub title_font_size: OptionF32,
531 pub title_font_weight: OptionU16,
533}
534
535impl Default for TitlebarMetrics {
536 fn default() -> Self {
537 Self {
538 button_side: TitlebarButtonSide::Right,
539 buttons: TitlebarButtons::default(),
540 height: OptionPixelValue::Some(PixelValue::px(32.0)),
541 button_area_width: OptionPixelValue::Some(PixelValue::px(100.0)),
542 padding_horizontal: OptionPixelValue::Some(PixelValue::px(8.0)),
543 safe_area: SafeAreaInsets::default(),
544 title_font: OptionString::None,
545 title_font_size: OptionF32::Some(13.0),
546 title_font_weight: OptionU16::Some(600), }
548 }
549}
550
551impl TitlebarMetrics {
552 pub fn windows() -> Self {
554 Self {
555 button_side: TitlebarButtonSide::Right,
556 buttons: TitlebarButtons {
557 has_close: true,
558 has_minimize: true,
559 has_maximize: true,
560 has_fullscreen: false,
561 },
562 height: OptionPixelValue::Some(PixelValue::px(32.0)),
563 button_area_width: OptionPixelValue::Some(PixelValue::px(138.0)), padding_horizontal: OptionPixelValue::Some(PixelValue::px(8.0)),
565 safe_area: SafeAreaInsets::default(),
566 title_font: OptionString::Some("Segoe UI Variable Text".into()),
567 title_font_size: OptionF32::Some(12.0),
568 title_font_weight: OptionU16::Some(400), }
570 }
571
572 pub fn macos() -> Self {
574 Self {
575 button_side: TitlebarButtonSide::Left,
576 buttons: TitlebarButtons {
577 has_close: true,
578 has_minimize: true,
579 has_maximize: false, has_fullscreen: true,
581 },
582 height: OptionPixelValue::Some(PixelValue::px(28.0)),
583 button_area_width: OptionPixelValue::Some(PixelValue::px(78.0)), padding_horizontal: OptionPixelValue::Some(PixelValue::px(8.0)),
585 safe_area: SafeAreaInsets::default(),
586 title_font: OptionString::Some(".SF NS".into()),
587 title_font_size: OptionF32::Some(13.0),
588 title_font_weight: OptionU16::Some(600), }
590 }
591
592 pub fn linux_gnome() -> Self {
594 Self {
595 button_side: TitlebarButtonSide::Right, buttons: TitlebarButtons {
597 has_close: true,
598 has_minimize: true,
599 has_maximize: true,
600 has_fullscreen: false,
601 },
602 height: OptionPixelValue::Some(PixelValue::px(35.0)),
603 button_area_width: OptionPixelValue::Some(PixelValue::px(100.0)),
604 padding_horizontal: OptionPixelValue::Some(PixelValue::px(12.0)),
605 safe_area: SafeAreaInsets::default(),
606 title_font: OptionString::Some("Cantarell".into()),
607 title_font_size: OptionF32::Some(11.0),
608 title_font_weight: OptionU16::Some(700), }
610 }
611
612 pub fn ios() -> Self {
614 Self {
615 button_side: TitlebarButtonSide::Left,
616 buttons: TitlebarButtons {
617 has_close: false, has_minimize: false,
619 has_maximize: false,
620 has_fullscreen: false,
621 },
622 height: OptionPixelValue::Some(PixelValue::px(44.0)),
623 button_area_width: OptionPixelValue::Some(PixelValue::px(0.0)),
624 padding_horizontal: OptionPixelValue::Some(PixelValue::px(16.0)),
625 safe_area: SafeAreaInsets {
626 top: OptionPixelValue::Some(PixelValue::px(47.0)),
628 bottom: OptionPixelValue::Some(PixelValue::px(34.0)),
629 left: OptionPixelValue::None,
630 right: OptionPixelValue::None,
631 },
632 title_font: OptionString::Some(".SFUI-Semibold".into()),
633 title_font_size: OptionF32::Some(17.0),
634 title_font_weight: OptionU16::Some(600),
635 }
636 }
637
638 pub fn android() -> Self {
640 Self {
641 button_side: TitlebarButtonSide::Left, buttons: TitlebarButtons {
643 has_close: false,
644 has_minimize: false,
645 has_maximize: false,
646 has_fullscreen: false,
647 },
648 height: OptionPixelValue::Some(PixelValue::px(56.0)),
649 button_area_width: OptionPixelValue::Some(PixelValue::px(48.0)), padding_horizontal: OptionPixelValue::Some(PixelValue::px(16.0)),
651 safe_area: SafeAreaInsets::default(),
652 title_font: OptionString::Some("Roboto Medium".into()),
653 title_font_size: OptionF32::Some(20.0),
654 title_font_weight: OptionU16::Some(500),
655 }
656 }
657}
658
659#[derive(Debug, Clone, Copy, PartialEq)]
672#[repr(C)]
673pub struct InputMetrics {
674 pub double_click_time_ms: u32,
676 pub double_click_distance_px: f32,
678 pub drag_threshold_px: f32,
680 pub caret_blink_rate_ms: u32,
682 pub caret_width_px: f32,
684 pub wheel_scroll_lines: u32,
686 pub hover_time_ms: u32,
689}
690
691impl Default for InputMetrics {
692 fn default() -> Self {
693 Self {
694 double_click_time_ms: 500,
695 double_click_distance_px: 4.0,
696 drag_threshold_px: 5.0,
697 caret_blink_rate_ms: 530,
698 caret_width_px: 1.0,
699 wheel_scroll_lines: 3,
700 hover_time_ms: 400,
701 }
702 }
703}
704
705#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
709#[repr(C)]
710pub enum SubpixelType {
711 #[default]
713 None,
714 Rgb,
716 Bgr,
718 VRgb,
720 VBgr,
722}
723
724#[derive(Debug, Clone, Copy, PartialEq)]
729#[repr(C)]
730pub struct TextRenderingHints {
731 pub font_smoothing_enabled: bool,
733 pub subpixel_type: SubpixelType,
735 pub font_smoothing_gamma: u32,
737 pub increased_contrast: bool,
739}
740
741impl Default for TextRenderingHints {
742 fn default() -> Self {
743 Self {
744 font_smoothing_enabled: true,
745 subpixel_type: SubpixelType::None,
746 font_smoothing_gamma: 1000,
747 increased_contrast: false,
748 }
749 }
750}
751
752#[derive(Debug, Default, Clone, Copy, PartialEq)]
759#[repr(C)]
760pub struct FocusVisuals {
761 pub focus_ring_color: OptionColorU,
764 pub focus_border_width: OptionPixelValue,
767 pub focus_border_height: OptionPixelValue,
769}
770
771#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
775#[repr(C)]
776pub enum ScrollbarVisibility {
777 Always,
779 #[default]
781 WhenScrolling,
782 Automatic,
784}
785
786#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
788#[repr(C)]
789pub enum ScrollbarTrackClick {
790 JumpToPosition,
792 #[default]
794 PageUpDown,
795}
796
797#[derive(Debug, Clone, Copy, PartialEq)]
802#[repr(C)]
803pub struct ScrollbarPreferences {
804 pub visibility: ScrollbarVisibility,
807 pub track_click: ScrollbarTrackClick,
809}
810
811impl Default for ScrollbarPreferences {
812 fn default() -> Self {
813 Self {
814 visibility: ScrollbarVisibility::WhenScrolling,
815 track_click: ScrollbarTrackClick::PageUpDown,
816 }
817 }
818}
819
820#[derive(Debug, Default, Clone, PartialEq)]
827#[repr(C)]
828pub struct LinuxCustomization {
829 pub gtk_theme: OptionString,
831 pub icon_theme: OptionString,
833 pub cursor_theme: OptionString,
835 pub cursor_size: u32,
837 pub titlebar_button_layout: OptionString,
840}
841
842#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
846#[repr(C)]
847pub enum ToolbarStyle {
848 #[default]
850 IconsOnly,
851 TextOnly,
853 TextBesideIcon,
855 TextBelowIcon,
857}
858
859#[derive(Debug, Clone, Copy, PartialEq)]
864#[repr(C)]
865pub struct VisualHints {
866 pub show_button_images: bool,
869 pub show_menu_images: bool,
872 pub toolbar_style: ToolbarStyle,
875 pub show_tooltips: bool,
877 pub flash_on_alert: bool,
879}
880
881impl Default for VisualHints {
882 fn default() -> Self {
883 Self {
884 show_button_images: false,
885 show_menu_images: true,
886 toolbar_style: ToolbarStyle::IconsOnly,
887 show_tooltips: true,
888 flash_on_alert: true,
889 }
890 }
891}
892
893#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
897#[repr(C)]
898pub enum FocusBehavior {
899 #[default]
901 AlwaysVisible,
902 KeyboardOnly,
905}
906
907#[derive(Debug, Clone, Copy, PartialEq)]
919#[repr(C)]
920pub struct AnimationMetrics {
921 pub animations_enabled: bool,
923 pub animation_duration_factor: f32,
926 pub focus_indicator_behavior: FocusBehavior,
928}
929
930impl Default for AnimationMetrics {
931 fn default() -> Self {
932 Self {
933 animations_enabled: true,
934 animation_duration_factor: 1.0,
935 focus_indicator_behavior: FocusBehavior::AlwaysVisible,
936 }
937 }
938}
939
940#[derive(Debug, Clone, Copy, PartialEq, Eq)]
953#[repr(C)]
954pub struct AudioMetrics {
955 pub event_sounds_enabled: bool,
957 pub input_feedback_sounds_enabled: bool,
959}
960
961impl Default for AudioMetrics {
962 fn default() -> Self {
963 Self {
964 event_sounds_enabled: true,
965 input_feedback_sounds_enabled: false,
966 }
967 }
968}
969
970pub mod apple_fonts {
976 pub const SYSTEM_FONT: &str = "System Font";
979
980 pub const SF_NS_ROUNDED: &str = "SF NS Rounded";
982
983 pub const SF_COMPACT: &str = "SF Compact";
986
987 pub const SF_MONO: &str = "SF NS Mono Light";
990
991 pub const NEW_YORK: &str = "New York";
994
995 pub const SF_ARABIC: &str = "SF Arabic";
997
998 pub const SF_ARMENIAN: &str = "SF Armenian";
1000
1001 pub const SF_GEORGIAN: &str = "SF Georgian";
1003
1004 pub const SF_HEBREW: &str = "SF Hebrew";
1006
1007 pub const MENLO: &str = "Menlo";
1009 pub const MENLO_REGULAR: &str = "Menlo Regular";
1010 pub const MENLO_BOLD: &str = "Menlo Bold";
1011 pub const MONACO: &str = "Monaco";
1012 pub const LUCIDA_GRANDE: &str = "Lucida Grande";
1013 pub const LUCIDA_GRANDE_BOLD: &str = "Lucida Grande Bold";
1014 pub const HELVETICA_NEUE: &str = "Helvetica Neue";
1015 pub const HELVETICA_NEUE_BOLD: &str = "Helvetica Neue Bold";
1016}
1017
1018pub mod windows_fonts {
1020 pub const SEGOE_UI_VARIABLE: &str = "Segoe UI Variable";
1022 pub const SEGOE_UI_VARIABLE_TEXT: &str = "Segoe UI Variable Text";
1023 pub const SEGOE_UI_VARIABLE_DISPLAY: &str = "Segoe UI Variable Display";
1024
1025 pub const SEGOE_UI: &str = "Segoe UI";
1027 pub const CONSOLAS: &str = "Consolas";
1028 pub const CASCADIA_CODE: &str = "Cascadia Code";
1029 pub const CASCADIA_MONO: &str = "Cascadia Mono";
1030
1031 pub const TAHOMA: &str = "Tahoma";
1033 pub const MS_SANS_SERIF: &str = "MS Sans Serif";
1034 pub const LUCIDA_CONSOLE: &str = "Lucida Console";
1035 pub const COURIER_NEW: &str = "Courier New";
1036}
1037
1038pub mod linux_fonts {
1040 pub const CANTARELL: &str = "Cantarell";
1042 pub const ADWAITA: &str = "Adwaita";
1043
1044 pub const UBUNTU: &str = "Ubuntu";
1046 pub const UBUNTU_MONO: &str = "Ubuntu Mono";
1047
1048 pub const DEJAVU_SANS: &str = "DejaVu Sans";
1050 pub const DEJAVU_SANS_MONO: &str = "DejaVu Sans Mono";
1051 pub const DEJAVU_SERIF: &str = "DejaVu Serif";
1052
1053 pub const LIBERATION_SANS: &str = "Liberation Sans";
1055 pub const LIBERATION_MONO: &str = "Liberation Mono";
1056 pub const LIBERATION_SERIF: &str = "Liberation Serif";
1057
1058 pub const NOTO_SANS: &str = "Noto Sans";
1060 pub const NOTO_MONO: &str = "Noto Sans Mono";
1061 pub const NOTO_SERIF: &str = "Noto Serif";
1062
1063 pub const HACK: &str = "Hack";
1065
1066 pub const MONOSPACE: &str = "Monospace";
1068 pub const SANS_SERIF: &str = "Sans";
1069 pub const SERIF: &str = "Serif";
1070}
1071
1072impl SystemFontType {
1073 pub fn get_fallback_chain(&self, platform: &Platform) -> Vec<&'static str> {
1078 match platform {
1079 Platform::MacOs | Platform::Ios => self.macos_fallback_chain(),
1080 Platform::Windows => self.windows_fallback_chain(),
1081 Platform::Linux(_) => self.linux_fallback_chain(),
1082 Platform::Android => self.android_fallback_chain(),
1083 Platform::Unknown => self.generic_fallback_chain(),
1084 }
1085 }
1086
1087 fn macos_fallback_chain(&self) -> Vec<&'static str> {
1088 match self {
1089 SystemFontType::Ui => vec![
1091 apple_fonts::SYSTEM_FONT,
1092 apple_fonts::HELVETICA_NEUE,
1093 apple_fonts::LUCIDA_GRANDE,
1094 ],
1095 SystemFontType::UiBold => vec![
1097 apple_fonts::HELVETICA_NEUE, apple_fonts::LUCIDA_GRANDE,
1099 ],
1100 SystemFontType::Monospace => vec![
1102 apple_fonts::MENLO,
1103 apple_fonts::MONACO,
1104 ],
1105 SystemFontType::MonospaceBold | SystemFontType::MonospaceItalic => vec![
1106 apple_fonts::MENLO, apple_fonts::MONACO,
1108 ],
1109 SystemFontType::Title => vec![
1111 apple_fonts::SYSTEM_FONT,
1112 apple_fonts::HELVETICA_NEUE,
1113 ],
1114 SystemFontType::TitleBold => vec![
1115 apple_fonts::HELVETICA_NEUE, apple_fonts::LUCIDA_GRANDE,
1117 ],
1118 SystemFontType::Menu => vec![
1119 apple_fonts::SYSTEM_FONT,
1120 apple_fonts::HELVETICA_NEUE,
1121 ],
1122 SystemFontType::Small => vec![
1123 apple_fonts::SYSTEM_FONT,
1124 apple_fonts::HELVETICA_NEUE,
1125 ],
1126 SystemFontType::Serif => vec![
1128 apple_fonts::NEW_YORK,
1129 "Georgia",
1130 "Times New Roman",
1131 ],
1132 SystemFontType::SerifBold => vec![
1133 "Georgia", "Times New Roman",
1135 ],
1136 }
1137 }
1138
1139 fn windows_fallback_chain(&self) -> Vec<&'static str> {
1140 match self {
1141 SystemFontType::Ui | SystemFontType::UiBold => vec![
1142 windows_fonts::SEGOE_UI_VARIABLE_TEXT,
1143 windows_fonts::SEGOE_UI,
1144 windows_fonts::TAHOMA,
1145 ],
1146 SystemFontType::Monospace | SystemFontType::MonospaceBold | SystemFontType::MonospaceItalic => vec![
1147 windows_fonts::CASCADIA_MONO,
1148 windows_fonts::CASCADIA_CODE,
1149 windows_fonts::CONSOLAS,
1150 windows_fonts::LUCIDA_CONSOLE,
1151 windows_fonts::COURIER_NEW,
1152 ],
1153 SystemFontType::Title | SystemFontType::TitleBold => vec![
1154 windows_fonts::SEGOE_UI_VARIABLE_DISPLAY,
1155 windows_fonts::SEGOE_UI,
1156 ],
1157 SystemFontType::Menu => vec![
1158 windows_fonts::SEGOE_UI,
1159 windows_fonts::TAHOMA,
1160 ],
1161 SystemFontType::Small => vec![
1162 windows_fonts::SEGOE_UI,
1163 ],
1164 SystemFontType::Serif | SystemFontType::SerifBold => vec![
1165 "Cambria",
1166 "Georgia",
1167 "Times New Roman",
1168 ],
1169 }
1170 }
1171
1172 fn linux_fallback_chain(&self) -> Vec<&'static str> {
1173 match self {
1174 SystemFontType::Ui | SystemFontType::UiBold => vec![
1175 linux_fonts::CANTARELL,
1176 linux_fonts::UBUNTU,
1177 linux_fonts::NOTO_SANS,
1178 linux_fonts::DEJAVU_SANS,
1179 linux_fonts::LIBERATION_SANS,
1180 linux_fonts::SANS_SERIF,
1181 ],
1182 SystemFontType::Monospace | SystemFontType::MonospaceBold | SystemFontType::MonospaceItalic => vec![
1183 linux_fonts::UBUNTU_MONO,
1184 linux_fonts::HACK,
1185 linux_fonts::NOTO_MONO,
1186 linux_fonts::DEJAVU_SANS_MONO,
1187 linux_fonts::LIBERATION_MONO,
1188 linux_fonts::MONOSPACE,
1189 ],
1190 SystemFontType::Title | SystemFontType::TitleBold => vec![
1191 linux_fonts::CANTARELL,
1192 linux_fonts::UBUNTU,
1193 linux_fonts::NOTO_SANS,
1194 ],
1195 SystemFontType::Menu => vec![
1196 linux_fonts::CANTARELL,
1197 linux_fonts::UBUNTU,
1198 linux_fonts::NOTO_SANS,
1199 ],
1200 SystemFontType::Small => vec![
1201 linux_fonts::CANTARELL,
1202 linux_fonts::UBUNTU,
1203 linux_fonts::NOTO_SANS,
1204 ],
1205 SystemFontType::Serif | SystemFontType::SerifBold => vec![
1206 linux_fonts::NOTO_SERIF,
1207 linux_fonts::DEJAVU_SERIF,
1208 linux_fonts::LIBERATION_SERIF,
1209 linux_fonts::SERIF,
1210 ],
1211 }
1212 }
1213
1214 fn android_fallback_chain(&self) -> Vec<&'static str> {
1215 match self {
1216 SystemFontType::Ui | SystemFontType::UiBold => vec!["Roboto", "Noto Sans"],
1217 SystemFontType::Monospace | SystemFontType::MonospaceBold | SystemFontType::MonospaceItalic => {
1218 vec!["Roboto Mono", "Droid Sans Mono", "monospace"]
1219 }
1220 SystemFontType::Title | SystemFontType::TitleBold => vec!["Roboto", "Noto Sans"],
1221 SystemFontType::Menu => vec!["Roboto"],
1222 SystemFontType::Small => vec!["Roboto"],
1223 SystemFontType::Serif | SystemFontType::SerifBold => vec!["Noto Serif", "Droid Serif", "serif"],
1224 }
1225 }
1226
1227 fn generic_fallback_chain(&self) -> Vec<&'static str> {
1228 match self {
1229 SystemFontType::Ui | SystemFontType::UiBold => vec!["sans-serif"],
1230 SystemFontType::Monospace | SystemFontType::MonospaceBold | SystemFontType::MonospaceItalic => {
1231 vec!["monospace"]
1232 }
1233 SystemFontType::Title | SystemFontType::TitleBold => vec!["sans-serif"],
1234 SystemFontType::Menu => vec!["sans-serif"],
1235 SystemFontType::Small => vec!["sans-serif"],
1236 SystemFontType::Serif | SystemFontType::SerifBold => vec!["serif"],
1237 }
1238 }
1239}
1240
1241impl SystemStyle {
1242
1243 pub fn to_json_string(&self) -> AzString {
1248 use alloc::format;
1249
1250 fn opt_color(c: &OptionColorU) -> alloc::string::String {
1251 match c.as_ref() {
1252 Some(c) => format!("\"#{:02x}{:02x}{:02x}{:02x}\"", c.r, c.g, c.b, c.a),
1253 None => "null".into(),
1254 }
1255 }
1256 fn opt_str(s: &OptionString) -> alloc::string::String {
1257 match s.as_ref() {
1258 Some(s) => format!("\"{}\"", s.as_str()),
1259 None => "null".into(),
1260 }
1261 }
1262 fn opt_f32(v: &OptionF32) -> alloc::string::String {
1263 match v.into_option() {
1264 Some(v) => format!("{:.2}", v),
1265 None => "null".into(),
1266 }
1267 }
1268 fn opt_u16(v: &OptionU16) -> alloc::string::String {
1269 match v.into_option() {
1270 Some(v) => format!("{}", v),
1271 None => "null".into(),
1272 }
1273 }
1274 fn opt_px(v: &OptionPixelValue) -> alloc::string::String {
1275 match v.as_ref() {
1276 Some(v) => format!("{:.1}", v.to_pixels_internal(0.0, 0.0)),
1277 None => "null".into(),
1278 }
1279 }
1280
1281 let tm = &self.metrics.titlebar;
1282 let inp = &self.input;
1283 let tr = &self.text_rendering;
1284 let acc = &self.accessibility;
1285 let sp = &self.scrollbar_preferences;
1286 let lnx = &self.linux;
1287 let vh = &self.visual_hints;
1288 let anim = &self.animation;
1289 let audio = &self.audio;
1290
1291 let json = format!(
1292r#"{{
1293 "theme": "{:?}",
1294 "platform": "{:?}",
1295 "os_version": "{:?}:{}",
1296 "language": "{}",
1297 "prefers_reduced_motion": {:?},
1298 "prefers_high_contrast": {:?},
1299 "colors": {{
1300 "text": {},
1301 "secondary_text": {},
1302 "tertiary_text": {},
1303 "background": {},
1304 "accent": {},
1305 "accent_text": {},
1306 "button_face": {},
1307 "button_text": {},
1308 "disabled_text": {},
1309 "window_background": {},
1310 "under_page_background": {},
1311 "selection_background": {},
1312 "selection_text": {},
1313 "selection_background_inactive": {},
1314 "selection_text_inactive": {},
1315 "link": {},
1316 "separator": {},
1317 "grid": {},
1318 "find_highlight": {},
1319 "sidebar_background": {},
1320 "sidebar_selection": {}
1321 }},
1322 "fonts": {{
1323 "ui_font": {},
1324 "ui_font_size": {},
1325 "monospace_font": {},
1326 "title_font": {},
1327 "menu_font": {},
1328 "small_font": {}
1329 }},
1330 "titlebar": {{
1331 "button_side": "{:?}",
1332 "height": {},
1333 "button_area_width": {},
1334 "padding_horizontal": {},
1335 "title_font": {},
1336 "title_font_size": {},
1337 "title_font_weight": {},
1338 "has_close": {},
1339 "has_minimize": {},
1340 "has_maximize": {},
1341 "has_fullscreen": {}
1342 }},
1343 "input": {{
1344 "double_click_time_ms": {},
1345 "double_click_distance_px": {:.1},
1346 "drag_threshold_px": {:.1},
1347 "caret_blink_rate_ms": {},
1348 "caret_width_px": {:.1},
1349 "wheel_scroll_lines": {},
1350 "hover_time_ms": {}
1351 }},
1352 "text_rendering": {{
1353 "font_smoothing_enabled": {},
1354 "subpixel_type": "{:?}",
1355 "font_smoothing_gamma": {},
1356 "increased_contrast": {}
1357 }},
1358 "accessibility": {{
1359 "prefers_bold_text": {},
1360 "prefers_larger_text": {},
1361 "text_scale_factor": {:.2},
1362 "prefers_high_contrast": {},
1363 "prefers_reduced_motion": {},
1364 "prefers_reduced_transparency": {},
1365 "screen_reader_active": {},
1366 "differentiate_without_color": {}
1367 }},
1368 "scrollbar_preferences": {{
1369 "visibility": "{:?}",
1370 "track_click": "{:?}"
1371 }},
1372 "linux": {{
1373 "gtk_theme": {},
1374 "icon_theme": {},
1375 "cursor_theme": {},
1376 "cursor_size": {},
1377 "titlebar_button_layout": {}
1378 }},
1379 "visual_hints": {{
1380 "show_button_images": {},
1381 "show_menu_images": {},
1382 "toolbar_style": "{:?}",
1383 "show_tooltips": {}
1384 }},
1385 "animation": {{
1386 "animations_enabled": {},
1387 "animation_duration_factor": {:.2},
1388 "focus_indicator_behavior": "{:?}"
1389 }},
1390 "audio": {{
1391 "event_sounds_enabled": {},
1392 "input_feedback_sounds_enabled": {}
1393 }}
1394}}"#,
1395 self.theme,
1397 self.platform,
1398 self.os_version.os, self.os_version.version_id,
1399 self.language.as_str(),
1400 self.prefers_reduced_motion,
1401 self.prefers_high_contrast,
1402 opt_color(&self.colors.text),
1404 opt_color(&self.colors.secondary_text),
1405 opt_color(&self.colors.tertiary_text),
1406 opt_color(&self.colors.background),
1407 opt_color(&self.colors.accent),
1408 opt_color(&self.colors.accent_text),
1409 opt_color(&self.colors.button_face),
1410 opt_color(&self.colors.button_text),
1411 opt_color(&self.colors.disabled_text),
1412 opt_color(&self.colors.window_background),
1413 opt_color(&self.colors.under_page_background),
1414 opt_color(&self.colors.selection_background),
1415 opt_color(&self.colors.selection_text),
1416 opt_color(&self.colors.selection_background_inactive),
1417 opt_color(&self.colors.selection_text_inactive),
1418 opt_color(&self.colors.link),
1419 opt_color(&self.colors.separator),
1420 opt_color(&self.colors.grid),
1421 opt_color(&self.colors.find_highlight),
1422 opt_color(&self.colors.sidebar_background),
1423 opt_color(&self.colors.sidebar_selection),
1424 opt_str(&self.fonts.ui_font),
1426 opt_f32(&self.fonts.ui_font_size),
1427 opt_str(&self.fonts.monospace_font),
1428 opt_str(&self.fonts.title_font),
1429 opt_str(&self.fonts.menu_font),
1430 opt_str(&self.fonts.small_font),
1431 tm.button_side,
1433 opt_px(&tm.height),
1434 opt_px(&tm.button_area_width),
1435 opt_px(&tm.padding_horizontal),
1436 opt_str(&tm.title_font),
1437 opt_f32(&tm.title_font_size),
1438 opt_u16(&tm.title_font_weight),
1439 tm.buttons.has_close,
1440 tm.buttons.has_minimize,
1441 tm.buttons.has_maximize,
1442 tm.buttons.has_fullscreen,
1443 inp.double_click_time_ms,
1445 inp.double_click_distance_px,
1446 inp.drag_threshold_px,
1447 inp.caret_blink_rate_ms,
1448 inp.caret_width_px,
1449 inp.wheel_scroll_lines,
1450 inp.hover_time_ms,
1451 tr.font_smoothing_enabled,
1453 tr.subpixel_type,
1454 tr.font_smoothing_gamma,
1455 tr.increased_contrast,
1456 acc.prefers_bold_text,
1458 acc.prefers_larger_text,
1459 acc.text_scale_factor,
1460 acc.prefers_high_contrast,
1461 acc.prefers_reduced_motion,
1462 acc.prefers_reduced_transparency,
1463 acc.screen_reader_active,
1464 acc.differentiate_without_color,
1465 sp.visibility,
1467 sp.track_click,
1468 opt_str(&lnx.gtk_theme),
1470 opt_str(&lnx.icon_theme),
1471 opt_str(&lnx.cursor_theme),
1472 lnx.cursor_size,
1473 opt_str(&lnx.titlebar_button_layout),
1474 vh.show_button_images,
1476 vh.show_menu_images,
1477 vh.toolbar_style,
1478 vh.show_tooltips,
1479 anim.animations_enabled,
1481 anim.animation_duration_factor,
1482 anim.focus_indicator_behavior,
1483 audio.event_sounds_enabled,
1485 audio.input_feedback_sounds_enabled,
1486 );
1487
1488 AzString::from(json)
1489 }
1490
1491 pub fn detect() -> Self {
1499 let mut style = {
1506 #[cfg(feature = "system")]
1508 {
1509 #[cfg(target_os = "macos")]
1510 {
1511 native_macos::discover()
1512 }
1513 #[cfg(target_os = "windows")]
1514 {
1515 native_windows::discover()
1516 }
1517 #[cfg(target_os = "linux")]
1518 {
1519 native_linux::discover()
1520 }
1521 #[cfg(not(any(
1522 target_os = "macos",
1523 target_os = "windows",
1524 target_os = "linux",
1525 )))]
1526 {
1527 Self::default()
1528 }
1529 }
1530 #[cfg(all(not(feature = "system"), feature = "io"))]
1532 {
1533 #[cfg(target_os = "linux")]
1534 {
1535 discover_linux_style()
1536 }
1537 #[cfg(target_os = "windows")]
1538 {
1539 discover_windows_style()
1540 }
1541 #[cfg(target_os = "macos")]
1542 {
1543 discover_macos_style()
1544 }
1545 #[cfg(target_os = "android")]
1546 {
1547 defaults::android_material_light()
1548 }
1549 #[cfg(target_os = "ios")]
1550 {
1551 defaults::ios_light()
1552 }
1553 #[cfg(not(any(
1554 target_os = "linux",
1555 target_os = "windows",
1556 target_os = "macos",
1557 target_os = "android",
1558 target_os = "ios"
1559 )))]
1560 {
1561 Self::default()
1562 }
1563 }
1564 #[cfg(not(any(feature = "system", feature = "io")))]
1566 {
1567 #[cfg(target_os = "windows")]
1568 {
1569 defaults::windows_11_light()
1570 }
1571 #[cfg(target_os = "macos")]
1572 {
1573 defaults::macos_modern_light()
1574 }
1575 #[cfg(target_os = "linux")]
1576 {
1577 defaults::gnome_adwaita_light()
1578 }
1579 #[cfg(target_os = "android")]
1580 {
1581 defaults::android_material_light()
1582 }
1583 #[cfg(target_os = "ios")]
1584 {
1585 defaults::ios_light()
1586 }
1587 #[cfg(not(any(
1588 target_os = "linux",
1589 target_os = "windows",
1590 target_os = "macos",
1591 target_os = "android",
1592 target_os = "ios"
1593 )))]
1594 {
1595 Self::default()
1596 }
1597 }
1598 };
1599
1600 #[cfg(feature = "io")]
1602 {
1603 if std::env::var("AZUL_DISABLE_RICING").is_ok() {
1604 return style; }
1606
1607 if let Some(stylesheet) = load_app_specific_stylesheet() {
1609 style.app_specific_stylesheet = Some(Box::new(stylesheet));
1610 }
1611 }
1612
1613 style
1614 }
1615
1616 #[inline(always)]
1618 pub fn new() -> Self {
1619 Self::detect()
1620 }
1621
1622 pub fn create_csd_stylesheet(&self) -> Stylesheet {
1627 use alloc::format;
1628
1629 use crate::parser2::new_from_str;
1630
1631 let mut css = String::new();
1633
1634 let bg_color = self
1636 .colors
1637 .window_background
1638 .as_option()
1639 .copied()
1640 .unwrap_or(ColorU::new_rgb(240, 240, 240));
1641 let text_color = self
1642 .colors
1643 .text
1644 .as_option()
1645 .copied()
1646 .unwrap_or(ColorU::new_rgb(0, 0, 0));
1647 let accent_color = self
1648 .colors
1649 .accent
1650 .as_option()
1651 .copied()
1652 .unwrap_or(ColorU::new_rgb(0, 120, 215));
1653 let border_color = match self.theme {
1654 Theme::Dark => ColorU::new_rgb(60, 60, 60),
1655 Theme::Light => ColorU::new_rgb(200, 200, 200),
1656 };
1657
1658 let corner_radius = self
1660 .metrics
1661 .corner_radius
1662 .map(|px| {
1663 use crate::props::basic::pixel::DEFAULT_FONT_SIZE;
1664 format!("{}px", px.to_pixels_internal(1.0, DEFAULT_FONT_SIZE))
1665 })
1666 .unwrap_or_else(|| "4px".to_string());
1667
1668 css.push_str(&format!(
1670 ".csd-titlebar {{ width: 100%; height: 32px; background: rgb({}, {}, {}); \
1671 border-bottom: 1px solid rgb({}, {}, {}); display: flex; flex-direction: row; \
1672 align-items: center; justify-content: space-between; padding: 0 8px; \
1673 cursor: grab; user-select: none; }} ",
1674 bg_color.r, bg_color.g, bg_color.b, border_color.r, border_color.g, border_color.b,
1675 ));
1676
1677 css.push_str(&format!(
1679 ".csd-title {{ color: rgb({}, {}, {}); font-size: 13px; flex-grow: 1; text-align: \
1680 center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; \
1681 user-select: none; }} ",
1682 text_color.r, text_color.g, text_color.b,
1683 ));
1684
1685 css.push_str(".csd-buttons { display: flex; flex-direction: row; gap: 4px; } ");
1687
1688 css.push_str(&format!(
1690 ".csd-button {{ width: 32px; height: 24px; border-radius: {}; background: \
1691 transparent; color: rgb({}, {}, {}); font-size: 16px; line-height: 24px; text-align: \
1692 center; cursor: pointer; user-select: none; }} ",
1693 corner_radius, text_color.r, text_color.g, text_color.b,
1694 ));
1695
1696 let hover_color = match self.theme {
1698 Theme::Dark => ColorU::new_rgb(60, 60, 60),
1699 Theme::Light => ColorU::new_rgb(220, 220, 220),
1700 };
1701 css.push_str(&format!(
1702 ".csd-button:hover {{ background: rgb({}, {}, {}); }} ",
1703 hover_color.r, hover_color.g, hover_color.b,
1704 ));
1705
1706 css.push_str(
1708 ".csd-close:hover { background: rgb(232, 17, 35); color: rgb(255, 255, 255); } ",
1709 );
1710
1711 match self.platform {
1713 Platform::MacOs => {
1714 css.push_str(".csd-buttons { position: absolute; left: 8px; } ");
1716 css.push_str(
1717 ".csd-close { background: rgb(255, 95, 86); width: 12px; height: 12px; \
1718 border-radius: 50%; } ",
1719 );
1720 css.push_str(
1721 ".csd-minimize { background: rgb(255, 189, 46); width: 12px; height: 12px; \
1722 border-radius: 50%; } ",
1723 );
1724 css.push_str(
1725 ".csd-maximize { background: rgb(40, 201, 64); width: 12px; height: 12px; \
1726 border-radius: 50%; } ",
1727 );
1728 }
1729 Platform::Linux(_) => {
1730 css.push_str(".csd-title { text-align: left; } ");
1732 }
1733 _ => {
1734 }
1736 }
1737
1738 let (mut parsed_css, _warnings) = new_from_str(&css);
1740
1741 if !parsed_css.stylesheets.is_empty() {
1743 parsed_css.stylesheets.into_library_owned_vec().remove(0)
1744 } else {
1745 Stylesheet::default()
1746 }
1747 }
1748}
1749
1750#[cfg(feature = "io")]
1753fn discover_linux_style() -> SystemStyle {
1754 if std::env::var("AZUL_SMOKE_AND_MIRRORS").is_err() {
1757 if let Ok(kde_style) = discover_kde_style() {
1759 return kde_style;
1760 }
1761 if let Ok(gnome_style) = discover_gnome_style() {
1762 return gnome_style;
1763 }
1764 }
1765
1766 if let Ok(riced_style) = discover_riced_style() {
1768 return riced_style;
1769 }
1770
1771 defaults::gnome_adwaita_light()
1773}
1774
1775#[cfg(feature = "io")]
1776fn discover_gnome_style() -> Result<SystemStyle, ()> {
1777 use crate::dynamic_selector::BoolCondition;
1778
1779 let theme_name = run_command_with_timeout(
1780 "gsettings",
1781 &["get", "org.gnome.desktop.interface", "gtk-theme"],
1782 Duration::from_secs(1),
1783 )?;
1784 let theme_name = theme_name.trim().trim_matches('\'');
1785
1786 let color_scheme = run_command_with_timeout(
1787 "gsettings",
1788 &["get", "org.gnome.desktop.interface", "color-scheme"],
1789 Duration::from_secs(1),
1790 )
1791 .unwrap_or_default();
1792 let theme = if color_scheme.contains("prefer-dark") {
1793 Theme::Dark
1794 } else {
1795 Theme::Light
1796 };
1797
1798 let ui_font = run_command_with_timeout(
1799 "gsettings",
1800 &["get", "org.gnome.desktop.interface", "font-name"],
1801 Duration::from_secs(1),
1802 )
1803 .ok();
1804 let monospace_font = run_command_with_timeout(
1805 "gsettings",
1806 &["get", "org.gnome.desktop.interface", "monospace-font-name"],
1807 Duration::from_secs(1),
1808 )
1809 .ok();
1810
1811 let mut style = if theme == Theme::Dark {
1812 defaults::gnome_adwaita_dark()
1813 } else {
1814 defaults::gnome_adwaita_light()
1815 };
1816
1817 style.platform = Platform::Linux(DesktopEnvironment::Gnome);
1818 style.language = detect_system_language();
1819 style.os_version = detect_linux_version();
1820 style.prefers_reduced_motion = detect_gnome_reduced_motion();
1821 style.prefers_high_contrast = detect_gnome_high_contrast();
1822
1823 if let Some(font) = ui_font {
1824 style.fonts.ui_font = OptionString::Some(font.trim().trim_matches('\'').to_string().into());
1825 }
1826 if let Some(font) = monospace_font {
1827 style.fonts.monospace_font =
1828 OptionString::Some(font.trim().trim_matches('\'').to_string().into());
1829 }
1830
1831 Ok(style)
1832}
1833
1834#[cfg(feature = "io")]
1835fn discover_kde_style() -> Result<SystemStyle, ()> {
1836 use crate::dynamic_selector::BoolCondition;
1837
1838 run_command_with_timeout("kreadconfig5", &["--version"], Duration::from_secs(1))?;
1840
1841 let scheme_name = run_command_with_timeout(
1843 "kreadconfig5",
1844 &["--group", "General", "--key", "ColorScheme"],
1845 Duration::from_secs(1),
1846 )
1847 .unwrap_or_default();
1848 let theme = if scheme_name.to_lowercase().contains("dark") {
1849 Theme::Dark
1850 } else {
1851 Theme::Light
1852 };
1853
1854 let mut style = if theme == Theme::Dark {
1856 defaults::gnome_adwaita_dark()
1858 } else {
1859 defaults::kde_breeze_light()
1860 };
1861 style.platform = Platform::Linux(DesktopEnvironment::Kde);
1862 style.language = detect_system_language();
1863 style.os_version = detect_linux_version();
1864 style.prefers_reduced_motion = detect_kde_reduced_motion();
1865 style.prefers_high_contrast = BoolCondition::False; if let Ok(font_str) = run_command_with_timeout(
1869 "kreadconfig5",
1870 &["--group", "General", "--key", "font"],
1871 Duration::from_secs(1),
1872 ) {
1873 let mut parts = font_str.trim().split(',');
1874 if let Some(font_name) = parts.next() {
1875 style.fonts.ui_font = OptionString::Some(font_name.to_string().into());
1876 }
1877 if let Some(font_size_str) = parts.next() {
1878 if let Ok(size) = font_size_str.parse::<f32>() {
1879 style.fonts.ui_font_size = OptionF32::Some(size);
1880 }
1881 }
1882 }
1883
1884 if let Ok(font_str) = run_command_with_timeout(
1886 "kreadconfig5",
1887 &["--group", "General", "--key", "fixed"],
1888 Duration::from_secs(1),
1889 ) {
1890 if let Some(font_name) = font_str.trim().split(',').next() {
1891 style.fonts.monospace_font = OptionString::Some(font_name.to_string().into());
1892 }
1893 }
1894
1895 if let Ok(color_str) = run_command_with_timeout(
1897 "kreadconfig5",
1898 &["--group", "WM", "--key", "activeBackground"],
1899 Duration::from_secs(1),
1900 ) {
1901 let rgb: Vec<Result<u8, _>> = color_str
1902 .trim()
1903 .split(',')
1904 .map(|c| c.parse::<u8>())
1905 .collect();
1906 if rgb.len() == 3 {
1907 if let (Ok(r), Ok(g), Ok(b)) = (&rgb[0], &rgb[1], &rgb[2]) {
1908 style.colors.accent = OptionColorU::Some(ColorU::new_rgb(*r, *g, *b));
1909 }
1910 }
1911 }
1912
1913 Ok(style)
1914}
1915
1916#[cfg(feature = "io")]
1917fn discover_riced_style() -> Result<SystemStyle, ()> {
1919 let is_hyprland = std::env::var("HYPRLAND_INSTANCE_SIGNATURE").is_ok();
1922 if !is_hyprland {
1923 return Err(());
1926 }
1927
1928 let mut style = SystemStyle {
1929 platform: Platform::Linux(DesktopEnvironment::Other("Tiling WM".into())),
1930 ..defaults::gnome_adwaita_dark()
1932 };
1933 style.language = detect_system_language();
1934
1935 let home_dir = std::env::var("HOME").unwrap_or_default();
1937 let wal_cache_path = format!("{}/.cache/wal/colors.json", home_dir);
1938 if let Ok(json_content) = std::fs::read_to_string(wal_cache_path) {
1939 if let Ok(json) = serde_json::from_str::<serde_json::Value>(&json_content) {
1940 let colors = &json["colors"];
1941 style.colors.background = colors["color0"]
1942 .as_str()
1943 .and_then(|s| parse_css_color(s).ok())
1944 .map(OptionColorU::Some)
1945 .unwrap_or(OptionColorU::None);
1946 style.colors.text = colors["color7"]
1947 .as_str()
1948 .and_then(|s| parse_css_color(s).ok())
1949 .map(OptionColorU::Some)
1950 .unwrap_or(OptionColorU::None);
1951 style.colors.accent = colors["color4"]
1952 .as_str()
1953 .and_then(|s| parse_css_color(s).ok())
1954 .map(OptionColorU::Some)
1955 .unwrap_or(OptionColorU::None);
1956 style.theme = Theme::Dark; }
1958 }
1959
1960 let hypr_conf_path = format!("{}/.config/hypr/hyprland.conf", home_dir);
1962 if let Ok(conf_content) = std::fs::read_to_string(hypr_conf_path) {
1963 for line in conf_content.lines() {
1964 let line = line.trim();
1965 if line.starts_with('#') || !line.contains('=') {
1966 continue;
1967 }
1968 let mut parts = line.splitn(2, '=').map(|s| s.trim());
1969 let key = parts.next();
1970 let value = parts.next();
1971
1972 if let (Some(k), Some(v)) = (key, value) {
1973 match k {
1974 "rounding" => {
1975 if let Ok(px) = v.parse::<f32>() {
1976 style.metrics.corner_radius = OptionPixelValue::Some(PixelValue::px(px));
1977 }
1978 }
1979 "border_size" => {
1980 if let Ok(px) = v.parse::<f32>() {
1981 style.metrics.border_width = OptionPixelValue::Some(PixelValue::px(px));
1982 }
1983 }
1984 "col.active_border" if style.colors.accent.is_none() => {
1986 if let Some(hex_str) = v.split_whitespace().last() {
1988 if let Ok(color) = parse_css_color(&format!("#{}", hex_str)) {
1989 style.colors.accent = OptionColorU::Some(color);
1990 }
1991 }
1992 }
1993 _ => {}
1994 }
1995 }
1996 }
1997 }
1998
1999 if let Ok(font_str) = run_command_with_timeout(
2001 "gsettings",
2002 &["get", "org.gnome.desktop.interface", "font-name"],
2003 Duration::from_secs(1),
2004 ) {
2005 if let Some(font_name) = font_str.trim().trim_matches('\'').split(' ').next() {
2006 style.fonts.ui_font = OptionString::Some(font_name.to_string().into());
2007 }
2008 }
2009
2010 Ok(style)
2011}
2012
2013#[cfg(feature = "io")]
2014fn discover_windows_style() -> SystemStyle {
2015 use crate::dynamic_selector::{BoolCondition, OsVersion};
2016
2017 let mut style = defaults::windows_11_light(); style.platform = Platform::Windows;
2019 style.language = detect_system_language();
2020 style.os_version = detect_windows_version();
2021 style.prefers_reduced_motion = detect_windows_reduced_motion();
2022 style.prefers_high_contrast = detect_windows_high_contrast();
2023
2024 let theme_val = run_command_with_timeout(
2025 "reg",
2026 &[
2027 "query",
2028 r"HKCU\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize",
2029 "/v",
2030 "AppsUseLightTheme",
2031 ],
2032 Duration::from_secs(1),
2033 );
2034 if let Ok(output) = theme_val {
2035 if output.contains("0x0") {
2036 style = defaults::windows_11_dark();
2037 style.os_version = detect_windows_version();
2038 style.prefers_reduced_motion = detect_windows_reduced_motion();
2039 style.prefers_high_contrast = detect_windows_high_contrast();
2040 }
2041 }
2042
2043 let accent_val = run_command_with_timeout(
2044 "reg",
2045 &[
2046 "query",
2047 r"HKCU\Software\Microsoft\Windows\DWM",
2048 "/v",
2049 "AccentColor",
2050 ],
2051 Duration::from_secs(1),
2052 );
2053 if let Ok(output) = accent_val {
2054 if let Some(hex_str) = output.split_whitespace().last() {
2055 if let Ok(hex_val) = u32::from_str_radix(hex_str.trim_start_matches("0x"), 16) {
2056 let a = (hex_val >> 24) as u8;
2057 let b = (hex_val >> 16) as u8;
2058 let g = (hex_val >> 8) as u8;
2059 let r = hex_val as u8;
2060 style.colors.accent = OptionColorU::Some(ColorU::new(r, g, b, a));
2061 style.colors.selection_background = OptionColorU::Some(ColorU::new(r, g, b, 255));
2063 }
2064 }
2065 }
2066
2067 style
2068}
2069
2070#[cfg(feature = "io")]
2072fn detect_windows_version() -> crate::dynamic_selector::OsVersion {
2073 use crate::dynamic_selector::OsVersion;
2074
2075 if let Ok(output) = run_command_with_timeout(
2077 "reg",
2078 &[
2079 "query",
2080 r"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion",
2081 "/v",
2082 "CurrentBuildNumber",
2083 ],
2084 Duration::from_secs(1),
2085 ) {
2086 for line in output.lines() {
2088 if line.contains("CurrentBuildNumber") {
2089 if let Some(build_str) = line.split_whitespace().last() {
2090 if let Ok(build) = build_str.parse::<u32>() {
2091 return match build {
2092 22000..=22499 => OsVersion::WIN_11_21H2,
2094 22500..=22620 => OsVersion::WIN_11_22H2,
2095 22621..=22630 => OsVersion::WIN_11_23H2,
2096 22631.. => OsVersion::WIN_11_24H2,
2097 19041..=19042 => OsVersion::WIN_10_2004,
2099 19043 => OsVersion::WIN_10_21H1,
2100 19044 => OsVersion::WIN_10_21H2,
2101 19045 => OsVersion::WIN_10_22H2,
2102 18362..=18363 => OsVersion::WIN_10_1903,
2103 17763 => OsVersion::WIN_10_1809,
2104 17134 => OsVersion::WIN_10_1803,
2105 16299 => OsVersion::WIN_10_1709,
2106 15063 => OsVersion::WIN_10_1703,
2107 14393 => OsVersion::WIN_10_1607,
2108 10586 => OsVersion::WIN_10_1511,
2109 10240 => OsVersion::WIN_10_1507,
2110 9600 => OsVersion::WIN_8_1,
2112 9200 => OsVersion::WIN_8,
2113 7601 => OsVersion::WIN_7,
2114 6002 => OsVersion::WIN_VISTA,
2115 2600 => OsVersion::WIN_XP,
2116 _ => OsVersion::WIN_10, };
2118 }
2119 }
2120 }
2121 }
2122 }
2123 OsVersion::WIN_10 }
2125
2126#[cfg(feature = "io")]
2128fn detect_windows_reduced_motion() -> crate::dynamic_selector::BoolCondition {
2129 use crate::dynamic_selector::BoolCondition;
2130
2131 if let Ok(output) = run_command_with_timeout(
2133 "reg",
2134 &[
2135 "query",
2136 r"HKCU\Control Panel\Desktop\WindowMetrics",
2137 "/v",
2138 "MinAnimate",
2139 ],
2140 Duration::from_secs(1),
2141 ) {
2142 if output.contains("0x0") {
2143 return BoolCondition::True;
2144 }
2145 }
2146 BoolCondition::False
2147}
2148
2149#[cfg(feature = "io")]
2151fn detect_windows_high_contrast() -> crate::dynamic_selector::BoolCondition {
2152 use crate::dynamic_selector::BoolCondition;
2153
2154 if let Ok(output) = run_command_with_timeout(
2155 "reg",
2156 &[
2157 "query",
2158 r"HKCU\Control Panel\Accessibility\HighContrast",
2159 "/v",
2160 "Flags",
2161 ],
2162 Duration::from_secs(1),
2163 ) {
2164 if let Some(hex_str) = output.split_whitespace().last() {
2166 if let Ok(flags) = u32::from_str_radix(hex_str.trim_start_matches("0x"), 16) {
2167 if flags & 1 != 0 {
2168 return BoolCondition::True;
2169 }
2170 }
2171 }
2172 }
2173 BoolCondition::False
2174}
2175
2176#[cfg(feature = "io")]
2177fn discover_macos_style() -> SystemStyle {
2178 use crate::dynamic_selector::BoolCondition;
2179
2180 let mut style = defaults::macos_modern_light();
2181 style.platform = Platform::MacOs;
2182 style.language = detect_system_language();
2183 style.os_version = detect_macos_version();
2184 style.prefers_reduced_motion = detect_macos_reduced_motion();
2185 style.prefers_high_contrast = detect_macos_high_contrast();
2186
2187 let theme_val = run_command_with_timeout(
2189 "defaults",
2190 &["read", "-g", "AppleInterfaceStyle"],
2191 Duration::from_secs(1),
2192 );
2193 if theme_val.is_ok() {
2194 style = defaults::macos_modern_dark();
2195 style.os_version = detect_macos_version();
2196 style.prefers_reduced_motion = detect_macos_reduced_motion();
2197 style.prefers_high_contrast = detect_macos_high_contrast();
2198 }
2199
2200 if let Ok(accent_str) = run_command_with_timeout(
2202 "defaults",
2203 &["read", "-g", "AppleAccentColor"],
2204 Duration::from_secs(1),
2205 ) {
2206 let accent_color = match accent_str.trim() {
2207 "-1" => ColorU::new_rgb(142, 142, 147), "0" => ColorU::new_rgb(255, 59, 48), "1" => ColorU::new_rgb(255, 149, 0), "2" => ColorU::new_rgb(255, 204, 0), "3" => ColorU::new_rgb(40, 205, 65), "4" => ColorU::new_rgb(0, 122, 255), "5" => ColorU::new_rgb(175, 82, 222), "6" => ColorU::new_rgb(255, 45, 85), _ => ColorU::new_rgb(0, 122, 255), };
2217 style.colors.accent = OptionColorU::Some(accent_color);
2218 }
2219
2220 if let Ok(highlight_str) = run_command_with_timeout(
2223 "defaults",
2224 &["read", "-g", "AppleHighlightColor"],
2225 Duration::from_secs(1),
2226 ) {
2227 let parts: Vec<&str> = highlight_str.trim().split_whitespace().collect();
2228 if parts.len() >= 3 {
2229 if let (Ok(r), Ok(g), Ok(b)) = (
2230 parts[0].parse::<f32>(),
2231 parts[1].parse::<f32>(),
2232 parts[2].parse::<f32>(),
2233 ) {
2234 let selection_color = ColorU::new(
2236 (r * 255.0) as u8,
2237 (g * 255.0) as u8,
2238 (b * 255.0) as u8,
2239 128, );
2241 style.colors.selection_background = OptionColorU::Some(selection_color);
2242 let selection_text = match style.theme {
2245 Theme::Dark => ColorU::new_rgb(255, 255, 255),
2246 Theme::Light => ColorU::new_rgb(0, 0, 0),
2247 };
2248 style.colors.selection_text = OptionColorU::Some(selection_text);
2249 }
2250 }
2251 }
2252
2253 style
2254}
2255
2256#[cfg(feature = "io")]
2258fn detect_macos_version() -> crate::dynamic_selector::OsVersion {
2259 use crate::dynamic_selector::OsVersion;
2260
2261 if let Ok(output) = run_command_with_timeout(
2262 "sw_vers",
2263 &["-productVersion"],
2264 Duration::from_secs(1),
2265 ) {
2266 let version = output.trim();
2267 let parts: Vec<&str> = version.split('.').collect();
2269 if let Some(major_str) = parts.first() {
2270 if let Ok(major) = major_str.parse::<u32>() {
2271 return match major {
2272 26 => OsVersion::MACOS_TAHOE,
2273 15 => OsVersion::MACOS_SEQUOIA,
2274 14 => OsVersion::MACOS_SONOMA,
2275 13 => OsVersion::MACOS_VENTURA,
2276 12 => OsVersion::MACOS_MONTEREY,
2277 11 => OsVersion::MACOS_BIG_SUR,
2278 10 => {
2279 if let Some(minor_str) = parts.get(1) {
2281 if let Ok(minor) = minor_str.parse::<u32>() {
2282 return match minor {
2283 15 => OsVersion::MACOS_CATALINA,
2284 14 => OsVersion::MACOS_MOJAVE,
2285 13 => OsVersion::MACOS_HIGH_SIERRA,
2286 12 => OsVersion::MACOS_SIERRA,
2287 11 => OsVersion::MACOS_EL_CAPITAN,
2288 10 => OsVersion::MACOS_YOSEMITE,
2289 9 => OsVersion::MACOS_MAVERICKS,
2290 _ => OsVersion::MACOS_CATALINA, };
2292 }
2293 }
2294 OsVersion::MACOS_CATALINA
2295 }
2296 _ => OsVersion::MACOS_SONOMA, };
2298 }
2299 }
2300 }
2301 OsVersion::MACOS_SONOMA }
2303
2304#[cfg(feature = "io")]
2306fn detect_macos_reduced_motion() -> crate::dynamic_selector::BoolCondition {
2307 use crate::dynamic_selector::BoolCondition;
2308
2309 if let Ok(output) = run_command_with_timeout(
2310 "defaults",
2311 &["read", "-g", "com.apple.universalaccess", "reduceMotion"],
2312 Duration::from_secs(1),
2313 ) {
2314 if output.trim() == "1" {
2315 return BoolCondition::True;
2316 }
2317 }
2318 BoolCondition::False
2319}
2320
2321#[cfg(feature = "io")]
2323fn detect_macos_high_contrast() -> crate::dynamic_selector::BoolCondition {
2324 use crate::dynamic_selector::BoolCondition;
2325
2326 if let Ok(output) = run_command_with_timeout(
2327 "defaults",
2328 &["read", "-g", "com.apple.universalaccess", "increaseContrast"],
2329 Duration::from_secs(1),
2330 ) {
2331 if output.trim() == "1" {
2332 return BoolCondition::True;
2333 }
2334 }
2335 BoolCondition::False
2336}
2337
2338#[cfg(feature = "io")]
2342fn detect_linux_version() -> crate::dynamic_selector::OsVersion {
2343 use crate::dynamic_selector::OsVersion;
2344
2345 if let Ok(output) = run_command_with_timeout(
2346 "uname",
2347 &["-r"],
2348 Duration::from_secs(1),
2349 ) {
2350 let version = output.trim();
2352 let parts: Vec<&str> = version.split('.').collect();
2353 if let Some(major_str) = parts.first() {
2354 if let Ok(major) = major_str.parse::<u32>() {
2355 return match major {
2356 6 => OsVersion::LINUX_6_0,
2357 5 => OsVersion::LINUX_5_0,
2358 4 => OsVersion::LINUX_4_0,
2359 3 => OsVersion::LINUX_3_0,
2360 2 => OsVersion::LINUX_2_6,
2361 _ => OsVersion::LINUX_6_0, };
2363 }
2364 }
2365 }
2366 OsVersion::LINUX_6_0 }
2368
2369#[cfg(feature = "io")]
2371fn detect_gnome_reduced_motion() -> crate::dynamic_selector::BoolCondition {
2372 use crate::dynamic_selector::BoolCondition;
2373
2374 if let Ok(output) = run_command_with_timeout(
2375 "gsettings",
2376 &["get", "org.gnome.desktop.interface", "enable-animations"],
2377 Duration::from_secs(1),
2378 ) {
2379 if output.trim() == "false" {
2381 return BoolCondition::True;
2382 }
2383 }
2384 BoolCondition::False
2385}
2386
2387#[cfg(feature = "io")]
2389fn detect_gnome_high_contrast() -> crate::dynamic_selector::BoolCondition {
2390 use crate::dynamic_selector::BoolCondition;
2391
2392 if let Ok(output) = run_command_with_timeout(
2393 "gsettings",
2394 &["get", "org.gnome.desktop.a11y.interface", "high-contrast"],
2395 Duration::from_secs(1),
2396 ) {
2397 if output.trim() == "true" {
2398 return BoolCondition::True;
2399 }
2400 }
2401 BoolCondition::False
2402}
2403
2404#[cfg(feature = "io")]
2406fn detect_kde_reduced_motion() -> crate::dynamic_selector::BoolCondition {
2407 use crate::dynamic_selector::BoolCondition;
2408
2409 if let Ok(output) = run_command_with_timeout(
2411 "kreadconfig5",
2412 &["--group", "KDE", "--key", "AnimationDurationFactor"],
2413 Duration::from_secs(1),
2414 ) {
2415 if let Ok(factor) = output.trim().parse::<f32>() {
2417 if factor == 0.0 {
2418 return BoolCondition::True;
2419 }
2420 }
2421 }
2422 BoolCondition::False
2423}
2424
2425#[cfg(feature = "io")]
2427pub fn detect_linux_desktop_env() -> DesktopEnvironment {
2428 if let Ok(desktop) = std::env::var("XDG_CURRENT_DESKTOP") {
2430 let desktop = desktop.to_lowercase();
2431 if desktop.contains("gnome") {
2432 return DesktopEnvironment::Gnome;
2433 }
2434 if desktop.contains("kde") || desktop.contains("plasma") {
2435 return DesktopEnvironment::Kde;
2436 }
2437 if desktop.contains("xfce") {
2438 return DesktopEnvironment::Other("XFCE".into());
2439 }
2440 if desktop.contains("unity") {
2441 return DesktopEnvironment::Other("Unity".into());
2442 }
2443 if desktop.contains("cinnamon") {
2444 return DesktopEnvironment::Other("Cinnamon".into());
2445 }
2446 if desktop.contains("mate") {
2447 return DesktopEnvironment::Other("MATE".into());
2448 }
2449 if desktop.contains("lxde") || desktop.contains("lxqt") {
2450 return DesktopEnvironment::Other(desktop.to_uppercase().into());
2451 }
2452 if desktop.contains("hyprland") {
2453 return DesktopEnvironment::Other("Hyprland".into());
2454 }
2455 if desktop.contains("sway") {
2456 return DesktopEnvironment::Other("Sway".into());
2457 }
2458 if desktop.contains("i3") {
2459 return DesktopEnvironment::Other("i3".into());
2460 }
2461 }
2462
2463 if let Ok(session) = std::env::var("DESKTOP_SESSION") {
2465 let session = session.to_lowercase();
2466 if session.contains("gnome") {
2467 return DesktopEnvironment::Gnome;
2468 }
2469 if session.contains("plasma") || session.contains("kde") {
2470 return DesktopEnvironment::Kde;
2471 }
2472 }
2473
2474 if std::env::var("GNOME_DESKTOP_SESSION_ID").is_ok() {
2476 return DesktopEnvironment::Gnome;
2477 }
2478 if std::env::var("KDE_FULL_SESSION").is_ok() {
2479 return DesktopEnvironment::Kde;
2480 }
2481 if std::env::var("HYPRLAND_INSTANCE_SIGNATURE").is_ok() {
2482 return DesktopEnvironment::Other("Hyprland".into());
2483 }
2484 if std::env::var("SWAYSOCK").is_ok() {
2485 return DesktopEnvironment::Other("Sway".into());
2486 }
2487 if std::env::var("I3SOCK").is_ok() {
2488 return DesktopEnvironment::Other("i3".into());
2489 }
2490
2491 DesktopEnvironment::Other("Unknown".into())
2492}
2493
2494#[cfg(feature = "io")]
2495fn discover_android_style() -> SystemStyle {
2496 defaults::android_material_light()
2498}
2499
2500#[cfg(feature = "io")]
2501fn discover_ios_style() -> SystemStyle {
2502 defaults::ios_light()
2504}
2505
2506#[cfg(feature = "io")]
2509fn run_command_with_timeout(program: &str, args: &[&str], timeout: Duration) -> Result<String, ()> {
2511 use std::{
2512 process::{Command, Stdio},
2513 thread,
2514 };
2515
2516 let mut child = Command::new(program)
2517 .args(args)
2518 .stdout(Stdio::piped())
2519 .stderr(Stdio::piped())
2520 .spawn()
2521 .map_err(|_| ())?;
2522
2523 let (tx, rx) = std::sync::mpsc::channel();
2524
2525 let child_thread = thread::spawn(move || {
2526 let output = child.wait_with_output();
2527 tx.send(output).ok();
2528 });
2529
2530 match rx.recv_timeout(timeout) {
2531 Ok(Ok(output)) if output.status.success() => {
2532 Ok(String::from_utf8(output.stdout).unwrap_or_default())
2533 }
2534 _ => {
2535 child_thread.join().ok(); Err(())
2539 }
2540 }
2541}
2542
2543#[cfg(feature = "io")]
2548#[cfg(feature = "io")]
2553fn load_app_specific_stylesheet() -> Option<Stylesheet> {
2554 let exe_path = std::env::current_exe().ok()?;
2556 let exe_name = exe_path.file_name()?.to_str()?;
2557
2558 let config_dir = dirs_next::config_dir()?;
2562
2563 let css_path = config_dir
2564 .join("azul")
2565 .join("styles")
2566 .join(format!("{}.css", exe_name));
2567
2568 let css_content = std::fs::read_to_string(css_path).ok()?;
2571
2572 if css_content.trim().is_empty() {
2573 return None;
2574 }
2575
2576 let (mut css, _warnings) = new_from_str(&css_content);
2577
2578 if !css.stylesheets.is_empty() {
2581 let mut owned_vec = css.stylesheets.into_library_owned_vec();
2582 Some(owned_vec.remove(0))
2583 } else {
2584 None
2585 }
2586}
2587
2588#[cfg(feature = "io")]
2593pub fn detect_system_language() -> AzString {
2594 #[cfg(target_os = "windows")]
2595 {
2596 detect_language_windows()
2597 }
2598 #[cfg(target_os = "macos")]
2599 {
2600 detect_language_macos()
2601 }
2602 #[cfg(target_os = "linux")]
2603 {
2604 detect_language_linux()
2605 }
2606 #[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
2607 {
2608 AzString::from_const_str("en-US")
2609 }
2610}
2611
2612#[cfg(all(feature = "io", target_os = "windows"))]
2614fn detect_language_windows() -> AzString {
2615 if let Ok(output) = run_command_with_timeout(
2617 "powershell",
2618 &["-Command", "(Get-Culture).Name"],
2619 Duration::from_secs(2),
2620 ) {
2621 let lang = output.trim();
2622 if !lang.is_empty() && lang.contains('-') {
2623 return AzString::from(lang.to_string());
2624 }
2625 }
2626
2627 if let Ok(output) = run_command_with_timeout(
2629 "reg",
2630 &[
2631 "query",
2632 r"HKCU\Control Panel\International",
2633 "/v",
2634 "LocaleName",
2635 ],
2636 Duration::from_secs(1),
2637 ) {
2638 for line in output.lines() {
2640 if line.contains("LocaleName") {
2641 if let Some(lang) = line.split_whitespace().last() {
2642 let lang = lang.trim();
2643 if !lang.is_empty() {
2644 return AzString::from(lang.to_string());
2645 }
2646 }
2647 }
2648 }
2649 }
2650
2651 AzString::from_const_str("en-US")
2652}
2653
2654#[cfg(all(feature = "io", target_os = "macos"))]
2656fn detect_language_macos() -> AzString {
2657 if let Ok(output) = run_command_with_timeout(
2659 "defaults",
2660 &["read", "-g", "AppleLocale"],
2661 Duration::from_secs(1),
2662 ) {
2663 let locale = output.trim();
2664 if !locale.is_empty() {
2665 return AzString::from(locale.replace('_', "-"));
2667 }
2668 }
2669
2670 if let Ok(output) = run_command_with_timeout(
2672 "defaults",
2673 &["read", "-g", "AppleLanguages"],
2674 Duration::from_secs(1),
2675 ) {
2676 for line in output.lines() {
2679 let trimmed = line
2680 .trim()
2681 .trim_matches(|c| c == '"' || c == ',' || c == '(' || c == ')');
2682 if !trimmed.is_empty() && trimmed.contains('-') {
2683 return AzString::from(trimmed.to_string());
2684 }
2685 }
2686 }
2687
2688 AzString::from_const_str("en-US")
2689}
2690
2691#[cfg(all(feature = "io", target_os = "linux"))]
2693fn detect_language_linux() -> AzString {
2694 let env_vars = ["LANGUAGE", "LC_ALL", "LC_MESSAGES", "LANG"];
2696
2697 for var in &env_vars {
2698 if let Ok(value) = std::env::var(var) {
2699 let value = value.trim();
2700 if value.is_empty() || value == "C" || value == "POSIX" {
2701 continue;
2702 }
2703
2704 let lang = value
2706 .split('.') .next()
2708 .unwrap_or(value)
2709 .replace('_', "-"); if !lang.is_empty() {
2712 return AzString::from(lang);
2713 }
2714 }
2715 }
2716
2717 AzString::from_const_str("en-US")
2718}
2719
2720#[cfg(not(feature = "io"))]
2722pub fn detect_system_language() -> AzString {
2723 AzString::from_const_str("en-US")
2724}
2725
2726pub mod defaults {
2727 use crate::{
2733 corety::{AzString, OptionF32, OptionString},
2734 dynamic_selector::{BoolCondition, OsVersion},
2735 props::{
2736 basic::{
2737 color::{ColorU, OptionColorU},
2738 pixel::{PixelValue, OptionPixelValue},
2739 },
2740 layout::{
2741 dimensions::LayoutWidth,
2742 spacing::{LayoutPaddingLeft, LayoutPaddingRight},
2743 },
2744 style::{
2745 background::StyleBackgroundContent,
2746 scrollbar::{
2747 ComputedScrollbarStyle, OverflowScrolling, OverscrollBehavior, ScrollBehavior,
2748 ScrollPhysics, ScrollbarInfo,
2749 SCROLLBAR_ANDROID_DARK, SCROLLBAR_ANDROID_LIGHT, SCROLLBAR_CLASSIC_DARK,
2750 SCROLLBAR_CLASSIC_LIGHT, SCROLLBAR_IOS_DARK, SCROLLBAR_IOS_LIGHT,
2751 SCROLLBAR_MACOS_DARK, SCROLLBAR_MACOS_LIGHT, SCROLLBAR_WINDOWS_DARK,
2752 SCROLLBAR_WINDOWS_LIGHT,
2753 },
2754 },
2755 },
2756 system::{
2757 DesktopEnvironment, Platform, SystemColors, SystemFonts, SystemMetrics, SystemStyle,
2758 Theme, IconStyleOptions, TitlebarMetrics,
2759 },
2760 };
2761
2762 pub const SCROLLBAR_WINDOWS_CLASSIC: ScrollbarInfo = ScrollbarInfo {
2766 width: LayoutWidth::Px(crate::props::basic::pixel::PixelValue::const_px(17)),
2767 padding_left: LayoutPaddingLeft {
2768 inner: crate::props::basic::pixel::PixelValue::const_px(0),
2769 },
2770 padding_right: LayoutPaddingRight {
2771 inner: crate::props::basic::pixel::PixelValue::const_px(0),
2772 },
2773 track: StyleBackgroundContent::Color(ColorU {
2774 r: 223,
2775 g: 223,
2776 b: 223,
2777 a: 255,
2778 }), thumb: StyleBackgroundContent::Color(ColorU {
2780 r: 208,
2781 g: 208,
2782 b: 208,
2783 a: 255,
2784 }), button: StyleBackgroundContent::Color(ColorU {
2786 r: 208,
2787 g: 208,
2788 b: 208,
2789 a: 255,
2790 }),
2791 corner: StyleBackgroundContent::Color(ColorU {
2792 r: 223,
2793 g: 223,
2794 b: 223,
2795 a: 255,
2796 }),
2797 resizer: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
2798 clip_to_container_border: false,
2799 scroll_behavior: ScrollBehavior::Auto,
2800 overscroll_behavior_x: OverscrollBehavior::None,
2801 overscroll_behavior_y: OverscrollBehavior::None,
2802 overflow_scrolling: OverflowScrolling::Auto,
2803 };
2804
2805 pub const SCROLLBAR_MACOS_AQUA: ScrollbarInfo = ScrollbarInfo {
2807 width: LayoutWidth::Px(crate::props::basic::pixel::PixelValue::const_px(15)),
2808 padding_left: LayoutPaddingLeft {
2809 inner: crate::props::basic::pixel::PixelValue::const_px(0),
2810 },
2811 padding_right: LayoutPaddingRight {
2812 inner: crate::props::basic::pixel::PixelValue::const_px(0),
2813 },
2814 track: StyleBackgroundContent::Color(ColorU {
2815 r: 238,
2816 g: 238,
2817 b: 238,
2818 a: 128,
2819 }), thumb: StyleBackgroundContent::Color(ColorU {
2821 r: 105,
2822 g: 173,
2823 b: 255,
2824 a: 255,
2825 }), button: StyleBackgroundContent::Color(ColorU {
2827 r: 105,
2828 g: 173,
2829 b: 255,
2830 a: 255,
2831 }),
2832 corner: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
2833 resizer: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
2834 clip_to_container_border: true,
2835 scroll_behavior: ScrollBehavior::Smooth,
2836 overscroll_behavior_x: OverscrollBehavior::Auto,
2837 overscroll_behavior_y: OverscrollBehavior::Auto,
2838 overflow_scrolling: OverflowScrolling::Auto,
2839 };
2840
2841 pub const SCROLLBAR_KDE_OXYGEN: ScrollbarInfo = ScrollbarInfo {
2843 width: LayoutWidth::Px(crate::props::basic::pixel::PixelValue::const_px(14)),
2844 padding_left: LayoutPaddingLeft {
2845 inner: crate::props::basic::pixel::PixelValue::const_px(2),
2846 },
2847 padding_right: LayoutPaddingRight {
2848 inner: crate::props::basic::pixel::PixelValue::const_px(2),
2849 },
2850 track: StyleBackgroundContent::Color(ColorU {
2851 r: 242,
2852 g: 242,
2853 b: 242,
2854 a: 255,
2855 }),
2856 thumb: StyleBackgroundContent::Color(ColorU {
2857 r: 177,
2858 g: 177,
2859 b: 177,
2860 a: 255,
2861 }),
2862 button: StyleBackgroundContent::Color(ColorU {
2863 r: 216,
2864 g: 216,
2865 b: 216,
2866 a: 255,
2867 }),
2868 corner: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
2869 resizer: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
2870 clip_to_container_border: false,
2871 scroll_behavior: ScrollBehavior::Auto,
2872 overscroll_behavior_x: OverscrollBehavior::Auto,
2873 overscroll_behavior_y: OverscrollBehavior::Auto,
2874 overflow_scrolling: OverflowScrolling::Auto,
2875 };
2876
2877 fn scrollbar_info_to_computed(info: &ScrollbarInfo) -> ComputedScrollbarStyle {
2879 ComputedScrollbarStyle {
2880 width: Some(info.width.clone()),
2881 thumb_color: match info.thumb {
2882 StyleBackgroundContent::Color(c) => Some(c),
2883 _ => None,
2884 },
2885 track_color: match info.track {
2886 StyleBackgroundContent::Color(c) => Some(c),
2887 _ => None,
2888 },
2889 }
2890 }
2891
2892 pub fn windows_11_light() -> SystemStyle {
2895 SystemStyle {
2896 theme: Theme::Light,
2897 platform: Platform::Windows,
2898 colors: SystemColors {
2899 text: OptionColorU::Some(ColorU::new_rgb(0, 0, 0)),
2900 background: OptionColorU::Some(ColorU::new_rgb(243, 243, 243)),
2901 accent: OptionColorU::Some(ColorU::new_rgb(0, 95, 184)),
2902 window_background: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
2903 selection_background: OptionColorU::Some(ColorU::new_rgb(0, 120, 215)),
2904 selection_text: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
2905 ..Default::default()
2906 },
2907 fonts: SystemFonts {
2908 ui_font: OptionString::Some("Segoe UI Variable Text".into()),
2909 ui_font_size: OptionF32::Some(9.0),
2910 monospace_font: OptionString::Some("Consolas".into()),
2911 ..Default::default()
2912 },
2913 metrics: SystemMetrics {
2914 corner_radius: OptionPixelValue::Some(PixelValue::px(4.0)),
2915 border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
2916 button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(12.0)),
2917 button_padding_vertical: OptionPixelValue::Some(PixelValue::px(6.0)),
2918 titlebar: TitlebarMetrics::windows(),
2919 },
2920 scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_WINDOWS_LIGHT))),
2921 app_specific_stylesheet: None,
2922 icon_style: IconStyleOptions::default(),
2923 language: AzString::from_const_str("en-US"),
2924 os_version: OsVersion::WIN_11,
2925 prefers_reduced_motion: BoolCondition::False,
2926 prefers_high_contrast: BoolCondition::False,
2927 scroll_physics: ScrollPhysics::windows(),
2928 ..Default::default()
2929 }
2930 }
2931
2932 pub fn windows_11_dark() -> SystemStyle {
2933 SystemStyle {
2934 theme: Theme::Dark,
2935 platform: Platform::Windows,
2936 colors: SystemColors {
2937 text: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
2938 background: OptionColorU::Some(ColorU::new_rgb(32, 32, 32)),
2939 accent: OptionColorU::Some(ColorU::new_rgb(0, 120, 215)),
2940 window_background: OptionColorU::Some(ColorU::new_rgb(25, 25, 25)),
2941 selection_background: OptionColorU::Some(ColorU::new_rgb(0, 120, 215)),
2942 selection_text: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
2943 ..Default::default()
2944 },
2945 fonts: SystemFonts {
2946 ui_font: OptionString::Some("Segoe UI Variable Text".into()),
2947 ui_font_size: OptionF32::Some(9.0),
2948 monospace_font: OptionString::Some("Consolas".into()),
2949 ..Default::default()
2950 },
2951 metrics: SystemMetrics {
2952 corner_radius: OptionPixelValue::Some(PixelValue::px(4.0)),
2953 border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
2954 button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(12.0)),
2955 button_padding_vertical: OptionPixelValue::Some(PixelValue::px(6.0)),
2956 titlebar: TitlebarMetrics::windows(),
2957 },
2958 scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_WINDOWS_DARK))),
2959 app_specific_stylesheet: None,
2960 icon_style: IconStyleOptions::default(),
2961 language: AzString::from_const_str("en-US"),
2962 os_version: OsVersion::WIN_11,
2963 prefers_reduced_motion: BoolCondition::False,
2964 prefers_high_contrast: BoolCondition::False,
2965 scroll_physics: ScrollPhysics::windows(),
2966 ..Default::default()
2967 }
2968 }
2969
2970 pub fn windows_7_aero() -> SystemStyle {
2971 SystemStyle {
2972 theme: Theme::Light,
2973 platform: Platform::Windows,
2974 colors: SystemColors {
2975 text: OptionColorU::Some(ColorU::new_rgb(0, 0, 0)),
2976 background: OptionColorU::Some(ColorU::new_rgb(240, 240, 240)),
2977 accent: OptionColorU::Some(ColorU::new_rgb(51, 153, 255)),
2978 window_background: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
2979 selection_background: OptionColorU::Some(ColorU::new_rgb(51, 153, 255)),
2980 selection_text: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
2981 ..Default::default()
2982 },
2983 fonts: SystemFonts {
2984 ui_font: OptionString::Some("Segoe UI".into()),
2985 ui_font_size: OptionF32::Some(9.0),
2986 monospace_font: OptionString::Some("Consolas".into()),
2987 ..Default::default()
2988 },
2989 metrics: SystemMetrics {
2990 corner_radius: OptionPixelValue::Some(PixelValue::px(6.0)),
2991 border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
2992 button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(10.0)),
2993 button_padding_vertical: OptionPixelValue::Some(PixelValue::px(5.0)),
2994 titlebar: TitlebarMetrics::windows(),
2995 },
2996 scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_CLASSIC_LIGHT))),
2997 app_specific_stylesheet: None,
2998 icon_style: IconStyleOptions::default(),
2999 language: AzString::from_const_str("en-US"),
3000 os_version: OsVersion::WIN_7,
3001 prefers_reduced_motion: BoolCondition::False,
3002 prefers_high_contrast: BoolCondition::False,
3003 scroll_physics: ScrollPhysics::windows(),
3004 ..Default::default()
3005 }
3006 }
3007
3008 pub fn windows_xp_luna() -> SystemStyle {
3009 SystemStyle {
3010 theme: Theme::Light,
3011 platform: Platform::Windows,
3012 colors: SystemColors {
3013 text: OptionColorU::Some(ColorU::new_rgb(0, 0, 0)),
3014 background: OptionColorU::Some(ColorU::new_rgb(236, 233, 216)),
3015 accent: OptionColorU::Some(ColorU::new_rgb(49, 106, 197)),
3016 window_background: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
3017 selection_background: OptionColorU::Some(ColorU::new_rgb(49, 106, 197)),
3018 selection_text: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
3019 ..Default::default()
3020 },
3021 fonts: SystemFonts {
3022 ui_font: OptionString::Some("Tahoma".into()),
3023 ui_font_size: OptionF32::Some(8.0),
3024 monospace_font: OptionString::Some("Lucida Console".into()),
3025 ..Default::default()
3026 },
3027 metrics: SystemMetrics {
3028 corner_radius: OptionPixelValue::Some(PixelValue::px(3.0)),
3029 border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
3030 button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(8.0)),
3031 button_padding_vertical: OptionPixelValue::Some(PixelValue::px(4.0)),
3032 titlebar: TitlebarMetrics::windows(),
3033 },
3034 scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_WINDOWS_CLASSIC))),
3035 app_specific_stylesheet: None,
3036 icon_style: IconStyleOptions::default(),
3037 language: AzString::from_const_str("en-US"),
3038 os_version: OsVersion::WIN_XP,
3039 prefers_reduced_motion: BoolCondition::False,
3040 prefers_high_contrast: BoolCondition::False,
3041 scroll_physics: ScrollPhysics::windows(),
3042 ..Default::default()
3043 }
3044 }
3045
3046 pub fn macos_modern_light() -> SystemStyle {
3049 SystemStyle {
3050 platform: Platform::MacOs,
3051 theme: Theme::Light,
3052 colors: SystemColors {
3053 text: OptionColorU::Some(ColorU::new(0, 0, 0, 221)),
3054 background: OptionColorU::Some(ColorU::new_rgb(242, 242, 247)),
3055 accent: OptionColorU::Some(ColorU::new_rgb(0, 122, 255)),
3056 window_background: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
3057 selection_background: OptionColorU::Some(ColorU::new(0, 122, 255, 128)),
3059 selection_text: OptionColorU::Some(ColorU::new_rgb(0, 0, 0)),
3060 ..Default::default()
3061 },
3062 fonts: SystemFonts {
3063 ui_font: OptionString::Some(".SF NS".into()),
3064 ui_font_size: OptionF32::Some(13.0),
3065 monospace_font: OptionString::Some("Menlo".into()),
3066 ..Default::default()
3067 },
3068 metrics: SystemMetrics {
3069 corner_radius: OptionPixelValue::Some(PixelValue::px(8.0)),
3070 border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
3071 button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(16.0)),
3072 button_padding_vertical: OptionPixelValue::Some(PixelValue::px(6.0)),
3073 titlebar: TitlebarMetrics::macos(),
3074 },
3075 scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_MACOS_LIGHT))),
3076 app_specific_stylesheet: None,
3077 icon_style: IconStyleOptions::default(),
3078 language: AzString::from_const_str("en-US"),
3079 os_version: OsVersion::MACOS_SONOMA,
3080 prefers_reduced_motion: BoolCondition::False,
3081 prefers_high_contrast: BoolCondition::False,
3082 scroll_physics: ScrollPhysics::macos(),
3083 ..Default::default()
3084 }
3085 }
3086
3087 pub fn macos_modern_dark() -> SystemStyle {
3088 SystemStyle {
3089 platform: Platform::MacOs,
3090 theme: Theme::Dark,
3091 colors: SystemColors {
3092 text: OptionColorU::Some(ColorU::new(255, 255, 255, 221)),
3093 background: OptionColorU::Some(ColorU::new_rgb(28, 28, 30)),
3094 accent: OptionColorU::Some(ColorU::new_rgb(10, 132, 255)),
3095 window_background: OptionColorU::Some(ColorU::new_rgb(44, 44, 46)),
3096 selection_background: OptionColorU::Some(ColorU::new(10, 132, 255, 128)),
3098 selection_text: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
3099 ..Default::default()
3100 },
3101 fonts: SystemFonts {
3102 ui_font: OptionString::Some(".SF NS".into()),
3103 ui_font_size: OptionF32::Some(13.0),
3104 monospace_font: OptionString::Some("SF Mono".into()),
3105 monospace_font_size: OptionF32::Some(12.0),
3106 title_font: OptionString::Some(".SF NS".into()),
3107 title_font_size: OptionF32::Some(13.0),
3108 menu_font: OptionString::Some(".SF NS".into()),
3109 menu_font_size: OptionF32::Some(13.0),
3110 small_font: OptionString::Some(".SF NS".into()),
3111 small_font_size: OptionF32::Some(11.0),
3112 ..Default::default()
3113 },
3114 metrics: SystemMetrics {
3115 corner_radius: OptionPixelValue::Some(PixelValue::px(8.0)),
3116 border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
3117 button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(16.0)),
3118 button_padding_vertical: OptionPixelValue::Some(PixelValue::px(6.0)),
3119 titlebar: TitlebarMetrics::macos(),
3120 },
3121 scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_MACOS_DARK))),
3122 app_specific_stylesheet: None,
3123 icon_style: IconStyleOptions::default(),
3124 language: AzString::from_const_str("en-US"),
3125 os_version: OsVersion::MACOS_SONOMA,
3126 prefers_reduced_motion: BoolCondition::False,
3127 prefers_high_contrast: BoolCondition::False,
3128 scroll_physics: ScrollPhysics::macos(),
3129 ..Default::default()
3130 }
3131 }
3132
3133 pub fn macos_aqua() -> SystemStyle {
3134 SystemStyle {
3135 platform: Platform::MacOs,
3136 theme: Theme::Light,
3137 colors: SystemColors {
3138 text: OptionColorU::Some(ColorU::new_rgb(0, 0, 0)),
3139 background: OptionColorU::Some(ColorU::new_rgb(229, 229, 229)),
3140 accent: OptionColorU::Some(ColorU::new_rgb(63, 128, 234)),
3141 window_background: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
3142 ..Default::default()
3143 },
3144 fonts: SystemFonts {
3145 ui_font: OptionString::Some("Lucida Grande".into()),
3146 ui_font_size: OptionF32::Some(13.0),
3147 monospace_font: OptionString::Some("Monaco".into()),
3148 monospace_font_size: OptionF32::Some(12.0),
3149 ..Default::default()
3150 },
3151 metrics: SystemMetrics {
3152 corner_radius: OptionPixelValue::Some(PixelValue::px(12.0)),
3153 border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
3154 button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(16.0)),
3155 button_padding_vertical: OptionPixelValue::Some(PixelValue::px(6.0)),
3156 titlebar: TitlebarMetrics::macos(),
3157 },
3158 scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_MACOS_AQUA))),
3159 app_specific_stylesheet: None,
3160 icon_style: IconStyleOptions::default(),
3161 language: AzString::from_const_str("en-US"),
3162 os_version: OsVersion::MACOS_TIGER,
3163 prefers_reduced_motion: BoolCondition::False,
3164 prefers_high_contrast: BoolCondition::False,
3165 scroll_physics: ScrollPhysics::macos(),
3166 ..Default::default()
3167 }
3168 }
3169
3170 pub fn gnome_adwaita_light() -> SystemStyle {
3173 SystemStyle {
3174 platform: Platform::Linux(DesktopEnvironment::Gnome),
3175 theme: Theme::Light,
3176 colors: SystemColors {
3177 text: OptionColorU::Some(ColorU::new_rgb(46, 52, 54)),
3178 background: OptionColorU::Some(ColorU::new_rgb(249, 249, 249)),
3179 accent: OptionColorU::Some(ColorU::new_rgb(53, 132, 228)),
3180 window_background: OptionColorU::Some(ColorU::new_rgb(237, 237, 237)),
3181 ..Default::default()
3182 },
3183 fonts: SystemFonts {
3184 ui_font: OptionString::Some("Cantarell".into()),
3185 ui_font_size: OptionF32::Some(11.0),
3186 monospace_font: OptionString::Some("Monospace".into()),
3187 ..Default::default()
3188 },
3189 metrics: SystemMetrics {
3190 corner_radius: OptionPixelValue::Some(PixelValue::px(4.0)),
3191 border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
3192 button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(12.0)),
3193 button_padding_vertical: OptionPixelValue::Some(PixelValue::px(8.0)),
3194 titlebar: TitlebarMetrics::linux_gnome(),
3195 },
3196 scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_CLASSIC_LIGHT))),
3197 app_specific_stylesheet: None,
3198 icon_style: IconStyleOptions::default(),
3199 language: AzString::from_const_str("en-US"),
3200 os_version: OsVersion::LINUX_6_0,
3201 prefers_reduced_motion: BoolCondition::False,
3202 prefers_high_contrast: BoolCondition::False,
3203 ..Default::default()
3204 }
3205 }
3206
3207 pub fn gnome_adwaita_dark() -> SystemStyle {
3208 SystemStyle {
3209 platform: Platform::Linux(DesktopEnvironment::Gnome),
3210 theme: Theme::Dark,
3211 colors: SystemColors {
3212 text: OptionColorU::Some(ColorU::new_rgb(238, 238, 236)),
3213 background: OptionColorU::Some(ColorU::new_rgb(36, 36, 36)),
3214 accent: OptionColorU::Some(ColorU::new_rgb(53, 132, 228)),
3215 window_background: OptionColorU::Some(ColorU::new_rgb(48, 48, 48)),
3216 ..Default::default()
3217 },
3218 fonts: SystemFonts {
3219 ui_font: OptionString::Some("Cantarell".into()),
3220 ui_font_size: OptionF32::Some(11.0),
3221 monospace_font: OptionString::Some("Monospace".into()),
3222 ..Default::default()
3223 },
3224 metrics: SystemMetrics {
3225 corner_radius: OptionPixelValue::Some(PixelValue::px(4.0)),
3226 border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
3227 button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(12.0)),
3228 button_padding_vertical: OptionPixelValue::Some(PixelValue::px(8.0)),
3229 titlebar: TitlebarMetrics::linux_gnome(),
3230 },
3231 scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_CLASSIC_DARK))),
3232 app_specific_stylesheet: None,
3233 icon_style: IconStyleOptions::default(),
3234 language: AzString::from_const_str("en-US"),
3235 os_version: OsVersion::LINUX_6_0,
3236 prefers_reduced_motion: BoolCondition::False,
3237 prefers_high_contrast: BoolCondition::False,
3238 ..Default::default()
3239 }
3240 }
3241
3242 pub fn gtk2_clearlooks() -> SystemStyle {
3243 SystemStyle {
3244 platform: Platform::Linux(DesktopEnvironment::Gnome),
3245 theme: Theme::Light,
3246 colors: SystemColors {
3247 text: OptionColorU::Some(ColorU::new_rgb(0, 0, 0)),
3248 background: OptionColorU::Some(ColorU::new_rgb(239, 239, 239)),
3249 accent: OptionColorU::Some(ColorU::new_rgb(245, 121, 0)),
3250 ..Default::default()
3251 },
3252 fonts: SystemFonts {
3253 ui_font: OptionString::Some("DejaVu Sans".into()),
3254 ui_font_size: OptionF32::Some(10.0),
3255 monospace_font: OptionString::Some("DejaVu Sans Mono".into()),
3256 ..Default::default()
3257 },
3258 metrics: SystemMetrics {
3259 corner_radius: OptionPixelValue::Some(PixelValue::px(4.0)),
3260 border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
3261 button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(10.0)),
3262 button_padding_vertical: OptionPixelValue::Some(PixelValue::px(6.0)),
3263 titlebar: TitlebarMetrics::linux_gnome(),
3264 },
3265 scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_CLASSIC_LIGHT))),
3266 app_specific_stylesheet: None,
3267 icon_style: IconStyleOptions::default(),
3268 language: AzString::from_const_str("en-US"),
3269 os_version: OsVersion::LINUX_2_6,
3270 prefers_reduced_motion: BoolCondition::False,
3271 prefers_high_contrast: BoolCondition::False,
3272 ..Default::default()
3273 }
3274 }
3275
3276 pub fn kde_breeze_light() -> SystemStyle {
3277 SystemStyle {
3278 platform: Platform::Linux(DesktopEnvironment::Kde),
3279 theme: Theme::Light,
3280 colors: SystemColors {
3281 text: OptionColorU::Some(ColorU::new_rgb(31, 36, 39)),
3282 background: OptionColorU::Some(ColorU::new_rgb(239, 240, 241)),
3283 accent: OptionColorU::Some(ColorU::new_rgb(61, 174, 233)),
3284 ..Default::default()
3285 },
3286 fonts: SystemFonts {
3287 ui_font: OptionString::Some("Noto Sans".into()),
3288 ui_font_size: OptionF32::Some(10.0),
3289 monospace_font: OptionString::Some("Hack".into()),
3290 ..Default::default()
3291 },
3292 metrics: SystemMetrics {
3293 corner_radius: OptionPixelValue::Some(PixelValue::px(4.0)),
3294 border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
3295 button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(12.0)),
3296 button_padding_vertical: OptionPixelValue::Some(PixelValue::px(6.0)),
3297 titlebar: TitlebarMetrics::linux_gnome(),
3298 },
3299 scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_KDE_OXYGEN))),
3300 app_specific_stylesheet: None,
3301 icon_style: IconStyleOptions::default(),
3302 language: AzString::from_const_str("en-US"),
3303 os_version: OsVersion::LINUX_6_0,
3304 prefers_reduced_motion: BoolCondition::False,
3305 prefers_high_contrast: BoolCondition::False,
3306 ..Default::default()
3307 }
3308 }
3309
3310 pub fn android_material_light() -> SystemStyle {
3313 SystemStyle {
3314 platform: Platform::Android,
3315 theme: Theme::Light,
3316 colors: SystemColors {
3317 text: OptionColorU::Some(ColorU::new_rgb(0, 0, 0)),
3318 background: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
3319 accent: OptionColorU::Some(ColorU::new_rgb(98, 0, 238)),
3320 ..Default::default()
3321 },
3322 fonts: SystemFonts {
3323 ui_font: OptionString::Some("Roboto".into()),
3324 ui_font_size: OptionF32::Some(14.0),
3325 monospace_font: OptionString::Some("Droid Sans Mono".into()),
3326 ..Default::default()
3327 },
3328 metrics: SystemMetrics {
3329 corner_radius: OptionPixelValue::Some(PixelValue::px(12.0)),
3330 border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
3331 button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(16.0)),
3332 button_padding_vertical: OptionPixelValue::Some(PixelValue::px(10.0)),
3333 titlebar: TitlebarMetrics::android(),
3334 },
3335 scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_ANDROID_LIGHT))),
3336 app_specific_stylesheet: None,
3337 icon_style: IconStyleOptions::default(),
3338 language: AzString::from_const_str("en-US"),
3339 os_version: OsVersion::ANDROID_14,
3340 prefers_reduced_motion: BoolCondition::False,
3341 prefers_high_contrast: BoolCondition::False,
3342 scroll_physics: ScrollPhysics::android(),
3343 ..Default::default()
3344 }
3345 }
3346
3347 pub fn android_holo_dark() -> SystemStyle {
3348 SystemStyle {
3349 platform: Platform::Android,
3350 theme: Theme::Dark,
3351 colors: SystemColors {
3352 text: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
3353 background: OptionColorU::Some(ColorU::new_rgb(0, 0, 0)),
3354 accent: OptionColorU::Some(ColorU::new_rgb(51, 181, 229)),
3355 ..Default::default()
3356 },
3357 fonts: SystemFonts {
3358 ui_font: OptionString::Some("Roboto".into()),
3359 ui_font_size: OptionF32::Some(14.0),
3360 monospace_font: OptionString::Some("Droid Sans Mono".into()),
3361 ..Default::default()
3362 },
3363 metrics: SystemMetrics {
3364 corner_radius: OptionPixelValue::Some(PixelValue::px(2.0)),
3365 border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
3366 button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(12.0)),
3367 button_padding_vertical: OptionPixelValue::Some(PixelValue::px(8.0)),
3368 titlebar: TitlebarMetrics::android(),
3369 },
3370 scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_ANDROID_DARK))),
3371 app_specific_stylesheet: None,
3372 icon_style: IconStyleOptions::default(),
3373 language: AzString::from_const_str("en-US"),
3374 os_version: OsVersion::ANDROID_ICE_CREAM_SANDWICH,
3375 prefers_reduced_motion: BoolCondition::False,
3376 prefers_high_contrast: BoolCondition::False,
3377 scroll_physics: ScrollPhysics::android(),
3378 ..Default::default()
3379 }
3380 }
3381
3382 pub fn ios_light() -> SystemStyle {
3383 SystemStyle {
3384 platform: Platform::Ios,
3385 theme: Theme::Light,
3386 colors: SystemColors {
3387 text: OptionColorU::Some(ColorU::new_rgb(0, 0, 0)),
3388 background: OptionColorU::Some(ColorU::new_rgb(242, 242, 247)),
3389 accent: OptionColorU::Some(ColorU::new_rgb(0, 122, 255)),
3390 ..Default::default()
3391 },
3392 fonts: SystemFonts {
3393 ui_font: OptionString::Some(".SFUI-Display-Regular".into()),
3394 ui_font_size: OptionF32::Some(17.0),
3395 monospace_font: OptionString::Some("Menlo".into()),
3396 ..Default::default()
3397 },
3398 metrics: SystemMetrics {
3399 corner_radius: OptionPixelValue::Some(PixelValue::px(10.0)),
3400 border_width: OptionPixelValue::Some(PixelValue::px(0.5)),
3401 button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(20.0)),
3402 button_padding_vertical: OptionPixelValue::Some(PixelValue::px(12.0)),
3403 titlebar: TitlebarMetrics::ios(),
3404 },
3405 scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_IOS_LIGHT))),
3406 app_specific_stylesheet: None,
3407 icon_style: IconStyleOptions::default(),
3408 language: AzString::from_const_str("en-US"),
3409 os_version: OsVersion::IOS_17,
3410 prefers_reduced_motion: BoolCondition::False,
3411 prefers_high_contrast: BoolCondition::False,
3412 scroll_physics: ScrollPhysics::ios(),
3413 ..Default::default()
3414 }
3415 }
3416}