1use crate::error::{PaneError, TreeError};
2use crate::node::{NodeId, PanelId};
3use crate::panel::Constraints;
4use crate::tree::LayoutTree;
5use crate::validate::{check_f32_non_negative, float_invalid_to_constraint};
6
7fn validate_gap(value: f32) -> Result<(), PaneError> {
9 check_f32_non_negative(value)
10 .map_err(|e| PaneError::InvalidConstraint(float_invalid_to_constraint("gap", e)))
11}
12
13fn sentinel_panel() -> PanelId {
15 PanelId::from_raw(u32::MAX)
16}
17
18pub struct LayoutBuilder {
23 tree: LayoutTree,
24 root_set: bool,
25}
26
27impl LayoutBuilder {
28 pub fn new() -> Self {
30 Self {
31 tree: LayoutTree::new(),
32 root_set: false,
33 }
34 }
35
36 pub fn panel(&mut self, kind: impl Into<std::sync::Arc<str>>) -> Result<PanelId, PaneError> {
38 self.panel_with(kind, crate::panel::grow(1.0))
39 }
40
41 pub fn panel_with(
43 &mut self,
44 kind: impl Into<std::sync::Arc<str>>,
45 constraints: Constraints,
46 ) -> Result<PanelId, PaneError> {
47 let (pid, _nid) = self.tree.add_panel(kind, constraints)?;
48 Ok(pid)
49 }
50
51 pub fn row(&mut self, f: impl FnOnce(&mut ContainerCtx)) -> Result<(), PaneError> {
53 self.row_gap(0.0, f)
54 }
55
56 pub fn row_gap(
58 &mut self,
59 gap: f32,
60 f: impl FnOnce(&mut ContainerCtx),
61 ) -> Result<(), PaneError> {
62 self.require_no_root()?;
63 validate_gap(gap)?;
64 let children = collect_children(&mut self.tree, f)?;
65 let nid = self.tree.add_row(gap, children)?;
66 self.tree.set_root(nid);
67 self.root_set = true;
68 Ok(())
69 }
70
71 pub fn col(&mut self, f: impl FnOnce(&mut ContainerCtx)) -> Result<(), PaneError> {
73 self.col_gap(0.0, f)
74 }
75
76 pub fn col_gap(
78 &mut self,
79 gap: f32,
80 f: impl FnOnce(&mut ContainerCtx),
81 ) -> Result<(), PaneError> {
82 self.require_no_root()?;
83 validate_gap(gap)?;
84 let children = collect_children(&mut self.tree, f)?;
85 let nid = self.tree.add_col(gap, children)?;
86 self.tree.set_root(nid);
87 self.root_set = true;
88 Ok(())
89 }
90
91 pub fn set_window_size(&mut self, size: usize) {
94 self.tree.set_window_size(size);
95 }
96
97 pub fn build(self) -> Result<crate::layout::Layout, PaneError> {
99 if !self.root_set {
100 return Err(PaneError::InvalidTree(TreeError::RootNotSet));
101 }
102 self.tree.validate()?;
103 Ok(crate::layout::Layout::from_tree(self.tree))
104 }
105
106 fn require_no_root(&self) -> Result<(), PaneError> {
107 match self.root_set {
108 true => Err(PaneError::InvalidTree(TreeError::RootAlreadySet)),
109 false => Ok(()),
110 }
111 }
112}
113
114impl Default for LayoutBuilder {
115 fn default() -> Self {
116 Self::new()
117 }
118}
119
120pub struct ContainerCtx<'a> {
125 tree: &'a mut LayoutTree,
126 children: Vec<NodeId>,
127 error: Option<PaneError>,
128}
129
130impl ContainerCtx<'_> {
131 pub fn add(&mut self, pid: PanelId) {
133 if self.error.is_some() {
134 return;
135 }
136 match self.tree.node_for_panel(pid) {
137 Some(nid) => self.children.push(nid),
138 None => self.error = Some(PaneError::PanelNotFound(pid)),
139 }
140 }
141
142 pub fn panel(&mut self, kind: impl Into<std::sync::Arc<str>>) -> PanelId {
144 self.panel_with(kind, crate::panel::grow(1.0))
145 }
146
147 pub fn panel_with(
149 &mut self,
150 kind: impl Into<std::sync::Arc<str>>,
151 constraints: Constraints,
152 ) -> PanelId {
153 if self.error.is_some() {
154 return sentinel_panel();
155 }
156 match self.tree.add_panel(kind, constraints) {
157 Ok((pid, nid)) => {
158 self.children.push(nid);
159 pid
160 }
161 Err(e) => {
162 self.error = Some(e);
163 sentinel_panel()
164 }
165 }
166 }
167
168 pub fn row(&mut self, f: impl FnOnce(&mut ContainerCtx)) {
170 self.row_gap(0.0, f);
171 }
172
173 pub fn row_gap(&mut self, gap: f32, f: impl FnOnce(&mut ContainerCtx)) {
175 if self.error.is_some() {
176 return;
177 }
178 if let Err(e) = validate_gap(gap) {
179 self.error = Some(e);
180 return;
181 }
182 let children = match collect_children(self.tree, f) {
183 Ok(c) => c,
184 Err(e) => {
185 self.error = Some(e);
186 return;
187 }
188 };
189 match self.tree.add_row(gap, children) {
190 Ok(nid) => self.children.push(nid),
191 Err(e) => self.error = Some(e),
192 }
193 }
194
195 pub fn col(&mut self, f: impl FnOnce(&mut ContainerCtx)) {
197 self.col_gap(0.0, f);
198 }
199
200 pub fn col_gap(&mut self, gap: f32, f: impl FnOnce(&mut ContainerCtx)) {
202 if self.error.is_some() {
203 return;
204 }
205 if let Err(e) = validate_gap(gap) {
206 self.error = Some(e);
207 return;
208 }
209 let children = match collect_children(self.tree, f) {
210 Ok(c) => c,
211 Err(e) => {
212 self.error = Some(e);
213 return;
214 }
215 };
216 match self.tree.add_col(gap, children) {
217 Ok(nid) => self.children.push(nid),
218 Err(e) => self.error = Some(e),
219 }
220 }
221
222 pub fn taffy_node(&mut self, style: taffy::Style, f: impl FnOnce(&mut ContainerCtx)) {
224 if self.error.is_some() {
225 return;
226 }
227 let children = match collect_children(self.tree, f) {
228 Ok(c) => c,
229 Err(e) => {
230 self.error = Some(e);
231 return;
232 }
233 };
234 match self.tree.add_taffy_node(style, children) {
235 Ok(nid) => self.children.push(nid),
236 Err(e) => self.error = Some(e),
237 }
238 }
239
240 pub(crate) fn set_error(&mut self, err: PaneError) {
243 if self.error.is_none() {
244 self.error = Some(err);
245 }
246 }
247}
248
249fn collect_children(
251 tree: &mut LayoutTree,
252 f: impl FnOnce(&mut ContainerCtx),
253) -> Result<Vec<NodeId>, PaneError> {
254 let mut ctx = ContainerCtx {
255 tree,
256 children: Vec::new(),
257 error: None,
258 };
259 f(&mut ctx);
260 match ctx.error {
261 Some(e) => Err(e),
262 None => Ok(ctx.children),
263 }
264}