saudade 0.5.0

Classic looking retained-mode, cross-platform Rust GUI library
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
use std::path::PathBuf;

use crate::geometry::{Point, Size};

/// How many document lines one mouse-wheel detent (notch) scrolls. Matches the
/// modern desktop default of three lines per notch. The platform backends
/// multiply a raw wheel step by this when building an [`Event::Scroll`], so the
/// "3 lines per notch" policy lives in one place rather than in every widget.
pub(crate) const WHEEL_LINES_PER_DETENT: f32 = 3.0;

/// Nominal logical-pixel height of one line. The backends divide a continuous
/// (trackpad / high-resolution) pixel scroll delta by this to express it in the
/// same line units a wheel detent uses, so both kinds of scroll feed widgets a
/// single currency.
pub(crate) const SCROLL_PIXELS_PER_LINE: f32 = 16.0;

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MouseButton {
    Left,
    Right,
    Middle,
}

/// Identifies a key press independent of any text it produces. `Char` events
/// carry the *text* the user typed; `KeyDown` / `KeyUp` carry the *key*. Most
/// editing widgets want both — `Char` for insertion, `KeyDown(Named)` for
/// navigation and editing actions like Backspace.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Key {
    Named(NamedKey),
    Char(char),
}

impl From<char> for Key {
    fn from(c: char) -> Self {
        Key::Char(c)
    }
}

impl From<NamedKey> for Key {
    fn from(named: NamedKey) -> Self {
        Key::Named(named)
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum NamedKey {
    Enter,
    Backspace,
    Delete,
    Tab,
    Escape,
    Space,
    Left,
    Right,
    Up,
    Down,
    Home,
    End,
    PageUp,
    PageDown,
}

#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Modifiers {
    pub shift: bool,
    pub control: bool,
    /// Any Alt / Option key, either side.
    pub alt: bool,
    /// The right Alt key specifically — `AltGr` on PC keyboards, the right
    /// `Option` on macOS. It is reserved for *composing* characters (umlauts,
    /// currency symbols, …), so it deliberately does not open menu mnemonics
    /// and does not count as a command modifier. Whenever `alt_graph` is set,
    /// `alt` is set too.
    pub alt_graph: bool,
    pub logo: bool,
}

impl Modifiers {
    /// True if a command modifier (Ctrl / left Alt / Logo) is held. Editing
    /// widgets use this to decide whether a `Char` event should be inserted as
    /// text or treated as a hotkey instead.
    ///
    /// `AltGr` (right Alt) is intentionally *not* a command modifier: it
    /// composes characters and must let the resulting text through. Windows
    /// reports `AltGr` as `Ctrl+Alt`, so when it is held we ignore those bits.
    pub fn has_command(&self) -> bool {
        if self.alt_graph {
            return false;
        }
        self.control || self.alt || self.logo
    }

    /// True when an Alt key that should activate menu mnemonics is held — the
    /// left Alt only. The right Alt (`AltGr`) is reserved for composing
    /// characters such as umlauts and must not steal those keystrokes.
    pub fn mnemonic_alt(&self) -> bool {
        self.alt && !self.alt_graph
    }
}

/// The payload of a drag-and-drop operation — what the user is dragging into
/// the window.
///
/// Today this is the list of file-system paths a drag carries (parsed from the
/// platform's `text/uri-list` on Wayland, or winit's `HoveredFile` /
/// `DroppedFile` paths). It's a struct rather than a bare `Vec<PathBuf>` so that
/// future payload kinds (dragged text, an image) can be added without breaking
/// the [`Event`] variants that carry it.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct DragData {
    /// File-system paths being dragged. Empty when the drag carries no files
    /// the backend could resolve to local paths.
    pub paths: Vec<PathBuf>,
}

impl DragData {
    /// A payload carrying the given file paths.
    pub fn from_paths(paths: impl IntoIterator<Item = PathBuf>) -> Self {
        Self {
            paths: paths.into_iter().collect(),
        }
    }

    /// `true` when the drag resolved to at least one file path.
    pub fn has_paths(&self) -> bool {
        !self.paths.is_empty()
    }
}

/// Note: [`Event`] is `Clone` but **not** `Copy` — the drag variants carry a
/// [`DragData`] (which owns a `Vec`). Dispatch always passes `&Event`, so this
/// costs nothing on the hot path; only a widget that wants to *keep* a dropped
/// payload pays for the clone.
#[derive(Clone, Debug)]
pub enum Event {
    PointerMove {
        pos: Point,
    },
    PointerDown {
        pos: Point,
        button: MouseButton,
        /// Keyboard modifiers held at the moment of the press. Carried so a
        /// widget can distinguish a plain click from Ctrl/Shift+click — e.g. a
        /// multi-selection [`List`](crate::widgets::List) toggles on
        /// Ctrl/Cmd+click and range-selects on Shift+click. Mirrors the
        /// `modifiers` already on [`Event::KeyDown`].
        modifiers: Modifiers,
    },
    PointerUp {
        pos: Point,
        button: MouseButton,
        /// Keyboard modifiers held at the moment of the release. See
        /// [`Event::PointerDown::modifiers`].
        modifiers: Modifiers,
    },
    PointerLeave,
    /// The mouse wheel turned or a trackpad scroll gesture moved. `delta` is
    /// measured in document *lines*, and positive values scroll toward the end
    /// of the content — down for `delta_y`, right for `delta_x` — the same
    /// direction a [`ScrollBar`](crate::widgets::ScrollBar)'s value grows. One
    /// wheel notch is [`WHEEL_LINES_PER_DETENT`] lines; trackpad pixel deltas
    /// are converted to a fractional line count, which is why the fields are
    /// `f32`. `pos` is the cursor's position when the wheel turned, so a
    /// container can route the gesture to the widget under the pointer.
    Scroll {
        pos: Point,
        delta_x: f32,
        delta_y: f32,
    },
    /// A drag carrying droppable content entered the window over `pos`. A drop
    /// target highlights itself here. The payload itself arrives with
    /// [`Event::Drop`] — not here — because the platforms only let us read it
    /// reliably once the user actually drops (reading mid-hover can block on a
    /// source that withholds the data until then). `pos` is the pointer
    /// location in logical pixels; on Wayland it is exact, on the winit backends
    /// (macOS / Windows / X11) it is best-effort — winit reports no cursor
    /// coordinates during a file drag, so it reflects the last in-window
    /// pointer position.
    DragEnter {
        pos: Point,
    },
    /// The drag moved to `pos` while still inside the window. Routed to the
    /// widget under the cursor, so dragging across widgets hands the highlight
    /// from one drop target to the next. Only the Wayland backend tracks the
    /// pointer during a drag and emits this; the winit backends go straight
    /// from [`Event::DragEnter`] to [`Event::Drop`].
    DragMove {
        pos: Point,
    },
    /// The drag left the window, or was cancelled, without dropping. Like
    /// [`Event::PointerLeave`] it carries no position and is broadcast to every
    /// widget so any drop target can clear its highlight.
    DragLeave,
    /// The content was released over `pos`. This is where the payload (see
    /// [`DragData`]) is consumed — e.g. open the dropped file. Routed to the
    /// widget under the cursor exactly like [`Event::PointerUp`].
    Drop {
        pos: Point,
        data: DragData,
    },
    KeyDown {
        key: Key,
        modifiers: Modifiers,
    },
    KeyUp {
        key: Key,
        modifiers: Modifiers,
    },
    /// A character produced by the user's keyboard. Backspace, arrow keys etc.
    /// arrive as `KeyDown` only; this event is for inserting visible text.
    Char {
        ch: char,
        modifiers: Modifiers,
    },
    /// Periodic animation tick, fired by the runtime at roughly 60 Hz while
    /// any widget in the tree returns `wants_ticks() == true`.
    Tick,
}

