Skip to content

Commit 7dd32d1

Browse files
Merge 3b51d1c into aa7049d
2 parents aa7049d + 3b51d1c commit 7dd32d1

6 files changed

Lines changed: 120 additions & 49 deletions

File tree

source/UIAHandler/__init__.py

Lines changed: 42 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@
2828
import time
2929
import IAccessibleHandler.internalWinEventHandler
3030
import config
31+
from config import (
32+
AllowUiaInChromium,
33+
AllowUiaInMSWord,
34+
)
3135
import api
3236
import appModuleHandler
3337
import controlTypes
@@ -222,18 +226,35 @@
222226
ignoreWinEventsMap[id] = [0]
223227

224228

225-
class AllowUiaInChromium(Enum):
226-
_DEFAULT = 0 # maps to 'when necessary'
227-
WHEN_NECESSARY = 1 # the current default
228-
YES = 2
229-
NO = 3
230-
231-
@staticmethod
232-
def getConfig() -> 'AllowUiaInChromium':
233-
allow = AllowUiaInChromium(config.conf['UIA']['allowInChromium'])
234-
if allow == AllowUiaInChromium._DEFAULT:
235-
return AllowUiaInChromium.WHEN_NECESSARY
236-
return allow
229+
def shouldUseUIAInMSWord(appModule: appModuleHandler.AppModule) -> bool:
230+
allow = AllowUiaInMSWord.getConfig()
231+
if allow == AllowUiaInMSWord.ALWAYS:
232+
log.debug("User has requested UIA in MS Word always")
233+
return True
234+
canUseOlderInProcessApproach = bool(appModule.helperLocalBindingHandle)
235+
if not canUseOlderInProcessApproach:
236+
log.debug("Using UIA in MS Word as no alternative object model available")
237+
return True
238+
if winVersion.getWinVer() < winVersion.WIN11:
239+
log.debug("Not using UIA in MS Word on pre Windows 11 OS due to missing custom extensions")
240+
return False
241+
if allow != AllowUiaInMSWord.WHERE_SUITABLE:
242+
log.debug("User does not want UIA in MS Word unless necessary")
243+
return False
244+
isOfficeApp = appModule.productName.startswith(("Microsoft Office", "Microsoft Outlook"))
245+
if not isOfficeApp:
246+
log.debug(f"Unknown Office app: {appModule.productName}")
247+
return False
248+
try:
249+
officeVersion = tuple(int(x) for x in appModule.productVersion.split('.')[:3])
250+
except Exception:
251+
log.debugWarning(f"Unable to parse office version: {appModule.productVersion}", exc_info=True)
252+
return False
253+
if officeVersion < (16, 0, 15000):
254+
log.debug(f"MS word too old for suitable UIA, Office version: {officeVersion}")
255+
return False
256+
log.debug(f"Using UIA due to suitable Office version: {officeVersion}")
257+
return True
237258

238259

239260
class UIAHandler(COMObject):
@@ -765,25 +786,15 @@ def _isUIAWindowHelper(self,hwnd):
765786
# Ask the window if it supports UIA natively
766787
res=windll.UIAutomationCore.UiaHasServerSideProvider(hwnd)
767788
if res:
768-
# The window does support UIA natively, but MS Word documents now
769-
# have a fairly usable UI Automation implementation.
770-
# However, builds of MS Office 2016 before build 9000 or so had bugs which
771-
# we cannot work around.
772-
# And even current builds of Office 2016 are still missing enough info from
773-
# UIA that it is still impossible to switch to UIA completely.
774-
# Therefore, if we can inject in-process, refuse to use UIA and instead
775-
# fall back to the MS Word object model.
776-
canUseOlderInProcessApproach = bool(appModule.helperLocalBindingHandle)
777-
if (
778-
# An MS Word document window
779-
windowClass == MS_WORD_DOCUMENT_WINDOW_CLASS
780-
# Disabling is only useful if we can inject in-process (and use our older code)
781-
and canUseOlderInProcessApproach
782-
# Allow the user to explicitly force UIA support for MS Word documents
783-
# no matter the Office version
784-
and not config.conf['UIA']['useInMSWordWhenAvailable']
785-
):
786-
return False
789+
if windowClass == MS_WORD_DOCUMENT_WINDOW_CLASS:
790+
# The window does support UIA natively, but MS Word documents now
791+
# have a fairly usable UI Automation implementation.
792+
# However, builds of MS Office 2016 before build 15000 or so had bugs which
793+
# we cannot work around.
794+
# Therefore, if we can inject in-process, refuse to use UIA and instead
795+
# fall back to the MS Word object model.
796+
if not shouldUseUIAInMSWord(appModule):
797+
return False
787798
# MS Excel spreadsheets now have a fairly usable UI Automation implementation.
788799
# However, builds of MS Office 2016 before build 9000 or so had bugs which we
789800
# cannot work around.

