From dbbaacea10ac1ec8c07dbe943535d86284546861 Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Wed, 9 Dec 2015 15:49:56 +1000 Subject: [PATCH] Support for the Modern IME candidate list UI (used in Windows 8 and above for such input methods as Chinese Simplified Microsoft Pinyin). Fixes #4145 Fixes #4011 --- source/IAccessibleHandler.py | 6 +++- source/NVDAObjects/IAccessible/__init__.py | 2 +- source/NVDAObjects/IAccessible/mscandui.py | 34 +++++++++++++++++++++- source/_UIAHandler.py | 1 + 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/source/IAccessibleHandler.py b/source/IAccessibleHandler.py index 5ffb609978d..9507888fcc6 100644 --- a/source/IAccessibleHandler.py +++ b/source/IAccessibleHandler.py @@ -566,6 +566,9 @@ def winEventCallback(handle,eventID,window,objectID,childID,threadID,timestamp): window=tempWindow windowClassName=winUser.getClassName(window) + # Modern IME candidate list windows fire menu events which confuse us and can't be used properly inconjunction with input composition support + if windowClassName=="Microsoft.IME.UIManager.CandidateWindow.Host" and eventID in MENU_EVENTIDS: + return #At the moment we can't handle show, hide or reorder events on Mozilla Firefox Location bar,as there are just too many of them #Ignore show, hide and reorder on MozillaDropShadowWindowClass windows. if windowClassName.startswith('Mozilla') and eventID in (winUser.EVENT_OBJECT_SHOW,winUser.EVENT_OBJECT_HIDE,winUser.EVENT_OBJECT_REORDER) and childID<0: @@ -778,7 +781,8 @@ def processDestroyWinEvent(window,objectID,childID): # so can't use generic focus correction. (#2695) focus=api.getFocusObject() from NVDAObjects.IAccessible.mscandui import BaseCandidateItem - if objectID==0 and childID==0 and isinstance(focus,BaseCandidateItem) and window==focus.windowHandle and not eventHandler.isPendingEvents("gainFocus"): + windowClassName=winUser.getClassName(window) + if childID==0 and isinstance(focus,BaseCandidateItem) and window==focus.windowHandle and not eventHandler.isPendingEvents("gainFocus"): obj=focus.container if obj: eventHandler.queueEvent("gainFocus",obj) diff --git a/source/NVDAObjects/IAccessible/__init__.py b/source/NVDAObjects/IAccessible/__init__.py index 9bd30226ffd..6464503d12d 100644 --- a/source/NVDAObjects/IAccessible/__init__.py +++ b/source/NVDAObjects/IAccessible/__init__.py @@ -424,7 +424,7 @@ def findOverlayClasses(self,clsList): parentWindow=winUser.getAncestor(self.windowHandle,winUser.GA_PARENT) if parentWindow and winUser.getClassName(parentWindow)=="Frame Notification Bar": clsList.append(IENotificationBar) - if windowClassName.lower().startswith('mscandui'): + if windowClassName.lower().startswith('mscandui') or windowClassName in ("Microsoft.IME.CandidateWindow.View","Microsoft.IME.UIManager.CandidateWindow.Host"): import mscandui mscandui.findExtraOverlayClasses(self,clsList) elif windowClassName=="GeckoPluginWindow" and self.event_objectID==0 and self.IAccessibleChildID==0: diff --git a/source/NVDAObjects/IAccessible/mscandui.py b/source/NVDAObjects/IAccessible/mscandui.py index 865b43aa057..71e66f2eaf5 100755 --- a/source/NVDAObjects/IAccessible/mscandui.py +++ b/source/NVDAObjects/IAccessible/mscandui.py @@ -212,10 +212,42 @@ def event_show(self): item=MSCandUIWindow_candidateListItem(IAccessibleObject=self.IAccessibleObject,IAccessibleChildID=3) reportSelectedCandidate(item) +class ModernCandidateUICandidateItem(BaseCandidateItem): + + def _get_candidateCharacters(self): + return super(BaseCandidateItem,self).name + + _candidateNumber="" + + def _get_candidateNumber(self): + # The following property call sets candidateNumber + self.visibleCandidateItemsText + return self._candidateNumber + + def _get_visibleCandidateItemsText(self): + textList=[] + index=1 + for child in super(ModernCandidateUICandidateItem,self).parent.children: + if not isinstance(child,ModernCandidateUICandidateItem) or controlTypes.STATE_SELECTABLE not in child.states: continue + child.candidateNumber=index + textList.append(child.name) + if child.candidateCharacters==self.candidateCharacters: + self._candidateNumber=index + index+=1 + if len(textList)<=1: return None + self.visibleCandidateItemsText=(u", ".join(textList))+u", " + return self.visibleCandidateItemsText + + def event_stateChange(self): + if controlTypes.STATE_SELECTED in self.states: + reportSelectedCandidate(self) + def findExtraOverlayClasses(obj,clsList): windowClassName=obj.windowClassName role=obj.IAccessibleRole - if windowClassName=="MSCandUIWindow_Candidate": + if windowClassName=="Microsoft.IME.CandidateWindow.View" and obj.role==controlTypes.ROLE_BUTTON: + clsList.append(ModernCandidateUICandidateItem) + elif windowClassName=="MSCandUIWindow_Candidate": if role==oleacc.ROLE_SYSTEM_CLIENT: clsList.append(MSCandUIWindow) elif role==oleacc.ROLE_SYSTEM_LISTITEM: diff --git a/source/_UIAHandler.py b/source/_UIAHandler.py index 9be29b12526..efb72ce1b0a 100644 --- a/source/_UIAHandler.py +++ b/source/_UIAHandler.py @@ -33,6 +33,7 @@ UIA_SizeOfSetPropertyId=30153 badUIAWindowClassNames=[ + "Microsoft.IME.CandidateWindow.View", "SysTreeView32", "WuDuiListView", "ComboBox",