Repository: nvaccess/nvda

Issue:
Direct Jump To Specific Element, Inside Elements List Dialog, and Elements List Enhanscement
**Reported by tmantv on 2010-03-05 05:07**
Currently, while inside the NVDA Elements List dialog, invoked by pressing NVDA-F7, activating any of the hot keys provided for jumping to a particular element's list, E.G. Alt-H for headings, does not move the user directly to the desired location automatically, but instead takes you to the elements radio button, thereby the user needing to press tab to drop in to the list itself.
Reason for automatic focus from one element to another, is so that the person can be more productive, and not have to press the tab key 2 times if they wish to filter a particular list.
If the person wishes to move directly to the list of landmarks manually, they can tab/or shift-tab to the list of radio buttons themselves, if they so choose to do so.

For example, to filter a list of headings with the word you chosen to narrow down to, 
1. Press NVDA-f7 to bring up elements dialog.
2. Press Alt-H to jump directly to the list of headings, landing on the first, or nearest  heading match found on the page, which shows up in the tree.
3. Press alt-f to filter the list. 
4. Press enter or shift-tab to focus the narrowed results found in the list itself.
5. If not desired match, press tab or alt-f to type in a new string, then repeat process til desired.
   This also applies to land marks and links as well.
6. Press Alt-M or enter, to move you directly on to the desired heading, found on  the web page itself.

If made in a future release, have controls on web pages be apart of the elements list, via the Alt-C command, listing all control items that are shown on a web page, which could also be filtered with the alt-f command. 
For example, names of buttons, check boxes, edit boxes, combo boxes, etc. Then press enter or Alt-A to activate, focused control within element's list, or Alt-M to move to, focused control, while in element's list.



Discussion:
Comment 1 by jteh on 2010-03-05 05:32
This ticket actually makes two separate requests: direct jumping to the list and new element types in the list. Please file them as separate tickets in future. Thanks!
Changes:
Milestone changed from 2010.2 to 2010.3

Comment 2 by jteh on 2010-11-24 07:02
Changes:
Milestone changed from 2011.1 to None

I'd be interested in adding more element types to the elements list as well. @jcsteh, is this a feature you guys would accept in a pull request?


Resolving patch:
diff --git a/source/UIABrowseMode.py b/source/UIABrowseMode.py
--- a/source/UIABrowseMode.py
+++ b/source/UIABrowseMode.py
@@ -15,7 +15,6 @@
 import textInfos
 import browseMode
 from NVDAObjects.UIA import UIA
-import aria
 
 class UIATextRangeQuickNavItem(browseMode.TextInfoQuickNavItem):
 
@@ -40,10 +39,7 @@ def obj(self):
 
 	@property
 	def label(self):
-		if self.itemType=="landmark":
-			obj=self.obj
-			return " ".join(x for x in (obj.name,aria.landmarkRoles.get(obj.landmark)) if x)
-		return super(UIATextRangeQuickNavItem,self).label
+		return self._getLabelForProperties(lambda prop: getattr(self.obj, prop, None))
 
 class HeadingUIATextInfoQuickNavItem(browseMode.TextInfoQuickNavItem):
 
diff --git a/source/browseMode.py b/source/browseMode.py
--- a/source/browseMode.py
+++ b/source/browseMode.py
@@ -196,6 +196,51 @@ def isAfterSelection(self):
 		caret=self.document.makeTextInfo(textInfos.POSITION_CARET)
 		return self.textInfo.compareEndPoints(caret, "startToStart") > 0
 