source/config/__init__.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1327,3 +1327,31 @@ def exit(self):
13271327

13281328
def __exit__(self, excType, excVal, traceback):
13291329
self.exit()
1330+
1331+
1332+
class AllowUiaInChromium(Enum):
1333+
_DEFAULT = 0 # maps to 'when necessary'
1334+
WHEN_NECESSARY = 1 # the current default
1335+
YES = 2
1336+
NO = 3
1337+
1338+
@staticmethod
1339+
def getConfig() -> 'AllowUiaInChromium':
1340+
allow = AllowUiaInChromium(conf['UIA']['allowInChromium'])
1341+
if allow == AllowUiaInChromium._DEFAULT:
1342+
return AllowUiaInChromium.WHEN_NECESSARY
1343+
return allow
1344+
1345+
1346+
class AllowUiaInMSWord(Enum):
1347+
_DEFAULT = 0 # maps to 'where suitable'
1348+
WHEN_NECESSARY = 1
1349+
WHERE_SUITABLE = 2
1350+
ALWAYS = 3
1351+
1352+
@staticmethod
1353+
def getConfig() -> 'AllowUiaInMSWord':
1354+
allow = AllowUiaInMSWord(conf['UIA']['allowInMSWord'])
1355+
if allow == AllowUiaInMSWord._DEFAULT:
1356+
return AllowUiaInMSWord.WHERE_SUITABLE
1357+
return allow

source/config/configSpec.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
#: provide an upgrade step (@see profileUpgradeSteps.py). An upgrade step does not need to be added when
1313
#: just adding a new element to (or removing from) the schema, only when old versions of the config
1414
#: (conforming to old schema versions) will not work correctly with the new schema.
15-
latestSchemaVersion = 5
15+
latestSchemaVersion = 6
1616

1717
#: The configuration specification string
1818
#: @type: String
@@ -229,12 +229,13 @@
229229
230230
[UIA]
231231
enabled = boolean(default=true)
232-
useInMSWordWhenAvailable = boolean(default=false)
233232
useInMSExcelWhenAvailable = boolean(default=false)
234233
winConsoleImplementation= option("auto", "legacy", "UIA", default="auto")
235234
selectiveEventRegistration = boolean(default=false)
236235
# 0:default, 1:Only when necessary, 2:yes, 3:no
237236
allowInChromium = integer(0, 3, default=0)
237+
# 0:default (where suitable), 1:Only when necessary, 2: where suitable, 3: always
238+
allowInMSWord = integer(0, 3, default=0)
238239
239240
[annotations]
240241
reportDetails = boolean(default=true)

source/config/profileUpgradeSteps.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
from logHandler import log
1818

19+
1920
def upgradeConfigFrom_0_to_1(profile):
2021
# Schema has been modified to set a new minimum blink rate
2122
# The blink rate could previously be set to zero to disable blinking (while still
@@ -84,3 +85,17 @@ def upgradeConfigFrom_4_to_5(profile):
8485
except KeyError:
8586
# Setting does not exist, no need for upgrade of this setting
8687
log.debug("reportDetails not present, no action taken.")
88+
89+
90+
def upgradeConfigFrom_5_to_6(profile: Dict):
91+
"""
92+
useInMSWordWhenAvailable in UIA section has been replaced with allowInMSWord multichoice.
93+
"""
94+
try:
95+
useInMSWord = profile['UIA']['useInMSWordWhenAvailable']
96+
del profile['UIA']['useInMSWordWhenAvailable']
97+
except KeyError:
98+
useInMSWord = False
99+
if useInMSWord:
100+
from . import AllowUiaInMSWord
101+
profile['UIA']['allowInMSWord'] = AllowUiaInMSWord.ALWAYS.value

