summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTim Oram <dev@mitmaro.ca>2022-04-20 09:15:43 -0230
committerTim Oram <dev@mitmaro.ca>2022-04-20 09:51:11 -0230
commit190a0596fee6de9ff336b5d739e1aa6e5d1a89a4 (patch)
tree38deff933e6237efa45855fcffad8bcf60ecc2d4
parent8bd68f558cff4c1eeed637383c079146892a4727 (diff)
Add keybindings for view scrolling
-rw-r--r--CHANGELOG.md6
-rw-r--r--README.md70
-rw-r--r--src/config/src/key_bindings.rs59
-rw-r--r--src/core/src/modules/show_commit/util.rs15
-rw-r--r--src/core/src/util.rs2
-rw-r--r--src/input/src/event_handler.rs68
-rw-r--r--src/input/src/key_bindings.rs26
-rw-r--r--src/input/src/testutil.rs10
-rw-r--r--src/view/src/render_slice/mod.rs10
-rw-r--r--src/view/src/render_slice/render_action.rs2
-rw-r--r--src/view/src/render_slice/tests.rs45
-rw-r--r--src/view/src/scroll_position.rs144
-rw-r--r--src/view/src/sender.rs28
-rw-r--r--src/view/src/testutil.rs2
14 files changed, 378 insertions, 109 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5de422f..edb56cc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,11 @@
# Change Log
All notable changes to this project will be documented in this file.
-The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/).
+The format is based on [Keep a Changelog](http://keepachangelog.com/).
+
+## [2.2.0] - 2022-04-15
+### Added
+- Added mew keybindings for customizing the scrolling the view. ([#647](https://github.com/MitMaro/git-interactive-rebase-tool/pull/647))
## [2.1.0] - 2021-04-20
diff --git a/README.md b/README.md
index c3635bf..0f1c38a 100644
--- a/README.md
+++ b/README.md
@@ -25,7 +25,7 @@ Native cross-platform full feature terminal based [sequence editor][git-sequence
### Cross-platform
-Built and works on Linux, macOS, Windows and BSD.
+Built and works on Linux, macOS, Windows and many others.
### Set action
@@ -120,36 +120,44 @@ The tool has built-in help that can be accessed by hitting the `?` key.
Key bindings can be configured, see [configuration](readme/customization.md#key-bindings) for more information.
-| Key | Mode | Description |
-| ------------ | ------ | ----------- |
-| Up | All | Move selection up |
-| Down | All | Move selection down |
-| Page Up | All | Move selection up five lines |
-| Page Down | All | Move selection down five lines |
-| Home | All | Move selection to start of list |
-| End | All | Move selection to home of list |
-| `q` | Normal | Abort interactive rebase |
-| `Q` | Normal | Immediately abort interactive rebase |
-| `w` | Normal | Write interactive rebase file |
-| `W` | Normal | Immediately write interactive rebase file |
-| `?` | All | Show help |
-| `c` | Normal | Show commit information |
-| `j` | All | Move selected commit(s) down |
-| `k` | All | Move selected commit(s) up |
-| `b` | Normal | Toggle break action |
-| `p` | All | Set selected commit(s) to be picked |
-| `r` | All | Set selected commit(s) to be reworded |
-| `e` | All | Set selected commit(s) to be edited |
-| `s` | All | Set selected commit(s) to be squashed |
-| `f` | All | Set selected commit(s) to be fixed-up |
-| `d` | All | Set selected commit(s) to be dropped |
-| `E` | Normal | Edit the command of an exec action |
-| `v` | All | Enter and exit visual mode |
-| `d` | Diff | Show full commit diff |
-| `I` | Normal | Insert a new line |
-| `Delete` | All | Remove selected lines |
-| `Control+z` | All | Undo the previous change |
-| `Control+y` | All | Redo the previously undone change |
+| Key | Mode | Description |
+|-------------| ------ |-------------------------------------------|
+| Up | All | Move selection up |
+| Down | All | Move selection down |
+| Page Up | All | Move selection up five lines |
+| Page Down | All | Move selection down five lines |
+| Home | All | Move selection to start of list |
+| End | All | Move selection to home of list |
+| `q` | Normal | Abort interactive rebase |
+| `Q` | Normal | Immediately abort interactive rebase |
+| `w` | Normal | Write interactive rebase file |
+| `W` | Normal | Immediately write interactive rebase file |
+| `?` | All | Show help |
+| `c` | Normal | Show commit information |
+| `j` | All | Move selected commit(s) down |
+| `k` | All | Move selected commit(s) up |
+| `b` | Normal | Toggle break action |
+| `p` | All | Set selected commit(s) to be picked |
+| `r` | All | Set selected commit(s) to be reworded |
+| `e` | All | Set selected commit(s) to be edited |
+| `s` | All | Set selected commit(s) to be squashed |
+| `f` | All | Set selected commit(s) to be fixed-up |
+| `d` | All | Set selected commit(s) to be dropped |
+| `E` | Normal | Edit the command of an exec action |
+| `v` | All | Enter and exit visual mode |
+| `I` | Normal | Insert a new line |
+| `Delete` | All | Remove selected lines |
+| `Control+z` | All | Undo the previous change |
+| `Control+y` | All | Redo the previously undone change |
+| Down | Diff | Scroll view down |
+| Up | Diff | Scroll view up |
+| Left | Diff | Scroll view left |
+| Right | Diff | Scroll view right |
+| Home | Diff | Scroll view to the top |
+| End | Diff | Scroll view to the end |
+| PageUp | Diff | Scroll view a step up |
+| PageDown | Diff | Scroll view a step down |
+| `d` | Diff | Show full commit diff |
## Development
diff --git a/src/config/src/key_bindings.rs b/src/config/src/key_bindings.rs
index 55eb5ce..a6a5d80 100644
--- a/src/config/src/key_bindings.rs
+++ b/src/config/src/key_bindings.rs
@@ -38,10 +38,9 @@ pub struct KeyBindings {
pub help: Vec<String>,
/// Key bindings for inserting a line.
pub insert_line: Vec<String>,
+
/// Key bindings for moving down.
pub move_down: Vec<String>,
- /// Key bindings for moving down a step.
- pub move_down_step: Vec<String>,
/// Key bindings for moving to the end.
pub move_end: Vec<String>,
/// Key bindings for moving to the start.
@@ -50,14 +49,34 @@ pub struct KeyBindings {
pub move_left: Vec<String>,
/// Key bindings for moving to the right.
pub move_right: Vec<String>,
- /// Key bindings for moving the selection down.
- pub move_selection_down: Vec<String>,
- /// Key bindings for moving the selection up.
- pub move_selection_up: Vec<String>,
/// Key bindings for moving up.
pub move_up: Vec<String>,
+ /// Key bindings for moving down a step.
+ pub move_down_step: Vec<String>,
/// Key bindings for moving up a step.
pub move_up_step: Vec<String>,
+ /// Key bindings for moving the selection down.
+ pub move_selection_down: Vec<String>,
+ /// Key bindings for moving the selection up.
+ pub move_selection_up: Vec<String>,
+
+ /// Key bindings for scrolling down.
+ pub scroll_down: Vec<String>,
+ /// Key bindings for scrolling to the end.
+ pub scroll_end: Vec<String>,
+ /// Key bindings for scrolling to the start.
+ pub scroll_home: Vec<String>,
+ /// Key bindings for scrolling to the left.
+ pub scroll_left: Vec<String>,
+ /// Key bindings for scrolling to the right.
+ pub scroll_right: Vec<String>,
+ /// Key bindings for scrolling up.
+ pub scroll_up: Vec<String>,
+ /// Key bindings for scrolling down a step.
+ pub scroll_step_down: Vec<String>,
+ /// Key bindings for scrolling up a step.
+ pub scroll_step_up: Vec<String>,
+
/// Key bindings for opening the external editor.
pub open_in_external_editor: Vec<String>,
/// Key bindings for rebasing.
@@ -110,15 +129,23 @@ impl KeyBindings {
help: get_input(git_config, "interactive-rebase-tool.inputHelp", "?")?,
insert_line: get_input(git_config, "interactive-rebase-tool.insertLine", "I")?,
move_down: get_input(git_config, "interactive-rebase-tool.inputMoveDown", "Down")?,
- move_down_step: get_input(git_config, "interactive-rebase-tool.inputMoveStepDown", "PageDown")?,
move_end: get_input(git_config, "interactive-rebase-tool.inputMoveEnd", "End")?,
move_home: get_input(git_config, "interactive-rebase-tool.inputMoveHome", "Home")?,
move_left: get_input(git_config, "interactive-rebase-tool.inputMoveLeft", "Left")?,
move_right: get_input(git_config, "interactive-rebase-tool.inputMoveRight", "Right")?,
- move_selection_down: get_input(git_config, "interactive-rebase-tool.inputMoveSelectionDown", "j")?,
- move_selection_up: get_input(git_config, "interactive-rebase-tool.inputMoveSelectionUp", "k")?,
+ move_down_step: get_input(git_config, "interactive-rebase-tool.inputMoveStepDown", "PageDown")?,
move_up_step: get_input(git_config, "interactive-rebase-tool.inputMoveStepUp", "PageUp")?,
move_up: get_input(git_config, "interactive-rebase-tool.inputMoveUp", "Up")?,
+ move_selection_down: get_input(git_config, "interactive-rebase-tool.inputMoveSelectionDown", "j")?,
+ move_selection_up: get_input(git_config, "interactive-rebase-tool.inputMoveSelectionUp", "k")?,
+ scroll_down: get_input(git_config, "interactive-rebase-tool.inputScrollDown", "Down")?,
+ scroll_end: get_input(git_config, "interactive-rebase-tool.inputScrollEnd", "End")?,
+ scroll_home: get_input(git_config, "interactive-rebase-tool.inputScrollHome", "Home")?,
+ scroll_left: get_input(git_config, "interactive-rebase-tool.inputScrollLeft", "Left")?,
+ scroll_right: get_input(git_config, "interactive-rebase-tool.inputScrollRight", "Right")?,
+ scroll_up: get_input(git_config, "interactive-rebase-tool.inputScrollUp", "Up")?,
+ scroll_step_down: get_input(git_config, "interactive-rebase-tool.inputScrollStepDown", "PageDown")?,
+ scroll_step_up: get_input(git_config, "interactive-rebase-tool.inputScrollStepUp", "PageUp")?,
open_in_external_editor: get_input(git_config, "interactive-rebase-tool.inputOpenInExternalEditor", "!")?,
rebase: get_input(git_config, "interactive-rebase-tool.inputRebase", "w")?,
redo: get_input(git_config, "interactive-rebase-tool.inputRedo", "control+y")?,
@@ -278,15 +305,23 @@ mod tests {
#[case::help("inputHelp", "?", |bindings: KeyBindings| bindings.help)]
#[case::insert_line("insertLine", "I", |bindings: KeyBindings| bindings.insert_line)]
#[case::move_down("inputMoveDown", "Down", |bindings: KeyBindings| bindings.move_down)]
- #[case::move_down_step("inputMoveStepDown", "PageDown", |bindings: KeyBindings| bindings.move_down_step)]
#[case::move_end("inputMoveEnd", "End", |bindings: KeyBindings| bindings.move_end)]
#[case::move_home("inputMoveHome", "Home", |bindings: KeyBindings| bindings.move_home)]
#[case::move_left("inputMoveLeft", "Left", |bindings: KeyBindings| bindings.move_left)]
#[case::move_right("inputMoveRight", "Right", |bindings: KeyBindings| bindings.move_right)]
- #[case::move_selection_down("inputMoveSelectionDown", "j", |bindings: KeyBindings| bindings.move_selection_down)]
- #[case::move_selection_up("inputMoveSelectionUp", "k", |bindings: KeyBindings| bindings.move_selection_up)]
#[case::move_up("inputMoveUp", "Up", |bindings: KeyBindings| bindings.move_up)]
+ #[case::move_down_step("inputMoveStepDown", "PageDown", |bindings: KeyBindings| bindings.move_down_step)]
#[case::move_up_step("inputMoveStepUp", "PageUp", |bindings: KeyBindings| bindings.move_up_step)]
+ #[case::move_selection_down("inputMoveSelectionDown", "j", |bindings: KeyBindings| bindings.move_selection_down)]
+ #[case::move_selection_up("inputMoveSelectionUp", "k", |bindings: KeyBindings| bindings.move_selection_up)]
+ #[case::scroll_down("inputScrollDown", "Down", |bindings: KeyBindings| bindings.scroll_down)]
+ #[case::scroll_end("inputScrollEnd", "End", |bindings: KeyBindings| bindings.scroll_end)]
+ #[case::scroll_home("inputScrollHome", "Home", |bindings: KeyBindings| bindings.scroll_home)]
+ #[case::scroll_left("inputScrollLeft", "Left", |bindings: KeyBindings| bindings.scroll_left)]
+ #[case::scroll_right("inputScrollRight", "Right", |bindings: KeyBindings| bindings.scroll_right)]
+ #[case::scroll_up("inputScrollUp", "Up", |bindings: KeyBindings| bindings.scroll_up)]
+ #[case::scroll_page_down("inputScrollStepDown", "PageDown", |bindings: KeyBindings| bindings.scroll_step_down)]
+ #[case::scroll_page_up("inputScrollStepUp", "PageUp", |bindings: KeyBindings| bindings.scroll_step_up)]
#[case::open_in_external_editor(
"inputOpenInExternalEditor",
"!",
diff --git a/src/core/src/modules/show_commit/util.rs b/src/core/src/modules/show_commit/util.rs
index 19f5d43..f45b375 100644
--- a/src/core/src/modules/show_commit/util.rs
+++ b/src/core/src/modules/show_commit/util.rs
@@ -12,15 +12,18 @@ const TO_FILE_INDICATOR_SHORT: &str = "\u{2192}"; // "→"
pub(super) fn get_show_commit_help_lines(key_bindings: &KeyBindings) -> Vec<(Vec<String>, String)> {
vec![
- (key_bindings.move_up.clone(), String::from("Scroll up")),
- (key_bindings.move_down.clone(), String::from("Scroll down")),
- (key_bindings.move_up_step.clone(), String::from("Scroll up half a page")),
+ (key_bindings.scroll_up.clone(), String::from("Scroll up")),
+ (key_bindings.scroll_down.clone(), String::from("Scroll down")),
(
- key_bindings.move_down_step.clone(),
+ key_bindings.scroll_step_up.clone(),
+ String::from("Scroll up half a page"),
+ ),
+ (
+ key_bindings.scroll_step_down.clone(),
String::from("Scroll down half a page"),
),
- (key_bindings.move_right.clone(), String::from("Scroll right")),
- (key_bindings.move_left.clone(), String::from("Scroll left")),
+ (key_bindings.scroll_right.clone(), String::from("Scroll right")),
+ (key_bindings.scroll_left.clone(), String::from("Scroll left")),
(key_bindings.show_diff.clone(), String::from("Show full diff")),
(key_bindings.help.clone(), String::from("Show help")),
]
diff --git a/src/core/src/util.rs b/src/core/src/util.rs
index bdb0400..8f59034 100644
--- a/src/core/src/util.rs
+++ b/src/core/src/util.rs
@@ -45,6 +45,8 @@ pub(crate) fn handle_view_data_scroll(event: Event, view_sender: &ViewSender) ->
Event::Standard(meta_event) if meta_event == StandardEvent::ScrollRight => view_sender.scroll_right(),
Event::Standard(meta_event) if meta_event == StandardEvent::ScrollDown => view_sender.scroll_down(),
Event::Standard(meta_event) if meta_event == StandardEvent::ScrollUp => view_sender.scroll_up(),
+ Event::Standard(meta_event) if meta_event == StandardEvent::ScrollTop => view_sender.scroll_top(),
+ Event::Standard(meta_event) if meta_event == StandardEvent::ScrollBottom => view_sender.scroll_bottom(),
Event::Standard(meta_event) if meta_event == StandardEvent::ScrollJumpDown => view_sender.scroll_page_down(),
Event::Standard(meta_event) if meta_event == StandardEvent::ScrollJumpUp => view_sender.scroll_page_up(),
_ => return None,
diff --git a/src/input/src/event_handler.rs b/src/input/src/event_handler.rs
index 78923aa..fdf6aa6 100644
--- a/src/input/src/event_handler.rs
+++ b/src/input/src/event_handler.rs
@@ -44,7 +44,7 @@ impl<CustomKeybinding: crate::CustomKeybinding, CustomEvent: crate::CustomEvent>
}
if input_options.contains(InputOptions::MOVEMENT) {
- if let Some(evt) = Self::handle_movement_inputs(event) {
+ if let Some(evt) = Self::handle_movement_inputs(&self.key_bindings, event) {
return evt;
}
}
@@ -74,42 +74,54 @@ impl<CustomKeybinding: crate::CustomKeybinding, CustomEvent: crate::CustomEvent>
}
#[allow(clippy::wildcard_enum_match_arm)]
- fn handle_movement_inputs(event: Event<CustomEvent>) -> Option<Event<CustomEvent>> {
- match event {
+ fn handle_movement_inputs(
+ key_bindings: &KeyBindings<CustomKeybinding, CustomEvent>,
+ event: Event<CustomEvent>,
+ ) -> Option<Event<CustomEvent>> {
+ Some(match event {
+ e if key_bindings.scroll_down.contains(&e) => Event::from(StandardEvent::ScrollDown),
+ e if key_bindings.scroll_end.contains(&e) => Event::from(StandardEvent::ScrollBottom),
+ e if key_bindings.scroll_home.contains(&e) => Event::from(StandardEvent::ScrollTop),
+ e if key_bindings.scroll_left.contains(&e) => Event::from(StandardEvent::ScrollLeft),
+ e if key_bindings.scroll_right.contains(&e) => Event::from(StandardEvent::ScrollRight),
+ e if key_bindings.scroll_up.contains(&e) => Event::from(StandardEvent::ScrollUp),
+ e if key_bindings.scroll_step_down.contains(&e) => Event::from(StandardEvent::ScrollJumpDown),
+ e if key_bindings.scroll_step_up.contains(&e) => Event::from(StandardEvent::ScrollJumpUp),
+ // these are required, since in some contexts (like editing), other keybindings will not work
Event::Key(KeyEvent {
code: KeyCode::Up,
modifiers: KeyModifiers::NONE,
- }) => Some(Event::from(StandardEvent::ScrollUp)),
+ }) => Event::from(StandardEvent::ScrollUp),
Event::Key(KeyEvent {
code: KeyCode::Down,
modifiers: KeyModifiers::NONE,
- }) => Some(Event::from(StandardEvent::ScrollDown)),
+ }) => Event::from(StandardEvent::ScrollDown),
Event::Key(KeyEvent {
code: KeyCode::Left,
modifiers: KeyModifiers::NONE,
- }) => Some(Event::from(StandardEvent::ScrollLeft)),
+ }) => Event::from(StandardEvent::ScrollLeft),
Event::Key(KeyEvent {
code: KeyCode::Right,
modifiers: KeyModifiers::NONE,
- }) => Some(Event::from(StandardEvent::ScrollRight)),
+ }) => Event::from(StandardEvent::ScrollRight),
Event::Key(KeyEvent {
code: KeyCode::PageUp,
modifiers: KeyModifiers::NONE,
- }) => Some(Event::from(StandardEvent::ScrollJumpUp)),
+ }) => Event::from(StandardEvent::ScrollJumpUp),
Event::Key(KeyEvent {
code: KeyCode::PageDown,
modifiers: KeyModifiers::NONE,
- }) => Some(Event::from(StandardEvent::ScrollJumpDown)),
+ }) => Event::from(StandardEvent::ScrollJumpDown),
Event::Key(KeyEvent {
code: KeyCode::Home,
modifiers: KeyModifiers::NONE,
- }) => Some(Event::from(StandardEvent::ScrollTop)),
+ }) => Event::from(StandardEvent::ScrollTop),
Event::Key(KeyEvent {
code: KeyCode::End,
modifiers: KeyModifiers::NONE,
- }) => Some(Event::from(StandardEvent::ScrollBottom)),
- _ => None,
- }
+ }) => Event::from(StandardEvent::ScrollBottom),
+ _ => return None,
+ })
}
fn handle_undo_redo(
@@ -133,7 +145,10 @@ mod tests {
use rstest::rstest;
use super::*;
- use crate::testutil::local::{create_test_keybindings, Event, EventHandler};
+ use crate::{
+ map_keybindings,
+ testutil::local::{create_test_keybindings, Event, EventHandler},
+ };
#[rstest]
#[case::standard(Event::Key(KeyEvent {
@@ -223,6 +238,31 @@ mod tests {
}
#[rstest]
+ #[case::standard(Event::from(KeyCode::Up), Event::from(StandardEvent::ScrollUp))]
+ #[case::standard(Event::from(KeyCode::Down), Event::from(StandardEvent::ScrollDown))]
+ #[case::standard(Event::from(KeyCode::Left), Event::from(StandardEvent::ScrollLeft))]
+ #[case::standard(Event::from(KeyCode::Right), Event::from(StandardEvent::ScrollRight))]
+ #[case::standard(Event::from(KeyCode::PageUp), Event::from(StandardEvent::ScrollJumpUp))]
+ #[case::standard(Event::from(KeyCode::PageDown), Event::from(StandardEvent::ScrollJumpDown))]
+ #[case::standard(Event::from(KeyCode::Home), Event::from(StandardEvent::ScrollTop))]
+ #[case::standard(Event::from(KeyCode::End), Event::from(StandardEvent::ScrollBottom))]
+ #[case::other(Event::from('a'), Event::from(KeyCode::Null))]
+ fn default_movement_inputs(#[case] event: Event, #[case] expected: Event) {
+ let mut bindings = create_test_keybindings();
+ bindings.scroll_down = map_keybindings(&[String::from("x")]);
+ bindings.scroll_end = map_keybindings(&[String::from("x")]);
+ bindings.scroll_home = map_keybindings(&[String::from("x")]);
+ bindings.scroll_left = map_keybindings(&[String::from("x")]);
+ bindings.scroll_right = map_keybindings(&[String::from("x")]);
+ bindings.scroll_up = map_keybindings(&[String::from("x")]);
+ bindings.scroll_step_down = map_keybindings(&[String::from("x")]);
+ bindings.scroll_step_up = map_keybindings(&[String::from("x")]);
+ let event_handler = EventHandler::new(bindings);
+ let result = event_handler.read_event(event, &InputOptions::MOVEMENT, |_, _| Event::from(KeyCode::Null));
+ assert_eq!(result, expected);
+ }
+
+ #[rstest]
#[case::standard(Event::Key(KeyEvent {
code: KeyCode::Char('z'),
modifiers: KeyModifiers::CONTROL,
diff --git a/src/input/src/key_bindings.rs b/src/input/src/key_bindings.rs
index a0be033..fe3fff3 100644
--- a/src/input/src/key_bindings.rs
+++ b/src/input/src/key_bindings.rs
@@ -8,6 +8,24 @@ pub struct KeyBindings<CustomKeybinding: crate::CustomKeybinding, CustomEvent: c
pub redo: Vec<Event<CustomEvent>>,
/// Key bindings for undoing a change.
pub undo: Vec<Event<CustomEvent>>,
+
+ /// Key bindings for scrolling down.
+ pub scroll_down: Vec<Event<CustomEvent>>,
+ /// Key bindings for scrolling to the end.
+ pub scroll_end: Vec<Event<CustomEvent>>,
+ /// Key bindings for scrolling to the start.
+ pub scroll_home: Vec<Event<CustomEvent>>,
+ /// Key bindings for scrolling to the left.
+ pub scroll_left: Vec<Event<CustomEvent>>,
+ /// Key bindings for scrolling to the right.
+ pub scroll_right: Vec<Event<CustomEvent>>,
+ /// Key bindings for scrolling up.
+ pub scroll_up: Vec<Event<CustomEvent>>,
+ /// Key bindings for scrolling down a step.
+ pub scroll_step_down: Vec<Event<CustomEvent>>,
+ /// Key bindings for scrolling up a step.
+ pub scroll_step_up: Vec<Event<CustomEvent>>,
+
/// Custom keybindings
pub custom: CustomKeybinding,
}
@@ -73,6 +91,14 @@ impl<CustomKeybinding: crate::CustomKeybinding, CustomEvent: crate::CustomEvent>
Self {
redo: map_keybindings(&key_bindings.redo),
undo: map_keybindings(&key_bindings.undo),
+ scroll_down: map_keybindings(&key_bindings.scroll_down),
+ scroll_end: map_keybindings(&key_bindings.scroll_end),
+ scroll_home: map_keybindings(&key_bindings.scroll_home),
+ scroll_left: map_keybindings(&key_bindings.scroll_left),
+ scroll_right: map_keybindings(&key_bindings.scroll_right),
+ scroll_up: map_keybindings(&key_bindings.scroll_up),
+ scroll_step_down: map_keybindings(&key_bindings.scroll_step_down),
+ scroll_step_up: map_keybindings(&key_bindings.scroll_step_up),
custom: CustomKeybinding::new(key_bindings),
}
}
diff --git a/src/input/src/testutil.rs b/src/input/src/testutil.rs
index 95c43b0..ef7399c 100644
--- a/src/input/src/testutil.rs
+++ b/src/input/src/testutil.rs
@@ -1,7 +1,7 @@
//! Utilities for writing tests that interact with input events.
use super::{Event, EventHandler, KeyBindings, KeyCode, KeyEvent, KeyModifiers};
-use crate::{event_action::EventAction, Sender};
+use crate::{event_action::EventAction, map_keybindings, Sender};
#[cfg(test)]
pub(crate) mod local {
@@ -46,6 +46,14 @@ pub fn create_test_keybindings<TestKeybinding: crate::CustomKeybinding, CustomEv
modifiers: KeyModifiers::CONTROL,
}
})],
+ scroll_down: map_keybindings(&[String::from("Down")]),
+ scroll_end: map_keybindings(&[String::from("End")]),
+ scroll_home: map_keybindings(&[String::from("Home")]),
+ scroll_left: map_keybindings(&[String::from("Left")]),
+ scroll_right: map_keybindings(&[String::from("Right")]),
+ scroll_up: map_keybindings(&[String::from("Up")]),
+ scroll_step_down: map_keybindings(&[String::from("PageDown")]),
+ scroll_step_up: map_keybindings(&[String::from("PageUp")]),
custom: custom_key_bindings,
}
}
diff --git a/src/view/src/render_slice/mod.rs b/src/view/src/render_slice/mod.rs
index 9227087..857b9a5 100644
--- a/src/view/src/render_slice/mod.rs
+++ b/src/view/src/render_slice/mod.rs
@@ -79,6 +79,14 @@ impl RenderSlice {
self.actions.push_back(RenderAction::ScrollRight);
}
+ pub fn record_scroll_top(&mut self) {
+ self.actions.push_back(RenderAction::ScrollTop);
+ }
+
+ pub fn record_scroll_bottom(&mut self) {
+ self.actions.push_back(RenderAction::ScrollBottom);
+ }
+
pub fn record_resize(&mut self, width: usize, height: usize) {
self.actions.push_back(RenderAction::Resize(width, height));
}
@@ -97,6 +105,8 @@ impl RenderSlice {
RenderAction::ScrollUp => self.scroll_position.scroll_up(),
RenderAction::ScrollRight => self.scroll_position.scroll_right(),
RenderAction::ScrollLeft => self.scroll_position.scroll_left(),
+ RenderAction::ScrollTop => self.scroll_position.scroll_top(),
+ RenderAction::ScrollBottom => self.scroll_position.scroll_bottom(),
RenderAction::PageUp => self.scroll_position.page_up(),
RenderAction::PageDown => self.scroll_position.page_down(),
RenderAction::Resize(width, height) => self.set_size(width, height),
diff --git a/src/view/src/render_slice/render_action.rs b/src/view/src/render_slice/render_action.rs
index 05ffc4b..df0cbd2 100644
--- a/src/view/src/render_slice/render_action.rs
+++ b/src/view/src/render_slice/render_action.rs
@@ -4,6 +4,8 @@ pub(crate) enum RenderAction {
ScrollUp,
ScrollRight,
ScrollLeft,
+ ScrollTop,
+ ScrollBottom,
PageUp,
PageDown,
Resize(usize, usize),
diff --git a/src/view/src/render_slice/tests.rs b/src/view/src/render_slice/tests.rs
index 9837f89..8fda4a5 100644
--- a/src/view/src/render_slice/tests.rs
+++ b/src/view/src/render_slice/tests.rs
@@ -210,6 +210,51 @@ fn resize_action() {
}
#[test]
+fn scroll_top_action() {
+ let view_data = create_view_data(2, 10, 2);
+ let mut render_slice = create_render_slice(100, 8, &view_data);
+ for _ in 0..6 {
+ render_slice.scroll_position.scroll_down();
+ }
+ render_slice.record_scroll_top();
+ render_slice.sync_view_data(&view_data);
+ assert_rendered(&render_slice, &[
+ "{LEADING}",
+ "{Normal}L(1)",
+ "{Normal}L(2)",
+ "{BODY}",
+ "{Normal}B(1)",
+ "{Normal}B(2)",
+ "{Normal}B(3)",
+ "{Normal}B(4)",
+ "{TRAILING}",
+ "{Normal}T(1)",
+ "{Normal}T(2)",
+ ]);
+}
+
+#[test]
+fn scroll_bottom_action() {
+ let view_data = create_view_data(2, 10, 2);
+ let mut render_slice = create_render_slice(100, 8, &view_data);
+ render_slice.record_scroll_bottom();
+ render_slice.sync_view_data(&view_data);
+ assert_rendered(&render_slice, &[
+ "{LEADING}",
+ "{Normal}L(1)",
+ "{Normal}L(2)",
+ "{BODY}",
+ "{Normal}B(7)",
+ "{Normal}B(8)",
+ "{Normal}B(9)",
+ "{Normal}B(10)",
+ "{TRAILING}",
+ "{Normal}T(1)",
+ "{Normal}T(2)",
+ ]);
+}
+
+#[test]
fn resize_action_zero_width() {
let view_data = create_view_data(0, 3, 0);
let mut render_slice = create_render_slice(1, 1, &view_data);
diff --git a/src/view/src/scroll_position.rs b/src/view/src/scroll_position.rs
index 06a1b8a..9a589b6 100644
--- a/src/view/src/scroll_position.rs
+++ b/src/view/src/scroll_position.rs
@@ -1,9 +1,11 @@
#[derive(Copy, Clone, Debug, PartialEq)]
enum ScrollDirection {
- Up,
- Down,
- Left,
- Right,
+ Top,
+ Up(usize),
+ Down(usize),
+ Left(usize),
+ Right(usize),
+ Bottom,
}
#[derive(Debug)]
@@ -38,27 +40,35 @@ impl ScrollPosition {
}
pub(crate) fn scroll_up(&mut self) {
- self.update_top(1, ScrollDirection::Up);
+ self.update_top(ScrollDirection::Up(1));
}
pub(crate) fn scroll_down(&mut self) {
- self.update_top(1, ScrollDirection::Down);
+ self.update_top(ScrollDirection::Down(1));
}
pub(crate) fn page_up(&mut self) {
- self.update_top(self.view_height / 2, ScrollDirection::Up);
+ self.update_top(ScrollDirection::Up(self.view_height / 2));
}
pub(crate) fn page_down(&mut self) {
- self.update_top(self.view_height / 2, ScrollDirection::Down);
+ self.update_top(ScrollDirection::Down(self.view_height / 2));
}
pub(crate) fn scroll_left(&mut self) {
- self.update_left(1, ScrollDirection::Left);
+ self.update_left(ScrollDirection::Left(1));
}
pub(crate) fn scroll_right(&mut self) {
- self.update_left(1, ScrollDirection::Right);
+ self.update_left(ScrollDirection::Right(1));
+ }
+
+ pub(crate) fn scroll_top(&mut self) {
+ self.update_top(ScrollDirection::Top);
+ }
+
+ pub(crate) fn scroll_bottom(&mut self) {
+ self.update_top(ScrollDirection::Bottom);
}
pub(crate) fn ensure_line_visible(&mut self, line_index: usize) {
@@ -141,10 +151,9 @@ impl ScrollPosition {
}
// recalculate left to remove any padding space to the right
else if self.max_line_width > self.view_width && self.max_line_width <= (self.view_width + self.left_value) {
- self.update_left(
+ self.update_left(ScrollDirection::Left(
self.view_width + self.left_value - self.max_line_width,
- ScrollDirection::Left,
- );
+ ));
}
}
@@ -154,14 +163,13 @@ impl ScrollPosition {
}
// recalculate top to remove any padding space below the set of lines
else if self.lines_length > self.view_height && self.lines_length <= (self.view_height + self.top_value) {
- self.update_top(
+ self.update_top(ScrollDirection::Up(
self.view_height + self.top_value - self.lines_length,
- ScrollDirection::Up,
- );
+ ));
}
}
- fn update_top(&mut self, amount: usize, direction: ScrollDirection) {
+ fn update_top(&mut self, direction: ScrollDirection) {
if self.view_height >= self.lines_length {
self.top_value = 0;
return;
@@ -169,41 +177,53 @@ impl ScrollPosition {
let current_value = self.top_value;
- if direction == ScrollDirection::Up {
- if current_value < amount {
- self.top_value = 0;
- }
- else {
- self.top_value = current_value - amount;
- }
- }
- else if current_value + amount + self.view_height > self.lines_length {
- self.top_value = self.lines_length - self.view_height;
- }
- else {
- self.top_value = current_value + amount;
+ match direction {
+ ScrollDirection::Top => self.top_value = 0,
+ ScrollDirection::Up(amount) => {
+ if current_value < amount {
+ self.top_value = 0;
+ }
+ else {
+ self.top_value = current_value - amount;
+ }
+ },
+ ScrollDirection::Down(amount) => {
+ if current_value + amount + self.view_height > self.lines_length {
+ self.top_value = self.lines_length - self.view_height;
+ }
+ else {
+ self.top_value = current_value + amount;
+ }
+ },
+ ScrollDirection::Bottom => self.top_value = self.lines_length - self.view_height,
+ ScrollDirection::Left(_) | ScrollDirection::Right(_) => {},
}
}
- fn update_left(&mut self, amount: usize, direction: ScrollDirection) {
+ fn update_left(&mut self, direction: ScrollDirection) {
if self.view_width >= self.max_line_width {
self.left_value = 0;
return;
}
- if direction == ScrollDirection::Left {
- if self.left_value < amount {
- self.left_value = 0;
- }
- else {
- self.left_value -= amount;
- }
- }
- else if self.left_value + amount + self.view_width > self.max_line_width {
- self.left_value = self.max_line_width - self.view_width;
- }
- else {
- self.left_value += amount;
+ match direction {
+ ScrollDirection::Left(amount) => {
+ if self.left_value < amount {
+ self.left_value = 0;
+ }
+ else {
+ self.left_value -= amount;
+ }
+ },
+ ScrollDirection::Right(amount) => {
+ if self.left_value + amount + self.view_width > self.max_line_width {
+ self.left_value = self.max_line_width - self.view_width;
+ }
+ else {
+ self.left_value += amount;
+ }
+ },
+ ScrollDirection::Top | ScrollDirection::Up(_) | ScrollDirection::Down(_) | ScrollDirection::Bottom => {},
}
}
}
@@ -575,6 +595,42 @@ mod tests {
}
#[test]
+ fn scroll_position_scroll_top() {
+ let mut scroll_position = ScrollPosition::new();
+ scroll_position.lines_length = 5;
+ scroll_position.view_height = 10;
+ scroll_position.scroll_top();
+ assert_eq!(scroll_position.get_top_position(), 0);
+ }
+
+ #[test]
+ fn scroll_position_scroll_bottom_view_size_equal_list() {
+ let mut scroll_position = ScrollPosition::new();
+ scroll_position.lines_length = 10;
+ scroll_position.view_height = 10;
+ scroll_position.scroll_bottom();
+ assert_eq!(scroll_position.get_top_position(), 0);
+ }
+
+ #[test]
+ fn scroll_position_scroll_bottom_view_size_less_list() {
+ let mut scroll_position = ScrollPosition::new();
+ scroll_position.lines_length = 10;
+ scroll_position.view_height = 5;
+ scroll_position.scroll_bottom();
+ assert_eq!(scroll_position.get_top_position(), 5);
+ }
+
+ #[test]
+ fn scroll_position_scroll_bottom_view_size_greater_list() {
+ let mut scroll_position = ScrollPosition::new();
+ scroll_position.lines_length = 5;
+ scroll_position.view_height = 15;
+ scroll_position.scroll_bottom();
+ assert_eq!(scroll_position.get_top_position(), 0);
+ }
+
+ #[test]
fn scroll_position_resize_set_height_width() {
let mut scroll_position = ScrollPosition::new();
scroll_position.resize(111, 222);
diff --git a/src/vie