1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
//! Tree storage: one node per widget, addressed by a generational key.
use alloc::vec::Vec;
use denise::Rect;
use crate::anchor::{Anchors, Dock};
use crate::arena::NodeId;
use crate::widget::{BoxedWidget, VisualState};
pub(crate) struct Node<M> {
pub(crate) widget: BoxedWidget<M>,
/// Position and extent relative to the parent's origin.
pub(crate) layout: Rect,
/// Absolute bounds, recomputed when the node or an ancestor moves.
pub(crate) bounds: Rect,
/// [`Node::bounds`] intersected with every ancestor's bounds: what the widget
/// may actually paint into, and what damage from this node covers.
pub(crate) clip: Rect,
/// Sort key among siblings. Ties keep insertion order.
pub(crate) z: i32,
pub(crate) visible: bool,
pub(crate) enabled: bool,
pub(crate) parent: Option<NodeId>,
pub(crate) children: Vec<NodeId>,
/// Which scene this node belongs to, as an index into the stack.
pub(crate) scene: usize,
pub(crate) state: VisualState,
/// Whether the tree may scroll this node's content on wheel, page keys and
/// reveal requests. Explicit rather than inferred from overflowing content,
/// so a panel with a decoratively clipped child does not start moving under
/// the wheel.
pub(crate) scrollable: bool,
/// Text shown after the pointer rests on this node. See
/// [`Ui::set_tooltip`](crate::Ui::set_tooltip) — the tree owns everything
/// about a tooltip except the string.
pub(crate) tooltip: Option<alloc::string::String>,
/// How far this node's content is scrolled: children are shifted up and
/// left by this much. Applied in `reflow`, which is the single place that
/// turns layouts into absolute bounds — so paint, clip and hit testing
/// cannot disagree about where a scrolled child is.
pub(crate) scroll: denise::Point,
/// `Some(spacing)` makes this node a vertical stack: its visible children
/// are placed top-to-bottom in order, each at the running y. Applied in
/// `reflow`, like scrolling and for the same reason — one place turns
/// layouts into bounds, so nothing can disagree about where a moved
/// sibling is. See [`Ui::set_stack`](crate::Ui::set_stack).
pub(crate) stack: Option<i32>,
/// Which of the parent's edges this node keeps its distance from.
///
/// [`Anchors::TOP_LEFT`] by default, which derives the rectangle the node
/// already had — so a tree that never mentions anchoring behaves exactly as
/// it did before anchoring existed.
pub(crate) anchors: Anchors,
/// An edge of the parent this node takes entirely, if any. Applied in
/// `reflow`, before the anchored children and before any stack, so a docked
/// bar shrinks the box the rest are placed in.
pub(crate) dock: Option<Dock>,
/// The size of the box this node was placed in when its layout was last set,
/// against which [`Node::anchors`] measures the parent's growth.
///
/// `None` until the first reflow places it, and captured there rather than
/// at insertion, because the box a child is placed in is not knowable until
/// its docked siblings have taken their edges. Reset by
/// [`Ui::set_layout`](crate::Ui::set_layout) — a new layout is a new design,
/// stated against whatever the parent is now — but *not* by a layout tween,
/// which would otherwise re-baseline on every frame and stand still.
pub(crate) anchor_base: Option<denise::Size>,
}
impl<M> Node<M> {
pub(crate) fn new(widget: BoxedWidget<M>, layout: Rect, scene: usize) -> Self {
Self {
widget,
layout,
bounds: layout,
clip: layout,
z: 0,
visible: true,
enabled: true,
parent: None,
children: Vec::new(),
scene,
state: VisualState::NONE,
scrollable: false,
tooltip: None,
scroll: denise::Point::ZERO,
stack: None,
anchors: Anchors::TOP_LEFT,
dock: None,
anchor_base: None,
}
}
/// Returns `true` if this node can be painted at all.
#[inline]
pub(crate) fn paintable(&self) -> bool {
self.visible && !self.clip.is_empty()
}
}
/// One layer of the scene stack.
///
/// The stack is how CoreCanvas did dialogs and it is how Denise does them: a modal
/// is not a widget inside the page, it is a scene pushed on top of it.
#[derive(Clone, Copy, Debug)]
pub(crate) struct Scene {
pub(crate) root: NodeId,
/// Alpha of the backdrop painted under this scene, `0` for none.
pub(crate) dim: u8,
/// Set when this scene is a popup: the node the popup is anchored to and
/// the container holding its content. What makes a popup a popup is not the
/// scene — input capture comes from scene-ness itself — but the dismissal
/// rules: a press outside the container closes it, Escape closes it, and
/// focus goes back to the anchor.
pub(crate) popup: Option<Popup>,
}
/// The dismissal bookkeeping of a popup scene.
#[derive(Clone, Copy, Debug)]
pub(crate) struct Popup {
/// The node the popup is attached to, and where focus returns on close.
pub(crate) anchor: NodeId,
/// The positioned container the caller fills. A press outside its bounds
/// dismisses the popup.
pub(crate) container: NodeId,
}