Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 0 additions & 178 deletions source/gui/accPropServer.py

This file was deleted.

93 changes: 21 additions & 72 deletions source/gui/nvdaControls.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import wx
from comtypes import GUID
from wx.lib.mixins import listctrl as listmix
from . import accPropServer
from .dpiScalingHelper import DpiScalingHelperMixin
from . import guiHelper
import oleacc
Expand Down Expand Up @@ -86,85 +85,35 @@ def OnSetFocus(self, evt):
self.SetSelection(0, numChars)
evt.Skip()

class AccPropertyOverride(accPropServer.IAccPropServer_Impl):

def __init__(self, control, propertyAnnotations):
"""
A simple class for overriding specific values on a control
:type propertyAnnotations: dict
"""
super(AccPropertyOverride, self).__init__(
control,
annotateProperties=list(propertyAnnotations.keys()),
annotateChildren=False
)
self.propertyAnnotations = propertyAnnotations
class ListCtrlAccessible(wx.Accessible):
"""WX Accessible implementation for checkable lists which aren't fully accessible."""

def _getPropValue(self, pIDString, dwIDStringLen, idProp):
control = self.control() # self.control held as a weak ref, ensure it stays alive for the duration of this method
if control is None or not self.propertyAnnotations:
return None
def GetRole(self, childId):
if childId == winUser.CHILDID_SELF:
return super().GetRole(childId)
return (wx.ACC_OK, wx.ROLE_SYSTEM_CHECKBUTTON)

try:
val = self.propertyAnnotations[idProp]
if callable(val):
val = val()
return self._hasProp(val)
except KeyError:
pass

return None

def _cleanup(self):
# could contain references (via lambda) of our owner, set it to None to avoid a circular reference which
# would block destruction.
self.propertyAnnotations = None
super(AccPropertyOverride, self)._cleanup()

class ListCtrlAccPropServer(accPropServer.IAccPropServer_Impl):
"""AccPropServer for wx checkable lists which aren't fully accessible."""

def __init__(self, control):
super(ListCtrlAccPropServer, self).__init__(
control,
annotateProperties=[
oleacc.PROPID_ACC_ROLE, # supposed to be checkbox, rather than list item
oleacc.PROPID_ACC_STATE # should include the checkable state and checked state if the item is checked.
],
annotateChildren=True
)
def GetState(self, childId):
if childId == winUser.CHILDID_SELF:
return super().GetState(childId)
states = wx.ACC_STATE_SYSTEM_SELECTABLE | wx.ACC_STATE_SYSTEM_FOCUSABLE
if self.Window.IsChecked(childId - 1):
states |= wx.ACC_STATE_SYSTEM_CHECKED
if self.Window.IsSelected(childId - 1):
# wx doesn't seem to have a method to check whether a list item is focused.
# Therefore, assume that a selected item is focused,which is the case in single select list boxes.
states |= wx.ACC_STATE_SYSTEM_SELECTED | wx.ACC_STATE_SYSTEM_FOCUSED
return (wx.ACC_OK, states)

def _getPropValue(self, pIDString: str, dwIDStringLen: int, idProp: GUID) -> Optional[Tuple[BOOL, Any]]:
control = self.control() # self.control held as a weak ref, ensure it stays alive for the duration of this method
if control is None:
return None

# Import late to prevent circular import.
from IAccessibleHandler import accPropServices
handle, objid, childid = accPropServices.DecomposeHwndIdentityString(pIDString, dwIDStringLen)
if childid == winUser.CHILDID_SELF:
return None

if idProp == oleacc.PROPID_ACC_ROLE:
return self._hasProp(oleacc.ROLE_SYSTEM_CHECKBUTTON)

if idProp == oleacc.PROPID_ACC_STATE:
states = oleacc.STATE_SYSTEM_SELECTABLE|oleacc.STATE_SYSTEM_FOCUSABLE
if control.IsChecked(childid-1):
states |= oleacc.STATE_SYSTEM_CHECKED
if control.IsSelected(childid-1):
# wx doesn't seem to have a method to check whether a list item is focused.
# Therefore, assume that a selected item is focused,which is the case in single select list boxes.
states |= oleacc.STATE_SYSTEM_SELECTED | oleacc.STATE_SYSTEM_FOCUSED
return self._hasProp(states)

