enable alternate scroll in transcript mode (#2686)
this allows the mouse wheel to scroll the transcript / diff views.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
use std::io::Result;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::insert_history;
|
||||
use crate::tui;
|
||||
@@ -227,6 +228,7 @@ impl TranscriptApp {
|
||||
}
|
||||
|
||||
fn handle_key_event(&mut self, tui: &mut tui::Tui, key_event: KeyEvent) {
|
||||
let mut defer_draw_ms: Option<u64> = None;
|
||||
match key_event {
|
||||
// Ctrl+Z is handled at the App level when transcript overlay is active
|
||||
KeyEvent {
|
||||
@@ -254,6 +256,7 @@ impl TranscriptApp {
|
||||
..
|
||||
} => {
|
||||
self.scroll_offset = self.scroll_offset.saturating_sub(1);
|
||||
defer_draw_ms = Some(16);
|
||||
}
|
||||
KeyEvent {
|
||||
code: KeyCode::Down,
|
||||
@@ -261,6 +264,7 @@ impl TranscriptApp {
|
||||
..
|
||||
} => {
|
||||
self.scroll_offset = self.scroll_offset.saturating_add(1);
|
||||
defer_draw_ms = Some(16);
|
||||
}
|
||||
KeyEvent {
|
||||
code: KeyCode::PageUp,
|
||||
@@ -269,6 +273,7 @@ impl TranscriptApp {
|
||||
} => {
|
||||
let area = self.scroll_area(tui.terminal.viewport_area);
|
||||
self.scroll_offset = self.scroll_offset.saturating_sub(area.height as usize);
|
||||
defer_draw_ms = Some(16);
|
||||
}
|
||||
KeyEvent {
|
||||
code: KeyCode::PageDown | KeyCode::Char(' '),
|
||||
@@ -277,6 +282,7 @@ impl TranscriptApp {
|
||||
} => {
|
||||
let area = self.scroll_area(tui.terminal.viewport_area);
|
||||
self.scroll_offset = self.scroll_offset.saturating_add(area.height as usize);
|
||||
defer_draw_ms = Some(16);
|
||||
}
|
||||
KeyEvent {
|
||||
code: KeyCode::Home,
|
||||
@@ -284,6 +290,7 @@ impl TranscriptApp {
|
||||
..
|
||||
} => {
|
||||
self.scroll_offset = 0;
|
||||
defer_draw_ms = Some(16);
|
||||
}
|
||||
KeyEvent {
|
||||
code: KeyCode::End,
|
||||
@@ -291,12 +298,18 @@ impl TranscriptApp {
|
||||
..
|
||||
} => {
|
||||
self.scroll_offset = usize::MAX;
|
||||
defer_draw_ms = Some(16);
|
||||
}
|
||||
_ => {
|
||||
return;
|
||||
}
|
||||
}
|
||||
tui.frame_requester().schedule_frame();
|
||||
if let Some(ms) = defer_draw_ms {
|
||||
tui.frame_requester()
|
||||
.schedule_frame_in(Duration::from_millis(ms));
|
||||
} else {
|
||||
tui.frame_requester().schedule_frame();
|
||||
}
|
||||
}
|
||||
|
||||
fn scroll_area(&self, area: Rect) -> Rect {
|
||||
|
||||
@@ -11,6 +11,7 @@ use std::sync::atomic::Ordering;
|
||||
use std::time::Duration;
|
||||
use std::time::Instant;
|
||||
|
||||
use crossterm::Command;
|
||||
use crossterm::SynchronizedUpdate;
|
||||
use crossterm::cursor;
|
||||
use crossterm::cursor::MoveTo;
|
||||
@@ -65,6 +66,48 @@ pub fn set_modes() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
struct EnableAlternateScroll;
|
||||
|
||||
impl Command for EnableAlternateScroll {
|
||||
fn write_ansi(&self, f: &mut impl std::fmt::Write) -> std::fmt::Result {
|
||||
write!(f, "\x1b[?1007h")
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn execute_winapi(&self) -> std::io::Result<()> {
|
||||
Err(std::io::Error::other(
|
||||
"tried to execute EnableAlternateScroll using WinAPI; use ANSI instead",
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn is_ansi_code_supported(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
struct DisableAlternateScroll;
|
||||
|
||||
impl Command for DisableAlternateScroll {
|
||||
fn write_ansi(&self, f: &mut impl std::fmt::Write) -> std::fmt::Result {
|
||||
write!(f, "\x1b[?1007l")
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn execute_winapi(&self) -> std::io::Result<()> {
|
||||
Err(std::io::Error::other(
|
||||
"tried to execute DisableAlternateScroll using WinAPI; use ANSI instead",
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn is_ansi_code_supported(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Restore the terminal to its original state.
|
||||
/// Inverse of `set_modes`.
|
||||
pub fn restore() -> Result<()> {
|
||||
@@ -290,6 +333,8 @@ impl Tui {
|
||||
)
|
||||
{
|
||||
if alt_screen_active.load(Ordering::Relaxed) {
|
||||
// Disable alternate scroll when suspending from alt-screen
|
||||
let _ = execute!(stdout(), DisableAlternateScroll);
|
||||
let _ = execute!(stdout(), LeaveAlternateScreen);
|
||||
resume_pending.store(ResumeAction::RestoreAlt as u8, Ordering::Relaxed);
|
||||
} else {
|
||||
@@ -370,6 +415,8 @@ impl Tui {
|
||||
}
|
||||
PreparedResumeAction::RestoreAltScreen => {
|
||||
execute!(self.terminal.backend_mut(), EnterAlternateScreen)?;
|
||||
// Enable "alternate scroll" so terminals may translate wheel to arrows
|
||||
execute!(self.terminal.backend_mut(), EnableAlternateScroll)?;
|
||||
if let Ok(size) = self.terminal.size() {
|
||||
self.terminal.set_viewport_area(ratatui::layout::Rect::new(
|
||||
0,
|
||||
@@ -388,6 +435,8 @@ impl Tui {
|
||||
/// inline viewport for restoration when leaving.
|
||||
pub fn enter_alt_screen(&mut self) -> Result<()> {
|
||||
let _ = execute!(self.terminal.backend_mut(), EnterAlternateScreen);
|
||||
// Enable "alternate scroll" so terminals may translate wheel to arrows
|
||||
let _ = execute!(self.terminal.backend_mut(), EnableAlternateScroll);
|
||||
if let Ok(size) = self.terminal.size() {
|
||||
self.alt_saved_viewport = Some(self.terminal.viewport_area);
|
||||
self.terminal.set_viewport_area(ratatui::layout::Rect::new(
|
||||
@@ -404,6 +453,8 @@ impl Tui {
|
||||
|
||||
/// Leave alternate screen and restore the previously saved inline viewport, if any.
|
||||
pub fn leave_alt_screen(&mut self) -> Result<()> {
|
||||
// Disable alternate scroll when leaving alt-screen
|
||||
let _ = execute!(self.terminal.backend_mut(), DisableAlternateScroll);
|
||||
let _ = execute!(self.terminal.backend_mut(), LeaveAlternateScreen);
|
||||
if let Some(saved) = self.alt_saved_viewport.take() {
|
||||
self.terminal.set_viewport_area(saved);
|
||||
|
||||
Reference in New Issue
Block a user