Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
154 commits
Select commit Hold shift + click to select a range
5b411ab
Add ability to scroll automatically with braille
nvdaes Oct 21, 2025
ab1056e
Add ability to scroll automatically in braille
nvdaes Oct 21, 2025
098bedc
Pre-commit auto-fix
pre-commit-ci[bot] Oct 21, 2025
b997c66
Beep instead of speaking message when scrolling is enabled, to avoid …
nvdaes Oct 22, 2025
7e89c2c
Merge branch 'brailleScroll' of https://github.com/nvdaes/nvda into b…
nvdaes Oct 22, 2025
d380d88
Added edit box to set the interval in seconds for autoScroll
nvdaes Oct 22, 2025
01e4f87
Convert to milliseconds autoScrollTimeout
nvdaes Oct 22, 2025
79d83f9
Add combo box for autoScroll intervals
nvdaes Oct 24, 2025
c3c7e47
Merge branch 'master' into brailleScroll
nvdaes Oct 24, 2025
f033be7
Save autoScroll interval option
nvdaes Oct 24, 2025
5b6fca5
Pre-commit auto-fix
pre-commit-ci[bot] Oct 24, 2025
572f1af
Use option for autoScrollInterval in config
nvdaes Oct 24, 2025
e1e8b70
Add commands to increase and decrease autoScroll
nvdaes Oct 25, 2025
7ab4eb2
Pre-commit auto-fix
pre-commit-ci[bot] Oct 25, 2025
3d60cd1
Update coment
nvdaes Oct 25, 2025
43335b5
Merge remote-tracking branch 'origin/master' into brailleScroll
nvdaes Oct 25, 2025
6873bae
Use oneShot in autoScroll
nvdaes Oct 25, 2025
17d52c6
Fix pyright analysis locally
nvdaes Oct 25, 2025
3463e1d
Merge remote-tracking branch 'origin/master' into brailleScroll
nvdaes Oct 30, 2025
a1a748c
Remove combo box to select how to calculate the autoScroll interval
nvdaes Oct 30, 2025
22515bb
Pre-commit auto-fix
pre-commit-ci[bot] Oct 30, 2025
5a26e26
Fix max and min timeout
nvdaes Oct 30, 2025
96bab6d
Merge branch 'brailleScroll' of https://github.com/nvdaes/nvda into b…
nvdaes Oct 30, 2025
ca22023
Pre-commit auto-fix
pre-commit-ci[bot] Oct 30, 2025
6920874
Merge branch 'master' into brailleScroll
nvdaes Nov 17, 2025
59b1694
Merge branch 'master' into brailleScroll
nvdaes Nov 18, 2025
a1b816b
Update braille.py
nvdaes Nov 18, 2025
55cd1bb
Use autoScrollRate and timeout when appropriate
nvdaes Nov 18, 2025
2b4d750
Pre-commit auto-fix
pre-commit-ci[bot] Nov 18, 2025
619c02c
Update variables in globalCommands
nvdaes Nov 18, 2025
3dce11a
Merge branch 'brailleScroll' of https://github.com/nvdaes/nvda into b…
nvdaes Nov 18, 2025
e1acf80
Update braille
nvdaes Nov 18, 2025
a2c7611
Fixes
nvdaes Nov 18, 2025
a4e11b5
Pre-commit auto-fix
pre-commit-ci[bot] Nov 18, 2025
9032f60
Merge branch 'master' into brailleScroll
nvdaes Nov 20, 2025
ff6880d
Update user guide
nvdaes Nov 20, 2025
a23a05a
Rename option
nvdaes Nov 20, 2025
bc64da2
Update user guide
nvdaes Nov 20, 2025
3207953
Disable autoScroll when the session is locked
nvdaes Nov 20, 2025
00ab1ae
Update user guide
nvdaes Nov 20, 2025
3efcc0e
Update changelog
nvdaes Nov 20, 2025
f3a2041
Apply suggestions from code review
nvdaes Nov 21, 2025
0734bb6
Apply suggestions from code review
nvdaes Nov 25, 2025
a6a142a
Pre-commit auto-fix
pre-commit-ci[bot] Nov 25, 2025
367a301
Address code review
nvdaes Nov 25, 2025
dbcffc5
Pre-commit auto-fix
pre-commit-ci[bot] Nov 25, 2025
a8f2459
Improve toggling scroll handling message timeout
nvdaes Nov 25, 2025
f8630ea
Merge branch 'brailleScroll' of https://github.com/nvdaes/nvda into b…
nvdaes Nov 25, 2025
87f6ec8
Apply suggestions from code review
nvdaes Dec 5, 2025
3d754c3
Merge
nvdaes Dec 5, 2025
b3052eb
Restore liblouis
nvdaes Dec 5, 2025
72cc841
Increase maximum for scroll rate
nvdaes Dec 5, 2025
c19039d
Allow scrolling when braille follows speech
nvdaes Dec 5, 2025
8f5eb26
Merge branch 'master' into brailleScroll
nvdaes Dec 5, 2025
b2a18f1
Merge branch 'master' into brailleScroll
nvdaes Dec 8, 2025
f3758fb
Use float for braille autoScroll rate
nvdaes Dec 8, 2025
b4c28b1
Use slider
nvdaes Dec 8, 2025
ece83ae
Pre-commit auto-fix
pre-commit-ci[bot] Dec 8, 2025
f5c6402
Update commands to change scroll rate
nvdaes Dec 8, 2025
004b3f1
Merge
nvdaes Dec 8, 2025
696ae2d
Pre-commit auto-fix
pre-commit-ci[bot] Dec 8, 2025
1604a3a
Improve command
nvdaes Dec 8, 2025
16acdf4
Merge
nvdaes Dec 8, 2025
811e48b
Pre-commit auto-fix
pre-commit-ci[bot] Dec 8, 2025
1b9a677
Fix lint
nvdaes Dec 8, 2025
8dfe987
Merge branch 'brailleScroll' of https://github.com/nvdaes/nvda into b…
nvdaes Dec 8, 2025
d7977d1
Add comments to explain why we are multiplying and dividing by 5 in s…
nvdaes Dec 8, 2025
9c419c9
Pre-commit auto-fix
pre-commit-ci[bot] Dec 8, 2025
73c2406
Merge master
nvdaes Dec 9, 2025
f466215
Merge master
nvdaes Dec 9, 2025
2b81e48
Reset changelog
nvdaes Dec 9, 2025
62520ec
Update changelog
nvdaes Dec 9, 2025
eb12323
Remove crash file
nvdaes Dec 9, 2025
662cbc6
Fix an issue where autoScroll couldn't be toggled using braille speec…
nvdaes Dec 9, 2025
039764d
Pre-commit auto-fix
pre-commit-ci[bot] Dec 9, 2025
4b2444f
Update user guide
nvdaes Dec 9, 2025
9a5182d
Merge
nvdaes Dec 9, 2025
0255812
Merge branch 'master' into brailleScroll
nvdaes Dec 10, 2025
057dd7a
Merge branch 'master' into brailleScroll
nvdaes Dec 12, 2025
c7464ac
Add spin double
nvdaes Dec 12, 2025
c7c0c86
Pre-commit auto-fix
pre-commit-ci[bot] Dec 12, 2025
5f61788
Improve control
nvdaes Dec 12, 2025
1e9af48
Merge branch 'master' into brailleScroll
nvdaes Dec 16, 2025
d152666
Revert "Add spin double"
nvdaes Dec 16, 2025
899b083
Improve slider for braille scroll
nvdaes Dec 27, 2025
6102d0e
Merge branch 'master' into brailleScroll
nvdaes Jan 12, 2026
e614982
Update globalCommands
nvdaes Jan 12, 2026
597abaf
Pre-commit auto-fix
pre-commit-ci[bot] Jan 12, 2026
8cb60a2
Merge branch 'master' into brailleScroll
nvdaes Jan 15, 2026
e763bc5
Merge branch 'brailleScroll' of https://github.com/nvdaes/nvda into b…
nvdaes Jan 15, 2026
040a79f
Remove unused variable
nvdaes Jan 15, 2026
2588554
Merge branch 'master' into brailleScroll
nvdaes Jan 16, 2026
49f0c26
Apply suggestions from code review
SaschaCowley Jan 22, 2026
675ce64
Merge master
nvdaes Jan 22, 2026
a10bf9b
Use float values
nvdaes Jan 22, 2026
db11e4b
Pre-commit auto-fix
pre-commit-ci[bot] Jan 22, 2026
bcd20a9
Apply suggestions from code review
nvdaes Jan 22, 2026
ac16c86
Pre-commit auto-fix
pre-commit-ci[bot] Jan 22, 2026
9e846e5
Add helper function
nvdaes Jan 23, 2026
09c6b2e
Pre-commit auto-fix
pre-commit-ci[bot] Jan 23, 2026
4072166
Update slider control
nvdaes Jan 23, 2026
01c20e3
Update slider
nvdaes Jan 23, 2026
beecbf7
Import calculatePercentageFromRange at the top of the module
nvdaes Jan 23, 2026
2ba1c3c
Import calculatePercentageFromRange at the method level to avoid crash
nvdaes Jan 23, 2026
d7c9604
Use calculateValueFromPercentage function for slider
nvdaes Jan 23, 2026
3332bac
Merge remote-tracking branch 'origin/master' into brailleScroll
nvdaes Jan 23, 2026
50b26a8
Split helper functions according to single responsibility principle
nvdaes Jan 23, 2026
ecac3e9
Update slider
nvdaes Jan 23, 2026
d2862ca
Pre-commit auto-fix
pre-commit-ci[bot] Jan 23, 2026
ddf4146
Update changelog
nvdaes Jan 23, 2026
db5eb29
Update changelog
nvdaes Jan 23, 2026
9c3a5bb
Remove unused variable
nvdaes Jan 23, 2026
6c6f99e
Merge remote-tracking branch 'origin/master' into brailleScroll
nvdaes Jan 27, 2026
239e8ed
Merge branch 'master' into brailleScroll
nvdaes Jan 29, 2026
9aa0b1c
Merge remote-tracking branch 'origin/master' into brailleScroll
nvdaes Feb 5, 2026
a03aed3
Merge remote-tracking branch 'origin/master' into brailleScroll
nvdaes Feb 6, 2026
2d3e418
Update source/braille.py
nvdaes Feb 6, 2026
246bb83
Update source/globalCommands.py
nvdaes Feb 6, 2026
3efb045
Update source/globalCommands.py
nvdaes Feb 6, 2026
63efb4d
Apply suggestion from code review
nvdaes Feb 6, 2026
69e9a9c
Merge branch 'brailleScroll' of https://github.com/nvdaes/nvda into b…
nvdaes Feb 6, 2026
ed44d22
Fix syntax for type annotation
nvdaes Feb 6, 2026
4b98d3f
Pre-commit auto-fix
pre-commit-ci[bot] Feb 6, 2026
dfe17de
Address code review
nvdaes Feb 6, 2026
c3eb751
Merge branch 'brailleScroll' of https://github.com/nvdaes/nvda into b…
nvdaes Feb 6, 2026
c9bdd86
Pre-commit auto-fix
pre-commit-ci[bot] Feb 6, 2026
26b510e
Address feedback and type improvement
nvdaes Feb 6, 2026
6238f38
Fix slider for braille scroll
nvdaes Feb 6, 2026
8c5659c
Pre-commit auto-fix
pre-commit-ci[bot] Feb 6, 2026
192bed8
Update changelog
nvdaes Feb 6, 2026
fccf6ac
Merge branch 'brailleScroll' of https://github.com/nvdaes/nvda into b…
nvdaes Feb 6, 2026
1772c06
Revert erroneous changes.
SaschaCowley Feb 20, 2026
3e10878
Restore syntax with asterisks in typing
nvdaes Feb 20, 2026
f31bc5e
Merge branch 'master' into brailleScroll
nvdaes Feb 26, 2026
2d33072
Update source/globalCommands.py
nvdaes Feb 26, 2026
0682d6a
Update source/globalCommands.py
nvdaes Feb 26, 2026
4b65963
Merge branch 'brailleScroll' of https://github.com/nvdaes/nvda into b…
nvdaes Feb 26, 2026
db050d9
Fix keyPath annotation for several functions
nvdaes Feb 26, 2026
3099abf
Merge remote-tracking branch 'origin/master' into brailleScroll
nvdaes Mar 2, 2026
a59c98b
Add functions to config.conf
nvdaes Mar 2, 2026
bfe04c5
Move functions to the ConfigManager class
nvdaes Mar 5, 2026
9e05281
Use function from config.conf
nvdaes Mar 5, 2026
7e668bd
Remove functions from globalCommands
nvdaes Mar 5, 2026
0d88ff9
Pre-commit auto-fix
pre-commit-ci[bot] Mar 5, 2026
f4e5f4b
Remove extra blank line in config module
nvdaes Mar 5, 2026
2d85236
Pre-commit auto-fix
pre-commit-ci[bot] Mar 5, 2026
3035f57
Update changelog
nvdaes Mar 5, 2026
63550c6
Merge branch 'brailleScroll' of https://github.com/nvdaes/nvda into b…
nvdaes Mar 5, 2026
f6d2e9a
Pre-commit auto-fix
pre-commit-ci[bot] Mar 5, 2026
62c99b1
Merge remote-tracking branch 'origin/master' into brailleScroll
nvdaes Mar 6, 2026
169219a
Update user_docs/en/userGuide.md
seanbudd Mar 10, 2026
8717fba
Merge branch 'master' into brailleScroll
nvdaes Mar 20, 2026
ea83478
Apply suggestion from review
nvdaes Mar 20, 2026
e2ef43c
Merge branch 'master' into brailleScroll
SaschaCowley Apr 2, 2026
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
48 changes: 47 additions & 1 deletion source/braille.py
Original file line number Diff line number Diff line change
Expand Up @@ -1712,10 +1712,11 @@ def nextLine(self):
try:
dest.obj.turnPage()
except RuntimeError:
pass
handler.autoScroll(enable=False)
else:
dest = dest.obj.makeTextInfo(textInfos.POSITION_FIRST)
else: # no page turn support
handler.autoScroll(enable=False)
shouldCollapseToEnd = True
dest.collapse(shouldCollapseToEnd)
self._setCursor(dest)
Expand Down Expand Up @@ -2483,6 +2484,7 @@ def __init__(self):
self._cursorBlinkUp = True
self._cells = []
self._cursorBlinkTimer = None
self._autoScrollCallLater: wx.CallLater | None = None
config.post_configProfileSwitch.register(self.handlePostConfigProfileSwitch)
if config.conf["braille"]["tetherTo"] == TetherTo.AUTO.value:
self._tether = TetherTo.FOCUS.value
Expand Down Expand Up @@ -2518,6 +2520,7 @@ def terminate(self):
if self._cursorBlinkTimer:
self._cursorBlinkTimer.Stop()
self._cursorBlinkTimer = None
self.autoScroll(enable=False)
config.post_configProfileSwitch.unregister(self.handlePostConfigProfileSwitch)
post_secureDesktopStateChange.unregister(self._onSecureDesktopStateChanged)
post_sessionLockStateChanged.unregister(self._onSessionLockStateChanged)
Expand All @@ -2533,12 +2536,14 @@ def terminate(self):

