Skip to content

Commit

Permalink
Add accessibility support to SwitchButton on Windows (#804)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
LeonarddeR authored Sep 1, 2023
1 parent afe1fe8 commit b20d33a
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 1 deletion.
60 changes: 59 additions & 1 deletion src/custom_buttons.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<SwitchButton*>(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__
Expand Down
12 changes: 12 additions & 0 deletions src/custom_buttons.h
Original file line number Diff line number Diff line change
Expand Up @@ -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__
};
Expand Down

0 comments on commit b20d33a

Please sign in to comment.