Fix terminal selection when cursor leaves terminal bounds (#3898)
Previously, terminal mouse selection didn't work when the cursor moved outside the bounds of the terminal, which made it difficult to select large amounts of text in the terminal.
This commit is contained in:
commit
c29a7f28b1
3 changed files with 61 additions and 61 deletions
|
@ -158,39 +158,41 @@ pub fn mouse_moved_report(point: AlacPoint, e: &MouseMoveEvent, mode: TermMode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mouse_side(
|
pub fn grid_point(pos: Point<Pixels>, cur_size: TerminalSize, display_offset: usize) -> AlacPoint {
|
||||||
|
grid_point_and_side(pos, cur_size, display_offset).0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn grid_point_and_side(
|
||||||
pos: Point<Pixels>,
|
pos: Point<Pixels>,
|
||||||
cur_size: TerminalSize,
|
cur_size: TerminalSize,
|
||||||
) -> alacritty_terminal::index::Direction {
|
display_offset: usize,
|
||||||
let cell_width = cur_size.cell_width.floor();
|
) -> (AlacPoint, Side) {
|
||||||
if cell_width == px(0.) {
|
let mut col = GridCol((pos.x / cur_size.cell_width) as usize);
|
||||||
return Side::Right;
|
let cell_x = cmp::max(px(0.), pos.x) % cur_size.cell_width;
|
||||||
}
|
let half_cell_width = cur_size.cell_width / 2.0;
|
||||||
|
let mut side = if cell_x > half_cell_width {
|
||||||
let x = pos.x.floor();
|
|
||||||
|
|
||||||
let cell_x = cmp::max(px(0.), x - cell_width) % cell_width;
|
|
||||||
let half_cell_width = (cur_size.cell_width / 2.0).floor();
|
|
||||||
let additional_padding = (cur_size.width() - cur_size.cell_width * 2.) % cur_size.cell_width;
|
|
||||||
let end_of_grid = cur_size.width() - cur_size.cell_width - additional_padding;
|
|
||||||
|
|
||||||
//Width: Pixels or columns?
|
|
||||||
if cell_x > half_cell_width
|
|
||||||
// Edge case when mouse leaves the window.
|
|
||||||
|| x >= end_of_grid
|
|
||||||
{
|
|
||||||
Side::Right
|
Side::Right
|
||||||
} else {
|
} else {
|
||||||
Side::Left
|
Side::Left
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
pub fn grid_point(pos: Point<Pixels>, cur_size: TerminalSize, display_offset: usize) -> AlacPoint {
|
if col > cur_size.last_column() {
|
||||||
let col = GridCol((pos.x / cur_size.cell_width) as usize);
|
col = cur_size.last_column();
|
||||||
|
side = Side::Right;
|
||||||
|
}
|
||||||
let col = min(col, cur_size.last_column());
|
let col = min(col, cur_size.last_column());
|
||||||
let line = (pos.y / cur_size.line_height) as i32;
|
let mut line = (pos.y / cur_size.line_height) as i32;
|
||||||
let line = min(line, cur_size.bottommost_line().0);
|
if line > cur_size.bottommost_line() {
|
||||||
AlacPoint::new(GridLine(line - display_offset as i32), col)
|
line = cur_size.bottommost_line().0 as i32;
|
||||||
|
side = Side::Right;
|
||||||
|
} else if line < 0 {
|
||||||
|
side = Side::Left;
|
||||||
|
}
|
||||||
|
|
||||||
|
(
|
||||||
|
AlacPoint::new(GridLine(line - display_offset as i32), col),
|
||||||
|
side,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
///Generate the bytes to send to the terminal, from the cell location, a mouse event, and the terminal mode
|
///Generate the bytes to send to the terminal, from the cell location, a mouse event, and the terminal mode
|
||||||
|
|
|
@ -28,7 +28,8 @@ use futures::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use mappings::mouse::{
|
use mappings::mouse::{
|
||||||
alt_scroll, grid_point, mouse_button_report, mouse_moved_report, mouse_side, scroll_report,
|
alt_scroll, grid_point, grid_point_and_side, mouse_button_report, mouse_moved_report,
|
||||||
|
scroll_report,
|
||||||
};
|
};
|
||||||
|
|
||||||
use procinfo::LocalProcessInfo;
|
use procinfo::LocalProcessInfo;
|
||||||
|
@ -704,14 +705,12 @@ impl Terminal {
|
||||||
}
|
}
|
||||||
InternalEvent::UpdateSelection(position) => {
|
InternalEvent::UpdateSelection(position) => {
|
||||||
if let Some(mut selection) = term.selection.take() {
|
if let Some(mut selection) = term.selection.take() {
|
||||||
let point = grid_point(
|
let (point, side) = grid_point_and_side(
|
||||||
*position,
|
*position,
|
||||||
self.last_content.size,
|
self.last_content.size,
|
||||||
term.grid().display_offset(),
|
term.grid().display_offset(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let side = mouse_side(*position, self.last_content.size);
|
|
||||||
|
|
||||||
selection.update(point, side);
|
selection.update(point, side);
|
||||||
term.selection = Some(selection);
|
term.selection = Some(selection);
|
||||||
|
|
||||||
|
@ -1088,12 +1087,11 @@ impl Terminal {
|
||||||
let position = e.position - origin;
|
let position = e.position - origin;
|
||||||
self.last_mouse_position = Some(position);
|
self.last_mouse_position = Some(position);
|
||||||
if self.mouse_mode(e.modifiers.shift) {
|
if self.mouse_mode(e.modifiers.shift) {
|
||||||
let point = grid_point(
|
let (point, side) = grid_point_and_side(
|
||||||
position,
|
position,
|
||||||
self.last_content.size,
|
self.last_content.size,
|
||||||
self.last_content.display_offset,
|
self.last_content.display_offset,
|
||||||
);
|
);
|
||||||
let side = mouse_side(position, self.last_content.size);
|
|
||||||
|
|
||||||
if self.mouse_changed(point, side) {
|
if self.mouse_changed(point, side) {
|
||||||
if let Some(bytes) = mouse_moved_report(point, e, self.last_content.mode) {
|
if let Some(bytes) = mouse_moved_report(point, e, self.last_content.mode) {
|
||||||
|
@ -1175,15 +1173,12 @@ impl Terminal {
|
||||||
}
|
}
|
||||||
} else if e.button == MouseButton::Left {
|
} else if e.button == MouseButton::Left {
|
||||||
let position = e.position - origin;
|
let position = e.position - origin;
|
||||||
let point = grid_point(
|
let (point, side) = grid_point_and_side(
|
||||||
position,
|
position,
|
||||||
self.last_content.size,
|
self.last_content.size,
|
||||||
self.last_content.display_offset,
|
self.last_content.display_offset,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Use .opposite so that selection is inclusive of the cell clicked.
|
|
||||||
let side = mouse_side(position, self.last_content.size);
|
|
||||||
|
|
||||||
let selection_type = match e.click_count {
|
let selection_type = match e.click_count {
|
||||||
0 => return, //This is a release
|
0 => return, //This is a release
|
||||||
1 => Some(SelectionType::Simple),
|
1 => Some(SelectionType::Simple),
|
||||||
|
|
|
@ -2,10 +2,11 @@ use editor::{Cursor, HighlightedRange, HighlightedRangeLine};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, fill, point, px, red, relative, AnyElement, AsyncWindowContext, AvailableSpace,
|
div, fill, point, px, red, relative, AnyElement, AsyncWindowContext, AvailableSpace,
|
||||||
BorrowWindow, Bounds, DispatchPhase, Element, ElementId, ExternalPaths, FocusHandle, Font,
|
BorrowWindow, Bounds, DispatchPhase, Element, ElementId, ExternalPaths, FocusHandle, Font,
|
||||||
FontStyle, FontWeight, HighlightStyle, Hsla, InteractiveElement, InteractiveElementState,
|
FontStyle, FontWeight, HighlightStyle, Hsla, InteractiveBounds, InteractiveElement,
|
||||||
Interactivity, IntoElement, LayoutId, Model, ModelContext, ModifiersChangedEvent, MouseButton,
|
InteractiveElementState, Interactivity, IntoElement, LayoutId, Model, ModelContext,
|
||||||
Pixels, PlatformInputHandler, Point, ShapedLine, StatefulInteractiveElement, StyleRefinement,
|
ModifiersChangedEvent, MouseButton, MouseMoveEvent, Pixels, PlatformInputHandler, Point,
|
||||||
Styled, TextRun, TextStyle, TextSystem, UnderlineStyle, WhiteSpace, WindowContext,
|
ShapedLine, StatefulInteractiveElement, StyleRefinement, Styled, TextRun, TextStyle,
|
||||||
|
TextSystem, UnderlineStyle, WhiteSpace, WindowContext,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use language::CursorShape;
|
use language::CursorShape;
|
||||||
|
@ -598,33 +599,48 @@ impl TerminalElement {
|
||||||
) {
|
) {
|
||||||
let focus = self.focus.clone();
|
let focus = self.focus.clone();
|
||||||
let terminal = self.terminal.clone();
|
let terminal = self.terminal.clone();
|
||||||
|
let interactive_bounds = InteractiveBounds {
|
||||||
|
bounds: bounds.intersect(&cx.content_mask().bounds),
|
||||||
|
stacking_order: cx.stacking_order().clone(),
|
||||||
|
};
|
||||||
|
|
||||||
self.interactivity.on_mouse_down(MouseButton::Left, {
|
self.interactivity.on_mouse_down(MouseButton::Left, {
|
||||||
let terminal = terminal.clone();
|
let terminal = terminal.clone();
|
||||||
let focus = focus.clone();
|
let focus = focus.clone();
|
||||||
move |e, cx| {
|
move |e, cx| {
|
||||||
cx.focus(&focus);
|
cx.focus(&focus);
|
||||||
//todo!(context menu)
|
|
||||||
// v.context_menu.update(cx, |menu, _cx| menu.delay_cancel());
|
|
||||||
terminal.update(cx, |terminal, cx| {
|
terminal.update(cx, |terminal, cx| {
|
||||||
terminal.mouse_down(&e, origin);
|
terminal.mouse_down(&e, origin);
|
||||||
|
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
self.interactivity.on_mouse_move({
|
|
||||||
let terminal = terminal.clone();
|
cx.on_mouse_event({
|
||||||
let focus = focus.clone();
|
let bounds = bounds.clone();
|
||||||
move |e, cx| {
|
let focus = self.focus.clone();
|
||||||
if e.pressed_button.is_some() && focus.is_focused(cx) && !cx.has_active_drag() {
|
let terminal = self.terminal.clone();
|
||||||
|
move |e: &MouseMoveEvent, phase, cx| {
|
||||||
|
if phase != DispatchPhase::Bubble || !focus.is_focused(cx) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.pressed_button.is_some() && !cx.has_active_drag() {
|
||||||
terminal.update(cx, |terminal, cx| {
|
terminal.update(cx, |terminal, cx| {
|
||||||
terminal.mouse_drag(e, origin, bounds);
|
terminal.mouse_drag(e, origin, bounds);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if interactive_bounds.visibly_contains(&e.position, cx) {
|
||||||
|
terminal.update(cx, |terminal, cx| {
|
||||||
|
terminal.mouse_move(&e, origin);
|
||||||
|
cx.notify();
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
self.interactivity.on_mouse_up(
|
self.interactivity.on_mouse_up(
|
||||||
MouseButton::Left,
|
MouseButton::Left,
|
||||||
TerminalElement::generic_button_handler(
|
TerminalElement::generic_button_handler(
|
||||||
|
@ -651,19 +667,6 @@ impl TerminalElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
self.interactivity.on_mouse_move({
|
|
||||||
let terminal = terminal.clone();
|
|
||||||
let focus = focus.clone();
|
|
||||||
move |e, cx| {
|
|
||||||
if focus.is_focused(cx) {
|
|
||||||
terminal.update(cx, |terminal, cx| {
|
|
||||||
terminal.mouse_move(&e, origin);
|
|
||||||
cx.notify();
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
self.interactivity.on_scroll_wheel({
|
self.interactivity.on_scroll_wheel({
|
||||||
let terminal = terminal.clone();
|
let terminal = terminal.clone();
|
||||||
move |e, cx| {
|
move |e, cx| {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue