use serde::{Deserialize, Serialize};
use crate::SplitDirection;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "wasm", derive(tsify_next::Tsify))]
#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
pub enum LogicalLayout {
Single {
buffer_id: u64,
viewport_id: u64,
},
Split {
direction: SplitDirection,
children: Vec<Self>,
ratios: Vec<f32>,
},
Tabs {
tabs: Vec<Self>,
active: usize,
},
}
impl LogicalLayout {
#[must_use]
pub const fn single(buffer_id: u64, viewport_id: u64) -> Self {
Self::Single {
buffer_id,
viewport_id,
}
}
#[must_use]
#[allow(clippy::cast_precision_loss)] pub fn hsplit(children: Vec<Self>) -> Self {
let count = children.len();
let ratio = 1.0 / count as f32;
Self::Split {
direction: SplitDirection::Horizontal,
children,
ratios: vec![ratio; count],
}
}
#[must_use]
#[allow(clippy::cast_precision_loss)] pub fn vsplit(children: Vec<Self>) -> Self {
let count = children.len();
let ratio = 1.0 / count as f32;
Self::Split {
direction: SplitDirection::Vertical,
children,
ratios: vec![ratio; count],
}
}
#[must_use]
pub fn split_with_ratios(
direction: SplitDirection,
children: Vec<Self>,
ratios: Vec<f32>,
) -> Self {
debug_assert_eq!(children.len(), ratios.len());
Self::Split {
direction,
children,
ratios,
}
}
#[must_use]
#[cfg_attr(coverage_nightly, coverage(off))]
pub fn tabs(tabs: Vec<Self>, active: usize) -> Self {
debug_assert!(active < tabs.len() || tabs.is_empty());
Self::Tabs { tabs, active }
}
#[must_use]
pub fn window_count(&self) -> usize {
match self {
Self::Single { .. } => 1,
Self::Split { children, .. } => children.iter().map(Self::window_count).sum(),
Self::Tabs { tabs, .. } => tabs.iter().map(Self::window_count).sum(),
}
}
#[must_use]
pub const fn is_leaf(&self) -> bool {
matches!(self, Self::Single { .. })
}
#[must_use]
pub const fn buffer_id(&self) -> Option<u64> {
match self {
Self::Single { buffer_id, .. } => Some(*buffer_id),
_ => None,
}
}
#[must_use]
pub fn find_viewport(&self, target_viewport: u64) -> Option<Vec<usize>> {
self.find_viewport_inner(target_viewport, Vec::new())
}
fn find_viewport_inner(&self, target: u64, path: Vec<usize>) -> Option<Vec<usize>> {
match self {
Self::Single { viewport_id, .. } => {
if *viewport_id == target {
Some(path)
} else {
None
}
}
Self::Split { children, .. } | Self::Tabs { tabs: children, .. } => {
for (i, child) in children.iter().enumerate() {
let mut child_path = path.clone();
child_path.push(i);
if let Some(found) = child.find_viewport_inner(target, child_path) {
return Some(found);
}
}
None
}
}
}
}
#[cfg(test)]
#[path = "layout_tests.rs"]
mod tests;