#![cfg(feature = "parser")]
use alloc::{
boxed::Box,
string::{String, ToString},
vec::Vec,
};
#[cfg(feature = "io")]
use core::time::Duration;
use crate::{
corety::{AzString, OptionF32, OptionString, OptionU16},
css::Stylesheet,
parser2::{new_from_str, CssParseWarnMsg},
props::{
basic::{
color::{parse_css_color, ColorU, OptionColorU},
pixel::{PixelValue, OptionPixelValue},
},
style::scrollbar::{ComputedScrollbarStyle, OverscrollBehavior, ScrollBehavior, ScrollPhysics},
},
};
#[cfg(all(feature = "system", target_os = "macos"))]
#[path = "system_native_macos.rs"]
mod native_macos;
#[cfg(all(feature = "system", target_os = "windows"))]
#[path = "system_native_windows.rs"]
mod native_windows;
#[cfg(all(feature = "system", target_os = "linux"))]
#[path = "system_native_linux.rs"]
mod native_linux;
#[derive(Debug, Default, Clone, PartialEq, Eq)]
#[repr(C, u8)]
pub enum Platform {
Windows,
MacOs,
Linux(DesktopEnvironment),
Android,
Ios,
#[default]
Unknown,
}
impl Platform {
#[inline]
pub fn current() -> Self {
#[cfg(target_os = "macos")]
{ Platform::MacOs }
#[cfg(target_os = "windows")]
{ Platform::Windows }
#[cfg(target_os = "linux")]
{ Platform::Linux(DesktopEnvironment::Other(AzString::from_const_str("unknown"))) }
#[cfg(target_os = "android")]
{ Platform::Android }
#[cfg(target_os = "ios")]
{ Platform::Ios }
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux", target_os = "android", target_os = "ios")))]
{ Platform::Unknown }
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[repr(C, u8)]
pub enum DesktopEnvironment {
Gnome,
Kde,
Other(AzString),
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub enum Theme {
#[default]
Light,
Dark,
}
#[derive(Debug, Default, Clone, PartialEq)]
#[repr(C)]
pub struct SystemStyle {
pub theme: Theme,
pub platform: Platform,
pub os_version: crate::dynamic_selector::OsVersion,
pub colors: SystemColors,
pub fonts: SystemFonts,
pub metrics: SystemMetrics,
pub language: AzString,
pub prefers_reduced_motion: crate::dynamic_selector::BoolCondition,
pub prefers_high_contrast: crate::dynamic_selector::BoolCondition,
pub app_specific_stylesheet: Option<Box<Stylesheet>>,
pub icon_style: IconStyleOptions,
pub scrollbar: Option<Box<ComputedScrollbarStyle>>,
pub accessibility: AccessibilitySettings,
pub input: InputMetrics,
pub text_rendering: TextRenderingHints,
pub focus_visuals: FocusVisuals,
pub scrollbar_preferences: ScrollbarPreferences,
pub linux: LinuxCustomization,
pub visual_hints: VisualHints,
pub animation: AnimationMetrics,
pub audio: AudioMetrics,
pub scroll_physics: ScrollPhysics,
}
#[derive(Debug, Default, Clone, PartialEq)]
#[repr(C)]
pub struct IconStyleOptions {
pub prefer_grayscale: bool,
pub tint_color: OptionColorU,
pub inherit_text_color: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub enum SystemFontType {
Ui,
UiBold,
Monospace,
MonospaceBold,
MonospaceItalic,
Title,
TitleBold,
Menu,
Small,
Serif,
SerifBold,
}
impl Default for SystemFontType {
fn default() -> Self {
SystemFontType::Ui
}
}
impl SystemFontType {
pub fn from_css_str(s: &str) -> Option<Self> {
let s = s.trim();
if !s.starts_with("system:") {
return None;
}
let rest = &s[7..]; match rest {
"ui" => Some(SystemFontType::Ui),
"ui:bold" => Some(SystemFontType::UiBold),
"monospace" => Some(SystemFontType::Monospace),
"monospace:bold" => Some(SystemFontType::MonospaceBold),
"monospace:italic" => Some(SystemFontType::MonospaceItalic),
"title" => Some(SystemFontType::Title),
"title:bold" => Some(SystemFontType::TitleBold),
"menu" => Some(SystemFontType::Menu),
"small" => Some(SystemFontType::Small),
"serif" => Some(SystemFontType::Serif),
"serif:bold" => Some(SystemFontType::SerifBold),
_ => None,
}
}
pub fn as_css_str(&self) -> &'static str {
match self {
SystemFontType::Ui => "system:ui",
SystemFontType::UiBold => "system:ui:bold",
SystemFontType::Monospace => "system:monospace",
SystemFontType::MonospaceBold => "system:monospace:bold",
SystemFontType::MonospaceItalic => "system:monospace:italic",
SystemFontType::Title => "system:title",
SystemFontType::TitleBold => "system:title:bold",
SystemFontType::Menu => "system:menu",
SystemFontType::Small => "system:small",
SystemFontType::Serif => "system:serif",
SystemFontType::SerifBold => "system:serif:bold",
}
}
pub fn is_bold(&self) -> bool {
matches!(
self,
SystemFontType::UiBold
| SystemFontType::MonospaceBold
| SystemFontType::TitleBold
| SystemFontType::SerifBold
)
}
pub fn is_italic(&self) -> bool {
matches!(self, SystemFontType::MonospaceItalic)
}
}
#[derive(Debug, Default, Clone, PartialEq)]
#[repr(C)]
pub struct AccessibilitySettings {
pub prefers_bold_text: bool,
pub prefers_larger_text: bool,
pub text_scale_factor: f32,
pub prefers_high_contrast: bool,
pub prefers_reduced_motion: bool,
pub prefers_reduced_transparency: bool,
pub screen_reader_active: bool,
pub differentiate_without_color: bool,
}
#[derive(Debug, Default, Clone, PartialEq)]
#[repr(C)]
pub struct SystemColors {
pub text: OptionColorU,
pub secondary_text: OptionColorU,
pub tertiary_text: OptionColorU,
pub background: OptionColorU,
pub accent: OptionColorU,
pub accent_text: OptionColorU,
pub button_face: OptionColorU,
pub button_text: OptionColorU,
pub disabled_text: OptionColorU,
pub window_background: OptionColorU,
pub under_page_background: OptionColorU,
pub selection_background: OptionColorU,
pub selection_text: OptionColorU,
pub selection_background_inactive: OptionColorU,
pub selection_text_inactive: OptionColorU,
pub link: OptionColorU,
pub separator: OptionColorU,
pub grid: OptionColorU,
pub find_highlight: OptionColorU,
pub sidebar_background: OptionColorU,
pub sidebar_selection: OptionColorU,
}
#[derive(Debug, Default, Clone, PartialEq)]
#[repr(C)]
pub struct SystemFonts {
pub ui_font: OptionString,
pub ui_font_size: OptionF32,
pub monospace_font: OptionString,
pub monospace_font_size: OptionF32,
pub ui_font_bold: OptionString,
pub title_font: OptionString,
pub title_font_size: OptionF32,
pub menu_font: OptionString,
pub menu_font_size: OptionF32,
pub small_font: OptionString,
pub small_font_size: OptionF32,
}
#[derive(Debug, Default, Clone, PartialEq)]
#[repr(C)]
pub struct SystemMetrics {
pub corner_radius: OptionPixelValue,
pub border_width: OptionPixelValue,
pub button_padding_horizontal: OptionPixelValue,
pub button_padding_vertical: OptionPixelValue,
pub titlebar: TitlebarMetrics,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
#[repr(C)]
pub enum TitlebarButtonSide {
Left,
#[default]
Right,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C)]
pub struct TitlebarButtons {
pub has_close: bool,
pub has_minimize: bool,
pub has_maximize: bool,
pub has_fullscreen: bool,
}
impl Default for TitlebarButtons {
fn default() -> Self {
Self {
has_close: true,
has_minimize: true,
has_maximize: true,
has_fullscreen: false,
}
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq)]
#[repr(C)]
pub struct SafeAreaInsets {
pub top: OptionPixelValue,
pub bottom: OptionPixelValue,
pub left: OptionPixelValue,
pub right: OptionPixelValue,
}
#[derive(Debug, Clone, PartialEq)]
#[repr(C)]
pub struct TitlebarMetrics {
pub button_side: TitlebarButtonSide,
pub buttons: TitlebarButtons,
pub height: OptionPixelValue,
pub button_area_width: OptionPixelValue,
pub padding_horizontal: OptionPixelValue,
pub safe_area: SafeAreaInsets,
pub title_font: OptionString,
pub title_font_size: OptionF32,
pub title_font_weight: OptionU16,
}
impl Default for TitlebarMetrics {
fn default() -> Self {
Self {
button_side: TitlebarButtonSide::Right,
buttons: TitlebarButtons::default(),
height: OptionPixelValue::Some(PixelValue::px(32.0)),
button_area_width: OptionPixelValue::Some(PixelValue::px(100.0)),
padding_horizontal: OptionPixelValue::Some(PixelValue::px(8.0)),
safe_area: SafeAreaInsets::default(),
title_font: OptionString::None,
title_font_size: OptionF32::Some(13.0),
title_font_weight: OptionU16::Some(600), }
}
}
impl TitlebarMetrics {
pub fn windows() -> Self {
Self {
button_side: TitlebarButtonSide::Right,
buttons: TitlebarButtons {
has_close: true,
has_minimize: true,
has_maximize: true,
has_fullscreen: false,
},
height: OptionPixelValue::Some(PixelValue::px(32.0)),
button_area_width: OptionPixelValue::Some(PixelValue::px(138.0)), padding_horizontal: OptionPixelValue::Some(PixelValue::px(8.0)),
safe_area: SafeAreaInsets::default(),
title_font: OptionString::Some("Segoe UI Variable Text".into()),
title_font_size: OptionF32::Some(12.0),
title_font_weight: OptionU16::Some(400), }
}
pub fn macos() -> Self {
Self {
button_side: TitlebarButtonSide::Left,
buttons: TitlebarButtons {
has_close: true,
has_minimize: true,
has_maximize: false, has_fullscreen: true,
},
height: OptionPixelValue::Some(PixelValue::px(28.0)),
button_area_width: OptionPixelValue::Some(PixelValue::px(78.0)), padding_horizontal: OptionPixelValue::Some(PixelValue::px(8.0)),
safe_area: SafeAreaInsets::default(),
title_font: OptionString::Some(".SF NS".into()),
title_font_size: OptionF32::Some(13.0),
title_font_weight: OptionU16::Some(600), }
}
pub fn linux_gnome() -> Self {
Self {
button_side: TitlebarButtonSide::Right, buttons: TitlebarButtons {
has_close: true,
has_minimize: true,
has_maximize: true,
has_fullscreen: false,
},
height: OptionPixelValue::Some(PixelValue::px(35.0)),
button_area_width: OptionPixelValue::Some(PixelValue::px(100.0)),
padding_horizontal: OptionPixelValue::Some(PixelValue::px(12.0)),
safe_area: SafeAreaInsets::default(),
title_font: OptionString::Some("Cantarell".into()),
title_font_size: OptionF32::Some(11.0),
title_font_weight: OptionU16::Some(700), }
}
pub fn ios() -> Self {
Self {
button_side: TitlebarButtonSide::Left,
buttons: TitlebarButtons {
has_close: false, has_minimize: false,
has_maximize: false,
has_fullscreen: false,
},
height: OptionPixelValue::Some(PixelValue::px(44.0)),
button_area_width: OptionPixelValue::Some(PixelValue::px(0.0)),
padding_horizontal: OptionPixelValue::Some(PixelValue::px(16.0)),
safe_area: SafeAreaInsets {
top: OptionPixelValue::Some(PixelValue::px(47.0)),
bottom: OptionPixelValue::Some(PixelValue::px(34.0)),
left: OptionPixelValue::None,
right: OptionPixelValue::None,
},
title_font: OptionString::Some(".SFUI-Semibold".into()),
title_font_size: OptionF32::Some(17.0),
title_font_weight: OptionU16::Some(600),
}
}
pub fn android() -> Self {
Self {
button_side: TitlebarButtonSide::Left, buttons: TitlebarButtons {
has_close: false,
has_minimize: false,
has_maximize: false,
has_fullscreen: false,
},
height: OptionPixelValue::Some(PixelValue::px(56.0)),
button_area_width: OptionPixelValue::Some(PixelValue::px(48.0)), padding_horizontal: OptionPixelValue::Some(PixelValue::px(16.0)),
safe_area: SafeAreaInsets::default(),
title_font: OptionString::Some("Roboto Medium".into()),
title_font_size: OptionF32::Some(20.0),
title_font_weight: OptionU16::Some(500),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(C)]
pub struct InputMetrics {
pub double_click_time_ms: u32,
pub double_click_distance_px: f32,
pub drag_threshold_px: f32,
pub caret_blink_rate_ms: u32,
pub caret_width_px: f32,
pub wheel_scroll_lines: u32,
pub hover_time_ms: u32,
}
impl Default for InputMetrics {
fn default() -> Self {
Self {
double_click_time_ms: 500,
double_click_distance_px: 4.0,
drag_threshold_px: 5.0,
caret_blink_rate_ms: 530,
caret_width_px: 1.0,
wheel_scroll_lines: 3,
hover_time_ms: 400,
}
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub enum SubpixelType {
#[default]
None,
Rgb,
Bgr,
VRgb,
VBgr,
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(C)]
pub struct TextRenderingHints {
pub font_smoothing_enabled: bool,
pub subpixel_type: SubpixelType,
pub font_smoothing_gamma: u32,
pub increased_contrast: bool,
}
impl Default for TextRenderingHints {
fn default() -> Self {
Self {
font_smoothing_enabled: true,
subpixel_type: SubpixelType::None,
font_smoothing_gamma: 1000,
increased_contrast: false,
}
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq)]
#[repr(C)]
pub struct FocusVisuals {
pub focus_ring_color: OptionColorU,
pub focus_border_width: OptionPixelValue,
pub focus_border_height: OptionPixelValue,
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub enum ScrollbarVisibility {
Always,
#[default]
WhenScrolling,
Automatic,
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub enum ScrollbarTrackClick {
JumpToPosition,
#[default]
PageUpDown,
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(C)]
pub struct ScrollbarPreferences {
pub visibility: ScrollbarVisibility,
pub track_click: ScrollbarTrackClick,
}
impl Default for ScrollbarPreferences {
fn default() -> Self {
Self {
visibility: ScrollbarVisibility::WhenScrolling,
track_click: ScrollbarTrackClick::PageUpDown,
}
}
}
#[derive(Debug, Default, Clone, PartialEq)]
#[repr(C)]
pub struct LinuxCustomization {
pub gtk_theme: OptionString,
pub icon_theme: OptionString,
pub cursor_theme: OptionString,
pub cursor_size: u32,
pub titlebar_button_layout: OptionString,
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub enum ToolbarStyle {
#[default]
IconsOnly,
TextOnly,
TextBesideIcon,
TextBelowIcon,
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(C)]
pub struct VisualHints {
pub show_button_images: bool,
pub show_menu_images: bool,
pub toolbar_style: ToolbarStyle,
pub show_tooltips: bool,
pub flash_on_alert: bool,
}
impl Default for VisualHints {
fn default() -> Self {
Self {
show_button_images: false,
show_menu_images: true,
toolbar_style: ToolbarStyle::IconsOnly,
show_tooltips: true,
flash_on_alert: true,
}
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub enum FocusBehavior {
#[default]
AlwaysVisible,
KeyboardOnly,
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(C)]
pub struct AnimationMetrics {
pub animations_enabled: bool,
pub animation_duration_factor: f32,
pub focus_indicator_behavior: FocusBehavior,
}
impl Default for AnimationMetrics {
fn default() -> Self {
Self {
animations_enabled: true,
animation_duration_factor: 1.0,
focus_indicator_behavior: FocusBehavior::AlwaysVisible,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub struct AudioMetrics {
pub event_sounds_enabled: bool,
pub input_feedback_sounds_enabled: bool,
}
impl Default for AudioMetrics {
fn default() -> Self {
Self {
event_sounds_enabled: true,
input_feedback_sounds_enabled: false,
}
}
}
pub mod apple_fonts {
pub const SYSTEM_FONT: &str = "System Font";
pub const SF_NS_ROUNDED: &str = "SF NS Rounded";
pub const SF_COMPACT: &str = "SF Compact";
pub const SF_MONO: &str = "SF NS Mono Light";
pub const NEW_YORK: &str = "New York";
pub const SF_ARABIC: &str = "SF Arabic";
pub const SF_ARMENIAN: &str = "SF Armenian";
pub const SF_GEORGIAN: &str = "SF Georgian";
pub const SF_HEBREW: &str = "SF Hebrew";
pub const MENLO: &str = "Menlo";
pub const MENLO_REGULAR: &str = "Menlo Regular";
pub const MENLO_BOLD: &str = "Menlo Bold";
pub const MONACO: &str = "Monaco";
pub const LUCIDA_GRANDE: &str = "Lucida Grande";
pub const LUCIDA_GRANDE_BOLD: &str = "Lucida Grande Bold";
pub const HELVETICA_NEUE: &str = "Helvetica Neue";
pub const HELVETICA_NEUE_BOLD: &str = "Helvetica Neue Bold";
}
pub mod windows_fonts {
pub const SEGOE_UI_VARIABLE: &str = "Segoe UI Variable";
pub const SEGOE_UI_VARIABLE_TEXT: &str = "Segoe UI Variable Text";
pub const SEGOE_UI_VARIABLE_DISPLAY: &str = "Segoe UI Variable Display";
pub const SEGOE_UI: &str = "Segoe UI";
pub const CONSOLAS: &str = "Consolas";
pub const CASCADIA_CODE: &str = "Cascadia Code";
pub const CASCADIA_MONO: &str = "Cascadia Mono";
pub const TAHOMA: &str = "Tahoma";
pub const MS_SANS_SERIF: &str = "MS Sans Serif";
pub const LUCIDA_CONSOLE: &str = "Lucida Console";
pub const COURIER_NEW: &str = "Courier New";
}
pub mod linux_fonts {
pub const CANTARELL: &str = "Cantarell";
pub const ADWAITA: &str = "Adwaita";
pub const UBUNTU: &str = "Ubuntu";
pub const UBUNTU_MONO: &str = "Ubuntu Mono";
pub const DEJAVU_SANS: &str = "DejaVu Sans";
pub const DEJAVU_SANS_MONO: &str = "DejaVu Sans Mono";
pub const DEJAVU_SERIF: &str = "DejaVu Serif";
pub const LIBERATION_SANS: &str = "Liberation Sans";
pub const LIBERATION_MONO: &str = "Liberation Mono";
pub const LIBERATION_SERIF: &str = "Liberation Serif";
pub const NOTO_SANS: &str = "Noto Sans";
pub const NOTO_MONO: &str = "Noto Sans Mono";
pub const NOTO_SERIF: &str = "Noto Serif";
pub const HACK: &str = "Hack";
pub const MONOSPACE: &str = "Monospace";
pub const SANS_SERIF: &str = "Sans";
pub const SERIF: &str = "Serif";
}
impl SystemFontType {
pub fn get_fallback_chain(&self, platform: &Platform) -> Vec<&'static str> {
match platform {
Platform::MacOs | Platform::Ios => self.macos_fallback_chain(),
Platform::Windows => self.windows_fallback_chain(),
Platform::Linux(_) => self.linux_fallback_chain(),
Platform::Android => self.android_fallback_chain(),
Platform::Unknown => self.generic_fallback_chain(),
}
}
fn macos_fallback_chain(&self) -> Vec<&'static str> {
match self {
SystemFontType::Ui => vec![
apple_fonts::SYSTEM_FONT,
apple_fonts::HELVETICA_NEUE,
apple_fonts::LUCIDA_GRANDE,
],
SystemFontType::UiBold => vec![
apple_fonts::HELVETICA_NEUE, apple_fonts::LUCIDA_GRANDE,
],
SystemFontType::Monospace => vec![
apple_fonts::MENLO,
apple_fonts::MONACO,
],
SystemFontType::MonospaceBold | SystemFontType::MonospaceItalic => vec![
apple_fonts::MENLO, apple_fonts::MONACO,
],
SystemFontType::Title => vec![
apple_fonts::SYSTEM_FONT,
apple_fonts::HELVETICA_NEUE,
],
SystemFontType::TitleBold => vec![
apple_fonts::HELVETICA_NEUE, apple_fonts::LUCIDA_GRANDE,
],
SystemFontType::Menu => vec![
apple_fonts::SYSTEM_FONT,
apple_fonts::HELVETICA_NEUE,
],
SystemFontType::Small => vec![
apple_fonts::SYSTEM_FONT,
apple_fonts::HELVETICA_NEUE,
],
SystemFontType::Serif => vec![
apple_fonts::NEW_YORK,
"Georgia",
"Times New Roman",
],
SystemFontType::SerifBold => vec![
"Georgia", "Times New Roman",
],
}
}
fn windows_fallback_chain(&self) -> Vec<&'static str> {
match self {
SystemFontType::Ui | SystemFontType::UiBold => vec![
windows_fonts::SEGOE_UI_VARIABLE_TEXT,
windows_fonts::SEGOE_UI,
windows_fonts::TAHOMA,
],
SystemFontType::Monospace | SystemFontType::MonospaceBold | SystemFontType::MonospaceItalic => vec![
windows_fonts::CASCADIA_MONO,
windows_fonts::CASCADIA_CODE,
windows_fonts::CONSOLAS,
windows_fonts::LUCIDA_CONSOLE,
windows_fonts::COURIER_NEW,
],
SystemFontType::Title | SystemFontType::TitleBold => vec![
windows_fonts::SEGOE_UI_VARIABLE_DISPLAY,
windows_fonts::SEGOE_UI,
],
SystemFontType::Menu => vec![
windows_fonts::SEGOE_UI,
windows_fonts::TAHOMA,
],
SystemFontType::Small => vec![
windows_fonts::SEGOE_UI,
],
SystemFontType::Serif | SystemFontType::SerifBold => vec![
"Cambria",
"Georgia",
"Times New Roman",
],
}
}
fn linux_fallback_chain(&self) -> Vec<&'static str> {
match self {
SystemFontType::Ui | SystemFontType::UiBold => vec![
linux_fonts::CANTARELL,
linux_fonts::UBUNTU,
linux_fonts::NOTO_SANS,
linux_fonts::DEJAVU_SANS,
linux_fonts::LIBERATION_SANS,
linux_fonts::SANS_SERIF,
],
SystemFontType::Monospace | SystemFontType::MonospaceBold | SystemFontType::MonospaceItalic => vec![
linux_fonts::UBUNTU_MONO,
linux_fonts::HACK,
linux_fonts::NOTO_MONO,
linux_fonts::DEJAVU_SANS_MONO,
linux_fonts::LIBERATION_MONO,
linux_fonts::MONOSPACE,
],
SystemFontType::Title | SystemFontType::TitleBold => vec![
linux_fonts::CANTARELL,
linux_fonts::UBUNTU,
linux_fonts::NOTO_SANS,
],
SystemFontType::Menu => vec![
linux_fonts::CANTARELL,
linux_fonts::UBUNTU,
linux_fonts::NOTO_SANS,
],
SystemFontType::Small => vec![
linux_fonts::CANTARELL,
linux_fonts::UBUNTU,
linux_fonts::NOTO_SANS,
],
SystemFontType::Serif | SystemFontType::SerifBold => vec![
linux_fonts::NOTO_SERIF,
linux_fonts::DEJAVU_SERIF,
linux_fonts::LIBERATION_SERIF,
linux_fonts::SERIF,
],
}
}
fn android_fallback_chain(&self) -> Vec<&'static str> {
match self {
SystemFontType::Ui | SystemFontType::UiBold => vec!["Roboto", "Noto Sans"],
SystemFontType::Monospace | SystemFontType::MonospaceBold | SystemFontType::MonospaceItalic => {
vec!["Roboto Mono", "Droid Sans Mono", "monospace"]
}
SystemFontType::Title | SystemFontType::TitleBold => vec!["Roboto", "Noto Sans"],
SystemFontType::Menu => vec!["Roboto"],
SystemFontType::Small => vec!["Roboto"],
SystemFontType::Serif | SystemFontType::SerifBold => vec!["Noto Serif", "Droid Serif", "serif"],
}
}
fn generic_fallback_chain(&self) -> Vec<&'static str> {
match self {
SystemFontType::Ui | SystemFontType::UiBold => vec!["sans-serif"],
SystemFontType::Monospace | SystemFontType::MonospaceBold | SystemFontType::MonospaceItalic => {
vec!["monospace"]
}
SystemFontType::Title | SystemFontType::TitleBold => vec!["sans-serif"],
SystemFontType::Menu => vec!["sans-serif"],
SystemFontType::Small => vec!["sans-serif"],
SystemFontType::Serif | SystemFontType::SerifBold => vec!["serif"],
}
}
}
impl SystemStyle {
pub fn to_json_string(&self) -> AzString {
use alloc::format;
fn opt_color(c: &OptionColorU) -> alloc::string::String {
match c.as_ref() {
Some(c) => format!("\"#{:02x}{:02x}{:02x}{:02x}\"", c.r, c.g, c.b, c.a),
None => "null".into(),
}
}
fn opt_str(s: &OptionString) -> alloc::string::String {
match s.as_ref() {
Some(s) => format!("\"{}\"", s.as_str()),
None => "null".into(),
}
}
fn opt_f32(v: &OptionF32) -> alloc::string::String {
match v.into_option() {
Some(v) => format!("{:.2}", v),
None => "null".into(),
}
}
fn opt_u16(v: &OptionU16) -> alloc::string::String {
match v.into_option() {
Some(v) => format!("{}", v),
None => "null".into(),
}
}
fn opt_px(v: &OptionPixelValue) -> alloc::string::String {
match v.as_ref() {
Some(v) => format!("{:.1}", v.to_pixels_internal(0.0, 0.0)),
None => "null".into(),
}
}
let tm = &self.metrics.titlebar;
let inp = &self.input;
let tr = &self.text_rendering;
let acc = &self.accessibility;
let sp = &self.scrollbar_preferences;
let lnx = &self.linux;
let vh = &self.visual_hints;
let anim = &self.animation;
let audio = &self.audio;
let json = format!(
r#"{{
"theme": "{:?}",
"platform": "{:?}",
"os_version": "{:?}:{}",
"language": "{}",
"prefers_reduced_motion": {:?},
"prefers_high_contrast": {:?},
"colors": {{
"text": {},
"secondary_text": {},
"tertiary_text": {},
"background": {},
"accent": {},
"accent_text": {},
"button_face": {},
"button_text": {},
"disabled_text": {},
"window_background": {},
"under_page_background": {},
"selection_background": {},
"selection_text": {},
"selection_background_inactive": {},
"selection_text_inactive": {},
"link": {},
"separator": {},
"grid": {},
"find_highlight": {},
"sidebar_background": {},
"sidebar_selection": {}
}},
"fonts": {{
"ui_font": {},
"ui_font_size": {},
"monospace_font": {},
"title_font": {},
"menu_font": {},
"small_font": {}
}},
"titlebar": {{
"button_side": "{:?}",
"height": {},
"button_area_width": {},
"padding_horizontal": {},
"title_font": {},
"title_font_size": {},
"title_font_weight": {},
"has_close": {},
"has_minimize": {},
"has_maximize": {},
"has_fullscreen": {}
}},
"input": {{
"double_click_time_ms": {},
"double_click_distance_px": {:.1},
"drag_threshold_px": {:.1},
"caret_blink_rate_ms": {},
"caret_width_px": {:.1},
"wheel_scroll_lines": {},
"hover_time_ms": {}
}},
"text_rendering": {{
"font_smoothing_enabled": {},
"subpixel_type": "{:?}",
"font_smoothing_gamma": {},
"increased_contrast": {}
}},
"accessibility": {{
"prefers_bold_text": {},
"prefers_larger_text": {},
"text_scale_factor": {:.2},
"prefers_high_contrast": {},
"prefers_reduced_motion": {},
"prefers_reduced_transparency": {},
"screen_reader_active": {},
"differentiate_without_color": {}
}},
"scrollbar_preferences": {{
"visibility": "{:?}",
"track_click": "{:?}"
}},
"linux": {{
"gtk_theme": {},
"icon_theme": {},
"cursor_theme": {},
"cursor_size": {},
"titlebar_button_layout": {}
}},
"visual_hints": {{
"show_button_images": {},
"show_menu_images": {},
"toolbar_style": "{:?}",
"show_tooltips": {}
}},
"animation": {{
"animations_enabled": {},
"animation_duration_factor": {:.2},
"focus_indicator_behavior": "{:?}"
}},
"audio": {{
"event_sounds_enabled": {},
"input_feedback_sounds_enabled": {}
}}
}}"#,
self.theme,
self.platform,
self.os_version.os, self.os_version.version_id,
self.language.as_str(),
self.prefers_reduced_motion,
self.prefers_high_contrast,
opt_color(&self.colors.text),
opt_color(&self.colors.secondary_text),
opt_color(&self.colors.tertiary_text),
opt_color(&self.colors.background),
opt_color(&self.colors.accent),
opt_color(&self.colors.accent_text),
opt_color(&self.colors.button_face),
opt_color(&self.colors.button_text),
opt_color(&self.colors.disabled_text),
opt_color(&self.colors.window_background),
opt_color(&self.colors.under_page_background),
opt_color(&self.colors.selection_background),
opt_color(&self.colors.selection_text),
opt_color(&self.colors.selection_background_inactive),
opt_color(&self.colors.selection_text_inactive),
opt_color(&self.colors.link),
opt_color(&self.colors.separator),
opt_color(&self.colors.grid),
opt_color(&self.colors.find_highlight),
opt_color(&self.colors.sidebar_background),
opt_color(&self.colors.sidebar_selection),
opt_str(&self.fonts.ui_font),
opt_f32(&self.fonts.ui_font_size),
opt_str(&self.fonts.monospace_font),
opt_str(&self.fonts.title_font),
opt_str(&self.fonts.menu_font),
opt_str(&self.fonts.small_font),
tm.button_side,
opt_px(&tm.height),
opt_px(&tm.button_area_width),
opt_px(&tm.padding_horizontal),
opt_str(&tm.title_font),
opt_f32(&tm.title_font_size),
opt_u16(&tm.title_font_weight),
tm.buttons.has_close,
tm.buttons.has_minimize,
tm.buttons.has_maximize,
tm.buttons.has_fullscreen,
inp.double_click_time_ms,
inp.double_click_distance_px,
inp.drag_threshold_px,
inp.caret_blink_rate_ms,
inp.caret_width_px,
inp.wheel_scroll_lines,
inp.hover_time_ms,
tr.font_smoothing_enabled,
tr.subpixel_type,
tr.font_smoothing_gamma,
tr.increased_contrast,
acc.prefers_bold_text,
acc.prefers_larger_text,
acc.text_scale_factor,
acc.prefers_high_contrast,
acc.prefers_reduced_motion,
acc.prefers_reduced_transparency,
acc.screen_reader_active,
acc.differentiate_without_color,
sp.visibility,
sp.track_click,
opt_str(&lnx.gtk_theme),
opt_str(&lnx.icon_theme),
opt_str(&lnx.cursor_theme),
lnx.cursor_size,
opt_str(&lnx.titlebar_button_layout),
vh.show_button_images,
vh.show_menu_images,
vh.toolbar_style,
vh.show_tooltips,
anim.animations_enabled,
anim.animation_duration_factor,
anim.focus_indicator_behavior,
audio.event_sounds_enabled,
audio.input_feedback_sounds_enabled,
);
AzString::from(json)
}
pub fn detect() -> Self {
let mut style = {
#[cfg(feature = "system")]
{
#[cfg(target_os = "macos")]
{
native_macos::discover()
}
#[cfg(target_os = "windows")]
{
native_windows::discover()
}
#[cfg(target_os = "linux")]
{
native_linux::discover()
}
#[cfg(not(any(
target_os = "macos",
target_os = "windows",
target_os = "linux",
)))]
{
Self::default()
}
}
#[cfg(all(not(feature = "system"), feature = "io"))]
{
#[cfg(target_os = "linux")]
{
discover_linux_style()
}
#[cfg(target_os = "windows")]
{
discover_windows_style()
}
#[cfg(target_os = "macos")]
{
discover_macos_style()
}
#[cfg(target_os = "android")]
{
defaults::android_material_light()
}
#[cfg(target_os = "ios")]
{
defaults::ios_light()
}
#[cfg(not(any(
target_os = "linux",
target_os = "windows",
target_os = "macos",
target_os = "android",
target_os = "ios"
)))]
{
Self::default()
}
}
#[cfg(not(any(feature = "system", feature = "io")))]
{
#[cfg(target_os = "windows")]
{
defaults::windows_11_light()
}
#[cfg(target_os = "macos")]
{
defaults::macos_modern_light()
}
#[cfg(target_os = "linux")]
{
defaults::gnome_adwaita_light()
}
#[cfg(target_os = "android")]
{
defaults::android_material_light()
}
#[cfg(target_os = "ios")]
{
defaults::ios_light()
}
#[cfg(not(any(
target_os = "linux",
target_os = "windows",
target_os = "macos",
target_os = "android",
target_os = "ios"
)))]
{
Self::default()
}
}
};
#[cfg(feature = "io")]
{
if std::env::var("AZUL_DISABLE_RICING").is_ok() {
return style; }
if let Some(stylesheet) = load_app_specific_stylesheet() {
style.app_specific_stylesheet = Some(Box::new(stylesheet));
}
}
style
}
#[inline(always)]
pub fn new() -> Self {
Self::detect()
}
pub fn create_csd_stylesheet(&self) -> Stylesheet {
use alloc::format;
use crate::parser2::new_from_str;
let mut css = String::new();
let bg_color = self
.colors
.window_background
.as_option()
.copied()
.unwrap_or(ColorU::new_rgb(240, 240, 240));
let text_color = self
.colors
.text
.as_option()
.copied()
.unwrap_or(ColorU::new_rgb(0, 0, 0));
let accent_color = self
.colors
.accent
.as_option()
.copied()
.unwrap_or(ColorU::new_rgb(0, 120, 215));
let border_color = match self.theme {
Theme::Dark => ColorU::new_rgb(60, 60, 60),
Theme::Light => ColorU::new_rgb(200, 200, 200),
};
let corner_radius = self
.metrics
.corner_radius
.map(|px| {
use crate::props::basic::pixel::DEFAULT_FONT_SIZE;
format!("{}px", px.to_pixels_internal(1.0, DEFAULT_FONT_SIZE))
})
.unwrap_or_else(|| "4px".to_string());
css.push_str(&format!(
".csd-titlebar {{ width: 100%; height: 32px; background: rgb({}, {}, {}); \
border-bottom: 1px solid rgb({}, {}, {}); display: flex; flex-direction: row; \
align-items: center; justify-content: space-between; padding: 0 8px; \
cursor: grab; user-select: none; }} ",
bg_color.r, bg_color.g, bg_color.b, border_color.r, border_color.g, border_color.b,
));
css.push_str(&format!(
".csd-title {{ color: rgb({}, {}, {}); font-size: 13px; flex-grow: 1; text-align: \
center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; \
user-select: none; }} ",
text_color.r, text_color.g, text_color.b,
));
css.push_str(".csd-buttons { display: flex; flex-direction: row; gap: 4px; } ");
css.push_str(&format!(
".csd-button {{ width: 32px; height: 24px; border-radius: {}; background: \
transparent; color: rgb({}, {}, {}); font-size: 16px; line-height: 24px; text-align: \
center; cursor: pointer; user-select: none; }} ",
corner_radius, text_color.r, text_color.g, text_color.b,
));
let hover_color = match self.theme {
Theme::Dark => ColorU::new_rgb(60, 60, 60),
Theme::Light => ColorU::new_rgb(220, 220, 220),
};
css.push_str(&format!(
".csd-button:hover {{ background: rgb({}, {}, {}); }} ",
hover_color.r, hover_color.g, hover_color.b,
));
css.push_str(
".csd-close:hover { background: rgb(232, 17, 35); color: rgb(255, 255, 255); } ",
);
match self.platform {
Platform::MacOs => {
css.push_str(".csd-buttons { position: absolute; left: 8px; } ");
css.push_str(
".csd-close { background: rgb(255, 95, 86); width: 12px; height: 12px; \
border-radius: 50%; } ",
);
css.push_str(
".csd-minimize { background: rgb(255, 189, 46); width: 12px; height: 12px; \
border-radius: 50%; } ",
);
css.push_str(
".csd-maximize { background: rgb(40, 201, 64); width: 12px; height: 12px; \
border-radius: 50%; } ",
);
}
Platform::Linux(_) => {
css.push_str(".csd-title { text-align: left; } ");
}
_ => {
}
}
let (mut parsed_css, _warnings) = new_from_str(&css);
if !parsed_css.stylesheets.is_empty() {
parsed_css.stylesheets.into_library_owned_vec().remove(0)
} else {
Stylesheet::default()
}
}
}
#[cfg(feature = "io")]
fn discover_linux_style() -> SystemStyle {
if std::env::var("AZUL_SMOKE_AND_MIRRORS").is_err() {
if let Ok(kde_style) = discover_kde_style() {
return kde_style;
}
if let Ok(gnome_style) = discover_gnome_style() {
return gnome_style;
}
}
if let Ok(riced_style) = discover_riced_style() {
return riced_style;
}
defaults::gnome_adwaita_light()
}
#[cfg(feature = "io")]
fn discover_gnome_style() -> Result<SystemStyle, ()> {
use crate::dynamic_selector::BoolCondition;
let theme_name = run_command_with_timeout(
"gsettings",
&["get", "org.gnome.desktop.interface", "gtk-theme"],
Duration::from_secs(1),
)?;
let theme_name = theme_name.trim().trim_matches('\'');
let color_scheme = run_command_with_timeout(
"gsettings",
&["get", "org.gnome.desktop.interface", "color-scheme"],
Duration::from_secs(1),
)
.unwrap_or_default();
let theme = if color_scheme.contains("prefer-dark") {
Theme::Dark
} else {
Theme::Light
};
let ui_font = run_command_with_timeout(
"gsettings",
&["get", "org.gnome.desktop.interface", "font-name"],
Duration::from_secs(1),
)
.ok();
let monospace_font = run_command_with_timeout(
"gsettings",
&["get", "org.gnome.desktop.interface", "monospace-font-name"],
Duration::from_secs(1),
)
.ok();
let mut style = if theme == Theme::Dark {
defaults::gnome_adwaita_dark()
} else {
defaults::gnome_adwaita_light()
};
style.platform = Platform::Linux(DesktopEnvironment::Gnome);
style.language = detect_system_language();
style.os_version = detect_linux_version();
style.prefers_reduced_motion = detect_gnome_reduced_motion();
style.prefers_high_contrast = detect_gnome_high_contrast();
if let Some(font) = ui_font {
style.fonts.ui_font = OptionString::Some(font.trim().trim_matches('\'').to_string().into());
}
if let Some(font) = monospace_font {
style.fonts.monospace_font =
OptionString::Some(font.trim().trim_matches('\'').to_string().into());
}
Ok(style)
}
#[cfg(feature = "io")]
fn discover_kde_style() -> Result<SystemStyle, ()> {
use crate::dynamic_selector::BoolCondition;
run_command_with_timeout("kreadconfig5", &["--version"], Duration::from_secs(1))?;
let scheme_name = run_command_with_timeout(
"kreadconfig5",
&["--group", "General", "--key", "ColorScheme"],
Duration::from_secs(1),
)
.unwrap_or_default();
let theme = if scheme_name.to_lowercase().contains("dark") {
Theme::Dark
} else {
Theme::Light
};
let mut style = if theme == Theme::Dark {
defaults::gnome_adwaita_dark()
} else {
defaults::kde_breeze_light()
};
style.platform = Platform::Linux(DesktopEnvironment::Kde);
style.language = detect_system_language();
style.os_version = detect_linux_version();
style.prefers_reduced_motion = detect_kde_reduced_motion();
style.prefers_high_contrast = BoolCondition::False;
if let Ok(font_str) = run_command_with_timeout(
"kreadconfig5",
&["--group", "General", "--key", "font"],
Duration::from_secs(1),
) {
let mut parts = font_str.trim().split(',');
if let Some(font_name) = parts.next() {
style.fonts.ui_font = OptionString::Some(font_name.to_string().into());
}
if let Some(font_size_str) = parts.next() {
if let Ok(size) = font_size_str.parse::<f32>() {
style.fonts.ui_font_size = OptionF32::Some(size);
}
}
}
if let Ok(font_str) = run_command_with_timeout(
"kreadconfig5",
&["--group", "General", "--key", "fixed"],
Duration::from_secs(1),
) {
if let Some(font_name) = font_str.trim().split(',').next() {
style.fonts.monospace_font = OptionString::Some(font_name.to_string().into());
}
}
if let Ok(color_str) = run_command_with_timeout(
"kreadconfig5",
&["--group", "WM", "--key", "activeBackground"],
Duration::from_secs(1),
) {
let rgb: Vec<Result<u8, _>> = color_str
.trim()
.split(',')
.map(|c| c.parse::<u8>())
.collect();
if rgb.len() == 3 {
if let (Ok(r), Ok(g), Ok(b)) = (&rgb[0], &rgb[1], &rgb[2]) {
style.colors.accent = OptionColorU::Some(ColorU::new_rgb(*r, *g, *b));
}
}
}
Ok(style)
}
#[cfg(feature = "io")]
fn discover_riced_style() -> Result<SystemStyle, ()> {
let is_hyprland = std::env::var("HYPRLAND_INSTANCE_SIGNATURE").is_ok();
if !is_hyprland {
return Err(());
}
let mut style = SystemStyle {
platform: Platform::Linux(DesktopEnvironment::Other("Tiling WM".into())),
..defaults::gnome_adwaita_dark()
};
style.language = detect_system_language();
let home_dir = std::env::var("HOME").unwrap_or_default();
let wal_cache_path = format!("{}/.cache/wal/colors.json", home_dir);
if let Ok(json_content) = std::fs::read_to_string(wal_cache_path) {
if let Ok(json) = serde_json::from_str::<serde_json::Value>(&json_content) {
let colors = &json["colors"];
style.colors.background = colors["color0"]
.as_str()
.and_then(|s| parse_css_color(s).ok())
.map(OptionColorU::Some)
.unwrap_or(OptionColorU::None);
style.colors.text = colors["color7"]
.as_str()
.and_then(|s| parse_css_color(s).ok())
.map(OptionColorU::Some)
.unwrap_or(OptionColorU::None);
style.colors.accent = colors["color4"]
.as_str()
.and_then(|s| parse_css_color(s).ok())
.map(OptionColorU::Some)
.unwrap_or(OptionColorU::None);
style.theme = Theme::Dark; }
}
let hypr_conf_path = format!("{}/.config/hypr/hyprland.conf", home_dir);
if let Ok(conf_content) = std::fs::read_to_string(hypr_conf_path) {
for line in conf_content.lines() {
let line = line.trim();
if line.starts_with('#') || !line.contains('=') {
continue;
}
let mut parts = line.splitn(2, '=').map(|s| s.trim());
let key = parts.next();
let value = parts.next();
if let (Some(k), Some(v)) = (key, value) {
match k {
"rounding" => {
if let Ok(px) = v.parse::<f32>() {
style.metrics.corner_radius = OptionPixelValue::Some(PixelValue::px(px));
}
}
"border_size" => {
if let Ok(px) = v.parse::<f32>() {
style.metrics.border_width = OptionPixelValue::Some(PixelValue::px(px));
}
}
"col.active_border" if style.colors.accent.is_none() => {
if let Some(hex_str) = v.split_whitespace().last() {
if let Ok(color) = parse_css_color(&format!("#{}", hex_str)) {
style.colors.accent = OptionColorU::Some(color);
}
}
}
_ => {}
}
}
}
}
if let Ok(font_str) = run_command_with_timeout(
"gsettings",
&["get", "org.gnome.desktop.interface", "font-name"],
Duration::from_secs(1),
) {
if let Some(font_name) = font_str.trim().trim_matches('\'').split(' ').next() {
style.fonts.ui_font = OptionString::Some(font_name.to_string().into());
}
}
Ok(style)
}
#[cfg(feature = "io")]
fn discover_windows_style() -> SystemStyle {
use crate::dynamic_selector::{BoolCondition, OsVersion};
let mut style = defaults::windows_11_light(); style.platform = Platform::Windows;
style.language = detect_system_language();
style.os_version = detect_windows_version();
style.prefers_reduced_motion = detect_windows_reduced_motion();
style.prefers_high_contrast = detect_windows_high_contrast();
let theme_val = run_command_with_timeout(
"reg",
&[
"query",
r"HKCU\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize",
"/v",
"AppsUseLightTheme",
],
Duration::from_secs(1),
);
if let Ok(output) = theme_val {
if output.contains("0x0") {
style = defaults::windows_11_dark();
style.os_version = detect_windows_version();
style.prefers_reduced_motion = detect_windows_reduced_motion();
style.prefers_high_contrast = detect_windows_high_contrast();
}
}
let accent_val = run_command_with_timeout(
"reg",
&[
"query",
r"HKCU\Software\Microsoft\Windows\DWM",
"/v",
"AccentColor",
],
Duration::from_secs(1),
);
if let Ok(output) = accent_val {
if let Some(hex_str) = output.split_whitespace().last() {
if let Ok(hex_val) = u32::from_str_radix(hex_str.trim_start_matches("0x"), 16) {
let a = (hex_val >> 24) as u8;
let b = (hex_val >> 16) as u8;
let g = (hex_val >> 8) as u8;
let r = hex_val as u8;
style.colors.accent = OptionColorU::Some(ColorU::new(r, g, b, a));
style.colors.selection_background = OptionColorU::Some(ColorU::new(r, g, b, 255));
}
}
}
style
}
#[cfg(feature = "io")]
fn detect_windows_version() -> crate::dynamic_selector::OsVersion {
use crate::dynamic_selector::OsVersion;
if let Ok(output) = run_command_with_timeout(
"reg",
&[
"query",
r"HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion",
"/v",
"CurrentBuildNumber",
],
Duration::from_secs(1),
) {
for line in output.lines() {
if line.contains("CurrentBuildNumber") {
if let Some(build_str) = line.split_whitespace().last() {
if let Ok(build) = build_str.parse::<u32>() {
return match build {
22000..=22499 => OsVersion::WIN_11_21H2,
22500..=22620 => OsVersion::WIN_11_22H2,
22621..=22630 => OsVersion::WIN_11_23H2,
22631.. => OsVersion::WIN_11_24H2,
19041..=19042 => OsVersion::WIN_10_2004,
19043 => OsVersion::WIN_10_21H1,
19044 => OsVersion::WIN_10_21H2,
19045 => OsVersion::WIN_10_22H2,
18362..=18363 => OsVersion::WIN_10_1903,
17763 => OsVersion::WIN_10_1809,
17134 => OsVersion::WIN_10_1803,
16299 => OsVersion::WIN_10_1709,
15063 => OsVersion::WIN_10_1703,
14393 => OsVersion::WIN_10_1607,
10586 => OsVersion::WIN_10_1511,
10240 => OsVersion::WIN_10_1507,
9600 => OsVersion::WIN_8_1,
9200 => OsVersion::WIN_8,
7601 => OsVersion::WIN_7,
6002 => OsVersion::WIN_VISTA,
2600 => OsVersion::WIN_XP,
_ => OsVersion::WIN_10, };
}
}
}
}
}
OsVersion::WIN_10 }
#[cfg(feature = "io")]
fn detect_windows_reduced_motion() -> crate::dynamic_selector::BoolCondition {
use crate::dynamic_selector::BoolCondition;
if let Ok(output) = run_command_with_timeout(
"reg",
&[
"query",
r"HKCU\Control Panel\Desktop\WindowMetrics",
"/v",
"MinAnimate",
],
Duration::from_secs(1),
) {
if output.contains("0x0") {
return BoolCondition::True;
}
}
BoolCondition::False
}
#[cfg(feature = "io")]
fn detect_windows_high_contrast() -> crate::dynamic_selector::BoolCondition {
use crate::dynamic_selector::BoolCondition;
if let Ok(output) = run_command_with_timeout(
"reg",
&[
"query",
r"HKCU\Control Panel\Accessibility\HighContrast",
"/v",
"Flags",
],
Duration::from_secs(1),
) {
if let Some(hex_str) = output.split_whitespace().last() {
if let Ok(flags) = u32::from_str_radix(hex_str.trim_start_matches("0x"), 16) {
if flags & 1 != 0 {
return BoolCondition::True;
}
}
}
}
BoolCondition::False
}
#[cfg(feature = "io")]
fn discover_macos_style() -> SystemStyle {
use crate::dynamic_selector::BoolCondition;
let mut style = defaults::macos_modern_light();
style.platform = Platform::MacOs;
style.language = detect_system_language();
style.os_version = detect_macos_version();
style.prefers_reduced_motion = detect_macos_reduced_motion();
style.prefers_high_contrast = detect_macos_high_contrast();
let theme_val = run_command_with_timeout(
"defaults",
&["read", "-g", "AppleInterfaceStyle"],
Duration::from_secs(1),
);
if theme_val.is_ok() {
style = defaults::macos_modern_dark();
style.os_version = detect_macos_version();
style.prefers_reduced_motion = detect_macos_reduced_motion();
style.prefers_high_contrast = detect_macos_high_contrast();
}
if let Ok(accent_str) = run_command_with_timeout(
"defaults",
&["read", "-g", "AppleAccentColor"],
Duration::from_secs(1),
) {
let accent_color = match accent_str.trim() {
"-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), };
style.colors.accent = OptionColorU::Some(accent_color);
}
if let Ok(highlight_str) = run_command_with_timeout(
"defaults",
&["read", "-g", "AppleHighlightColor"],
Duration::from_secs(1),
) {
let parts: Vec<&str> = highlight_str.trim().split_whitespace().collect();
if parts.len() >= 3 {
if let (Ok(r), Ok(g), Ok(b)) = (
parts[0].parse::<f32>(),
parts[1].parse::<f32>(),
parts[2].parse::<f32>(),
) {
let selection_color = ColorU::new(
(r * 255.0) as u8,
(g * 255.0) as u8,
(b * 255.0) as u8,
128, );
style.colors.selection_background = OptionColorU::Some(selection_color);
let selection_text = match style.theme {
Theme::Dark => ColorU::new_rgb(255, 255, 255),
Theme::Light => ColorU::new_rgb(0, 0, 0),
};
style.colors.selection_text = OptionColorU::Some(selection_text);
}
}
}
style
}
#[cfg(feature = "io")]
fn detect_macos_version() -> crate::dynamic_selector::OsVersion {
use crate::dynamic_selector::OsVersion;
if let Ok(output) = run_command_with_timeout(
"sw_vers",
&["-productVersion"],
Duration::from_secs(1),
) {
let version = output.trim();
let parts: Vec<&str> = version.split('.').collect();
if let Some(major_str) = parts.first() {
if let Ok(major) = major_str.parse::<u32>() {
return match major {
26 => OsVersion::MACOS_TAHOE,
15 => OsVersion::MACOS_SEQUOIA,
14 => OsVersion::MACOS_SONOMA,
13 => OsVersion::MACOS_VENTURA,
12 => OsVersion::MACOS_MONTEREY,
11 => OsVersion::MACOS_BIG_SUR,
10 => {
if let Some(minor_str) = parts.get(1) {
if let Ok(minor) = minor_str.parse::<u32>() {
return match minor {
15 => OsVersion::MACOS_CATALINA,
14 => OsVersion::MACOS_MOJAVE,
13 => OsVersion::MACOS_HIGH_SIERRA,
12 => OsVersion::MACOS_SIERRA,
11 => OsVersion::MACOS_EL_CAPITAN,
10 => OsVersion::MACOS_YOSEMITE,
9 => OsVersion::MACOS_MAVERICKS,
_ => OsVersion::MACOS_CATALINA, };
}
}
OsVersion::MACOS_CATALINA
}
_ => OsVersion::MACOS_SONOMA, };
}
}
}
OsVersion::MACOS_SONOMA }
#[cfg(feature = "io")]
fn detect_macos_reduced_motion() -> crate::dynamic_selector::BoolCondition {
use crate::dynamic_selector::BoolCondition;
if let Ok(output) = run_command_with_timeout(
"defaults",
&["read", "-g", "com.apple.universalaccess", "reduceMotion"],
Duration::from_secs(1),
) {
if output.trim() == "1" {
return BoolCondition::True;
}
}
BoolCondition::False
}
#[cfg(feature = "io")]
fn detect_macos_high_contrast() -> crate::dynamic_selector::BoolCondition {
use crate::dynamic_selector::BoolCondition;
if let Ok(output) = run_command_with_timeout(
"defaults",
&["read", "-g", "com.apple.universalaccess", "increaseContrast"],
Duration::from_secs(1),
) {
if output.trim() == "1" {
return BoolCondition::True;
}
}
BoolCondition::False
}
#[cfg(feature = "io")]
fn detect_linux_version() -> crate::dynamic_selector::OsVersion {
use crate::dynamic_selector::OsVersion;
if let Ok(output) = run_command_with_timeout(
"uname",
&["-r"],
Duration::from_secs(1),
) {
let version = output.trim();
let parts: Vec<&str> = version.split('.').collect();
if let Some(major_str) = parts.first() {
if let Ok(major) = major_str.parse::<u32>() {
return match major {
6 => OsVersion::LINUX_6_0,
5 => OsVersion::LINUX_5_0,
4 => OsVersion::LINUX_4_0,
3 => OsVersion::LINUX_3_0,
2 => OsVersion::LINUX_2_6,
_ => OsVersion::LINUX_6_0, };
}
}
}
OsVersion::LINUX_6_0 }
#[cfg(feature = "io")]
fn detect_gnome_reduced_motion() -> crate::dynamic_selector::BoolCondition {
use crate::dynamic_selector::BoolCondition;
if let Ok(output) = run_command_with_timeout(
"gsettings",
&["get", "org.gnome.desktop.interface", "enable-animations"],
Duration::from_secs(1),
) {
if output.trim() == "false" {
return BoolCondition::True;
}
}
BoolCondition::False
}
#[cfg(feature = "io")]
fn detect_gnome_high_contrast() -> crate::dynamic_selector::BoolCondition {
use crate::dynamic_selector::BoolCondition;
if let Ok(output) = run_command_with_timeout(
"gsettings",
&["get", "org.gnome.desktop.a11y.interface", "high-contrast"],
Duration::from_secs(1),
) {
if output.trim() == "true" {
return BoolCondition::True;
}
}
BoolCondition::False
}
#[cfg(feature = "io")]
fn detect_kde_reduced_motion() -> crate::dynamic_selector::BoolCondition {
use crate::dynamic_selector::BoolCondition;
if let Ok(output) = run_command_with_timeout(
"kreadconfig5",
&["--group", "KDE", "--key", "AnimationDurationFactor"],
Duration::from_secs(1),
) {
if let Ok(factor) = output.trim().parse::<f32>() {
if factor == 0.0 {
return BoolCondition::True;
}
}
}
BoolCondition::False
}
#[cfg(feature = "io")]
pub fn detect_linux_desktop_env() -> DesktopEnvironment {
if let Ok(desktop) = std::env::var("XDG_CURRENT_DESKTOP") {
let desktop = desktop.to_lowercase();
if desktop.contains("gnome") {
return DesktopEnvironment::Gnome;
}
if desktop.contains("kde") || desktop.contains("plasma") {
return DesktopEnvironment::Kde;
}
if desktop.contains("xfce") {
return DesktopEnvironment::Other("XFCE".into());
}
if desktop.contains("unity") {
return DesktopEnvironment::Other("Unity".into());
}
if desktop.contains("cinnamon") {
return DesktopEnvironment::Other("Cinnamon".into());
}
if desktop.contains("mate") {
return DesktopEnvironment::Other("MATE".into());
}
if desktop.contains("lxde") || desktop.contains("lxqt") {
return DesktopEnvironment::Other(desktop.to_uppercase().into());
}
if desktop.contains("hyprland") {
return DesktopEnvironment::Other("Hyprland".into());
}
if desktop.contains("sway") {
return DesktopEnvironment::Other("Sway".into());
}
if desktop.contains("i3") {
return DesktopEnvironment::Other("i3".into());
}
}
if let Ok(session) = std::env::var("DESKTOP_SESSION") {
let session = session.to_lowercase();
if session.contains("gnome") {
return DesktopEnvironment::Gnome;
}
if session.contains("plasma") || session.contains("kde") {
return DesktopEnvironment::Kde;
}
}
if std::env::var("GNOME_DESKTOP_SESSION_ID").is_ok() {
return DesktopEnvironment::Gnome;
}
if std::env::var("KDE_FULL_SESSION").is_ok() {
return DesktopEnvironment::Kde;
}
if std::env::var("HYPRLAND_INSTANCE_SIGNATURE").is_ok() {
return DesktopEnvironment::Other("Hyprland".into());
}
if std::env::var("SWAYSOCK").is_ok() {
return DesktopEnvironment::Other("Sway".into());
}
if std::env::var("I3SOCK").is_ok() {
return DesktopEnvironment::Other("i3".into());
}
DesktopEnvironment::Other("Unknown".into())
}
#[cfg(feature = "io")]
fn discover_android_style() -> SystemStyle {
defaults::android_material_light()
}
#[cfg(feature = "io")]
fn discover_ios_style() -> SystemStyle {
defaults::ios_light()
}
#[cfg(feature = "io")]
fn run_command_with_timeout(program: &str, args: &[&str], timeout: Duration) -> Result<String, ()> {
use std::{
process::{Command, Stdio},
thread,
};
let mut child = Command::new(program)
.args(args)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.map_err(|_| ())?;
let (tx, rx) = std::sync::mpsc::channel();
let child_thread = thread::spawn(move || {
let output = child.wait_with_output();
tx.send(output).ok();
});
match rx.recv_timeout(timeout) {
Ok(Ok(output)) if output.status.success() => {
Ok(String::from_utf8(output.stdout).unwrap_or_default())
}
_ => {
child_thread.join().ok(); Err(())
}
}
}
#[cfg(feature = "io")]
#[cfg(feature = "io")]
fn load_app_specific_stylesheet() -> Option<Stylesheet> {
let exe_path = std::env::current_exe().ok()?;
let exe_name = exe_path.file_name()?.to_str()?;
let config_dir = dirs_next::config_dir()?;
let css_path = config_dir
.join("azul")
.join("styles")
.join(format!("{}.css", exe_name));
let css_content = std::fs::read_to_string(css_path).ok()?;
if css_content.trim().is_empty() {
return None;
}
let (mut css, _warnings) = new_from_str(&css_content);
if !css.stylesheets.is_empty() {
let mut owned_vec = css.stylesheets.into_library_owned_vec();
Some(owned_vec.remove(0))
} else {
None
}
}
#[cfg(feature = "io")]
pub fn detect_system_language() -> AzString {
#[cfg(target_os = "windows")]
{
detect_language_windows()
}
#[cfg(target_os = "macos")]
{
detect_language_macos()
}
#[cfg(target_os = "linux")]
{
detect_language_linux()
}
#[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
{
AzString::from_const_str("en-US")
}
}
#[cfg(all(feature = "io", target_os = "windows"))]
fn detect_language_windows() -> AzString {
if let Ok(output) = run_command_with_timeout(
"powershell",
&["-Command", "(Get-Culture).Name"],
Duration::from_secs(2),
) {
let lang = output.trim();
if !lang.is_empty() && lang.contains('-') {
return AzString::from(lang.to_string());
}
}
if let Ok(output) = run_command_with_timeout(
"reg",
&[
"query",
r"HKCU\Control Panel\International",
"/v",
"LocaleName",
],
Duration::from_secs(1),
) {
for line in output.lines() {
if line.contains("LocaleName") {
if let Some(lang) = line.split_whitespace().last() {
let lang = lang.trim();
if !lang.is_empty() {
return AzString::from(lang.to_string());
}
}
}
}
}
AzString::from_const_str("en-US")
}
#[cfg(all(feature = "io", target_os = "macos"))]
fn detect_language_macos() -> AzString {
if let Ok(output) = run_command_with_timeout(
"defaults",
&["read", "-g", "AppleLocale"],
Duration::from_secs(1),
) {
let locale = output.trim();
if !locale.is_empty() {
return AzString::from(locale.replace('_', "-"));
}
}
if let Ok(output) = run_command_with_timeout(
"defaults",
&["read", "-g", "AppleLanguages"],
Duration::from_secs(1),
) {
for line in output.lines() {
let trimmed = line
.trim()
.trim_matches(|c| c == '"' || c == ',' || c == '(' || c == ')');
if !trimmed.is_empty() && trimmed.contains('-') {
return AzString::from(trimmed.to_string());
}
}
}
AzString::from_const_str("en-US")
}
#[cfg(all(feature = "io", target_os = "linux"))]
fn detect_language_linux() -> AzString {
let env_vars = ["LANGUAGE", "LC_ALL", "LC_MESSAGES", "LANG"];
for var in &env_vars {
if let Ok(value) = std::env::var(var) {
let value = value.trim();
if value.is_empty() || value == "C" || value == "POSIX" {
continue;
}
let lang = value
.split('.') .next()
.unwrap_or(value)
.replace('_', "-");
if !lang.is_empty() {
return AzString::from(lang);
}
}
}
AzString::from_const_str("en-US")
}
#[cfg(not(feature = "io"))]
pub fn detect_system_language() -> AzString {
AzString::from_const_str("en-US")
}
pub mod defaults {
use crate::{
corety::{AzString, OptionF32, OptionString},
dynamic_selector::{BoolCondition, OsVersion},
props::{
basic::{
color::{ColorU, OptionColorU},
pixel::{PixelValue, OptionPixelValue},
},
layout::{
dimensions::LayoutWidth,
spacing::{LayoutPaddingLeft, LayoutPaddingRight},
},
style::{
background::StyleBackgroundContent,
scrollbar::{
ComputedScrollbarStyle, OverflowScrolling, OverscrollBehavior, ScrollBehavior,
ScrollPhysics, ScrollbarInfo,
SCROLLBAR_ANDROID_DARK, SCROLLBAR_ANDROID_LIGHT, SCROLLBAR_CLASSIC_DARK,
SCROLLBAR_CLASSIC_LIGHT, SCROLLBAR_IOS_DARK, SCROLLBAR_IOS_LIGHT,
SCROLLBAR_MACOS_DARK, SCROLLBAR_MACOS_LIGHT, SCROLLBAR_WINDOWS_DARK,
SCROLLBAR_WINDOWS_LIGHT,
},
},
},
system::{
DesktopEnvironment, Platform, SystemColors, SystemFonts, SystemMetrics, SystemStyle,
Theme, IconStyleOptions, TitlebarMetrics,
},
};
pub const SCROLLBAR_WINDOWS_CLASSIC: ScrollbarInfo = ScrollbarInfo {
width: LayoutWidth::Px(crate::props::basic::pixel::PixelValue::const_px(17)),
padding_left: LayoutPaddingLeft {
inner: crate::props::basic::pixel::PixelValue::const_px(0),
},
padding_right: LayoutPaddingRight {
inner: crate::props::basic::pixel::PixelValue::const_px(0),
},
track: StyleBackgroundContent::Color(ColorU {
r: 223,
g: 223,
b: 223,
a: 255,
}), thumb: StyleBackgroundContent::Color(ColorU {
r: 208,
g: 208,
b: 208,
a: 255,
}), button: StyleBackgroundContent::Color(ColorU {
r: 208,
g: 208,
b: 208,
a: 255,
}),
corner: StyleBackgroundContent::Color(ColorU {
r: 223,
g: 223,
b: 223,
a: 255,
}),
resizer: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
clip_to_container_border: false,
scroll_behavior: ScrollBehavior::Auto,
overscroll_behavior_x: OverscrollBehavior::None,
overscroll_behavior_y: OverscrollBehavior::None,
overflow_scrolling: OverflowScrolling::Auto,
};
pub const SCROLLBAR_MACOS_AQUA: ScrollbarInfo = ScrollbarInfo {
width: LayoutWidth::Px(crate::props::basic::pixel::PixelValue::const_px(15)),
padding_left: LayoutPaddingLeft {
inner: crate::props::basic::pixel::PixelValue::const_px(0),
},
padding_right: LayoutPaddingRight {
inner: crate::props::basic::pixel::PixelValue::const_px(0),
},
track: StyleBackgroundContent::Color(ColorU {
r: 238,
g: 238,
b: 238,
a: 128,
}), thumb: StyleBackgroundContent::Color(ColorU {
r: 105,
g: 173,
b: 255,
a: 255,
}), button: StyleBackgroundContent::Color(ColorU {
r: 105,
g: 173,
b: 255,
a: 255,
}),
corner: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
resizer: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
clip_to_container_border: true,
scroll_behavior: ScrollBehavior::Smooth,
overscroll_behavior_x: OverscrollBehavior::Auto,
overscroll_behavior_y: OverscrollBehavior::Auto,
overflow_scrolling: OverflowScrolling::Auto,
};
pub const SCROLLBAR_KDE_OXYGEN: ScrollbarInfo = ScrollbarInfo {
width: LayoutWidth::Px(crate::props::basic::pixel::PixelValue::const_px(14)),
padding_left: LayoutPaddingLeft {
inner: crate::props::basic::pixel::PixelValue::const_px(2),
},
padding_right: LayoutPaddingRight {
inner: crate::props::basic::pixel::PixelValue::const_px(2),
},
track: StyleBackgroundContent::Color(ColorU {
r: 242,
g: 242,
b: 242,
a: 255,
}),
thumb: StyleBackgroundContent::Color(ColorU {
r: 177,
g: 177,
b: 177,
a: 255,
}),
button: StyleBackgroundContent::Color(ColorU {
r: 216,
g: 216,
b: 216,
a: 255,
}),
corner: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
resizer: StyleBackgroundContent::Color(ColorU::TRANSPARENT),
clip_to_container_border: false,
scroll_behavior: ScrollBehavior::Auto,
overscroll_behavior_x: OverscrollBehavior::Auto,
overscroll_behavior_y: OverscrollBehavior::Auto,
overflow_scrolling: OverflowScrolling::Auto,
};
fn scrollbar_info_to_computed(info: &ScrollbarInfo) -> ComputedScrollbarStyle {
ComputedScrollbarStyle {
width: Some(info.width.clone()),
thumb_color: match info.thumb {
StyleBackgroundContent::Color(c) => Some(c),
_ => None,
},
track_color: match info.track {
StyleBackgroundContent::Color(c) => Some(c),
_ => None,
},
}
}
pub fn windows_11_light() -> SystemStyle {
SystemStyle {
theme: Theme::Light,
platform: Platform::Windows,
colors: SystemColors {
text: OptionColorU::Some(ColorU::new_rgb(0, 0, 0)),
background: OptionColorU::Some(ColorU::new_rgb(243, 243, 243)),
accent: OptionColorU::Some(ColorU::new_rgb(0, 95, 184)),
window_background: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
selection_background: OptionColorU::Some(ColorU::new_rgb(0, 120, 215)),
selection_text: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
..Default::default()
},
fonts: SystemFonts {
ui_font: OptionString::Some("Segoe UI Variable Text".into()),
ui_font_size: OptionF32::Some(9.0),
monospace_font: OptionString::Some("Consolas".into()),
..Default::default()
},
metrics: SystemMetrics {
corner_radius: OptionPixelValue::Some(PixelValue::px(4.0)),
border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(12.0)),
button_padding_vertical: OptionPixelValue::Some(PixelValue::px(6.0)),
titlebar: TitlebarMetrics::windows(),
},
scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_WINDOWS_LIGHT))),
app_specific_stylesheet: None,
icon_style: IconStyleOptions::default(),
language: AzString::from_const_str("en-US"),
os_version: OsVersion::WIN_11,
prefers_reduced_motion: BoolCondition::False,
prefers_high_contrast: BoolCondition::False,
scroll_physics: ScrollPhysics::windows(),
..Default::default()
}
}
pub fn windows_11_dark() -> SystemStyle {
SystemStyle {
theme: Theme::Dark,
platform: Platform::Windows,
colors: SystemColors {
text: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
background: OptionColorU::Some(ColorU::new_rgb(32, 32, 32)),
accent: OptionColorU::Some(ColorU::new_rgb(0, 120, 215)),
window_background: OptionColorU::Some(ColorU::new_rgb(25, 25, 25)),
selection_background: OptionColorU::Some(ColorU::new_rgb(0, 120, 215)),
selection_text: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
..Default::default()
},
fonts: SystemFonts {
ui_font: OptionString::Some("Segoe UI Variable Text".into()),
ui_font_size: OptionF32::Some(9.0),
monospace_font: OptionString::Some("Consolas".into()),
..Default::default()
},
metrics: SystemMetrics {
corner_radius: OptionPixelValue::Some(PixelValue::px(4.0)),
border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(12.0)),
button_padding_vertical: OptionPixelValue::Some(PixelValue::px(6.0)),
titlebar: TitlebarMetrics::windows(),
},
scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_WINDOWS_DARK))),
app_specific_stylesheet: None,
icon_style: IconStyleOptions::default(),
language: AzString::from_const_str("en-US"),
os_version: OsVersion::WIN_11,
prefers_reduced_motion: BoolCondition::False,
prefers_high_contrast: BoolCondition::False,
scroll_physics: ScrollPhysics::windows(),
..Default::default()
}
}
pub fn windows_7_aero() -> SystemStyle {
SystemStyle {
theme: Theme::Light,
platform: Platform::Windows,
colors: SystemColors {
text: OptionColorU::Some(ColorU::new_rgb(0, 0, 0)),
background: OptionColorU::Some(ColorU::new_rgb(240, 240, 240)),
accent: OptionColorU::Some(ColorU::new_rgb(51, 153, 255)),
window_background: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
selection_background: OptionColorU::Some(ColorU::new_rgb(51, 153, 255)),
selection_text: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
..Default::default()
},
fonts: SystemFonts {
ui_font: OptionString::Some("Segoe UI".into()),
ui_font_size: OptionF32::Some(9.0),
monospace_font: OptionString::Some("Consolas".into()),
..Default::default()
},
metrics: SystemMetrics {
corner_radius: OptionPixelValue::Some(PixelValue::px(6.0)),
border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(10.0)),
button_padding_vertical: OptionPixelValue::Some(PixelValue::px(5.0)),
titlebar: TitlebarMetrics::windows(),
},
scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_CLASSIC_LIGHT))),
app_specific_stylesheet: None,
icon_style: IconStyleOptions::default(),
language: AzString::from_const_str("en-US"),
os_version: OsVersion::WIN_7,
prefers_reduced_motion: BoolCondition::False,
prefers_high_contrast: BoolCondition::False,
scroll_physics: ScrollPhysics::windows(),
..Default::default()
}
}
pub fn windows_xp_luna() -> SystemStyle {
SystemStyle {
theme: Theme::Light,
platform: Platform::Windows,
colors: SystemColors {
text: OptionColorU::Some(ColorU::new_rgb(0, 0, 0)),
background: OptionColorU::Some(ColorU::new_rgb(236, 233, 216)),
accent: OptionColorU::Some(ColorU::new_rgb(49, 106, 197)),
window_background: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
selection_background: OptionColorU::Some(ColorU::new_rgb(49, 106, 197)),
selection_text: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
..Default::default()
},
fonts: SystemFonts {
ui_font: OptionString::Some("Tahoma".into()),
ui_font_size: OptionF32::Some(8.0),
monospace_font: OptionString::Some("Lucida Console".into()),
..Default::default()
},
metrics: SystemMetrics {
corner_radius: OptionPixelValue::Some(PixelValue::px(3.0)),
border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(8.0)),
button_padding_vertical: OptionPixelValue::Some(PixelValue::px(4.0)),
titlebar: TitlebarMetrics::windows(),
},
scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_WINDOWS_CLASSIC))),
app_specific_stylesheet: None,
icon_style: IconStyleOptions::default(),
language: AzString::from_const_str("en-US"),
os_version: OsVersion::WIN_XP,
prefers_reduced_motion: BoolCondition::False,
prefers_high_contrast: BoolCondition::False,
scroll_physics: ScrollPhysics::windows(),
..Default::default()
}
}
pub fn macos_modern_light() -> SystemStyle {
SystemStyle {
platform: Platform::MacOs,
theme: Theme::Light,
colors: SystemColors {
text: OptionColorU::Some(ColorU::new(0, 0, 0, 221)),
background: OptionColorU::Some(ColorU::new_rgb(242, 242, 247)),
accent: OptionColorU::Some(ColorU::new_rgb(0, 122, 255)),
window_background: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
selection_background: OptionColorU::Some(ColorU::new(0, 122, 255, 128)),
selection_text: OptionColorU::Some(ColorU::new_rgb(0, 0, 0)),
..Default::default()
},
fonts: SystemFonts {
ui_font: OptionString::Some(".SF NS".into()),
ui_font_size: OptionF32::Some(13.0),
monospace_font: OptionString::Some("Menlo".into()),
..Default::default()
},
metrics: SystemMetrics {
corner_radius: OptionPixelValue::Some(PixelValue::px(8.0)),
border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(16.0)),
button_padding_vertical: OptionPixelValue::Some(PixelValue::px(6.0)),
titlebar: TitlebarMetrics::macos(),
},
scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_MACOS_LIGHT))),
app_specific_stylesheet: None,
icon_style: IconStyleOptions::default(),
language: AzString::from_const_str("en-US"),
os_version: OsVersion::MACOS_SONOMA,
prefers_reduced_motion: BoolCondition::False,
prefers_high_contrast: BoolCondition::False,
scroll_physics: ScrollPhysics::macos(),
..Default::default()
}
}
pub fn macos_modern_dark() -> SystemStyle {
SystemStyle {
platform: Platform::MacOs,
theme: Theme::Dark,
colors: SystemColors {
text: OptionColorU::Some(ColorU::new(255, 255, 255, 221)),
background: OptionColorU::Some(ColorU::new_rgb(28, 28, 30)),
accent: OptionColorU::Some(ColorU::new_rgb(10, 132, 255)),
window_background: OptionColorU::Some(ColorU::new_rgb(44, 44, 46)),
selection_background: OptionColorU::Some(ColorU::new(10, 132, 255, 128)),
selection_text: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
..Default::default()
},
fonts: SystemFonts {
ui_font: OptionString::Some(".SF NS".into()),
ui_font_size: OptionF32::Some(13.0),
monospace_font: OptionString::Some("SF Mono".into()),
monospace_font_size: OptionF32::Some(12.0),
title_font: OptionString::Some(".SF NS".into()),
title_font_size: OptionF32::Some(13.0),
menu_font: OptionString::Some(".SF NS".into()),
menu_font_size: OptionF32::Some(13.0),
small_font: OptionString::Some(".SF NS".into()),
small_font_size: OptionF32::Some(11.0),
..Default::default()
},
metrics: SystemMetrics {
corner_radius: OptionPixelValue::Some(PixelValue::px(8.0)),
border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(16.0)),
button_padding_vertical: OptionPixelValue::Some(PixelValue::px(6.0)),
titlebar: TitlebarMetrics::macos(),
},
scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_MACOS_DARK))),
app_specific_stylesheet: None,
icon_style: IconStyleOptions::default(),
language: AzString::from_const_str("en-US"),
os_version: OsVersion::MACOS_SONOMA,
prefers_reduced_motion: BoolCondition::False,
prefers_high_contrast: BoolCondition::False,
scroll_physics: ScrollPhysics::macos(),
..Default::default()
}
}
pub fn macos_aqua() -> SystemStyle {
SystemStyle {
platform: Platform::MacOs,
theme: Theme::Light,
colors: SystemColors {
text: OptionColorU::Some(ColorU::new_rgb(0, 0, 0)),
background: OptionColorU::Some(ColorU::new_rgb(229, 229, 229)),
accent: OptionColorU::Some(ColorU::new_rgb(63, 128, 234)),
window_background: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
..Default::default()
},
fonts: SystemFonts {
ui_font: OptionString::Some("Lucida Grande".into()),
ui_font_size: OptionF32::Some(13.0),
monospace_font: OptionString::Some("Monaco".into()),
monospace_font_size: OptionF32::Some(12.0),
..Default::default()
},
metrics: SystemMetrics {
corner_radius: OptionPixelValue::Some(PixelValue::px(12.0)),
border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(16.0)),
button_padding_vertical: OptionPixelValue::Some(PixelValue::px(6.0)),
titlebar: TitlebarMetrics::macos(),
},
scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_MACOS_AQUA))),
app_specific_stylesheet: None,
icon_style: IconStyleOptions::default(),
language: AzString::from_const_str("en-US"),
os_version: OsVersion::MACOS_TIGER,
prefers_reduced_motion: BoolCondition::False,
prefers_high_contrast: BoolCondition::False,
scroll_physics: ScrollPhysics::macos(),
..Default::default()
}
}
pub fn gnome_adwaita_light() -> SystemStyle {
SystemStyle {
platform: Platform::Linux(DesktopEnvironment::Gnome),
theme: Theme::Light,
colors: SystemColors {
text: OptionColorU::Some(ColorU::new_rgb(46, 52, 54)),
background: OptionColorU::Some(ColorU::new_rgb(249, 249, 249)),
accent: OptionColorU::Some(ColorU::new_rgb(53, 132, 228)),
window_background: OptionColorU::Some(ColorU::new_rgb(237, 237, 237)),
..Default::default()
},
fonts: SystemFonts {
ui_font: OptionString::Some("Cantarell".into()),
ui_font_size: OptionF32::Some(11.0),
monospace_font: OptionString::Some("Monospace".into()),
..Default::default()
},
metrics: SystemMetrics {
corner_radius: OptionPixelValue::Some(PixelValue::px(4.0)),
border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(12.0)),
button_padding_vertical: OptionPixelValue::Some(PixelValue::px(8.0)),
titlebar: TitlebarMetrics::linux_gnome(),
},
scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_CLASSIC_LIGHT))),
app_specific_stylesheet: None,
icon_style: IconStyleOptions::default(),
language: AzString::from_const_str("en-US"),
os_version: OsVersion::LINUX_6_0,
prefers_reduced_motion: BoolCondition::False,
prefers_high_contrast: BoolCondition::False,
..Default::default()
}
}
pub fn gnome_adwaita_dark() -> SystemStyle {
SystemStyle {
platform: Platform::Linux(DesktopEnvironment::Gnome),
theme: Theme::Dark,
colors: SystemColors {
text: OptionColorU::Some(ColorU::new_rgb(238, 238, 236)),
background: OptionColorU::Some(ColorU::new_rgb(36, 36, 36)),
accent: OptionColorU::Some(ColorU::new_rgb(53, 132, 228)),
window_background: OptionColorU::Some(ColorU::new_rgb(48, 48, 48)),
..Default::default()
},
fonts: SystemFonts {
ui_font: OptionString::Some("Cantarell".into()),
ui_font_size: OptionF32::Some(11.0),
monospace_font: OptionString::Some("Monospace".into()),
..Default::default()
},
metrics: SystemMetrics {
corner_radius: OptionPixelValue::Some(PixelValue::px(4.0)),
border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(12.0)),
button_padding_vertical: OptionPixelValue::Some(PixelValue::px(8.0)),
titlebar: TitlebarMetrics::linux_gnome(),
},
scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_CLASSIC_DARK))),
app_specific_stylesheet: None,
icon_style: IconStyleOptions::default(),
language: AzString::from_const_str("en-US"),
os_version: OsVersion::LINUX_6_0,
prefers_reduced_motion: BoolCondition::False,
prefers_high_contrast: BoolCondition::False,
..Default::default()
}
}
pub fn gtk2_clearlooks() -> SystemStyle {
SystemStyle {
platform: Platform::Linux(DesktopEnvironment::Gnome),
theme: Theme::Light,
colors: SystemColors {
text: OptionColorU::Some(ColorU::new_rgb(0, 0, 0)),
background: OptionColorU::Some(ColorU::new_rgb(239, 239, 239)),
accent: OptionColorU::Some(ColorU::new_rgb(245, 121, 0)),
..Default::default()
},
fonts: SystemFonts {
ui_font: OptionString::Some("DejaVu Sans".into()),
ui_font_size: OptionF32::Some(10.0),
monospace_font: OptionString::Some("DejaVu Sans Mono".into()),
..Default::default()
},
metrics: SystemMetrics {
corner_radius: OptionPixelValue::Some(PixelValue::px(4.0)),
border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(10.0)),
button_padding_vertical: OptionPixelValue::Some(PixelValue::px(6.0)),
titlebar: TitlebarMetrics::linux_gnome(),
},
scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_CLASSIC_LIGHT))),
app_specific_stylesheet: None,
icon_style: IconStyleOptions::default(),
language: AzString::from_const_str("en-US"),
os_version: OsVersion::LINUX_2_6,
prefers_reduced_motion: BoolCondition::False,
prefers_high_contrast: BoolCondition::False,
..Default::default()
}
}
pub fn kde_breeze_light() -> SystemStyle {
SystemStyle {
platform: Platform::Linux(DesktopEnvironment::Kde),
theme: Theme::Light,
colors: SystemColors {
text: OptionColorU::Some(ColorU::new_rgb(31, 36, 39)),
background: OptionColorU::Some(ColorU::new_rgb(239, 240, 241)),
accent: OptionColorU::Some(ColorU::new_rgb(61, 174, 233)),
..Default::default()
},
fonts: SystemFonts {
ui_font: OptionString::Some("Noto Sans".into()),
ui_font_size: OptionF32::Some(10.0),
monospace_font: OptionString::Some("Hack".into()),
..Default::default()
},
metrics: SystemMetrics {
corner_radius: OptionPixelValue::Some(PixelValue::px(4.0)),
border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(12.0)),
button_padding_vertical: OptionPixelValue::Some(PixelValue::px(6.0)),
titlebar: TitlebarMetrics::linux_gnome(),
},
scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_KDE_OXYGEN))),
app_specific_stylesheet: None,
icon_style: IconStyleOptions::default(),
language: AzString::from_const_str("en-US"),
os_version: OsVersion::LINUX_6_0,
prefers_reduced_motion: BoolCondition::False,
prefers_high_contrast: BoolCondition::False,
..Default::default()
}
}
pub fn android_material_light() -> SystemStyle {
SystemStyle {
platform: Platform::Android,
theme: Theme::Light,
colors: SystemColors {
text: OptionColorU::Some(ColorU::new_rgb(0, 0, 0)),
background: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
accent: OptionColorU::Some(ColorU::new_rgb(98, 0, 238)),
..Default::default()
},
fonts: SystemFonts {
ui_font: OptionString::Some("Roboto".into()),
ui_font_size: OptionF32::Some(14.0),
monospace_font: OptionString::Some("Droid Sans Mono".into()),
..Default::default()
},
metrics: SystemMetrics {
corner_radius: OptionPixelValue::Some(PixelValue::px(12.0)),
border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(16.0)),
button_padding_vertical: OptionPixelValue::Some(PixelValue::px(10.0)),
titlebar: TitlebarMetrics::android(),
},
scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_ANDROID_LIGHT))),
app_specific_stylesheet: None,
icon_style: IconStyleOptions::default(),
language: AzString::from_const_str("en-US"),
os_version: OsVersion::ANDROID_14,
prefers_reduced_motion: BoolCondition::False,
prefers_high_contrast: BoolCondition::False,
scroll_physics: ScrollPhysics::android(),
..Default::default()
}
}
pub fn android_holo_dark() -> SystemStyle {
SystemStyle {
platform: Platform::Android,
theme: Theme::Dark,
colors: SystemColors {
text: OptionColorU::Some(ColorU::new_rgb(255, 255, 255)),
background: OptionColorU::Some(ColorU::new_rgb(0, 0, 0)),
accent: OptionColorU::Some(ColorU::new_rgb(51, 181, 229)),
..Default::default()
},
fonts: SystemFonts {
ui_font: OptionString::Some("Roboto".into()),
ui_font_size: OptionF32::Some(14.0),
monospace_font: OptionString::Some("Droid Sans Mono".into()),
..Default::default()
},
metrics: SystemMetrics {
corner_radius: OptionPixelValue::Some(PixelValue::px(2.0)),
border_width: OptionPixelValue::Some(PixelValue::px(1.0)),
button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(12.0)),
button_padding_vertical: OptionPixelValue::Some(PixelValue::px(8.0)),
titlebar: TitlebarMetrics::android(),
},
scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_ANDROID_DARK))),
app_specific_stylesheet: None,
icon_style: IconStyleOptions::default(),
language: AzString::from_const_str("en-US"),
os_version: OsVersion::ANDROID_ICE_CREAM_SANDWICH,
prefers_reduced_motion: BoolCondition::False,
prefers_high_contrast: BoolCondition::False,
scroll_physics: ScrollPhysics::android(),
..Default::default()
}
}
pub fn ios_light() -> SystemStyle {
SystemStyle {
platform: Platform::Ios,
theme: Theme::Light,
colors: SystemColors {
text: OptionColorU::Some(ColorU::new_rgb(0, 0, 0)),
background: OptionColorU::Some(ColorU::new_rgb(242, 242, 247)),
accent: OptionColorU::Some(ColorU::new_rgb(0, 122, 255)),
..Default::default()
},
fonts: SystemFonts {
ui_font: OptionString::Some(".SFUI-Display-Regular".into()),
ui_font_size: OptionF32::Some(17.0),
monospace_font: OptionString::Some("Menlo".into()),
..Default::default()
},
metrics: SystemMetrics {
corner_radius: OptionPixelValue::Some(PixelValue::px(10.0)),
border_width: OptionPixelValue::Some(PixelValue::px(0.5)),
button_padding_horizontal: OptionPixelValue::Some(PixelValue::px(20.0)),
button_padding_vertical: OptionPixelValue::Some(PixelValue::px(12.0)),
titlebar: TitlebarMetrics::ios(),
},
scrollbar: Some(Box::new(scrollbar_info_to_computed(&SCROLLBAR_IOS_LIGHT))),
app_specific_stylesheet: None,
icon_style: IconStyleOptions::default(),
language: AzString::from_const_str("en-US"),
os_version: OsVersion::IOS_17,
prefers_reduced_motion: BoolCondition::False,
prefers_high_contrast: BoolCondition::False,
scroll_physics: ScrollPhysics::ios(),
..Default::default()
}
}
}