6161from autoSettingsUtils .driverSetting import BooleanDriverSetting , NumericDriverSetting
6262from utils .security import objectBelowLockScreenAndWindowsIsLocked
6363import hwIo
64+ from buildVersion import version_year
65+ import NVDAState
6466
6567if TYPE_CHECKING :
6668 from NVDAObjects import NVDAObject
@@ -2014,6 +2016,11 @@ class BrailleHandler(baseObject.AutoPropertyObject):
20142016 queuedWrite : Optional [List [int ]] = None
20152017 queuedWriteLock : threading .Lock
20162018 ackTimerHandle : int
2019+ _regionsPendingUpdate : Set [Region ]
2020+ """
2021+ Regions pending an update.
2022+ Regions are added by L{handleUpdate} and L{handleCaretMove} and cleared in L{_handlePendingUpdate}.
2023+ """
20172024
20182025 def __init__ (self ):
20192026 louisHelper .initialize ()
@@ -2032,6 +2039,7 @@ def __init__(self):
20322039 with its previous output.
20332040 If L{decide_enabled} decides to disable the handler, pending output should be cleared.
20342041 """
2042+ self ._regionsPendingUpdate = set ()
20352043
20362044
20372045 self .mainBuffer = BrailleBuffer (self )
@@ -2472,29 +2480,46 @@ def handleCaretMove(
24722480 region = self .mainBuffer .regions [- 1 ] if self .mainBuffer .regions else None
24732481 if region and region .obj == obj :
24742482 region .pendingCaretUpdate = True
2483+ self ._regionsPendingUpdate .add (region )
24752484 elif prevTether == TetherTo .REVIEW .value :
24762485 # The caret moved in a different object than the review position.
24772486 self ._doNewObject (getFocusRegions (obj , review = False ))
24782487
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
2488+ if version_year < 2024 and NVDAState . _allowDeprecatedAPI ( ):
2489+ def handlePendingCaretUpdate ( self ):
2490+ log . warning (
2491+ "braille.BrailleHandler.handlePendingCaretUpdate is now deprecated "
2492+ "with no public replacement. "
2493+ "It will be removed in NVDA 2024.1."
2494+ )
2495+ self . _handlePendingUpdate ()
24872496
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 ()
2497+ def _handlePendingUpdate (self ):
2498+ """When any region is pending an update, updates the region and the braille display.
2499+ """
2500+ if not self ._regionsPendingUpdate :
2501+ return
2502+ try :
2503+ scrollTo : Optional [TextInfoRegion ] = None
2504+ self .mainBuffer .saveWindow ()
2505+ for region in self ._regionsPendingUpdate :
2506+ region .update ()
2507+ if isinstance (region , TextInfoRegion ) and region .pendingCaretUpdate :
2508+ scrollTo = region
2509+ region .pendingCaretUpdate = False
2510+ self .mainBuffer .update ()
2511+ self .mainBuffer .restoreWindow ()
2512+ if scrollTo is not None :
2513+ self .scrollToCursorOrSelection (scrollTo )
2514+ if self .buffer is self .mainBuffer :
2515+ self .update ()
2516+ elif (
2517+ self .buffer is self .messageBuffer
2518+ and keyboardHandler .keyCounter > self ._keyCountForLastMessage
2519+ ):
2520+ self ._dismissMessage ()
2521+ finally :
2522+ self ._regionsPendingUpdate .clear ()
24982523
24992524 def scrollToCursorOrSelection (self , region ):
25002525 if region .brailleCursorPos is not None :
@@ -2548,14 +2573,7 @@ def handleUpdate(self, obj: "NVDAObject") -> None:
25482573 if isinstance (obj , NVDAObject ) and obj .role == controlTypes .Role .PROGRESSBAR and obj .isInForeground :
25492574 self ._handleProgressBarUpdate (obj )
25502575 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 ()
2576+ self ._regionsPendingUpdate .add (region )
25592577
25602578 def handleReviewMove (self , shouldAutoTether = True ):
25612579 if not self .enabled :
@@ -2567,7 +2585,7 @@ def handleReviewMove(self, shouldAutoTether=True):
25672585 return
25682586 region = self .mainBuffer .regions [- 1 ] if self .mainBuffer .regions else None
25692587 if region and region .obj == reviewPos .obj :
2570- self ._doCursorMove (region )
2588+ self ._regionsPendingUpdate . add (region )
25712589 else :
25722590 # We're reviewing a different object.
25732591 self ._doNewObject (getFocusRegions (reviewPos .obj , review = True ))
@@ -2720,8 +2738,8 @@ def initialize():
27202738 handler .setDisplayByName (config .conf ["braille" ]["display" ])
27212739
27222740def pumpAll ():
2723- """Runs tasks at the end of each core cycle. For now just caret updates."""
2724- handler .handlePendingCaretUpdate ()
2741+ """Runs tasks at the end of each core cycle. For now just region updates, e.g. for caret movement ."""
2742+ handler ._handlePendingUpdate ()
27252743
27262744def terminate ():
27272745 global handler
0 commit comments