summaryrefslogtreecommitdiff
path: root/src/list/scroll_state.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/list/scroll_state.rs')
-rw-r--r--src/list/scroll_state.rs104
1 files changed, 104 insertions, 0 deletions
diff --git a/src/list/scroll_state.rs b/src/list/scroll_state.rs
new file mode 100644
index 0000000..25a7373
--- /dev/null
+++ b/src/list/scroll_state.rs
@@ -0,0 +1,104 @@
+pub struct ScrollState {
+ n_rows: usize,
+ max_n_rows_to_display: usize,
+ selected: Option<usize>,
+ offset: usize,
+ scroll_padding: usize,
+ max_scroll_padding: usize,
+}
+
+impl ScrollState {
+ pub fn new(n_rows: usize, selected: Option<usize>, max_scroll_padding: usize) -> Self {
+ Self {
+ n_rows,
+ max_n_rows_to_display: 0,
+ selected,
+ offset: selected.map_or(0, |selected| selected.saturating_sub(max_scroll_padding)),
+ scroll_padding: 0,
+ max_scroll_padding,
+ }
+ }
+
+ #[inline]
+ pub fn offset(&self) -> usize {
+ self.offset
+ }
+
+ fn update_offset(&mut self) {
+ let Some(selected) = self.selected else {
+ return;
+ };
+
+ let min_offset = (selected + self.scroll_padding)
+ .saturating_sub(self.max_n_rows_to_display.saturating_sub(1));
+ let max_offset = selected.saturating_sub(self.scroll_padding);
+ let global_max_offset = self.n_rows.saturating_sub(self.max_n_rows_to_display);
+
+ self.offset = self
+ .offset
+ .max(min_offset)
+ .min(max_offset)
+ .min(global_max_offset);
+ }
+
+ #[inline]
+ pub fn selected(&self) -> Option<usize> {
+ self.selected
+ }
+
+ fn set_selected(&mut self, selected: usize) {
+ self.selected = Some(selected);
+ self.update_offset();
+ }
+
+ pub fn select_next(&mut self) {
+ if let Some(selected) = self.selected {
+ self.set_selected((selected + 1).min(self.n_rows - 1));
+ }
+ }
+
+ pub fn select_previous(&mut self) {
+ if let Some(selected) = self.selected {
+ self.set_selected(selected.saturating_sub(1));
+ }
+ }
+
+ pub fn select_first(&mut self) {
+ if self.n_rows > 0 {
+ self.set_selected(0);
+ }
+ }
+
+ pub fn select_last(&mut self) {
+ if self.n_rows > 0 {
+ self.set_selected(self.n_rows - 1);
+ }
+ }
+
+ pub fn set_n_rows(&mut self, n_rows: usize) {
+ self.n_rows = n_rows;
+
+ if self.n_rows == 0 {
+ self.selected = None;
+ return;
+ }
+
+ self.set_selected(self.selected.map_or(0, |selected| selected.min(n_rows - 1)));
+ }
+
+ #[inline]
+ fn update_scroll_padding(&mut self) {
+ self.scroll_padding = (self.max_n_rows_to_display / 4).min(self.max_scroll_padding);
+ }
+
+ #[inline]
+ pub fn max_n_rows_to_display(&self) -> usize {
+ self.max_n_rows_to_display
+ }
+
+ pub fn set_max_n_rows_to_display(&mut self, max_n_rows_to_display: usize) {
+ self.max_n_rows_to_display = max_n_rows_to_display;
+ self.update_scroll_padding();
+ self.update_offset();
+ }
+}