Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions nvdaHelper/vbufBackends/mshtml/mshtml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,7 @@ inline void getAttributesFromHTMLDOMNode(IHTMLDOMNode* pHTMLDOMNode,wstring& nod
macro_addHTMLAttributeToMap(L"aria-relevant",false,pHTMLAttributeCollection2,attribsMap,tempVar,tempAttribNode);
macro_addHTMLAttributeToMap(L"aria-busy",false,pHTMLAttributeCollection2,attribsMap,tempVar,tempAttribNode);
macro_addHTMLAttributeToMap(L"aria-atomic",false,pHTMLAttributeCollection2,attribsMap,tempVar,tempAttribNode);
macro_addHTMLAttributeToMap(L"aria-current",false,pHTMLAttributeCollection2,attribsMap,tempVar,tempAttribNode);
pHTMLAttributeCollection2->Release();
}

Expand Down
3 changes: 3 additions & 0 deletions source/NVDAObjects/IAccessible/MSHTML.py
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,9 @@ def _get_treeInterceptorClass(self):
return virtualBuffers.MSHTML.MSHTML
return super(MSHTML,self).treeInterceptorClass

def getValueForAriaCurrent(self):
return self.HTMLAttributes["aria-current"]

def _get_HTMLAttributes(self):
return HTMLAttribCache(self.HTMLNode)

Expand Down
4 changes: 4 additions & 0 deletions source/NVDAObjects/IAccessible/ia2Web.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ def _get_positionInfo(self):
info['level']=level
return info

def getValueForAriaCurrent(self):
current = self.IA2Attributes.get("current", False)
return current

class Document(Ia2Web):
value = None

Expand Down
14 changes: 13 additions & 1 deletion source/NVDAObjects/UIA/edge.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
import config
import controlTypes
import cursorManager
import re
import aria
import textInfos
import UIAHandler
from UIABrowseMode import UIABrowseModeDocument, UIABrowseModeDocumentTextInfo
import aria
from UIAUtils import *
from . import UIA, UIATextInfo

