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

Addition, deletion, sort of Labels communicated by events...

... and LabelTrack listens to its own events, to update certain state.

This is roundabout for now, but that state is view-related and will move into
another class.
This commit is contained in:
Paul Licameli 2018-12-05 14:10:05 -05:00
parent aa5f9550bd
commit 604fbd0a2c
5 changed files with 183 additions and 40 deletions

View File

@ -76,6 +76,10 @@ for drawing different aspects of the label and its text box.
#include "widgets/AudacityMessageBox.h" #include "widgets/AudacityMessageBox.h"
#include "widgets/ErrorDialog.h" #include "widgets/ErrorDialog.h"
wxDEFINE_EVENT(EVT_LABELTRACK_ADDITION, LabelTrackEvent);
wxDEFINE_EVENT(EVT_LABELTRACK_DELETION, LabelTrackEvent);
wxDEFINE_EVENT(EVT_LABELTRACK_PERMUTED, LabelTrackEvent);
enum enum
{ {
OnCutSelectedTextID = 1, // OSX doesn't like a 0 menu id OnCutSelectedTextID = 1, // OSX doesn't like a 0 menu id
@ -131,6 +135,10 @@ LabelTrack::LabelTrack(const std::shared_ptr<DirManager> &projDirManager):
// reset flags // reset flags
ResetFlags(); ResetFlags();
Bind( EVT_LABELTRACK_ADDITION, &LabelTrack::OnLabelAdded, this );
Bind( EVT_LABELTRACK_DELETION, &LabelTrack::OnLabelDeleted, this );
Bind( EVT_LABELTRACK_PERMUTED, &LabelTrack::OnLabelPermuted, this );
} }
LabelTrack::LabelTrack(const LabelTrack &orig) : LabelTrack::LabelTrack(const LabelTrack &orig) :
@ -806,7 +814,7 @@ namespace {
if (target) { if (target) {
auto handle = dynamic_cast<LabelGlyphHandle*>( target.get() ); auto handle = dynamic_cast<LabelGlyphHandle*>( target.get() );
if (handle) if (handle)
return &handle->mHit; return &*handle->mpHit;
} }
return nullptr; return nullptr;
} }
@ -1581,7 +1589,7 @@ bool LabelTrack::HandleGlyphDragRelease
//the NEW size of the label. //the NEW size of the label.
*newSel = mLabels[mSelIndex].selectedRegion; *newSel = mLabels[mSelIndex].selectedRegion;
} }
SortLabels( &hit ); SortLabels();
} }
return false; return false;
@ -2802,8 +2810,17 @@ int LabelTrackView::AddLabel(const SelectedRegion &selectedRegion,
return pos; return pos;
} }
void LabelTrack::OnLabelAdded( const wxString &title, int pos ) void LabelTrack::OnLabelAdded( LabelTrackEvent &e )
{ {
e.Skip();
if ( e.mpTrack.lock().get() != this )
return;
const auto &title = e.mTitle;
const auto pos = e.mPresentPosition;
mInitialCursorPos = mCurrentCursorPos = title.length();
// restoreFocus is -2 e.g. from Nyquist label creation, when we should not // restoreFocus is -2 e.g. from Nyquist label creation, when we should not
// even lose the focus and open the label to edit in the first place. // even lose the focus and open the label to edit in the first place.
// -1 means we don't need to restore it to anywhere. // -1 means we don't need to restore it to anywhere.
@ -2824,8 +2841,6 @@ void LabelTrack::OnLabelAdded( const wxString &title, int pos )
// If the label is added during actions like import, then the // If the label is added during actions like import, then the
// mDrawCursor flag will be reset once the action is complete. // mDrawCursor flag will be reset once the action is complete.
mDrawCursor = true; mDrawCursor = true;
mInitialCursorPos = mCurrentCursorPos = title.length();
} }
@ -2842,7 +2857,10 @@ int LabelTrack::AddLabel(const SelectedRegion &selectedRegion,
mLabels.insert(mLabels.begin() + pos, l); mLabels.insert(mLabels.begin() + pos, l);
OnLabelAdded( title, pos ); // wxWidgets will own the event object
QueueEvent( safenew LabelTrackEvent{
EVT_LABELTRACK_ADDITION, SharedPointer<LabelTrack>(), title, -1, pos
} );
return pos; return pos;
} }
@ -2850,7 +2868,24 @@ int LabelTrack::AddLabel(const SelectedRegion &selectedRegion,
void LabelTrack::DeleteLabel(int index) void LabelTrack::DeleteLabel(int index)
{ {
wxASSERT((index < (int)mLabels.size())); wxASSERT((index < (int)mLabels.size()));
mLabels.erase(mLabels.begin() + index); auto iter = mLabels.begin() + index;
const auto title = iter->title;
mLabels.erase(iter);
// wxWidgets will own the event object
QueueEvent( safenew LabelTrackEvent{
EVT_LABELTRACK_DELETION, SharedPointer<LabelTrack>(), title, index, -1
} );
}
void LabelTrack::OnLabelDeleted( LabelTrackEvent &e )
{
e.Skip();
if ( e.mpTrack.lock().get() != this )
return;
auto index = e.mFormerPosition;
// IF we've deleted the selected label // IF we've deleted the selected label
// THEN set no label selected. // THEN set no label selected.
if( mSelIndex== index ) if( mSelIndex== index )
@ -2866,6 +2901,23 @@ void LabelTrack::DeleteLabel(int index)
} }
} }
void LabelTrack::OnLabelPermuted( LabelTrackEvent &e )
{
e.Skip();
if ( e.mpTrack.lock().get() != this )
return;
auto former = e.mFormerPosition;
auto present = e.mPresentPosition;
if ( mSelIndex == former )
mSelIndex = present;
else if ( former < mSelIndex && mSelIndex <= present )
-- mSelIndex;
else if ( former > mSelIndex && mSelIndex >= present )
++ mSelIndex;
}
wxBitmap & LabelTrack::GetGlyph( int i) wxBitmap & LabelTrack::GetGlyph( int i)
{ {
return theTheme.Bitmap( i + bmpLabelGlyph0); return theTheme.Bitmap( i + bmpLabelGlyph0);
@ -3013,7 +3065,7 @@ static bool IsGoodLabelEditKey(const wxKeyEvent & evt)
/// This function is called often (whilst dragging a label) /// This function is called often (whilst dragging a label)
/// We expect them to be very nearly in order, so insertion /// We expect them to be very nearly in order, so insertion
/// sort (with a linear search) is a reasonable choice. /// sort (with a linear search) is a reasonable choice.
void LabelTrack::SortLabels( LabelTrackHit *pHit ) void LabelTrack::SortLabels()
{ {
const auto begin = mLabels.begin(); const auto begin = mLabels.begin();
const auto nn = (int)mLabels.size(); const auto nn = (int)mLabels.size();
@ -3039,20 +3091,12 @@ void LabelTrack::SortLabels( LabelTrackHit *pHit )
begin + i + 1 begin + i + 1
); );
// Various indices need to be updated with the moved items... // Let listeners update their stored indices
auto update = [=](int &index) { // wxWidgets will own the event object
if( index <= i ) { QueueEvent( safenew LabelTrackEvent{
if( index == i ) EVT_LABELTRACK_PERMUTED, SharedPointer<LabelTrack>(),
index = j; mLabels[j].title, i, j
else if( index >= j) } );
++index;
}
};
if ( pHit ) {
update( pHit->mMouseOverLabelLeft );
update( pHit->mMouseOverLabelRight );
}
update(mSelIndex);
} }
} }

