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