1use egui::style::ScrollStyle;
8use serde::{Deserialize, Serialize};
9
10#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
13pub enum ScrollVisibility {
14 AlwaysVisible,
15 VisibleWhenNeeded,
16 AlwaysHidden,
17}
18
19impl ScrollVisibility {
20 pub fn to_egui(self) -> egui::scroll_area::ScrollBarVisibility {
21 use egui::scroll_area::ScrollBarVisibility as V;
22 match self {
23 ScrollVisibility::AlwaysVisible => V::AlwaysVisible,
24 ScrollVisibility::VisibleWhenNeeded => V::VisibleWhenNeeded,
25 ScrollVisibility::AlwaysHidden => V::AlwaysHidden,
26 }
27 }
28}
29
30#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
33pub struct ScrollSpec {
34 pub floating: bool,
35 pub bar_width: f32,
36 pub handle_min_length: f32,
37 pub bar_inner_margin: f32,
38 pub bar_outer_margin: f32,
39 pub floating_width: f32,
40 pub floating_allocated_width: f32,
41 pub foreground_color: bool,
42 pub dormant_background_opacity: f32,
43 pub active_background_opacity: f32,
44 pub interact_background_opacity: f32,
45 pub dormant_handle_opacity: f32,
46 pub active_handle_opacity: f32,
47 pub interact_handle_opacity: f32,
48 pub visibility: ScrollVisibility,
49}
50
51impl Default for ScrollSpec {
52 fn default() -> Self {
53 Self::windows()
54 }
55}
56
57impl ScrollSpec {
58 pub fn windows() -> Self {
60 Self {
61 floating: false,
62 bar_width: 14.0,
63 handle_min_length: 16.0,
64 bar_inner_margin: 4.0,
65 bar_outer_margin: 0.0,
66 floating_width: 2.0,
67 floating_allocated_width: 0.0,
68 foreground_color: false,
69 dormant_background_opacity: 0.6,
70 active_background_opacity: 0.7,
71 interact_background_opacity: 0.9,
72 dormant_handle_opacity: 0.7,
73 active_handle_opacity: 0.9,
74 interact_handle_opacity: 1.0,
75 visibility: ScrollVisibility::AlwaysVisible,
76 }
77 }
78
79 pub fn macos() -> Self {
81 Self {
82 floating: true,
83 bar_width: 10.0,
84 handle_min_length: 16.0,
85 bar_inner_margin: 2.0,
86 bar_outer_margin: 0.0,
87 floating_width: 8.0,
88 floating_allocated_width: 0.0,
89 foreground_color: true,
90 dormant_background_opacity: 0.0,
91 active_background_opacity: 0.0,
92 interact_background_opacity: 0.4,
93 dormant_handle_opacity: 0.0,
94 active_handle_opacity: 0.6,
95 interact_handle_opacity: 1.0,
96 visibility: ScrollVisibility::VisibleWhenNeeded,
97 }
98 }
99
100 pub fn device() -> Self {
102 Self {
103 floating: false,
104 bar_width: 16.0,
105 handle_min_length: 24.0,
106 bar_inner_margin: 4.0,
107 bar_outer_margin: 0.0,
108 floating_width: 2.0,
109 floating_allocated_width: 0.0,
110 foreground_color: false,
111 dormant_background_opacity: 1.0,
112 active_background_opacity: 1.0,
113 interact_background_opacity: 1.0,
114 dormant_handle_opacity: 1.0,
115 active_handle_opacity: 1.0,
116 interact_handle_opacity: 1.0,
117 visibility: ScrollVisibility::AlwaysVisible,
118 }
119 }
120
121 pub fn to_scroll_style(&self) -> ScrollStyle {
126 let mut s = if self.floating { ScrollStyle::floating() } else { ScrollStyle::solid() };
127 s.floating = self.floating;
128 s.bar_width = self.bar_width;
129 s.handle_min_length = self.handle_min_length;
130 s.bar_inner_margin = self.bar_inner_margin;
131 s.bar_outer_margin = self.bar_outer_margin;
132 s.floating_width = self.floating_width;
133 s.floating_allocated_width = self.floating_allocated_width;
134 s.foreground_color = self.foreground_color;
135 s.dormant_background_opacity = self.dormant_background_opacity;
136 s.active_background_opacity = self.active_background_opacity;
137 s.interact_background_opacity = self.interact_background_opacity;
138 s.dormant_handle_opacity = self.dormant_handle_opacity;
139 s.active_handle_opacity = self.active_handle_opacity;
140 s.interact_handle_opacity = self.interact_handle_opacity;
141 s
142 }
143}
144
145#[cfg(test)]
146mod tests {
147 use super::*;
148
149 #[test]
150 fn windows_is_solid_and_always_visible() {
151 let s = ScrollSpec::windows();
152 assert!(!s.floating);
153 assert_eq!(s.visibility, ScrollVisibility::AlwaysVisible);
154 assert!(s.bar_width >= 14.0);
155 }
156
157 #[test]
158 fn macos_is_floating_and_dormant_invisible() {
159 let s = ScrollSpec::macos();
160 assert!(s.floating);
161 assert_eq!(s.dormant_handle_opacity, 0.0, "macOS bar fades away when idle");
162 }
163
164 #[test]
165 fn device_is_always_opaque() {
166 let s = ScrollSpec::device();
167 assert_eq!(s.dormant_handle_opacity, 1.0);
168 assert_eq!(s.visibility, ScrollVisibility::AlwaysVisible);
169 }
170
171 #[test]
172 fn to_scroll_style_copies_all_fields() {
173 let s = ScrollSpec::macos();
174 let e = s.to_scroll_style();
175 assert_eq!(e.bar_width, s.bar_width);
176 assert_eq!(e.floating, s.floating);
177 assert_eq!(e.interact_handle_opacity, s.interact_handle_opacity);
178 }
179}