Skip to content

Commit

Permalink
Merge pull request #22 from preiter93/fix-scroll-up
Browse files Browse the repository at this point in the history
Fix scroll up
  • Loading branch information
preiter93 committed Aug 25, 2024
2 parents 7372b2e + a41a34e commit 6cebf4c
Show file tree
Hide file tree
Showing 9 changed files with 409 additions and 296 deletions.
2 changes: 1 addition & 1 deletion examples/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ impl App {

fn draw(&mut self, terminal: &mut Terminal<impl Backend>) -> Result<()> {
terminal.draw(|frame| {
frame.render_widget(self, frame.size());
frame.render_widget(self, frame.area());
})?;
Ok(())
}
Expand Down
Binary file modified examples/tapes/demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified examples/tapes/simple.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 2 additions & 7 deletions examples/var_sizes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ impl Widget for &mut App {
where
Self: Sized,
{
let sizes = vec![4, 6, 5, 4, 3, 3, 6, 5, 7, 3, 6, 9, 4, 6];
let sizes = vec![100, 100, 4, 6, 5, 4, 3, 3, 6, 5, 7, 3, 6, 9, 10, 4, 4, 6];
let item_count = sizes.len();

let block = Block::default().borders(Borders::ALL).title("Outer block");
Expand Down Expand Up @@ -129,7 +129,7 @@ impl App {
pub fn run_app<B: Backend>(terminal: &mut Terminal<B>, mut app: App) -> io::Result<()> {
loop {
terminal.draw(|frame| {
frame.render_widget(&mut app, frame.size());
frame.render_widget(&mut app, frame.area());
})?;

if let Event::Key(key) = event::read()? {
Expand All @@ -144,8 +144,3 @@ pub fn run_app<B: Backend>(terminal: &mut Terminal<B>, mut app: App) -> io::Resu
}
}
}

// pub fn ui(f: &mut Frame, app: App) {
// // let list = app.list;
// f.render_stateful_widget_ref(app.list, f.size(), &mut app.state);
// }
92 changes: 5 additions & 87 deletions src/legacy/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,16 @@ pub(crate) fn layout_on_viewport<T: PreRender>(

// If the selected value is smaller than the offset, we roll
// the offset so that the selected value is at the top
if selected < state.offset {
state.offset = selected;
if selected < state.view_state.offset {
state.view_state.offset = selected;
}

let mut main_axis_size_cache: HashMap<usize, u16> = HashMap::new();

// Check if the selected item is in the current view
let (mut y, mut index) = (0, state.offset);
let (mut y, mut index) = (0, state.view_state.offset);
let mut found = false;
for widget in widgets.iter_mut().skip(state.offset) {
for widget in widgets.iter_mut().skip(state.view_state.offset) {
// Get the main axis size of the widget.
let is_selected = state.selected.map_or(false, |j| index == j);
let context = PreRenderContext::new(is_selected, cross_axis_size, scroll_axis, index);
Expand Down Expand Up @@ -101,7 +101,7 @@ pub(crate) fn layout_on_viewport<T: PreRender>(
truncated_by: main_axis_size.saturating_sub(dy),
},
);
state.offset = index;
state.view_state.offset = index;
break;
}

Expand All @@ -125,85 +125,3 @@ pub(crate) struct ViewportLayout {
pub(crate) main_axis_size: u16,
pub(crate) truncated_by: u16,
}

#[cfg(test)]
mod tests {
use ratatui::{
prelude::*,
widgets::{Block, Borders},
};

use super::*;

struct TestItem {
main_axis_size: u16,
}

impl Widget for TestItem {
fn render(self, area: Rect, buf: &mut Buffer)
where
Self: Sized,
{
Block::default().borders(Borders::ALL).render(area, buf);
}
}

impl PreRender for TestItem {
fn pre_render(&mut self, _context: &PreRenderContext) -> u16 {
self.main_axis_size
}
}

macro_rules! update_view_port_tests {
($($name:ident:
[
$given_offset:expr,
$given_selected:expr,
$given_sizes:expr,
$given_max_size:expr
],
[
$expected_offset:expr,
$expected_sizes:expr
],)*) => {
$(
#[test]
fn $name() {
// given
let mut given_state = ListState {
offset: $given_offset,
selected: $given_selected,
num_elements: $given_sizes.len(),
circular: true,
};

//when
let mut widgets: Vec<TestItem> = $given_sizes
.into_iter()
.map(|main_axis_size| TestItem {
main_axis_size: main_axis_size as u16,
})
.collect();
let scroll_axis = ScrollAxis::default();
let layouts = layout_on_viewport(&mut given_state, &mut widgets, $given_max_size, 0, scroll_axis);
let offset = given_state.offset;

// then
let main_axis_sizes: Vec<u16> = layouts.iter().map(|x| x.main_axis_size).collect();
assert_eq!(offset, $expected_offset);
assert_eq!(main_axis_sizes, $expected_sizes);
}
)*
}
}

update_view_port_tests! {
happy_path: [0, Some(0), vec![2, 3], 6], [0, vec![2, 3]],
empty_list: [0, None, Vec::<u16>::new(), 4], [0, vec![]],
update_offset_down: [0, Some(2), vec![2, 3, 3], 6], [1, vec![3, 3]],
update_offset_up: [1, Some(0), vec![2, 3, 3], 6], [0, vec![2, 3, 1]],
truncate_bottom: [0, Some(0), vec![2, 3], 4], [0, vec![2, 2]],
truncate_top: [0, Some(1), vec![2, 3], 4], [0, vec![1, 3]],
num_elements: [0, None, vec![1, 1, 1, 1, 1], 3], [0, vec![1, 1, 1]],
}
}
5 changes: 4 additions & 1 deletion src/legacy/widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,10 @@ impl<'a, T: PreRender> StatefulWidget for List<'a, T> {
// Drain out elements that are shown on the view port from the vector of
// all elements.
let num_items_viewport = viewport_layouts.len();
let (start, end) = (state.offset, num_items_viewport + state.offset);
let (start, end) = (
state.view_state.offset,
num_items_viewport + state.view_state.offset,
);
let items_viewport = items.drain(start..end);

// The starting coordinates of the current item
Expand Down
20 changes: 15 additions & 5 deletions src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ pub struct ListState {
/// The selected item. If `None`, no item is currently selected.
pub selected: Option<usize>,

/// The index of the first item displayed on the screen.
pub(crate) offset: usize,

/// The total number of elements in the list. This is necessary to correctly
/// handle item selection.
pub(crate) num_elements: usize,
Expand All @@ -16,15 +13,28 @@ pub struct ListState {
///
/// True by default.
pub(crate) circular: bool,

/// The state for the viewport. Keeps track which item to show
/// first and how much it is truncated.
pub(crate) view_state: ViewState,
}

#[derive(Debug, Clone, Default, Eq, PartialEq)]
pub(crate) struct ViewState {
/// The index of the first item displayed on the screen.
pub(crate) offset: usize,

/// The truncation in rows/columns of the first item displayed on the screen.
pub(crate) first_truncated: u16,
}

impl Default for ListState {
fn default() -> Self {
Self {
selected: None,
offset: 0,
num_elements: 0,
circular: true,
view_state: ViewState::default(),
}
}
}
Expand All @@ -48,7 +58,7 @@ impl ListState {
pub fn select(&mut self, index: Option<usize>) {
self.selected = index;
if index.is_none() {
self.offset = 0;
self.view_state.offset = 0;
}
}

Expand Down
Loading

0 comments on commit 6cebf4c

Please sign in to comment.