mirror of
https://github.com/gitui-org/gitui
synced 2026-05-24 09:28:21 +00:00
feat: file and status tab support pageup and pagedown (#2496)
This commit is contained in:
parent
ee5c243cbf
commit
706cdf9243
5 changed files with 99 additions and 4 deletions
|
|
@ -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))
|
* 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
|
### 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))
|
* 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))
|
* 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))
|
* 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))
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use crate::{
|
||||||
error::Result, filetreeitems::FileTreeItems,
|
error::Result, filetreeitems::FileTreeItems,
|
||||||
tree_iter::TreeIterator, TreeItemInfo,
|
tree_iter::TreeIterator, TreeItemInfo,
|
||||||
};
|
};
|
||||||
use std::{collections::BTreeSet, path::Path};
|
use std::{cell::Cell, collections::BTreeSet, path::Path};
|
||||||
|
|
||||||
///
|
///
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
|
@ -30,6 +30,7 @@ pub struct FileTree {
|
||||||
selection: Option<usize>,
|
selection: Option<usize>,
|
||||||
// caches the absolute selection translated to visual index
|
// caches the absolute selection translated to visual index
|
||||||
visual_selection: Option<VisualSelection>,
|
visual_selection: Option<VisualSelection>,
|
||||||
|
pub window_height: Cell<Option<usize>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileTree {
|
impl FileTree {
|
||||||
|
|
@ -42,6 +43,7 @@ impl FileTree {
|
||||||
items: FileTreeItems::new(list, collapsed)?,
|
items: FileTreeItems::new(list, collapsed)?,
|
||||||
selection: if list.is_empty() { None } else { Some(0) },
|
selection: if list.is_empty() { None } else { Some(0) },
|
||||||
visual_selection: None,
|
visual_selection: None,
|
||||||
|
window_height: None.into(),
|
||||||
};
|
};
|
||||||
new_self.visual_selection = new_self.calc_visual_selection();
|
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 {
|
pub fn move_selection(&mut self, dir: MoveSelection) -> bool {
|
||||||
self.selection.is_some_and(|selection| {
|
self.selection.is_some_and(|selection| {
|
||||||
|
|
@ -130,9 +144,13 @@ impl FileTree {
|
||||||
Self::selection_start(selection)
|
Self::selection_start(selection)
|
||||||
}
|
}
|
||||||
MoveSelection::End => self.selection_end(selection),
|
MoveSelection::End => self.selection_end(selection),
|
||||||
MoveSelection::PageDown | MoveSelection::PageUp => {
|
MoveSelection::PageUp => {
|
||||||
None
|
self.selection_page_updown((0..=selection).rev())
|
||||||
}
|
}
|
||||||
|
MoveSelection::PageDown => self
|
||||||
|
.selection_page_updown(
|
||||||
|
selection..(self.items.len()),
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
let changed_index =
|
let changed_index =
|
||||||
|
|
@ -514,4 +532,36 @@ mod test {
|
||||||
assert_eq!(s.count, 3);
|
assert_eq!(s.count, 3);
|
||||||
assert_eq!(s.index, 2);
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -275,6 +275,8 @@ impl RevisionFilesComponent {
|
||||||
let tree_height = usize::from(area.height.saturating_sub(2));
|
let tree_height = usize::from(area.height.saturating_sub(2));
|
||||||
let tree_width = usize::from(area.width);
|
let tree_width = usize::from(area.width);
|
||||||
|
|
||||||
|
self.tree.window_height.set(Some(tree_height));
|
||||||
|
|
||||||
self.tree.visual_selection().map_or_else(
|
self.tree.visual_selection().map_or_else(
|
||||||
|| {
|
|| {
|
||||||
self.scroll.reset();
|
self.scroll.reset();
|
||||||
|
|
|
||||||
|
|
@ -351,6 +351,7 @@ impl DrawableComponent for StatusTreeComponent {
|
||||||
.map(|idx| idx.saturating_sub(selection_offset))
|
.map(|idx| idx.saturating_sub(selection_offset))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let tree_height = r.height.saturating_sub(2) as usize;
|
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.set(ui::calc_scroll_top(
|
||||||
self.scroll_top.get(),
|
self.scroll_top.get(),
|
||||||
|
|
@ -504,6 +505,15 @@ impl Component for StatusTreeComponent {
|
||||||
|| key_match(e, self.key_config.keys.shift_down)
|
|| key_match(e, self.key_config.keys.shift_down)
|
||||||
{
|
{
|
||||||
Ok(self.move_selection(MoveSelection::End).into())
|
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)
|
} else if key_match(e, self.key_config.keys.move_left)
|
||||||
{
|
{
|
||||||
Ok(self
|
Ok(self
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use super::filetree::{
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use asyncgit::StatusItem;
|
use asyncgit::StatusItem;
|
||||||
use std::{cmp, collections::BTreeSet};
|
use std::{cell::Cell, cmp, collections::BTreeSet};
|
||||||
|
|
||||||
//TODO: use new `filetreelist` crate
|
//TODO: use new `filetreelist` crate
|
||||||
|
|
||||||
|
|
@ -16,6 +16,8 @@ pub struct StatusTree {
|
||||||
// some folders may be folded up, this allows jumping
|
// some folders may be folded up, this allows jumping
|
||||||
// over folders which are folded into their parent
|
// over folders which are folded into their parent
|
||||||
pub available_selections: Vec<usize>,
|
pub available_selections: Vec<usize>,
|
||||||
|
|
||||||
|
pub window_height: Cell<Option<usize>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
|
@ -27,6 +29,8 @@ pub enum MoveSelection {
|
||||||
Right,
|
Right,
|
||||||
Home,
|
Home,
|
||||||
End,
|
End,
|
||||||
|
PageDown,
|
||||||
|
PageUp,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
|
@ -143,6 +147,15 @@ impl StatusTree {
|
||||||
}
|
}
|
||||||
MoveSelection::Home => SelectionChange::new(0, false),
|
MoveSelection::Home => SelectionChange::new(0, false),
|
||||||
MoveSelection::End => self.selection_end(),
|
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 =
|
let changed_index =
|
||||||
|
|
@ -283,6 +296,25 @@ impl StatusTree {
|
||||||
SelectionChange::new(new_index, false)
|
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 {
|
fn is_visible_index(&self, idx: usize) -> bool {
|
||||||
self.tree[idx].info.visible
|
self.tree[idx].info.visible
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue