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
//! Thread-local draw-request and invalidation signals.
//!
//! Two independent channels feed the host's event loop:
//!
//! 1. **Immediate draw request** — [`request_draw`] / [`wants_draw`]. Any
//! widget whose visual output just changed calls `request_draw()`; the next
//! iteration of the host loop draws a frame and clears the flag. The same
//! call advances [`invalidation_epoch`], letting event dispatch dirty the
//! affected retained ancestor path even when the event bubbles as ignored.
//!
//! 2. **Scheduled draw** — [`request_draw_after`] /
//! [`take_next_draw_deadline`]. A
//! widget that needs a draw *at a future time* (text-cursor blink,
//! tooltip delay) calls `request_draw_after(Duration)`; the host's
//! loop goes to sleep with `ControlFlow::WaitUntil(that_instant)` and
//! draws when the deadline fires. Successive calls keep the EARLIEST
//! deadline.
//!
//! The host loop draws iff `wants_draw() || now >= take_next_draw_deadline()`.
//! Between draws it idles; no frames are drawn while nothing has changed.
use Cell;
use Duration;
use Instant;
thread_local!
/// Request that the host schedule another draw as soon as possible.
///
/// **This is the right default for every widget state mutation that affects
/// visual output.** Calling it from inside an `on_event` handler advances
/// [`invalidation_epoch`]; `dispatch_event` reads that epoch before/after
/// delivery and automatically calls `mark_dirty` up the ancestor path when
/// it sees a bump — so a retained ancestor's backbuffer cache invalidates
/// without the widget needing to know about that ancestor at all.
///
/// Without the epoch bump, a `Widget::on_event` that returns `Ignored` (the
/// common case for `MouseMove`) leaves the ancestor cache thinking
/// "nothing changed", and the next frame composites a stale bitmap. Hover
/// effects, focus rings, and any other appearance change driven by event
/// state ALL need this hook.
///
/// Reach for [`request_draw_without_invalidation`] only when you're certain
/// no retained widget's *content* changed — overlays, position-only
/// translations, and similar. When in doubt, use `request_draw`.
/// Request a frame **without** advancing [`invalidation_epoch`].
///
/// `dispatch_event` won't mark retained ancestors dirty for this call, so
/// any widget that drew its previous frame into a backbuffer cache will
/// composite the cached bitmap unchanged. Use this **only** when:
///
/// * The change lives in an app-level overlay that paints fresh every
/// frame outside any retained subtree (inspector hover rectangle, popup
/// menus rendered via `paint_global_overlay`, scroll-fade decorations).
/// * The change is position-only — a window drag-move, where the cached
/// content is reused at a translated origin (see `Window::on_event` for
/// the canonical example).
///
/// **Do NOT call this from a widget that mutated its own state and expects
/// the next paint to reflect it.** That's [`request_draw`]'s job. Hover
/// indices, focus changes, animation ticks, button-press states — anything
/// where the *content* of a retained widget differs from the cached
/// bitmap — must call `request_draw` so the cache invalidates. The
/// `MenuBar` hover regression in `widgets/menu/widget/tests_2.rs` exists
/// precisely because this distinction was missed once already.
/// Non-destructive read. Hosts call this after drawing to decide control-flow
/// for the next loop iteration.
/// Monotonic draw-request epoch used to detect visual changes during dispatch.
/// Note that an async-side state change happened (image loader finished,
/// font loaded, etc.). Calls `request_draw()` so the next frame fires,
/// AND bumps the [`async_state_epoch`] so retained backbuffers
/// re-rasterise — without the latter, the freshly-loaded data gets
/// drawn into whatever placeholder rect the previous layout reserved
/// (the markdown SVG-badge "wrong scale on first frame" bug).
/// Current async-state epoch. Backbuffer caches store this and force
/// a re-raster when it doesn't match.
/// Reset the per-frame draw flags. The `App::paint` entry point calls
/// this before delegating to the root widget so each frame starts fresh —
/// widgets that still need a draw (animation in flight, focus blink, etc.)
/// must re-arm during their draw, otherwise the loop goes idle.
/// Schedule a future draw. Keeps the EARLIEST pending deadline, so multiple
/// widgets asking for different delays will all be served by the soonest one
/// (each widget re-arms its own deadline on the next draw anyway).
/// Read-and-clear the scheduled draw deadline. The host reads this after
/// drawing so the next frame's scheduled wake is determined entirely by what
/// the fresh draw registered (e.g. a text field re-arms the 500 ms blink
/// each frame while it remains focused; losing focus means no re-arm and the
/// loop goes idle).
// ── Tween ────────────────────────────────────────────────────────────────────
//
// Small reusable time-based interpolator for widgets that want a smooth
// transition between two scalar states (hover ↔ dormant, off ↔ on, etc.).
// Ease-out cubic; reversal preserves the current value so rapid toggles
// don't snap. Requests a draw automatically while in flight.
/// Smooth scalar tween between `0.0` and `1.0` (or any pair of values the
/// caller interprets). Drives animations such as the scroll-bar hover
/// expansion and toggle-switch on/off slide.