source/gui/settingsDialogs.py

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2652,13 +2652,26 @@ def __init__(self, parent):
26522652
self._getDefaultValue(["UIA", "selectiveEventRegistration"])
26532653
)
26542654

2655-
# Translators: This is the label for a checkbox in the
2656-
# Advanced settings panel.
2657-
label = _("Use UI Automation to access Microsoft &Word document controls when available")
2658-
self.UIAInMSWordCheckBox = UIAGroup.addItem(wx.CheckBox(UIABox, label=label))
2659-
self.bindHelpEvent("AdvancedSettingsUseUiaForWord", self.UIAInMSWordCheckBox)
2660-
self.UIAInMSWordCheckBox.SetValue(config.conf["UIA"]["useInMSWordWhenAvailable"])
2661-
self.UIAInMSWordCheckBox.defaultValue = self._getDefaultValue(["UIA", "useInMSWordWhenAvailable"])
2655+
label = pgettext(
2656+
"advanced.uiaWithMSWord",
2657+
# Translators: Label for the Use UIA with MS Word combobox, in the Advanced settings panel.
2658+
"Use UI Automation to access Microsoft Word document controls"
2659+
)
2660+
wordChoices = (
2661+
# Translators: Label for the default value of the Use UIA with MS Word combobox,
2662+
# in the Advanced settings panel.
2663+
pgettext("advanced.uiaWithMSWord", "Default (Where suitable)"),
2664+
# Translators: Label for a value in the Use UIA with MS Word combobox, in the Advanced settings panel.
2665+
pgettext("advanced.uiaWithMSWord", "Only when necessary"),
2666+
# Translators: Label for a value in the Use UIA with MS Word combobox, in the Advanced settings panel.
2667+
pgettext("advanced.uiaWithMSWord", "Where suitable"),
2668+
# Translators: Label for a value in the Use UIA with MS Word combobox, in the Advanced settings panel.
2669+
pgettext("advanced.uiaWithMSWord", "Always"),
2670+
)
2671+
self.UIAInMSWordCombo = UIAGroup.addLabeledControl(label, wx.Choice, choices=wordChoices)
2672+
self.bindHelpEvent("MSWordUIA", self.UIAInMSWordCombo)
2673+
self.UIAInMSWordCombo.SetSelection(config.conf["UIA"]["allowInMSWord"])
2674+
self.UIAInMSWordCombo.defaultValue = self._getDefaultValue(["UIA", "allowInMSWord"])
26622675

