Repository: nvaccess/nvda

Issue:
Win11: NVDA is not announcing the available suggestions for “Find a setting” search field in settings page  
### Steps to reproduce:
1. Open Settings with Win+I
2. Tab to search field, type "test"
3. Search results are displayed

### Actual behavior:
NVDA does not read that suggestions are available and does not read first item

### Expected behavior:
NVDA should announce that suggestions are available and read the first suggestion
Note: both JAWS and Narrator work as expected.

### System configuration
#### NVDA installed/portable/running from source:
Installed/portable both repro the same issue

#### NVDA version:
2021.1

#### Windows version:
Windows 11 Insider builds

#### Name and version of other software in use when reproducing the issue:
N/A

#### Other information about your system:

### Other questions
#### Does the issue still occur after restarting your computer?
Yes

#### Have you tried any other versions of NVDA? If so, please report their behaviors.
No. Both Narrator and JAWS work as expected.
#### If add-ons are disabled, is your problem still occurring?
Yes

#### Does the issue still occur after you run the COM Registration Fixing Tool in NVDA's tools menu?
Yes


Discussion:
Hi, Windows App Essentials add-on should resolve this. Thanks.


Hi,

Note that Settings app suggestions list is similar to Start suggestions list but it is not - it requires NVDA to listen to UIA layout invalidated event for proper functionality. I am willing to donate this part of Windows App Essentials add-on to NV Access provided that we come up with a baseline user experience notes.

Thanks.
Hi,

An important point to consider: Narrator does not announce the top suggestion - it will announce suggestion count followed by "suggestion". My suggestion about using Windows App Essentials add-on to test this still stands.

Thanks.
Hi,

Another important point to consider: this issue is not limited to Windows 11 - it also happens with Windows 10 and it happens for suggestions list that raises UIA layout invalidated event.

I think there is an issue concerning this problem (if found, this issue will be tagged and might be closed as a duplicate).

Thanks.
Hi,

And found it: #7330 - I may need to raise a summary issue for these two at some point - implementation is ready to go, as what's needed to be done is separate the responsible component from Windows App Essentials add-on and transplant it inside UIA objects mix-in.

Thanks.

Resolving patch:
diff --git a/source/NVDAObjects/UIA/__init__.py b/source/NVDAObjects/UIA/__init__.py
--- a/source/NVDAObjects/UIA/__init__.py
+++ b/source/NVDAObjects/UIA/__init__.py
@@ -1013,6 +1013,12 @@ def findOverlayClasses(self,clsList):
 				clsList.append(SearchField)
 		except COMError:
 			log.debug("Failed to locate UIA search field", exc_info=True)
+		# #12790: detect suggestions list views firing layout invalidated event.
+		try:
+			if UIAAutomationId == "SuggestionsList":
+				clsList.append(SuggestionsList)
+		except COMError:
+			log.debug("Could not detect suggestions list", exc_info=True)
 		try:
 			# Nested block here in order to catch value error and variable binding error when attempting to access automation ID for invalid elements.
 			try:
@@ -2062,15 +2068,40 @@ def event_UIA_controllerFor(self):
 			self.event_suggestionsClosed()
 
 
+class SuggestionsList(UIA):
+	"""A list of suggestions in response to search terms being entered.
+	This list shows suggestions without selecting the top suggestion.
+	Examples include suggestions lists in modern apps such as Settings app in Windows 10 and later.
+	"""
+
+	def event_UIA_layoutInvalidated(self):
+		# #12790: announce number of items found
+		if self.childCount == 0:
+			return
+		# In some cases, suggestions list fires layout invalidated event repeatedly.
+		# This is the case with Microsoft Store's search field.
+		speech.cancelSpeech()
+		# Item count must be the last one spoken.
+		suggestionsCount: int = self.childCount
+		suggestionsMessage = (
+			# Translators: part of the suggestions count message for one suggestion.
+			_("1 suggestion")
+			# Translators: part of the suggestions count message (for example: 2 suggestions).
+			if suggestionsCount == 1 else _("{} suggestions").format(suggestionsCount)
+		)
+		ui.message(suggestionsMessage)
+
+
 class SuggestionListItem(UIA):
 	"""Recent Windows releases use suggestions lists for various things, including Start menu suggestions, Store, Settings app and so on.
+	Unlike suggestions list class, top suggestion is automatically selected.
 	"""
 
-	role=controlTypes.Role.LISTITEM
+	role = controlTypes.Role.LISTITEM
 
 	def event_UIA_elementSelected(self):
-		focusControllerFor=api.getFocusObject().controllerFor
-		if len(focusControllerFor)>0 and focusControllerFor[0].appModule is self.appModule and self.name:
+		focusControllerFor = api.getFocusObject().controllerFor
+		if len(focusControllerFor) > 0 and focusControllerFor[0].appModule is self.appModule and self.name:
 			speech.cancelSpeech()
 			api.setNavigatorObject(self, isFocus=True)
 			self.reportFocus()
diff --git a/source/_UIAHandler.py b/source/_UIAHandler.py
--- a/source/_UIAHandler.py
+++ b/source/_UIAHandler.py
@@ -177,18 +177,19 @@
 	UIA.UIA_SearchLandmarkTypeId: "search",
 }
 
-UIAEventIdsToNVDAEventNames={
-	UIA_LiveRegionChangedEventId:"liveRegionChange",
-	UIA_SelectionItem_ElementSelectedEventId:"UIA_elementSelected",
-	UIA_MenuOpenedEventId:"gainFocus",
-	UIA_SelectionItem_ElementAddedToSelectionEventId:"stateChange",
-	UIA_SelectionItem_ElementRemovedFromSelectionEventId:"stateChange",
+UIAEventIdsToNVDAEventNames: Dict[int, str] = {
+	UIA.UIA_LiveRegionChangedEventId: "liveRegionChange",
+	UIA.UIA_SelectionItem_ElementSelectedEventId: "UIA_elementSelected",
+	UIA.UIA_MenuOpenedEventId: "gainFocus",
+	UIA.UIA_SelectionItem_ElementAddedToSelectionEventId: "stateChange",
+	UIA.UIA_SelectionItem_ElementRemovedFromSelectionEventId: "stateChange",
 	#UIA_MenuModeEndEventId:"menuModeEnd",
-	UIA_ToolTipOpenedEventId:"UIA_toolTipOpened",
+	UIA.UIA_ToolTipOpenedEventId: "UIA_toolTipOpened",
 	#UIA_AsyncContentLoadedEventId:"documentLoadComplete",
 	#UIA_ToolTipClosedEventId:"hide",
-	UIA_Window_WindowOpenedEventId:"UIA_window_windowOpen",
-	UIA_SystemAlertEventId:"UIA_systemAlert",
+	UIA.UIA_Window_WindowOpenedEventId: "UIA_window_windowOpen",
+	UIA.UIA_SystemAlertEventId: "UIA_systemAlert",
+	UIA.UIA_LayoutInvalidatedEventId: "UIA_layoutInvalidated",
 }
 
 localEventHandlerGroupUIAEventIds = set()


Test patch: