vizia_core/
environment.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//! A model for system specific state which can be accessed by any model or view.
use crate::prelude::*;

use unic_langid::LanguageIdentifier;
use vizia_derive::Lens;
use web_time::Duration;

#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub enum ThemeMode {
    DarkMode,
    #[default]
    LightMode,
}

use crate::{context::EventContext, events::Event};

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AppTheme {
    /// System theme, if we choose this as our theme vizia
    /// will follow system theme in supported platforms.
    System,
    /// builtin vizia themes
    BuiltIn(ThemeMode),
    // Custom(String),
}

#[derive(Lens)]
pub struct Theme {
    /// The current application theme
    pub app_theme: AppTheme,
    /// The current system theme
    pub sys_theme: Option<ThemeMode>,
}

impl Default for Theme {
    fn default() -> Self {
        Self { app_theme: AppTheme::BuiltIn(ThemeMode::LightMode), sys_theme: None }
    }
}

impl Theme {
    pub fn get_current_theme(&self) -> ThemeMode {
        match self.app_theme {
            AppTheme::System => self.sys_theme.unwrap_or_default(),
            AppTheme::BuiltIn(theme) => theme,
        }
    }
}

/// A model for system specific state which can be accessed by any model or view.
#[derive(Lens)]
pub struct Environment {
    /// The locale used for localization.
    pub locale: LanguageIdentifier,
    /// Current application and system theme.
    pub theme: Theme,
    /// The timer used to blink the caret of a textbox.
    pub(crate) caret_timer: Timer,
}

impl Environment {
    pub fn new(cx: &mut Context) -> Self {
        let locale = sys_locale::get_locale().and_then(|l| l.parse().ok()).unwrap_or_default();
        let caret_timer = cx.add_timer(Duration::from_millis(530), None, |cx, action| {
            if matches!(action, TimerAction::Tick(_)) {
                cx.emit(TextEvent::ToggleCaret);
            }
        });
        Self { locale, theme: Theme::default(), caret_timer }
    }
}

/// Events for setting the state in the [Environment].
pub enum EnvironmentEvent {
    /// Set the locale used for the whole application.
    SetLocale(LanguageIdentifier),
    /// Set the default theme mode.
    // TODO: add SetSysTheme event when the winit `set_theme` fixed.
    SetThemeMode(AppTheme),
    /// Reset the locale to use the system provided locale.
    UseSystemLocale,
    /// Alternate between dark and light theme modes.
    ToggleThemeMode,
}

impl Model for Environment {
    fn event(&mut self, cx: &mut EventContext, event: &mut Event) {
        event.take(|event, _| match event {
            EnvironmentEvent::SetLocale(locale) => {
                self.locale = locale;
            }

            EnvironmentEvent::SetThemeMode(theme) => {
                theme.clone_into(&mut self.theme.app_theme);

                cx.set_theme_mode(self.theme.get_current_theme());
                cx.reload_styles().unwrap();
            }

            EnvironmentEvent::UseSystemLocale => {
                self.locale =
                    sys_locale::get_locale().map(|l| l.parse().unwrap()).unwrap_or_default();
            }

            EnvironmentEvent::ToggleThemeMode => {
                let theme_mode = match self.theme.get_current_theme() {
                    ThemeMode::DarkMode => ThemeMode::LightMode,
                    ThemeMode::LightMode => ThemeMode::DarkMode,
                };

                self.theme.app_theme = AppTheme::BuiltIn(theme_mode);

                cx.set_theme_mode(theme_mode);
                cx.reload_styles().unwrap();
            }
        });

        event.map(|event, _| match event {
            WindowEvent::ThemeChanged(theme) => {
                self.theme.sys_theme = Some(*theme);
                if self.theme.app_theme == AppTheme::System {
                    cx.set_theme_mode(*theme);
                    cx.reload_styles().unwrap();
                }
            }
            _ => (),
        })
    }
}