Add selection expansion

This allows for expanding the selection using the right mouse button.
The new selection type depends on the number of clicks and applies to
both sides of the selection.

Fixes #1554.
This commit is contained in:
Christian Duerr 2020-06-23 09:57:15 +00:00 committed by GitHub
parent 87e5b1aa25
commit 43c0ad6ea9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 84 additions and 44 deletions

View file

@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Support for Fontconfig embolden and matrix options
- Opt-out compilation flag `winpty` to disable WinPTY support
- Scrolling during selection when mouse is at top/bottom of window
- Expanding existing selections using the right mouse button
### Changed

View file

@ -15,7 +15,7 @@ use std::sync::Arc;
use std::time::Instant;
use glutin::dpi::PhysicalSize;
use glutin::event::{ElementState, Event as GlutinEvent, ModifiersState, WindowEvent};
use glutin::event::{ElementState, Event as GlutinEvent, ModifiersState, MouseButton, WindowEvent};
use glutin::event_loop::{ControlFlow, EventLoop, EventLoopProxy, EventLoopWindowTarget};
use glutin::platform::desktop::EventLoopExtDesktop;
#[cfg(not(any(target_os = "macos", windows)))]
@ -351,6 +351,7 @@ pub struct Mouse {
pub middle_button_state: ElementState,
pub right_button_state: ElementState,
pub last_click_timestamp: Instant,
pub last_click_button: MouseButton,
pub click_state: ClickState,
pub scroll_px: f64,
pub line: Line,
@ -367,6 +368,7 @@ impl Default for Mouse {
x: 0,
y: 0,
last_click_timestamp: Instant::now(),
last_click_button: MouseButton::Left,
left_button_state: ElementState::Released,
middle_button_state: ElementState::Released,
right_button_state: ElementState::Released,

View file

@ -492,44 +492,80 @@ impl<'a, T: EventListener, A: ActionContext<T>> Processor<'a, T, A> {
};
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;
// Calculate time since the last click to handle double/triple clicks.
let now = Instant::now();
let elapsed = now - self.ctx.mouse().last_click_timestamp;
self.ctx.mouse_mut().last_click_timestamp = now;
// Update multi-click state.
let mouse_config = &self.ctx.config().ui_config.mouse;
self.ctx.mouse_mut().click_state = match self.ctx.mouse().click_state {
// Reset click state if button has changed.
_ if button != self.ctx.mouse().last_click_button => {
self.ctx.mouse_mut().last_click_button = button;
ClickState::Click
},
ClickState::Click if elapsed < mouse_config.double_click.threshold => {
ClickState::DoubleClick
},
ClickState::DoubleClick if elapsed < mouse_config.triple_click.threshold => {
ClickState::TripleClick
},
_ => ClickState::Click,
};
// Load mouse point, treating message bar and padding as the 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);
match button {
MouseButton::Left => self.on_left_click(point),
MouseButton::Right => self.on_right_click(point),
// Do nothing when using buttons other than LMB.
_ => self.ctx.mouse_mut().click_state = ClickState::None,
}
}
}
/// Handle selection expansion on right click.
fn on_right_click(&mut self, point: Point) {
match self.ctx.mouse().click_state {
ClickState::Click => {
let selection_type = if self.ctx.modifiers().ctrl() {
SelectionType::Block
} else {
SelectionType::Simple
};
self.expand_selection(point, selection_type);
},
ClickState::DoubleClick => self.expand_selection(point, SelectionType::Semantic),
ClickState::TripleClick => self.expand_selection(point, SelectionType::Lines),
ClickState::None => (),
}
}
/// Expand existing selection.
fn expand_selection(&mut self, point: Point, selection_type: SelectionType) {
let cell_side = self.ctx.mouse().cell_side;
let selection = match &mut self.ctx.terminal_mut().selection {
Some(selection) => selection,
None => return,
};
selection.ty = selection_type;
self.ctx.update_selection(point, cell_side);
}
/// 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);
fn on_left_click(&mut self, point: Point) {
let side = self.ctx.mouse().cell_side;
self.ctx.mouse_mut().click_state = match self.ctx.mouse().click_state {
ClickState::Click
if elapsed < self.ctx.config().ui_config.mouse.double_click.threshold =>
{
self.ctx.mouse_mut().block_url_launcher = true;
self.ctx.start_selection(SelectionType::Semantic, point, side);
ClickState::DoubleClick
}
ClickState::DoubleClick
if elapsed < self.ctx.config().ui_config.mouse.triple_click.threshold =>
{
self.ctx.mouse_mut().block_url_launcher = true;
self.ctx.start_selection(SelectionType::Lines, point, side);
ClickState::TripleClick
}
_ => {
match self.ctx.mouse().click_state {
ClickState::Click => {
// Don't launch URLs if this click cleared the selection.
self.ctx.mouse_mut().block_url_launcher = !self.ctx.selection_is_empty();
@ -541,9 +577,16 @@ impl<'a, T: EventListener, A: ActionContext<T>> Processor<'a, T, A> {
} else {
self.ctx.start_selection(SelectionType::Simple, point, side);
}
ClickState::Click
},
ClickState::DoubleClick => {
self.ctx.mouse_mut().block_url_launcher = true;
self.ctx.start_selection(SelectionType::Semantic, point, side);
},
ClickState::TripleClick => {
self.ctx.mouse_mut().block_url_launcher = true;
self.ctx.start_selection(SelectionType::Lines, point, side);
},
ClickState::None => (),
};
// Move vi mode cursor to mouse position.
@ -1080,7 +1123,7 @@ mod tests {
unimplemented!();
}
fn scheduler_mut (&mut self) -> &mut Scheduler {
fn scheduler_mut(&mut self) -> &mut Scheduler {
unimplemented!();
}
}
@ -1209,7 +1252,7 @@ mod tests {
},
window_id: unsafe { std::mem::transmute_copy(&0) },
},
end_state: ClickState::None,
end_state: ClickState::Click,
}
test_clickstate! {
@ -1273,7 +1316,7 @@ mod tests {
},
window_id: unsafe { std::mem::transmute_copy(&0) },
},
end_state: ClickState::None,
end_state: ClickState::Click,
}
test_process_binding! {

View file

@ -46,13 +46,7 @@ impl Scheduler {
}
/// Schedule a new event.
pub fn schedule(
&mut self,
event: Event,
interval: Duration,
repeat: bool,
timer_id: TimerId,
) {
pub fn schedule(&mut self, event: Event, interval: Duration, repeat: bool, timer_id: TimerId) {
let deadline = Instant::now() + interval;
// Get insert position in the schedule.