Skip to main content

panes/preset/
dwindle.rs

1use std::sync::Arc;
2
3use crate::builder::LayoutBuilder;
4use crate::error::PaneError;
5use crate::layout::Layout;
6use crate::panel::grow;
7use crate::preset::{
8    col_style, collect_kinds, row_style, validate_f32_param, validate_kinds, validate_share_param,
9};
10
11/// Builder for the dwindle preset layout.
12pub struct Dwindle {
13    kinds: Arc<[Arc<str>]>,
14    ratio: f32,
15    gap: f32,
16}
17
18impl Dwindle {
19    pub(crate) fn new(kinds: impl IntoIterator<Item = impl Into<Arc<str>>>) -> Self {
20        Self {
21            kinds: collect_kinds(kinds),
22            ratio: 0.5,
23            gap: 0.0,
24        }
25    }
26
27    crate::macros::builder_setters!(
28        /// Set the split ratio.
29        ratio(ratio: f32);
30        /// Set the gap between panels.
31        gap(gap: f32)
32    );
33
34    /// Consume the builder and produce a [`Layout`].
35    pub fn build(&self) -> Result<Layout, PaneError> {
36        validate_kinds(&self.kinds)?;
37        validate_share_param("ratio", self.ratio)?;
38        validate_f32_param("gap", self.gap)?;
39
40        let mut b = LayoutBuilder::new();
41        let kinds = &self.kinds;
42        let ratio = self.ratio;
43        let gap_px = self.gap;
44
45        b.row_gap(gap_px, |r| {
46            build_recursive(r, kinds, 0, ratio, gap_px, false);
47        })?;
48
49        b.build()
50    }
51}
52
53/// Recursively build dwindle/spiral layout.
54/// `reverse_even` controls spiral behavior (child order reversal on even-depth levels >= 2).
55pub(crate) fn build_recursive(
56    ctx: &mut crate::ContainerCtx,
57    kinds: &[Arc<str>],
58    depth: usize,
59    ratio: f32,
60    gap_px: f32,
61    reverse_even: bool,
62) {
63    match kinds.len() {
64        0 => {}
65        1 => {
66            ctx.panel(Arc::clone(&kinds[0]));
67        }
68        2 => add_pair(ctx, kinds, depth, ratio, reverse_even),
69        _ => add_nested(ctx, kinds, depth, ratio, gap_px, reverse_even),
70    }
71}
72
73fn add_pair(
74    ctx: &mut crate::ContainerCtx,
75    kinds: &[Arc<str>],
76    depth: usize,
77    ratio: f32,
78    reverse_even: bool,
79) {
80    let (first, second) = (Arc::clone(&kinds[0]), Arc::clone(&kinds[1]));
81    let should_reverse = reverse_even && depth >= 2 && depth.is_multiple_of(2);
82    match should_reverse {
83        true => {
84            ctx.panel_with(second, grow(1.0 - ratio));
85            ctx.panel_with(first, grow(ratio));
86        }
87        false => {
88            ctx.panel_with(first, grow(ratio));
89            ctx.panel_with(second, grow(1.0 - ratio));
90        }
91    }
92}
93
94fn add_nested(
95    ctx: &mut crate::ContainerCtx,
96    kinds: &[Arc<str>],
97    depth: usize,
98    ratio: f32,
99    gap_px: f32,
100    reverse_even: bool,
101) {
102    let first = Arc::clone(&kinds[0]);
103    let rest = &kinds[1..];
104    let should_reverse = reverse_even && depth >= 2 && depth.is_multiple_of(2);
105    let next_depth = depth + 1;
106
107    // Alternate between row and col at each depth
108    let nest_style = match next_depth.is_multiple_of(2) {
109        true => row_style(1.0 - ratio, gap_px),
110        false => col_style(1.0 - ratio, gap_px),
111    };
112
113    match should_reverse {
114        true => {
115            ctx.taffy_node(nest_style, |inner| {
116                build_recursive(inner, rest, next_depth, ratio, gap_px, reverse_even);
117            });
118            ctx.panel_with(first, grow(ratio));
119        }
120        false => {
121            ctx.panel_with(first, grow(ratio));
122            ctx.taffy_node(nest_style, |inner| {
123                build_recursive(inner, rest, next_depth, ratio, gap_px, reverse_even);
124            });
125        }
126    }
127}
128
129super::impl_preset!(
130    Dwindle,
131    runtime(kinds, |this| crate::strategy::StrategyKind::BinarySplit {
132        spiral: false,
133        ratio: this.ratio,
134        gap: this.gap,
135    })
136);