Skip to main content

sequoia_view/
dock.rs

1use sequoia_core::{Color, Edges, Length};
2use sequoia_layout::{
3    Align, Dock, DockStyle, Indents, Inset, LayoutStyle, Overflow, Position, sanitize_gap,
4};
5use sequoia_style::BoxStyle;
6
7use crate::any::AnyView;
8use crate::view::{PaintCtx, View};
9use crate::views::{IntoChildren, Unit};
10
11#[derive(Clone, Default)]
12pub struct DockArea {
13    children: Vec<AnyView>,
14    docks: Vec<Dock>,
15    style: LayoutStyle,
16    gap: f32,
17    last_child_fill: bool,
18    ordered: bool,
19    deco: BoxStyle,
20}
21
22impl DockArea {
23    pub fn new() -> Self {
24        Self {
25            children: Vec::new(),
26            docks: Vec::new(),
27            style: LayoutStyle {
28                width: Length::Fit,
29                height: Length::Fit,
30                ..LayoutStyle::default()
31            },
32            gap: 0.0,
33            last_child_fill: false,
34            ordered: false,
35            deco: BoxStyle::new(),
36        }
37    }
38
39    pub fn dock<V: View + 'static>(mut self, child: V, dock: Dock) -> Self {
40        self.children.push(AnyView::new(child));
41        self.docks.push(dock);
42        self.debug_check_fill_order();
43        self
44    }
45
46    pub fn dock_if<V: View + 'static>(
47        self,
48        cond: bool,
49        make: impl FnOnce() -> V,
50        dock: Dock,
51    ) -> Self {
52        if cond { self.dock(make(), dock) } else { self }
53    }
54
55    pub fn dock_opt<V: View + 'static>(self, child: Option<V>, dock: Dock) -> Self {
56        match child {
57            Some(v) => self.dock(v, dock),
58            None => self,
59        }
60    }
61
62    pub fn children(mut self, children: impl IntoChildren, dock: Dock) -> Self {
63        for child in children.into_children() {
64            self.children.push(child);
65            self.docks.push(dock);
66        }
67        self.debug_check_fill_order();
68        self
69    }
70
71    pub fn len(&self) -> usize {
72        self.children.len()
73    }
74
75    pub fn is_empty(&self) -> bool {
76        self.children.is_empty()
77    }
78
79    pub fn docks(&self) -> &[Dock] {
80        &self.docks
81    }
82
83    pub fn get_dock(&self, index: usize) -> Option<Dock> {
84        self.docks.get(index).copied()
85    }
86
87    pub fn is_ordered(&self) -> bool {
88        self.ordered
89    }
90
91    pub fn is_border(&self) -> bool {
92        !self.ordered
93    }
94
95    pub fn dock_at<V: View + 'static>(mut self, index: usize, child: V, dock: Dock) -> Self {
96        let i = index.min(self.children.len());
97        self.children.insert(i, AnyView::new(child));
98        self.docks.insert(i, dock);
99        self.debug_check_fill_order();
100        self
101    }
102
103    pub fn remove(mut self, index: usize) -> Self {
104        if index < self.children.len() {
105            self.children.remove(index);
106            self.docks.remove(index);
107        }
108        self
109    }
110
111    pub fn move_child(mut self, from: usize, slot: usize) -> Self {
112        crate::dnd::move_item(&mut self.children, from, slot);
113
114        crate::dnd::move_item(&mut self.docks, from, slot);
115        self.debug_check_fill_order();
116        self
117    }
118
119    pub fn set_dock(mut self, index: usize, dock: Dock) -> Self {
120        if let Some(d) = self.docks.get_mut(index) {
121            *d = dock;
122        }
123        self.debug_check_fill_order();
124        self
125    }
126
127    pub fn clear(mut self) -> Self {
128        self.children.clear();
129        self.docks.clear();
130        self
131    }
132
133    pub fn dock_mut<V: View + 'static>(&mut self, child: V, dock: Dock) {
134        self.children.push(AnyView::new(child));
135        self.docks.push(dock);
136        self.debug_check_fill_order();
137    }
138
139    pub fn dock_at_mut<V: View + 'static>(&mut self, index: usize, child: V, dock: Dock) {
140        let i = index.min(self.children.len());
141        self.children.insert(i, AnyView::new(child));
142        self.docks.insert(i, dock);
143        self.debug_check_fill_order();
144    }
145
146    pub fn remove_mut(&mut self, index: usize) {
147        if index < self.children.len() {
148            self.children.remove(index);
149            self.docks.remove(index);
150        }
151    }
152
153    pub fn move_child_mut(&mut self, from: usize, slot: usize) {
154        crate::dnd::move_item(&mut self.children, from, slot);
155        crate::dnd::move_item(&mut self.docks, from, slot);
156        self.debug_check_fill_order();
157    }
158
159    pub fn set_dock_mut(&mut self, index: usize, dock: Dock) {
160        if let Some(d) = self.docks.get_mut(index) {
161            *d = dock;
162        }
163        self.debug_check_fill_order();
164    }
165
166    pub fn clear_mut(&mut self) {
167        self.children.clear();
168        self.docks.clear();
169    }
170
171    fn debug_check_fill_order(&self) {
172        #[cfg(debug_assertions)]
173        {
174            let n = self.docks.len();
175            if n == 0 {
176                return;
177            }
178            if !self.ordered && self.last_child_fill {
179                log::warn!(
180                    "sequoia-view::DockArea: .last_child_fill(true) is ignored in Border mode - remove it or set it to .ordered(true)"
181                );
182            }
183            if self.ordered {
184                for i in 0..n {
185                    let effective =
186                        sequoia_layout::effective_dock(&self.docks, self.last_child_fill, i, n);
187                    if effective == Dock::Fill && i + 1 != n {
188                        log::warn!(
189                            "sequoia-view::DockArea(ordered): Fill at position {i}/{n} - children after it will get 0x0; put Fill last or use Border mode"
190                        );
191                        break;
192                    }
193                }
194            } else {
195                let fills = self.docks.iter().filter(|d| **d == Dock::Fill).count();
196                if fills > 1 {
197                    log::warn!(
198                        "sequoia-view::DockArea: {fills}× Fill - the first one takes the rest, the next ones will get 0x0"
199                    );
200                }
201            }
202        }
203    }
204
205    pub fn left<V: View + 'static>(self, child: V) -> Self {
206        self.dock(child, Dock::Left)
207    }
208
209    pub fn right<V: View + 'static>(self, child: V) -> Self {
210        self.dock(child, Dock::Right)
211    }
212
213    pub fn top<V: View + 'static>(self, child: V) -> Self {
214        self.dock(child, Dock::Top)
215    }
216
217    pub fn bottom<V: View + 'static>(self, child: V) -> Self {
218        self.dock(child, Dock::Bottom)
219    }
220
221    pub fn fill<V: View + 'static>(self, child: V) -> Self {
222        self.dock(child, Dock::Fill)
223    }
224
225    pub fn center<V: View + 'static>(self, child: V) -> Self {
226        self.fill(child)
227    }
228
229    pub fn left_if<V: View + 'static>(self, cond: bool, make: impl FnOnce() -> V) -> Self {
230        self.dock_if(cond, make, Dock::Left)
231    }
232
233    pub fn right_if<V: View + 'static>(self, cond: bool, make: impl FnOnce() -> V) -> Self {
234        self.dock_if(cond, make, Dock::Right)
235    }
236
237    pub fn top_if<V: View + 'static>(self, cond: bool, make: impl FnOnce() -> V) -> Self {
238        self.dock_if(cond, make, Dock::Top)
239    }
240
241    pub fn bottom_if<V: View + 'static>(self, cond: bool, make: impl FnOnce() -> V) -> Self {
242        self.dock_if(cond, make, Dock::Bottom)
243    }
244
245    pub fn fill_if<V: View + 'static>(self, cond: bool, make: impl FnOnce() -> V) -> Self {
246        self.dock_if(cond, make, Dock::Fill)
247    }
248
249    pub fn center_if<V: View + 'static>(self, cond: bool, make: impl FnOnce() -> V) -> Self {
250        self.dock_if(cond, make, Dock::Fill)
251    }
252
253    pub fn left_opt<V: View + 'static>(self, child: Option<V>) -> Self {
254        self.dock_opt(child, Dock::Left)
255    }
256
257    pub fn right_opt<V: View + 'static>(self, child: Option<V>) -> Self {
258        self.dock_opt(child, Dock::Right)
259    }
260
261    pub fn top_opt<V: View + 'static>(self, child: Option<V>) -> Self {
262        self.dock_opt(child, Dock::Top)
263    }
264
265    pub fn bottom_opt<V: View + 'static>(self, child: Option<V>) -> Self {
266        self.dock_opt(child, Dock::Bottom)
267    }
268
269    pub fn fill_opt<V: View + 'static>(self, child: Option<V>) -> Self {
270        self.dock_opt(child, Dock::Fill)
271    }
272
273    pub fn center_opt<V: View + 'static>(self, child: Option<V>) -> Self {
274        self.dock_opt(child, Dock::Fill)
275    }
276
277    pub fn last_child_fill(mut self, fill: bool) -> Self {
278        self.last_child_fill = fill;
279        self.debug_check_fill_order();
280        self
281    }
282
283    pub fn ordered(mut self, ordered: bool) -> Self {
284        self.ordered = ordered;
285        self.debug_check_fill_order();
286        self
287    }
288
289    pub fn border_layout(mut self, border: bool) -> Self {
290        self.ordered = !border;
291        self.debug_check_fill_order();
292        self
293    }
294
295    pub fn gap(mut self, gap: f32) -> Self {
296        self.gap = sanitize_gap(gap);
297        self
298    }
299
300    pub fn padding(mut self, padding: impl Into<Edges>) -> Self {
301        self.style = self.style.padding(padding);
302        self
303    }
304
305    pub fn pad(self, padding: impl Into<Edges>) -> Self {
306        self.padding(padding)
307    }
308
309    pub fn padding_xy(mut self, x: f32, y: f32) -> Self {
310        self.style = self.style.padding_xy(x, y);
311        self
312    }
313
314    pub fn padding_x(mut self, x: f32) -> Self {
315        self.style = self.style.padding_x(x);
316        self
317    }
318
319    pub fn padding_y(mut self, y: f32) -> Self {
320        self.style = self.style.padding_y(y);
321        self
322    }
323
324    pub fn padding_left(mut self, v: f32) -> Self {
325        self.style = self.style.padding_left(v);
326        self
327    }
328
329    pub fn padding_right(mut self, v: f32) -> Self {
330        self.style = self.style.padding_right(v);
331        self
332    }
333
334    pub fn padding_top(mut self, v: f32) -> Self {
335        self.style = self.style.padding_top(v);
336        self
337    }
338
339    pub fn padding_bottom(mut self, v: f32) -> Self {
340        self.style = self.style.padding_bottom(v);
341        self
342    }
343
344    pub fn margin(mut self, margin: impl Into<Edges>) -> Self {
345        self.style = self.style.margin(margin);
346        self
347    }
348
349    pub fn margin_xy(mut self, x: f32, y: f32) -> Self {
350        self.style = self.style.margin_xy(x, y);
351        self
352    }
353
354    pub fn margin_x(mut self, x: f32) -> Self {
355        self.style = self.style.margin_x(x);
356        self
357    }
358
359    pub fn margin_y(mut self, y: f32) -> Self {
360        self.style = self.style.margin_y(y);
361        self
362    }
363
364    pub fn margin_left(mut self, v: f32) -> Self {
365        self.style = self.style.margin_left(v);
366        self
367    }
368
369    pub fn margin_right(mut self, v: f32) -> Self {
370        self.style = self.style.margin_right(v);
371        self
372    }
373
374    pub fn margin_top(mut self, v: f32) -> Self {
375        self.style = self.style.margin_top(v);
376        self
377    }
378
379    pub fn margin_bottom(mut self, v: f32) -> Self {
380        self.style = self.style.margin_bottom(v);
381        self
382    }
383
384    pub fn indent_top(mut self, v: f32) -> Self {
385        self.style = self.style.indent_top(v);
386        self
387    }
388
389    pub fn indent_bottom(mut self, v: f32) -> Self {
390        self.style = self.style.indent_bottom(v);
391        self
392    }
393
394    pub fn indent_left(mut self, v: f32) -> Self {
395        self.style = self.style.indent_left(v);
396        self
397    }
398
399    pub fn indent_right(mut self, v: f32) -> Self {
400        self.style = self.style.indent_right(v);
401        self
402    }
403
404    pub fn clear_indents(mut self) -> Self {
405        self.style = self.style.clear_indents();
406        self
407    }
408
409    pub fn indent(mut self, indents: Indents) -> Self {
410        self.style = self.style.indent(indents);
411        self
412    }
413
414    pub fn indent_all(mut self, v: f32) -> Self {
415        self.style = self.style.indent_all(v);
416        self
417    }
418
419    pub fn indent_xy(mut self, x: f32, y: f32) -> Self {
420        self.style = self.style.indent_xy(x, y);
421        self
422    }
423
424    pub fn indent_axes(mut self, horizontal: f32, vertical: f32) -> Self {
425        self.style = self.style.indent_axes(horizontal, vertical);
426        self
427    }
428
429    pub fn width(mut self, width: impl Into<Length>) -> Self {
430        self.style = self.style.width(width.into());
431        self
432    }
433
434    pub fn height(mut self, height: impl Into<Length>) -> Self {
435        self.style = self.style.height(height.into());
436        self
437    }
438
439    pub fn size(mut self, width: impl Into<Length>, height: impl Into<Length>) -> Self {
440        self.style = self.style.width(width.into()).height(height.into());
441        self
442    }
443
444    pub fn square(mut self, side: impl Into<Length>) -> Self {
445        let side = side.into();
446        self.style = self.style.width(side).height(side);
447        self
448    }
449
450    pub fn min_width(mut self, v: f32) -> Self {
451        self.style = self.style.min_width(v);
452        self
453    }
454
455    pub fn max_width(mut self, v: f32) -> Self {
456        self.style = self.style.max_width(v);
457        self
458    }
459
460    pub fn min_height(mut self, v: f32) -> Self {
461        self.style = self.style.min_height(v);
462        self
463    }
464
465    pub fn max_height(mut self, v: f32) -> Self {
466        self.style = self.style.max_height(v);
467        self
468    }
469
470    pub fn overflow(mut self, overflow: Overflow) -> Self {
471        self.style = self.style.overflow(overflow);
472        self
473    }
474
475    pub fn flex(mut self, weight: f32) -> Self {
476        self.style = self.style.flex(weight);
477        self
478    }
479
480    pub fn flex_grow(mut self, weight: f32) -> Self {
481        self.style = self.style.flex_grow(weight);
482        self
483    }
484
485    pub fn flex_shrink(mut self, shrink: f32) -> Self {
486        self.style = self.style.flex_shrink(shrink);
487        self
488    }
489
490    pub fn flex_basis(mut self, basis: Option<Length>) -> Self {
491        self.style = self.style.flex_basis(basis);
492        self
493    }
494
495    pub fn align_self(mut self, align: Option<Align>) -> Self {
496        self.style = self.style.align_self(align);
497        self
498    }
499
500    pub fn align_self_value(mut self, align: Align) -> Self {
501        self.style = self.style.align_self_value(align);
502        self
503    }
504
505    pub fn aspect_ratio(mut self, ratio: Option<f32>) -> Self {
506        self.style = self.style.aspect_ratio(ratio);
507        self
508    }
509
510    pub fn aspect(mut self, ratio: f32) -> Self {
511        self.style = self.style.aspect(ratio);
512        self
513    }
514
515    pub fn position(mut self, position: Position) -> Self {
516        self.style = self.style.position(position);
517        self
518    }
519
520    pub fn absolute(mut self) -> Self {
521        self.style = self.style.absolute();
522        self
523    }
524
525    pub fn absolute_at(mut self, left: f32, top: f32) -> Self {
526        self.style = self.style.absolute_at(left, top);
527        self
528    }
529
530    pub fn absolute_inset(mut self, inset: Inset) -> Self {
531        self.style = self.style.absolute_inset(inset);
532        self
533    }
534
535    pub fn fit(mut self) -> Self {
536        self.style = self.style.fit();
537        self
538    }
539
540    pub fn hit_testable(mut self, on: bool) -> Self {
541        self.style = self.style.hit_testable(on);
542        self
543    }
544
545    pub fn expand(self) -> Self {
546        self.flex(1.0)
547    }
548
549    pub fn fill_width(mut self) -> Self {
550        self.style = self.style.width(Length::Fill(1.0));
551        self
552    }
553
554    pub fn fill_height(mut self) -> Self {
555        self.style = self.style.height(Length::Fill(1.0));
556        self
557    }
558
559    pub fn fill_size(self) -> Self {
560        self.fill_width().fill_height()
561    }
562
563    pub fn background(mut self, color: Color) -> Self {
564        self.deco = self.deco.background(color);
565        self
566    }
567
568    pub fn border(mut self, width: f32, color: Color) -> Self {
569        self.deco = self.deco.border(width, color);
570        self
571    }
572
573    pub fn border_left(mut self, width: f32, color: Color) -> Self {
574        self.deco = self.deco.border_left(width, color);
575        self
576    }
577
578    pub fn border_right(mut self, width: f32, color: Color) -> Self {
579        self.deco = self.deco.border_right(width, color);
580        self
581    }
582
583    pub fn border_top(mut self, width: f32, color: Color) -> Self {
584        self.deco = self.deco.border_top(width, color);
585        self
586    }
587
588    pub fn border_bottom(mut self, width: f32, color: Color) -> Self {
589        self.deco = self.deco.border_bottom(width, color);
590        self
591    }
592
593    pub fn border_x(mut self, width: f32, color: Color) -> Self {
594        self.deco = self.deco.border_x(width, color);
595        self
596    }
597
598    pub fn border_y(mut self, width: f32, color: Color) -> Self {
599        self.deco = self.deco.border_y(width, color);
600        self
601    }
602
603    pub fn radius(mut self, radius: f32) -> Self {
604        self.deco = self.deco.radius(radius);
605        self
606    }
607
608    pub fn box_style(mut self, style: BoxStyle) -> Self {
609        self.deco = style;
610        self
611    }
612
613    pub fn style(self, style: BoxStyle) -> Self {
614        self.box_style(style)
615    }
616}
617
618impl View for DockArea {
619    fn body(&self) -> impl View + 'static {
620        Unit
621    }
622    fn children_override(&self) -> Option<Vec<AnyView>> {
623        Some(self.children.clone())
624    }
625    fn layout_style(&self) -> LayoutStyle {
626        self.style
627    }
628    fn dock(&self) -> Option<DockStyle> {
629        Some(DockStyle {
630            docks: self.docks.clone(),
631            gap: self.gap,
632            last_child_fill: self.last_child_fill,
633            ordered: self.ordered,
634        })
635    }
636    fn paint_self(&self, ctx: &mut PaintCtx) {
637        ctx.paint_box(self.deco);
638    }
639}