New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Significantly speed up heading quick navigation in Microsoft Edge by walking heading Text elements rather than looking at all paragraphs #7343
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,7 +17,7 @@ | |
import aria | ||
import textInfos | ||
import UIAHandler | ||
from UIABrowseMode import UIABrowseModeDocument, UIABrowseModeDocumentTextInfo | ||
from UIABrowseMode import UIABrowseModeDocument, UIABrowseModeDocumentTextInfo, UIATextRangeQuickNavItem,UIAControlQuicknavIterator | ||
from UIAUtils import * | ||
from . import UIA, UIATextInfo | ||
|
||
|
@@ -508,13 +508,48 @@ def event_gainFocus(self): | |
return | ||
return super(EdgeHTMLRootContainer,self).event_gainFocus() | ||
|
||
class EdgeHeadingQuickNavItem(UIATextRangeQuickNavItem): | ||
|
||
@property | ||
def level(self): | ||
if not hasattr(self,'_level'): | ||
styleVal=getUIATextAttributeValueFromRange(self.textInfo._rangeObj,UIAHandler.UIA_StyleIdAttributeId) | ||
self._level=styleVal-(UIAHandler.StyleId_Heading1-1) if UIAHandler.StyleId_Heading1<=styleVal<=UIAHandler.StyleId_Heading6 else None | ||
return self._level | ||
|
||
def isChild(self,parent): | ||
return self.level>parent.level | ||
|
||
def EdgeHeadingQuicknavIterator(itemType,document,position,direction="next"): | ||
""" | ||
A helper for L{EdgeHTMLTreeInterceptor._iterNodesByType} that specifically yields L{EdgeHeadingQuickNavItem} objects found in the given document, starting the search from the given position, searching in the given direction. | ||
See L{browseMode._iterNodesByType} for details on these specific arguments. | ||
""" | ||
# Edge exposes all headings as UIA elements with a controlType of text, and a level. Thus we can quickly search for these. | ||
# However, sometimes when ARIA is used, the level on the element may not match the level in the text attributes. | ||
# Therefore we need to search for all levels 1 through 6, even if a specific level is specified. | ||
# Though this is still much faster than searching text attributes alone | ||
levels=range(1,7) | ||
condition=createUIAMultiPropertyCondition({UIAHandler.UIA_ControlTypePropertyId:UIAHandler.UIA_TextControlTypeId,UIAHandler.UIA_LevelPropertyId:levels}) | ||
levelString=itemType[7:] | ||
for item in UIAControlQuicknavIterator(itemType,document,position,condition,direction=direction,itemClass=EdgeHeadingQuickNavItem): | ||
# Verify this is the correct heading level via text attributes | ||
if item.level and (not levelString or levelString==str(item.level)): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this to catch mistakes in what Edge returns? Do we have any examples of where this is required? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The UIA search condition searches for an element with a controlType of text, and some kind of level. Edge itself does not provide anything more specific to search for. Thus, once we do have an element and have placed it in a headingQuickNavItem we then need to verify it is actually a heading by checking its level property. HeadingQuicknavItem.level is only valid if the element is in deed a valid heading (I.e. it also checks some text attributes). |
||
yield item | ||
|
||
class EdgeHTMLTreeInterceptor(cursorManager.ReviewCursorManager,UIABrowseModeDocument): | ||
|
||
TextInfo=UIABrowseModeDocumentTextInfo | ||
|
||
def _get_documentConstantIdentifier(self): | ||
return self.rootNVDAObject.parent.name | ||
|
||
def _iterNodesByType(self,nodeType,direction="next",pos=None): | ||
if nodeType.startswith("heading"): | ||
return EdgeHeadingQuicknavIterator(nodeType,self,pos,direction=direction) | ||
else: | ||
return super(EdgeHTMLTreeInterceptor,self)._iterNodesByType(nodeType,direction=direction,pos=pos) | ||
|
||
def shouldPassThrough(self,obj,reason=None): | ||
# Enter focus mode for selectable list items (<select> and role=listbox) | ||
if reason==controlTypes.REASON_FOCUS and obj.role==controlTypes.ROLE_LISTITEM and controlTypes.STATE_SELECTABLE in obj.states: | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add a doc string here? This isn't an inherited interface is it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The docstring can be found on browseMode.QuickNavItem.isChild
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I meant on
EdgeHeadingQuicknavIterator
. As an aside, how would you feel about adding doc strings on derived class functions to say something like@see parentclass.methodname
. This would make it clearer that this is an override, and where to find the documentation for it.