fix: avoid redraw backlogs during rapid mouse scrolling
This commit is contained in:
+98
-5
@@ -217,6 +217,27 @@ impl BufferViewports {
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn scroll_ready(
|
||||
&mut self,
|
||||
session: &mut EditorSession,
|
||||
mut mouse: MouseEvent,
|
||||
mut next: impl FnMut() -> io::Result<Option<Event>>,
|
||||
) -> io::Result<(bool, Option<Event>)> {
|
||||
let mut redraw = false;
|
||||
// Bound input work so drawing and background replies cannot starve.
|
||||
for index in 0..64 {
|
||||
redraw |= self.scroll(session, mouse);
|
||||
if index == 63 {
|
||||
break;
|
||||
}
|
||||
match next()? {
|
||||
Some(Event::Mouse(report)) => mouse = report,
|
||||
pending => return Ok((redraw, pending)),
|
||||
}
|
||||
}
|
||||
Ok((redraw, None))
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_frame<W: io::Write>(
|
||||
@@ -240,6 +261,7 @@ pub fn run(session: &mut EditorSession) -> io::Result<()> {
|
||||
Terminal::new(CrosstermBackend::new(io::stdout()))?;
|
||||
let mut viewports = BufferViewports::default();
|
||||
let mut redraw = true;
|
||||
let mut pending = None;
|
||||
while !session.editor().should_quit() {
|
||||
redraw |= session.poll();
|
||||
if redraw {
|
||||
@@ -248,10 +270,15 @@ pub fn run(session: &mut EditorSession) -> io::Result<()> {
|
||||
})?;
|
||||
redraw = false;
|
||||
}
|
||||
if !event::poll(Duration::from_millis(25))? {
|
||||
continue;
|
||||
}
|
||||
match event::read()? {
|
||||
let input = if let Some(input) = pending.take() {
|
||||
input
|
||||
} else {
|
||||
if !event::poll(Duration::from_millis(25))? {
|
||||
continue;
|
||||
}
|
||||
event::read()?
|
||||
};
|
||||
match input {
|
||||
Event::Key(key) if key.kind != KeyEventKind::Release => {
|
||||
if let Some(input) = translate(key) {
|
||||
viewports.panels.follow_files();
|
||||
@@ -264,7 +291,17 @@ pub fn run(session: &mut EditorSession) -> io::Result<()> {
|
||||
session.handle(Input::Paste(text));
|
||||
redraw = true;
|
||||
}
|
||||
Event::Mouse(mouse) => redraw |= viewports.scroll(session, mouse),
|
||||
Event::Mouse(mouse) => {
|
||||
let (changed, following) = viewports.scroll_ready(session, mouse, || {
|
||||
if event::poll(Duration::ZERO)? {
|
||||
event::read().map(Some)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
})?;
|
||||
redraw |= changed;
|
||||
pending = following;
|
||||
}
|
||||
Event::Resize(_, _) => redraw = true,
|
||||
_ => {}
|
||||
}
|
||||
@@ -4182,6 +4219,62 @@ end
|
||||
assert!(terminal.backend().cursor_visible());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn queued_wheel_input_renders_latest_view_without_crossing_key_or_resize_barriers() {
|
||||
let directory = Directory::new();
|
||||
let text = (0..24)
|
||||
.map(|row| format!("row-{row:03}\n"))
|
||||
.collect::<String>();
|
||||
let path = directory.file("rows.txt", &text);
|
||||
let mut session = EditorSession::open(Some(path), vec![], None).unwrap();
|
||||
let mut terminal = Terminal::new(ratatui::backend::TestBackend::new(20, 6)).unwrap();
|
||||
let mut viewports = BufferViewports::default();
|
||||
let down = Event::Mouse(document_wheel(MouseEventKind::ScrollDown, 1, 1));
|
||||
let up = Event::Mouse(document_wheel(MouseEventKind::ScrollUp, 1, 1));
|
||||
for barrier in [
|
||||
Event::Key(KeyEvent::new(KeyCode::Char('i'), KeyModifiers::NONE)),
|
||||
Event::Resize(40, 12),
|
||||
] {
|
||||
terminal
|
||||
.draw(|frame| viewports.draw(frame, session.editor()))
|
||||
.unwrap();
|
||||
let mut queued = [
|
||||
down.clone(),
|
||||
up.clone(),
|
||||
down.clone(),
|
||||
barrier.clone(),
|
||||
down.clone(),
|
||||
]
|
||||
.into_iter();
|
||||
let (redraw, pending) = viewports
|
||||
.scroll_ready(
|
||||
&mut session,
|
||||
document_wheel(MouseEventKind::ScrollDown, 1, 1),
|
||||
|| Ok(queued.next()),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(redraw);
|
||||
terminal
|
||||
.draw(|frame| viewports.draw(frame, session.editor()))
|
||||
.unwrap();
|
||||
assert!(
|
||||
screen_text(&terminal)
|
||||
.lines()
|
||||
.next()
|
||||
.unwrap()
|
||||
.contains("row-006")
|
||||
);
|
||||
assert_eq!(pending, Some(barrier));
|
||||
assert_eq!(queued.next(), Some(down.clone()));
|
||||
assert_eq!(session.editor().cursor(), Cursor::default());
|
||||
assert_eq!(session.editor().mode(), Mode::Normal);
|
||||
assert!(!terminal.backend().cursor_visible());
|
||||
session.handle(Input::Up);
|
||||
}
|
||||
|
||||
assert_eq!(session.editor().lines().join("\n"), text);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_op_navigation_restores_caret_following_through_core_dispatch() {
|
||||
let directory = Directory::new();
|
||||
|
||||
Reference in New Issue
Block a user