use crate::grapheme::StyledGraphemes;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct ContentPosition {
pub row: usize,
pub column: usize,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct VisualPosition {
pub row: usize,
pub column: usize,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct ScreenPosition {
pub row: u16,
pub column: u16,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct WidgetPosition<K> {
pub index: K,
pub row: usize,
pub column: usize,
}
impl<K> WidgetPosition<K> {
pub fn content_position(&self) -> ContentPosition {
ContentPosition {
row: self.row,
column: self.column,
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum WidthMode {
#[default]
Wrap,
Truncate,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct WidgetLayout {
pub max_height: Option<usize>,
pub width_mode: WidthMode,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct CreatedGraphemes {
pub graphemes: StyledGraphemes,
pub layout: WidgetLayout,
pub cursor: Option<ContentPosition>,
}
impl From<StyledGraphemes> for CreatedGraphemes {
fn from(graphemes: StyledGraphemes) -> Self {
Self {
graphemes,
..Self::default()
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct WidgetViewport {
pub screen_row: u16,
pub height: u16,
pub content_row: usize,
}
impl WidgetViewport {
pub fn scroll_to_include(&mut self, position: VisualPosition) -> ViewportChange {
if self.height == 0 {
return ViewportChange::Unchanged;
}
let previous = self.content_row;
let height = self.height as usize;
if position.row < self.content_row {
self.content_row = position.row;
} else if position.row >= self.content_row.saturating_add(height) {
self.content_row = position.row.saturating_add(1).saturating_sub(height);
}
if self.content_row == previous {
ViewportChange::Unchanged
} else {
ViewportChange::Scrolled
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ViewportChange {
Unchanged,
Scrolled,
}
pub trait Widget {
fn create_graphemes(&self) -> CreatedGraphemes;
fn create_graphemes_in_viewport(&self, _width: u16, _height: u16) -> CreatedGraphemes {
self.create_graphemes()
}
}
#[cfg(test)]
mod tests {
use super::*;
mod widget_viewport {
use super::*;
mod scroll_to_include {
use super::*;
#[test]
fn does_not_scroll_while_the_position_is_visible() {
let mut viewport = WidgetViewport {
height: 3,
content_row: 4,
..Default::default()
};
assert_eq!(
viewport.scroll_to_include(VisualPosition { row: 6, column: 0 }),
ViewportChange::Unchanged
);
assert_eq!(viewport.content_row, 4);
}
#[test]
fn scrolls_the_minimum_distance_to_include_the_position() {
let mut viewport = WidgetViewport {
height: 3,
content_row: 4,
..Default::default()
};
assert_eq!(
viewport.scroll_to_include(VisualPosition { row: 7, column: 0 }),
ViewportChange::Scrolled
);
assert_eq!(viewport.content_row, 5);
assert_eq!(
viewport.scroll_to_include(VisualPosition { row: 2, column: 0 }),
ViewportChange::Scrolled
);
assert_eq!(viewport.content_row, 2);
}
}
}
}