Skip to main content

panes/
layout.rs

1use std::sync::Arc;
2
3use crate::builder::{ContainerCtx, LayoutBuilder};
4use crate::compiler::compile;
5use crate::error::PaneError;
6use crate::preset::PresetInfo;
7use crate::resolver::ResolvedLayout;
8use crate::tree::LayoutTree;
9
10/// An immutable, validated layout ready for resolution.
11pub struct Layout {
12    tree: LayoutTree,
13}
14
15impl std::fmt::Debug for Layout {
16    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17        f.debug_struct("Layout")
18            .field("panel_count", &self.tree.panel_count())
19            .finish()
20    }
21}
22
23impl Layout {
24    /// Create a `Layout` from a validated tree. Called by `LayoutBuilder::build()`.
25    pub(crate) fn from_tree(tree: LayoutTree) -> Self {
26        Self { tree }
27    }
28
29    /// Borrow the underlying tree for read-only traversal.
30    pub fn tree(&self) -> &LayoutTree {
31        &self.tree
32    }
33
34    /// How many panels the active window shows at once.
35    pub fn window_size(&self) -> usize {
36        self.tree.window_size()
37    }
38
39    /// Compile, compute, and resolve the layout at the given viewport size.
40    pub fn resolve(&self, width: f32, height: f32) -> Result<ResolvedLayout, PaneError> {
41        self.tree.resolve(width, height)
42    }
43
44    // -- Convenience constructors --
45
46    /// Build a row layout from a closure.
47    pub fn build_row(f: impl FnOnce(&mut ContainerCtx)) -> Result<Self, PaneError> {
48        let mut b = LayoutBuilder::new();
49        b.row(f)?;
50        b.build()
51    }
52
53    /// Build a column layout from a closure.
54    pub fn build_col(f: impl FnOnce(&mut ContainerCtx)) -> Result<Self, PaneError> {
55        let mut b = LayoutBuilder::new();
56        b.col(f)?;
57        b.build()
58    }
59
60    /// Build a row layout with gap from a closure.
61    pub fn build_row_gap(gap: f32, f: impl FnOnce(&mut ContainerCtx)) -> Result<Self, PaneError> {
62        let mut b = LayoutBuilder::new();
63        b.row_gap(gap, f)?;
64        b.build()
65    }
66
67    /// Build a column layout with gap from a closure.
68    pub fn build_col_gap(gap: f32, f: impl FnOnce(&mut ContainerCtx)) -> Result<Self, PaneError> {
69        let mut b = LayoutBuilder::new();
70        b.col_gap(gap, f)?;
71        b.build()
72    }
73
74    /// Equal-grow panels in a row, zero gap.
75    pub fn row(kinds: impl IntoIterator<Item = impl Into<Arc<str>>>) -> Result<Self, PaneError> {
76        let kinds: Vec<Arc<str>> = kinds.into_iter().map(Into::into).collect();
77        let mut b = LayoutBuilder::new();
78        b.row(|r| {
79            for kind in &kinds {
80                r.panel(Arc::clone(kind));
81            }
82        })?;
83        b.build()
84    }
85
86    /// Equal-grow panels in a column, zero gap.
87    pub fn col(kinds: impl IntoIterator<Item = impl Into<Arc<str>>>) -> Result<Self, PaneError> {
88        let kinds: Vec<Arc<str>> = kinds.into_iter().map(Into::into).collect();
89        let mut b = LayoutBuilder::new();
90        b.col(|r| {
91            for kind in &kinds {
92                r.panel(Arc::clone(kind));
93            }
94        })?;
95        b.build()
96    }
97
98    /// Panels in a row with explicit constraints per panel.
99    pub fn row_with(
100        panels: impl IntoIterator<Item = (impl Into<Arc<str>>, crate::panel::Constraints)>,
101    ) -> Result<Self, PaneError> {
102        let panels: Vec<_> = panels.into_iter().map(|(k, c)| (k.into(), c)).collect();
103        let mut b = LayoutBuilder::new();
104        b.row(|r| {
105            for (kind, constraints) in &panels {
106                r.panel_with(Arc::clone(kind), *constraints);
107            }
108        })?;
109        b.build()
110    }
111
112    /// Panels in a column with explicit constraints per panel.
113    pub fn col_with(
114        panels: impl IntoIterator<Item = (impl Into<Arc<str>>, crate::panel::Constraints)>,
115    ) -> Result<Self, PaneError> {
116        let panels: Vec<_> = panels.into_iter().map(|(k, c)| (k.into(), c)).collect();
117        let mut b = LayoutBuilder::new();
118        b.col(|c| {
119            for (kind, constraints) in &panels {
120                c.panel_with(Arc::clone(kind), *constraints);
121            }
122        })?;
123        b.build()
124    }
125
126    /// Return metadata for all built-in presets, sorted alphabetically by name.
127    pub fn presets() -> &'static [PresetInfo] {
128        &crate::preset::catalog::PRESETS
129    }
130
131    // -- Preset constructors --
132
133    /// Create a [`MasterStack`](crate::preset::MasterStack) builder.
134    pub fn master_stack(
135        kinds: impl IntoIterator<Item = impl Into<Arc<str>>>,
136    ) -> crate::preset::MasterStack {
137        crate::preset::MasterStack::new(kinds)
138    }
139
140    /// Create a [`CenteredMaster`](crate::preset::CenteredMaster) builder.
141    pub fn centered_master(
142        kinds: impl IntoIterator<Item = impl Into<Arc<str>>>,
143    ) -> crate::preset::CenteredMaster {
144        crate::preset::CenteredMaster::new(kinds)
145    }
146
147    /// Create a [`Monocle`](crate::preset::Monocle) builder.
148    pub fn monocle(kinds: impl IntoIterator<Item = impl Into<Arc<str>>>) -> crate::preset::Monocle {
149        crate::preset::Monocle::new(kinds)
150    }
151
152    /// Create a [`Scrollable`](crate::preset::Scrollable) builder.
153    pub fn scrollable(
154        kinds: impl IntoIterator<Item = impl Into<Arc<str>>>,
155    ) -> crate::preset::Scrollable {
156        crate::preset::Scrollable::new(kinds)
157    }
158
159    /// Create a [`Dwindle`](crate::preset::Dwindle) builder.
160    pub fn dwindle(kinds: impl IntoIterator<Item = impl Into<Arc<str>>>) -> crate::preset::Dwindle {
161        crate::preset::Dwindle::new(kinds)
162    }
163
164    /// Create a [`Spiral`](crate::preset::Spiral) builder.
165    pub fn spiral(kinds: impl IntoIterator<Item = impl Into<Arc<str>>>) -> crate::preset::Spiral {
166        crate::preset::Spiral::new(kinds)
167    }
168
169    /// Create a [`Columns`](crate::preset::Columns) builder.
170    pub fn columns(
171        count: usize,
172        kinds: impl IntoIterator<Item = impl Into<Arc<str>>>,
173    ) -> crate::preset::Columns {
174        crate::preset::Columns::new(count, kinds)
175    }
176
177    /// Create a [`Deck`](crate::preset::Deck) builder.
178    pub fn deck(kinds: impl IntoIterator<Item = impl Into<Arc<str>>>) -> crate::preset::Deck {
179        crate::preset::Deck::new(kinds)
180    }
181
182    /// Create a [`Tabbed`](crate::preset::Tabbed) builder.
183    pub fn tabbed(kinds: impl IntoIterator<Item = impl Into<Arc<str>>>) -> crate::preset::Tabbed {
184        crate::preset::Tabbed::new(kinds)
185    }
186
187    /// Create a [`Stacked`](crate::preset::Stacked) builder.
188    pub fn stacked(kinds: impl IntoIterator<Item = impl Into<Arc<str>>>) -> crate::preset::Stacked {
189        crate::preset::Stacked::new(kinds)
190    }
191
192    /// Create a [`Sidebar`](crate::preset::Sidebar) builder.
193    pub fn sidebar(
194        sidebar_kind: impl Into<Arc<str>>,
195        content_kind: impl Into<Arc<str>>,
196    ) -> crate::preset::Sidebar {
197        crate::preset::Sidebar::new(sidebar_kind, content_kind)
198    }
199
200    /// Create a [`HolyGrail`](crate::preset::HolyGrail) builder.
201    pub fn holy_grail(
202        header: impl Into<Arc<str>>,
203        footer: impl Into<Arc<str>>,
204        left: impl Into<Arc<str>>,
205        main: impl Into<Arc<str>>,
206        right: impl Into<Arc<str>>,
207    ) -> crate::preset::HolyGrail {
208        crate::preset::HolyGrail::new(header, footer, left, main, right)
209    }
210
211    /// Create a [`Dashboard`](crate::preset::Dashboard) builder.
212    pub fn dashboard(
213        cards: impl IntoIterator<Item = (impl Into<Arc<str>>, usize)>,
214    ) -> crate::preset::Dashboard {
215        crate::preset::Dashboard::new(cards)
216    }
217
218    /// Create a [`Split`](crate::preset::Split) builder.
219    pub fn split(first: impl Into<Arc<str>>, second: impl Into<Arc<str>>) -> crate::preset::Split {
220        crate::preset::Split::new(first, second)
221    }
222
223    /// Create a [`Grid`](crate::preset::Grid) builder.
224    pub fn grid(
225        cols: usize,
226        kinds: impl IntoIterator<Item = impl Into<Arc<str>>>,
227    ) -> crate::preset::Grid {
228        crate::preset::Grid::new(cols, kinds)
229    }
230}
231
232impl Layout {
233    /// Parse a TOML configuration string into a `Layout`.
234    #[cfg(feature = "toml")]
235    pub fn from_toml(input: &str) -> Result<Self, crate::toml_parse::TomlError> {
236        crate::toml_parse::parse(input)
237    }
238
239    /// Read a TOML file from disk and parse it into a `Layout`.
240    #[cfg(feature = "toml")]
241    pub fn from_toml_file(
242        path: impl AsRef<std::path::Path>,
243    ) -> Result<Self, crate::toml_parse::TomlError> {
244        let input = std::fs::read_to_string(path)?;
245        crate::toml_parse::parse(&input)
246    }
247
248    /// Compile the layout tree into a Taffy tree ready for layout computation.
249    pub fn compile(&self) -> Result<crate::compiler::CompileResult, PaneError> {
250        compile(&self.tree)
251    }
252}
253
254impl From<Layout> for LayoutTree {
255    fn from(layout: Layout) -> Self {
256        layout.tree
257    }
258}