Skip to content

Commit 8a5bed9

Browse files
authored
Merge 4721d55 into bdd3153
2 parents bdd3153 + 4721d55 commit 8a5bed9

9 files changed

Lines changed: 116 additions & 4918 deletions

File tree

source/NVDAObjects/behaviors.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import globalVars
3232
from typing import List, Union
3333
import diffHandler
34+
from config.configFlags import TypingEcho
3435

3536

3637
class ProgressBar(NVDAObject):
@@ -571,7 +572,10 @@ def event_typedCharacter(self, ch):
571572
else:
572573
self._hasTab = False
573574
if (
574-
(config.conf["keyboard"]["speakTypedCharacters"] or config.conf["keyboard"]["speakTypedWords"])
575+
(
576+
config.conf["keyboard"]["speakTypedCharacters"] > TypingEcho.OFF.value
577+
or config.conf["keyboard"]["speakTypedWords"] > TypingEcho.OFF.value
578+
)
575579
and not config.conf["terminals"]["speakPasswords"]
576580
and self._supportsTextChange
577581
):

source/NVDAObjects/inputComposition.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import eventHandler
22
import queueHandler
33
import controlTypes
4+
from config.configFlags import TypingEcho
45
import characterProcessing
56
import speech
67
import config
@@ -73,7 +74,10 @@ def findOverlayClasses(self, clsList):
7374
return clsList
7475

7576
def reportNewText(self, oldString, newString):
76-
if config.conf["keyboard"]["speakTypedCharacters"] or config.conf["keyboard"]["speakTypedWords"]:
77+
if (
78+
config.conf["keyboard"]["speakTypedCharacters"] > TypingEcho.OFF.value
79+
or config.conf["keyboard"]["speakTypedWords"] > TypingEcho.OFF.value
80+
):
7781
newText = calculateInsertedChars(oldString.strip("\u3000"), newString.strip("\u3000"))
7882
if newText:
7983
queueHandler.queueFunction(

source/config/configFlags.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,30 @@ def _displayStringLabels(self):
4545
}
4646

4747

