use std::sync::Arc;
use crate::error::PaneError;
use crate::layout::Layout;
use crate::runtime::LayoutRuntime;
use crate::tree::LayoutTree;
use super::build::build_tree_for_strategy;
use super::dashboard::DashboardStrategy;
use super::holy_grail::HolyGrailStrategy;
use super::sidebar::SidebarStrategy;
use crate::panel::Axis;
use super::{ActivePanelVariant, GridColumnMode, StrategyKind};
macro_rules! impl_build_strategy {
($Builder:ty => $Variant:ident { $($field:ident),* }) => {
impl $Builder {
pub fn build(self) -> Strategy {
Strategy {
kind: StrategyKind::$Variant { $($field: self.$field),* },
}
}
}
};
}
impl_build_strategy!(MasterStackStrategy => MasterStack { master_ratio, gap });
impl_build_strategy!(CenteredMasterStrategy => CenteredMaster { master_ratio, gap });
impl_build_strategy!(DeckStrategy => Deck { master_ratio, gap });
impl_build_strategy!(ActivePanelStrategy => ActivePanel { variant, bar_height });
impl_build_strategy!(WindowStrategy => Window { panel_count, gap });
impl_build_strategy!(BinarySplitStrategy => BinarySplit { spiral, ratio, gap });
macro_rules! impl_with_panels {
($($ty:ty),+ $(,)?) => { $(
impl $ty {
pub fn with_panels(
self,
panels: impl IntoIterator<Item = impl Into<Arc<str>>>,
) -> BoundStrategy {
self.build().with_panels(panels)
}
}
)+ };
}
impl_with_panels!(
MasterStackStrategy,
CenteredMasterStrategy,
DeckStrategy,
ActivePanelStrategy,
WindowStrategy,
BinarySplitStrategy,
);
macro_rules! impl_into_strategy {
($($ty:ty),+ $(,)?) => { $(
impl From<$ty> for Strategy {
fn from(builder: $ty) -> Self {
builder.build()
}
}
)+ };
}
impl_into_strategy!(
MasterStackStrategy,
CenteredMasterStrategy,
DeckStrategy,
ActivePanelStrategy,
WindowStrategy,
BinarySplitStrategy,
SplitStrategy,
);
#[derive(Debug, Clone)]
pub struct Strategy {
pub(crate) kind: StrategyKind,
}
impl Strategy {
pub fn from_kind(kind: StrategyKind) -> Self {
Self { kind }
}
pub fn kind(&self) -> &StrategyKind {
&self.kind
}
pub fn with_panels(
self,
panels: impl IntoIterator<Item = impl Into<Arc<str>>>,
) -> BoundStrategy {
let panels: Box<[Arc<str>]> = panels.into_iter().map(Into::into).collect();
BoundStrategy {
kind: self.kind,
panels,
tree_override: None,
}
}
pub fn master_stack() -> MasterStackStrategy {
MasterStackStrategy {
master_ratio: 0.5,
gap: 0.0,
}
}
pub fn centered_master() -> CenteredMasterStrategy {
CenteredMasterStrategy {
master_ratio: 0.5,
gap: 0.0,
}
}
pub fn deck() -> DeckStrategy {
DeckStrategy {
master_ratio: 0.5,
gap: 0.0,
}
}
pub fn monocle() -> ActivePanelStrategy {
ActivePanelStrategy {
variant: ActivePanelVariant::Monocle,
bar_height: 0.0,
}
}
pub fn tabbed() -> ActivePanelStrategy {
ActivePanelStrategy {
variant: ActivePanelVariant::Tabbed,
bar_height: 1.0,
}
}
pub fn stacked() -> ActivePanelStrategy {
ActivePanelStrategy {
variant: ActivePanelVariant::Stacked,
bar_height: 1.0,
}
}
pub fn scrollable() -> WindowStrategy {
WindowStrategy {
panel_count: 2,
gap: 0.0,
}
}
pub fn dwindle() -> BinarySplitStrategy {
BinarySplitStrategy {
spiral: false,
ratio: 0.5,
gap: 0.0,
}
}
pub fn spiral() -> BinarySplitStrategy {
BinarySplitStrategy {
spiral: true,
ratio: 0.5,
gap: 0.0,
}
}
pub fn split() -> SplitStrategy {
SplitStrategy {
ratio: 0.5,
gap: 0.0,
is_vertical: false,
}
}
pub fn dashboard() -> DashboardStrategy {
DashboardStrategy {
columns: GridColumnMode::Fixed(4),
gap: 0.0,
auto_rows: false,
}
}
pub fn sidebar() -> SidebarStrategy {
SidebarStrategy::new(0.0, 20.0)
}
pub fn holy_grail() -> HolyGrailStrategy {
HolyGrailStrategy::new(0.0, 20.0, 1.0, 1.0)
}
}
pub struct BoundStrategy {
kind: StrategyKind,
panels: Box<[Arc<str>]>,
tree_override: Option<Layout>,
}
impl BoundStrategy {
pub(crate) fn new(
kind: StrategyKind,
panels: Box<[Arc<str>]>,
tree_override: Option<Layout>,
) -> Self {
Self {
kind,
panels,
tree_override,
}
}
pub fn build(self) -> Result<Layout, PaneError> {
match self.tree_override {
Some(layout) => Ok(layout),
None => {
let tree = build_tree_for_strategy(&self.kind, &self.panels)?;
Ok(Layout::from_tree(tree))
}
}
}
pub fn into_runtime(self) -> Result<LayoutRuntime, PaneError> {
match self.tree_override {
Some(layout) => {
let tree = LayoutTree::from(layout);
Ok(LayoutRuntime::from_tree_and_strategy(
tree,
self.kind,
&self.panels,
))
}
None => LayoutRuntime::from_strategy(self.kind, &self.panels),
}
}
}
macro_rules! impl_master_ratio_gap {
($($Builder:ident),+) => { $(
impl $Builder {
pub fn master_ratio(mut self, ratio: f32) -> Self {
self.master_ratio = ratio;
self
}
pub fn gap(mut self, gap: f32) -> Self {
self.gap = gap;
self
}
}
)+ };
}
#[derive(Debug, Clone)]
pub struct MasterStackStrategy {
master_ratio: f32,
gap: f32,
}
#[derive(Debug, Clone)]
pub struct CenteredMasterStrategy {
master_ratio: f32,
gap: f32,
}
#[derive(Debug, Clone)]
pub struct DeckStrategy {
master_ratio: f32,
gap: f32,
}
impl_master_ratio_gap!(MasterStackStrategy, CenteredMasterStrategy, DeckStrategy);
#[derive(Debug, Clone)]
pub struct ActivePanelStrategy {
variant: ActivePanelVariant,
bar_height: f32,
}
impl ActivePanelStrategy {
crate::macros::builder_setters!(
bar_height(height: f32)
);
}
#[derive(Debug, Clone)]
pub struct WindowStrategy {
panel_count: usize,
gap: f32,
}
impl WindowStrategy {
crate::macros::builder_setters!(
panel_count(panel_count: usize);
gap(gap: f32)
);
}
#[derive(Debug, Clone)]
pub struct BinarySplitStrategy {
spiral: bool,
ratio: f32,
gap: f32,
}
impl BinarySplitStrategy {
crate::macros::builder_setters!(
ratio(ratio: f32);
gap(gap: f32)
);
}
#[derive(Debug, Clone)]
pub struct SplitStrategy {
ratio: f32,
gap: f32,
is_vertical: bool,
}
impl SplitStrategy {
crate::macros::builder_setters!(
ratio(ratio: f32);
gap(gap: f32)
);
crate::macros::builder_flag_setters!(
vertical -> is_vertical = true
);
pub fn build(self) -> Strategy {
Strategy {
kind: StrategyKind::Sequence {
axis: match self.is_vertical {
true => Axis::Col,
false => Axis::Row,
},
gap: self.gap,
ratio: Some(self.ratio),
},
}
}
pub fn with_panels(
self,
first: impl Into<Arc<str>>,
second: impl Into<Arc<str>>,
) -> BoundStrategy {
let panels: Box<[Arc<str>]> = Box::from([first.into(), second.into()]);
let kind = self.build().kind;
BoundStrategy {
kind,
panels,
tree_override: None,
}
}
}