Skip to main content

qframe/widget/
view.rs

1//! Building the view tree.
2
3use super::flex::{Axis, Flex};
4use super::{Align, Container, Key, Length, Node, Widget};
5use crate::env::Env;
6use crate::geometry::Padding;
7
8/// Collects the nodes of one container while an application's `view` runs.
9pub struct View<'a, Msg> {
10    nodes: &'a mut Vec<Node<Msg>>,
11    env: &'a Env,
12}
13
14impl<'a, Msg: 'static> View<'a, Msg> {
15    pub(crate) fn new(nodes: &'a mut Vec<Node<Msg>>, env: &'a Env) -> Self {
16        Self { nodes, env }
17    }
18
19    /// The environment: theme, icons, language and keymap.
20    #[must_use]
21    pub fn env(&self) -> &Env {
22        self.env
23    }
24
25    /// Adds a widget.
26    pub fn add<W: Widget<Msg>>(&mut self, widget: W) -> NodeMut<'_, Msg> {
27        let index = self.nodes.len();
28        self.nodes.push(Node::new(widget, index));
29        NodeMut { node: self.nodes.last_mut().expect("a node was just pushed") }
30    }
31
32    /// Adds a widget that contains other widgets, built by `build`.
33    pub fn add_with<W: Container<Msg>>(
34        &mut self,
35        mut widget: W,
36        build: impl FnOnce(&mut View<'_, Msg>),
37    ) -> NodeMut<'_, Msg> {
38        let mut children = Vec::new();
39        build(&mut View::new(&mut children, self.env));
40        widget.set_children(children);
41        self.add(widget)
42    }
43
44    /// Adds a column whose children are built by `build`.
45    pub fn column(&mut self, build: impl FnOnce(&mut View<'_, Msg>)) -> NodeMut<'_, Msg> {
46        self.container(Axis::Column, build)
47    }
48
49    /// Adds a row whose children are built by `build`.
50    pub fn row(&mut self, build: impl FnOnce(&mut View<'_, Msg>)) -> NodeMut<'_, Msg> {
51        self.container(Axis::Row, build)
52    }
53
54    /// Adds a stack: children are drawn on top of each other in the same area, later ones on top.
55    pub fn stack(&mut self, build: impl FnOnce(&mut View<'_, Msg>)) -> NodeMut<'_, Msg> {
56        self.container(Axis::Stack, build)
57    }
58
59    /// Adds a column that remembers its widgets' state (focus, scroll, cursors) while it is not
60    /// shown. Give every page of a router its own `key`.
61    pub fn page(&mut self, key: impl Into<String>, build: impl FnOnce(&mut View<'_, Msg>)) -> NodeMut<'_, Msg> {
62        let node = self.container(Axis::Column, build);
63        node.node.persistent = true;
64        node.node.key = Key::Named(key.into());
65        node.fill()
66    }
67
68    /// Adds empty space that takes the room left in a row or column.
69    pub fn spacer(&mut self) -> NodeMut<'_, Msg> {
70        self.container(Axis::Stack, |_| {}).fill()
71    }
72
73    fn container(&mut self, axis: Axis, build: impl FnOnce(&mut View<'_, Msg>)) -> NodeMut<'_, Msg> {
74        let mut children = Vec::new();
75        build(&mut View::new(&mut children, self.env));
76        self.add(Flex::new(axis, children))
77    }
78}
79
80/// Adjusts the node just added. Every method changes the node in place, so the result can be
81/// ignored or chained.
82pub struct NodeMut<'a, Msg> {
83    node: &'a mut Node<Msg>,
84}
85
86impl<'a, Msg> NodeMut<'a, Msg> {
87    /// Names the node. Name widgets whose position among their siblings can change (list rows,
88    /// optional widgets) so their state and focus follow them.
89    pub fn id(self, name: impl Into<String>) -> Self {
90        self.node.key = Key::Named(name.into());
91        self
92    }
93
94    /// Sets the width.
95    pub fn width(self, width: Length) -> Self {
96        self.node.layout.width = width;
97        self
98    }
99
100    /// Sets the height.
101    pub fn height(self, height: Length) -> Self {
102        self.node.layout.height = height;
103        self
104    }
105
106    /// Takes all space left in both directions.
107    pub fn fill(self) -> Self {
108        self.width(Length::Fill(1)).height(Length::Fill(1))
109    }
110
111    /// Takes all width left.
112    pub fn fill_width(self) -> Self {
113        self.width(Length::Fill(1))
114    }
115
116    /// Takes all height left.
117    pub fn fill_height(self) -> Self {
118        self.height(Length::Fill(1))
119    }
120
121    /// Keeps `padding` free inside the node.
122    pub fn padding(self, padding: Padding) -> Self {
123        self.node.layout.padding = padding;
124        self
125    }
126
127    /// Leaves `cells` between the children of a row or column.
128    pub fn gap(self, cells: u16) -> Self {
129        self.node.layout.gap = cells;
130        self
131    }
132
133    /// Places children along the main axis of a row or column (both axes of a stack).
134    pub fn justify(self, align: Align) -> Self {
135        self.node.layout.justify = align;
136        self
137    }
138
139    /// Whether a mouse drag may select text in this node. Nothing is selectable unless asked:
140    /// `true` makes the node a selection region, so a drag that starts inside it selects text
141    /// within the node only (widgets such as `CodeView` and `Markdown` are regions by
142    /// themselves). `false` keeps selection out of the node and everything inside it, also out
143    /// of regions within it, e.g. for a secret shown inside a selectable log.
144    pub fn selectable(self, selectable: bool) -> Self {
145        self.node.selectable = Some(selectable);
146        self
147    }
148
149    /// Places children across the main axis of a row or column.
150    pub fn align(self, align: Align) -> Self {
151        self.node.layout.align = align;
152        self
153    }
154}