5252 OutputMode ,
5353 ReportSpellingErrors ,
5454)
55- from config .featureFlagEnums import ReviewRoutingMovesSystemCaretFlag , FontFormattingBrailleModeFlag
55+ from config .featureFlagEnums import (
56+ BrailleTextWrapFlag ,
57+ FontFormattingBrailleModeFlag ,
58+ ReviewRoutingMovesSystemCaretFlag ,
59+ )
5660from logHandler import log
5761import controlTypes
5862import api
326330 (0xFF , _ ("All dots" )),
327331)
328332SELECTION_SHAPE = 0xC0 #: Dots 7 and 8
333+ CONTINUATION_SHAPE = 0xC0 #: Dots 7 and 8
329334
330335END_OF_BRAILLE_OUTPUT_SHAPE = 0xFF # All dots
331336"""
@@ -1385,7 +1390,12 @@ def _getTypeformFromFormatField(self, field, formatConfig):
13851390 typeform |= louis .underline
13861391 return typeform
13871392
1388- def _addFieldText (self , text , contentPos , separate = True ):
1393+ def _addFieldText (
1394+ self ,
1395+ text : str ,
1396+ contentPos : int ,
1397+ separate : bool = True ,
1398+ ):
13891399 if separate and self .rawText :
13901400 # Separate this field text from the rest of the text.
13911401 text = TEXT_SEPARATOR + text
@@ -1813,10 +1823,12 @@ def rindex(seq, item, start, end):
18131823
18141824
18151825class BrailleBuffer (baseObject .AutoPropertyObject ):
1826+ handler : "BrailleHandler"
1827+ regions : list [Region ]
1828+ """The regions in this buffer."""
1829+
18161830 def __init__ (self , handler ):
18171831 self .handler = handler
1818- #: The regions in this buffer.
1819- #: @type: [L{Region}, ...]
18201832 self .regions = []
18211833 #: The raw text of the entire buffer.
18221834 self .rawText = ""
@@ -1832,6 +1844,8 @@ def __init__(self, handler):
18321844 each item being a tuple of start and end braille buffer offsets.
18331845 Splitting the window into independent rows allows for optional avoidance of splitting words across rows.
18341846 """
1847+ self ._continuationRows : list [int ] = []
1848+ """A list of row indexes which should contain a continuation indicator at the end."""
18351849
18361850 def clear (self ):
18371851 """Clear the entire buffer.
@@ -1860,35 +1874,45 @@ def _get_regionsWithPositions(self):
18601874 yield RegionWithPositions (region , start , end )
18611875 start = end
18621876
1863- def _get_rawToBraillePos (self ):
1864- """@return: a list mapping positions in L{rawText} to positions in L{brailleCells} for the entire buffer.
1865- @rtype: [int, ...]
1866- """
1877+ rawToBraillePos : list [int ]
1878+ """Type definition for auto prop '_get_rawToBraillePos'"""
1879+
1880+ def _get_rawToBraillePos (self ) -> list [int ]:
1881+ """:return: a list mapping positions in L{rawText} to positions in L{brailleCells} for the entire buffer."""
18671882 rawToBraillePos = []
18681883 for region , regionStart , regionEnd in self .regionsWithPositions :
18691884 rawToBraillePos .extend (p + regionStart for p in region .rawToBraillePos )
18701885 return rawToBraillePos
18711886
1872- brailleToRawPos : List [int ]
1887+ brailleToRawPos : list [int ]
1888+ """Type definition for auto prop '_get_brailleToRawPos'"""
18731889
1874- def _get_brailleToRawPos (self ):
1875- """@return: a list mapping positions in L{brailleCells} to positions in L{rawText} for the entire buffer.
1876- @rtype: [int, ...]
1877- """
1890+ def _get_brailleToRawPos (self ) -> list [int ]:
1891+ """:return: a list mapping positions in L{brailleCells} to positions in L{rawText} for the entire buffer."""
18781892 brailleToRawPos = []
18791893 start = 0
18801894 for region in self .visibleRegions :
18811895 brailleToRawPos .extend (p + start for p in region .brailleToRawPos )
18821896 start += len (region .rawText )
18831897 return brailleToRawPos
18841898
1885- def bufferPosToRegionPos (self , bufferPos ):
1899+ def bufferPosToRegionPos (self , bufferPos : int ) -> tuple [Region , int ]:
1900+ """Converts a position relative to the braille buffer to a position relative to the region it is in.
1901+ :param bufferPos: The position relative to the braille buffer.
1902+ :return: A tuple of the region and the position relative to that region.
1903+ """
18861904 for region , start , end in self .regionsWithPositions :
18871905 if end > bufferPos :
18881906 return region , bufferPos - start
18891907 raise LookupError ("No such position" )
18901908
1891- def regionPosToBufferPos (self , region , pos , allowNearest = False ):
1909+ def regionPosToBufferPos (self , region : Region , pos : int , allowNearest : bool = False ) -> int :
1910+ """Converts a position relative to a region to a position relative to the braille buffer.
1911+ :param region: The region the position is relative to.
1912+ :param pos: The position relative to the region.
1913+ :param allowNearest: If True, if the position is outside the region, return the nearest position within the region. If False, raise LookupError if the position is outside the region.
1914+ :return: The position relative to the braille buffer.
1915+ """
18921916 start : int = 0
18931917 for testRegion , start , end in self .regionsWithPositions :
18941918 if region == testRegion :
@@ -1905,7 +1929,13 @@ def regionPosToBufferPos(self, region, pos, allowNearest=False):
19051929 return start
19061930 raise LookupError ("No such position" )
19071931
1908- def bufferPositionsToRawText (self , startPos , endPos ):
1932+ def bufferPositionsToRawText (self , startPos : int , endPos : int ) -> str :
1933+ """
1934+ Converts a range of positions in the braille buffer to the corresponding raw text.
1935+ :param startPos: The start position in the braille buffer.
1936+ :param endPos: The end position in the braille buffer.
1937+ :return: The corresponding raw text.
1938+ """
19091939 brailleToRawPos = self .brailleToRawPos
19101940 if not brailleToRawPos or not self .rawText :
19111941 # if either are empty, just return an empty string.
@@ -1927,6 +1957,11 @@ def bufferPositionsToRawText(self, startPos, endPos):
19271957 return ""
19281958
19291959 def bufferPosToWindowPos (self , bufferPos : int ) -> int :
1960+ """
1961+ Converts a position relative to the braille buffer to a position relative to the braille window.
1962+ :param bufferPos: The position relative to the braille buffer.
1963+ :return: The position relative to the braille window.
1964+ """
19301965 for row , (start , end ) in enumerate (self ._windowRowBufferOffsets ):
19311966 if start <= bufferPos < end :
19321967 return row * self .handler .displayDimensions .numCols + (bufferPos - start )
@@ -1957,32 +1992,47 @@ def _set_windowStartPos(self, pos: int) -> None:
19571992 def _calculateWindowRowBufferOffsets (self , pos : int ) -> None :
19581993 """
19591994 Calculates the start and end positions of each row in the braille window.
1960- Ensures that words are not split across rows when word wrap is enabled.
1995+ Ensures that words are not split across rows when text wrap is enabled.
19611996 Ensures that the window does not extend past the end of the braille buffer.
19621997 :param pos: The start position of the braille window.
19631998 """
19641999 self ._windowRowBufferOffsets .clear ()
2000+ self ._continuationRows .clear ()
19652001 if len (self .brailleCells ) == 0 :
19662002 # Initialising with no actual braille content.
19672003 self ._windowRowBufferOffsets = [(0 , 0 )]
19682004 return
1969- doWordWrap = config .conf ["braille" ]["wordWrap" ]
2005+ textWrap : BrailleTextWrapFlag = config .conf ["braille" ]["textWrap" ]. calculated ()
19702006 bufferEnd = len (self .brailleCells )
19712007 start = pos
19722008 clippedEnd = False
19732009 for row in range (self .handler .displayDimensions .numRows ):
2010+ showContinuationMark = False
19742011 end = start + self .handler .displayDimensions .numCols
19752012 if end > bufferEnd :
19762013 end = bufferEnd
19772014 clippedEnd = True
1978- elif doWordWrap :
2015+ elif (
2016+ textWrap == BrailleTextWrapFlag .MARK_WORD_CUTS
2017+ and end < bufferEnd
2018+ and all (self .brailleCells [end - 1 : end + 1 ])
2019+ ):
2020+ end -= 1
2021+ showContinuationMark = True
2022+ elif textWrap == BrailleTextWrapFlag .AT_WORD_BOUNDARIES :
19792023 try :
19802024 lastSpaceIndex = rindex (self .brailleCells , 0 , start , end + 1 )
19812025 if lastSpaceIndex < end :
19822026 # The next braille window doesn't start with space.
19832027 end = rindex (self .brailleCells , 0 , start , end ) + 1
19842028 except (ValueError , IndexError ):
1985- pass # No space on line
2029+ # No space on line - fall back to display-edge cut.
2030+ if all (self .brailleCells [end - 1 : end + 1 ]):
2031+ if end - start == self .handler .displayDimensions .numCols and end < bufferEnd :
2032+ end -= 1
2033+ showContinuationMark = True
2034+ if showContinuationMark :
2035+ self ._continuationRows .append (len (self ._windowRowBufferOffsets ))
19862036 self ._windowRowBufferOffsets .append ((start , end ))
19872037 if clippedEnd :
19882038 break
@@ -2001,7 +2051,7 @@ def _set_windowEndPos(self, endPos: int) -> None:
20012051 2. Whether one of the regions should be shown hard left on the braille display;
20022052 i.e. because of The configuration setting for focus context representation
20032053 or whether the braille region that corresponds with the focus represents a multi line edit box.
2004- 3. Whether word wrap is enabled."""
2054+ 3. Whether text wrap is enabled."""
20052055 startPos = endPos - self .handler .displaySize
20062056 # Loop through the currently displayed regions in reverse order
20072057 # If focusToHardLeft is set for one of the regions, the display shouldn't scroll further back than the start of that region
@@ -2022,7 +2072,10 @@ def _set_windowEndPos(self, endPos: int) -> None:
20222072 if startPos <= restrictPos :
20232073 self .windowStartPos = restrictPos
20242074 return
2025- if not config .conf ["braille" ]["wordWrap" ]:
2075+ if config .conf ["braille" ]["textWrap" ].calculated () in (
2076+ BrailleTextWrapFlag .NONE ,
2077+ BrailleTextWrapFlag .MARK_WORD_CUTS ,
2078+ ):
20262079 self .windowStartPos = startPos
20272080 return
20282081 try :
@@ -2037,7 +2090,7 @@ def _set_windowEndPos(self, endPos: int) -> None:
20372090 break
20382091 except ValueError :
20392092 pass
2040- # When word wrap is enabled, the first block of spaces may be removed from the current window.
2093+ # When text wrap is enabled, the first block of spaces may be removed from the current window.
20412094 # This may prevent displaying the start of paragraphs.
20422095 paragraphStartMarker = getParagraphStartMarker ()
20432096 if paragraphStartMarker and self .regions [- 1 ].rawText .startswith (
@@ -2144,9 +2197,12 @@ def _get_windowRawText(self):
21442197
21452198 def _get_windowBrailleCells (self ) -> list [int ]:
21462199 windowCells = []
2147- for start , end in self ._windowRowBufferOffsets :
2200+ for row , ( start , end ) in enumerate ( self ._windowRowBufferOffsets ) :
21482201 rowCells = self .brailleCells [start :end ]
21492202 remaining = self .handler .displayDimensions .numCols - len (rowCells )
2203+ if remaining > 0 and row in self ._continuationRows :
2204+ rowCells .append (CONTINUATION_SHAPE )
2205+ remaining -= 1
21502206 if remaining > 0 :
21512207 rowCells .extend ([0 ] * remaining )
21522208 windowCells .extend (rowCells )
0 commit comments