Make it easier to handle simple click events

This commit is contained in:
Kovid Goyal 2021-09-28 20:12:55 +05:30
parent a402a3ad12
commit 44bcbc4823
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
5 changed files with 56 additions and 17 deletions

View File

@ -24,7 +24,7 @@ class Mouse(Handler):
def finalize(self) -> None: def finalize(self) -> None:
self.cmd.set_cursor_visible(True) self.cmd.set_cursor_visible(True)
def on_mouse(self, ev: MouseEvent) -> None: def on_mouse_event(self, ev: MouseEvent) -> None:
self.current_mouse_event = ev self.current_mouse_event = ev
self.draw_screen() self.draw_screen()
@ -43,6 +43,10 @@ class Mouse(Handler):
if ev.mods: if ev.mods:
self.print(f'Modifiers: {format_mods(ev.mods)}') self.print(f'Modifiers: {format_mods(ev.mods)}')
def on_interrupt(self) -> None:
self.quit_loop(0)
on_eot = on_interrupt
def main(args: List[str]) -> None: def main(args: List[str]) -> None:
loop = Loop() loop = Loop()

View File

@ -3,16 +3,19 @@
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net> # License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
from collections import deque
from time import monotonic
from types import TracebackType from types import TracebackType
from typing import ( from typing import (
TYPE_CHECKING, Any, Callable, ContextManager, Dict, Optional, Sequence, TYPE_CHECKING, Any, Callable, ContextManager, Deque, Dict, NamedTuple,
Type, Union, cast Optional, Sequence, Type, Union, cast
) )
from kitty.types import DecoratedFunc, ParsedShortcut from kitty.types import DecoratedFunc, ParsedShortcut
from kitty.typing import ( from kitty.typing import (
AbstractEventLoop, BossType, Debug, ImageManagerType, KeyActionType, AbstractEventLoop, BossType, Debug, ImageManagerType, KeyActionType,
KeyEventType, LoopType, MouseEvent, ScreenSize, TermManagerType KeyEventType, LoopType, MouseButton, MouseEvent, ScreenSize,
TermManagerType
) )
from .operations import MouseTracking, pending_update from .operations import MouseTracking, pending_update
@ -21,6 +24,20 @@ if TYPE_CHECKING:
from kitty.file_transmission import FileTransmissionCommand from kitty.file_transmission import FileTransmissionCommand
class ButtonEvent(NamedTuple):
mouse_event: MouseEvent
timestamp: float
def is_click(a: ButtonEvent, b: ButtonEvent) -> bool:
from .loop import EventType
if a.mouse_event.type is not EventType.PRESS or b.mouse_event.type is not EventType.RELEASE:
return False
x = a.mouse_event.cell_x - b.mouse_event.cell_x
y = a.mouse_event.cell_y - b.mouse_event.cell_y
return x*x + y*y <= 4
class Handler: class Handler:
image_manager_class: Optional[Type[ImageManagerType]] = None image_manager_class: Optional[Type[ImageManagerType]] = None
@ -44,6 +61,7 @@ class Handler:
self.debug = debug self.debug = debug
self.cmd = commander(self) self.cmd = commander(self)
self._image_manager = image_manager self._image_manager = image_manager
self._button_events: Dict[MouseButton, Deque[ButtonEvent]] = {}
@property @property
def image_manager(self) -> ImageManagerType: def image_manager(self) -> ImageManagerType:
@ -106,7 +124,27 @@ class Handler:
def on_key(self, key_event: KeyEventType) -> None: def on_key(self, key_event: KeyEventType) -> None:
pass pass
def on_mouse(self, mouse_event: MouseEvent) -> None: def on_mouse_event(self, mouse_event: MouseEvent) -> None:
from .loop import EventType
if mouse_event.type is EventType.MOVE:
self.on_mouse_move(mouse_event)
elif mouse_event.type is EventType.PRESS:
q = self._button_events.setdefault(mouse_event.buttons, deque())
q.append(ButtonEvent(mouse_event, monotonic()))
if len(q) > 5:
q.popleft()
elif mouse_event.type is EventType.RELEASE:
q = self._button_events.setdefault(mouse_event.buttons, deque())
q.append(ButtonEvent(mouse_event, monotonic()))
if len(q) > 5:
q.popleft()
if len(q) > 1 and is_click(q[-2], q[-1]):
self.on_click(mouse_event)
def on_mouse_move(self, mouse_event: MouseEvent) -> None:
pass
def on_click(self, mouse_event: MouseEvent) -> None:
pass pass
def on_interrupt(self) -> None: def on_interrupt(self) -> None:

View File

@ -109,7 +109,7 @@ class TermManager:
class MouseButton(IntFlag): class MouseButton(IntFlag):
LEFT, MIDDLE, RIGHT, FOURTH, FIFTH = 1, 2, 4, 8, 16 NONE, LEFT, MIDDLE, RIGHT, FOURTH, FIFTH = 0, 1, 2, 4, 8, 16
bmap = {0: MouseButton.LEFT, 1: MouseButton.MIDDLE, 2: MouseButton.RIGHT} bmap = {0: MouseButton.LEFT, 1: MouseButton.MIDDLE, 2: MouseButton.RIGHT}
@ -132,7 +132,7 @@ class MouseEvent(NamedTuple):
pixel_x: int pixel_x: int
pixel_y: int pixel_y: int
type: EventType type: EventType
buttons: int buttons: MouseButton
mods: int mods: int
@ -146,7 +146,7 @@ def decode_sgr_mouse(text: str, screen_size: ScreenSize) -> MouseEvent:
m, y_ = y_[-1], y_[:-1] m, y_ = y_[-1], y_[:-1]
cb, x, y = map(int, (cb_, x_, y_)) cb, x, y = map(int, (cb_, x_, y_))
typ = EventType.RELEASE if m == 'm' else (EventType.MOVE if cb & MOTION_INDICATOR else EventType.PRESS) typ = EventType.RELEASE if m == 'm' else (EventType.MOVE if cb & MOTION_INDICATOR else EventType.PRESS)
buttons = 0 buttons: MouseButton = MouseButton.NONE
cb3 = cb & 3 cb3 = cb & 3
if cb3 != 3: if cb3 != 3:
if cb & EXTRA_BUTTON_INDICATOR: if cb & EXTRA_BUTTON_INDICATOR:
@ -310,7 +310,7 @@ class Loop:
except Exception: except Exception:
pass pass
else: else:
self.handler.on_mouse(ev) self.handler.on_mouse_event(ev)
elif q in 'u~ABCDEHFPQRS': elif q in 'u~ABCDEHFPQRS':
if csi == '200~': if csi == '200~':
self.in_bracketed_paste = True self.in_bracketed_paste = True

