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