vizia_core/
environment.rs

1//! A model for system specific state which can be accessed by any model or view.
2use crate::prelude::*;
3
4use unic_langid::LanguageIdentifier;
5use vizia_derive::Lens;
6use web_time::Duration;
7
8/// And enum which represents the current built-in theme mode.
9#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
10pub enum ThemeMode {
11    /// The built-in vizia dark theme.
12    DarkMode,
13    /// The built-in vizia light theme.
14    #[default]
15    LightMode,
16}
17
18use crate::{context::EventContext, events::Event};
19
20/// Represents the theme used by the application.
21#[derive(Debug, Clone, PartialEq, Eq)]
22pub enum AppTheme {
23    /// System theme, if we choose this as our theme vizia
24    /// will follow system theme in supported platforms.
25    System,
26    /// Built-in vizia themes.
27    BuiltIn(ThemeMode),
28    // Custom(String),
29}
30
31/// Represents the theme used by the application.
32#[derive(Lens)]
33pub struct Theme {
34    /// The current application theme
35    pub app_theme: AppTheme,
36    /// The current system theme
37    pub sys_theme: Option<ThemeMode>,
38}
39
40impl Default for Theme {
41    fn default() -> Self {
42        Self { app_theme: AppTheme::BuiltIn(ThemeMode::LightMode), sys_theme: None }
43    }
44}
45
46impl Theme {
47    /// Returns the current theme of the application.
48    pub fn get_current_theme(&self) -> ThemeMode {
49        match self.app_theme {
50            AppTheme::System => self.sys_theme.unwrap_or_default(),
51            AppTheme::BuiltIn(theme) => theme,
52        }
53    }
54}
55
56/// A model for system specific state which can be accessed by any model or view.
57#[derive(Lens)]
58pub struct Environment {
59    /// The locale used for localization.
60    pub locale: LanguageIdentifier,
61    /// Current application and system theme.
62    pub theme: Theme,
63    /// The timer used to blink the caret of a textbox.
64    pub(crate) caret_timer: Timer,
65}
66
67impl Environment {
68    pub(crate) fn new(cx: &mut Context) -> Self {
69        let locale = sys_locale::get_locale().and_then(|l| l.parse().ok()).unwrap_or_default();
70        let caret_timer = cx.add_timer(Duration::from_millis(530), None, |cx, action| {
71            if matches!(action, TimerAction::Tick(_)) {
72                cx.emit(TextEvent::ToggleCaret);
73            }
74        });
75        Self { locale, theme: Theme::default(), caret_timer }
76    }
77}
78
79/// Events for setting the state in the [Environment].
80pub enum EnvironmentEvent {
81    /// Set the locale used for the whole application.
82    SetLocale(LanguageIdentifier),
83    /// Set the default theme mode.
84    // TODO: add SetSysTheme event when the winit `set_theme` fixed.
85    SetThemeMode(AppTheme),
86    /// Reset the locale to use the system provided locale.
87    UseSystemLocale,
88    /// Alternate between dark and light theme modes.
89    ToggleThemeMode,
90}
91
92impl Model for Environment {
93    fn event(&mut self, cx: &mut EventContext, event: &mut Event) {
94        event.take(|event, _| match event {
95            EnvironmentEvent::SetLocale(locale) => {
96                self.locale = locale;
97            }
98
99            EnvironmentEvent::SetThemeMode(theme) => {
100                theme.clone_into(&mut self.theme.app_theme);
101
102                cx.set_theme_mode(self.theme.get_current_theme());
103                cx.reload_styles().unwrap();
104            }
105
106            EnvironmentEvent::UseSystemLocale => {
107                self.locale =
108                    sys_locale::get_locale().map(|l| l.parse().unwrap()).unwrap_or_default();
109            }
110
111            EnvironmentEvent::ToggleThemeMode => {
112                let theme_mode = match self.theme.get_current_theme() {
113                    ThemeMode::DarkMode => ThemeMode::LightMode,
114                    ThemeMode::LightMode => ThemeMode::DarkMode,
115                };
116
117                self.theme.app_theme = AppTheme::BuiltIn(theme_mode);
118
119                cx.set_theme_mode(theme_mode);
120                cx.reload_styles().unwrap();
121            }
122        });
123
124        event.map(|event, _| match event {
125            WindowEvent::ThemeChanged(theme) => {
126                self.theme.sys_theme = Some(*theme);
127                if self.theme.app_theme == AppTheme::System {
128                    cx.set_theme_mode(*theme);
129                    cx.reload_styles().unwrap();
130                }
131            }
132            _ => (),
133        })
134    }
135}