use crate::tokens::{
ButtonMetrics, ColorWellMetrics, FocusRingMetrics, LayoutMetrics, MacAppearanceColors,
MacControlMetrics, MacDesignTokens, MacMotionTokens, MacTypographyTokens, OutlineViewMetrics,
PopUpButtonMetrics, PopoverMetrics, ScrollViewMetrics, SearchFieldMetrics, SegmentedMetrics,
SliderMetrics, SplitViewMetrics, StackViewMetrics, StepperMetrics, TextFieldMetrics,
ThumbnailCellMetrics, ToggleMetrics, ToolbarMetrics,
};
use egui::Color32;
use std::sync::{Arc, OnceLock};
pub mod contrast;
pub use contrast::{
contrast_ratio, relative_luminance, srgb_channel_to_linear, MacBackgroundStyle,
};
static DEFAULT_TOKENS: OnceLock<Arc<MacDesignTokens>> = OnceLock::new();
fn default_cached_tokens() -> Arc<MacDesignTokens> {
DEFAULT_TOKENS
.get_or_init(|| Arc::new(MacDesignTokens::load_default()))
.clone()
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum MacAppearance {
#[default]
Aqua,
DarkAqua,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum WindowControlsStyle {
#[default]
AppleTrafficLights,
WindowsCaptionButtons,
NativeHost,
}
#[derive(Debug, Clone)]
pub struct MacTheme {
pub appearance: MacAppearance,
pub window_controls_style: WindowControlsStyle,
tokens: Arc<MacDesignTokens>,
custom_accent: Option<Color32>,
}
impl Default for MacTheme {
fn default() -> Self {
Self::new(MacAppearance::Aqua)
}
}
impl MacTheme {
pub fn new(appearance: MacAppearance) -> Self {
Self {
appearance,
window_controls_style: WindowControlsStyle::AppleTrafficLights,
tokens: default_cached_tokens(),
custom_accent: None,
}
}
pub fn with_window_controls_style(mut self, style: WindowControlsStyle) -> Self {
self.window_controls_style = style;
self
}
pub fn window_controls_style(&self) -> WindowControlsStyle {
self.window_controls_style
}
pub fn with_accent(mut self, accent: Color32) -> Self {
self.custom_accent = Some(accent);
self
}
pub fn with_tokens(appearance: MacAppearance, tokens: MacDesignTokens) -> Self {
Self {
appearance,
window_controls_style: WindowControlsStyle::AppleTrafficLights,
tokens: Arc::new(tokens),
custom_accent: None,
}
}
pub fn with_arc_tokens(appearance: MacAppearance, tokens: Arc<MacDesignTokens>) -> Self {
Self {
appearance,
window_controls_style: WindowControlsStyle::AppleTrafficLights,
tokens,
custom_accent: None,
}
}
pub fn tokens(&self) -> &MacDesignTokens {
&self.tokens
}
pub fn colors(&self) -> &MacAppearanceColors {
match self.appearance {
MacAppearance::Aqua => &self.tokens.colors().aqua,
MacAppearance::DarkAqua => &self.tokens.colors().dark_aqua,
}
}
pub fn window_background(&self) -> Color32 {
self.colors().window_background.to_color32()
}
pub fn control_background(&self) -> Color32 {
self.colors().control_background.to_color32()
}
pub fn control(&self) -> Color32 {
self.colors().control.to_color32()
}
pub fn control_accent(&self) -> Color32 {
self.custom_accent
.unwrap_or_else(|| self.colors().control_accent.to_color32())
}
pub fn selected_control(&self) -> Color32 {
self.custom_accent
.unwrap_or_else(|| self.colors().selected_control.to_color32())
}
pub fn selected_control_inactive(&self) -> Color32 {
self.colors().selected_control_inactive.to_color32()
}
pub fn separator(&self) -> Color32 {
self.colors().separator.to_color32()
}
pub fn placeholder_text(&self) -> Color32 {
self.colors().placeholder_text.to_color32()
}
pub fn keyboard_focus_indicator(&self) -> Color32 {
self.colors().keyboard_focus_indicator.to_color32()
}
pub fn label(&self) -> Color32 {
self.colors().label.to_color32()
}
pub fn secondary_label(&self) -> Color32 {
self.colors().secondary_label.to_color32()
}
pub fn apply_to_egui(&self, ctx: &egui::Context) {
let egui_theme = match self.appearance {
MacAppearance::Aqua => egui::Theme::Light,
MacAppearance::DarkAqua => egui::Theme::Dark,
};
ctx.set_theme(egui_theme);
let mut visuals = match self.appearance {
MacAppearance::Aqua => egui::Visuals::light(),
MacAppearance::DarkAqua => egui::Visuals::dark(),
};
let label_color = self.label();
visuals.override_text_color = Some(label_color);
visuals.widgets.noninteractive.fg_stroke.color = label_color;
visuals.widgets.inactive.fg_stroke.color = label_color;
visuals.panel_fill = self.window_background();
visuals.window_fill = self.window_background();
ctx.set_visuals_of(egui_theme, visuals.clone());
ctx.set_visuals(visuals);
}
pub fn control_metrics(&self) -> &MacControlMetrics {
self.tokens.control_metrics()
}
pub fn focus_ring(&self) -> &FocusRingMetrics {
&self.control_metrics().focus_ring
}
pub fn text_field(&self) -> &TextFieldMetrics {
&self.control_metrics().text_field
}
pub fn button(&self) -> &ButtonMetrics {
&self.control_metrics().button
}
pub fn segmented(&self) -> &SegmentedMetrics {
&self.control_metrics().segmented
}
pub fn popover_metrics(&self) -> &PopoverMetrics {
self.tokens.popover_metrics()
}
pub fn toolbar_metrics(&self) -> &ToolbarMetrics {
self.tokens.toolbar_metrics()
}
pub fn slider(&self) -> &SliderMetrics {
self.tokens.slider_metrics()
}
pub fn thumbnail(&self) -> &ThumbnailCellMetrics {
self.tokens.thumbnail_metrics()
}
pub fn split_view(&self) -> &SplitViewMetrics {
self.tokens.split_view_metrics()
}
pub fn layout_metrics(&self) -> &LayoutMetrics {
self.tokens.layout_metrics()
}
pub fn scroll_view(&self) -> &ScrollViewMetrics {
self.tokens.scroll_view_metrics()
}
pub fn stack_view(&self) -> &StackViewMetrics {
self.tokens.stack_view_metrics()
}
pub fn outline_view(&self) -> &OutlineViewMetrics {
self.tokens.outline_view_metrics()
}
pub fn popup_button(&self) -> &PopUpButtonMetrics {
self.tokens.popup_button_metrics()
}
pub fn search_field(&self) -> &SearchFieldMetrics {
self.tokens.search_field_metrics()
}
pub fn stepper(&self) -> &StepperMetrics {
self.tokens.stepper_metrics()
}
pub fn color_well(&self) -> &ColorWellMetrics {
self.tokens.color_well_metrics()
}
pub fn toggle(&self) -> &ToggleMetrics {
self.tokens.toggle_metrics()
}
pub fn motion(&self) -> &MacMotionTokens {
self.tokens.motion()
}
pub fn typography(&self) -> &MacTypographyTokens {
self.tokens.typography()
}
pub fn system_green(&self) -> Color32 {
self.colors().system_green.to_color32()
}
pub fn traffic_light_close(&self) -> (Color32, Color32) {
(
self.colors().traffic_light_close.to_color32(),
self.colors().traffic_light_close_border.to_color32(),
)
}
pub fn traffic_light_minimize(&self) -> (Color32, Color32) {
(
self.colors().traffic_light_minimize.to_color32(),
self.colors().traffic_light_minimize_border.to_color32(),
)
}
pub fn traffic_light_zoom(&self) -> (Color32, Color32) {
(
self.colors().traffic_light_zoom.to_color32(),
self.colors().traffic_light_zoom_border.to_color32(),
)
}
pub fn windows_caption_close_hover_bg(&self) -> Color32 {
Color32::from_rgb(232, 17, 35)
}
pub fn windows_caption_close_pressed_bg(&self) -> Color32 {
Color32::from_rgb(196, 43, 28)
}
pub fn windows_caption_hover_bg(&self) -> Color32 {
match self.appearance {
MacAppearance::Aqua => Color32::from_black_alpha(15),
MacAppearance::DarkAqua => Color32::from_white_alpha(20),
}
}
pub fn windows_caption_pressed_bg(&self) -> Color32 {
match self.appearance {
MacAppearance::Aqua => Color32::from_black_alpha(25),
MacAppearance::DarkAqua => Color32::from_white_alpha(35),
}
}
pub fn background_style(&self, bg: Color32) -> MacBackgroundStyle {
MacBackgroundStyle::from_color(bg)
}
pub fn window_background_style(&self) -> MacBackgroundStyle {
MacBackgroundStyle::from_color(self.window_background())
}
pub fn control_background_style(&self) -> MacBackgroundStyle {
MacBackgroundStyle::from_color(self.control_background())
}
pub fn selected_control_background_style(&self) -> MacBackgroundStyle {
MacBackgroundStyle::from_color(self.selected_control())
}
}