mirror of
https://github.com/alacritty/alacritty.git
synced 2024-11-18 13:55:23 -05:00
Fix selection start point lagging behind cursor
Since the mouse start position has been the first movement event after the mouse button was held down, there have been some issues with the start point lagging behind the cursor because movement events were not reported from the initial position but there was a gap until movement starts reporting. To fix this whenever the mouse button is pressed, the position and cell side is stored on the `Mouse` struct. Because of this it does not matter anymore if the movement events are all reported and we can just start a selection using the stored position/side whenever there currently is no selection present. This fixes #1366
This commit is contained in:
parent
593d7718d0
commit
5e97fcbea4
3 changed files with 55 additions and 32 deletions
|
@ -41,6 +41,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- Fix build failure on 32-bit systems
|
- Fix build failure on 32-bit systems
|
||||||
- Windows started as unfocused now show the hollow cursor if the setting is enabled
|
- Windows started as unfocused now show the hollow cursor if the setting is enabled
|
||||||
- Empty lines in selections are now properly copied to the clipboard
|
- Empty lines in selections are now properly copied to the clipboard
|
||||||
|
- Selection start point lagging behind initial cursor position
|
||||||
|
|
||||||
### Deprecated
|
### Deprecated
|
||||||
|
|
||||||
|
|
12
src/event.rs
12
src/event.rs
|
@ -82,17 +82,14 @@ impl<'a, N: Notify + 'a> input::ActionContext for ActionContext<'a, N> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_selection(&mut self, point: Point, side: Side) {
|
fn update_selection(&mut self, point: Point, side: Side) {
|
||||||
self.terminal.dirty = true;
|
|
||||||
let point = self.terminal.visible_to_buffer(point);
|
let point = self.terminal.visible_to_buffer(point);
|
||||||
|
|
||||||
// Update selection if one exists
|
// Update selection if one exists
|
||||||
if let Some(ref mut selection) = *self.terminal.selection_mut() {
|
if let Some(ref mut selection) = self.terminal.selection_mut() {
|
||||||
selection.update(point, side);
|
selection.update(point, side);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, start a regular selection
|
self.terminal.dirty = true;
|
||||||
*self.terminal.selection_mut() = Some(Selection::simple(point, side));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn simple_selection(&mut self, point: Point, side: Side) {
|
fn simple_selection(&mut self, point: Point, side: Side) {
|
||||||
|
@ -130,6 +127,11 @@ impl<'a, N: Notify + 'a> input::ActionContext for ActionContext<'a, N> {
|
||||||
self.mouse
|
self.mouse
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn mouse(&self) -> &Mouse {
|
||||||
|
self.mouse
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn received_count(&mut self) -> &mut usize {
|
fn received_count(&mut self) -> &mut usize {
|
||||||
&mut self.received_count
|
&mut self.received_count
|
||||||
|
|
74
src/input.rs
74
src/input.rs
|
@ -63,6 +63,7 @@ pub trait ActionContext {
|
||||||
fn semantic_selection(&mut self, point: Point);
|
fn semantic_selection(&mut self, point: Point);
|
||||||
fn line_selection(&mut self, point: Point);
|
fn line_selection(&mut self, point: Point);
|
||||||
fn mouse_mut(&mut self) -> &mut Mouse;
|
fn mouse_mut(&mut self) -> &mut Mouse;
|
||||||
|
fn mouse(&self) -> &Mouse;
|
||||||
fn mouse_coords(&self) -> Option<Point>;
|
fn mouse_coords(&self) -> Option<Point>;
|
||||||
fn received_count(&mut self) -> &mut usize;
|
fn received_count(&mut self) -> &mut usize;
|
||||||
fn suppress_chars(&mut self) -> &mut bool;
|
fn suppress_chars(&mut self) -> &mut bool;
|
||||||
|
@ -318,41 +319,27 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
|
||||||
let prev_line = mem::replace(&mut self.ctx.mouse_mut().line, point.line);
|
let prev_line = mem::replace(&mut self.ctx.mouse_mut().line, point.line);
|
||||||
let prev_col = mem::replace(&mut self.ctx.mouse_mut().column, point.col);
|
let prev_col = mem::replace(&mut self.ctx.mouse_mut().column, point.col);
|
||||||
|
|
||||||
let cell_x = x.saturating_sub(size_info.padding_x as usize) % size_info.cell_width as usize;
|
|
||||||
let half_cell_width = (size_info.cell_width / 2.0) as usize;
|
|
||||||
|
|
||||||
let additional_padding = (size_info.width - size_info.padding_x * 2.) % size_info.cell_width;
|
|
||||||
let end_of_grid = size_info.width - size_info.padding_x - additional_padding;
|
|
||||||
let cell_side = if cell_x > half_cell_width
|
|
||||||
// Edge case when mouse leaves the window
|
|
||||||
|| x as f32 >= end_of_grid
|
|
||||||
{
|
|
||||||
Side::Right
|
|
||||||
} else {
|
|
||||||
Side::Left
|
|
||||||
};
|
|
||||||
self.ctx.mouse_mut().cell_side = cell_side;
|
|
||||||
|
|
||||||
let motion_mode = TermMode::MOUSE_MOTION | TermMode::MOUSE_DRAG;
|
let motion_mode = TermMode::MOUSE_MOTION | TermMode::MOUSE_DRAG;
|
||||||
let report_mode = TermMode::MOUSE_REPORT_CLICK | motion_mode;
|
let report_mode = TermMode::MOUSE_REPORT_CLICK | motion_mode;
|
||||||
|
|
||||||
if self.ctx.mouse_mut().left_button_state == ElementState::Pressed &&
|
if self.ctx.mouse().left_button_state == ElementState::Pressed &&
|
||||||
( modifiers.shift || !self.ctx.terminal_mode().intersects(report_mode))
|
( modifiers.shift || !self.ctx.terminal_mode().intersects(report_mode))
|
||||||
{
|
{
|
||||||
|
let cell_side = self.get_mouse_side();
|
||||||
self.ctx.update_selection(Point {
|
self.ctx.update_selection(Point {
|
||||||
line: point.line,
|
line: point.line,
|
||||||
col: point.col
|
col: point.col
|
||||||
}, cell_side);
|
}, cell_side);
|
||||||
} else if self.ctx.terminal_mode().intersects(motion_mode)
|
} else if self.ctx.terminal_mode().intersects(motion_mode)
|
||||||
// Only report motion when changing cells
|
// Only report motion when changing cells
|
||||||
&& (prev_line != self.ctx.mouse_mut().line || prev_col != self.ctx.mouse_mut().column)
|
&& (prev_line != self.ctx.mouse().line || prev_col != self.ctx.mouse().column)
|
||||||
&& size_info.contains_point(x, y)
|
&& size_info.contains_point(x, y)
|
||||||
{
|
{
|
||||||
if self.ctx.mouse_mut().left_button_state == ElementState::Pressed {
|
if self.ctx.mouse().left_button_state == ElementState::Pressed {
|
||||||
self.mouse_report(32, ElementState::Pressed, modifiers);
|
self.mouse_report(32, ElementState::Pressed, modifiers);
|
||||||
} else if self.ctx.mouse_mut().middle_button_state == ElementState::Pressed {
|
} else if self.ctx.mouse().middle_button_state == ElementState::Pressed {
|
||||||
self.mouse_report(33, ElementState::Pressed, modifiers);
|
self.mouse_report(33, ElementState::Pressed, modifiers);
|
||||||
} else if self.ctx.mouse_mut().right_button_state == ElementState::Pressed {
|
} else if self.ctx.mouse().right_button_state == ElementState::Pressed {
|
||||||
self.mouse_report(34, ElementState::Pressed, modifiers);
|
self.mouse_report(34, ElementState::Pressed, modifiers);
|
||||||
} else if self.ctx.terminal_mode().contains(TermMode::MOUSE_MOTION) {
|
} else if self.ctx.terminal_mode().contains(TermMode::MOUSE_MOTION) {
|
||||||
self.mouse_report(35, ElementState::Pressed, modifiers);
|
self.mouse_report(35, ElementState::Pressed, modifiers);
|
||||||
|
@ -361,7 +348,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn normal_mouse_report(&mut self, button: u8) {
|
pub fn normal_mouse_report(&mut self, button: u8) {
|
||||||
let (line, column) = (self.ctx.mouse_mut().line, self.ctx.mouse_mut().column);
|
let (line, column) = (self.ctx.mouse().line, self.ctx.mouse().column);
|
||||||
|
|
||||||
if line < Line(223) && column < Column(223) {
|
if line < Line(223) && column < Column(223) {
|
||||||
let msg = vec![
|
let msg = vec![
|
||||||
|
@ -378,7 +365,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sgr_mouse_report(&mut self, button: u8, state: ElementState) {
|
pub fn sgr_mouse_report(&mut self, button: u8, state: ElementState) {
|
||||||
let (line, column) = (self.ctx.mouse_mut().line, self.ctx.mouse_mut().column);
|
let (line, column) = (self.ctx.mouse().line, self.ctx.mouse().column);
|
||||||
let c = match state {
|
let c = match state {
|
||||||
ElementState::Pressed => 'M',
|
ElementState::Pressed => 'M',
|
||||||
ElementState::Released => 'm',
|
ElementState::Released => 'm',
|
||||||
|
@ -425,10 +412,10 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
|
||||||
|
|
||||||
pub fn on_mouse_press(&mut self, button: MouseButton, modifiers: ModifiersState) {
|
pub fn on_mouse_press(&mut self, button: MouseButton, modifiers: ModifiersState) {
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
let elapsed = self.ctx.mouse_mut().last_click_timestamp.elapsed();
|
let elapsed = self.ctx.mouse().last_click_timestamp.elapsed();
|
||||||
self.ctx.mouse_mut().last_click_timestamp = now;
|
self.ctx.mouse_mut().last_click_timestamp = now;
|
||||||
|
|
||||||
self.ctx.mouse_mut().click_state = match self.ctx.mouse_mut().click_state {
|
self.ctx.mouse_mut().click_state = match self.ctx.mouse().click_state {
|
||||||
ClickState::Click if elapsed < self.mouse_config.double_click.threshold => {
|
ClickState::Click if elapsed < self.mouse_config.double_click.threshold => {
|
||||||
self.on_mouse_double_click();
|
self.on_mouse_double_click();
|
||||||
ClickState::DoubleClick
|
ClickState::DoubleClick
|
||||||
|
@ -439,6 +426,13 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
self.ctx.clear_selection();
|
self.ctx.clear_selection();
|
||||||
|
|
||||||
|
// Start new empty selection
|
||||||
|
if let Some(point) = self.ctx.mouse_coords() {
|
||||||
|
let side = self.get_mouse_side();
|
||||||
|
self.ctx.simple_selection(point, side);
|
||||||
|
}
|
||||||
|
|
||||||
let report_modes = TermMode::MOUSE_REPORT_CLICK | TermMode::MOUSE_DRAG | TermMode::MOUSE_MOTION;
|
let report_modes = TermMode::MOUSE_REPORT_CLICK | TermMode::MOUSE_DRAG | TermMode::MOUSE_MOTION;
|
||||||
if !modifiers.shift && self.ctx.terminal_mode().intersects(report_modes) {
|
if !modifiers.shift && self.ctx.terminal_mode().intersects(report_modes) {
|
||||||
match button {
|
match button {
|
||||||
|
@ -456,6 +450,26 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_mouse_side(&self) -> Side {
|
||||||
|
let size_info = self.ctx.size_info();
|
||||||
|
let x = self.ctx.mouse().x;
|
||||||
|
|
||||||
|
let cell_x = x.saturating_sub(size_info.padding_x as usize) % size_info.cell_width as usize;
|
||||||
|
let half_cell_width = (size_info.cell_width / 2.0) as usize;
|
||||||
|
|
||||||
|
let additional_padding = (size_info.width - size_info.padding_x * 2.) % size_info.cell_width;
|
||||||
|
let end_of_grid = size_info.width - size_info.padding_x - additional_padding;
|
||||||
|
|
||||||
|
if cell_x > half_cell_width
|
||||||
|
// Edge case when mouse leaves the window
|
||||||
|
|| x as f32 >= end_of_grid
|
||||||
|
{
|
||||||
|
Side::Right
|
||||||
|
} else {
|
||||||
|
Side::Left
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn on_mouse_release(&mut self, button: MouseButton, modifiers: ModifiersState) {
|
pub fn on_mouse_release(&mut self, button: MouseButton, modifiers: ModifiersState) {
|
||||||
let report_modes = TermMode::MOUSE_REPORT_CLICK | TermMode::MOUSE_DRAG | TermMode::MOUSE_MOTION;
|
let report_modes = TermMode::MOUSE_REPORT_CLICK | TermMode::MOUSE_DRAG | TermMode::MOUSE_MOTION;
|
||||||
if !modifiers.shift && self.ctx.terminal_mode().intersects(report_modes)
|
if !modifiers.shift && self.ctx.terminal_mode().intersects(report_modes)
|
||||||
|
@ -479,7 +493,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
|
||||||
pub fn on_mouse_wheel(&mut self, delta: MouseScrollDelta, phase: TouchPhase, modifiers: ModifiersState) {
|
pub fn on_mouse_wheel(&mut self, delta: MouseScrollDelta, phase: TouchPhase, modifiers: ModifiersState) {
|
||||||
match delta {
|
match delta {
|
||||||
MouseScrollDelta::LineDelta(_columns, lines) => {
|
MouseScrollDelta::LineDelta(_columns, lines) => {
|
||||||
let to_scroll = self.ctx.mouse_mut().lines_scrolled + lines;
|
let to_scroll = self.ctx.mouse().lines_scrolled + lines;
|
||||||
let code = if to_scroll > 0.0 {
|
let code = if to_scroll > 0.0 {
|
||||||
64
|
64
|
||||||
} else {
|
} else {
|
||||||
|
@ -503,8 +517,8 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> {
|
||||||
self.ctx.mouse_mut().scroll_px += y as i32;
|
self.ctx.mouse_mut().scroll_px += y as i32;
|
||||||
let height = self.ctx.size_info().cell_height as i32;
|
let height = self.ctx.size_info().cell_height as i32;
|
||||||
|
|
||||||
while self.ctx.mouse_mut().scroll_px.abs() >= height {
|
while self.ctx.mouse().scroll_px.abs() >= height {
|
||||||
let code = if self.ctx.mouse_mut().scroll_px > 0 {
|
let code = if self.ctx.mouse().scroll_px > 0 {
|
||||||
self.ctx.mouse_mut().scroll_px -= height;
|
self.ctx.mouse_mut().scroll_px -= height;
|
||||||
64
|
64
|
||||||
} else {
|
} else {
|
||||||
|
@ -757,6 +771,12 @@ mod tests {
|
||||||
fn mouse_mut(&mut self) -> &mut Mouse {
|
fn mouse_mut(&mut self) -> &mut Mouse {
|
||||||
self.mouse
|
self.mouse
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn mouse(&self) -> &Mouse {
|
||||||
|
self.mouse
|
||||||
|
}
|
||||||
|
|
||||||
fn received_count(&mut self) -> &mut usize {
|
fn received_count(&mut self) -> &mut usize {
|
||||||
&mut self.received_count
|
&mut self.received_count
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue