use std::cmp::Ordering;
use std::marker::PhantomData;
use ratatui::layout::Rect;
use modalkit::{
editing::application::ApplicationInfo,
editing::store::Store,
errors::{EditResult, UIResult},
prelude::*,
};
use self::layout::LayoutOps;
use self::size::{ResizeInfo, SizeDescription, MIN_WIN_LEN};
use self::tree::SubtreeOps;
mod layout;
mod size;
mod slot;
mod tree;
pub use self::layout::{
WindowLayout,
WindowLayoutDescription,
WindowLayoutRoot,
WindowLayoutState,
};
struct AxisTreeNode<W, X: AxisT, Y: AxisT> {
value: Value<W, X, Y>,
info: Info,
left: Option<Box<Self>>,
right: Option<Box<Self>>,
_p: PhantomData<(X, Y)>,
}
type AxisTree<W, X, Y> = Option<Box<AxisTreeNode<W, X, Y>>>;
struct Info {
dimensions: (usize, usize),
size: usize,
weight: usize,
}
impl Info {
fn from<W, X: AxisT, Y: AxisT>(
value: &Value<W, X, Y>,
left: &AxisTree<W, X, Y>,
right: &AxisTree<W, X, Y>,
) -> Info {
let size = left.size() + value.size() + right.size();
let weight = left.weight() + 1 + right.weight();
let (lw, lh) = left.dimensions();
let (vw, vh) = value.dimensions();
let (rw, rh) = right.dimensions();
let dimensions = match X::axis() {
Axis::Horizontal => (lw.max(rw).max(vw), lh + rh + vh),
Axis::Vertical => (lw + rw + vw, lh.max(rh).max(vh)),
};
Info { size, weight, dimensions }
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
struct TreeInfo {
area: Rect,
resized: ResizeInfo,
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
struct WindowInfo {
area: Rect,
}
impl WindowInfo {
fn to_tree(&self, dimension: Option<Axis>) -> TreeInfo {
let mut ri = ResizeInfo::default();
if let Some(axis) = dimension {
let len = self.get_length(axis);
let sd = SizeDescription::new(0..1, len, MIN_WIN_LEN);
ri.lengths = Some(vec![sd]);
}
TreeInfo { area: self.area, resized: ri }
}
fn get_length(&self, axis: Axis) -> u16 {
match axis {
Axis::Horizontal => self.area.height,
Axis::Vertical => self.area.width,
}
}
}
enum Value<W, X: AxisT, Y: AxisT> {
Window(W, WindowInfo),
Tree(Box<AxisTreeNode<W, Y, X>>, TreeInfo),
}
impl<W, X, Y> Value<W, X, Y>
where
X: AxisT,
Y: AxisT,
{
fn area(&self) -> Rect {
match self {
Value::Window(_, info) => info.area,
Value::Tree(_, info) => info.area,
}
}
}
impl<W, X, Y> From<W> for Value<W, X, Y>
where
X: AxisT,
Y: AxisT,
{
fn from(w: W) -> Self {
Value::Window(w, WindowInfo::default())
}
}
impl<W, X, Y> From<AxisTreeNode<W, Y, X>> for Value<W, X, Y>
where
X: AxisT,
Y: AxisT,
{
fn from(tree: AxisTreeNode<W, Y, X>) -> Self {
Value::Tree(Box::new(tree), TreeInfo::default())
}
}
impl<W, X, Y> From<AxisTreeNode<W, X, Y>> for AxisTree<W, X, Y>
where
X: AxisT,
Y: AxisT,
{
fn from(node: AxisTreeNode<W, X, Y>) -> AxisTree<W, X, Y> {
Some(Box::new(node))
}
}
impl<W, X, Y> From<Value<W, X, Y>> for AxisTree<W, X, Y>
where
X: AxisT,
Y: AxisT,
{
fn from(value: Value<W, X, Y>) -> AxisTree<W, X, Y> {
Some(Box::new(AxisTreeNode::from(value)))
}
}
impl<W, X, Y> From<Value<W, X, Y>> for AxisTreeNode<W, X, Y>
where
X: AxisT,
Y: AxisT,
{
fn from(value: Value<W, X, Y>) -> AxisTreeNode<W, X, Y> {
AxisTreeNode::new(value, None, None)
}
}
impl<W, X, Y> From<W> for AxisTreeNode<W, X, Y>
where
X: AxisT,
Y: AxisT,
{
fn from(window: W) -> AxisTreeNode<W, X, Y> {
AxisTreeNode::from(Value::from(window))
}
}
trait AxisT: std::fmt::Debug {
fn axis() -> Axis;
}
#[derive(Debug)]
enum HorizontalT {}
impl AxisT for HorizontalT {
fn axis() -> Axis {
Axis::Horizontal
}
}
#[derive(Debug)]
enum VerticalT {}
impl AxisT for VerticalT {
fn axis() -> Axis {
Axis::Vertical
}
}
fn winnr_cmp(at: usize, lsize: usize, vsize: usize) -> (Ordering, usize) {
if at < lsize {
(Ordering::Less, at)
} else if at - lsize < vsize {
(Ordering::Equal, at - lsize)
} else {
(Ordering::Greater, at - lsize - vsize)
}
}
pub(super) trait WindowActions<C, I>
where
I: ApplicationInfo,
{
fn window_close(
&mut self,
target: &WindowTarget,
flags: CloseFlags,
ctx: &C,
store: &mut Store<I>,
) -> EditResult<EditInfo, I>;
fn window_exchange(
&mut self,
change: &FocusChange,
ctx: &C,
store: &mut Store<I>,
) -> EditResult<EditInfo, I>;
fn window_focus(
&mut self,
change: &FocusChange,
ctx: &C,
store: &mut Store<I>,
) -> EditResult<EditInfo, I>;
fn window_move_side(
&mut self,
dir: MoveDir2D,
ctx: &C,
store: &mut Store<I>,
) -> EditResult<EditInfo, I>;
fn window_clear_sizes(&mut self, ctx: &C, store: &mut Store<I>) -> EditResult<EditInfo, I>;
fn window_resize(
&mut self,
target: &FocusChange,
axis: Axis,
size: &SizeChange<Count>,
ctx: &C,
store: &mut Store<I>,
) -> EditResult<EditInfo, I>;
fn window_rotate(
&mut self,
dir: MoveDir1D,
ctx: &C,
store: &mut Store<I>,
) -> EditResult<EditInfo, I>;
fn window_split(
&mut self,
target: &OpenTarget<I::WindowId>,
axis: Axis,
rel: MoveDir1D,
count: &Count,
ctx: &C,
store: &mut Store<I>,
) -> UIResult<EditInfo, I>;
fn window_open(
&mut self,
target: &OpenTarget<I::WindowId>,
axis: Axis,
rel: MoveDir1D,
count: &Count,
ctx: &C,
store: &mut Store<I>,
) -> UIResult<EditInfo, I>;
fn window_switch(
&mut self,
target: &OpenTarget<I::WindowId>,
ctx: &C,
store: &mut Store<I>,
) -> UIResult<EditInfo, I>;
fn window_write(
&mut self,
target: &WindowTarget,
path: Option<&str>,
flags: WriteFlags,
ctx: &C,
store: &mut Store<I>,
) -> UIResult<EditInfo, I>;
fn window_zoom_toggle(&mut self, ctx: &C, store: &mut Store<I>) -> EditResult<EditInfo, I>;
}