terminal: Implement word and line based selection
Double-click to select by word, triple click to select by line.
This commit is contained in:
+113
-48
@@ -364,6 +364,13 @@ enum escape_state {
|
|||||||
#define ESC_FLAG_DQUOTE 0x20
|
#define ESC_FLAG_DQUOTE 0x20
|
||||||
#define ESC_FLAG_SPACE 0x40
|
#define ESC_FLAG_SPACE 0x40
|
||||||
|
|
||||||
|
enum {
|
||||||
|
SELECT_NONE,
|
||||||
|
SELECT_CHAR,
|
||||||
|
SELECT_WORD,
|
||||||
|
SELECT_LINE
|
||||||
|
};
|
||||||
|
|
||||||
struct terminal {
|
struct terminal {
|
||||||
struct window *window;
|
struct window *window;
|
||||||
struct widget *widget;
|
struct widget *widget;
|
||||||
@@ -404,9 +411,12 @@ struct terminal {
|
|||||||
uint32_t hide_cursor_serial;
|
uint32_t hide_cursor_serial;
|
||||||
|
|
||||||
struct wl_data_source *selection;
|
struct wl_data_source *selection;
|
||||||
int32_t dragging;
|
uint32_t button_time;
|
||||||
|
int dragging, click_count;
|
||||||
int selection_start_x, selection_start_y;
|
int selection_start_x, selection_start_y;
|
||||||
int selection_end_x, selection_end_y;
|
int selection_end_x, selection_end_y;
|
||||||
|
int selection_start_row, selection_start_col;
|
||||||
|
int selection_end_row, selection_end_col;
|
||||||
struct wl_list link;
|
struct wl_list link;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -505,57 +515,20 @@ union decoded_attr {
|
|||||||
uint32_t key;
|
uint32_t key;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int
|
|
||||||
terminal_compare_position(struct terminal *terminal,
|
|
||||||
int x, int y, int32_t ref_row, int32_t ref_col)
|
|
||||||
{
|
|
||||||
struct rectangle allocation;
|
|
||||||
int top_margin, side_margin, col, row, ref_x;
|
|
||||||
|
|
||||||
widget_get_allocation(terminal->widget, &allocation);
|
|
||||||
side_margin = allocation.x + (allocation.width - terminal->width * terminal->extents.max_x_advance) / 2;
|
|
||||||
top_margin = allocation.y + (allocation.height - terminal->height * terminal->extents.height) / 2;
|
|
||||||
|
|
||||||
col = (x - side_margin) / terminal->extents.max_x_advance;
|
|
||||||
row = (y - top_margin) / terminal->extents.height;
|
|
||||||
|
|
||||||
ref_x = side_margin + ref_col * terminal->extents.max_x_advance +
|
|
||||||
terminal->extents.max_x_advance / 2;
|
|
||||||
|
|
||||||
if (row < ref_row)
|
|
||||||
return -1;
|
|
||||||
if (row == ref_row) {
|
|
||||||
if (col < ref_col)
|
|
||||||
return -1;
|
|
||||||
if (col == ref_col && x < ref_x)
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
terminal_decode_attr(struct terminal *terminal, int row, int col,
|
terminal_decode_attr(struct terminal *terminal, int row, int col,
|
||||||
union decoded_attr *decoded)
|
union decoded_attr *decoded)
|
||||||
{
|
{
|
||||||
struct attr attr;
|
struct attr attr;
|
||||||
int foreground, background, tmp;
|
int foreground, background, tmp;
|
||||||
int start_cmp, end_cmp;
|
|
||||||
|
|
||||||
start_cmp =
|
|
||||||
terminal_compare_position(terminal,
|
|
||||||
terminal->selection_start_x,
|
|
||||||
terminal->selection_start_y,
|
|
||||||
row, col);
|
|
||||||
end_cmp =
|
|
||||||
terminal_compare_position(terminal,
|
|
||||||
terminal->selection_end_x,
|
|
||||||
terminal->selection_end_y,
|
|
||||||
row, col);
|
|
||||||
decoded->attr.s = 0;
|
decoded->attr.s = 0;
|
||||||
if (start_cmp < 0 && end_cmp > 0)
|
if (((row == terminal->selection_start_row &&
|
||||||
decoded->attr.s = 1;
|
col >= terminal->selection_start_col) ||
|
||||||
else if (end_cmp < 0 && start_cmp > 0)
|
row > terminal->selection_start_row) &&
|
||||||
|
((row == terminal->selection_end_row &&
|
||||||
|
col < terminal->selection_end_col) ||
|
||||||
|
row < terminal->selection_end_row))
|
||||||
decoded->attr.s = 1;
|
decoded->attr.s = 1;
|
||||||
|
|
||||||
/* get the attributes for this character cell */
|
/* get the attributes for this character cell */
|
||||||
@@ -2275,6 +2248,86 @@ keyboard_focus_handler(struct window *window,
|
|||||||
window_schedule_redraw(terminal->window);
|
window_schedule_redraw(terminal->window);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int wordsep(int ch)
|
||||||
|
{
|
||||||
|
const char extra[] = "-,./?%&#:_=+@~";
|
||||||
|
|
||||||
|
return ch == 0 || !(isalpha(ch) || isdigit(ch) || strchr(extra, ch));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
recompute_selection(struct terminal *terminal)
|
||||||
|
{
|
||||||
|
struct rectangle allocation;
|
||||||
|
int col, x, width, height;
|
||||||
|
int start_row, end_row;
|
||||||
|
int word_start;
|
||||||
|
int side_margin, top_margin;
|
||||||
|
int start_x, end_x;
|
||||||
|
int cw, ch;
|
||||||
|
union utf8_char *data;
|
||||||
|
|
||||||
|
cw = terminal->extents.max_x_advance;
|
||||||
|
ch = terminal->extents.height;
|
||||||
|
widget_get_allocation(terminal->widget, &allocation);
|
||||||
|
width = terminal->width * cw;
|
||||||
|
height = terminal->height * ch;
|
||||||
|
side_margin = allocation.x + (allocation.width - width) / 2;
|
||||||
|
top_margin = allocation.y + (allocation.height - height) / 2;
|
||||||
|
|
||||||
|
start_row = (terminal->selection_start_y - top_margin) / ch;
|
||||||
|
end_row = (terminal->selection_end_y - top_margin) / ch;
|
||||||
|
if (start_row < end_row ||
|
||||||
|
(start_row == end_row &&
|
||||||
|
terminal->selection_start_x < terminal->selection_end_x)) {
|
||||||
|
terminal->selection_start_row = start_row;
|
||||||
|
terminal->selection_end_row = end_row;
|
||||||
|
start_x = terminal->selection_start_x;
|
||||||
|
end_x = terminal->selection_end_x;
|
||||||
|
} else {
|
||||||
|
terminal->selection_start_row = end_row;
|
||||||
|
terminal->selection_end_row = start_row;
|
||||||
|
start_x = terminal->selection_end_x;
|
||||||
|
end_x = terminal->selection_start_x;
|
||||||
|
}
|
||||||
|
|
||||||
|
x = side_margin + cw / 2;
|
||||||
|
data = terminal_get_row(terminal, terminal->selection_start_row);
|
||||||
|
word_start = 0;
|
||||||
|
for (col = 0; col < terminal->width; col++, x += cw) {
|
||||||
|
if (col == 0 || wordsep(data[col - 1].ch))
|
||||||
|
word_start = col;
|
||||||
|
if (start_x < x)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (terminal->dragging) {
|
||||||
|
case SELECT_LINE:
|
||||||
|
terminal->selection_start_col = 0;
|
||||||
|
break;
|
||||||
|
case SELECT_WORD:
|
||||||
|
terminal->selection_start_col = word_start;
|
||||||
|
break;
|
||||||
|
case SELECT_CHAR:
|
||||||
|
terminal->selection_start_col = col;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
x = side_margin + cw / 2;
|
||||||
|
data = terminal_get_row(terminal, terminal->selection_end_row);
|
||||||
|
for (col = 0; col < terminal->width; col++, x += cw) {
|
||||||
|
if (terminal->dragging == SELECT_CHAR && end_x < x)
|
||||||
|
break;
|
||||||
|
if (terminal->dragging == SELECT_WORD &&
|
||||||
|
end_x < x && wordsep(data[col].ch))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
terminal->selection_end_col = col;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
button_handler(struct widget *widget,
|
button_handler(struct widget *widget,
|
||||||
struct input *input, uint32_t time,
|
struct input *input, uint32_t time,
|
||||||
@@ -2286,15 +2339,25 @@ button_handler(struct widget *widget,
|
|||||||
switch (button) {
|
switch (button) {
|
||||||
case 272:
|
case 272:
|
||||||
if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
|
if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
|
||||||
terminal->dragging = 1;
|
|
||||||
|
if (time - terminal->button_time < 500)
|
||||||
|
terminal->click_count++;
|
||||||
|
else
|
||||||
|
terminal->click_count = 1;
|
||||||
|
|
||||||
|
terminal->button_time = time;
|
||||||
|
terminal->dragging =
|
||||||
|
(terminal->click_count - 1) % 3 + SELECT_CHAR;
|
||||||
|
|
||||||
input_get_position(input,
|
input_get_position(input,
|
||||||
&terminal->selection_start_x,
|
&terminal->selection_start_x,
|
||||||
&terminal->selection_start_y);
|
&terminal->selection_start_y);
|
||||||
terminal->selection_end_x = terminal->selection_start_x;
|
terminal->selection_end_x = terminal->selection_start_x;
|
||||||
terminal->selection_end_y = terminal->selection_start_y;
|
terminal->selection_end_y = terminal->selection_start_y;
|
||||||
widget_schedule_redraw(widget);
|
if (recompute_selection(terminal))
|
||||||
|
widget_schedule_redraw(widget);
|
||||||
} else {
|
} else {
|
||||||
terminal->dragging = 0;
|
terminal->dragging = SELECT_NONE;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -2311,7 +2374,9 @@ motion_handler(struct widget *widget,
|
|||||||
input_get_position(input,
|
input_get_position(input,
|
||||||
&terminal->selection_end_x,
|
&terminal->selection_end_x,
|
||||||
&terminal->selection_end_y);
|
&terminal->selection_end_y);
|
||||||
widget_schedule_redraw(widget);
|
|
||||||
|
if (recompute_selection(terminal))
|
||||||
|
widget_schedule_redraw(widget);
|
||||||
}
|
}
|
||||||
|
|
||||||
return CURSOR_IBEAM;
|
return CURSOR_IBEAM;
|
||||||
|
|||||||
Reference in New Issue
Block a user