From 62d0a625a74f8827018deb538f0fa5a1b121e9c9 Mon Sep 17 00:00:00 2001 From: Cyrille Bougot Date: Mon, 1 Jun 2026 16:55:07 +0200 Subject: [PATCH 01/26] Create Magnifier category in Input gestures dialog --- source/globalCommands.py | 41 +++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/source/globalCommands.py b/source/globalCommands.py index 57490c715cb..8cecb79bc11 100755 --- a/source/globalCommands.py +++ b/source/globalCommands.py @@ -102,6 +102,9 @@ #: Script category for Vision commands. # Translators: The name of a category of NVDA commands. SCRCAT_VISION = _("Vision") +#: Script category for Magnifier commands. +# Translators: The name of a category of NVDA commands. +SCRCAT_MAGNIFIER = _("Magnifier") #: Script category for tools commands. # Translators: The name of a category of NVDA commands. SCRCAT_TOOLS = pgettext("script category", "Tools") @@ -5042,7 +5045,7 @@ def script_reportScreenCurtainState(self, gesture: inputCore.InputGesture) -> No # Translators: Describes a command. "Toggles the magnifier on and off", ), - category=SCRCAT_VISION, + category=SCRCAT_MAGNIFIER, gesture="kb:NVDA+shift+w", ) def script_toggleMagnifier( @@ -5056,7 +5059,7 @@ def script_toggleMagnifier( # Translators: Describes a command. "Increases the magnification level of the magnifier", ), - category=SCRCAT_VISION, + category=SCRCAT_MAGNIFIER, gesture="kb:NVDA+shift+=", ) def script_zoomIn( @@ -5070,7 +5073,7 @@ def script_zoomIn( # Translators: Describes a command. "Decreases the magnification level of the magnifier", ), - category=SCRCAT_VISION, + category=SCRCAT_MAGNIFIER, gesture="kb:NVDA+shift+-", ) def script_zoomOut( @@ -5084,7 +5087,7 @@ def script_zoomOut( # Translators: Describes a command. "Pan the magnifier view to the left", ), - category=SCRCAT_VISION, + category=SCRCAT_MAGNIFIER, gesture="kb:nvda+alt+leftArrow", ) def script_panLeft( @@ -5098,7 +5101,7 @@ def script_panLeft( # Translators: Describes a command. "Pan the magnifier view to the right", ), - category=SCRCAT_VISION, + category=SCRCAT_MAGNIFIER, gesture="kb:nvda+alt+rightArrow", ) def script_panRight( @@ -5112,7 +5115,7 @@ def script_panRight( # Translators: Describes a command. "Pan the magnifier view up", ), - category=SCRCAT_VISION, + category=SCRCAT_MAGNIFIER, gesture="kb:nvda+alt+upArrow", ) def script_panUp( @@ -5126,7 +5129,7 @@ def script_panUp( # Translators: Describes a command. "Pan the magnifier view down", ), - category=SCRCAT_VISION, + category=SCRCAT_MAGNIFIER, gesture="kb:nvda+alt+downArrow", ) def script_panDown( @@ -5140,7 +5143,7 @@ def script_panDown( # Translators: Describes a command. "Pan the magnifier view to left edge", ), - category=SCRCAT_VISION, + category=SCRCAT_MAGNIFIER, gesture="kb:nvda+shift+alt+leftArrow", ) def script_panToLeftEdge( @@ -5154,7 +5157,7 @@ def script_panToLeftEdge( # Translators: Describes a command. "Pan the magnifier view to right edge", ), - category=SCRCAT_VISION, + category=SCRCAT_MAGNIFIER, gesture="kb:nvda+shift+alt+rightArrow", ) def script_panToRightEdge( @@ -5168,7 +5171,7 @@ def script_panToRightEdge( # Translators: Describes a command. "Pan the magnifier view to top edge", ), - category=SCRCAT_VISION, + category=SCRCAT_MAGNIFIER, gesture="kb:nvda+shift+alt+upArrow", ) def script_panToTopEdge( @@ -5182,7 +5185,7 @@ def script_panToTopEdge( # Translators: Describes a command. "Pan the magnifier view to bottom edge", ), - category=SCRCAT_VISION, + category=SCRCAT_MAGNIFIER, gesture="kb:nvda+shift+alt+downArrow", ) def script_panToBottomEdge( @@ -5196,7 +5199,7 @@ def script_panToBottomEdge( # Translators: Describes a command. "Toggle filter of the magnifier", ), - category=SCRCAT_VISION, + category=SCRCAT_MAGNIFIER, gesture="kb:NVDA+shift+i", ) def script_toggleFilter( @@ -5210,7 +5213,7 @@ def script_toggleFilter( # Translators: Describes a command. "Toggle follow mouse for the magnifier", ), - category=SCRCAT_VISION, + category=SCRCAT_MAGNIFIER, ) def script_toggleFollowMouse( self, @@ -5223,7 +5226,7 @@ def script_toggleFollowMouse( # Translators: Describes a command. "Toggle follow system focus for the magnifier", ), - category=SCRCAT_VISION, + category=SCRCAT_MAGNIFIER, ) def script_toggleFollowSystemFocus( self, @@ -5236,7 +5239,7 @@ def script_toggleFollowSystemFocus( # Translators: Describes a command. "Toggle follow review cursor for the magnifier", ), - category=SCRCAT_VISION, + category=SCRCAT_MAGNIFIER, ) def script_toggleFollowReview( self, @@ -5249,7 +5252,7 @@ def script_toggleFollowReview( # Translators: Describes a command. "Toggle follow navigator object for the magnifier", ), - category=SCRCAT_VISION, + category=SCRCAT_MAGNIFIER, ) def script_toggleFollowNavigatorObject( self, @@ -5262,7 +5265,7 @@ def script_toggleFollowNavigatorObject( # Translators: Describes a command. "Toggle all follow modes for the magnifier", ), - category=SCRCAT_VISION, + category=SCRCAT_MAGNIFIER, ) def script_toggleAllFollow( self, @@ -5275,7 +5278,7 @@ def script_toggleAllFollow( # Translators: Describes a command. "Toggle focus mode for the full-screen magnifier", ), - category=SCRCAT_VISION, + category=SCRCAT_MAGNIFIER, ) def script_toggleFullscreenMode( self, @@ -5288,7 +5291,7 @@ def script_toggleFullscreenMode( # Translators: Describe a command. "Launch spotlight if magnifier is full-screen", ), - category=SCRCAT_VISION, + category=SCRCAT_MAGNIFIER, gesture="kb:NVDA+shift+l", ) def script_startSpotlight( From 9c22ae706fa299d876d2e3f81f7856463b007f9f Mon Sep 17 00:00:00 2001 From: Cyrille Bougot Date: Mon, 1 Jun 2026 17:14:12 +0200 Subject: [PATCH 02/26] Commands description rewording: * Remove "of the magnifier" in all commands * "magnifier view" -> "magnified view" * filter -> color filter and toggle -> cycle * "follow X" -> "X tracking" * remove "for the full screen magnifier" or "if magnifier is full-screen" since we will have error messages in case no full-screen mode is not active. * spotlight command rewording --- source/globalCommands.py | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/source/globalCommands.py b/source/globalCommands.py index 8cecb79bc11..cfb636de96a 100755 --- a/source/globalCommands.py +++ b/source/globalCommands.py @@ -5057,7 +5057,7 @@ def script_toggleMagnifier( @script( description=_( # Translators: Describes a command. - "Increases the magnification level of the magnifier", + "Increases the magnification level", ), category=SCRCAT_MAGNIFIER, gesture="kb:NVDA+shift+=", @@ -5071,7 +5071,7 @@ def script_zoomIn( @script( description=_( # Translators: Describes a command. - "Decreases the magnification level of the magnifier", + "Decreases the magnification level", ), category=SCRCAT_MAGNIFIER, gesture="kb:NVDA+shift+-", @@ -5085,7 +5085,7 @@ def script_zoomOut( @script( description=_( # Translators: Describes a command. - "Pan the magnifier view to the left", + "Pan the magnified view to the left", ), category=SCRCAT_MAGNIFIER, gesture="kb:nvda+alt+leftArrow", @@ -5099,7 +5099,7 @@ def script_panLeft( @script( description=_( # Translators: Describes a command. - "Pan the magnifier view to the right", + "Pan the magnified view to the right", ), category=SCRCAT_MAGNIFIER, gesture="kb:nvda+alt+rightArrow", @@ -5113,7 +5113,7 @@ def script_panRight( @script( description=_( # Translators: Describes a command. - "Pan the magnifier view up", + "Pan the magnified view up", ), category=SCRCAT_MAGNIFIER, gesture="kb:nvda+alt+upArrow", @@ -5127,7 +5127,7 @@ def script_panUp( @script( description=_( # Translators: Describes a command. - "Pan the magnifier view down", + "Pan the magnified view down", ), category=SCRCAT_MAGNIFIER, gesture="kb:nvda+alt+downArrow", @@ -5141,7 +5141,7 @@ def script_panDown( @script( description=_( # Translators: Describes a command. - "Pan the magnifier view to left edge", + "Pan the magnified view to left edge", ), category=SCRCAT_MAGNIFIER, gesture="kb:nvda+shift+alt+leftArrow", @@ -5155,7 +5155,7 @@ def script_panToLeftEdge( @script( description=_( # Translators: Describes a command. - "Pan the magnifier view to right edge", + "Pan the magnified view to right edge", ), category=SCRCAT_MAGNIFIER, gesture="kb:nvda+shift+alt+rightArrow", @@ -5169,7 +5169,7 @@ def script_panToRightEdge( @script( description=_( # Translators: Describes a command. - "Pan the magnifier view to top edge", + "Pan the magnified view to top edge", ), category=SCRCAT_MAGNIFIER, gesture="kb:nvda+shift+alt+upArrow", @@ -5183,7 +5183,7 @@ def script_panToTopEdge( @script( description=_( # Translators: Describes a command. - "Pan the magnifier view to bottom edge", + "Pan the magnified view to bottom edge", ), category=SCRCAT_MAGNIFIER, gesture="kb:nvda+shift+alt+downArrow", @@ -5197,7 +5197,7 @@ def script_panToBottomEdge( @script( description=_( # Translators: Describes a command. - "Toggle filter of the magnifier", + "Cycle through the color filters", ), category=SCRCAT_MAGNIFIER, gesture="kb:NVDA+shift+i", @@ -5211,7 +5211,7 @@ def script_toggleFilter( @script( description=_( # Translators: Describes a command. - "Toggle follow mouse for the magnifier", + "Toggle mouse tracking", ), category=SCRCAT_MAGNIFIER, ) @@ -5224,7 +5224,7 @@ def script_toggleFollowMouse( @script( description=_( # Translators: Describes a command. - "Toggle follow system focus for the magnifier", + "Toggle system focus tracking", ), category=SCRCAT_MAGNIFIER, ) @@ -5237,7 +5237,7 @@ def script_toggleFollowSystemFocus( @script( description=_( # Translators: Describes a command. - "Toggle follow review cursor for the magnifier", + "Toggle review cursor tracking", ), category=SCRCAT_MAGNIFIER, ) @@ -5250,7 +5250,7 @@ def script_toggleFollowReview( @script( description=_( # Translators: Describes a command. - "Toggle follow navigator object for the magnifier", + "Toggle navigator object for the magnifier", ), category=SCRCAT_MAGNIFIER, ) @@ -5263,7 +5263,7 @@ def script_toggleFollowNavigatorObject( @script( description=_( # Translators: Describes a command. - "Toggle all follow modes for the magnifier", + "Toggle all tracking options", ), category=SCRCAT_MAGNIFIER, ) @@ -5276,7 +5276,7 @@ def script_toggleAllFollow( @script( description=_( # Translators: Describes a command. - "Toggle focus mode for the full-screen magnifier", + "Toggle focus mode", ), category=SCRCAT_MAGNIFIER, ) @@ -5289,11 +5289,11 @@ def script_toggleFullscreenMode( @script( description=_( # Translators: Describe a command. - "Launch spotlight if magnifier is full-screen", + "Temporary show the entire screen with spotlight around the mouse cursor", ), category=SCRCAT_MAGNIFIER, gesture="kb:NVDA+shift+l", - ) + )--------------- def script_startSpotlight( self, gesture: inputCore.InputGesture, From cdd73ce92ab3d07c16f27fd75bf867d300fdf129 Mon Sep 17 00:00:00 2001 From: Cyrille Bougot Date: Tue, 2 Jun 2026 17:06:59 +0200 Subject: [PATCH 03/26] Commands feedback improvement (WIP) --- source/_magnifier/commands.py | 45 +++++++++-------------------------- source/globalCommands.py | 2 +- 2 files changed, 12 insertions(+), 35 deletions(-) diff --git a/source/_magnifier/commands.py b/source/_magnifier/commands.py index 31e11df3d1b..5e48a80ae3e 100644 --- a/source/_magnifier/commands.py +++ b/source/_magnifier/commands.py @@ -12,13 +12,9 @@ import ui from . import changeMagnifiedView, getMagnifier, start, stop from .config import ( - getMagnifiedView, setMagnifiedView, - getZoomLevelString, - getFilter, getFollowState, setFilter, - getFullscreenMode, setFollowState, setFullscreenMode, toggleAllFollowStates, @@ -96,7 +92,7 @@ def toggleMagnifier() -> None: pgettext( "magnifier", # Translators: Message announced when stopping the NVDA magnifier. - "Exiting magnifier", + "Magnifier disabled", ), ) # Check if Screen Curtain is active @@ -110,32 +106,13 @@ def toggleMagnifier() -> None: ) else: start() - currentFilter = getFilter() - magnifiedView = getMagnifiedView() - zoomLevel = getZoomLevelString() - if magnifiedView == MagnifiedView.FULLSCREEN: - fullscreenMode = getFullscreenMode() - msg = pgettext( - "magnifier", - # Translators: Message announced when starting the NVDA magnifier. - "Starting {magnifiedView} magnifier with {zoomLevel} zoom level, {filter} filter, and {fullscreenMode} full-screen mode", - ).format( - magnifiedView=magnifiedView.displayString, - zoomLevel=zoomLevel, - filter=currentFilter.displayString, - fullscreenMode=fullscreenMode.displayString, - ) - else: - msg = pgettext( + ui.message( + pgettext( "magnifier", # Translators: Message announced when starting the NVDA magnifier. - "Starting {magnifiedView} magnifier with {zoomLevel} zoom level and {filter} filter", - ).format( - magnifiedView=magnifiedView.displayString, - zoomLevel=zoomLevel, - filter=currentFilter.displayString, - ) - ui.message(msg) + "Magnifier enabled", + ), + ) def zoom(direction: Direction) -> None: @@ -193,7 +170,7 @@ def toggleFilter() -> None: pgettext( "magnifier", # Translators: Message announced when changing the color filter with {filter} being the new color filter. - "Color filter changed to {filter}", + "Color filter {filter}", ).format(filter=magnifier.filterType.displayString), ) @@ -217,7 +194,7 @@ def cycleMagnifiedView() -> None: pgettext( "magnifier", # Translators: Message announced when changing the magnifier view with {view} being the new magnifier view. - "Magnifier view changed to {view}", + "{view} view", ).format(view=magnifier._MAGNIFIED_VIEW.displayString), ) @@ -270,13 +247,13 @@ def toggleAllFollow() -> None: stateMessage = pgettext( "magnifier", # Translators: State of all follow settings being toggled disabled. - "All follow settings disabled", + "All tracking settings disabled", ) else: stateMessage = pgettext( "magnifier", # Translators: State of all follow settings being restored. - "All follow settings restored", + "Tracking settings restored", ) ui.message(stateMessage) @@ -304,7 +281,7 @@ def toggleFullscreenMode() -> None: pgettext( "magnifier", # Translators: Message announced when changing the full-screen mode with {mode} being the new full-screen mode. - "Full-screen mode changed to {mode}", + "Full-screen mode {mode}", ).format(mode=newMode.displayString), ) diff --git a/source/globalCommands.py b/source/globalCommands.py index cfb636de96a..fbfcc28b13c 100755 --- a/source/globalCommands.py +++ b/source/globalCommands.py @@ -5263,7 +5263,7 @@ def script_toggleFollowNavigatorObject( @script( description=_( # Translators: Describes a command. - "Toggle all tracking options", + "Toggle all tracking settings", ), category=SCRCAT_MAGNIFIER, ) From 541c360b09da3a4dee71c1cf2317e04148135209 Mon Sep 17 00:00:00 2001 From: Cyrille Bougot Date: Thu, 4 Jun 2026 11:11:46 +0200 Subject: [PATCH 04/26] Apply suggestions from code review Co-authored-by: Quentin Christensen --- source/globalCommands.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/source/globalCommands.py b/source/globalCommands.py index fbfcc28b13c..ca5a18a4cda 100755 --- a/source/globalCommands.py +++ b/source/globalCommands.py @@ -5211,7 +5211,7 @@ def script_toggleFilter( @script( description=_( # Translators: Describes a command. - "Toggle mouse tracking", + "Toggle magnifier mouse tracking", ), category=SCRCAT_MAGNIFIER, ) @@ -5224,7 +5224,7 @@ def script_toggleFollowMouse( @script( description=_( # Translators: Describes a command. - "Toggle system focus tracking", + "Toggle magnifier system focus tracking", ), category=SCRCAT_MAGNIFIER, ) @@ -5237,7 +5237,7 @@ def script_toggleFollowSystemFocus( @script( description=_( # Translators: Describes a command. - "Toggle review cursor tracking", + "Toggle magnifier review cursor tracking", ), category=SCRCAT_MAGNIFIER, ) @@ -5250,7 +5250,7 @@ def script_toggleFollowReview( @script( description=_( # Translators: Describes a command. - "Toggle navigator object for the magnifier", + "Toggle magnifier navigator object tracking", ), category=SCRCAT_MAGNIFIER, ) @@ -5263,7 +5263,7 @@ def script_toggleFollowNavigatorObject( @script( description=_( # Translators: Describes a command. - "Toggle all tracking settings", + "Toggle all magnifier tracking settings", ), category=SCRCAT_MAGNIFIER, ) @@ -5276,7 +5276,7 @@ def script_toggleAllFollow( @script( description=_( # Translators: Describes a command. - "Toggle focus mode", + "Toggle magnifier focus mode", ), category=SCRCAT_MAGNIFIER, ) From 2523701a79c266c2fc3ee33edfbd84b1a3b633bb Mon Sep 17 00:00:00 2001 From: Cyrille Bougot Date: Thu, 4 Jun 2026 17:55:24 +0200 Subject: [PATCH 05/26] Rename spotlight --- source/_magnifier/commands.py | 8 ++++---- source/_magnifier/utils/spotlightManager.py | 6 +++--- source/_magnifier/utils/types.py | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/source/_magnifier/commands.py b/source/_magnifier/commands.py index 5e48a80ae3e..08c5d1111ff 100644 --- a/source/_magnifier/commands.py +++ b/source/_magnifier/commands.py @@ -304,8 +304,8 @@ def startSpotlight() -> None: ui.message( pgettext( "magnifier", - # Translators: Message announced when trying to start spotlight mode while it's already active. - "Spotlight mode is already active", + # Translators: Message announced when trying to show temporary overview of the screen while it's already active. + "The screen overview is already active", ), ) else: @@ -314,8 +314,8 @@ def startSpotlight() -> None: ui.message( pgettext( "magnifier", - # Translators: Message announced when spotlight mode is started. - "Spotlight mode started", + # Translators: Message announced when overview of the entire screen is being showed. + "Showing entire screen", ), ) diff --git a/source/_magnifier/utils/spotlightManager.py b/source/_magnifier/utils/spotlightManager.py index 6d62467a4dd..ff641a979c8 100644 --- a/source/_magnifier/utils/spotlightManager.py +++ b/source/_magnifier/utils/spotlightManager.py @@ -1,5 +1,5 @@ # A part of NonVisual Desktop Access (NVDA) -# Copyright (C) 2025-2026 NV Access Limited, Antoine Haffreingue +# Copyright (C) 2025-2026 NV Access Limited, Antoine Haffreingue, Cyrille Bougot # This file may be used under the terms of the GNU General Public License, version 2 or later, as modified by the NVDA license. # For full terms and any additional permissions, see the NVDA license file: https://github.com/nvaccess/nvda/blob/master/copying.txt @@ -65,8 +65,8 @@ def _stopSpotlight(self) -> None: ui.message( pgettext( "magnifier", - # Translators: Message announced when stopping the magnifier spotlight. - "Magnifier spotlight stopped", + # Translators: Message announced when turning back to normal view after entire screen overview. + "Back to normal view", ), ) if self._timer: diff --git a/source/_magnifier/utils/types.py b/source/_magnifier/utils/types.py index c1a47364603..5bc5443bcd8 100644 --- a/source/_magnifier/utils/types.py +++ b/source/_magnifier/utils/types.py @@ -85,7 +85,7 @@ def _displayStringLabels(self) -> dict["MagnifierAction", str]: # Translators: Action description for changing full-screen mode. self.CHANGE_FULLSCREEN_MODE: pgettext("magnifier action", "change full-screen mode"), # Translators: Action description for starting spotlight mode. - self.START_SPOTLIGHT: pgettext("magnifier action", "start spotlight mode"), + self.START_SPOTLIGHT: pgettext("magnifier action", "show screen overview"), } From 7786343d72efd4d66429d6e1a7c805d33d88531b Mon Sep 17 00:00:00 2001 From: Cyrille Bougot Date: Thu, 4 Jun 2026 18:01:09 +0200 Subject: [PATCH 06/26] Pan command consistency --- source/globalCommands.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/globalCommands.py b/source/globalCommands.py index ca5a18a4cda..aca837a338e 100755 --- a/source/globalCommands.py +++ b/source/globalCommands.py @@ -5085,7 +5085,7 @@ def script_zoomOut( @script( description=_( # Translators: Describes a command. - "Pan the magnified view to the left", + "Pan the magnified view left", ), category=SCRCAT_MAGNIFIER, gesture="kb:nvda+alt+leftArrow", @@ -5099,7 +5099,7 @@ def script_panLeft( @script( description=_( # Translators: Describes a command. - "Pan the magnified view to the right", + "Pan the magnified view right", ), category=SCRCAT_MAGNIFIER, gesture="kb:nvda+alt+rightArrow", @@ -5289,11 +5289,11 @@ def script_toggleFullscreenMode( @script( description=_( # Translators: Describe a command. - "Temporary show the entire screen with spotlight around the mouse cursor", + "Temporarily show an overview of the entire screen", ), category=SCRCAT_MAGNIFIER, gesture="kb:NVDA+shift+l", - )--------------- + ) def script_startSpotlight( self, gesture: inputCore.InputGesture, From ed2e2e606bbf362fef80d2e08c9cf4e649d747e8 Mon Sep 17 00:00:00 2001 From: Cyrille Bougot Date: Thu, 4 Jun 2026 18:05:35 +0200 Subject: [PATCH 07/26] Update command types --- source/_magnifier/utils/types.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/_magnifier/utils/types.py b/source/_magnifier/utils/types.py index 5bc5443bcd8..aa6c2276782 100644 --- a/source/_magnifier/utils/types.py +++ b/source/_magnifier/utils/types.py @@ -77,14 +77,14 @@ def _displayStringLabels(self) -> dict["MagnifierAction", str]: # Translators: Action description for panning to bottom edge. self.PAN_BOTTOM_EDGE: pgettext("magnifier action", "pan to bottom edge"), # Translators: Action description for toggling settings. - self.TOGGLE_FOLLOW_SETTINGS: pgettext("magnifier action", "toggle follow settings"), + self.TOGGLE_FOLLOW_SETTINGS: pgettext("magnifier action", "toggle tracking settings"), # Translators: Action description for toggling color filters. - self.TOGGLE_FILTER: pgettext("magnifier action", "toggle filters"), + self.TOGGLE_FILTER: pgettext("magnifier action", "cycle color filters"), # Translators: Action description for changing magnifier view. self.CHANGE_MAGNIFIER_VIEW: pgettext("magnifier action", "change magnifier view"), # Translators: Action description for changing full-screen mode. self.CHANGE_FULLSCREEN_MODE: pgettext("magnifier action", "change full-screen mode"), - # Translators: Action description for starting spotlight mode. + # Translators: Action description for showing entire screen overview. self.START_SPOTLIGHT: pgettext("magnifier action", "show screen overview"), } From cf9f16e42ffb313a3570d3f42d42562804720a9c Mon Sep 17 00:00:00 2001 From: Cyrille Bougot Date: Fri, 5 Jun 2026 10:25:11 +0200 Subject: [PATCH 08/26] Move Magnifier panel after vision --- source/gui/settingsDialogs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/gui/settingsDialogs.py b/source/gui/settingsDialogs.py index 7bf379aa070..9775ed4894c 100644 --- a/source/gui/settingsDialogs.py +++ b/source/gui/settingsDialogs.py @@ -6528,6 +6528,7 @@ class NVDASettingsDialog(MultiCategorySettingsDialog): AudioPanel, PrivacyAndSecuritySettingsPanel, VisionSettingsPanel, + MagnifierPanel, KeyboardSettingsPanel, MouseSettingsPanel, ReviewCursorPanel, @@ -6536,7 +6537,6 @@ class NVDASettingsDialog(MultiCategorySettingsDialog): BrowseModePanel, DocumentFormattingPanel, DocumentNavigationPanel, - MagnifierPanel, RemoteSettingsPanel, ] # #19634: the desktop heap on the secure desktop is quite restrictive. From 810f371140af3602fb0f9ffff3a82229b3963ac9 Mon Sep 17 00:00:00 2001 From: Cyrille Bougot Date: Fri, 5 Jun 2026 10:31:22 +0200 Subject: [PATCH 09/26] UGUI rewording --- source/gui/settingsDialogs.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/gui/settingsDialogs.py b/source/gui/settingsDialogs.py index 9775ed4894c..97f19df3c95 100644 --- a/source/gui/settingsDialogs.py +++ b/source/gui/settingsDialogs.py @@ -6139,7 +6139,7 @@ def makeSettings( # FILTER SETTINGS # Translators: The label for a setting in magnifier settings to select the default filter - filterLabelText = _("F&ilter:") + filterLabelText = _("Color f&ilter:") filterChoices = [f.displayString for f in Filter] self.filterList = sHelper.addLabeledControl( filterLabelText, @@ -6170,7 +6170,7 @@ def makeSettings( # TRUE CENTER # Translators: The label for a setting in magnifier settings to select whether true center is used in full-screen mode - trueCenterText = _("Use &true center in fullscreen mode") + trueCenterText = _("Use &true center tracking") self.trueCenterCheckBox = sHelper.addItem(wx.CheckBox(self, label=trueCenterText)) self.bindHelpEvent( "MagnifierUseTrueCenter", @@ -6188,7 +6188,7 @@ def makeSettings( # FOCUS GROUP # Translators: This is the label for a group of focus options in the magnifier settings panel - focusGroupText = _("Focus") + focusGroupText = _("Tracking") focusGroupSizer = wx.StaticBoxSizer(wx.VERTICAL, self, label=focusGroupText) focusGroupBox = focusGroupSizer.GetStaticBox() focusGroup = guiHelper.BoxSizerHelper(self, sizer=focusGroupSizer) From f16d2c75bf6fceab3053c5a53a34ea6802fca276 Mon Sep 17 00:00:00 2001 From: Cyrille Bougot Date: Fri, 5 Jun 2026 16:58:25 +0200 Subject: [PATCH 10/26] User Guide + Change log - First update --- user_docs/en/changes.md | 2 +- user_docs/en/userGuide.md | 93 ++++++++++++++------------------------- 2 files changed, 35 insertions(+), 60 deletions(-) diff --git a/user_docs/en/changes.md b/user_docs/en/changes.md index fb5d793dab4..1899e370528 100644 --- a/user_docs/en/changes.md +++ b/user_docs/en/changes.md @@ -29,7 +29,7 @@ When resetting NVDA to factory defaults, an Undo button is now available to rest * NVDA now includes a built-in Magnifier feature that allows you to zoom and magnify parts of the screen. (#19228, @Boumtchack) * The magnifier supports various zoom levels, color filters (normal, grayscale, inverted), and different focus tracking modes. * Color filters can help users with visual impairments or light sensitivity by inverting or desaturating screen colors. - * A spotlight mode is available for presentations or focused reading tasks. + * A command allows to temporarily show an overview of the entire screen * All magnifier settings can be configured in a new "Magnifier" panel in NVDA Settings. * The magnifier cannot be used simultaneously with Screen Curtain for security reasons. * Speech: diff --git a/user_docs/en/userGuide.md b/user_docs/en/userGuide.md index 6d1299395cb..605a8095c00 100644 --- a/user_docs/en/userGuide.md +++ b/user_docs/en/userGuide.md @@ -35,6 +35,7 @@ Major highlights include: * Support for common accessibility interfaces such as Microsoft Active Accessibility, Java Access Bridge, IAccessible2 and UI Automation * Support for Windows Command Prompt and console applications * The ability to highlight the system focus +* Built-in screen magnification and color filtering ### System Requirements {#SystemRequirements} @@ -1571,12 +1572,11 @@ It provides several configuration options to customize the magnification experie To enable or disable the magnifier, press `NVDA+shift+w`. The "Increase magnification level" keystroke, `NVDA+shift+equals`, will start the magnifier if it is not running. -When the magnifier is enabled, NVDA will announce the current zoom level, color filter, and focus tracking mode. When disabled, the screen returns to its normal size. -The Magnifier state is saved for future NVDA sessions, so after enabling Magnifier, it will start automatically after restarting NVDA +As with other NVDA settings, the Magnifier state is preserved across sessions. Important: The NVDA Magnifier cannot be used simultaneously with Screen Curtain for security reasons. -If you attempt to enable the magnifier while Screen Curtain is active, NVDA will prompt you to disable Screen Curtain first, and vice versa. +If you attempt to enable the magnifier while Screen Curtain is active, NVDA will prompt you to disable Screen Curtain first. ### Magnifier Controls {#MagnifierControls} @@ -1586,31 +1586,29 @@ Once the magnifier is enabled, you can use the following keyboard commands to co | Name | Key | Description | |---|---|---| -| Toggles the magnifier on and off | `NVDA+shift+w` | Enables or disables the magnifier | -| Increases the magnification level of the magnifier | `NVDA+shift+equals` | Increases the zoom level. Starts the magnifier if it's not already running. | -| Decreases the magnification level of the magnifier | `NVDA+shift+minus` | Decreases the zoom level | -| Toggle filter of the magnifier | `NVDA+shift+i` | Cycles through available color filters (normal, grayscale, inverted) | -| Toggle focus mode for the full-screen magnifier | None | Cycles through focus tracking modes (center, border, relative) | -| Launch spotlight if magnifier is full-screen | `NVDA+shift+l` | Activates spotlight mode for focused reading or presentations | -| Pan left | `NVDA+alt+leftArrow` | Pan the magnified view to the left by the specified panning step size | -| Pan right | `NVDA+alt+rightArrow` | Pan the magnified view to the right by the specified panning step size | -| Pan up | `NVDA+alt+upArrow` | Pan the magnified view upwards by the specified panning step size | -| Pan down | `NVDA+alt+downArrow` | Pan the magnified view downwards by the specified panning step size | -| Pan to left edge | `NVDA+shift+alt+leftArrow` | Pan the magnified view directly to the left edge of the screen | -| Pan to right edge | `NVDA+shift+alt+rightArrow` | Pan the magnified view directly to the right edge of the screen | -| Pan to top edge | `NVDA+shift+alt+upArrow` | Pan the magnified view directly to the top edge of the screen | -| Pan to bottom edge | `NVDA+shift+alt+downArrow` | Pan the magnified view directly to the bottom edge of the screen | +| Toggle the magnifier on and off | `NVDA+shift+w` | Enables or disables the magnifier | +| Increase the magnification level | `NVDA+shift+equals` | Increases the zoom level. Starts the magnifier if it's not already running. | +| Decrease the magnification level | `NVDA+shift+minus` | Decreases the zoom level | +| Cycle color filters | `NVDA+shift+i` | Cycles through the available color filters (normal, grayscale, inverted) | +| Cycle tracking mode | None | Cycles through tracking modes (center, border, relative) | +| Show entire screen overview | `NVDA+shift+l` | Temporarily shows an overview of the entire screen | +| Pan left | `NVDA+alt+leftArrow` | Moves the magnified view left by the configured panning step size | +| Pan right | `NVDA+alt+rightArrow` | Moves the magnified view right by the configured panning step size | +| Pan up | `NVDA+alt+upArrow` | Moves the magnified view up by the configured panning step size | +| Pan down | `NVDA+alt+downArrow` | Moves the magnified view down by the configured panning step size | +| Pan to left edge | `NVDA+shift+alt+leftArrow` | Moves the magnified view directly to the left edge of the screen | +| Pan to right edge | `NVDA+shift+alt+rightArrow` | Moves the magnified view directly to the right edge of the screen | +| Pan to top edge | `NVDA+shift+alt+upArrow` | Moves the magnified view directly to the top edge of the screen | +| Pan to bottom edge | `NVDA+shift+alt+downArrow` | Moves the magnified view directly to the bottom edge of the screen | ### Zoom Levels {#MagnifierZoomLevels} -The magnifier supports zoom levels from 1.0 (no magnification) to 10.0 (maximum magnification). +The magnifier supports zoom levels from 1.0 (no magnification) to 50.0 (maximum magnification). You can adjust the zoom level using the zoom in (`NVDA+shift+equals`) and zoom out (`NVDA+shift+minus`) commands. Each press increases or decreases the zoom by a fixed increment. -The default zoom level when the magnifier is first enabled can be configured in the [Magnifier settings](#MagnifierSettings). - ### Color Filters {#MagnifierColorFilters} Color filters can help users with certain visual impairments or light sensitivity by modifying the colors displayed on the screen. @@ -1621,28 +1619,22 @@ The magnifier provides three color filter options: * Inverted: Inverts all colors on the screen (black becomes white, white becomes black, etc.), which can be helpful for users who prefer light text on dark backgrounds or have photophobia. To cycle through the available filters press `NVDA+shift+i`. -NVDA will announce the name of the currently selected filter. - -The default color filter when the magnifier is first enabled can be configured in the [Magnifier settings](#MagnifierSettings). ### Focus Tracking Modes {#MagnifierFullscreenFocusModes} -The magnifier offers three different modes for tracking focus and determining which part of the screen to magnify: +The magnifier offers three different modes for tracking mouse, focus, review cursor and navigatore object, and determining which part of the screen to magnify: -* Center: The magnified area is centered on the current focus position. -This mode keeps the focused element at the center of the screen and clamps to the screen edge. +* Center: The magnified area is centered on the currently tracked position. +This mode keeps the tracked element at the center of the screen and clamps to the screen edge. To disable clamping, activate [true center mode in the Magnifier settings](#MagnifierUseTrueCenter). -* Border: The magnified area only moves when the focus approaches the edge of the visible area. +* Border: The magnified area only moves when the tracked position approaches the edge of the visible area. This mode provides a more stable view, only adjusting when necessary. -* Relative: The magnified area maintains the relative position of the focus within the screen. +* Relative: The magnified area maintains the relative position of the tracked element within the screen. This mode mimics the behavior of the Windows Magnifier. -To cycle through the focus tracking modes, please assign a custom gesture using the [Input Gestures dialog](#InputGestures). -NVDA will announce the name of the currently selected mode. - -The default focus mode when the magnifier is first enabled can be configured in the [Magnifier settings](#MagnifierSettings). +To cycle through the tracking modes, please assign a custom gesture using the [Input Gestures dialog](#InputGestures). -### Spotlight Mode {#MagnifierSpotlight} +### Show entire screen overview {#MagnifierSpotlight} Spotlight mode is a special feature designed for presentations or focused reading tasks. When activated, it temporarily zooms out the magnified view to show the full screen, then zooms back in to the current focus position after a brief period of mouse inactivity. @@ -2888,7 +2880,7 @@ The selected state is also saved for future NVDA sessions, so if you enable Magn | Options | Disabled, Enabled | | Default | Disabled | -##### Zoom level {#MagnifierZoom} +##### Zoom {#MagnifierZoom} This setting allows you to set the zoom level when using the magnifier. The zoom level can range from 100% (no magnification) to 5000% (maximum magnification). @@ -2901,7 +2893,7 @@ You can always adjust the zoom level on the fly using the zoom in (`NVDA+shift+e | Options | 100% to 5000% | | Default | 200% | -##### Filter {#MagnifierFilter} +##### Color filter {#MagnifierFilter} This combo box allows you to select the filter to apply when using the magnifier. You can cycle through the color filters by pressing `NVDA+shift+i`. @@ -2919,7 +2911,7 @@ The available options are: | Grayscale | Converts all colors to shades of gray, which can help reduce eye strain and improve contrast. | | Inverted | Inverts all colors on the screen, which can be helpful for users who prefer light text on dark backgrounds or have photophobia. | -##### Focus mode {#MagnifierFullscreenFocusMode} +##### Tracking mode {#MagnifierFullscreenFocusMode} This combo box allows you to select the focus tracking mode when using the magnifier. To cycle through the focus tracking modes, please assign a custom gesture using the [Input Gestures dialog](#InputGestures). @@ -2932,34 +2924,25 @@ The available options are: | Option | Description | |---|---| -| Center | The magnified area is always centered on the current focus position. | -| Border | The magnified area only moves when the focus approaches the edge of the visible area. | -| Relative | The magnified area maintains the relative position of the focus within the screen. | +| Center | The magnified area is always centered on the currently tracked position. | +| Border | The magnified area only moves when the tracked element approaches the edge of the visible area. | +| Relative | The magnified area maintains the relative position of the tracked element within the screen. | ##### Panning step size {#MagnifierPanningStepSize} This slider allows you to set the panning step size as a percentage of the visible magnified window. -The panning step size can range from 1% to 100%, with a default value of 10%. This means that when you use manual pan commands, the magnified view will move by the specified percentage of the current visible window size. - -For example, if your visible magnified window is 200 pixels wide and you have a panning step size of 10%, each pan command will move the view by 20 pixels. Higher percentages cause larger movements, making it faster to navigate across the screen, while lower percentages provide finer control for precise positioning. -The actual pixel distance will automatically adjust based on your current zoom level. - -Note: Pan commands allow you to manually move the magnified view in any direction, independent of the focus tracking mode. -Available pan actions include: | . {.hideHeaderRow} |.| |---|---| | Options | 1 to 100 | | Default | 10 | -##### Use true center {#MagnifierUseTrueCenter} +##### Use true center tracking {#MagnifierUseTrueCenter} -This checkbox controls whether the magnifier should always keep the focus centered on the screen, or if it should allow the focus to move towards the edges of the screen before moving the magnified area. -When enabled, the magnifier will always keep the focus centered on the screen, which can be helpful for users who prefer a consistent position of the focus within the magnified view. - -This option is disabled by default. +This checkbox controls whether the magnifier should always keep the tracked position centered on the screen, or if it should allow the tracked position to move towards the edges of the screen before moving the magnified area. +When enabled, the magnifier will always keep the tracked position centered on the screen, which can be helpful for users who prefer a consistent tracked position within the magnified view. | . {.hideHeaderRow} |.| |---|---| @@ -2971,8 +2954,6 @@ This option is disabled by default. This checkbox controls whether the magnifier should follow the mouse pointer. When enabled, the magnified area will automatically move to follow the mouse pointer, which can be helpful for users who navigate primarily using the mouse rather than the keyboard. -This option is enabled by default. - | . {.hideHeaderRow} |.| |---|---| | Options | Disabled, Enabled | @@ -2983,8 +2964,6 @@ This option is enabled by default. This checkbox controls whether the magnifier should follow the system focus. When enabled, the magnified area will automatically move to follow the system focus, which can be helpful for users who navigate primarily using the keyboard and want the magnifier to track their navigation. -This option is enabled by default. - | . {.hideHeaderRow} |.| |---|---| | Options | Disabled, Enabled | @@ -2995,8 +2974,6 @@ This option is enabled by default. This checkbox controls whether the magnifier should follow the review cursor. When enabled, the magnified area will automatically move to follow the review cursor, which can be helpful for users who use the review cursor to navigate through content and want the magnifier to track their navigation. -This option is enabled by default. - | . {.hideHeaderRow} |.| |---|---| | Options | Disabled, Enabled | @@ -3007,8 +2984,6 @@ This option is enabled by default. This checkbox controls whether the magnifier should follow the navigator object. When enabled, the magnified area will automatically move to follow the navigator object, which can be helpful for users who use object navigation to navigate through content and want the magnifier to track their navigation. -This option is enabled by default. - | . {.hideHeaderRow} |.| |---|---| | Options | Disabled, Enabled | From c1d593ae5e5b7f4a33e645c8c9f6d6f3c620b236 Mon Sep 17 00:00:00 2001 From: Cyrille Bougot Date: Fri, 5 Jun 2026 17:13:09 +0200 Subject: [PATCH 11/26] Remove NB-spaces --- user_docs/en/userGuide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user_docs/en/userGuide.md b/user_docs/en/userGuide.md index 605a8095c00..9212f172ee0 100644 --- a/user_docs/en/userGuide.md +++ b/user_docs/en/userGuide.md @@ -1550,7 +1550,7 @@ You can enable Screen Curtain in the [Privacy and Security category](#PrivacyAnd | Name |Key |Description| |---|---|---| |Toggles the state of the screen curtain |`NVDA+control+escape` |Enable to make the screen black or disable to show the contents of the screen. Pressed once, screen curtain is enabled until you restart NVDA. Pressed twice, screen curtain is enabled until you disable it.| -| Reports the state of the screen curtain | `None` | Announces the current status of the screen curtain, whether it is disabled or enabled or in temporary mode. | +| Reports the state of the screen curtain | `None` | Announces the current status of the screen curtain, whether it is disabled or enabled or in temporary mode. | From d04b7b54a1f97a98d7bcf724f5041e9d862cf18a Mon Sep 17 00:00:00 2001 From: Cyrille Bougot Date: Fri, 5 Jun 2026 17:18:44 +0200 Subject: [PATCH 12/26] Commend strings updated --- source/_magnifier/commands.py | 2 +- source/_magnifier/magnifier.py | 6 +++--- source/globalCommands.py | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/source/_magnifier/commands.py b/source/_magnifier/commands.py index 08c5d1111ff..571e262cc54 100644 --- a/source/_magnifier/commands.py +++ b/source/_magnifier/commands.py @@ -101,7 +101,7 @@ def toggleMagnifier() -> None: pgettext( "magnifier", # Translators: Message announced when trying to start magnifier while Screen Curtain is active. - "Cannot start magnifier: Screen Curtain is active. Please disable Screen Curtain first.", + "Cannot start magnifier. Please disable Screen Curtain first.", ), ) else: diff --git a/source/_magnifier/magnifier.py b/source/_magnifier/magnifier.py index 10abf3c3e9d..e221bb90573 100644 --- a/source/_magnifier/magnifier.py +++ b/source/_magnifier/magnifier.py @@ -176,7 +176,7 @@ def _startMagnifier(self) -> None: message = pgettext( "magnifier", # Translators: Message when trying to enable magnifier while screen curtain is active - "Cannot enable magnifier: screen curtain is active. Please disable screen curtain first.", + "Cannot enable magnifier. Please disable screen curtain first.", ) ui.message(message, speechPriority=speech.priorities.Spri.NOW) return @@ -265,7 +265,7 @@ def onScreenCurtainEnabled(self) -> None: pgettext( "magnifier", # Translators: Spoken message when magnifier is disabled due to screen curtain being enabled. - "Magnifier is active, disabling it before enabling screen curtain", + "Disabling magnifier", ), ) self._stopMagnifier() @@ -283,7 +283,7 @@ def onScreenCurtainDisabled(self) -> None: pgettext( "magnifier", # Translators: Spoken message when magnifier is re-enabled after screen curtain is disabled. - "Magnifier was active before screen curtain, re-enabling it", + "Re-enabling magnifier", ), ) self._startMagnifier() diff --git a/source/globalCommands.py b/source/globalCommands.py index aca837a338e..20c53fbcce7 100755 --- a/source/globalCommands.py +++ b/source/globalCommands.py @@ -5043,7 +5043,7 @@ def script_reportScreenCurtainState(self, gesture: inputCore.InputGesture) -> No @script( description=_( # Translators: Describes a command. - "Toggles the magnifier on and off", + "Toggle the magnifier on and off", ), category=SCRCAT_MAGNIFIER, gesture="kb:NVDA+shift+w", @@ -5057,7 +5057,7 @@ def script_toggleMagnifier( @script( description=_( # Translators: Describes a command. - "Increases the magnification level", + "Increase the magnification level", ), category=SCRCAT_MAGNIFIER, gesture="kb:NVDA+shift+=", @@ -5071,7 +5071,7 @@ def script_zoomIn( @script( description=_( # Translators: Describes a command. - "Decreases the magnification level", + "Decrease the magnification level", ), category=SCRCAT_MAGNIFIER, gesture="kb:NVDA+shift+-", From 146c19e525ea41a3436d0cacb0fc3ed90a5869b8 Mon Sep 17 00:00:00 2001 From: Cyrille Bougot Date: Fri, 5 Jun 2026 17:41:42 +0200 Subject: [PATCH 13/26] Standard key writing for Commands Quick Reference generation --- user_docs/en/userGuide.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/user_docs/en/userGuide.md b/user_docs/en/userGuide.md index 9212f172ee0..9fc4af1b2a1 100644 --- a/user_docs/en/userGuide.md +++ b/user_docs/en/userGuide.md @@ -2872,6 +2872,8 @@ This settings category contains the following options: ##### Enable Magnifier {#MagnifierEnable} +Key: `NVDA+shift+w` + When toggled, Magnifier will start and stop immediately. The selected state is also saved for future NVDA sessions, so if you enable Magnifier here, it will start automatically after restarting NVDA. @@ -2895,15 +2897,16 @@ You can always adjust the zoom level on the fly using the zoom in (`NVDA+shift+e ##### Color filter {#MagnifierFilter} -This combo box allows you to select the filter to apply when using the magnifier. -You can cycle through the color filters by pressing `NVDA+shift+i`. +Key: `NVDA+shift+i` + +This option allows you to select the filter to apply when using the magnifier. + The available options are: | . {.hideHeaderRow} |.| |---|---| | Options | Normal, Grayscale, Inverted | | Default | Normal | -| Toggle command | `NVDA+shift+i` | | Option | Description | |---|---| From 54334d65cad09fe03e0b2e4c538d40c91caff327 Mon Sep 17 00:00:00 2001 From: Cyrille Bougot Date: Fri, 5 Jun 2026 17:47:05 +0200 Subject: [PATCH 14/26] UG - All params are applied immediately --- user_docs/en/userGuide.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/user_docs/en/userGuide.md b/user_docs/en/userGuide.md index 9fc4af1b2a1..582dedc459d 100644 --- a/user_docs/en/userGuide.md +++ b/user_docs/en/userGuide.md @@ -2868,14 +2868,15 @@ For the supported settings per provider, please refer to the documentation for t Key: `NVDA+control+w` The Magnifier category in the NVDA Settings dialog allows you to configure the default behavior of NVDA's built-in [Magnifier](#Magnifier) feature. +All the options of this category have immediate effect on the magnifier but pressing "Cancel" or `escape` allow to dismiss the changes made. + This settings category contains the following options: ##### Enable Magnifier {#MagnifierEnable} Key: `NVDA+shift+w` -When toggled, Magnifier will start and stop immediately. -The selected state is also saved for future NVDA sessions, so if you enable Magnifier here, it will start automatically after restarting NVDA. +This option starts or stops the Magnifier. | . {.hideHeaderRow} |.| |---|---| From 93747c625c19df083a5b9cc2cceb356281d991e9 Mon Sep 17 00:00:00 2001 From: Cyrille Bougot Date: Fri, 5 Jun 2026 23:01:24 +0200 Subject: [PATCH 15/26] Modify variable names in API --- source/globalCommands.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/globalCommands.py b/source/globalCommands.py index 20c53fbcce7..7c188e937c6 100755 --- a/source/globalCommands.py +++ b/source/globalCommands.py @@ -5202,7 +5202,7 @@ def script_panToBottomEdge( category=SCRCAT_MAGNIFIER, gesture="kb:NVDA+shift+i", ) - def script_toggleFilter( + def script_cycleFilters( self, gesture: inputCore.InputGesture, ) -> None: @@ -5276,11 +5276,11 @@ def script_toggleAllFollow( @script( description=_( # Translators: Describes a command. - "Toggle magnifier focus mode", + "Cycle through tracking modes", ), category=SCRCAT_MAGNIFIER, ) - def script_toggleFullscreenMode( + def script_cycleTrackingModes( self, gesture: inputCore.InputGesture, ) -> None: @@ -5294,7 +5294,7 @@ def script_toggleFullscreenMode( category=SCRCAT_MAGNIFIER, gesture="kb:NVDA+shift+l", ) - def script_startSpotlight( + def script_showEntireScreenOverview( self, gesture: inputCore.InputGesture, ) -> None: From aece884c498b5c7e0bf61d3ba1164b11f0d53c3b Mon Sep 17 00:00:00 2001 From: Cyrille Bougot Date: Fri, 5 Jun 2026 23:30:53 +0200 Subject: [PATCH 16/26] User Guide - Spotlight -> Screen overview --- user_docs/en/userGuide.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/user_docs/en/userGuide.md b/user_docs/en/userGuide.md index 582dedc459d..098f8a03e95 100644 --- a/user_docs/en/userGuide.md +++ b/user_docs/en/userGuide.md @@ -1636,23 +1636,21 @@ To cycle through the tracking modes, please assign a custom gesture using the [I ### Show entire screen overview {#MagnifierSpotlight} -Spotlight mode is a special feature designed for presentations or focused reading tasks. -When activated, it temporarily zooms out the magnified view to show the full screen, then zooms back in to the current focus position after a brief period of mouse inactivity. +The magnifier can temporarily display an overview of the entire screen, making it useful for presentations or when you need to quickly understand the overall screen layout. This is useful when you want to: * Show context to your audience during a presentation before zooming in on specific details * Temporarily view the full screen layout while magnifying -To activate spotlight mode, press `NVDA+shift+l` while the magnifier is enabled. -Once activated, the magnifier will: +To display an overview of the entire screen, press `NVDA+shift+l`. +The magnifier will then: 1. Smoothly zoom out to show the full screen (zoom level 1.0) 2. Monitor mouse movement -3. When the mouse remains still for approximately 2 seconds, automatically zoom back in to the original zoom level at the mouse position +3. When the mouse remains still for approximately 2 seconds, automatically zoom back in to the original zoom level at the new mouse position -Spotlight mode automatically deactivates after zooming back in. -If you move the mouse before the zoom-back occurs, the timer resets, giving you more time to view the full screen. +Moving the mouse before the zoom-back occurs gives you more time to view the full screen. ### Magnifier Settings {#MagnifierSettings} From 16ed3e0ca7b34856a781ee40e2f3430de8742376 Mon Sep 17 00:00:00 2001 From: Cyrille Bougot Date: Fri, 5 Jun 2026 23:48:20 +0200 Subject: [PATCH 17/26] Anchors and comments --- source/gui/settingsDialogs.py | 9 ++++----- user_docs/en/userGuide.md | 10 +++++----- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/source/gui/settingsDialogs.py b/source/gui/settingsDialogs.py index 97f19df3c95..77918e03f47 100644 --- a/source/gui/settingsDialogs.py +++ b/source/gui/settingsDialogs.py @@ -6138,7 +6138,7 @@ def makeSettings( self.panSpinCtrl.Bind(wx.EVT_TEXT, self._onImmediateSettingChange) # FILTER SETTINGS - # Translators: The label for a setting in magnifier settings to select the default filter + # Translators: The label for a setting in magnifier settings to select the filter filterLabelText = _("Color f&ilter:") filterChoices = [f.displayString for f in Filter] self.filterList = sHelper.addLabeledControl( @@ -6164,7 +6164,7 @@ def makeSettings( choices=fullscreenModeChoices, ) self.bindHelpEvent( - "MagnifierFullscreenFocusMode", + "MagnifierTrackingMode", self.fullscreenModeList, ) @@ -6180,14 +6180,13 @@ def makeSettings( self._trueCenterInitially = self.trueCenterCheckBox.GetValue() self.trueCenterCheckBox.Bind(wx.EVT_CHECKBOX, self._onImmediateSettingChange) - # Set default value from config fullscreenMode = magnifierConfig.getFullscreenMode() self._fullscreenModeInitially = fullscreenMode self.fullscreenModeList.SetSelection(list(FullScreenMode).index(fullscreenMode)) self.fullscreenModeList.Bind(wx.EVT_CHOICE, self._onImmediateSettingChange) - # FOCUS GROUP - # Translators: This is the label for a group of focus options in the magnifier settings panel + # Tracking GROUP + # Translators: This is the label for a group of tracking options in the magnifier settings panel focusGroupText = _("Tracking") focusGroupSizer = wx.StaticBoxSizer(wx.VERTICAL, self, label=focusGroupText) focusGroupBox = focusGroupSizer.GetStaticBox() diff --git a/user_docs/en/userGuide.md b/user_docs/en/userGuide.md index 098f8a03e95..dfc2c9d35bb 100644 --- a/user_docs/en/userGuide.md +++ b/user_docs/en/userGuide.md @@ -1578,7 +1578,7 @@ As with other NVDA settings, the Magnifier state is preserved across sessions. Important: The NVDA Magnifier cannot be used simultaneously with Screen Curtain for security reasons. If you attempt to enable the magnifier while Screen Curtain is active, NVDA will prompt you to disable Screen Curtain first. -### Magnifier Controls {#MagnifierControls} +### Magnifier Commands {#MagnifierCommands} Once the magnifier is enabled, you can use the following keyboard commands to control it: @@ -1620,7 +1620,7 @@ The magnifier provides three color filter options: To cycle through the available filters press `NVDA+shift+i`. -### Focus Tracking Modes {#MagnifierFullscreenFocusModes} +### Tracking Modes {#MagnifierTrackingModes} The magnifier offers three different modes for tracking mouse, focus, review cursor and navigatore object, and determining which part of the screen to magnify: @@ -1634,7 +1634,7 @@ This mode mimics the behavior of the Windows Magnifier. To cycle through the tracking modes, please assign a custom gesture using the [Input Gestures dialog](#InputGestures). -### Show entire screen overview {#MagnifierSpotlight} +### Show entire screen overview {#MagnifierScreenOverview} The magnifier can temporarily display an overview of the entire screen, making it useful for presentations or when you need to quickly understand the overall screen layout. @@ -2913,7 +2913,7 @@ The available options are: | Grayscale | Converts all colors to shades of gray, which can help reduce eye strain and improve contrast. | | Inverted | Inverts all colors on the screen, which can be helpful for users who prefer light text on dark backgrounds or have photophobia. | -##### Tracking mode {#MagnifierFullscreenFocusMode} +##### Tracking mode {#MagnifierTrackingMode} This combo box allows you to select the focus tracking mode when using the magnifier. To cycle through the focus tracking modes, please assign a custom gesture using the [Input Gestures dialog](#InputGestures). @@ -2932,7 +2932,7 @@ The available options are: ##### Panning step size {#MagnifierPanningStepSize} -This slider allows you to set the panning step size as a percentage of the visible magnified window. +This option allows you to set the panning step size as a percentage of the visible magnified window. This means that when you use manual pan commands, the magnified view will move by the specified percentage of the current visible window size. Higher percentages cause larger movements, making it faster to navigate across the screen, while lower percentages provide finer control for precise positioning. From 77b82a75dc2962799906d4b827ddfb2001f84f7a Mon Sep 17 00:00:00 2001 From: Cyrille Bougot Date: Mon, 8 Jun 2026 00:13:51 +0200 Subject: [PATCH 18/26] True center trakcing --- user_docs/en/userGuide.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/user_docs/en/userGuide.md b/user_docs/en/userGuide.md index dfc2c9d35bb..19e291eb90b 100644 --- a/user_docs/en/userGuide.md +++ b/user_docs/en/userGuide.md @@ -1626,7 +1626,7 @@ The magnifier offers three different modes for tracking mouse, focus, review cur * Center: The magnified area is centered on the currently tracked position. This mode keeps the tracked element at the center of the screen and clamps to the screen edge. -To disable clamping, activate [true center mode in the Magnifier settings](#MagnifierUseTrueCenter). +To disable clamping, activate [true center tracking in the Magnifier settings](#MagnifierUseTrueCenter). * Border: The magnified area only moves when the tracked position approaches the edge of the visible area. This mode provides a more stable view, only adjusting when necessary. * Relative: The magnified area maintains the relative position of the tracked element within the screen. @@ -2943,7 +2943,7 @@ Higher percentages cause larger movements, making it faster to navigate across t ##### Use true center tracking {#MagnifierUseTrueCenter} -This checkbox controls whether the magnifier should always keep the tracked position centered on the screen, or if it should allow the tracked position to move towards the edges of the screen before moving the magnified area. +This checkbox controls whether the magnifier should always keep the tracked position centered on the screen, even when the tracked element is next to the screen edges, or if it should allow the tracked position to move towards the edges of the screen. When enabled, the magnifier will always keep the tracked position centered on the screen, which can be helpful for users who prefer a consistent tracked position within the magnified view. | . {.hideHeaderRow} |.| From 1d0db392dc97dfde437d9a3ee303950ff3042ebe Mon Sep 17 00:00:00 2001 From: Cyrille Bougot Date: Tue, 9 Jun 2026 15:31:32 +0200 Subject: [PATCH 19/26] focusType -> trackingType --- source/gui/settingsDialogs.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/source/gui/settingsDialogs.py b/source/gui/settingsDialogs.py index 217d8c3bcc8..5ee57b71fc8 100644 --- a/source/gui/settingsDialogs.py +++ b/source/gui/settingsDialogs.py @@ -6057,8 +6057,8 @@ def _applyCurrentSettingsToConfigAndRuntime(self): magnifierConfig.setFullscreenMode(selectedMode) config.conf["magnifier"]["isTrueCentered"] = self.trueCenterTrackingCheckBox - for focusType, checkBox in self._trackingTypeCheckBoxes.items(): - magnifierConfig.setFollowState(focusType, checkBox.GetValue()) + for trackingType, checkBox in self._trackingTypeCheckBoxes.items(): + magnifierConfig.setFollowState(trackingType, checkBox.GetValue()) magnifier = getMagnifier() if magnifier: @@ -6259,9 +6259,9 @@ def onSave(self): self._filterInitially = selectedFilter self._fullscreenModeInitially = selectedMode self._trueCenterTrackingInitially = isTrueCentered - for focusType, checkBox in self._trackingTypeCheckBoxes.items(): + for trackingType, checkBox in self._trackingTypeCheckBoxes.items(): shouldFollow = checkBox.GetValue() - self._trackingTypeInitially[focusType] = shouldFollow + self._trackingTypeInitially[trackingType] = shouldFollow def onDiscard(self): """Restore magnifier state from original settings from config.""" @@ -6270,8 +6270,8 @@ def onDiscard(self): magnifierConfig.setFilter(self._filterInitially) magnifierConfig.setFullscreenMode(self._fullscreenModeInitially) config.conf["magnifier"]["isTrueCentered"] = self._trueCenterTrackingInitially - for focusType, state in self._trackingTypeInitially.items(): - magnifierConfig.setFollowState(focusType, state) + for trackingType, state in self._trackingTypeInitially.items(): + magnifierConfig.setFollowState(trackingType, state) magnifier = getMagnifier() if magnifier: From 3ad7b8e84c60eea0d2e93daf8c41547f957ea188 Mon Sep 17 00:00:00 2001 From: Cyrille Bougot Date: Tue, 9 Jun 2026 15:44:32 +0200 Subject: [PATCH 20/26] MagnifierFollowFocusType -> MagnifierTrackingType --- source/_magnifier/commands.py | 4 +- source/_magnifier/config.py | 20 +++--- source/_magnifier/utils/focusManager.py | 32 +++++----- source/_magnifier/utils/types.py | 4 +- source/globalCommands.py | 10 +-- source/gui/settingsDialogs.py | 16 ++--- .../unit/test_magnifier/test_focusManager.py | 64 +++++++++---------- 7 files changed, 75 insertions(+), 75 deletions(-) diff --git a/source/_magnifier/commands.py b/source/_magnifier/commands.py index 571e262cc54..11bb29ea7ab 100644 --- a/source/_magnifier/commands.py +++ b/source/_magnifier/commands.py @@ -28,7 +28,7 @@ MagnifiedView, FullScreenMode, MagnifierAction, - MagnifierFollowFocusType, + MagnifierTrackingType, ) from logHandler import log @@ -199,7 +199,7 @@ def cycleMagnifiedView() -> None: ) -def toggleFollow(focusType: MagnifierFollowFocusType) -> None: +def toggleFollow(focusType: MagnifierTrackingType) -> None: """ Toggle the specified follow mode setting. diff --git a/source/_magnifier/config.py b/source/_magnifier/config.py index d61a43a54a7..51f2c8a4d9d 100644 --- a/source/_magnifier/config.py +++ b/source/_magnifier/config.py @@ -1,5 +1,5 @@ # A part of NonVisual Desktop Access (NVDA) -# Copyright (C) 2025-2026 NV Access Limited, Antoine Haffreingue +# Copyright (C) 2025-2026 NV Access Limited, Antoine Haffreingue, Cyrille Bougot # This file may be used under the terms of the GNU General Public License, version 2 or later, as modified by the NVDA license. # For full terms and any additional permissions, see the NVDA license file: https://github.com/nvaccess/nvda/blob/master/copying.txt @@ -10,7 +10,7 @@ import config from dataclasses import dataclass, field -from .utils.types import Filter, FullScreenMode, MagnifierFollowFocusType, MagnifiedView +from .utils.types import Filter, FullScreenMode, MagnifierTrackingType, MagnifiedView def setEnabled(enable: bool) -> None: @@ -153,17 +153,17 @@ def setMagnifiedView(magnifiedView: MagnifiedView) -> None: config.conf["magnifier"]["magnifiedView"] = magnifiedView.value -_FOLLOW_CONFIG_KEYS: dict[MagnifierFollowFocusType, str] = { - MagnifierFollowFocusType.MOUSE: "followMouse", - MagnifierFollowFocusType.SYSTEM_FOCUS: "followSystemFocus", - MagnifierFollowFocusType.REVIEW: "followReviewCursor", - MagnifierFollowFocusType.NAVIGATOR_OBJECT: "followNavigatorObject", +_FOLLOW_CONFIG_KEYS: dict[MagnifierTrackingType, str] = { + MagnifierTrackingType.MOUSE: "followMouse", + MagnifierTrackingType.SYSTEM_FOCUS: "followSystemFocus", + MagnifierTrackingType.REVIEW: "followReviewCursor", + MagnifierTrackingType.NAVIGATOR_OBJECT: "followNavigatorObject", } @dataclass class _FollowStateOverride: - savedStates: dict[MagnifierFollowFocusType, bool] = field(default_factory=dict) + savedStates: dict[MagnifierTrackingType, bool] = field(default_factory=dict) isActive: bool = False @@ -179,7 +179,7 @@ def _ensureSavedStatesInitialized() -> None: saveFollowStates() -def getFollowState(focusType: MagnifierFollowFocusType) -> bool: +def getFollowState(focusType: MagnifierTrackingType) -> bool: """ Get the current follow state for a given focus type. @@ -189,7 +189,7 @@ def getFollowState(focusType: MagnifierFollowFocusType) -> bool: return config.conf["magnifier"][_FOLLOW_CONFIG_KEYS[focusType]] -def setFollowState(focusType: MagnifierFollowFocusType, state: bool) -> None: +def setFollowState(focusType: MagnifierTrackingType, state: bool) -> None: """ Set the follow state for a given focus type. diff --git a/source/_magnifier/utils/focusManager.py b/source/_magnifier/utils/focusManager.py index 7bfe58ba4dd..64fd731d05f 100644 --- a/source/_magnifier/utils/focusManager.py +++ b/source/_magnifier/utils/focusManager.py @@ -16,7 +16,7 @@ import locationHelper import textInfos from textInfos.offsets import OffsetsTextInfo -from .types import Coordinates, MagnifierFollowFocusType +from .types import Coordinates, MagnifierTrackingType from ..config import getFollowState @@ -30,7 +30,7 @@ class FocusManager: def __init__(self): """Initialize the focus manager.""" - self._lastFocusedObject: MagnifierFollowFocusType | None = None + self._lastFocusedObject: MagnifierTrackingType | None = None self._lastReportedCoordinates = Coordinates(0, 0) self._lastMousePosition = Coordinates(0, 0) self._lastSystemFocusPosition = Coordinates(0, 0) @@ -62,10 +62,10 @@ def getCurrentFocusCoordinates(self) -> Coordinates: isClickPressed = winUser.getAsyncKeyState(winUser.VK_LBUTTON) < 0 # Cache settings once — each call reads from config.conf - isFollowMouse = getFollowState(MagnifierFollowFocusType.MOUSE) - isFollowSystemFocus = getFollowState(MagnifierFollowFocusType.SYSTEM_FOCUS) - isFollowReviewCursor = getFollowState(MagnifierFollowFocusType.REVIEW) - isFollowNavigatorObject = getFollowState(MagnifierFollowFocusType.NAVIGATOR_OBJECT) + isFollowMouse = getFollowState(MagnifierTrackingType.MOUSE) + isFollowSystemFocus = getFollowState(MagnifierTrackingType.SYSTEM_FOCUS) + isFollowReviewCursor = getFollowState(MagnifierTrackingType.REVIEW) + isFollowNavigatorObject = getFollowState(MagnifierTrackingType.NAVIGATOR_OBJECT) mouseChanged = self._lastMousePosition != mousePosition systemFocusChanged = self._lastSystemFocusPosition != systemFocusPosition @@ -85,7 +85,7 @@ def getCurrentFocusCoordinates(self) -> Coordinates: # Priority 1: Mouse — drag (fires even when stationary) or movement if (isClickPressed or mouseChanged) and isFollowMouse: - self._lastFocusedObject = MagnifierFollowFocusType.MOUSE + self._lastFocusedObject = MagnifierTrackingType.MOUSE return self._rememberAndReturnCoordinates(mousePosition) # Special case: table cell navigation (numpad). @@ -93,22 +93,22 @@ def getCurrentFocusCoordinates(self) -> Coordinates: # review cursor does not, the navigator object reflects the user's explicit navigation # intent and therefore takes priority over the system focus. if navigatorChanged and systemFocusChanged and not reviewChanged and isFollowNavigatorObject: - self._lastFocusedObject = MagnifierFollowFocusType.NAVIGATOR_OBJECT + self._lastFocusedObject = MagnifierTrackingType.NAVIGATOR_OBJECT return self._rememberAndReturnCoordinates(navigatorPosition) # Priority 2: System focus (focus object + browse mode cursor) if systemFocusChanged and isFollowSystemFocus: - self._lastFocusedObject = MagnifierFollowFocusType.SYSTEM_FOCUS + self._lastFocusedObject = MagnifierTrackingType.SYSTEM_FOCUS return self._rememberAndReturnCoordinates(systemFocusPosition) # Priority 3: Review cursor if reviewChanged and isFollowReviewCursor and reviewPosition is not None: - self._lastFocusedObject = MagnifierFollowFocusType.REVIEW + self._lastFocusedObject = MagnifierTrackingType.REVIEW return self._rememberAndReturnCoordinates(reviewPosition) # Priority 4: Navigator object (NumPad navigation) if navigatorChanged and isFollowNavigatorObject: - self._lastFocusedObject = MagnifierFollowFocusType.NAVIGATOR_OBJECT + self._lastFocusedObject = MagnifierTrackingType.NAVIGATOR_OBJECT return self._rememberAndReturnCoordinates(navigatorPosition) # Resolve the effective review position once (fallback to last valid when None) @@ -118,10 +118,10 @@ def getCurrentFocusCoordinates(self) -> Coordinates: # All sources in priority order _sources = ( - (MagnifierFollowFocusType.MOUSE, isFollowMouse, mousePosition), - (MagnifierFollowFocusType.SYSTEM_FOCUS, isFollowSystemFocus, systemFocusPosition), - (MagnifierFollowFocusType.REVIEW, isFollowReviewCursor, reviewEffectivePosition), - (MagnifierFollowFocusType.NAVIGATOR_OBJECT, isFollowNavigatorObject, navigatorPosition), + (MagnifierTrackingType.MOUSE, isFollowMouse, mousePosition), + (MagnifierTrackingType.SYSTEM_FOCUS, isFollowSystemFocus, systemFocusPosition), + (MagnifierTrackingType.REVIEW, isFollowReviewCursor, reviewEffectivePosition), + (MagnifierTrackingType.NAVIGATOR_OBJECT, isFollowNavigatorObject, navigatorPosition), ) # Keep current source if still enabled; otherwise clear it and freeze at _lastReportedCoordinates @@ -271,7 +271,7 @@ def _getNavigatorObjectPosition(self) -> Coordinates: return position return self._lastValidNavigatorObjectPosition - def getLastFocusType(self) -> MagnifierFollowFocusType | None: + def getLastFocusType(self) -> MagnifierTrackingType | None: """ Get the type of the last focused object. diff --git a/source/_magnifier/utils/types.py b/source/_magnifier/utils/types.py index aa6c2276782..791b97435c1 100644 --- a/source/_magnifier/utils/types.py +++ b/source/_magnifier/utils/types.py @@ -89,7 +89,7 @@ def _displayStringLabels(self) -> dict["MagnifierAction", str]: } -class MagnifierFollowFocusType(DisplayStringEnum): +class MagnifierTrackingType(DisplayStringEnum): """Type of focus the magnifier should follow based on user settings""" MOUSE = auto() @@ -98,7 +98,7 @@ class MagnifierFollowFocusType(DisplayStringEnum): NAVIGATOR_OBJECT = auto() @property - def _displayStringLabels(self) -> dict["MagnifierFollowFocusType", str]: + def _displayStringLabels(self) -> dict["MagnifierTrackingType", str]: return { # Translators: Focus type for magnifier to follow - mouse cursor. self.MOUSE: pgettext("magnifier follow focus type", "Mouse"), diff --git a/source/globalCommands.py b/source/globalCommands.py index 7c188e937c6..41c601e5ef6 100755 --- a/source/globalCommands.py +++ b/source/globalCommands.py @@ -26,7 +26,7 @@ import review import _magnifier import _magnifier.commands -from _magnifier.utils.types import MagnifierFollowFocusType +from _magnifier.utils.types import MagnifierTrackingType import controlTypes import api import textInfos @@ -5219,7 +5219,7 @@ def script_toggleFollowMouse( self, gesture: inputCore.InputGesture, ) -> None: - _magnifier.commands.toggleFollow(MagnifierFollowFocusType.MOUSE) + _magnifier.commands.toggleFollow(MagnifierTrackingType.MOUSE) @script( description=_( @@ -5232,7 +5232,7 @@ def script_toggleFollowSystemFocus( self, gesture: inputCore.InputGesture, ) -> None: - _magnifier.commands.toggleFollow(MagnifierFollowFocusType.SYSTEM_FOCUS) + _magnifier.commands.toggleFollow(MagnifierTrackingType.SYSTEM_FOCUS) @script( description=_( @@ -5245,7 +5245,7 @@ def script_toggleFollowReview( self, gesture: inputCore.InputGesture, ) -> None: - _magnifier.commands.toggleFollow(MagnifierFollowFocusType.REVIEW) + _magnifier.commands.toggleFollow(MagnifierTrackingType.REVIEW) @script( description=_( @@ -5258,7 +5258,7 @@ def script_toggleFollowNavigatorObject( self, gesture: inputCore.InputGesture, ) -> None: - _magnifier.commands.toggleFollow(MagnifierFollowFocusType.NAVIGATOR_OBJECT) + _magnifier.commands.toggleFollow(MagnifierTrackingType.NAVIGATOR_OBJECT) @script( description=_( diff --git a/source/gui/settingsDialogs.py b/source/gui/settingsDialogs.py index 5ee57b71fc8..6bb2b658c6e 100644 --- a/source/gui/settingsDialogs.py +++ b/source/gui/settingsDialogs.py @@ -42,7 +42,7 @@ from _magnifier import getMagnifier from _magnifier.commands import toggleMagnifier import _magnifier.config as magnifierConfig -from _magnifier.utils.types import Filter, FullScreenMode, MagnifierFollowFocusType +from _magnifier.utils.types import Filter, FullScreenMode, MagnifierTrackingType from _magnifier.fullscreenMagnifier import FullScreenMagnifier import queueHandler import requests @@ -6188,21 +6188,21 @@ def makeSettings( trackingGroup = guiHelper.BoxSizerHelper(trackingGroupBox, sizer=trackingGroupSizer) sHelper.addItem(trackingGroup) - _trackingTypeLabels: dict[MagnifierFollowFocusType, tuple[str, str]] = { + _trackingTypeLabels: dict[MagnifierTrackingType, tuple[str, str]] = { # Translators: The label for a setting in magnifier settings to select whether the magnifier view should follow the mouse - MagnifierFollowFocusType.MOUSE: (_("Follow &mouse"), "MagnifierFollowMouse"), + MagnifierTrackingType.MOUSE: (_("Follow &mouse"), "MagnifierFollowMouse"), # Translators: The label for a setting in magnifier settings to select whether the magnifier view should follow the system focus - MagnifierFollowFocusType.SYSTEM_FOCUS: (_("Follow &system focus"), "MagnifierFollowSystemFocus"), + MagnifierTrackingType.SYSTEM_FOCUS: (_("Follow &system focus"), "MagnifierFollowSystemFocus"), # Translators: The label for a setting in magnifier settings to select whether the magnifier view should follow the review cursor - MagnifierFollowFocusType.REVIEW: (_("Follow &review cursor"), "MagnifierFollowReviewCursor"), - MagnifierFollowFocusType.NAVIGATOR_OBJECT: ( + MagnifierTrackingType.REVIEW: (_("Follow &review cursor"), "MagnifierFollowReviewCursor"), + MagnifierTrackingType.NAVIGATOR_OBJECT: ( # Translators: The label for a setting in magnifier settings to select whether the magnifier view should follow the navigator object _("Follow &navigator object"), "MagnifierFollowNavigatorObject", ), } - self._trackingTypeCheckBoxes: dict[MagnifierFollowFocusType, wx.CheckBox] = {} - self._trackingTypeInitially: dict[MagnifierFollowFocusType, bool] = {} + self._trackingTypeCheckBoxes: dict[MagnifierTrackingType, wx.CheckBox] = {} + self._trackingTypeInitially: dict[MagnifierTrackingType, bool] = {} for trackingType, (label, helpId) in _trackingTypeLabels.items(): checkBox = trackingGroup.addItem(wx.CheckBox(trackingGroupBox, label=label)) self.bindHelpEvent(helpId, checkBox) diff --git a/tests/unit/test_magnifier/test_focusManager.py b/tests/unit/test_magnifier/test_focusManager.py index 829abbb17f4..0ea9226b9d5 100644 --- a/tests/unit/test_magnifier/test_focusManager.py +++ b/tests/unit/test_magnifier/test_focusManager.py @@ -1,11 +1,11 @@ # A part of NonVisual Desktop Access (NVDA) -# Copyright (C) 2025-2026 NV Access Limited, Antoine Haffreingue +# Copyright (C) 2025-2026 NV Access Limited, Antoine Haffreingue, Cyrille Bougot # This file may be used under the terms of the GNU General Public License, version 2 or later, as modified by the NVDA license. # For full terms and any additional permissions, see the NVDA license file: https://github.com/nvaccess/nvda/blob/master/copying.txt from dataclasses import dataclass from _magnifier.utils.focusManager import FocusManager -from _magnifier.utils.types import Coordinates, MagnifierFollowFocusType +from _magnifier.utils.types import Coordinates, MagnifierTrackingType import unittest from unittest.mock import MagicMock, Mock, patch import _ctypes @@ -19,10 +19,10 @@ def _makeFollowStateSideEffect( ): """Return a side_effect function for patching getFollowState.""" states = { - MagnifierFollowFocusType.MOUSE: followMouse, - MagnifierFollowFocusType.SYSTEM_FOCUS: followSystemFocus, - MagnifierFollowFocusType.REVIEW: followReview, - MagnifierFollowFocusType.NAVIGATOR_OBJECT: followNavigatorObject, + MagnifierTrackingType.MOUSE: followMouse, + MagnifierTrackingType.SYSTEM_FOCUS: followSystemFocus, + MagnifierTrackingType.REVIEW: followReview, + MagnifierTrackingType.NAVIGATOR_OBJECT: followNavigatorObject, } return states.__getitem__ @@ -36,9 +36,9 @@ class FocusTestParam: mousePos: tuple leftPressed: bool expectedCoords: Coordinates - expectedFocus: MagnifierFollowFocusType | None + expectedFocus: MagnifierTrackingType | None description: str = "" - lastFocusedObject: MagnifierFollowFocusType | None = None + lastFocusedObject: MagnifierTrackingType | None = None reviewPos: Coordinates | None = None followMouse: bool = True followSystemFocus: bool = True @@ -184,7 +184,7 @@ def testGetCurrentFocusCoordinates(self): mousePos=Coordinates(0, 0), leftPressed=True, expectedCoords=Coordinates(0, 0), - expectedFocus=MagnifierFollowFocusType.MOUSE, + expectedFocus=MagnifierTrackingType.MOUSE, description="Left click is pressed should return mouse position", ), FocusTestParam( @@ -193,7 +193,7 @@ def testGetCurrentFocusCoordinates(self): mousePos=Coordinates(10, 10), leftPressed=False, expectedCoords=Coordinates(10, 10), - expectedFocus=MagnifierFollowFocusType.MOUSE, + expectedFocus=MagnifierTrackingType.MOUSE, description="Mouse moving (not dragging)", ), FocusTestParam( @@ -202,7 +202,7 @@ def testGetCurrentFocusCoordinates(self): mousePos=Coordinates(0, 0), leftPressed=False, expectedCoords=Coordinates(15, 15), - expectedFocus=MagnifierFollowFocusType.SYSTEM_FOCUS, + expectedFocus=MagnifierTrackingType.SYSTEM_FOCUS, description="System focus changed alone (navigator did not move)", ), FocusTestParam( @@ -211,7 +211,7 @@ def testGetCurrentFocusCoordinates(self): mousePos=Coordinates(0, 0), leftPressed=False, expectedCoords=Coordinates(20, 20), - expectedFocus=MagnifierFollowFocusType.NAVIGATOR_OBJECT, + expectedFocus=MagnifierTrackingType.NAVIGATOR_OBJECT, description="Navigator object changed (NumPad navigation)", ), FocusTestParam( @@ -220,7 +220,7 @@ def testGetCurrentFocusCoordinates(self): mousePos=Coordinates(0, 0), leftPressed=False, expectedCoords=Coordinates(30, 30), - expectedFocus=MagnifierFollowFocusType.NAVIGATOR_OBJECT, + expectedFocus=MagnifierTrackingType.NAVIGATOR_OBJECT, description="Both system focus and navigator changed (table cell navigation): navigator wins", ), FocusTestParam( @@ -230,7 +230,7 @@ def testGetCurrentFocusCoordinates(self): leftPressed=False, reviewPos=Coordinates(30, 30), expectedCoords=Coordinates(30, 30), - expectedFocus=MagnifierFollowFocusType.REVIEW, + expectedFocus=MagnifierTrackingType.REVIEW, description="Review cursor changed with followReview enabled", ), FocusTestParam( @@ -240,7 +240,7 @@ def testGetCurrentFocusCoordinates(self): leftPressed=False, reviewPos=Coordinates(30, 30), expectedCoords=Coordinates(30, 30), - expectedFocus=MagnifierFollowFocusType.REVIEW, + expectedFocus=MagnifierTrackingType.REVIEW, description="Review has higher priority than navigator", ), FocusTestParam( @@ -251,7 +251,7 @@ def testGetCurrentFocusCoordinates(self): reviewPos=Coordinates(30, 30), followReview=False, expectedCoords=Coordinates(20, 20), - expectedFocus=MagnifierFollowFocusType.NAVIGATOR_OBJECT, + expectedFocus=MagnifierTrackingType.NAVIGATOR_OBJECT, description="Review cursor ignored when followReview=False", ), FocusTestParam( @@ -260,9 +260,9 @@ def testGetCurrentFocusCoordinates(self): mousePos=Coordinates(0, 0), leftPressed=False, expectedCoords=Coordinates(0, 0), - expectedFocus=MagnifierFollowFocusType.MOUSE, + expectedFocus=MagnifierTrackingType.MOUSE, description="Nothing changed, last was Mouse", - lastFocusedObject=MagnifierFollowFocusType.MOUSE, + lastFocusedObject=MagnifierTrackingType.MOUSE, ), FocusTestParam( navigatorObjectPos=Coordinates(0, 0), @@ -270,9 +270,9 @@ def testGetCurrentFocusCoordinates(self): mousePos=(0, 0), leftPressed=False, expectedCoords=Coordinates(0, 0), - expectedFocus=MagnifierFollowFocusType.NAVIGATOR_OBJECT, + expectedFocus=MagnifierTrackingType.NAVIGATOR_OBJECT, description="Nothing changed, last was NAVIGATOR", - lastFocusedObject=MagnifierFollowFocusType.NAVIGATOR_OBJECT, + lastFocusedObject=MagnifierTrackingType.NAVIGATOR_OBJECT, ), FocusTestParam( navigatorObjectPos=Coordinates(0, 0), @@ -281,9 +281,9 @@ def testGetCurrentFocusCoordinates(self): leftPressed=False, reviewPos=Coordinates(30, 30), expectedCoords=Coordinates(30, 30), - expectedFocus=MagnifierFollowFocusType.REVIEW, + expectedFocus=MagnifierTrackingType.REVIEW, description="Nothing changed, last was REVIEW - returns current review position", - lastFocusedObject=MagnifierFollowFocusType.REVIEW, + lastFocusedObject=MagnifierTrackingType.REVIEW, ), FocusTestParam( navigatorObjectPos=Coordinates(10, 10), @@ -291,7 +291,7 @@ def testGetCurrentFocusCoordinates(self): mousePos=Coordinates(20, 20), leftPressed=False, expectedCoords=Coordinates(20, 20), - expectedFocus=MagnifierFollowFocusType.MOUSE, + expectedFocus=MagnifierTrackingType.MOUSE, description="Both mouse and navigator object moved (mouse has priority)", ), FocusTestParam( @@ -300,7 +300,7 @@ def testGetCurrentFocusCoordinates(self): mousePos=Coordinates(20, 20), leftPressed=True, expectedCoords=Coordinates(20, 20), - expectedFocus=MagnifierFollowFocusType.MOUSE, + expectedFocus=MagnifierTrackingType.MOUSE, description="All three moved while dragging (mouse drag has highest priority)", ), ] @@ -354,7 +354,7 @@ def testGetLastFocusType(self): """Test getting the last focus type.""" self.assertIsNone(self.focusManager.getLastFocusType()) - for focusType in MagnifierFollowFocusType: + for focusType in MagnifierTrackingType: self.focusManager._lastFocusedObject = focusType self.assertEqual(self.focusManager.getLastFocusType(), focusType) @@ -401,7 +401,7 @@ def testFollowMouseDisabled(self): followNavigatorObject=True, ) self.assertEqual(coords, Coordinates(20, 20)) - self.assertEqual(self.focusManager.getLastFocusType(), MagnifierFollowFocusType.SYSTEM_FOCUS) + self.assertEqual(self.focusManager.getLastFocusType(), MagnifierTrackingType.SYSTEM_FOCUS) def testFollowSystemFocusDisabled(self): """When followSystemFocus=False, system focus changes are ignored and review wins.""" @@ -412,7 +412,7 @@ def testFollowSystemFocusDisabled(self): followNavigatorObject=True, ) self.assertEqual(coords, Coordinates(30, 30)) - self.assertEqual(self.focusManager.getLastFocusType(), MagnifierFollowFocusType.REVIEW) + self.assertEqual(self.focusManager.getLastFocusType(), MagnifierTrackingType.REVIEW) def testFollowReviewDisabled(self): """When followReview=False, review changes are ignored and navigator wins.""" @@ -423,7 +423,7 @@ def testFollowReviewDisabled(self): followNavigatorObject=True, ) self.assertEqual(coords, Coordinates(40, 40)) - self.assertEqual(self.focusManager.getLastFocusType(), MagnifierFollowFocusType.NAVIGATOR_OBJECT) + self.assertEqual(self.focusManager.getLastFocusType(), MagnifierTrackingType.NAVIGATOR_OBJECT) def testAllFollowDisabled(self): """When all settings are False, no source fires and focus remains frozen.""" @@ -506,12 +506,12 @@ def testFollowMouseDragIgnoresSettings(self): coords = self.focusManager.getCurrentFocusCoordinates() self.assertEqual(coords, Coordinates(10, 10)) - self.assertEqual(self.focusManager.getLastFocusType(), MagnifierFollowFocusType.MOUSE) + self.assertEqual(self.focusManager.getLastFocusType(), MagnifierTrackingType.MOUSE) def testDisableFollowMouseKeepsViewFrozen(self): """When followMouse is disabled, view remains frozen until a followed source changes.""" # Simulate: mouse was previously the active focus source - self.focusManager._lastFocusedObject = MagnifierFollowFocusType.MOUSE + self.focusManager._lastFocusedObject = MagnifierTrackingType.MOUSE self.focusManager._lastReportedCoordinates = Coordinates(10, 10) # Positions haven't changed from last recorded values (no "change" detected) self.focusManager._lastMousePosition = Coordinates(10, 10) @@ -544,7 +544,7 @@ def testDisableFollowMouseKeepsViewFrozen(self): def testDisableFollowMouseWhileMouseMovingKeepsViewFrozen(self): """When followMouse is disabled, mouse movement alone does not move the view.""" - self.focusManager._lastFocusedObject = MagnifierFollowFocusType.MOUSE + self.focusManager._lastFocusedObject = MagnifierTrackingType.MOUSE self.focusManager._lastReportedCoordinates = Coordinates(10, 10) self.focusManager._lastMousePosition = Coordinates(10, 10) self.focusManager._lastSystemFocusPosition = Coordinates(20, 20) @@ -577,7 +577,7 @@ def testDisableFollowMouseWhileMouseMovingKeepsViewFrozen(self): def testDisableFollowSystemFocusKeepsViewFrozen(self): """When followSystemFocus is disabled, view remains frozen until a followed source changes.""" - self.focusManager._lastFocusedObject = MagnifierFollowFocusType.SYSTEM_FOCUS + self.focusManager._lastFocusedObject = MagnifierTrackingType.SYSTEM_FOCUS self.focusManager._lastReportedCoordinates = Coordinates(20, 20) self.focusManager._lastMousePosition = Coordinates(10, 10) self.focusManager._lastSystemFocusPosition = Coordinates(20, 20) From 9294316e11e76133edce97fc5716eff3644fae6c Mon Sep 17 00:00:00 2001 From: Cyrille Bougot Date: Tue, 9 Jun 2026 15:45:44 +0200 Subject: [PATCH 21/26] Fix missing GetValue --- source/gui/settingsDialogs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/gui/settingsDialogs.py b/source/gui/settingsDialogs.py index 6bb2b658c6e..0d33147d5d6 100644 --- a/source/gui/settingsDialogs.py +++ b/source/gui/settingsDialogs.py @@ -6055,7 +6055,7 @@ def _applyCurrentSettingsToConfigAndRuntime(self): magnifierConfig.setPanStep(selectedPanStep) magnifierConfig.setFilter(selectedFilter) magnifierConfig.setFullscreenMode(selectedMode) - config.conf["magnifier"]["isTrueCentered"] = self.trueCenterTrackingCheckBox + config.conf["magnifier"]["isTrueCentered"] = self.trueCenterTrackingCheckBox.GetValue() for trackingType, checkBox in self._trackingTypeCheckBoxes.items(): magnifierConfig.setFollowState(trackingType, checkBox.GetValue()) From ab13ffbf6802ac47d6957e68260fcdd0f3d9cf5a Mon Sep 17 00:00:00 2001 From: Cyrille Bougot Date: Tue, 9 Jun 2026 15:57:56 +0200 Subject: [PATCH 22/26] Anchor for settings panel --- source/gui/settingsDialogs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/gui/settingsDialogs.py b/source/gui/settingsDialogs.py index 0d33147d5d6..cf89c372b0b 100644 --- a/source/gui/settingsDialogs.py +++ b/source/gui/settingsDialogs.py @@ -6040,7 +6040,7 @@ def onSave(self): class MagnifierPanel(SettingsPanel): # Translators: This is the label for the magnifier settings panel. title = _("Magnifier") - helpId = "MagnifierSettings" + helpId = "MagnifierSettingsCategory" def _applyCurrentSettingsToConfigAndRuntime(self): """Apply current control values to config and to the active magnifier instance.""" From 3f3b63589a2a98481d75d10989edd546577ada6b Mon Sep 17 00:00:00 2001 From: Cyrille Bougot Date: Tue, 9 Jun 2026 16:01:33 +0200 Subject: [PATCH 23/26] True center tracking anchor --- source/gui/settingsDialogs.py | 2 +- user_docs/en/userGuide.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/gui/settingsDialogs.py b/source/gui/settingsDialogs.py index cf89c372b0b..8a8e01e0e20 100644 --- a/source/gui/settingsDialogs.py +++ b/source/gui/settingsDialogs.py @@ -6151,7 +6151,7 @@ def makeSettings( wx.CheckBox(generalGroupBox, label=trueCenterTrackingText), ) self.bindHelpEvent( - "MagnifierUseTrueCenterTracking", + "MagnifierTrueCenterTracking", self.trueCenterTrackingCheckBox, ) self.trueCenterTrackingCheckBox.SetValue(magnifierConfig.isTrueCentered()) diff --git a/user_docs/en/userGuide.md b/user_docs/en/userGuide.md index 175149cbfc4..55354a4c02b 100644 --- a/user_docs/en/userGuide.md +++ b/user_docs/en/userGuide.md @@ -2913,7 +2913,7 @@ The available options are: | Grayscale | Converts all colors to shades of gray, which can help reduce eye strain and improve contrast. | | Inverted | Inverts all colors on the screen, which can be helpful for users who prefer light text on dark backgrounds or have photophobia. | -##### Use true center tracking {#MagnifierUseTrueCenterTracking} +##### true center tracking {#MagnifierTrueCenterTracking} This checkbox controls whether the magnifier should always keep the tracked position centered on the screen, even when the tracked element is next to the screen edges, or if it should allow the tracked position to move towards the edges of the screen. When enabled, the magnifier will always keep the tracked position centered on the screen, which can be helpful for users who prefer a consistent tracked position within the magnified view. From 07083033065e126c18579fd09202fd37626da012 Mon Sep 17 00:00:00 2001 From: Cyrille Bougot Date: Tue, 9 Jun 2026 16:51:23 +0200 Subject: [PATCH 24/26] Fix Commands Quick Reference generation --- user_docs/en/userGuide.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/user_docs/en/userGuide.md b/user_docs/en/userGuide.md index 55354a4c02b..819302cc0d3 100644 --- a/user_docs/en/userGuide.md +++ b/user_docs/en/userGuide.md @@ -2870,6 +2870,8 @@ All the options of this category have immediate effect on the magnifier but pres This settings category contains the following options: + + ##### Enable Magnifier {#MagnifierEnable} Key: `NVDA+shift+w` @@ -2894,6 +2896,8 @@ You can always adjust the zoom level on the fly using the zoom in (`NVDA+shift+e | Options | 100% to 5000% | | Default | 200% | + + ##### Color filter {#MagnifierFilter} Key: `NVDA+shift+i` From 3df86ddd9d82d8883d3bcddff8cea2643236e16f Mon Sep 17 00:00:00 2001 From: Cyrille Bougot Date: Wed, 10 Jun 2026 08:28:34 +0200 Subject: [PATCH 25/26] Apply suggestions from code review Co-authored-by: Quentin Christensen Co-authored-by: Sean Budd --- user_docs/en/changes.md | 2 +- user_docs/en/userGuide.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/user_docs/en/changes.md b/user_docs/en/changes.md index 1899e370528..211ad66c04e 100644 --- a/user_docs/en/changes.md +++ b/user_docs/en/changes.md @@ -29,7 +29,7 @@ When resetting NVDA to factory defaults, an Undo button is now available to rest * NVDA now includes a built-in Magnifier feature that allows you to zoom and magnify parts of the screen. (#19228, @Boumtchack) * The magnifier supports various zoom levels, color filters (normal, grayscale, inverted), and different focus tracking modes. * Color filters can help users with visual impairments or light sensitivity by inverting or desaturating screen colors. - * A command allows to temporarily show an overview of the entire screen + * A command allows temporarily showing an overview of the entire screen * All magnifier settings can be configured in a new "Magnifier" panel in NVDA Settings. * The magnifier cannot be used simultaneously with Screen Curtain for security reasons. * Speech: diff --git a/user_docs/en/userGuide.md b/user_docs/en/userGuide.md index 819302cc0d3..5cc5e1d18df 100644 --- a/user_docs/en/userGuide.md +++ b/user_docs/en/userGuide.md @@ -1646,7 +1646,7 @@ This is useful when you want to: To display an overview of the entire screen, press `NVDA+shift+l`. The magnifier will then: -1. Smoothly zoom out to show the full screen (zoom level 1.0) +1. Smoothly zoom out to show the full screen (zoom level 1.0x) 2. Monitor mouse movement 3. When the mouse remains still for approximately 2 seconds, automatically zoom back in to the original zoom level at the new mouse position From 444237c257d0555b0020fed53606f13203e955bf Mon Sep 17 00:00:00 2001 From: Cyrille Bougot Date: Wed, 10 Jun 2026 09:22:00 +0200 Subject: [PATCH 26/26] Always use 1. in ordered list --- user_docs/en/userGuide.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/user_docs/en/userGuide.md b/user_docs/en/userGuide.md index 5cc5e1d18df..30162af493e 100644 --- a/user_docs/en/userGuide.md +++ b/user_docs/en/userGuide.md @@ -1647,8 +1647,8 @@ To display an overview of the entire screen, press `NVDA+shift+l`. The magnifier will then: 1. Smoothly zoom out to show the full screen (zoom level 1.0x) -2. Monitor mouse movement -3. When the mouse remains still for approximately 2 seconds, automatically zoom back in to the original zoom level at the new mouse position +1. Monitor mouse movement +1. When the mouse remains still for approximately 2 seconds, automatically zoom back in to the original zoom level at the new mouse position Moving the mouse before the zoom-back occurs gives you more time to view the full screen.