Show the target of terminal hyperlinks when hovering over them with the mouse
Fixes #5830
This commit is contained in:
parent
b8abdd2b50
commit
f4ac03b791
@ -47,6 +47,8 @@ Detailed list of changes
|
|||||||
- Speed up the ``kitty @`` executable by ~10x reducing the time for typical
|
- Speed up the ``kitty @`` executable by ~10x reducing the time for typical
|
||||||
remote control commands from ~50ms to ~5ms
|
remote control commands from ~50ms to ~5ms
|
||||||
|
|
||||||
|
- Show the target of terminal hyperlinks when hovering over them with the mouse controlled by :opt:`show_hyperlink_targets` (:pull:`5830`)
|
||||||
|
|
||||||
- Keyboard protocol: Remove ``CSI R`` from the allowed encodings of the :kbd:`F3` key as it conflicts with the *Cursor Position Report* escape code (:disc:`5813`)
|
- Keyboard protocol: Remove ``CSI R`` from the allowed encodings of the :kbd:`F3` key as it conflicts with the *Cursor Position Report* escape code (:disc:`5813`)
|
||||||
|
|
||||||
- Allow using the cwd of the original process for :option:`launch --cwd` (:iss:`5672`)
|
- Allow using the cwd of the original process for :option:`launch --cwd` (:iss:`5672`)
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import os
|
|||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
from functools import partial
|
from functools import lru_cache, partial
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
from time import monotonic, sleep
|
from time import monotonic, sleep
|
||||||
from typing import (
|
from typing import (
|
||||||
@ -33,21 +33,19 @@ from .constants import (
|
|||||||
)
|
)
|
||||||
from .fast_data_types import (
|
from .fast_data_types import (
|
||||||
CLOSE_BEING_CONFIRMED, GLFW_MOD_ALT, GLFW_MOD_CONTROL, GLFW_MOD_SHIFT,
|
CLOSE_BEING_CONFIRMED, GLFW_MOD_ALT, GLFW_MOD_CONTROL, GLFW_MOD_SHIFT,
|
||||||
GLFW_MOD_SUPER, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS,
|
GLFW_MOD_SUPER, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, IMPERATIVE_CLOSE_REQUESTED,
|
||||||
IMPERATIVE_CLOSE_REQUESTED, NO_CLOSE_REQUESTED, ChildMonitor, Color,
|
NO_CLOSE_REQUESTED, ChildMonitor, Color, EllipticCurveKey, KeyEvent, SingleKey,
|
||||||
EllipticCurveKey, KeyEvent, SingleKey, add_timer, apply_options_update,
|
add_timer, apply_options_update, background_opacity_of, change_background_opacity,
|
||||||
background_opacity_of, change_background_opacity, change_os_window_state,
|
change_os_window_state, cocoa_hide_app, cocoa_hide_other_apps,
|
||||||
cocoa_hide_app, cocoa_hide_other_apps, cocoa_minimize_os_window,
|
cocoa_minimize_os_window, cocoa_set_menubar_title, create_os_window,
|
||||||
cocoa_set_menubar_title, create_os_window,
|
current_application_quit_request, current_focused_os_window_id, current_os_window,
|
||||||
current_application_quit_request, current_focused_os_window_id,
|
destroy_global_data, focus_os_window, get_boss, get_options, get_os_window_size,
|
||||||
current_os_window, destroy_global_data, focus_os_window, get_boss,
|
global_font_size, last_focused_os_window_id, mark_os_window_for_close,
|
||||||
get_options, get_os_window_size, global_font_size,
|
os_window_font_size, patch_global_colors, redirect_mouse_handling, ring_bell,
|
||||||
last_focused_os_window_id, mark_os_window_for_close, os_window_font_size,
|
|
||||||
patch_global_colors, redirect_mouse_handling, ring_bell,
|
|
||||||
run_with_activation_token, safe_pipe, send_data_to_peer,
|
run_with_activation_token, safe_pipe, send_data_to_peer,
|
||||||
set_application_quit_request, set_background_image, set_boss,
|
set_application_quit_request, set_background_image, set_boss, set_in_sequence_mode,
|
||||||
set_in_sequence_mode, set_options, set_os_window_size, set_os_window_title,
|
set_options, set_os_window_size, set_os_window_title, thread_write,
|
||||||
thread_write, toggle_fullscreen, toggle_maximized, toggle_secure_input,
|
toggle_fullscreen, toggle_maximized, toggle_secure_input,
|
||||||
)
|
)
|
||||||
from .key_encoding import get_name_to_functional_number_map
|
from .key_encoding import get_name_to_functional_number_map
|
||||||
from .keys import get_shortcut, shortcut_matches
|
from .keys import get_shortcut, shortcut_matches
|
||||||
@ -2604,3 +2602,8 @@ class Boss:
|
|||||||
def discard_event(self) -> None:
|
def discard_event(self) -> None:
|
||||||
pass
|
pass
|
||||||
mouse_discard_event = discard_event
|
mouse_discard_event = discard_event
|
||||||
|
|
||||||
|
@lru_cache(maxsize=64)
|
||||||
|
def sanitize_url_for_dispay_to_user(self, url: str) -> str:
|
||||||
|
# TODO: Use punycode, remove percent encoding, etc.
|
||||||
|
return url
|
||||||
|
|||||||
@ -340,8 +340,16 @@ handle_mouse_movement_in_kitty(Window *w, int button, bool mouse_cell_changed) {
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
detect_url(Screen *screen, unsigned int x, unsigned int y) {
|
detect_url(Screen *screen, unsigned int x, unsigned int y) {
|
||||||
if (screen_detect_url(screen, x, y)) mouse_cursor_shape = HAND;
|
int hid = screen_detect_url(screen, x, y);
|
||||||
else set_mouse_cursor_for_screen(screen);
|
screen->current_hyperlink_under_mouse.id = 0;
|
||||||
|
if (hid != 0) {
|
||||||
|
mouse_cursor_shape = HAND;
|
||||||
|
if (hid > 0) {
|
||||||
|
screen->current_hyperlink_under_mouse.id = (hyperlink_id_type)hid;
|
||||||
|
screen->current_hyperlink_under_mouse.x = x;
|
||||||
|
screen->current_hyperlink_under_mouse.y = y;
|
||||||
|
}
|
||||||
|
} else set_mouse_cursor_for_screen(screen);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
|||||||
@ -432,6 +432,10 @@ mouse cursor. By default, all characters that are legal in URLs are allowed.
|
|||||||
'''
|
'''
|
||||||
)
|
)
|
||||||
|
|
||||||
|
opt('show_hyperlink_targets', 'yes', option_type='to_bool', ctype='bool', long_text='''
|
||||||
|
When the mouse hovers over a terminal hyperlink, show the actual URL that will be
|
||||||
|
activated when the hyperlink is clicked.''')
|
||||||
|
|
||||||
opt('copy_on_select', 'no',
|
opt('copy_on_select', 'no',
|
||||||
option_type='copy_on_select',
|
option_type='copy_on_select',
|
||||||
long_text='''
|
long_text='''
|
||||||
|
|||||||
3
kitty/options/parse.py
generated
3
kitty/options/parse.py
generated
@ -1185,6 +1185,9 @@ class Parser:
|
|||||||
def shell_integration(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
|
def shell_integration(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
|
||||||
ans['shell_integration'] = shell_integration(val)
|
ans['shell_integration'] = shell_integration(val)
|
||||||
|
|
||||||
|
def show_hyperlink_targets(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
|
||||||
|
ans['show_hyperlink_targets'] = to_bool(val)
|
||||||
|
|
||||||
def single_window_margin_width(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
|
def single_window_margin_width(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
|
||||||
ans['single_window_margin_width'] = optional_edge_width(val)
|
ans['single_window_margin_width'] = optional_edge_width(val)
|
||||||
|
|
||||||
|
|||||||
15
kitty/options/to-c-generated.h
generated
15
kitty/options/to-c-generated.h
generated
@ -265,6 +265,19 @@ convert_from_opts_url_excluded_characters(PyObject *py_opts, Options *opts) {
|
|||||||
Py_DECREF(ret);
|
Py_DECREF(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
convert_from_python_show_hyperlink_targets(PyObject *val, Options *opts) {
|
||||||
|
opts->show_hyperlink_targets = PyObject_IsTrue(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
convert_from_opts_show_hyperlink_targets(PyObject *py_opts, Options *opts) {
|
||||||
|
PyObject *ret = PyObject_GetAttrString(py_opts, "show_hyperlink_targets");
|
||||||
|
if (ret == NULL) return;
|
||||||
|
convert_from_python_show_hyperlink_targets(ret, opts);
|
||||||
|
Py_DECREF(ret);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
convert_from_python_select_by_word_characters(PyObject *val, Options *opts) {
|
convert_from_python_select_by_word_characters(PyObject *val, Options *opts) {
|
||||||
select_by_word_characters(val, opts);
|
select_by_word_characters(val, opts);
|
||||||
@ -1061,6 +1074,8 @@ convert_opts_from_python_opts(PyObject *py_opts, Options *opts) {
|
|||||||
if (PyErr_Occurred()) return false;
|
if (PyErr_Occurred()) return false;
|
||||||
convert_from_opts_url_excluded_characters(py_opts, opts);
|
convert_from_opts_url_excluded_characters(py_opts, opts);
|
||||||
if (PyErr_Occurred()) return false;
|
if (PyErr_Occurred()) return false;
|
||||||
|
convert_from_opts_show_hyperlink_targets(py_opts, opts);
|
||||||
|
if (PyErr_Occurred()) return false;
|
||||||
convert_from_opts_select_by_word_characters(py_opts, opts);
|
convert_from_opts_select_by_word_characters(py_opts, opts);
|
||||||
if (PyErr_Occurred()) return false;
|
if (PyErr_Occurred()) return false;
|
||||||
convert_from_opts_select_by_word_characters_forward(py_opts, opts);
|
convert_from_opts_select_by_word_characters_forward(py_opts, opts);
|
||||||
|
|||||||
2
kitty/options/types.py
generated
2
kitty/options/types.py
generated
@ -418,6 +418,7 @@ option_names = ( # {{{
|
|||||||
'selection_foreground',
|
'selection_foreground',
|
||||||
'shell',
|
'shell',
|
||||||
'shell_integration',
|
'shell_integration',
|
||||||
|
'show_hyperlink_targets',
|
||||||
'single_window_margin_width',
|
'single_window_margin_width',
|
||||||
'startup_session',
|
'startup_session',
|
||||||
'strip_trailing_spaces',
|
'strip_trailing_spaces',
|
||||||
@ -568,6 +569,7 @@ class Options:
|
|||||||
selection_foreground: typing.Optional[kitty.fast_data_types.Color] = Color(0, 0, 0)
|
selection_foreground: typing.Optional[kitty.fast_data_types.Color] = Color(0, 0, 0)
|
||||||
shell: str = '.'
|
shell: str = '.'
|
||||||
shell_integration: typing.FrozenSet[str] = frozenset({'enabled'})
|
shell_integration: typing.FrozenSet[str] = frozenset({'enabled'})
|
||||||
|
show_hyperlink_targets: bool = True
|
||||||
single_window_margin_width: FloatEdges = FloatEdges(left=-1.0, top=-1.0, right=-1.0, bottom=-1.0)
|
single_window_margin_width: FloatEdges = FloatEdges(left=-1.0, top=-1.0, right=-1.0, bottom=-1.0)
|
||||||
startup_session: typing.Optional[str] = None
|
startup_session: typing.Optional[str] = None
|
||||||
strip_trailing_spaces: choices_for_strip_trailing_spaces = 'never'
|
strip_trailing_spaces: choices_for_strip_trailing_spaces = 'never'
|
||||||
|
|||||||
@ -2722,15 +2722,16 @@ get_url_sentinel(Line *line, index_type url_start) {
|
|||||||
return sentinel;
|
return sentinel;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
int
|
||||||
screen_detect_url(Screen *screen, unsigned int x, unsigned int y) {
|
screen_detect_url(Screen *screen, unsigned int x, unsigned int y) {
|
||||||
bool has_url = false;
|
bool has_url = false;
|
||||||
index_type url_start, url_end = 0;
|
index_type url_start, url_end = 0;
|
||||||
Line *line = screen_visual_line(screen, y);
|
Line *line = screen_visual_line(screen, y);
|
||||||
if (!line || x >= screen->columns) return false;
|
if (!line || x >= screen->columns) return 0;
|
||||||
if (line->cpu_cells[x].hyperlink_id) {
|
hyperlink_id_type hid;
|
||||||
|
if ((hid = line->cpu_cells[x].hyperlink_id)) {
|
||||||
screen_mark_hyperlink(screen, x, y);
|
screen_mark_hyperlink(screen, x, y);
|
||||||
return true;
|
return hid;
|
||||||
}
|
}
|
||||||
char_type sentinel = 0;
|
char_type sentinel = 0;
|
||||||
if (line) {
|
if (line) {
|
||||||
@ -2754,7 +2755,7 @@ screen_detect_url(Screen *screen, unsigned int x, unsigned int y) {
|
|||||||
} else {
|
} else {
|
||||||
screen_mark_url(screen, 0, 0, 0, 0);
|
screen_mark_url(screen, 0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
return has_url;
|
return has_url ? -1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -150,6 +150,10 @@ typedef struct {
|
|||||||
bool is_set;
|
bool is_set;
|
||||||
} last_visited_prompt;
|
} last_visited_prompt;
|
||||||
PyObject *last_reported_cwd;
|
PyObject *last_reported_cwd;
|
||||||
|
struct {
|
||||||
|
hyperlink_id_type id;
|
||||||
|
index_type x, y;
|
||||||
|
} current_hyperlink_under_mouse;
|
||||||
} Screen;
|
} Screen;
|
||||||
|
|
||||||
|
|
||||||
@ -261,7 +265,7 @@ void screen_push_key_encoding_flags(Screen *self, uint32_t val);
|
|||||||
void screen_pop_key_encoding_flags(Screen *self, uint32_t num);
|
void screen_pop_key_encoding_flags(Screen *self, uint32_t num);
|
||||||
uint8_t screen_current_key_encoding_flags(Screen *self);
|
uint8_t screen_current_key_encoding_flags(Screen *self);
|
||||||
void screen_report_key_encoding_flags(Screen *self);
|
void screen_report_key_encoding_flags(Screen *self);
|
||||||
bool screen_detect_url(Screen *screen, unsigned int x, unsigned int y);
|
int screen_detect_url(Screen *screen, unsigned int x, unsigned int y);
|
||||||
int screen_cursor_at_a_shell_prompt(const Screen *);
|
int screen_cursor_at_a_shell_prompt(const Screen *);
|
||||||
bool screen_fake_move_cursor_to_position(Screen *, index_type x, index_type y);
|
bool screen_fake_move_cursor_to_position(Screen *, index_type x, index_type y);
|
||||||
bool screen_send_signal_for_key(Screen *, char key);
|
bool screen_send_signal_for_key(Screen *, char key);
|
||||||
|
|||||||
@ -571,32 +571,38 @@ set_cell_uniforms(float current_inactive_text_alpha, bool force) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static GLfloat
|
static GLfloat
|
||||||
render_window_title(OSWindow *os_window, Screen *screen UNUSED, GLfloat xstart, GLfloat ystart, GLfloat width, Window *window, GLfloat left, GLfloat right) {
|
render_a_bar(OSWindow *os_window, Screen *screen, const CellRenderData *crd, WindowBarData *bar, PyObject *title, bool along_bottom) {
|
||||||
|
GLfloat left = os_window->viewport_width * (crd->gl.xstart + 1.f) / 2.f;
|
||||||
|
GLfloat right = left + os_window->viewport_width * crd->gl.width / 2.f;
|
||||||
unsigned bar_height = os_window->fonts_data->cell_height + 2;
|
unsigned bar_height = os_window->fonts_data->cell_height + 2;
|
||||||
if (!bar_height || right <= left) return 0;
|
if (!bar_height || right <= left) return 0;
|
||||||
unsigned bar_width = (unsigned)ceilf(right - left);
|
unsigned bar_width = (unsigned)ceilf(right - left);
|
||||||
if (!window->title_bar_data.buf || window->title_bar_data.width != bar_width || window->title_bar_data.height != bar_height) {
|
if (!bar->buf || bar->width != bar_width || bar->height != bar_height) {
|
||||||
free(window->title_bar_data.buf);
|
free(bar->buf);
|
||||||
Py_CLEAR(window->title_bar_data.last_drawn_title_object_id);
|
bar->buf = malloc((size_t)4 * bar_width * bar_height);
|
||||||
window->title_bar_data.buf = malloc((size_t)4 * bar_width * bar_height);
|
if (!bar->buf) return 0;
|
||||||
if (!window->title_bar_data.buf) return 0;
|
bar->height = bar_height;
|
||||||
window->title_bar_data.height = bar_height;
|
bar->width = bar_width;
|
||||||
window->title_bar_data.width = bar_width;
|
bar->needs_render = true;
|
||||||
}
|
}
|
||||||
static char title[2048] = {0};
|
|
||||||
if (window->title_bar_data.last_drawn_title_object_id != window->title) {
|
if (bar->last_drawn_title_object_id != title || bar->needs_render) {
|
||||||
snprintf(title, arraysz(title), " %s", PyUnicode_AsUTF8(window->title));
|
static char titlebuf[2048] = {0};
|
||||||
|
if (!title) return 0;
|
||||||
|
snprintf(titlebuf, arraysz(titlebuf), " %s", PyUnicode_AsUTF8(title));
|
||||||
#define RGBCOL(which, fallback) ( 0xff000000 | colorprofile_to_color_with_fallback(screen->color_profile, screen->color_profile->overridden.which, screen->color_profile->configured.which, screen->color_profile->overridden.fallback, screen->color_profile->configured.fallback))
|
#define RGBCOL(which, fallback) ( 0xff000000 | colorprofile_to_color_with_fallback(screen->color_profile, screen->color_profile->overridden.which, screen->color_profile->configured.which, screen->color_profile->overridden.fallback, screen->color_profile->configured.fallback))
|
||||||
if (!draw_window_title(os_window, title, RGBCOL(highlight_fg, default_fg), RGBCOL(highlight_bg, default_bg), window->title_bar_data.buf, bar_width, bar_height)) return 0;
|
if (!draw_window_title(os_window, titlebuf, RGBCOL(highlight_fg, default_fg), RGBCOL(highlight_bg, default_bg), bar->buf, bar_width, bar_height)) return 0;
|
||||||
#undef RGBCOL
|
#undef RGBCOL
|
||||||
window->title_bar_data.last_drawn_title_object_id = window->title;
|
bar->last_drawn_title_object_id = title;
|
||||||
Py_INCREF(window->title_bar_data.last_drawn_title_object_id);
|
Py_INCREF(bar->last_drawn_title_object_id);
|
||||||
}
|
}
|
||||||
static ImageRenderData data = {.group_count=1};
|
static ImageRenderData data = {.group_count=1};
|
||||||
xstart = clamp_position_to_nearest_pixel(xstart, os_window->viewport_width);
|
GLfloat xstart, ystart;
|
||||||
ystart = clamp_position_to_nearest_pixel(ystart, os_window->viewport_height);
|
xstart = clamp_position_to_nearest_pixel(crd->gl.xstart, os_window->viewport_width);
|
||||||
GLfloat height_gl = gl_size(bar_height, os_window->viewport_height);
|
GLfloat height_gl = gl_size(bar_height, os_window->viewport_height);
|
||||||
gpu_data_for_image(&data, xstart, ystart, xstart + width, ystart - height_gl);
|
if (along_bottom) ystart = crd->gl.ystart - crd->gl.height + height_gl;
|
||||||
|
else ystart = clamp_position_to_nearest_pixel(crd->gl.ystart, os_window->viewport_height);
|
||||||
|
gpu_data_for_image(&data, xstart, ystart, xstart + crd->gl.width, ystart - height_gl);
|
||||||
if (!data.texture_id) { glGenTextures(1, &data.texture_id); }
|
if (!data.texture_id) { glGenTextures(1, &data.texture_id); }
|
||||||
glBindTexture(GL_TEXTURE_2D, data.texture_id);
|
glBindTexture(GL_TEXTURE_2D, data.texture_id);
|
||||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||||
@ -604,7 +610,7 @@ render_window_title(OSWindow *os_window, Screen *screen UNUSED, GLfloat xstart,
|
|||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bar_width, bar_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, window->title_bar_data.buf);
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bar_width, bar_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bar->buf);
|
||||||
set_cell_uniforms(1.f, false);
|
set_cell_uniforms(1.f, false);
|
||||||
bind_program(GRAPHICS_PROGRAM);
|
bind_program(GRAPHICS_PROGRAM);
|
||||||
send_graphics_data_to_gpu(1, os_window->gvao_idx, &data);
|
send_graphics_data_to_gpu(1, os_window->gvao_idx, &data);
|
||||||
@ -615,6 +621,25 @@ render_window_title(OSWindow *os_window, Screen *screen UNUSED, GLfloat xstart,
|
|||||||
return height_gl;
|
return height_gl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
draw_hyperlink_target(OSWindow *os_window, Screen *screen, const CellRenderData *crd, Window *window) {
|
||||||
|
WindowBarData *bd = &window->url_target_bar_data;
|
||||||
|
if (bd->hyperlink_id_for_title_object != screen->current_hyperlink_under_mouse.id) {
|
||||||
|
bd->hyperlink_id_for_title_object = screen->current_hyperlink_under_mouse.id;
|
||||||
|
Py_CLEAR(bd->last_drawn_title_object_id);
|
||||||
|
const char *url = get_hyperlink_for_id(screen->hyperlink_pool, bd->hyperlink_id_for_title_object, true);
|
||||||
|
if (url == NULL) url = "";
|
||||||
|
PyObject *h = PyObject_CallMethod(global_state.boss, "sanitize_url_for_dispay_to_user", "s", url);
|
||||||
|
if (h == NULL) { PyErr_Print(); return; }
|
||||||
|
bd->last_drawn_title_object_id = h;
|
||||||
|
Py_INCREF(bd->last_drawn_title_object_id);
|
||||||
|
bd->needs_render = true;
|
||||||
|
}
|
||||||
|
if (bd->last_drawn_title_object_id == NULL) return;
|
||||||
|
const bool along_bottom = screen->lines < 3 || screen->current_hyperlink_under_mouse.y < screen->lines - 2;
|
||||||
|
render_a_bar(os_window, screen, crd, &window->title_bar_data, bd->last_drawn_title_object_id, along_bottom);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
draw_window_logo(ssize_t vao_idx, OSWindow *os_window, const WindowLogoRenderData *wl, const CellRenderData *crd) {
|
draw_window_logo(ssize_t vao_idx, OSWindow *os_window, const WindowLogoRenderData *wl, const CellRenderData *crd) {
|
||||||
if (os_window->live_resize.in_progress) return;
|
if (os_window->live_resize.in_progress) return;
|
||||||
@ -642,7 +667,7 @@ draw_window_number(OSWindow *os_window, Screen *screen, const CellRenderData *cr
|
|||||||
GLfloat title_bar_height = 0;
|
GLfloat title_bar_height = 0;
|
||||||
size_t requested_height = (size_t)(os_window->viewport_height * crd->gl.height / 2.f);
|
size_t requested_height = (size_t)(os_window->viewport_height * crd->gl.height / 2.f);
|
||||||
if (window->title && PyUnicode_Check(window->title) && (requested_height > (os_window->fonts_data->cell_height + 1) * 2)) {
|
if (window->title && PyUnicode_Check(window->title) && (requested_height > (os_window->fonts_data->cell_height + 1) * 2)) {
|
||||||
title_bar_height = render_window_title(os_window, screen, crd->gl.xstart, crd->gl.ystart, crd->gl.width, window, left, right);
|
title_bar_height = render_a_bar(os_window, screen, crd, &window->title_bar_data, window->title, false);
|
||||||
}
|
}
|
||||||
GLfloat ystart = crd->gl.ystart, height = crd->gl.height, xstart = crd->gl.xstart, width = crd->gl.width;
|
GLfloat ystart = crd->gl.ystart, height = crd->gl.height, xstart = crd->gl.xstart, width = crd->gl.width;
|
||||||
if (title_bar_height > 0) {
|
if (title_bar_height > 0) {
|
||||||
@ -933,6 +958,7 @@ draw_cells(ssize_t vao_idx, ssize_t gvao_idx, const ScreenRenderData *srd, float
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (window && screen->display_window_char) draw_window_number(os_window, screen, &crd, window);
|
if (window && screen->display_window_char) draw_window_number(os_window, screen, &crd, window);
|
||||||
|
if (OPT(show_hyperlink_targets) && window && screen->current_hyperlink_under_mouse.id) draw_hyperlink_target(os_window, screen, &crd, window);
|
||||||
}
|
}
|
||||||
// }}}
|
// }}}
|
||||||
|
|
||||||
|
|||||||
@ -335,6 +335,8 @@ destroy_window(Window *w) {
|
|||||||
Py_CLEAR(w->render_data.screen); Py_CLEAR(w->title);
|
Py_CLEAR(w->render_data.screen); Py_CLEAR(w->title);
|
||||||
Py_CLEAR(w->title_bar_data.last_drawn_title_object_id);
|
Py_CLEAR(w->title_bar_data.last_drawn_title_object_id);
|
||||||
free(w->title_bar_data.buf); w->title_bar_data.buf = NULL;
|
free(w->title_bar_data.buf); w->title_bar_data.buf = NULL;
|
||||||
|
Py_CLEAR(w->url_target_bar_data.last_drawn_title_object_id);
|
||||||
|
free(w->url_target_bar_data.buf); w->url_target_bar_data.buf = NULL;
|
||||||
release_gpu_resources_for_window(w);
|
release_gpu_resources_for_window(w);
|
||||||
if (w->window_logo.id) {
|
if (w->window_logo.id) {
|
||||||
decref_window_logo(global_state.all_window_logos, w->window_logo.id);
|
decref_window_logo(global_state.all_window_logos, w->window_logo.id);
|
||||||
|
|||||||
@ -87,6 +87,7 @@ typedef struct {
|
|||||||
struct {
|
struct {
|
||||||
float val; AdjustmentUnit unit;
|
float val; AdjustmentUnit unit;
|
||||||
} underline_position, underline_thickness, strikethrough_position, strikethrough_thickness, cell_width, cell_height, baseline;
|
} underline_position, underline_thickness, strikethrough_position, strikethrough_thickness, cell_width, cell_height, baseline;
|
||||||
|
bool show_hyperlink_targets;
|
||||||
} Options;
|
} Options;
|
||||||
|
|
||||||
typedef struct WindowLogoRenderData {
|
typedef struct WindowLogoRenderData {
|
||||||
@ -126,6 +127,14 @@ typedef struct MousePosition {
|
|||||||
bool in_left_half_of_cell;
|
bool in_left_half_of_cell;
|
||||||
} MousePosition;
|
} MousePosition;
|
||||||
|
|
||||||
|
typedef struct WindowBarData {
|
||||||
|
unsigned width, height;
|
||||||
|
uint8_t *buf;
|
||||||
|
PyObject *last_drawn_title_object_id;
|
||||||
|
hyperlink_id_type hyperlink_id_for_title_object;
|
||||||
|
bool needs_render;
|
||||||
|
} WindowBarData;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
id_type id;
|
id_type id;
|
||||||
bool visible, cursor_visible_at_last_render;
|
bool visible, cursor_visible_at_last_render;
|
||||||
@ -142,11 +151,7 @@ typedef struct {
|
|||||||
ClickQueue click_queues[8];
|
ClickQueue click_queues[8];
|
||||||
monotonic_t last_drag_scroll_at;
|
monotonic_t last_drag_scroll_at;
|
||||||
uint32_t last_special_key_pressed;
|
uint32_t last_special_key_pressed;
|
||||||
struct {
|
WindowBarData title_bar_data, url_target_bar_data;
|
||||||
unsigned width, height;
|
|
||||||
uint8_t *buf;
|
|
||||||
PyObject *last_drawn_title_object_id;
|
|
||||||
} title_bar_data;
|
|
||||||
} Window;
|
} Window;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user