Skip to main content

zest_core/
widget.rs

1//! Widget trait and [`Element`] wrapper.
2
3use alloc::vec::Vec;
4
5/// Heterogeneous boxed widget.
6pub mod element;
7
8pub use element::{Element, IntoElement};
9
10use crate::{Constraints, Length, RenderError, Renderer, TouchPhase, UiAction, WidgetId};
11use embedded_graphics::{pixelcolor::PixelColor, prelude::*, primitives::Rectangle};
12use zest_theme::Theme;
13
14/// Object-safe widget contract.
15pub trait Widget<C: PixelColor, M: Clone> {
16    /// Report the size this widget wants within `constraints`.
17    fn measure(&mut self, constraints: Constraints) -> Size;
18
19    /// Per-axis sizing intent set via `.width(...)` / `.height(...)`.
20    /// Containers consult this during layout to allocate fixed slots,
21    /// query intrinsic sizes, and split residual space among
22    /// `Fill` / `FillPortion` children. Default is `Length::Fill` on
23    /// both axes.
24    fn preferred_size(&self) -> (Length, Length) {
25        (Length::Fill, Length::Fill)
26    }
27
28    /// Commit the final rectangle, recursively arranging any children.
29    fn arrange(&mut self, rect: Rectangle);
30
31    /// The current arranged rectangle.
32    fn rect(&self) -> Rectangle;
33
34    /// Dispatch a touch event. Returns a message if consumed.
35    fn handle_touch(&mut self, point: Point, phase: TouchPhase) -> Option<M>;
36
37    /// Set this widget's pressed flag if `point` hits. No message emit.
38    /// Containers forward to children. Default no-op.
39    fn mark_pressed(&mut self, point: Point) {
40        let _ = point;
41    }
42
43    /// Stable id of this widget, if it participates in focus.
44    fn widget_id(&self) -> Option<WidgetId> {
45        None
46    }
47
48    /// True if this widget should appear in focus traversal.
49    fn is_focusable(&self) -> bool {
50        false
51    }
52
53    /// Append focusable widget ids in traversal order.
54    fn collect_focusable(&self, out: &mut Vec<WidgetId>) {
55        if self.is_focusable()
56            && let Some(id) = self.widget_id()
57        {
58            out.push(id);
59        }
60    }
61
62    /// Synchronize focused state against the runtime's currently-focused id.
63    fn sync_focus(&mut self, focused: Option<WidgetId>) {
64        let _ = focused;
65    }
66
67    /// Handle a semantic action directed at this widget.
68    fn handle_action(&mut self, action: UiAction) -> Option<M> {
69        let _ = action;
70        None
71    }
72
73    /// Route a semantic action to the widget with id `target`.
74    fn route_action(&mut self, target: WidgetId, action: UiAction) -> Option<M> {
75        if self.widget_id() == Some(target) {
76            self.handle_action(action)
77        } else {
78            None
79        }
80    }
81
82    /// Request a focus movement relative to `target` for directional actions.
83    fn navigate_focus(&self, target: WidgetId, action: UiAction) -> Option<WidgetId> {
84        let _ = (target, action);
85        None
86    }
87
88    /// Rectangle currently occupied by the focusable target, if any.
89    fn focus_rect(&self, target: WidgetId) -> Option<Rectangle> {
90        if self.widget_id() == Some(target) {
91            Some(self.rect())
92        } else {
93            None
94        }
95    }
96
97    /// Focusable widget id at `point`, if any.
98    fn focus_at(&self, point: Point) -> Option<WidgetId> {
99        let _ = point;
100        None
101    }
102
103    /// Paint into `renderer` at [`rect`](Self::rect).
104    fn draw<'t>(
105        &self,
106        renderer: &mut dyn Renderer<C>,
107        theme: &Theme<'t, C>,
108    ) -> Result<(), RenderError>;
109}