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},
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},
38 },
39};
40
41#[derive(Debug, Default, Clone, PartialEq, Eq)]
45#[repr(C, u8)]
46pub enum Platform {
47 Windows,
48 MacOs,
49 Linux(DesktopEnvironment),
50 Android,
51 Ios,
52 #[default]
53 Unknown,
54}
55
56#[derive(Debug, Clone, PartialEq, Eq)]
58#[repr(C, u8)]
59pub enum DesktopEnvironment {
60 Gnome,
61 Kde,
62 Other(AzString),
63}
64
65#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
67#[repr(C)]
68pub enum Theme {
69 #[default]
70 Light,
71 Dark,
72}
73
74#[derive(Debug, Default, Clone, PartialEq)]
76#[repr(C)]
77pub struct SystemStyle {
78 pub theme: Theme,
79 pub platform: Platform,
80 pub colors: SystemColors,
81 pub fonts: SystemFonts,
82 pub metrics: SystemMetrics,
83 pub language: AzString,
86 pub app_specific_stylesheet: Option<Box<Stylesheet>>,
91 pub icon_style: IconStyleOptions,
93 pub scrollbar: Option<Box<ComputedScrollbarStyle>>,
95}
96
97#[derive(Debug, Default, Clone, PartialEq)]
102#[repr(C)]
103pub struct IconStyleOptions {
104 pub prefer_grayscale: bool,
107 pub tint_color: OptionColorU,
110 pub inherit_text_color: bool,
113}
114
115#[derive(Debug, Default, Clone, PartialEq)]
117#[repr(C)]
118pub struct SystemColors {
119 pub text: OptionColorU,
120 pub background: OptionColorU,
121 pub accent: OptionColorU,
122 pub accent_text: OptionColorU,
123 pub button_face: OptionColorU,
124 pub button_text: OptionColorU,
125 pub window_background: OptionColorU,
126 pub selection_background: OptionColorU,
127 pub selection_text: OptionColorU,
128}
129
130#[derive(Debug, Default, Clone, PartialEq)]
132#[repr(C)]
133pub struct SystemFonts {
134 pub ui_font: OptionString,
136 pub ui_font_size: OptionF32,
138 pub monospace_font: OptionString,
140}
141
142#[derive(Debug, Default, Clone, PartialEq)]
144#[repr(C)]
145pub struct SystemMetrics {
146 pub corner_radius: OptionPixelValue,
148 pub border_width: OptionPixelValue,
150}
151
152impl SystemStyle {
153 pub fn new() -> Self {
161 let mut style = {
163 #[cfg(feature = "io")]
164 {
165 #[cfg(target_os = "linux")]
166 {
167 discover_linux_style()
168 }
169 #[cfg(target_os = "windows")]
170 {
171 discover_windows_style()
172 }
173 #[cfg(target_os = "macos")]
174 {
175 discover_macos_style()
176 }
177 #[cfg(target_os = "android")]
178 {
179 defaults::android_material_light()
180 }
181 #[cfg(target_os = "ios")]
182 {
183 defaults::ios_light()
184 }
185 #[cfg(not(any(
186 target_os = "linux",
187 target_os = "windows",
188 target_os = "macos",
189 target_os = "android",
190 target_os = "ios"
191 )))]
192 {
193 Self::default()
194 } }
196 #[cfg(not(feature = "io"))]
197 {
198 #[cfg(target_os = "windows")]
200 {
201 defaults::windows_11_light()
202 }
203 #[cfg(target_os = "macos")]
204 {
205 defaults::macos_modern_light()
206 }
207 #[cfg(target_os = "linux")]
208 {
209 defaults::gnome_adwaita_light()
210 }
211 #[cfg(target_os = "android")]
212 {
213 defaults::android_material_light()
214 }
215 #[cfg(target_os = "ios")]
216 {
217 defaults::ios_light()
218 }
219 #[cfg(not(any(
220 target_os = "linux",
221 target_os = "windows",
222 target_os = "macos",
223 target_os = "android",
224 target_os = "ios"
225 )))]
226 {
227 Self::default()
228 }
229 }
230 };
231
232 #[cfg(feature = "io")]
234 {
235 if std::env::var("AZUL_DISABLE_RICING").is_ok() {
236 return style; }
238
239 if let Some(stylesheet) = load_app_specific_stylesheet() {
241 style.app_specific_stylesheet = Some(Box::new(stylesheet));
242 }
243 }
244
245 style
246 }
247
248 pub fn create_csd_stylesheet(&self) -> Stylesheet {
253 use alloc::format;
254
255 use crate::parser2::new_from_str;
256
257 let mut css = String::new();
259
260 let bg_color = self
262 .colors
263 .window_background
264 .as_option()
265 .copied()
266 .unwrap_or(ColorU::new_rgb(240, 240, 240));
267 let text_color = self
268 .colors
269 .text
270 .as_option()
271 .copied()
272 .unwrap_or(ColorU::new_rgb(0, 0, 0));
273 let accent_color = self
274 .colors
275 .accent
276 .as_option()
277 .copied()
278 .unwrap_or(ColorU::new_rgb(0, 120, 215));
279 let border_color = match self.theme {
280 Theme::Dark => ColorU::new_rgb(60, 60, 60),
281 Theme::Light => ColorU::new_rgb(200, 200, 200),
282 };
283
284 let corner_radius = self
286 .metrics
287 .corner_radius
288 .map(|px| {
289 use crate::props::basic::pixel::DEFAULT_FONT_SIZE;
290 format!("{}px", px.to_pixels_internal(1.0, DEFAULT_FONT_SIZE))
291 })
292 .unwrap_or_else(|| "4px".to_string());
293
294 css.push_str(&format!(
296 ".csd-titlebar {{ width: 100%; height: 32px; background: rgb({}, {}, {}); \
297 border-bottom: 1px solid rgb({}, {}, {}); display: flex; flex-direction: row; \
298 align-items: center; justify-content: space-between; padding: 0 8px; }} ",
299 bg_color.r, bg_color.g, bg_color.b, border_color.r, border_color.g, border_color.b,
300 ));
301
302 css.push_str(&format!(
304 ".csd-title {{ color: rgb({}, {}, {}); font-size: 13px; flex-grow: 1; text-align: \
305 center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }} ",
306 text_color.r, text_color.g, text_color.b,
307 ));
308
309 css.push_str(".csd-buttons { display: flex; flex-direction: row; gap: 4px; } ");
311
312 css.push_str(&format!(
314 ".csd-button {{ width: 32px; height: 24px; border-radius: {}; background: \
315 transparent; color: rgb({}, {}, {}); font-size: 16px; line-height: 24px; text-align: \
316 center; cursor: pointer; user-select: none; }} ",
317 corner_radius, text_color.r, text_color.g, text_color.b,
318 ));
319
320 let hover_color = match self.theme {
322 Theme::Dark => ColorU::new_rgb(60, 60, 60),
323 Theme::Light => ColorU::new_rgb(220, 220, 220),
324 };
325 css.push_str(&format!(
326 ".csd-button:hover {{ background: rgb({}, {}, {}); }} ",
327 hover_color.r, hover_color.g, hover_color.b,
328 ));
329
330 css.push_str(
332 ".csd-close:hover { background: rgb(232, 17, 35); color: rgb(255, 255, 255); } ",
333 );
334
335 match self.platform {
337 Platform::MacOs => {
338 css.push_str(".csd-buttons { position: absolute; left: 8px; } ");
340 css.push_str(
341 ".csd-close { background: rgb(255, 95, 86); width: 12px; height: 12px; \
342 border-radius: 50%; } ",
343 );
344 css.push_str(
345 ".csd-minimize { background: rgb(255, 189, 46); width: 12px; height: 12px; \
346 border-radius: 50%; } ",
347 );
348 css.push_str(
349 ".csd-maximize { background: rgb(40, 201, 64); width: 12px; height: 12px; \
350 border-radius: 50%; } ",
351 );
352 }
353 Platform::Linux(_) => {
354 css.push_str(".csd-title { text-align: left; } ");
356 }
357 _ => {
358 }
360 }
361
362 let (mut parsed_css, _warnings) = new_from_str(&css);
364
365 if !parsed_css.stylesheets.is_empty() {
367 parsed_css.stylesheets.into_library_owned_vec().remove(0)
368 } else {
369 Stylesheet::default()
370 }
371 }
372}
373
374#[cfg(feature = "io")]
377fn discover_linux_style() -> SystemStyle {
378 if std::env::var("AZUL_SMOKE_AND_MIRRORS").is_err() {
381 if let Ok(kde_style) = discover_kde_style() {
383 return kde_style;
384 }
385 if let Ok(gnome_style) = discover_gnome_style() {
386 return gnome_style;
387 }
388 }
389
390 if let Ok(riced_style) = discover_riced_style() {
392 return riced_style;
393 }
394
395 defaults::gnome_adwaita_light()
397}
398
399#[cfg(feature = "io")]
400fn discover_gnome_style() -> Result<SystemStyle, ()> {
401 let theme_name = run_command_with_timeout(
402 "gsettings",
403 &["get", "org.gnome.desktop.interface", "gtk-theme"],
404 Duration::from_secs(1),
405 )?;
406 let theme_name = theme_name.trim().trim_matches('\'');
407
408 let color_scheme = run_command_with_timeout(
409 "gsettings",
410 &["get", "org.gnome.desktop.interface", "color-scheme"],
411 Duration::from_secs(1),
412 )
413 .unwrap_or_default();
414 let theme = if color_scheme.contains("prefer-dark") {
415 Theme::Dark
416 } else {
417 Theme::Light
418 };
419
420 let ui_font = run_command_with_timeout(
421 "gsettings",
422 &["get", "org.gnome.desktop.interface", "font-name"],
423 Duration::from_secs(1),
424 )
425 .ok();
426 let monospace_font = run_command_with_timeout(
427 "gsettings",
428 &["get", "org.gnome.desktop.interface", "monospace-font-name"],
429 Duration::from_secs(1),
430 )
431 .ok();
432
433 let mut style = if theme == Theme::Dark {
434 defaults::gnome_adwaita_dark()
435 } else {
436 defaults::gnome_adwaita_light()
437 };
438
439 style.platform = Platform::Linux(DesktopEnvironment::Gnome);
440 style.language = detect_system_language();
441 if let Some(font) = ui_font {
442 style.fonts.ui_font = OptionString::Some(font.trim().trim_matches('\'').to_string().into());
443 }
444 if let Some(font) = monospace_font {
445 style.fonts.monospace_font =
446 OptionString::Some(font.trim().trim_matches('\'').to_string().into());
447 }
448
449 Ok(style)
450}
451
452#[cfg(feature = "io")]
453fn discover_kde_style() -> Result<SystemStyle, ()> {
454 run_command_with_timeout("kreadconfig5", &["--version"], Duration::from_secs(1))?;
456
457 let scheme_name = run_command_with_timeout(
459 "kreadconfig5",
460 &["--group", "General", "--key", "ColorScheme"],
461 Duration::from_secs(1),
462 )
463 .unwrap_or_default();
464 let theme = if scheme_name.to_lowercase().contains("dark") {
465 Theme::Dark
466 } else {
467 Theme::Light
468 };
469
470 let mut style = if theme == Theme::Dark {
472 defaults::gnome_adwaita_dark()
474 } else {
475 defaults::kde_breeze_light()
476 };
477 style.platform = Platform::Linux(DesktopEnvironment::Kde);
478 style.language = detect_system_language();
479
480 if let Ok(font_str) = run_command_with_timeout(
482 "kreadconfig5",
483 &["--group", "General", "--key", "font"],
484 Duration::from_secs(1),
485 ) {
486 let mut parts = font_str.trim().split(',');
487 if let Some(font_name) = parts.next() {
488 style.fonts.ui_font = OptionString::Some(font_name.to_string().into());
489 }
490 if let Some(font_size_str) = parts.next() {
491 if let Ok(size) = font_size_str.parse::<f32>() {
492 style.fonts.ui_font_size = OptionF32::Some(size);
493 }
494 }
495 }
496
497 if let Ok(font_str) = run_command_with_timeout(
499 "kreadconfig5",
500 &["--group", "General", "--key", "fixed"],
501 Duration::from_secs(1),
502 ) {
503 if let Some(font_name) = font_str.trim().split(',').next() {
504 style.fonts.monospace_font = OptionString::Some(font_name.to_string().into());
505 }
506 }
507
508 if let Ok(color_str) = run_command_with_timeout(
510 "kreadconfig5",
511 &["--group", "WM", "--key", "activeBackground"],
512 Duration::from_secs(1),
513 ) {
514 let rgb: Vec<Result<u8, _>> = color_str
515 .trim()
516 .split(',')
517 .map(|c| c.parse::<u8>())
518 .collect();
519 if rgb.len() == 3 {
520 if let (Ok(r), Ok(g), Ok(b)) = (&rgb[0], &rgb[1], &rgb[2]) {
521 style.colors.accent = OptionColorU::Some(ColorU::new_rgb(*r, *g, *b));
522 }
523 }
524 }
525
526 Ok(style)
527}
528
529#[cfg(feature = "io")]
530fn discover_riced_style() -> Result<SystemStyle, ()> {
532 let is_hyprland = std::env::var("HYPRLAND_INSTANCE_SIGNATURE").is_ok();
535 if !is_hyprland {
536 return Err(());
539 }
540
541 let mut style = SystemStyle {
542 platform: Platform::Linux(DesktopEnvironment::Other("Tiling WM".into())),
543 ..defaults::gnome_adwaita_dark()
545 };
546 style.language = detect_system_language();
547
548 let home_dir = std::env::var("HOME").unwrap_or_default();
550 let wal_cache_path = format!("{}/.cache/wal/colors.json", home_dir);
551 if let Ok(json_content) = std::fs::read_to_string(wal_cache_path) {
552 if let Ok(json) = serde_json::from_str::<serde_json::Value>(&json_content) {
553 let colors = &json["colors"];
554 style.colors.background = colors["color0"]
555 .as_str()
556 .and_then(|s| parse_css_color(s).ok())
557 .map(OptionColorU::Some)
558 .unwrap_or(OptionColorU::None);
559 style.colors.text = colors["color7"]
560 .as_str()
561 .and_then(|s| parse_css_color(s).ok())
562 .map(OptionColorU::Some)
563 .unwrap_or(OptionColorU::None);
564 style.colors.accent = colors["color4"]
565 .as_str()
566 .and_then(|s| parse_css_color(s).ok())
567 .map(OptionColorU::Some)
568 .unwrap_or(OptionColorU::None);
569 style.theme = Theme::Dark; }
571 }
572
573 let hypr_conf_path = format!("{}/.config/hypr/hyprland.conf", home_dir);
575 if let Ok(conf_content) = std::fs::read_to_string(hypr_conf_path) {
576 for line in conf_content.lines() {
577 let line = line.trim();
578 if line.starts_with('#') || !line.contains('=') {
579 continue;
580 }
581 let mut parts = line.splitn(2, '=').map(|s| s.trim());
582 let key = parts.next();
583 let value = parts.next();
584
585 if let (Some(k), Some(v)) = (key, value) {
586 match k {
587 "rounding" => {
588 if let Ok(px) = v.parse::<f32>() {
589 style.metrics.corner_radius = OptionPixelValue::Some(PixelValue::px(px));
590 }
591 }
592 "border_size" => {
593 if let Ok(px) = v.parse::<f32>() {
594 style.metrics.border_width = OptionPixelValue::Some(PixelValue::px(px));
595 }
596 }
597 "col.active_border" if style.colors.accent.is_none() => {
599 if let Some(hex_str) = v.split_whitespace().last() {
601 if let Ok(color) = parse_css_color(&format!("#{}", hex_str)) {
602 style.colors.accent = OptionColorU::Some(color);
603 }
604 }
605 }
606 _ => {}
607 }
608 }
609 }
610 }
611
612 if let Ok(font_str) = run_command_with_timeout(
614 "gsettings",
615 &["get", "org.gnome.desktop.interface", "font-name"],
616 Duration::from_secs(1),
617 ) {
618 if let Some(font_name) = font_str.trim().trim_matches('\'').split(' ').next() {
619 style.fonts.ui_font = OptionString::Some(font_name.to_string().into());
620 }
621 }
622
623 Ok(style)
624}
625
626#[cfg(feature = "io")]
627fn discover_windows_style() -> SystemStyle {
628 let mut style = defaults::windows_11_light(); style.platform = Platform::Windows;
630 style.language = detect_system_language();
631
632 let theme_val = run_command_with_timeout(
633 "reg",
634 &[
635 "query",
636 r"HKCU\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize",
637 "/v",
638 "AppsUseLightTheme",
639 ],
640 Duration::from_secs(1),
641 );
642 if let Ok(output) = theme_val {
643 if output.contains("0x0") {
644 style = defaults::windows_11_dark();
645 }
646 }
647
648 let accent_val = run_command_with_timeout(
649 "reg",
650 &[
651 "query",
652 r"HKCU\Software\Microsoft\Windows\DWM",
653 "/v",
654 "AccentColor",
655 ],
656 Duration::from_secs(1),
657 );
658 if let Ok(output) = accent_val {
659 if let Some(hex_str) = output.split_whitespace().last() {
660 if let Ok(hex_val) = u32::from_str_radix(hex_str.trim_start_matches("0x"), 16) {
661 let a = (hex_val >> 24) as u8;
662 let b = (hex_val >> 16) as u8;
663 let g = (hex_val >> 8) as u8;
664 let r = hex_val as u8;
665 style.colors.accent = OptionColorU::Some(ColorU::new(r, g, b, a));
666 }
667 }
668 }
669
670 style
671}
672
673#[cfg(feature = "io")]
674fn discover_macos_style() -> SystemStyle {
675 let mut style = defaults::macos_modern_light();
676 style.platform = Platform::MacOs;
677 style.language = detect_system_language();
678
679 let theme_val = run_command_with_timeout(
680 "defaults",
681 &["read", "-g", "AppleInterfaceStyle"],
682 Duration::from_secs(1),
683 );
684 if theme_val.is_ok() {
685 style = defaults::macos_modern_dark();
686 }
687
688 style
689}
690
691#[cfg(feature = "io")]
692fn discover_android_style() -> SystemStyle {
693 defaults::android_material_light()
695}
696
697#[cfg(feature = "io")]
698fn discover_ios_style() -> SystemStyle {
699 defaults::ios_light()
701}
702
703#[cfg(feature = "io")]
706fn run_command_with_timeout(program: &str, args: &[&str], timeout: Duration) -> Result<String, ()> {
708 use std::{
709 process::{Command, Stdio},
710 thread,
711 };
712
713 let mut child = Command::new(program)
714 .args(args)
715 .stdout(Stdio::piped())
716 .stderr(Stdio::piped())
717 .spawn()
718 .map_err(|_| ())?;
719
720 let (tx, rx) = std::sync::mpsc::channel();
721
722 let child_thread = thread::spawn(move || {
723 let output = child.wait_with_output();
724 tx.send(output).ok();
725 });
726
727 match rx.recv_timeout(timeout) {
728 Ok(Ok(output)) if output.status.success() => {
729 Ok(String::from_utf8(output.stdout).unwrap_or_default())
730 }
731 _ => {
732 child_thread.join().ok(); Err(())
736 }
737 }
738}
739
740#[cfg(feature = "io")]
745#[cfg(feature = "io")]
750fn load_app_specific_stylesheet() -> Option<Stylesheet> {
751 let exe_path = std::env::current_exe().ok()?;
753 let exe_name = exe_path.file_name()?.to_str()?;
754
755 let config_dir = dirs_next::config_dir()?;
759
760 let css_path = config_dir
761 .join("azul")
762 .join("styles")
763 .join(format!("{}.css", exe_name));
764
765 let css_content = std::fs::read_to_string(css_path).ok()?;
768
769 if css_content.trim().is_empty() {
770 return None;
771 }
772
773 let (mut css, _warnings) = new_from_str(&css_content);
774
775 if !css.stylesheets.is_empty() {
778 let mut owned_vec = css.stylesheets.into_library_owned_vec();
779 Some(owned_vec.remove(0))
780 } else {
781 None
782 }
783}
784
785#[cfg(feature = "io")]
790pub fn detect_system_language() -> AzString {
791 #[cfg(target_os = "windows")]
792 {
793 detect_language_windows()
794 }
795 #[cfg(target_os = "macos")]
796 {
797 detect_language_macos()
798 }
799 #[cfg(target_os = "linux")]
800 {
801 detect_language_linux()
802 }
803 #[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
804 {
805 AzString::from_const_str("en-US")
806 }
807}
808
809#[cfg(all(feature = "io", target_os = "windows"))]
811fn detect_language_windows() -> AzString {
812 if let Ok(output) = run_command_with_timeout(
814 "powershell",
815 &["-Command", "(Get-Culture).Name"],
816 Duration::from_secs(2),
817 ) {
818 let lang = output.trim();
819 if !lang.is_empty() && lang.contains('-') {
820 return AzString::from(lang.to_string());
821 }
822 }
823
824 if let Ok(output) = run_command_with_timeout(
826 "reg",
827 &[
828 "query",
829 r"HKCU\Control Panel\International",
830 "/v",
831 "LocaleName",
832 ],
833 Duration::from_secs(1),
834 ) {
835 for line in output.lines() {
837 if line.contains("LocaleName") {
838 if let Some(lang) = line.split_whitespace().last() {
839 let lang = lang.trim();
840 if !lang.is_empty() {
841 return AzString::from(lang.to_string());
842 }
843 }
844 }
845 }
846 }
847
848 AzString::from_const_str("en-US")
849}
850
851#[cfg(all(feature = "io", target_os = "macos"))]
853fn detect_language_macos() -> AzString {
854 if let Ok(output) = run_command_with_timeout(
856 "defaults",
857 &["read", "-g", "AppleLocale"],
858 Duration::from_secs(1),
859 ) {
860 let locale = output.trim();
861 if !locale.is_empty() {
862 return AzString::from(locale.replace('_', "-"));
864 }
865 }
866
867 if let Ok(output) = run_command_with_timeout(
869 "defaults",
870 &["read", "-g", "AppleLanguages"],
871 Duration::from_secs(1),
872 ) {
873 for line in output.lines() {
876 let trimmed = line
877 .trim()
878 .trim_matches(|c| c == '"' || c == ',' || c == '(' || c == ')');
879 if !trimmed.is_empty() && trimmed.contains('-') {
880 return AzString::from(trimmed.to_string());
881 }
882 }
883 }
884
885 AzString::from_const_str("en-US")
886}
887
888#[cfg(all(feature = "io", target_os = "linux"))]
890fn detect_language_linux() -> AzString {
891 let env_vars = ["LANGUAGE", "LC_ALL", "LC_MESSAGES", "LANG"];
893
894 for var in &env_vars {
895 if let Ok(value) = std::env::var(var) {
896 let value = value.trim();
897 if value.is_empty() || value == "C" || value == "POSIX" {
898 continue;
899 }
900
901 let lang = value
903 .split('.') .next()
905 .unwrap_or(value)
906 .replace('_', "-"); if !lang.is_empty() {
909 return AzString::from(lang);
910 }
911 }
912 }
913
914 AzString::from_const_str("en-US")
915}
916
917#[cfg(not(feature = "io"))]
919pub fn detect_system_language() -> AzString {
920 AzString::from_const_str("en-US")
921}
922
923pub mod defaults {
924 use crate::{
930 corety::{AzString, OptionF32, OptionString},
931 props::{
932 basic::{
933 color::{ColorU, OptionColorU},
934 pixel::{PixelValue, OptionPixelValue},
935 },
936 layout::{
937 dimensions::LayoutWidth,
938 spacing::{LayoutPaddingLeft, LayoutPaddingRight},
939 },
940 style::{
941 background::StyleBackgroundContent,
942 scrollbar::{
943 ComputedScrollbarStyle, OverscrollBehavior, ScrollBehavior, ScrollbarInfo,
944 SCROLLBAR_ANDROID_DARK, SCROLLBAR_ANDROID_LIGHT, SCROLLBAR_CLASSIC_DARK,
945 SCROLLBAR_CLASSIC_LIGHT, SCROLLBAR_IOS_DARK, SCROLLBAR_IOS_LIGHT,
946 SCROLLBAR_MACOS_DARK, SCROLLBAR_MACOS_LIGHT, SCROLLBAR_WINDOWS_DARK,
947 SCROLLBAR_WINDOWS_LIGHT,
948 },
949 },
950 },
951 system::{
952 DesktopEnvironment, Platform, SystemColors, SystemFonts, SystemMetrics, SystemStyle,
953 Theme, IconStyleOptions,
954 },
955 };
956
957 pub const SCROLLBAR_WINDOWS_CLASSIC: ScrollbarInfo = ScrollbarInfo {
961 width: LayoutWidth::Px(crate::props::basic::pixel::PixelValue::const_px(17)),
962 padding_left: LayoutPaddingLeft {
963 inner: crate::props::basic::pixel::PixelValue::const_px(0),
964 },
965 padding_right: LayoutPaddingRight {
966 inner: crate::props::basic::pixel::PixelValue::const_px(0),
967 },
968 track: StyleBackgroundContent::Color(ColorU {
969 r: 223,
970 g: 223,
971 b: 223,
972 a: 255,
973 }), thumb: StyleBackgroundContent::Color(ColorU {
975 r: 208,
976 g: 208,
977 b: 208,
978 a: 255,
979 }), button: StyleBackgroundContent::Color(ColorU {
981 r: 208,
982 g: 208,
983 b: 208,
984 a: 255,
985 }),
986 corner: StyleBackgroundContent::Color(ColorU {
987 r: 223,
988 g: 223,
989 b: 223,
990 a: 255,
991 }),
992 resizer: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
993 clip_to_container_border: false,
994 scroll_behavior: ScrollBehavior::Auto,
995 overscroll_behavior_x: OverscrollBehavior::None,
996 overscroll_behavior_y: OverscrollBehavior::None,
997 };
998
999 pub const SCROLLBAR_MACOS_AQUA: ScrollbarInfo = ScrollbarInfo {
1001 width: LayoutWidth::Px(crate::props::basic::pixel::PixelValue::const_px(15)),
1002 padding_left: LayoutPaddingLeft {
1003 inner: crate::props::basic::pixel::PixelValue::const_px(0),
1004 },
1005 padding_right: LayoutPaddingRight {
1006 inner: crate::props::basic::pixel::PixelValue::const_px(0),
1007 },
1008 track: StyleBackgroundContent::Color(ColorU {
1009 r: 238,
1010 g: 238,
1011 b: 238,
1012 a: 128,
1013 }), thumb: StyleBackgroundContent::Color(ColorU {
1015 r: 105,
1016 g: 173,
1017 b: 255,
1018 a: 255,
1019 }), button: StyleBackgroundContent::Color(ColorU {
1021 r: 105,
1022 g: 173,
1023 b: 255,
1024 a: 255,
1025 }),
1026 corner: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
1027 resizer: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
1028 clip_to_container_border: true,
1029 scroll_behavior: ScrollBehavior::Smooth,
1030 overscroll_behavior_x: OverscrollBehavior::Auto,
1031 overscroll_behavior_y: OverscrollBehavior::Auto,
1032 };
1033
1034 pub const SCROLLBAR_KDE_OXYGEN: ScrollbarInfo = ScrollbarInfo {
1036 width: LayoutWidth::Px(crate::props::basic::pixel::PixelValue::const_px(14)),
1037 padding_left: LayoutPaddingLeft {
1038 inner: crate::props::basic::pixel::PixelValue::const_px(2),
1039 },
1040 padding_right: LayoutPaddingRight {
1041 inner: crate::props::basic::pixel::PixelValue::const_px(2),
1042 },
1043 track: StyleBackgroundContent::Color(ColorU {
1044 r: 242,
1045 g: 242,
1046 b: 242,
1047 a: 255,
1048 }),
1049 thumb: StyleBackgroundContent::Color(ColorU {
1050 r: 177,
1051 g: 177,
1052 b: 177,
1053 a: 255,
1054 }),
1055 button: StyleBackgroundContent::Color(ColorU {
1056 r: 216,
1057 g: 216,
1058 b: 216,
1059 a: 255,
1060 }),
1061 corner: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
1062 resizer: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
1063 clip_to_container_border: false,
1064 scroll_behavior: ScrollBehavior::Auto,
1065 overscroll_behavior_x: OverscrollBehavior::Auto,
1066 overscroll_behavior_y: OverscrollBehavior::Auto,
1067 };
1068
1069 fn scrollbar_info_to_computed(info: &ScrollbarInfo) -> ComputedScrollbarStyle {
1071 ComputedScrollbarStyle {
1072 width: Some(info.width),
1073 thumb_color: match info.thumb {
1074 StyleBackgroundContent::Color(c) => Some(c),
1075 _ => None,
1076 },
1077 track_color: match info.track {
1078 StyleBackgroundContent::Color(c) => Some(c),
1079 _ => None,
1080 },
1081 }
1082 }
1083
1084 pub fn windows_11_light() -> SystemStyle {
1087 SystemStyle {
1088 theme: Theme::Light,
1089 platform: Platform::Windows,
1090 colors: SystemColors {
1091 text: OptionColorU::Some(ColorU::new_rgb(0, 0, 0)),
1092 background: OptionColorU::Some(ColorU::new_rgb(243, 243, 243)),
1093 accent: OptionColorU::Some(ColorU::new_rgb(0, 95, 184)),
1094 window_background: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
1095 selection_background: OptionColorU::Some(ColorU::new_rgb(0, 120, 215)),
1096 selection_text: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
1097 ..Default::default()
1098 },
1099 fonts: SystemFonts {
1100 ui_font: OptionString::Some("Segoe UI Variable Text".into()),
1101 ui_font_size: OptionF32::Some(9.0),
1102 monospace_font: OptionString::Some("Consolas".into()),
1103 },
1104 metrics: SystemMetrics {
1105 corner_radius: OptionPixelValue::Some(PixelValue::px(4.0)),
1106 border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
1107 },
1108 scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_WINDOWS_LIGHT))),
1109 app_specific_stylesheet: None,
1110 icon_style: IconStyleOptions::default(),
1111 language: AzString::from_const_str("en-US"),
1112 }
1113 }
1114
1115 pub fn windows_11_dark() -> SystemStyle {
1116 SystemStyle {
1117 theme: Theme::Dark,
1118 platform: Platform::Windows,
1119 colors: SystemColors {
1120 text: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
1121 background: OptionColorU::Some(ColorU::new_rgb(32, 32, 32)),
1122 accent: OptionColorU::Some(ColorU::new_rgb(0, 120, 215)),
1123 window_background: OptionColorU::Some(ColorU::new_rgb(25, 25, 25)),
1124 selection_background: OptionColorU::Some(ColorU::new_rgb(0, 120, 215)),
1125 selection_text: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
1126 ..Default::default()
1127 },
1128 fonts: SystemFonts {
1129 ui_font: OptionString::Some("Segoe UI Variable Text".into()),
1130 ui_font_size: OptionF32::Some(9.0),
1131 monospace_font: OptionString::Some("Consolas".into()),
1132 },
1133 metrics: SystemMetrics {
1134 corner_radius: OptionPixelValue::Some(PixelValue::px(4.0)),
1135 border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
1136 },
1137 scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_WINDOWS_DARK))),
1138 app_specific_stylesheet: None,
1139 icon_style: IconStyleOptions::default(),
1140 language: AzString::from_const_str("en-US"),
1141 }
1142 }
1143
1144 pub fn windows_7_aero() -> SystemStyle {
1145 SystemStyle {
1146 theme: Theme::Light,
1147 platform: Platform::Windows,
1148 colors: SystemColors {
1149 text: OptionColorU::Some(ColorU::new_rgb(0, 0, 0)),
1150 background: OptionColorU::Some(ColorU::new_rgb(240, 240, 240)),
1151 accent: OptionColorU::Some(ColorU::new_rgb(51, 153, 255)),
1152 window_background: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
1153 selection_background: OptionColorU::Some(ColorU::new_rgb(51, 153, 255)),
1154 selection_text: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
1155 ..Default::default()
1156 },
1157 fonts: SystemFonts {
1158 ui_font: OptionString::Some("Segoe UI".into()),
1159 ui_font_size: OptionF32::Some(9.0),
1160 monospace_font: OptionString::Some("Consolas".into()),
1161 },
1162 metrics: SystemMetrics {
1163 corner_radius: OptionPixelValue::Some(PixelValue::px(6.0)),
1164 border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
1165 },
1166 scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_CLASSIC_LIGHT))),
1167 app_specific_stylesheet: None,
1168 icon_style: IconStyleOptions::default(),
1169 language: AzString::from_const_str("en-US"),
1170 }
1171 }
1172
1173 pub fn windows_xp_luna() -> SystemStyle {
1174 SystemStyle {
1175 theme: Theme::Light,
1176 platform: Platform::Windows,
1177 colors: SystemColors {
1178 text: OptionColorU::Some(ColorU::new_rgb(0, 0, 0)),
1179 background: OptionColorU::Some(ColorU::new_rgb(236, 233, 216)),
1180 accent: OptionColorU::Some(ColorU::new_rgb(49, 106, 197)),
1181 window_background: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
1182 selection_background: OptionColorU::Some(ColorU::new_rgb(49, 106, 197)),
1183 selection_text: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
1184 ..Default::default()
1185 },
1186 fonts: SystemFonts {
1187 ui_font: OptionString::Some("Tahoma".into()),
1188 ui_font_size: OptionF32::Some(8.0),
1189 monospace_font: OptionString::Some("Lucida Console".into()),
1190 },
1191 metrics: SystemMetrics {
1192 corner_radius: OptionPixelValue::Some(PixelValue::px(3.0)),
1193 border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
1194 },
1195 scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_WINDOWS_CLASSIC))),
1196 app_specific_stylesheet: None,
1197 icon_style: IconStyleOptions::default(),
1198 language: AzString::from_const_str("en-US"),
1199 }
1200 }
1201
1202 pub fn macos_modern_light() -> SystemStyle {
1205 SystemStyle {
1206 platform: Platform::MacOs,
1207 theme: Theme::Light,
1208 colors: SystemColors {
1209 text: OptionColorU::Some(ColorU::new(0, 0, 0, 221)),
1210 background: OptionColorU::Some(ColorU::new_rgb(242, 242, 247)),
1211 accent: OptionColorU::Some(ColorU::new_rgb(0, 122, 255)),
1212 window_background: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
1213 ..Default::default()
1214 },
1215 fonts: SystemFonts {
1216 ui_font: OptionString::Some(".SF NS".into()),
1217 ui_font_size: OptionF32::Some(13.0),
1218 monospace_font: OptionString::Some("Menlo".into()),
1219 },
1220 metrics: SystemMetrics {
1221 corner_radius: OptionPixelValue::Some(PixelValue::px(8.0)),
1222 border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
1223 },
1224 scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_MACOS_LIGHT))),
1225 app_specific_stylesheet: None,
1226 icon_style: IconStyleOptions::default(),
1227 language: AzString::from_const_str("en-US"),
1228 }
1229 }
1230
1231 pub fn macos_modern_dark() -> SystemStyle {
1232 SystemStyle {
1233 platform: Platform::MacOs,
1234 theme: Theme::Dark,
1235 colors: SystemColors {
1236 text: OptionColorU::Some(ColorU::new(255, 255, 255, 221)),
1237 background: OptionColorU::Some(ColorU::new_rgb(28, 28, 30)),
1238 accent: OptionColorU::Some(ColorU::new_rgb(10, 132, 255)),
1239 window_background: OptionColorU::Some(ColorU::new_rgb(44, 44, 46)),
1240 ..Default::default()
1241 },
1242 fonts: SystemFonts {
1243 ui_font: OptionString::Some(".SF NS".into()),
1244 ui_font_size: OptionF32::Some(13.0),
1245 monospace_font: OptionString::Some("Menlo".into()),
1246 },
1247 metrics: SystemMetrics {
1248 corner_radius: OptionPixelValue::Some(PixelValue::px(8.0)),
1249 border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
1250 },
1251 scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_MACOS_DARK))),
1252 app_specific_stylesheet: None,
1253 icon_style: IconStyleOptions::default(),
1254 language: AzString::from_const_str("en-US"),
1255 }
1256 }
1257
1258 pub fn macos_aqua() -> SystemStyle {
1259 SystemStyle {
1260 platform: Platform::MacOs,
1261 theme: Theme::Light,
1262 colors: SystemColors {
1263 text: OptionColorU::Some(ColorU::new_rgb(0, 0, 0)),
1264 background: OptionColorU::Some(ColorU::new_rgb(229, 229, 229)),
1265 accent: OptionColorU::Some(ColorU::new_rgb(63, 128, 234)),
1266 window_background: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
1267 ..Default::default()
1268 },
1269 fonts: SystemFonts {
1270 ui_font: OptionString::Some("Lucida Grande".into()),
1271 ui_font_size: OptionF32::Some(13.0),
1272 monospace_font: OptionString::Some("Monaco".into()),
1273 },
1274 metrics: SystemMetrics {
1275 corner_radius: OptionPixelValue::Some(PixelValue::px(12.0)),
1276 border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
1277 },
1278 scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_MACOS_AQUA))),
1279 app_specific_stylesheet: None,
1280 icon_style: IconStyleOptions::default(),
1281 language: AzString::from_const_str("en-US"),
1282 }
1283 }
1284
1285 pub fn gnome_adwaita_light() -> SystemStyle {
1288 SystemStyle {
1289 platform: Platform::Linux(DesktopEnvironment::Gnome),
1290 theme: Theme::Light,
1291 colors: SystemColors {
1292 text: OptionColorU::Some(ColorU::new_rgb(46, 52, 54)),
1293 background: OptionColorU::Some(ColorU::new_rgb(249, 249, 249)),
1294 accent: OptionColorU::Some(ColorU::new_rgb(53, 132, 228)),
1295 window_background: OptionColorU::Some(ColorU::new_rgb(237, 237, 237)),
1296 ..Default::default()
1297 },
1298 fonts: SystemFonts {
1299 ui_font: OptionString::Some("Cantarell".into()),
1300 ui_font_size: OptionF32::Some(11.0),
1301 monospace_font: OptionString::Some("Monospace".into()),
1302 },
1303 metrics: SystemMetrics {
1304 corner_radius: OptionPixelValue::Some(PixelValue::px(4.0)),
1305 border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
1306 },
1307 scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_CLASSIC_LIGHT))),
1308 app_specific_stylesheet: None,
1309 icon_style: IconStyleOptions::default(),
1310 language: AzString::from_const_str("en-US"),
1311 }
1312 }
1313
1314 pub fn gnome_adwaita_dark() -> SystemStyle {
1315 SystemStyle {
1316 platform: Platform::Linux(DesktopEnvironment::Gnome),
1317 theme: Theme::Dark,
1318 colors: SystemColors {
1319 text: OptionColorU::Some(ColorU::new_rgb(238, 238, 236)),
1320 background: OptionColorU::Some(ColorU::new_rgb(36, 36, 36)),
1321 accent: OptionColorU::Some(ColorU::new_rgb(53, 132, 228)),
1322 window_background: OptionColorU::Some(ColorU::new_rgb(48, 48, 48)),
1323 ..Default::default()
1324 },
1325 fonts: SystemFonts {
1326 ui_font: OptionString::Some("Cantarell".into()),
1327 ui_font_size: OptionF32::Some(11.0),
1328 monospace_font: OptionString::Some("Monospace".into()),
1329 },
1330 metrics: SystemMetrics {
1331 corner_radius: OptionPixelValue::Some(PixelValue::px(4.0)),
1332 border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
1333 },
1334 scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_CLASSIC_DARK))),
1335 app_specific_stylesheet: None,
1336 icon_style: IconStyleOptions::default(),
1337 language: AzString::from_const_str("en-US"),
1338 }
1339 }
1340
1341 pub fn gtk2_clearlooks() -> SystemStyle {
1342 SystemStyle {
1343 platform: Platform::Linux(DesktopEnvironment::Gnome),
1344 theme: Theme::Light,
1345 colors: SystemColors {
1346 text: OptionColorU::Some(ColorU::new_rgb(0, 0, 0)),
1347 background: OptionColorU::Some(ColorU::new_rgb(239, 239, 239)),
1348 accent: OptionColorU::Some(ColorU::new_rgb(245, 121, 0)),
1349 ..Default::default()
1350 },
1351 fonts: SystemFonts {
1352 ui_font: OptionString::Some("DejaVu Sans".into()),
1353 ui_font_size: OptionF32::Some(10.0),
1354 monospace_font: OptionString::Some("DejaVu Sans Mono".into()),
1355 },
1356 metrics: SystemMetrics {
1357 corner_radius: OptionPixelValue::Some(PixelValue::px(4.0)),
1358 border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
1359 },
1360 scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_CLASSIC_LIGHT))),
1361 app_specific_stylesheet: None,
1362 icon_style: IconStyleOptions::default(),
1363 language: AzString::from_const_str("en-US"),
1364 }
1365 }
1366
1367 pub fn kde_breeze_light() -> SystemStyle {
1368 SystemStyle {
1369 platform: Platform::Linux(DesktopEnvironment::Kde),
1370 theme: Theme::Light,
1371 colors: SystemColors {
1372 text: OptionColorU::Some(ColorU::new_rgb(31, 36, 39)),
1373 background: OptionColorU::Some(ColorU::new_rgb(239, 240, 241)),
1374 accent: OptionColorU::Some(ColorU::new_rgb(61, 174, 233)),
1375 ..Default::default()
1376 },
1377 fonts: SystemFonts {
1378 ui_font: OptionString::Some("Noto Sans".into()),
1379 ui_font_size: OptionF32::Some(10.0),
1380 monospace_font: OptionString::Some("Hack".into()),
1381 },
1382 metrics: SystemMetrics {
1383 corner_radius: OptionPixelValue::Some(PixelValue::px(4.0)),
1384 border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
1385 },
1386 scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_KDE_OXYGEN))),
1387 app_specific_stylesheet: None,
1388 icon_style: IconStyleOptions::default(),
1389 language: AzString::from_const_str("en-US"),
1390 }
1391 }
1392
1393 pub fn android_material_light() -> SystemStyle {
1396 SystemStyle {
1397 platform: Platform::Android,
1398 theme: Theme::Light,
1399 colors: SystemColors {
1400 text: OptionColorU::Some(ColorU::new_rgb(0, 0, 0)),
1401 background: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
1402 accent: OptionColorU::Some(ColorU::new_rgb(98, 0, 238)),
1403 ..Default::default()
1404 },
1405 fonts: SystemFonts {
1406 ui_font: OptionString::Some("Roboto".into()),
1407 ui_font_size: OptionF32::Some(14.0),
1408 monospace_font: OptionString::Some("Droid Sans Mono".into()),
1409 },
1410 metrics: SystemMetrics {
1411 corner_radius: OptionPixelValue::Some(PixelValue::px(12.0)),
1412 border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
1413 },
1414 scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_ANDROID_LIGHT))),
1415 app_specific_stylesheet: None,
1416 icon_style: IconStyleOptions::default(),
1417 language: AzString::from_const_str("en-US"),
1418 }
1419 }
1420
1421 pub fn android_holo_dark() -> SystemStyle {
1422 SystemStyle {
1423 platform: Platform::Android,
1424 theme: Theme::Dark,
1425 colors: SystemColors {
1426 text: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
1427 background: OptionColorU::Some(ColorU::new_rgb(0, 0, 0)),
1428 accent: OptionColorU::Some(ColorU::new_rgb(51, 181, 229)),
1429 ..Default::default()
1430 },
1431 fonts: SystemFonts {
1432 ui_font: OptionString::Some("Roboto".into()),
1433 ui_font_size: OptionF32::Some(14.0),
1434 monospace_font: OptionString::Some("Droid Sans Mono".into()),
1435 },
1436 metrics: SystemMetrics {
1437 corner_radius: OptionPixelValue::Some(PixelValue::px(2.0)),
1438 border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
1439 },
1440 scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_ANDROID_DARK))),
1441 app_specific_stylesheet: None,
1442 icon_style: IconStyleOptions::default(),
1443 language: AzString::from_const_str("en-US"),
1444 }
1445 }
1446
1447 pub fn ios_light() -> SystemStyle {
1448 SystemStyle {
1449 platform: Platform::Ios,
1450 theme: Theme::Light,
1451 colors: SystemColors {
1452 text: OptionColorU::Some(ColorU::new_rgb(0, 0, 0)),
1453 background: OptionColorU::Some(ColorU::new_rgb(242, 242, 247)),
1454 accent: OptionColorU::Some(ColorU::new_rgb(0, 122, 255)),
1455 ..Default::default()
1456 },
1457 fonts: SystemFonts {
1458 ui_font: OptionString::Some(".SFUI-Display-Regular".into()),
1459 ui_font_size: OptionF32::Some(17.0),
1460 monospace_font: OptionString::Some("Menlo".into()),
1461 },
1462 metrics: SystemMetrics {
1463 corner_radius: OptionPixelValue::Some(PixelValue::px(10.0)),
1464 border_width: OptionPixelValue::Some(PixelValue::px(0.5)),
1465 },
1466 scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_IOS_LIGHT))),
1467 app_specific_stylesheet: None,
1468 icon_style: IconStyleOptions::default(),
1469 language: AzString::from_const_str("en-US"),
1470 }
1471 }
1472}