def _clearAll(self) -> None:
"""Clear the braille buffers and update the braille display."""
self.autoScroll(enable=False)
self.mainBuffer.clear()
if self.buffer is self.messageBuffer:
self._dismissMessage(False)
self.update()

def _onSecureDesktopStateChanged(self, isSecureDesktop: bool):
self.autoScroll(enable=False)
self.mainBuffer.clear()
if not easeOfAccess.isRegistered():
if isSecureDesktop:
Expand Down Expand Up @@ -2992,13 +2997,20 @@ def scrollForward(self):
self.buffer.scrollForward()
if self.buffer is self.messageBuffer:
self._resetMessageTimer()
if self._autoScrollCallLater:
# Reset the timer.
self._resetAutoScroll()

def scrollBack(self):
self.buffer.scrollBack()
if self.buffer is self.messageBuffer:
self._resetMessageTimer()
if self._autoScrollCallLater:
# Reset the timer.
self._resetAutoScroll()

def routeTo(self, windowPos):
self.autoScroll(enable=False)
self.buffer.routeTo(windowPos)
if self.buffer is self.messageBuffer:
self._dismissMessage()
Expand All @@ -3023,6 +3035,7 @@ def message(self, text):
):
return
_pre_showBrailleMessage.notify()
self.autoScroll(enable=False)
if self.buffer is self.messageBuffer:
self.buffer.clear()
else:
Expand Down Expand Up @@ -3063,6 +3076,38 @@ def _dismissMessage(self, shouldUpdate: bool = True):
self.update()
_post_dismissBrailleMessage.notify()

