Highlight links with a underline on mouseover

This commit is contained in:
Kovid Goyal 2017-09-14 21:24:37 +05:30
parent 464291bbb1
commit 53ea5c678d
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
6 changed files with 75 additions and 26 deletions

View File

@ -4,6 +4,7 @@ uniform vec4 steps; // xstart, ystart, dx, dy
uniform vec2 sprite_layout; // dx, dy
uniform ivec2 color_indices; // which color to use as fg and which as bg
uniform uvec4 default_colors; // The default colors
uniform uvec4 url_range; // The range for the currently highlighted URL (start_x, end_x, start_y, end_y)
uniform ColorTable {
uint color_table[256]; // The color table
};
@ -71,10 +72,16 @@ vec3 apply_selection(vec3 color, uint which) {
return is_selected * color_to_vec(which) + (1.0 - is_selected) * color;
}
uint in_range(uvec4 range, uint x, uint y) {
if (range[0] <= x && x <= range[1] && range[2] <= y && y <= range[3]) return ONE;
return ZERO;
}
void main() {
uint instance_id = uint(gl_InstanceID);
uint r = instance_id / dimensions.x;
uint c = instance_id - r * dimensions.x;
uint in_url = in_range(url_range, c, r);
float left = steps[0] + c * steps[2];
float top = steps[1] - r * steps[3];
vec2 xpos = vec2(left, left + steps[2]);
@ -87,10 +94,11 @@ void main() {
uint fg = colors[color_indices[reverse]];
uint bg = colors[color_indices[ONE - reverse]];
uint resolved_fg = as_color(fg, default_colors[color_indices[0]]);
uint decoration = colors[2];
uint underline;
foreground = apply_selection(color_to_vec(resolved_fg), default_colors[2]);
background = apply_selection(to_color(bg, default_colors[color_indices[1]]), default_colors[3]);
decoration_fg = to_color(decoration, resolved_fg);
underline_pos = to_sprite_pos(pos, (text_attrs >> 26) & DECORATION_MASK, ZERO, ZERO);
if (in_url == ONE) { underline = ONE; decoration_fg = color_to_vec(uint(255)); }
else { underline = (text_attrs >> 26) & DECORATION_MASK; decoration_fg = to_color(colors[2], resolved_fg); }
underline_pos = to_sprite_pos(pos, underline, ZERO, ZERO);
strike_pos = to_sprite_pos(pos, ((text_attrs >> 31) & STRIKE_MASK) * THREE, ZERO, ZERO);
}

View File

@ -234,6 +234,7 @@ typedef struct {
uint32_t utf8_state, utf8_codepoint, *g0_charset, *g1_charset, *g_charset;
Selection selection;
SelectionBoundary last_rendered_selection_start, last_rendered_selection_end;
Selection url_range;
bool use_latin1, selection_updated_once, is_dirty, scroll_changed;
Cursor *cursor;
SavepointBuffer main_savepoints, alt_savepoints;

View File

@ -149,14 +149,33 @@ drag_scroll(Window *w) {
}
static inline void
detect_url(Window *w, 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);
has_url = url_end > url_start ? true : false;
}
if (has_url) {
mouse_cursor_shape = HAND;
screen_mark_url(screen, url_start, w->mouse_cell_y, url_end, w->mouse_cell_y);
} else {
mouse_cursor_shape = BEAM;
screen_mark_url(screen, 0, 0, 0, 0);
}
}
HANDLER(handle_move_event) {
unsigned int x, y;
if (!cell_for_pos(w, &x, &y)) return;
Line *line = screen_visual_line(w->render_data.screen, y);
mouse_cursor_shape = (line && line_url_start_at(line, x) < line->xnum) ? HAND : BEAM;
Screen *screen = w->render_data.screen;
detect_url(w, screen, x, y);
bool mouse_cell_changed = x != w->mouse_cell_x || y != w->mouse_cell_y ? true : false;
w->mouse_cell_x = x; w->mouse_cell_y = y;
Screen *screen = w->render_data.screen;
bool handle_in_kitty = (
(screen->modes.mouse_tracking_mode == ANY_MODE ||
(screen->modes.mouse_tracking_mode == MOTION_MODE && button >= 0)) &&

View File

@ -172,6 +172,7 @@ screen_resize(Screen *self, unsigned int lines, unsigned int columns) {
init_tabstops(self->alt_tabstops, self->columns);
self->is_dirty = true;
self->selection = EMPTY_SELECTION;
self->url_range = EMPTY_SELECTION;
if (index_after_resize) screen_index(self);
return true;
@ -1121,7 +1122,9 @@ screen_update_cell_data(Screen *self, void *address, size_t UNUSED sz) {
linebuf_init_line(self->linebuf, y - self->scrolled_by);
update_line_data(self->linebuf->line, y, address);
}
if (selection_must_be_cleared) self->selection = EMPTY_SELECTION;
if (selection_must_be_cleared) {
self->selection = EMPTY_SELECTION; self->url_range = EMPTY_SELECTION;
}
}
static inline bool
@ -1143,13 +1146,12 @@ selection_coord(Screen *self, unsigned int x, unsigned int y, unsigned int ydelt
}
}
static inline void
selection_limits_(Screen *self, SelectionBoundary *left, SelectionBoundary *right) {
SelectionBoundary a, b;
selection_coord(self, self->selection.start_x, self->selection.start_y, self->selection.start_scrolled_by, &a);
selection_coord(self, self->selection.end_x, self->selection.end_y, self->selection.end_scrolled_by, &b);
if (a.y < b.y || (a.y == b.y && a.x <= b.x)) { *left = a; *right = b; }
else { *left = b; *right = a; }
#define selection_limits_(which, left, right) { \
SelectionBoundary a, b; \
selection_coord(self, self->which.start_x, self->which.start_y, self->which.start_scrolled_by, &a); \
selection_coord(self, self->which.end_x, self->which.end_y, self->which.end_scrolled_by, &b); \
if (a.y < b.y || (a.y == b.y && a.x <= b.x)) { *(left) = a; *(right) = b; } \
else { *(left) = b; *(right) = a; } \
}
static inline Line*
@ -1171,7 +1173,7 @@ screen_apply_selection(Screen *self, void *address, size_t size) {
#define end (self->last_rendered_selection_end)
float *data = address;
memset(data, 0, size);
selection_limits_(self, &start, &end);
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; }
@ -1186,6 +1188,14 @@ screen_apply_selection(Screen *self, void *address, size_t size) {
#undef end
}
void
screen_url_range(Screen *self, unsigned int *start_x, unsigned int *start_y, unsigned int *end_x, unsigned int *end_y) {
SelectionBoundary start, end;
selection_limits_(url_range, &start, &end);
if (is_selection_empty(self, start.x, start.y, end.x, end.y)) { *start_y = self->lines; *end_y = self->lines; *start_x = self->columns; *end_x = self->columns; }
else { *start_x = start.x; *start_y = start.y; *end_x = end.x; *end_y = end.y; }
}
// }}}
// Python interface {{{
@ -1314,7 +1324,7 @@ change_scrollback_size(Screen *self, PyObject *args) {
static PyObject*
text_for_selection(Screen *self) {
SelectionBoundary start, end;
selection_limits_(self, &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);
@ -1410,7 +1420,7 @@ scroll(Screen *self, PyObject *args) {
bool
screen_is_selection_dirty(Screen *self) {
SelectionBoundary start, end;
selection_limits_(self, &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;
return false;
}
@ -1422,6 +1432,13 @@ screen_start_selection(Screen *self, index_type x, index_type y) {
#undef A
}
void
screen_mark_url(Screen *self, index_type start_x, index_type start_y, index_type end_x, index_type end_y) {
#define A(attr, val) self->url_range.attr = val;
A(start_x, start_x); A(end_x, end_x); A(start_y, start_y); A(end_y, end_y); A(start_scrolled_by, self->scrolled_by); A(end_scrolled_by, self->scrolled_by);
#undef A
}
void
screen_update_selection(Screen *self, index_type x, index_type y, bool ended) {
self->selection.end_x = x; self->selection.end_y = y; self->selection.end_scrolled_by = self->scrolled_by;

View File

@ -73,6 +73,8 @@ 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, unsigned int *start_x, unsigned int *start_y, unsigned int *limit_x, unsigned int *limit_y);
void screen_mark_url(Screen *self, index_type start_x, index_type start_y, index_type end_x, index_type end_y);
#define DECLARE_CH_SCREEN_HANDLER(name) void screen_##name(Screen *screen);
DECLARE_CH_SCREEN_HANDLER(bell)
DECLARE_CH_SCREEN_HANDLER(backspace)

View File

@ -552,7 +552,7 @@ destroy_sprite_map() {
// Cell {{{
enum CellUniforms { CELL_dimensions, CELL_default_colors, CELL_color_indices, CELL_steps, CELL_sprites, CELL_sprite_layout, CELL_color_table, NUM_CELL_UNIFORMS };
enum CellUniforms { CELL_dimensions, CELL_default_colors, CELL_color_indices, CELL_steps, CELL_sprites, CELL_sprite_layout, CELL_url_range, CELL_color_table, NUM_CELL_UNIFORMS };
static GLint cell_uniform_locations[NUM_CELL_UNIFORMS] = {0};
static GLint cell_color_table_stride = 0, cell_color_table_offset = 0, cell_color_table_size = 0, cell_color_table_block_index = 0;
@ -569,6 +569,7 @@ init_cell_program() {
else SET_LOC(steps);
else SET_LOC(sprites);
else SET_LOC(sprite_layout);
else SET_LOC(url_range);
else if (strcmp(p->uniforms[i].name, "color_table[0]") == 0) { ctable_idx = i; cell_uniform_locations[CELL_color_table] = p->uniforms[i].location; }
else { fatal("Unknown uniform in cell program: %s", p->uniforms[i].name); }
}
@ -637,6 +638,10 @@ draw_cells_impl(ssize_t vao_idx, GLfloat xstart, GLfloat ystart, GLfloat dx, GLf
glUniform4ui(UL(default_colors), COLOR(default_fg), COLOR(default_bg), COLOR(highlight_fg), COLOR(highlight_bg));
check_gl();
#undef COLOR
GLuint start_x, start_y, end_x, end_y;
screen_url_range(screen, &start_x, &start_y, &end_x, &end_y);
glUniform4ui(UL(url_range), start_x, end_x, start_y, end_y);
check_gl();
glUniform1i(UL(sprites), sprite_map_unit);
check_gl();
unsigned int x, y, z;
@ -767,14 +772,11 @@ compile_program(PyObject UNUSED *self, PyObject *args) {
if (programs[which].id != 0) { PyErr_SetString(PyExc_ValueError, "program already compiled"); return NULL; }
programs[which].id = glCreateProgram();
check_gl();
vertex_shader_id = compile_shader(GL_VERTEX_SHADER, vertex_shader);
fragment_shader_id = compile_shader(GL_FRAGMENT_SHADER, fragment_shader);
glAttachShader(programs[which].id, vertex_shader_id);
check_gl();
glAttachShader(programs[which].id, fragment_shader_id);
check_gl();
glLinkProgram(programs[which].id);
check_gl();
vertex_shader_id = compile_shader(GL_VERTEX_SHADER, vertex_shader); check_gl();
fragment_shader_id = compile_shader(GL_FRAGMENT_SHADER, fragment_shader); check_gl();
glAttachShader(programs[which].id, vertex_shader_id); check_gl();
glAttachShader(programs[which].id, fragment_shader_id); check_gl();
glLinkProgram(programs[which].id); check_gl();
GLint ret = GL_FALSE;
glGetProgramiv(programs[which].id, GL_LINK_STATUS, &ret);
if (ret != GL_TRUE) {