From 2257fa642a9f39a8e1babe39797809b908f65b72 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Mon, 10 Jun 2019 11:20:07 -0400 Subject: [PATCH 1/2] Remove TrackPanel::EnsureVisible; TrackList sends an event instead --- src/ProjectWindow.cpp | 8 +++- src/Track.cpp | 18 ++++++++ src/Track.h | 10 +++++ src/TrackPanel.cpp | 27 ++++++------ src/TrackPanel.h | 3 +- src/TrackPanelAx.cpp | 3 +- src/TrackUtilities.cpp | 2 +- src/effects/EffectManager.cpp | 10 +++-- src/menus/EditMenus.cpp | 21 +++++++--- src/menus/LabelMenus.cpp | 4 +- src/menus/NavigationMenus.cpp | 77 ++++++++++++++++------------------- src/menus/TrackMenus.cpp | 14 +++---- 12 files changed, 116 insertions(+), 81 deletions(-) diff --git a/src/ProjectWindow.cpp b/src/ProjectWindow.cpp index 809372143..a0e7b4bc7 100644 --- a/src/ProjectWindow.cpp +++ b/src/ProjectWindow.cpp @@ -1604,6 +1604,7 @@ void ProjectWindow::OnMouseEvent(wxMouseEvent & event) void ProjectWindow::ZoomAfterImport(Track *pTrack) { auto &project = mProject; + auto &tracks = TrackList::Get( project ); auto &trackPanel = TrackPanel::Get( project ); DoZoomFit(); @@ -1611,8 +1612,11 @@ void ProjectWindow::ZoomAfterImport(Track *pTrack) trackPanel.SetFocus(); RedrawProject(); if (!pTrack) - pTrack = trackPanel.GetFirstSelectedTrack(); - trackPanel.EnsureVisible(pTrack); + pTrack = *tracks.Selected().begin(); + if (!pTrack) + pTrack = *tracks.Any().begin(); + if (pTrack) + pTrack->EnsureVisible(); } // Utility function called by other zoom methods diff --git a/src/Track.cpp b/src/Track.cpp index 9a4d50cd3..aca0103ed 100644 --- a/src/Track.cpp +++ b/src/Track.cpp @@ -101,6 +101,13 @@ void Track::SetSelected(bool s) } } +void Track::EnsureVisible( bool modifyState ) +{ + auto pList = mList.lock(); + if (pList) + pList->EnsureVisibleEvent( SharedPointer(), modifyState ); +} + void Track::Merge(const Track &orig) { mSelected = orig.mSelected; @@ -475,6 +482,7 @@ std::pair TrackList::FindSyncLockGroup(Track *pMember) const // wxDEFINE_EVENT(EVT_TRACKLIST_TRACK_DATA_CHANGE, TrackListEvent); wxDEFINE_EVENT(EVT_TRACKLIST_SELECTION_CHANGE, TrackListEvent); +wxDEFINE_EVENT(EVT_TRACKLIST_TRACK_REQUEST_VISIBLE, TrackListEvent); wxDEFINE_EVENT(EVT_TRACKLIST_PERMUTED, TrackListEvent); wxDEFINE_EVENT(EVT_TRACKLIST_RESIZING, TrackListEvent); wxDEFINE_EVENT(EVT_TRACKLIST_ADDITION, TrackListEvent); @@ -579,6 +587,16 @@ void TrackList::DataEvent( const std::shared_ptr &pTrack, int code ) safenew TrackListEvent{ EVT_TRACKLIST_TRACK_DATA_CHANGE, pTrack, code } ); } +void TrackList::EnsureVisibleEvent( + const std::shared_ptr &pTrack, bool modifyState ) +{ + auto pEvent = std::make_unique( + EVT_TRACKLIST_TRACK_REQUEST_VISIBLE, pTrack, 0 ); + pEvent->SetInt( modifyState ? 1 : 0 ); + // wxWidgets will own the event object + QueueEvent( pEvent.release() ); +} + void TrackList::PermutationEvent(TrackNodePointer node) { // wxWidgets will own the event object diff --git a/src/Track.h b/src/Track.h index d850a983a..5a1757a2d 100644 --- a/src/Track.h +++ b/src/Track.h @@ -336,6 +336,10 @@ private: void SetSelected(bool s); + // The argument tells whether the last undo history state should be + // updated for the appearance change + void EnsureVisible( bool modifyState = false ); + public: virtual ChannelType GetChannel() const { return mChannel;} @@ -1091,6 +1095,10 @@ wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API, wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API, EVT_TRACKLIST_TRACK_DATA_CHANGE, TrackListEvent); +// Posted when a track needs to be scrolled into view. +wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API, + EVT_TRACKLIST_TRACK_REQUEST_VISIBLE, TrackListEvent); + // Posted when tracks are reordered but otherwise unchanged. // mpTrack points to the moved track that is earliest in the New ordering. wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API, @@ -1491,6 +1499,8 @@ private: void SelectionEvent( const std::shared_ptr &pTrack ); void PermutationEvent(TrackNodePointer node); void DataEvent( const std::shared_ptr &pTrack, int code ); + void EnsureVisibleEvent( + const std::shared_ptr &pTrack, bool modifyState ); void DeletionEvent(TrackNodePointer node = {}); void AdditionEvent(TrackNodePointer node); void ResizingEvent(TrackNodePointer node); diff --git a/src/TrackPanel.cpp b/src/TrackPanel.cpp index 914cfa25b..80e1d4ea2 100644 --- a/src/TrackPanel.cpp +++ b/src/TrackPanel.cpp @@ -296,6 +296,9 @@ TrackPanel::TrackPanel(wxWindow * parent, wxWindowID id, mTracks->Bind(EVT_TRACKLIST_DELETION, &TrackPanel::OnTrackListDeletion, this); + mTracks->Bind(EVT_TRACKLIST_TRACK_REQUEST_VISIBLE, + &TrackPanel::OnEnsureVisible, + this); auto theProject = GetProject(); theProject->Bind(EVT_ODTASK_UPDATE, &TrackPanel::OnODTask, this); @@ -656,7 +659,7 @@ void TrackPanel::ProcessUIHandleResult } if ((refreshResult & RefreshCode::EnsureVisible) && pClickedTrack) - panel->EnsureVisible(pClickedTrack); + pClickedTrack->EnsureVisible(); } void TrackPanel::HandlePageUpKey() @@ -812,7 +815,7 @@ void TrackPanel::OnMouseEvent(wxMouseEvent & event) const auto foundCell = FindCell(event.m_x, event.m_y); const auto t = FindTrack( foundCell.pCell.get() ); if ( t ) - EnsureVisible(t.get()); + t->EnsureVisible(); } ); } @@ -1304,18 +1307,15 @@ void TrackPanel::OnTrackMenu(Track *t) CellularPanel::DoContextMenu( &TrackView::Get( *t ) ); } -Track * TrackPanel::GetFirstSelectedTrack() +// Tracks have been removed from the list. +void TrackPanel::OnEnsureVisible(TrackListEvent & e) { - auto t = *GetTracks()->Selected().begin(); - if (t) - return t; - else - //if nothing is selected, return the first track - return *GetTracks()->Any().begin(); -} + e.Skip(); + bool modifyState = e.GetInt(); + + auto pTrack = e.mpTrack.lock(); + auto t = pTrack.get(); -void TrackPanel::EnsureVisible(Track * t) -{ SetFocusedTrack(t); int trackTop = 0; @@ -1349,6 +1349,9 @@ void TrackPanel::EnsureVisible(Track * t) } } Refresh(false); + + if ( modifyState ) + ProjectHistory::Get( *GetProject() ).ModifyState( false ); } // 0.0 scrolls to top diff --git a/src/TrackPanel.h b/src/TrackPanel.h index ca7f446de..57c6c061e 100644 --- a/src/TrackPanel.h +++ b/src/TrackPanel.h @@ -91,6 +91,7 @@ class AUDACITY_DLL_API TrackPanel final void OnTrackListResizing(TrackListEvent & event); void OnTrackListDeletion(wxEvent & event); + void OnEnsureVisible(TrackListEvent & event); void UpdateViewIfNoTracks(); // Call this to update mViewInfo, etc, after track(s) removal, before Refresh(). double GetMostRecentXPos(); @@ -126,9 +127,7 @@ class AUDACITY_DLL_API TrackPanel final void ScrollIntoView(int x); void OnTrackMenu(Track *t = NULL); - Track * GetFirstSelectedTrack(); - void EnsureVisible(Track * t); void VerticalScroll( float fracPosition); TrackPanelCell *GetFocusedCell() override; diff --git a/src/TrackPanelAx.cpp b/src/TrackPanelAx.cpp index 8d5391c3a..d1e8f125a 100644 --- a/src/TrackPanelAx.cpp +++ b/src/TrackPanelAx.cpp @@ -704,8 +704,7 @@ wxAccStatus TrackPanelAx::Select(int childId, wxAccSelectionFlags selectFlags) Track* t = FindTrack(childId).get(); if (t) { mTrackPanel->SetFocusedTrack(t); - mTrackPanel->EnsureVisible(t); - mTrackPanel->MakeParentModifyState(false); + t->EnsureVisible( true ); } } else diff --git a/src/TrackUtilities.cpp b/src/TrackUtilities.cpp index ca2f239af..bd1600a91 100644 --- a/src/TrackUtilities.cpp +++ b/src/TrackUtilities.cpp @@ -50,7 +50,7 @@ void DoRemoveTracks( AudacityProject &project ) // If we actually have something left, then make sure it's seen if (f) - trackPanel.EnsureVisible(f); + f->EnsureVisible(); ProjectHistory::Get( project ) .PushState(_("Removed audio track(s)"), _("Remove Track")); diff --git a/src/effects/EffectManager.cpp b/src/effects/EffectManager.cpp index e0012420d..a3567d60c 100644 --- a/src/effects/EffectManager.cpp +++ b/src/effects/EffectManager.cpp @@ -219,9 +219,13 @@ void EffectManager::UnregisterEffect(const PluginID & ID) if( tracks.size() > nTracksOriginally ){ // 0.0 is min scroll position, 1.0 is max scroll position. trackPanel.VerticalScroll( 1.0 ); - } else { - trackPanel.EnsureVisible(trackPanel.GetFirstSelectedTrack()); - trackPanel.Refresh(false); + } + else { + auto pTrack = *tracks.Selected().begin(); + if (!pTrack) + pTrack = *tracks.Any().begin(); + if (pTrack) + pTrack->EnsureVisible(); } return true; diff --git a/src/menus/EditMenus.cpp b/src/menus/EditMenus.cpp index 79b1865aa..97fdbae80 100644 --- a/src/menus/EditMenus.cpp +++ b/src/menus/EditMenus.cpp @@ -80,7 +80,6 @@ bool DoPasteNothingSelected(AudacityProject &project) { auto &tracks = TrackList::Get( project ); auto &trackFactory = TrackFactory::Get( project ); - auto &trackPanel = TrackPanel::Get( project ); auto &selectedRegion = ViewInfo::Get( project ).selectedRegion; auto &window = ProjectWindow::Get( project ); @@ -161,7 +160,7 @@ bool DoPasteNothingSelected(AudacityProject &project) window.RedrawProject(); if (pFirstNewTrack) - trackPanel.EnsureVisible(pFirstNewTrack); + pFirstNewTrack->EnsureVisible(); return true; } @@ -178,6 +177,7 @@ struct Handler : CommandHandlerObject { void OnUndo(const CommandContext &context) { auto &project = context.project; + auto &tracks = TrackList::Get( project ); auto &trackPanel = TrackPanel::Get( project ); auto &undoManager = UndoManager::Get( project ); auto &window = ProjectWindow::Get( project ); @@ -196,11 +196,17 @@ void OnUndo(const CommandContext &context) [&]( const UndoState &state ){ ProjectHistory::Get( project ).PopState( state ); } ); - trackPanel.EnsureVisible(trackPanel.GetFirstSelectedTrack()); + auto t = *tracks.Selected().begin(); + if (!t) + t = *tracks.Any().begin(); + if (t) + t->EnsureVisible(); } + void OnRedo(const CommandContext &context) { auto &project = context.project; + auto &tracks = TrackList::Get( project ); auto &trackPanel = TrackPanel::Get( project ); auto &undoManager = UndoManager::Get( project ); auto &window = ProjectWindow::Get( project ); @@ -218,7 +224,11 @@ void OnRedo(const CommandContext &context) [&]( const UndoState &state ){ ProjectHistory::Get( project ).PopState( state ); } ); - trackPanel.EnsureVisible(trackPanel.GetFirstSelectedTrack()); + auto t = *tracks.Selected().begin(); + if (!t) + t = *tracks.Any().begin(); + if (t) + t->EnsureVisible(); } void OnCut(const CommandContext &context) @@ -374,7 +384,6 @@ void OnPaste(const CommandContext &context) { auto &project = context.project; auto &tracks = TrackList::Get( project ); - auto &trackPanel = TrackPanel::Get( project ); auto &trackFactory = TrackFactory::Get( project ); auto &selectedRegion = ViewInfo::Get( project ).selectedRegion; const auto &settings = ProjectSettings::Get( project ); @@ -633,7 +642,7 @@ void OnPaste(const CommandContext &context) window.RedrawProject(); if (ff) - trackPanel.EnsureVisible(ff); + ff->EnsureVisible(); } } diff --git a/src/menus/LabelMenus.cpp b/src/menus/LabelMenus.cpp index b5942f266..4fb29dba4 100644 --- a/src/menus/LabelMenus.cpp +++ b/src/menus/LabelMenus.cpp @@ -78,7 +78,7 @@ int DoAddLabel( window.RedrawProject(); if (!useDialog) { - trackPanel.EnsureVisible(lt); + lt->EnsureVisible(); } trackPanel.SetFocus(); @@ -336,7 +336,7 @@ void OnPasteNewLabel(const CommandContext &context) // plt should point to the last label track pasted to -- ensure it's visible // and set focus if (plt) { - trackPanel.EnsureVisible(plt); + plt->EnsureVisible(); trackPanel.SetFocus(); } diff --git a/src/menus/NavigationMenus.cpp b/src/menus/NavigationMenus.cpp index c9c66705c..c668b4dad 100644 --- a/src/menus/NavigationMenus.cpp +++ b/src/menus/NavigationMenus.cpp @@ -96,8 +96,8 @@ void DoPrevTrack( { t = *tracks.Any().rbegin(); trackPanel.SetFocusedTrack( t ); - trackPanel.EnsureVisible( t ); - projectHistory.ModifyState(false); + if (t) + t->EnsureVisible( true ); return; } @@ -116,7 +116,7 @@ void DoPrevTrack( p = *tracks.Any().rbegin(); else { - trackPanel.EnsureVisible( t ); + t->EnsureVisible(); return; } } @@ -128,8 +128,8 @@ void DoPrevTrack( selectionState.SelectTrack ( *t, false, false ); trackPanel.SetFocusedTrack( p ); // move focus to next track up - trackPanel.EnsureVisible( p ); - projectHistory.ModifyState(false); + if (p) + p->EnsureVisible( true ); return; } if( tSelected && !pSelected ) @@ -137,8 +137,8 @@ void DoPrevTrack( selectionState.SelectTrack ( *p, true, false ); trackPanel.SetFocusedTrack( p ); // move focus to next track up - trackPanel.EnsureVisible( p ); - projectHistory.ModifyState(false); + if (p) + p->EnsureVisible( true ); return; } if( !tSelected && pSelected ) @@ -146,8 +146,8 @@ void DoPrevTrack( selectionState.SelectTrack ( *p, false, false ); trackPanel.SetFocusedTrack( p ); // move focus to next track up - trackPanel.EnsureVisible( p ); - projectHistory.ModifyState(false); + if (p) + p->EnsureVisible( true ); return; } if( !tSelected && !pSelected ) @@ -155,8 +155,8 @@ void DoPrevTrack( selectionState.SelectTrack ( *t, true, false ); trackPanel.SetFocusedTrack( p ); // move focus to next track up - trackPanel.EnsureVisible( p ); - projectHistory.ModifyState(false); + if (p) + p->EnsureVisible( true ); return; } } @@ -171,21 +171,20 @@ void DoPrevTrack( auto range = tracks.Leaders(); p = * range.rbegin(); // null if range is empty trackPanel.SetFocusedTrack( p ); // Wrap to the last track - trackPanel.EnsureVisible( p ); - projectHistory.ModifyState(false); + if (p) + p->EnsureVisible( true ); return; } else { - trackPanel.EnsureVisible( t ); + t->EnsureVisible(); return; } } else { trackPanel.SetFocusedTrack( p ); // move focus to next track up - trackPanel.EnsureVisible( p ); - projectHistory.ModifyState(false); + p->EnsureVisible( true ); return; } } @@ -207,8 +206,8 @@ void DoNextTrack( { t = *tracks.Any().begin(); trackPanel.SetFocusedTrack( t ); - trackPanel.EnsureVisible( t ); - projectHistory.ModifyState(false); + if (t) + t->EnsureVisible( true ); return; } @@ -222,7 +221,7 @@ void DoNextTrack( n = *tracks.Any().begin(); else { - trackPanel.EnsureVisible( t ); + t->EnsureVisible(); return; } } @@ -233,8 +232,8 @@ void DoNextTrack( selectionState.SelectTrack ( *t, false, false ); trackPanel.SetFocusedTrack( n ); // move focus to next track down - trackPanel.EnsureVisible( n ); - projectHistory.ModifyState(false); + if (n) + n->EnsureVisible( true ); return; } if( tSelected && !nSelected ) @@ -242,8 +241,8 @@ void DoNextTrack( selectionState.SelectTrack ( *n, true, false ); trackPanel.SetFocusedTrack( n ); // move focus to next track down - trackPanel.EnsureVisible( n ); - projectHistory.ModifyState(false); + if (n) + n->EnsureVisible( true ); return; } if( !tSelected && nSelected ) @@ -251,8 +250,8 @@ void DoNextTrack( selectionState.SelectTrack ( *n, false, false ); trackPanel.SetFocusedTrack( n ); // move focus to next track down - trackPanel.EnsureVisible( n ); - projectHistory.ModifyState(false); + if (n) + n->EnsureVisible( true ); return; } if( !tSelected && !nSelected ) @@ -260,8 +259,8 @@ void DoNextTrack( selectionState.SelectTrack ( *t, true, false ); trackPanel.SetFocusedTrack( n ); // move focus to next track down - trackPanel.EnsureVisible( n ); - projectHistory.ModifyState(false); + if (n) + n->EnsureVisible( true ); return; } } @@ -275,21 +274,20 @@ void DoNextTrack( { n = *tracks.Any().begin(); trackPanel.SetFocusedTrack( n ); // Wrap to the first track - trackPanel.EnsureVisible( n ); - projectHistory.ModifyState(false); + if (n) + n->EnsureVisible( true ); return; } else { - trackPanel.EnsureVisible( t ); + t->EnsureVisible(); return; } } else { trackPanel.SetFocusedTrack( n ); // move focus to next track down - trackPanel.EnsureVisible( n ); - projectHistory.ModifyState(false); + n->EnsureVisible( true ); return; } } @@ -472,11 +470,9 @@ void OnFirstTrack(const CommandContext &context) auto f = *tracks.Any().begin(); if (t != f) - { trackPanel.SetFocusedTrack(f); - ProjectHistory::Get( project ).ModifyState(false); - } - trackPanel.EnsureVisible(f); + if (f) + f->EnsureVisible( t != f ); } void OnLastTrack(const CommandContext &context) @@ -491,11 +487,9 @@ void OnLastTrack(const CommandContext &context) auto l = *tracks.Any().rbegin(); if (t != l) - { trackPanel.SetFocusedTrack(l); - ProjectHistory::Get( project ).ModifyState(false); - } - trackPanel.EnsureVisible(l); + if (l) + l->EnsureVisible( t != l ); } void OnShiftUp(const CommandContext &context) @@ -524,8 +518,7 @@ void OnToggle(const CommandContext &context) selectionState.SelectTrack ( *t, !t->GetSelected(), true ); - trackPanel.EnsureVisible( t ); - ProjectHistory::Get( project ).ModifyState(false); + t->EnsureVisible( true ); trackPanel.GetAx().Updated(); diff --git a/src/menus/TrackMenus.cpp b/src/menus/TrackMenus.cpp index ffe61516b..e1cd080c0 100644 --- a/src/menus/TrackMenus.cpp +++ b/src/menus/TrackMenus.cpp @@ -114,7 +114,7 @@ void DoMixAndRender trackPanel.SetFocus(); trackPanel.SetFocusedTrack(pNewLeft); - trackPanel.EnsureVisible(pNewLeft); + pNewLeft->EnsureVisible(); window.RedrawProject(); } } @@ -577,7 +577,6 @@ void OnNewWaveTrack(const CommandContext &context) const auto &settings = ProjectSettings::Get( project ); auto &tracks = TrackList::Get( project ); auto &trackFactory = TrackFactory::Get( project ); - auto &trackPanel = TrackPanel::Get( project ); auto &window = ProjectWindow::Get( project ); auto defaultFormat = settings.GetDefaultFormat(); @@ -592,7 +591,7 @@ void OnNewWaveTrack(const CommandContext &context) .PushState(_("Created new audio track"), _("New Track")); window.RedrawProject(); - trackPanel.EnsureVisible(t); + t->EnsureVisible(); } void OnNewStereoTrack(const CommandContext &context) @@ -601,7 +600,6 @@ void OnNewStereoTrack(const CommandContext &context) const auto &settings = ProjectSettings::Get( project ); auto &tracks = TrackList::Get( project ); auto &trackFactory = TrackFactory::Get( project ); - auto &trackPanel = TrackPanel::Get( project ); auto &window = ProjectWindow::Get( project ); auto defaultFormat = settings.GetDefaultFormat(); @@ -621,7 +619,7 @@ void OnNewStereoTrack(const CommandContext &context) .PushState(_("Created new stereo audio track"), _("New Track")); window.RedrawProject(); - trackPanel.EnsureVisible(left); + left->EnsureVisible(); } void OnNewLabelTrack(const CommandContext &context) @@ -629,7 +627,6 @@ void OnNewLabelTrack(const CommandContext &context) auto &project = context.project; auto &tracks = TrackList::Get( project ); auto &trackFactory = TrackFactory::Get( project ); - auto &trackPanel = TrackPanel::Get( project ); auto &window = ProjectWindow::Get( project ); auto t = tracks.Add( trackFactory.NewLabelTrack() ); @@ -642,7 +639,7 @@ void OnNewLabelTrack(const CommandContext &context) .PushState(_("Created new label track"), _("New Track")); window.RedrawProject(); - trackPanel.EnsureVisible(t); + t->EnsureVisible(); } void OnNewTimeTrack(const CommandContext &context) @@ -650,7 +647,6 @@ void OnNewTimeTrack(const CommandContext &context) auto &project = context.project; auto &tracks = TrackList::Get( project ); auto &trackFactory = TrackFactory::Get( project ); - auto &trackPanel = TrackPanel::Get( project ); auto &window = ProjectWindow::Get( project ); if ( *tracks.Any().begin() ) { @@ -668,7 +664,7 @@ void OnNewTimeTrack(const CommandContext &context) .PushState(_("Created new time track"), _("New Track")); window.RedrawProject(); - trackPanel.EnsureVisible(t); + t->EnsureVisible(); } void OnStereoToMono(const CommandContext &context) From 25f4d6cf8b17156a4780508c11fe6b55e5fe981f Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Tue, 25 Jun 2019 10:56:25 -0400 Subject: [PATCH 2/2] TrackPanelAx does not depend on TrackPanel... ... TrackPanelAx now sends an event to the project when track focus changes, and TrackPanel listens for it. TrackPanel also initializes TrackPanelAx with a callback to do the details of rectangle calculation. --- src/TrackPanel.cpp | 28 ++++++++---- src/TrackPanel.h | 2 +- src/TrackPanelAx.cpp | 66 +++++++++++++++++------------ src/TrackPanelAx.h | 27 ++++++++++-- src/tracks/ui/TrackSelectHandle.cpp | 2 +- 5 files changed, 85 insertions(+), 40 deletions(-) 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 =