Options for focus context presentation in braille#7236
Conversation
…rd left * Added focus presentation to the gui * Updated copyright headers
|
You'll also want to update the user guide once the final wording has been decided. |
…k, not yet ready for review
…de, also did some rewording in the gui
…he spec accordingly
|
@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. |
| if region.focusToHardLeft: | ||
| # Only scroll to the start of this region. | ||
| restrictPos = endPos - regionPos - 1 | ||
| elif config.conf["braille"]["focusContextPresentation"]==2: |
There was a problem hiding this comment.
While NVDA certainly isn't too strict about this, I think having constants for this option would improve readability a lot.
There was a problem hiding this comment.
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.
| @@ -1211,6 +1213,20 @@ def _set_windowEndPos(self, endPos): | |||
| if region.focusToHardLeft: | |||
| # Only scroll to the start of this region. | |||
There was a problem hiding this comment.
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.
|
I just added unit tests for this. |
jcsteh
left a comment
There was a problem hiding this comment.
This looks pretty good for the most part. Very nice work. Thanks.
| 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 |
There was a problem hiding this comment.
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.
| if region.focusToHardLeft: | ||
| # Only scroll to the start of this region. | ||
| restrictPos = endPos - regionPos - 1 | ||
| # Loop through the currently displayed regions in reverse order |
There was a problem hiding this comment.
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."
| restrictPos = regionStart | ||
| break | ||
| elif config.conf["braille"]["focusContextPresentation"]!='hybrid': | ||
| # Do not process the other regions |
There was a problem hiding this comment.
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."
| commonRegionsEnd = 0 | ||
| newAncestorsStart = 1 | ||
| # Yield the common regions. | ||
| # Yield the common regions, thereby setting focusToHardLeft back to False. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
| region = NVDAObjectRegion(parent, appendText=TEXT_SEPARATOR) | ||
| region._focusAncestorIndex = index | ||
| if config.conf["braille"]["focusContextPresentation"]=='hybrid' and not focusToHardLeftSet: | ||
| region.focusToHardLeft = True |
There was a problem hiding this comment.
- Would this be more readable if it just did
index == newAncestorsStart? That would avoid the need for thefocusToHardLeftSetflag and also make it clear that we do this only for the first new ancestor. - 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.
There was a problem hiding this comment.
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.
| 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.""" |
There was a problem hiding this comment.
Perhaps "all its ancestors" to be super clear?
| # 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]) |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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.
| 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 |
There was a problem hiding this comment.
"its parent" -> "its ancestors"
|
Oh, and this now has merge conflicts. :( |
|
note that this not yet ready for another round of review, there are some
open comments.
As for the review regarding the unit tests, I decided to create a new
module called objectProvider which we can use to add more object related
stuff to if we so desire later.
|
… presentation option. Still a bit rough though.
jcsteh
left a comment
There was a problem hiding this comment.
Very nice comments and documentation. Thank you. Almost there. :)
| 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. |
There was a problem hiding this comment.
nit: Semi-colon instead of full stop at end of this line.
| """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 |
There was a problem hiding this comment.
nit: No parenthesis here, since we just continue on from semi-colon on previous line. (Also, there was no close parenthesis originally.)
| # 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 |
There was a problem hiding this comment.
nit: focusTohardLeft has a lower case h; should be upper.
| tetherTo = string(default="focus") | ||
| readByParagraph = boolean(default=false) | ||
| wordWrap = boolean(default=true) | ||
| focusContextPresentation = option("changedContext","fill","scroll",default="changedContext") |
There was a problem hiding this comment.
nit: Spaces after commas here please.
| api.setFocusObject(phObj) | ||
| api.setNavigatorObject(phObj) | ||
|
|
||
| api.setDesktopObject(phObj) |
There was a problem hiding this comment.
nit: Reinstate blank line after this.
| 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. |
There was a problem hiding this comment.
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.
| 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. |
There was a problem hiding this comment.
"as much as possible context information" -> "as much context information as possible"
| 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. |
There was a problem hiding this comment.
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.
|
|
||
| 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. |
There was a problem hiding this comment.
- Need capital at start of sentence.
- 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.
| 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. |
There was a problem hiding this comment.
- "it is presented on the braille display that" -> "NVDA will display that"
- Two sentences on one line. Please split.
|
@leonardder commented on 11 jul. 2017 18:37 CEST:
@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. |
| index = values.index(config.conf["braille"]["focusContextPresentation"]) | ||
| except: | ||
| index=0 | ||
| newIndex = index+1 if index+1<len(values) else 0 |
There was a problem hiding this comment.
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. :)
| config.conf["braille"]["focusContextPresentation"] = values[newIndex] | ||
| braille.invalidateCachedFocusAncestors(0) | ||
| braille.handler.handleGainFocus(api.getFocusObject()) | ||
| # Translators: Reports the new state of braille focus context presentation. |
There was a problem hiding this comment.
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.
|
|
||
| ==== 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. |
There was a problem hiding this comment.
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".
| 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. |
There was a problem hiding this comment.
"as much as possible context information" -> "as much context information as possible"
|
1. I decided to go the "Braille focus context presentation: always fill
display" way for the global command. I agree that the message is getting
a bit long, but:
* This is left unbound by default anyway
* I find "Only when scrolling back" a bit too little verbose when
changing this option that way.
* But, if you have alternative ideas or still think that we should
remove the prefix, let me know.
2. That (newIndex+1) % len(values) is actually very cool, this really
shows my improper computer science background.
|
Link to issue number:
Fixes #217
Summary of the issue:
from #217 (comment)
Description of how this pull request fixes the issue:
This implements two types of focus context/ancestry presentation brought up by @jcsteh:
Testing performed:
Known issues with pull request:
Open question
I'm using an integer in the config spec, is this preferred or should we go for a string instead?