Skip to content

Magnifier integration #19172

Closed
Boumtchack wants to merge 26 commits into
nvaccess:masterfrom
France-Travail:reorganisationOfMagnifierLogics
Closed

Magnifier integration #19172
Boumtchack wants to merge 26 commits into
nvaccess:masterfrom
France-Travail:reorganisationOfMagnifierLogics

Conversation

@Boumtchack

@Boumtchack Boumtchack commented Nov 5, 2025

Copy link
Copy Markdown
Contributor

Link to issue number:

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:

TODO

Description of development approach:

Added new classes (GlobalPanel, GlobalFrame, DockedFrame, LensFrame) in windowsHandler.py to implement docked and lens magnifier windows, with crosshair overlays, color filter support, and optimized screen capture and scaling.

Testing strategy:

unit tests:

  • unittest for each functions and classes

system tests:

  • a system test which toggles each type of magnifier (lens, docked, etc)

manual tests

  • a manual test plan in tests/manual which goes over how to manually test the magnifier

Known issues with pull request:

docked window can't be resized, lens magnifier is not centered on the mouse due to zooming infinitly

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.

@Boumtchack Boumtchack marked this pull request as ready for review November 5, 2025 12:47
Copilot AI review requested due to automatic review settings November 5, 2025 12:47
@Boumtchack Boumtchack requested a review from a team as a code owner November 5, 2025 12:48
@Boumtchack Boumtchack requested a review from seanbudd November 5, 2025 12:48
@Boumtchack Boumtchack marked this pull request as draft November 5, 2025 12:50

Copilot AI 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.

Pull Request Overview

This PR adds a comprehensive NVDA Magnifier feature with three magnification modes (fullscreen, docked, and lens) along with extensive unit test coverage. The implementation includes color filtering, zoom controls, spotlight mode, and various focusing mechanisms.

  • Implements NVDAMagnifier classes for fullscreen, docked, and lens magnification with Windows API integration
  • Adds keyboard shortcuts and scripts in globalCommands.py to control the magnifier
  • Provides comprehensive unit test coverage for all magnifier components

Reviewed Changes

Copilot reviewed 9 out of 10 changed files in this pull request and generated 20 comments.

Show a summary per file
File Description
source/NVDAMagnifier/init.py Core magnifier implementation including base class, fullscreen, docked, lens magnifiers, and spotlight manager
source/NVDAMagnifier/windowsHandler.py Window management for different magnifier types with panel rendering and color filter application
source/globalCommands.py Global command scripts and keyboard shortcuts for controlling the magnifier
tests/unit/test_NVDAMagnifier/test_window.py Tests for window handling components (panels and frames)
tests/unit/test_NVDAMagnifier/test_fullscreenMagnifier.py Tests for fullscreen magnifier functionality
tests/unit/test_NVDAMagnifier/test_dockedMagnifier.py Tests for docked magnifier functionality
tests/unit/test_NVDAMagnifier/test_lensMagnifier.py Tests for lens magnifier functionality
tests/unit/test_NVDAMagnifier/test_spotlightManager.py Tests for spotlight animation manager
tests/unit/test_NVDAMagnifier/test_NVDAMagnifier.py Tests for base magnifier class functionality
Comments suppressed due to low confidence (1)

source/NVDAMagnifier/windowsHandler.py:168

  • This comment appears to contain commented-out code.
		# if self.colorFilter == "normal":
		# 	return image
		# elif self.colorFilter == "greyscale":
		# 	for y in range(height):
		# 		for x in range(width):
		# 			r, g, b = image.GetRed(x, y), image.GetGreen(x, y), image.GetBlue(x, y)
		# 			gray = int(0.299 * r + 0.587 * g + 0.114 * b)
		# 			gray = max(0, min(255, gray))
		# 			image.SetRGB(x, y, gray, gray, gray)
		# elif self.colorFilter == "inverted":
		# 	for y in range(height):
		# 		for x in range(width):
		# 			r, g, b = image.GetRed(x, y), image.GetGreen(x, y), image.GetBlue(x, y)
		# 			image.SetRGB(x, y, 255 - r, 255 - g, 255 - b)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/unit/test_NVDAMagnifier/test_fullscreenMagnifier.py Outdated
Comment thread tests/unit/test_NVDAMagnifier/test_fullscreenMagnifier.py Outdated
Comment thread tests/unit/test_NVDAMagnifier/test_fullscreenMagnifier.py Outdated
Comment thread tests/unit/test_NVDAMagnifier/test_fullscreenMagnifier.py Outdated
Comment thread tests/unit/test_NVDAMagnifier/test_spotlightManager.py Outdated
Comment on lines +347 to +349
def startMagnifying(self, colorFilter: str) -> None:
"""Start the magnification with lens-specific settings."""
self.setColorFilter(colorFilter)

Copilot AI Nov 5, 2025

Copy link

Choose a reason for hiding this comment

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

This method requires 2 positional arguments, whereas overridden GlobalFrame.startMagnifying requires 1.

Suggested change
def startMagnifying(self, colorFilter: str) -> None:
"""Start the magnification with lens-specific settings."""
self.setColorFilter(colorFilter)
def startMagnifying(self) -> None:
"""Start the magnification with lens-specific settings."""
# Assumes self.colorFilter is set before this is called
self.setColorFilter(self.colorFilter)

Copilot uses AI. Check for mistakes.
self.setColorFilter(colorFilter)
super().startMagnifying()

def updateMagnifier(self, focusX: int, focusY: int, zoomLevel: float, colorFilter: str) -> None:

Copilot AI Nov 5, 2025

Copy link

Choose a reason for hiding this comment

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

This method requires 5 positional arguments, whereas overridden GlobalFrame.updateMagnifier requires 4.

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,843 @@
import ctypes

Copilot AI Nov 5, 2025

Copy link

Choose a reason for hiding this comment

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

Module 'ctypes' is imported with both 'import' and 'import from'.

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,241 @@
import NVDAMagnifier

Copilot AI Nov 5, 2025

Copy link

Choose a reason for hiding this comment

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

Module 'NVDAMagnifier' is imported with both 'import' and 'import from'.

Copilot uses AI. Check for mistakes.
Comment thread source/NVDAMagnifier/__init__.py Outdated
Boumtchack and others added 11 commits November 5, 2025 15:01
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

@seanbudd seanbudd left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks @Boumtchack - this is in a really solid state feature and design-wise.
I've given some broader comments on code layout and styling issues.

The test coverage here is really great.

Comment on lines +14 to +16
Args:
parent: Parent window
panelType: Either "docked" or "lens" to determine behavior

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please use our docstring style, sphinx

Suggested change
Args:
parent: Parent window
panelType: Either "docked" or "lens" to determine behavior
:param parent: Parent window
:param panelType: Either "docked" or "lens" to determine behavior

import wx


class GlobalPanel(wx.Panel):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

lets go with "MagnifierBase" rather than "Global" when talking about code that serves as a shared base for other code. Global tends to imply functionality that is enforced globally, not where it can be overwritten with inheritance

Suggested change
class GlobalPanel(wx.Panel):
class MagnifierBasePanel(wx.Panel):

@@ -0,0 +1,842 @@
import ctypes

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@@ -0,0 +1,842 @@
import ctypes

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

please refactor the majority of this code into files within this folder. The init file should really only have high level logic such as initialization and termination of the module.

@@ -0,0 +1,842 @@
import ctypes

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please rename this submodule to just "magnifier"

self.drawPlaceholder(dc)


class GlobalFrame(wx.Frame):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would suggest moving the frames to their own submodules magnifier.frames.base, magnifier.frames.docked, etc

)


# Base Code

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

these kinds of comments can largely be removed. code grouping should be largely done through separate files

Comment thread source/globalCommands.py
category=SCRCAT_VISION,
gestures=["kb:windows+numLock+numpadPlus", "kb:windows+numpadPlus", "kb:windows+="],
)
def script_startMagnifier(self, gesture):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

please add type hints for gesture params

Comment thread source/globalCommands.py
category=SCRCAT_VISION,
gestures=["kb:windows+numLock+numpadPlus", "kb:windows+numpadPlus", "kb:windows+="],
)
def script_startMagnifier(self, gesture):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Would you be able to define the function logic for these commands in magnifier.commands, so that globalCommands.script_startMagnifier just calls a function in magnifier.commands?


super().__init__(
frameType="lens",
title="NVDA Lens Magnifier",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

make sure strings like this are translated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
title="NVDA Lens Magnifier",
# Translators: ...
title=pgetttext("magnifier", "Lens Magnifier"),

@seanbudd seanbudd changed the title NVDA Magnifier integration Magnifier integration Nov 6, 2025
@@ -0,0 +1,347 @@
import array

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Could you please also add a changelog entry and user guide entry?

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 sure what you are referencing. what should that look like?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

NVDA has a changelog to list changes in each release of NVDA:

We also expect general user documentation in the user guide:

Please take your best pass at it, and we can fix up any issues with English, and make it more user friendly

@seanbudd

seanbudd commented Nov 6, 2025

Copy link
Copy Markdown
Member

@Boumtchack - I've updated the PR description (CoPilot assisted). Can you please review it and update as appropriate?

Comment thread source/globalCommands.py
category=SCRCAT_VISION,
gesture="kb:control+alt+m",
)
def script_cycleMagnifierMode(self, gesture):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can you also add a new settings panel to retain some of these settings. This is so people don't have to change magnifier modes every time they start. Please create a new settings panel for the magnifier, and add a setting for current magnifier mode.

Extra note: An unbound (e.g. no default gesture) script to open the setting panel is required for each new settings panel.

Comment thread source/globalCommands.py
category=SCRCAT_VISION,
gesture="kb:NVDA+shift+q",
)
def script_toggleFullscreenMode(self, gesture):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

same here in regards to a setting in a settings panel

Comment thread source/globalCommands.py
category=SCRCAT_VISION,
gesture="kb:control+alt+i",
)
def script_cycleColorFilter(self, gesture):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

same here in regards to a setting in a settings panel

Comment thread source/globalCommands.py
category=SCRCAT_VISION,
gesture="kb:windows+escape",
)
def script_StopMagnifier(self, gesture):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

lets remove this and just use a single script_toggleMagnifier rather than script_startMagnifier or script_StopMagnifier. That way a single gesture turns the magnifier on or off. This should also be a setting, so magnifier starts by default with NVDA starts if desired.

Comment thread source/globalCommands.py
category=SCRCAT_VISION,
gestures=["kb:windows+numLock+numpadMinus", "kb:windows+numpadMinus", "kb:windows+$"],
)
def script_zoomOut(self, gesture):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

can we get a zoom in script?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Also a setting for default zoom level

@CyrilleB79

Copy link
Copy Markdown
Contributor

Hi @CyrilleB79 - @Boumtchack is a new contributor and French speaker. Is it possible you can pass on your email so that he can message you to ask questions about starting NVDA development in his native language French?

Of course!
@Boumtchack feel free to write me in French at cyrille (dot) bougot2 (at) laposte (dot) net regarding NVDA development.
Though, I'll keep here the discussions that should involve NV Access or design choices so that everybody can follow.

@CyrilleB79

Copy link
Copy Markdown
Contributor

I have not done thorough tests nor a deep review. However here is already my first feedback, both for @Boumtchack but more for NV Access (@seanbudd).

  1. This is a big project and a big work. From what I know, there is no design, no corresponding GitHub issue. The result is impressive though. Have you privately discussed design choices with NV Access? More specifically, I think to the expected user experience and the integration in NVDA's codebase. Would it be possible to summarize the design and the expected user experience? Alternatively, we can discuss all this in this PR and the future changes in the User Guide, but I'd prefer that design choice modification do not cause you too much rework...

  2. In this PR, the magnifier is implemented as a separate standard module, and @seanbudd recommands a standard separate settings panel in his review comment. On the opposite, when the Magnifier project was discussed before, the idea was to make it a vision enhancement provider and thus to make its parameters land in the Vision settings panel; in NV Access previous attempt to implement the magnification API (see [WIP] Magnifier python sample #17416), the magnifier also seemed to be handled as a vision enhancement provider. Is the different choice done in this PR intentional (i.e. do not make a vision enhancemeent provider)? If yes why? Excluding magnifier from the Vision Enhancement Providers would make them almost useless.

  3. The gestures chosen to control NVDA Magnifier are mostly the same than the ones of Windows Magnifier. I guess this choice is intentional, except if you just copied and translated some pieces of code of Microsoft magnification API material, keeping the same shortcuts.
    There are pros and cons for choosing the same gestures as Windows Magnifier:

  • pros:
    • people may already know these gestures so this makes a smooth transition from Windows Magnifier to NVDA Magnifier
    • since these gestures are specific to Windows Magnifier and do not include NVDA key, there is a low probability that they are taken by other NVDA commands or add-ons (except for table navigation; see later); thus they do not use gestures that could be needed by NVDA or add-ons in the future.
  • cons:
    • NVDA Magnifier may not do the same thing as Windows Magnifier for the same shortcut, so this may confuse people. E.g.:
      • Windows Magnifier has 1 shortcut to start and another to stop, while @seanbudd had recommended one shortcut to toggle start/stop
      • control+alt+i toggles color inversion in Windows Magnifier while it cycles through color schemes in NVDA Magnifier
      • if you implement move magnified view command: Windows Magnifier use control+alt+arrows to move the magnified view while NVDA uses them for table navigation; you can reasonably not use them for NVDA Magnifier
    • if someone prefers using native Windows Magnifier (let's hope not!), they have to unassign all the gestures of NVDA Magnifier to be able to use again native Windows Magnifier shortcuts
    • Most of NVDA gestures are easily identified because they use NVDA key; reusing Windows Magnifier shortcuts makes it unclear if it's an NVDA feature or not.
    • we may end up with unconsistent shortcuts, the ones inherited from Windows Magnifier, without NVDA key, and the ones added later, e.g. disable tracking, that would probably include NVDA key.

I'd recommend to make a complete list of NVDA Magnifier commands, present and probable future ones, and to check its consistency.

  1. After my first tests, in full view mode, the commands that I miss the most is the possibility to move the magnified view up/down/left/right, i.e. same as control+alt+up/down/left/rightArrow with Windows Magnifier. I think that they are quite mandatory in the first NVDA release including NVDA Magnifier.

  2. There are many other commands and/or config parameters that would be nice to have implemented in the future:

  • enable / disable tracking: globally and for each type of tracking: mouse, system cursor, browse mode cursor, review cursor, system focus, navigator object, or relevant groups of cursors / object if it is more appropriate.
  • move the mouse in the center of the zoomed view
  • move the zoomed view to cursor (system or browse mode) / focus (if no cursor); useful when tracking is off
  • move the zoomed view to review cursor / navigator object (if no review cursor); useful when tracking is off
  • Switch between mouse pointer tracking modes (at the edges of the screen or centered on the screen); this is a Windows Magnifier feature
  • Switch between text tracking modes (at the edges of the screen or centered on the screen); this is a Windows Magnifier feature
  • and many others... Looking at Zoomtext, there are many interesting things
    But all these do not need to be implemented in the first dev chunk. I'd prefer have a strong agreement on the UX and the integration in NVDA's codebase before implementing so many new magnifier commands/features.

Note: FYI, I am the author of Windows Magnifier add-on (available in NVDA Add-on Store). It's not at all another implementation of a magnifier for NVDA. It's an NVDA add-on meant to work when native Windows Magnifier is being used and that provides two things:

  • it vocalizes nativ Windows Magnifier commands, e.g. zoom commands report the new zoom level
  • It provides commands (gestures) to modify Windows Magnifier options without the need to open Windows Magnifier settings panel, e.g. being able to disable the tracking without moving the focus is very handy.

@seanbudd

seanbudd commented Nov 7, 2025

Copy link
Copy Markdown
Member

Hi @CyrilleB79 - thanks for your thorough feedback

To answer some of your questions

(1) - We've been working closely with a few community members to get this up. We have a rough design plan - I think we have just failed to publish it publicly.
(2) - Ideally yes this would be a vision enhancement provider. However, setting it up that way has been a severe technical issue. I think in this case it's considerably easier to build this first, then refactor vision enhancement providers to make it more compatible with what's required. We don't have a clear idea of what kind of rework is required until this work is complete. Some examples we've run into: these settings should be global, vision enhancement providers are profile specific; API issues when drawing windows using VEP; poor documentation on how to implement a VEP.

if someone prefers using native Windows Magnifier (let's hope not!), they have to unassign all the gestures of NVDA Magnifier to be able to use again native Windows Magnifier shortcuts ... we may end up with unconsistent shortcuts, the ones inherited from Windows Magnifier, without NVDA key, and the ones added later, e.g. disable tracking, that would probably include NVDA key...
I'd recommend to make a complete list of NVDA Magnifier commands, present and probable future ones, and to check its consistency.

I wonder if we can pass through the keys if NVDA magnifier is disabled somewhere. Otherwise I think using windows magnifier keys as a rough base is useful for portability, and I don't think there's anything wrong with tweaking them slightly to optimise for our more typical patterns, e.g. toggles. I agree that comparing commands more formally is a good idea.

@seanbudd

seanbudd commented Nov 7, 2025

Copy link
Copy Markdown
Member

I'll look into creating a GitHub discussion which will include our design plans and roadmap

@CyrilleB79

Copy link
Copy Markdown
Contributor

Thanks @seanbudd for your answers. Below my reactions:

(2) - Ideally yes this would be a vision enhancement provider. However, setting it up that way has been a severe technical issue. I think in this case it's considerably easier to build this first, then refactor vision enhancement providers to make it more compatible with what's required.

If we get a working and user-friendly NVDA magnifier in 2026.1, I doubt someone will make so many efforts to convert it to a VEP later, unless there is a strong compelling reason identified to convert it in the future. The drawback is that NVDA's code will be less consistent. On the other side, being personally a magnifier user, I'd really push to have a first version of NVDA magnifier available as soon as possible, no matter if it's a VEP or not; I do not wish to wait two more years just because we have not been able to make NVDA magnifier a VEP.

these settings should be global, vision enhancement providers are profile specific;

Why? I can imagine use cases where profile specific magnifier configurations could be useful.
If it's due to a technical limitation or a difficulty to implement, it would be nice to know, e.g. in the issue/discussion about design choices that you plan to publish.

API issues when drawing windows using VEP;

Is there a documented GitHub issue for this? I have never encountered this, nor heard of this.
Even if the issue is not fixed, it would really be a plus to have such issue or limitation documented if any.

poor documentation on how to implement a VEP.

Wouldn't it be the opportunity to improve the documentation instead?
Note that there is already the VEP example in NVDA's code...

Well, we end up with:

  • screen curtain about to be excluded from VEPs (see Make Screen Curtain profile independent #19177), and I agree that it's a good choice: a screen curtain is not a visual aid at all, instead it prevent from seeing anything on the screen!
  • NVDA Magnifier implemented separately, not as a VEP, with an uncertainty to know if and when it will be converted to a VEP.
  • highlighters probably being the only VEP remaining in the Vision panel
  • no known add-on implementing another VEP after 6 years

I wonder if VEPs are useful or not. They are quite complex; I have tried to implement one once, without success, maybe by lack of documentation? If it does not interest NV Access nor add-on developers, is it worth keeping this complexity?

The fact that all VEPs end up in the Vision panel and that, by default, there is a strong link between the GUI and the VEP's configuration do not make them easy nor appealing to implement.

I wonder if we can pass through the keys if NVDA magnifier is disabled somewhere.

I imagine it's quite easy if you keep the same gesturs as Windows Magnifier: check if NVDA magnifier is running and if not, execute gesture.send().

Otherwise I think using windows magnifier keys as a rough base is useful for portability, and I don't think there's anything wrong with tweaking them slightly to optimise for our more typical patterns, e.g. toggles. I agree that comparing commands more formally is a good idea.

OK. I have no very strong opinion on this for the moment.
But making a compared list of present and future commands would help to define, or check that we have, a consistent UX.

@seanbudd seanbudd added the conceptApproved Similar 'triaged' for issues, PR accepted in theory, implementation needs review. label Nov 11, 2025
@seanbudd

Copy link
Copy Markdown
Member

@CyrilleB79 the idea of something like Vision Enhancement Providers is to modularize code.
This principle is sometimes known as DRY - Don't Repeat Yourself.
A similar principle ties into this called WET- Write Everything Twice.
Without applying WET you cannot DRY well.
In the case of vision enhancement providers, we only ever wrote highlighter and screen curtain. Screen curtain was decided to be a poor fit and torn out in #19177. As you mentioned, they are overly complex and not designed well. We need to fully write out a functional magnifier before we can properly design and interface like VEP that works across cases.
I would suggest once magnifier is complete, we consider what we can refactor between highlight and magnifier to create a more useful vision enhancement provider.
I agree that they need to have more adaptable settings panels.
In terms of public documentation on the limitations we ran into developing magnifier - I don't think this is a valuable use of time. VEP is not a good framework, and when creating something new, we can look more into why that is. But the kind of issues we ran are not as relevant. Whatever we are building is going to be based on creating something that actually works for more than 1 complex feature, using 2 working feature as a base.

@CyrilleB79

Copy link
Copy Markdown
Contributor

I would suggest once magnifier is complete, we consider what we can refactor between highlight and magnifier to create a more useful vision enhancement provider.

OK, no problem. Clearly you should do what seems more efficient to you.
My concern was just that, as soon as NVDA Magnifier is working and integrated, there is a risk to keep it forever as a separate piece of code rather than making it a VEP (same for Image description feature and recognizers).

In terms of public documentation on the limitations we ran into developing magnifier - I don't think this is a valuable use of time.

For now, the choice seems to be to make NVDA Magnifier settings global settings, i.e. not profile-dependent.
IMO, this is an unfortunate limitation. Hence my question regarding design documentation and the reason why choices have been made.

@Boumtchack

Boumtchack commented Nov 12, 2025

Copy link
Copy Markdown
Contributor Author

Hello everyone,

I've red all your comments and I'll start doing the changes, and at the same time, I was going to work on the step-by-step submission like you asked if it's still something you'll like me to do.
I think I can do that in this order:

  1. Fullscreen magnifier centered
  2. Fullscreen magnifier bordered
  3. Fullscreen magnifier relative
  4. spotlight
  5. color filters
  6. docked mode
  7. lens mode

You also talked a little bit about VEP and I'm not sure about what it means for me.


Regarding design I tried to copy what I had in windows magnifier, I think the main parts are for the non-fullscreen magnifiers, as they requier frames and panel so we can look at this when everything before is working fine maybe? I'll follow your lead on the design.

Also for color filters, I just did the basic ones, I think I remember when we talk with Julien that color inversion is not possible atm with how Windows work, but we can look into wich color filter can be added like for color blindness


Also I can change the commands gesture, but I'm not sure I'm the best to choose what these would be, so just tell me what is better for you, I think you know a lot more gestures than I do. if you can give me a list of the gesture you wants for:

  • toggle start/stop
  • zoom in
  • zoom out
  • spotlight
  • color filter
  • fullscreen mode toggle (center/border/relative/...)
  • magnifier mode toggle (fullscreen/docked/lens/...)
  • settings?
  • other?

Can you also add a new settings panel to retain some of these settings. This is so people don't have to change magnifier modes every time they start. Please create a new settings panel for the magnifier, and add a setting for current magnifier mode.

I'm not sure how to work on that if you could give me an exemple also.


There are many other commands and/or config parameters that would be nice to have implemented in the future

I think it would be nice to know wich one we would implement in the future so I can prepare the code accordingly even if it's not top priority.


After my first tests, in full view mode, the commands that I miss the most is the possibility to move the magnified view up/down/left/right, i.e. same as control+alt+up/down/left/rightArrow with Windows Magnifier. I think that they are quite mandatory in the first NVDA release including NVDA Magnifier.

I think I can already look into this as a main feature.

@CyrilleB79

Copy link
Copy Markdown
Contributor

Thanks for your answers. Below my first reactions:

You also talked a little bit about VEP and I'm not sure about what it means for me.

VEP stands here, unofficially, for vision enhancement provider (see visionEnhancementProviders folder in NVDA's source code).
But NV Access (Sean)'s position is to deliver NVDA Magnifier separately for now. So you do not need to bother with this.

Regarding design I tried to copy what I had in windows magnifier, I think the main parts are for the non-fullscreen magnifiers, as they requier frames and panel so we can look at this when everything before is working fine maybe?

Again, I'll let NV Access (Sean) provide official guidelines.
But as a user of Windows Magnifier, I only use full screen magnification. My opinion is that it's from far the main use case. Dockked is probably the second one, far behind. And Lens the last one. Thus, if you face any difficulty with the Docked or Lens mode development for these two modes, I'd recommend to deliver only full screen mode first.

Also for color filters, I just did the basic ones, I think I remember when we talk with Julien that color inversion is not possible atm with how Windows work, but we can look into wich color filter can be added like for color blindness

I have experimented too with color transformation (see this unfinished piece of code). I can confirm that Windows color transformation matrix does not allow to perform all possibl color transformation that one can find in other magnification software such as Zoomtext, e.g. invert brightness).

IMO, there is no problem starting with few color filters and adding more of them if / when needed. You may implement standard Windows color filter that probably fit the most common use cases.

@CyrilleB79

Copy link
Copy Markdown
Contributor

Would you be able to define the function logic for these commands in magnifier.commands, so that globalCommands.script_startMagnifier just calls a function in magnifier.commands?

I don't think I know how to do that but I can try to, maybe if you have an exemple of how it's done it would help.

I guess that Sean's point is:

  • Rename source/NVDAMagnifier to source/magnifier (Sean's previous comment)
  • Create a file source/magnifier/commands.py
  • In this file create a cycleMagnifierMode function, put the content of script_cycleMagnifierMode
  • In script_cycleMagnifierMode just call cycleMagnifierMode

Sean, please confirm.

Also I can change the commands gesture, but I'm not sure I'm the best to choose what these would be, so just tell me what is better for you, I think you know a lot more gestures than I do. if you can give me a list of the gesture you wants for:

OK. I'll try to think to something consistent. Else, let's stick with something similar as Windows Magnifier, following Sean's reasoning. In any case, this can be changed after the merge if needed.

Can you also add a new settings panel to retain some of these settings. This is so people don't have to change magnifier modes every time they start. Please create a new settings panel for the magnifier, and add a setting for current magnifier mode.

I'm not sure how to work on that if you could give me an exemple also.

The settings panels are in the dialog tha can be opened through NVDA menu -> Preferences -> Settings... You have one panel per category and all these panels are implemented in gui/settingsDialogs.py

There are many other commands and/or config parameters that would be nice to have implemented in the future

I think it would be nice to know wich one we would implement in the future so I can prepare the code accordingly even if it's not top priority.

I can imagine so many features that would be useful in a magnification software. A software such as Zoomtext for example has much more options and commands than Windows Magnifier and is highly customizable.

Below are some examples

Commands

Commands to move the magnified view

  • Commands Pane left/right/up/down: move magnified view (the most important one as written before) [nativeWinMag: control+alt+arrows]

  • Commands Move view to top/bottom/Left/Right edge: move the magnified view at the top/bottom/left/right edge of the screen [Zoomtext]; maybe also jump to top left, top right, bottom left, bottom right corners of the screen

  • Command Move view to center: position the magnifier view at the center of the screen. [Zoomtext]

  • Command Move the zoomed view to text cursor (system or browse mode) / focus (if no cursor); useful when tracking is off [Zoomtext]

  • Command Move the zoomed view to review cursor / navigator object (if no review cursor); useful when tracking is off

Comands to move to view

  • Command Move the mouse in the center of the zoomed view [winMag add-on: NVDA+alt+O, V]

Commands to control settings

These commands are shortcuts that can control one or more setting without having to open the settings dialog. NVDA is full of such commands. These commands do not need to have an assigned gesture. Only users who need it will assign them.

  • Commands to enable / disable tracking:
    • Enable/Disable mouse tracking [winMag add-on: NVDA+alt+O, M]
    • Enable / disable text cursor tracking (includes system cursor and browse mode cursor) [winMag add-on: NVDA+alt+O, C]
    • Enable / disable review cursor tracking
    • Enable / disable focus tracking [winMag add-on: NVDA+alt+O, F]
    • Enable / disable navigator object tracking
    • Enable / disable tracking globally (one tracking shortcut to rule them all...) [winMag add-on: NVDA+alt+O, T]
  • Command Switch between mouse pointer tracking modes (at the edges of the screen or centered on the screen); this is a Windows Magnifier feature [winMag add-on: NVDA+alt+O, R]
  • Command Switch between text tracking modes (at the edges of the screen or centered on the screen); this is a Windows Magnifier feature [winMag add-on: NVDA+alt+O, X]

Notes:

I have put between brackets:

  • native Windows Magnifier commands, when it exists
  • winMag add-on command: command defined in NVDA's Windows Magnifier add-on that I develop (also available in NVDA Add-on Store)
  • Zoomtext: if I recall tha the command exists in Zoomtext, even if I have not used this magnification software for long.

Parameter (setting)

  • The magnified view follows mouse cursor
  • The magnified view follow text cursor
  • The magnified view follow focus
  • The magnified view follow navigator object
  • Zoom increment e.g. 0.5, 1, or even progressive, e.g. (1, 1.25, 1.5, 1.75, 2, 2.5, 3, 3.5, 4, 5, 6, 7, 8, 10, 12, etc.)
  • Keep mouse cursor within the edges of the magnified view / centered on the magnified view
  • Keep text cursor within the edges of the magnified view / centered on the magnified view

@Boumtchack

Copy link
Copy Markdown
Contributor Author

I'll be looking at that next monday,
thanks for the help!

@seanbudd

Copy link
Copy Markdown
Member

Thanks @CyrilleB79 for the assistance - I think you covered most of how I would answer @Boumtchack's questions.
In terms of what to focus on for now, please keep this PR minimal with the same scope it currently has.
Lets try to polish what we have as much as possible.
All that's stopping this from merging is documentation, polish and a settings panel.
Let's focus on that before building out the features.

@Boumtchack

Copy link
Copy Markdown
Contributor Author

Hello guys,

So I've started a new branch with only the fullscreen magnifier, no border and no relative mode, no color filter.
So it's based on this current branch but will be better to had step by step feature.

I've taken into account some of the suggestions, but I'm still don't understand on how the setting pannel should be done.

I'm just gonna make sure everything is working fine this morning and I'm pushing it asap. So maybe we can make the settings together?

@Boumtchack

Copy link
Copy Markdown
Contributor Author

#19228

@seanbudd

Copy link
Copy Markdown
Member

Hi @Boumtchack - should we close this PR or do you intend to work on it separately? I've DMd you on slack in regards to getting settings panels together, unless you've contact Cyrille off-GitHub already?

@CyrilleB79

Copy link
Copy Markdown
Contributor

I have written to @Boumtchack by e-mail. To summarize:

  1. For a settings panel implementation, I have suggested to look at settingsDialogs.py.
  2. I am available to provide more information regarding settings panel implementation (or other) through a live call if needed
  3. Regarding the opening of Magnifier implementation #19228: I am not sure it was / is necessary. It seems to me that Sean's point (Magnifier integration  #19172 (comment)) was not to remove features that have already been developed here, the more if they are working (e.g. color filters), but to avoid adding new features or options. (@seanbudd can you confirm this?)

@Boumtchack

Copy link
Copy Markdown
Contributor Author

Regarding my new PR, it was talked about that we do step by step implementation of the magnifier to work on little bit of what I had done so far.

I'll let @seanbudd decide whether or not I'm keeping this pr or the new one. This one was primarelly to show what work I've already have.

@CyrilleB79

Copy link
Copy Markdown
Contributor

OK. No problem. Do as you wish.

Feel free to contact me if/when you need and I'll let you know my availability.

@seanbudd

Copy link
Copy Markdown
Member

@CyrilleB79 - yes, my initial comment was encouraging work to continue at the scope of this PR.
Splitting up the PR into something smaller is fine though, and will make reviewing easier.

@seanbudd

seanbudd commented Dec 4, 2025

Copy link
Copy Markdown
Member

Closing in favour of #19228

@seanbudd seanbudd closed this Dec 4, 2025
@Boumtchack Boumtchack deleted the reorganisationOfMagnifierLogics branch January 28, 2026 09:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

conceptApproved Similar 'triaged' for issues, PR accepted in theory, implementation needs review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow Windows Magnifier to follow NVDA virtual cursor

4 participants