use crate::prelude::*;
use unic_langid::CharacterDirection;
use unic_langid::LanguageIdentifier;
use web_time::Duration;
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub enum ThemeMode {
#[default]
System,
DarkMode,
LightMode,
}
use crate::{context::EventContext, events::Event};
pub struct Environment {
pub locale: Signal<LanguageIdentifier>,
pub direction: Signal<Direction>,
pub double_click_interval: Duration,
pub tooltip_delay: Duration,
pub theme_mode: ThemeMode,
pub system_theme_mode: ThemeMode,
pub(crate) caret_timer: Timer,
}
fn direction_from_locale(locale: &LanguageIdentifier) -> Direction {
match locale.character_direction() {
CharacterDirection::RTL => Direction::RightToLeft,
_ => Direction::LeftToRight,
}
}
fn apply_direction_class(cx: &mut EventContext, direction: Direction) {
let rtl = direction == Direction::RightToLeft;
let window_entities = cx.windows.keys().copied().collect::<Vec<_>>();
cx.with_current(Entity::root(), |cx| {
cx.toggle_class("rtl", rtl);
});
for window_entity in window_entities {
cx.with_current(window_entity, |cx| {
cx.toggle_class("rtl", rtl);
});
}
}
impl Environment {
pub(crate) fn new(cx: &mut Context) -> Self {
let locale: LanguageIdentifier =
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);
}
});
let direction = direction_from_locale(&locale);
Self {
locale: Signal::new(locale.clone()),
direction: Signal::new(direction),
double_click_interval: Duration::from_millis(500),
tooltip_delay: Duration::from_millis(1500),
theme_mode: ThemeMode::default(),
system_theme_mode: ThemeMode::LightMode,
caret_timer,
}
}
pub fn effective_theme(&self) -> ThemeMode {
match self.theme_mode {
ThemeMode::System => self.system_theme_mode,
other => other,
}
}
}
pub enum EnvironmentEvent {
SetLocale(LanguageIdentifier),
SetDirection(Direction),
SetThemeMode(ThemeMode),
UseSystemLocale,
ToggleThemeMode,
SetDoubleClickInterval(Duration),
SetTooltipDelay(Duration),
}
impl Model for Environment {
fn event(&mut self, cx: &mut EventContext, event: &mut Event) {
event.take(|event, _| match event {
EnvironmentEvent::SetLocale(locale) => {
self.locale.set(locale.clone());
let direction = direction_from_locale(&locale);
self.direction.set(direction);
apply_direction_class(cx, direction);
cx.reload_styles().unwrap();
}
EnvironmentEvent::SetDirection(direction) => {
self.direction.set_if_changed(direction);
apply_direction_class(cx, direction);
cx.reload_styles().unwrap();
}
EnvironmentEvent::SetThemeMode(theme) => {
self.theme_mode = theme;
let is_dark = self.effective_theme() == ThemeMode::DarkMode;
cx.with_current(Entity::root(), |cx| {
cx.toggle_class("dark", is_dark);
});
cx.reload_styles().unwrap();
}
EnvironmentEvent::UseSystemLocale => {
let locale: LanguageIdentifier =
sys_locale::get_locale().map(|l| l.parse().unwrap()).unwrap_or_default();
let direction = direction_from_locale(&locale);
self.locale.set(locale);
self.direction.set(direction);
apply_direction_class(cx, direction);
cx.reload_styles().unwrap();
}
EnvironmentEvent::ToggleThemeMode => {
let theme_mode = match self.theme_mode {
ThemeMode::System => ThemeMode::System,
ThemeMode::DarkMode => ThemeMode::LightMode,
ThemeMode::LightMode => ThemeMode::DarkMode,
};
self.theme_mode = theme_mode;
let is_dark = self.effective_theme() == ThemeMode::DarkMode;
cx.with_current(Entity::root(), |cx| {
cx.toggle_class("dark", is_dark);
});
cx.reload_styles().unwrap();
}
EnvironmentEvent::SetDoubleClickInterval(interval) => {
self.double_click_interval = interval;
}
EnvironmentEvent::SetTooltipDelay(delay) => {
self.tooltip_delay = delay;
}
});
event.map(|event, _| match event {
WindowEvent::ThemeChanged(theme) => {
self.system_theme_mode = *theme;
if self.theme_mode == ThemeMode::System {
let is_dark = self.system_theme_mode == ThemeMode::DarkMode;
cx.with_current(Entity::root(), |cx| {
cx.toggle_class("dark", is_dark);
});
cx.reload_styles().unwrap();
}
}
_ => (),
})
}
}