mirror of
https://github.com/cookiengineer/audacity
synced 2025-07-22 07:28:03 +02:00
Bug1892: VoiceOver on Mac should read category names in Preferences...
... Achieved by implementing a wxAccessible subclass for wxTreeCtrl, but on Mac only. Leave be on Windows, just making the new type name an alias.
This commit is contained in:
parent
0a0baa39bb
commit
6248226163
@ -78,6 +78,326 @@
|
||||
#endif
|
||||
|
||||
|
||||
#if wxUSE_ACCESSIBILITY
|
||||
|
||||
#ifndef __WXMAC__
|
||||
|
||||
// Just an alias
|
||||
using TreeCtrlAx = WindowAccessible;
|
||||
|
||||
#else
|
||||
|
||||
// utility functions
|
||||
namespace {
|
||||
template< typename Result, typename Fn >
|
||||
Result VisitItems( const wxTreeCtrl &ctrl, Fn fn )
|
||||
{
|
||||
// Do preorder visit of items in the tree until satisfying a test
|
||||
std::vector< wxTreeItemId > stack;
|
||||
stack.push_back( ctrl.GetRootItem() );
|
||||
unsigned position = 0;
|
||||
while ( !stack.empty() ) {
|
||||
auto itemId = stack.back();
|
||||
auto pair = fn( itemId, position );
|
||||
if ( pair.first )
|
||||
return pair.second;
|
||||
|
||||
wxTreeItemIdValue cookie;
|
||||
auto childId = ctrl.GetFirstChild( itemId, cookie );
|
||||
if ( childId )
|
||||
stack.push_back( childId );
|
||||
else do {
|
||||
auto &id = stack.back();
|
||||
if ( !!( id = ctrl.GetNextSibling( id ) ) )
|
||||
break;
|
||||
} while ( stack.pop_back(), !stack.empty() );
|
||||
|
||||
++position;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
unsigned FindItemPosition( const wxTreeCtrl &ctrl, wxTreeItemId id )
|
||||
{
|
||||
// Return the 1-based count of the item's position in the pre-order
|
||||
// visit of the items in the tree (not counting the root item which we
|
||||
// assume is a dummy that never matches id)
|
||||
return VisitItems<unsigned>( ctrl,
|
||||
[=]( wxTreeItemId itemId, unsigned position ){
|
||||
return std::make_pair( itemId == id, position ); } );
|
||||
}
|
||||
|
||||
wxTreeItemId FindItem( const wxTreeCtrl &ctrl, int nn )
|
||||
{
|
||||
// The inverse of the function above
|
||||
return VisitItems<wxTreeItemId>( ctrl,
|
||||
[=]( wxTreeItemId itemId, unsigned position ){
|
||||
return std::make_pair( nn == position, itemId ); } );
|
||||
}
|
||||
}
|
||||
|
||||
// Define a custom class
|
||||
class TreeCtrlAx final
|
||||
: public WindowAccessible
|
||||
{
|
||||
public:
|
||||
TreeCtrlAx(wxTreeCtrl * ctrl);
|
||||
virtual ~ TreeCtrlAx();
|
||||
|
||||
wxAccStatus GetChild(int childId, wxAccessible** child) override;
|
||||
|
||||
wxAccStatus GetChildCount(int* childCount) override;
|
||||
|
||||
wxAccStatus GetDefaultAction(int childId, wxString *actionName) override;
|
||||
|
||||
// Returns the description for this object or a child.
|
||||
wxAccStatus GetDescription(int childId, wxString *description) override;
|
||||
|
||||
// Gets the window with the keyboard focus.
|
||||
// If childId is 0 and child is NULL, no object in
|
||||
// this subhierarchy has the focus.
|
||||
// If this object has the focus, child should be 'this'.
|
||||
wxAccStatus GetFocus(int *childId, wxAccessible **child) override;
|
||||
|
||||
// Returns help text for this object or a child, similar to tooltip text.
|
||||
wxAccStatus GetHelpText(int childId, wxString *helpText) override;
|
||||
|
||||
// Returns the keyboard shortcut for this object or child.
|
||||
// Return e.g. ALT+K
|
||||
wxAccStatus GetKeyboardShortcut(int childId, wxString *shortcut) override;
|
||||
|
||||
// Returns the rectangle for this object (id = 0) or a child element (id > 0).
|
||||
// rect is in screen coordinates.
|
||||
wxAccStatus GetLocation(wxRect& rect, int elementId) override;
|
||||
|
||||
// Gets the name of the specified object.
|
||||
wxAccStatus GetName(int childId, wxString *name) override;
|
||||
|
||||
// Returns a role constant.
|
||||
wxAccStatus GetRole(int childId, wxAccRole *role) override;
|
||||
|
||||
// Gets a variant representing the selected children
|
||||
// of this object.
|
||||
// Acceptable values:
|
||||
// - a null variant (IsNull() returns TRUE)
|
||||
// - a list variant (GetType() == wxT("list"))
|
||||
// - an integer representing the selected child element,
|
||||
// or 0 if this object is selected (GetType() == wxT("long"))
|
||||
// - a "void*" pointer to a wxAccessible child object
|
||||
//wxAccStatus GetSelections(wxVariant *selections) override;
|
||||
// leave unimplemented
|
||||
|
||||
// Returns a state constant.
|
||||
wxAccStatus GetState(int childId, long* state) override;
|
||||
|
||||
// Returns a localized string representing the value for the object
|
||||
// or child.
|
||||
wxAccStatus GetValue(int childId, wxString* strValue) override;
|
||||
|
||||
// Navigates from fromId to toId/toObject
|
||||
// wxAccStatus Navigate(wxNavDir navDir, int fromId, int* toId, wxAccessible** toObject) override;
|
||||
|
||||
// Modify focus or selection
|
||||
wxAccStatus Select(int childId, wxAccSelectionFlags selectFlags) override;
|
||||
|
||||
private:
|
||||
wxTreeCtrl *GetCtrl() { return static_cast<wxTreeCtrl*>( GetWindow() ); }
|
||||
};
|
||||
|
||||
TreeCtrlAx::TreeCtrlAx( wxTreeCtrl *ctrl )
|
||||
: WindowAccessible{ ctrl }
|
||||
{
|
||||
}
|
||||
|
||||
TreeCtrlAx::~TreeCtrlAx() = default;
|
||||
|
||||
wxAccStatus TreeCtrlAx::GetChild( int childId, wxAccessible** child )
|
||||
{
|
||||
if( childId == wxACC_SELF )
|
||||
{
|
||||
*child = this;
|
||||
}
|
||||
else
|
||||
{
|
||||
*child = NULL;
|
||||
}
|
||||
|
||||
return wxACC_OK;
|
||||
}
|
||||
|
||||
wxAccStatus TreeCtrlAx::GetChildCount(int* childCount)
|
||||
{
|
||||
auto ctrl = GetCtrl();
|
||||
if (!ctrl)
|
||||
return wxACC_FAIL;
|
||||
|
||||
*childCount = ctrl->GetCount();
|
||||
return wxACC_OK;
|
||||
}
|
||||
|
||||
wxAccStatus TreeCtrlAx::GetDefaultAction(int WXUNUSED(childId), wxString* actionName)
|
||||
{
|
||||
actionName->clear();
|
||||
|
||||
return wxACC_OK;
|
||||
}
|
||||
|
||||
// Returns the description for this object or a child.
|
||||
wxAccStatus TreeCtrlAx::GetDescription( int WXUNUSED(childId), wxString *description )
|
||||
{
|
||||
description->clear();
|
||||
|
||||
return wxACC_OK;
|
||||
}
|
||||
|
||||
// This isn't really used yet by wxWidgets as patched by Audacity for
|
||||
// Mac accessibility, as of Audacity 2.3.2, but here it is anyway, keeping the
|
||||
// analogy with TrackPanelAx
|
||||
wxAccStatus TreeCtrlAx::GetFocus( int *childId, wxAccessible **child )
|
||||
{
|
||||
auto ctrl = GetCtrl();
|
||||
if (!ctrl)
|
||||
return wxACC_FAIL;
|
||||
|
||||
auto item = ctrl->GetFocusedItem();
|
||||
auto id = FindItemPosition( *ctrl, item );
|
||||
*childId = id;
|
||||
*child = nullptr;
|
||||
return wxACC_OK;
|
||||
}
|
||||
|
||||
// Returns help text for this object or a child, similar to tooltip text.
|
||||
wxAccStatus TreeCtrlAx::GetHelpText( int WXUNUSED(childId), wxString *helpText )
|
||||
{
|
||||
helpText->clear();
|
||||
|
||||
return wxACC_OK;
|
||||
}
|
||||
|
||||
// Returns the keyboard shortcut for this object or child.
|
||||
// Return e.g. ALT+K
|
||||
wxAccStatus TreeCtrlAx::GetKeyboardShortcut( int WXUNUSED(childId), wxString *shortcut )
|
||||
{
|
||||
shortcut->clear();
|
||||
|
||||
return wxACC_OK;
|
||||
}
|
||||
|
||||
wxAccStatus TreeCtrlAx::GetLocation( wxRect& rect, int elementId )
|
||||
{
|
||||
auto ctrl = GetCtrl();
|
||||
if (!ctrl)
|
||||
return wxACC_FAIL;
|
||||
|
||||
if (elementId == wxACC_SELF)
|
||||
rect = ctrl->GetRect();
|
||||
else {
|
||||
auto item = FindItem( *ctrl, elementId );
|
||||
if ( !( item && ctrl->GetBoundingRect( item, rect ) ) )
|
||||
return wxACC_INVALID_ARG;
|
||||
}
|
||||
rect.SetPosition( ctrl->GetParent()->ClientToScreen( rect.GetPosition() ) );
|
||||
return wxACC_OK;
|
||||
}
|
||||
|
||||
wxAccStatus TreeCtrlAx::GetName(int childId, wxString* name)
|
||||
{
|
||||
if ( childId == wxACC_SELF )
|
||||
return WindowAccessible::GetName( childId, name );
|
||||
else {
|
||||
auto ctrl = GetCtrl();
|
||||
if (!ctrl)
|
||||
return wxACC_FAIL;
|
||||
|
||||
auto item = FindItem( *ctrl, childId );
|
||||
if ( item ) {
|
||||
*name = ctrl->GetItemText( item );
|
||||
return wxACC_OK;
|
||||
}
|
||||
else
|
||||
return wxACC_INVALID_ARG;
|
||||
}
|
||||
}
|
||||
|
||||
wxAccStatus TreeCtrlAx::GetRole( int childId, wxAccRole* role )
|
||||
{
|
||||
// Not sure if this correct, but it is analogous with what we use in
|
||||
// TrackPanel
|
||||
*role =
|
||||
childId == wxACC_SELF ? wxROLE_SYSTEM_PANE : wxROLE_SYSTEM_STATICTEXT;
|
||||
return wxACC_OK;
|
||||
}
|
||||
|
||||
// Returns a state constant.
|
||||
wxAccStatus TreeCtrlAx::GetState(int childId, long* state)
|
||||
{
|
||||
auto ctrl = GetCtrl();
|
||||
if (!ctrl)
|
||||
return wxACC_FAIL;
|
||||
|
||||
*state = wxACC_STATE_SYSTEM_FOCUSABLE | wxACC_STATE_SYSTEM_SELECTABLE;
|
||||
|
||||
if ( childId != wxACC_SELF ) {
|
||||
auto item = FindItem( *ctrl, childId );
|
||||
if (item) {
|
||||
if( item == ctrl->GetFocusedItem() )
|
||||
*state |= wxACC_STATE_SYSTEM_FOCUSED;
|
||||
|
||||
if( item == ctrl->GetSelection() )
|
||||
*state |= wxACC_STATE_SYSTEM_SELECTED;
|
||||
}
|
||||
}
|
||||
|
||||
return wxACC_OK;
|
||||
}
|
||||
|
||||
// Returns a localized string representing the value for the object
|
||||
// or child.
|
||||
wxAccStatus TreeCtrlAx::GetValue(int childId, wxString* strValue)
|
||||
{
|
||||
*strValue = wxString{};
|
||||
return wxACC_OK;
|
||||
}
|
||||
|
||||
//wxAccStatus TreeCtrlAx::Navigate(
|
||||
// wxNavDir navDir, int fromId, int* toId, wxAccessible** toObject)
|
||||
//{
|
||||
// to do
|
||||
//}
|
||||
|
||||
// Modify focus or selection
|
||||
wxAccStatus TreeCtrlAx::Select(int childId, wxAccSelectionFlags selectFlags)
|
||||
{
|
||||
auto ctrl = GetCtrl();
|
||||
if (!ctrl)
|
||||
return wxACC_FAIL;
|
||||
|
||||
if (childId != wxACC_SELF) {
|
||||
int childCount;
|
||||
GetChildCount( &childCount );
|
||||
if (childId > childCount)
|
||||
return wxACC_FAIL;
|
||||
|
||||
auto item = FindItem( *ctrl, childId );
|
||||
if ( item ) {
|
||||
if (selectFlags == wxACC_SEL_TAKEFOCUS)
|
||||
ctrl->SetFocusedItem( item );
|
||||
else if (selectFlags == wxACC_SEL_TAKESELECTION)
|
||||
ctrl->SelectItem( item );
|
||||
else
|
||||
return wxACC_NOT_IMPLEMENTED;
|
||||
return wxACC_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return wxACC_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// PrefsPanel might move out into its own file in due ocurse.
|
||||
PluginPath PrefsPanel::GetPath(){ return BUILTIN_PREFS_PANEL_PREFIX + GetSymbol().Internal(); }
|
||||
VendorSymbol PrefsPanel::GetVendor(){ return XO("Audacity");}
|
||||
@ -263,7 +583,8 @@ PrefsDialog::PrefsDialog
|
||||
mCategories = safenew wxTreebookExt(this, wxID_ANY, mTitlePrefix);
|
||||
#if wxUSE_ACCESSIBILITY
|
||||
// so that name can be set on a standard control
|
||||
mCategories->GetTreeCtrl()->SetAccessible(safenew WindowAccessible(mCategories->GetTreeCtrl()));
|
||||
mCategories->GetTreeCtrl()->SetAccessible(
|
||||
safenew TreeCtrlAx(mCategories->GetTreeCtrl()));
|
||||
#endif
|
||||
// RJH: Prevent NVDA from reading "treeCtrl"
|
||||
mCategories->GetTreeCtrl()->SetName(_("Category"));
|
||||
|
Loading…
x
Reference in New Issue
Block a user