densa_ui/widget.rs
1use std::any::Any;
2
3use densa_core::Size;
4use densa_draw::Canvas;
5
6use crate::{AccessNode, AccessRequest, Event, EventCx, LayoutCx, PaintCx, WidgetId};
7
8/// A part of the interface: it can measure itself, paint, and handle events.
9///
10/// A [`Ui`](crate::Ui) owns its widgets in a tree; their places come from each widget's
11/// [`Style`](crate::Style) and, for widgets without children, from
12/// [`measure`](Self::measure). Painting and event handling run every frame, so they must
13/// not allocate once a widget's content is set up; measuring and
14/// [`layout`](Self::layout) run only when sizes change and may.
15///
16/// Every method has a default: a plain container needs to implement nothing.
17pub trait Widget: Any {
18 /// The size the widget wants within `constraints`, for a widget without children (one
19 /// with children is sized by its style and its children). Physical pixels.
20 fn measure(&mut self, cx: &mut LayoutCx<'_>, constraints: Constraints) -> Size {
21 let _ = (cx, constraints);
22 Size::ZERO
23 }
24
25 /// Layout decided the widget's size: re-wrap text or recompute anything that depends on
26 /// it. Called after every layout pass that changed the size.
27 fn layout(&mut self, cx: &mut LayoutCx<'_>, size: Size) {
28 let _ = (cx, size);
29 }
30
31 /// Paints the widget below its children.
32 fn paint(&mut self, cx: &PaintCx<'_>, canvas: &mut Canvas<'_>) {
33 let _ = (cx, canvas);
34 }
35
36 /// Paints the widget over its children: scroll bars, focus rings, overlays.
37 fn paint_over(&mut self, cx: &PaintCx<'_>, canvas: &mut Canvas<'_>) {
38 let _ = (cx, canvas);
39 }
40
41 /// Handles `event`. An event the widget does not handle goes on to its parent.
42 fn event(&mut self, cx: &mut EventCx<'_>, event: &Event<'_>) -> Handled {
43 let _ = (cx, event);
44 Handled::No
45 }
46
47 /// Whether the widget takes keyboard focus, by click or by Tab.
48 fn accepts_focus(&self) -> bool {
49 false
50 }
51
52 /// Whether a press on the widget, or on a widget inside it that takes no focus, focuses
53 /// it (if it [accepts focus](Self::accepts_focus)). A container reachable by Tab, such
54 /// as a dock's tab list, says no, so clicks on its contents do not focus it; it can still
55 /// ask for focus itself.
56 fn focus_on_press(&self) -> bool {
57 self.accepts_focus()
58 }
59
60 /// Describes the widget to assistive technology: fills `node`, which starts as a
61 /// [`Role::Group`](crate::Role::Group) with the widget's rectangle. Called only while
62 /// assistive technology is listening; it may allocate.
63 fn accessibility(&self, cx: &PaintCx<'_>, node: &mut AccessNode) {
64 let _ = (cx, node);
65 }
66
67 /// Does what assistive technology asks (only actions the widget's node lists are asked
68 /// of it). Focus and scrolling into view are done for every widget; a request the widget
69 /// does not handle goes on to its parent if it is a scroll.
70 fn access_action(&mut self, cx: &mut EventCx<'_>, request: &AccessRequest<'_>) -> Handled {
71 let _ = (cx, request);
72 Handled::No
73 }
74}
75
76/// What layout asks a widget in [`Widget::measure`].
77#[derive(Debug, Clone, Copy, PartialEq)]
78#[non_exhaustive]
79pub struct Constraints {
80 /// A width layout already fixed: the answer's width must be this.
81 pub width: Option<f32>,
82 /// A height layout already fixed: the answer's height must be this.
83 pub height: Option<f32>,
84 /// Room for the width.
85 pub available_width: Available,
86 /// Room for the height.
87 pub available_height: Available,
88}
89
90/// Room along one axis.
91#[derive(Debug, Clone, Copy, PartialEq)]
92#[non_exhaustive]
93pub enum Available {
94 /// This many physical pixels.
95 Definite(f32),
96 /// As little as possible: the narrowest the content can be (text wrapped at every
97 /// opportunity).
98 MinContent,
99 /// As much as the content wants (text on one line).
100 MaxContent,
101}
102
103impl Available {
104 /// The room as a limit, `None` for the content's own size.
105 pub fn limit(self) -> Option<f32> {
106 match self {
107 Self::Definite(room) => Some(room),
108 Self::MinContent | Self::MaxContent => None,
109 }
110 }
111}
112
113/// Whether a widget handled an event; one it did not goes on to the widget's parent.
114#[derive(Debug, Clone, Copy, PartialEq, Eq)]
115pub enum Handled {
116 /// Handled: the event stops here.
117 Yes,
118 /// Not handled: the parent gets it.
119 No,
120}
121
122impl From<bool> for Handled {
123 fn from(handled: bool) -> Self {
124 if handled {
125 Self::Yes
126 } else {
127 Self::No
128 }
129 }
130}
131
132/// Something a widget reports to the application: a button was clicked, a text changed.
133/// Read them with [`Ui::actions`](crate::Ui::actions) after delivering events.
134#[derive(Debug, Clone, Copy, PartialEq)]
135pub struct Action {
136 /// The widget that reported it.
137 pub source: WidgetId,
138 /// What happened.
139 pub kind: ActionKind,
140}
141
142/// What a widget reports. The details are on the widget: read them through
143/// [`Ui::widget`](crate::Ui::widget).
144#[derive(Debug, Clone, Copy, PartialEq)]
145#[non_exhaustive]
146pub enum ActionKind {
147 /// Clicked or activated with the keyboard.
148 Clicked,
149 /// Switched on (`true`) or off.
150 Toggled(bool),
151 /// Its value or text changed.
152 Changed,
153 /// Its value or text was confirmed (Enter in a text input).
154 Submitted,
155 /// An item was selected, by index.
156 Selected(usize),
157 /// A command was chosen (a menu item), by the number the application gave it.
158 Command(u32),
159 /// The user asked for a context menu on the widget: a secondary press at `at`, or the
160 /// context menu key (or Shift+F10) while it has focus, `at` then its top-left corner.
161 /// The widget is the one that handled the press, else the one under the pointer.
162 ContextMenu {
163 /// Where, in physical pixels from the window's top-left corner.
164 at: densa_core::Point,
165 },
166}