feat: file and status tab support pageup and pagedown (#2496)

This commit is contained in:
Fatpandac 2025-04-22 02:23:05 +08:00 committed by GitHub
parent ee5c243cbf
commit 706cdf9243
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 99 additions and 4 deletions

View file

@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* execute git-hooks directly if possible (on *nix) else use sh instead of bash (without reading SHELL variable) [[@Joshix](https://github.com/Joshix-1)] ([#2483](https://github.com/extrawurst/gitui/pull/2483))
### Added
* Files and status tab support pageUp and pageDown [[@fatpandac](https://github.com/fatpandac)] ([#1951](https://github.com/extrawurst/gitui/issues/1951))
* support loading custom syntax highlighting themes from a file [[@acuteenvy](https://github.com/acuteenvy)] ([#2565](https://github.com/gitui-org/gitui/pull/2565))
* Select syntax highlighting theme out of the defaults from syntect [[@vasilismanol](https://github.com/vasilismanol)] ([#1931](https://github.com/extrawurst/gitui/issues/1931))
* new command-line option to override the default log file path (`--logfile`) [[@acuteenvy](https://github.com/acuteenvy)] ([#2539](https://github.com/gitui-org/gitui/pull/2539))

View file

@ -2,7 +2,7 @@ use crate::{
error::Result, filetreeitems::FileTreeItems,
tree_iter::TreeIterator, TreeItemInfo,
};
use std::{collections::BTreeSet, path::Path};
use std::{cell::Cell, collections::BTreeSet, path::Path};
///
#[derive(Copy, Clone, Debug)]
@ -30,6 +30,7 @@ pub struct FileTree {
selection: Option<usize>,
// caches the absolute selection translated to visual index
visual_selection: Option<VisualSelection>,
pub window_height: Cell<Option<usize>>,
}
impl FileTree {
@ -42,6 +43,7 @@ impl FileTree {
items: FileTreeItems::new(list, collapsed)?,
selection: if list.is_empty() { None } else { Some(0) },
visual_selection: None,
window_height: None.into(),
};
new_self.visual_selection = new_self.calc_visual_selection();
@ -112,6 +114,18 @@ impl FileTree {
}
}
fn selection_page_updown(
&self,
range: impl Iterator<Item = usize>,
) -> Option<usize> {
let page_size = self.window_height.get().unwrap_or(0);
range
.filter(|index| self.is_visible_index(*index))
.take(page_size)
.last()
}
///
pub fn move_selection(&mut self, dir: MoveSelection) -> bool {
self.selection.is_some_and(|selection| {
@ -130,9 +144,13 @@ impl FileTree {
Self::selection_start(selection)
}
MoveSelection::End => self.selection_end(selection),
MoveSelection::PageDown | MoveSelection::PageUp => {
None
MoveSelection::PageUp => {
self.selection_page_updown((0..=selection).rev())
}
MoveSelection::PageDown => self
.selection_page_updown(
selection..(self.items.len()),
),
};
let changed_index =
@ -514,4 +532,36 @@ mod test {
assert_eq!(s.count, 3);
assert_eq!(s.index, 2);
}
#[test]
fn test_selection_page_updown() {
let items = vec![
Path::new("a/b/c"), //
Path::new("a/b/c2"), //
Path::new("a/d"), //
Path::new("a/e"), //
];
//0 a/
//1 b/
//2 c
//3 c2
//4 d
//5 e
let mut tree =
FileTree::new(&items, &BTreeSet::new()).unwrap();
tree.window_height.set(Some(3));
tree.selection = Some(0);
assert!(tree.move_selection(MoveSelection::PageDown));
assert_eq!(tree.selection, Some(2));
assert!(tree.move_selection(MoveSelection::PageDown));
assert_eq!(tree.selection, Some(4));
assert!(tree.move_selection(MoveSelection::PageUp));
assert_eq!(tree.selection, Some(2));
assert!(tree.move_selection(MoveSelection::PageUp));
assert_eq!(tree.selection, Some(0));
}
}

View file

@ -275,6 +275,8 @@ impl RevisionFilesComponent {
let tree_height = usize::from(area.height.saturating_sub(2));
let tree_width = usize::from(area.width);
self.tree.window_height.set(Some(tree_height));
self.tree.visual_selection().map_or_else(
|| {
self.scroll.reset();

View file

@ -351,6 +351,7 @@ impl DrawableComponent for StatusTreeComponent {
.map(|idx| idx.saturating_sub(selection_offset))
.unwrap_or_default();
let tree_height = r.height.saturating_sub(2) as usize;
self.tree.window_height.set(Some(tree_height));
self.scroll_top.set(ui::calc_scroll_top(
self.scroll_top.get(),
@ -504,6 +505,15 @@ impl Component for StatusTreeComponent {
|| key_match(e, self.key_config.keys.shift_down)
{
Ok(self.move_selection(MoveSelection::End).into())
} else if key_match(e, self.key_config.keys.page_up) {
Ok(self
.move_selection(MoveSelection::PageUp)
.into())
} else if key_match(e, self.key_config.keys.page_down)
{
Ok(self
.move_selection(MoveSelection::PageDown)
.into())
} else if key_match(e, self.key_config.keys.move_left)
{
Ok(self

View file

@ -3,7 +3,7 @@ use super::filetree::{
};
use anyhow::Result;
use asyncgit::StatusItem;
use std::{cmp, collections::BTreeSet};
use std::{cell::Cell, cmp, collections::BTreeSet};
//TODO: use new `filetreelist` crate
@ -16,6 +16,8 @@ pub struct StatusTree {
// some folders may be folded up, this allows jumping
// over folders which are folded into their parent
pub available_selections: Vec<usize>,
pub window_height: Cell<Option<usize>>,
}
///
@ -27,6 +29,8 @@ pub enum MoveSelection {
Right,
Home,
End,
PageDown,
PageUp,
}
#[derive(Copy, Clone, Debug)]
@ -143,6 +147,15 @@ impl StatusTree {
}
MoveSelection::Home => SelectionChange::new(0, false),
MoveSelection::End => self.selection_end(),
MoveSelection::PageUp => self.selection_page_updown(
selection,
(0..=selection).rev(),
),
MoveSelection::PageDown => self
.selection_page_updown(
selection,
selection..(self.tree.len()),
),
};
let changed_index =
@ -283,6 +296,25 @@ impl StatusTree {
SelectionChange::new(new_index, false)
}
fn selection_page_updown(
&self,
current_index: usize,
range: impl Iterator<Item = usize>,
) -> SelectionChange {
let page_size = self.window_height.get().unwrap_or(0);
let new_index = range
.filter(|index| {
self.available_selections.contains(index)
&& self.is_visible_index(*index)
})
.take(page_size)
.last()
.unwrap_or(current_index);
SelectionChange::new(new_index, false)
}
fn is_visible_index(&self, idx: usize) -> bool {
self.tree[idx].info.visible
}