Expand Down Expand Up @@ -147,6 +147,8 @@ def _getControlFieldForObject(self,obj,isEmbedded=False,startOfNode=False,endOfN
# Combo boxes with a text pattern are editable
if obj.role==controlTypes.ROLE_COMBOBOX and obj.UIATextPattern:
field['states'].add(controlTypes.STATE_EDITABLE)
# report if the field is 'current'
field['current']=obj.getValueForAriaCurrent()
# For certain controls, if ARIA overrides the label, then force the field's content (value) to the label
# Later processing in Edge's getTextWithFields will remove descendant content from fields with a content attribute.
ariaProperties=obj.UIAElement.currentAriaProperties
Expand Down Expand Up @@ -387,6 +389,16 @@ def _get_description(self):
pass
return super(EdgeNode,self).description

def getValueForAriaCurrent(self):
ariaProperties=self.UIAElement.currentAriaProperties
match = re.match("current=(\w+);", ariaProperties)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make this a compiled regular expression constant. See re.compile. We tend to name such constants RE_FOO.

log.debug("aria props = %s" % ariaProperties)
if match:
valueOfAriaCurrent = match.group(1)
log.debug("aria current value = %s" % valueOfAriaCurrent)
return valueOfAriaCurrent
return False

class EdgeList(EdgeNode):

# non-focusable lists are readonly lists (ensures correct NVDA presentation category)
Expand Down
6 changes: 6 additions & 0 deletions source/NVDAObjects/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,12 @@ def _get_statusBar(self):
"""
return None

def getValueForAriaCurrent(self):
"""Gets the value for aria-current. Normally returns False. If this object is current
it will return one of the following values: True, "page", "step", "location", "date", "time"
"""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. This should be a property rather than a method. Aside from consistency with other NVDAObject properties (name, description, etc.), this means this will be cached during a core tick which improves performance. In NVDA, you can do this by simply naming the method _get_foo, which will create a property named foo.
  2. While this is from the ARIA spec, we try to keep global concepts pretty abstract/platform agnostic. This one is tricky to name because just having a property called current looks kinda weird. We feel the name isCurrent is probably best. While this is perhaps a tiny bit misleading (the "is" prefix normally suggests it's a boolean), it's the best we could come up with... and it is boolean-ish anyway in the sense that it's either current or not, with the type just being a nicety for the user.
  3. The docstring should probably say that this indicates whether this object is the current element in a set of related elements... or something like that; something similar to the summary sentence in the ARIA spec. It can still say it's mapped from aria-current, though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah ok. I'm not 100% comfortable with the name, but I can't think of anything better.

return False

def reportFocus(self):
"""Announces this object in a way suitable such that it gained focus.
"""
Expand Down
19 changes: 18 additions & 1 deletion source/speech.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ def speakObjectProperties(obj,reason=controlTypes.REASON_QUERY,index=None,**allo
newPropertyValues["_tableID"]=obj.tableID
except NotImplementedError:
pass
newPropertyValues['current']=obj.getValueForAriaCurrent()
#Get the speech text for the properties we want to speak, and then speak it
text=getSpeechTextForProperties(reason,**newPropertyValues)
if text:
Expand Down Expand Up @@ -1001,6 +1002,20 @@ def getSpeechTextForProperties(reason=controlTypes.REASON_QUERY,**propertyValues
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 is not None and ariaCurrent != False:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could simplify this to just: if ariaCurrent:

if ariaCurrent=="page":
textList.append(_("current page"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. I'd suggest moving these names into a dict in controlTypes, perhaps named currentLabels.
  2. Each of these translatable strings needs a translators comment.

elif ariaCurrent=="step":
textList.append(_("current step"))
elif ariaCurrent=="location":
textList.append(_("current location"))
elif ariaCurrent=="date":
textList.append(_("current date"))
elif ariaCurrent=="time":
textList.append(_("current time"))
else:
textList.append(_("current"))
indexInGroup=propertyValues.get('positionInfo_indexInGroup',0)
similarItemsInGroup=propertyValues.get('positionInfo_similarItemsInGroup',0)
if 0<indexInGroup<=similarItemsInGroup:
Expand Down Expand Up @@ -1035,6 +1050,7 @@ def getControlFieldSpeech(attrs,ancestorAttrs,fieldType,formatConfig=None,extraD
role=attrs.get('role',controlTypes.ROLE_UNKNOWN)
states=attrs.get('states',set())
keyboardShortcut=attrs.get('keyboardShortcut', "")
ariaCurrent=attrs.get('current', None)
value=attrs.get('value',"")
if reason==controlTypes.REASON_FOCUS or attrs.get('alwaysReportDescription',False):
description=attrs.get('description',"")
Expand All @@ -1050,6 +1066,7 @@ def getControlFieldSpeech(attrs,ancestorAttrs,fieldType,formatConfig=None,extraD
roleText=getSpeechTextForProperties(reason=reason,role=role)
stateText=getSpeechTextForProperties(reason=reason,states=states,_role=role)
keyboardShortcutText=getSpeechTextForProperties(reason=reason,keyboardShortcut=keyboardShortcut) if config.conf["presentation"]["reportKeyboardShortcuts"] else ""
ariaCurrentText=getSpeechTextForProperties(reason=reason,current=ariaCurrent)
nameText=getSpeechTextForProperties(reason=reason,name=name)
valueText=getSpeechTextForProperties(reason=reason,value=value)
descriptionText=(getSpeechTextForProperties(reason=reason,description=description)
Expand Down Expand Up @@ -1112,7 +1129,7 @@ def getControlFieldSpeech(attrs,ancestorAttrs,fieldType,formatConfig=None,extraD
content = attrs.get("content")
if content and speakContentFirst:
out.append(content)
out.extend(x for x in (nameText,(stateText if speakStatesFirst else roleText),(roleText if speakStatesFirst else stateText),valueText,descriptionText,levelText,keyboardShortcutText) if x)
out.extend(x for x in (nameText,(stateText if speakStatesFirst else roleText),(roleText if speakStatesFirst else stateText),ariaCurrentText,valueText,descriptionText,levelText,keyboardShortcutText) if x)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's possible that someone might set aria-current on a table cell; e.g. in a calendar. So, it also needs to be reported for the table cell case above this.

if content and not speakContentFirst:
out.append(content)
return CHUNK_SEPARATOR.join(out)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding speaking for list items, etc., this can be achieved by handling current similar to the way we handle clickable below. The conditions for current should be the same as clickable except we do want to report it even if extraDetail is True (whereas we don't for clickable). The tricky thing here is that ideally, we still want to report current if we're reporting clickable (and vice versa). So, I guess the clickable and current block will have to be merged.

Expand Down
3 changes: 3 additions & 0 deletions source/virtualBuffers/MSHTML.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ def _normalizeFormatField(self, attrs):

def _normalizeControlField(self,attrs):
level=None
ariaCurrent = attrs.get('HTMLAttrib::aria-current', None)
if ariaCurrent is not None:
attrs['current']=ariaCurrent
accRole=attrs.get('IAccessible::role',0)
accRole=int(accRole) if isinstance(accRole,basestring) and accRole.isdigit() else accRole
nodeName=attrs.get('IHTMLDOMNode::nodeName',"")
Expand Down
6 changes: 6 additions & 0 deletions source/virtualBuffers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,12 @@ def _getParagraphOffsets(self,offset):
return lineStart.value,lineEnd.value

def _normalizeControlField(self,attrs):

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extraneous blank line.

ariaCurrent = attrs.get("IAccessible2::attribute_current")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move this into the gecko_ia2 virtual buffer.

if ariaCurrent != None:
attrs['current']= ariaCurrent
del attrs["IAccessible2::attribute_current"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably no need to delete this; we don't do it with other attributes and leaving it there might be useful for debugging.


tableLayout=attrs.get('table-layout')
if tableLayout:
attrs['table-layout']=tableLayout=="1"
Expand Down