diff --git a/src/TrackPanel.cpp b/src/TrackPanel.cpp index 80e1d4ea2..e8ac38025 100644 --- a/src/TrackPanel.cpp +++ b/src/TrackPanel.cpp @@ -266,7 +266,16 @@ TrackPanel::TrackPanel(wxWindow * parent, wxWindowID id, SetBackgroundStyle(wxBG_STYLE_PAINT); { - auto pAx = std::make_unique ( this ); + auto pAx = std::make_unique ( *project ); + pAx->SetWindow( this ); + wxWeakRef< TrackPanel > weakThis{ this }; + pAx->SetFinder( + [weakThis]( const Track &track ) -> wxRect { + if (weakThis) + return weakThis->FindTrackRect( &track ); + return {}; + } + ); #if wxUSE_ACCESSIBILITY // wxWidgets owns the accessible object SetAccessible(mAx = pAx.release()); @@ -305,6 +314,8 @@ TrackPanel::TrackPanel(wxWindow * parent, wxWindowID id, theProject->Bind(EVT_ODTASK_COMPLETE, &TrackPanel::OnODTask, this); theProject->Bind( EVT_PROJECT_SETTINGS_CHANGE, &TrackPanel::OnProjectSettingsChange, this); + theProject->Bind( + EVT_TRACK_FOCUS_CHANGE, &TrackPanel::OnTrackFocusChange, this ); theProject->Bind(EVT_UNDO_RESET, &TrackPanel::OnUndoReset, this); @@ -706,12 +717,6 @@ void TrackPanel::UpdateAccessibility() mAx->Updated(); } -// Counts tracks, counting stereo tracks as one track. -size_t TrackPanel::GetTrackCount() const -{ - return GetTracks()->Leaders().size(); -} - // Counts selected tracks, counting stereo tracks as one track. size_t TrackPanel::GetSelectedTrackCount() const { @@ -1670,7 +1675,14 @@ void TrackPanel::SetFocusedTrack( Track *t ) // Make sure we always have the first linked track of a stereo track t = *GetTracks()->FindLeader(t); - auto cell = mAx->SetFocus( Track::SharedPointer( t ) ).get(); + // This will cause callback to the handler function, defined next + mAx->SetFocus( Track::SharedPointer( t ) ); +} + +void TrackPanel::OnTrackFocusChange( wxCommandEvent &event ) +{ + event.Skip(); + auto cell = mAx->GetFocus().get(); if (cell) { KeyboardCapture::Capture(this); diff --git a/src/TrackPanel.h b/src/TrackPanel.h index 57c6c061e..5c6a3bea5 100644 --- a/src/TrackPanel.h +++ b/src/TrackPanel.h @@ -100,6 +100,7 @@ class AUDACITY_DLL_API TrackPanel final void OnTimer(wxTimerEvent& event); void OnODTask(wxCommandEvent &event); void OnProjectSettingsChange(wxCommandEvent &event); + void OnTrackFocusChange( wxCommandEvent &event ); int GetLeftOffset() const { return GetLabelWidth() + 1;} @@ -148,7 +149,6 @@ class AUDACITY_DLL_API TrackPanel final bool IsAudioActive(); public: - size_t GetTrackCount() const; size_t GetSelectedTrackCount() const; protected: diff --git a/src/TrackPanelAx.cpp b/src/TrackPanelAx.cpp index d1e8f125a..75585a92c 100644 --- a/src/TrackPanelAx.cpp +++ b/src/TrackPanelAx.cpp @@ -30,16 +30,20 @@ #include +#include "Project.h" #include "Track.h" -#include "TrackPanel.h" -TrackPanelAx::TrackPanelAx( wxWindow *window ) + +wxDEFINE_EVENT(EVT_TRACK_FOCUS_CHANGE, wxCommandEvent); + +TrackPanelAx::TrackPanelAx( AudacityProject &project ) + : #if wxUSE_ACCESSIBILITY - :WindowAccessible( window ) + WindowAccessible( nullptr ) // window pointer must be set after construction + , #endif + mProject{ project } { - mTrackPanel = wxDynamicCast( window, TrackPanel ); - mTrackName = true; mMessageCount = 0; mNumFocusedTrack = 0; @@ -49,6 +53,11 @@ TrackPanelAx::~TrackPanelAx() { } +TrackList &TrackPanelAx::GetTracks() +{ + return TrackList::Get( mProject ); +} + // Returns currently focused track // if that track no longer exists, if there is a track at // the same position, use that, else if there is a first @@ -65,7 +74,7 @@ std::shared_ptr TrackPanelAx::GetFocus() } if (!focusedTrack) { focusedTrack = - Track::SharedPointer( *mTrackPanel->GetTracks()->Any().first ); + Track::SharedPointer( *GetTracks().Any().first ); // only call SetFocus if the focus has changed to avoid // unnecessary focus events if (focusedTrack) @@ -93,25 +102,28 @@ std::shared_ptr TrackPanelAx::SetFocus( std::shared_ptr track ) if( focusedTrack && !focusedTrack->GetSelected() ) { NotifyEvent( wxACC_EVENT_OBJECT_SELECTIONREMOVE, - mTrackPanel, + GetWindow(), wxOBJID_CLIENT, TrackNum( focusedTrack ) ); } #endif if( !track ) - track = Track::SharedPointer( *mTrackPanel->GetTracks()->Any().begin() ); + track = Track::SharedPointer( *GetTracks().Any().begin() ); - mFocusedTrack = track; + if ( mFocusedTrack.lock() != track ) { + mFocusedTrack = track; + mProject.QueueEvent( safenew wxCommandEvent{ EVT_TRACK_FOCUS_CHANGE } ); + } mNumFocusedTrack = TrackNum(track); #if wxUSE_ACCESSIBILITY if( track ) { - if (mTrackPanel == wxWindow::FindFocus()) + if (GetWindow() == wxWindow::FindFocus()) { NotifyEvent( wxACC_EVENT_OBJECT_FOCUS, - mTrackPanel, + GetWindow(), wxOBJID_CLIENT, mNumFocusedTrack ); } @@ -119,7 +131,7 @@ std::shared_ptr TrackPanelAx::SetFocus( std::shared_ptr track ) if( track->GetSelected() ) { NotifyEvent( wxACC_EVENT_OBJECT_SELECTION, - mTrackPanel, + GetWindow(), wxOBJID_CLIENT, mNumFocusedTrack ); } @@ -127,7 +139,7 @@ std::shared_ptr TrackPanelAx::SetFocus( std::shared_ptr track ) else { NotifyEvent(wxACC_EVENT_OBJECT_FOCUS, - mTrackPanel, + GetWindow(), wxOBJID_CLIENT, wxACC_SELF); } @@ -146,7 +158,7 @@ bool TrackPanelAx::IsFocused( const Track *track ) // Remap track pointer if there are oustanding pending updates auto origTrack = - mTrackPanel->GetTracks()->FindById( track->GetId() ); + GetTracks().FindById( track->GetId() ); if (origTrack) track = origTrack; @@ -161,7 +173,7 @@ int TrackPanelAx::TrackNum( const std::shared_ptr &target ) // found int ndx = 0; - for ( auto t : mTrackPanel->GetTracks()->Leaders() ) + for ( auto t : GetTracks().Leaders() ) { ndx++; if( t == target.get() ) @@ -177,7 +189,7 @@ std::shared_ptr TrackPanelAx::FindTrack( int num ) { int ndx = 0; - for ( auto t : mTrackPanel->GetTracks()->Leaders() ) + for ( auto t : GetTracks().Leaders() ) { ndx++; if( ndx == num ) @@ -196,12 +208,12 @@ void TrackPanelAx::Updated() // The object_focus event is only needed by Window-Eyes // and can be removed when we cease to support this screen reader. NotifyEvent(wxACC_EVENT_OBJECT_FOCUS, - mTrackPanel, + GetWindow(), wxOBJID_CLIENT, TrackNum(t)); NotifyEvent(wxACC_EVENT_OBJECT_NAMECHANGE, - mTrackPanel, + GetWindow(), wxOBJID_CLIENT, TrackNum(t)); #endif @@ -210,7 +222,7 @@ void TrackPanelAx::Updated() void TrackPanelAx::MessageForScreenReader(const wxString& message) { #if wxUSE_ACCESSIBILITY - if (mTrackPanel == wxWindow::FindFocus()) + if (GetWindow() == wxWindow::FindFocus()) { auto t = GetFocus(); int childId = t ? TrackNum(t) : 0; @@ -225,7 +237,7 @@ void TrackPanelAx::MessageForScreenReader(const wxString& message) mTrackName = false; NotifyEvent(wxACC_EVENT_OBJECT_NAMECHANGE, - mTrackPanel, + GetWindow(), wxOBJID_CLIENT, childId); } @@ -254,7 +266,7 @@ wxAccStatus TrackPanelAx::GetChild( int childId, wxAccessible** child ) // Gets the number of children. wxAccStatus TrackPanelAx::GetChildCount( int* childCount ) { - *childCount = mTrackPanel->GetTrackCount(); + *childCount = GetTracks().Leaders().size(); return wxACC_OK; } @@ -304,7 +316,7 @@ wxAccStatus TrackPanelAx::GetLocation( wxRect& rect, int elementId ) if( elementId == wxACC_SELF ) { - rect = mTrackPanel->GetRect(); + rect = GetWindow()->GetRect(); } else { @@ -315,7 +327,7 @@ wxAccStatus TrackPanelAx::GetLocation( wxRect& rect, int elementId ) return wxACC_FAIL; } - rect = mTrackPanel->FindTrackRect( t.get() ); + rect = mFinder ? mFinder( *t ) : wxRect{}; // Inflate the screen reader's rectangle so it overpaints Audacity's own // yellow focus rectangle. #ifdef __WXMAC__ @@ -326,7 +338,7 @@ wxAccStatus TrackPanelAx::GetLocation( wxRect& rect, int elementId ) rect.Inflate(dx, dx); } - rect.SetPosition( mTrackPanel->GetParent()->ClientToScreen( rect.GetPosition() ) ); + rect.SetPosition( GetWindow()->GetParent()->ClientToScreen( rect.GetPosition() ) ); return wxACC_OK; } @@ -593,7 +605,7 @@ wxAccStatus TrackPanelAx::GetFocus( int *childId, wxAccessible **child ) { #if defined(__WXMSW__) - if (mTrackPanel == wxWindow::FindFocus()) + if (GetWindow() == wxWindow::FindFocus()) { auto focusedTrack = mFocusedTrack.lock(); if (focusedTrack) @@ -703,8 +715,8 @@ wxAccStatus TrackPanelAx::Select(int childId, wxAccSelectionFlags selectFlags) Track* t = FindTrack(childId).get(); if (t) { - mTrackPanel->SetFocusedTrack(t); - t->EnsureVisible( true ); + SetFocus( t->SharedPointer() ); + t->EnsureVisible(); } } else diff --git a/src/TrackPanelAx.h b/src/TrackPanelAx.h index bc549d9b6..ece6b2510 100644 --- a/src/TrackPanelAx.h +++ b/src/TrackPanelAx.h @@ -11,8 +11,10 @@ #ifndef __AUDACITY_TRACK_PANEL_ACCESSIBILITY__ #define __AUDACITY_TRACK_PANEL_ACCESSIBILITY__ +#include #include +#include // to declare custom event types #include // for wxUSE_* macros #include // member variable @@ -21,9 +23,15 @@ #include "widgets/WindowAccessible.h" // to inherit #endif +class wxRect; +class AudacityProject; class Track; -class TrackPanel; +class TrackList; + +// An event sent to the project +wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API, + EVT_TRACK_FOCUS_CHANGE, wxCommandEvent); class TrackPanelAx final #if wxUSE_ACCESSIBILITY @@ -31,9 +39,12 @@ class TrackPanelAx final #endif { public: - TrackPanelAx(wxWindow * window); + TrackPanelAx(AudacityProject &project); virtual ~ TrackPanelAx(); + using RectangleFinder = std::function< wxRect( Track& ) >; + void SetFinder( const RectangleFinder &finder ) { mFinder = finder; } + // Returns currently focused track or first one if none focused std::shared_ptr GetFocus(); @@ -114,14 +125,24 @@ public: // Modify focus or selection wxAccStatus Select(int childId, wxAccSelectionFlags selectFlags) override; +#else + wxWindow *GetWindow() const { return mWindow; } + void SetWindow( wxWindow *window ) { mWindow = window; } #endif private: + TrackList &GetTracks(); int TrackNum( const std::shared_ptr &track ); std::shared_ptr FindTrack( int num ); - TrackPanel *mTrackPanel; + AudacityProject &mProject; + +#if !wxUSE_ACCESSIBILITY + wxWindow *mWindow{}; +#endif + + RectangleFinder mFinder; std::weak_ptr mFocusedTrack; int mNumFocusedTrack; diff --git a/src/tracks/ui/TrackSelectHandle.cpp b/src/tracks/ui/TrackSelectHandle.cpp index cc4ad8653..3d29e98e6 100644 --- a/src/tracks/ui/TrackSelectHandle.cpp +++ b/src/tracks/ui/TrackSelectHandle.cpp @@ -145,7 +145,7 @@ UIHandle::Result TrackSelectHandle::Drag HitTestPreview TrackSelectHandle::Preview (const TrackPanelMouseState &, const AudacityProject *project) { - const auto trackCount = TrackPanel::Get( *project ).GetTrackCount(); + const auto trackCount = TrackList::Get( *project ).Leaders().size(); auto message = Message(trackCount); if (mClicked) { static auto disabledCursor =