def autoScroll(self, enable: bool) -> None:
"""
Enable or disable automatic scroll.

:param enable: ``True`` if automatic scroll should be enabled, ``False`` otherwise.
"""

if not self.enabled:
return
if enable and self._autoScrollCallLater is None:
self._autoScrollCallLater = wx.CallLater(self._calculateAutoScrollTimeout(), self.scrollForward)
elif not enable and self._autoScrollCallLater is not None:
self._autoScrollCallLater.Stop()
self._autoScrollCallLater = None

def _calculateAutoScrollTimeout(self) -> int:
"""
Calculate the timeout for automatic scroll.

:return: The number of milliseconds to wait until the next scroll.
"""

autoScrollRate = config.conf["braille"]["autoScrollRate"]
return int((self.displaySize / autoScrollRate) * 1000)

def _resetAutoScroll(self) -> None:
"""
Reset autoScroll.
"""

self._autoScrollCallLater.Restart()

def handleGainFocus(self, obj: "NVDAObject", shouldAutoTether: bool = True) -> None:
if not self.enabled or config.conf["braille"]["mode"] == BrailleMode.SPEECH_OUTPUT.value:
return
Expand All @@ -3086,6 +3131,7 @@ def handleGainFocus(self, obj: "NVDAObject", shouldAutoTether: bool = True) -> N
)

def _doNewObject(self, regions):
self.autoScroll(enable=False)
self.mainBuffer.clear()
focusToHardLeftSet = False
for region in regions:
Expand Down
82 changes: 82 additions & 0 deletions source/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import easeOfAccess
from fileUtils import FaultTolerantFile
import extensionPoints
import functools

from . import profileUpgrader
from . import aggregatedSection
Expand Down Expand Up @@ -1046,6 +1047,87 @@ def getConfigValidation(self, keyPath):
data.default = conf.validator.get_default_value(spec)
return data

def getConfigValue(self, *keyPath: *tuple[str, str, *tuple[str, ...]]) -> any:
"""
Retrieves the value of a configuration key.
:param keyPath: The path to the configuration key to retrieve.
:return: The value of the specified configuration key.
"""
return functools.reduce(lambda d, x: d.get(x), keyPath, self)

def setConfigValue(
self,
value: bool | int | float | str,
*keyPath: *tuple[str, str, *tuple[str, ...]],
) -> None:
"""
Sets the value of a configuration key.
:param value: The value to set for the configuration key.
:param keyPath: The path to the configuration key to set.
:return: None.
"""
dictToUpdate = functools.reduce(lambda d, x: d.get(x), keyPath[:-1], self)
dictToUpdate[keyPath[-1]] = value

