Skip to content

Commit

Permalink
Correct focus in Objects Explorer (vs2017 and vs2019) (PR #9415)
Browse files Browse the repository at this point in the history
Enforced UIA implementation of all UI elements in Visual Studio 15.3 and up.

This adds an overlay class for the tree view items in object explorer. It takes a `focusRedirect` event and checks for its states
If `STATE_FOCUSED` is not in the states set, it redirects the event to the real focused object.
I'm not really sure if it is the right way of doing this.

Closes #9311
  • Loading branch information
francipvb authored and feerrenrut committed May 6, 2019
1 parent 029c0e3 commit 2239a76
Showing 1 changed file with 35 additions and 5 deletions.
40 changes: 35 additions & 5 deletions source/appModules/devenv.py
Expand Up @@ -48,8 +48,10 @@
from NVDAObjects.window import Window

from NVDAObjects.window import DisplayModelEditableText
from NVDAObjects.IAccessible import IAccessible

import appModuleHandler
import controlTypes


#
Expand Down Expand Up @@ -88,6 +90,9 @@

class AppModule(appModuleHandler.AppModule):
def chooseNVDAObjectOverlayClasses(self, obj, clsList):
vsMajor, vsMinor, rest = self.productVersion.split(".", 2)
vsMajor, vsMinor = int(vs_major), int(vs_minor)

# Only use this overlay class if the top level automation object for the IDE can be retrieved,
# as it will not work otherwise.
if obj.windowClassName == VsTextEditPaneClassName and self._getDTE():
Expand All @@ -96,15 +101,21 @@ def chooseNVDAObjectOverlayClasses(self, obj, clsList):
except ValueError:
pass
clsList.insert(0, VsTextEditPane)


if ((vsMajor == 15 and vsMinor >= 3)
or vsMajor >= 16):
if obj.role == controlTypes.ROLE_TREEVIEWITEM and obj.windowClassName == "LiteTreeView32":
clsList.insert(0, ObjectsTreeItem)


def _getDTE(self):
# Return the already fetched instance if there is one.
try:
if self._DTE:
return self._DTE
except AttributeError:
pass

# Retrieve and cache the top level automation object for the IDE
DTEVersion = VsVersion_None
bctx = objbase.CreateBindCtx()
Expand All @@ -120,7 +131,7 @@ def _getDTE(self):
DTEVersion = VsVersion_2003
elif "!VisualStudio.DTE:%d"%self.processID==displayName:
DTEVersion = VsVersion_2002

if DTEVersion != VsVersion_None:
self._DTEVersion = DTEVersion
self._DTE = comtypes.client.dynamic.Dispatch(ROT.GetObject(mon).QueryInterface(IDispatch))
Expand All @@ -134,7 +145,7 @@ def _getDTE(self):

# Loop has completed
return self._DTE

def _getTextManager(self):
try:
if self._textManager:
Expand All @@ -145,7 +156,6 @@ def _getTextManager(self):
self._textManager = serviceProvider.QueryService(SVsTextManager, IVsTextManager)
return self._textManager


class VsTextEditPaneTextInfo(textInfos.offsets.OffsetsTextInfo):
def _InformUnsupportedWindowType(self,type):
log.error("An unsupported window type `%d' was encountered, please inform the NVDA development team." %type)
Expand Down Expand Up @@ -473,3 +483,23 @@ class IVsTextManager(comtypes.IUnknown):
COMMETHOD([], HRESULT, 'SetTopLine',
( ['in'], c_int, 'iBaseLine' )),
]

class ObjectsTreeItem(IAccessible):

def _get_focusRedirect(self):
"""
Returns the correct focused item in the object explorer trees
"""

if not controlTypes.STATE_FOCUSED in self.states:
# Object explorer tree views have a bad IAccessible implementation.
# When expanding a primary node and going to secondary node, the
# focus is placed to the next root node, so we need to redirect
# it to the real focused widget. Fortunately, the states are
# still correct and we can detect if this is really focused or not.
return self.objectWithFocus()

def _get_positionInfo(self):
return {
"level": int(self.IAccessibleObject.accValue(self.IAccessibleChildID))
}

0 comments on commit 2239a76

Please sign in to comment.