View File

@ -12,7 +12,7 @@ SessionTab = SessionType = LayoutType = SpecialWindowInstance = None
MarkType = RemoteCommandType = CoreTextFont = FontConfigPattern = None MarkType = RemoteCommandType = CoreTextFont = FontConfigPattern = None
KeyEventType = ImageManagerType = KittyCommonOpts = HandlerType = None KeyEventType = ImageManagerType = KittyCommonOpts = HandlerType = None
GRT_t = GRT_a = GRT_d = GRT_f = GRT_m = GRT_o = GRT_C = None GRT_t = GRT_a = GRT_d = GRT_f = GRT_m = GRT_o = GRT_C = None
ScreenSize = KittensKeyActionType = MouseEvent = AbstractEventLoop = None ScreenSize = KittensKeyActionType = MouseEvent = MouseButton = AbstractEventLoop = None
TermManagerType = LoopType = Debug = GraphicsCommandType = None TermManagerType = LoopType = Debug = GraphicsCommandType = None
CompletedProcess = Tuple CompletedProcess = Tuple
@ -21,5 +21,4 @@ EdgeLiteral = str
PowerlineStyle = str PowerlineStyle = str
MatchType = str MatchType = str
Protocol = object Protocol = object
MouseEvent = dict
OptionsProtocol = object OptionsProtocol = object

View File

@ -9,8 +9,8 @@ from kittens.tui.images import (
GraphicsCommand as GraphicsCommandType, ImageManager as ImageManagerType GraphicsCommand as GraphicsCommandType, ImageManager as ImageManagerType
) )
from kittens.tui.loop import ( from kittens.tui.loop import (
Debug as Debug, Loop as LoopType, MouseEvent as MouseEvent, Debug as Debug, Loop as LoopType, MouseButton as MouseButton,
TermManager as TermManagerType MouseEvent as MouseEvent, TermManager as TermManagerType
) )
from .boss import Boss as BossType from .boss import Boss as BossType
@ -23,9 +23,7 @@ from .fast_data_types import (
) )
from .key_encoding import KeyEvent as KeyEventType from .key_encoding import KeyEvent as KeyEventType
from .layout.base import Layout as LayoutType from .layout.base import Layout as LayoutType
from .options.utils import ( from .options.utils import KeyMap as KeyMap, SequenceMap as SequenceMap
KeyMap as KeyMap, SequenceMap as SequenceMap
)
from .rc.base import RemoteCommand as RemoteCommandType from .rc.base import RemoteCommand as RemoteCommandType
from .session import Session as SessionType, Tab as SessionTab from .session import Session as SessionType, Tab as SessionTab
from .tabs import ( from .tabs import (
@ -56,7 +54,7 @@ __all__ = (
'EdgeLiteral', 'MatchType', 'GRT_a', 'GRT_f', 'GRT_t', 'GRT_o', 'GRT_m', 'GRT_d', 'EdgeLiteral', 'MatchType', 'GRT_a', 'GRT_f', 'GRT_t', 'GRT_o', 'GRT_m', 'GRT_d',
'GraphicsCommandType', 'HandlerType', 'AbstractEventLoop', 'AddressFamily', 'Socket', 'CompletedProcess', 'GraphicsCommandType', 'HandlerType', 'AbstractEventLoop', 'AddressFamily', 'Socket', 'CompletedProcess',
'PopenType', 'Protocol', 'TypedDict', 'MarkType', 'ImageManagerType', 'Debug', 'LoopType', 'MouseEvent', 'PopenType', 'Protocol', 'TypedDict', 'MarkType', 'ImageManagerType', 'Debug', 'LoopType', 'MouseEvent',
'TermManagerType', 'BossType', 'ChildType', 'BadLineType', 'TermManagerType', 'BossType', 'ChildType', 'BadLineType', 'MouseButton',
'KeyActionType', 'KeyMap', 'KittyCommonOpts', 'SequenceMap', 'CoreTextFont', 'WindowSystemMouseEvent', 'KeyActionType', 'KeyMap', 'KittyCommonOpts', 'SequenceMap', 'CoreTextFont', 'WindowSystemMouseEvent',
'FontConfigPattern', 'ScreenType', 'StartupCtx', 'KeyEventType', 'LayoutType', 'PowerlineStyle', 'FontConfigPattern', 'ScreenType', 'StartupCtx', 'KeyEventType', 'LayoutType', 'PowerlineStyle',
'RemoteCommandType', 'SessionType', 'SessionTab', 'SpecialWindowInstance', 'TabType', 'ScreenSize', 'WindowType' 'RemoteCommandType', 'SessionType', 'SessionTab', 'SpecialWindowInstance', 'TabType', 'ScreenSize', 'WindowType'