Skip to main content

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}