View File

@ -31,6 +31,7 @@ class DirManager;
class TimeWarper; class TimeWarper;
class ZoomInfo; class ZoomInfo;
class LabelTrackEvent;
struct LabelTrackHit; struct LabelTrackHit;
struct TrackPanelDrawingContext; struct TrackPanelDrawingContext;
@ -102,7 +103,9 @@ const int NUM_GLYPH_CONFIGS = 3;
const int NUM_GLYPH_HIGHLIGHTS = 4; const int NUM_GLYPH_HIGHLIGHTS = 4;
const int MAX_NUM_ROWS =80; const int MAX_NUM_ROWS =80;
class AUDACITY_DLL_API LabelTrack final : public Track class AUDACITY_DLL_API LabelTrack final
: public Track
, public wxEvtHandler
{ {
friend class LabelTrackView; friend class LabelTrackView;
friend class LabelStruct; friend class LabelStruct;
@ -253,7 +256,8 @@ public:
int FindPrevLabel(const SelectedRegion& currentSelection); int FindPrevLabel(const SelectedRegion& currentSelection);
public: public:
void SortLabels(LabelTrackHit *pHit = nullptr); void SortLabels();
private: private:
TrackKind GetKind() const override { return TrackKind::Label; } TrackKind GetKind() const override { return TrackKind::Label; }
@ -296,6 +300,10 @@ private:
void calculateFontHeight(wxDC & dc) const; void calculateFontHeight(wxDC & dc) const;
void RemoveSelectedText(); void RemoveSelectedText();
void OnLabelAdded( LabelTrackEvent& );
void OnLabelDeleted( LabelTrackEvent& );
void OnLabelPermuted( LabelTrackEvent& );
static wxFont msFont; static wxFont msFont;
protected: protected:
@ -303,4 +311,44 @@ protected:
std::shared_ptr<TrackControls> DoGetControls() override; std::shared_ptr<TrackControls> DoGetControls() override;
}; };
struct LabelTrackEvent : TrackListEvent
{
explicit
LabelTrackEvent(
wxEventType commandType, const std::shared_ptr<LabelTrack> &pTrack,
const wxString &title,
int formerPosition,
int presentPosition
)
: TrackListEvent{ commandType, pTrack }
, mTitle{ title }
, mFormerPosition{ formerPosition }
, mPresentPosition{ presentPosition }
{}
LabelTrackEvent( const LabelTrackEvent& ) = default;
wxEvent *Clone() const override {
// wxWidgets will own the event object
return safenew LabelTrackEvent(*this); }
wxString mTitle;
// invalid for addition event
int mFormerPosition{ -1 };
// invalid for deletion event
int mPresentPosition{ -1 };
};
// Posted when a label is added.
wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API,
EVT_LABELTRACK_ADDITION, LabelTrackEvent);
// Posted when a label is deleted.
wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API,
EVT_LABELTRACK_DELETION, LabelTrackEvent);
// Posted when a label is repositioned in the sequence of labels.
wxDECLARE_EXPORTED_EVENT(AUDACITY_DLL_API,
EVT_LABELTRACK_PERMUTED, LabelTrackEvent);
#endif #endif