+	def _getLabelForProperties(self, labelPropertyGetter):
+		"""
+		Fetches required properties for this L{TextInfoQuickNavItem} and constructs a label to be shown in an elements list.
+		This can be used by subclasses to implement the L{label} property.
+		@Param labelPropertyGetter: A callable taking 1 argument, specifying the property to fetch.
+			For example, if L{itemType} is landmark, the callable must return the landmark type when "landmark" is passed as the property argument.
+			Alternative property names might be name or value.
+			The callable must return None if the property doesn't exist.
+			An expected callable might be get method on a L{Dict},
+			or "lambda property: getattr(self.obj, property, None)" for an L{NVDAObject}.
+		"""
+		content = self.textInfo.text.strip()
+		if self.itemType is "heading":
+			# Output: displayed text of the heading.
+			return content
+		labelParts = None
+		name = labelPropertyGetter("name")
+		if self.itemType is "landmark":
+			landmark = aria.landmarkRoles.get(labelPropertyGetter("landmark"))
+			# Example output: main menu; navigation
+			labelParts = (name, landmark)
+		else: 
+			role = labelPropertyGetter("role")
+			roleText = controlTypes.roleLabels[role]
+			# Translators: Reported label in the elements list for an element which which has no name and value
+			unlabeled = _("Unlabeled")
+			realStates = labelPropertyGetter("states")
+			positiveStates = " ".join(controlTypes.stateLabels[st] for st in controlTypes.processPositiveStates(role, realStates, controlTypes.REASON_FOCUS, realStates))
+			negativeStates = " ".join(controlTypes.negativeStateLabels[st] for st in controlTypes.processNegativeStates(role, realStates, controlTypes.REASON_FOCUS, realStates))
+			if self.itemType is "formField":
+				if role in (controlTypes.ROLE_BUTTON,controlTypes.ROLE_DROPDOWNBUTTON,controlTypes.ROLE_TOGGLEBUTTON,controlTypes.ROLE_SPLITBUTTON,controlTypes.ROLE_MENUBUTTON,controlTypes.ROLE_DROPDOWNBUTTONGRID,controlTypes.ROLE_SPINBUTTON,controlTypes.ROLE_TREEVIEWBUTTON):
+					# Example output: Mute; toggle button; pressed
+					labelParts = (content or name or unlabeled, roleText, positiveStates, negativeStates)
+				else:
+					# Example output: Find a repository...; edit; has auto complete; NVDA
+					labelParts = (name or unlabeled, roleText, positiveStates, negativeStates, content)
+			elif self.itemType in ("link", "button"):
+				# Example output: You have unread notifications; visited
+				labelParts = (content or name or unlabeled, positiveStates, negativeStates)
+		if labelParts:
+			label = "; ".join(lp for lp in labelParts if lp)
+		else:
+			label = content
+		return label
+
 class BrowseModeTreeInterceptor(treeInterceptorHandler.TreeInterceptor):
 	scriptCategory = inputCore.SCRCAT_BROWSEMODE
 	disableAutoPassThrough = False
@@ -708,6 +753,12 @@ class ElementsListDialog(wx.Dialog):
 		("heading", _("&Headings")),
 		# Translators: The label of a radio button to select the type of element
 		# in the browse mode Elements List dialog.
+		("formField", _("&Form fields")),
+		# Translators: The label of a radio button to select the type of element
+		# in the browse mode Elements List dialog.
+		("button", _("&Buttons")),
+		# Translators: The label of a radio button to select the type of element
+		# in the browse mode Elements List dialog.
 		("landmark", _("Lan&dmarks")),
 	)
 
@@ -778,8 +829,8 @@ def onElementTypeChange(self, evt):
 		self.lastSelectedElementType=elementType
 
 	def initElementType(self, elType):
-		if elType == "link":
-			# Links can be activated.
+		if elType in ("link","button"):
+			# Links and buttons can be activated.
 			self.activateButton.Enable()
 			self.SetAffirmativeId(self.activateButton.GetId())
 		else:
diff --git a/source/virtualBuffers/__init__.py b/source/virtualBuffers/__init__.py
--- a/source/virtualBuffers/__init__.py
+++ b/source/virtualBuffers/__init__.py
@@ -109,14 +109,16 @@ def obj(self):
 
 	@property
 	def label(self):
-		if self.itemType == "landmark":
-			attrs = self.textInfo._getControlFieldAttribs(self.vbufFieldIdentifier[0], self.vbufFieldIdentifier[1])
-			name = attrs.get("name", "")
-			if name:
-				name += " "
-			return name + aria.landmarkRoles[attrs["landmark"]]
-		else:
-			return super(VirtualBufferQuickNavItem,self).label
+		attrs = {}
+
+		def propertyGetter(prop):
+			if not attrs:
+				# Lazily fetch the attributes the first time they're needed.
+				# We do this because we don't want to do this if they're not needed at all.
+				attrs.update(self.textInfo._getControlFieldAttribs(self.vbufFieldIdentifier[0], self.vbufFieldIdentifier[1]))
+			return attrs.get(prop)
+
+		return self._getLabelForProperties(propertyGetter)
 
 	def isChild(self,parent): 
 		if self.itemType == "heading":


Test patch: