When hovering over a URL, highlight the URL fully even if it continues over multiple lines. Note that URL detection has not changed. A URL will be detected only if the mouse hovers over the line containing the start of the URL.

This commit is contained in:
Kovid Goyal 2017-12-13 11:11:13 +05:30
parent d25c0d42bc
commit 95683c658b
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
8 changed files with 84 additions and 68 deletions

View File

@ -291,6 +291,9 @@ class Boss:
if url:
open_url(url, self.opts.open_url_with)
def open_url_lines(self, lines):
self.open_url(''.join(lines))
def destroy(self):
self.shutting_down = True
self.child_monitor.shutdown_monitor()

View File

@ -10,7 +10,7 @@ layout(std140) uniform CellRenderData {
int color1, color2;
uint xnum, ynum, cursor_x, cursor_y, cursor_w, url_xl, url_yl, url_xr, url_yr;
uint xnum, ynum, cursor_x, cursor_y, cursor_w;
uint color_table[256];
};
@ -18,7 +18,7 @@ layout(std140) uniform CellRenderData {
// Have to use fixed locations here as all variants of the cell program share the same VAO
layout(location=0) in uvec3 colors;
layout(location=1) in uvec4 sprite_coords;
layout(location=2) in float is_selected;
layout(location=2) in uint is_selected;
@ -108,11 +108,6 @@ vec3 choose_color(float q, vec3 a, vec3 b) {
return mix(b, a, q);
}
float in_range(uint x, uint y) {
if (url_yl == y && url_xl <= x && x <= url_xr) return 1.0;
return 0.0;
}
float is_cursor(uint x, uint y) {
if (y == cursor_y && (x == cursor_x || x == cursor_w)) return 1.0;
return 0.0;
@ -159,9 +154,9 @@ void main() {
uint resolved_fg = resolve_color(colors[fg_index], default_colors[fg_index]);
foreground = color_to_vec(resolved_fg);
// Selection
foreground = choose_color(is_selected, color_to_vec(highlight_fg), foreground);
foreground = choose_color(float(is_selected & ONE), color_to_vec(highlight_fg), foreground);
// Underline and strike through (rendered via sprites)
float in_url = in_range(c, r);
float in_url = float((is_selected & TWO) >> 1);
decoration_fg = choose_color(in_url, color_to_vec(url_color), to_color(colors[2], resolved_fg));
underline_pos = choose_color(in_url, to_sprite_pos(pos, url_style, ZERO, ZERO), to_sprite_pos(pos, (text_attrs >> 2) & DECORATION_MASK, ZERO, ZERO));
strike_pos = to_sprite_pos(pos, ((text_attrs >> 7) & STRIKE_MASK) * FOUR, ZERO, ZERO);
@ -189,11 +184,11 @@ void main() {
#if defined(SPECIAL) || defined(SIMPLE)
// Selection and cursor
bg = choose_color(is_selected, color_to_vec(highlight_bg), bg);
bg = choose_color(float(is_selected & ONE), color_to_vec(highlight_bg), bg);
background = choose_color(cursor, color_to_vec(cursor_color), bg);
#ifdef SPECIAL
// bg_alpha should be 1 if cursor/selection otherwise 0
bg_alpha = mix(0.0, 1.0, step(0.5, is_selected + cursor));
bg_alpha = mix(0.0, 1.0, step(0.5, float(is_selected & ONE) + cursor));
#endif
#endif

View File

@ -135,9 +135,9 @@ line_url_start_at(Line *self, index_type x) {
}
index_type
line_url_end_at(Line *self, index_type x) {
line_url_end_at(Line *self, index_type x, bool check_short) {
index_type ans = x;
if (x >= self->xnum || self->xnum <= MIN_URL_LEN + 3) return 0;
if (x >= self->xnum || (check_short && self->xnum <= MIN_URL_LEN + 3)) return 0;
while (ans < self->xnum && is_url_char(self->cells[ans].ch)) ans++;
ans--;
while (ans > x && can_strip_from_end_of_url(self->cells[ans].ch)) ans--;
@ -153,7 +153,7 @@ url_start_at(Line *self, PyObject *x) {
static PyObject*
url_end_at(Line *self, PyObject *x) {
#define url_end_at_doc "url_end_at(x) -> Return the end cell number for a URL containing x or 0 if not found"
return PyLong_FromUnsignedLong((unsigned long)line_url_end_at(self, PyLong_AsUnsignedLong(x)));
return PyLong_FromUnsignedLong((unsigned long)line_url_end_at(self, PyLong_AsUnsignedLong(x), true));
}
// }}}

View File

@ -51,7 +51,7 @@ void line_set_char(Line *, unsigned int , uint32_t , unsigned int , Cursor *, bo
void line_right_shift(Line *, unsigned int , unsigned int );
void line_add_combining_char(Line *, uint32_t , unsigned int );
index_type line_url_start_at(Line *self, index_type x);
index_type line_url_end_at(Line *self, index_type x);
index_type line_url_end_at(Line *self, index_type x, bool);
index_type line_as_ansi(Line *self, Py_UCS4 *buf, index_type buflen);
unsigned int line_length(Line *self);
size_t cell_as_unicode(Cell *cell, bool include_cc, Py_UCS4 *buf, char_type);

View File

@ -142,20 +142,33 @@ drag_scroll(Window *w, OSWindow *frame) {
return false;
}
static inline void
extend_url(Screen *screen, Line *line, index_type *x, index_type *y) {
while(true) {
if (*x != line->xnum - 1) break;
line = screen_visual_line(screen, *y + 1);
if (!line) break; // we deliberately allow non-continued lines as some programs, like mutt split URLs with newlines at line boundaries
index_type new_x = line_url_end_at(line, 0, false);
if (!new_x) break;
*y += 1; *x = new_x;
}
}
static inline void
detect_url(Window *w, Screen *screen, unsigned int x, unsigned int y) {
detect_url(Screen *screen, unsigned int x, unsigned int y) {
bool has_url = false;
index_type url_start, url_end = 0;
Line *line = screen_visual_line(screen, y);
if (line) {
url_start = line_url_start_at(line, x);
if (url_start < line->xnum) url_end = line_url_end_at(line, x);
if (url_start < line->xnum) url_end = line_url_end_at(line, x, true);
has_url = url_end > url_start;
}
if (has_url) {
mouse_cursor_shape = HAND;
screen_mark_url(screen, url_start, w->mouse_cell_y, url_end, w->mouse_cell_y);
index_type y_extended = y;
extend_url(screen, line, &url_end, &y_extended);
screen_mark_url(screen, url_start, y, url_end, y_extended);
} else {
mouse_cursor_shape = BEAM;
screen_mark_url(screen, 0, 0, 0, 0);
@ -173,7 +186,7 @@ HANDLER(handle_move_event) {
}
if (!cell_for_pos(w, &x, &y)) return;
Screen *screen = w->render_data.screen;
detect_url(w, screen, x, y);
detect_url(screen, x, y);
bool mouse_cell_changed = x != w->mouse_cell_x || y != w->mouse_cell_y;
w->mouse_cell_x = x; w->mouse_cell_y = y;
bool handle_in_kitty = (
@ -237,16 +250,8 @@ HANDLER(add_click) {
static inline void
open_url(Window *w) {
Line *line = screen_visual_line(w->render_data.screen, w->mouse_cell_y);
if (line) {
index_type start = line_url_start_at(line, w->mouse_cell_x);
if (start < line->xnum) {
index_type end = line_url_end_at(line, w->mouse_cell_x);
if (end > start) {
call_boss(open_url, "N", unicode_in_range(line, start, end + 1, true, 0));
}
}
}
Screen *screen = w->render_data.screen;
screen_open_url(screen);
}
HANDLER(handle_button_event) {

View File

@ -180,6 +180,7 @@ screen_resize(Screen *self, unsigned int lines, unsigned int columns) {
self->is_dirty = true;
self->selection = EMPTY_SELECTION;
self->url_range = EMPTY_SELECTION;
self->selection_updated_once = false;
// Ensure cursor is on the correct line
self->cursor->x = 0;
@ -1225,33 +1226,57 @@ visual_line_(Screen *self, index_type y) {
return self->linebuf->line;
}
void
screen_apply_selection(Screen *self, void *address, size_t size) {
#define start (self->last_rendered_selection_start)
#define end (self->last_rendered_selection_end)
float *data = address;
memset(data, 0, size);
selection_limits_(selection, &start, &end);
self->last_selection_scrolled_by = self->scrolled_by;
self->selection_updated_once = true;
if (is_selection_empty(self, start.x, start.y, end.x, end.y)) { return; }
for (index_type y = start.y; y <= end.y; y++) {
static inline void
apply_selection(Screen *self, uint8_t *data, SelectionBoundary *start, SelectionBoundary *end, uint8_t set_mask) {
if (is_selection_empty(self, start->x, start->y, end->x, end->y)) return;
for (index_type y = start->y; y <= end->y; y++) {
Line *line = visual_line_(self, y);
index_type xlimit = xlimit_for_line(line);
if (y == end.y) xlimit = MIN(end.x + 1, xlimit);
float *line_start = data + self->columns * y;
for (index_type x = (y == start.y ? start.x : 0); x < xlimit; x++) line_start[x] = 1.0;
if (y == end->y) xlimit = MIN(end->x + 1, xlimit);
uint8_t *line_start = data + self->columns * y;
for (index_type x = (y == start->y ? start->x : 0); x < xlimit; x++) line_start[x] |= set_mask;
}
#undef start
#undef end
}
void
screen_url_range(Screen *self, uint32_t *data) {
screen_apply_selection(Screen *self, void *address, size_t size) {
memset(address, 0, size);
self->last_selection_scrolled_by = self->scrolled_by;
self->selection_updated_once = true;
selection_limits_(selection, &self->last_rendered_selection_start, &self->last_rendered_selection_end);
apply_selection(self, address, &self->last_rendered_selection_start, &self->last_rendered_selection_end, 1);
selection_limits_(url_range, &self->last_rendered_url_start, &self->last_rendered_url_end);
apply_selection(self, address, &self->last_rendered_url_start, &self->last_rendered_url_end, 2);
}
static inline PyObject*
text_for_range(Screen *self, SelectionBoundary start, SelectionBoundary end) {
Py_ssize_t i = 0, num_of_lines = end.y - start.y + 1;
PyObject *ans = PyTuple_New(num_of_lines);
if (ans == NULL) return PyErr_NoMemory();
for (index_type y = start.y; y <= end.y; y++, i++) {
Line *line = visual_line_(self, y);
index_type xlimit = xlimit_for_line(line);
if (y == end.y) xlimit = MIN(end.x + 1, xlimit);
index_type xstart = (y == start.y ? start.x : 0);
char leading_char = i > 0 && !line->continued ? '\n' : 0;
PyObject *text = unicode_in_range(line, xstart, xlimit, true, leading_char);
if (text == NULL) { Py_DECREF(ans); return PyErr_NoMemory(); }
PyTuple_SET_ITEM(ans, i, text);
}
return ans;
}
bool
screen_open_url(Screen *self) {
SelectionBoundary start, end;
selection_limits_(url_range, &start, &end);
if (is_selection_empty(self, start.x, start.y, end.x, end.y)) { *(data + 1) = self->lines; *(data + 3) = self->lines; *data = self->columns; *(data + 2) = self->columns; }
else { *data = start.x; *(data+1) = start.y; *(data + 2) = end.x; *(data + 3) = end.y; }
if (is_selection_empty(self, start.x, start.y, end.x, end.y)) return false;
PyObject *text = text_for_range(self, start, end);
if (text) { call_boss(open_url_lines, "(O)", text); Py_CLEAR(text); }
else PyErr_Print();
return true;
}
// }}}
@ -1410,20 +1435,7 @@ text_for_selection(Screen *self) {
SelectionBoundary start, end;
selection_limits_(selection, &start, &end);
if (is_selection_empty(self, start.x, start.y, end.x, end.y)) return PyTuple_New(0);
Py_ssize_t i = 0, num_of_lines = end.y - start.y + 1;
PyObject *ans = PyTuple_New(num_of_lines);
if (ans == NULL) return PyErr_NoMemory();
for (index_type y = start.y; y <= end.y; y++, i++) {
Line *line = visual_line_(self, y);
index_type xlimit = xlimit_for_line(line);
if (y == end.y) xlimit = MIN(end.x + 1, xlimit);
index_type xstart = (y == start.y ? start.x : 0);
char leading_char = i > 0 && !line->continued ? '\n' : 0;
PyObject *text = unicode_in_range(line, xstart, xlimit, true, leading_char);
if (text == NULL) { Py_DECREF(ans); return PyErr_NoMemory(); }
PyTuple_SET_ITEM(ans, i, text);
}
return ans;
return text_for_range(self, start, end);
}
bool
@ -1506,6 +1518,8 @@ screen_is_selection_dirty(Screen *self) {
SelectionBoundary start, end;
selection_limits_(selection, &start, &end);
if (self->last_selection_scrolled_by != self->scrolled_by || start.x != self->last_rendered_selection_start.x || start.y != self->last_rendered_selection_start.y || end.x != self->last_rendered_selection_end.x || end.y != self->last_rendered_selection_end.y || !self->selection_updated_once) return true;
selection_limits_(url_range, &start, &end);
if (start.x != self->last_rendered_url_start.x || start.y != self->last_rendered_url_start.y || end.x != self->last_rendered_url_end.x || end.y != self->last_rendered_url_end.y) return true;
return false;
}

View File

@ -34,7 +34,7 @@ typedef struct {
id_type window_id;
uint32_t utf8_state, utf8_codepoint, *g0_charset, *g1_charset, *g_charset;
Selection selection;
SelectionBoundary last_rendered_selection_start, last_rendered_selection_end;
SelectionBoundary last_rendered_selection_start, last_rendered_selection_end, last_rendered_url_start, last_rendered_url_end;
Selection url_range;
bool use_latin1, selection_updated_once, is_dirty, scroll_changed;
Cursor *cursor;
@ -130,9 +130,9 @@ void screen_update_selection(Screen *self, index_type x, index_type y, bool ende
bool screen_history_scroll(Screen *self, int amt, bool upwards);
Line* screen_visual_line(Screen *self, index_type y);
unsigned long screen_current_char_width(Screen *self);
void screen_url_range(Screen *self, uint32_t *);
void screen_mark_url(Screen *self, index_type start_x, index_type start_y, index_type end_x, index_type end_y);
void screen_handle_graphics_command(Screen *self, const GraphicsCommand *cmd, const uint8_t *payload);
bool screen_open_url(Screen*);
#define DECLARE_CH_SCREEN_HANDLER(name) void screen_##name(Screen *screen);
DECLARE_CH_SCREEN_HANDLER(bell)
DECLARE_CH_SCREEN_HANDLER(backspace)

View File

@ -177,7 +177,7 @@ create_cell_vao() {
A1(colors, 3, GL_UNSIGNED_INT, fg);
add_buffer_to_vao(vao_idx, GL_ARRAY_BUFFER);
A(is_selected, 1, GL_FLOAT, NULL, 0);
A(is_selected, 1, GL_UNSIGNED_BYTE, NULL, 0);
size_t bufnum = add_buffer_to_vao(vao_idx, GL_UNIFORM_BUFFER);
alloc_vao_buffer(vao_idx, cell_program_layouts[CELL_PROGRAM].render_data.size, bufnum, GL_STREAM_DRAW);
@ -204,7 +204,7 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, G
GLint color1, color2;
GLuint xnum, ynum, cursor_x, cursor_y, cursor_w, url_xl, url_yl, url_xr, url_yr;
GLuint xnum, ynum, cursor_x, cursor_y, cursor_w;
};
static struct CellRenderData *rd;
@ -222,7 +222,6 @@ cell_update_uniform_block(ssize_t vao_idx, Screen *screen, int uniform_buffer, G
rd->cursor_w = rd->cursor_x + MAX(1, screen_current_char_width(screen)) - 1;
rd->xnum = screen->columns; rd->ynum = screen->lines;
screen_url_range(screen, &rd->url_xl);
rd->xstart = xstart; rd->ystart = ystart; rd->dx = dx; rd->dy = dy;
unsigned int x, y, z;
@ -255,7 +254,7 @@ cell_prepare_to_render(ssize_t vao_idx, ssize_t gvao_idx, Screen *screen, GLfloa
}
if (screen_is_selection_dirty(screen)) {
sz = sizeof(GLfloat) * screen->lines * screen->columns;
sz = screen->lines * screen->columns;
address = alloc_and_map_vao_buffer(vao_idx, sz, selection_buffer, GL_STREAM_DRAW, GL_WRITE_ONLY);
screen_apply_selection(screen, address, sz);
unmap_vao_buffer(vao_idx, selection_buffer); address = NULL;