use super::{FrameworkError, FrameworkItem};
use tui::layout::Constraint;
pub struct RowItem {
pub item: Box<dyn FrameworkItem>,
pub width: Constraint,
}
pub struct Row {
pub items: Vec<RowItem>,
pub centered: bool,
pub height: Constraint,
}
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(&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: Direction,
selectables: &Vec<Vec<(usize, usize)>>,
) -> Result<(), FrameworkError> {
match direction {
Direction::Up => self.up(),
Direction::Down => self.down(),
Direction::Left => self.left(),
Direction::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 Direction {
Up,
Down,
Left,
Right,
}