Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new extension points for keyboard input and speech pause #17428

Merged
merged 6 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions projectDocs/dev/developerGuide/developerGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -1361,6 +1361,7 @@ For examples of how to define and use new extension points, please see the code

| Type |Extension Point |Description|
|---|---|---|
|`Decider` |`decide_handleRawKey` |Notifies when a raw keyboard event is received, before any NVDA processing, allowing other code to decide if it should be handled.|
|`Decider` |`decide_executeGesture` |Notifies when a gesture is about to be executed, allowing other code to decide if it should be.|

### logHandler {#logHandlerExtPts}
Expand All @@ -1382,6 +1383,7 @@ For examples of how to define and use new extension points, please see the code
|`Action` |`speechCanceled` |Triggered when speech is canceled.|
|`Action` |`pre_speechCanceled` |Triggered before speech is canceled.|
|`Action` |`pre_speech` |Triggered before NVDA handles prepared speech.|
|`Action` |`post_speechPaused` |Triggered when speech is paused or resumed.|
|`Filter` |`filter_speechSequence` |Allows components or add-ons to filter speech sequence before it passes to the synth driver.|

### synthDriverHandler {#synthDriverHandlerExtPts}
Expand Down
16 changes: 16 additions & 0 deletions source/inputCore.py
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,22 @@ def __eq__(self, other: Any) -> bool:
return NotImplemented


decide_handleRawKey = extensionPoints.Decider()
seanbudd marked this conversation as resolved.
Show resolved Hide resolved
ctoth marked this conversation as resolved.
Show resolved Hide resolved
"""
Notifies when a raw keyboard event is received, before any NVDA processing.
Handlers can decide whether the key should be processed by NVDA and/or passed to the OS.
:param vkCode: The virtual key code
:type vkCode: int
:param scanCode: The scan code
:type scanCode: int
:param extended: Whether this is an extended key
:type extended: bool
:param pressed: Whether this is a key press or release
:type pressed: bool
:return: True to allow normal processing, False to block the key
:rtype: bool
"""

decide_executeGesture = extensionPoints.Decider()
"""
Notifies when a gesture is about to be executed,
Expand Down
14 changes: 14 additions & 0 deletions source/keyboardHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,13 @@ def shouldUseToUnicodeEx(focus: Optional["NVDAObject"] = None):

def internal_keyDownEvent(vkCode, scanCode, extended, injected):
"""Event called by winInputHook when it receives a keyDown."""
if not inputCore.decide_handleRawKey.decide(
vkCode=vkCode,
scanCode=scanCode,
extended=extended,
pressed=True,
):
return False
gestureExecuted = False
try:
global \
Expand Down Expand Up @@ -313,6 +320,13 @@ def internal_keyDownEvent(vkCode, scanCode, extended, injected):

def internal_keyUpEvent(vkCode, scanCode, extended, injected):
"""Event called by winInputHook when it receives a keyUp."""
if not inputCore.decide_handleRawKey.decide(
vkCode=vkCode,
scanCode=scanCode,
extended=extended,
pressed=False,
):
return False
try:
global \
lastNVDAModifier, \
Expand Down
3 changes: 2 additions & 1 deletion source/speech/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
spellTextInfo,
splitTextIndentation,
)
from .extensions import speechCanceled
from .extensions import speechCanceled, post_speechPaused
from .priorities import Spri

from .types import (
Expand Down Expand Up @@ -142,6 +142,7 @@
"spellTextInfo",
"splitTextIndentation",
"speechCanceled",
"post_speechPaused",
]

import synthDriverHandler
Expand Down
8 changes: 8 additions & 0 deletions source/speech/extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@
Handlers are called without arguments.
"""

post_speechPaused = Action()
"""
Notifies when speech is paused.

:param switch: True if speech is paused, False if speech is resumed.
:type switch: bool
"""

pre_speech = Action()
"""
Notifies when code attempts to speak text.
Expand Down
3 changes: 2 additions & 1 deletion source/speech/speech.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from textUtils import unicodeNormalize
from textUtils.uniscribe import splitAtCharacterBoundaries
from . import manager
from .extensions import speechCanceled, pre_speechCanceled, pre_speech
from .extensions import speechCanceled, post_speechPaused, pre_speechCanceled, pre_speech
from .extensions import filter_speechSequence
from .commands import (
# Commands that are used in this file.
Expand Down Expand Up @@ -211,6 +211,7 @@ def cancelSpeech():

def pauseSpeech(switch):
getSynth().pause(switch)
post_speechPaused.notify(switch=switch)
_speechState.isPaused = switch
_speechState.beenCanceled = False

Expand Down
9 changes: 9 additions & 0 deletions tests/unit/test_speech.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
_getSpellingSpeechAddCharMode,
_getSpellingSpeechWithoutCharMode,
cancelSpeech,
pauseSpeech,
speechCanceled,
post_speechPaused,
)
from speech.commands import (
BeepCommand,
Expand Down Expand Up @@ -591,3 +593,10 @@ def test_speechCanceledExtensionPoint(self):
speechCanceled,
):
cancelSpeech()

def test_post_speechPausedExtensionPoint(self):
with actionTester(self, post_speechPaused, switch=True):
pauseSpeech(True)

with actionTester(self, post_speechPaused, switch=False):
pauseSpeech(False)
4 changes: 3 additions & 1 deletion user_docs/en/changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,9 @@ Add-ons will need to be re-tested and have their manifest updated.
* Retrieving the `labeledBy` property now works for:
* objects in applications implementing the `labelled-by` IAccessible2 relation. (#17436, @michaelweghorn)
* UIA elements supporting the corresponding `LabeledBy` UIA property. (#17442, @michaelweghorn)

* Added the following extension points (#17428, @ctoth):
* `inputCore.decide_handleRawKey`: called on each keypress
* `speech.extensions.post_speechPaused`: called when speech is paused or unpaused

#### API Breaking Changes

Expand Down
Loading