Skip to content

Options for focus context presentation in braille#7236

Merged
jcsteh merged 18 commits into
nvaccess:masterfrom
BabbageCom:i217
Aug 1, 2017
Merged

Options for focus context presentation in braille#7236
jcsteh merged 18 commits into
nvaccess:masterfrom
BabbageCom:i217

Conversation

@LeonarddeR

@LeonarddeR LeonarddeR commented May 31, 2017

Copy link
Copy Markdown
Collaborator

Link to issue number:

Fixes #217

Summary of the issue:

from #217 (comment)

When you arrow through a list of items and are reading the braille display, the start of each list item may begin in a different place for one list item than it is for another. This makes it more difficult to easily arrow down through a list in search of something, because you have to stop and read the display to be able to find the list item.

Description of how this pull request fixes the issue:

This implements two types of focus context/ancestry presentation brought up by @jcsteh:

  1. To have braille insure that lists and such consistently present the start of items in the same place on the display #217 (comment): an option that allows you to always show the object's content at the very left of the braille display. This means that you will always have to scroll back if you want to see the ancestry, but this is no problem for speech oriented braille users and might even be their preference.
  2. To have braille insure that lists and such consistently present the start of items in the same place on the display #217 (comment) : An option that only presents new focus ancestry information to the user when the ancestry has changed.

Testing performed:

  1. Go to braille settings
  2. Set "Focus context presentation" to "Only when scrolling back", and press ok
  3. Arrow around the desktop. Desktop items will show up at the left of the braille display
  4. Set "Focus context presentation" to "Fill display for context changes", and press ok
  5. When the desktop list is focused, the ancestry will be shown as earlier. When you start arrowing around, the selected desktop icon will be shown hard left, as there is no context change
  6. Set "Focus context presentation" back to "Always fill display", and press ok
  7. Arrowing through the desktop reveals default behaviour on the braille display.

Known issues with pull request:

  1. I'd like feedback from people about the wording of the new focus context presentation options and its options. It is quite hard to give a short description of the current, default behaviour actually. Also, I'm not that good in writing good summaries for the changes file.
  2. This doesn't have a global command yet

Open question

I'm using an integer in the config spec, is this preferred or should we go for a string instead?

Leonard de Ruijter added 2 commits May 31, 2017 15:16
@dkager

dkager commented Jun 5, 2017

Copy link
Copy Markdown
Contributor

You'll also want to update the user guide once the final wording has been decided.

@LeonarddeR LeonarddeR changed the title Option to always show objects hard left on a braille display Options to show objects hard left on a braille display Jun 21, 2017
@LeonarddeR

Copy link
Copy Markdown
Collaborator Author

@jcsteh: Not trying to push, but just wanted to let you know that this is ready for review.

One thing I'd like to point your attention at, is the use of a focusToHardLeftSet variable. I'm not particularly happy with this approach, but I can't come up with alternatives here.

Comment thread source/braille.py Outdated
if region.focusToHardLeft:
# Only scroll to the start of this region.
restrictPos = endPos - regionPos - 1
elif config.conf["braille"]["focusContextPresentation"]==2:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While NVDA certainly isn't too strict about this, I think having constants for this option would improve readability a lot.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we'd go for constants, I'd say we should go for strings, or for
option to be super modern. Items like tethering in the config spec
should be option type as well, actually. Unless you can't set a default
for those.

Comment thread source/braille.py Outdated
@@ -1211,6 +1213,20 @@ def _set_windowEndPos(self, endPos):
if region.focusToHardLeft:
# Only scroll to the start of this region.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function has some double looping due to my recent changes, so I'm planning to change the order a bit later today. I want it to loop through the regions only once.

@LeonarddeR

Copy link
Copy Markdown
Collaborator Author

I just added unit tests for this.

@jcsteh jcsteh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks pretty good for the most part. Very nice work. Thanks.

Comment thread source/braille.py
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()
# Don't touch focusToHardLeft if it is already true

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think some explanation/example of how this might already be True would be helpful; e.g. that this can be set by getFocusContextRegions or BrailleHandler._doNewObject.

