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}