Skip to main content

workflow_egui/
device.rs

1use crate::imports::*;
2
3#[derive(Default, Clone, Copy, Debug, Eq, PartialEq)]
4/// Screen orientation of the display area.
5pub enum Orientation {
6    /// Wider than tall.
7    #[default]
8    Landscape,
9    /// Taller than wide (or narrow).
10    Portrait,
11}
12
13/// Tracks device characteristics (mobile detection, orientation and screen
14/// size) used to drive responsive layout decisions.
15#[derive(Default, Clone)]
16pub struct Device {
17    /// `true` when the running platform is detected as a mobile device.
18    pub mobile_device: bool,
19    /// User/debug override forcing mobile behaviour regardless of detection.
20    pub mobile_forced: bool,
21    /// Orientation derived from the most recently observed screen size.
22    pub orientation: Orientation,
23    /// Override that pins the reported orientation when set.
24    pub orientation_forced: Option<Orientation>,
25    /// Most recently observed screen size, in points.
26    pub screen_size: Vec2,
27}
28
29impl Device {
30    /// Record the current screen size from `rect` and re-derive the orientation,
31    /// classifying narrow or sub-540px-wide areas as portrait.
32    pub fn set_screen_size(&mut self, rect: &Rect) {
33        let size = rect.size();
34
35        if size.x * 3. < size.y * 2. || size.x < 540.0 {
36            self.orientation = Orientation::Portrait;
37        } else {
38            self.orientation = Orientation::Landscape;
39        }
40
41        self.screen_size = rect.size();
42    }
43
44    /// Returns the effective orientation, preferring a forced override when set
45    /// and otherwise the orientation derived from the screen size.
46    pub fn orientation(&self) -> Orientation {
47        self.orientation_forced.unwrap_or(self.orientation)
48    }
49
50    /// Returns `true` if the device is detected as mobile or has been forced
51    /// into mobile mode.
52    pub fn is_mobile(&self) -> bool {
53        self.mobile_device || self.mobile_forced
54    }
55
56    /// Toggle a forced portrait orientation: enables it when no override is set,
57    /// otherwise clears the override and reverts to auto-detection.
58    pub fn toggle_portrait(&mut self) {
59        if self.orientation_forced.is_none() {
60            self.orientation_forced = Some(Orientation::Portrait);
61        } else {
62            self.orientation_forced = None;
63        }
64    }
65
66    /// Toggle the forced-mobile override on or off.
67    pub fn toggle_mobile(&mut self) {
68        self.mobile_forced = !self.mobile_forced;
69    }
70
71    /// Returns `true` when the UI should collapse to a single pane, such as on
72    /// mobile (real or forced), in portrait orientation, or inside a Chrome
73    /// extension popup.
74    pub fn is_single_pane(&self) -> bool {
75        workflow_core::runtime::is_chrome_extension()
76            || self.mobile_forced
77            || self.mobile_device
78            || self.orientation() == Orientation::Portrait
79    }
80
81    /// Returns `true` when the device should use a multi-pane desktop layout
82    /// (i.e. the inverse of [`Device::is_single_pane`]).
83    #[inline]
84    pub fn is_desktop(&self) -> bool {
85        !self.is_single_pane()
86    }
87
88    /// Force the reported orientation to a specific value, or pass `None` to
89    /// resume using the orientation derived from the screen size.
90    pub fn force_orientation(&mut self, orientation: Option<Orientation>) {
91        self.orientation_forced = orientation;
92    }
93}