Skip to main content

agg_gui/
widget.rs

1//! Widget trait, tree traversal, and the top-level [`App`] struct.
2//!
3//! # Coordinate system
4//!
5//! Widget bounds are expressed in **parent-local** first-quadrant (Y-up)
6//! coordinates. A widget at `bounds.x = 10, bounds.y = 20` is drawn 10 units
7//! right and 20 units up from its parent's bottom-left corner.
8//!
9//! OS/browser mouse events arrive in Y-down screen coordinates. The single
10//! conversion `y_up = viewport_height - y_down` happens inside
11//! [`App::on_mouse_move`] / [`App::on_mouse_down`] / [`App::on_mouse_up`].
12//! All widget code sees Y-up coordinates only.
13//!
14//! # Tree traversal
15//!
16//! Paint: root → leaves (children painted on top of parents).
17//! Hit test: root → leaves (deepest child under cursor wins).
18//! Event dispatch: leaf → root (events bubble up; any widget can consume).
19
20use crate::draw_ctx::DrawCtx;
21use crate::event::{Event, EventResult, Key, Modifiers};
22use crate::geometry::{Point, Rect, Size};
23use crate::layout_props::{HAnchor, Insets, VAnchor, WidgetBase};
24
25// ---------------------------------------------------------------------------
26// Widget trait
27// ---------------------------------------------------------------------------
28
29/// Every visible element in the UI is a widget.
30///
31/// Implementors handle their own painting and event handling. The framework
32/// takes care of tree traversal, coordinate translation, and focus management.
33pub trait Widget {
34    /// Bounding rectangle in **parent-local** Y-up coordinates.
35    fn bounds(&self) -> Rect;
36
37    /// Set the bounding rectangle. Called by the parent during layout.
38    fn set_bounds(&mut self, bounds: Rect);
39
40    /// Immutable access to child widgets.
41    fn children(&self) -> &[Box<dyn Widget>];
42
43    /// Mutable access to child widgets (required for event dispatch + layout).
44    fn children_mut(&mut self) -> &mut Vec<Box<dyn Widget>>;
45
46    /// Compute desired size given available space, and update internal layout.
47    ///
48    /// The parent passes the space it can offer; the widget returns the size it
49    /// actually wants to occupy. The parent uses the returned size to set this
50    /// widget's bounds before calling `layout` on the next sibling.
51    fn layout(&mut self, available: Size) -> Size;
52
53    /// Paint this widget's own content into `ctx`.
54    ///
55    /// The framework has already translated `ctx` so that `(0, 0)` is this
56    /// widget's bottom-left corner. **Do not paint children here** — the
57    /// framework recurses into them automatically after `paint` returns.
58    ///
59    /// `ctx` is a `&mut dyn DrawCtx`; the concrete type is either a software
60    /// `GfxCtx` (back-buffer path) or a `GlGfxCtx` (hardware GL path).
61    fn paint(&mut self, ctx: &mut dyn DrawCtx);
62
63    /// Return `true` if `local_pos` (in this widget's local coordinates) falls
64    /// inside this widget's interactive area. Default: axis-aligned rect test.
65    fn hit_test(&self, local_pos: Point) -> bool {
66        let b = self.bounds();
67        local_pos.x >= 0.0
68            && local_pos.x <= b.width
69            && local_pos.y >= 0.0
70            && local_pos.y <= b.height
71    }
72
73    /// When `true`, `hit_test_subtree` stops recursing into this widget's
74    /// children and returns this widget as the hit target.  Used for floating
75    /// overlays (e.g. a scrollbar painted above its content) that must claim
76    /// the pointer before children that happen to share the same pixels.
77    /// Default: `false`.
78    fn claims_pointer_exclusively(&self, _local_pos: Point) -> bool {
79        false
80    }
81
82    /// When `true`, this widget disables *all* interaction — pointer and
83    /// keyboard — within its subtree, reproducing egui's
84    /// `UiBuilder::disabled()`.  Unlike [`claims_pointer_exclusively`], this is
85    /// a position-independent state predicate: pointer hit-testing stops at
86    /// this widget (so clicks are swallowed rather than passing through), and
87    /// focus collection skips the whole subtree (so Tab cannot reach a child).
88    /// Default: `false`.
89    fn blocks_child_interaction(&self) -> bool {
90        false
91    }
92
93    /// Return true when `local_pos` hits an app-level overlay owned by this
94    /// widget. Unlike normal hit testing, ancestors may be missed because the
95    /// overlay is painted outside their bounds.
96    fn hit_test_global_overlay(&self, _local_pos: Point) -> bool {
97        false
98    }
99
100    /// Whether this widget currently owns an app-modal interaction layer.
101    ///
102    /// When true anywhere in the tree, [`App`](crate::App) routes pointer and
103    /// key events to that modal subtree before normal hit testing so content
104    /// underneath the modal backdrop cannot be interacted with.
105    fn has_active_modal(&self) -> bool {
106        false
107    }
108
109    /// Handle an event. The event's positions are already in **local** Y-up
110    /// coordinates. Return [`EventResult::Consumed`] to stop bubbling.
111    ///
112    /// # Invalidation contract
113    ///
114    /// If your handler mutates state that affects the next paint (hover
115    /// index, focus, button-pressed bool, animation phase, ...), call
116    /// [`crate::animation::request_draw`] from inside the handler.  That
117    /// bumps the invalidation epoch, which `dispatch_event` reads to mark
118    /// retained ancestor backbuffers dirty — without it, the cached
119    /// bitmap composites unchanged and your state mutation is invisible
120    /// until something else dirties the cache.
121    ///
122    /// Returning `Consumed` *also* dirties the ancestor path automatically,
123    /// so a consumed click handler that calls `request_draw` is belt-and-
124    /// suspenders.  But `MouseMove` handlers that return `Ignored` (the
125    /// usual case for hover-tracking) **only** invalidate via
126    /// `request_draw`'s epoch bump.  Forgetting it produces "hover only
127    /// works the first time after I drag the window resize edge" bugs.
128    ///
129    /// `request_draw_without_invalidation` is for the rare cases where
130    /// the visual change is in an app-overlay or a position-only
131    /// composite — see its rustdoc.
132    fn on_event(&mut self, event: &Event) -> EventResult;
133
134    /// Handle a key that was not consumed by the focused widget path.
135    ///
136    /// This is used for window/menu accelerators: focused controls get first
137    /// chance at the key, then visible widgets in paint order may claim it.
138    fn on_unconsumed_key(&mut self, _key: &Key, _modifiers: Modifiers) -> EventResult {
139        EventResult::Ignored
140    }
141
142    /// Whether this widget can receive keyboard focus. Default: false.
143    fn is_focusable(&self) -> bool {
144        false
145    }
146
147    /// Stable identifier for the programmatic focus channel
148    /// ([`crate::focus::request_focus`]).
149    ///
150    /// App code can't reach the [`App`](crate::widget::App)'s private focus
151    /// path to focus a widget the moment it appears (e.g. a search field
152    /// that should grab the keyboard when its overlay opens). A widget that
153    /// returns `Some(id)` here can be focused by calling
154    /// [`crate::focus::request_focus(id)`](crate::focus::request_focus); the
155    /// `App` services the request on its next `layout`, moving focus to the
156    /// matching focusable widget (which dispatches `FocusGained` and raises
157    /// the on-screen keyboard for text inputs). Default: `None`.
158    fn focus_id(&self) -> Option<crate::focus::FocusId> {
159        None
160    }
161
162    /// A static name for this widget type, used by the inspector. Default: "Widget".
163    fn type_name(&self) -> &'static str {
164        "Widget"
165    }
166
167    /// Optional human-readable identifier for this widget instance.
168    ///
169    /// Distinct from [`type_name`] (which is per-type and constant):
170    /// `id` lets external code look up a specific *instance* — used
171    /// today by the demo's z-order persistence to match a saved title
172    /// against a live `Window` in the canvas `Stack`.  Default
173    /// implementation returns `None`; widgets that want to be
174    /// identifiable (e.g. `Window` returning its title) override.
175    fn id(&self) -> Option<&str> {
176        None
177    }
178
179    /// Return `false` to suppress painting this widget **and all its children**.
180    /// The widget's own `paint()` will not be called.  Default: `true`.
181    fn is_visible(&self) -> bool {
182        true
183    }
184
185    /// Return type-specific properties for the inspector properties pane.
186    ///
187    /// Each entry is `(name, display_value)`.  The default returns an empty
188    /// list; widgets override this to expose their state to the inspector.
189    fn properties(&self) -> Vec<(&'static str, String)> {
190        vec![]
191    }
192
193    /// `true` when this widget accepts free-form character input (typing
194    /// arbitrary letters, numbers, punctuation). Used by the on-screen
195    /// software keyboard (`crate::widgets::on_screen_keyboard`) to decide
196    /// whether to slide up when this widget gains focus.
197    ///
198    /// Default is `false`. `TextField` and `TextArea` override to `true`.
199    /// `DragValue` and similar numeric editors should override to `true`
200    /// only when they are in their full-text-edit mode; otherwise the
201    /// keyboard would appear for transient drag interactions.
202    ///
203    /// This is independent of [`is_focusable`](Self::is_focusable) — a
204    /// `Button` is focusable but doesn't accept typed text.
205    fn accepts_text_input(&self) -> bool {
206        false
207    }
208
209    /// Current text contents of this widget if it is text-bearing.
210    /// Used by the on-screen software keyboard to apply the
211    /// sentence-start auto-capitalize heuristic: an empty field (or one
212    /// ending in `.`, `!`, `?`, newline) opens the keyboard with Shift
213    /// active.
214    ///
215    /// Default is `None`. `TextField` and `TextArea` override to return
216    /// their current text. Callers that only need to know *whether* the
217    /// widget accepts text input should use
218    /// [`accepts_text_input`](Self::accepts_text_input).
219    fn text_input_value(&self) -> Option<String> {
220        None
221    }
222
223    /// Preferred keyboard input mode for this widget — used by the
224    /// on-screen software keyboard to pick the initial layer when this
225    /// widget gains focus.  Default is
226    /// [`KeyboardInputMode::Text`](crate::widgets::on_screen_keyboard::KeyboardInputMode::Text);
227    /// numeric fields override to
228    /// [`Numeric`](crate::widgets::on_screen_keyboard::KeyboardInputMode::Numeric)
229    /// so the digit pad slides up instead of the letter row.
230    ///
231    /// Only consulted when [`accepts_text_input`](Self::accepts_text_input)
232    /// also returns `true`.
233    fn text_input_mode(&self) -> crate::widgets::on_screen_keyboard::KeyboardInputMode {
234        crate::widgets::on_screen_keyboard::KeyboardInputMode::Text
235    }
236
237    /// Try to lift this widget's visible content upward by `amount`
238    /// pixels.  Used by the on-screen-keyboard auto-scroll so a
239    /// focused text field doesn't end up hidden behind the keyboard
240    /// panel: the App walks UP the focus path and asks each ancestor
241    /// to absorb some of the deficit.
242    ///
243    /// Scrolling containers (notably
244    /// [`ScrollView`](crate::widgets::ScrollView)) override this and
245    /// increase their vertical scroll offset by up to `amount`,
246    /// returning how much they actually applied (clamped to their
247    /// remaining slack).  Negative `amount` reverses the operation
248    /// (used to restore scroll when focus leaves a text-input).
249    /// Default returns `0.0` — non-scrolling widgets contribute
250    /// nothing.
251    fn try_scroll_to_lift(&mut self, _amount: f64) -> f64 {
252        0.0
253    }
254
255    /// If this widget is text-bearing (e.g. `Label`), update its foreground
256    /// colour.  Default is a no-op.  Composite widgets call this on their
257    /// children to retint labels without rebuilding them — used by `Button`
258    /// when toggling between active (white text on accent) and inactive
259    /// (theme text on subtle bg) appearances.
260    fn set_label_color(&mut self, _color: crate::color::Color) {}
261
262    /// If this widget is text-bearing (e.g. `Label`), update its
263    /// displayed text.  Default is a no-op.  Composite widgets that
264    /// own a `Label` child use this to push live values (e.g. an FPS
265    /// counter) into the child without bypassing the standard
266    /// backbuffered glyph cache — calling this on a `Label` only
267    /// invalidates the cache when the text actually changed.
268    fn set_label_text(&mut self, _text: &str) {}
269
270    /// Opt-in reflection accessor for the inspector's typed property editors.
271    ///
272    /// Widgets that derive [`bevy_reflect::Reflect`] (via the `reflect`
273    /// cargo feature) override this to return `Some(self)` so the inspector
274    /// can walk their fields with type information — boolean toggles,
275    /// numeric sliders, color pickers, enum dropdowns — instead of falling
276    /// back to the read-only string [`properties`](Self::properties) list.
277    ///
278    /// Default returns `None`; the inspector then uses the string list.
279    /// Available only with the `reflect` feature so consumers without it
280    /// don't pay the dependency cost.
281    #[cfg(feature = "reflect")]
282    fn as_reflect(&self) -> Option<&dyn bevy_reflect::Reflect> {
283        None
284    }
285
286    /// Mutable counterpart of [`as_reflect`](Self::as_reflect).  Used by the
287    /// inspector to write edits back into the live widget.
288    #[cfg(feature = "reflect")]
289    fn as_reflect_mut(&mut self) -> Option<&mut dyn bevy_reflect::Reflect> {
290        None
291    }
292
293    /// Whether this widget renders into its own offscreen buffer before
294    /// compositing into the parent.
295    ///
296    /// When `true`, `paint_subtree` wraps the widget (and all its descendants)
297    /// in `ctx.push_layer` / `ctx.pop_layer`.  The widget and its children draw
298    /// into a fresh transparent framebuffer; when complete, the buffer is
299    /// SrcOver-composited back into the parent render target.  This enables
300    /// per-widget alpha compositing, caching, and isolation.
301    ///
302    /// Default: `false` (pass-through rendering).
303    fn has_backbuffer(&self) -> bool {
304        false
305    }
306
307    /// Request that this widget subtree be painted into a transient
308    /// transparent compositing layer before being blended into its parent.
309    ///
310    /// Renderers that do not implement real layers ignore this hook. The
311    /// method is mutable so widgets can advance visibility tweens at the
312    /// point where the traversal knows the layer will be painted.
313    fn compositing_layer(&mut self) -> Option<CompositingLayer> {
314        None
315    }
316
317    /// Unified widget-owned backbuffer request.
318    fn backbuffer_spec(&mut self) -> BackbufferSpec {
319        let mode = self.backbuffer_mode();
320        if self.backbuffer_cache_mut().is_some() {
321            BackbufferSpec {
322                kind: match mode {
323                    BackbufferMode::Rgba => BackbufferKind::SoftwareRgba,
324                    BackbufferMode::LcdCoverage => BackbufferKind::SoftwareLcd,
325                },
326                cached: true,
327                alpha: 1.0,
328                outsets: Insets::ZERO,
329                rounded_clip: None,
330            }
331        } else {
332            BackbufferSpec::none()
333        }
334    }
335
336    /// Mutable retained backbuffer state for widgets that request a
337    /// [`BackbufferSpec`] other than [`BackbufferKind::None`].
338    fn backbuffer_state_mut(&mut self) -> Option<&mut BackbufferState> {
339        None
340    }
341
342    /// Mark this widget's own retained surface dirty, if it owns one.
343    ///
344    /// Invalidates *both* the retained-layer [`BackbufferState`] and the
345    /// per-widget bitmap [`BackbufferCache`].  Inspector edits that mutate
346    /// a widget's reflected props bypass setters that normally invalidate
347    /// the cache (e.g. `Label::set_text`); calling `mark_dirty` after an
348    /// edit restores correct re-raster on the next frame.
349    fn mark_dirty(&mut self) {
350        if let Some(state) = self.backbuffer_state_mut() {
351            state.invalidate();
352        }
353        if let Some(cache) = self.backbuffer_cache_mut() {
354            cache.invalidate();
355        }
356    }
357
358    /// Opt into per-widget CPU bitmap caching with a dirty flag.
359    ///
360    /// Widgets that return `Some(&mut cache)` get their paint +
361    /// children cached as a `Vec<u8>` of RGBA8 pixels.  `paint_subtree`
362    /// re-rasterises via AGG only when `cache.dirty` is true; otherwise
363    /// it blits the existing bitmap.  GL backends key their texture
364    /// cache on the `Arc`'s pointer identity so the uploaded GPU
365    /// texture is also reused across frames.
366    ///
367    /// The widget is responsible for calling `cache.invalidate()` (or
368    /// setting `cache.dirty = true`) from any mutation that could
369    /// change the rendered output — text/color setters, focus/hover
370    /// state changes, layout size changes, etc.  The framework clears
371    /// the flag after a successful re-raster.
372    ///
373    /// Default: `None` (no caching — paint every frame directly).
374    fn backbuffer_cache_mut(&mut self) -> Option<&mut BackbufferCache> {
375        None
376    }
377
378    /// Opt into over-scan band caching for a *scrolling* backbuffered widget.
379    ///
380    /// When this returns `Some`, `paint_subtree_backbuffered` rasters a band
381    /// taller than the widget bounds (viewport + over-scan) and composites it at
382    /// a blit offset, so scrolling within the band re-blits instead of
383    /// re-rasterising. See [`BackbufferBand`] for the full contract (units,
384    /// physical-pixel quantization, bounds clipping, invalidation).
385    ///
386    /// Only consulted when [`backbuffer_cache_mut`](Self::backbuffer_cache_mut)
387    /// returns `Some`. Default `None` keeps the byte-identical bounds-sized
388    /// raster + 1:1 blit path for every other widget.
389    fn backbuffer_band(&self) -> Option<BackbufferBand> {
390        None
391    }
392
393    /// Storage format for this widget's backbuffer.  Ignored unless
394    /// [`backbuffer_cache_mut`] returns `Some`.  Default
395    /// [`BackbufferMode::Rgba`] — correct for any widget.
396    /// Opt into [`BackbufferMode::LcdCoverage`] only when the widget
397    /// paints opaque content covering its full bounds.
398    fn backbuffer_mode(&self) -> BackbufferMode {
399        BackbufferMode::Rgba
400    }
401
402    /// Whether the inspector should recurse into this widget's children.
403    ///
404    /// Returns `false` for widgets that are part of the inspector infrastructure
405    /// (e.g. the inspector's own `TreeView`) to prevent the inspector from
406    /// showing itself recursively, which would grow the node list every frame.
407    ///
408    /// The widget itself is still included in the inspector snapshot — only
409    /// its subtree is suppressed.
410    fn contributes_children_to_inspector(&self) -> bool {
411        true
412    }
413
414    /// Return `false` to hide this widget (and its subtree) from the inspector
415    /// node snapshot entirely.  Intended for zero-size utility widgets such
416    /// as layout-time watchers / tickers / invisible composers — they bloat
417    /// the inspector tree without providing user-relevant information and,
418    /// at scale, can make the inspector's per-frame tree rebuild expensive.
419    fn show_in_inspector(&self) -> bool {
420        true
421    }
422
423    /// Per-widget LCD subpixel preference for backbuffered text rendering.
424    ///
425    /// - `Some(true)`  — always raster text with LCD subpixel.
426    /// - `Some(false)` — always use grayscale AA.
427    /// - `None`        — defer to the global `font_settings::lcd_enabled()`.
428    ///
429    /// Only widgets that raster text into an offscreen backbuffer act on
430    /// this flag (today: `Label`).  Defaulting to `None` means every such
431    /// widget follows the global toggle unless the instance explicitly
432    /// opts in or out.
433    fn lcd_preference(&self) -> Option<bool> {
434        None
435    }
436
437    /// Paint decorations that must appear **on top of all children**.
438    ///
439    /// Called by [`paint_subtree`] after all children have been painted.
440    /// The default implementation is a no-op; override in widgets that need
441    /// to draw overlays (e.g. resize handles, drag previews) that must not
442    /// be occluded by child content.
443    fn paint_overlay(&mut self, _ctx: &mut dyn DrawCtx) {}
444
445    /// Called after `paint`, child painting, and optional overlay painting.
446    ///
447    /// Most widgets do not need this. It exists for widgets that intentionally
448    /// open a backend compositing scope in `paint` and must close it after all
449    /// descendants have rendered into that scope.
450    fn finish_paint(&mut self, _ctx: &mut dyn DrawCtx) {}
451
452    /// Paint app-level overlays after the entire widget tree has been painted.
453    ///
454    /// The traversal preserves this widget's local transform but skips ancestor
455    /// clips and retained parent redraw requirements. Use this for portal-style
456    /// UI that draws outside normal bounds while still participating in the
457    /// widget tree's Z order.
458    fn paint_global_overlay(&mut self, _ctx: &mut dyn DrawCtx) {}
459
460    /// Opt this widget's *entire subtree* out of the normal inline paint pass so
461    /// it renders in [`paint_global_overlay`](Self::paint_global_overlay)
462    /// instead — the same clip-escaping global pass used by menus and tooltips.
463    ///
464    /// When this returns `true` and the widget is visible, [`paint_subtree`]
465    /// skips it during the ordinary tree walk (nothing, including children, is
466    /// painted inline), and the widget is expected to paint itself in its
467    /// `paint_global_overlay` via [`paint_subtree_forced`]. Because the global
468    /// overlay walk applies only per-widget translations (never ancestor
469    /// clips), the subtree then floats above and outside any ancestor's clip —
470    /// which is exactly what a modal dialog nested inside a scrolled/clipped
471    /// container needs so it can't be truncated by its host.
472    ///
473    /// Default `false`: ordinary widgets paint inline as usual.
474    ///
475    /// # Z-order caveat for deferred hosts
476    ///
477    /// The global-overlay walk is post-order (a parent's
478    /// `paint_global_overlay` runs *after* its descendants'), so a deferred
479    /// widget's body — painted in its own `paint_global_overlay` — draws on top
480    /// of anything a descendant painted *directly* in an earlier
481    /// `paint_global_overlay` (e.g. a `menu`/`PopupMenu` surface, the markdown
482    /// link layer, or a nested modal). Descendants that instead submit to a
483    /// drained queue (`ComboBox` popups, `Tooltip`) are unaffected — those
484    /// queues drain after the whole overlay walk. Today the colour dialog's
485    /// content uses only queue-based descendants, so it's safe; a future modal
486    /// host embedding a direct-overlay child must account for this occlusion.
487    fn defer_paint_to_overlay(&self) -> bool {
488        false
489    }
490
491    /// Return a clip rectangle (in local coordinates) that constrains all child
492    /// painting.  `paint_subtree` applies this clip before recursing into
493    /// children, then restores the previous clip state afterward.  The clip does
494    /// **not** affect `paint_overlay`, which runs after the clip is removed.
495    ///
496    /// The default clips children to this widget's own bounds, preventing
497    /// overflow.  Override to return a narrower rect (e.g. Window clips to the
498    /// content area below the title bar, or an empty rect when collapsed).
499    fn clip_children_rect(&self) -> Option<(f64, f64, f64, f64)> {
500        let b = self.bounds();
501        Some((0.0, 0.0, b.width, b.height))
502    }
503
504    /// Affine transform applied between this widget and its children during
505    /// inspector traversal.  Mirrors what `paint()` does — e.g. a widget
506    /// that pushes pan/zoom in `paint()` and pops it in `finish_paint()`
507    /// makes the framework recurse into its children with pan/zoom active,
508    /// so `collect_inspector_nodes` must apply the same transform when
509    /// accumulating descendant screen bounds.  Without this hook the
510    /// inspector hover overlay lands at the un-transformed canvas position
511    /// when the widget sits inside a panning/zooming container.
512    ///
513    /// Default: identity (most widgets translate their children only
514    /// through `child.bounds()`, which `collect_inspector_nodes` already
515    /// accumulates separately).
516    ///
517    /// Defaults to [`child_transform`](Self::child_transform) so a widget that
518    /// injects a real pan/zoom into pointer + paint (a [`Scene`]) is seen by the
519    /// inspector at the same on-screen position without having to implement two
520    /// hooks.  Widgets that need an inspector-only transform (rare) override
521    /// this directly.
522    fn inspector_child_transform(&self) -> crate::TransAffine {
523        self.child_transform().unwrap_or_else(crate::TransAffine::new)
524    }
525
526    /// Affine transform (child-local → this widget's local space) that the
527    /// framework applies to **all** of this widget's children for painting,
528    /// hit-testing, and event dispatch alike.
529    ///
530    /// This is the "scale hook" that lets a container magnify/pan its whole
531    /// child subtree (a [`Scene`]) while keeping those children fully
532    /// first-class: because they live in [`children`](Self::children), keyboard
533    /// focus (Tab + click-to-focus), the inspector, and pointer input all reach
534    /// them through the framework's normal traversals, which map coordinates
535    /// through this transform when descending.
536    ///
537    /// The transform is applied to the child group as a whole; per-child
538    /// `bounds()` offsets are interpreted **inside** the transform (i.e. in the
539    /// child/scene space), so a container that pins its content at the origin
540    /// (the common case) needs no further care.  Pointer traversals invert this
541    /// transform to map an incoming screen-local point into child space.
542    ///
543    /// Default: `None` — children are positioned by `bounds()` alone.
544    ///
545    /// [`Scene`]: crate::widgets::Scene
546    fn child_transform(&self) -> Option<crate::TransAffine> {
547        None
548    }
549
550    // -------------------------------------------------------------------------
551    // Layout properties (universal — every widget carries these)
552    // -------------------------------------------------------------------------
553
554    /// Outer margin around this widget in logical units.
555    ///
556    /// The parent layout reads this to compute spacing and position.
557    /// Default: [`Insets::ZERO`].
558    fn margin(&self) -> Insets {
559        Insets::ZERO
560    }
561
562    /// Inner padding — space the widget reserves between its own bounds and
563    /// its child layout area.  Only container widgets carry padding; leaf
564    /// widgets default to [`Insets::ZERO`].
565    ///
566    /// The inspector reads this to draw a Chrome F12-style padding band on
567    /// the highlighted widget.  Containers that already store padding
568    /// internally (e.g. `FlexColumn::inner_padding`) override this to expose
569    /// it to the inspector.
570    fn padding(&self) -> Insets {
571        Insets::ZERO
572    }
573
574    /// Horizontal anchor: how this widget sizes/positions itself horizontally
575    /// within the slot the parent assigns.
576    /// Default: [`HAnchor::FIT`] (take natural content width).
577    fn h_anchor(&self) -> HAnchor {
578        HAnchor::FIT
579    }
580
581    /// Vertical anchor: how this widget sizes/positions itself vertically
582    /// within the slot the parent assigns.
583    /// Default: [`VAnchor::FIT`] (take natural content height).
584    fn v_anchor(&self) -> VAnchor {
585        VAnchor::FIT
586    }
587
588    /// Minimum size constraint (logical units).
589    ///
590    /// The parent will never assign a slot smaller than this.
591    /// Default: [`Size::ZERO`] (no minimum).
592    fn min_size(&self) -> Size {
593        Size::ZERO
594    }
595
596    /// Maximum size constraint (logical units).
597    ///
598    /// The parent will never assign a slot larger than this.
599    /// Default: [`Size::MAX`] (no maximum).
600    fn max_size(&self) -> Size {
601        Size::MAX
602    }
603
604    /// Direct read access to the widget's embedded [`WidgetBase`].
605    ///
606    /// Returns `Some` for every widget that embeds `WidgetBase` — effectively
607    /// all concrete widgets.  The inspector uses this to read margin, anchors,
608    /// and size constraints without going through the individual trait methods.
609    /// Default returns `None`; widgets that embed `WidgetBase` override.
610    fn widget_base(&self) -> Option<&WidgetBase> {
611        None
612    }
613
614    /// Mutable counterpart of [`widget_base`](Self::widget_base).
615    ///
616    /// The inspector calls this to apply live edits to margin, h_anchor,
617    /// v_anchor, min_size, and max_size from the properties pane.
618    fn widget_base_mut(&mut self) -> Option<&mut WidgetBase> {
619        None
620    }
621
622    /// Hover-help text for the central tooltip controller, or `None`.
623    ///
624    /// This is the universal accessor the App's per-frame tooltip pass reads to
625    /// find the deepest hovered tipped widget. The default reads the embedded
626    /// [`WidgetBase::tooltip`], so **every widget that embeds a `WidgetBase`
627    /// participates for free** — set the text with
628    /// [`with_tooltip`](Self::with_tooltip) (chaining) or
629    /// [`set_tooltip_text`](Self::set_tooltip_text) (on a `&mut dyn Widget`).
630    /// A widget with no `WidgetBase`, or one that stores its tip elsewhere,
631    /// overrides this directly.
632    fn tooltip_text(&self) -> Option<&str> {
633        self.widget_base().and_then(|b| b.tooltip.as_deref())
634    }
635
636    /// Builder sugar: attach hover-help text, returning `self` for chaining.
637    ///
638    /// Available on every widget that embeds a [`WidgetBase`] with **zero
639    /// per-widget code** — it writes through [`widget_base_mut`](Self::widget_base_mut).
640    /// A widget without a `WidgetBase` silently ignores the call (there is
641    /// nowhere to store the text); such widgets should expose their own tip API.
642    /// Excluded from the trait object vtable via `where Self: Sized`, so the
643    /// trait stays object-safe.
644    fn with_tooltip(mut self, text: impl Into<String>) -> Self
645    where
646        Self: Sized,
647    {
648        if let Some(base) = self.widget_base_mut() {
649            base.tooltip = Some(text.into());
650        }
651        self
652    }
653
654    /// Object-safe setter counterpart of [`with_tooltip`](Self::with_tooltip),
655    /// callable on a `&mut dyn Widget` / `Box<dyn Widget>` (used by builders
656    /// that assemble a control tree from boxed widgets, e.g. toolbars). Writes
657    /// through [`widget_base_mut`](Self::widget_base_mut); a no-op for widgets
658    /// without a `WidgetBase`. Pass `None` to clear.
659    fn set_tooltip_text(&mut self, text: Option<String>) {
660        if let Some(base) = self.widget_base_mut() {
661            base.tooltip = text;
662        }
663    }
664
665    /// Whether [`paint_subtree`] should snap this widget's incoming
666    /// translation to the physical pixel grid.
667    ///
668    /// Defaults to the process-wide
669    /// [`pixel_bounds::default_enforce_integer_bounds`](crate::pixel_bounds::default_enforce_integer_bounds)
670    /// flag so the common case — crisp UI text + strokes — works without
671    /// ceremony.  Widgets with a [`WidgetBase`] should delegate to
672    /// `self.base().enforce_integer_bounds` so per-instance overrides take
673    /// effect; widgets that genuinely want sub-pixel positioning (smooth
674    /// scroll markers, zoomed canvases) override to return `false`.
675    ///
676    /// Mirrors MatterCAD's `GuiWidget.EnforceIntegerBounds` accessor.
677    fn enforce_integer_bounds(&self) -> bool {
678        crate::pixel_bounds::default_enforce_integer_bounds()
679    }
680
681    /// Report the minimum height this widget needs to fully render
682    /// its content when given the supplied `available_w` for width.
683    ///
684    /// Used by parents whose layout strategy depends on a true
685    /// content-required height that's independent of the slot they
686    /// might hand the widget — most importantly by
687    /// `Window::with_tight_content_fit(true)` to enforce "no
688    /// clipping, no whitespace" on the height axis even when the
689    /// content tree contains a flex-fill widget that would
690    /// otherwise return `available.height` from `layout`.
691    ///
692    /// Default returns `min_size().height` — accurate for widgets
693    /// whose minimum doesn't depend on width.  Width-sensitive
694    /// widgets (wrapped text containers like `TextArea`, recursive
695    /// containers like `FlexColumn`) override and compute properly.
696    fn measure_min_height(&self, _available_w: f64) -> f64 {
697        self.min_size().height
698    }
699
700    /// Container widgets (notably [`crate::widgets::Stack`]) call this on each
701    /// child at the start of `layout()`.  A widget that returns `true` is
702    /// moved to the END of its parent's child list — painted last, i.e.
703    /// raised to the top of the z-order.  `take_` semantics: the call is
704    /// also expected to **clear** the request so the child doesn't keep
705    /// getting raised every frame.
706    ///
707    /// Default: no raise ever requested.  `Window` overrides to fire on the
708    /// false→true visibility transition (see its `with_visible_cell`), so
709    /// toggling a demo checkbox on in the sidebar automatically pops that
710    /// window to the front.
711    fn take_raise_request(&mut self) -> bool {
712        false
713    }
714
715    // -------------------------------------------------------------------------
716    // Visibility-gated scheduled draw propagation
717    // -------------------------------------------------------------------------
718    //
719    // The host render loop walks the widget tree from the root to decide
720    // whether a visible subtree has a scheduled draw need such as cursor blink.
721    // Ordinary visual invalidation should call `animation::request_draw`, which
722    // also advances the retained-layer invalidation epoch.  `needs_draw` stays
723    // for visibility-gated future/ongoing draw needs: invisible subtrees
724    // (collapsed Window, non-selected TabView tab, off-viewport content)
725    // must NOT keep the app in a continuous draw loop.
726
727    /// Return `true` if this widget, or any visible descendant, has an ongoing
728    /// draw need that should keep the host drawing.
729    ///
730    /// The default walks visible children.  Widgets with their own pending
731    /// state OR that state with the default walk — see `WidgetBase` helpers.
732    fn needs_draw(&self) -> bool {
733        if !self.is_visible() {
734            return false;
735        }
736        self.children().iter().any(|c| c.needs_draw())
737    }
738
739    /// Return the earliest wall-clock instant at which this widget (or any
740    /// visible descendant) wants the next draw.  `None` = no scheduled wake.
741    /// The host loop turns a `Some(t)` into `ControlFlow::WaitUntil(t)` so
742    /// e.g. a cursor blink fires without continuous polling.
743    ///
744    /// Same visibility contract as [`needs_draw`]: hidden subtrees return
745    /// `None` regardless of what the widget *would* ask for if shown.
746    fn next_draw_deadline(&self) -> Option<web_time::Instant> {
747        if !self.is_visible() {
748            return None;
749        }
750        let mut best: Option<web_time::Instant> = None;
751        for c in self.children() {
752            if let Some(t) = c.next_draw_deadline() {
753                best = Some(match best {
754                    Some(b) if b <= t => b,
755                    _ => t,
756                });
757            }
758        }
759        best
760    }
761}
762
763mod app;
764mod backbuffer;
765pub(crate) mod keyboard_scroll;
766mod paint;
767pub(crate) mod paint_timing;
768mod tree;
769mod tree_inspector;
770
771pub use app::App;
772pub use backbuffer::{
773    BackbufferBand, BackbufferCache, BackbufferKind, BackbufferMode, BackbufferSpec,
774    BackbufferState, CompositingLayer,
775};
776pub(crate) use backbuffer::next_content_version;
777pub use paint::{current_paint_clip, paint_global_overlays, paint_subtree};
778pub(crate) use paint::paint_subtree_forced;
779pub use tree::{
780    active_modal_path, dispatch_event, dispatch_event_broadcast, dispatch_event_dyn,
781    dispatch_unconsumed_key, global_overlay_hit_path, hit_test_subtree, mark_subtree_dirty,
782};
783#[cfg(feature = "reflect")]
784pub use tree_inspector::{apply_inspector_edit, reflect_fields, InspectorEdit};
785pub use tree_inspector::{
786    apply_widget_base_edit, collect_inspector_nodes, current_mouse_world, current_viewport,
787    debug_draw_report, find_widget_by_id, find_widget_by_id_mut, find_widget_by_type,
788    set_current_mouse_world,
789    set_current_viewport, walk_path_mut, InspectorNode, InspectorOverlay, WidgetBaseEdit,
790    WidgetBaseField,
791};