Implement clicking on URLs to open them
This commit is contained in:
parent
a05b64f2fe
commit
63f8fd5929
@ -9,7 +9,7 @@ from threading import Lock
|
||||
|
||||
from .config import build_ansi_color_table
|
||||
from .constants import tab_manager, viewport_size, cell_size, ScreenGeometry, GLuint
|
||||
from .utils import get_logical_dpi, to_color, set_primary_selection
|
||||
from .utils import get_logical_dpi, to_color, set_primary_selection, open_url
|
||||
from .fast_data_types import (
|
||||
glUniform2ui, glUniform4f, glUniform1i, glUniform2f, glDrawArraysInstanced,
|
||||
GL_TRIANGLE_FAN, glEnable, glDisable, GL_BLEND, glDrawArrays, ColorProfile,
|
||||
@ -169,6 +169,8 @@ class Selection:
|
||||
|
||||
class CharGrid:
|
||||
|
||||
url_pat = re.compile('(?:http|https|file|ftp)://\S+', re.IGNORECASE)
|
||||
|
||||
def __init__(self, screen, opts):
|
||||
self.buffer_lock = Lock()
|
||||
self.current_selection = Selection()
|
||||
@ -283,6 +285,27 @@ class CharGrid:
|
||||
if ps and ps.strip():
|
||||
set_primary_selection(ps)
|
||||
|
||||
def has_url_at(self, x, y):
|
||||
x, y = self.cell_for_pos(x, y)
|
||||
l = self.screen_line(y)
|
||||
if l is not None:
|
||||
text = l.as_base_text()
|
||||
for m in self.url_pat.finditer(text):
|
||||
if m.start() <= x < m.end():
|
||||
return True
|
||||
return False
|
||||
|
||||
def click_url(self, x, y):
|
||||
x, y = self.cell_for_pos(x, y)
|
||||
l = self.screen_line(y)
|
||||
if l is not None:
|
||||
text = l.as_base_text()
|
||||
for m in self.url_pat.finditer(text):
|
||||
if m.start() <= x < m.end():
|
||||
url = ''.join(l[i] for i in range(*m.span()))
|
||||
if url:
|
||||
open_url(url, self.opts.open_url_with)
|
||||
|
||||
def screen_line(self, y):
|
||||
' Return the Line object corresponding to the yth line on the rendered screen '
|
||||
if y >= 0 and y < self.screen.lines:
|
||||
|
||||
@ -38,11 +38,45 @@ def to_opacity(x):
|
||||
return max(0.3, min(float(x), 1))
|
||||
|
||||
|
||||
def parse_mods(parts):
|
||||
|
||||
def map_mod(m):
|
||||
return {'CTRL': 'CONTROL', 'CMD': 'CONTROL'}.get(m, m)
|
||||
|
||||
mods = 0
|
||||
for m in parts:
|
||||
try:
|
||||
mods |= getattr(defines, 'GLFW_MOD_' + map_mod(m.upper()))
|
||||
except AttributeError:
|
||||
print('Shortcut: {} has an unknown modifier, ignoring'.format(parts.join('+')), file=sys.stderr)
|
||||
return
|
||||
|
||||
return mods
|
||||
|
||||
|
||||
def parse_key(val, keymap):
|
||||
sc, action = val.partition(' ')[::2]
|
||||
if not sc or not action:
|
||||
return
|
||||
parts = sc.split('+')
|
||||
mods = parse_mods(parts[:-1])
|
||||
key = getattr(defines, 'GLFW_KEY_' + parts[-1].upper(), None)
|
||||
if key is None:
|
||||
print('Shortcut: {} has an unknown key, ignoring'.format(val), file=sys.stderr)
|
||||
return
|
||||
keymap[(mods, key)] = action
|
||||
|
||||
|
||||
def to_open_url_modifiers(val):
|
||||
return parse_mods(val.split('+'))
|
||||
|
||||
|
||||
type_map = {
|
||||
'scrollback_lines': int,
|
||||
'font_size': to_font_size,
|
||||
'cursor_shape': to_cursor_shape,
|
||||
'cursor_opacity': to_opacity,
|
||||
'open_url_modifiers': to_open_url_modifiers,
|
||||
'repaint_delay': int,
|
||||
'window_border_width': float,
|
||||
'wheel_scroll_multiplier': float,
|
||||
@ -58,30 +92,6 @@ for i in range(16):
|
||||
type_map['color%d' % i] = lambda x: to_color(x, validate=True)
|
||||
|
||||
|
||||
def parse_key(val, keymap):
|
||||
sc, action = val.partition(' ')[::2]
|
||||
if not sc or not action:
|
||||
return
|
||||
parts = sc.split('+')
|
||||
|
||||
def map_mod(m):
|
||||
return {'CTRL': 'CONTROL', 'CMD': 'CONTROL'}.get(m, m)
|
||||
|
||||
mods = 0
|
||||
for m in parts[:-1]:
|
||||
try:
|
||||
mods |= getattr(defines, 'GLFW_MOD_' + map_mod(m.upper()))
|
||||
except AttributeError:
|
||||
print('Shortcut: {} has an unknown modifier, ignoring'.format(val), file=sys.stderr)
|
||||
return
|
||||
|
||||
key = getattr(defines, 'GLFW_KEY_' + parts[-1].upper(), None)
|
||||
if key is None:
|
||||
print('Shortcut: {} has an unknown key, ignoring'.format(val), file=sys.stderr)
|
||||
return
|
||||
keymap[(mods, key)] = action
|
||||
|
||||
|
||||
def parse_config(lines):
|
||||
ans = {'keymap': {}}
|
||||
for line in lines:
|
||||
|
||||
@ -1,13 +1,20 @@
|
||||
# vim:fileencoding=utf-8:ft=config
|
||||
|
||||
# The value of the TERM environment variable to set
|
||||
term xterm-kitty
|
||||
# Font family
|
||||
font_family monospace
|
||||
|
||||
# Font size (in pts)
|
||||
font_size 11.0
|
||||
|
||||
# The foreground color
|
||||
foreground #dddddd
|
||||
|
||||
# The background color
|
||||
background #000000
|
||||
|
||||
# The foreground for selections
|
||||
selection_foreground #000000
|
||||
|
||||
# The background for selections
|
||||
selection_background #FFFACD
|
||||
|
||||
@ -28,12 +35,6 @@ cursor_blink_interval 0.5
|
||||
# zero or a negative number to never stop blinking.
|
||||
cursor_stop_blinking_after 15.0
|
||||
|
||||
# Font family
|
||||
font_family monospace
|
||||
|
||||
# Font size (in pts)
|
||||
font_size 11.0
|
||||
|
||||
# Number of lines of history to keep in memory for scrolling back
|
||||
scrollback_lines 2000
|
||||
|
||||
@ -52,6 +53,16 @@ repaint_delay 20
|
||||
# zero or a negative number to disable mouse cursor hiding.
|
||||
mouse_hide_wait 3.0
|
||||
|
||||
# The modifier keys to press when clicking with the mouse on URLs to open the URL
|
||||
open_url_modifiers ctrl+shift
|
||||
|
||||
# The program with which to open URLs that are clicked on. The special value "default" means to
|
||||
# use the operating system's default URL handler.
|
||||
open_url_with default
|
||||
|
||||
# The value of the TERM environment variable to set
|
||||
term xterm-kitty
|
||||
|
||||
# The width (in pts) of window borders. Will be rounded to the nearest number of pixels based on screen resolution.
|
||||
window_border_width 2
|
||||
|
||||
@ -61,6 +72,9 @@ active_border_color #00ff00
|
||||
# The color for the border of inactive windows
|
||||
inactive_border_color #cccccc
|
||||
|
||||
# The 16 terminal colors. There are 8 basic colors, each color has a dull and
|
||||
# bright version.
|
||||
|
||||
# black
|
||||
color0 #000000
|
||||
color8 #4d4d4d
|
||||
|
||||
@ -19,7 +19,7 @@ from .constants import viewport_size, shell_path, appname, set_tab_manager, tab_
|
||||
from .fast_data_types import (
|
||||
glViewport, glBlendFunc, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GLFW_PRESS,
|
||||
GLFW_REPEAT, GLFW_MOUSE_BUTTON_1, glfw_post_empty_event,
|
||||
GLFW_CURSOR_NORMAL, GLFW_CURSOR, GLFW_CURSOR_HIDDEN
|
||||
GLFW_CURSOR_NORMAL, GLFW_CURSOR, GLFW_CURSOR_HIDDEN,
|
||||
)
|
||||
from .fonts import set_font_family
|
||||
from .borders import Borders, BordersProgram
|
||||
@ -385,6 +385,9 @@ class TabManager(Thread):
|
||||
def hide_mouse_cursor(self):
|
||||
self.glfw_window.set_input_mode(GLFW_CURSOR, GLFW_CURSOR_HIDDEN)
|
||||
|
||||
def change_mouse_cursor(self, click=False):
|
||||
self.glfw_window.set_click_cursor(click)
|
||||
|
||||
def start_cursor_blink(self):
|
||||
self.cursor_blinking = True
|
||||
if self.opts.cursor_stop_blinking_after > 0:
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
import re
|
||||
import os
|
||||
import signal
|
||||
import shlex
|
||||
import subprocess
|
||||
import ctypes
|
||||
from collections import namedtuple
|
||||
@ -270,3 +271,12 @@ def set_primary_selection(text):
|
||||
p = subprocess.Popen(['xsel', '-i', '-p'], stdin=subprocess.PIPE)
|
||||
p.stdin.write(text), p.stdin.close()
|
||||
p.wait()
|
||||
|
||||
|
||||
def open_url(url, program='default'):
|
||||
if program == 'default':
|
||||
cmd = ['xdg-open']
|
||||
else:
|
||||
cmd = shlex.split(program)
|
||||
cmd.append(url)
|
||||
subprocess.Popen(cmd).wait()
|
||||
|
||||
@ -156,6 +156,8 @@ class Window:
|
||||
x, y = max(0, x - self.geometry.left), max(0, y - self.geometry.top)
|
||||
self.char_grid.update_drag(action == GLFW_PRESS, x, y)
|
||||
if action == GLFW_RELEASE:
|
||||
if mods == self.char_grid.opts.open_url_modifiers:
|
||||
self.char_grid.click_url(x, y)
|
||||
self.click_queue.append(monotonic())
|
||||
self.dispatch_multi_click(x, y)
|
||||
elif button == GLFW_MOUSE_BUTTON_MIDDLE:
|
||||
@ -167,8 +169,11 @@ class Window:
|
||||
x, y = self.char_grid.cell_for_pos(x, y)
|
||||
|
||||
def on_mouse_move(self, window, x, y):
|
||||
x, y = max(0, x - self.geometry.left), max(0, y - self.geometry.top)
|
||||
if self.char_grid.current_selection.in_progress:
|
||||
self.char_grid.update_drag(None, max(0, x - self.geometry.left), max(0, y - self.geometry.top))
|
||||
self.char_grid.update_drag(None, x, y)
|
||||
tm = tab_manager()
|
||||
tm.queue_ui_action(tab_manager().change_mouse_cursor, self.char_grid.has_url_at(x, y))
|
||||
|
||||
def on_mouse_scroll(self, window, x, y):
|
||||
handle_event = (
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user