selection: allow selections to pivot around a range instead of a point

Extend selection pivoting to allow selections to pivot around a
range.

Use this in word- and row-based selections to pivot around the initial
word/row that was selected.

This mimics the behavior of at least urxvt and xterm.
This commit is contained in:
Daniel Eklöf 2021-01-03 13:11:46 +01:00
parent c821914e67
commit 2fd7b2fbd4
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
2 changed files with 83 additions and 26 deletions

View file

@ -352,11 +352,13 @@ selection_start(struct terminal *term, int col, int row,
term->selection.ongoing = true; term->selection.ongoing = true;
term->selection.spaces_only = spaces_only; term->selection.spaces_only = spaces_only;
switch (semantic) { switch (semantic) {
case SELECTION_SEMANTIC_NONE: case SELECTION_SEMANTIC_NONE:
term->selection.start = (struct coord){col, term->grid->view + row}; term->selection.start = (struct coord){col, term->grid->view + row};
term->selection.end = (struct coord){-1, -1}; term->selection.end = (struct coord){-1, -1};
term->selection.pivot.start = term->selection.start;
term->selection.pivot.end = term->selection.end;
break; break;
case SELECTION_SEMANTIC_WORD: { case SELECTION_SEMANTIC_WORD: {
@ -367,15 +369,22 @@ selection_start(struct terminal *term, int col, int row,
term->selection.start = (struct coord){ term->selection.start = (struct coord){
start.col, term->grid->view + start.row}; start.col, term->grid->view + start.row};
term->selection.pivot.start = term->selection.start;
term->selection.pivot.end = (struct coord){end.col, term->grid->view + end.row};
selection_update(term, end.col, end.row); selection_update(term, end.col, end.row);
break; break;
} }
case SELECTION_SEMANTIC_ROW: case SELECTION_SEMANTIC_ROW:
term->selection.start = (struct coord){0, term->grid->view + row}; term->selection.start = (struct coord){0, term->grid->view + row};
term->selection.pivot.start = term->selection.start;
term->selection.pivot.end = (struct coord){term->cols - 1, term->grid->view + row};
selection_update(term, term->cols - 1, row); selection_update(term, term->cols - 1, row);
break; break;
} }
} }
/* Context used while (un)marking selected cells, to be able to /* Context used while (un)marking selected cells, to be able to
@ -511,50 +520,95 @@ selection_update(struct terminal *term, int col, int row)
/* Adjust start point if the selection has changed 'direction' */ /* Adjust start point if the selection has changed 'direction' */
if (!(new_end.row == new_start.row && new_end.col == new_start.col)) { if (!(new_end.row == new_start.row && new_end.col == new_start.col)) {
enum selection_direction new_direction; enum selection_direction new_direction = term->selection.direction;
if (new_end.row > new_start.row || struct coord *pivot_start = &term->selection.pivot.start;
(new_end.row == new_start.row && new_end.col > new_start.col)) struct coord *pivot_end = &term->selection.pivot.end;
if (new_end.row < pivot_start->row ||
(new_end.row == pivot_start->row && new_end.col < pivot_start->col))
{ {
/* New end point is after the start point */ /* New end point is before the start point */
new_direction = SELECTION_RIGHT;
} else {
/* The new end point is before the start point */
new_direction = SELECTION_LEFT; new_direction = SELECTION_LEFT;
} else {
/* The new end point is after the start point */
new_direction = SELECTION_RIGHT;
} }
if (term->selection.direction != new_direction) { if (term->selection.direction != new_direction) {
if (term->selection.direction != SELECTION_UNDIR) { if (term->selection.direction == SELECTION_UNDIR &&
if (new_direction == SELECTION_LEFT) { pivot_end->row < 0)
{
/* First, make sure start isnt in the middle of a
* multi-column character */
while (true) {
const struct row *row = term->grid->rows[pivot_start->row];
const struct cell *cell = &row->cells[pivot_start->col];
if (cell->wc != CELL_MULT_COL_SPACER)
break;
/* Multi-column chars dont cross rows */
assert(pivot_start->col > 0);
if (pivot_start->col == 0)
break;
pivot_start->col--;
}
/*
* Setup pivot end to be one character *before* start
* Which one we move, the end or start point, depends
* on the initial selection direction.
*/
*pivot_end = *pivot_start;
if (new_direction == SELECTION_RIGHT) {
bool keep_going = true; bool keep_going = true;
while (keep_going) { while (keep_going) {
const wchar_t wc = row_start->cells[new_start.col].wc; const struct row *row = term->grid->rows[pivot_end->row];
const wchar_t wc = row->cells[pivot_end->col].wc;
keep_going = wc == CELL_MULT_COL_SPACER; keep_going = wc == CELL_MULT_COL_SPACER;
new_start.col--; if (pivot_end->col == 0) {
if (new_start.col < 0) { if (pivot_end->row > 0) {
new_start.col = term->cols - 1; pivot_end->col = term->cols - 1;
new_start.row--; pivot_end->row--;
} }
} else
pivot_end->col--;
} }
} else { } else {
bool keep_going = true; bool keep_going = true;
while (keep_going) { while (keep_going) {
const wchar_t wc = new_start.col < term->cols - 1 const struct row *row = term->grid->rows[pivot_start->row];
? row_start->cells[new_start.col + 1].wc const wchar_t wc = pivot_start->col < term->cols - 1
: 0; ? row->cells[pivot_start->col + 1].wc : 0;
keep_going = wc == CELL_MULT_COL_SPACER; keep_going = wc == CELL_MULT_COL_SPACER;
new_start.col++; if (pivot_start->col >= term->cols - 1) {
if (new_start.col >= term->cols) { if (pivot_start->row < term->rows - 1) {
new_start.col = 0; pivot_start->col = 0;
new_start.row++; pivot_start->row++;
} }
} else
pivot_start->col++;
} }
} }
assert(term->grid->rows[pivot_start->row]->cells[pivot_start->col].wc != CELL_MULT_COL_SPACER);
assert(term->grid->rows[pivot_end->row]->cells[pivot_end->col].wc != CELL_MULT_COL_SPACER);
} }
if (new_direction == SELECTION_LEFT) {
assert(pivot_end->row >= 0);
new_start = *pivot_end;
} else
new_start = *pivot_start;
term->selection.direction = new_direction; term->selection.direction = new_direction;
} }
} }
@ -574,7 +628,6 @@ selection_update(struct terminal *term, int col, int row)
break; break;
case SELECTION_UNDIR: case SELECTION_UNDIR:
assert(false);
break; break;
} }
break; break;
@ -590,7 +643,6 @@ selection_update(struct terminal *term, int col, int row)
break; break;
case SELECTION_UNDIR: case SELECTION_UNDIR:
assert(false);
break; break;
} }
break; break;

View file

@ -367,6 +367,11 @@ struct terminal {
bool ongoing; bool ongoing;
bool spaces_only; /* SELECTION_SEMANTIC_WORD */ bool spaces_only; /* SELECTION_SEMANTIC_WORD */
struct {
struct coord start;
struct coord end;
} pivot;
struct { struct {
int fd; int fd;
int col; int col;