impl Event {
    pub fn position(&self) -> Option<Point> {
        match self {
            Event::PointerMove { pos }
            | Event::PointerDown { pos, .. }
            | Event::PointerUp { pos, .. }
            | Event::Scroll { pos, .. }
            | Event::DragEnter { pos, .. }
            | Event::DragMove { pos, .. }
            | Event::Drop { pos, .. } => Some(*pos),
            _ => None,
        }
    }

    pub fn is_keyboard(&self) -> bool {
        matches!(
            self,
            Event::KeyDown { .. } | Event::KeyUp { .. } | Event::Char { .. }
        )
    }
}

/// The shape the mouse pointer takes while it rests over a widget.
///
/// A widget asks for one with [`EventCtx::set_cursor`] while handling a pointer
/// event; the runtime maps it onto the platform's cursor — a `wp_cursor_shape`
/// request on Wayland, a `CursorIcon` on the winit backends (X11 / Windows /
/// macOS). The names follow the CSS / freedesktop cursor vocabulary the two
/// platforms share, so every variant has a native equivalent on each. A
/// compositor that supplies no themed cursor for a given shape falls back to
/// its own default; nothing here can fail.
///
/// The default everywhere is [`Cursor::Default`] — the arrow. A widget that
/// never calls [`EventCtx::set_cursor`] keeps it, which is why plain content
/// (labels, panels) needs no cursor code at all.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum Cursor {
    /// The normal arrow — every widget's pointer unless it asks otherwise.
    #[default]
    Default,
    /// Pointing hand / "finger": the thing under the pointer can be clicked or
    /// activated. [`Button`](crate::widgets::Button) and
    /// [`Checkbox`](crate::widgets::Checkbox) use it (CSS `pointer`).
    Hand,
    /// I-beam / vertical text bar: text here can be placed-into, selected, or
    /// edited. [`TextInput`](crate::widgets::TextInput) and
    /// [`TextEditor`](crate::widgets::TextEditor) use it.
    Text,
    /// Sideways I-beam, for text laid out vertically.
    VerticalText,
    /// Precise crosshair, for picking an exact point (drawing, color-picking).
    Crosshair,
    /// Four-way arrow — the element under the pointer can be moved.
    Move,
    /// Open hand: something here can be grabbed and dragged.
    Grab,
    /// Closed hand: a grab / drag is in progress.
    Grabbing,
    /// "No" circle — the attempted action is not allowed here.
    NotAllowed,
    /// Like [`Cursor::NotAllowed`], but specifically "a drop won't land here".
    NoDrop,
    /// A copy will be made — the usual drag-with-modifier feedback.
    Copy,
    /// An alias / shortcut will be created, rather than a copy or a move.
    Alias,
    /// A context menu is available for the element under the pointer.
    ContextMenu,
    /// Help is available — the arrow gains a question mark.
    Help,
    /// The program is busy but still interactive (arrow + spinner).
    Progress,
    /// The program is busy and not accepting input (hourglass / watch).
    Wait,
    /// A table / grid cell can be selected.
    Cell,
    /// Something under the pointer can be zoomed in,
    ZoomIn,
    /// …or zoomed out.
    ZoomOut,
    /// Scroll in any direction — the middle-button autoscroll anchor.
    AllScroll,
    /// Resize a column boundary (a vertical splitter): a left-right bar.
    ColResize,
    /// Resize a row boundary (a horizontal splitter): an up-down bar.
    RowResize,
    /// Bidirectional east-west resize handle.
    EwResize,
    /// Bidirectional north-south resize handle.
    NsResize,
    /// Bidirectional resize along the ↗↙ diagonal.
    NeswResize,
    /// Bidirectional resize along the ↖↘ diagonal.
    NwseResize,
}

/// Capabilities granted to a widget while it handles an event.
///
/// Widgets do not mutate the runtime directly: they set request flags here, and
/// the runtime applies them after dispatch completes.
pub struct EventCtx {
    pub(crate) paint_requested: bool,
    pub(crate) close_requested: bool,
    pub(crate) focus_requested: bool,
    pub(crate) focus_released: bool,
    /// Set by a widget that has fully handled an event and wants to stop
    /// further routing. Parent containers check it after each dispatch
    /// step (accelerator pass, focused dispatch, …) and bail out so the
    /// event doesn't trigger a second action elsewhere in the tree.
    pub(crate) consumed: bool,
    /// Set by content inside a modal dialog to ask its host to close. The
    /// hosting [`Modal`](crate::widgets::Modal) observes this after forwarding
    /// an event to its content, runs its `on_dismiss` handler, and tears the
    /// dialog down — the same protocol a message box's OK button uses.
    pub(crate) dismiss_requested: bool,
    /// Set when a widget calls [`Self::request_window_size`] to ask the runtime
    /// to resize the window. Applied after dispatch, alongside the other
    /// requests. The window's size is the app's to choose (unlike the scale
    /// factor, which only the OS sets).
    pub(crate) resize_request: Option<Size>,
    /// Set when a widget calls [`Self::start_drag`] to begin dragging content
    /// out of the window. Consumed by the backend after dispatch, which turns
    /// it into a real OS drag. Only the Wayland backend acts on it (see the
    /// method docs); the winit backends drop it.
    pub(crate) drag_request: Option<DragData>,
    /// Set when a widget calls [`Self::accept_drop`] while handling an incoming
    /// [`Event::DragEnter`] / [`Event::DragMove`] to say it will take a drop at
    /// this position. The Wayland backend reads it after dispatch to accept or
    /// reject the drag offer, so the source app learns whether this is a valid
    /// drop target.
    pub(crate) accepts_drop: bool,
    /// Set by a widget that has fully handled a key press and wants the runtime
    /// to drop the rest of it — see [`Self::swallow_key_until_release`].
    pub(crate) swallow_key: bool,
    /// Set by a widget that wants the runtime to deliver at least one more
    /// [`Event::Tick`] soon — see [`Self::request_tick`].
    pub(crate) tick_requested: bool,
    /// Set by a mnemonic-bearing widget (e.g. a [`FocusLabel`](crate::widgets::FocusLabel))
    /// whose accelerator was just matched, asking its parent container to move
    /// keyboard focus to the *next focusable sibling* after it. The container
    /// acts on it right after the accelerator dispatch. See
    /// [`Self::request_focus_next`].
    pub(crate) focus_next_requested: bool,
    /// The pointer shape a widget asked for via [`Self::set_cursor`] while
    /// handling this event, or `None` if none did. After dispatching a pointer
    /// *move*, the runtime applies it to the window — falling back to
    /// [`Cursor::Default`] when it's `None` — so the shape always reflects the
    /// widget the pointer currently rests over.
    pub(crate) cursor_request: Option<Cursor>,
}

impl EventCtx {
    pub(crate) fn new() -> Self {
        Self {
            paint_requested: false,
            close_requested: false,
            focus_requested: false,
            focus_released: false,
            consumed: false,
            dismiss_requested: false,
            resize_request: None,
            drag_request: None,
            accepts_drop: false,
            swallow_key: false,
            tick_requested: false,
            focus_next_requested: false,
            cursor_request: None,
        }
    }

    /// Returns `true` if a widget has called [`Self::consume_event`] during
    /// this dispatch. Used by parent containers to decide whether to keep
    /// routing the event.
    pub fn is_consumed(&self) -> bool {
        self.consumed
    }

    /// Mark the current event as handled. Parent containers stop routing
    /// once they see this flag, so the same keystroke doesn't fire two
    /// actions (e.g., a default button's Enter accelerator stopping the
    /// focused list from also reacting to the keypress).
    pub fn consume_event(&mut self) {
        self.consumed = true;
    }

    /// Ask the runtime to discard the remainder of the current key press: any
    /// text (`Char`) it still produces *and* every later event for the same
    /// key — autorepeats and the release — up to and including that release.
    ///
    /// Unlike [`Self::consume_event`], which only stops routing *this* event,
    /// this reaches across the separate `KeyDown` / `Char` / `KeyUp` events a
    /// single press generates. A menu item fired by its mnemonic uses it: the
    /// keystroke that picked the item must not also land in whatever the item
    /// opens on the spot — e.g. the freshly focused field of a dialog — which
    /// otherwise receives the trailing `Char` and types the letter.
    pub fn swallow_key_until_release(&mut self) {
        self.swallow_key = true;
    }

    /// Mark the window dirty so the runtime repaints on the next idle tick.
    pub fn request_paint(&mut self) {
        self.paint_requested = true;
    }

    /// Ask the runtime to deliver at least one more [`Event::Tick`] soon.
    ///
    /// This is the *push* counterpart to [`Widget::wants_ticks`](crate::Widget::wants_ticks):
    /// where `wants_ticks` is a steady-state flag the runtime *pulls* by walking
    /// the widget tree (so every container in the path must forward it), this
    /// request rides the shared `EventCtx` straight back to the runtime — no
    /// ancestor has to know or forward anything, exactly like
    /// [`Self::request_paint`]. A widget buried under custom wrappers can thus
    /// drive an animation without its parents cooperating.
    ///
    /// It is one-shot: it guarantees the *next* tick, not a stream. A widget
    /// that needs continuous ticks (e.g. a scrollbar auto-repeating while its
    /// arrow button is held) simply calls this again each time it handles a
    /// [`Event::Tick`], for as long as it still needs them; the moment it stops
    /// re-requesting, the ticks wind down on their own.
    pub fn request_tick(&mut self) {
        self.tick_requested = true;
    }

    /// Ask the runtime to close the window after this dispatch completes.
    pub fn close(&mut self) {
        self.close_requested = true;
    }

    /// `true` if a widget called [`Self::request_focus`] during this dispatch.
    /// Custom container widgets (outside saudade) read this after forwarding
    /// an event to a child to learn the child wants focus, then call
    /// [`Self::clear_focus_flags`] and move focus to it — the same protocol the
    /// built-in `Container` / `Column` use internally.
    pub fn is_focus_requested(&self) -> bool {
        self.focus_requested
    }

    /// `true` if a widget called [`Self::release_focus`] during this dispatch.
    pub fn is_focus_released(&self) -> bool {
        self.focus_released
    }

    /// Reset both focus-change flags after a custom container has acted on
    /// them.
    pub fn clear_focus_flags(&mut self) {
        self.focus_requested = false;
        self.focus_released = false;
    }

    /// The widget asks to become the keyboard-focused widget. Parent
    /// containers observe this flag during pointer dispatch and route
    /// subsequent keyboard events here.
    pub fn request_focus(&mut self) {
        self.focus_requested = true;
        self.focus_released = false;
    }

    /// The widget asks to drop keyboard focus. Useful when an editor wants
    /// the window to stop sending it characters.
    pub fn release_focus(&mut self) {
        self.focus_released = true;
        self.focus_requested = false;
    }

    /// A mnemonic-bearing widget (typically a
    /// [`FocusLabel`](crate::widgets::FocusLabel)) asks its parent container to
    /// move keyboard focus to the *next focusable sibling* after it — the
    /// classic buddy-label behaviour, where `"Last &name:"` hands focus to the
    /// text field that follows it. Call this from the widget's `event` handler
    /// when its accelerator letter is pressed; the container performs the move
    /// once dispatch unwinds. The requesting widget needs no knowledge of its
    /// own position in the tree.
    pub fn request_focus_next(&mut self) {
        self.focus_next_requested = true;
    }

    /// `true` if a widget called [`Self::request_focus_next`] during this
    /// dispatch. Read by container widgets — including custom ones outside
    /// saudade — to learn a buddy label's accelerator was matched, so they can
    /// move focus to the next focusable child after the requester.
    pub fn is_focus_next_requested(&self) -> bool {
        self.focus_next_requested
    }

