mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-28 11:11:47 +02:00
Implement double/triple clicks to select word/line
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
# vim:fileencoding=utf-8
|
# vim:fileencoding=utf-8
|
||||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
import re
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from ctypes import addressof, memmove, sizeof
|
from ctypes import addressof, memmove, sizeof
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
@@ -264,22 +265,24 @@ class CharGrid:
|
|||||||
return int(x // cell_size.width), int(y // cell_size.height)
|
return int(x // cell_size.width), int(y // cell_size.height)
|
||||||
|
|
||||||
def update_drag(self, is_press, x, y):
|
def update_drag(self, is_press, x, y):
|
||||||
with self.buffer_lock:
|
x, y = self.cell_for_pos(x, y)
|
||||||
x, y = self.cell_for_pos(x, y)
|
if 0 <= x < self.screen.columns and 0 <= y < self.screen.lines:
|
||||||
if is_press:
|
ps = None
|
||||||
self.current_selection.start_x = self.current_selection.end_x = x
|
with self.buffer_lock:
|
||||||
self.current_selection.start_y = self.current_selection.end_y = y
|
if is_press:
|
||||||
self.current_selection.start_scrolled_by = self.current_selection.end_scrolled_by = self.scrolled_by
|
self.current_selection.start_x = self.current_selection.end_x = x
|
||||||
self.current_selection.in_progress = True
|
self.current_selection.start_y = self.current_selection.end_y = y
|
||||||
elif self.current_selection.in_progress:
|
self.current_selection.start_scrolled_by = self.current_selection.end_scrolled_by = self.scrolled_by
|
||||||
self.current_selection.end_x = x
|
self.current_selection.in_progress = True
|
||||||
self.current_selection.end_y = y
|
elif self.current_selection.in_progress:
|
||||||
self.current_selection.end_scrolled_by = self.scrolled_by
|
self.current_selection.end_x = x
|
||||||
if is_press is False:
|
self.current_selection.end_y = y
|
||||||
self.current_selection.in_progress = False
|
self.current_selection.end_scrolled_by = self.scrolled_by
|
||||||
text = self.text_for_selection()
|
if is_press is False:
|
||||||
if text and text.strip():
|
self.current_selection.in_progress = False
|
||||||
set_primary_selection(text)
|
ps = self.text_for_selection()
|
||||||
|
if ps and ps.strip():
|
||||||
|
set_primary_selection(ps)
|
||||||
|
|
||||||
def screen_line(self, y):
|
def screen_line(self, y):
|
||||||
' Return the Line object corresponding to the yth line on the rendered screen '
|
' Return the Line object corresponding to the yth line on the rendered screen '
|
||||||
@@ -291,6 +294,39 @@ class CharGrid:
|
|||||||
else:
|
else:
|
||||||
return self.screen.line(y)
|
return self.screen.line(y)
|
||||||
|
|
||||||
|
def multi_click(self, count, x, y):
|
||||||
|
x, y = self.cell_for_pos(x, y)
|
||||||
|
line = self.screen_line(y)
|
||||||
|
if line is not None and 0 <= x < self.screen.columns and count in (2, 3):
|
||||||
|
s = self.current_selection
|
||||||
|
s.start_scrolled_by = s.end_scrolled_by = self.scrolled_by
|
||||||
|
s.start_y = s.end_y = y
|
||||||
|
s.in_progress = False
|
||||||
|
if count == 3:
|
||||||
|
for i in range(self.screen.columns):
|
||||||
|
if line[i] != ' ':
|
||||||
|
s.start_x = i
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
s.start_x = 0
|
||||||
|
for i in range(self.screen.columns):
|
||||||
|
c = self.screen.columns - 1 - i
|
||||||
|
if line[c] != ' ':
|
||||||
|
s.end_x = c
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
s.end_x = self.screen.columns - 1
|
||||||
|
elif count == 2:
|
||||||
|
i = x
|
||||||
|
pat = re.compile(r'\w')
|
||||||
|
while i >= 0 and pat.match(line[i]) is not None:
|
||||||
|
i -= 1
|
||||||
|
s.start_x = i if i == x else i + 1
|
||||||
|
i = x
|
||||||
|
while i < self.screen.columns and pat.match(line[i]) is not None:
|
||||||
|
i += 1
|
||||||
|
s.end_x = i if i == x else i - 1
|
||||||
|
|
||||||
def text_for_selection(self, sel=None):
|
def text_for_selection(self, sel=None):
|
||||||
start, end = (sel or self.current_selection).limits(self.scrolled_by)
|
start, end = (sel or self.current_selection).limits(self.scrolled_by)
|
||||||
lines = []
|
lines = []
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ type_map = {
|
|||||||
'repaint_delay': int,
|
'repaint_delay': int,
|
||||||
'window_border_width': float,
|
'window_border_width': float,
|
||||||
'wheel_scroll_multiplier': float,
|
'wheel_scroll_multiplier': float,
|
||||||
|
'click_interval': float,
|
||||||
}
|
}
|
||||||
|
|
||||||
for name in 'foreground background cursor active_border_color inactive_border_color selection_foreground selection_background'.split():
|
for name in 'foreground background cursor active_border_color inactive_border_color selection_foreground selection_background'.split():
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ scrollback_lines 2000
|
|||||||
# Wheel scroll multiplier (modify the amount scrolled by the mouse wheel)
|
# Wheel scroll multiplier (modify the amount scrolled by the mouse wheel)
|
||||||
wheel_scroll_multiplier 5.0
|
wheel_scroll_multiplier 5.0
|
||||||
|
|
||||||
|
# The interval between successive clicks to detect double/triple clicks (in seconds)
|
||||||
|
click_interval 0.5
|
||||||
|
|
||||||
# Delay (in milliseconds) between screen updates. Decreasing it, increases fps
|
# Delay (in milliseconds) between screen updates. Decreasing it, increases fps
|
||||||
# at the cost of more CPU usage. The default value yields ~50fps which is more
|
# at the cost of more CPU usage. The default value yields ~50fps which is more
|
||||||
# that sufficient for most uses.
|
# that sufficient for most uses.
|
||||||
|
|||||||
@@ -4,7 +4,9 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import weakref
|
import weakref
|
||||||
|
from collections import deque
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
from time import monotonic
|
||||||
|
|
||||||
import glfw
|
import glfw
|
||||||
import glfw_constants
|
import glfw_constants
|
||||||
@@ -21,6 +23,7 @@ class Window:
|
|||||||
|
|
||||||
def __init__(self, tab, child, opts, args):
|
def __init__(self, tab, child, opts, args):
|
||||||
self.tabref = weakref.ref(tab)
|
self.tabref = weakref.ref(tab)
|
||||||
|
self.click_queue = deque(maxlen=3)
|
||||||
self.geometry = WindowGeometry(0, 0, 0, 0, 0, 0)
|
self.geometry = WindowGeometry(0, 0, 0, 0, 0, 0)
|
||||||
self.needs_layout = True
|
self.needs_layout = True
|
||||||
self.title = appname
|
self.title = appname
|
||||||
@@ -113,11 +116,23 @@ class Window:
|
|||||||
def request_capabilities(self, q):
|
def request_capabilities(self, q):
|
||||||
self.write_to_child(get_capabilities(q))
|
self.write_to_child(get_capabilities(q))
|
||||||
|
|
||||||
|
def dispatch_multi_click(self, x, y):
|
||||||
|
if len(self.click_queue) > 2 and self.click_queue[-1] - self.click_queue[-3] <= 2 * self.opts.click_interval:
|
||||||
|
self.char_grid.multi_click(3, x, y)
|
||||||
|
glfw.glfwPostEmptyEvent()
|
||||||
|
elif len(self.click_queue) > 1 and self.click_queue[-1] - self.click_queue[-2] <= self.opts.click_interval:
|
||||||
|
self.char_grid.multi_click(2, x, y)
|
||||||
|
glfw.glfwPostEmptyEvent()
|
||||||
|
|
||||||
def on_mouse_button(self, window, button, action, mods):
|
def on_mouse_button(self, window, button, action, mods):
|
||||||
ignore_mouse_mode = mods == glfw_constants.GLFW_MOD_SHIFT or not self.screen.mouse_button_tracking_enabled()
|
ignore_mouse_mode = mods == glfw_constants.GLFW_MOD_SHIFT or not self.screen.mouse_button_tracking_enabled()
|
||||||
if button == glfw_constants.GLFW_MOUSE_BUTTON_1 and ignore_mouse_mode:
|
if button == glfw_constants.GLFW_MOUSE_BUTTON_1 and ignore_mouse_mode:
|
||||||
x, y = glfw.glfwGetCursorPos(window)
|
x, y = glfw.glfwGetCursorPos(window)
|
||||||
self.char_grid.update_drag(action == glfw_constants.GLFW_PRESS, max(0, x - self.geometry.left), max(0, y - self.geometry.top))
|
x, y = max(0, x - self.geometry.left), max(0, y - self.geometry.top)
|
||||||
|
self.char_grid.update_drag(action == glfw_constants.GLFW_PRESS, x, y)
|
||||||
|
if action == glfw_constants.GLFW_RELEASE:
|
||||||
|
self.click_queue.append(monotonic())
|
||||||
|
self.dispatch_multi_click(x, y)
|
||||||
if action == glfw_constants.GLFW_RELEASE:
|
if action == glfw_constants.GLFW_RELEASE:
|
||||||
if button == glfw_constants.GLFW_MOUSE_BUTTON_MIDDLE:
|
if button == glfw_constants.GLFW_MOUSE_BUTTON_MIDDLE:
|
||||||
self.paste_from_selection()
|
self.paste_from_selection()
|
||||||
|
|||||||
Reference in New Issue
Block a user