Issue Post: https://github.com/nvaccess/nvda/issues/18029
My fixes commits: https://github.com/nvaccess/nvda/pull/18215/commits
Understanding the Issues
I took the advise from the community member to start with the “good-first-issue”.
The issue I found was described in Issue #18029, where pressing Alt + A to trigger the Apply button in the settings dialog caused focus to unexpectedly jump back to the categories list. This confused users, especially screen users who rely on predictable focus and auditory feedback to understand what’s happening.
The core problem wasn’t that the settings weren’t being applied — they were — but the focus jump made it unclear whether the action had succeeded. Since there was no confirmation message or visual cue, it appeared as if nothing happened. For a screen reader user, that kind of silent behavior can be very disorienting.
The issue was user-facing but deeply tied to GUI behavior. It was also subtle enough that it might not have seemed urgent at first glance, but for accessibility software, predictable interaction flow is everything. Solving it would mean making NVDA feel more intuitive and trustworthy, especially during repetitive settings changes.
Solving the Issues
Once everything is set up. I looked into the code. The code that is responsible for this part is inside source > gui > settingDialogs
. I found the specific parts of the code and marked them.
The first part is the onApply()
function. As shown below:

What the fuction currently does is :
- It calls
postInit()
— likely to apply the actual settings - Then it returns the dialog result as
Apply
To add a notification for the user to know that the settings have been applied, I will need to add my code here.
Initially, I added a pop-up confirmation window saying “Settings successfully applied.” with a [OK] button to close it.
wx.MessageBox("Settings successfully applied.", "Confirmation", wx.OK)
Then I remembered other contributors on the thread mentioning adding an extra confirmation step might be unnecessary, as it will take an extra step for the users to complete the same task. So I decided to add just a small floating messgage pop-up that automatically disappears after a few seconds (I set it to 3 sec).

As the onApply()
function calls the postInit()
, I have to also look into the postInit()
to find out what they actually do.

It does:
- If a specific post-init focus function was set, it uses that
- Otherwise — which is what happens when you click Apply — it calls:
self.catListCtrl.SetFocus()
This is why pressing Alt+A (Apply) moves the focus back to the categories list, which is confusing for the users.
In order to stop this behaviour, I came up with 3 options to approach it.
- In
postInit()
else:
, instead of setting it toself.catListCtrl.SetFocus()
, I could simple change it to pass then it would not do anything (moving focus to the categories list). - In
postInit()
else:
, setting it to change to the Apply button instead. - Since the
postInit()
checks if a specific post-init focus function was set, I could set a post-init focus in theonApply()
. Setting the focus to the Apply button. Then when it callspostInit()
it will fall into the “specific post-init focus function was set” case and stay on the Apply button.
After careful consideration, I chose to go with the 3rd option because:
- Uses NVDA’s intended design: the
setPostInitFocus
pattern already exists to customise focus - Keeps
postInit()
untouched and reusable - Avoids messing with global focus rules for other use cases
- Most maintainable for upstream contributions — the NVDA team is more likely to accept this in a pull request
Problems with the 1st option:
- It works, but it’s a silent override of the default behavior
- Future contributors may not realise why
catListCtrl
isn’t getting focus - Less flexible if behavior needs to change again later
Problems with the 2nd option:
- Works functionally, but still modifies
postInit()
logic directly, which isn’t needed - Doesn’t leverage the
setPostInitFocus
hook NVDA provides - For other functions that also calls
postInit()
, they might ended up with changing the focus to the Apply button.
My complete code:

Creating Pull Request

Writing comments for the Pull Request

Waiting for review & approval

Feedback from Reviewer
The reviewer requries UX strings to be translatable and asks to use ui.message
instead of wx.TipWindow
. As this is a non-visual desktop, there’s no point deploying a function that has unreliable audio cues.

I accidently put self.setPostInitFocus = lambda: self.FindWindowById(wx.ID_APPLY).SetFocus()
after self.postInit()
, which caused setting focus to Apply not woking.

The reviewer suggested having a timeout is inaccessible and it is better to leave out.

Resolve Feedback
This is my resolved code. Changes I’ve made:
- – Moved self.setPostInitFocus to before self.postInit()
- Replaced wx.TipWindow with ui.message to ensure screen reader users receive the message reliably.
- Marked the message string as translatable using _() and added translation comment for localisation.
- Added a 500ms delay using core.callLater to avoid timing conflicts during dialog focus changes.
- Removed timeout behaviour, which is inaccessible to screen reader users.


Then I noticed after the commit that it did not pass the pre-commit.ci. I read the error report and it shows that ui
is not defined.

So I edited the code once again and added import ui
.

Leave a Reply