lingxia_shell/
sidebar_chrome.rs1use serde::{Deserialize, Serialize};
9
10#[cfg(target_os = "macos")]
14pub const DEFAULT_EXPANDED_SIDEBAR_WIDTH: f64 = 148.0;
15#[cfg(not(target_os = "macos"))]
16pub const DEFAULT_EXPANDED_SIDEBAR_WIDTH: f64 = 184.0;
17
18#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
19#[serde(rename_all = "camelCase")]
20pub struct SidebarChrome {
21 pub expanded: bool,
22 pub expanded_width: f64,
23}
24
25impl Default for SidebarChrome {
26 fn default() -> Self {
27 Self {
28 expanded: true,
29 expanded_width: DEFAULT_EXPANDED_SIDEBAR_WIDTH,
30 }
31 }
32}
33
34impl SidebarChrome {
35 pub fn rail(&self) -> bool {
36 !self.expanded
37 }
38
39 pub fn with_expanded(expanded: bool, expanded_width: f64) -> Self {
40 Self {
41 expanded,
42 expanded_width,
43 }
44 .normalized()
45 }
46
47 pub(crate) fn normalized(self) -> Self {
48 let expanded_width = if self.expanded_width.is_finite() && self.expanded_width > 0.0 {
49 self.expanded_width
50 } else {
51 DEFAULT_EXPANDED_SIDEBAR_WIDTH
52 };
53 Self {
54 expanded: self.expanded,
55 expanded_width,
56 }
57 }
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
61#[serde(rename_all = "camelCase")]
62pub struct WindowFrame {
63 pub x: f64,
64 pub y: f64,
65 pub width: f64,
66 pub height: f64,
67}
68
69impl WindowFrame {
70 pub fn new(x: f64, y: f64, width: f64, height: f64) -> Option<Self> {
71 let frame = Self {
72 x,
73 y,
74 width,
75 height,
76 };
77 frame.valid().then_some(frame)
78 }
79
80 pub fn valid(&self) -> bool {
81 self.x.is_finite()
82 && self.y.is_finite()
83 && self.width.is_finite()
84 && self.height.is_finite()
85 && self.width > 0.0
86 && self.height > 0.0
87 }
88}
89
90#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Default)]
91#[serde(rename_all = "camelCase")]
92pub struct ShellWindowState {
93 pub sidebar: SidebarChrome,
94 #[serde(skip_serializing_if = "Option::is_none")]
95 pub window: Option<WindowFrame>,
96}
97
98impl ShellWindowState {
99 pub(crate) fn normalized(mut self) -> Self {
100 self.sidebar = self.sidebar.normalized();
101 self.window = self.window.filter(WindowFrame::valid);
102 self
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109
110 #[test]
113 fn the_default_is_expanded() {
114 assert!(SidebarChrome::default().expanded);
115 assert_eq!(
116 SidebarChrome::default().expanded_width,
117 DEFAULT_EXPANDED_SIDEBAR_WIDTH
118 );
119 assert!(!SidebarChrome::default().rail());
120 }
121
122 #[test]
123 fn window_state_round_trips_through_json() {
124 for expanded in [false, true] {
125 let saved = ShellWindowState {
126 sidebar: SidebarChrome::with_expanded(expanded, 252.5),
127 window: WindowFrame::new(40.0, 60.0, 1200.0, 800.0),
128 };
129 let raw = serde_json::to_string(&saved).expect("serialize");
130 let loaded: ShellWindowState = serde_json::from_str(&raw).expect("deserialize");
131 assert_eq!(loaded, saved);
132 assert_eq!(loaded.sidebar.expanded, expanded);
133 }
134 }
135
136 #[test]
137 fn invalid_geometry_is_dropped_and_width_uses_the_default() {
138 let state = ShellWindowState {
139 sidebar: SidebarChrome {
140 expanded: false,
141 expanded_width: -10.0,
142 },
143 window: Some(WindowFrame {
144 x: 10.0,
145 y: 20.0,
146 width: 0.0,
147 height: 600.0,
148 }),
149 }
150 .normalized();
151
152 assert!(!state.sidebar.expanded);
153 assert_eq!(state.sidebar.expanded_width, DEFAULT_EXPANDED_SIDEBAR_WIDTH);
154 assert_eq!(state.window, None);
155 }
156}