use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SplitNode {
Leaf {
project_id: Option<String>,
id: usize,
},
Horizontal {
left: Box<SplitNode>,
right: Box<SplitNode>,
ratio: f32,
},
Vertical {
top: Box<SplitNode>,
bottom: Box<SplitNode>,
ratio: f32,
},
}
impl SplitNode {
pub fn new_leaf(id: usize) -> Self {
SplitNode::Leaf {
project_id: None,
id,
}
}
pub fn split_horizontal(&mut self, new_id: usize) {
let old = std::mem::replace(self, SplitNode::new_leaf(0));
*self = SplitNode::Horizontal {
left: Box::new(old),
right: Box::new(SplitNode::new_leaf(new_id)),
ratio: 0.5,
};
}
pub fn split_vertical(&mut self, new_id: usize) {
let old = std::mem::replace(self, SplitNode::new_leaf(0));
*self = SplitNode::Vertical {
top: Box::new(old),
bottom: Box::new(SplitNode::new_leaf(new_id)),
ratio: 0.5,
};
}
pub fn find_pane_mut(&mut self, id: usize) -> Option<&mut SplitNode> {
match self {
SplitNode::Leaf { id: leaf_id, .. } if *leaf_id == id => Some(self),
SplitNode::Horizontal { left, right, .. } => {
left.find_pane_mut(id).or_else(|| right.find_pane_mut(id))
}
SplitNode::Vertical { top, bottom, .. } => {
top.find_pane_mut(id).or_else(|| bottom.find_pane_mut(id))
}
_ => None,
}
}
pub fn find_pane(&self, id: usize) -> Option<&SplitNode> {
match self {
SplitNode::Leaf { id: leaf_id, .. } if *leaf_id == id => Some(self),
SplitNode::Horizontal { left, right, .. } => {
left.find_pane(id).or_else(|| right.find_pane(id))
}
SplitNode::Vertical { top, bottom, .. } => {
top.find_pane(id).or_else(|| bottom.find_pane(id))
}
_ => None,
}
}
pub fn close_pane(&mut self, id: usize) -> bool {
match self {
SplitNode::Leaf { id: leaf_id, .. } if *leaf_id == id => {
false
}
SplitNode::Horizontal { left, right, .. } => {
if let SplitNode::Leaf { id: left_id, .. } = left.as_ref() {
if *left_id == id {
*self = *right.clone();
return true;
}
}
if let SplitNode::Leaf { id: right_id, .. } = right.as_ref() {
if *right_id == id {
*self = *left.clone();
return true;
}
}
left.close_pane(id) || right.close_pane(id)
}
SplitNode::Vertical { top, bottom, .. } => {
if let SplitNode::Leaf { id: top_id, .. } = top.as_ref() {
if *top_id == id {
*self = *bottom.clone();
return true;
}
}
if let SplitNode::Leaf { id: bottom_id, .. } = bottom.as_ref() {
if *bottom_id == id {
*self = *top.clone();
return true;
}
}
top.close_pane(id) || bottom.close_pane(id)
}
_ => false,
}
}
pub fn collect_pane_ids(&self) -> Vec<usize> {
match self {
SplitNode::Leaf { id, .. } => vec![*id],
SplitNode::Horizontal { left, right, .. } => {
let mut ids = left.collect_pane_ids();
ids.extend(right.collect_pane_ids());
ids
}
SplitNode::Vertical { top, bottom, .. } => {
let mut ids = top.collect_pane_ids();
ids.extend(bottom.collect_pane_ids());
ids
}
}
}
pub fn find_adjacent_pane(&self, current_id: usize, direction: Direction) -> Option<usize> {
self.find_adjacent_internal(current_id, direction, true)
}
fn find_adjacent_internal(&self, current_id: usize, direction: Direction, is_root: bool) -> Option<usize> {
match self {
SplitNode::Leaf { id, .. } => {
if *id == current_id {
None
} else if is_root {
None
} else {
Some(*id)
}
}
SplitNode::Horizontal { left, right, .. } => {
match direction {
Direction::Left => {
if right.contains_pane(current_id) {
left.get_rightmost_pane()
} else {
left.find_adjacent_internal(current_id, direction, false)
}
}
Direction::Right => {
if left.contains_pane(current_id) {
right.get_leftmost_pane()
} else {
right.find_adjacent_internal(current_id, direction, false)
}
}
Direction::Up | Direction::Down => {
left.find_adjacent_internal(current_id, direction, false)
.or_else(|| right.find_adjacent_internal(current_id, direction, false))
}
}
}
SplitNode::Vertical { top, bottom, .. } => {
match direction {
Direction::Up => {
if bottom.contains_pane(current_id) {
top.get_bottommost_pane()
} else {
top.find_adjacent_internal(current_id, direction, false)
}
}
Direction::Down => {
if top.contains_pane(current_id) {
bottom.get_topmost_pane()
} else {
bottom.find_adjacent_internal(current_id, direction, false)
}
}
Direction::Left | Direction::Right => {
top.find_adjacent_internal(current_id, direction, false)
.or_else(|| bottom.find_adjacent_internal(current_id, direction, false))
}
}
}
}
}
fn contains_pane(&self, id: usize) -> bool {
match self {
SplitNode::Leaf { id: leaf_id, .. } => *leaf_id == id,
SplitNode::Horizontal { left, right, .. } => {
left.contains_pane(id) || right.contains_pane(id)
}
SplitNode::Vertical { top, bottom, .. } => {
top.contains_pane(id) || bottom.contains_pane(id)
}
}
}
fn get_leftmost_pane(&self) -> Option<usize> {
match self {
SplitNode::Leaf { id, .. } => Some(*id),
SplitNode::Horizontal { left, .. } => left.get_leftmost_pane(),
SplitNode::Vertical { top, .. } => top.get_leftmost_pane(),
}
}
fn get_rightmost_pane(&self) -> Option<usize> {
match self {
SplitNode::Leaf { id, .. } => Some(*id),
SplitNode::Horizontal { right, .. } => right.get_rightmost_pane(),
SplitNode::Vertical { bottom, .. } => bottom.get_rightmost_pane(),
}
}
fn get_topmost_pane(&self) -> Option<usize> {
match self {
SplitNode::Leaf { id, .. } => Some(*id),
SplitNode::Horizontal { left, .. } => left.get_topmost_pane(),
SplitNode::Vertical { top, .. } => top.get_topmost_pane(),
}
}
fn get_bottommost_pane(&self) -> Option<usize> {
match self {
SplitNode::Leaf { id, .. } => Some(*id),
SplitNode::Horizontal { right, .. } => right.get_bottommost_pane(),
SplitNode::Vertical { bottom, .. } => bottom.get_bottommost_pane(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Direction {
Left,
Right,
Up,
Down,
}