Minor fixes and increased debugging verbosity (which necessitated some minor tweaks to timers)
This commit is contained in:
parent
80ee62f1b9
commit
04f9073f9d
|
|
@ -230,6 +230,9 @@ class BaseKeyParser(QObject):
|
|||
self._mode.name)
|
||||
log.keyboard.debug(prefix + msg)
|
||||
|
||||
def get_do_log(self) -> bool:
|
||||
return self._do_log
|
||||
|
||||
def _match_key(self, sequence: keyutils.KeySequence) -> MatchResult:
|
||||
"""Try to match a given keystring with any bound keychain.
|
||||
|
||||
|
|
@ -311,7 +314,7 @@ class BaseKeyParser(QObject):
|
|||
self._debug_log(f"Got key: {info!r} (dry_run {dry_run})")
|
||||
|
||||
# Modifier keys should be previously handled by modeman
|
||||
if keyutils.is_modifier_key(key):
|
||||
if info.is_modifier_key():
|
||||
self._debug_log("Ignoring, only modifier")
|
||||
return QKeySequence.SequenceMatch.NoMatch
|
||||
|
||||
|
|
@ -433,6 +436,8 @@ class BaseKeyParser(QObject):
|
|||
raise utils.Unreachable("Invalid match value {!r}".format(
|
||||
result.match_type))
|
||||
|
||||
return result.match_type
|
||||
|
||||
@config.change_filter('bindings')
|
||||
def _on_config_changed(self) -> None:
|
||||
self._read_config()
|
||||
|
|
|
|||
|
|
@ -565,10 +565,10 @@ class QueuedKeyEventPair:
|
|||
def to_events(self) -> Sequence[QKeyEvent]:
|
||||
"""Get a QKeyEvent from this QueuedEvent."""
|
||||
if self.key_info_release is None:
|
||||
return (self.key_info_press.to_event(QEvent.KeyPress),)
|
||||
return (self.key_info_press.to_event(QEvent.Type.KeyPress),)
|
||||
else:
|
||||
return (self.key_info_press.to_event(QEvent.KeyPress),
|
||||
self.key_info_release.to_event(QEvent.KeyRelease))
|
||||
return (self.key_info_press.to_event(QEvent.Type.KeyPress),
|
||||
self.key_info_release.to_event(QEvent.Type.KeyRelease))
|
||||
|
||||
|
||||
class KeySequence:
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
"""Mode manager (per window) which handles the current keyboard mode."""
|
||||
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
from qutebrowser.qt.widgets import QApplication
|
||||
|
||||
import functools
|
||||
import dataclasses
|
||||
|
|
@ -278,7 +278,8 @@ class ModeManager(QObject):
|
|||
"""
|
||||
curmode = self.mode
|
||||
parser = self.parsers[curmode]
|
||||
if curmode != usertypes.KeyMode.insert:
|
||||
do_log = parser.get_do_log()
|
||||
if do_log:
|
||||
log.modes.debug("got keypress in mode {} - delegating to "
|
||||
"{}".format(curmode, utils.qualname(parser)))
|
||||
|
||||
|
|
@ -286,11 +287,14 @@ class ModeManager(QObject):
|
|||
if parser.allow_forward and (not dry_run) and (not had_empty_queue):
|
||||
# Immediately record the event so that parser.handle may forward if
|
||||
# appropriate from its logic.
|
||||
if do_log:
|
||||
log.modes.debug("Enqueuing key event due to non-empty queue: "
|
||||
"{}".format(event))
|
||||
self._partial_match_events.append(
|
||||
keyutils.QueuedKeyEventPair.from_event_press(event))
|
||||
|
||||
if keyutils.is_modifier_key(Qt.Key(event.key())):
|
||||
if curmode != usertypes.KeyMode.insert:
|
||||
if keyutils.KeyInfo.from_event(event).is_modifier_key():
|
||||
if do_log:
|
||||
log.modes.debug("Ignoring, only modifier")
|
||||
if not dry_run:
|
||||
# Since this is a NoMatch without a call to parser.handle, we
|
||||
|
|
@ -304,30 +308,44 @@ class ModeManager(QObject):
|
|||
if match == QKeySequence.SequenceMatch.ExactMatch:
|
||||
filter_this = True
|
||||
if not dry_run:
|
||||
if do_log:
|
||||
log.modes.debug("Stopping partial timer.")
|
||||
self._stop_partial_timer()
|
||||
if do_log:
|
||||
log.modes.debug("Clearing partial match events.")
|
||||
self.clear_partial_match_events()
|
||||
elif match == QKeySequence.SequenceMatch.PartialMatch:
|
||||
filter_this = True
|
||||
if parser.allow_forward and (not dry_run):
|
||||
if had_empty_queue:
|
||||
# Begin recording partial match events
|
||||
if do_log:
|
||||
log.modes.debug("Enqueuing key event as first entry "
|
||||
"in an empty queue: {}".format(event))
|
||||
self._partial_match_events.append(
|
||||
keyutils.QueuedKeyEventPair.from_event_press(event))
|
||||
if do_log:
|
||||
log.modes.debug("Starting partial timer.")
|
||||
self._start_partial_timer()
|
||||
elif not had_empty_queue:
|
||||
# Since partial events were recorded, this event must be filtered.
|
||||
# Since a NoMatch was found, this event has already been forwarded
|
||||
filter_this = True
|
||||
if not dry_run:
|
||||
if do_log:
|
||||
log.modes.debug("Stopping partial timer.")
|
||||
self._stop_partial_timer()
|
||||
else:
|
||||
key_info = keyutils.KeyInfo.from_event(event)
|
||||
filter_this = self._should_filter_event(key_info, parser)
|
||||
|
||||
if not filter_this and not dry_run:
|
||||
if do_log:
|
||||
log.modes.debug("Adding release event for pass: "
|
||||
"{}".format(event))
|
||||
self._releaseevents_to_pass.add(keyutils.KeyEvent.from_event(event))
|
||||
|
||||
if curmode != usertypes.KeyMode.insert:
|
||||
if do_log:
|
||||
if machinery.IS_QT5: # FIXME:v4 needed for Qt 5 typing
|
||||
ignored_modifiers = [
|
||||
cast(Qt.KeyboardModifiers, Qt.KeyboardModifier.NoModifier),
|
||||
|
|
@ -365,7 +383,11 @@ class ModeManager(QObject):
|
|||
"""
|
||||
# handle like matching KeyPress
|
||||
keyevent = keyutils.KeyEvent.from_event(event)
|
||||
do_log = (self.mode in self.parsers) and \
|
||||
self.parsers[self.mode].get_do_log()
|
||||
if keyevent in self._releaseevents_to_pass:
|
||||
if do_log:
|
||||
log.modes.debug("Passing release event: {}".format(keyevent))
|
||||
self._releaseevents_to_pass.remove(keyevent)
|
||||
filter_this = False
|
||||
else:
|
||||
|
|
@ -375,7 +397,7 @@ class ModeManager(QObject):
|
|||
if match_event.add_event_release(event):
|
||||
break
|
||||
filter_this = True
|
||||
if self.mode != usertypes.KeyMode.insert:
|
||||
if do_log:
|
||||
log.modes.debug("filter: {}".format(filter_this))
|
||||
return filter_this
|
||||
|
||||
|
|
@ -413,6 +435,7 @@ class ModeManager(QObject):
|
|||
raise ValueError("Can't forward partial key: No keyparser for "
|
||||
"mode {}".format(mode))
|
||||
parser = self.parsers[mode]
|
||||
do_log = parser.get_do_log()
|
||||
if not self._partial_match_events:
|
||||
if parser.allow_forward:
|
||||
log.modes.warning("Attempting to forward for mode {} "
|
||||
|
|
@ -423,7 +446,7 @@ class ModeManager(QObject):
|
|||
match_event = self._partial_match_events.pop(0)
|
||||
if parser.allow_forward and (not
|
||||
self._should_filter_event(match_event.key_info_press, parser)):
|
||||
if mode != usertypes.KeyMode.insert:
|
||||
if do_log:
|
||||
log.modes.debug("Forwarding partial match event in mode "
|
||||
"{}.".format(mode))
|
||||
text_actual = str(match_event.key_info_press)
|
||||
|
|
@ -447,22 +470,33 @@ class ModeManager(QObject):
|
|||
window=self._win_id)
|
||||
send_event = functools.partial(QApplication.sendEvent, widget)
|
||||
for event_ in match_event.to_events():
|
||||
if do_log:
|
||||
log.modes.debug("Sending event {}".format(event_))
|
||||
send_event(event_)
|
||||
if not match_event.is_released():
|
||||
if do_log:
|
||||
log.modes.debug("Adding release event for pass: "
|
||||
"{}".format(match_event.key_event))
|
||||
self._releaseevents_to_pass.add(match_event.key_event)
|
||||
|
||||
@pyqtSlot(usertypes.KeyMode)
|
||||
def forward_all_partial_match_events(self, mode: usertypes.KeyMode, *,
|
||||
stop_timer: bool = False) -> None:
|
||||
stop_timer: bool = False,
|
||||
is_timeout: bool = False) -> None:
|
||||
"""Forward all partial match events for a given mode.
|
||||
|
||||
Args:
|
||||
mode: The mode from which the forwarded match is.
|
||||
stop_timer: If true, stop the partial timer (and any nested timers)
|
||||
as well. Default is False.
|
||||
is_timeout: True if this invocation is the result of a timeout.
|
||||
"""
|
||||
log.modes.debug("Forwarding all partial matches.")
|
||||
do_log = (mode in self.parsers) and self.parsers[mode].get_do_log()
|
||||
if do_log:
|
||||
log.modes.debug(f"Forwarding all partial matches ({is_timeout=}).")
|
||||
if stop_timer:
|
||||
if do_log:
|
||||
log.modes.debug("Stopping partial timer.")
|
||||
self._stop_partial_timer()
|
||||
if mode in self.parsers:
|
||||
parser = self.parsers[mode]
|
||||
|
|
@ -494,7 +528,8 @@ class ModeManager(QObject):
|
|||
except TypeError:
|
||||
pass
|
||||
self._partial_timer.timeout.connect(functools.partial(
|
||||
self.forward_all_partial_match_events, self.mode))
|
||||
self.forward_all_partial_match_events, self.mode,
|
||||
is_timeout=True))
|
||||
self._partial_timer.start()
|
||||
|
||||
def _stop_partial_timer(self) -> None:
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ Module attributes:
|
|||
|
||||
import traceback
|
||||
import enum
|
||||
import functools
|
||||
from typing import TYPE_CHECKING, List
|
||||
from collections.abc import Sequence
|
||||
|
||||
|
|
@ -154,8 +155,8 @@ class HintKeyParser(basekeyparser.BaseKeyParser):
|
|||
self.clear_partial_match_events)
|
||||
self._partial_timer = usertypes.Timer(self, 'partial-match')
|
||||
self._partial_timer.setSingleShot(True)
|
||||
self._partial_timer.timeout.connect(
|
||||
self.forward_all_partial_match_events)
|
||||
self._partial_timer.timeout.connect(functools.partial(
|
||||
self.forward_all_partial_match_events, is_timeout=True))
|
||||
|
||||
def _handle_filter_key(self, e: QKeyEvent) -> QKeySequence.SequenceMatch:
|
||||
"""Handle keys for string filtering."""
|
||||
|
|
@ -206,11 +207,14 @@ class HintKeyParser(basekeyparser.BaseKeyParser):
|
|||
if not had_empty_queue:
|
||||
# Immediately record the event so that parser.handle may forward if
|
||||
# appropriate from its logic.
|
||||
self._debug_log("Enqueuing key event due to non-empty queue: "
|
||||
"{}".format(e))
|
||||
self._partial_match_events.append(
|
||||
keyutils.QueuedKeyEventPair.from_event_press(e))
|
||||
|
||||
result = self._command_parser.handle(e)
|
||||
if result == QKeySequence.SequenceMatch.ExactMatch:
|
||||
self._debug_log("Stopping partial timer.")
|
||||
self._stop_partial_timer()
|
||||
self.clear_partial_match_events()
|
||||
log.keyboard.debug("Handling key via command parser")
|
||||
|
|
@ -220,11 +224,15 @@ class HintKeyParser(basekeyparser.BaseKeyParser):
|
|||
log.keyboard.debug("Handling key via command parser")
|
||||
if had_empty_queue:
|
||||
# Begin recording partial match events
|
||||
self._debug_log("Enqueuing key event as first entry in an "
|
||||
"empty queue: {}".format(e))
|
||||
self._partial_match_events.append(
|
||||
keyutils.QueuedKeyEventPair.from_event_press(e))
|
||||
self._debug_log("Staring partial timer.")
|
||||
self._start_partial_timer()
|
||||
return result
|
||||
elif not had_empty_queue:
|
||||
self._debug_log("Stopping partial timer.")
|
||||
self._stop_partial_timer()
|
||||
# It's unclear exactly what the return here should be. The safest
|
||||
# bet seems to be PartialMatch as it won't clear the unused
|
||||
|
|
@ -275,14 +283,18 @@ class HintKeyParser(basekeyparser.BaseKeyParser):
|
|||
|
||||
@pyqtSlot()
|
||||
def forward_all_partial_match_events(self, *,
|
||||
stop_timer: bool = False) -> None:
|
||||
stop_timer: bool = False,
|
||||
is_timeout: bool = False) -> None:
|
||||
"""Forward all partial match events.
|
||||
|
||||
Args:
|
||||
stop_timer: If true, stop the partial timer as well. Default is False.
|
||||
stop_timer: If true, stop the partial timer as well. Default is
|
||||
False.
|
||||
is_timeout: True if this invocation is the result of a timeout.
|
||||
"""
|
||||
self._debug_log("Forwarding all partial matches.")
|
||||
self._debug_log(f"Forwarding all partial matches ({is_timeout=}).")
|
||||
if stop_timer:
|
||||
self._debug_log("Stopping partial timer.")
|
||||
self._stop_partial_timer()
|
||||
if self._partial_match_events:
|
||||
while self._partial_match_events:
|
||||
|
|
|
|||
|
|
@ -192,38 +192,38 @@ def test_release_forwarding(modeman_with_basekeyparser):
|
|||
info_b = keyutils.KeyInfo(Qt.Key.Key_B, Qt.KeyboardModifier.NoModifier)
|
||||
info_c = keyutils.KeyInfo(Qt.Key.Key_C, Qt.KeyboardModifier.NoModifier)
|
||||
|
||||
res = mwb.handle_event(info_b.to_event(QEvent.KeyPress))
|
||||
res = mwb.handle_event(info_b.to_event(QEvent.Type.KeyPress))
|
||||
assert (True, 1, 0) == helper_data(res, mwb)
|
||||
assert not mwb._partial_match_events[0].is_released()
|
||||
res = mwb.handle_event(info_c.to_event(QEvent.KeyPress))
|
||||
res = mwb.handle_event(info_c.to_event(QEvent.Type.KeyPress))
|
||||
assert (True, 0, 2) == helper_data(res, mwb)
|
||||
res = mwb.handle_event(info_b.to_event(QEvent.KeyRelease))
|
||||
res = mwb.handle_event(info_b.to_event(QEvent.Type.KeyRelease))
|
||||
assert (False, 0, 1) == helper_data(res, mwb)
|
||||
res = mwb.handle_event(info_c.to_event(QEvent.KeyRelease))
|
||||
res = mwb.handle_event(info_c.to_event(QEvent.Type.KeyRelease))
|
||||
assert (False, 0, 0) == helper_data(res, mwb)
|
||||
|
||||
info_y = keyutils.KeyInfo(Qt.Key.Key_Y, Qt.KeyboardModifier.NoModifier)
|
||||
|
||||
res = mwb.handle_event(info_b.to_event(QEvent.KeyPress))
|
||||
res = mwb.handle_event(info_b.to_event(QEvent.Type.KeyPress))
|
||||
assert (True, 1, 0) == helper_data(res, mwb)
|
||||
assert not mwb._partial_match_events[0].is_released()
|
||||
res = mwb.handle_event(info_y.to_event(QEvent.KeyPress))
|
||||
res = mwb.handle_event(info_y.to_event(QEvent.Type.KeyPress))
|
||||
assert (True, 2, 0) == helper_data(res, mwb)
|
||||
assert not mwb._partial_match_events[0].is_released()
|
||||
assert not mwb._partial_match_events[1].is_released()
|
||||
res = mwb.handle_event(info_y.to_event(QEvent.KeyRelease))
|
||||
res = mwb.handle_event(info_y.to_event(QEvent.Type.KeyRelease))
|
||||
assert (True, 2, 0) == helper_data(res, mwb)
|
||||
assert not mwb._partial_match_events[0].is_released()
|
||||
assert mwb._partial_match_events[1].is_released()
|
||||
res = mwb.handle_event(info_c.to_event(QEvent.KeyPress))
|
||||
res = mwb.handle_event(info_c.to_event(QEvent.Type.KeyPress))
|
||||
assert (True, 0, 2) == helper_data(res, mwb)
|
||||
res = mwb.handle_event(info_c.to_event(QEvent.KeyRelease))
|
||||
res = mwb.handle_event(info_c.to_event(QEvent.Type.KeyRelease))
|
||||
assert (False, 0, 1) == helper_data(res, mwb)
|
||||
res = mwb.handle_event(info_b.to_event(QEvent.KeyRelease))
|
||||
res = mwb.handle_event(info_b.to_event(QEvent.Type.KeyRelease))
|
||||
assert (False, 0, 0) == helper_data(res, mwb)
|
||||
|
||||
res = mwb.handle_event(info_b.to_event(QEvent.KeyPress))
|
||||
res = mwb.handle_event(info_b.to_event(QEvent.Type.KeyPress))
|
||||
assert (True, 1, 0) == helper_data(res, mwb)
|
||||
assert not mwb._partial_match_events[0].is_released()
|
||||
res = mwb.handle_event(info_c.to_event(QEvent.KeyRelease))
|
||||
res = mwb.handle_event(info_c.to_event(QEvent.Type.KeyRelease))
|
||||
assert (True, 1, 0) == helper_data(res, mwb)
|
||||
|
|
|
|||
Loading…
Reference in New Issue