Skip to main content

rosace_core/
platform.rs

1//! Global platform provider: `use_platform()` and `set_platform()` (D105).
2//!
3//! Widgets never branch on platform directly — the running platform exists
4//! only to drive THEME resolution (a platform-keyed `Themes` bundle picks
5//! the active `ThemeData` once at startup). This mirrors the `safe_area`
6//! provider's shape exactly: a detected default, overridable, read through a
7//! `GlobalAtom` so it's cheap and consistent everywhere.
8
9use rosace_state::GlobalAtom;
10use rosace_trace::event::AtomId;
11
12/// Reserved atom ID for the platform atom (must not collide with other
13/// reserved IDs — see `rosace_theme::provider::THEME_ATOM_ID` at 0xFFFF and
14/// `safe_area::SAFE_AREA_ATOM_ID` at 0xFFFE).
15const PLATFORM_ATOM_ID: AtomId = AtomId(0xFFFD);
16
17/// The platform ROSACE is running on.
18///
19/// Deliberately flat (no separate "Desktop" catch-all alongside `MacOs`/
20/// `Windows`/`Linux`) — the AppBar proof (Phase 23 Step 3) needs to tell
21/// macOS apart from other desktop OSes (traffic-light inset), so folding
22/// them into one variant would immediately need un-folding.
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
24pub enum Platform {
25    MacOs,
26    Windows,
27    Linux,
28    Ios,
29    Android,
30    Web,
31}
32
33impl Platform {
34    /// Detects the platform at compile time — `cfg(target_arch = "wasm32")`
35    /// for web (sufficient on its own: nothing else compiles to wasm32 in
36    /// this codebase, so no runtime `navigator.platform` query is needed),
37    /// `cfg(target_os)` for every native target.
38    pub fn detect() -> Self {
39        #[cfg(target_arch = "wasm32")]
40        return Platform::Web;
41        #[cfg(target_os = "macos")]
42        return Platform::MacOs;
43        #[cfg(target_os = "windows")]
44        return Platform::Windows;
45        #[cfg(target_os = "linux")]
46        return Platform::Linux;
47        #[cfg(target_os = "ios")]
48        return Platform::Ios;
49        #[cfg(target_os = "android")]
50        return Platform::Android;
51        // Unreachable for every target this workspace actually builds for;
52        // kept so the function is total rather than panicking on a future
53        // target this list hasn't been taught about yet.
54        #[allow(unreachable_code)]
55        Platform::Linux
56    }
57
58    pub fn is_desktop(&self) -> bool {
59        matches!(self, Platform::MacOs | Platform::Windows | Platform::Linux)
60    }
61
62    pub fn is_mobile(&self) -> bool {
63        matches!(self, Platform::Ios | Platform::Android)
64    }
65}
66
67static CURRENT_PLATFORM: GlobalAtom<Platform> = GlobalAtom::new(PLATFORM_ATOM_ID, Platform::detect);
68
69/// Returns the currently active platform — the real detected one unless
70/// overridden via [`set_platform`] (e.g. `App::platform(Platform::Ios)` to
71/// preview an iOS theme on desktop).
72pub fn use_platform() -> Platform {
73    CURRENT_PLATFORM.get()
74}
75
76/// Overrides the active platform. Called by `App::platform(..)` at startup;
77/// app code should not normally need this directly.
78pub fn set_platform(p: Platform) {
79    CURRENT_PLATFORM.set(p);
80}