Skip to content

Commit 4465872

Browse files
authored
Merge 4f94fc0 into 7776fc0
2 parents 7776fc0 + 4f94fc0 commit 4465872

4 files changed

Lines changed: 109 additions & 10 deletions

File tree

source/UIAHandler/__init__.py

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1021,12 +1021,12 @@ def IUIAutomationPropertyChangedEventHandler_HandlePropertyChangedEvent(
10211021

10221022
def IUIAutomationNotificationEventHandler_HandleNotificationEvent(
10231023
self,
1024-
sender,
1025-
NotificationKind,
1026-
NotificationProcessing,
1027-
displayString,
1028-
activityId,
1029-
):
1024+
sender: UIA.IUIAutomationElement,
1025+
NotificationKind: int,
1026+
NotificationProcessing: int,
1027+
displayString: str,
1028+
activityId: str,
1029+
) -> None:
10301030
if _isDebug():
10311031
log.debug(
10321032
"handleNotificationEvent called "
@@ -1041,10 +1041,42 @@ def IUIAutomationNotificationEventHandler_HandleNotificationEvent(
10411041
if _isDebug():
10421042
log.debug("HandleNotificationEvent: event received while not fully initialized")
10431043
return
1044+
# Sometimes notification events can be fired on a UIAElement that has no windowHandle
1045+
# and does not connect through parents back to the desktop.
1046+
# #17841: yet messages such as window restored/maximized coming from File Explorer (Windows shell)
1047+
# should be announced from everywhere (applicable on Windows 11 24H2 and later).
1048+
# Therefore, ask app modules if notifications (including from these elements) should be processed.
1049+
try:
1050+
processId = sender.CachedProcessID
1051+
except COMError:
1052+
pass
1053+
else:
1054+
appMod = appModuleHandler.getAppModuleFromProcessID(processId)
1055+
if not appMod.shouldProcessUIANotificationEvent(
1056+
sender,
1057+
NotificationKind=NotificationKind,
1058+
NotificationProcessing=NotificationProcessing,
1059+
displayString=displayString,
1060+
activityId=activityId,
1061+
):
1062+
if _isDebug():
1063+
log.debugWarning(
1064+
"HandleNotificationEvent: dropping notification event "
1065+
f"at request of appModule {appMod.appName}",
1066+
)
1067+
return
1068+
# Take desktop window handle as a substitute if window handle is not set.
1069+
if not (window := self.getNearestWindowHandle(sender)):
1070+
window = api.getDesktopObject().windowHandle
1071+
if _isDebug():
1072+
log.debugWarning(
1073+
"HandleNotificationEvent: native window handle not found, "
1074+
f"using desktop window handle {window}",
1075+
)
10441076
import NVDAObjects.UIA
10451077

10461078
try:
1047-
obj = NVDAObjects.UIA.UIA(UIAElement=sender)
1079+
obj = NVDAObjects.UIA.UIA(UIAElement=sender, windowHandle=window)
10481080
except Exception:
10491081
if _isDebug():
10501082
log.debugWarning(
@@ -1056,8 +1088,7 @@ def IUIAutomationNotificationEventHandler_HandleNotificationEvent(
10561088
)
10571089
return
10581090
if not obj:
1059-
# Sometimes notification events can be fired on a UIAElement that has no windowHandle and does not connect through parents back to the desktop.
1060-
# There is nothing we can do with these.
1091+
# Sometimes UIA object can be None despite setting window handle to something else.
10611092
if _isDebug():
10621093
log.debug(
10631094
"HandleNotificationEvent: Ignoring because no object: "

source/appModuleHandler.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
from fileUtils import getFileVersionInfo
4545
import globalVars
4646
from systemUtils import getCurrentProcessLogonSessionId, getProcessLogonSessionId
47+
from comInterfaces import UIAutomationClient as UIA
4748

4849

4950
# Dictionary of processID:appModule pairs used to hold the currently running modules
@@ -765,6 +766,36 @@ def shouldProcessUIAPropertyChangedEvent(self, sender, propertyId):
765766
"""
766767
return True
767768

769+
def shouldProcessUIANotificationEvent(
770+
self,
771+
sender: UIA.IUIAutomationElement,
772+
NotificationKind: int | None = None,
773+
NotificationProcessing: int | None = None,
774+
displayString: str = "",
775+
activityId: str = "",
776+
) -> bool:
777+
"""
778+
Determines whether NVDA should process a UIA notification event.
779+
780+
By default, events from elements with window handle value set
781+
and traversable back to the desktop will be accepted.
782+
Returning ``False`` will cause the event to be dropped completely.
783+
Returning ``True`` means that the event will be processed, but it might still
784+
be rejected later; e.g. because it isn't native UIA, because
785+
shouldAcceptEvent returns False, etc.
786+
787+
:param sender: UIA element raising the notification event.
788+
:param NotificationKind: notification kind such as activity completion.
789+
:param NotificationProcessing: how NVDA should process notifications such as canceling speech.
790+
:param displayString: notification content/text.
791+
:param activityId: notification description.
792+
:return: Whether NVDA components including ap modules and NVDA objects should process notification events.
793+
"""
794+
import UIAHandler
795+
796+
# By default, see if UIA tree can be traversed to arrive at the desktop element.
797+
return bool(UIAHandler.handler.getNearestWindowHandle(sender))
798+
768799
def dumpOnCrash(self):
769800
"""Request that this process writes a minidump when it crashes for debugging.
770801
This should only be called if instructed by a developer.

source/appModules/explorer.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# -*- coding: UTF-8 -*-
22
# A part of NonVisual Desktop Access (NVDA)
3-
# Copyright (C) 2006-2023 NV Access Limited, Joseph Lee, Łukasz Golonka, Julien Cochuyt
3+
# Copyright (C) 2006-2025 NV Access Limited, Joseph Lee, Łukasz Golonka, Julien Cochuyt
44
# This file is covered by the GNU General Public License.
55
# See the file COPYING for more details.
66

@@ -26,7 +26,9 @@
2626
from NVDAObjects.behaviors import ToolTip
2727
from NVDAObjects.window.edit import RichEdit50, Edit
2828
import config
29+
import ui
2930
from winAPI.types import HWNDValT
31+
from comInterfaces import UIAutomationClient
3032

3133

3234
# Suppress incorrect Win 10 Task switching window focus
@@ -560,3 +562,37 @@ def event_UIA_elementSelected(self, obj: NVDAObject, nextHandler: Callable[[], N
560562
),
561563
)
562564
nextHandler()
565+
566+
def shouldProcessUIANotificationEvent(
567+
self,
568+
sender: UIAutomationClient.IUIAutomationElement,
569+
NotificationKind: int | None = None,
570+
NotificationProcessing: int | None = None,
571+
displayString: str = "",
572+
activityId: str = "",
573+
) -> bool:
574+
if activityId == "Windows.Shell.SnapComponent.SnapHotKeyResults":
575+
return True
576+
return super().shouldProcessUIANotificationEvent(
577+
sender,
578+
NotificationKind=NotificationKind,
579+
NotificationProcessing=NotificationProcessing,
580+
displayString=displayString,
581+
activityId=activityId,
582+
)
583+
584+
def event_UIA_notification(
585+
self,
586+
obj: NVDAObject,
587+
nextHandler: Callable[[], None],
588+
NotificationKind: int | None = None,
589+
NotificationProcessing: int | None = None,
590+
displayString: str = "",
591+
activityId: str = "",
592+
) -> None:
593+
# #17841: announce window states across apps (Windows 11 24H2 and later).
594+
# These messages come from a File Explorer (shell) element and there is no native window handle.
595+
if activityId == "Windows.Shell.SnapComponent.SnapHotKeyResults":
596+
ui.message(displayString)
597+
return
598+
nextHandler()

user_docs/en/changes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
### New Features
88

99
* In Microsoft Word, the new view is now reported when using the shortcuts to switch to page view (`control+alt+p`) or outline view (`control+alt+o`). (#18091, @CyrilleB79)
10+
* In Windows 11 2024 Update and Server 2025 and later, NVDA will announce foreground window states such as restore, maximize, and snap when changing window position by pressing `windows+arrow` keys. (#17841, #18175, @josephsl)
1011
* Unassigned commands have been added to open the NVDA settings dialog in the following categories: Vision, Windows OCR, Add-on Store and Advanced. (#18313, @CyrilleB79)
1112

1213
### Changes

0 commit comments

Comments
 (0)