26632676
# Translators: This is the label for a checkbox in the
26642677
# Advanced settings panel.
@@ -2965,7 +2978,7 @@ def haveConfigDefaultsBeenRestored(self):
29652978
self.selectiveUIAEventRegistrationCheckBox.IsChecked()
29662979
== self.selectiveUIAEventRegistrationCheckBox.defaultValue
29672980
)
2968-
and self.UIAInMSWordCheckBox.IsChecked() == self.UIAInMSWordCheckBox.defaultValue
2981+
and self.UIAInMSWordCombo.GetSelection() == self.UIAInMSWordCombo.defaultValue
29692982
and self.UIAInMSExcelCheckBox.IsChecked() == self.UIAInMSExcelCheckBox.defaultValue
29702983
and self.ConsoleUIACheckBox.IsChecked() == (self.ConsoleUIACheckBox.defaultValue == 'UIA')
29712984
and self.cancelExpiredFocusSpeechCombo.GetSelection() == self.cancelExpiredFocusSpeechCombo.defaultValue
@@ -2985,7 +2998,7 @@ def haveConfigDefaultsBeenRestored(self):
29852998
def restoreToDefaults(self):
29862999
self.scratchpadCheckBox.SetValue(self.scratchpadCheckBox.defaultValue)
29873000
self.selectiveUIAEventRegistrationCheckBox.SetValue(self.selectiveUIAEventRegistrationCheckBox.defaultValue)
2988-
self.UIAInMSWordCheckBox.SetValue(self.UIAInMSWordCheckBox.defaultValue)
3001+
self.UIAInMSWordCombo.SetSelection(self.UIAInMSWordCombo.defaultValue)
29893002
self.UIAInMSExcelCheckBox.SetValue(self.UIAInMSExcelCheckBox.defaultValue)
29903003
self.ConsoleUIACheckBox.SetValue(self.ConsoleUIACheckBox.defaultValue == 'UIA')
29913004
self.UIAInChromiumCombo.SetSelection(self.UIAInChromiumCombo.defaultValue)
@@ -3005,7 +3018,7 @@ def onSave(self):
30053018
log.debug("Saving advanced config")
30063019
config.conf["development"]["enableScratchpadDir"]=self.scratchpadCheckBox.IsChecked()
30073020
config.conf["UIA"]["selectiveEventRegistration"] = self.selectiveUIAEventRegistrationCheckBox.IsChecked()
3008-
config.conf["UIA"]["useInMSWordWhenAvailable"]=self.UIAInMSWordCheckBox.IsChecked()
3021+
config.conf["UIA"]["allowInMSWord"] = self.UIAInMSWordCombo.GetSelection()
30093022
config.conf["UIA"]["useInMSExcelWhenAvailable"] = self.UIAInMSExcelCheckBox.IsChecked()
30103023
if self.ConsoleUIACheckBox.IsChecked():
30113024
config.conf['UIA']['winConsoleImplementation'] = "UIA"

user_docs/en/userGuide.t2t

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1864,12 +1864,15 @@ This has a major negative impact on performance, especially in applications like
18641864
Therefore, when this option is enabled, NVDA will limit event registration to the system focus for most events.
18651865
If you suffer from performance issues in one or more applications, We recommend you to try this functionality to see whether performance improves.
18661866

1867-
==== Use UI automation to access Microsoft Word document controls when available ====[AdvancedSettingsUseUiaForWord]
1868-
When this option is enabled, NVDA will try to use the Microsoft UI Automation accessibility API in order to fetch information from Microsoft Word document controls.
1869-
This includes Microsoft Word itself, and also the Microsoft Outlook message viewer and composer.
1870-
For the most recent versions of Microsoft Office 2016/365 running on Windows 10 and later, UI Automation support is complete enough to provide access to Microsoft Word documents almost equal to NVDA's existing Microsoft Word support, with the added advantage that responsiveness is majorly increased.
1871-
However, There may be some information which is either not exposed, or exposed incorrectly in some versions of Microsoft Office, which means this UI automation support cannot always be relied upon.
1872-
We still do not recommend that the majority of users turn this on by default, though we do welcome users of Office 2016/365 to test this feature and provide feedback.
1867+
==== Use UI automation to access Microsoft Word document controls ====[MSWordUIA]
1868+
Configures whether or not NVDA should use the UI Automation accessibility API to access Microsoft Word documents, rather than the older Microsoft Word object model.
1869+
This applies to documents in Microsoft word itself, plus messages in Microsoft Outlook.
1870+
This setting contains the following values:
1871+
- Default (where suitable)
1872+
- Only where necessary: where the Microsoft Word object model is not available at all
1873+
- Where suitable: Microsoft Word version 16.0.15000 or higher, or where the Microsoft Word object model is unavailable
1874+
- Always: where ever UI automation is available in Microsoft word (no matter how complete).
1875+
-
18731876

18741877
==== Use UI Automation to access the Windows Console when available ====[AdvancedSettingsConsoleUIA]
18751878
When this option is enabled, NVDA will use a new, work in progress version of its support for Windows Console which takes advantage of [accessibility improvements made by Microsoft https://devblogs.microsoft.com/commandline/whats-new-in-windows-console-in-windows-10-fall-creators-update/]. This feature is highly experimental and is still incomplete, so its use is not yet recommended. However, once completed, it is anticipated that this new support will become the default, improving NVDA's performance and stability in Windows command consoles.

0 commit comments

Comments
 (0)