Skip to content

Browse mode with auto focus focusable elements disabled: Fix activation of non-focusable objects and activation after using the tab key.#11130

Merged
michaelDCurran merged 1 commit into
nvaccess:masterfrom
jcsteh:browseModeEnterNoAutoFocus
May 13, 2020

Conversation

@jcsteh

@jcsteh jcsteh commented May 8, 2020

Copy link
Copy Markdown
Contributor

Link to issue number:

Fixes #8528. Fixes #9886.
Based on @LeonarddeR's try build in #9511 (comment), commit 72316d5, but with a fix for the over-chatting described in #9511 (comment).

Summary of the issue:

In browse mode, with auto focus focusable elements disabled, it is not possible to activate non-focusable elements or activate an object after tabbing. Also, activating would sometimes cause NVDA to click in the wrong place, causing weirdness such as focusing the address bar or task bar.

Description of how this pull request fixes the issue:

Previously, with auto focus focusable elements disabled, browse mode tracked the last focusable object and used this when activating at the caret. This meant that it wasn't possible to activate objects that weren't focusable. Furthermore, this wasn't updated correctly when using the tab key, which meant it wasn't possible to activate at the caret after tabbing.

To fix this:

  1. Don't keep track of the last focusable object. Instead, _focusLastFocusableObject always uses the focusable ancestor of the caret.
  2. If _focusLastFocusableObject is requested to activate, it does so using the object at the caret, not the focusable ancestor. This handles the case where the object at the caret isn't focusable.
  3. _focusLastFocusableObject temporarily saves the object which is pending focus. This is because we might be about to activate or pass through a key which will cause this object to change (e.g. checing a check box). However, we won't actually get the focus event until after the change has occurred. The speech property cache on this saved object reflects the properties before the activation.
  4. When the focus event arrives, event_gainFocus uses this saved pending focus object to report the change (if any) that occurred.

Testing performed:

Test case:
data:text/html,<button>a</button><button onClick="alert('b');">b</button><div onClick="alert('c');">c</div><input type="checkbox" aria-label="d"><div contenteditable><p>e</p></div>

  1. Tabbed to "b". Pressed enter and confirmed that a "b" dialog appeared. Dismissed the dialog.
  2. Pressed down arrow to move to "c". Pressed enter. Confirmed that a "c" dialog appeared.
  3. Pressed down arrow to move to "d". Pressed enter. Confirmed that NVDA said "checked".
  4. Pressed down arrow to move to "e". Pressed enter to switch to focus mode. Pressed NVDA+tab. Confirmed that the "e" text box was focused.

Known issues with pull request:

None known.

Change log entry:

Bug fixes:

- In browse mode with Automatically set system focus to focusable elements disabled, it is now possible to activate elements that aren't focusable.
- In browse mode with Automatically set system focus to focusable elements disabled, it is now possible to activate elements reached by pressing the tab key. (#8528)
- In browse mode with Automatically set system focus to focusable elements disabled, activating certain elements no longer clicks in an incorrect location. (#9886)

…on of non-focusable objects and activation after using the tab key.

Previously, with auto focus focusable elements disabled, browse mode tracked the last focusable object and used this when activating at the caret.
This meant that it wasn't possible to activate objects that weren't focusable.
Furthermore, this wasn't updated correctly when using the tab key, which meant it wasn't possible to activate at the caret after tabbing.

To fix this:

1. Don't keep track of the last focusable object. Instead, _focusLastFocusableObject always uses the focusable ancestor of the caret.
2. If _focusLastFocusableObject is requested to activate, it does so using the object at the caret, not the focusable ancestor. This handles the case where the object at the caret isn't focusable.
3. _focusLastFocusableObject temporarily saves the object which is pending focus. This is because we might be about to activate or pass through a key which will cause this object to change (e.g. checing a check box). However, we won't actually get the focus event until after the change has occurred. The speech property cache on this saved object reflects the properties before the activation.
4. When the focus event arrives, event_gainFocus uses this saved pending focus object to report the change (if any) that occurred.

@LeonarddeR LeonarddeR left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks a lot for taking this further!

Comment thread source/browseMode.py
if reason == controlTypes.REASON_FOCUS:
self._lastCaretMoveWasFocus = True
focusObj = api.getFocusObject()
self._lastFocusableObj = None

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should self._objPendingFocusBeforeActivate also be set to None here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I'm not entirely sure why _lastFocusableObj was set to None there. I suspect that might have been what was breaking the tab key. Of course, that won't happen with the new implementation, but I also don't know that there's any need to clear it here either.

@michaelDCurran michaelDCurran merged commit dae6193 into nvaccess:master May 13, 2020
@nvaccessAuto nvaccessAuto added this to the 2020.2 milestone May 13, 2020
michaelDCurran added a commit that referenced this pull request May 13, 2020
@jcsteh jcsteh deleted the browseModeEnterNoAutoFocus branch May 13, 2020 23:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

4 participants