4242 TetherTo ,
4343 ReportTableHeaders ,
4444)
45+ from config .featureFlag import FeatureFlag
4546from config .featureFlagEnums import ReviewRoutingMovesSystemCaretFlag
4647from logHandler import log
4748import controlTypes
@@ -1569,6 +1570,7 @@ def bufferPosToRegionPos(self, bufferPos):
15691570 raise LookupError ("No such position" )
15701571
15711572 def regionPosToBufferPos (self , region , pos , allowNearest = False ):
1573+ start : int = 0
15721574 for testRegion , start , end in self .regionsWithPositions :
15731575 if region == testRegion :
15741576 if pos < end - start :
@@ -2003,6 +2005,22 @@ def formatCellsForLog(cells: List[int]) -> str:
20032005"""
20042006
20052007
2008+ class UpdateTimer (wx .Timer ):
2009+ """Repeating timer for keeping display content always up to date."""
2010+
2011+ def __init__ (self ):
2012+ """Constructor."""
2013+ super ().__init__ ()
2014+
2015+ def Notify (self ) -> None :
2016+ """Check if braille needs update."""
2017+ handler .brailleUpdateCheck ()
2018+
2019+
2020+ BRAILLE_UPDATE_CHECK_INTERVAL : int = 500
2021+ """Timer interval in milliseconds for L{BrailleHandler._brailleUpdateCheck}."""
2022+
2023+
20062024class BrailleHandler (baseObject .AutoPropertyObject ):
20072025 # TETHER_AUTO, TETHER_FOCUS, TETHER_REVIEW and tetherValues
20082026 # are deprecated, but remain to retain API backwards compatibility
@@ -2042,6 +2060,12 @@ def __init__(self):
20422060 self ._cursorPos = None
20432061 self ._cursorBlinkUp = True
20442062 self ._cells = []
2063+ self ._showSelection : FeatureFlag = config .conf ["braille" ]["showSelection" ]
2064+ self ._showCursor : bool = config .conf ["braille" ]["showCursor" ]
2065+ # Was braille line updated during previous timer cycle.
2066+ self ._alreadyUpdated : bool = False
2067+ self ._updateTimer = UpdateTimer ()
2068+ self ._updateTimer .Start (BRAILLE_UPDATE_CHECK_INTERVAL )
20452069 self ._cursorBlinkTimer = None
20462070 config .post_configProfileSwitch .register (self .handlePostConfigProfileSwitch )
20472071 if config .conf ["braille" ]["tetherTo" ] == TetherTo .AUTO .value :
@@ -2064,6 +2088,9 @@ def terminate(self):
20642088 if self ._cursorBlinkTimer :
20652089 self ._cursorBlinkTimer .Stop ()
20662090 self ._cursorBlinkTimer = None
2091+ if self ._updateTimer :
2092+ self ._updateTimer .Stop ()
2093+ self ._updateTimer = None
20672094 config .post_configProfileSwitch .unregister (self .handlePostConfigProfileSwitch )
20682095 if self .display :
20692096 self .display .terminate ()
@@ -2332,6 +2359,7 @@ def _blink(self):
23322359 self ._displayWithCursor ()
23332360
23342361 def update (self ):
2362+ self ._alreadyUpdated = True
23352363 cells = self .buffer .windowBrailleCells
23362364 self ._rawText = self .buffer .windowRawText
23372365 if log .isEnabledFor (log .IO ):
@@ -2341,6 +2369,8 @@ def update(self):
23412369 self ._cells = cells + [0 ] * (self .displaySize - len (cells ))
23422370 self ._cursorPos = self .buffer .cursorWindowPos
23432371 self ._updateDisplay ()
2372+ self ._showSelection = config .conf ["braille" ]["showSelection" ]
2373+ self ._showCursor = config .conf ["braille" ]["showCursor" ]
23442374
23452375 def scrollForward (self ):
23462376 self .buffer .scrollForward ()
@@ -2468,6 +2498,12 @@ def handleCaretMove(
24682498 if shouldAutoTether :
24692499 self .setTether (TetherTo .FOCUS .value , auto = True )
24702500 if self ._tether != TetherTo .FOCUS .value :
2501+ # Braille display content is updated in case where:
2502+ # braille is tethered to review, review cursor does not follow system caret,
2503+ # and focus object is navigator object.
2504+ if not config .conf ["reviewCursor" ]["followCaret" ]:
2505+ if obj == api .getNavigatorObject ():
2506+ self .handleUpdate (obj )
24712507 return
24722508 region = self .mainBuffer .regions [- 1 ] if self .mainBuffer .regions else None
24732509 if region and region .obj == obj :
@@ -2686,6 +2722,28 @@ def _ackTimeoutResetter(self, param: int):
26862722 self .display ._awaitingAck = False
26872723 self ._writeCellsInBackground ()
26882724
2725+ def brailleUpdateCheck (self ) -> None :
2726+ """Braille may need update when show cursor or show selection state change or when in terminal window."""
2727+ if self .buffer is not self .mainBuffer :
2728+ return
2729+ if self ._alreadyUpdated :
2730+ self ._alreadyUpdated = False
2731+ return
2732+ obj : NVDAObject
2733+ if api .isObjectInActiveTreeInterceptor (api .getNavigatorObject ()):
2734+ obj = api .getCaretObject ()
2735+ elif self .getTether () == TetherTo .FOCUS .value :
2736+ obj = api .getFocusObject ()
2737+ else :
2738+ obj = api .getNavigatorObject ()
2739+ if (
2740+ hasattr (obj , "role" ) and obj .role == controlTypes .Role .TERMINAL
2741+ or self ._showSelection != config .conf ["braille" ]["showSelection" ]
2742+ ):
2743+ self .handleUpdate (obj )
2744+ elif self ._showCursor != config .conf ["braille" ]["showCursor" ]:
2745+ self .update ()
2746+
26892747
26902748# Maps old braille display driver names to new drivers that supersede old drivers.
26912749# Ensure that if a user has set a preferred driver which has changed name, the new
@@ -2728,6 +2786,7 @@ def terminate():
27282786 handler .terminate ()
27292787 handler = None
27302788
2789+
27312790class BrailleDisplayDriver (driverHandler .Driver ):
27322791 """Abstract base braille display driver.
27332792 Each braille display driver should be a separate Python module in the root brailleDisplayDrivers directory
0 commit comments