use altui_core::layout::Layout;
use altui_core::layout::Rect;
use crate::context::ViewCtx;
pub const EMPTY_AREA: Rect = Rect {
x: 0,
y: 0,
width: 0,
height: 0,
};
#[derive(Debug, Default)]
pub struct AreaCache {
cached: Vec<CachedSplit>,
pub(crate) current_split: usize,
}
impl AreaCache {
pub fn split(&mut self, layout: Layout, area: Rect) -> Vec<Rect> {
let cached = match self.cached.get_mut(self.current_split) {
Some(v) => v,
None => {
let result = layout.split(area);
self.cached
.push(CachedSplit::new(layout.clone(), area, result.clone()));
self.current_split += 1;
return result;
}
};
self.current_split += 1;
if cached.offsets.len() > 1 {
panic!("Failed to use 'CtxStore::split' method after 'CtxStore::split_ext'")
}
match cached.layout.cache_eq(&layout) {
true => match area == cached.prev_area {
true => cached.offsets[0].clone(),
false => {
cached.prev_area = area;
let result = layout.split(area);
cached.offsets[0] = result.clone();
result
}
},
false => {
cached.prev_area = area;
cached.layout = layout.clone();
let result = layout.split(area);
cached.offsets[0] = result.clone();
result
}
}
}
pub fn split_ext(
&mut self,
layout: Layout,
area: Rect,
scrollstate: &mut u16,
scroll: &mut u16,
) -> Vec<Rect> {
if *scroll > 0 && scrollstate > scroll {
*scrollstate = *scroll;
}
let idx: usize = *scrollstate as usize;
let cached_layout = match self.cached.get_mut(self.current_split) {
Some(v) => v,
None => {
let result = layout.split_ext(area, scrollstate, scroll);
self.cached
.push(CachedSplit::new(layout.clone(), area, result.clone()));
self.current_split += 1;
return result;
}
};
self.current_split += 1;
if !cached_layout.layout.cache_eq(&layout) {
cached_layout.layout = layout.clone();
cached_layout.offsets.clear();
}
cached_layout.offsets.resize_with(idx + 1, Vec::new);
if area == cached_layout.prev_area && !cached_layout.offsets[idx].is_empty() {
return cached_layout.offsets[idx].clone();
}
let result = layout.split_ext(area, scrollstate, scroll);
cached_layout.offsets[idx] = result.clone();
cached_layout.prev_area = area;
result
}
}
#[derive(Debug, Default)]
struct CachedSplit {
layout: Layout,
prev_area: Rect,
offsets: Vec<Vec<Rect>>,
}
impl CachedSplit {
fn new(layout: Layout, prev_area: Rect, area: Vec<Rect>) -> Self {
Self {
layout,
prev_area,
offsets: vec![area],
}
}
}
pub struct CtxStore {
pub(crate) contexts: Vec<ViewCtx>,
pub(crate) interactive: Vec<usize>,
pub(crate) current_interactive_idx: usize,
pub(crate) is_hovered: bool,
pub(crate) current_area: usize,
pub(crate) scroll_step: usize,
pub(crate) layout_cache: AreaCache,
}
impl Default for CtxStore {
fn default() -> Self {
Self {
contexts: Default::default(),
interactive: Default::default(),
current_interactive_idx: Default::default(),
is_hovered: false,
current_area: Default::default(),
scroll_step: 2,
layout_cache: AreaCache::default(),
}
}
}
impl CtxStore {
pub fn split(&mut self, layout: Layout, area: Rect) -> Vec<Rect> {
self.layout_cache.split(layout, area)
}
pub fn split_ext(
&mut self,
layout: Layout,
area: Rect,
scrollstate: &mut u16,
scroll: &mut u16,
) -> Vec<Rect> {
self.layout_cache
.split_ext(layout, area, scrollstate, scroll)
}
pub fn next_area(&mut self, area: Rect) {
match self.contexts.get_mut(self.current_area) {
Some(contexts) => {
contexts.set_area(area);
self.current_area += 1;
}
None => {
panic!(
"Failed to find {}'s View while setting areas",
self.current_area
)
}
}
}
pub fn next_areas(&mut self, areas: &[Rect]) {
for area in areas {
match self.contexts.get_mut(self.current_area) {
Some(contexts) => {
contexts.set_area(*area);
self.current_area += 1;
}
None => {
panic!(
"Failed to find {}'s View while setting areas",
self.current_area
)
}
}
}
}
pub fn skip_areas(&mut self, number: usize) {
for _ in 0..number {
match self.contexts.get_mut(self.current_area) {
Some(contexts) => {
contexts.set_area(EMPTY_AREA);
self.current_area += 1;
}
None => {
panic!(
"Failed to find {}'s View while setting areas",
self.current_area
)
}
}
}
}
#[inline(always)]
fn get_current_index(&self) -> usize {
self.interactive[self.current_interactive_idx]
}
pub(crate) fn set_vscroll(&mut self, step: usize) {
self.scroll_step = step.clamp(1, 20);
}
pub(crate) fn push(&mut self, ctx: ViewCtx) {
self.contexts.push(ctx);
}
pub(crate) fn push_interactive(&mut self, ctx: ViewCtx) {
let index = self.contexts.len();
self.contexts.push(ctx);
self.interactive.push(index);
}
pub(crate) fn first(&mut self) {
if !self.interactive.is_empty() {
self.current_interactive_idx = self.interactive.len() - 1;
self.is_hovered = true;
self.next();
}
}
pub(crate) fn last(&mut self) {
if !self.interactive.is_empty() {
self.current_interactive_idx = 0;
self.is_hovered = true;
self.previous();
}
}
pub(crate) fn next(&mut self) {
let len = self.interactive.len() - 1;
let start = self.current_interactive_idx;
loop {
match self.is_hovered {
true => match self.current_interactive_idx != len {
true => self.current_interactive_idx = self.current_interactive_idx + 1,
false => self.current_interactive_idx = 0,
},
false => self.is_hovered = true,
}
let ctx = &self.contexts[self.get_current_index()];
match ctx.is_visible() && ctx.is_interactive() {
true => {
ctx.set_hover();
break;
}
false => match self.current_interactive_idx == start {
true => break,
false => continue,
},
}
}
}
pub(crate) fn previous(&mut self) {
let len = self.interactive.len() - 1;
let start = self.current_interactive_idx;
loop {
match self.is_hovered {
true => match self.current_interactive_idx != 0 {
true => self.current_interactive_idx = self.current_interactive_idx - 1,
false => self.current_interactive_idx = len,
},
false => self.is_hovered = true,
}
let ctx = &self.contexts[self.get_current_index()];
match ctx.is_visible() && ctx.is_interactive() {
true => {
ctx.set_hover();
break;
}
false => match self.current_interactive_idx == start {
true => break,
false => continue,
},
}
}
}
pub(crate) fn down(&mut self) {
let len = self.interactive.len() - 1;
let start = self.current_interactive_idx;
let mut count = 1;
loop {
match self.is_hovered {
true => match self.current_interactive_idx != len {
true => self.current_interactive_idx = self.current_interactive_idx + 1,
false => self.current_interactive_idx = 0,
},
false => self.is_hovered = true,
}
let ctx = &self.contexts[self.get_current_index()];
match ctx.is_visible() && ctx.is_interactive() {
true => match count == self.scroll_step {
true => {
ctx.set_hover();
break;
}
false => count += 1,
},
false => match self.current_interactive_idx == start {
true => break,
false => continue,
},
}
}
}
pub(crate) fn up(&mut self) {
let len = self.interactive.len() - 1;
let start = self.current_interactive_idx;
let mut count = 1;
loop {
match self.is_hovered {
true => match self.current_interactive_idx != 0 {
true => self.current_interactive_idx = self.current_interactive_idx - 1,
false => self.current_interactive_idx = len,
},
false => self.is_hovered = true,
}
let state = &self.contexts[self.get_current_index()];
match state.is_visible() && state.is_interactive() {
true => match count == self.scroll_step {
true => {
state.set_hover();
break;
}
false => count += 1,
},
false => match self.current_interactive_idx == start {
true => break,
false => continue,
},
}
}
}
pub(crate) fn mouse_set_hover(&mut self, x: u16, y: u16) {
for (interactive_i, currrent_i) in self.interactive.iter().enumerate() {
let state = &self.contexts[*currrent_i];
if state.is_visible() && state.is_interactive() && state.get_area_ref().inhere(x, y) {
state.set_hover();
self.current_interactive_idx = interactive_i;
self.is_hovered = true;
break;
}
}
}
pub(crate) fn mouse_is_hovered(&mut self, x: u16, y: u16) -> bool {
match self.interactive.is_empty() {
true => false,
false => {
let state = &self.contexts[self.get_current_index()];
match state.get_area_ref().inhere(x, y) {
true => true && self.is_hovered && state.is_visible() && state.is_interactive(),
false => {
self.is_hovered = false;
false
}
}
}
}
}
}