Skip to content

Commit 9de47c2

Browse files
authored
Merge 864213b into 7653056
2 parents 7653056 + 864213b commit 9de47c2

5 files changed

Lines changed: 79 additions & 32 deletions

File tree

source/NVDAObjects/UIA/winConsoleUIA.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
# A part of NonVisual Desktop Access (NVDA)
22
# This file is covered by the GNU General Public License.
33
# See the file COPYING for more details.
4-
# Copyright (C) 2019-2022 Bill Dengler, Leonard de Ruijter
4+
# Copyright (C) 2019-2023 Bill Dengler, Leonard de Ruijter
55

66
import api
7+
import braille
78
import config
89
import controlTypes
910
import ctypes
@@ -471,6 +472,7 @@ def event_UIA_notification(
471472
# Do not announce output from background terminals.
472473
if self.appModule != api.getFocusObject().appModule:
473474
return
475+
braille.handler.handleUpdate(self)
474476
# microsoft/terminal#12358: Automatic reading of terminal output
475477
# is provided by UIA notifications. If the user does not want
476478
# automatic reporting of dynamic output, suppress this notification.

source/NVDAObjects/behaviors.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
# -*- coding: UTF-8 -*-
21
# A part of NonVisual Desktop Access (NVDA)
32
# This file is covered by the GNU General Public License.
43
# See the file COPYING for more details.
5-
# Copyright (C) 2006-2022 NV Access Limited, Peter Vágner, Joseph Lee, Bill Dengler
4+
# Copyright (C) 2006-2023 NV Access Limited, Peter Vágner, Joseph Lee, Bill Dengler,
5+
# Burman's Computer and Education Ltd.
66

77
"""Mix-in classes which provide common behaviour for particular types of controls across different APIs.
88
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.
@@ -475,6 +475,13 @@ def _get_caretMovementDetectionUsesEvents(self):
475475
prompt to be read when quickly deleting text."""
476476
return False
477477

478+
def event_textChange(self) -> None:
479+
"""Fired when the text changes.
480+
@note: Updates also braille.
481+
"""
482+
super().event_textChange()
483+
braille.handler.handleUpdate(self)
484+
478485

479486
class EnhancedTermTypedCharSupport(Terminal):
480487
"""A Terminal object with keyboard support enhancements for console applications.

source/braille.py

Lines changed: 50 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@
6161
from autoSettingsUtils.driverSetting import BooleanDriverSetting, NumericDriverSetting
6262
from utils.security import objectBelowLockScreenAndWindowsIsLocked
6363
import hwIo
64+
from buildVersion import version_year
65+
import NVDAState
6466

6567
if TYPE_CHECKING:
6668
from NVDAObjects import NVDAObject
@@ -1569,6 +1571,7 @@ def bufferPosToRegionPos(self, bufferPos):
15691571
raise LookupError("No such position")
15701572

15711573
def regionPosToBufferPos(self, region, pos, allowNearest=False):
1574+
start: int = 0
15721575
for testRegion, start, end in self.regionsWithPositions:
15731576
if region == testRegion:
15741577
if pos < end - start:
@@ -2014,6 +2017,11 @@ class BrailleHandler(baseObject.AutoPropertyObject):
20142017
queuedWrite: Optional[List[int]] = None
20152018
queuedWriteLock: threading.Lock
20162019
ackTimerHandle: int
2020+
_regionsPendingUpdate: Set[Region]
2021+
"""
2022+
Regions pending an update.
2023+
Regions are added by L{handleUpdate} and L{handleCaretMove} and cleared in L{_handlePendingUpdate}.
2024+
"""
20172025

20182026
def __init__(self):
20192027
louisHelper.initialize()
@@ -2032,6 +2040,7 @@ def __init__(self):
20322040
with its previous output.
20332041
If L{decide_enabled} decides to disable the handler, pending output should be cleared.
20342042
"""
2043+
self._regionsPendingUpdate = set()
20352044

20362045

20372046
self.mainBuffer = BrailleBuffer(self)
@@ -2472,29 +2481,46 @@ def handleCaretMove(
24722481
region = self.mainBuffer.regions[-1] if self.mainBuffer.regions else None
24732482
if region and region.obj==obj:
24742483
region.pendingCaretUpdate=True
2484+
self._regionsPendingUpdate.add(region)
24752485
elif prevTether == TetherTo.REVIEW.value:
24762486
# The caret moved in a different object than the review position.
24772487
self._doNewObject(getFocusRegions(obj, review=False))
24782488

2479-
def handlePendingCaretUpdate(self):
2480-
"""Checks to see if the final text region needs its caret updated and if so calls _doCursorMove for the region."""
2481-
region=self.mainBuffer.regions[-1] if self.mainBuffer.regions else None
2482-
if isinstance(region,TextInfoRegion) and region.pendingCaretUpdate:
2483-
try:
2484-
self._doCursorMove(region)
2485-
finally:
2486-
region.pendingCaretUpdate=False
2489+
if version_year < 2024 and NVDAState._allowDeprecatedAPI():
2490+
def handlePendingCaretUpdate(self):
2491+
log.warning(
2492+
"braille.BrailleHandler.handlePendingCaretUpdate is now deprecated "
2493+
"with no public replacement. "
2494+
"It will be removed in NVDA 2024.1."
2495+
)
2496+
self._handlePendingUpdate()
24872497

2488-
def _doCursorMove(self, region):
2489-
self.mainBuffer.saveWindow()
2490-
region.update()
2491-
self.mainBuffer.update()
2492-
self.mainBuffer.restoreWindow()
2493-
self.scrollToCursorOrSelection(region)
2494-
if self.buffer is self.mainBuffer:
2495-
self.update()
2496-
elif self.buffer is self.messageBuffer and keyboardHandler.keyCounter>self._keyCountForLastMessage:
2497-
self._dismissMessage()
2498+
def _handlePendingUpdate(self):
2499+
"""When any region is pending an update, updates the region and the braille display.
2500+
"""
2501+
if not self._regionsPendingUpdate:
2502+
return
2503+
try:
2504+
scrollTo: Optional[TextInfoRegion] = None
2505+
self.mainBuffer.saveWindow()
2506+
for region in self._regionsPendingUpdate:
2507+
region.update()
2508+
if isinstance(region, TextInfoRegion) and region.pendingCaretUpdate:
2509+
scrollTo = region
2510+
region.pendingCaretUpdate = False
2511+
self.mainBuffer.update()
2512+
self.mainBuffer.restoreWindow()
2513+
if scrollTo is not None:
2514+
self.scrollToCursorOrSelection(scrollTo)
2515+
if self.buffer is self.mainBuffer:
2516+
self.update()
2517+
elif (
2518+
self.buffer is self.messageBuffer
2519+
and keyboardHandler.keyCounter > self._keyCountForLastMessage
2520+
):
2521+
self._dismissMessage()
2522+
finally:
2523+
self._regionsPendingUpdate.clear()
24982524

24992525
def scrollToCursorOrSelection(self, region):
25002526
if region.brailleCursorPos is not None:
@@ -2548,14 +2574,7 @@ def handleUpdate(self, obj: "NVDAObject") -> None:
25482574
if isinstance(obj, NVDAObject) and obj.role == controlTypes.Role.PROGRESSBAR and obj.isInForeground:
25492575
self._handleProgressBarUpdate(obj)
25502576
return
2551-
self.mainBuffer.saveWindow()
2552-
region.update()
2553-
self.mainBuffer.update()
2554-
self.mainBuffer.restoreWindow()
2555-
if self.buffer is self.mainBuffer:
2556-
self.update()
2557-
elif self.buffer is self.messageBuffer and keyboardHandler.keyCounter>self._keyCountForLastMessage:
2558-
self._dismissMessage()
2577+
self._regionsPendingUpdate.add(region)
25592578

25602579
def handleReviewMove(self, shouldAutoTether=True):
25612580
if not self.enabled:
@@ -2567,7 +2586,8 @@ def handleReviewMove(self, shouldAutoTether=True):
25672586
return
25682587
region = self.mainBuffer.regions[-1] if self.mainBuffer.regions else None
25692588
if region and region.obj == reviewPos.obj:
2570-
self._doCursorMove(region)
2589+
region.pendingCaretUpdate = True
2590+
self._regionsPendingUpdate.add(region)
25712591
else:
25722592
# We're reviewing a different object.
25732593
self._doNewObject(getFocusRegions(reviewPos.obj, review=True))
@@ -2720,14 +2740,15 @@ def initialize():
27202740
handler.setDisplayByName(config.conf["braille"]["display"])
27212741

27222742
def pumpAll():
2723-
"""Runs tasks at the end of each core cycle. For now just caret updates."""
2724-
handler.handlePendingCaretUpdate()
2743+
"""Runs tasks at the end of each core cycle. For now just region updates, e.g. for caret movement."""
2744+
handler._handlePendingUpdate()
27252745

27262746
def terminate():
27272747
global handler
27282748
handler.terminate()
27292749
handler = None
27302750

2751+
27312752
class BrailleDisplayDriver(driverHandler.Driver):
27322753
"""Abstract base braille display driver.
27332754
Each braille display driver should be a separate Python module in the root brailleDisplayDrivers directory

source/globalCommands.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3295,6 +3295,8 @@ def script_braille_toggleShowCursor(self, gesture):
32953295
# Translators: The message announced when toggling the braille cursor.
32963296
state = _("Braille cursor on")
32973297
config.conf["braille"]["showCursor"]=True
3298+
# To hide or show cursor immediately on braille line
3299+
braille.handler._updateDisplay()
32983300
ui.message(state)
32993301

33003302
@script(
@@ -3363,6 +3365,8 @@ def script_braille_cycleShowSelection(self, gesture: inputCore.InputGesture) ->
33633365
# Translators: Reports which show braille selection state is used
33643366
# (disabled or enabled).
33653367
msg = _("Braille show selection %s") % BoolFlag[nextName].displayString
3368+
# To hide or show selection immediately on braille line
3369+
braille.handler.initialDisplay()
33663370
ui.message(msg)
33673371

33683372
@script(

tests/unit/__init__.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,19 @@ def getFakeCellCount(numCells: int) -> int:
9292

9393

9494
braille.filter_displaySize.register(getFakeCellCount)
95+
_original_handleReviewMove = braille.handler.handleReviewMove
96+
97+
98+
def _patched_handleReviewMove(shouldAutoTether=True):
99+
"""
100+
For braille unit tests that rely on updating the review cursor, ensure that we simulate a core cycle
101+
by executing BrailleHandler._handlePendingUpdate after Braillehandler.handleReviewMove
102+
"""
103+
_original_handleReviewMove(shouldAutoTether)
104+
braille.handler._handlePendingUpdate()
105+
106+
107+
braille.handler.handleReviewMove = _patched_handleReviewMove
95108

96109
# Changing braille displays might call braille.handler.disableDetection(),
97110
# which requires the bdDetect.deviceInfoFetcher to be set.

0 commit comments

Comments
 (0)