1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-05-03 09:09:47 +02:00

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.
This commit is contained in:
Paul Licameli 2019-06-25 10:56:25 -04:00
parent 2257fa642a
commit 25f4d6cf8b
5 changed files with 85 additions and 40 deletions

View File

@ -266,7 +266,16 @@ TrackPanel::TrackPanel(wxWindow * parent, wxWindowID id,
SetBackgroundStyle(wxBG_STYLE_PAINT);
{
auto pAx = std::make_unique <TrackPanelAx>( this );
auto pAx = std::make_unique <TrackPanelAx>( *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);

View File

@ -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:

View File

@ -30,16 +30,20 @@
#include <wx/intl.h>
#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<Track> 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<Track> TrackPanelAx::SetFocus( std::shared_ptr<Track> 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<Track> TrackPanelAx::SetFocus( std::shared_ptr<Track> track )
if( track->GetSelected() )
{
NotifyEvent( wxACC_EVENT_OBJECT_SELECTION,
mTrackPanel,
GetWindow(),
wxOBJID_CLIENT,
mNumFocusedTrack );
}
@ -127,7 +139,7 @@ std::shared_ptr<Track> TrackPanelAx::SetFocus( std::shared_ptr<Track> 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<Track> &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<Track> 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

View File

@ -11,8 +11,10 @@
#ifndef __AUDACITY_TRACK_PANEL_ACCESSIBILITY__
#define __AUDACITY_TRACK_PANEL_ACCESSIBILITY__
#include <functional>
#include <memory>
#include <wx/event.h> // to declare custom event types
#include <wx/setup.h> // for wxUSE_* macros
#include <wx/string.h> // 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<Track> 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> &track );
std::shared_ptr<Track> FindTrack( int num );
TrackPanel *mTrackPanel;
AudacityProject &mProject;
#if !wxUSE_ACCESSIBILITY
wxWindow *mWindow{};
#endif
RectangleFinder mFinder;
std::weak_ptr<Track> mFocusedTrack;
int mNumFocusedTrack;

View File

@ -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 =