Fix various mouse mode + vi mode interactions

This commit fixes some issues introduced by
1a8cd172e520e493bacc9c6a2ae6f80de086eaa3:

 1. Vi cursor not moving properly on double/triple click
 2. URL not launching via mouse click in vi mode + mouse mode
 3. Ability to select in mouse mode with double/triple click regardless
    of shift modifier
This commit is contained in:
Kirill Chibisov 2020-04-10 21:23:50 +03:00 committed by GitHub
parent 2fc5120327
commit f14d24542c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 94 additions and 99 deletions

View File

@ -364,7 +364,8 @@ impl Display {
let size_info = self.size_info;
let selection = !terminal.selection().as_ref().map(Selection::is_empty).unwrap_or(true);
let mouse_mode = terminal.mode().intersects(TermMode::MOUSE_MODE);
let mouse_mode = terminal.mode().intersects(TermMode::MOUSE_MODE)
&& !terminal.mode().contains(TermMode::VI);
let vi_mode_cursor = if terminal.mode().contains(TermMode::VI) {
Some(terminal.vi_mode_cursor)

View File

@ -12,7 +12,7 @@ use std::sync::Arc;
use std::time::Instant;
use glutin::dpi::PhysicalSize;
use glutin::event::{ElementState, Event as GlutinEvent, ModifiersState, MouseButton, WindowEvent};
use glutin::event::{ElementState, Event as GlutinEvent, ModifiersState, WindowEvent};
use glutin::event_loop::{ControlFlow, EventLoop, EventLoopProxy, EventLoopWindowTarget};
use glutin::platform::desktop::EventLoopExtDesktop;
#[cfg(not(any(target_os = "macos", windows)))]
@ -295,7 +295,7 @@ impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionCon
}
}
#[derive(Debug)]
#[derive(Debug, Eq, PartialEq)]
pub enum ClickState {
None,
Click,
@ -319,7 +319,6 @@ pub struct Mouse {
pub cell_side: Side,
pub lines_scrolled: f32,
pub block_url_launcher: bool,
pub last_button: MouseButton,
pub inside_grid: bool,
}
@ -339,7 +338,6 @@ impl Default for Mouse {
cell_side: Side::Left,
lines_scrolled: 0.,
block_url_launcher: false,
last_button: MouseButton::Other(0),
inside_grid: false,
}
}

View File

