@@ -1569,6 +1569,7 @@ def bufferPosToRegionPos(self, bufferPos):
15691569 raise LookupError ("No such position" )
15701570
15711571 def regionPosToBufferPos (self , region , pos , allowNearest = False ):
1572+ start : int = 0
15721573 for testRegion , start , end in self .regionsWithPositions :
15731574 if region == testRegion :
15741575 if pos < end - start :
@@ -2003,6 +2004,10 @@ def formatCellsForLog(cells: List[int]) -> str:
20032004"""
20042005
20052006
2007+ UPDATE_DISPLAY_PERIODICALLY_INTERVAL : int = 250
2008+ """Timer interval for L{BrailleHandler._updateDisplayPeriodically}."""
2009+
2010+
20062011class BrailleHandler (baseObject .AutoPropertyObject ):
20072012 # TETHER_AUTO, TETHER_FOCUS, TETHER_REVIEW and tetherValues
20082013 # are deprecated, but remain to retain API backwards compatibility
@@ -2042,6 +2047,8 @@ def __init__(self):
20422047 self ._cursorPos = None
20432048 self ._cursorBlinkUp = True
20442049 self ._cells = []
2050+ self ._oldCells : List [int ] = []
2051+ wx .CallAfter (self ._updateTimerInit )
20452052 self ._cursorBlinkTimer = None
20462053 config .post_configProfileSwitch .register (self .handlePostConfigProfileSwitch )
20472054 if config .conf ["braille" ]["tetherTo" ] == TetherTo .AUTO .value :
@@ -2064,6 +2071,9 @@ def terminate(self):
20642071 if self ._cursorBlinkTimer :
20652072 self ._cursorBlinkTimer .Stop ()
20662073 self ._cursorBlinkTimer = None
2074+ if self ._updateTimer :
2075+ self ._updateTimer .Stop ()
2076+ self ._updateTimer = None
20672077 config .post_configProfileSwitch .unregister (self .handlePostConfigProfileSwitch )
20682078 if self .display :
20692079 self .display .terminate ()
@@ -2468,6 +2478,12 @@ def handleCaretMove(
24682478 if shouldAutoTether :
24692479 self .setTether (TetherTo .FOCUS .value , auto = True )
24702480 if self ._tether != TetherTo .FOCUS .value :
2481+ # Braille display content is updated in case where:
2482+ # braille is tethered to review, review cursor does not follow system caret,
2483+ # and focus object is navigator object.
2484+ if not config .conf ["reviewCursor" ]["followCaret" ]:
2485+ if obj == api .getNavigatorObject ():
2486+ self .handleUpdate (obj )
24712487 return
24722488 region = self .mainBuffer .regions [- 1 ] if self .mainBuffer .regions else None
24732489 if region and region .obj == obj :
@@ -2552,10 +2568,13 @@ def handleUpdate(self, obj: "NVDAObject") -> None:
25522568 region .update ()
25532569 self .mainBuffer .update ()
25542570 self .mainBuffer .restoreWindow ()
2571+ if self ._oldCells == self .buffer .windowBrailleCells :
2572+ return
25552573 if self .buffer is self .mainBuffer :
25562574 self .update ()
25572575 elif self .buffer is self .messageBuffer and keyboardHandler .keyCounter > self ._keyCountForLastMessage :
25582576 self ._dismissMessage ()
2577+ self ._oldCells = self .buffer .windowBrailleCells .copy ()
25592578
25602579 def handleReviewMove (self , shouldAutoTether = True ):
25612580 if not self .enabled :
@@ -2686,6 +2705,25 @@ def _ackTimeoutResetter(self, param: int):
26862705 self .display ._awaitingAck = False
26872706 self ._writeCellsInBackground ()
26882707
2708+ def _updateTimerInit (self ) -> None :
2709+ """Initializes update timer for checking if braille needs update."""
2710+ self ._updateTimer = gui .NonReEntrantTimer (self ._updateDisplayPeriodically )
2711+ # Start from main thread
2712+ self ._updateTimer .Start (UPDATE_DISPLAY_PERIODICALLY_INTERVAL )
2713+
2714+ def _updateDisplayPeriodically (self ):
2715+ """Timer runs this function periodically to check if braille needs update."""
2716+ if self .buffer is not self .mainBuffer :
2717+ return
2718+ obj : NVDAObject
2719+ if api .isObjectInActiveTreeInterceptor (api .getNavigatorObject ()):
2720+ obj = api .getCaretObject ()
2721+ elif handler .getTether () == TetherTo .FOCUS .value :
2722+ obj = api .getFocusObject ()
2723+ else :
2724+ obj = api .getNavigatorObject ()
2725+ self .handleUpdate (obj )
2726+
26892727
26902728# Maps old braille display driver names to new drivers that supersede old drivers.
26912729# Ensure that if a user has set a preferred driver which has changed name, the new
@@ -2728,6 +2766,7 @@ def terminate():
27282766 handler .terminate ()
27292767 handler = None
27302768
2769+
27312770class BrailleDisplayDriver (driverHandler .Driver ):
27322771 """Abstract base braille display driver.
27332772 Each braille display driver should be a separate Python module in the root brailleDisplayDrivers directory
0 commit comments