Skip to content

Commit d282f4c

Browse files
authored
Merge a79f574 into 1b563fd
2 parents 1b563fd + a79f574 commit d282f4c

4 files changed

Lines changed: 99 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 foreground window handle as a substitute if window handle is not set.
1069+
if not (window := self.getNearestWindowHandle(sender)):
1070+
window = api.getForegroundObject().windowHandle
1071+
if _isDebug():
1072+
log.debugWarning(
1073+
"HandleNotificationEvent: native window handle not found, "
1074+
f"using foreground 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: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,28 @@ def shouldProcessUIAPropertyChangedEvent(self, sender, propertyId):
765765
"""
766766
return True
767767

768+
def shouldProcessUIANotificationEvent(
769+
self,
770+
sender,
771+
NotificationKind: int | None = None,
772+
NotificationProcessing: int | None = None,
773+
displayString: str = "",
774+
activityId: str = "",
775+
) -> bool:
776+
"""
777+
Determines whether NVDA should process a UIA notification event
778+
By default, events from elements with window handle value set
779+
and traversable back to the desktop will be accepted.
780+
Returning False will cause the event to be dropped completely.
781+
Returning True means that the event will be processed, but it might still
782+
be rejected later; e.g. because it isn't native UIA, because
783+
shouldAcceptEvent returns False, etc.
784+
"""
785+
import UIAHandler
786+
787+
# By default, see if UIA tree can be traversed to arrive at the desktop element.
788+
return bool(UIAHandler.handler.getNearestWindowHandle(sender))
789+
768790
def dumpOnCrash(self):
769791
"""Request that this process writes a minidump when it crashes for debugging.
770792
This should only be called if instructed by a developer.

source/appModules/explorer.py

Lines changed: 36 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,6 +26,7 @@
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
3031

3132

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

1112
### Changes
1213

0 commit comments

Comments
 (0)