View File

@ -1081,7 +1081,9 @@ struct TrackListEvent : public wxCommandEvent
TrackListEvent( const TrackListEvent& ) = default; TrackListEvent( const TrackListEvent& ) = default;
wxEvent *Clone() const override { return new TrackListEvent(*this); } wxEvent *Clone() const override {
// wxWidgets will own the event object
return safenew TrackListEvent(*this); }
std::weak_ptr<Track> mpTrack; std::weak_ptr<Track> mpTrack;
int mCode; int mCode;

View File

@ -22,10 +22,46 @@ Paul Licameli split from TrackPanel.cpp
#include <wx/cursor.h> #include <wx/cursor.h>
#include <wx/translation.h> #include <wx/translation.h>
LabelTrackHit::LabelTrackHit( const std::shared_ptr<LabelTrack> &pLT )
: mpLT{ pLT }
{
pLT->Bind(
EVT_LABELTRACK_PERMUTED, &LabelTrackHit::OnLabelPermuted, this );
}
LabelTrackHit::~LabelTrackHit()
{
// Must do this because this sink isn't wxEvtHandler
mpLT->Unbind(
EVT_LABELTRACK_PERMUTED, &LabelTrackHit::OnLabelPermuted, this );
}
void LabelTrackHit::OnLabelPermuted( LabelTrackEvent &e )
{
e.Skip();
if ( e.mpTrack.lock() != mpLT )
return;
auto former = e.mFormerPosition;
auto present = e.mPresentPosition;
auto update = [=]( int &index ){
if ( index == former )
index = present;
else if ( former < index && index <= present )
-- index;
else if ( former > index && index >= present )
++ index;
};
update( mMouseOverLabelLeft );
update( mMouseOverLabelRight );
}
LabelGlyphHandle::LabelGlyphHandle LabelGlyphHandle::LabelGlyphHandle
(const std::shared_ptr<LabelTrack> &pLT, (const std::shared_ptr<LabelTrack> &pLT,
const wxRect &rect, const LabelTrackHit &hit) const wxRect &rect, const std::shared_ptr<LabelTrackHit> &pHit)
: mHit{ hit } : mpHit{ pHit }
, mpLT{ pLT } , mpLT{ pLT }
, mRect{ rect } , mRect{ rect }
{ {
@ -39,7 +75,7 @@ void LabelGlyphHandle::Enter(bool)
UIHandle::Result LabelGlyphHandle::NeedChangeHighlight UIHandle::Result LabelGlyphHandle::NeedChangeHighlight
(const LabelGlyphHandle &oldState, const LabelGlyphHandle &newState) (const LabelGlyphHandle &oldState, const LabelGlyphHandle &newState)
{ {
if (oldState.mHit.mEdge != newState.mHit.mEdge) if (oldState.mpHit->mEdge != newState.mpHit->mEdge)
// pointer moves between the circle and the chevron // pointer moves between the circle and the chevron
return RefreshCode::RefreshCell; return RefreshCode::RefreshCell;
return 0; return 0;
@ -61,14 +97,18 @@ UIHandlePtr LabelGlyphHandle::HitTest
const wxMouseState &state, const wxMouseState &state,
const std::shared_ptr<LabelTrack> &pLT, const wxRect &rect) const std::shared_ptr<LabelTrack> &pLT, const wxRect &rect)
{ {
LabelTrackHit hit{}; // Allocate on heap because there are pointers to it when it is bound as
pLT->OverGlyph(hit, state.m_x, state.m_y); // an event sink, therefore it's not copyable; make it shared so
// LabelGlyphHandle can be copyable:
auto pHit = std::make_shared<LabelTrackHit>( pLT );
pLT->OverGlyph(*pHit, state.m_x, state.m_y);
// IF edge!=0 THEN we've set the cursor and we're done. // IF edge!=0 THEN we've set the cursor and we're done.
// signal this by setting the tip. // signal this by setting the tip.
if ( hit.mEdge & 3 ) if ( pHit->mEdge & 3 )
{ {
auto result = std::make_shared<LabelGlyphHandle>( pLT, rect, hit ); auto result = std::make_shared<LabelGlyphHandle>( pLT, rect, pHit );
result = AssignUIHandlePtr(holder, result); result = AssignUIHandlePtr(holder, result);
return result; return result;
} }
@ -89,9 +129,9 @@ UIHandle::Result LabelGlyphHandle::Click
auto &viewInfo = ViewInfo::Get( *pProject ); auto &viewInfo = ViewInfo::Get( *pProject );
mpLT->HandleGlyphClick mpLT->HandleGlyphClick
(mHit, event, mRect, viewInfo, &viewInfo.selectedRegion); (*mpHit, event, mRect, viewInfo, &viewInfo.selectedRegion);
if (! mHit.mIsAdjustingLabel ) if (! mpHit->mIsAdjustingLabel )
{ {
// The positive hit test should have ensured otherwise // The positive hit test should have ensured otherwise
//wxASSERT(false); //wxASSERT(false);
@ -119,7 +159,7 @@ UIHandle::Result LabelGlyphHandle::Drag
const wxMouseEvent &event = evt.event; const wxMouseEvent &event = evt.event;
auto &viewInfo = ViewInfo::Get( *pProject ); auto &viewInfo = ViewInfo::Get( *pProject );
mpLT->HandleGlyphDragRelease mpLT->HandleGlyphDragRelease
(mHit, event, mRect, viewInfo, &viewInfo.selectedRegion); (*mpHit, event, mRect, viewInfo, &viewInfo.selectedRegion);
// Refresh all so that the change of selection is redrawn in all tracks // Refresh all so that the change of selection is redrawn in all tracks
return result | RefreshCode::RefreshAll | RefreshCode::DrawOverlays; return result | RefreshCode::RefreshAll | RefreshCode::DrawOverlays;
@ -128,7 +168,7 @@ UIHandle::Result LabelGlyphHandle::Drag
HitTestPreview LabelGlyphHandle::Preview HitTestPreview LabelGlyphHandle::Preview
(const TrackPanelMouseState &, const AudacityProject *) (const TrackPanelMouseState &, const AudacityProject *)
{ {
return HitPreview( (mHit.mEdge & 4 )!=0); return HitPreview( (mpHit->mEdge & 4 )!=0);
} }
UIHandle::Result LabelGlyphHandle::Release UIHandle::Result LabelGlyphHandle::Release
@ -140,7 +180,7 @@ UIHandle::Result LabelGlyphHandle::Release
const wxMouseEvent &event = evt.event; const wxMouseEvent &event = evt.event;
auto &viewInfo = ViewInfo::Get( *pProject ); auto &viewInfo = ViewInfo::Get( *pProject );
if (mpLT->HandleGlyphDragRelease if (mpLT->HandleGlyphDragRelease
(mHit, event, mRect, viewInfo, &viewInfo.selectedRegion)) { (*mpHit, event, mRect, viewInfo, &viewInfo.selectedRegion)) {
ProjectHistory::Get( *pProject ).PushState(_("Modified Label"), ProjectHistory::Get( *pProject ).PushState(_("Modified Label"),
_("Label Edit"), _("Label Edit"),
UndoPush::CONSOLIDATE); UndoPush::CONSOLIDATE);

View File

@ -15,6 +15,7 @@ Paul Licameli split from TrackPanel.cpp
class wxMouseState; class wxMouseState;
class LabelTrack; class LabelTrack;
class LabelTrackEvent;
/// mEdge: /// mEdge:
/// 0 if not over a glyph, /// 0 if not over a glyph,
@ -26,12 +27,20 @@ class LabelTrack;
/// mMouseLabelLeft - index of any left label hit /// mMouseLabelLeft - index of any left label hit
/// mMouseLabelRight - index of any right label hit /// mMouseLabelRight - index of any right label hit
/// ///
struct LabelTrackHit { struct LabelTrackHit
{
LabelTrackHit( const std::shared_ptr<LabelTrack> &pLT );
~LabelTrackHit();
int mEdge{}; int mEdge{};
int mMouseOverLabelLeft{ -1 }; /// Keeps track of which left label the mouse is currently over. int mMouseOverLabelLeft{ -1 }; /// Keeps track of which left label the mouse is currently over.
int mMouseOverLabelRight{ -1 }; /// Keeps track of which right label the mouse is currently over. int mMouseOverLabelRight{ -1 }; /// Keeps track of which right label the mouse is currently over.
bool mbIsMoving {}; bool mbIsMoving {};
bool mIsAdjustingLabel {}; bool mIsAdjustingLabel {};
std::shared_ptr<LabelTrack> mpLT {};
void OnLabelPermuted( LabelTrackEvent &e );
}; };
class LabelGlyphHandle final : public LabelDefaultClickHandle class LabelGlyphHandle final : public LabelDefaultClickHandle
@ -41,7 +50,7 @@ class LabelGlyphHandle final : public LabelDefaultClickHandle
public: public:
explicit LabelGlyphHandle explicit LabelGlyphHandle
(const std::shared_ptr<LabelTrack> &pLT, (const std::shared_ptr<LabelTrack> &pLT,
const wxRect &rect, const LabelTrackHit &hit); const wxRect &rect, const std::shared_ptr<LabelTrackHit> &pHit);
LabelGlyphHandle &operator=(const LabelGlyphHandle&) = default; LabelGlyphHandle &operator=(const LabelGlyphHandle&) = default;
@ -72,7 +81,7 @@ public:
bool StopsOnKeystroke() override { return true; } bool StopsOnKeystroke() override { return true; }
LabelTrackHit mHit{}; std::shared_ptr<LabelTrackHit> mpHit{};
static UIHandle::Result NeedChangeHighlight static UIHandle::Result NeedChangeHighlight
(const LabelGlyphHandle &oldState, const LabelGlyphHandle &newState); (const LabelGlyphHandle &oldState, const LabelGlyphHandle &newState);