diff --git a/source/UIABrowseMode.py b/source/UIABrowseMode.py index 1565acb389d..7fbf41cf88e 100644 --- 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 index 182b52c1152..bbba0980832 100644 --- 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 index 9506046ac1a..8f1c7cd74e0 100644 --- 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": diff --git a/user_docs/en/userGuide.t2t b/user_docs/en/userGuide.t2t index 0197a548cba..1060a7536b2 100644 --- a/user_docs/en/userGuide.t2t +++ b/user_docs/en/userGuide.t2t @@ -525,7 +525,7 @@ To toggle single letter navigation on and off for the current document, press NV ++ The Elements List ++ The elements list provides access to a list of various types of elements in the document as appropriate for the application. -For example, in web browsers, the elements list can list links, headings or landmarks. +For example, in web browsers, the elements list can list links, headings, form fields, buttons or landmarks. Radio buttons allow you to switch between the different types of elements. An edit field is also provided in the dialog which allows you to filter the list to help you search for a particular item on the page. Once you have chosen an item, you can use the provided buttons in the dialog to move to or activate that item.