use crate::{get_scroll_adjusted_row_index,
locate_cursor_in_viewport,
AnsiStyledText,
CalculateResizeHint,
CaretVerticalViewportLocation,
ChUnit,
HowToChoose,
InlineString,
InlineVec,
ItemsOwned,
Size};
#[derive(Debug, Default, PartialEq, Eq, Clone)]
pub struct State {
pub max_display_height: ChUnit,
pub max_display_width: ChUnit,
pub raw_caret_row_index: ChUnit,
pub scroll_offset_row_index: ChUnit,
pub items: ItemsOwned,
pub selected_items: ItemsOwned,
pub header: Header,
pub selection_mode: HowToChoose,
pub resize_hint: Option<ResizeHint>,
pub window_size: Option<Size>,
}
#[derive(Debug, PartialEq, Clone, Eq)]
#[allow(clippy::large_enum_variant)]
pub enum Header {
SingleLine(InlineString),
MultiLine(InlineVec<InlineVec<AnsiStyledText>>),
}
mod convert_to_header {
use super::*;
impl From<Vec<Vec<AnsiStyledText>>> for Header {
fn from(header: Vec<Vec<AnsiStyledText>>) -> Self {
Header::MultiLine(header.into_iter().map(InlineVec::from).collect())
}
}
impl From<InlineVec<InlineVec<AnsiStyledText>>> for Header {
fn from(header: InlineVec<InlineVec<AnsiStyledText>>) -> Self {
Header::MultiLine(header)
}
}
impl From<InlineString> for Header {
fn from(header: InlineString) -> Self { Header::SingleLine(header) }
}
impl From<String> for Header {
fn from(header: String) -> Self { Header::SingleLine(InlineString::from(header)) }
}
impl From<&str> for Header {
fn from(header: &str) -> Self { Header::SingleLine(InlineString::from(header)) }
}
impl Default for Header {
fn default() -> Self { Header::SingleLine(InlineString::new()) }
}
}
#[cfg(test)]
mod tests {
use smallvec::smallvec;
use super::*;
use crate::{assert_eq2, ast};
#[test]
fn test_header_enum() {
let state = State {
header: Header::MultiLine(smallvec![smallvec![ast(
"line1",
smallvec::smallvec![],
)]]),
..Default::default()
};
let lhs = state.header;
let rhs =
Header::MultiLine(smallvec![smallvec![ast("line1", smallvec::smallvec![])]]);
assert_eq2!(lhs, rhs);
}
}
impl CalculateResizeHint for State {
fn set_size(&mut self, new_size: Size) {
self.window_size = Some(new_size);
self.clear_resize_hint();
}
fn get_resize_hint(&self) -> Option<ResizeHint> { self.resize_hint.clone() }
fn set_resize_hint(&mut self, new_size: Size) {
self.resize_hint = if let Some(old_size) = self.window_size {
if new_size != old_size {
if (new_size.col_width > old_size.col_width)
|| (new_size.row_height > old_size.row_height)
{
Some(ResizeHint::GotBigger)
} else if (new_size.col_width < old_size.col_width)
|| (new_size.row_height < old_size.row_height)
{
Some(ResizeHint::GotSmaller)
} else {
Some(ResizeHint::NoChange)
}
} else {
None
}
} else {
None
};
if self.window_size.is_some() {
self.set_size(new_size)
}
}
fn clear_resize_hint(&mut self) { self.resize_hint = None; }
}
#[derive(Debug, Default, PartialEq, Eq, Hash, Clone)]
pub enum ResizeHint {
GotBigger,
GotSmaller,
#[default]
NoChange,
}
impl State {
pub fn get_focused_index(&self) -> ChUnit {
get_scroll_adjusted_row_index(
self.raw_caret_row_index,
self.scroll_offset_row_index,
)
}
pub fn locate_cursor_in_viewport(&self) -> CaretVerticalViewportLocation {
locate_cursor_in_viewport(
self.raw_caret_row_index,
self.scroll_offset_row_index,
self.max_display_height,
self.items.len().into(),
)
}
}