class CustomCheckListBox(wx.CheckListBox):
"""Custom checkable list to fix a11y bugs in the standard wx checkable list box."""

def __init__(self, *args, **kwargs):
super(CustomCheckListBox, self).__init__(*args, **kwargs)
# Register object with COM to fix accessibility bugs in wx.
self.server = ListCtrlAccPropServer(self)
# Register a custom wx.Accessible implementation to fix accessibility incompleties
self.SetAccessible(ListCtrlAccessible(self))
# Register ourself with ourself's selected event, so that we can notify winEvent of the state change.
self.Bind(wx.EVT_CHECKLISTBOX, self.notifyIAccessible)

Expand All @@ -189,8 +138,8 @@ def __init__(self, parent, id=wx.ID_ANY, autoSizeColumn="LAST", pos=wx.DefaultPo
):
AutoWidthColumnListCtrl.__init__(self, parent, id=id, pos=pos, size=size, style=style, autoSizeColumn=autoSizeColumn)
listmix.CheckListCtrlMixin.__init__(self, check_image, uncheck_image, imgsz)
# Register object with COM to fix accessibility bugs in wx.
self.server = ListCtrlAccPropServer(self)
# Register a custom wx.Accessible implementation to fix accessibility incompleties
self.SetAccessible(ListCtrlAccessible(self))
# Register our hook to check/uncheck items with space.
# Use wx.EVT_CHAR_HOOK, because EVT_LIST_KEY_DOWN isn't triggered for space.
self.Bind(wx.EVT_CHAR_HOOK, self.onCharHook)
Expand Down
25 changes: 17 additions & 8 deletions source/gui/settingsDialogs.py
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,22 @@ def _sendLayoutUpdatedEvent(self):
event.SetEventObject(self)
self.GetEventHandler().ProcessEvent(event)


class SettingsPanelAccessible(wx.Accessible):
"""
WX Accessible implementation to set the role of a settings panel to property page,
as well as to set the accessible description based on the panel's description.
"""

Window: SettingsPanel

def GetRole(self, childId):
return (wx.ACC_OK, wx.ROLE_SYSTEM_PROPERTYPAGE)

def GetDescription(self, childId):
return (wx.ACC_OK, self.Window.panelDescription)


class MultiCategorySettingsDialog(SettingsDialog):
"""A settings dialog with multiple settings categories.
A multi category settings dialog consists of a list view with settings categories on the left side,
Expand Down Expand Up @@ -524,14 +540,7 @@ def _getCategoryPanel(self, catId):
).format(cls, panel.Size[0])
)
panel.SetLabel(panel.title)
import oleacc
panel.server = nvdaControls.AccPropertyOverride(
panel,
propertyAnnotations={
oleacc.PROPID_ACC_ROLE: oleacc.ROLE_SYSTEM_PROPERTYPAGE, # change the role from pane to property page
oleacc.PROPID_ACC_DESCRIPTION: panel.panelDescription, # set a description
}
)
panel.SetAccessible(SettingsPanelAccessible(panel))
return panel

def postInit(self):
Expand Down
1 change: 1 addition & 0 deletions user_docs/en/changes.t2t
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ What's New in NVDA
- `speech.re_last_pause` has been removed - please use `speech.SpeechWithoutPauses.re_last_pause` instead. (#12195)
- `WelcomeDialog`, `LauncherDialog` and `AskAllowUsageStatsDialog` are moved to the `gui.startupDialogs`. (#12105)
- `getDocFilePath` has been moved from `gui` to the `documentationUtils` module. (#12105)
- The gui.accPropServer module as well as the AccPropertyOverride and ListCtrlAccPropServer classes from the gui.nvdaControls module have been removed in favor of WX' native support for overriding accessibility properties. When enhancing accessibility of WX controls, implement wx.Accessible instead. (#12215)


= 2020.4 =
Expand Down