Comment thread source/braille.py
if region.focusToHardLeft:
# Only scroll to the start of this region.
restrictPos = endPos - regionPos - 1
# Loop through the currently displayed regions in reverse order

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that this code is getting more complex, I think a comment before this line would be useful explaining what this block of code does; e.g. "If there's a region that should be placed at the left of the display, make sure we only scroll back that far."

Comment thread source/braille.py Outdated
restrictPos = regionStart
break
elif config.conf["braille"]["focusContextPresentation"]!='hybrid':
# Do not process the other regions

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? It'd be good to clarify the comment. I think this is because if we're not doing context change presentation, we only need to consider the last region. We could perhaps say "If this isn't context change presentation, we only need to consider the last region."

Comment thread source/braille.py Outdated
commonRegionsEnd = 0
newAncestorsStart = 1
# Yield the common regions.
# Yield the common regions, thereby setting focusToHardLeft back to False.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't quite follow this comment. I think you mean "Yield the common regions and also set their focusToHardLeft to False" or similar. Still, I can't easily glean an understanding of why this is done. I assume this is because it was set based on a previous focus change, but it'd be good to clarify this.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, this was part of my initial implementation of this and I wanted to make sure that all cached regions with focusToHardLeft set to True had it set back to False. But, this isn't necessary.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, no, the current implementation still relies on setting focusTohardLeft to False. Let me investigate this in more detail and come up with a descriptive comment. Cheers for unit testing.

Comment thread source/braille.py
region = NVDAObjectRegion(parent, appendText=TEXT_SEPARATOR)
region._focusAncestorIndex = index
if config.conf["braille"]["focusContextPresentation"]=='hybrid' and not focusToHardLeftSet:
region.focusToHardLeft = True

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Would this be more readable if it just did index == newAncestorsStart? That would avoid the need for the focusToHardLeftSet flag and also make it clear that we do this only for the first new ancestor.
  2. A comment might be nice stating why we do this; i.e. we're doing context change presentation and this is the first new ancestor, so only scroll back this far.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this be more readable if it just did  index == newAncestorsStart ? That would avoid the need for the  focusToHardLeftSet  flag and also make it clear that we do this only for the first new ancestor.

This is not possible due to the "if not parent.isPresentableFocusAncestor:" line.

Comment thread tests/unit/test_braille.py Outdated
self.assertEqual(len(self.regionsWithPositions),3)

def test_fillDisplay(self):
"""Test for the case where both the focus object and its ancestors should be visible on a 40 cell display."""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps "all its ancestors" to be super clear?

Comment thread tests/unit/test_braille.py Outdated
# This is because getting windowEndPos can update windowStartPos
# Both the focus object and its parent should fit on the display
# Thus, the window end position is equal to the end position of the 3rd region
self.assertEqual(braille.handler.buffer.windowEndPos,self.regionsWithPositions[2][2])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's not too much trouble, consider making the regions with positions code in braille return a namedtuple. That would make these tests more readable; e.g. self.regionsWithPositions[2].end.

# Only the focus object should be visible now, equivalent to scroll only
self.assertEqual(braille.handler.buffer.windowEndPos,self.regionsWithPositions[2][2])
self.assertEqual(braille.handler.buffer.windowStartPos,self.regionsWithPositions[2][1])
# Clean up the cached focus ancestors

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're only cleaning up ancestors to a certain point, so I think this comment needs some clarification. You could use the roles to help explain.

# Forcefully create a fake focus ancestry
globalVars.focusAncestors=[api.getDesktopObject(),NVDAObjectWithRole(role=controlTypes.ROLE_DIALOG),NVDAObjectWithRole(role=controlTypes.ROLE_LIST)]
braille.handler.handleGainFocus(self.obj)
# Make sure that we are testing with three regions

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be super helpful if this comment could be expanded to list the roles (dialog, list, list item) and note that braille excludes the desktop object. I forgot about this last point myself and was thus very confused.