48+
@unique
49+
class TypingEcho(DisplayStringIntEnum):
50+
"""Enumeration containing the possible config values for typing echo (characters and words).
51+
52+
Use TypingEcho.MEMBER.value to compare with the config;
53+
use TypingEcho.MEMBER.displayString in the UI for a translatable description of this member.
54+
"""
55+
56+
OFF = 0
57+
ON = 1
58+
EDIT_CONTROLS = 2
59+
60+
@property
61+
def _displayStringLabels(self):
62+
return {
63+
# Translators: One of the choices for typing echo in keyboard settings
64+
TypingEcho.OFF: _("Off"),
65+
# Translators: One of the choices for typing echo in keyboard settings
66+
TypingEcho.ON: _("On"),
67+
# Translators: One of the choices for typing echo in keyboard settings
68+
TypingEcho.EDIT_CONTROLS: _("Only in edit controls"),
69+
}
70+
71+
4872
@unique
4973
class ShowMessages(DisplayStringIntEnum):
5074
"""Enumeration containing the possible config values for "Show messages" option in braille settings.

source/config/configSpec.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,10 @@
179179
# Default = 6: NumpadInsert + ExtendedInsert
180180
NVDAModifierKeys = integer(1, 7, default=6)
181181
keyboardLayout = string(default="desktop")
182-
speakTypedCharacters = boolean(default=true)
183-
speakTypedWords = boolean(default=false)
182+
# 0: Off, 1: on, 2: Only in edit controls
183+
speakTypedCharacters = integer(default=1,min=0,max=2)
184+
# 0: Off, 1: on, 2: Only in edit controls
185+
speakTypedWords = integer(default=1,min=0,max=2)
184186
beepForLowercaseWithCapslock = boolean(default=true)
185187
speakCommandKeys = boolean(default=false)
186188
speechInterruptForCharacters = boolean(default=true)

source/globalCommands.py

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
ShowMessages,
4545
BrailleMode,
4646
OutputMode,
47+
TypingEcho,
4748
)
4849
from config.featureFlag import FeatureFlag
4950
from config.featureFlagEnums import BoolFlag
@@ -542,15 +543,12 @@ def script_previousSynthSetting(self, gesture):
542543
gesture="kb:NVDA+2",
543544
)
544545
def script_toggleSpeakTypedCharacters(self, gesture):
545-
if config.conf["keyboard"]["speakTypedCharacters"]:
546-
# Translators: The message announced when toggling the speak typed characters keyboard setting.
547-
state = _("speak typed characters off")
548-
config.conf["keyboard"]["speakTypedCharacters"] = False
549-
else:
550-
# Translators: The message announced when toggling the speak typed characters keyboard setting.
551-
state = _("speak typed characters on")
552-
config.conf["keyboard"]["speakTypedCharacters"] = True
553-
ui.message(state)
546+
numVals = len(TypingEcho)
547+
state = TypingEcho((config.conf["keyboard"]["speakTypedCharacters"] + 1) % numVals)
548+
config.conf["keyboard"]["speakTypedCharacters"] = state.value
549+
# Translators: Reported when the user cycles through speak typed characters modes.
550+
# {mode} will be replaced with the mode; e.g. Off, On, Only in edit controls.
551+
ui.message(_("Speak typed characters {mode}").format(mode=state.displayString))
554552

555553
@script(
556554
# Translators: Input help mode message for toggle speak typed words command.
@@ -559,15 +557,12 @@ def script_toggleSpeakTypedCharacters(self, gesture):
559557
gesture="kb:NVDA+3",
560558
)
561559
def script_toggleSpeakTypedWords(self, gesture):
562-
if config.conf["keyboard"]["speakTypedWords"]:
563-
# Translators: The message announced when toggling the speak typed words keyboard setting.
564-
state = _("speak typed words off")
565-
config.conf["keyboard"]["speakTypedWords"] = False
566-
else:
567-
# Translators: The message announced when toggling the speak typed words keyboard setting.
568-
state = _("speak typed words on")
569-
config.conf["keyboard"]["speakTypedWords"] = True
570-
ui.message(state)
560+
numVals = len(TypingEcho)
561+
state = TypingEcho((config.conf["keyboard"]["speakTypedWords"] + 1) % numVals)
562+
config.conf["keyboard"]["speakTypedWords"] = state.value
563+
# Translators: Reported when the user cycles through speak typed words modes.
564+
# {mode} will be replaced with the mode; e.g. Off, On, Only in edit controls.
565+
ui.message(_("Speak typed words {mode}").format(mode=state.displayString))
571566

572567
@script(
573568
# Translators: Input help mode message for toggle speak command keys command.

source/gui/settingsDialogs.py

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
ReportTableHeaders,
3939
ReportCellBorders,
4040
OutputMode,
41+
TypingEcho,
4142
)
4243
import languageHandler
4344
import speech
@@ -1981,24 +1982,27 @@ def makeSettings(self, settingsSizer):
19811982
checkedItems.append(n)
19821983
self.modifierList.CheckedItems = checkedItems
19831984
self.modifierList.Select(0)
1984-
19851985
self.bindHelpEvent("KeyboardSettingsModifiers", self.modifierList)
1986-
# Translators: This is the label for a checkbox in the
1987-
# keyboard settings panel.
1988-
charsText = _("Speak typed &characters")
1989-
self.charsCheckBox = sHelper.addItem(wx.CheckBox(self, label=charsText))
1990-
self.bindHelpEvent(
1991-
"KeyboardSettingsSpeakTypedCharacters",
1992-
self.charsCheckBox,
1993-
)
1994-
self.charsCheckBox.SetValue(config.conf["keyboard"]["speakTypedCharacters"])
19951986

1996-
# Translators: This is the label for a checkbox in the
1997-
# keyboard settings panel.
1998-
speakTypedWordsText = _("Speak typed &words")
1999-
self.wordsCheckBox = sHelper.addItem(wx.CheckBox(self, label=speakTypedWordsText))
2000-
self.bindHelpEvent("KeyboardSettingsSpeakTypedWords", self.wordsCheckBox)
2001-
self.wordsCheckBox.SetValue(config.conf["keyboard"]["speakTypedWords"])
1987+
# Translators: This is the label for a combobox in the keyboard settings panel.
1988+
charsLabelText = _("Speak typed &characters:")
1989+
charsChoices = [mode.displayString for mode in TypingEcho]
1990+
self.charsList = sHelper.addLabeledControl(charsLabelText, wx.Choice, choices=charsChoices)
1991+
self.bindHelpEvent("KeyboardSettingsSpeakTypedCharacters", self.charsList)
1992+
try:
1993+
self.charsList.SetSelection(config.conf["keyboard"]["speakTypedCharacters"])
1994+
except:
1995+
log.debugWarning("Could not set characters echo list to current setting", exc_info=True)
1996+
1997+
# Translators: This is the label for a combobox in the keyboard settings panel.
1998+
wordsLabelText = _("Speak typed &words:")
1999+
wordsChoices = [mode.displayString for mode in TypingEcho]
2000+
self.wordsList = sHelper.addLabeledControl(wordsLabelText, wx.Choice, choices=wordsChoices)
2001+
self.bindHelpEvent("KeyboardSettingsSpeakTypedWords", self.wordsList)
2002+
try:
2003+
self.wordsList.SetSelection(config.conf["keyboard"]["speakTypedWords"])
2004+
except:
2005+
log.debugWarning("Could not set words echo list to current setting", exc_info=True)
20022006

20032007
# Translators: This is the label for a checkbox in the
20042008
# keyboard settings panel.
@@ -2098,8 +2102,8 @@ def onSave(self):
20982102
config.conf["keyboard"]["NVDAModifierKeys"] = sum(
20992103
key.value for (n, key) in enumerate(NVDAKey) if self.modifierList.IsChecked(n)
21002104
)
2101-
config.conf["keyboard"]["speakTypedCharacters"] = self.charsCheckBox.IsChecked()
2102-
config.conf["keyboard"]["speakTypedWords"] = self.wordsCheckBox.IsChecked()
2105+
config.conf["keyboard"]["speakTypedCharacters"] = self.charsList.GetSelection()
2106+
config.conf["keyboard"]["speakTypedWords"] = self.wordsList.GetSelection()
21032107
config.conf["keyboard"]["speechInterruptForCharacters"] = (
21042108
self.speechInterruptForCharsCheckBox.IsChecked()
21052109
)

source/speech/speech.py

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
ReportTableHeaders,
6969
ReportCellBorders,
7070
OutputMode,
71+
TypingEcho,
7172
)
7273
import aria
7374
from .priorities import Spri
@@ -780,8 +781,8 @@ def _getPlaceholderSpeechIfTextEmpty(
780781
reason: OutputReason,
781782
) -> Tuple[bool, SpeechSequence]:
782783
"""Attempt to get speech for placeholder attribute if text for 'obj' is empty. Don't report the placeholder
783-
value unless the text is empty, because it is confusing to hear the current value (presumably typed by the
784-
user) *and* the placeholder. The placeholder should "disappear" once the user types a value.
784+
value unless the text is empty, because it is confusing to hear the current value (presumably typed by the
785+
user) *and* the placeholder. The placeholder should "disappear" once the user types a value.
785786
@return: (True, SpeechSequence) if text for obj was considered empty and we attempted to get speech for the
786787
placeholder value. (False, []) if text for obj was not considered empty.
787788
"""
@@ -1355,6 +1356,14 @@ def _suppressSpeakTypedCharacters(number: int):
13551356
FIRST_NONCONTROL_CHAR = " "
13561357

13571358

1359+
def is_editableControl() -> bool:
1360+
obj = api.getFocusObject()
1361+
controls = {controlTypes.ROLE_EDITABLETEXT, controlTypes.ROLE_DOCUMENT, controlTypes.ROLE_TERMINAL}
1362+
return (
1363+
obj.role in controls or controlTypes.STATE_EDITABLE in obj.states
1364+
) and controlTypes.STATE_READONLY not in obj.states
1365+
1366+
13581367
def speakTypedCharacters(ch: str):
13591368
typingIsProtected = api.isTypingProtected()
13601369
if typingIsProtected:
@@ -1374,8 +1383,14 @@ def speakTypedCharacters(ch: str):
13741383
clearTypedWordBuffer()
13751384
if log.isEnabledFor(log.IO):
13761385
log.io("typed word: %s" % typedWord)
1377-
if config.conf["keyboard"]["speakTypedWords"] and not typingIsProtected:
1378-
speakText(typedWord)
1386+
typingEchoMode = config.conf["keyboard"]["speakTypedWords"]
1387+
if typingEchoMode > TypingEcho.OFF.value and not typingIsProtected:
1388+
if (
1389+
typingEchoMode == TypingEcho.EDIT_CONTROLS.value
1390+
and is_editableControl()
1391+
or typingEchoMode == TypingEcho.ON.value
1392+
):
1393+
speakText(typedWord)
13791394
if _speechState._suppressSpeakTypedCharactersNumber > 0:
13801395
# We primarily suppress based on character count and still have characters to suppress.
13811396
# However, we time out after a short while just in case.
@@ -1387,8 +1402,15 @@ def speakTypedCharacters(ch: str):
13871402
_speechState._suppressSpeakTypedCharactersTime = None
13881403
else:
13891404
suppress = False
1390-
if not suppress and config.conf["keyboard"]["speakTypedCharacters"] and ch >= FIRST_NONCONTROL_CHAR:
1391-
speakSpelling(realChar)
1405+
1406+
typingEchoMode = config.conf["keyboard"]["speakTypedCharacters"]
1407+
if not suppress and typingEchoMode > TypingEcho.OFF.value and ch >= FIRST_NONCONTROL_CHAR:
1408+
if (
1409+
typingEchoMode == TypingEcho.EDIT_CONTROLS.value
1410+
and is_editableControl()
1411+
or typingEchoMode == TypingEcho.ON.value
1412+
):
1413+
speakSpelling(realChar)
13921414

13931415

13941416
class SpeakTextInfoState(object):

0 commit comments

Comments
 (0)