Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix scroll up #22

Merged
merged 1 commit into from
Aug 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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