use std::sync::Arc;
use crate::decoration::DecorationRole;
use crate::error::{PaneError, TreeError};
use crate::node::{NodeId, PanelId};
use crate::panel::Constraints;
use crate::strategy::{CardSpan, GridColumnMode};
use crate::tree::LayoutTree;
use crate::validate::{check_f32_non_negative, float_invalid_to_constraint};
fn validate_gap(value: f32) -> Result<(), PaneError> {
check_f32_non_negative(value)
.map_err(|e| PaneError::InvalidConstraint(float_invalid_to_constraint("gap", e)))
}
fn sentinel_panel() -> PanelId {
PanelId::from_raw(u32::MAX)
}
fn try_alloc(
children: &mut Vec<NodeId>,
error: &mut Option<PaneError>,
alloc: impl FnOnce() -> Result<(PanelId, NodeId), PaneError>,
) -> PanelId {
if error.is_some() {
return sentinel_panel();
}
match alloc() {
Ok((pid, nid)) => {
children.push(nid);
pid
}
Err(e) => {
*error = Some(e);
sentinel_panel()
}
}
}
pub struct LayoutBuilder {
tree: LayoutTree,
root_set: bool,
}
impl LayoutBuilder {
pub fn new() -> Self {
Self {
tree: LayoutTree::new(),
root_set: false,
}
}
pub fn panel(&mut self, kind: impl Into<std::sync::Arc<str>>) -> Result<PanelId, PaneError> {
self.panel_with(kind, crate::panel::grow(1.0))
}
pub fn panel_with(
&mut self,
kind: impl Into<std::sync::Arc<str>>,
constraints: Constraints,
) -> Result<PanelId, PaneError> {
let (pid, _nid) = self.tree.add_panel(kind, constraints)?;
Ok(pid)
}
pub fn row(&mut self, f: impl FnOnce(&mut ContainerCtx)) -> Result<(), PaneError> {
self.row_gap(0.0, f)
}
pub fn row_gap(
&mut self,
gap: f32,
f: impl FnOnce(&mut ContainerCtx),
) -> Result<(), PaneError> {
self.require_no_root()?;
validate_gap(gap)?;
let children = collect_children(&mut self.tree, f)?;
let nid = self.tree.add_row(gap, children)?;
self.tree.set_root(nid);
self.root_set = true;
Ok(())
}
pub fn col(&mut self, f: impl FnOnce(&mut ContainerCtx)) -> Result<(), PaneError> {
self.col_gap(0.0, f)
}
pub fn col_gap(
&mut self,
gap: f32,
f: impl FnOnce(&mut ContainerCtx),
) -> Result<(), PaneError> {
self.require_no_root()?;
validate_gap(gap)?;
let children = collect_children(&mut self.tree, f)?;
let nid = self.tree.add_col(gap, children)?;
self.tree.set_root(nid);
self.root_set = true;
Ok(())
}
pub fn grid(&mut self, grid: Grid, f: impl FnOnce(&mut GridCtx)) -> Result<(), PaneError> {
self.require_no_root()?;
crate::preset::validate_grid_columns(grid.columns)?;
validate_gap(grid.gap)?;
let children = collect_grid_children(&mut self.tree, f)?;
let nid = self
.tree
.add_grid(grid.columns, grid.gap, grid.auto_rows, children)?;
self.tree.set_root(nid);
self.root_set = true;
Ok(())
}
pub fn set_window_panel_count(&mut self, panel_count: usize) -> Result<(), PaneError> {
self.tree.set_window_panel_count(panel_count)
}
pub fn build(self) -> Result<crate::layout::Layout, PaneError> {
match self.root_set {
false => return Err(PaneError::InvalidTree(TreeError::RootNotSet)),
true => {}
}
self.tree.validate()?;
Ok(crate::layout::Layout::from_tree(self.tree))
}
fn require_no_root(&self) -> Result<(), PaneError> {
match self.root_set {
true => Err(PaneError::InvalidTree(TreeError::RootAlreadySet)),
false => Ok(()),
}
}
}
impl Default for LayoutBuilder {
fn default() -> Self {
Self::new()
}
}
pub struct ContainerCtx<'a> {
tree: &'a mut LayoutTree,
children: Vec<NodeId>,
error: Option<PaneError>,
}
impl ContainerCtx<'_> {
pub fn add(&mut self, pid: PanelId) {
match self.error {
Some(_) => return,
None => {}
}
match self.tree.node_for_panel(pid) {
Some(nid) => self.children.push(nid),
None => self.error = Some(PaneError::PanelNotFound(pid)),
}
}
pub fn panel(&mut self, kind: impl Into<std::sync::Arc<str>>) -> PanelId {
self.panel_with(kind, crate::panel::grow(1.0))
}
pub fn panel_with(
&mut self,
kind: impl Into<std::sync::Arc<str>>,
constraints: Constraints,
) -> PanelId {
let tree = &mut *self.tree;
try_alloc(&mut self.children, &mut self.error, move || {
tree.add_panel(kind, constraints)
})
}
pub(crate) fn decoration_panel(
&mut self,
content_kind: Arc<str>,
constraints: Constraints,
role: DecorationRole,
) -> PanelId {
let tree = &mut *self.tree;
try_alloc(&mut self.children, &mut self.error, move || {
tree.add_decoration_panel(content_kind, constraints, role)
})
}
pub fn row(&mut self, f: impl FnOnce(&mut ContainerCtx)) {
self.row_gap(0.0, f);
}
pub fn row_gap(&mut self, gap: f32, f: impl FnOnce(&mut ContainerCtx)) {
self.container_with_gap(gap, f, |tree, c| tree.add_row(gap, c));
}
pub fn row_with(&mut self, constraints: Constraints, f: impl FnOnce(&mut ContainerCtx)) {
self.row_gap_with(0.0, constraints, f);
}
pub fn row_gap_with(
&mut self,
gap: f32,
constraints: Constraints,
f: impl FnOnce(&mut ContainerCtx),
) {
self.container_with_gap(gap, f, |tree, c| {
tree.add_row_constrained(gap, Some(constraints), c)
});
}
pub fn col(&mut self, f: impl FnOnce(&mut ContainerCtx)) {
self.col_gap(0.0, f);
}
pub fn col_gap(&mut self, gap: f32, f: impl FnOnce(&mut ContainerCtx)) {
self.container_with_gap(gap, f, |tree, c| tree.add_col(gap, c));
}
pub fn col_with(&mut self, constraints: Constraints, f: impl FnOnce(&mut ContainerCtx)) {
self.col_gap_with(0.0, constraints, f);
}
pub fn col_gap_with(
&mut self,
gap: f32,
constraints: Constraints,
f: impl FnOnce(&mut ContainerCtx),
) {
self.container_with_gap(gap, f, |tree, c| {
tree.add_col_constrained(gap, Some(constraints), c)
});
}
pub fn grid(&mut self, grid: Grid, f: impl FnOnce(&mut GridCtx)) {
build_nested_grid(self.tree, &mut self.children, &mut self.error, grid, f);
}
pub fn taffy_node(&mut self, style: taffy::Style, f: impl FnOnce(&mut ContainerCtx)) {
self.add_container(f, |tree, c| tree.add_taffy_node(style, c));
}
fn container_with_gap(
&mut self,
gap: f32,
f: impl FnOnce(&mut ContainerCtx),
build: impl FnOnce(&mut LayoutTree, Vec<NodeId>) -> Result<NodeId, PaneError>,
) {
match validate_gap(gap) {
Err(e) => self.set_error(e),
Ok(()) => self.add_container(f, build),
}
}
fn add_container(
&mut self,
f: impl FnOnce(&mut ContainerCtx),
build: impl FnOnce(&mut LayoutTree, Vec<NodeId>) -> Result<NodeId, PaneError>,
) {
match self.error {
Some(_) => return,
None => {}
}
let children = match collect_children(self.tree, f) {
Ok(c) => c,
Err(e) => {
self.error = Some(e);
return;
}
};
match build(self.tree, children) {
Ok(nid) => self.children.push(nid),
Err(e) => self.error = Some(e),
}
}
pub(crate) fn set_error(&mut self, err: PaneError) {
match self.error {
None => self.error = Some(err),
Some(_) => {}
}
}
}
fn collect_children(
tree: &mut LayoutTree,
f: impl FnOnce(&mut ContainerCtx),
) -> Result<Vec<NodeId>, PaneError> {
let mut ctx = ContainerCtx {
tree,
children: Vec::new(),
error: None,
};
f(&mut ctx);
match ctx.error {
Some(e) => Err(e),
None => Ok(ctx.children),
}
}
#[derive(Debug, Clone, Copy)]
pub struct Grid {
pub(crate) columns: GridColumnMode,
pub(crate) gap: f32,
pub(crate) auto_rows: bool,
}
impl Grid {
pub fn columns(n: usize) -> Self {
Self::from_column_mode(GridColumnMode::Fixed(n))
}
pub fn auto_fill(min_width: f32) -> Self {
Self::from_column_mode(GridColumnMode::AutoFill { min_width })
}
pub fn auto_fit(min_width: f32) -> Self {
Self::from_column_mode(GridColumnMode::AutoFit { min_width })
}
pub(crate) fn from_column_mode(columns: GridColumnMode) -> Self {
Self {
columns,
gap: 0.0,
auto_rows: false,
}
}
pub fn gap(mut self, gap: f32) -> Self {
self.gap = gap;
self
}
pub fn auto_rows(mut self) -> Self {
self.auto_rows = true;
self
}
pub(crate) fn apply_auto_rows(mut self, enabled: bool) -> Self {
self.auto_rows = enabled;
self
}
}
pub struct GridCtx<'a> {
tree: &'a mut LayoutTree,
children: Vec<NodeId>,
error: Option<PaneError>,
}
impl GridCtx<'_> {
pub fn panel(&mut self, kind: impl Into<Arc<str>>) -> PanelId {
self.panel_with(kind, crate::panel::grow(1.0))
}
pub fn panel_with(&mut self, kind: impl Into<Arc<str>>, constraints: Constraints) -> PanelId {
let tree = &mut *self.tree;
try_alloc(&mut self.children, &mut self.error, move || {
tree.add_panel(kind, constraints)
})
}
pub fn panel_span(&mut self, kind: impl Into<Arc<str>>, span: CardSpan) -> PanelId {
self.panel_with_span(kind, crate::panel::grow(1.0), span)
}
pub fn panel_with_span(
&mut self,
kind: impl Into<Arc<str>>,
constraints: Constraints,
span: CardSpan,
) -> PanelId {
match self.error {
Some(_) => return sentinel_panel(),
None => {}
}
match crate::preset::validate_grid_span(span) {
Err(e) => {
self.error = Some(e);
return sentinel_panel();
}
Ok(()) => {}
}
let (pid, panel_nid) = match self.tree.add_panel(kind, constraints) {
Ok(pair) => pair,
Err(e) => {
self.error = Some(e);
return sentinel_panel();
}
};
match self.tree.add_grid_item(span, panel_nid) {
Ok(wrapper_nid) => {
self.children.push(wrapper_nid);
pid
}
Err(e) => self.fail_grid_item_wrapper(pid, e),
}
}
fn fail_grid_item_wrapper(&mut self, pid: PanelId, error: PaneError) -> PanelId {
let rollback = self.tree.remove_panel(pid);
self.error = Some(match rollback {
Ok(()) => error,
Err(remove_error) => remove_error,
});
sentinel_panel()
}
pub fn row(&mut self, f: impl FnOnce(&mut ContainerCtx)) {
self.row_gap(0.0, f);
}
pub fn row_gap(&mut self, gap: f32, f: impl FnOnce(&mut ContainerCtx)) {
self.add_container_with_gap(gap, f, |tree, g, c| tree.add_row(g, c));
}
pub fn col(&mut self, f: impl FnOnce(&mut ContainerCtx)) {
self.col_gap(0.0, f);
}
pub fn col_gap(&mut self, gap: f32, f: impl FnOnce(&mut ContainerCtx)) {
self.add_container_with_gap(gap, f, |tree, g, c| tree.add_col(g, c));
}
pub fn row_gap_with(
&mut self,
gap: f32,
constraints: Constraints,
f: impl FnOnce(&mut ContainerCtx),
) {
self.add_container_with_gap(gap, f, |tree, g, c| {
tree.add_row_constrained(g, Some(constraints), c)
});
}
pub fn col_gap_with(
&mut self,
gap: f32,
constraints: Constraints,
f: impl FnOnce(&mut ContainerCtx),
) {
self.add_container_with_gap(gap, f, |tree, g, c| {
tree.add_col_constrained(g, Some(constraints), c)
});
}
pub fn grid(&mut self, grid: Grid, f: impl FnOnce(&mut GridCtx)) {
build_nested_grid(self.tree, &mut self.children, &mut self.error, grid, f);
}
fn add_container_with_gap(
&mut self,
gap: f32,
f: impl FnOnce(&mut ContainerCtx),
build: impl FnOnce(&mut LayoutTree, f32, Vec<NodeId>) -> Result<NodeId, PaneError>,
) {
match self.error {
Some(_) => return,
None => {}
}
match validate_gap(gap) {
Err(e) => {
self.error = Some(e);
return;
}
Ok(()) => {}
}
let children = match collect_children(self.tree, f) {
Ok(c) => c,
Err(e) => {
self.error = Some(e);
return;
}
};
match build(self.tree, gap, children) {
Ok(nid) => self.children.push(nid),
Err(e) => self.error = Some(e),
}
}
}
fn build_nested_grid(
tree: &mut LayoutTree,
children: &mut Vec<NodeId>,
error: &mut Option<PaneError>,
grid: Grid,
f: impl FnOnce(&mut GridCtx),
) {
match *error {
Some(_) => return,
None => {}
}
match crate::preset::validate_grid_columns(grid.columns) {
Err(e) => {
*error = Some(e);
return;
}
Ok(()) => {}
}
match validate_gap(grid.gap) {
Err(e) => {
*error = Some(e);
return;
}
Ok(()) => {}
}
let grid_children = match collect_grid_children(tree, f) {
Ok(c) => c,
Err(e) => {
*error = Some(e);
return;
}
};
match tree.add_grid(grid.columns, grid.gap, grid.auto_rows, grid_children) {
Ok(nid) => children.push(nid),
Err(e) => *error = Some(e),
}
}
fn collect_grid_children(
tree: &mut LayoutTree,
f: impl FnOnce(&mut GridCtx),
) -> Result<Vec<NodeId>, PaneError> {
let mut ctx = GridCtx {
tree,
children: Vec::new(),
error: None,
};
f(&mut ctx);
match ctx.error {
Some(e) => Err(e),
None => Ok(ctx.children),
}
}