use super::{FrameworkError, FrameworkItem};
use tui::layout::{Constraint, Direction, Layout, Rect};
#[derive(Clone)]
pub struct RowItem {
pub item: Box<dyn FrameworkItem>,
pub width: Constraint,
}
#[derive(Clone)]
pub struct Row {
pub items: Vec<RowItem>,
pub centered: bool,
pub height: Constraint,
}
#[derive(Clone)]
pub struct State(pub Vec<Row>);
impl State {
pub fn selectables(&self) -> Vec<Vec<(usize, usize)>> {
let mut selectables = Vec::new();
self.0.iter().enumerate().for_each(|(y, row)| {
let mut row_selectables = Vec::new();
row.items.iter().enumerate().for_each(|(x, row_item)| {
if row_item.item.selectable() {
row_selectables.push((x, y));
}
});
if row_selectables.len() != 0 {
selectables.push(row_selectables);
}
});
selectables
}
pub fn get_chunks(&self, area: Rect) -> Vec<Vec<Rect>> {
let mut row_constraints = vec![Constraint::Length(0)];
row_constraints.extend(self.0.iter().map(|row| row.height));
row_constraints.push(Constraint::Length(0));
let row_constraints_length = row_constraints.len() - 2;
let rows = Layout::default()
.direction(Direction::Vertical)
.constraints(row_constraints)
.split(area)
.into_iter()
.skip(1)
.take(row_constraints_length)
.collect::<Vec<_>>();
let constraints = self
.0
.iter()
.map(|row| {
let begin_length = if row.centered {
Constraint::Length(
(area.width
- row
.items
.iter()
.map(|item| item.width.apply(area.width))
.sum::<u16>())
/ 2,
)
} else {
Constraint::Length(0)
};
let mut out = vec![begin_length];
out.extend(row.items.iter().map(|item| item.width));
out.push(Constraint::Length(0));
out
})
.collect::<Vec<_>>();
rows.into_iter()
.zip(constraints.into_iter())
.map(|(row_chunk, constraints)| {
let constraints_length = constraints.len() - 2;
Layout::default()
.direction(Direction::Horizontal)
.constraints(constraints)
.split(row_chunk)
.into_iter()
.skip(1)
.take(constraints_length)
.collect()
})
.collect::<Vec<_>>()
}
pub fn get(&self, x: usize, y: usize) -> &Box<dyn FrameworkItem> {
&self.0[y].items[x].item
}
pub fn get_mut(&mut self, x: usize, y: usize) -> &mut Box<dyn FrameworkItem> {
&mut self.0[y].items[x].item
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum CursorState {
None,
Hover(usize, usize),
Selected(usize, usize),
}
impl Default for CursorState {
fn default() -> Self {
Self::None
}
}
impl CursorState {
pub fn select(&mut self) -> Result<(), FrameworkError> {
match self {
Self::Hover(x, y) => *self = Self::Selected(*x, *y),
_ => return Err(FrameworkError::CursorStateMismatch),
}
Ok(())
}
pub fn deselect(&mut self) -> Result<(), FrameworkError> {
match self {
Self::Selected(x, y) => *self = Self::Hover(*x, *y),
_ => return Err(FrameworkError::CursorStateMismatch),
}
Ok(())
}
}
impl CursorState {
pub fn to_hover(location: (usize, usize)) -> Self {
Self::Hover(location.0, location.1)
}
pub fn to_selected(location: (usize, usize)) -> Self {
Self::Selected(location.0, location.1)
}
pub fn hover(&self, selectables: &Vec<Vec<(usize, usize)>>) -> Option<(usize, usize)> {
match self {
Self::Hover(x, y) if selectables.len() != 0 => {
Some(Self::selectables_to_coors(&selectables, (*x, *y)))
}
_ => None,
}
}
pub fn selected(&self, selectables: &Vec<Vec<(usize, usize)>>) -> Option<(usize, usize)> {
match self {
Self::Selected(x, y) if selectables.len() != 0 => {
Some(Self::selectables_to_coors(&selectables, (*x, *y)))
}
_ => None,
}
}
fn selectables_to_coors(
selectables: &Vec<Vec<(usize, usize)>>,
location: (usize, usize),
) -> (usize, usize) {
let (location_x, location_y) = location;
selectables[location_y][location_x]
}
}
impl CursorState {
pub fn r#move(
&mut self,
direction: FrameworkDirection,
selectables: &Vec<Vec<(usize, usize)>>,
) -> Result<(), FrameworkError> {
match direction {
FrameworkDirection::Up => self.up(),
FrameworkDirection::Down => self.down(),
FrameworkDirection::Left => self.left(),
FrameworkDirection::Right => self.right(),
}?;
self.move_check(selectables);
Ok(())
}
fn move_check(&mut self, selectables: &Vec<Vec<(usize, usize)>>) {
if let Self::Hover(x, y) = self {
if selectables.len() == 0 {
*x = 0;
*y = 0;
return;
}
let y_max = selectables.len() - 1;
if *y > y_max {
*y = y_max;
}
let x_max = selectables[*y].len() - 1;
if *x > x_max {
*x = x_max;
}
} else {
unreachable!("move_check is only called after a hovering cursor is moved, when cursor is at hover state")
}
}
fn left(&mut self) -> Result<(), FrameworkError> {
match self {
Self::Hover(x, _) => {
if *x != 0 {
*x -= 1
}
}
Self::None => *self = Self::Hover(0, 0),
Self::Selected(_, _) => return Err(FrameworkError::MoveSelected),
}
Ok(())
}
fn right(&mut self) -> Result<(), FrameworkError> {
match self {
Self::Hover(x, _) => *x += 1,
Self::None => *self = Self::Hover(usize::MAX, 0),
Self::Selected(_, _) => return Err(FrameworkError::MoveSelected),
}
Ok(())
}
fn up(&mut self) -> Result<(), FrameworkError> {
match self {
Self::Hover(_, y) => {
if *y != 0 {
*y -= 1
}
}
Self::None => *self = Self::Hover(0, 0),
Self::Selected(_, _) => return Err(FrameworkError::MoveSelected),
}
Ok(())
}
fn down(&mut self) -> Result<(), FrameworkError> {
match self {
Self::Hover(_, y) => *y += 1,
Self::None => *self = Self::Hover(0, usize::MAX),
Self::Selected(_, _) => return Err(FrameworkError::MoveSelected),
}
Ok(())
}
}
#[derive(Clone, Copy)]
pub enum FrameworkDirection {
Up,
Down,
Left,
Right,
}
#[derive(Clone, Copy)]
pub struct ItemInfo {
pub selected: bool,
pub hover: bool,
pub x: usize,
pub y: usize,
}