1pub const PRIORITY_DEFAULT: u8 = 0;
8pub const PRIORITY_WIDGET: u8 = 100;
9pub const PRIORITY_DRAG: u8 = 150;
10pub const PRIORITY_MODAL: u8 = 200;
11pub const PRIORITY_SYSTEM: u8 = 255;
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
15pub enum CursorIcon {
16 #[default]
18 Default,
19 None,
21
22 ContextMenu,
25 Help,
27 PointingHand,
29
30 Progress,
33 Wait,
35
36 Cell,
39 Crosshair,
41 Text,
43 VerticalText,
45
46 Alias,
49 Copy,
51 Move,
53 NoDrop,
55 NotAllowed,
57 Grab,
59 Grabbing,
61
62 AllScroll,
65 ResizeHorizontal,
67 ResizeVertical,
69 ResizeNeSw,
71 ResizeNwSe,
73 ResizeEast,
75 ResizeWest,
77 ResizeNorth,
79 ResizeSouth,
81 ResizeNorthEast,
83 ResizeNorthWest,
85 ResizeSouthEast,
87 ResizeSouthWest,
89 ResizeColumn,
91 ResizeRow,
93
94 ZoomIn,
97 ZoomOut,
99}
100
101impl CursorIcon {
102 pub fn is_resize(&self) -> bool {
104 matches!(
105 self,
106 CursorIcon::AllScroll
107 | CursorIcon::ResizeHorizontal
108 | CursorIcon::ResizeVertical
109 | CursorIcon::ResizeNeSw
110 | CursorIcon::ResizeNwSe
111 | CursorIcon::ResizeEast
112 | CursorIcon::ResizeWest
113 | CursorIcon::ResizeNorth
114 | CursorIcon::ResizeSouth
115 | CursorIcon::ResizeNorthEast
116 | CursorIcon::ResizeNorthWest
117 | CursorIcon::ResizeSouthEast
118 | CursorIcon::ResizeSouthWest
119 | CursorIcon::ResizeColumn
120 | CursorIcon::ResizeRow
121 )
122 }
123
124 pub fn is_drag(&self) -> bool {
126 matches!(self, CursorIcon::Grab | CursorIcon::Grabbing | CursorIcon::Move)
127 }
128
129 pub fn css_name(&self) -> &'static str {
131 match self {
132 CursorIcon::Default => "default",
133 CursorIcon::None => "none",
134 CursorIcon::ContextMenu => "context-menu",
135 CursorIcon::Help => "help",
136 CursorIcon::PointingHand => "pointer",
137 CursorIcon::Progress => "progress",
138 CursorIcon::Wait => "wait",
139 CursorIcon::Cell => "cell",
140 CursorIcon::Crosshair => "crosshair",
141 CursorIcon::Text => "text",
142 CursorIcon::VerticalText => "vertical-text",
143 CursorIcon::Alias => "alias",
144 CursorIcon::Copy => "copy",
145 CursorIcon::Move => "move",
146 CursorIcon::NoDrop => "no-drop",
147 CursorIcon::NotAllowed => "not-allowed",
148 CursorIcon::Grab => "grab",
149 CursorIcon::Grabbing => "grabbing",
150 CursorIcon::AllScroll => "all-scroll",
151 CursorIcon::ResizeHorizontal => "ew-resize",
152 CursorIcon::ResizeVertical => "ns-resize",
153 CursorIcon::ResizeNeSw => "nesw-resize",
154 CursorIcon::ResizeNwSe => "nwse-resize",
155 CursorIcon::ResizeEast => "e-resize",
156 CursorIcon::ResizeWest => "w-resize",
157 CursorIcon::ResizeNorth => "n-resize",
158 CursorIcon::ResizeSouth => "s-resize",
159 CursorIcon::ResizeNorthEast => "ne-resize",
160 CursorIcon::ResizeNorthWest => "nw-resize",
161 CursorIcon::ResizeSouthEast => "se-resize",
162 CursorIcon::ResizeSouthWest => "sw-resize",
163 CursorIcon::ResizeColumn => "col-resize",
164 CursorIcon::ResizeRow => "row-resize",
165 CursorIcon::ZoomIn => "zoom-in",
166 CursorIcon::ZoomOut => "zoom-out",
167 }
168 }
169}
170
171#[derive(Clone, Debug)]
176pub struct CursorState {
177 requested: CursorIcon,
179 priority: u8,
181}
182
183impl Default for CursorState {
184 fn default() -> Self {
185 Self::new()
186 }
187}
188
189impl CursorState {
190 pub fn new() -> Self {
192 Self {
193 requested: CursorIcon::Default,
194 priority: PRIORITY_DEFAULT,
195 }
196 }
197
198 pub fn set(&mut self, icon: CursorIcon) {
202 self.set_with_priority(icon, PRIORITY_WIDGET);
203 }
204
205 pub fn set_with_priority(&mut self, icon: CursorIcon, priority: u8) {
211 if priority >= self.priority {
212 self.requested = icon;
213 self.priority = priority;
214 }
215 }
216
217 pub fn get(&self) -> CursorIcon {
219 self.requested
220 }
221
222 pub fn reset(&mut self) {
226 self.requested = CursorIcon::Default;
227 self.priority = PRIORITY_DEFAULT;
228 }
229
230 pub fn is_default(&self) -> bool {
232 self.requested == CursorIcon::Default
233 }
234}
235
236#[cfg(test)]
237mod tests {
238 use super::*;
239
240 #[test]
241 fn test_cursor_state_new() {
242 let state = CursorState::new();
243 assert_eq!(state.get(), CursorIcon::Default);
244 assert!(state.is_default());
245 }
246
247 #[test]
248 fn test_cursor_state_set() {
249 let mut state = CursorState::new();
250 state.set(CursorIcon::PointingHand);
251 assert_eq!(state.get(), CursorIcon::PointingHand);
252 assert!(!state.is_default());
253 }
254
255 #[test]
256 fn test_cursor_state_reset() {
257 let mut state = CursorState::new();
258 state.set(CursorIcon::Grab);
259 assert!(!state.is_default());
260
261 state.reset();
262 assert_eq!(state.get(), CursorIcon::Default);
263 assert!(state.is_default());
264 }
265
266 #[test]
267 fn test_cursor_priority_override() {
268 let mut state = CursorState::new();
269
270 state.set_with_priority(CursorIcon::PointingHand, PRIORITY_WIDGET);
272 assert_eq!(state.get(), CursorIcon::PointingHand);
273
274 state.set_with_priority(CursorIcon::Grab, PRIORITY_DRAG);
276 assert_eq!(state.get(), CursorIcon::Grab);
277
278 state.set_with_priority(CursorIcon::Text, PRIORITY_WIDGET);
280 assert_eq!(state.get(), CursorIcon::Grab);
281
282 state.set_with_priority(CursorIcon::Grabbing, PRIORITY_DRAG);
284 assert_eq!(state.get(), CursorIcon::Grabbing);
285
286 state.set_with_priority(CursorIcon::Wait, PRIORITY_SYSTEM);
288 assert_eq!(state.get(), CursorIcon::Wait);
289 }
290
291 #[test]
292 fn test_cursor_icon_is_resize() {
293 assert!(CursorIcon::ResizeHorizontal.is_resize());
294 assert!(CursorIcon::ResizeVertical.is_resize());
295 assert!(CursorIcon::ResizeNeSw.is_resize());
296 assert!(CursorIcon::ResizeNwSe.is_resize());
297 assert!(CursorIcon::ResizeEast.is_resize());
298 assert!(CursorIcon::ResizeWest.is_resize());
299 assert!(CursorIcon::ResizeNorth.is_resize());
300 assert!(CursorIcon::ResizeSouth.is_resize());
301 assert!(CursorIcon::ResizeNorthEast.is_resize());
302 assert!(CursorIcon::ResizeNorthWest.is_resize());
303 assert!(CursorIcon::ResizeSouthEast.is_resize());
304 assert!(CursorIcon::ResizeSouthWest.is_resize());
305 assert!(CursorIcon::ResizeColumn.is_resize());
306 assert!(CursorIcon::ResizeRow.is_resize());
307 assert!(CursorIcon::AllScroll.is_resize());
308
309 assert!(!CursorIcon::Default.is_resize());
310 assert!(!CursorIcon::PointingHand.is_resize());
311 assert!(!CursorIcon::Grab.is_resize());
312 assert!(!CursorIcon::Text.is_resize());
313 }
314
315 #[test]
316 fn test_cursor_icon_is_drag() {
317 assert!(CursorIcon::Grab.is_drag());
318 assert!(CursorIcon::Grabbing.is_drag());
319 assert!(CursorIcon::Move.is_drag());
320
321 assert!(!CursorIcon::Default.is_drag());
322 assert!(!CursorIcon::PointingHand.is_drag());
323 assert!(!CursorIcon::ResizeHorizontal.is_drag());
324 assert!(!CursorIcon::Copy.is_drag());
325 }
326
327 #[test]
328 fn test_cursor_icon_css_name() {
329 assert_eq!(CursorIcon::Default.css_name(), "default");
330 assert_eq!(CursorIcon::None.css_name(), "none");
331 assert_eq!(CursorIcon::PointingHand.css_name(), "pointer");
332 assert_eq!(CursorIcon::Grab.css_name(), "grab");
333 assert_eq!(CursorIcon::Grabbing.css_name(), "grabbing");
334 assert_eq!(CursorIcon::Move.css_name(), "move");
335 assert_eq!(CursorIcon::Text.css_name(), "text");
336 assert_eq!(CursorIcon::ResizeHorizontal.css_name(), "ew-resize");
337 assert_eq!(CursorIcon::ResizeVertical.css_name(), "ns-resize");
338 assert_eq!(CursorIcon::ResizeNeSw.css_name(), "nesw-resize");
339 assert_eq!(CursorIcon::ResizeNwSe.css_name(), "nwse-resize");
340 assert_eq!(CursorIcon::Wait.css_name(), "wait");
341 assert_eq!(CursorIcon::Progress.css_name(), "progress");
342 assert_eq!(CursorIcon::NotAllowed.css_name(), "not-allowed");
343 assert_eq!(CursorIcon::ZoomIn.css_name(), "zoom-in");
344 assert_eq!(CursorIcon::ZoomOut.css_name(), "zoom-out");
345 }
346
347 #[test]
348 fn test_default_priority_constant() {
349 let mut state = CursorState::new();
350 state.set(CursorIcon::PointingHand);
351 assert_eq!(state.priority, PRIORITY_WIDGET);
352 }
353
354 #[test]
355 fn test_cursor_state_default_trait() {
356 let state = CursorState::default();
357 assert_eq!(state.get(), CursorIcon::Default);
358 assert!(state.is_default());
359 }
360
361 #[test]
362 fn test_cursor_icon_default_trait() {
363 let icon = CursorIcon::default();
364 assert_eq!(icon, CursorIcon::Default);
365 }
366}