Created
October 21, 2024 02:59
-
-
Save jcsteh/a7c94e4c52219ebe93d71882021ac916 to your computer and use it in GitHub Desktop.
NVDA app module for Microsoft Phone Link
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Copyright 2024 James Teh | |
# License: GNU General Public License version 2.0 | |
"""App module for Phone Link. | |
This does the following: | |
1. Works around the Phone Link crash when tabbing into the messages list. | |
2. Makes alt+1 focus the Conversations list when the Conversations view is | |
already active. Without this, alt+1 does nothing in this case. This makes it | |
much easier to switch conversations. | |
3. Adds alt+d to focus the message input text box. This is useful if you've | |
moved somewhere else in the Messages view and want to get back to the input | |
text box quickly. | |
""" | |
import api | |
import appModuleHandler | |
import controlTypes | |
import UIAHandler | |
from NVDAObjects.UIA import UIA | |
from scriptHandler import script | |
class AppModule(appModuleHandler.AppModule): | |
def _focusMessageList(self, container): | |
cond = UIAHandler.handler.clientObject.CreatePropertyCondition( | |
UIAHandler.UIA_ClassNamePropertyId, | |
"ListView" | |
) | |
el = container.UIAElement.FindFirst(UIAHandler.TreeScope_Children, cond) | |
if ( | |
not el | |
or not UIAHandler.handler.clientObject.RawViewWalker.GetFirstChildElement(el) | |
): | |
# The list isn't visible or it contains no items, so it can't get focus. | |
# Note that IsKeyboardFocusable returns false even when there are items. | |
return False | |
el.setFocus() | |
return True | |
def _isMessageLink(self, obj): | |
if obj.role != controlTypes.Role.LINK: | |
return False | |
try: | |
return obj.parent.parent.parent.role == controlTypes.Role.LISTITEM | |
except AttributeError: | |
return False | |
@script(gesture="kb:shift+tab") | |
def script_shiftTab(self, gesture): | |
# Shift+tabbing into the message list often crashes the app. However, | |
# programmatically focusing it seems to work fine. | |
focus = api.getFocusObject() | |
if ( | |
isinstance(focus, UIA) | |
and ( | |
focus.UIAElement.CurrentAutomationId == "InputTextBox" | |
or self._isMessageLink(focus) | |
) | |
and self._focusMessageList(focus.parent) | |
): | |
return | |
gesture.send() | |
@script(gesture="kb:tab") | |
def script_tab(self, gesture): | |
# Tabbing into the message list often crashes the app. However, | |
# programmatically focusing it seems to work fine. | |
focus = api.getFocusObject() | |
if ( | |
isinstance(focus, UIA) | |
and focus.UIAElement.CurrentAutomationId == "CallingFromMessagesButton" | |
and self._focusMessageList(focus.parent.parent) | |
): | |
return | |
gesture.send() | |
def _getRootUia(self): | |
return api.getFocusAncestors()[5].UIAElement | |
def _findConversationsList(self): | |
cond = UIAHandler.handler.clientObject.CreatePropertyCondition( | |
UIAHandler.UIA_AutomationIdPropertyId, | |
"CVSListView" | |
) | |
el = self._getRootUia().FindFirst(UIAHandler.TreeScope_Descendants, cond) | |
return el or None | |
@script(gesture="kb:alt+1") | |
def script_messagesView(self, gesture): | |
convList = self._findConversationsList() | |
if convList: | |
# The Conversations list is visible, which means the Messages view is | |
# active. Alt+1 won't do anything in this case. Focus the Conversations | |
# list to make it easier to switch conversations. | |
convList.setFocus() | |
return | |
# The Messages view isn't active, so let the app handle this. | |
gesture.send() | |
@script(gesture="kb:alt+d") | |
def script_focusInputTextBox(self, gesture): | |
cond = UIAHandler.handler.clientObject.CreatePropertyCondition( | |
UIAHandler.UIA_AutomationIdPropertyId, | |
"InputTextBox" | |
) | |
el = self._getRootUia().FindFirst(UIAHandler.TreeScope_Descendants, cond) | |
if el: | |
el.SetFocus() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment