Skip to content

Commit

Permalink
When using a braille display and text is selected on the current line…
Browse files Browse the repository at this point in the history
… (e.g. when searching in a text editor for text which occurs on the same line), the braille display will be scrolled if appropriate.

On a pending caret move if there is no cursor, try to scroll the braille display to the selection.

* Move the marking of a selection to braille.Region.
* Add Region.brailleSelectionStart/End.
* If there is a selection but not a cursor, e.g. in a TextInfoRegion, scroll the braille display to the selection start.

Fixes #5410.
  • Loading branch information
dkager authored and jcsteh committed Dec 9, 2015
1 parent f7fec7e commit a5afa2c
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 21 deletions.
61 changes: 40 additions & 21 deletions source/braille.py
Expand Up @@ -401,8 +401,8 @@ def getDisplayList():
class Region(object):
"""A region of braille to be displayed.
Each portion of braille to be displayed is represented by a region.
The region is responsible for retrieving its text and cursor position, translating it into braille cells and handling cursor routing requests relative to its braille cells.
The L{BrailleBuffer} containing this region will call L{update} and expect that L{brailleCells} and L{brailleCursorPos} will be set appropriately.
The region is responsible for retrieving its text and the cursor and selection positions, translating it into braille cells and handling cursor routing requests relative to its braille cells.
The L{BrailleBuffer} containing this region will call L{update} and expect that L{brailleCells}, L{brailleCursorPos}, L{brailleSelectionStart} and L{brailleSelectionEnd} will be set appropriately.
L{routeTo} will be called to handle a cursor routing request.
"""

Expand All @@ -412,6 +412,12 @@ def __init__(self):
#: The position of the cursor in L{rawText}, C{None} if the cursor is not in this region.
#: @type: int
self.cursorPos = None
#: The start of the selection in L{rawText} (inclusive), C{None} if there is no selection in this region.
#: @type: int
self.selectionStart = None
#: The end of the selection in L{rawText} (exclusive), C{None} if there is no selection in this region.
#: @type: int
self.selectionEnd = None
#: The translated braille representation of this region.
#: @type: [int, ...]
self.brailleCells = []
Expand All @@ -428,6 +434,12 @@ def __init__(self):
#: The position of the cursor in L{brailleCells}, C{None} if the cursor is not in this region.
#: @type: int
self.brailleCursorPos = None
#: The position of the selection start in L{brailleCells}, C{None} if there is no selection in this region.
#: @type: int
self.brailleSelectionStart = None
#: The position of the selection end in L{brailleCells}, C{None} if there is no selection in this region.
#: @type: int
self.brailleSelectionEnd = None
#: Whether to hide all previous regions.
#: @type: bool
self.hidePreviousRegions = False
Expand All @@ -437,12 +449,12 @@ def __init__(self):

def update(self):
"""Update this region.
Subclasses should extend this to update L{rawText} and L{cursorPos} if necessary.
Subclasses should extend this to update L{rawText}, L{cursorPos}, L{selectionStart} and L{selectionEnd} if necessary.
The base class method handles translation of L{rawText} into braille, placing the result in L{brailleCells}.
Typeform information from L{rawTextTypeforms} is used, if any.
L{rawToBraillePos} and L{brailleToRawPos} are updated according to the translation.
L{brailleCursorPos} is similarly updated based on L{cursorPos}.
@postcondition: L{brailleCells} and L{brailleCursorPos} are updated and ready for rendering.
L{brailleCursorPos}, L{brailleSelectionStart} and L{brailleSelectionEnd} are similarly updated based on L{cursorPos}, L{selectionStart} and L{selectionEnd}, respectively.
@postcondition: L{brailleCells}, L{brailleCursorPos}, L{brailleSelectionStart} and L{brailleSelectionEnd} are updated and ready for rendering.
"""
mode = louis.dotsIO | louis.pass1Only
if config.conf["braille"]["expandAtCursor"] and self.cursorPos is not None:
Expand Down Expand Up @@ -477,6 +489,18 @@ def update(self):
else:
brailleCursorPos = None
self.brailleCursorPos = brailleCursorPos
if self.selectionStart is not None and self.selectionEnd is not None:
try:
# Mark the selection with dots 7 and 8.
self.brailleSelectionStart = self.rawToBraillePos[self.selectionStart]
if self.selectionEnd >= len(self.rawText):
self.brailleSelectionEnd = len(self.brailleCells)
else:
self.brailleSelectionEnd = self.rawToBraillePos[self.selectionEnd]
for pos in xrange(self.brailleSelectionStart, self.brailleSelectionEnd):
self.brailleCells[pos] |= DOT7 | DOT8
except IndexError:
pass