    /// Content inside a modal dialog calls this to ask the hosting
    /// [`Modal`](crate::widgets::Modal) to close — e.g. from an OK / Close
    /// button's `on_click`. The modal runs its `on_dismiss` handler and
    /// dismisses after the current event finishes dispatching.
    pub fn request_dismiss(&mut self) {
        self.dismiss_requested = true;
    }

    /// `true` if a widget called [`Self::request_dismiss`] during this
    /// dispatch. Read by [`Modal`](crate::widgets::Modal) after forwarding an
    /// event to its content.
    pub fn is_dismiss_requested(&self) -> bool {
        self.dismiss_requested
    }

    /// Ask the runtime to resize the window to `width × height` *logical*
    /// pixels, applied after the current event finishes dispatching (and
    /// followed by a repaint, so callers needn't also call
    /// [`Self::request_paint`]). Handy for a window that should grow or shrink
    /// to fit a mode the user just toggled. The window's size is the app's to
    /// set — unlike the logical→physical scale factor, which only the OS
    /// controls.
    pub fn request_window_size(&mut self, width: i32, height: i32) {
        self.resize_request = Some(Size::new(width.max(1), height.max(1)));
    }

    /// Begin dragging `data` out of this window as an OS drag-and-drop
    /// operation — the mirror of receiving a [`Event::Drop`]. Call this from a
    /// widget's `event` handler once a press-then-move gesture is recognized
    /// (typically on [`Event::PointerMove`] after the pointer has travelled a
    /// few pixels from a [`Event::PointerDown`]), so a plain click still reads
    /// as a click rather than starting a drag.
    ///
    /// **Wayland only.** The winit backends (macOS, Windows, X11) expose no API
    /// to *initiate* a drag, so this is a no-op there; only the Wayland backend
    /// turns it into a real drag whose `text/uri-list` payload other
    /// applications can drop. Receiving drops, by contrast, works on every
    /// backend. The drag copies (never moves) the referenced files.
    pub fn start_drag(&mut self, data: DragData) {
        self.drag_request = Some(data);
    }

    /// Signal, while handling an incoming [`Event::DragEnter`] or
    /// [`Event::DragMove`], that this widget will accept a drop at the current
    /// position. A drop target **must** call this to receive drops: the runtime
    /// treats a widget that doesn't as not interested, so the drag falls through
    /// it. Re-evaluated on every move, so a target can accept only over its hot
    /// region and decline elsewhere in the same window.
    ///
    /// On Wayland this is what tells the source app the spot is a valid target
    /// (its drag cursor / feedback reflects it) and is the prerequisite for a
    /// later [`Event::Drop`] landing here. On the winit backends the OS has
    /// already committed to offering the drop, so this is advisory there —
    /// [`Event::Drop`] arrives regardless — but calling it keeps drop targets
    /// portable.
    pub fn accept_drop(&mut self) {
        self.accepts_drop = true;
    }

    /// Ask the runtime to show `cursor` as the mouse-pointer shape while it
    /// rests over this widget. Call it while handling a pointer event — almost
    /// always [`Event::PointerMove`].
    ///
    /// The runtime reconciles the pointer shape after every move: whatever a
    /// widget requests during that move's dispatch becomes the cursor, and a
    /// move no widget answers falls back to [`Cursor::Default`] (the arrow). A
    /// widget that wants a non-default cursor therefore simply re-requests it
    /// on each `PointerMove` it receives — the cost is one enum write, and
    /// there's nothing to "unset" on the way out: moving onto another widget,
    /// or off this one, re-runs the reconciliation. Because only moves drive
    /// it, a request made while handling a press, key, or tick is ignored.
    ///
    /// Buttons request [`Cursor::Hand`] and text fields [`Cursor::Text`] this
    /// way; a widget that never calls this keeps the normal arrow.
    pub fn set_cursor(&mut self, cursor: Cursor) {
        self.cursor_request = Some(cursor);
    }
}