fix(mouse): avoid forwarding click events on pane border (#1584)

* if left click is on pane border do not forward to application

* properly handle frames

* fix comment

* fix another comment

* add tests, fix edge case
This commit is contained in:
Thomas Linford 2022-07-15 16:28:08 +02:00 committed by GitHub
parent d1fa067713
commit e2a3438c86
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 300 additions and 24 deletions

View File

@ -8,7 +8,8 @@ use std::collections::HashMap;
use std::rc::Rc;
use zellij_utils::{
data::{Palette, Style},
pane_size::{PaneGeom, SizeInPixels},
pane_size::{Offset, PaneGeom, SizeInPixels},
position::Position,
};
use std::fmt::Write;
@ -391,3 +392,255 @@ pub fn keep_working_after_corrupted_sixel_image() {
terminal_pane.handle_pty_bytes(text_to_fill_pane.into_bytes());
assert_snapshot!(format!("{:?}", terminal_pane.grid));
}
#[test]
pub fn pane_with_frame_position_is_on_frame() {
let mut fake_win_size = PaneGeom {
x: 10,
y: 10,
..PaneGeom::default()
};
fake_win_size.cols.set_inner(121);
fake_win_size.rows.set_inner(20);
let pid = 1;
let style = Style::default();
let sixel_image_store = Rc::new(RefCell::new(SixelImageStore::default()));
let terminal_emulator_colors = Rc::new(RefCell::new(Palette::default()));
let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new()));
let character_cell_size = Rc::new(RefCell::new(Some(SizeInPixels {
width: 8,
height: 21,
})));
let mut terminal_pane = TerminalPane::new(
pid,
fake_win_size,
style,
0,
String::new(),
Rc::new(RefCell::new(LinkHandler::new())),
character_cell_size,
sixel_image_store,
terminal_emulator_colors,
terminal_emulator_color_codes,
); // 0 is the pane index
terminal_pane.set_content_offset(Offset::frame(1));
// row above pane: no border
assert!(!terminal_pane.position_is_on_frame(&Position::new(9, 9)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(9, 10)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(9, 11)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(9, 70)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(9, 129)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(9, 130)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(9, 131)));
// first row: border for 10 <= col <= 130
assert!(!terminal_pane.position_is_on_frame(&Position::new(10, 9)));
assert!(terminal_pane.position_is_on_frame(&Position::new(10, 10)));
assert!(terminal_pane.position_is_on_frame(&Position::new(10, 11)));
assert!(terminal_pane.position_is_on_frame(&Position::new(10, 70)));
assert!(terminal_pane.position_is_on_frame(&Position::new(10, 129)));
assert!(terminal_pane.position_is_on_frame(&Position::new(10, 130)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(10, 131)));
// second row: border only at col=10,130
assert!(!terminal_pane.position_is_on_frame(&Position::new(11, 9)));
assert!(terminal_pane.position_is_on_frame(&Position::new(11, 10)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(11, 11)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(11, 70)));
assert!(terminal_pane.position_is_on_frame(&Position::new(11, 130)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(11, 131)));
// row in the middle: border only at col=10,130
assert!(!terminal_pane.position_is_on_frame(&Position::new(15, 9)));
assert!(terminal_pane.position_is_on_frame(&Position::new(15, 10)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(15, 11)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(15, 70)));
assert!(terminal_pane.position_is_on_frame(&Position::new(15, 130)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(15, 131)));
// last row: border for 10 <= col <= 130
assert!(!terminal_pane.position_is_on_frame(&Position::new(29, 9)));
assert!(terminal_pane.position_is_on_frame(&Position::new(29, 10)));
assert!(terminal_pane.position_is_on_frame(&Position::new(29, 11)));
assert!(terminal_pane.position_is_on_frame(&Position::new(29, 70)));
assert!(terminal_pane.position_is_on_frame(&Position::new(29, 130)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(29, 131)));
// row below pane: no border
assert!(!terminal_pane.position_is_on_frame(&Position::new(30, 10)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(30, 11)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(30, 70)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(30, 130)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(30, 131)));
}
#[test]
pub fn pane_with_bottom_and_right_borders_position_is_on_frame() {
let mut fake_win_size = PaneGeom {
x: 10,
y: 10,
..PaneGeom::default()
};
fake_win_size.cols.set_inner(121);
fake_win_size.rows.set_inner(20);
let pid = 1;
let style = Style::default();
let sixel_image_store = Rc::new(RefCell::new(SixelImageStore::default()));
let terminal_emulator_colors = Rc::new(RefCell::new(Palette::default()));
let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new()));
let character_cell_size = Rc::new(RefCell::new(Some(SizeInPixels {
width: 8,
height: 21,
})));
let mut terminal_pane = TerminalPane::new(
pid,
fake_win_size,
style,
0,
String::new(),
Rc::new(RefCell::new(LinkHandler::new())),
character_cell_size,
sixel_image_store,
terminal_emulator_colors,
terminal_emulator_color_codes,
); // 0 is the pane index
terminal_pane.set_content_offset(Offset::shift(1, 1));
// row above pane: no border
assert!(!terminal_pane.position_is_on_frame(&Position::new(9, 9)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(9, 10)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(9, 11)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(9, 70)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(9, 129)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(9, 130)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(9, 131)));
// first row: border only at col=130
assert!(!terminal_pane.position_is_on_frame(&Position::new(10, 9)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(10, 10)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(10, 11)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(10, 70)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(10, 129)));
assert!(terminal_pane.position_is_on_frame(&Position::new(10, 130)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(10, 131)));
// second row: border only at col=130
assert!(!terminal_pane.position_is_on_frame(&Position::new(11, 9)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(11, 10)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(11, 11)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(11, 70)));
assert!(terminal_pane.position_is_on_frame(&Position::new(11, 130)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(11, 131)));
// row in the middle: border only at col=130
assert!(!terminal_pane.position_is_on_frame(&Position::new(15, 9)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(15, 10)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(15, 11)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(15, 70)));
assert!(terminal_pane.position_is_on_frame(&Position::new(15, 130)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(15, 131)));
// last row: border for 10 <= col <= 130
assert!(!terminal_pane.position_is_on_frame(&Position::new(29, 9)));
assert!(terminal_pane.position_is_on_frame(&Position::new(29, 10)));
assert!(terminal_pane.position_is_on_frame(&Position::new(29, 11)));
assert!(terminal_pane.position_is_on_frame(&Position::new(29, 70)));
assert!(terminal_pane.position_is_on_frame(&Position::new(29, 130)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(29, 131)));
// row below pane: no border
assert!(!terminal_pane.position_is_on_frame(&Position::new(30, 10)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(30, 11)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(30, 70)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(30, 130)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(30, 131)));
}
#[test]
pub fn frameless_pane_position_is_on_frame() {
let mut fake_win_size = PaneGeom {
x: 10,
y: 10,
..PaneGeom::default()
};
fake_win_size.cols.set_inner(121);
fake_win_size.rows.set_inner(20);
let pid = 1;
let style = Style::default();
let sixel_image_store = Rc::new(RefCell::new(SixelImageStore::default()));
let terminal_emulator_colors = Rc::new(RefCell::new(Palette::default()));
let terminal_emulator_color_codes = Rc::new(RefCell::new(HashMap::new()));
let character_cell_size = Rc::new(RefCell::new(Some(SizeInPixels {
width: 8,
height: 21,
})));
let mut terminal_pane = TerminalPane::new(
pid,
fake_win_size,
style,
0,
String::new(),
Rc::new(RefCell::new(LinkHandler::new())),
character_cell_size,
sixel_image_store,
terminal_emulator_colors,
terminal_emulator_color_codes,
); // 0 is the pane index
terminal_pane.set_content_offset(Offset::default());
// row above pane: no border
assert!(!terminal_pane.position_is_on_frame(&Position::new(9, 9)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(9, 10)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(9, 11)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(9, 70)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(9, 129)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(9, 130)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(9, 131)));
// first row: no border
assert!(!terminal_pane.position_is_on_frame(&Position::new(10, 9)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(10, 10)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(10, 11)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(10, 70)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(10, 129)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(10, 130)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(10, 131)));
// second row: no border
assert!(!terminal_pane.position_is_on_frame(&Position::new(11, 9)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(11, 10)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(11, 11)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(11, 70)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(11, 130)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(11, 131)));
// random row in the middle: no border
assert!(!terminal_pane.position_is_on_frame(&Position::new(15, 9)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(15, 10)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(15, 11)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(15, 70)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(15, 130)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(15, 131)));
// last row: no border
assert!(!terminal_pane.position_is_on_frame(&Position::new(29, 9)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(29, 10)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(29, 11)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(29, 70)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(29, 130)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(29, 131)));
// row below pane: no border
assert!(!terminal_pane.position_is_on_frame(&Position::new(30, 10)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(30, 11)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(30, 70)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(30, 130)));
assert!(!terminal_pane.position_is_on_frame(&Position::new(30, 131)));
}

View File

@ -262,13 +262,32 @@ pub trait Pane {
fn relative_position(&self, position_on_screen: &Position) -> Position {
position_on_screen.relative_to(self.get_content_y(), self.get_content_x())
}
fn position_is_on_frame(&self, position_on_screen: &Position) -> bool {
// TODO: handle cases where we have no frame
position_on_screen.line() == self.y() as isize
|| position_on_screen.line()
== (self.y() as isize + self.rows() as isize).saturating_sub(1)
|| position_on_screen.column() == self.x()
|| position_on_screen.column() == (self.x() + self.cols()).saturating_sub(1)
fn position_is_on_frame(&self, position: &Position) -> bool {
if !self.contains(position) {
return false;
}
if (self.x()..self.get_content_x()).contains(&position.column()) {
// position is on left border
return true;
}
if (self.get_content_x() + self.get_content_columns()..(self.x() + self.cols()))
.contains(&position.column())
{
// position is on right border
return true;
}
if (self.y() as isize..self.get_content_y() as isize).contains(&position.line()) {
// position is on top border
return true;
}
if ((self.get_content_y() + self.get_content_rows()) as isize
..(self.y() + self.rows()) as isize)
.contains(&position.line())
{
// position is on bottom border
return true;
}
false
}
fn store_pane_name(&mut self);
fn load_pane_name(&mut self);
@ -1709,12 +1728,14 @@ impl Tab {
let relative_position = pane.relative_position(position);
if pane.mouse_mode() {
let mouse_event = format!(
"\u{1b}[<0;{:?};{:?}M",
relative_position.column.0 + 1,
relative_position.line.0 + 1
);
self.write_to_active_terminal(mouse_event.into_bytes(), client_id);
if !pane.position_is_on_frame(position) {
let mouse_event = format!(
"\u{1b}[<0;{:?};{:?}M",
relative_position.column() + 1,
relative_position.line() + 1
);
self.write_to_active_terminal(mouse_event.into_bytes(), client_id);
}
} else {
// TODO: rename this method, it is used to forward click events to plugin panes
pane.start_selection(&relative_position, client_id);
@ -1730,12 +1751,14 @@ impl Tab {
if let Some(pane) = self.get_pane_at(position, false) {
let relative_position = pane.relative_position(position);
if pane.mouse_mode() {
let mouse_event = format!(
"\u{1b}[<2;{:?};{:?}M",
relative_position.column.0 + 1,
relative_position.line.0 + 1
);
self.write_to_active_terminal(mouse_event.into_bytes(), client_id);
if !pane.position_is_on_frame(position) {
let mouse_event = format!(
"\u{1b}[<2;{:?};{:?}M",
relative_position.column() + 1,
relative_position.line() + 1
);
self.write_to_active_terminal(mouse_event.into_bytes(), client_id);
}
} else {
pane.handle_right_click(&relative_position, client_id);
}
@ -1777,11 +1800,11 @@ impl Tab {
let relative_position = active_pane.relative_position(position);
if active_pane.mouse_mode() {
// ensure that coordinates are valid
let col = (relative_position.column.0 + 1)
let col = (relative_position.column() + 1)
.max(1)
.min(active_pane.get_content_columns());
let line = (relative_position.line.0 + 1)
let line = (relative_position.line() + 1)
.max(1)
.min(active_pane.get_content_rows() as isize);
let mouse_event = format!("\u{1b}[<0;{:?};{:?}m", col, line);
@ -1840,11 +1863,11 @@ impl Tab {
let relative_position = active_pane.relative_position(position_on_screen);
if active_pane.mouse_mode() && !is_repeated {
// ensure that coordinates are valid
let col = (relative_position.column.0 + 1)
let col = (relative_position.column() + 1)
.max(1)
.min(active_pane.get_content_columns());
let line = (relative_position.line.0 + 1)
let line = (relative_position.line() + 1)
.max(1)
.min(active_pane.get_content_rows() as isize);