mirror of
https://github.com/wxWidgets/wxWidgets.git
synced 2026-10-06 07:00:16 +08:00
Use preceding label as accessibility title of controls under macOS
Under MSW, screen readers use the label preceding a control without its own label, such as wxChoice or wxTextCtrl, as its name, so dialogs are accessible without doing anything special. Under macOS this didn't happen, and VoiceOver only read the labels as separate texts, so users tabbing through a dialog didn't hear what the controls were for. Now, when a native control without its own label is created right after a wxStaticText, the label is set as its accessibility title element, which VoiceOver reads as the name of the control. Notice that for most controls the accessibility element is the cell and not the view itself, so this uses NSAccessibilityUnignoredDescendant() to link the elements actually used by VoiceOver, and uses the document view for the controls inside a scroll view. Closes #27060.
This commit is contained in:
committed by
Vadim Zeitlin
parent
787dcd2de0
commit
07590bec33
@@ -169,6 +169,7 @@ public :
|
||||
void SetFont(const wxFont & font) override;
|
||||
void SetToolTip( wxToolTip* tooltip ) override;
|
||||
void SetAccessibilityLabel(const wxString& label) override;
|
||||
void SetAccessibilityTitleElement(wxWidgetImpl* title) override;
|
||||
|
||||
void InstallEventHandler( WXWidget control = nullptr ) override;
|
||||
bool EnableTouchEvents(int eventsMask) override;
|
||||
|
||||
@@ -397,6 +397,7 @@ public :
|
||||
virtual void SetToolTip(wxToolTip* WXUNUSED(tooltip)) { }
|
||||
|
||||
virtual void SetAccessibilityLabel(const wxString& WXUNUSED(label)) { }
|
||||
virtual void SetAccessibilityTitleElement(wxWidgetImpl* WXUNUSED(title)) { }
|
||||
|
||||
// is the clicked event sent AFTER the state already changed, so no additional
|
||||
// state changing logic is required from the outside
|
||||
|
||||
@@ -38,6 +38,8 @@ public:
|
||||
|
||||
virtual bool AcceptsFocus() const override { return false; }
|
||||
|
||||
virtual wxOSXWidgetImpl* GetLabelPeer() const override { return GetPeer(); }
|
||||
|
||||
protected :
|
||||
|
||||
virtual wxString WXGetVisibleLabel() const override;
|
||||
|
||||
@@ -280,6 +280,10 @@ public:
|
||||
// the 'true' OS level control for this wxWindow
|
||||
wxOSXWidgetImpl* GetPeer() const;
|
||||
|
||||
// the peer of this window if it is a label which can be used as the
|
||||
// accessibility title of the next control, or nullptr otherwise
|
||||
virtual wxOSXWidgetImpl* GetLabelPeer() const { return nullptr; }
|
||||
|
||||
// optimization to avoid creating a user pane in wxWindow::Create if we already know
|
||||
// we will replace it with our own peer
|
||||
void DontCreatePeer();
|
||||
|
||||
@@ -4042,6 +4042,24 @@ void wxWidgetCocoaImpl::SetAccessibilityLabel(const wxString& label)
|
||||
[view setAccessibilityLabel:str];
|
||||
}
|
||||
|
||||
void wxWidgetCocoaImpl::SetAccessibilityTitleElement(wxWidgetImpl* title)
|
||||
{
|
||||
// VoiceOver reads the view inside a scroll view, e.g. the text view of a
|
||||
// multiline wxTextCtrl, and not the scroll view itself.
|
||||
NSView* view = m_osxView;
|
||||
if ( [view isKindOfClass:[NSScrollView class]] )
|
||||
{
|
||||
NSView* const documentView = [(NSScrollView*)view documentView];
|
||||
if ( documentView )
|
||||
view = documentView;
|
||||
}
|
||||
|
||||
// For most controls the accessibility element is not the view itself but
|
||||
// its cell, so link the elements actually used by VoiceOver.
|
||||
[NSAccessibilityUnignoredDescendant(view) setAccessibilityTitleUIElement:
|
||||
NSAccessibilityUnignoredDescendant(title->GetWXWidget())];
|
||||
}
|
||||
|
||||
void wxWidgetCocoaImpl::InstallEventHandler( WXWidget control )
|
||||
{
|
||||
WXWidget c = control ? control : (WXWidget) m_osxView;
|
||||
|
||||
@@ -453,6 +453,17 @@ void wxWindowMac::MacPostControlCreate(const wxPoint& pos,
|
||||
}
|
||||
#endif
|
||||
|
||||
// Controls without their own label are typically preceded by a label
|
||||
// describing them, which screen readers use as their name under MSW, so
|
||||
// do the same here.
|
||||
if ( GetLabel().empty() && !GetLabelPeer() )
|
||||
{
|
||||
const wxWindow* const prev = GetPrevSibling();
|
||||
wxOSXWidgetImpl* const labelPeer = prev ? prev->GetLabelPeer() : nullptr;
|
||||
if ( labelPeer )
|
||||
GetPeer()->SetAccessibilityTitleElement(labelPeer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void wxWindowMac::DoSetWindowVariant( wxWindowVariant variant )
|
||||
|
||||
Reference in New Issue
Block a user