@ -455,89 +455,79 @@ impl<'a, T: EventListener, A: ActionContext<T>> Processor<'a, T, A> {
}
}
fn on_mouse_double_click(&mut self, button: MouseButton, point: Point) {
if button == MouseButton::Left {
let side = self.ctx.mouse().cell_side;
self.ctx.start_selection(SelectionType::Semantic, point, side);
}
}
fn on_mouse_triple_click(&mut self, button: MouseButton, point: Point) {
if button == MouseButton::Left {
let side = self.ctx.mouse().cell_side;
self.ctx.start_selection(SelectionType::Lines, point, side);
}
}
fn on_mouse_press(&mut self, button: MouseButton) {
let now = Instant::now();
let elapsed = self.ctx.mouse().last_click_timestamp.elapsed();
self.ctx.mouse_mut().last_click_timestamp = now;
// Handle mouse mode
if !self.ctx.modifiers().shift() && self.ctx.mouse_mode() {
self.ctx.mouse_mut().click_state = ClickState::None;
let button_changed = self.ctx.mouse().last_button != button;
let code = match button {
MouseButton::Left => 0,
MouseButton::Middle => 1,
MouseButton::Right => 2,
// Can't properly report more than three buttons.
MouseButton::Other(_) => return,
};
self.mouse_report(code, ElementState::Pressed);
} else if button == MouseButton::Left {
self.on_left_click();
} else {
// Do nothing when using buttons other than LMB
self.ctx.mouse_mut().click_state = ClickState::None;
}
}
/// Handle left click selection and vi mode cursor movement.
fn on_left_click(&mut self) {
// Calculate time since the last click to handle double/triple clicks in normal mode
let now = Instant::now();
let elapsed = now - self.ctx.mouse().last_click_timestamp;
self.ctx.mouse_mut().last_click_timestamp = now;
// Load mouse point, treating message bar and padding as closest cell
let mouse = self.ctx.mouse();
let mut point = self.ctx.size_info().pixels_to_coords(mouse.x, mouse.y);
point.line = min(point.line, self.ctx.terminal().grid().num_lines() - 1);
let side = self.ctx.mouse().cell_side;
self.ctx.mouse_mut().click_state = match self.ctx.mouse().click_state {
ClickState::Click
if !button_changed
&& elapsed < self.ctx.config().ui_config.mouse.double_click.threshold =>
if elapsed < self.ctx.config().ui_config.mouse.double_click.threshold =>
{
self.ctx.mouse_mut().block_url_launcher = true;
self.on_mouse_double_click(button, point);
self.ctx.start_selection(SelectionType::Semantic, point, side);
ClickState::DoubleClick
}
ClickState::DoubleClick
if !button_changed
&& elapsed < self.ctx.config().ui_config.mouse.triple_click.threshold =>
if elapsed < self.ctx.config().ui_config.mouse.triple_click.threshold =>
{
self.ctx.mouse_mut().block_url_launcher = true;
self.on_mouse_triple_click(button, point);
self.ctx.start_selection(SelectionType::Lines, point, side);
ClickState::TripleClick
}
_ => {
if button == MouseButton::Left
&& (self.ctx.modifiers().shift()
|| !self.ctx.terminal().mode().intersects(TermMode::MOUSE_MODE))
{
// Don't launch URLs if this click cleared the selection
self.ctx.mouse_mut().block_url_launcher = !self.ctx.selection_is_empty();
// Don't launch URLs if this click cleared the selection
self.ctx.mouse_mut().block_url_launcher = !self.ctx.selection_is_empty();
self.ctx.clear_selection();
self.ctx.clear_selection();
// Start new empty selection
let side = self.ctx.mouse().cell_side;
if self.ctx.modifiers().ctrl() {
self.ctx.start_selection(SelectionType::Block, point, side);
} else {
self.ctx.start_selection(SelectionType::Simple, point, side);
}
// Move vi mode cursor to mouse position
if self.ctx.terminal().mode().contains(TermMode::VI) {
// Update vi mode cursor position on click
self.ctx.terminal_mut().vi_mode_cursor.point = point;
}
}
if !self.ctx.modifiers().shift() && self.ctx.mouse_mode() {
let code = match button {
MouseButton::Left => 0,
MouseButton::Middle => 1,
MouseButton::Right => 2,
// Can't properly report more than three buttons.
MouseButton::Other(_) => return,
};
self.mouse_report(code, ElementState::Pressed);
return;
// Start new empty selection
if self.ctx.modifiers().ctrl() {
self.ctx.start_selection(SelectionType::Block, point, side);
} else {
self.ctx.start_selection(SelectionType::Simple, point, side);
}
ClickState::Click
},
};
// Move vi mode cursor to mouse position
if self.ctx.terminal().mode().contains(TermMode::VI) {
// Update Vi mode cursor position on click
self.ctx.terminal_mut().vi_mode_cursor.point = point;
}
}
fn on_mouse_release(&mut self, button: MouseButton) {
@ -695,8 +685,6 @@ impl<'a, T: EventListener, A: ActionContext<T>> Processor<'a, T, A> {
ElementState::Released => self.on_mouse_release(button),
}
}
self.ctx.mouse_mut().last_button = button;
}
/// Process key input.
@ -910,20 +898,12 @@ mod tests {
fn send_event(&self, _event: TerminalEvent) {}
}
#[derive(Debug, PartialEq)]
enum MultiClick {
DoubleClick,
TripleClick,
None,
}
struct ActionContext<'a, T> {
pub terminal: &'a mut Term<T>,
pub selection: &'a mut Option<Selection>,
pub size_info: &'a SizeInfo,
pub mouse: &'a mut Mouse,
pub message_buffer: &'a mut MessageBuffer,
pub last_action: MultiClick,
pub received_count: usize,
pub suppress_chars: bool,
pub modifiers: ModifiersState,
@ -935,13 +915,7 @@ mod tests {
fn update_selection(&mut self, _point: Point, _side: Side) {}
fn start_selection(&mut self, ty: SelectionType, _point: Point, _side: Side) {
match ty {
SelectionType::Semantic => self.last_action = MultiClick::DoubleClick,
SelectionType::Lines => self.last_action = MultiClick::TripleClick,
_ => (),
}
}
fn start_selection(&mut self, _ty: SelectionType, _point: Point, _side: Side) {}
fn toggle_selection(&mut self, _ty: SelectionType, _point: Point, _side: Side) {}
@ -1051,8 +1025,7 @@ mod tests {
initial_state: $initial_state:expr,
initial_button: $initial_button:expr,
input: $input:expr,
end_state: $end_state:pat,
last_action: $last_action:expr
end_state: $end_state:expr,
} => {
#[test]
fn $name() {
@ -1082,7 +1055,6 @@ mod tests {
let mut mouse = Mouse::default();
mouse.click_state = $initial_state;
mouse.last_button = $initial_button;
let mut selection = None;
@ -1093,7 +1065,6 @@ mod tests {
selection: &mut selection,
mouse: &mut mouse,
size_info: &size,
last_action: MultiClick::None,
received_count: 0,
suppress_chars: false,
modifiers: Default::default(),
@ -1116,10 +1087,7 @@ mod tests {
processor.mouse_input(state, button);
};
assert!(match processor.ctx.mouse.click_state {
$end_state => processor.ctx.last_action == $last_action,
_ => false
});
assert_eq!(processor.ctx.mouse.click_state, $end_state);
}
}
}
@ -1151,13 +1119,44 @@ mod tests {
event: WindowEvent::MouseInput {
state: ElementState::Pressed,
button: MouseButton::Left,
device_id: unsafe { ::std::mem::transmute_copy(&0) },
device_id: unsafe { std::mem::transmute_copy(&0) },
modifiers: ModifiersState::default(),
},
window_id: unsafe { ::std::mem::transmute_copy(&0) },
window_id: unsafe { std::mem::transmute_copy(&0) },
},
end_state: ClickState::Click,
last_action: MultiClick::None
}
test_clickstate! {
name: single_right_click,
initial_state: ClickState::None,
initial_button: MouseButton::Other(0),
input: Event::WindowEvent {
event: WindowEvent::MouseInput {
state: ElementState::Pressed,
button: MouseButton::Right,
device_id: unsafe { std::mem::transmute_copy(&0) },
modifiers: ModifiersState::default(),
},
window_id: unsafe { std::mem::transmute_copy(&0) },
},
end_state: ClickState::None,
}
test_clickstate! {
name: single_middle_click,
initial_state: ClickState::None,
initial_button: MouseButton::Other(0),
input: Event::WindowEvent {
event: WindowEvent::MouseInput {
state: ElementState::Pressed,
button: MouseButton::Middle,
device_id: unsafe { std::mem::transmute_copy(&0) },
modifiers: ModifiersState::default(),
},
window_id: unsafe { std::mem::transmute_copy(&0) },
},
end_state: ClickState::None,
}
test_clickstate! {
@ -1168,13 +1167,12 @@ mod tests {
event: WindowEvent::MouseInput {
state: ElementState::Pressed,
button: MouseButton::Left,
device_id: unsafe { ::std::mem::transmute_copy(&0) },
device_id: unsafe { std::mem::transmute_copy(&0) },
modifiers: ModifiersState::default(),
},
window_id: unsafe { ::std::mem::transmute_copy(&0) },
window_id: unsafe { std::mem::transmute_copy(&0) },
},
end_state: ClickState::DoubleClick,
last_action: MultiClick::DoubleClick
}
test_clickstate! {
@ -1185,13 +1183,12 @@ mod tests {
event: WindowEvent::MouseInput {
state: ElementState::Pressed,
button: MouseButton::Left,
device_id: unsafe { ::std::mem::transmute_copy(&0) },
device_id: unsafe { std::mem::transmute_copy(&0) },
modifiers: ModifiersState::default(),
},
window_id: unsafe { ::std::mem::transmute_copy(&0) },
window_id: unsafe { std::mem::transmute_copy(&0) },
},
end_state: ClickState::TripleClick,
last_action: MultiClick::TripleClick
}
test_clickstate! {
@ -1202,13 +1199,12 @@ mod tests {
event: WindowEvent::MouseInput {
state: ElementState::Pressed,
button: MouseButton::Right,
device_id: unsafe { ::std::mem::transmute_copy(&0) },
device_id: unsafe { std::mem::transmute_copy(&0) },
modifiers: ModifiersState::default(),
},
window_id: unsafe { ::std::mem::transmute_copy(&0) },
window_id: unsafe { std::mem::transmute_copy(&0) },
},
end_state: ClickState::Click,
last_action: MultiClick::None
end_state: ClickState::None,
}
test_process_binding! {