Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 85 additions & 5 deletions source/touchHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,33 @@ class POINTER_TOUCH_INFO(Structure):
touchThread = None


_flickActions: frozenset[str] = frozenset(
{
touchTracker.action_flickRight,
touchTracker.action_flickLeft,
touchTracker.action_flickUp,
touchTracker.action_flickDown,
},
)
"""The set of single-direction flick actions that can begin a sequential flick gesture."""

_flickSequenceMap: dict[tuple[str, str], str] = {
(touchTracker.action_flickRight, touchTracker.action_flickLeft): touchTracker.action_flickRightThenLeft,
(touchTracker.action_flickLeft, touchTracker.action_flickRight): touchTracker.action_flickLeftThenRight,
(touchTracker.action_flickUp, touchTracker.action_flickDown): touchTracker.action_flickUpThenDown,
(touchTracker.action_flickDown, touchTracker.action_flickUp): touchTracker.action_flickDownThenUp,
(touchTracker.action_flickRight, touchTracker.action_flickUp): touchTracker.action_flickRightThenUp,
(touchTracker.action_flickRight, touchTracker.action_flickDown): touchTracker.action_flickRightThenDown,
(touchTracker.action_flickLeft, touchTracker.action_flickUp): touchTracker.action_flickLeftThenUp,
(touchTracker.action_flickLeft, touchTracker.action_flickDown): touchTracker.action_flickLeftThenDown,
(touchTracker.action_flickUp, touchTracker.action_flickRight): touchTracker.action_flickUpThenRight,
(touchTracker.action_flickUp, touchTracker.action_flickLeft): touchTracker.action_flickUpThenLeft,
(touchTracker.action_flickDown, touchTracker.action_flickRight): touchTracker.action_flickDownThenRight,
(touchTracker.action_flickDown, touchTracker.action_flickLeft): touchTracker.action_flickDownThenLeft,
}
"""Maps (firstFlickAction, secondFlickAction) to the corresponding sequential flick action."""


class TouchInputGesture(inputCore.InputGesture):
"""
Represents a gesture performed on a touch screen.
Expand Down Expand Up @@ -267,7 +294,7 @@ def getDisplayTextForIdentifier(cls, identifier):
foundAction = foundPlural = False
for subID in reversed(ID.split("_")):
if not foundAction:
action = touchTracker.actionLabels[subID]
action = touchTracker.TouchAction(subID).displayString
foundAction = True
continue
if not foundPlural:
Expand Down Expand Up @@ -389,13 +416,66 @@ def setMode(self, mode):
raise ValueError("Unknown mode %s" % mode)
self._curTouchMode = mode

def _executeGesture(self, gesture: "TouchInputGesture") -> None:
"""Execute a touch gesture, silently ignoring unbound gestures.

:param gesture: The gesture to execute.
"""
try:
inputCore.manager.executeGesture(gesture)
except inputCore.NoInputGestureAction:
pass

def _tryBuildSequentialGesture(
self,
first: "TouchInputGesture",
second: "TouchInputGesture",
) -> "TouchInputGesture | None":
"""Attempt to combine two consecutive flick gestures into a single sequential flick gesture.

:param first: The first flick gesture.
:param second: The second flick gesture.
:return: A combined sequential gesture, or ``None`` if the pair is not a recognised combination.
"""
if first.tracker.numFingers != second.tracker.numFingers:
return None
compoundAction = _flickSequenceMap.get((first.tracker.action, second.tracker.action))
if compoundAction is None:
return None
compoundTracker = touchTracker.MultiTouchTracker(
compoundAction,
first.tracker.x,
first.tracker.y,
first.tracker.startTime,
second.tracker.endTime,
numFingers=second.tracker.numFingers,
)
return TouchInputGesture(first.preheldTracker, compoundTracker, first.mode)

def pump(self):
# pendingFlick holds the first flick within this pump cycle, waiting to see if a second follows.
# This is a local variable — no timer, no cross-pump buffering, so normal flicks fire immediately.
pendingFlick: TouchInputGesture | None = None
for preheldTracker, tracker in self.trackerManager.emitTrackers():
gesture = TouchInputGesture(preheldTracker, tracker, self._curTouchMode.value)
try:
inputCore.manager.executeGesture(gesture)
except inputCore.NoInputGestureAction:
pass
if tracker.action in _flickActions:
if pendingFlick is not None:
sequentialGesture = self._tryBuildSequentialGesture(pendingFlick, gesture)
if sequentialGesture is not None:
pendingFlick = None
self._executeGesture(sequentialGesture)
continue
# No match yet: flush any earlier flick, then hold this one for the rest of this cycle.
if pendingFlick is not None:
self._executeGesture(pendingFlick)
pendingFlick = gesture
else:
if pendingFlick is not None:
self._executeGesture(pendingFlick)
pendingFlick = None
self._executeGesture(gesture)
if pendingFlick is not None:
self._executeGesture(pendingFlick)
Comment thread
kefaslungu marked this conversation as resolved.
Resolved
Comment thread
kefaslungu marked this conversation as resolved.
Resolved
interval = self.trackerManager.pendingEmitInterval
if interval and interval > 0:
# Ensure we are pumped again by the time more pending multiTouch trackers are ready
Expand Down
Loading