Skip to content

2026.2 Final master to beta merge#20104

Merged
seanbudd merged 196 commits into
betafrom
master
May 11, 2026
Merged

2026.2 Final master to beta merge#20104
seanbudd merged 196 commits into
betafrom
master

Conversation

@seanbudd
Copy link
Copy Markdown
Member

Final merge for 2026.2 release, using commit 567a579 from master.
Changes intended for 2026.2 must now set base/target to the beta branch

Merge not squash

SaschaCowley and others added 30 commits December 9, 2025 16:02
Start the dev cycle for the 2026.2 release.
This won't be a compatibility breaking release.

Complete:
- [x] New section in the change log.
- [x] Update NVDA version in `master`
- [x] Update [`nvdaAPIVersions.json` to include the next
version](https://github.com/nvaccess/addon-datastore-transform)
nvaccess/addon-datastore-transform#36
- Re-run the last "Transform NVDA addons to views" on
[addon-datastore](https://github.com/nvaccess/addon-datastore/actions/workflows/transformDataToViews.yml)
to regenerate projections (views) for the add-on datastore API.

On merge:
- [x] [Update auto milestone
ID](https://github.com/nvaccess/nvda/settings/variables/actions)
Close #625
First step to #18400

### Summary of the issue:
People want commands to repeat the last spoken information, to be able
to display it and to copy it.
Actually, they also want a deeper history of the speech, but NV Access
has recommended in #18400 to begin with the last speech.

### Description of user facing changes:
A new commands has been introduced: Repeat last spoken information
(`NVDA+shift+F12`). Pressing it twice displays this information in a
browseable message.

The copy concern can be considered addressed since browseable message
now have a copy button.

When in on-demand speech mode, the last string to be spoken is
memorized, no matter if it is actually spoken (on-demand command), or
not. This provide a quite handy mode to retrive the last information
that should have been spoken if speech mode had been on.

### Description of developer facing changes:
N/A
### Description of development approach:
Use `pre_speech` extension point to store the last spoken information

### Testing strategy:
Manual test, with speech modes on and on-demand.

### Known issues with pull request:
This minimalist PR may be frustrating for a lot of people expecting
more, i.e. a more complete speech history feature as in the add-on, in
Jaws or in Narrator.
Fixed: #17434

### Summary of the issue:

Previously, we called `InvalidateRect` every 100 milliseconds to refresh
the entire window. This caused `dwm.exe` to perform extensive
calculations, resulting in high GPU or CPU utilization—particularly
noticeable on low-performance machines.

### Description of user facing changes:

Enabling the highlighter does not cause `dwm.exe` to consume excessive
resources continuously, especially when no objects are being updated.

### Description of developer facing changes:

* **Type Hinting Improvements:** Updated type hints across
`autoSettings.py`, `mouseHandler.py`, and `NVDAHighlighter.py` to use
modern Python syntax (e.g., standard collection generics like
`list[...]` instead of `typing.List`, and `type` aliases). Added
`override` decorators where appropriate.
* **Formatting import** Use ruff to format import statement.
* **NVDAHighlighter Refactor:**
* `HighlightStyle` is now a `NamedTuple` with explicit type annotations.
* Refactored `HighlightWindow` to separate logic for coordinate mapping
(`_mapRectToClient`) and determining draw targets (`_getDrawRects`).
* Introduced state tracking (`_prevContextRects`) to the highlighter
window to facilitate delta updates.

### Description of development approach:

To address the performance issue with `dwm.exe`:

1. **Dirty Rectangle Invalidation:** Instead of blindly invalidating the
entire screen overlay window every refresh cycle (`InvalidateRect(...,
None, ...)`), the approach was changed to calculate specific "dirty"
regions.
2. **State Tracking:** The `HighlightWindow` now caches the rectangles
rendered in the previous frame.
3.  **Delta Calculation:** During `refresh()`:
    * The current required rectangles are calculated.
    * They are compared with the cached previous state.
* If a context's rectangle has changed or was removed, the **old**
screen region is invalidated (to clear the artifact).
* If a context's rectangle is new or changed, the **new** screen region
is invalidated (to draw the new highlight).
4. **Region Calculation:** A helper method `_invalidateContextRect` maps
the logical object rectangle to client coordinates and adds necessary
padding (accounting for the pen width and anti-aliasing) to ensure the
invalidation rect covers the entire visual drawing.

This minimizes the surface area that the Windows DWM needs to recompose,
drastically reducing GPU/CPU usage.

### Testing strategy:

Manual Testing:

1. Build and install the self-signed launcher.
2. Enable the highlighter tool and move the focus to check for drawing
errors. (especially drawn on the start menu)
Closes #19318

### Summary of the issue:
The log cannot be opened when log level is on Disabled.
### Description of user facing changes:
The log can be opened when log level is on Disabled.
### Description of developer facing changes:
N/A
### Description of development approach:
When log is disabled, just open the log as if we had clicked the
corresponding NVDA menu item.
### Testing strategy:
Manual test with:
* Changing the log level in the settings dialog
* starting with `-l 100` flag

### Known issues with pull request:
No test performed with `--no-logging` flag due to #19333.
Closes #19211

### Summary of the issue:
There was no command to toggle keyboard layout (desktop / laptop). It
may be useful for testing, or for someone switching between built-in and
external keyboard.

### Description of user facing changes:
* Added an unassigned command to toggle keyboard layout.
### Description of developer facing changes:
N/A
### Description of development approach:
Added the command script in `globalCommands.py`.

### Testing strategy:
Manual test
### Known issues with pull request:
None
### Summary of the issue:
Updated actions/cache@v4 to actions/cache@v5
### Description of user facing changes:
none
### Description of developer facing changes:
Update /.github/workflows/testAndPublish.yml file
actions/cache@version

### Description of development approach:
Update /.github/workflows/testAndPublish.yml file
### Testing strategy:
Because GitHub Actions' stability has been quite poor recently, the
build didn't succeed on the first attempt, so we need to carefully test
whether it can compile successfully.
Co-authored-by: Cyrille Bougot <cyrille.bougot2@laposte.net>
Co-authored-by: Bill Dengler <codeofdusk@gmail.com>
Co-authored-by: Sascha Cowley <16543535+SaschaCowley@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…ments with a height less than 0. #19384

Fixes: #19383
Summary of the issue:

The highlighter gets an error getting a rectangle of objects with heights less than 0. This is because we are not handling the ValueError exception thrown by locationHelper.RectLTRB.
Description of user facing changes:

The highlighter does not report an error when it encounters an object with a height less than 0.
Description of developer facing changes:
Description of development approach:

Handle ValueError exception when NVDAHighlighter.updateContextRect gets the context rectangle
)

Fix-up of #19354
Summary of the issue:

In the PR to start 2026.2 dev cycle, the new 2026.2 section has been created, but the markdown disable instruction has remained below 2026.1 section.
Description of user facing changes:

None
Description of developer facing changes:

Moved the instruction to disable markdown linting below 2026.2 section.
Fixes #19395
Fix-up of #19173.
Summary of the issue:

Sometimes, e.g. in Notepad, skim reading process was not correctly interrupted when pressing control key: say all was restarting when another key was pressed.
Description of user facing changes:

Skim reading process is now correctly interrupted when control is pressed.
Description of developer facing changes:

N/A
Description of development approach:

When the speech sequence is stored, it is first filtered.
CallbackCommands are filtered out of the sequence since callback functions such as say all ones should not be called again when repeating the last speech (this was the cause of say all restarting).

Not all BaseCallbackCommands are filtered since other BaseCallbackCommand such as BeepCommand (for indentation reporting) or WaveFileCommands (for spelling error reporting) still need to be reported.

I have not included IndexCommands in the filtering since in any case, they only seem to appear downstream, in the speech manager part.

Regarding CancellableSpeechCommands, I have included them, since I've seen that they are also filtered out from remote speech. Though, I do not exactly know how cancellable speech works, so please double check. Thanks.
…) (#19394)

Fixes #19386
Summary of the issue:

When using UIA to access Microsoft Word documents, page change announcements do not occur when navigating between table rows that span different pages. Page changes are only announced after exiting the table. This does not occur in IAccessible mode ("only where necessary" setting), where page changes are announced correctly within tables.
Description of user facing changes:

When navigating table rows in Word documents using UIA, NVDA will now correctly announce page changes (e.g., "page 2") when moving between rows on different pages.
Description of developer facing changes:
Description of development approach:

The WordDocumentTextInfo._getFormatFieldAtRange method now retrieves page numbers via Word's custom attribute API (UIACustomAttributeID.PAGE_NUMBER), similar to how line numbers and section numbers are already retrieved. The fallback mechanism in getTextWithFields has been updated to only propagate page numbers from control elements when UIARemote is not supported, preserving the more accurate custom attribute values.
Fixes #17005
Summary of the issue:

Currently, slider controls (HTML range inputs and ARIA sliders) are not included in browse mode navigation. Users cannot navigate to sliders using quick navigation keys.
Description of user facing changes:

    Slider controls are now included in browse mode navigation.
    Users can navigate to sliders using quick navigation. By default, no gesture is assigned.

Description of developer facing changes:

    Added "slider" node type to browseMode.py base implementation;
    Implemented slider detection for:
        Gecko (Firefox) virtual buffers,
        MSHTML (Internet Explorer) virtual buffers,
        UIA (Edge, modern UI) browsers,
        Chromium (Chrome) IAccessible implementation.
    Updated changes.md documentation.

Description of development approach:

    Extended the existing browse mode navigation framework to recognize slider controls;
    Used existing control type detection patterns (ARIA roles, HTML input types, UIA control types);
    Followed the same pattern as other form controls (checkbox, radio button, etc.).
…contains only hidden content (#19409)

Summary of the issue:

When a form control (such as a checkbox or radio button) has both:

    An aria-label attribute providing the accessible name
    A <label for="..."> element whose content is entirely aria-hidden

NVDA's browse mode in Firefox fails to announce the accessible name. The name is correctly announced in focus mode.

Example HTML that reproduces the issue:

<input type="checkbox" id="testinput" aria-label="TestArea">
<label for="testinput"><span aria-hidden="true">Test</span></label>

In Firefox browse mode, navigating to this checkbox announces only "check box not checked" with no name, even though the accessible name is "TestArea" from aria-label.

The root cause is in getLabelInfo() which determines if a control's label is "visible" by checking IA2_RELATION_LABELLED_BY targets. It only checked if the label element had STATE_SYSTEM_INVISIBLE, but a label element can be visible while containing only aria-hidden content, meaning it contributes no accessible text to the virtual buffer. This caused NVDA to incorrectly assume the user would read the label content in browse mode, so it didn't force the aria-label name to be announced.

It could be argued that exposing the labelled by association even if the name is set using aria-label is a bug in Firefox, since the label element is no longer contributing to the accessible name of the element and that it should be fixed there. I'm willing to open a Firefox bug for this if preferred.
Description of user facing changes:

Form controls with aria-label will now correctly have their accessible name announced in browse mode, even when an associated <label for> element exists but contains only hidden content.
# Conflicts:
#	source/globalCommands.py
… of page (#19424)

Fixes #19423 

### Summary of the issue:
When using UIA to access Microsoft Word documents, NVDA reports "page
-1" when navigating to the final line of a page. This regression was
introduced in PR #19394 which added support for retrieving page numbers
via Word's Custom Attributes API.

The issue occurs because Word's Custom Attributes API returns `-1` when
the page number value is not yet available. The original code accepted
any integer value without validation, causing NVDA to announce "page -1"
to users.

### Description of user facing changes:
NVDA will now correctly announce the page number (e.g., "page 2")
instead of "page -1" when reaching the final line of a page.

### Description of developer facing changes:
N/A

### Description of development approach:
The fix implements a two-layer validation and fallback mechanism:

1. **Custom Attributes API Validation**: Page numbers from the Custom
Attributes API are now validated to reject invalid values (< 1). When
Word returns -1, the value is not used.

2. **Enhanced Fallback Mechanism**: The existing control field fallback
(which uses automation IDs like `UIA_AutomationId_Word_Page_2`) now:
- Runs regardless of UIARemote support status (previously it was
disabled when UIARemote was supported)
   - Only propagates page numbers from control fields, not format fields
- Only fills in missing page numbers, preserving valid Custom Attributes
API values
- Converts control field page numbers (strings) to integers to match
Custom Attributes API type

This ensures type consistency and prevents false page change detections
due to type mismatches between string `"2"` and integer `2`.
Since this is a fixup pr, I did not include a changelog entry.

### Testing strategy:
Manual testing with:
- Multi-page Word documents
- Forward navigation (down arrow) - page changes announced correctly
- Backward navigation (up arrow) - page changes announced correctly
- Navigation within tables - continues to work as fixed in #19394
- Navigation outside tables - now works correctly in both directions

### Known issues with pull request:
None known
Co-authored-by: Sascha Cowley <16543535+SaschaCowley@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Bill Dengler <codeofdusk@gmail.com>
### Summary of the issue:
Currently, failing type checks in CI only output errors and warning to
the [workflow
summary](https://github.com/nvaccess/nvda/actions/runs/20938000634/attempts/1#summary-60166146854),
rather than the [job run
details](https://github.com/nvaccess/nvda/actions/runs/20938000634/job/60166146854?pr=19432#step:5:26).

### Description of user facing changes:
None

### Description of developer facing changes:
Type checking failures in CI output details to both locations, making
them easier to find.

### Description of development approach:
Tee the output of running pyright rather than saving it directly to a
variable.

### Testing strategy:
Ran `ci/scripts/test/typeCheck.ps1` locally on a branch with pyright
violations.

### Known issues with pull request:
None known.
…when available instead of the legacy model (#19367)

<!-- Please read and fill in the following template, for an explanation
of the sections see:

https://github.com/nvaccess/nvda/blob/master/projectDocs/dev/githubPullRequestTemplateExplanationAndExamples.md
Please also note that the NVDA project has a Citizen and Contributor
Code of Conduct which can be found at
https://github.com/nvaccess/nvda/blob/master/CODE_OF_CONDUCT.md. NV
Access expects that all contributors and other community members read
and abide by the rules set out in this document while participating or
contributing to this project. This includes creating or commenting on
issues and pull requests.

Please initially open PRs as a draft.
When you would like a review, mark the PR as "ready for review".
See
https://github.com/nvaccess/nvda/blob/master/.github/CONTRIBUTING.md.
-->

### Link to issue number:
Closes #13517.

### Summary of the issue:
NVDA exclusively uses Word's legacy object model for sentence
navigation, even when UIA is enabled.

### Description of how this pull request fixes the issue:
Added support for the [UIA custom
extension](https://docs.microsoft.com/en-gb/office/uia/word/wordcustompatterns)
to move and expand by sentence in supported scenarios. The legacy
implementation remains as a fallback on systems without remote ops
support (Windows below 11/Cobalt platform) or older Office versions.

### Testing strategy:
Moved by sentence in a large document and verified functionality.
Beta to master
Closes #12539
Summary of the issue:

This pull request introduces a new NVDA magnifier feature, including docked and lens magnifier modes, color filter support, and a set of global commands for controlling magnification. The changes add new classes for handling magnifier windows, provide optimized color filtering, and implement keyboard shortcuts for toggling magnification, zooming, cycling modes, and color filters.
Description of user facing changes:

Implemented new script commands in globalCommands.py for starting/stopping the magnifier, zooming in/out, cycling color filters, toggling fullscreen mode, cycling magnifier types, and spotlighting the magnifier window, each with descriptive messages and gestures.
Description of developer facing changes:

No significant changes.
A new displayChanged extension point under winAPI._displayTracking, which notifies handlers of display configuration changes.
Description of development approach:

    Introduced the main magnifier module with initialization, activation, and shutdown logic in __init__.py, supporting only full-screen magnification for now.
    Added a FullScreenMagnifier class implementing full-screen magnification, handling zoom, color filters, mouse centering, and spotlight mode, interfacing with the Windows Magnification API.
    Implemented keyboard command handlers for toggling the magnifier, zooming, cycling color filters, changing full-screen modes, and starting spotlight mode in commands.py.
    Added a configuration module (config.py) to manage default zoom level, color filter, full-screen mode, and mouse centering settings, with utilities for retrieving and setting these values.
Fixes #19435
Summary of the issue:

NVDA on X86 and X64 is not linked with the /CETCOMPAT flag. This flag should improve security.
Description of user facing changes:

None
Description of developer facing changes:

None
Description of development approach:

Add the flag, but only on X64 and X86.
Summary of the issue:

When trying to execute Magnifier commands while Magnifier is not active, there is no error message and an error is logged because pgettext context argument has been put at a wrong position.
Description of user facing changes:

    Executing Magnifier commands when Magnifier is not active now report a message as expected.
    The reported messages have been improved to be more similar to other NVDA messages when an action cannot be performed.

Description of developer facing changes:

N/A
Description of development approach:

    Fix pgettext call
    Reworded error messages
#19462
Summary of the issue:

    Mouse movement was not smooth enough while using magnifier
    Spotlight steps were sacading due to zoom validation

Description of user facing changes:

None
Description of developer facing changes:

None
Description of development approach:

Reduce the value of refreshment for mouse
Adding a function to zoom with raw values ( for animation purpose)
cary-rowen and others added 19 commits April 29, 2026 12:47
Fixes #20015
Summary of the issue:

After cancelling an add-on download by closing the Add-on Store and choosing "Yes", the store can get into a bad state.
If the user opens it again and starts another download, NVDA can raise:

AttributeError: 'NoneType' object has no attribute 'submit'

Closing the store again after that can also raise:

AttributeError: 'NoneType' object has no attribute 'shutdown'

Description of user facing changes:

After cancelling a download, the Add-on Store can be used again normally.
Starting another download no longer throws those exceptions.
Closing the store again after that also works as expected.
Description of developer facing changes:

AddonFileDownloader now tracks state per download attempt.
This prevents an older cancelled worker from interfering with a later retry of the same add-on.
Description of development approach:

The fix updates the downloader so each download attempt has its own state and temporary file path.
After cancelAll(), the downloader can recreate its executor and be reused safely.
When a worker finishes, it now checks whether it still belongs to the active attempt before updating progress, cleaning up files, or completing the download.
fix #20012
Summary of the issue:

When Windows Magnifier was already running, NVDA Magnifier could still start because MagInitialize() succeeds even when the API is held by another process. Only MagSetFullscreenTransform() would fail, triggering the recovery loop — which itself only tested MagInitialize() and MagSetFullscreenColorEffect(), both of which succeed, causing it to falsely declare victory and restart the timer indefinitely.
Description of user facing changes:

NVDA now shows a clear message ("Cannot start magnifier: the magnification API is unavailable. Windows Magnifier may already be running.") when the magnification API cannot be fully initialized at startup. If the conflict occurs after startup, the recovery mechanism is now capped at 3 attempts before stopping the magnifier and notifying the user, preventing log spam.
Description of developer facing changes:

_initializeNativeMagnification() no longer uses @trackNativeMagnifierErrors — it now raises OSError on failure (including a MagSetFullscreenTransform probe that detects the Windows Magnifier conflict). _startMagnifier() catches this error, calls _stopMagnifier() to properly unregister the display change handler, and returns early. _attemptRecovery() reuses _initializeNativeMagnification() to avoid duplicating the probe logic, and is now capped by _MAX_RECOVERY_ATTEMPTS = 3.
Description of development approach:

The fix centers on making _initializeNativeMagnification() a reliable gate: it probes MagSetFullscreenTransform (the actual failing call) as part of initialization and raises on failure rather than silently returning. This single change fixes both the startup path and the recovery path, since _attemptRecovery() can delegate to the same method. A _recoveryAttempts counter (capped at _MAX_RECOVERY_ATTEMPTS) prevents the infinite restart loop when the API remains permanently unavailable.
[upgrade mikepenz/action-junit-report to v6 to resolve Node.js
deprecation](https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/)

"Node.js deprecation warnings (e.g., mikepenz/action-junit-report@v5
requiring Node.js 20) no longer appear in specific Actions runs, such as
this job, following the upgrade to v6."

https://github.com/dpy013/nvda/actions/runs/25235122899/job/74002155719?spm=5176.28103460.0.0.96a02988qH35o5

Signed-off-by: dpy013 <26911141+dpy013@users.noreply.github.com>
This serves as a fix for the math reading bug in #19813, but doesn't entirely close that issue since the underlying issue with ISimpleDOM is still present.
Summary of the issue:

NVDA can fail to read MathML in Chromium if the page was already open before NVDA starts or restarts. This seems to be due to ISimpleDOM not being registered.
Description of user facing changes:

Math is more reliably read in Chromium, even when a page is already opened before NVDA starts or restarts.
Description of developer facing changes:

The method Math._get_mathMl now checks the IA2 math attribute before falling back to ISimpleDOM.
Description of development approach:

Update Math._get_mathMl to use the IA2 math attribute when available, strip embedded HTML comments, and return the result wrapped in <math>. If the IA2 math attribute is not available, fall back to the existing ISimpleDOM behavior.

A quick note about the embedded HTML comments: I only realized stripping of HTML comments was necessary after testing an initial implementation. It seems like Chromium inserts HTML comments into the IA2 math attribute, which was causing parse errors in MathCAT.
Testing strategy:

    Started Chrome and opened a page containing math.
    Ran NVDA from source in debug mode after having already started Chrome.
    Moved through the page with NVDA and verified that the math was being read correctly.
    Inspected the log and verified that the new debug logs ("Got MathML from IA2 math attribute") were present.
    Repeated steps 1-4 with Edge instead of Chrome.
…0027)

Fixes #20013.
Summary of the issue:

In NVDA's speech dictionaries, the four word-boundary entry types (Whole word, Part of word, Start of word and End of word) generate patterns based on \b and \w.
Python's stdlib re treats Unicode combining marks (general category Mn) as non-word characters.
For scripts that rely on combining marks — Hebrew niqqud, Arabic harakat, Devanagari matras, etc. — this means \w treats a base letter and its mark as separate runs and \b reports spurious word boundaries between them.
As a result, word-boundary entries fail to match (or match in unintended places) on Hebrew, Arabic, Indic and other texts.
Description of user facing changes:

    Speech dictionary entries of type Whole word, Part of word, Start of word and End of word now handle Unicode combining marks correctly.
        Entries match the intended word in scripts such as Hebrew, Arabic and Devanagari.
        Whole word entries no longer match inside larger words when those words contain combining marks.
    New Advanced setting "Use modern regular expression engine for speech dictionary entries" lets users opt Regular expression entries into the same modern engine. Disabled by default.

Description of developer facing changes:

    New runtime dependency: the third-party regex package (Apache-2.0).
    speechDictHandler.types.SpeechDictEntry selects between re and regex per entry type:
        Word-boundary entry types (WORD, PART_OF_WORD, START_OF_WORD, END_OF_WORD) always compile via regex with regex.VERSION1 | regex.UNICODE.
        REGEXP continues to use stdlib re unless the new [featureFlag] speechDictsUseModernRegex flag is enabled.
        ANYWHERE keeps using stdlib re.
        UNIX is unaffected: fnmatch.translate output is still compiled via stdlib re.
    New [featureFlag] config key speechDictsUseModernRegex (BoolFlag, default disabled).

Description of development approach:

    Engine choice is localised to SpeechDictEntry.__post_init__. The rest of the codebase still works with re.
    The regex module's VERSION1 mode classifies combining marks as word characters and respects them around \b, which is exactly the missing semantics. Word-boundary entry types switch unconditionally because they are new in 2026.2 and have no existing user data to break.
    REGEXP entries are gated behind a feature flag to avoid silently changing the meaning of patterns that users have written against re semantics. The flag is intended to flip to enabled in a future release once feedback confirms there are no regressions.
fixes #19691
Summary of the issue:

When using the fullscreen magnifier in Center mode, clicking on controls in certain apps (Discord, Microsoft Teams, HyperX NGENUITY Beta) had no effect. The magnifier transform could shift between the physical button press and Windows applying it to the click coordinates, causing the click to land at the wrong logical position.
Description of user facing changes:

Mouse clicks on controls in apps such as Discord, Microsoft Teams, and HyperX NGENUITY Beta should work correctly when the fullscreen magnifier is running in Center mode.
Description of developer facing changes:

Replaced winUser.getKeyState / mouseHandler.isLeftMouseButtonLocked with winUser.getAsyncKeyState in FocusManager.getCurrentFocusCoordinates and FullScreenMagnifier._keepMouseCentered. Updated tests accordingly.
Description of development approach:

tKeyState is queue-based and can return a stale button state in the wx main thread when the message queue hasn't yet processed the click event. Switching to GetAsyncKeyState ensures the magnifier detects a button press immediately, preventing the Center mode transform from shifting between the physical press and Windows applying it to the click coordinates.
pre - #19473
parts of #19810
Summary of the issue:

needed to add logic for changing types before new types
Description of user facing changes:

Magnifier got a new cycle gesture to change to soon to be implemented new types: fixed/docked/lens
Description of developer facing changes:

added a placeholder for easier developement to be done.
Description of development approach:

as the new modes will inherite from the same logics, first making sure they can be added correctly to the actual code
Summary of the issue:

Caches expire after 6 hours, it would be nice if we could increase that number higher, to maybe 8-12.
Description of user facing changes:

None
Description of developer facing changes:

We can tweak cache expiration via github variables
Description of development approach:

Add variable
fix #19878
Summary of the issue:

The magnifier could receive focus coordinates outside the screen boundaries, which allowed the magnified view to drift beyond the visible area depending on the mode.
Description of user facing changes:

The magnifier now stays within screen boundaries at all times, in both normal mode and true center mode. This makes the visible behavior more stable and prevents off-screen overflow.
Description of developer facing changes:

Automatic boundary protection was added through a currentCoordinates property that clamps coordinates on every assignment. The main magnifier lifecycle paths and pan logic now use this property, which centralizes validation and removes unsafe direct coordinate handling.
Description of development approach:

I isolated the nearest control point for magnifier coordinates, then added reusable clamping logic based on the existing screen limit calculation.
Summary of the issue:

#18207 introduced these two lines in nvwave.py:

		if not isinstance(data, bytes):
			data = string_at(data, size)

string_at creates another copy of the data buffer stored as bytes, which is not needed, since it will only be passed to wasPlay_feed. As many built-in synths pass in c_void_p or a ctype array instead of bytes, this would create a lot of unnecessary memory copies. data should be passed to wasPlay_feed directly if possible.

As it turned out, those two lines were added because of the argtypes set for wasPlay_feed:

wasPlay_feed = dll.wasPlay_feed
wasPlay_feed.restype = HRESULT
wasPlay_feed.argtypes = (
	HWasapiPlayer,  # player
	c_char_p,  # data
	c_uint,  # size
	POINTER(c_uint),  # id
)

c_void_p cannot be implicitly converted to c_char_p, so string_at was added as a fix.
Description of user facing changes:

None.
Description of developer facing changes:

None. data still accepts anything convertible to c_void_p and bytes.
Description of development approach:

This PR chooses another way to fix that problem: cast data to c_char_p explicitly. Anything that can be converted to c_void_p can be converted to c_char_p this way, including c_void_p, bytes, and ctypes array.
Summary of the issue:

Clean up the magnifying glass-related code.
Description of user facing changes:

None
Description of developer facing changes:

Clean up the magnifying glass-related code.
Description of development approach:

    Remove duplicate screen curtain checks.
    Type hint: Added @OverRide annotation.
    fix tyype hint
    Remove redundant return
    rename filter variable to currentFilter, Because it shadows a builtin functions.
Closes #20046
Summary of the issue:

libmathcat_py.pyd is created both in the NVDA installation directory and in include\nvda-mathcat\assets.
Description of user facing changes:

Reduces the size of the launcher by ~4 MB.
Description of developer facing changes:

None; libmathcat_py can still be imported the same way. The duplicate being removed was not imported or used by any other code.
Description of development approach:

Excludes importable files in the same way that is already used for synthDrivers on line 389 of setup.py.
Fixes #20021
Summary of the issue:

In File Explorer on Windows 10, pressing Ctrl+F should move focus directly to the search edit field. Since NVDA 2026.1 beta, NVDA first reports the search edit field, but then reports a pane, so users need to press Ctrl+F a second time before they can reliably type in the search box.

This is a regression from #19117. That PR removed several old Windows 8.x Start screen workarounds from the Explorer app module, including SearchBoxClient. Although that class was described as a Windows 8 workaround, it was still filtering a redundant File Explorer search band focus event. On Windows 10, the redundant object has:

role == controlTypes.Role.PANE
windowClassName == "Search Box"

Description of user facing changes:

Same behavior as before 2026.1Beta.
Description of developer facing changes:

Restores a small Explorer app module overlay for File Explorer search band pane objects. The overlay suppresses redundant IAccessible focus events from Search Box and UniversalSearchBand pane objects.

Search Box has been confirmed on Windows 10. UniversalSearchBand is retained from the pre-#19117 filter because it appears to be another Explorer search band class, and the match is still limited to IAccessible pane objects.
Description of development approach:

This keeps the #19117 cleanup intact, except for the part that affected File Explorer search focus. The Windows 8 Start screen tile, group, launcher, and suggestion overlays are not restored.
<!-- Please read and fill in the following template, for an explanation
of the sections see:

https://github.com/nvaccess/nvda/blob/master/projectDocs/dev/githubPullRequestTemplateExplanationAndExamples.md
Please also note that the NVDA project has a Citizen and Contributor
Code of Conduct which can be found at
https://github.com/nvaccess/nvda/blob/master/CODE_OF_CONDUCT.md. NV
Access expects that all contributors and other community members read
and abide by the rules set out in this document while participating or
contributing to this project. This includes creating or commenting on
issues and pull requests.

Please initially open PRs as a draft.
When you would like a review, mark the PR as "ready for review".
See
https://github.com/nvaccess/nvda/blob/master/.github/CONTRIBUTING.md.
-->

### Link to issue number:
<!-- Use Closes/Fixes/Resolves #xxx to link this PR to the issue it is
responding to. -->

### Summary of the issue:

### Description of user facing changes:

### Description of developer facing changes:

### Description of development approach:

### Testing strategy:

### Known issues with pull request:

### Code Review Checklist:

<!--
This checklist is a reminder of things commonly forgotten in a new PR.
Authors, please do a self-review of this pull-request.
Check items to confirm you have thought about the relevance of the item.
Where items are missing (eg unit / system tests), please explain in the
PR.
To check an item `- [ ]` becomes `- [x]`, note spacing.
You can also check the checkboxes after the PR is created.
A detailed explanation of this checklist is available here:

https://github.com/nvaccess/nvda/blob/master/projectDocs/dev/githubPullRequestTemplateExplanationAndExamples.md#code-review-checklist
-->

- [ ] Documentation:
  - Change log entry
  - User Documentation
  - Developer / Technical Documentation
  - Context sensitive help for GUI changes
- [ ] Testing:
  - Unit tests
  - System (end to end) tests
  - Manual testing
- [ ] UX of all users considered:
  - Speech
  - Braille
  - Low Vision
  - Different web browsers
  - Localization in other languages / culture than English
- [ ] API is compatible with existing add-ons.
- [ ] Security precautions taken.
#19938
Summary of the issue:

This PR adds pinch in and pinch out gestures as the first step toward expanding NVDA's touch gestures. See #19938 for more information.
Description of user facing changes:

Two new touch gestures are now available for assignment:

    Pinch in: place two fingers on the screen and move them toward each other.
    Pinch out: place two fingers on the screen and move them away from each other.

The fingers must move at least 50 pixels closer together or further apart to be recognised as a pinch.
Description of developer facing changes:
Description of development approach:

The pinch gesture is detected entirely within TrackerManager in touchTracker.py.
When two fingers are on screen simultaneously, the initial distance between them is recorded.
When both fingers lift, the final distance is computed from their last known positions.
If the change in distance meets or exceeds the minimum threshold, a pinch in or pinch out MultiTouchTracker is queued and emitted like any other gesture.
…#20096)

Fixes #15159.
Summary of the issue:

In focus mode in web browsers, when a control specifies a label using aria-label or aria-labelledby, it is impossible to review or spell that label; e.g. using read current line or the review cursor. This is particularly annoying and detrimental to efficiency when using web apps such as Gmail and Slack, among others, where you have to switch to browse mode to work around this even though it might be more efficient to use focus mode for navigation.
Description of user facing changes:

In focus mode in web browsers, it is now possible to review and spell the labels of controls when those labels are specifically provided for accessibility; e.g. via aria-label or aria-labelledby.
Description of development approach:

When in focus mode, for non-editable controls, both speech and braille report the name, description, value, etc. of the control; they don't use the TextInfo. However, previously, the review cursor and the read line command always used the TextInfo. Since IA2TextTextInfo is used for all objects supporting IAccessibleText, this meant that if the content wasn't a flat piece of text equal to the label, the review cursor and read line command would report something different, often nothing at all.

To fix this, for IA2Web objects that aren't editors, use NVDAObjectTextInfo instead of the object's TextInfo for object review and the read line command. I first attempted to do this generically in #18271 using _hasNavigableText, but this caused problems elsewhere. Instead, the scope of this change is very specific, introducing a new _shouldUseTextInfoForReview NVDAObject attribute which is only set to False for Ia2Web objects.
Fixes #20080
Summary of the issue:

When NVDA is configured to report if the language of the text been read is supported, an error is produced if the syntesizer exposes available languages as None.
Description of user facing changes:

Fixed an error with certain syntesizers when reporting if the language of the text been read is supported.
Description of developer facing changes:
Description of development approach:

Check if available language is None.
Copilot AI review requested due to automatic review settings May 11, 2026 08:48
@seanbudd seanbudd requested review from a team as code owners May 11, 2026 08:48
@seanbudd seanbudd merged commit 59cb7ee into beta May 11, 2026
51 of 55 checks passed
@github-actions github-actions Bot added this to the 2026.3 milestone May 11, 2026
@seanbudd seanbudd modified the milestones: 2026.3, 2026.2 May 11, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR is the final master→beta merge for the NVDA 2026.2 release, bringing beta up to date with commit 567a5797cb from master.

Changes:

  • Introduces/updates multiple user-facing features and infrastructure changes (e.g. magnifier work, input help speech changes, logging updates) as part of the 2026.2 release merge.
  • Expands automated test coverage (unit + system tests) for newly added/changed behavior.
  • Updates build/CI/tooling + project documentation to align with the merged release state.

Reviewed changes

Copilot reviewed 186 out of 191 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/unit/test_speech.py Adjust speech unit sequence.
tests/unit/test_remote/test_localMachine.py Readability tweak in param generation.
tests/unit/test_magnifier/test_magnifierCommands.py New magnifier command unit tests.
tests/unit/test_magnifier/init.py Magnifier unit test package init.
tests/unit/test_logHandler.py New tests for log redaction.
tests/unit/test_asyncioEventLoop.py New asyncio event loop tests.
tests/system/robot/symbolPronunciationTests.py Update expected speech spacing.
tests/system/robot/chromeTests.robot Add chrome_link robot tests.
tests/system/robot/chromeTests.py Add link destination system tests.
tests/sconscript Remove old checkPot SCons alias.
tests/manual/nvdaUI/addonStore.md Markdown table formatting tweak.
tests/checkPot.py Update checkPot strings/CLI exit behavior.
source/winBindings/magnification.py Add MagSetFullscreenTransform binding.
source/winAPI/_powerTracking.py Use ngettext for percent string.
source/winAPI/_displayTracking.py Add displayChanged extension point + typing.
source/virtualBuffers/MSHTML.py Add slider searchable attrs.
source/virtualBuffers/gecko_ia2.py Add slider searchable attrs.
source/virtualBuffers/init.py Refactor screen layout toggle helper.
source/utils/raii.py Add RAII helpers for cleanup.
source/utils/_deprecate.py Add callValue option to RemovedSymbol.
source/UIAHandler/remote.py Refactor Word extended pattern + new sentence op.
source/UIAHandler/browseMode.py Add slider quicknav iterator.
source/touchTracker.py Add pinch gesture tracking.
source/touchHandler.py Add TouchMode enum + browse-mode touch integration.
source/textUtils/init.py Supplementary Unicode normalization mapping.
source/textInfos/init.py Improve link destination lookup traversal.
source/synthDrivers/oneCore.py Add punctuation silence support.
source/synthDriverHandler.py Add PunctuationSilenceSetting + language support fix.
source/speechDictHandler/dictFormatUpgrade.py Logging/formatting cleanup in dict upgrade.
source/speechDictHandler/definitions.py New speech dictionary definitions registry.
source/speech/init.py Register pre_speech hook to track last speech string.
source/setup.py Bundle detect-secrets modules + adjust MathCAT assets path.
source/screenCurtain/_screenCurtain.py Notify magnifier on screen curtain enable/disable.
source/review.py Add typing + use _shouldUseTextInfoForReading.
source/nvwave.py Adjust feed path + lastActiveTime typing.
source/NVDAObjects/JAB/init.py Optimize table-parent detection.
source/NVDAObjects/IAccessible/winword.py Lambda formatting readability tweak.
source/NVDAObjects/IAccessible/ia2Web.py MathML via IA2 attr + reading TextInfo flag.
source/NVDAObjects/behaviors.py Use ngettext for percent string.
source/NVDAObjects/init.py Add _shouldUseTextInfoForReading flag.
source/nvda.pyw Add shebang.
source/nvda_slave.pyw Add shebang.
source/mouseHandler.py Add type hints for geometry helpers.
source/mathPres/MathCAT/MathCAT.py Refactor MathCAT navigation scripting.
source/mathPres/init.py Initialize MathCAT nav scripts.
source/logHandler.py Add secrets log level + secret redaction support.
source/locale/fr/gestures.ini Add FR zoomOut gesture mapping.
source/locale/fi/symbols.dic Update Finnish caret symbol name.
source/languageHandler.py Fix getLanguageDescription return type.
source/keyboardHandler.py Add character derivation via ToUnicodeEx.
source/inputCore.py Improve input help speech/braille output logic.
source/gui/installerGui.py Add post-install dialog w/ restart/start options.
source/gui/configManagement.py Add factory reset undo dialog flow.
source/gui/addonStoreGui/viewModels/store.py Parenthesize validCheck lambda.
source/gui/addonStoreGui/controls/storeDialog.py Sorting/filter UI logic updates.
source/gui/addonStoreGui/controls/messageDialogs.py Parenthesize filterFunc lambda.
source/gui/addonStoreGui/controls/addonList.py Typing fixes + sortableFields usage.
source/core.py Initialize/terminate asyncio event loop.
source/config/configSpec.py Add settings (magnifier, braille rate, etc).
source/config/configFlags.py Add SECRETS log level + PlayErrorSound enum.
source/comInterfaces_sconscript Add shebang + formatting.
source/buildVersion.py Bump version_major to 2.
source/brailleTables/__tables.py Add/update table display names.
source/brailleDisplayDrivers/freedomScientific.py Simplify lambda formatting.
source/brailleDisplayDrivers/dotPad/defs.py Add DP command helpers + key groups.
source/brailleDisplayDrivers/brailliantB.py Reformat bluetooth detection predicate.
source/brailleDisplayDrivers/baum.py Add Orbit Reader 40 IDs.
source/braille.py Add auto-scroll support + disable on navigation.
source/autoSettingsUtils/autoSettings.py Typing cleanups + modern type alias.
source/argsParsing.py Add secrets log level CLI choice.
source/appModules/whatsapp_root.py New app module for WhatsApp WebView2.
source/appModules/explorer.py Explorer focus event workaround + product info.
source/addonHandler/init.py Add speechDictionaries manifest support.
source/_magnifier/utils/filterHandler.py New magnifier filter matrices.
source/_magnifier/utils/errorHandling.py New magnifier native error decorator.
source/_magnifier/utils/init.py Magnifier utils package init.
source/_magnifier/lensMagnifier.py New lens magnifier skeleton.
source/_magnifier/fixedMagnifier.py New fixed magnifier skeleton.
source/_magnifier/dockedMagnifier.py New docked magnifier skeleton.
source/_magnifier/init.py New magnifier module entry points.
source/_bridge/runtimes/synthDriverHost/synthDriverHost.py Add brokerAudio option + nvwave proxy binding.
source/_bridge/runtimes/synthDriverHost/main.pyw Add shebang.
source/_bridge/runtimes/synthDriverHost/config.py Lambda formatting tweak.
source/_bridge/components/services/nvwave.py New WavePlayer RPYC service.
source/_bridge/components/proxies/synthDriver.py Remove audio ducking suspender behavior.
source/_bridge/components/proxies/nvwave.py New WavePlayer proxy implementation.
source/_bridge/clients/synthDriverHost32/launcher.py Expose WavePlayer service + installProxies args.
source/_bridge/base.py Add pipe handle duplication helpers.
source/_asyncioEventLoop/utils.py Add coroutine scheduling helpers.
source/_asyncioEventLoop/_state.py New shared state module.
source/_asyncioEventLoop/init.py New background asyncio loop lifecycle.
security.md Update severity levels + SLAs.
runcheckpot.bat New helper for translation checks.
pyproject.toml Dependency bumps + new deps.
projectDocs/testing/automated.md Update translation check instructions.
projectDocs/issues/triage.md Document ADR-required flow.
projectDocs/issues/readme.md Clarify where to report issues.
projectDocs/issues/githubIssueTemplateExplanationAndExamples.md Document ADR template + translator scope.
projectDocs/dev/userGuideStandards.md Update markdown examples + terminology.
projectDocs/dev/proposingMajorChanges.md New ADR process doc.
projectDocs/dev/developerGuide/sconscript Add shebang + formatting + Return placement.
projectDocs/dev/createDevEnvironment.md Update liblouis version.
projectDocs/dev/contributing.md Rewrite contribution guidance + checkPot update.
projectDocs/dev/codingStandards.md Clarify gettext usage + fix typos.
projectDocs/dev/buildingNVDA.md Adjust scons instructions.
projectDocs/dev/addons.md Update addon links.
projectDocs/community/expertsList.md Markdown table formatting.
nvdaHelper/vbufBase/sconscript Add shebang + formatting.
nvdaHelper/vbufBackends/webKit/sconscript Add shebang + formatting.
nvdaHelper/vbufBackends/mshtml/sconscript Add shebang + formatting.
nvdaHelper/vbufBackends/lotusNotesRichText/sconscript Add shebang + formatting.
nvdaHelper/vbufBackends/gecko_ia2/sconscript Add shebang + formatting.
nvdaHelper/vbufBackends/gecko_ia2/gecko_ia2.cpp Improve label visibility check logic.
nvdaHelper/vbufBackends/adobeAcrobat/sconscript Add shebang + formatting.
nvdaHelper/uwp/sconscript Add shebang + formatting.
nvdaHelper/UIARemote/sconscript Add shebang + formatting.
nvdaHelper/sonic/sconscript Add shebang + formatting.
nvdaHelper/remoteLoader/sconscript Add shebang.
nvdaHelper/remote/sconscript Add shebang + formatting.
nvdaHelper/localWin10/sconscript Add shebang.
nvdaHelper/localWin10/oneCoreSpeech.h Add punctuation silence API exports.
nvdaHelper/localWin10/oneCoreSpeech.cpp Implement punctuation silence get/set.
nvdaHelper/local/sconscript Add shebang + formatting.
nvdaHelper/local/nvdaHelperLocal.cpp Hook SendMessageA/TimeoutA + refactor helper.
nvdaHelper/liblouis/sconscript Add shebang + formatting.
nvdaHelper/javaAccessBridge/sconscript Add shebang + formatting.
nvdaHelper/ISimpleDOM_sconscript Add shebang + formatting.
nvdaHelper/ia2_sconscript Add shebang.
nvdaHelper/espeak/sconscript Add shebang + formatting/cleanup.
nvdaHelper/detours/sconscript Add shebang.
nvdaHelper/client/sconscript Add shebang + formatting.
nvdaHelper/archBuild_sconscript Add shebang + formatting + CET compat flag.
nvdaHelper/acrobatAccess_sconscript Add shebang + formatting.
ensureuv.ps1 Bump uv version.
cldrDict_sconscript Add shebang.
ci/scripts/tests/typeCheck.ps1 Preserve pyright output for failure message.
ci/scripts/tests/translationCheck.ps1 Switch to runcheckpot.bat.
ci/README.md Expand fork setup guidance + wording tweaks.
appx/sconscript Add shebang + formatting.
.pre-commit-config.yaml Add actionlint + bump tool versions + runcheckpot hook.
.markdownlint.jsonc Configure table column style.
.github/workflows/testAndPublish.yml Update action versions + add chrome_link suite.
.github/workflows/clearCaches.yml Improve inputs + default cache expiration logic.
.github/ISSUE_TEMPLATE/16-adr_major_project.md Add ADR advanced template.
.github/ISSUE_TEMPLATE/11-bug_report.md Update tool name wording.
.github/ISSUE_TEMPLATE/06-adr_major_project.yaml Add ADR form template.
.github/ISSUE_TEMPLATE/01-bug_report.yaml Update tool name wording.
.github/instructions/userGuide.instructions.md Add user guide authoring instructions.
.github/instructions/review.instructions.md Add PR review guidance for Copilot.
.github/instructions/cpp.instructions.md Add C++ review guidance.
.gitattributes Normalize eol/text attributes.
.git-blame-ignore-revs Add formatting-only revision ignores.

Comment thread source/nvwave.py
Comment on lines +343 to 351
# wasPlay_feed requires c_char_p, so cast data to c_char_p before calling.
# Casting bytes to c_char_p is also fine, as long as the original data is not released.
dataptr = cast(data, c_char_p)
try:
wasapi.wasPlay_feed(
self._player,
data,
dataptr,
size if size is not None else len(data),
byref(feedId) if onDone else None,
Comment on lines +51 to +52
case _:
raise ValueError(f"Unsupported magnifier view: {MagnifiedView}")
Comment on lines +16 to +19
eventLoop: asyncio.BaseEventLoop
"""The asyncio event loop used by NVDA."""
asyncioThread: Thread
"""Thread running the asyncio event loop."""
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.