def routeTo(self, braillePos):
"""Handle a cursor routing request.
Expand Down Expand Up @@ -784,9 +808,9 @@ def _addTextWithFields(self, info, formatConfig, isSelection=False):
self.rawText += " "
self.rawTextTypeforms.append(louis.plain_text)
self._rawToContentPos.append(self._currentContentPos)
if isSelection and self._selectionStart is None:
if isSelection and self.selectionStart is None:
# This is where the content begins.
self._selectionStart = len(self.rawText)
self.selectionStart = len(self.rawText)
elif shouldMoveCursorToFirstContent:
# This is the first piece of content after the cursor.
# Position the cursor here, as it may currently be positioned on control field text.
Expand All @@ -800,7 +824,7 @@ def _addTextWithFields(self, info, formatConfig, isSelection=False):
self._currentContentPos = endPos
if isSelection:
# The last time this is set will be the end of the content.
self._selectionEnd = len(self.rawText)
self.selectionEnd = len(self.rawText)
self._endsWithField = False
elif isinstance(command, textInfos.FieldCommand):
cmd = command.command
Expand Down Expand Up @@ -828,8 +852,8 @@ def _addTextWithFields(self, info, formatConfig, isSelection=False):
if fieldStart > 0:
# There'll be a space before the field text.
fieldStart += 1
if isSelection and self._selectionStart is None:
self._selectionStart = fieldStart
if isSelection and self.selectionStart is None:
self.selectionStart = fieldStart
elif shouldMoveCursorToFirstContent:
self.cursorPos = fieldStart
shouldMoveCursorToFirstContent = False
Expand All @@ -843,7 +867,7 @@ def _addTextWithFields(self, info, formatConfig, isSelection=False):
# Map this field text to the end of the field's content.
self._addFieldText(text, self._currentContentPos - 1)
self._endsWithField = True
if isSelection and self._selectionStart is None:
if isSelection and self.selectionStart is None:
# There is no selection. This is a cursor.
self.cursorPos = len(self.rawText)
if not self._skipFieldsNotAtStartOfNode:
Expand Down Expand Up @@ -877,7 +901,7 @@ def update(self):
# Therefore, maintain a map of positions in the output to positions in the content.
self._rawToContentPos = []
self._currentContentPos = 0
self._selectionStart = self._selectionEnd = None
self.selectionStart = self.selectionEnd = None
self._skipFieldsNotAtStartOfNode = False
self._endsWithField = False

Expand Down Expand Up @@ -922,15 +946,6 @@ def update(self):
self.focusToHardLeft = self._isMultiline()
super(TextInfoRegion, self).update()

if self._selectionStart is not None:
# Mark the selection with dots 7 and 8.
if self._selectionEnd >= len(self.rawText):
brailleSelEnd = len(self.brailleCells)
else:
brailleSelEnd = self.rawToBraillePos[self._selectionEnd]
for pos in xrange(self.rawToBraillePos[self._selectionStart], brailleSelEnd):
self.brailleCells[pos] |= DOT7 | DOT8

def routeTo(self, braillePos):
if braillePos == self.brailleCursorPos:
# The cursor is already at this position,
Expand Down Expand Up @@ -1537,6 +1552,8 @@ def _doNewObject(self, regions):
self.mainBuffer.focus(region)
if region.brailleCursorPos is not None:
self.mainBuffer.scrollTo(region, region.brailleCursorPos)
elif region.brailleSelectionStart is not None:
self.mainBuffer.scrollTo(region, region.brailleSelectionStart)
if self.buffer is self.mainBuffer:
self.update()
elif self.buffer is self.messageBuffer and keyboardHandler.keyCounter>self._keyCountForLastMessage:
Expand Down Expand Up @@ -1570,6 +1587,8 @@ def _doCursorMove(self, region):
self.mainBuffer.restoreWindow()
if region.brailleCursorPos is not None:
self.mainBuffer.scrollTo(region, region.brailleCursorPos)
elif region.brailleSelectionStart is not None:
self.mainBuffer.scrollTo(region, region.brailleSelectionStart)
if self.buffer is self.mainBuffer:
self.update()
elif self.buffer is self.messageBuffer and keyboardHandler.keyCounter>self._keyCountForLastMessage:
Expand Down
1 change: 1 addition & 0 deletions user_docs/en/changes.t2t
Expand Up @@ -22,6 +22,7 @@
- When toggling between browse mode and focus mode, the mode is reported in braille as well as speech. (#5239)
- The Start buttn on the Taskbar is no longer reported as a list and/or as selected in some versions of Windows. (#5178)
- Messages such as "inserted" are no longer reported when composing messages in Microsoft Outlook. (#5486)
- When using a braille display and text is selected on the current line (e.g. when searching in a text editor for text which occurs on the same line), the braille display will be scrolled if appropriate. (#5410)


= 2015.4 =
Expand Down

0 comments on commit a5afa2c

Please sign in to comment.