Skip to content

Commit

Permalink
It is now possible to have braille displayed by paragraphs instead of…
Browse files Browse the repository at this point in the history
… lines, which may allow for more fluent reading of large amounts of text. This is configurable using the Read by paragraphs option in the Braille Settings dialog.

Fixes #1891.
  • Loading branch information
jcsteh committed Nov 17, 2011
2 parents 634a20f + f3406a0 commit 4c38e73
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 30 deletions.
64 changes: 36 additions & 28 deletions source/braille.py
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ def getFormatFieldBraille(field):
if linePrefix:
return linePrefix
return None

class TextInfoRegion(Region):

def __init__(self, obj):
Expand Down Expand Up @@ -527,25 +527,29 @@ def _addTextWithFields(self, info, formatConfig, isSelection=False):
# There is no selection. This is a cursor.
self.cursorPos = len(self.rawText)
if not self._skipFieldsNotAtStartOfNode:
# We only render fields that aren't at the start of their nodes for the first part of the line.
# We only render fields that aren't at the start of their nodes for the first part of the reading unit.
# Otherwise, we'll render fields that have already been rendered.
self._skipFieldsNotAtStartOfNode = True

def _getReadingUnit(self):
return textInfos.UNIT_PARAGRAPH if config.conf["braille"]["readByParagraph"] else textInfos.UNIT_LINE

def update(self):
formatConfig = config.conf["documentFormatting"]
unit = self._getReadingUnit()
# HACK: Some TextInfos only support UNIT_LINE properly if they are based on POSITION_CARET,
# so use the original cursor TextInfo for line and copy for cursor.
self._line = line = self._getCursor()
cursor = line.copy()
# Get the line at the cursor.
line.expand(textInfos.UNIT_LINE)
self._readingInfo = readingInfo = self._getCursor()
cursor = readingInfo.copy()
# Get the reading unit at the cursor.
readingInfo.expand(unit)
# Get the selection.
sel = self._getSelection()
# Restrict the selection to the line at the cursor.
if sel.compareEndPoints(line, "startToStart") < 0:
sel.setEndPoint(line, "startToStart")
if sel.compareEndPoints(line, "endToEnd") > 0:
sel.setEndPoint(line, "endToEnd")
# Restrict the selection to the reading unit at the cursor.
if sel.compareEndPoints(readingInfo, "startToStart") < 0:
sel.setEndPoint(readingInfo, "startToStart")
if sel.compareEndPoints(readingInfo, "endToEnd") > 0:
sel.setEndPoint(readingInfo, "endToEnd")
self.rawText = ""
self.rawTextTypeforms = []
self.cursorPos = None
Expand All @@ -556,27 +560,27 @@ def update(self):
self._selectionStart = self._selectionEnd = None
self._skipFieldsNotAtStartOfNode = False

# Not all text APIs support offsets, so we can't always get the offset of the selection relative to the start of the line.
# Therefore, grab the line in three parts.
# First, the chunk from the start of the line to the start of the selection.
chunk = line.copy()
# Not all text APIs support offsets, so we can't always get the offset of the selection relative to the start of the reading unit.
# Therefore, grab the reading unit in three parts.
# First, the chunk from the start of the reading unit to the start of the selection.
chunk = readingInfo.copy()
chunk.collapse()
chunk.setEndPoint(sel, "endToStart")
self._addTextWithFields(chunk, formatConfig)
# Now, the selection itself.
self._addTextWithFields(sel, formatConfig, isSelection=True)
# Finally, get the chunk from the end of the selection to the end of the line.
chunk.setEndPoint(line, "endToEnd")
# Finally, get the chunk from the end of the selection to the end of the reading unit.
chunk.setEndPoint(readingInfo, "endToEnd")
chunk.setEndPoint(sel, "startToEnd")
self._addTextWithFields(chunk, formatConfig)
# Strip line ending characters, but add a space in case the cursor is at the end of the line.
# Strip line ending characters, but add a space in case the cursor is at the end of the reading unit.
self.rawText = self.rawText.rstrip("\r\n\0\v\f") + " "
del self.rawTextTypeforms[len(self.rawText) - 1:]
self.rawTextTypeforms.append(louis.plain_text)

