ferroui_base/platform/
default_platform_settings.rs1use super::{IPlatformSettings, PlatformColorValues, PlatformThemeVariant};
2use crate::input::platform::PlatformHotkeyConfiguration;
3use crate::input::PointerType;
4use crate::reactive::{Disposable, IDisposable};
5use crate::utilities::HandlerList;
6use crate::{FerroLocator, LocatorExtensions, Size};
7use std::rc::Rc;
8use std::time::Duration;
9
10const TOUCH_TAP_SIZE: f64 = 10.0;
11const TOUCH_DOUBLE_TAP_SIZE: f64 = 50.0;
13
14#[derive(Default)]
21pub struct DefaultPlatformSettings {
22 color_values_changed: Rc<HandlerList<dyn Fn(&PlatformColorValues)>>,
23 preferred_application_language_changed: Rc<HandlerList<dyn Fn()>>,
24}
25
26impl DefaultPlatformSettings {
27 pub fn new() -> Self {
29 Self::default()
30 }
31
32 pub fn on_color_values_changed(&self, color_values: PlatformColorValues) {
37 if self.color_values_changed.is_empty() {
38 return;
39 }
40 for (_, handler) in self.color_values_changed.snapshot().iter() {
41 handler(&color_values);
42 }
43 }
44
45 pub fn on_preferred_application_language_changed(&self) {
50 if self.preferred_application_language_changed.is_empty() {
51 return;
52 }
53 for (_, handler) in self.preferred_application_language_changed.snapshot().iter() {
54 handler();
55 }
56 }
57
58 fn language_tag_from_locale(locale: &str) -> String {
61 let name = locale.split(['.', '@']).next().unwrap_or("");
62 if name.is_empty() || name == "C" || name == "POSIX" {
63 return String::new();
64 }
65 name.replace('_', "-")
66 }
67}
68
69impl IPlatformSettings for DefaultPlatformSettings {
70 fn get_tap_size(&self, type_: PointerType) -> Size {
71 match type_ {
72 PointerType::Touch | PointerType::Pen => Size::new(TOUCH_TAP_SIZE, TOUCH_TAP_SIZE),
73 PointerType::Mouse => Size::new(4.0, 4.0),
74 }
75 }
76
77 fn get_double_tap_size(&self, type_: PointerType) -> Size {
78 match type_ {
79 PointerType::Touch | PointerType::Pen => Size::new(TOUCH_DOUBLE_TAP_SIZE, TOUCH_DOUBLE_TAP_SIZE),
80 PointerType::Mouse => Size::new(4.0, 4.0),
81 }
82 }
83
84 fn get_double_tap_time(&self, _type: PointerType) -> Duration {
85 Duration::from_millis(500)
86 }
87
88 fn hold_wait_duration(&self) -> Duration {
89 Duration::from_millis(300)
90 }
91
92 fn hotkey_configuration(&self) -> Rc<PlatformHotkeyConfiguration> {
96 FerroLocator::current().get_required_service::<PlatformHotkeyConfiguration>()
97 }
98
99 fn preferred_application_language(&self) -> String {
103 ["LC_ALL", "LC_MESSAGES", "LANG"]
104 .iter()
105 .filter_map(|name| std::env::var(name).ok())
106 .find(|value| !value.is_empty())
107 .map(|value| Self::language_tag_from_locale(&value))
108 .unwrap_or_default()
109 }
110
111 fn get_color_values(&self) -> PlatformColorValues {
112 PlatformColorValues::new().with_theme_variant(PlatformThemeVariant::Light)
113 }
114
115 fn color_values_changed(&self, handler: Rc<dyn Fn(&PlatformColorValues)>) -> Rc<dyn IDisposable> {
116 let token = self.color_values_changed.add(handler);
117 let handlers = self.color_values_changed.clone();
118 Disposable::create(move || {
119 handlers.remove(token);
120 })
121 }
122
123 fn preferred_application_language_changed(&self, handler: Rc<dyn Fn()>) -> Rc<dyn IDisposable> {
124 let token = self.preferred_application_language_changed.add(handler);
125 let handlers = self.preferred_application_language_changed.clone();
126 Disposable::create(move || {
127 handlers.remove(token);
128 })
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135 use std::cell::RefCell;
136
137 #[test]
138 fn default_color_values_are_light() {
139 let settings = DefaultPlatformSettings::new();
140 assert_eq!(settings.get_color_values(), PlatformColorValues::new());
141 assert_eq!(settings.get_color_values().theme_variant(), PlatformThemeVariant::Light);
142 }
143
144 #[test]
145 fn change_events_reach_subscribers_until_disposed() {
146 let settings = DefaultPlatformSettings::new();
147 let log = Rc::new(RefCell::new(Vec::new()));
148
149 let colors = {
150 let log = log.clone();
151 settings.color_values_changed(Rc::new(move |values: &PlatformColorValues| {
152 log.borrow_mut().push(format!("{:?}", values.theme_variant()))
153 }))
154 };
155 let language = {
156 let log = log.clone();
157 settings.preferred_application_language_changed(Rc::new(move || log.borrow_mut().push("language".to_string())))
158 };
159
160 settings.on_color_values_changed(PlatformColorValues::new().with_theme_variant(PlatformThemeVariant::Dark));
161 settings.on_preferred_application_language_changed();
162 assert_eq!(*log.borrow(), ["Dark", "language"]);
163
164 colors.dispose();
165 language.dispose();
166 settings.on_color_values_changed(PlatformColorValues::new());
167 settings.on_preferred_application_language_changed();
168 assert_eq!(log.borrow().len(), 2);
169 }
170
171 #[test]
172 fn language_tag_is_derived_from_the_locale_name() {
173 assert_eq!(DefaultPlatformSettings::language_tag_from_locale("en_US.UTF-8"), "en-US");
174 assert_eq!(DefaultPlatformSettings::language_tag_from_locale("sr_RS@latin"), "sr-RS");
175 assert_eq!(DefaultPlatformSettings::language_tag_from_locale("pl"), "pl");
176 assert_eq!(DefaultPlatformSettings::language_tag_from_locale("C"), "");
177 assert_eq!(DefaultPlatformSettings::language_tag_from_locale("POSIX"), "");
178 assert_eq!(DefaultPlatformSettings::language_tag_from_locale(""), "");
179 }
180}