use std::{any::Any, collections::HashMap, error::Error, fmt::Display};
use crossterm::event::KeyEvent;
use ratatui::{layout::Rect, Frame};
use super::{
CursorState, FrameworkClean, FrameworkData, FrameworkDirection, FrameworkHistory, ItemInfo,
State,
};
#[derive(Clone)]
pub struct Framework {
pub selectables: Vec<Vec<(usize, usize)>>,
pub data: FrameworkData,
pub state: State,
pub cursor: CursorState,
pub history: Vec<FrameworkHistory>,
pub frame_area: Option<Rect>,
}
impl Framework {
pub fn clear_history(&mut self) {
self.history.clear();
}
pub fn push_history(&mut self) {
self.history.push(FrameworkHistory {
selectables: self.selectables.clone(),
data: self.data.state.clone(),
state: self.state.clone(),
cursor: self.cursor,
});
}
pub fn pop_history(&mut self) -> Option<FrameworkHistory> {
self.history.pop()
}
pub fn revert_last_history(&mut self) -> Result<(), FrameworkError> {
let history = match self.history.pop() {
None => return Err(FrameworkError::NoSuchSave),
Some(history) => history,
};
self.selectables = history.selectables;
self.data.state = history.data;
self.state = history.state;
self.cursor = history.cursor;
Ok(())
}
pub fn revert_history(&mut self, index: usize) -> Result<(), FrameworkError> {
if index >= self.history.len() {
return Err(FrameworkError::NoSuchSave);
}
let history = self.history.remove(index);
self.selectables = history.selectables;
self.data.state = history.data;
self.state = history.state;
self.cursor = history.cursor;
Ok(())
}
}
impl Framework {
pub fn is_selected(&self) -> bool {
self.cursor.is_selected()
}
pub fn is_hover(&self) -> bool {
self.cursor.is_hover()
}
pub fn is_none(&self) -> bool {
self.cursor.is_none()
}
}
impl Framework {
pub fn new(state: State) -> Self {
Self {
selectables: state.selectables(),
data: FrameworkData::default(),
state,
frame_area: None,
cursor: CursorState::default(),
history: Vec::new(),
}
}
pub fn set_state(&mut self, state: State) {
self.state = state;
self.selectables = self.state.selectables();
}
pub fn render(&mut self, frame: &mut Frame) {
let area = frame.area();
self.frame_area = Some(area);
let chunks = self.state.get_chunks(area);
let selected = self.cursor.selected(&self.selectables);
let hover = self.cursor.hover(&self.selectables);
self.render_raw(frame, &chunks, selected, hover, false);
self.render_raw(frame, &chunks, selected, hover, true);
}
pub fn render_raw(
&mut self,
frame: &mut Frame,
chunks: &[Vec<Rect>],
selected: Option<(usize, usize)>,
hover: Option<(usize, usize)>,
popup_render: bool,
) {
let (mut frameworkclean, state) = self.split_clean();
for (y, (row, row_chunks)) in state.0.iter_mut().zip(chunks.iter()).enumerate() {
for (x, (row_item, item_chunk)) in
row.items.iter_mut().zip(row_chunks.iter()).enumerate()
{
row_item.item.render(
frame,
&mut frameworkclean,
*item_chunk,
popup_render,
ItemInfo {
selected: Some((x, y)) == selected,
hover: Some((x, y)) == hover,
x,
y,
},
);
}
}
}
pub fn render_only(&mut self, frame: &mut Frame, x: usize, y: usize) {
let chunk = self.state.get_chunks(frame.area())[y][x];
let selected = self.cursor.selected(&self.selectables);
let hover = self.cursor.hover(&self.selectables);
self.render_only_raw(frame, x, y, chunk, false, selected, hover);
self.render_only_raw(frame, x, y, chunk, true, selected, hover);
}
pub fn render_only_multiple(&mut self, frame: &mut Frame, locations: &[(usize, usize)]) {
let chunks = self.state.get_chunks(frame.area());
let selected = self.cursor.selected(&self.selectables);
let hover = self.cursor.hover(&self.selectables);
locations.iter().for_each(|(x, y)| {
self.render_only_raw(frame, *x, *y, chunks[*y][*x], false, selected, hover);
});
locations.iter().for_each(|(x, y)| {
self.render_only_raw(frame, *x, *y, chunks[*y][*x], true, selected, hover);
});
}
pub fn render_only_raw(
&mut self,
frame: &mut Frame,
x: usize,
y: usize,
chunk: Rect,
popup_render: bool,
selected: Option<(usize, usize)>,
hover: Option<(usize, usize)>,
) {
let (mut frameworkclean, state) = self.split_clean();
state.get_mut(x, y).render(
frame,
&mut frameworkclean,
chunk,
popup_render,
ItemInfo {
selected: selected == Some((x, y)),
hover: hover == Some((x, y)),
x,
y,
},
)
}
pub fn key_input(&mut self, key: KeyEvent) -> Result<(), Box<dyn Error>> {
let selected = self.cursor.selected(&self.selectables);
let (mut frameworkclean, state) = self.split_clean();
if let Some((x, y)) = selected {
state.get_mut(x, y).key_event(
&mut frameworkclean,
key,
ItemInfo {
selected: true,
hover: false,
x,
y,
},
)?;
}
Ok(())
}
pub fn mouse_event(&mut self, col: u16, row: u16) -> bool {
let chunks = match self.frame_area {
Some(area) => self.state.get_chunks(area),
None => return false,
};
for (y, row_items) in self.state.0.iter().enumerate() {
for x in 0..row_items.items.len() {
let chunk = chunks[y][x];
if !chunk.intersects(Rect::new(col, row, 1, 1)) {
continue;
}
if self.cursor.selected(&self.selectables) == Some((x, y)) {
let (mut clean, state) = self.split_clean();
let a = state.get_mut(x, y).mouse_passthrough(
&mut clean,
true,
col - chunk.x,
row - chunk.y,
col,
row,
);
let b = state.get_mut(x, y).mouse_event(
&mut clean,
col - chunk.x,
row - chunk.y,
col,
row,
);
return a || b;
} else {
let (mut clean, state) = self.split_clean();
state.get_mut(x, y).mouse_passthrough(
&mut clean,
false,
col - chunk.x,
row - chunk.y,
col,
row,
);
}
if self.cursor.hover(&self.selectables) == Some((x, y)) {
return self.select().is_ok();
}
let mut selectable_index = None;
for (sel_row, row_items) in self.selectables.iter().enumerate() {
for (sel_col, (x2, y2)) in row_items.iter().enumerate() {
if (x2, y2) == (&x, &y) {
selectable_index = Some((sel_col, sel_row))
}
}
}
if let Some(sel_index) = selectable_index {
self.deselect().ok();
self.cursor = CursorState::to_hover(sel_index);
}
return true;
}
}
self.deselect().ok();
self.cursor = CursorState::default();
true
}
pub fn message(&mut self, data: HashMap<String, Box<dyn Any>>) -> bool {
let selected = self.cursor.selected(&self.selectables);
let (mut frameworkclean, state) = self.split_clean();
if let Some((x, y)) = selected {
return state.get_mut(x, y).message(&mut frameworkclean, data);
}
false
}
pub fn load(&mut self) -> Result<(), Box<dyn Error>> {
let selected = self.cursor.selected(&self.selectables);
let hover = self.cursor.hover(&self.selectables);
let (mut frameworkclean, state) = self.split_clean();
for (y, row) in state.0.iter_mut().enumerate() {
for (x, row_item) in row.items.iter_mut().enumerate() {
row_item.item.load_item(
&mut frameworkclean,
ItemInfo {
selected: Some((x, y)) == selected,
hover: Some((x, y)) == hover,
x,
y,
},
)?;
}
}
Ok(())
}
pub fn load_only(&mut self, x: usize, y: usize) -> Result<(), Box<dyn Error>> {
let selected = self.cursor.selected(&self.selectables);
let hover = self.cursor.hover(&self.selectables);
let (mut frameworkclean, state) = self.split_clean();
state.get_mut(x, y).load_item(
&mut frameworkclean,
ItemInfo {
selected: Some((x, y)) == selected,
hover: Some((x, y)) == hover,
x,
y,
},
)
}
pub fn load_only_multiple(&mut self, locations: &[(usize, usize)]) {
let selected = self.cursor.selected(&self.selectables);
let hover = self.cursor.hover(&self.selectables);
let (mut frameworkclean, state) = self.split_clean();
locations.iter().for_each(|(x, y)| {
let _ = state.get_mut(*x, *y).load_item(
&mut frameworkclean,
ItemInfo {
selected: Some((*x, *y)) == selected,
hover: Some((*x, *y)) == hover,
x: *x,
y: *y,
},
);
})
}
}
impl Framework {
pub fn split_clean(&mut self) -> (FrameworkClean<'_>, &mut State) {
self.into()
}
}
impl Framework {
pub fn r#move(&mut self, direction: FrameworkDirection) -> Result<(), FrameworkError> {
self.cursor.r#move(direction, &self.selectables)
}
pub fn select(&mut self) -> Result<(), Box<dyn Error>> {
if let Some((x, y)) = self.cursor.hover(&self.selectables) {
let (mut frameworkclean, state) = self.split_clean();
let item = state.get_mut(x, y);
if item.select(&mut frameworkclean) {
self.cursor.select()?;
}
} else {
Err(FrameworkError::CursorStateMismatch)?;
}
Ok(())
}
pub fn deselect(&mut self) -> Result<(), Box<dyn Error>> {
if let Some((x, y)) = self.cursor.selected(&self.selectables) {
let (mut frameworkclean, state) = self.split_clean();
let item = state.get_mut(x, y);
if item.deselect(&mut frameworkclean) {
self.cursor.deselect()?;
}
} else {
Err(FrameworkError::CursorStateMismatch)?;
}
Ok(())
}
}
#[derive(Debug)]
pub enum FrameworkError {
MoveSelected,
CursorStateMismatch,
NoSuchSave,
}
impl Display for FrameworkError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("{:?}", self))
}
}
impl Error for FrameworkError {}