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,11 @@ 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 : Optional [UpdateTimer ] = None
20452068 self ._cursorBlinkTimer = None
20462069 config .post_configProfileSwitch .register (self .handlePostConfigProfileSwitch )
20472070 if config .conf ["braille" ]["tetherTo" ] == TetherTo .AUTO .value :
@@ -2064,6 +2087,9 @@ def terminate(self):
20642087 if self ._cursorBlinkTimer :
20652088 self ._cursorBlinkTimer .Stop ()
20662089 self ._cursorBlinkTimer = None
2090+ if self ._updateTimer :
2091+ self ._updateTimer .Stop ()
2092+ self ._updateTimer = None
20672093 config .post_configProfileSwitch .unregister (self .handlePostConfigProfileSwitch )
20682094 if self .display :
20692095 self .display .terminate ()
@@ -2257,6 +2283,9 @@ def _onBrailleViewerChangedState(self, created):
22572283 log .debug ("Braille Viewer enabled: {}" .format (self .enabled ))
22582284
22592285 def _updateDisplay (self ):
2286+ if not self ._updateTimer :
2287+ self ._updateTimer = UpdateTimer ()
2288+ self ._updateTimer .Start (BRAILLE_UPDATE_CHECK_INTERVAL )
22602289 if self ._cursorBlinkTimer :
22612290 self ._cursorBlinkTimer .Stop ()
22622291 self ._cursorBlinkTimer = None
@@ -2332,6 +2361,7 @@ def _blink(self):
23322361 self ._displayWithCursor ()
23332362
23342363 def update (self ):
2364+ self ._alreadyUpdated = True
23352365 cells = self .buffer .windowBrailleCells
23362366 self ._rawText = self .buffer .windowRawText
23372367 if log .isEnabledFor (log .IO ):
@@ -2341,6 +2371,8 @@ def update(self):
23412371 self ._cells = cells + [0 ] * (self .displaySize - len (cells ))
23422372 self ._cursorPos = self .buffer .cursorWindowPos
23432373 self ._updateDisplay ()
2374+ self ._showSelection = config .conf ["braille" ]["showSelection" ]
2375+ self ._showCursor = config .conf ["braille" ]["showCursor" ]
23442376
23452377 def scrollForward (self ):
23462378 self .buffer .scrollForward ()
@@ -2468,6 +2500,12 @@ def handleCaretMove(
24682500 if shouldAutoTether :
24692501 self .setTether (TetherTo .FOCUS .value , auto = True )
24702502 if self ._tether != TetherTo .FOCUS .value :
2503+ # Braille display content is updated in case where:
2504+ # braille is tethered to review, review cursor does not follow system caret,
2505+ # and focus object is navigator object.
2506+ if not config .conf ["reviewCursor" ]["followCaret" ]:
2507+ if obj == api .getNavigatorObject ():
2508+ self .handleUpdate (obj )
24712509 return
24722510 region = self .mainBuffer .regions [- 1 ] if self .mainBuffer .regions else None
24732511 if region and region .obj == obj :
@@ -2686,6 +2724,28 @@ def _ackTimeoutResetter(self, param: int):
26862724 self .display ._awaitingAck = False
26872725 self ._writeCellsInBackground ()
26882726
2727+ def brailleUpdateCheck (self ) -> None :
2728+ """Braille may need update when show cursor or show selection state change or when in terminal window."""
2729+ if self .buffer is not self .mainBuffer :
2730+ return
2731+ if self ._alreadyUpdated :
2732+ self ._alreadyUpdated = False
2733+ return
2734+ obj : NVDAObject
2735+ if api .isObjectInActiveTreeInterceptor (api .getNavigatorObject ()):
2736+ obj = api .getCaretObject ()
2737+ elif self .getTether () == TetherTo .FOCUS .value :
2738+ obj = api .getFocusObject ()
2739+ else :
2740+ obj = api .getNavigatorObject ()
2741+ if (
2742+ hasattr (obj , "role" ) and obj .role == controlTypes .Role .TERMINAL
2743+ or self ._showSelection != config .conf ["braille" ]["showSelection" ]
2744+ ):
2745+ self .handleUpdate (obj )
2746+ elif self ._showCursor != config .conf ["braille" ]["showCursor" ]:
2747+ self .update ()
2748+
26892749
26902750# Maps old braille display driver names to new drivers that supersede old drivers.
26912751# Ensure that if a user has set a preferred driver which has changed name, the new
@@ -2728,6 +2788,7 @@ def terminate():
27282788 handler .terminate ()
27292789 handler = None
27302790
2791+
27312792class BrailleDisplayDriver (driverHandler .Driver ):
27322793 """Abstract base braille display driver.
27332794 Each braille display driver should be a separate Python module in the root brailleDisplayDrivers directory
0 commit comments