Skip to main content

densa_ui/
ui.rs

1use std::any::Any;
2use std::collections::VecDeque;
3use std::fmt;
4use std::time::Duration;
5
6use densa_core::{Point, Rect, Size};
7use densa_draw::Canvas;
8use densa_paragraph::TextSystem;
9use taffy::style::AvailableSpace;
10use taffy::{TaffyResult, TaffyTree};
11
12use crate::clipboard::{Clipboard, Held};
13use crate::popup::{Incoming, Popup};
14use crate::{
15    Action, Available, Constraints, Cursor, EventCx, LayoutCx, Overflow, PaintCx, PointerButton,
16    Style, Theme, Widget, WidgetId,
17};
18
19/// Actions held without allocating between reads.
20const ACTION_CAPACITY: usize = 64;
21
22/// Why a [`Ui`] refused a change.
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24#[non_exhaustive]
25pub enum UiError {
26    /// The widget was removed, or belongs to another `Ui`.
27    NoSuchWidget(WidgetId),
28    /// More than 2^32 widgets.
29    TooManyWidgets,
30}
31
32impl fmt::Display for UiError {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        match self {
35            Self::NoSuchWidget(id) => write!(f, "there is no widget {id:?}"),
36            Self::TooManyWidgets => f.write_str("more than 2^32 widgets"),
37        }
38    }
39}
40
41impl std::error::Error for UiError {}
42
43/// A widget's place in the tree and its layout.
44#[derive(Debug)]
45pub(crate) struct Node {
46    pub(crate) generation: u32,
47    /// Its number for assistive technology: never reused, unlike its slot.
48    pub(crate) access: u64,
49    pub(crate) live: bool,
50    pub(crate) parent: Option<WidgetId>,
51    pub(crate) children: Vec<WidgetId>,
52    pub(crate) taffy: taffy::NodeId,
53    pub(crate) style: Style,
54    /// Where it is, in physical pixels from the window's top-left corner.
55    pub(crate) rect: Rect,
56    /// `rect` inside the padding.
57    pub(crate) content: Rect,
58    /// What its children cover.
59    pub(crate) content_size: Size,
60    /// How far its children are scrolled.
61    pub(crate) scroll: Point,
62    /// Whether the pointer is over it or one of its children.
63    pub(crate) hovered: bool,
64    pub(crate) cursor: Option<Cursor>,
65    /// `rect` grown to cover children that overflow it: what painting it can touch.
66    pub(crate) extent: Rect,
67    /// Its style or content changed since `Widget::layout` was last called.
68    pub(crate) dirty: bool,
69    /// The size `Widget::layout` was last called with.
70    pub(crate) notified: Option<Size>,
71}
72
73/// A press remembered to count multi-clicks.
74#[derive(Debug, Clone, Copy)]
75pub(crate) struct Press {
76    pub(crate) time: Duration,
77    pub(crate) position: Point,
78    pub(crate) button: PointerButton,
79    pub(crate) clicks: u8,
80}
81
82/// Everything but the widgets: what contexts borrow while a widget runs.
83#[derive(Debug)]
84pub(crate) struct State {
85    pub(crate) nodes: Vec<Node>,
86    free: Vec<u32>,
87    /// The last widget serial given out (the root has 0).
88    pub(crate) next_access: u64,
89    taffy: TaffyTree<WidgetId>,
90    pub(crate) root: WidgetId,
91    /// The popup layer: a second root over the first, laid out against the viewport.
92    pub(crate) layer: WidgetId,
93    /// Open popups, oldest first.
94    pub(crate) popups: Vec<Popup>,
95    /// Widgets added during an event, given to the `Ui` after it.
96    pub(crate) incoming: Incoming,
97    /// Popups closed during an event, removed after it.
98    pub(crate) closing: Vec<WidgetId>,
99    pub(crate) theme: Theme,
100    pub(crate) scale: f32,
101    viewport: Size,
102    pub(crate) text: TextSystem,
103    pub(crate) focus: Option<WidgetId>,
104    pub(crate) hover: Option<WidgetId>,
105    pub(crate) capture: Option<WidgetId>,
106    /// Focus asked for by a widget, given once its event is handled.
107    pub(crate) requested_focus: Option<Option<WidgetId>>,
108    pub(crate) actions: VecDeque<Action>,
109    pub(crate) needs_layout: bool,
110    pub(crate) needs_paint: bool,
111    /// Scrolling moved widgets: their rectangles need computing again, not their layout.
112    pub(crate) needs_place: bool,
113    pub(crate) ime_area: Option<(WidgetId, Rect)>,
114    pub(crate) last_press: Option<Press>,
115    /// Where the pointer is, if over the window: hover follows layout changes under it.
116    pub(crate) pointer: Option<Point>,
117    pub(crate) clipboard: Held,
118}
119
120/// The layout tree mirrors the widget tree, so its operations only fail on ids this crate
121/// never hands it.
122fn sync<T>(result: TaffyResult<T>) -> T {
123    match result {
124        Ok(value) => value,
125        Err(error) => unreachable!("the layout tree is out of step with the widgets: {error}"),
126    }
127}
128
129impl State {
130    pub(crate) fn is_live(&self, id: WidgetId) -> bool {
131        self.nodes
132            .get(id.slot())
133            .is_some_and(|node| node.live && node.generation == id.generation)
134    }
135
136    pub(crate) fn node(&self, id: WidgetId) -> Option<&Node> {
137        self.is_live(id).then(|| &self.nodes[id.slot()])
138    }
139
140    pub(crate) fn viewport(&self) -> Size {
141        self.viewport
142    }
143
144    /// `id`'s layout engine style: the root and the popup layer fill the viewport.
145    fn taffy_style(&self, id: WidgetId) -> taffy::Style {
146        let mut style = self.nodes[id.slot()].style.to_taffy(self.scale);
147        if id == self.root || id == self.layer {
148            style.size = taffy::Size {
149                width: taffy::style::Dimension::length(self.viewport.width),
150                height: taffy::style::Dimension::length(self.viewport.height),
151            };
152        }
153        style
154    }
155
156    /// Gives `id` a new, childless layout node in its old one's place. taffy keeps the
157    /// absolutely positioned descendants it lays out on their container ("hoisted children")
158    /// and clears that list only when it lays the container out as one: a container whose
159    /// last child is removed is laid out as a leaf from then on, and rounding the layout
160    /// panicked on the removed child's id.
161    fn renew_layout_node(&mut self, id: WidgetId) {
162        let old = self.nodes[id.slot()].taffy;
163        let fresh = sync(self.taffy.new_leaf_with_context(self.taffy_style(id), id));
164        if let Some(parent) = self.taffy.parent(old) {
165            let index = sync(self.taffy.children(parent))
166                .iter()
167                .position(|child| *child == old);
168            if let Some(index) = index {
169                sync(self.taffy.replace_child_at_index(parent, index, fresh));
170            }
171        }
172        sync(self.taffy.remove(old));
173        self.nodes[id.slot()].taffy = fresh;
174        self.nodes[id.slot()].dirty = true;
175    }
176
177    /// Adds a node for a new widget as child number `position` of `parent` (past the end:
178    /// the last); the caller puts the widget in the `Ui`'s slot for it.
179    pub(crate) fn new_node(
180        &mut self,
181        parent: WidgetId,
182        position: usize,
183        style: Style,
184    ) -> Result<WidgetId, UiError> {
185        if !self.is_live(parent) {
186            return Err(UiError::NoSuchWidget(parent));
187        }
188        let position = position.min(self.nodes[parent.slot()].children.len());
189        let index = match self.free.pop() {
190            Some(index) => index,
191            None => u32::try_from(self.nodes.len()).map_err(|_| UiError::TooManyWidgets)?,
192        };
193        let slot = index as usize;
194        let generation = self.nodes.get(slot).map_or(0, |node| node.generation);
195        let id = WidgetId { index, generation };
196        let taffy = sync(
197            self.taffy
198                .new_leaf_with_context(style.to_taffy(self.scale), id),
199        );
200        sync(
201            self.taffy
202                .insert_child_at_index(self.nodes[parent.slot()].taffy, position, taffy),
203        );
204        self.next_access += 1;
205        let node = Node {
206            generation,
207            access: self.next_access,
208            live: true,
209            parent: Some(parent),
210            children: Vec::new(),
211            taffy,
212            style,
213            rect: Rect::default(),
214            content: Rect::default(),
215            content_size: Size::ZERO,
216            scroll: Point::ORIGIN,
217            hovered: false,
218            cursor: None,
219            extent: Rect::default(),
220            dirty: true,
221            notified: None,
222        };
223        if slot == self.nodes.len() {
224            self.nodes.push(node);
225        } else {
226            self.nodes[slot] = node;
227        }
228        self.nodes[parent.slot()].children.insert(position, id);
229        self.needs_layout = true;
230        self.needs_paint = true;
231        Ok(id)
232    }
233
234    pub(crate) fn set_style(&mut self, id: WidgetId, style: Style) -> bool {
235        if !self.is_live(id) {
236            return false;
237        }
238        self.nodes[id.slot()].style = style;
239        self.nodes[id.slot()].dirty = true;
240        let taffy_style = self.taffy_style(id);
241        sync(
242            self.taffy
243                .set_style(self.nodes[id.slot()].taffy, taffy_style),
244        );
245        self.needs_layout = true;
246        self.needs_paint = true;
247        true
248    }
249
250    /// Whether `id` and every widget around it are shown (none hidden).
251    pub(crate) fn is_shown(&self, id: WidgetId) -> bool {
252        let mut at = Some(id);
253        while let Some(current) = at.filter(|w| self.is_live(*w)) {
254            let node = &self.nodes[current.slot()];
255            if node.style.hidden {
256                return false;
257            }
258            at = node.parent;
259        }
260        self.is_live(id)
261    }
262
263    /// Whether `id` is `ancestor` or inside it.
264    pub(crate) fn is_within(&self, id: WidgetId, ancestor: WidgetId) -> bool {
265        let mut at = Some(id);
266        while let Some(current) = at.filter(|w| self.is_live(*w)) {
267            if current == ancestor {
268                return true;
269            }
270            at = self.nodes[current.slot()].parent;
271        }
272        false
273    }
274
275    pub(crate) fn mark_dirty(&mut self, id: WidgetId) {
276        if self.is_live(id) {
277            sync(self.taffy.mark_dirty(self.nodes[id.slot()].taffy));
278            self.nodes[id.slot()].dirty = true;
279            self.needs_layout = true;
280            self.needs_paint = true;
281        }
282    }
283
284    pub(crate) fn scroll_to(&mut self, id: WidgetId, offset: Point) -> Point {
285        let node = &mut self.nodes[id.slot()];
286        let room = |content: f32, shown: f32| (content - shown).max(0.0);
287        let max_x = room(node.content_size.width, node.rect.width());
288        let max_y = room(node.content_size.height, node.rect.height());
289        let clamped = Point::new(offset.x.clamp(0.0, max_x), offset.y.clamp(0.0, max_y));
290        if clamped != node.scroll {
291            node.scroll = clamped;
292            self.needs_place = true;
293            self.needs_paint = true;
294        }
295        clamped
296    }
297}
298
299/// A tree of widgets: their layout, painting and events.
300///
301/// The `Ui` owns its widgets and lays them out with flexbox when their styles, contents or
302/// the viewport change. Every frame, [`paint`](Self::paint) draws them onto a
303/// [`Canvas`], and the window layer feeds input through [`pointer`](Self::pointer),
304/// [`key`](Self::key) and [`ime`](Self::ime). Painting, hit-testing and delivering events
305/// do not allocate. Coordinates are physical pixels; [`Style`] lengths are logical pixels,
306/// multiplied by [`scale`](Self::scale).
307pub struct Ui {
308    pub(crate) widgets: Vec<Option<Box<dyn Widget>>>,
309    pub(crate) state: State,
310}
311
312impl fmt::Debug for Ui {
313    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
314        f.debug_struct("Ui")
315            .field("widgets", &self.widgets.len())
316            .field("viewport", &self.state.viewport)
317            .field("focus", &self.state.focus)
318            .finish_non_exhaustive()
319    }
320}
321
322/// The widget at the root: the window's background.
323struct Root;
324
325/// The widget holding the popups: paints nothing of its own.
326struct Layer;
327
328impl Widget for Layer {}
329
330impl Widget for Root {
331    fn paint(&mut self, cx: &PaintCx<'_>, canvas: &mut Canvas<'_>) {
332        canvas.fill_rect(cx.rect(), cx.theme().background);
333    }
334}
335
336impl Ui {
337    /// An empty interface with the dark theme at scale 1, its root a column filling the
338    /// viewport. `text` shapes every widget's text.
339    pub fn new(text: TextSystem) -> Self {
340        let root = WidgetId {
341            index: 0,
342            generation: 0,
343        };
344        let layer = WidgetId {
345            index: 1,
346            generation: 0,
347        };
348        let style = Style::column();
349        let mut taffy = TaffyTree::new();
350        let node = sync(taffy.new_leaf_with_context(style.to_taffy(1.0), root));
351        let layer_node = sync(taffy.new_leaf_with_context(style.to_taffy(1.0), layer));
352        let top = |taffy: taffy::NodeId| Node {
353            generation: 0,
354            // The layer is never in the accessibility tree (its popups join the window's).
355            access: 0,
356            live: true,
357            parent: None,
358            children: Vec::new(),
359            taffy,
360            style: style.clone(),
361            rect: Rect::default(),
362            content: Rect::default(),
363            content_size: Size::ZERO,
364            scroll: Point::ORIGIN,
365            hovered: false,
366            cursor: None,
367            extent: Rect::default(),
368            dirty: true,
369            notified: None,
370        };
371        let mut state = State {
372            nodes: vec![top(node), top(layer_node)],
373            free: Vec::new(),
374            next_access: 0,
375            taffy,
376            root,
377            layer,
378            popups: Vec::new(),
379            incoming: Incoming::default(),
380            closing: Vec::new(),
381            theme: Theme::default(),
382            scale: 1.0,
383            viewport: Size::ZERO,
384            text,
385            focus: None,
386            hover: None,
387            capture: None,
388            requested_focus: None,
389            actions: VecDeque::with_capacity(ACTION_CAPACITY),
390            needs_layout: true,
391            needs_paint: true,
392            needs_place: false,
393            ime_area: None,
394            last_press: None,
395            pointer: None,
396            clipboard: Held::default(),
397        };
398        for (id, taffy) in [(root, node), (layer, layer_node)] {
399            let style = state.taffy_style(id);
400            sync(state.taffy.set_style(taffy, style));
401        }
402        Self {
403            widgets: vec![Some(Box::new(Root)), Some(Box::new(Layer))],
404            state,
405        }
406    }
407
408    /// The root: a column filling the viewport, painted with the theme's background.
409    pub fn root(&self) -> WidgetId {
410        self.state.root
411    }
412
413    /// Adds `widget` as the last child of `parent`, laid out by `style`.
414    ///
415    /// # Errors
416    /// [`UiError::NoSuchWidget`] if `parent` was removed, [`UiError::TooManyWidgets`].
417    pub fn add(
418        &mut self,
419        parent: WidgetId,
420        widget: impl Widget,
421        style: Style,
422    ) -> Result<WidgetId, UiError> {
423        let index = self.children(parent).len();
424        self.insert(parent, index, widget, style)
425    }
426
427    /// Adds `widget` as child number `index` of `parent` (past the end: the last), laid out
428    /// by `style`.
429    ///
430    /// # Errors
431    /// [`UiError::NoSuchWidget`] if `parent` was removed, [`UiError::TooManyWidgets`].
432    pub fn insert(
433        &mut self,
434        parent: WidgetId,
435        index: usize,
436        widget: impl Widget,
437        style: Style,
438    ) -> Result<WidgetId, UiError> {
439        let id = self.state.new_node(parent, index, style)?;
440        let slot = id.slot();
441        if slot >= self.widgets.len() {
442            self.widgets.resize_with(slot + 1, || None);
443        }
444        self.widgets[slot] = Some(Box::new(widget));
445        Ok(id)
446    }
447
448    /// Removes `id` and everything inside it; `false` if it was already gone, or is the
449    /// root. An open popup is closed as [`close_popup`](Self::close_popup) closes it: with
450    /// the popups opened after it, focus going back.
451    pub fn remove(&mut self, id: WidgetId) -> bool {
452        if self.state.close_popup(id) {
453            self.settle();
454            return true;
455        }
456        self.remove_widget(id)
457    }
458
459    /// Removes `id` and everything inside it.
460    pub(crate) fn remove_widget(&mut self, id: WidgetId) -> bool {
461        if !self.state.is_live(id) || id == self.state.root || id == self.state.layer {
462            return false;
463        }
464        if let Some(hover) = self.state.hover.filter(|h| self.state.is_within(*h, id)) {
465            self.mark_hovered(Some(hover), false);
466            self.state.hover = None;
467        }
468        let parent = self.state.nodes[id.slot()].parent;
469        if let Some(parent) = parent {
470            self.state.nodes[parent.slot()]
471                .children
472                .retain(|child| *child != id);
473        }
474        let mut doomed = vec![id];
475        while let Some(gone) = doomed.pop() {
476            let node = &mut self.state.nodes[gone.slot()];
477            doomed.append(&mut node.children);
478            node.live = false;
479            node.generation = node.generation.wrapping_add(1);
480            node.cursor = None;
481            let taffy = node.taffy;
482            sync(self.state.taffy.remove(taffy));
483            self.widgets[gone.slot()] = None;
484            self.state.free.push(gone.index);
485        }
486        if let Some(parent) = parent.filter(|p| self.state.nodes[p.slot()].children.is_empty()) {
487            self.state.renew_layout_node(parent);
488        }
489        let state = &mut self.state;
490        let live = |slot: &mut Option<WidgetId>, nodes: &[Node]| {
491            if slot.is_some_and(|w| {
492                !(nodes[w.slot()].live && nodes[w.slot()].generation == w.generation)
493            }) {
494                *slot = None;
495            }
496        };
497        live(&mut state.focus, &state.nodes);
498        live(&mut state.hover, &state.nodes);
499        live(&mut state.capture, &state.nodes);
500        let nodes = &state.nodes;
501        state.popups.retain(|popup| {
502            nodes[popup.id.slot()].live && nodes[popup.id.slot()].generation == popup.id.generation
503        });
504        if state.ime_area.is_some_and(|(w, _)| !state.is_live(w)) {
505            state.ime_area = None;
506        }
507        state.needs_layout = true;
508        state.needs_paint = true;
509        true
510    }
511
512    /// Changes `id`'s style; `false` if it was removed.
513    pub fn set_style(&mut self, id: WidgetId, style: Style) -> bool {
514        self.state.set_style(id, style)
515    }
516
517    /// `id`'s style.
518    pub fn style(&self, id: WidgetId) -> Option<&Style> {
519        self.state.node(id).map(|node| &node.style)
520    }
521
522    /// `id`'s parent; `None` for the root or a removed widget.
523    pub fn parent(&self, id: WidgetId) -> Option<WidgetId> {
524        self.state.node(id).and_then(|node| node.parent)
525    }
526
527    /// `id`'s children, first painted first; empty for a removed widget.
528    pub fn children(&self, id: WidgetId) -> &[WidgetId] {
529        self.state.node(id).map_or(&[], |node| &node.children)
530    }
531
532    /// Where `id` is, in physical pixels from the window's top-left corner, as of the last
533    /// layout.
534    pub fn rect(&self, id: WidgetId) -> Option<Rect> {
535        self.state.node(id).map(|node| node.rect)
536    }
537
538    /// The widget `id`, if it is a `T`.
539    pub fn widget<T: Widget>(&self, id: WidgetId) -> Option<&T> {
540        if !self.state.is_live(id) {
541            return None;
542        }
543        let widget: &dyn Widget = self.widgets.get(id.slot())?.as_deref()?;
544        (widget as &dyn Any).downcast_ref::<T>()
545    }
546
547    /// Runs `update` on the widget `id`, if it is a `T`, with a context to request layout,
548    /// paint or focus as an event handler would.
549    pub fn update<T: Widget, R>(
550        &mut self,
551        id: WidgetId,
552        update: impl FnOnce(&mut T, &mut EventCx<'_>) -> R,
553    ) -> Option<R> {
554        if !self.state.is_live(id) {
555            return None;
556        }
557        let widget: &mut dyn Widget = self.widgets.get_mut(id.slot())?.as_deref_mut()?;
558        let widget = (widget as &mut dyn Any).downcast_mut::<T>()?;
559        let mut cx = EventCx {
560            state: &mut self.state,
561            id,
562        };
563        let result = update(widget, &mut cx);
564        self.settle();
565        Some(result)
566    }
567
568    /// The theme.
569    pub fn theme(&self) -> &Theme {
570        &self.state.theme
571    }
572
573    /// Changes the theme; widgets are measured again (sizes may differ).
574    pub fn set_theme(&mut self, theme: Theme) {
575        self.state.theme = theme;
576        self.restyle();
577    }
578
579    /// The clipboard widgets copy to and paste from.
580    pub fn clipboard(&mut self) -> &mut dyn Clipboard {
581        self.state.clipboard.0.as_mut()
582    }
583
584    /// Gives widgets `clipboard` (the system's, from a window) in place of the
585    /// [`MemoryClipboard`](crate::MemoryClipboard) a `Ui` starts with.
586    pub fn set_clipboard(&mut self, clipboard: impl Clipboard + 'static) {
587        self.state.clipboard = Held(Box::new(clipboard));
588    }
589
590    /// Physical pixels per logical pixel.
591    pub fn scale(&self) -> f32 {
592        self.state.scale
593    }
594
595    /// Changes the scale factor (the window moved to a screen of another density).
596    pub fn set_scale(&mut self, scale: f32) {
597        if scale.is_finite() && scale > 0.0 && scale != self.state.scale {
598            self.state.scale = scale;
599            self.restyle();
600        }
601    }
602
603    /// The area the root fills, in physical pixels.
604    pub fn viewport(&self) -> Size {
605        self.state.viewport
606    }
607
608    /// Changes the area the root fills (the window was resized).
609    pub fn resize(&mut self, viewport: Size) {
610        if viewport != self.state.viewport {
611            self.state.viewport = viewport;
612            let root = self.state.root;
613            let style = self.state.taffy_style(root);
614            sync(
615                self.state
616                    .taffy
617                    .set_style(self.state.nodes[root.slot()].taffy, style),
618            );
619            self.state.needs_layout = true;
620            self.state.needs_paint = true;
621        }
622    }
623
624    /// The text system widgets shape their text with.
625    pub fn text_system(&mut self) -> &mut TextSystem {
626        &mut self.state.text
627    }
628
629    /// Whether something changed since the last [`paint`](Self::paint).
630    pub fn needs_paint(&self) -> bool {
631        self.state.needs_paint || self.state.needs_layout || self.state.needs_place
632    }
633
634    /// The cursor to show: the one set by the widget under the pointer (or holding it), or
635    /// its nearest parent that set one.
636    pub fn cursor(&self) -> Cursor {
637        let mut at = self.state.capture.or(self.state.hover);
638        while let Some(id) = at.filter(|id| self.state.is_live(*id)) {
639            let node = &self.state.nodes[id.slot()];
640            if let Some(cursor) = node.cursor {
641                return cursor;
642            }
643            at = node.parent;
644        }
645        Cursor::Default
646    }
647
648    /// Where the focused widget wants input method candidates, if it does.
649    pub fn ime_area(&self) -> Option<Rect> {
650        self.state
651            .ime_area
652            .filter(|(id, _)| self.state.focus == Some(*id))
653            .map(|(_, area)| area)
654    }
655
656    /// The actions widgets reported and not yet read, oldest first, taken out.
657    pub fn actions(&mut self) -> impl Iterator<Item = Action> + '_ {
658        self.state.actions.drain(..)
659    }
660
661    /// The oldest action not yet read, taken out: lets the application act on each with the
662    /// `Ui` at hand.
663    pub fn next_action(&mut self) -> Option<Action> {
664        self.state.actions.pop_front()
665    }
666
667    /// Lays the widgets out if anything changed. [`paint`](Self::paint) and the event
668    /// methods call it first; call it to read [`rect`](Self::rect)s before painting.
669    pub fn layout(&mut self) {
670        if self.state.needs_layout {
671            self.compute(self.state.root);
672            if !self.state.nodes[self.state.layer.slot()]
673                .children
674                .is_empty()
675            {
676                self.compute(self.state.layer);
677            }
678            self.state.needs_layout = false;
679            self.place(self.state.root, Point::ORIGIN, true, true);
680            self.place(self.state.layer, Point::ORIGIN, true, true);
681            self.state.needs_place = false;
682            self.after_moves();
683        }
684        if self.state.needs_place {
685            self.place(self.state.root, Point::ORIGIN, false, true);
686            self.place(self.state.layer, Point::ORIGIN, false, true);
687            self.state.needs_place = false;
688            self.after_moves();
689        }
690    }
691
692    /// Lays out the tree under `top` (the root, or the popup layer) in the viewport.
693    fn compute(&mut self, top: WidgetId) {
694        {
695            let Self { widgets, state } = self;
696            let root = state.nodes[top.slot()].taffy;
697            let available = taffy::Size {
698                width: AvailableSpace::Definite(state.viewport.width),
699                height: AvailableSpace::Definite(state.viewport.height),
700            };
701            let scale = state.scale;
702            let (text, theme) = (&mut state.text, &state.theme);
703            let computed = state.taffy.compute_layout_with_measure(
704                root,
705                available,
706                |inputs, _, context, style| {
707                    taffy::compute_leaf_layout(
708                        inputs,
709                        style,
710                        |_, _| 0.0,
711                        |known, room| {
712                            let widget =
713                                context.and_then(|id| widgets.get_mut(id.slot())?.as_mut());
714                            let Some(widget) = widget else {
715                                return taffy::Size::ZERO;
716                            };
717                            let mut cx = LayoutCx {
718                                text: &mut *text,
719                                theme,
720                                scale,
721                            };
722                            let size = widget.measure(&mut cx, constraints(known, room));
723                            taffy::Size {
724                                width: known.width.unwrap_or(size.width),
725                                height: known.height.unwrap_or(size.height),
726                            }
727                        },
728                    )
729                },
730            );
731            sync(computed);
732        }
733    }
734
735    /// After widgets moved, resized or were hidden: a hidden widget loses focus, and hover
736    /// follows the widgets that are now under a pointer that did not move.
737    fn after_moves(&mut self) {
738        if self.state.focus.is_some_and(|f| !self.state.is_shown(f)) {
739            self.set_focus(None);
740        }
741        if let (Some(at), None) = (self.state.pointer, self.state.capture) {
742            let (hit, _) = self.hit_top(at);
743            self.set_hover(hit, at);
744        }
745    }
746
747    /// Computes `id`'s rectangle (and its children's) from the last layout, its parent's
748    /// content starting at `origin`; `notify` tells widgets their sizes.
749    fn place(&mut self, id: WidgetId, origin: Point, notify: bool, shown: bool) {
750        let state = &mut self.state;
751        let (taffy, parent) = (state.nodes[id.slot()].taffy, state.nodes[id.slot()].parent);
752        let layout = *sync(state.taffy.layout(taffy));
753        let size = Size::new(layout.size.width, layout.size.height);
754        let corner = match parent.filter(|parent| *parent == state.layer) {
755            // On whole pixels, as their text and borders are.
756            Some(_) => state
757                .popup_origin(id, size)
758                .map(|at| Point::new(at.x.round(), at.y.round())),
759            None => None,
760        }
761        .unwrap_or(Point::new(
762            origin.x + layout.location.x,
763            origin.y + layout.location.y,
764        ));
765        let node = &mut state.nodes[id.slot()];
766        let rect = Rect::new(corner.x, corner.y, size.width, size.height);
767        node.rect = rect;
768        node.content = Rect::from_edges(
769            rect.left() + layout.border.left + layout.padding.left,
770            rect.top() + layout.border.top + layout.padding.top,
771            rect.right() - layout.border.right - layout.padding.right,
772            rect.bottom() - layout.border.bottom - layout.padding.bottom,
773        );
774        // Measured from the padding box's corner: how far the content reaches.
775        let overflow = layout.scrollable_overflow_rect;
776        node.content_size = Size::new(overflow.right, overflow.bottom);
777        // Content that shrank, or a view that grew, leaves less to scroll. A hidden widget
778        // has no size, so it keeps its scroll for when it is shown again (a tab switched
779        // away and back).
780        let shown = shown && !node.style.hidden;
781        let room = |content: f32, view: f32| (content - view).max(0.0);
782        let clamped = if !shown {
783            node.scroll
784        } else {
785            Point::new(
786                node.scroll.x.clamp(0.0, room(overflow.right, rect.width())),
787                node.scroll
788                    .y
789                    .clamp(0.0, room(overflow.bottom, rect.height())),
790            )
791        };
792        if clamped != node.scroll {
793            node.scroll = clamped;
794            state.needs_paint = true;
795        }
796        node.extent = if node.style.overflow == Overflow::Visible {
797            Rect::from_edges(
798                rect.left() + overflow.left.min(0.0),
799                rect.top() + overflow.top.min(0.0),
800                rect.right().max(rect.left() + overflow.right),
801                rect.bottom().max(rect.top() + overflow.bottom),
802            )
803        } else {
804            rect
805        };
806        let inner = Point::new(rect.left() - node.scroll.x, rect.top() - node.scroll.y);
807        let changed = node.dirty || node.notified != Some(rect.size());
808        if notify && changed {
809            node.dirty = false;
810            node.notified = Some(rect.size());
811            if let Some(widget) = self.widgets.get_mut(id.slot()).and_then(Option::as_mut) {
812                let mut cx = LayoutCx {
813                    text: &mut state.text,
814                    theme: &state.theme,
815                    scale: state.scale,
816                };
817                widget.layout(&mut cx, rect.size());
818            }
819        }
820        for index in 0..self.state.nodes[id.slot()].children.len() {
821            let child = self.state.nodes[id.slot()].children[index];
822            self.place(child, inner, notify, shown);
823        }
824    }
825
826    /// Paints every visible widget onto `canvas`, laying them out first if needed.
827    pub fn paint(&mut self, canvas: &mut Canvas<'_>) {
828        self.layout();
829        self.paint_node(self.state.root, canvas);
830        self.paint_node(self.state.layer, canvas);
831        self.state.needs_paint = false;
832    }
833
834    fn paint_node(&mut self, id: WidgetId, canvas: &mut Canvas<'_>) {
835        let node = &self.state.nodes[id.slot()];
836        if node.style.hidden {
837            return;
838        }
839        let rect = node.rect;
840        let clips = node.style.overflow != Overflow::Visible;
841        // Nothing it or its children paint would show.
842        if canvas.clip_rect().intersect(node.extent).is_empty() {
843            return;
844        }
845        let saved = canvas.state();
846        if let Some(widget) = self.widgets.get_mut(id.slot()).and_then(Option::as_mut) {
847            widget.paint(
848                &PaintCx {
849                    state: &self.state,
850                    id,
851                },
852                canvas,
853            );
854        }
855        if clips {
856            canvas.clip(rect);
857        }
858        for index in 0..self.state.nodes[id.slot()].children.len() {
859            let child = self.state.nodes[id.slot()].children[index];
860            self.paint_node(child, canvas);
861        }
862        canvas.restore(saved);
863        if let Some(widget) = self.widgets.get_mut(id.slot()).and_then(Option::as_mut) {
864            widget.paint_over(
865                &PaintCx {
866                    state: &self.state,
867                    id,
868                },
869                canvas,
870            );
871        }
872    }
873
874    /// Re-styles every widget (scale or theme changed), so all are measured again.
875    fn restyle(&mut self) {
876        for slot in 0..self.state.nodes.len() {
877            let node = &self.state.nodes[slot];
878            if node.live {
879                let id = WidgetId {
880                    index: slot as u32,
881                    generation: node.generation,
882                };
883                let style = self.state.taffy_style(id);
884                sync(self.state.taffy.set_style(node.taffy, style));
885                self.state.nodes[slot].dirty = true;
886            }
887        }
888        self.state.needs_layout = true;
889        self.state.needs_paint = true;
890    }
891
892    /// Gives focus a widget asked for while handling an event, and re-places scrolled widgets.
893    pub(crate) fn settle(&mut self) {
894        // Focus events can ask for focus again, or open and close popups: give up after a
895        // few rounds.
896        for _ in 0..4 {
897            self.adopt();
898            match self.state.requested_focus.take() {
899                Some(focus) => self.give_focus(focus),
900                None => break,
901            }
902        }
903        // What the last focus events opened or closed.
904        self.adopt();
905        self.state.requested_focus = None;
906    }
907}
908
909/// Layout's question in densa's terms.
910fn constraints(known: taffy::Size<Option<f32>>, room: taffy::Size<AvailableSpace>) -> Constraints {
911    let available = |space: AvailableSpace| match space {
912        AvailableSpace::Definite(room) => Available::Definite(room),
913        AvailableSpace::MinContent => Available::MinContent,
914        AvailableSpace::MaxContent => Available::MaxContent,
915    };
916    Constraints {
917        width: known.width,
918        height: known.height,
919        available_width: available(room.width),
920        available_height: available(room.height),
921    }
922}