# If this is not the first line, hide all previous regions.
# If this is not the start of the object, hide all previous regions.
start = cursor.obj.makeTextInfo(textInfos.POSITION_FIRST)
self.hidePreviousRegions = (start.compareEndPoints(line, "startToStart") < 0)
self.hidePreviousRegions = (start.compareEndPoints(readingInfo, "startToStart") < 0)
# If this is a multiline control, position it at the absolute left of the display when focused.
self.focusToHardLeft = self._isMultiline()
super(TextInfoRegion, self).update()
Expand All @@ -592,27 +596,31 @@ def update(self):

def routeTo(self, braillePos):
pos = self._rawToContentPos[self.brailleToRawPos[braillePos]]
# pos is relative to the start of the line.
# Therefore, get the start of the line...
dest = self._line.copy()
# pos is relative to the start of the reading unit.
# Therefore, get the start of the reading unit...
dest = self._readingInfo.copy()
dest.collapse()
# and move pos characters from there.
dest.move(textInfos.UNIT_CHARACTER, pos)
self._setCursor(dest)

def nextLine(self):
dest = self._line.copy()
moved = dest.move(textInfos.UNIT_LINE, 1)
dest = self._readingInfo.copy()
moved = dest.move(self._getReadingUnit(), 1)
if not moved:
return
dest.collapse()
self._setCursor(dest)

def previousLine(self, start=False):
dest = self._line.copy()
dest = self._readingInfo.copy()
dest.collapse()
# If the end of the line is desired, move to the last character.
moved = dest.move(textInfos.UNIT_LINE if start else textInfos.UNIT_CHARACTER, -1)
if start:
unit = self._getReadingUnit()
else:
# If the end of the reading unit is desired, move to the last character.
unit = textInfos.UNIT_CHARACTER
moved = dest.move(unit, -1)
if not moved:
return
dest.collapse()
Expand Down
3 changes: 1 addition & 2 deletions source/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,7 @@ def validateConfig(configObj,validator,validationResult=None,keyList=None):
cursorBlinkRate = integer(default=500,min=0,max=2000)
messageTimeout = integer(default=4,min=1,max=20)
tetherTo = string(default="focus")
readByParagraph = boolean(default=false)
# Presentation settings
[presentation]
Expand Down
5 changes: 5 additions & 0 deletions source/gui/settingsDialogs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1026,6 +1026,10 @@ def makeSettings(self, settingsSizer):
sizer.Add(self.tetherList)
settingsSizer.Add(sizer, border=10, flag=wx.BOTTOM)

item = self.readByParagraphCheckBox = wx.CheckBox(self, label=_("Read by &paragraph"))
item.Value = config.conf["braille"]["readByParagraph"]
settingsSizer.Add(item, border=10, flag=wx.BOTTOM)

def postInit(self):
self.displayList.SetFocus()

Expand All @@ -1049,6 +1053,7 @@ def onOk(self, evt):
if 1 <= val <= 20:
config.conf["braille"]["messageTimeout"] = val
braille.handler.tether = self.tetherValues[self.tetherList.GetSelection()][0]
config.conf["braille"]["readByParagraph"] = self.readByParagraphCheckBox.Value
super(BrailleSettingsDialog, self).onOk(evt)

class SpeechSymbolsDialog(SettingsDialog):
Expand Down
1 change: 1 addition & 0 deletions user_docs/en/changes.t2t
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
- Braille now reports information about controls within documents such as links, buttons and headings. (#202)
- Support for the hedo ProfiLine USB braille display. (#1863)
- NVDA now avoids splitting words in braille when possible. (#1890)
- It is now possible to have braille displayed by paragraphs instead of lines, which may allow for more fluent reading of large amounts of text. This is configurable using the Read by paragraphs option in the Braille Settings dialog. (#1891)


== Changes ==
Expand Down
7 changes: 7 additions & 0 deletions user_docs/en/userGuide.t2t
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,13 @@ Key: NVDA+control+t

This option allows you to choose whether the braille display will follow the system focus, or whether it follows the navigator object / review cursor.

==== Read by Paragraph ====
If enabled, braille will be displayed by paragraphs instead of lines.
Also, the next and previous line commands will move by paragraph accordingly.
This means that you do not have to scroll the display at the end of each line even where more text would fit on the display.
This may allow for more fluent reading of large amounts of text.
It is disabled by default.

+++ Keyboard settings (NVDA+control+k) +++
This dialog box is found in the Preferences menu, under "Keyboard settings...".
It contains the following options:
Expand Down

0 comments on commit 4c38e73

Please sign in to comment.