diff --git a/source/NVDAObjects/behaviors.py b/source/NVDAObjects/behaviors.py index bc8eb8845a4..e958230bbb5 100755 --- a/source/NVDAObjects/behaviors.py +++ b/source/NVDAObjects/behaviors.py @@ -1,8 +1,8 @@ # A part of NonVisual Desktop Access (NVDA) # This file is covered by the GNU General Public License. # See the file COPYING for more details. -# Copyright (C) 2006-2023 NV Access Limited, Peter Vágner, Joseph Lee, Bill Dengler, -# Burman's Computer and Education Ltd. +# Copyright (C) 2006-2025 NV Access Limited, Peter Vágner, Joseph Lee, Bill Dengler, +# Burman's Computer and Education Ltd, Cary-rowen """Mix-in classes which provide common behaviour for particular types of controls across different APIs. Behaviors described in this mix-in include providing table navigation commands for certain table rows, terminal input and output support, announcing notifications and suggestion items and so on. @@ -31,6 +31,7 @@ import globalVars from typing import List, Union import diffHandler +from config.configFlags import TypingEcho class ProgressBar(NVDAObject): @@ -571,7 +572,10 @@ def event_typedCharacter(self, ch): else: self._hasTab = False if ( - (config.conf["keyboard"]["speakTypedCharacters"] or config.conf["keyboard"]["speakTypedWords"]) + ( + config.conf["keyboard"]["speakTypedCharacters"] != TypingEcho.OFF.value + or config.conf["keyboard"]["speakTypedWords"] != TypingEcho.OFF.value + ) and not config.conf["terminals"]["speakPasswords"] and self._supportsTextChange ): diff --git a/source/NVDAObjects/inputComposition.py b/source/NVDAObjects/inputComposition.py index e0493d4ec2a..8f7e721c116 100644 --- a/source/NVDAObjects/inputComposition.py +++ b/source/NVDAObjects/inputComposition.py @@ -1,6 +1,12 @@ +# A part of NonVisual Desktop Access (NVDA) +# Copyright (C) 2012-2025 NV Access Limited, Cary-Rowen +# This file is covered by the GNU General Public License. +# See the file COPYING for more details. + import eventHandler import queueHandler import controlTypes +from config.configFlags import TypingEcho import characterProcessing import speech import config @@ -73,7 +79,10 @@ def findOverlayClasses(self, clsList): return clsList def reportNewText(self, oldString, newString): - if config.conf["keyboard"]["speakTypedCharacters"] or config.conf["keyboard"]["speakTypedWords"]: + if ( + config.conf["keyboard"]["speakTypedCharacters"] != TypingEcho.OFF.value + or config.conf["keyboard"]["speakTypedWords"] != TypingEcho.OFF.value + ): newText = calculateInsertedChars(oldString.strip("\u3000"), newString.strip("\u3000")) if newText: queueHandler.queueFunction( diff --git a/source/config/configFlags.py b/source/config/configFlags.py index ed9ca62586f..c3ebf2789a4 100644 --- a/source/config/configFlags.py +++ b/source/config/configFlags.py @@ -1,5 +1,5 @@ # A part of NonVisual Desktop Access (NVDA) -# Copyright (C) 2022-2024 NV Access Limited, Cyrille Bougot +# Copyright (C) 2022-2025 NV Access Limited, Cyrille Bougot, Cary-rowen # This file is covered by the GNU General Public License. # See the file COPYING for more details. @@ -45,6 +45,30 @@ def _displayStringLabels(self): } +@unique +class TypingEcho(DisplayStringIntEnum): + """Enumeration containing the possible config values for typing echo (characters and words). + + Use TypingEcho.MEMBER.value to compare with the config; + use TypingEcho.MEMBER.displayString in the UI for a translatable description of this member. + """ + + OFF = 0 + EDIT_CONTROLS = 1 + ALWAYS = 2 + + @property + def _displayStringLabels(self): + return { + # Translators: One of the choices for typing echo in keyboard settings + TypingEcho.OFF: _("Off"), + # Translators: One of the choices for typing echo in keyboard settings + TypingEcho.EDIT_CONTROLS: _("Only in edit controls"), + # Translators: One of the choices for typing echo in keyboard settings + TypingEcho.ALWAYS: _("Always"), + } + + @unique class ShowMessages(DisplayStringIntEnum): """Enumeration containing the possible config values for "Show messages" option in braille settings. diff --git a/source/config/configSpec.py b/source/config/configSpec.py index 8a6a082dab1..b1395bc23b9 100644 --- a/source/config/configSpec.py +++ b/source/config/configSpec.py @@ -1,7 +1,7 @@ # A part of NonVisual Desktop Access (NVDA) -# Copyright (C) 2006-2024 NV Access Limited, Babbage B.V., Davy Kager, Bill Dengler, Julien Cochuyt, +# Copyright (C) 2006-2025 NV Access Limited, Babbage B.V., Davy Kager, Bill Dengler, Julien Cochuyt, # Joseph Lee, Dawid Pieper, mltony, Bram Duvigneau, Cyrille Bougot, Rob Meredith, -# Burman's Computer and Education Ltd., Leonard de Ruijter, Łukasz Golonka +# Burman's Computer and Education Ltd., Leonard de Ruijter, Łukasz Golonka, Cary-rowen # This file is covered by the GNU General Public License. # See the file COPYING for more details. @@ -13,7 +13,7 @@ #: provide an upgrade step (@see profileUpgradeSteps.py). An upgrade step does not need to be added when #: just adding a new element to (or removing from) the schema, only when old versions of the config #: (conforming to old schema versions) will not work correctly with the new schema. -latestSchemaVersion = 14 +latestSchemaVersion = 15 #: The configuration specification string #: @type: String @@ -178,8 +178,10 @@ # Default = 6: NumpadInsert + ExtendedInsert NVDAModifierKeys = integer(1, 7, default=6) keyboardLayout = string(default="desktop") - speakTypedCharacters = boolean(default=true) - speakTypedWords = boolean(default=false) + # 0: Off, 1: Only in edit controls, 2: Always + speakTypedCharacters = integer(default=1,min=0,max=2) + # 0: Off, 1: Only in edit controls, 2: Always + speakTypedWords = integer(default=0,min=0,max=2) beepForLowercaseWithCapslock = boolean(default=true) speakCommandKeys = boolean(default=false) speechInterruptForCharacters = boolean(default=true) diff --git a/source/config/profileUpgradeSteps.py b/source/config/profileUpgradeSteps.py index 7eb4c31a7c5..bf851b10289 100644 --- a/source/config/profileUpgradeSteps.py +++ b/source/config/profileUpgradeSteps.py @@ -1,5 +1,5 @@ # A part of NonVisual Desktop Access (NVDA) -# Copyright (C) 2016-2024 NV Access Limited, Bill Dengler, Cyrille Bougot, Łukasz Golonka, Leonard de Ruijter +# Copyright (C) 2016-2025 NV Access Limited, Bill Dengler, Cyrille Bougot, Łukasz Golonka, Leonard de Ruijter, Cary-rowen # This file is covered by the GNU General Public License. # See the file COPYING for more details. @@ -22,6 +22,7 @@ ReportTableHeaders, ReportCellBorders, OutputMode, + TypingEcho, ) import configobj.validate from configobj import ConfigObj @@ -471,3 +472,30 @@ def _friendlyNameToEndpointId(friendlyName: str) -> str | None: # Proceed to the next device state. continue return None + + +def upgradeConfigFrom_14_to_15(profile: ConfigObj): + """Convert keyboard typing echo configurations from boolean to integer values.""" + _convertTypingEcho(profile, "speakTypedCharacters") + _convertTypingEcho(profile, "speakTypedWords") + + +def _convertTypingEcho(profile: ConfigObj, key: str) -> None: + """ + Convert a keyboard typing echo configuration from boolean to integer values. + + :param profile: The `ConfigObj` instance representing the user's NVDA configuration file. + :param key: The configuration key to convert. + """ + try: + oldValue: bool = profile["keyboard"].as_bool(key) + except KeyError: + log.debug(f"'{key}' not present in config, no action taken.") + return + except ValueError: + log.error(f"'{key}' is not a boolean, no action taken.") + return + else: + newValue = TypingEcho.EDIT_CONTROLS.value if oldValue else TypingEcho.OFF.value + profile["keyboard"][key] = newValue + log.debug(f"Converted '{key}' from {oldValue!r} to {newValue} ({TypingEcho(newValue).name}).") diff --git a/source/globalCommands.py b/source/globalCommands.py index 9c22287a3c5..c504803b910 100755 --- a/source/globalCommands.py +++ b/source/globalCommands.py @@ -2,10 +2,10 @@ # A part of NonVisual Desktop Access (NVDA) # This file is covered by the GNU General Public License. # See the file COPYING for more details. -# Copyright (C) 2006-2024 NV Access Limited, Peter Vágner, Aleksey Sadovoy, Rui Batista, Joseph Lee, +# Copyright (C) 2006-2025 NV Access Limited, Peter Vágner, Aleksey Sadovoy, Rui Batista, Joseph Lee, # Leonard de Ruijter, Derek Riemer, Babbage B.V., Davy Kager, Ethan Holliger, Łukasz Golonka, Accessolutions, # Julien Cochuyt, Jakub Lukowicz, Bill Dengler, Cyrille Bougot, Rob Meredith, Luke Davis, -# Burman's Computer and Education Ltd. +# Burman's Computer and Education Ltd, Cary-rowen. import itertools from typing import ( @@ -44,6 +44,7 @@ ShowMessages, BrailleMode, OutputMode, + TypingEcho, ) from config.featureFlag import FeatureFlag from config.featureFlagEnums import BoolFlag @@ -69,6 +70,7 @@ from utils.security import objectBelowLockScreenAndWindowsIsLocked import audio from audio import appsVolume +from utils.displayString import DisplayStringEnum #: Script category for text review commands. @@ -150,6 +152,31 @@ def toggleBooleanValue( ui.message(msg) +def toggleIntegerValue( + configSection: str, + configKey: str, + enumClass: "DisplayStringEnum", + messageTemplate: str, +) -> None: + """ + Cycles through integer configuration values and displays the corresponding message. + + :param configSection: The configuration section containing the integer key. + :param configKey: The configuration key associated with the integer value. + :param enumClass: The enumeration class representing possible states. + :param messageTemplate: The message template with a placeholder, `{mode}`, for the state. + :return: None. + """ + currentValue = config.conf[configSection][configKey] + numVals = len(enumClass) + newValue = (currentValue + 1) % numVals + config.conf[configSection][configKey] = newValue + + state = enumClass(newValue) + msg = messageTemplate.format(mode=state.displayString) + ui.message(msg) + + class GlobalCommands(ScriptableObject): """Commands that are available at all times, regardless of the current focus.""" @@ -536,38 +563,36 @@ def script_previousSynthSetting(self, gesture): ui.message("%s %s" % (previousSettingName, previousSettingValue)) @script( - # Translators: Input help mode message for toggle speaked typed characters command. - description=_("Toggles on and off the speaking of typed characters"), + # Translators: Input help mode message for cycling the reporting of typed characters. + description=_("Cycles through options for when to speak typed characters."), category=SCRCAT_SPEECH, gesture="kb:NVDA+2", ) - def script_toggleSpeakTypedCharacters(self, gesture): - if config.conf["keyboard"]["speakTypedCharacters"]: - # Translators: The message announced when toggling the speak typed characters keyboard setting. - state = _("speak typed characters off") - config.conf["keyboard"]["speakTypedCharacters"] = False - else: - # Translators: The message announced when toggling the speak typed characters keyboard setting. - state = _("speak typed characters on") - config.conf["keyboard"]["speakTypedCharacters"] = True - ui.message(state) + def script_toggleSpeakTypedCharacters(self, gesture: "inputCore.InputGesture") -> None: + toggleIntegerValue( + configSection="keyboard", + configKey="speakTypedCharacters", + enumClass=TypingEcho, + # Translators: Reported when the user cycles through speak typed characters modes. + # {mode} will be replaced with the mode; e.g. Off, On, Only in edit controls. + messageTemplate=_("Speak typed characters {mode}"), + ) @script( - # Translators: Input help mode message for toggle speak typed words command. - description=_("Toggles on and off the speaking of typed words"), + # Translators: Input help mode message for cycling the reporting of typed words. + description=_("Cycles through options for when to speak typed words."), category=SCRCAT_SPEECH, gesture="kb:NVDA+3", ) - def script_toggleSpeakTypedWords(self, gesture): - if config.conf["keyboard"]["speakTypedWords"]: - # Translators: The message announced when toggling the speak typed words keyboard setting. - state = _("speak typed words off") - config.conf["keyboard"]["speakTypedWords"] = False - else: - # Translators: The message announced when toggling the speak typed words keyboard setting. - state = _("speak typed words on") - config.conf["keyboard"]["speakTypedWords"] = True - ui.message(state) + def script_toggleSpeakTypedWords(self, gesture: "inputCore.InputGesture") -> None: + toggleIntegerValue( + configSection="keyboard", + configKey="speakTypedWords", + enumClass=TypingEcho, + # Translators: Reported when the user cycles through speak typed words modes. + # {mode} will be replaced with the mode; e.g. Off, On, Only in edit controls. + messageTemplate=_("Speak typed words {mode}"), + ) @script( # Translators: Input help mode message for toggle speak command keys command. diff --git a/source/gui/settingsDialogs.py b/source/gui/settingsDialogs.py index 793f6e04f32..8a3859b66e0 100644 --- a/source/gui/settingsDialogs.py +++ b/source/gui/settingsDialogs.py @@ -1,11 +1,11 @@ # A part of NonVisual Desktop Access (NVDA) -# Copyright (C) 2006-2024 NV Access Limited, Peter Vágner, Aleksey Sadovoy, +# Copyright (C) 2006-2025 NV Access Limited, Peter Vágner, Aleksey Sadovoy, # Rui Batista, Joseph Lee, Heiko Folkerts, Zahari Yurukov, Leonard de Ruijter, # Derek Riemer, Babbage B.V., Davy Kager, Ethan Holliger, Bill Dengler, # Thomas Stivers, Julien Cochuyt, Peter Vágner, Cyrille Bougot, Mesar Hameed, # Łukasz Golonka, Aaron Cannon, Adriani90, André-Abush Clause, Dawid Pieper, # Takuya Nishimoto, jakubl7545, Tony Malykh, Rob Meredith, -# Burman's Computer and Education Ltd, hwf1324. +# Burman's Computer and Education Ltd, hwf1324, Cary-rowen. # This file is covered by the GNU General Public License. # See the file COPYING for more details. import logging @@ -39,6 +39,7 @@ ReportTableHeaders, ReportCellBorders, OutputMode, + TypingEcho, ) import languageHandler import speech @@ -1974,24 +1975,29 @@ def makeSettings(self, settingsSizer): checkedItems.append(n) self.modifierList.CheckedItems = checkedItems self.modifierList.Select(0) - self.bindHelpEvent("KeyboardSettingsModifiers", self.modifierList) - # Translators: This is the label for a checkbox in the - # keyboard settings panel. - charsText = _("Speak typed &characters") - self.charsCheckBox = sHelper.addItem(wx.CheckBox(self, label=charsText)) - self.bindHelpEvent( - "KeyboardSettingsSpeakTypedCharacters", - self.charsCheckBox, + + # Translators: This is the label for a combobox in the keyboard settings panel. + speakTypedCharsLabelText = _("Speak typed &characters:") + speakTypedCharsChoices = [mode.displayString for mode in TypingEcho] + self.speakTypedCharsList = sHelper.addLabeledControl( + speakTypedCharsLabelText, + wx.Choice, + choices=speakTypedCharsChoices, ) - self.charsCheckBox.SetValue(config.conf["keyboard"]["speakTypedCharacters"]) + self.bindHelpEvent("KeyboardSettingsSpeakTypedCharacters", self.speakTypedCharsList) + self.speakTypedCharsList.SetSelection(config.conf["keyboard"]["speakTypedCharacters"]) - # Translators: This is the label for a checkbox in the - # keyboard settings panel. - speakTypedWordsText = _("Speak typed &words") - self.wordsCheckBox = sHelper.addItem(wx.CheckBox(self, label=speakTypedWordsText)) - self.bindHelpEvent("KeyboardSettingsSpeakTypedWords", self.wordsCheckBox) - self.wordsCheckBox.SetValue(config.conf["keyboard"]["speakTypedWords"]) + # Translators: This is the label for a combobox in the keyboard settings panel. + speakTypedWordsLabelText = _("Speak typed &words:") + speakTypedWordsChoices = [mode.displayString for mode in TypingEcho] + self.speakTypedWordsList = sHelper.addLabeledControl( + speakTypedWordsLabelText, + wx.Choice, + choices=speakTypedWordsChoices, + ) + self.bindHelpEvent("KeyboardSettingsSpeakTypedWords", self.speakTypedWordsList) + self.speakTypedWordsList.SetSelection(config.conf["keyboard"]["speakTypedWords"]) # Translators: This is the label for a checkbox in the # keyboard settings panel. @@ -2091,8 +2097,8 @@ def onSave(self): config.conf["keyboard"]["NVDAModifierKeys"] = sum( key.value for (n, key) in enumerate(NVDAKey) if self.modifierList.IsChecked(n) ) - config.conf["keyboard"]["speakTypedCharacters"] = self.charsCheckBox.IsChecked() - config.conf["keyboard"]["speakTypedWords"] = self.wordsCheckBox.IsChecked() + config.conf["keyboard"]["speakTypedCharacters"] = self.speakTypedCharsList.GetSelection() + config.conf["keyboard"]["speakTypedWords"] = self.speakTypedWordsList.GetSelection() config.conf["keyboard"]["speechInterruptForCharacters"] = ( self.speechInterruptForCharsCheckBox.IsChecked() ) diff --git a/source/speech/speech.py b/source/speech/speech.py index 49992c0b03f..a56e341fe94 100644 --- a/source/speech/speech.py +++ b/source/speech/speech.py @@ -1,8 +1,8 @@ # A part of NonVisual Desktop Access (NVDA) # This file is covered by the GNU General Public License. # See the file COPYING for more details. -# Copyright (C) 2006-2024 NV Access Limited, Peter Vágner, Aleksey Sadovoy, Babbage B.V., Bill Dengler, -# Julien Cochuyt, Derek Riemer, Cyrille Bougot, Leonard de Ruijter, Łukasz Golonka +# Copyright (C) 2006-2025 NV Access Limited, Peter Vágner, Aleksey Sadovoy, Babbage B.V., Bill Dengler, +# Julien Cochuyt, Derek Riemer, Cyrille Bougot, Leonard de Ruijter, Łukasz Golonka, Cary-rowen """High-level functions to speak information.""" @@ -68,6 +68,7 @@ ReportTableHeaders, ReportCellBorders, OutputMode, + TypingEcho, ) import aria from .priorities import Spri @@ -780,10 +781,10 @@ def _getPlaceholderSpeechIfTextEmpty( reason: OutputReason, ) -> Tuple[bool, SpeechSequence]: """Attempt to get speech for placeholder attribute if text for 'obj' is empty. Don't report the placeholder - value unless the text is empty, because it is confusing to hear the current value (presumably typed by the - user) *and* the placeholder. The placeholder should "disappear" once the user types a value. - @return: (True, SpeechSequence) if text for obj was considered empty and we attempted to get speech for the - placeholder value. (False, []) if text for obj was not considered empty. + value unless the text is empty, because it is confusing to hear the current value (presumably typed by the + user) *and* the placeholder. The placeholder should "disappear" once the user types a value. + :return: `(True, SpeechSequence)` if text for obj was considered empty and we attempted to get speech for the + placeholder value. `(False, [])` if text for obj was not considered empty. """ textEmpty = obj._isTextEmpty if textEmpty: @@ -1355,6 +1356,17 @@ def _suppressSpeakTypedCharacters(number: int): FIRST_NONCONTROL_CHAR = " " +def isFocusEditable() -> bool: + """Check if the currently focused object is editable. + :return: ``True`` if the focused object is editable, ``False`` otherwise. + """ + obj = api.getFocusObject() + controls = {controlTypes.ROLE_EDITABLETEXT, controlTypes.ROLE_DOCUMENT, controlTypes.ROLE_TERMINAL} + return ( + obj.role in controls or controlTypes.STATE_EDITABLE in obj.states + ) and controlTypes.STATE_READONLY not in obj.states + + def speakTypedCharacters(ch: str): typingIsProtected = api.isTypingProtected() if typingIsProtected: @@ -1374,8 +1386,12 @@ def speakTypedCharacters(ch: str): clearTypedWordBuffer() if log.isEnabledFor(log.IO): log.io("typed word: %s" % typedWord) - if config.conf["keyboard"]["speakTypedWords"] and not typingIsProtected: - speakText(typedWord) + typingEchoMode = config.conf["keyboard"]["speakTypedWords"] + if typingEchoMode != TypingEcho.OFF.value and not typingIsProtected: + if typingEchoMode == TypingEcho.ALWAYS.value or ( + typingEchoMode == TypingEcho.EDIT_CONTROLS.value and isFocusEditable() + ): + speakText(typedWord) if _speechState._suppressSpeakTypedCharactersNumber > 0: # We primarily suppress based on character count and still have characters to suppress. # However, we time out after a short while just in case. @@ -1387,8 +1403,13 @@ def speakTypedCharacters(ch: str): _speechState._suppressSpeakTypedCharactersTime = None else: suppress = False - if not suppress and config.conf["keyboard"]["speakTypedCharacters"] and ch >= FIRST_NONCONTROL_CHAR: - speakSpelling(realChar) + + typingEchoMode = config.conf["keyboard"]["speakTypedCharacters"] + if not suppress and typingEchoMode != TypingEcho.OFF.value and ch >= FIRST_NONCONTROL_CHAR: + if typingEchoMode == TypingEcho.ALWAYS.value or ( + typingEchoMode == TypingEcho.EDIT_CONTROLS.value and isFocusEditable() + ): + speakSpelling(realChar) class SpeakTextInfoState(object): diff --git a/user_docs/en/changes.md b/user_docs/en/changes.md index 52fefa2bc17..fb619977e2d 100644 --- a/user_docs/en/changes.md +++ b/user_docs/en/changes.md @@ -52,6 +52,8 @@ To use this feature, "allow NVDA to control the volume of other applications" mu * Short versions of the most commonly used command line options have been added: `-d` for `--disable-addons` and `-n` for `--lang`. Prefix matching on command line flags, e.g. using `--di` for `--disable-addons` is no longer supported. (#11644, @CyrilleB79) * Microsoft Speech API version 5 and Microsoft Speech Platform voices now use WASAPI for audio output, which may improve the responsiveness of those voices. (#13284, @gexgd0419) +* The keyboard settings for "Speak typed characters" and "Speak typed words" now have three options: Off, Only in edit controls, and Always. (#17505, @Cary-rowen) + * By default, "Speak typed characters" is now set to "Only in edit controls". ### Bug Fixes @@ -179,6 +181,10 @@ Instead, a `callback` property has been added, which returns a function that per * Because SAPI5 voices now use `nvwave.WavePlayer` to output audio: (#17592, @gexgd0419) * `synthDrivers.sapi5.SPAudioState` has been removed. * `synthDrivers.sapi5.SynthDriver.ttsAudioStream` has been removed. +* Changed keyboard typing echo configuration from boolean to integer values. (#17505, @Cary-rowen) + * `config.conf["keyboard"]["speakTypedCharacters"]` and `config.conf["keyboard"]["speakTypedWords"]` now use integer values. + * Added `TypingEcho` enum in `config.configFlags` to represent these modes, 0=Off, 1=Only in edit controls, 2=Always. + * `gui.settingsDialogs.KeyboardSettingsPanel.wordsCheckBox` and `gui.settingsDialogs.KeyboardSettingsPanel.charsCheckBox` has been removed. #### Deprecations diff --git a/user_docs/en/userGuide.md b/user_docs/en/userGuide.md index 8b5ef0d340b..7796680601c 100644 --- a/user_docs/en/userGuide.md +++ b/user_docs/en/userGuide.md @@ -247,8 +247,8 @@ The actual commands will not execute while in input help mode. | Name |Desktop key |Laptop key |Description| |---|---|---|---| -|Speak typed characters |`NVDA+2` |`NVDA+2` |When enabled, NVDA will announce all characters you type on the keyboard.| -|Speak typed words |`NVDA+3` |`NVDA+3` |When enabled, NVDA will announce word you type on the keyboard.| +|Speak typed characters |`NVDA+2` |`NVDA+2` |Controls when NVDA announces characters you type. | +|Speak typed words |`NVDA+3` |`NVDA+3` |Controls when NVDA announces words you type. | |Speak command keys |`NVDA+4` |`NVDA+4` |When enabled, NVDA will announce all non-character keys you type on the keyboard. This includes key combinations such as control plus another letter.| |Enable mouse tracking |`NVDA+m` |`NVDA+m` |When enabled, NVDA will announce the text currently under the mouse pointer, as you move it around the screen. This allows you to find things on the screen, by physically moving the mouse, rather than trying to find them through object navigation.| @@ -2588,17 +2588,27 @@ If no key is chosen as the NVDA key it may be impossible to access many NVDA com ##### Speak Typed Characters {#KeyboardSettingsSpeakTypedCharacters} -Key: NVDA+2 +Key: `NVDA+2` -When enabled, NVDA will announce all characters you type on the keyboard. +This option controls when NVDA announces characters you type on the keyboard. +The available options are: + +* Off: NVDA will not announce typed characters. +* Only in edit controls: NVDA will only announce characters typed in edit controls and other areas where text can be typed. +* Always: NVDA will announce all typed characters. ##### Speak Typed Words {#KeyboardSettingsSpeakTypedWords} -Key: NVDA+3 +Key: `NVDA+3` + +This option controls when NVDA announces words you type on the keyboard. +The available options are: -When enabled, NVDA will announce all words you type on the keyboard. +* Off: NVDA will not announce typed words. +* Only in edit controls: NVDA will only announce words typed in edit controls and other areas where text can be typed. +* Always: NVDA will announce all typed words. ##### Speech interrupt for typed characters {#KeyboardSettingsSpeechInteruptForCharacters}