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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
use std::{
borrow::Cow,
rc::Rc,
};
use bytes::Bytes;
pub use mundy::{
AccentColor,
Srgba,
};
use torin::prelude::Size2D;
use crate::{
accessibility::id::AccessibilityId,
current_context::CurrentContext,
prelude::{
State,
consume_root_context,
try_consume_root_context,
},
user_event::UserEvent,
};
/// How the user is navigating the application.
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug, Hash)]
pub enum NavigationMode {
/// Navigation is driven by a pointing device or touch input.
#[default]
NotKeyboard,
/// Navigation is driven by keyboard input.
Keyboard,
}
/// The color theme preferred by the operating system.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub enum PreferredTheme {
/// The operating system prefers a light color theme.
#[default]
Light,
/// The operating system prefers a dark color theme.
Dark,
}
/// Operating system targeted by an application.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TargetPlatform {
/// Microsoft Windows.
Windows,
/// Apple macOS.
MacOs,
/// Linux.
Linux,
/// Android.
Android,
/// Apple iOS.
Ios,
/// An operating system Freya does not recognize.
Unknown,
}
impl TargetPlatform {
/// Returns the target platform from the current Freya context, or detects the compile target.
pub fn get() -> Self {
CurrentContext::try_with(|_| try_consume_root_context())
.flatten()
.unwrap_or_else(Self::detect)
}
/// Detects the operating system of the current compile target.
pub fn detect() -> Self {
if cfg!(target_os = "windows") {
Self::Windows
} else if cfg!(target_os = "macos") {
Self::MacOs
} else if cfg!(target_os = "linux") {
Self::Linux
} else if cfg!(target_os = "android") {
Self::Android
} else if cfg!(target_os = "ios") {
Self::Ios
} else {
Self::Unknown
}
}
/// Returns whether this is a desktop operating system.
pub fn is_desktop(&self) -> bool {
matches!(self, Self::Windows | Self::MacOs | Self::Linux)
}
/// Returns whether this is a mobile operating system.
pub fn is_mobile(&self) -> bool {
matches!(self, Self::Android | Self::Ios)
}
}
/// Reactive state and APIs provided by Freya for the current window.
///
/// Retrieve it from a component with [`Platform::get`]. Each state field is a [`State`]. Calling
/// [`State::read`] subscribes the component to changes, so use it when the UI should react to a
/// platform update.
///
/// # Example
///
/// ```rust,no_run
/// use freya_core::prelude::*;
///
/// fn app() -> impl IntoElement {
/// let platform = Platform::get();
/// let root_size = *platform.root_size.read();
/// let preferred_theme = *platform.preferred_theme.read();
///
/// format!(
/// "Window: {:.0} × {:.0}, preferred theme: {preferred_theme:?}",
/// root_size.width, root_size.height,
/// )
/// }
/// ```
#[derive(Clone)]
pub struct Platform {
/// The [`AccessibilityId`] of the node currently focused for accessibility.
pub focused_accessibility_id: State<AccessibilityId>,
/// Accessibility data for the node currently focused for accessibility.
pub focused_accessibility_node: State<accesskit::Node>,
/// The root window's logical size.
pub root_size: State<Size2D>,
/// The effective rendering scale factor, including the operating system and custom factors.
pub scale_factor: State<f64>,
/// The custom scale factor. Use [`Platform::set_custom_scale_factor`] to change it.
pub custom_scale_factor: State<f64>,
/// The current input [`NavigationMode`].
pub navigation_mode: State<NavigationMode>,
/// The color theme preferred by the operating system.
pub preferred_theme: State<PreferredTheme>,
/// Whether the application currently has operating-system focus.
pub is_app_focused: State<bool>,
/// The accent color supplied by the operating system, when available.
pub accent_color: State<AccentColor>,
/// Dispatches [`UserEvent`] values to the active renderer.
pub sender: Rc<dyn Fn(UserEvent)>,
}
impl Platform {
/// Returns the [`Platform`] for the current window.
///
/// This must be called while a Freya component is rendering or handling an event.
#[track_caller]
pub fn get() -> Self {
consume_root_context()
}
/// Dispatches a [`UserEvent`] to the active renderer.
pub fn send(&self, event: UserEvent) {
(self.sender)(event)
}
/// Requests a custom rendering scale factor.
///
/// The effective scale factor is this value multiplied by the operating system scale factor.
/// Freya clamps the requested value to its supported range.
pub fn set_custom_scale_factor(&self, custom_scale_factor: f64) {
self.send(UserEvent::SetCustomScaleFactor(custom_scale_factor));
}
/// Loads a font at runtime under the given family name in all windows.
///
/// # Example
///
/// ```rust,no_run
/// use freya_core::prelude::*;
///
/// fn load_rubik() {
/// let font = std::fs::read("./Rubik.ttf").expect("Failed to read the font file.");
/// Platform::get().load_font("Rubik", font);
/// }
/// ```
pub fn load_font(&self, font_name: impl Into<Cow<'static, str>>, font_data: impl Into<Bytes>) {
self.send(UserEvent::LoadFont {
font_name: font_name.into(),
font_data: font_data.into(),
});
}
}