Handle real modifier events in `ChartView`

Ignore modifier-only key events before action dispatch so Qt's real
Ctrl-G sequence reaches the chart-local gap-overlay binding.

Drive a shown and focused widget through `QtBot.keyPress()` to cover
the production event relay without synthetic event dispatch.

(this patch was generated in some part by `opencode` using `gpt-5.6-sol` (`openai`))
wkt/replay_provider_e2e
Gud Boi 2026-08-31 22:12:23 -04:00
parent b1f372bad1
commit a36c444b78
2 changed files with 27 additions and 10 deletions

View File

@ -226,6 +226,16 @@ async def handle_viewmode_kb_inputs(
if mods == Qt.KeyboardModifier.ControlModifier: if mods == Qt.KeyboardModifier.ControlModifier:
ctrl = True ctrl = True
# Real key chords deliver modifier-only events before the
# bound key. Nothing below handles those as actions.
if key in {
Qt.Key.Key_Alt,
Qt.Key.Key_Control,
Qt.Key.Key_Meta,
Qt.Key.Key_Shift,
}:
continue
# UI REPL-shell, with ctrl-p (for "pause") # UI REPL-shell, with ctrl-p (for "pause")
if ( if (
ctrl ctrl

View File

@ -10,7 +10,6 @@ from types import SimpleNamespace
import msgspec import msgspec
import numpy as np import numpy as np
from PyQt6.QtGui import QKeyEvent
from PyQt6.QtWidgets import QGraphicsScene from PyQt6.QtWidgets import QGraphicsScene
import pyqtgraph as pg import pyqtgraph as pg
import pytest import pytest
@ -36,7 +35,6 @@ from piker.ui._interaction import (
) )
from piker.ui.qt import ( from piker.ui.qt import (
QApplication, QApplication,
QEvent,
QPointF, QPointF,
QRectF, QRectF,
Qt, Qt,
@ -630,14 +628,16 @@ def test_ctrl_g_event_renders_real_history_overlay(
) -> None: ) -> None:
''' '''
Route a real Qt Ctrl-G event into a rendered history gap layer. Route real QtBot Ctrl-G input into a rendered history gap layer.
Calling the toggle helper directly does not prove that Qt event Calling the toggle helper directly does not prove that Qt event
filtering, `KeyboardMsg` conversion or the asynchronous view-mode filtering, `KeyboardMsg` conversion or the asynchronous view-mode
handler recognizes the configured binding. Install the production handler recognizes the configured binding. Install the production
`EventRelay` on a real widget, send one offscreen `QKeyEvent`, `EventRelay` on a shown and focused widget, press Ctrl-G through
`QtBot.keyPress()` so Qt emits its modifier-only event before G,
and wait until the handler blocks again. The resulting manager and wait until the handler blocks again. The resulting manager
and scene state prove the complete local keyboard path ran. and scene state prove the complete local keyboard path ran
without crashing or directly dispatching a synthetic event.
''' '''
ds: SimpleNamespace ds: SimpleNamespace
@ -670,6 +670,13 @@ def test_ctrl_g_event_renders_real_history_overlay(
cursor=SimpleNamespace(in_query_mode=False), cursor=SimpleNamespace(in_query_mode=False),
) )
source.setMouseMode = lambda mode: None source.setMouseMode = lambda mode: None
source.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
with qtbot.waitExposed(source):
source.show()
source.raise_()
source.activateWindow()
source.setFocus()
qtbot.waitUntil(lambda: source.hasFocus())
async def drive_key_event() -> None: async def drive_key_event() -> None:
''' '''
@ -680,13 +687,13 @@ def test_ctrl_g_event_renders_real_history_overlay(
source, source,
dss={FQME: ds}, dss={FQME: ds},
): ):
key_event: QKeyEvent = QKeyEvent( qtbot.keyPress(
QEvent.Type.KeyPress, source,
Qt.Key.Key_G, Qt.Key.Key_G,
Qt.KeyboardModifier.ControlModifier, modifier=(
'g', Qt.KeyboardModifier.ControlModifier
),
) )
assert QApplication.sendEvent(source, key_event)
await wait_all_tasks_blocked() await wait_all_tasks_blocked()
try: try: