1
1
mirror of https://github.com/wez/wezterm.git synced 2024-11-23 15:04:36 +03:00

add rectangular selection

Alt-dragging will use rectangular selection in the default mouse
assignments.

refs: https://github.com/wez/wezterm/issues/1361
This commit is contained in:
Wez Furlong 2022-05-05 20:49:22 -07:00
parent 1b00bbaf2f
commit ef4a95211e
8 changed files with 58 additions and 7 deletions

View File

@ -95,6 +95,7 @@ pub enum SelectionMode {
Word,
Line,
SemanticZone,
Block,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]

View File

@ -25,6 +25,7 @@ As features stabilize some brief notes about them will accumulate here.
* `wezterm cli list --format json` and `wezterm cli list-clients --format json` allow retrieving data in json format. Thanks to [@ratmice](https://github.com/ratmice)! [#1911](https://github.com/wez/wezterm/pull/1911)
* macOS: you may now drag and drop files from other programs and have their paths paste into the terminal. The new [quote_dropped_files](config/lua/config/quote_dropped_files.md) option controls how the file names are quoted. Thanks to [@junnplus](https://github.com/junnplus)! [#1868](https://github.com/wez/wezterm/pull/1868)
* The mouse scroll wheel now cycles between tabs when hovering over the tab tab. Thanks to [@junnplus](https://github.com/junnplus)! [#1726](https://github.com/wez/wezterm/issues/1726)
* Holding down `ALT` while dragging the left button will select a rectangular block. [ExtendSelectionToMouseCursor](config/lua/keyassignment/ExtendSelectionToMouseCursor.md) now accepts `"Block"` as a selection mode. [#1361](https://github.com/wez/wezterm/issues/1361)
#### Updated
* Bundled harfbuzz to 4.2.1

View File

@ -23,4 +23,6 @@ when unspecified, wezterm will use a default mode which at the time
of writing is `Cell`, but in a future release may be context sensitive
based on recent actions.
*Since: nightly builds only*
The mode argument can also be `"Block"` to enable a rectangular block selection.

View File

@ -32,11 +32,14 @@ that order.
| Double Left Down | `NONE` | `SelectTextAtMouseCursor="Word"` |
| Single Left Down | `NONE` | `SelectTextAtMouseCursor="Cell"` |
| Single Left Down | `SHIFT` | `ExtendSelectionToMouseCursor={}` |
| Single Left Down | `ALT` | `ExtendSelectionToMouseCursor="Block"` (*since: nightly builds only*) |
| Single Left Up | `SHIFT` | `CompleteSelectionOrOpenLinkAtMouseCursor="PrimarySelection"` |
| Single Left Up | `NONE` | `CompleteSelectionOrOpenLinkAtMouseCursor="PrimarySelection"` |
| Single Left Up | `ALT` | `CompleteSelection="PrimarySelection"` (*since: nightly builds only*) |
| Double Left Up | `NONE` | `CompleteSelection="PrimarySelection"` |
| Triple Left Up | `NONE` | `CompleteSelection="PrimarySelection"` |
| Single Left Drag | `NONE` | `ExtendSelectionToMouseCursor="Cell"` |
| Single Left Drag | `ALT` | `ExtendSelectionToMouseCursor="Block"` (*since: nightly builds only*) |
| Double Left Drag | `NONE` | `ExtendSelectionToMouseCursor="Word"` |
| Triple Left Drag | `NONE` | `ExtendSelectionToMouseCursor="Line"` |
| Single Middle Down | `NONE` | `PasteFrom="PrimarySelection"` |

View File

@ -75,6 +75,14 @@ impl InputMap {
},
SelectTextAtMouseCursor(SelectionMode::Cell)
],
[
Modifiers::ALT,
MouseEventTrigger::Down {
streak: 1,
button: MouseButton::Left
},
SelectTextAtMouseCursor(SelectionMode::Block)
],
[
Modifiers::SHIFT,
MouseEventTrigger::Down {
@ -103,6 +111,14 @@ impl InputMap {
ClipboardCopyDestination::PrimarySelection
)
],
[
Modifiers::ALT,
MouseEventTrigger::Up {
streak: 1,
button: MouseButton::Left
},
CompleteSelection(ClipboardCopyDestination::PrimarySelection)
],
[
Modifiers::NONE,
MouseEventTrigger::Up {
@ -127,6 +143,14 @@ impl InputMap {
},
ExtendSelectionToMouseCursor(Some(SelectionMode::Cell))
],
[
Modifiers::ALT,
MouseEventTrigger::Drag {
streak: 1,
button: MouseButton::Left
},
ExtendSelectionToMouseCursor(Some(SelectionMode::Block))
],
[
Modifiers::NONE,
MouseEventTrigger::Drag {

View File

@ -17,6 +17,8 @@ pub struct Selection {
pub range: Option<SelectionRange>,
/// When the selection was made wrt. the pane content
pub seqno: SequenceNo,
/// Whether the selection is rectangular
pub rectangular: bool,
}
pub use config::keyassignment::SelectionMode;
@ -215,9 +217,9 @@ impl SelectionRange {
/// Since this struct has no knowledge of line length, it cannot be
/// more precise than that.
/// Must be called on a normalized range!
pub fn cols_for_row(&self, row: StableRowIndex) -> Range<usize> {
pub fn cols_for_row(&self, row: StableRowIndex, rectangular: bool) -> Range<usize> {
let norm = self.normalize();
if row < norm.start.y || row > norm.end.y {
let range = if row < norm.start.y || row > norm.end.y {
0..0
} else if norm.start.y == norm.end.y {
// A single line selection
@ -235,6 +237,12 @@ impl SelectionRange {
} else {
// some "middle" line of multi-line
0..usize::max_value()
};
if rectangular {
range.start.max(norm.start.x)..range.end.min(norm.end.x.saturating_add(1))
} else {
range
}
}
}

View File

@ -1349,7 +1349,10 @@ impl super::TermWindow {
)?;
}
let selrange = self.selection(pos.pane.pane_id()).range.clone();
let (selrange, rectangular) = {
let sel = self.selection(pos.pane.pane_id());
(sel.range.clone(), sel.rectangular)
};
let start = Instant::now();
let selection_fg = palette.selection_fg.to_linear();
@ -1362,7 +1365,7 @@ impl super::TermWindow {
for (line_idx, line) in lines.iter().enumerate() {
let stable_row = stable_top + line_idx as StableRowIndex;
let selrange = selrange.map_or(0..0, |sel| sel.cols_for_row(stable_row));
let selrange = selrange.map_or(0..0, |sel| sel.cols_for_row(stable_row, rectangular));
// Constrain to the pane width!
let selrange = selrange.start..selrange.end.min(dims.cols);

View File

@ -12,6 +12,7 @@ impl super::TermWindow {
pub fn selection_text(&self, pane: &Rc<dyn Pane>) -> String {
let mut s = String::new();
let rectangular = self.selection(pane.pane_id()).rectangular;
if let Some(sel) = self
.selection(pane.pane_id())
.range
@ -31,7 +32,7 @@ impl super::TermWindow {
let this_row = line.first_row + idx as StableRowIndex;
if this_row >= first_row && this_row < last_row {
let last_phys_idx = phys.cells().len().saturating_sub(1);
let cols = sel.cols_for_row(this_row);
let cols = sel.cols_for_row(this_row, rectangular);
let last_col_idx = cols.end.saturating_sub(1).min(last_phys_idx);
let col_span = phys.columns_as_str(cols);
// Only trim trailing whitespace if we are the last line
@ -76,7 +77,7 @@ impl super::TermWindow {
};
let x = position.column;
match mode {
SelectionMode::Cell => {
SelectionMode::Cell | SelectionMode::Block => {
// Origin is the cell in which the selection action started. E.g. the cell
// that had the mouse over it when the left mouse button was pressed
let origin = self
@ -84,6 +85,7 @@ impl super::TermWindow {
.origin
.unwrap_or(SelectionCoordinate { x, y });
self.selection(pane.pane_id()).origin = Some(origin);
self.selection(pane.pane_id()).rectangular = mode == SelectionMode::Block;
// Compute the start and end horizontall cell of the selection.
// The selection extent depends on the mouse cursor position in relation
@ -128,6 +130,7 @@ impl super::TermWindow {
let selection_range = start_word.extend_with(end_word);
self.selection(pane.pane_id()).range = Some(selection_range);
self.selection(pane.pane_id()).rectangular = false;
}
SelectionMode::Line => {
let end_line = SelectionRange::line_around(SelectionCoordinate { x, y }, &**pane);
@ -141,6 +144,7 @@ impl super::TermWindow {
let selection_range = start_line.extend_with(end_line);
self.selection(pane.pane_id()).range = Some(selection_range);
self.selection(pane.pane_id()).rectangular = false;
}
SelectionMode::SemanticZone => {
let end_word = SelectionRange::zone_around(SelectionCoordinate { x, y }, &**pane);
@ -154,6 +158,7 @@ impl super::TermWindow {
let selection_range = start_word.extend_with(end_word);
self.selection(pane.pane_id()).range = Some(selection_range);
self.selection(pane.pane_id()).rectangular = false;
}
}
@ -184,6 +189,7 @@ impl super::TermWindow {
self.selection(pane.pane_id()).origin = Some(start);
self.selection(pane.pane_id()).range = Some(selection_range);
self.selection(pane.pane_id()).rectangular = false;
}
SelectionMode::Word => {
let selection_range =
@ -191,6 +197,7 @@ impl super::TermWindow {
self.selection(pane.pane_id()).origin = Some(selection_range.start);
self.selection(pane.pane_id()).range = Some(selection_range);
self.selection(pane.pane_id()).rectangular = false;
}
SelectionMode::SemanticZone => {
let selection_range =
@ -198,10 +205,12 @@ impl super::TermWindow {
self.selection(pane.pane_id()).origin = Some(selection_range.start);
self.selection(pane.pane_id()).range = Some(selection_range);
self.selection(pane.pane_id()).rectangular = false;
}
SelectionMode::Cell => {
SelectionMode::Cell | SelectionMode::Block => {
self.selection(pane.pane_id())
.begin(SelectionCoordinate { x, y });
self.selection(pane.pane_id()).rectangular = mode == SelectionMode::Block;
}
}