use crate::splitter::{ResizeConstraint, SPLIT_WIDTH, Split, SplitState, SplitType};
use ratatui_core::layout::{Constraint, Direction, Flex, Layout, Position, Rect};
use ratatui_widgets::block::BlockExt;
use std::cmp::min;
pub(super) fn layout_split<'a>(widget: &Split<'a>, area: Rect, state: &mut SplitState) {
state.area = area;
state.inner = widget.block.inner_if_some(area);
state.relocate_split = true;
let inner = state.inner;
let layout_change = state.area_length.len() != widget.constraints.len();
let meta_change = state.direction != widget.direction
|| state.split_type != widget.split_type
|| state.mark_offset != widget.mark_offset;
let old_len = |v: &Rect| {
if state.direction == Direction::Horizontal {
v.width
} else {
v.height
}
};
let new_len = |v: &Rect| {
if widget.direction == Direction::Horizontal {
v.width
} else {
v.height
}
};
let new_split_areas = if layout_change {
let new_areas = Layout::new(widget.direction, widget.constraints.clone())
.flex(Flex::Legacy)
.split(inner);
Some(new_areas)
} else {
let old_length: u16 = state.area_length.iter().sum();
if meta_change || old_len(&inner) != old_length {
if state.area_constraint.is_empty() {
for i in 0..state.area_length.len() {
match widget.resize_constraints[i] {
ResizeConstraint::Fixed => {
state
.area_constraint
.push(Constraint::Length(state.area_length[i]));
}
ResizeConstraint::ScaleProportional => {
state
.area_constraint
.push(Constraint::Fill(state.area_length[i]));
}
ResizeConstraint::ScaleEqual => {
state.area_constraint.push(Constraint::Fill(1));
}
}
}
}
let new_areas =
Layout::new(widget.direction, state.area_constraint.clone()).split(inner);
Some(new_areas)
} else {
None
}
};
if let Some(new_split_areas) = new_split_areas {
state.area_length.clear();
for v in new_split_areas.iter() {
state.area_length.push(new_len(v));
}
while state.hidden_length.len() < state.area_length.len() {
state.hidden_length.push(0);
}
while state.hidden_length.len() > state.area_length.len() {
state.hidden_length.pop();
}
}
state.direction = widget.direction;
state.split_type = widget.split_type;
state.resize = widget.resize;
state.mark_offset = widget.mark_offset;
layout_from_widths(widget, state);
}
fn layout_from_widths<'a>(widget: &Split<'a>, state: &mut SplitState) {
state.widget_areas.clear();
state.splitline_areas.clear();
state.splitline_mark_position.clear();
let inner = state.inner;
let mut total = 0;
for length in state
.area_length
.iter()
.take(state.area_length.len().saturating_sub(1))
.copied()
{
let mut area = if widget.direction == Direction::Horizontal {
Rect::new(inner.x + total, inner.y, length, inner.height)
} else {
Rect::new(inner.x, inner.y + total, inner.width, length)
};
let mut split = if widget.direction == Direction::Horizontal {
Rect::new(
inner.x + total + length.saturating_sub(SPLIT_WIDTH),
inner.y,
min(1, length),
inner.height,
)
} else {
Rect::new(
inner.x,
inner.y + total + length.saturating_sub(SPLIT_WIDTH),
inner.width,
min(1, length),
)
};
let mut mark = if widget.direction == Direction::Horizontal {
Position::new(
inner.x + total + length.saturating_sub(SPLIT_WIDTH),
inner.y + widget.mark_offset,
)
} else {
Position::new(
inner.x + widget.mark_offset,
inner.y + total + length.saturating_sub(SPLIT_WIDTH),
)
};
adjust_for_split_type(
widget.direction,
widget.split_type,
&mut area,
&mut split,
&mut mark,
);
state.widget_areas.push(area);
state.splitline_areas.push(split);
state.splitline_mark_position.push(mark);
total += length;
}
if let Some(length) = state.area_length.last().copied() {
let area = if widget.direction == Direction::Horizontal {
Rect::new(inner.x + total, inner.y, length, inner.height)
} else {
Rect::new(inner.x, inner.y + total, inner.width, length)
};
state.widget_areas.push(area);
}
if let Some(test) = state.widget_areas.first() {
if widget.direction == Direction::Horizontal {
if test.height != state.inner.height {
for r in &mut state.widget_areas {
r.height = state.inner.height;
}
for r in &mut state.splitline_areas {
r.height = state.inner.height;
}
}
} else {
if test.width != state.inner.width {
for r in &mut state.widget_areas {
r.width = state.inner.width;
}
for r in &mut state.splitline_areas {
r.width = state.inner.width;
}
}
}
}
}
fn adjust_for_split_type(
direction: Direction,
split_type: SplitType,
area: &mut Rect,
split: &mut Rect,
mark: &mut Position,
) {
use Direction::*;
use SplitType::*;
match (direction, split_type) {
(
Horizontal,
FullEmpty | FullPlain | FullDouble | FullThick | FullQuadrantInside
| FullQuadrantOutside,
) => {
area.width = area.width.saturating_sub(SPLIT_WIDTH);
}
(
Vertical,
FullEmpty | FullPlain | FullDouble | FullThick | FullQuadrantInside
| FullQuadrantOutside,
) => {
area.height = area.height.saturating_sub(SPLIT_WIDTH);
}
(Horizontal, Scroll) => {
split.y = mark.y;
split.height = 2;
}
(Vertical, Scroll) => {
split.x = mark.x;
split.width = 2;
}
(Horizontal, Widget) => {}
(Vertical, Widget) => {}
}
}