def _getConfigValueRange(self, *keyPath: tuple[str, str, *tuple[str, ...]]) -> tuple[float, float]:
"""
Gets the minimum and maximum allowed values for a configuration key.
:param keyPath: The path to The configuration key to evaluate.
:return: A tuple of (minValue, maxValue).
"""
validation = self.getConfigValidation(keyPath)
minValue = float(validation.kwargs["min"])
maxValue = float(validation.kwargs["max"])
return minValue, maxValue

def _clampValue(self, currentValue: float, minValue: float, maxValue: float, step: float) -> float:
"""
Calculates a new value by applying a step, constrained within min/max bounds.
:param currentValue: The current value.
:param minValue: The minimum allowed value.
:param maxValue: The maximum allowed value.
:param step: The amount to change the value by (positive or negative).
:return: The new value, clamped between min and max.
"""
return min(max(currentValue + step, minValue), maxValue)

def valueToPercentage(self, *keyPath: tuple[str, str, *tuple[str, ...]]) -> int:
"""
Calculates the percentage representation of a configuration value within its defined range.
:param keyPath: The path to the configuration key to evaluate.
:return: The percentage (0-100) of the value within the range.
"""
minValue, maxValue = self._getConfigValueRange(*keyPath)
currentValue = self.getConfigValue(*keyPath)
return round((currentValue - minValue) / (maxValue - minValue) * 100)

