From 04fc5c05bae18700514df8c4f852861c91d02cfa Mon Sep 17 00:00:00 2001 From: Reef Turner Date: Fri, 23 Oct 2020 17:18:17 +0200 Subject: [PATCH 01/12] Fix misleading log message for aria-current="true" Types and naming for isCurrent were confusing, so normalize the types using an enum and only name as ariaCurrent when the data is specific to aria-current, otherwise use 'isCurrent'. Include comments to link aria-current as the potential (likely) source for 'isCurrent' --- source/NVDAObjects/IAccessible/MSHTML.py | 8 +++-- source/NVDAObjects/IAccessible/ia2Web.py | 12 ++++--- source/NVDAObjects/UIA/edge.py | 15 +++++---- source/NVDAObjects/__init__.py | 7 ++-- source/braille.py | 12 +++---- source/controlTypes.py | 43 +++++++++++++++++++----- source/speech/__init__.py | 29 +++++++--------- source/virtualBuffers/MSHTML.py | 14 ++++++-- source/virtualBuffers/gecko_ia2.py | 11 ++++-- 9 files changed, 95 insertions(+), 56 deletions(-) diff --git a/source/NVDAObjects/IAccessible/MSHTML.py b/source/NVDAObjects/IAccessible/MSHTML.py index 0990b2ae766..7f8aa1963a4 100644 --- a/source/NVDAObjects/IAccessible/MSHTML.py +++ b/source/NVDAObjects/IAccessible/MSHTML.py @@ -545,9 +545,11 @@ def _get_treeInterceptorClass(self): def _get_isCurrent(self): isCurrent = self.HTMLAttributes["aria-current"] - if isCurrent == "false": - isCurrent = None - return isCurrent + try: + return controlTypes.IS_CURRENT(isCurrent) + except ValueError: + log.debugWarning(f"Unknown aria-current value: {isCurrent}") + return controlTypes.IS_CURRENT.NO def _get_HTMLAttributes(self): return HTMLAttribCache(self.HTMLNode) diff --git a/source/NVDAObjects/IAccessible/ia2Web.py b/source/NVDAObjects/IAccessible/ia2Web.py index 73e7f1588d5..956979fa8b1 100644 --- a/source/NVDAObjects/IAccessible/ia2Web.py +++ b/source/NVDAObjects/IAccessible/ia2Web.py @@ -34,11 +34,13 @@ def _get_positionInfo(self): info['level']=level return info - def _get_isCurrent(self): - current = self.IA2Attributes.get("current", None) - if current == "false": - current = None - return current + def _get_isCurrent(self) -> controlTypes.IS_CURRENT: + ia2attrCurrent:str = self.IA2Attributes.get("current", "false") + try: + return controlTypes.IS_CURRENT(ia2attrCurrent) + except ValueError: + log.debugWarning(f"Unknown 'current' IA2Attribute value: {ia2attrCurrent}") + return controlTypes.IS_CURRENT.NO def _get_placeholder(self): placeholder = self.IA2Attributes.get('placeholder', None) diff --git a/source/NVDAObjects/UIA/edge.py b/source/NVDAObjects/UIA/edge.py index cbd84185868..f1f1532a6ad 100644 --- a/source/NVDAObjects/UIA/edge.py +++ b/source/NVDAObjects/UIA/edge.py @@ -156,7 +156,7 @@ def _getControlFieldForObject(self,obj,isEmbedded=False,startOfNode=False,endOfN if obj.role==controlTypes.ROLE_COMBOBOX and obj.UIATextPattern: field['states'].add(controlTypes.STATE_EDITABLE) # report if the field is 'current' - field['current']=obj.isCurrent + field['current'] = obj.isCurrent if obj.placeholder and obj._isTextEmpty: field['placeholder']=obj.placeholder # For certain controls, if ARIA overrides the label, then force the field's content (value) to the label @@ -470,15 +470,18 @@ def _get_ariaProperties(self): # "false" is ignored by the regEx and will not produce a match RE_ARIA_CURRENT_PROP_VALUE = re.compile("current=(?!false)(\w+);") - def _get_isCurrent(self): + def _get_isCurrent(self) -> controlTypes.IS_CURRENT: ariaProperties=self._getUIACacheablePropertyValue(UIAHandler.UIA_AriaPropertiesPropertyId) match = self.RE_ARIA_CURRENT_PROP_VALUE.search(ariaProperties) - log.debug("aria props = %s" % ariaProperties) if match: valueOfAriaCurrent = match.group(1) - log.debug("aria current value = %s" % valueOfAriaCurrent) - return valueOfAriaCurrent - return None + try: + return controlTypes.IS_CURRENT(valueOfAriaCurrent) + except ValueError: + log.debugWarning( + f"Unknown aria-current value: {valueOfAriaCurrent}, ariaProperties: {ariaProperties}" + ) + return controlTypes.IS_CURRENT.NO def _get_roleText(self): roleText = self.ariaProperties.get('roledescription', None) diff --git a/source/NVDAObjects/__init__.py b/source/NVDAObjects/__init__.py index 47021689e36..eb0df5af379 100644 --- a/source/NVDAObjects/__init__.py +++ b/source/NVDAObjects/__init__.py @@ -975,12 +975,11 @@ def _get_statusBar(self): """ return None - def _get_isCurrent(self): + def _get_isCurrent(self) -> controlTypes.IS_CURRENT: """Gets the value that indicates whether this object is the current element in a set of related - elements. This maps to aria-current. Normally returns None. If this object is current - it will return one of the following values: "true", "page", "step", "location", "date", "time" + elements. This maps to aria-current. """ - return None + return controlTypes.IS_CURRENT.NO def _get_shouldAcceptShowHideCaretEvent(self): """Some objects/applications send show/hide caret events when we don't expect it, such as when the cursor is blinking. diff --git a/source/braille.py b/source/braille.py index 6f7615979c7..f0c94f09495 100644 --- a/source/braille.py +++ b/source/braille.py @@ -571,13 +571,9 @@ def getPropertiesBraille(**propertyValues) -> str: # noqa: C901 # %s is replaced with the column number. columnStr = _("c{columnNumber}").format(columnNumber=columnNumber) textList.append(columnStr) - current = propertyValues.get('current', False) - if current: - try: - textList.append(controlTypes.isCurrentLabels[current]) - except KeyError: - log.debugWarning("Aria-current value not handled: %s"%current) - textList.append(controlTypes.isCurrentLabels[True]) + isCurrent = propertyValues.get('current', controlTypes.IS_CURRENT.NO) + if isCurrent != controlTypes.IS_CURRENT.NO: + textList.append(controlTypes.IS_CURRENT.getTranslationForUI(isCurrent)) placeholder = propertyValues.get('placeholder', None) if placeholder: textList.append(placeholder) @@ -662,7 +658,7 @@ def getControlFieldBraille(info, field, ancestors, reportStart, formatConfig): states = field.get("states", set()) value=field.get('value',None) - current=field.get('current', None) + current = field.get('current', controlTypes.IS_CURRENT.NO) placeholder=field.get('placeholder', None) roleText = field.get('roleTextBraille', field.get('roleText')) landmark = field.get("landmark") diff --git a/source/controlTypes.py b/source/controlTypes.py index d74e8b73636..58723eaa11f 100644 --- a/source/controlTypes.py +++ b/source/controlTypes.py @@ -6,6 +6,8 @@ from typing import Dict, Union, Set, Any, Optional, List from enum import Enum, auto +from logHandler import log + ROLE_UNKNOWN=0 ROLE_WINDOW=1 ROLE_TITLEBAR=2 @@ -662,21 +664,46 @@ class OutputReason(Enum): REASON_CARET = OutputReason.CARET REASON_ONLYCACHE = OutputReason.ONLYCACHE + +class IS_CURRENT(Enum): + """Values to use within NVDA to denote 'current' values. + These describe if an item is the current item within a particular kind of selection. + EG aria-current + """ + NO = "false" + Yes = "true" + PAGE = "page" + STEP = "step" + LOCATION = "location" + DATE = "date" + TIME = "time" + + def getTranslationForUI(self): + try: + return isCurrentLabels[self] + except KeyError: + log.debugWarning(f"No translation mapping for: {self}") + # there is a value for 'current' but NVDA hasn't learned about it yet, + # at least describe in the general sense that this item is 'current' + return isCurrentLabels[IS_CURRENT.Yes] + + #: Text to use for 'current' values. These describe if an item is the current item -#: within a particular kind of selection. -isCurrentLabels: Dict[Union[bool, str], str] = { +#: within a particular kind of selection. EG aria-current +isCurrentLabels: Dict[IS_CURRENT, str] = { + IS_CURRENT.NO: "", # There is nothing extra to say for items that are not current. # Translators: Presented when an item is marked as current in a collection of items - True:_("current"), + IS_CURRENT.Yes: _("current"), # Translators: Presented when a page item is marked as current in a collection of page items - "page":_("current page"), + IS_CURRENT.PAGE: _("current page"), # Translators: Presented when a step item is marked as current in a collection of step items - "step":_("current step"), + IS_CURRENT.STEP: _("current step"), # Translators: Presented when a location item is marked as current in a collection of location items - "location":_("current location"), + IS_CURRENT.LOCATION: _("current location"), # Translators: Presented when a date item is marked as current in a collection of date items - "date":_("current date"), + IS_CURRENT.DATE: _("current date"), # Translators: Presented when a time item is marked as current in a collection of time items - "time":_("current time"), + IS_CURRENT.TIME: _("current time"), } diff --git a/source/speech/__init__.py b/source/speech/__init__.py index 17234044e77..f1a95072038 100755 --- a/source/speech/__init__.py +++ b/source/speech/__init__.py @@ -362,7 +362,7 @@ def getObjectPropertiesSpeech( # noqa: C901 positionInfo=obj.positionInfo elif value and name == "current": # getPropertiesSpeech names this "current", but the NVDAObject property is - # named "isCurrent". + # named "isCurrent", it's type should always be controltypes.IS_CURRENT newPropertyValues['current'] = obj.isCurrent elif value: # Certain properties such as row and column numbers have presentational versions, which should be used for speech if they are available. @@ -1606,15 +1606,12 @@ def getPropertiesSpeech( # noqa: C901 if rowCount or columnCount: # The caller is entering a table, so ensure that it is treated as a new table, even if the previous table was the same. oldTableID = None - ariaCurrent = propertyValues.get('current', False) - if ariaCurrent: - try: - ariaCurrentLabel = controlTypes.isCurrentLabels[ariaCurrent] - textList.append(ariaCurrentLabel) - except KeyError: - log.debugWarning("Aria-current value not handled: %s"%ariaCurrent) - ariaCurrentLabel = controlTypes.isCurrentLabels[True] - textList.append(ariaCurrentLabel) + + # speak isCurrent property EG aria-current + isCurrent = propertyValues.get('current', controlTypes.IS_CURRENT.NO) + if isCurrent != controlTypes.IS_CURRENT.NO: + textList.append(controlTypes.IS_CURRENT.getTranslationForUI(isCurrent)) + placeholder: Optional[str] = propertyValues.get('placeholder', None) if placeholder: textList.append(placeholder) @@ -1677,7 +1674,7 @@ def getControlFieldSpeech( # noqa: C901 name = "" states=attrs.get('states',set()) keyboardShortcut=attrs.get('keyboardShortcut', "") - ariaCurrent=attrs.get('current', None) + isCurrent = attrs.get('current', controlTypes.IS_CURRENT.NO) placeholderValue=attrs.get('placeholder', None) value=attrs.get('value',"") if reason==controlTypes.REASON_FOCUS or attrs.get('alwaysReportDescription',False): @@ -1707,7 +1704,7 @@ def getControlFieldSpeech( # noqa: C901 keyboardShortcutSequence = getPropertiesSpeech( reason=reason, keyboardShortcut=keyboardShortcut ) - ariaCurrentSequence = getPropertiesSpeech(reason=reason, current=ariaCurrent) + isCurrentSequence = getPropertiesSpeech(reason=reason, current=isCurrent) placeholderSequence = getPropertiesSpeech(reason=reason, placeholder=placeholderValue) nameSequence = getPropertiesSpeech(reason=reason, name=name) valueSequence = getPropertiesSpeech(reason=reason, value=value) @@ -1824,7 +1821,7 @@ def getControlFieldSpeech( # noqa: C901 getProps['columnHeaderText'] = attrs.get("table-columnheadertext") tableCellSequence = getPropertiesSpeech(_tableID=tableID, **getProps) tableCellSequence.extend(stateTextSequence) - tableCellSequence.extend(ariaCurrentSequence) + tableCellSequence.extend(isCurrentSequence) types.logBadSequenceTypes(tableCellSequence) return tableCellSequence @@ -1873,7 +1870,7 @@ def getControlFieldSpeech( # noqa: C901 out.extend(stateTextSequence if speakStatesFirst else roleTextSequence) out.extend(roleTextSequence if speakStatesFirst else stateTextSequence) out.append(containerContainsText) - out.extend(ariaCurrentSequence) + out.extend(isCurrentSequence) out.extend(valueSequence) out.extend(descriptionSequence) out.extend(levelSequence) @@ -1908,8 +1905,8 @@ def getControlFieldSpeech( # noqa: C901 # Special cases elif not speakEntry and fieldType in ("start_addedToControlFieldStack","start_relative"): out = [] - if ariaCurrent: - out.extend(ariaCurrentSequence) + if isCurrent != controlTypes.IS_CURRENT.NO: + out.extend(isCurrentSequence) # Speak expanded / collapsed / level for treeview items (in ARIA treegrids) if role == controlTypes.ROLE_TREEVIEWITEM: if controlTypes.STATE_EXPANDED in states: diff --git a/source/virtualBuffers/MSHTML.py b/source/virtualBuffers/MSHTML.py index 204a92f9705..d4388c7e16f 100644 --- a/source/virtualBuffers/MSHTML.py +++ b/source/virtualBuffers/MSHTML.py @@ -48,9 +48,17 @@ def _normalizeFormatField(self, attrs): def _normalizeControlField(self,attrs): level=None - ariaCurrent = attrs.get('HTMLAttrib::aria-current', None) - if ariaCurrent not in (None, "false"): - attrs['current']=ariaCurrent + + ariaCurrentValue = attrs.get('HTMLAttrib::aria-current', 'false') + try: + ariaCurrent = controlTypes.IS_CURRENT(ariaCurrentValue) + except ValueError: + log.debugWarning(f"Unknown aria-current value: {ariaCurrentValue}") + ariaCurrent = controlTypes.IS_CURRENT.NO + + if ariaCurrent != controlTypes.IS_CURRENT.NO: + attrs['current'] = ariaCurrent + placeholder = self._getPlaceholderAttribute(attrs, 'HTMLAttrib::aria-placeholder') if placeholder: attrs['placeholder']=placeholder diff --git a/source/virtualBuffers/gecko_ia2.py b/source/virtualBuffers/gecko_ia2.py index c2ac7be94c3..3b5c6ab2ecc 100755 --- a/source/virtualBuffers/gecko_ia2.py +++ b/source/virtualBuffers/gecko_ia2.py @@ -52,9 +52,14 @@ def _normalizeControlField(self,attrs): if attrVal is not None: attrs[attr]=int(attrVal) - current = attrs.get("IAccessible2::attribute_current") - if current not in (None, 'false'): - attrs['current']= current + valForCurrent = attrs.get("IAccessible2::attribute_current", "false") + try: + isCurrent = controlTypes.IS_CURRENT(valForCurrent) + except ValueError: + log.debugWarning(f"Unknown isCurrent value: {valForCurrent}") + isCurrent = controlTypes.IS_CURRENT.NO + if isCurrent != controlTypes.IS_CURRENT.NO: + attrs['current'] = isCurrent placeholder = self._getPlaceholderAttribute(attrs, "IAccessible2::attribute_placeholder") if placeholder is not None: attrs['placeholder']= placeholder From d329e3ed259452c0cd8986d0e1a336c89dc6676a Mon Sep 17 00:00:00 2001 From: Reef Turner Date: Fri, 23 Oct 2020 19:38:05 +0200 Subject: [PATCH 02/12] Add type information for auto property --- source/NVDAObjects/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/NVDAObjects/__init__.py b/source/NVDAObjects/__init__.py index eb0df5af379..01bd265874d 100644 --- a/source/NVDAObjects/__init__.py +++ b/source/NVDAObjects/__init__.py @@ -975,6 +975,8 @@ def _get_statusBar(self): """ return None + isCurrent: controlTypes.IS_CURRENT #: type info for auto property _get_isCurrent + def _get_isCurrent(self) -> controlTypes.IS_CURRENT: """Gets the value that indicates whether this object is the current element in a set of related elements. This maps to aria-current. From cb4870b3acbf11e493b2ceea7e1bb28befec59f6 Mon Sep 17 00:00:00 2001 From: Reef Turner Date: Mon, 26 Oct 2020 11:12:50 +0100 Subject: [PATCH 03/12] fix lint --- source/NVDAObjects/IAccessible/ia2Web.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/NVDAObjects/IAccessible/ia2Web.py b/source/NVDAObjects/IAccessible/ia2Web.py index 956979fa8b1..22ecb55e192 100644 --- a/source/NVDAObjects/IAccessible/ia2Web.py +++ b/source/NVDAObjects/IAccessible/ia2Web.py @@ -35,7 +35,7 @@ def _get_positionInfo(self): return info def _get_isCurrent(self) -> controlTypes.IS_CURRENT: - ia2attrCurrent:str = self.IA2Attributes.get("current", "false") + ia2attrCurrent: str = self.IA2Attributes.get("current", "false") try: return controlTypes.IS_CURRENT(ia2attrCurrent) except ValueError: From 41a8f11aa4b15ce76f031156483614c203cf420c Mon Sep 17 00:00:00 2001 From: Reef Turner Date: Tue, 24 Nov 2020 10:41:35 +0800 Subject: [PATCH 04/12] Make isCurrent enum to translation private --- source/controlTypes.py | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/source/controlTypes.py b/source/controlTypes.py index 58723eaa11f..6dff49a7893 100644 --- a/source/controlTypes.py +++ b/source/controlTypes.py @@ -680,31 +680,32 @@ class IS_CURRENT(Enum): def getTranslationForUI(self): try: - return isCurrentLabels[self] + return self._isCurrentLabels[self] except KeyError: log.debugWarning(f"No translation mapping for: {self}") # there is a value for 'current' but NVDA hasn't learned about it yet, # at least describe in the general sense that this item is 'current' - return isCurrentLabels[IS_CURRENT.Yes] + return self._isCurrentLabels[IS_CURRENT.Yes] - -#: Text to use for 'current' values. These describe if an item is the current item -#: within a particular kind of selection. EG aria-current -isCurrentLabels: Dict[IS_CURRENT, str] = { - IS_CURRENT.NO: "", # There is nothing extra to say for items that are not current. - # Translators: Presented when an item is marked as current in a collection of items - IS_CURRENT.Yes: _("current"), - # Translators: Presented when a page item is marked as current in a collection of page items - IS_CURRENT.PAGE: _("current page"), - # Translators: Presented when a step item is marked as current in a collection of step items - IS_CURRENT.STEP: _("current step"), - # Translators: Presented when a location item is marked as current in a collection of location items - IS_CURRENT.LOCATION: _("current location"), - # Translators: Presented when a date item is marked as current in a collection of date items - IS_CURRENT.DATE: _("current date"), - # Translators: Presented when a time item is marked as current in a collection of time items - IS_CURRENT.TIME: _("current time"), -} + #: Text to use for 'current' values. These describe if an item is the current item + #: within a particular kind of selection. EG aria-current + _isCurrentLabels: Dict[Enum, str] = { + NO: "", # There is nothing extra to say for items that are not current. + # Translators: Presented when an item is marked as current in a collection of items + Yes: _("current"), + # Translators: Presented when a page item is marked as current in a collection of page items + PAGE: _("current page"), + # Translators: Presented when a step item is marked as current in a collection of + # step items + STEP: _("current step"), + # Translators: Presented when a location item is marked as current in a collection of + # location items + LOCATION: _("current location"), + # Translators: Presented when a date item is marked as current in a collection of date items + DATE: _("current date"), + # Translators: Presented when a time item is marked as current in a collection of time items + TIME: _("current time"), + } def processPositiveStates(role, states, reason: OutputReason, positiveStates=None): From b1eec4ffc8c8575dfaf3238e23947710fb344ede Mon Sep 17 00:00:00 2001 From: Reef Turner Date: Tue, 24 Nov 2020 10:42:38 +0800 Subject: [PATCH 05/12] Shorten calls to getTranslationForUI --- source/braille.py | 2 +- source/speech/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/braille.py b/source/braille.py index f0c94f09495..b47d8fc2efc 100644 --- a/source/braille.py +++ b/source/braille.py @@ -573,7 +573,7 @@ def getPropertiesBraille(**propertyValues) -> str: # noqa: C901 textList.append(columnStr) isCurrent = propertyValues.get('current', controlTypes.IS_CURRENT.NO) if isCurrent != controlTypes.IS_CURRENT.NO: - textList.append(controlTypes.IS_CURRENT.getTranslationForUI(isCurrent)) + textList.append(isCurrent.getTranslationForUI()) placeholder = propertyValues.get('placeholder', None) if placeholder: textList.append(placeholder) diff --git a/source/speech/__init__.py b/source/speech/__init__.py index f1a95072038..1a61ca4ef1b 100755 --- a/source/speech/__init__.py +++ b/source/speech/__init__.py @@ -1610,7 +1610,7 @@ def getPropertiesSpeech( # noqa: C901 # speak isCurrent property EG aria-current isCurrent = propertyValues.get('current', controlTypes.IS_CURRENT.NO) if isCurrent != controlTypes.IS_CURRENT.NO: - textList.append(controlTypes.IS_CURRENT.getTranslationForUI(isCurrent)) + textList.append(isCurrent.getTranslationForUI()) placeholder: Optional[str] = propertyValues.get('placeholder', None) if placeholder: From 97278effb2b6de7713eb44aca1d5d5bd8fb52264 Mon Sep 17 00:00:00 2001 From: Reef Turner Date: Tue, 24 Nov 2020 12:47:25 +0800 Subject: [PATCH 06/12] _isCurrentLabels was being converted into an Enum Member --- source/controlTypes.py | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/source/controlTypes.py b/source/controlTypes.py index 6dff49a7893..05495017e12 100644 --- a/source/controlTypes.py +++ b/source/controlTypes.py @@ -680,32 +680,31 @@ class IS_CURRENT(Enum): def getTranslationForUI(self): try: - return self._isCurrentLabels[self] + return _isCurrentLabels[self] except KeyError: log.debugWarning(f"No translation mapping for: {self}") # there is a value for 'current' but NVDA hasn't learned about it yet, # at least describe in the general sense that this item is 'current' - return self._isCurrentLabels[IS_CURRENT.Yes] + return _isCurrentLabels[IS_CURRENT.Yes] - #: Text to use for 'current' values. These describe if an item is the current item - #: within a particular kind of selection. EG aria-current - _isCurrentLabels: Dict[Enum, str] = { - NO: "", # There is nothing extra to say for items that are not current. - # Translators: Presented when an item is marked as current in a collection of items - Yes: _("current"), - # Translators: Presented when a page item is marked as current in a collection of page items - PAGE: _("current page"), - # Translators: Presented when a step item is marked as current in a collection of - # step items - STEP: _("current step"), - # Translators: Presented when a location item is marked as current in a collection of - # location items - LOCATION: _("current location"), - # Translators: Presented when a date item is marked as current in a collection of date items - DATE: _("current date"), - # Translators: Presented when a time item is marked as current in a collection of time items - TIME: _("current time"), - } + +#: Text to use for 'current' values. These describe if an item is the current item +#: within a particular kind of selection. EG aria-current +_isCurrentLabels: Dict[Enum, str] = { + IS_CURRENT.NO: "", # There is nothing extra to say for items that are not current. + # Translators: Presented when an item is marked as current in a collection of items + IS_CURRENT.Yes: _("current"), + # Translators: Presented when a page item is marked as current in a collection of page items + IS_CURRENT.PAGE: _("current page"), + # Translators: Presented when a step item is marked as current in a collection of step items + IS_CURRENT.STEP: _("current step"), + # Translators: Presented when a location item is marked as current in a collection of location items + IS_CURRENT.LOCATION: _("current location"), + # Translators: Presented when a date item is marked as current in a collection of date items + IS_CURRENT.DATE: _("current date"), + # Translators: Presented when a time item is marked as current in a collection of time items + IS_CURRENT.TIME: _("current time"), +} def processPositiveStates(role, states, reason: OutputReason, positiveStates=None): From 815648938766f2f732856800b9c9e662014b6263 Mon Sep 17 00:00:00 2001 From: Reef Turner Date: Thu, 31 Dec 2020 16:20:27 +0800 Subject: [PATCH 07/12] review actions - Use a property - shorten name --- source/braille.py | 2 +- source/controlTypes.py | 6 +++++- source/speech/__init__.py | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/source/braille.py b/source/braille.py index eae1616db94..4ace213a096 100644 --- a/source/braille.py +++ b/source/braille.py @@ -573,7 +573,7 @@ def getPropertiesBraille(**propertyValues) -> str: # noqa: C901 textList.append(columnStr) isCurrent = propertyValues.get('current', controlTypes.IS_CURRENT.NO) if isCurrent != controlTypes.IS_CURRENT.NO: - textList.append(isCurrent.getTranslationForUI()) + textList.append(isCurrent.displayString) placeholder = propertyValues.get('placeholder', None) if placeholder: textList.append(placeholder) diff --git a/source/controlTypes.py b/source/controlTypes.py index 05495017e12..ef68092b7e2 100644 --- a/source/controlTypes.py +++ b/source/controlTypes.py @@ -678,7 +678,11 @@ class IS_CURRENT(Enum): DATE = "date" TIME = "time" - def getTranslationForUI(self): + @property + def displayString(self): + """ + @return: The translated UI display string that should be used for this value of the IS_CURRENT enum + """ try: return _isCurrentLabels[self] except KeyError: diff --git a/source/speech/__init__.py b/source/speech/__init__.py index d4c6c056201..61653eb1147 100755 --- a/source/speech/__init__.py +++ b/source/speech/__init__.py @@ -1610,7 +1610,7 @@ def getPropertiesSpeech( # noqa: C901 # speak isCurrent property EG aria-current isCurrent = propertyValues.get('current', controlTypes.IS_CURRENT.NO) if isCurrent != controlTypes.IS_CURRENT.NO: - textList.append(isCurrent.getTranslationForUI()) + textList.append(isCurrent.displayString) placeholder: Optional[str] = propertyValues.get('placeholder', None) if placeholder: From 0176db52cfe5ef1adc38101f1c3b15175f4c4050 Mon Sep 17 00:00:00 2001 From: Reef Turner Date: Thu, 31 Dec 2020 17:11:02 +0800 Subject: [PATCH 08/12] review: Use pascal case for IsCurrent --- source/NVDAObjects/IAccessible/MSHTML.py | 4 ++-- source/NVDAObjects/IAccessible/ia2Web.py | 6 +++--- source/NVDAObjects/UIA/edge.py | 6 +++--- source/NVDAObjects/__init__.py | 6 +++--- source/braille.py | 6 +++--- source/controlTypes.py | 20 ++++++++++---------- source/speech/__init__.py | 10 +++++----- source/virtualBuffers/MSHTML.py | 6 +++--- source/virtualBuffers/gecko_ia2.py | 6 +++--- 9 files changed, 35 insertions(+), 35 deletions(-) diff --git a/source/NVDAObjects/IAccessible/MSHTML.py b/source/NVDAObjects/IAccessible/MSHTML.py index d995d3e1531..16ed0988041 100644 --- a/source/NVDAObjects/IAccessible/MSHTML.py +++ b/source/NVDAObjects/IAccessible/MSHTML.py @@ -546,10 +546,10 @@ def _get_treeInterceptorClass(self): def _get_isCurrent(self): isCurrent = self.HTMLAttributes["aria-current"] try: - return controlTypes.IS_CURRENT(isCurrent) + return controlTypes.IsCurrent(isCurrent) except ValueError: log.debugWarning(f"Unknown aria-current value: {isCurrent}") - return controlTypes.IS_CURRENT.NO + return controlTypes.IsCurrent.NO def _get_HTMLAttributes(self): return HTMLAttribCache(self.HTMLNode) diff --git a/source/NVDAObjects/IAccessible/ia2Web.py b/source/NVDAObjects/IAccessible/ia2Web.py index 99cb65468f2..767390d3d40 100644 --- a/source/NVDAObjects/IAccessible/ia2Web.py +++ b/source/NVDAObjects/IAccessible/ia2Web.py @@ -34,13 +34,13 @@ def _get_positionInfo(self): info['level']=level return info - def _get_isCurrent(self) -> controlTypes.IS_CURRENT: + def _get_isCurrent(self) -> controlTypes.IsCurrent: ia2attrCurrent: str = self.IA2Attributes.get("current", "false") try: - return controlTypes.IS_CURRENT(ia2attrCurrent) + return controlTypes.IsCurrent(ia2attrCurrent) except ValueError: log.debugWarning(f"Unknown 'current' IA2Attribute value: {ia2attrCurrent}") - return controlTypes.IS_CURRENT.NO + return controlTypes.IsCurrent.NO def _get_placeholder(self): placeholder = self.IA2Attributes.get('placeholder', None) diff --git a/source/NVDAObjects/UIA/edge.py b/source/NVDAObjects/UIA/edge.py index f1f1532a6ad..19fbda81635 100644 --- a/source/NVDAObjects/UIA/edge.py +++ b/source/NVDAObjects/UIA/edge.py @@ -470,18 +470,18 @@ def _get_ariaProperties(self): # "false" is ignored by the regEx and will not produce a match RE_ARIA_CURRENT_PROP_VALUE = re.compile("current=(?!false)(\w+);") - def _get_isCurrent(self) -> controlTypes.IS_CURRENT: + def _get_isCurrent(self) -> controlTypes.IsCurrent: ariaProperties=self._getUIACacheablePropertyValue(UIAHandler.UIA_AriaPropertiesPropertyId) match = self.RE_ARIA_CURRENT_PROP_VALUE.search(ariaProperties) if match: valueOfAriaCurrent = match.group(1) try: - return controlTypes.IS_CURRENT(valueOfAriaCurrent) + return controlTypes.IsCurrent(valueOfAriaCurrent) except ValueError: log.debugWarning( f"Unknown aria-current value: {valueOfAriaCurrent}, ariaProperties: {ariaProperties}" ) - return controlTypes.IS_CURRENT.NO + return controlTypes.IsCurrent.NO def _get_roleText(self): roleText = self.ariaProperties.get('roledescription', None) diff --git a/source/NVDAObjects/__init__.py b/source/NVDAObjects/__init__.py index 01bd265874d..9bd337dec57 100644 --- a/source/NVDAObjects/__init__.py +++ b/source/NVDAObjects/__init__.py @@ -975,13 +975,13 @@ def _get_statusBar(self): """ return None - isCurrent: controlTypes.IS_CURRENT #: type info for auto property _get_isCurrent + isCurrent: controlTypes.IsCurrent #: type info for auto property _get_isCurrent - def _get_isCurrent(self) -> controlTypes.IS_CURRENT: + def _get_isCurrent(self) -> controlTypes.IsCurrent: """Gets the value that indicates whether this object is the current element in a set of related elements. This maps to aria-current. """ - return controlTypes.IS_CURRENT.NO + return controlTypes.IsCurrent.NO def _get_shouldAcceptShowHideCaretEvent(self): """Some objects/applications send show/hide caret events when we don't expect it, such as when the cursor is blinking. diff --git a/source/braille.py b/source/braille.py index 4ace213a096..97833feb5e6 100644 --- a/source/braille.py +++ b/source/braille.py @@ -571,8 +571,8 @@ def getPropertiesBraille(**propertyValues) -> str: # noqa: C901 # %s is replaced with the column number. columnStr = _("c{columnNumber}").format(columnNumber=columnNumber) textList.append(columnStr) - isCurrent = propertyValues.get('current', controlTypes.IS_CURRENT.NO) - if isCurrent != controlTypes.IS_CURRENT.NO: + isCurrent = propertyValues.get('current', controlTypes.IsCurrent.NO) + if isCurrent != controlTypes.IsCurrent.NO: textList.append(isCurrent.displayString) placeholder = propertyValues.get('placeholder', None) if placeholder: @@ -658,7 +658,7 @@ def getControlFieldBraille(info, field, ancestors, reportStart, formatConfig): states = field.get("states", set()) value=field.get('value',None) - current = field.get('current', controlTypes.IS_CURRENT.NO) + current = field.get('current', controlTypes.IsCurrent.NO) placeholder=field.get('placeholder', None) roleText = field.get('roleTextBraille', field.get('roleText')) landmark = field.get("landmark") diff --git a/source/controlTypes.py b/source/controlTypes.py index ef68092b7e2..a0d9650e385 100644 --- a/source/controlTypes.py +++ b/source/controlTypes.py @@ -665,7 +665,7 @@ class OutputReason(Enum): REASON_ONLYCACHE = OutputReason.ONLYCACHE -class IS_CURRENT(Enum): +class IsCurrent(Enum): """Values to use within NVDA to denote 'current' values. These describe if an item is the current item within a particular kind of selection. EG aria-current @@ -681,7 +681,7 @@ class IS_CURRENT(Enum): @property def displayString(self): """ - @return: The translated UI display string that should be used for this value of the IS_CURRENT enum + @return: The translated UI display string that should be used for this value of the IsCurrent enum """ try: return _isCurrentLabels[self] @@ -689,25 +689,25 @@ def displayString(self): log.debugWarning(f"No translation mapping for: {self}") # there is a value for 'current' but NVDA hasn't learned about it yet, # at least describe in the general sense that this item is 'current' - return _isCurrentLabels[IS_CURRENT.Yes] + return _isCurrentLabels[IsCurrent.Yes] #: Text to use for 'current' values. These describe if an item is the current item #: within a particular kind of selection. EG aria-current _isCurrentLabels: Dict[Enum, str] = { - IS_CURRENT.NO: "", # There is nothing extra to say for items that are not current. + IsCurrent.NO: "", # There is nothing extra to say for items that are not current. # Translators: Presented when an item is marked as current in a collection of items - IS_CURRENT.Yes: _("current"), + IsCurrent.Yes: _("current"), # Translators: Presented when a page item is marked as current in a collection of page items - IS_CURRENT.PAGE: _("current page"), + IsCurrent.PAGE: _("current page"), # Translators: Presented when a step item is marked as current in a collection of step items - IS_CURRENT.STEP: _("current step"), + IsCurrent.STEP: _("current step"), # Translators: Presented when a location item is marked as current in a collection of location items - IS_CURRENT.LOCATION: _("current location"), + IsCurrent.LOCATION: _("current location"), # Translators: Presented when a date item is marked as current in a collection of date items - IS_CURRENT.DATE: _("current date"), + IsCurrent.DATE: _("current date"), # Translators: Presented when a time item is marked as current in a collection of time items - IS_CURRENT.TIME: _("current time"), + IsCurrent.TIME: _("current time"), } diff --git a/source/speech/__init__.py b/source/speech/__init__.py index 61653eb1147..aefaa112d88 100755 --- a/source/speech/__init__.py +++ b/source/speech/__init__.py @@ -362,7 +362,7 @@ def getObjectPropertiesSpeech( # noqa: C901 positionInfo=obj.positionInfo elif value and name == "current": # getPropertiesSpeech names this "current", but the NVDAObject property is - # named "isCurrent", it's type should always be controltypes.IS_CURRENT + # named "isCurrent", it's type should always be controltypes.IsCurrent newPropertyValues['current'] = obj.isCurrent elif value: # Certain properties such as row and column numbers have presentational versions, which should be used for speech if they are available. @@ -1608,8 +1608,8 @@ def getPropertiesSpeech( # noqa: C901 oldTableID = None # speak isCurrent property EG aria-current - isCurrent = propertyValues.get('current', controlTypes.IS_CURRENT.NO) - if isCurrent != controlTypes.IS_CURRENT.NO: + isCurrent = propertyValues.get('current', controlTypes.IsCurrent.NO) + if isCurrent != controlTypes.IsCurrent.NO: textList.append(isCurrent.displayString) placeholder: Optional[str] = propertyValues.get('placeholder', None) @@ -1674,7 +1674,7 @@ def getControlFieldSpeech( # noqa: C901 name = "" states=attrs.get('states',set()) keyboardShortcut=attrs.get('keyboardShortcut', "") - isCurrent = attrs.get('current', controlTypes.IS_CURRENT.NO) + isCurrent = attrs.get('current', controlTypes.IsCurrent.NO) placeholderValue=attrs.get('placeholder', None) value=attrs.get('value',"") if reason==controlTypes.REASON_FOCUS or attrs.get('alwaysReportDescription',False): @@ -1905,7 +1905,7 @@ def getControlFieldSpeech( # noqa: C901 # Special cases elif not speakEntry and fieldType in ("start_addedToControlFieldStack","start_relative"): out = [] - if isCurrent != controlTypes.IS_CURRENT.NO: + if isCurrent != controlTypes.IsCurrent.NO: out.extend(isCurrentSequence) # Speak expanded / collapsed / level for treeview items (in ARIA treegrids) if role == controlTypes.ROLE_TREEVIEWITEM: diff --git a/source/virtualBuffers/MSHTML.py b/source/virtualBuffers/MSHTML.py index d4388c7e16f..797c7477de2 100644 --- a/source/virtualBuffers/MSHTML.py +++ b/source/virtualBuffers/MSHTML.py @@ -51,12 +51,12 @@ def _normalizeControlField(self,attrs): ariaCurrentValue = attrs.get('HTMLAttrib::aria-current', 'false') try: - ariaCurrent = controlTypes.IS_CURRENT(ariaCurrentValue) + ariaCurrent = controlTypes.IsCurrent(ariaCurrentValue) except ValueError: log.debugWarning(f"Unknown aria-current value: {ariaCurrentValue}") - ariaCurrent = controlTypes.IS_CURRENT.NO + ariaCurrent = controlTypes.IsCurrent.NO - if ariaCurrent != controlTypes.IS_CURRENT.NO: + if ariaCurrent != controlTypes.IsCurrent.NO: attrs['current'] = ariaCurrent placeholder = self._getPlaceholderAttribute(attrs, 'HTMLAttrib::aria-placeholder') diff --git a/source/virtualBuffers/gecko_ia2.py b/source/virtualBuffers/gecko_ia2.py index 3b5c6ab2ecc..910cdb94845 100755 --- a/source/virtualBuffers/gecko_ia2.py +++ b/source/virtualBuffers/gecko_ia2.py @@ -54,11 +54,11 @@ def _normalizeControlField(self,attrs): valForCurrent = attrs.get("IAccessible2::attribute_current", "false") try: - isCurrent = controlTypes.IS_CURRENT(valForCurrent) + isCurrent = controlTypes.IsCurrent(valForCurrent) except ValueError: log.debugWarning(f"Unknown isCurrent value: {valForCurrent}") - isCurrent = controlTypes.IS_CURRENT.NO - if isCurrent != controlTypes.IS_CURRENT.NO: + isCurrent = controlTypes.IsCurrent.NO + if isCurrent != controlTypes.IsCurrent.NO: attrs['current'] = isCurrent placeholder = self._getPlaceholderAttribute(attrs, "IAccessible2::attribute_placeholder") if placeholder is not None: From 964b8f873ccec8357231fe572b35f1de203685b2 Mon Sep 17 00:00:00 2001 From: Reef Turner Date: Thu, 14 Jan 2021 16:09:07 +0800 Subject: [PATCH 09/12] Upper case for enum members --- source/controlTypes.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/controlTypes.py b/source/controlTypes.py index a0d9650e385..5a62b1777a7 100644 --- a/source/controlTypes.py +++ b/source/controlTypes.py @@ -671,7 +671,7 @@ class IsCurrent(Enum): EG aria-current """ NO = "false" - Yes = "true" + YES = "true" PAGE = "page" STEP = "step" LOCATION = "location" @@ -689,7 +689,7 @@ def displayString(self): log.debugWarning(f"No translation mapping for: {self}") # there is a value for 'current' but NVDA hasn't learned about it yet, # at least describe in the general sense that this item is 'current' - return _isCurrentLabels[IsCurrent.Yes] + return _isCurrentLabels[IsCurrent.YES] #: Text to use for 'current' values. These describe if an item is the current item @@ -697,7 +697,7 @@ def displayString(self): _isCurrentLabels: Dict[Enum, str] = { IsCurrent.NO: "", # There is nothing extra to say for items that are not current. # Translators: Presented when an item is marked as current in a collection of items - IsCurrent.Yes: _("current"), + IsCurrent.YES: _("current"), # Translators: Presented when a page item is marked as current in a collection of page items IsCurrent.PAGE: _("current page"), # Translators: Presented when a step item is marked as current in a collection of step items From d97430a09d9d1d358c6c21003086f4419c468455 Mon Sep 17 00:00:00 2001 From: Reef Turner Date: Fri, 29 Jan 2021 16:18:53 +0800 Subject: [PATCH 10/12] update changes file for PR #11782 --- user_docs/en/changes.t2t | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/user_docs/en/changes.t2t b/user_docs/en/changes.t2t index 5eafac90abb..b99adc7a8cc 100644 --- a/user_docs/en/changes.t2t +++ b/user_docs/en/changes.t2t @@ -37,6 +37,11 @@ What's New in NVDA - getConfigDirs - use globalVars.appArgs.configPath instead - Module level REASON_* constants are removed from controlTypes - please use controlTypes.OutputReason instead. (#11969) - REASON_QUICKNAV has been removed from browseMode - use controlTypes.OutputReason.QUICKNAV instead. (#11969) +- 'NVDAObject' (and derivatives) property 'isCurrent' now strictly returns Enum class 'controlTypes.IsCurrent'. (#11782) + - 'isCurrent' is no longer Optional, and thus will not return None. + - When an object is not current 'controlTypes.IsCurrent.NO' is returned. +- The 'controlTypes.isCurrentLabels' mapping has been removed. (#11782) + - Instead use the 'displayString' property on a 'controlTypes.IsCurrent' enum value. EG 'controlTypes.IsCurrent.YES.displayString' = 2020.4 = From c8d17d1cfb231d81f27f914d781462e839feca2b Mon Sep 17 00:00:00 2001 From: Reef Turner Date: Wed, 3 Feb 2021 15:30:39 +0800 Subject: [PATCH 11/12] fix lint --- source/controlTypes.py | 1 - 1 file changed, 1 deletion(-) diff --git a/source/controlTypes.py b/source/controlTypes.py index 5c97204cb88..b29b4da5b68 100644 --- a/source/controlTypes.py +++ b/source/controlTypes.py @@ -651,7 +651,6 @@ class OutputReason(Enum): QUICKNAV = auto() - class IsCurrent(Enum): """Values to use within NVDA to denote 'current' values. These describe if an item is the current item within a particular kind of selection. From 87b74f5ade4849bd18b02a79364919adf98ed93c Mon Sep 17 00:00:00 2001 From: Reef Turner Date: Wed, 3 Feb 2021 15:36:20 +0800 Subject: [PATCH 12/12] Update change log for PR #11782 --- user_docs/en/changes.t2t | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/user_docs/en/changes.t2t b/user_docs/en/changes.t2t index b99adc7a8cc..2f53c3aaa56 100644 --- a/user_docs/en/changes.t2t +++ b/user_docs/en/changes.t2t @@ -42,6 +42,10 @@ What's New in NVDA - When an object is not current 'controlTypes.IsCurrent.NO' is returned. - The 'controlTypes.isCurrentLabels' mapping has been removed. (#11782) - Instead use the 'displayString' property on a 'controlTypes.IsCurrent' enum value. EG 'controlTypes.IsCurrent.YES.displayString' +- 'NVDAObject' (and derivatives) property 'isCurrent' now strictly returns 'controlTypes.IsCurrent'. (#11782) + - 'isCurrent' is no longer Optional, and thus will not return None, when an object is not current 'controlTypes.IsCurrent.NO' is returned. +- The 'controlTypes.isCurrentLabels' has been removed, instead use the 'displayString' property on a 'controlTypes.IsCurrent' enum value. (#11782) + - EG 'controlTypes.IsCurrent.YES.displayString' = 2020.4 =