Comment thread tests/unit/test_braille.py Outdated
braille.handler.handleGainFocus(self.obj)
# WindowEndPos should be retrieved before we attempt to get the start position
# This is because getting windowEndPos can update windowStartPos
# Both the focus object and its parent should fit on the display

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"its parent" -> "its ancestors"

@jcsteh

jcsteh commented Jul 11, 2017

Copy link
Copy Markdown
Contributor

Oh, and this now has merge conflicts. :(

@LeonarddeR

LeonarddeR commented Jul 11, 2017 via email

Copy link
Copy Markdown
Collaborator Author

@jcsteh jcsteh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice comments and documentation. Thank you. Almost there. :)

Comment thread source/braille.py Outdated
def _set_windowEndPos(self, endPos):
"""Sets the end position for the braille window and recalculates the window start position based on several variables.
1. Braille display size.
2. Whether one of the regions should be shown hard left on the braille display.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Semi-colon instead of full stop at end of this line.

Comment thread source/braille.py Outdated
"""Sets the end position for the braille window and recalculates the window start position based on several variables.
1. Braille display size.
2. Whether one of the regions should be shown hard left on the braille display.
(i.e. because of The configuration setting for focus context representation

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: No parenthesis here, since we just continue on from semi-colon on previous line. (Also, there was no close parenthesis originally.)

Comment thread source/braille.py Outdated
# Only scroll to the start of this region.
restrictPos = endPos - regionPos - 1
# Loop through the currently displayed regions in reverse order
# If focusTohardLeft is set for one of the regions, the display shouldn't scroll further back than the start of that region

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: focusTohardLeft has a lower case h; should be upper.

Comment thread source/config/configSpec.py Outdated
tetherTo = string(default="focus")
readByParagraph = boolean(default=false)
wordWrap = boolean(default=true)
focusContextPresentation = option("changedContext","fill","scroll",default="changedContext")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Spaces after commas here please.

Comment thread tests/unit/__init__.py
api.setFocusObject(phObj)
api.setNavigatorObject(phObj)

api.setDesktopObject(phObj)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Reinstate blank line after this.

Comment thread user_docs/en/userGuide.t2t Outdated
When set to fill display for context changes, NVDA will try to display as much as possible context information on the braille display, but only for the parts of the context that have changed.
For the example above, this means that when changing focus to the list, NVDA will show the list item on the braille display.
Furthermore, if there is enough space left on the braille display, NVDA will try to show that the list item is part of a list.
Now, if you start moving through the list with your arrow keys, it is assumed that you are aware of the fact that you are moving through a list.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps this:

If you then start moving through the list with your arrow keys, it is assumed that you are aware that you are still in the list.

Comment thread user_docs/en/userGuide.t2t Outdated
Thus, for the remaining list items you focus, NVDA will only show the focused list item on the display.
In order for you to read the context again (i.e. that you are in a list and that the list is part of a dialog), you will have to scroll your braille display back.

When this option is set to always fill the display, NVDA will try to show as much as possible context information on the braille display, regardless of whether you have seen the same context information before.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"as much as possible context information" -> "as much context information as possible"

Comment thread user_docs/en/userGuide.t2t Outdated
In order for you to read the context again (i.e. that you are in a list and that the list is part of a dialog), you will have to scroll your braille display back.

When this option is set to always fill the display, NVDA will try to show as much as possible context information on the braille display, regardless of whether you have seen the same context information before.
This has the advantage that NVDA will make use of the braille display in an effective way, filling up as much as possible empty display space.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know what you mean here, but some users (you and Davy, for example, heh) might argue this isn't "effective". Perhaps this:

This has the advantage that NVDA will fit as much information as possible on the display.

Comment thread user_docs/en/userGuide.t2t Outdated

When this option is set to always fill the display, NVDA will try to show as much as possible context information on the braille display, regardless of whether you have seen the same context information before.
This has the advantage that NVDA will make use of the braille display in an effective way, filling up as much as possible empty display space.
however, this option has the downside that there is always a difference in the position where the representation of the object with focus starts on the braille display.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Need capital at start of sentence.
  2. I think we can simplify/shorten this:

However, the downside is that there is always a difference in the position where the focus starts on the braille display.

Would it also be useful to mention that this makes skimming difficult? For example:

This can make it difficult to skim a long list of items, for example, as you will need to continually move your finger to find the start of the item.

Comment thread user_docs/en/userGuide.t2t Outdated
This was the default behavior for NVDA 2017.2 and before.

When you set the focus context presentation option to only show the context information when scrolling back, NVDA never shows context information on your braille display by default.
Thus, in the example above, it is presented on the braille display that you focused a list item. However, in order for you to read the context (i.e. that you are in a list and that the list is part of a dialog), you will have to scroll your braille display back.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. "it is presented on the braille display that" -> "NVDA will display that"
  2. Two sentences on one line. Please split.

@LeonarddeR

Copy link
Copy Markdown
Collaborator Author

@leonardder commented on 11 jul. 2017 18:37 CEST:

note that this not yet ready for another round of review

@jcsteh: It seems you decided otherwise, many thanks. I've addressed your comments.

I also added a script to globalCommands, which I left unbound by default. I'd argue that users don't change this every single day. If you think this should be bound by default, I propose control+NVDA+F for focus.

Comment thread source/globalCommands.py Outdated
index = values.index(config.conf["braille"]["focusContextPresentation"])
except:
index=0
newIndex = index+1 if index+1<len(values) else 0

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI, this can be shortened to:

newIndex = index % len(values)

I'll leave it up to you as to whether you want to change this. Whether it makes things more readable or not is certainly debatable. :)

Comment thread source/globalCommands.py
config.conf["braille"]["focusContextPresentation"] = values[newIndex]
braille.invalidateCachedFocusAncestors(0)
braille.handler.handleGainFocus(api.getFocusObject())
# Translators: Reports the new state of braille focus context presentation.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be helpful to note that %s gets replaced with the context presentation setting and give an example:

# %s will be replaced with the context presentation setting.
# For example, the full message might be "Braille focus context presentation Fill display for context changes"

Actually, given the length of that example, I wonder whether we need a colon here. Normally, we prefer to avoid this for brevity (linguistic correctness is less important for these kinds of things), but this is long enough that it might be confusing without it. Alternatively, we could just cut the prefix altogether and just use the setting name. Arguably, the setting names are long/unique enough that the context will be fairly clear.

Comment thread user_docs/en/userGuide.t2t Outdated

==== Focus context presentation ====
This option allows you to choose what context information NVDA will show on the braille display when an object gets focus.
Context information (or focus ancestry) refers to the hierarchy of objects containing the focus.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still wonder whether we should drop the term "focus ancestry" here. I'm concerned it will just confuse most users and it's certainly more technical than "context information".

Comment thread user_docs/en/userGuide.t2t Outdated
This list might be contained by a dialog, etc.
Please consult the section about [object navigation #ObjectNavigation] for more information about the hierarchy that applies to objects in NVDA.

When set to fill display for context changes, NVDA will try to display as much as possible context information on the braille display, but only for the parts of the context that have changed.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"as much as possible context information" -> "as much context information as possible"

@LeonarddeR

LeonarddeR commented Jul 14, 2017 via email

Copy link
Copy Markdown
Collaborator Author

@jcsteh jcsteh changed the title Options to show objects hard left on a braille display Options for focus context presentation in braille Jul 19, 2017
jcsteh added a commit that referenced this pull request Jul 19, 2017
@jcsteh jcsteh merged commit 1785d5f into nvaccess:master Aug 1, 2017
@nvaccessAuto nvaccessAuto added this to the 2017.3 milestone Aug 1, 2017
@LeonarddeR LeonarddeR added the BabbageWork Pull requests filed on behalf of Babbage B.V. label Oct 11, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

BabbageWork Pull requests filed on behalf of Babbage B.V.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

To have braille insure that lists and such consistently present the start of items in the same place on the display

4 participants