summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorThomas Linford <tlinford@users.noreply.github.com>2021-07-02 16:40:50 +0200
committerGitHub <noreply@github.com>2021-07-02 16:40:50 +0200
commitf93308f211565fae527f9b1e5788e12d0ea887c4 (patch)
treeedb915be32a81c1caae2c7218398c96344a29bd0 /src
parent2a024db839e3be60f9428d39f6e84c7e748162d0 (diff)
feat(ui): initial mouse support (#448)
* Initial mouse support * enable mouse support (termion MouseTerminal) * handle scroll up and down events * Allow enabling/disabling of mouse reporting Store the mouse terminal on OsInputOutput * WIP: switch pane focus with mouse * testing to get mouse character selection * wip mouse selection * wip: mouse selection - initial handling of mouse events for text selection within a pane - wide characters currently problematic - selection is not highlighted * highlight currently selected text * improve get currently selected text from TerminalPane * copy selection to clipboard (wayland only for now) * Add missing set_should_render on selection update * Improve text selection - Strip whitespace from end of lines - Insert newlines when selection spans multiple lines * Simplify selection logic - Remove Range struct - Selection is not an Option anymore, it's empty when start == end * copy selection to clipboard with OSC-52 sequence * Improve text selection with multiple panes - Constrain mouse hold and release events to currently active pane - Fix calculation of columns with side by side panes * fix for PositionAndSize changes * Fix mouse selection with full screen pane. - Transform mouse event positions to relative when passing to pane. * Move selection to grid, rework highlighting. - Mark selected lines for update in grid output buffer, for now in the simplest way possible, but can be made more efficient by calculating changed lines only. - Clear selection on pane resize. - Re-add logic to forward mouse hold and release events only to currently active pane. * Tidy up selection - add method to get selection line range - add method to sort the current selection * Grid: move current selection up/down when scrolling - Make the selection work with lines in lines_above and lines_below - Todo: update selection end when scrolling with mouse button being held - Todo: figure out how to move selection when new characters are added. * Grid: move selection when new lines are added * Keep track of selection being active - Handle the selection growing/shrinking when scrolling with mouse button held down but not yet released. * Improve selection end with multiple panes * Tidying up - remove logging statements, unused code * Add some unit tests for selection, move position to zellij-utils * Change Position::new to take i32 for line * Grid: add unit tests for copy from selection * add basic integration test for mouse pane focus * Add basic integration test for mouse scroll * Use color from palette for selection rendering * Improve performance of selection render - Try to minimize lines to update on selection start/update/end * fixes for updated start method * fix lines not in viewport being marked for rendering - the previous optimization to grid selection rendering was always adding the lines at the extremes of previous selection, causing problems when scrolling up or down - make sure to only add lines in viewport * Disable mouse mode on exit * use saturating_sub for usize subtractions * copy selection to clipboard on mouse release * disable mouse on exit after error * remove left-over comments * remove copy keybinding * add default impl for selection methods in Pane - remove the useless methods from Impl Pane in PluginPane * move line diff between selections to selection * add option to disable mouse mode * Allow scrolling with mouse while selecting. * move action repeater to os_input_output, change timeout to 10ms - change repeater to take an action instead of a position with hardcoded action * replace mouse mode integration tests with e2e tests * cleanup * cleanup * check if mouse mode is disabled from merged options * fix missing changes in tests, cleanup
Diffstat (limited to 'src')
-rw-r--r--src/tests/e2e/cases.rs154
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__focus_pane_with_mouse.snap29
-rw-r--r--src/tests/e2e/snapshots/zellij__tests__e2e__cases__scrolling_inside_a_pane_with_mouse.snap29
-rwxr-xr-xsrc/tests/fixtures/grid_copy59
4 files changed, 270 insertions, 1 deletions
diff --git a/src/tests/e2e/cases.rs b/src/tests/e2e/cases.rs
index 69233f25a..8d41d44f9 100644
--- a/src/tests/e2e/cases.rs
+++ b/src/tests/e2e/cases.rs
@@ -1,7 +1,7 @@
#![allow(unused)]
use ::insta::assert_snapshot;
-use zellij_utils::pane_size::PositionAndSize;
+use zellij_utils::{pane_size::PositionAndSize, position::Position};
use rand::Rng;
@@ -54,6 +54,17 @@ pub const BRACKETED_PASTE_START: [u8; 6] = [27, 91, 50, 48, 48, 126]; // \u{1b}[
pub const BRACKETED_PASTE_END: [u8; 6] = [27, 91, 50, 48, 49, 126]; // \u{1b}[201
pub const SLEEP: [u8; 0] = [];
+// simplified, slighty adapted version of alacritty mouse reporting code
+pub fn normal_mouse_report(position: Position, button: u8) -> Vec<u8> {
+ let Position { line, column } = position;
+
+ let mut command = vec![b'\x1b', b'[', b'M', 32 + button];
+ command.push(32 + 1 + column.0 as u8);
+ command.push(32 + 1 + line.0 as u8);
+
+ command
+}
+
// All the E2E tests are marked as "ignored" so that they can be run separately from the normal
// tests
@@ -785,3 +796,144 @@ pub fn accepts_basic_layout() {
.run_all_steps();
assert_snapshot!(last_snapshot);
}
+
+#[test]
+#[ignore]
+fn focus_pane_with_mouse() {
+ let fake_win_size = PositionAndSize {
+ cols: 120,
+ rows: 24,
+ x: 0,
+ y: 0,
+ ..Default::default()
+ };
+
+ let last_snapshot = RemoteRunner::new("split_terminals_vertically", fake_win_size, None)
+ .add_step(Step {
+ name: "Split pane to the right",
+ instruction: |mut remote_terminal: RemoteTerminal| -> bool {
+ let mut step_is_complete = false;
+ if remote_terminal.status_bar_appears() && remote_terminal.cursor_position_is(2, 2)
+ {
+ remote_terminal.send_key(&PANE_MODE);
+ remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
+ // back to normal mode after split
+ remote_terminal.send_key(&ENTER);
+ step_is_complete = true;
+ }
+ step_is_complete
+ },
+ })
+ .add_step(Step {
+ name: "Click left pane",
+ instruction: |mut remote_terminal: RemoteTerminal| -> bool {
+ let mut step_is_complete = false;
+ if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() {
+ remote_terminal.send_key(&normal_mouse_report(Position::new(5, 2), 0));
+ step_is_complete = true;
+ }
+ step_is_complete
+ },
+ })
+ .add_step(Step {
+ name: "Wait for left pane to be focused",
+ instruction: |remote_terminal: RemoteTerminal| -> bool {
+ let mut step_is_complete = false;
+ if remote_terminal.cursor_position_is(2, 2) && remote_terminal.tip_appears() {
+ // cursor is in the newly opened second pane
+ step_is_complete = true;
+ }
+ step_is_complete
+ },
+ })
+ .run_all_steps();
+ assert_snapshot!(last_snapshot);
+}
+
+#[test]
+#[ignore]
+pub fn scrolling_inside_a_pane_with_mouse() {
+ let fake_win_size = PositionAndSize {
+ cols: 120,
+ rows: 24,
+ x: 0,
+ y: 0,
+ ..Default::default()
+ };
+ let last_snapshot =
+ RemoteRunner::new("scrolling_inside_a_pane_with_mouse", fake_win_size, None)
+ .add_step(Step {
+ name: "Split pane to the right",
+ instruction: |mut remote_terminal: RemoteTerminal| -> bool {
+ let mut step_is_complete = false;
+ if remote_terminal.status_bar_appears()
+ && remote_terminal.cursor_position_is(2, 2)
+ {
+ remote_terminal.send_key(&PANE_MODE);
+ remote_terminal.send_key(&SPLIT_RIGHT_IN_PANE_MODE);
+ // back to normal mode after split
+ remote_terminal.send_key(&ENTER);
+ step_is_complete = true;
+ }
+ step_is_complete
+ },
+ })
+ .add_step(Step {
+ name: "Fill terminal with text",
+ instruction: |mut remote_terminal: RemoteTerminal| -> bool {
+ let mut step_is_complete = false;
+ if remote_terminal.cursor_position_is(63, 2) && remote_terminal.tip_appears() {
+ // cursor is in the newly opened second pane
+ remote_terminal.send_key(&format!("{:0<57}", "line1 ").as_bytes());
+ remote_terminal.send_key(&format!("{:0<59}", "line2 ").as_bytes());
+ remote_terminal.send_key(&format!("{:0<59}", "line3 ").as_bytes());
+ remote_terminal.send_key(&format!("{:0<59}", "line4 ").as_bytes());
+ remote_terminal.send_key(&format!("{:0<59}", "line5 ").as_bytes());
+ remote_terminal.send_key(&format!("{:0<59}", "line6 ").as_bytes());
+ remote_terminal.send_key(&format!("{:0<59}", "line7 ").as_bytes());
+ remote_terminal.send_key(&format!("{:0<59}", "line8 ").as_bytes());
+ remote_terminal.send_key(&format!("{:0<59}", "line9 ").as_bytes());
+ remote_terminal.send_key(&format!("{:0<59}", "line10 ").as_bytes());
+ remote_terminal.send_key(&format!("{:0<59}", "line11 ").as_bytes());
+ remote_terminal.send_key(&format!("{:0<59}", "line12 ").as_bytes());
+ remote_terminal.send_key(&format!("{:0<59}", "line13 ").as_bytes());
+ remote_terminal.send_key(&format!("{:0<59}", "line14 ").as_bytes());
+ remote_terminal.send_key(&format!("{:0<59}", "line15 ").as_bytes());
+ remote_terminal.send_key(&format!("{:0<59}", "line16 ").as_bytes());
+ remote_terminal.send_key(&format!("{:0<59}", "line17 ").as_bytes());
+ remote_terminal.send_key(&format!("{:0<59}", "line18 ").as_bytes());
+ remote_terminal.send_key(&format!("{:0<59}", "line19 ").as_bytes());
+ remote_terminal.send_key(&format!("{:0<58}", "line20 ").as_bytes());
+ step_is_complete = true;
+ }
+ step_is_complete
+ },
+ })
+ .add_step(Step {
+ name: "Scroll up inside pane",
+ instruction: |mut remote_terminal: RemoteTerminal| -> bool {
+ let mut step_is_complete = false;
+ if remote_terminal.cursor_position_is(119, 20) {
+ // all lines have been written to the pane
+ remote_terminal.send_key(&normal_mouse_report(Position::new(2, 64), 64));
+ step_is_complete = true;
+ }
+ step_is_complete
+ },
+ })
+ .add_step(Step {
+ name: "Wait for scroll to finish",
+ instruction: |remote_terminal: RemoteTerminal| -> bool {
+ let mut step_is_complete = false;
+ if remote_terminal.cursor_position_is(119, 20)
+ && remote_terminal.snapshot_contains("line1 ")
+ {
+ // scrolled up one line
+ step_is_complete = true;
+ }
+ step_is_complete
+ },
+ })
+ .run_all_steps();
+ assert_snapshot!(last_snapshot);
+}
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__focus_pane_with_mouse.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__focus_pane_with_mouse.snap
new file mode 100644
index 000000000..79e84810d
--- /dev/null
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__focus_pane_with_mouse.snap
@@ -0,0 +1,29 @@
+---
+source: src/tests/e2e/cases.rs
+expression: last_snapshot
+
+---
+ Zellij  Tab #1 
+
+$ █ │$
+ │
+ │
+ │
+ │
+ │
+ │
+ │
+ │
+ │
+ │
+ │
+ │
+ │
+ │
+ │
+ │
+ │
+ │
+
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <r> RESIZE  <s> SCROLL  <o> SESSION  <q> QUIT 
+ Tip: Alt + n => open new pane. Alt + [] or hjkl => navigate between panes.
diff --git a/src/tests/e2e/snapshots/zellij__tests__e2e__cases__scrolling_inside_a_pane_with_mouse.snap b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__scrolling_inside_a_pane_with_mouse.snap
new file mode 100644
index 000000000..bff403764
--- /dev/null
+++ b/src/tests/e2e/snapshots/zellij__tests__e2e__cases__scrolling_inside_a_pane_with_mouse.snap
@@ -0,0 +1,29 @@
+---
+source: src/tests/e2e/cases.rs
+expression: last_snapshot
+
+---
+ Zellij  Tab #1 
+
+$ │$ line1 000000000000000000000000000000000000000000000000000
+ │line2 00000000000000000000000000000000000000000000000000000
+ │line3 00000000000000000000000000000000000000000000000000000
+ │line4 00000000000000000000000000000000000000000000000000000
+ │line5 00000000000000000000000000000000000000000000000000000
+ │line6 00000000000000000000000000000000000000000000000000000
+ │line7 00000000000000000000000000000000000000000000000000000
+ │line8 00000000000000000000000000000000000000000000000000000
+ │line9 00000000000000000000000000000000000000000000000000000
+ │line10 0000000000000000000000000000000000000000000000000000
+ │line11 0000000000000000000000000000000000000000000000000000
+ │line12 0000000000000000000000000000000000000000000000000000
+ │line13 0000000000000000000000000000000000000000000000000000
+ │line14 0000000000000000000000000000000000000000000000000000
+ │line15 0000000000000000000000000000000000000000000000000000
+ │line16 0000000000000000000000000000000000000000000000000000
+ │line17 0000000000000000000000000000000000000000000000000000
+ │line18 0000000000000000000000000000000000000000000000000000
+ │line19 000000000000000000000000000000000000000000000000000█
+
+ Ctrl + <g> LOCK  <p> PANE  <t> TAB  <r> RESIZE  <s> SCROLL  <o> SESSION  <q> QUIT 
+ Tip: Alt + n => open new pane. Alt + [] or hjkl => navigate between panes.
diff --git a/src/tests/fixtures/grid_copy b/src/tests/fixtures/grid_copy
new file mode 100755
index 000000000..6d093741a
--- /dev/null
+++ b/src/tests/fixtures/grid_copy
@@ -0,0 +1,59 @@
+⏎(B ⏎ Welcome to fish, the friendly interactive shell
+Type `help` for instructions on how to use fish
+[?2004h]0;fish /home/thomas/Projects/zellij(B
+zellij on  mouse-support [?] is 📦 v0.14.0 via 🦀 v1.53.0-beta.3 
+❯  cc(Bat test-input.txt(Bat test-input.txt(Bt test-input.txt(Bcat test-input.txt(B test-input.txt(B test-input.txt(B
+(B[?2004l]0;cat test-input.txt /home/thomas/Projects/zellij(B Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+Velit ut tortor pretium viverra suspendisse potenti nullam ac tortor. Adipiscing elit ut aliquam purus sit amet luctus venenatis.
+Duis ut diam quam nulla porttitor massa id neque aliquam. Suspendisse potenti nullam ac tortor vitae purus faucibus ornare suspendisse.
+Vitae nunc sed velit dignissim sodales ut eu sem integer.
+Tortor id aliquet lectus proin nibh nisl.
+Commodo odio aenean sed adipiscing diam donec adipiscing tristique risus.
+Velit dignissim sodales ut eu sem. Lacus suspendisse faucibus interdum posuere lorem. Ac placerat vestibulum lectus mauris ultrices eros. Elementum integer enim neque volutpat ac. Augue interdum velit euismod in.
+
+Egestas sed sed risus pretium quam vulputate dignissim.
+Gravida rutrum quisque non tellus orci ac auctor augue.
+Risus nec feugiat in fermentum posuere urna nec tincidunt praesent.
+Elementum eu facilisis sed odio morbi quis.
+Mattis ullamcorper velit sed ullamcorper morbi.
+Dui vivamus arcu felis bibendum. Sit amet aliquam id diam.
+Suscipit tellus mauris a diam maecenas sed enim.
+Odio ut sem nulla pharetra.
+Cras ornare arcu dui vivamus arcu felis bibendum.
+Egestas fringilla phasellus faucibus scelerisque eleifend.
+Purus semper eget duis at tellus at urna condimentum.
+Aliquam etiam erat velit scelerisque in dictum non.
+Porta non pulvinar neque laoreet suspendisse interdum consectetur.
+Tempor nec feugiat nisl pretium. Sit amet consectetur adipiscing elit.
+Cras semper auctor neque vitae tempus quam pellentesque.
+Laoreet non curabitur gravida arcu ac tortor dignissim.
+Sed nisi lacus sed viverra tellus in.
+Rutrum tellus pellentesque eu tincidunt tortor aliquam nulla.
+
+Nascetur ridiculus mus mauris vitae ultricies leo integer malesuada.
+Interdum posuere lorem ipsum dolor sit amet consectetur.
+Porta non pulvinar neque laoreet suspendisse interdum.
+Fames ac turpis egestas integer eget aliquet nibh praesent.
+Congue nisi vitae suscipit tellus mauris a diam maecenas sed.
+Nec ultrices dui sapien eget mi proin sed libero enim.
+Tellus rutrum tellus pellentesque eu tincidunt.
+Ultrices eros in cursus turpis massa tincidunt dui ut ornare.
+Arcu cursus vitae congue mauris rhoncus aenean vel elit scelerisque.
+Viverra mauris in aliquam sem fringilla ut.
+Vulputate eu scelerisque felis imperdiet proin fermentum leo.
+Cursus risus at ultrices mi tempus.
+Laoreet id donec ultrices tincidunt arcu non sodales.
+Amet dictum sit amet justo donec enim.
+Hac habitasse platea dictumst vestibulum rhoncus est pellentesque.
+Facilisi cras fermentum odio eu feugiat.
+Elit ut aliquam purus sit amet luctus venenatis lectus.
+Dignissim enim sit amet venenatis urna cursus.
+Amet consectetur adipiscing elit ut aliquam purus.
+Elementum pulvinar etiam non quam lacus suspendisse.
+
+Quisque id diam vel quam. Id porta nibh venenatis cras sed felis eget velit aliquet. Sagittis aliquam malesuada bibendum arcu. Libero id faucibus nisl tincidunt eget nullam non. Sed elementum tempus egestas sed sed risus pretium quam vulputate. Turpis egestas maecenas pharetra convallis. Arcu cursus vitae congue mauris rhoncus aenean vel. Augue ut lectus arcu bibendum. Scelerisque varius morbi enim nunc faucibus a pellentesque. Mattis pellentesque id nibh tortor id aliquet lectus proin nibh. In aliquam sem fringilla ut. Urna et pharetra pharetra massa massa ultricies mi. Enim nulla aliquet porttitor lacus luctus accumsan tortor posuere. Malesuada fames ac turpis egestas integer. Venenatis tellus in metus vulputate eu scelerisque felis. Suspendisse faucibus interdum posuere lorem ipsum dolor sit amet.
+
+Quam elementum pulvinar etiam non quam lacus suspendisse faucibus. Egestas sed sed risus pretium quam vulputate dignissim suspendisse. Risus nec feugiat in fermentum posuere urna. Vestibulum lorem sed risus ultricies. Egestas maecenas pharetra convallis posuere morbi. Egestas tellus rutrum tellus pellentesque. Pulvinar etiam non quam lacus suspendisse faucibus. Lectus proin nibh nisl condimentum id venenatis a condimentum. Adipiscing elit pellentesque habitant morbi tristique senectus et netus. Nunc id cursus metus aliquam eleifend. Urna nec tincidunt praesent semper feugiat nibh sed pulvinar. Donec ultrices tincidunt arcu non sodales neque sodales ut etiam. Suspendisse sed nisi lacus sed viverra tellus in hac habitasse. Nunc scelerisque viverra mauris in aliquam sem fringilla.
+⏎(B ⏎ [?2004h]0;fish /home/thomas/Projects/zellij(B
+zellij on  mouse-support [?] is 📦 v0.14.0 via 🦀 v1.53.0-beta.3 
+❯  \ No newline at end of file