inset_services/mouse_cursor.rs
1//! Flutter counterpart: `services/mouse_cursor.dart`.
2
3use std::any::Any;
4use std::collections::HashMap;
5use std::fmt;
6use std::rc::Rc;
7
8use inset_embedder::{Platform, SystemMouseCursorKind, ViewId};
9use inset_gestures::PointerEvent;
10
11/// A shared reference to an immutable [`MouseCursor`] (a Dart object reference).
12pub type MouseCursorRef = Rc<dyn MouseCursor>;
13
14/// Maintains the state of mouse cursors and manages how cursors are searched
15/// for.
16///
17/// This is typically created as a global singleton and owned by `MouseTracker`.
18pub struct MouseCursorManager {
19 /// The mouse cursor to use if all cursor candidates choose to defer.
20 ///
21 /// See also:
22 ///
23 /// * `MouseCursor.defer`, the mouse cursor object to use to defer.
24 pub fallback_mouse_cursor: MouseCursorRef,
25 last_session: HashMap<i64, Box<dyn MouseCursorSession>>,
26}
27
28impl MouseCursorManager {
29 /// Create a [`MouseCursorManager`] by specifying the fallback cursor.
30 ///
31 /// The `fallback_mouse_cursor` must not be `MouseCursor.defer`.
32 pub fn new(fallback_mouse_cursor: MouseCursorRef) -> MouseCursorManager {
33 debug_assert!(!fallback_mouse_cursor.is_defer());
34 MouseCursorManager {
35 fallback_mouse_cursor,
36 last_session: HashMap::new(),
37 }
38 }
39
40 /// Returns the active mouse cursor of a device.
41 ///
42 /// The return value is the last [`MouseCursor`] activated onto this device,
43 /// even if the activation failed.
44 ///
45 /// This function is only active in debug mode: in release it returns `None`.
46 pub fn debug_device_active_cursor(&self, device: i64) -> Option<MouseCursorRef> {
47 if !cfg!(debug_assertions) {
48 return None;
49 }
50 self.last_session
51 .get(&device)
52 .map(|session| session.cursor())
53 }
54
55 /// Handles the changes that cause a pointer device to have a new list of
56 /// mouse cursor candidates.
57 ///
58 /// This change can be caused by a pointer event, in which case
59 /// `triggering_event` should not be `None`, or by other changes, such as when a
60 /// widget has moved under a still mouse, which is detected after the current
61 /// frame is complete. In either case, `cursor_candidates` should be the list
62 /// of cursors at the location of the mouse in hit-test order.
63 ///
64 /// `view` is the one the device is on, from its latest event: the cursor is that view's
65 /// host's to show, and the session asks it.
66 pub fn handle_device_cursor_update(
67 &mut self,
68 platform: &dyn Platform,
69 view: ViewId,
70 device: i64,
71 triggering_event: Option<&PointerEvent>,
72 cursor_candidates: impl IntoIterator<Item = MouseCursorRef>,
73 ) {
74 if let Some(PointerEvent::Removed(_)) = triggering_event {
75 self.last_session.remove(&device);
76 return;
77 }
78 let next_cursor = first_non_deferred(cursor_candidates)
79 .unwrap_or_else(|| Rc::clone(&self.fallback_mouse_cursor));
80 debug_assert!(!next_cursor.is_defer());
81 if let Some(last_session) = self.last_session.get(&device)
82 && *last_session.cursor() == *next_cursor
83 {
84 return;
85 }
86 let mut next_session = next_cursor.create_session(device);
87 if let Some(mut last_session) = self.last_session.remove(&device) {
88 last_session.dispose();
89 }
90 next_session.activate(platform, view);
91 self.last_session.insert(device, next_session);
92 }
93}
94
95/// Manages the duration that a pointing device should display a specific mouse
96/// cursor.
97///
98/// While [`MouseCursor`] classes describe the kind of cursors, [`MouseCursorSession`]
99/// classes represent a continuous use of the cursor on a pointing device. The
100/// [`MouseCursorSession`] classes can be stateful. For example, a cursor that
101/// needs to load resources might want to try a fallback cursor first, and switch
102/// to the target cursor when the resources are loaded.
103///
104/// A [`MouseCursorSession`] has the following lifecycle:
105///
106/// * When a pointing device should start displaying a cursor, `MouseTracker`
107/// creates a session by calling [`MouseCursor::create_session`] on the target
108/// cursor, and stores it in a table associated with the device.
109/// * `MouseTracker` then immediately calls the session's [`activate`], where
110/// the session should fetch resources and make system calls.
111/// * When the pointing device should start displaying another cursor,
112/// `MouseTracker` calls [`dispose`] on this session. After [`dispose`], this
113/// session will no longer be used in the future.
114///
115/// [`activate`]: MouseCursorSession::activate
116/// [`dispose`]: MouseCursorSession::dispose
117pub trait MouseCursorSession {
118 /// The cursor that created this session.
119 fn cursor(&self) -> MouseCursorRef;
120
121 /// The device ID of the pointing device.
122 fn device(&self) -> i64;
123
124 /// Override this method to do the work of changing the cursor of the device.
125 ///
126 /// Called right after this session is created.
127 ///
128 /// This method has full control over the cursor until the [`dispose`] call,
129 /// and can make system calls to change the pointer cursor as many times as
130 /// necessary (usually through the system cursor request of the host of `view`,
131 /// the one the device is on, which `platform` finds; a session that changes the
132 /// cursor later keeps it).
133 ///
134 /// [`dispose`]: MouseCursorSession::dispose
135 fn activate(&mut self, platform: &dyn Platform, view: ViewId);
136
137 /// Called when device stops displaying the cursor.
138 ///
139 /// After this call, this session instance will no longer be used in the
140 /// future.
141 ///
142 /// When implementing this method in subclasses, you should release resources
143 /// and prevent [`activate`] from causing side effects after disposal.
144 ///
145 /// [`activate`]: MouseCursorSession::activate
146 fn dispose(&mut self);
147}
148
149/// An interface for mouse cursor definitions.
150///
151/// A mouse cursor is a graphical image on the screen that echoes the movement
152/// of a pointing device, such as a mouse or a stylus. A [`MouseCursor`] object
153/// defines a kind of mouse cursor, such as an arrow, a pointing hand, or an
154/// I-beam.
155///
156/// During the painting phase, [`MouseCursor`] objects are assigned to regions on
157/// the screen via annotations. Later during a device update (e.g. when a mouse
158/// moves), `MouseTracker` finds the _active cursor_, the top-most region
159/// associated with the position of each mouse device, and activates the cursor
160/// as necessary.
161///
162/// ## Cursor classes
163///
164/// A [`SystemMouseCursor`] is a cursor that is natively supported by the
165/// platform that the program is running on. All supported system mouse cursors
166/// are enumerated in [`SystemMouseCursors`].
167///
168/// ## Using cursors
169///
170/// A [`MouseCursor`] object is used by being assigned to a `MouseRegion` or
171/// another widget that exposes the `MouseRegion` API, such as
172/// `InkResponse.mouseCursor`.
173///
174/// ## Related classes
175///
176/// [`MouseCursorSession`] represents the duration when a pointing device
177/// displays a cursor, and defines the states and behaviors of the cursor.
178///
179/// [`MouseCursorManager`] is a mixin that manages the states of mouse cursors,
180/// such as `MouseTracker`.
181///
182/// Values are shared as [`MouseCursorRef`]. Dart's `MouseCursor.defer` and
183/// `MouseCursor.uncontrolled` are `<dyn MouseCursor>::defer()` and
184/// `<dyn MouseCursor>::uncontrolled()`.
185pub trait MouseCursor: fmt::Debug {
186 /// Associate a pointing device to this cursor.
187 ///
188 /// A mouse cursor class usually has a corresponding [`MouseCursorSession`]
189 /// class, and instantiates such class in this method.
190 ///
191 /// This method is called each time a pointing device starts displaying this
192 /// cursor. A given cursor can be displayed by multiple devices at the same
193 /// time, in which case this method will be called separately for each device.
194 fn create_session(&self, device: i64) -> Box<dyn MouseCursorSession>;
195
196 /// A very short description of the mouse cursor.
197 ///
198 /// The `debug_description` should be a few words that can describe this cursor
199 /// to make debug information more readable. It is returned as the `Debug`
200 /// output when the diagnostic level is at or above info.
201 fn debug_description(&self) -> String;
202
203 /// Downcast support for Dart's `==` between cursor classes.
204 fn as_any(&self) -> &dyn Any;
205
206 /// Dart's `==`. Identity unless a class overrides it, as [`SystemMouseCursor`] does.
207 fn eq_cursor(&self, other: &dyn MouseCursor) -> bool {
208 std::ptr::addr_eq(self as *const Self, other as *const dyn MouseCursor)
209 }
210
211 /// Whether this is `MouseCursor.defer`.
212 fn is_defer(&self) -> bool {
213 self.as_any().is::<DeferringMouseCursor>()
214 }
215}
216
217impl dyn MouseCursor {
218 /// A special class that indicates that the region with this cursor defers the
219 /// choice of cursor to the next region behind it.
220 ///
221 /// When an event occurs, `MouseTracker` will update each pointer's cursor by
222 /// finding the list of regions that contain the pointer's location, from front
223 /// to back in hit-test order. The pointer's cursor will be the first cursor
224 /// that is not deferred.
225 pub fn defer() -> MouseCursorRef {
226 Rc::new(DeferringMouseCursor)
227 }
228
229 /// A special value that doesn't change cursor by itself, but make a region
230 /// that blocks other regions behind it from changing the cursor.
231 ///
232 /// When a pointer enters a region with a cursor of `uncontrolled`, the pointer
233 /// retains its previous cursor and keeps so until it moves out of the region.
234 /// Technically, this region absorb the mouse cursor hit test without changing
235 /// the pointer's cursor.
236 ///
237 /// This is useful in a region that displays a platform view, which let the
238 /// operating system handle pointer events and change cursors accordingly. To
239 /// achieve this, the region's cursor must not be any Flutter cursor, since
240 /// that might overwrite the system request upon pointer entering; the cursor
241 /// must not be `None` either, since that allows the widgets behind the region to
242 /// change cursors.
243 pub fn uncontrolled() -> MouseCursorRef {
244 Rc::new(NoopMouseCursor)
245 }
246}
247
248impl PartialEq for dyn MouseCursor {
249 fn eq(&self, other: &dyn MouseCursor) -> bool {
250 self.eq_cursor(other)
251 }
252}
253
254/// The cursor behind `MouseCursor.defer`. Every instance is equal.
255#[derive(Debug)]
256pub struct DeferringMouseCursor;
257
258impl MouseCursor for DeferringMouseCursor {
259 fn create_session(&self, _device: i64) -> Box<dyn MouseCursorSession> {
260 panic!("DeferringMouseCursor can not create a session");
261 }
262
263 fn debug_description(&self) -> String {
264 "defer".to_owned()
265 }
266
267 fn as_any(&self) -> &dyn Any {
268 self
269 }
270
271 fn eq_cursor(&self, other: &dyn MouseCursor) -> bool {
272 other.is_defer()
273 }
274}
275
276/// Returns the first cursor from the given cursors that is not a
277/// [`DeferringMouseCursor`], or `None` if none is found.
278fn first_non_deferred(cursors: impl IntoIterator<Item = MouseCursorRef>) -> Option<MouseCursorRef> {
279 cursors.into_iter().find(|cursor| !cursor.is_defer())
280}
281
282struct NoopMouseCursorSession {
283 device: i64,
284}
285
286impl MouseCursorSession for NoopMouseCursorSession {
287 fn cursor(&self) -> MouseCursorRef {
288 <dyn MouseCursor>::uncontrolled()
289 }
290
291 fn device(&self) -> i64 {
292 self.device
293 }
294
295 fn activate(&mut self, _platform: &dyn Platform, _view: ViewId) {
296 // Nothing.
297 }
298
299 fn dispose(&mut self) {
300 // Nothing.
301 }
302}
303
304/// The cursor behind `MouseCursor.uncontrolled`. Every instance is equal.
305#[derive(Debug)]
306pub struct NoopMouseCursor;
307
308impl MouseCursor for NoopMouseCursor {
309 fn create_session(&self, device: i64) -> Box<dyn MouseCursorSession> {
310 Box::new(NoopMouseCursorSession { device })
311 }
312
313 fn debug_description(&self) -> String {
314 "uncontrolled".to_owned()
315 }
316
317 fn as_any(&self) -> &dyn Any {
318 self
319 }
320
321 fn eq_cursor(&self, other: &dyn MouseCursor) -> bool {
322 other.as_any().is::<NoopMouseCursor>()
323 }
324}
325
326struct SystemMouseCursorSession {
327 cursor: SystemMouseCursor,
328 device: i64,
329}
330
331impl MouseCursorSession for SystemMouseCursorSession {
332 fn cursor(&self) -> MouseCursorRef {
333 Rc::new(self.cursor)
334 }
335
336 fn device(&self) -> i64 {
337 self.device
338 }
339
340 fn activate(&mut self, platform: &dyn Platform, view: ViewId) {
341 if let Some(view) = platform.view(view) {
342 view.host()
343 .activate_system_cursor(self.device, self.cursor.kind());
344 }
345 }
346
347 fn dispose(&mut self) {
348 // Nothing.
349 }
350}
351
352/// A mouse cursor that is natively supported on the platform that the
353/// application is running on.
354///
355/// System cursors can be used without external resources, and their appearances
356/// match the experience of native apps. Examples of system cursors are a
357/// pointing arrow, a pointing hand, a double arrow for resizing, or a text
358/// I-beam, etc.
359///
360/// An instance of [`SystemMouseCursor`] refers to one cursor from each platform
361/// that represents the same concept, such as being text, being clickable, or
362/// being a forbidden operation. Since the set of system cursors supported by each
363/// platform varies, multiple instances can correspond to the same system cursor.
364///
365/// Each cursor is noted with its corresponding native cursors on each platform:
366///
367/// * Android: API name in Java
368/// * Web: CSS cursor
369/// * Windows: Win32 API
370/// * Linux: GDK, `gdk_cursor_new_from_name`
371/// * macOS: API name in ObjectiveC
372///
373/// If the platform that the application is running on is not listed for a
374/// cursor, using this cursor falls back to [`SystemMouseCursors::BASIC`].
375///
376/// [`SystemMouseCursors`] enumerates the complete set of system cursors supported
377/// by Flutter, which are hard-coded in the engine. Therefore, manually
378/// instantiating this class is not supported.
379#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
380pub struct SystemMouseCursor {
381 kind: SystemMouseCursorKind,
382}
383
384impl SystemMouseCursor {
385 const fn new(kind: SystemMouseCursorKind) -> SystemMouseCursor {
386 SystemMouseCursor { kind }
387 }
388
389 /// The kind of the cursor.
390 ///
391 /// The interpretation of `kind` is platform-dependent.
392 pub fn kind(&self) -> SystemMouseCursorKind {
393 self.kind
394 }
395}
396
397impl MouseCursor for SystemMouseCursor {
398 fn create_session(&self, device: i64) -> Box<dyn MouseCursorSession> {
399 Box::new(SystemMouseCursorSession {
400 cursor: *self,
401 device,
402 })
403 }
404
405 fn debug_description(&self) -> String {
406 format!("SystemMouseCursor({:?})", self.kind)
407 }
408
409 fn as_any(&self) -> &dyn Any {
410 self
411 }
412
413 fn eq_cursor(&self, other: &dyn MouseCursor) -> bool {
414 other
415 .as_any()
416 .downcast_ref::<SystemMouseCursor>()
417 .is_some_and(|other| other.kind == self.kind)
418 }
419}
420
421impl From<SystemMouseCursor> for MouseCursorRef {
422 fn from(cursor: SystemMouseCursor) -> MouseCursorRef {
423 Rc::new(cursor)
424 }
425}
426
427/// A collection of system [`MouseCursor`]s.
428///
429/// System cursors are standard mouse cursors that are provided by the current
430/// platform. They don't require external resources.
431///
432/// [`SystemMouseCursors`] is a superset of the system cursors of every platform
433/// that Flutter supports, therefore some of these objects might map to the same
434/// result, or fallback to the [`BASIC`](Self::BASIC) arrow. This mapping is defined
435/// by the Flutter engine.
436///
437/// The cursors should be named based on the cursors' use cases instead of their
438/// appearance, because different platforms might (although not commonly) use
439/// different shapes for the same use case.
440pub struct SystemMouseCursors;
441
442impl SystemMouseCursors {
443 /// Hide the cursor.
444 ///
445 /// Any cursor other than [`NONE`](Self::NONE) or `MouseCursor.uncontrolled`
446 /// unhides the cursor.
447 pub const NONE: SystemMouseCursor = SystemMouseCursor::new(SystemMouseCursorKind::None);
448
449 /// The platform-dependent basic cursor.
450 ///
451 /// Typically the shape of an arrow.
452 pub const BASIC: SystemMouseCursor = SystemMouseCursor::new(SystemMouseCursorKind::Basic);
453
454 /// A cursor that emphasizes an element being clickable, such as a hyperlink.
455 ///
456 /// Typically the shape of a pointing hand.
457 pub const CLICK: SystemMouseCursor = SystemMouseCursor::new(SystemMouseCursorKind::Click);
458
459 /// A cursor indicating an operation that will not be carried out.
460 ///
461 /// Typically the shape of a circle with a diagonal line. May fall back to
462 /// [`NO_DROP`](Self::NO_DROP).
463 pub const FORBIDDEN: SystemMouseCursor =
464 SystemMouseCursor::new(SystemMouseCursorKind::Forbidden);
465
466 /// A cursor indicating the status that the program is busy and therefore
467 /// can not be interacted with.
468 ///
469 /// Typically the shape of an hourglass or a watch.
470 ///
471 /// This cursor is not available as a system cursor on macOS. Although macOS
472 /// displays a "spinning ball" cursor when busy, it's handled by the OS and not
473 /// exposed for applications to choose.
474 pub const WAIT: SystemMouseCursor = SystemMouseCursor::new(SystemMouseCursorKind::Wait);
475
476 /// A cursor indicating the status that the program is busy but can still be
477 /// interacted with.
478 ///
479 /// Typically the shape of an arrow with an hourglass or a watch at the
480 /// corner. Does *not* fall back to [`WAIT`](Self::WAIT) if unavailable.
481 pub const PROGRESS: SystemMouseCursor = SystemMouseCursor::new(SystemMouseCursorKind::Progress);
482
483 /// A cursor indicating somewhere the user can trigger a context menu.
484 ///
485 /// Typically the shape of an arrow with a small menu at the corner.
486 pub const CONTEXT_MENU: SystemMouseCursor =
487 SystemMouseCursor::new(SystemMouseCursorKind::ContextMenu);
488
489 /// A cursor indicating help information.
490 ///
491 /// Typically the shape of a question mark, or an arrow therewith.
492 pub const HELP: SystemMouseCursor = SystemMouseCursor::new(SystemMouseCursorKind::Help);
493
494 /// A cursor indicating selectable text.
495 ///
496 /// Typically the shape of a capital I.
497 pub const TEXT: SystemMouseCursor = SystemMouseCursor::new(SystemMouseCursorKind::Text);
498
499 /// A cursor indicating selectable vertical text.
500 ///
501 /// Typically the shape of a capital I rotated to be horizontal. May fall
502 /// back to [`TEXT`](Self::TEXT).
503 pub const VERTICAL_TEXT: SystemMouseCursor =
504 SystemMouseCursor::new(SystemMouseCursorKind::VerticalText);
505
506 /// A cursor indicating selectable table cells.
507 ///
508 /// Typically the shape of a hollow plus sign.
509 pub const CELL: SystemMouseCursor = SystemMouseCursor::new(SystemMouseCursorKind::Cell);
510
511 /// A cursor indicating precise selection, such as selecting a pixel in a
512 /// bitmap.
513 ///
514 /// Typically the shape of a crosshair.
515 pub const PRECISE: SystemMouseCursor = SystemMouseCursor::new(SystemMouseCursorKind::Precise);
516
517 /// A cursor indicating moving something.
518 ///
519 /// Typically the shape of four-way arrow. May fall back to
520 /// [`ALL_SCROLL`](Self::ALL_SCROLL).
521 pub const MOVE: SystemMouseCursor = SystemMouseCursor::new(SystemMouseCursorKind::Move);
522
523 /// A cursor indicating something that can be dragged.
524 ///
525 /// Typically the shape of an open hand.
526 pub const GRAB: SystemMouseCursor = SystemMouseCursor::new(SystemMouseCursorKind::Grab);
527
528 /// A cursor indicating something that is being dragged.
529 ///
530 /// Typically the shape of a closed hand.
531 pub const GRABBING: SystemMouseCursor = SystemMouseCursor::new(SystemMouseCursorKind::Grabbing);
532
533 /// A cursor indicating somewhere that the current item may not be dropped.
534 ///
535 /// Typically the shape of a hand with a [`FORBIDDEN`](Self::FORBIDDEN) sign at
536 /// the corner. May fall back to [`FORBIDDEN`](Self::FORBIDDEN).
537 pub const NO_DROP: SystemMouseCursor = SystemMouseCursor::new(SystemMouseCursorKind::NoDrop);
538
539 /// A cursor indicating that the current operation will create an alias of, or
540 /// a shortcut of the item.
541 ///
542 /// Typically the shape of an arrow with a shortcut icon at the corner.
543 pub const ALIAS: SystemMouseCursor = SystemMouseCursor::new(SystemMouseCursorKind::Alias);
544
545 /// A cursor indicating that the current operation will copy the item.
546 ///
547 /// Typically the shape of an arrow with a boxed plus sign at the corner.
548 pub const COPY: SystemMouseCursor = SystemMouseCursor::new(SystemMouseCursorKind::Copy);
549
550 /// A cursor indicating that the current operation will result in the
551 /// disappearance of the item.
552 ///
553 /// Typically the shape of an arrow with a cloud of smoke at the corner.
554 pub const DISAPPEARING: SystemMouseCursor =
555 SystemMouseCursor::new(SystemMouseCursorKind::Disappearing);
556
557 /// A cursor indicating scrolling in any direction.
558 ///
559 /// Typically the shape of a dot surrounded by 4 arrows.
560 pub const ALL_SCROLL: SystemMouseCursor =
561 SystemMouseCursor::new(SystemMouseCursorKind::AllScroll);
562
563 /// A cursor indicating resizing an object bidirectionally from its left or
564 /// right edge.
565 ///
566 /// Typically the shape of a bidirectional arrow pointing left and right.
567 pub const RESIZE_LEFT_RIGHT: SystemMouseCursor =
568 SystemMouseCursor::new(SystemMouseCursorKind::ResizeLeftRight);
569
570 /// A cursor indicating resizing an object bidirectionally from its top or
571 /// bottom edge.
572 ///
573 /// Typically the shape of a bidirectional arrow pointing up and down.
574 pub const RESIZE_UP_DOWN: SystemMouseCursor =
575 SystemMouseCursor::new(SystemMouseCursorKind::ResizeUpDown);
576
577 /// A cursor indicating resizing an object bidirectionally from its top left or
578 /// bottom right corner.
579 ///
580 /// Typically the shape of a bidirectional arrow pointing upper left and lower
581 /// right.
582 pub const RESIZE_UP_LEFT_DOWN_RIGHT: SystemMouseCursor =
583 SystemMouseCursor::new(SystemMouseCursorKind::ResizeUpLeftDownRight);
584
585 /// A cursor indicating resizing an object bidirectionally from its top right or
586 /// bottom left corner.
587 ///
588 /// Typically the shape of a bidirectional arrow pointing upper right and lower
589 /// left.
590 pub const RESIZE_UP_RIGHT_DOWN_LEFT: SystemMouseCursor =
591 SystemMouseCursor::new(SystemMouseCursorKind::ResizeUpRightDownLeft);
592
593 /// A cursor indicating resizing an object from its top edge.
594 ///
595 /// Typically the shape of an arrow pointing up. May fallback to
596 /// [`RESIZE_UP_DOWN`](Self::RESIZE_UP_DOWN).
597 pub const RESIZE_UP: SystemMouseCursor =
598 SystemMouseCursor::new(SystemMouseCursorKind::ResizeUp);
599
600 /// A cursor indicating resizing an object from its bottom edge.
601 ///
602 /// Typically the shape of an arrow pointing down. May fallback to
603 /// [`RESIZE_UP_DOWN`](Self::RESIZE_UP_DOWN).
604 pub const RESIZE_DOWN: SystemMouseCursor =
605 SystemMouseCursor::new(SystemMouseCursorKind::ResizeDown);
606
607 /// A cursor indicating resizing an object from its left edge.
608 ///
609 /// Typically the shape of an arrow pointing left. May fallback to
610 /// [`RESIZE_LEFT_RIGHT`](Self::RESIZE_LEFT_RIGHT).
611 pub const RESIZE_LEFT: SystemMouseCursor =
612 SystemMouseCursor::new(SystemMouseCursorKind::ResizeLeft);
613
614 /// A cursor indicating resizing an object from its right edge.
615 ///
616 /// Typically the shape of an arrow pointing right. May fallback to
617 /// [`RESIZE_LEFT_RIGHT`](Self::RESIZE_LEFT_RIGHT).
618 pub const RESIZE_RIGHT: SystemMouseCursor =
619 SystemMouseCursor::new(SystemMouseCursorKind::ResizeRight);
620
621 /// A cursor indicating resizing an object from its top-left corner.
622 ///
623 /// Typically the shape of an arrow pointing upper left. May fallback to
624 /// [`RESIZE_UP_LEFT_DOWN_RIGHT`](Self::RESIZE_UP_LEFT_DOWN_RIGHT).
625 pub const RESIZE_UP_LEFT: SystemMouseCursor =
626 SystemMouseCursor::new(SystemMouseCursorKind::ResizeUpLeft);
627
628 /// A cursor indicating resizing an object from its top-right corner.
629 ///
630 /// Typically the shape of an arrow pointing upper right. May fallback to
631 /// [`RESIZE_UP_RIGHT_DOWN_LEFT`](Self::RESIZE_UP_RIGHT_DOWN_LEFT).
632 pub const RESIZE_UP_RIGHT: SystemMouseCursor =
633 SystemMouseCursor::new(SystemMouseCursorKind::ResizeUpRight);
634
635 /// A cursor indicating resizing an object from its bottom-left corner.
636 ///
637 /// Typically the shape of an arrow pointing lower left. May fallback to
638 /// [`RESIZE_UP_RIGHT_DOWN_LEFT`](Self::RESIZE_UP_RIGHT_DOWN_LEFT).
639 pub const RESIZE_DOWN_LEFT: SystemMouseCursor =
640 SystemMouseCursor::new(SystemMouseCursorKind::ResizeDownLeft);
641
642 /// A cursor indicating resizing an object from its bottom-right corner.
643 ///
644 /// Typically the shape of an arrow pointing lower right. May fallback to
645 /// [`RESIZE_UP_LEFT_DOWN_RIGHT`](Self::RESIZE_UP_LEFT_DOWN_RIGHT).
646 pub const RESIZE_DOWN_RIGHT: SystemMouseCursor =
647 SystemMouseCursor::new(SystemMouseCursorKind::ResizeDownRight);
648
649 /// A cursor indicating resizing a column, or an item horizontally.
650 ///
651 /// Typically the shape of arrows pointing left and right with a vertical bar
652 /// separating them. May fallback to [`RESIZE_LEFT_RIGHT`](Self::RESIZE_LEFT_RIGHT).
653 pub const RESIZE_COLUMN: SystemMouseCursor =
654 SystemMouseCursor::new(SystemMouseCursorKind::ResizeColumn);
655
656 /// A cursor indicating resizing a row, or an item vertically.
657 ///
658 /// Typically the shape of arrows pointing up and down with a horizontal bar
659 /// separating them. May fallback to [`RESIZE_UP_DOWN`](Self::RESIZE_UP_DOWN).
660 pub const RESIZE_ROW: SystemMouseCursor =
661 SystemMouseCursor::new(SystemMouseCursorKind::ResizeRow);
662
663 /// A cursor indicating zooming in.
664 ///
665 /// Typically a magnifying glass with a plus sign.
666 pub const ZOOM_IN: SystemMouseCursor = SystemMouseCursor::new(SystemMouseCursorKind::ZoomIn);
667
668 /// A cursor indicating zooming out.
669 ///
670 /// Typically a magnifying glass with a minus sign.
671 pub const ZOOM_OUT: SystemMouseCursor = SystemMouseCursor::new(SystemMouseCursorKind::ZoomOut);
672}
673
674#[cfg(test)]
675mod tests {
676 use inset_embedder::ViewRef;
677 use inset_gestures::{PointerHoverEvent, PointerRemovedEvent};
678 use inset_test::{TestPlatform, TestView};
679
680 use super::*;
681
682 const VIEW: ViewId = ViewId(3);
683
684 /// A platform with view [`VIEW`], whose host records the cursors it is asked for, and the view.
685 fn recording_platform() -> (TestPlatform, Rc<TestView>) {
686 let view = Rc::new(TestView::new(800.0, 600.0).with_id(VIEW));
687 let platform = TestPlatform::new().with_view(Rc::clone(&view) as ViewRef);
688 (platform, view)
689 }
690
691 fn hover() -> PointerEvent {
692 PointerEvent::Hover(PointerHoverEvent::default())
693 }
694
695 #[test]
696 fn system_cursors_compare_by_kind_and_special_cursors_by_class() {
697 let click: MouseCursorRef = SystemMouseCursors::CLICK.into();
698 let click_again: MouseCursorRef = SystemMouseCursors::CLICK.into();
699 let basic: MouseCursorRef = SystemMouseCursors::BASIC.into();
700 assert!(*click == *click_again);
701 assert!(*click != *basic);
702 assert!(*<dyn MouseCursor>::defer() == *<dyn MouseCursor>::defer());
703 assert!(*<dyn MouseCursor>::uncontrolled() == *<dyn MouseCursor>::uncontrolled());
704 assert!(*<dyn MouseCursor>::defer() != *<dyn MouseCursor>::uncontrolled());
705 assert!(*<dyn MouseCursor>::defer() != *click);
706 assert_eq!(click.debug_description(), "SystemMouseCursor(Click)");
707 assert_eq!(<dyn MouseCursor>::defer().debug_description(), "defer");
708 }
709
710 #[test]
711 fn manager_activates_the_first_non_deferred_candidate() {
712 let (platform, view) = recording_platform();
713 let mut manager = MouseCursorManager::new(SystemMouseCursors::BASIC.into());
714 manager.handle_device_cursor_update(
715 &platform,
716 VIEW,
717 1,
718 Some(&hover()),
719 [<dyn MouseCursor>::defer(), SystemMouseCursors::TEXT.into()],
720 );
721 assert_eq!(
722 view.test_host().cursors(),
723 [(1, SystemMouseCursorKind::Text)],
724 "the deferring region lets the text region behind it decide"
725 );
726 let text: MouseCursorRef = SystemMouseCursors::TEXT.into();
727 assert!(*manager.debug_device_active_cursor(1).unwrap() == *text);
728 }
729
730 #[test]
731 fn manager_falls_back_when_every_candidate_defers() {
732 let (platform, view) = recording_platform();
733 let mut manager = MouseCursorManager::new(SystemMouseCursors::BASIC.into());
734 manager.handle_device_cursor_update(
735 &platform,
736 VIEW,
737 1,
738 Some(&hover()),
739 [<dyn MouseCursor>::defer()],
740 );
741 assert_eq!(
742 view.test_host().cursors(),
743 [(1, SystemMouseCursorKind::Basic)]
744 );
745 }
746
747 #[test]
748 fn manager_does_not_reactivate_the_same_cursor() {
749 let (platform, view) = recording_platform();
750 let mut manager = MouseCursorManager::new(SystemMouseCursors::BASIC.into());
751 manager.handle_device_cursor_update(
752 &platform,
753 VIEW,
754 1,
755 Some(&hover()),
756 [SystemMouseCursors::CLICK.into()],
757 );
758 manager.handle_device_cursor_update(
759 &platform,
760 VIEW,
761 1,
762 None,
763 [SystemMouseCursors::CLICK.into()],
764 );
765 assert_eq!(view.test_host().cursors().len(), 1);
766
767 manager.handle_device_cursor_update(
768 &platform,
769 VIEW,
770 1,
771 None,
772 [SystemMouseCursors::GRAB.into()],
773 );
774 assert_eq!(view.test_host().cursors().len(), 2);
775 }
776
777 #[test]
778 fn an_uncontrolled_region_keeps_the_previous_system_request() {
779 let (platform, view) = recording_platform();
780 let mut manager = MouseCursorManager::new(SystemMouseCursors::BASIC.into());
781 manager.handle_device_cursor_update(
782 &platform,
783 VIEW,
784 1,
785 Some(&hover()),
786 [SystemMouseCursors::CLICK.into()],
787 );
788 manager.handle_device_cursor_update(
789 &platform,
790 VIEW,
791 1,
792 None,
793 [<dyn MouseCursor>::uncontrolled()],
794 );
795 assert_eq!(
796 view.test_host().cursors(),
797 [(1, SystemMouseCursorKind::Click)],
798 "the no-op session makes no request; the host keeps whatever it shows"
799 );
800 assert!(
801 *manager.debug_device_active_cursor(1).unwrap() == *<dyn MouseCursor>::uncontrolled()
802 );
803 }
804
805 #[test]
806 fn a_removed_device_forgets_its_session() {
807 let (platform, _view) = recording_platform();
808 let mut manager = MouseCursorManager::new(SystemMouseCursors::BASIC.into());
809 manager.handle_device_cursor_update(
810 &platform,
811 VIEW,
812 1,
813 Some(&hover()),
814 [SystemMouseCursors::CLICK.into()],
815 );
816 let removed = PointerEvent::Removed(PointerRemovedEvent::default());
817 manager.handle_device_cursor_update(&platform, VIEW, 1, Some(&removed), []);
818 assert!(manager.debug_device_active_cursor(1).is_none());
819 }
820}