def percentageToValue(self, *keyPath: tuple[str, str, *tuple[str, ...]], percentage: int) -> float:
"""
Calculates the configuration value corresponding to a given percentage within its defined range.
:param keyPath: The path to the configuration key to evaluate.
:param percentage: The percentage (0-100) to convert to a value.
:return: The value corresponding to the given percentage within the defined range.
"""
minValue, maxValue = self._getConfigValueRange(*keyPath)
percentage = max(0, min(100, percentage))
value = minValue + (maxValue - minValue) * (percentage / 100)
return value

def clampedIncrementAndUpdateConfig(
self,
*keyPath: tuple[str, str, *tuple[str, ...]],
step: float,
) -> None:
"""
Updates a configuration value by applying a step, constrained within its valid range.
:param keyPath: The path to the configuration key to update.
:param step: The step adjustment value (positive, negative, or 0).
"""
currentValue = self.getConfigValue(*keyPath)
minValue, maxValue = self._getConfigValueRange(*keyPath)
newValue = self._clampValue(currentValue, minValue, maxValue, step)
self.setConfigValue(newValue, *keyPath)


class ConfigValidationData(object):
validationFuncName: str | None = None
Expand Down
2 changes: 2 additions & 0 deletions source/config/configSpec.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@
showMessages = integer(0, 2, default=1)
# Timeout after the message will disappear from braille display
messageTimeout = integer(default=4, min=1, max=20)
# Rate for automatic scroll (cells/sec)
autoScrollRate = float(default=10, min=1, max=20)
Comment thread
SaschaCowley marked this conversation as resolved.
tetherTo = option("auto", "focus", "review", default="auto")
reviewRoutingMovesSystemCaret = featureFlag(\
optionsEnum="ReviewRoutingMovesSystemCaretFlag", behaviorOfDefault="NEVER")
Expand Down
45 changes: 45 additions & 0 deletions source/globalCommands.py
Original file line number Diff line number Diff line change
Expand Up @@ -841,6 +841,51 @@ def script_toggleReportSpellingErrorsInBraille(self, gesture: inputCore.InputGes
# Translators: Message presented when turning off reporting spelling errors or grammar in braille.
ui.message(_("Report errors in braille off"))

@script(
# Translators: Input help mode message for command to toggle braille automatic scroll.
description=_("Toggles braille automatic scroll"),
category=SCRCAT_BRAILLE,
)
def script_toggleBrailleAutoScroll(self, gesture: inputCore.InputGesture):
shouldEnableAutoScroll = braille.handler._autoScrollCallLater is None
timeout = 0
if shouldEnableAutoScroll:
# Translators: Message reported when automatic scrolling has been enabled in braille.
ui.message(_("Automatic scrolling enabled"))
if not (
config.conf["braille"]["showMessages"] == ShowMessages.DISABLED
or config.conf["braille"]["mode"] == BrailleMode.SPEECH_OUTPUT.value
):
timeout = config.conf["braille"]["messageTimeout"] * 1000
else:
# Translators: Message reported when automatic scrolling has been disabled in braille.
ui.message(_("Automatic scrolling disabled"))
core.callLater(timeout, braille.handler.autoScroll, shouldEnableAutoScroll)

@script(
# Translators: Input help mode message for command to increase the rate for braille automatic scroll.
description=_("Increases the rate for braille automatic scroll"),
category=SCRCAT_BRAILLE,
)
def script_increaseBrailleAutoScrollRate(self, gesture: inputCore.InputGesture):
config.conf.clampedIncrementAndUpdateConfig("braille", "autoScrollRate", step=0.5)
percentage = config.conf.valueToPercentage("braille", "autoScrollRate")
# Translators: Message shown when increasing the braille auto scroll rate.
# {rate} will be replaced with the rate as a whole number from 0 to 100.
ui.message(_("Scroll rate {rate}").format(rate=percentage))

@script(
# Translators: Input help mode message for command to decrease the rate for braille automatic scroll.
description=_("Decreases the rate for braille automatic scroll"),
category=SCRCAT_BRAILLE,
)
def script_decreaseBrailleAutoScrollRate(self, gesture: inputCore.InputGesture):
Comment thread
SaschaCowley marked this conversation as resolved.
config.conf.clampedIncrementAndUpdateConfig("braille", "autoScrollRate", step=-0.5)
percentage = config.conf.valueToPercentage("braille", "autoScrollRate")
# Translators: Message shown when decreasing the braille auto scroll rate.
# {rate} will be replaced with the rate as a whole number from 0 to 100.
ui.message(_("Scroll rate {rate}").format(rate=percentage))

@script(
# Translators: Input help mode message for toggle report pages command.
description=_("Toggles on and off the reporting of pages"),
Expand Down
24 changes: 24 additions & 0 deletions source/gui/settingsDialogs.py
Original file line number Diff line number Diff line change
Expand Up @@ -5502,6 +5502,24 @@ def makeSettings(self, settingsSizer):
)
self.bindHelpEvent("BrailleSettingsInterruptSpeech", self.brailleInterruptSpeechCombo)

# Translators: The label for a setting in braille settings to change the rate for autoscroll.
autoScrollRateText = _("Auto&matic scroll rate")
self.autoScrollRateSlider: nvdaControls.EnhancedInputSlider = sHelper.addLabeledControl(
autoScrollRateText,
nvdaControls.EnhancedInputSlider,
minValue=0,
maxValue=100,
)

self.autoScrollRateSlider.SetValue(
config.conf.valueToPercentage(
"braille",
"autoScrollRate",
),
)
self.autoScrollRateSlider.SetPageSize(10)
self.bindHelpEvent("BrailleAutoScrollRate", self.autoScrollRateSlider)

if gui._isDebug():
log.debug("Finished making settings, now at %.2f seconds from start" % (time.time() - startTime))

Expand Down Expand Up @@ -5532,6 +5550,12 @@ def onSave(self):
]
config.conf["braille"]["showMessages"] = self.showMessagesList.GetSelection()
config.conf["braille"]["messageTimeout"] = self.messageTimeoutEdit.GetValue()

config.conf["braille"]["autoScrollRate"] = config.conf.percentageToValue(
"braille",
"autoScrollRate",
percentage=self.autoScrollRateSlider.GetValue(),
)
tetherChoice = [x.value for x in TetherTo][self.tetherList.GetSelection()]
if tetherChoice == TetherTo.AUTO.value:
config.conf["braille"]["tetherTo"] = TetherTo.AUTO.value
Expand Down
9 changes: 9 additions & 0 deletions user_docs/en/changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

### New Features

* Added the ability to automatically scroll the braille display. (#18573, @nvdaes)
* After installing or updating NVDA, a dialog now offers options to restart Windows, start the installed copy, or exit the installer. (#19268, #19718, @kefaslungu)
* NVDA now includes a built-in Magnifier feature that allows you to zoom and magnify parts of the screen. (#19228, @Boumtchack)
* The magnifier supports various zoom levels, color filters (normal, grayscale, inverted), and different focus tracking modes.
Expand Down Expand Up @@ -79,6 +80,14 @@ The `scons checkPot` target has also been replaced with `runcheckpot.bat`.
Use the individual test commands instead: `runcheckpot.bat`, `rununittests.bat`, `runsystemtests.bat`, `runlint.bat`. (#19606, #19676, @bramd)
* Updated Python 3.13.11 to 3.13.12 (#19572, @dpy013)
* Added a private `_asyncioEventLoop` module that provides an asyncio event loop running on a background thread for use by NVDA components. (#19816, @bramd)
* Added several functions related to the braille auto-scroll feature. (#18573, @nvdaes):
* Added an `autoScroll` method to `braille.handler`.
* Added several functions for handling configuration value conversions and updates in `config.conf`:
* Added a `getConfigValue` function to get the value for a provided configuration key path.
* Added a `setConfigValue` function to set a value for a provided configuration key path.
* Added a `valueToPercentage` function to calculate the percentage representation of a configuration value within its range.
* Added a `percentageToValue` function to convert a percentage to the corresponding configuration value.
* Added a `clampedIncrementAndUpdateConfig` function to update a configuration value by applying a step, constrained within its valid range.

#### Deprecations

Expand Down
13 changes: 13 additions & 0 deletions user_docs/en/userGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -2562,6 +2562,19 @@ Enabling this option will cause NVDA to speak lines or paragraphs reached using

To toggle this option from anywhere, please assign a custom gesture to "speakOnNavigatingByUnit" in the "Braille" section of the [Input Gestures dialog](#InputGestures).

##### Automatic Scroll Rate {#BrailleAutoScrollRate}
Comment thread
SaschaCowley marked this conversation as resolved.

This slider controls the rate of automatic braille display scrolling, measured in cells per second.
The minimum value (0%) will be 1 cell per second, and the maximum value (100%), 20 cells per second.
For example, with the default value of 10 cells/sec, if a braille display with 40 cells is used, the number of seconds between automatic scrolls will be 4.
If the display had 20 cells, each line of braille would be shown for 2 seconds.

While the automatic scroll option is enabled, you can still use the scroll back command to read previous contents again, and scroll forward, for example, to skip a blank line, or if the line being read is too short.

Automatic scrolling will be disabled if a routing key is pressed, if a message is presented in braille, if a new object is displayed, when entering a secure screen, when the session is locked, or when the end of the window is reached.

Commands can be assigned to toggle the automatic scroll option, and to increase or decrease the scroll rate, from the "Braille" section of the [Input Gestures dialog](#InputGestures).

##### Avoid splitting words when possible {#BrailleSettingsWordWrap}

If this is enabled, a word which is too large to fit at the end of the braille display will not be split.
Expand Down
Loading