From b20d33aab1653429a05984c546fdec9287a0270a Mon Sep 17 00:00:00 2001 From: Leonard de Ruijter Date: Fri, 1 Sep 2023 18:57:46 +0200 Subject: [PATCH] Add accessibility support to SwitchButton on Windows (#804) There is no reliable way for screen reader users to find out the fuzzy (needs work) status. After some research, the most likely cause is as follows: 1. SwitchButton is owner drawn and therefore sets the BS_OWNERDRAW window style 2. When BS_OWNERDRAW is set, all other window styles should be ignored according to the docs 3. The default MSAA implementation in Windows ignores other window styles. As it relies on window style to find out whether the button is either a push button or a check box, this information is lost, and MSAA treats the toggle as a plain button without checkable state. Fixed by implementing wxAccessible for the custom button. Fixes #693. --- src/custom_buttons.cpp | 60 +++++++++++++++++++++++++++++++++++++++++- src/custom_buttons.h | 12 +++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/src/custom_buttons.cpp b/src/custom_buttons.cpp index 41b321bc0d..f23e3fc716 100644 --- a/src/custom_buttons.cpp +++ b/src/custom_buttons.cpp @@ -407,9 +407,24 @@ SwitchButton::SwitchButton(wxWindow *parent, wxWindowID winid, const wxString& l SetBackgroundColour(parent->GetBackgroundColour()); MakeOwnerDrawn(); Bind(wxEVT_LEFT_DOWN, &SwitchButton::OnMouseClick, this); -#endif +#if wxUSE_ACCESSIBILITY + Bind(wxEVT_TOGGLEBUTTON, [=](wxCommandEvent& e) { + wxAccessible::NotifyEvent(wxACC_EVENT_OBJECT_STATECHANGE, this, wxOBJID_CLIENT, wxACC_SELF); + e.Skip(); + }); +#endif // wxUSE_ACCESSIBILITY +#endif // __WXMSW__ } +#ifdef __WXMSW__ +#if wxUSE_ACCESSIBILITY +wxAccessible* SwitchButton::CreateAccessible() +{ + return new accessible(this); +} +#endif // wxUSE_ACCESSIBILITY +#endif // __WXMSW__ + void SwitchButton::SetColors(const wxColour& on, const wxColour& offLabel) { (void)on; @@ -546,6 +561,49 @@ bool SwitchButton::MSWOnDraw(WXDRAWITEMSTRUCT *wxdis) return true; } +#if wxUSE_ACCESSIBILITY +wxAccStatus SwitchButton::accessible::GetRole(int childId, wxAccRole* role) +{ + if (childId != wxACC_SELF) + { + return wxAccessible::GetRole(childId, role); + } + *role = wxROLE_SYSTEM_CHECKBUTTON; + return wxACC_OK; +} + +wxAccStatus SwitchButton::accessible::GetState(int childId, long* state) +{ + if (childId != wxACC_SELF) + { + return wxAccessible::GetState(childId, state); + } + auto window = dynamic_cast(this->GetWindow()); + if (window->IsFocusable()) + { + *state |= wxACC_STATE_SYSTEM_FOCUSABLE; + } + if (!window->IsShown()) + { + *state |= wxACC_STATE_SYSTEM_INVISIBLE; + } + if (window->GetValue()) + { + *state |= wxACC_STATE_SYSTEM_CHECKED; + } + if (!window->IsEnabled()) + { + *state |= wxACC_STATE_SYSTEM_UNAVAILABLE; + } + if (window->HasFocus()) + { + *state |= wxACC_STATE_SYSTEM_FOCUSED; + } + return wxACC_OK; +} + +#endif // wxUSE_ACCESSIBILITY + #endif // __WXMSW__ #endif // !__WXOSX__ diff --git a/src/custom_buttons.h b/src/custom_buttons.h index eced948910..48057330d0 100644 --- a/src/custom_buttons.h +++ b/src/custom_buttons.h @@ -114,12 +114,24 @@ class SwitchButton : public wxToggleButton void SetColors(const wxColour& on, const wxColour& offLabel); #ifdef __WXMSW__ +#if wxUSE_ACCESSIBILITY + wxAccessible* CreateAccessible() override; +#endif // wxUSE_ACCESSIBILITY bool ShouldInheritColours() const override { return true; } void OnMouseClick(wxMouseEvent& e); bool MSWOnDraw(WXDRAWITEMSTRUCT *wxdis) override; wxSize DoGetBestSize() const override; private: +#if wxUSE_ACCESSIBILITY + class accessible : public wxAccessible + { + public: + accessible(SwitchButton* win) : wxAccessible(wxDynamicCast(win, wxWindow)) {} + wxAccStatus GetRole(int childId, wxAccRole* role) override; + wxAccStatus GetState(int childId, long* state) override; + }; +#endif // wxUSE_ACCESSIBILITY wxColour m_clrOn, m_clrOffLabel; #endif // __WXMSW__ };