From 828d9c3ccd3795bb665aaa59694ad89590d019bc Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sun, 18 Jun 2017 13:34:34 -0400 Subject: [PATCH 1/4] Simplify label track hit tests --- .../labeltrack/ui/LabelDefaultClickHandle.cpp | 80 +++---------------- .../labeltrack/ui/LabelDefaultClickHandle.h | 26 ++---- src/tracks/labeltrack/ui/LabelGlyphHandle.cpp | 24 +++--- src/tracks/labeltrack/ui/LabelGlyphHandle.h | 4 +- src/tracks/labeltrack/ui/LabelTextHandle.cpp | 13 ++- src/tracks/labeltrack/ui/LabelTextHandle.h | 4 +- src/tracks/labeltrack/ui/LabelTrackUI.cpp | 40 +++------- 7 files changed, 55 insertions(+), 136 deletions(-) diff --git a/src/tracks/labeltrack/ui/LabelDefaultClickHandle.cpp b/src/tracks/labeltrack/ui/LabelDefaultClickHandle.cpp index c96d76735..46be10c81 100644 --- a/src/tracks/labeltrack/ui/LabelDefaultClickHandle.cpp +++ b/src/tracks/labeltrack/ui/LabelDefaultClickHandle.cpp @@ -21,12 +21,6 @@ LabelDefaultClickHandle::LabelDefaultClickHandle() { } -LabelDefaultClickHandle &LabelDefaultClickHandle::Instance() -{ - static LabelDefaultClickHandle instance; - return instance; -} - LabelDefaultClickHandle::~LabelDefaultClickHandle() { } @@ -65,12 +59,16 @@ void LabelDefaultClickHandle::RestoreState( AudacityProject *pProject ) } } -void LabelDefaultClickHandle::DoClick -(const wxMouseEvent &event, AudacityProject *pProject, TrackPanelCell *pCell) +UIHandle::Result LabelDefaultClickHandle::Click +(const TrackPanelMouseEvent &evt, AudacityProject *pProject) { - LabelTrack *pLT = static_cast(pCell); + using namespace RefreshCode; + // Redraw to show the change of text box selection status + UIHandle::Result result = RefreshAll; - if (event.LeftDown()) + LabelTrack *pLT = static_cast(evt.pCell); + + if (evt.event.LeftDown()) { SaveState( pProject ); @@ -79,7 +77,7 @@ void LabelDefaultClickHandle::DoClick Track *n = iter.First(); while (n) { - if (n->GetKind() == Track::Label && pCell != n) { + if (n->GetKind() == Track::Label && evt.pCell != n) { LabelTrack *const lt = static_cast(n); lt->ResetFlags(); lt->Unselect(); @@ -87,22 +85,6 @@ void LabelDefaultClickHandle::DoClick n = iter.Next(); } } -} - -UIHandle::Result LabelDefaultClickHandle::Click -(const TrackPanelMouseEvent &evt, AudacityProject *pProject) -{ - using namespace RefreshCode; - // Redraw to show the change of text box selection status - UIHandle::Result result = RefreshAll; - - DoClick(evt.event, pProject, evt.pCell); - - if (mpForward) - result |= mpForward->Click(evt, pProject); - else - // No drag or release follows - result |= Cancelled; return result; } @@ -110,19 +92,7 @@ UIHandle::Result LabelDefaultClickHandle::Click UIHandle::Result LabelDefaultClickHandle::Drag (const TrackPanelMouseEvent &evt, AudacityProject *pProject) { - if (mpForward) - return mpForward->Drag(evt, pProject); - else - return RefreshCode::RefreshNone; -} - -HitTestPreview LabelDefaultClickHandle::Preview -(const TrackPanelMouseEvent &evt, const AudacityProject *pProject) -{ - if (mpForward) - return mpForward->Preview(evt, pProject); - else - return {}; + return RefreshCode::RefreshNone; } UIHandle::Result LabelDefaultClickHandle::Release @@ -130,40 +100,12 @@ UIHandle::Result LabelDefaultClickHandle::Release wxWindow *pParent) { mLabelState.reset(); - if (mpForward) - return mpForward->Release(evt, pProject, pParent); - else - return RefreshCode::RefreshNone; + return RefreshCode::RefreshNone; } UIHandle::Result LabelDefaultClickHandle::Cancel(AudacityProject *pProject) { UIHandle::Result result = RefreshCode::RefreshNone; - if (mpForward) - result |= mpForward->Cancel(pProject); RestoreState( pProject ); return result; } - -void LabelDefaultClickHandle::DrawExtras -(DrawingPass pass, - wxDC * dc, const wxRegion &updateRegion, const wxRect &panelRect) -{ - UIHandle::DrawExtras(pass, dc, updateRegion, panelRect); - if (mpForward) - mpForward->DrawExtras(pass, dc, updateRegion, panelRect); -} - -bool LabelDefaultClickHandle::StopsOnKeystroke() -{ - return - (mpForward && mpForward->StopsOnKeystroke()) || - UIHandle::StopsOnKeystroke(); -} - -void LabelDefaultClickHandle::OnProjectChange(AudacityProject *pProject) -{ - if (mpForward) - return mpForward->OnProjectChange(pProject); - UIHandle::OnProjectChange(pProject); -} diff --git a/src/tracks/labeltrack/ui/LabelDefaultClickHandle.h b/src/tracks/labeltrack/ui/LabelDefaultClickHandle.h index c7ce8ac63..3be5bece4 100644 --- a/src/tracks/labeltrack/ui/LabelDefaultClickHandle.h +++ b/src/tracks/labeltrack/ui/LabelDefaultClickHandle.h @@ -18,29 +18,24 @@ class wxMouseEvent; struct HitTestResult; class LabelTrack; -// Adds some behavior to click, then calls through to other mouse handling. -class LabelDefaultClickHandle final : public UIHandle +// Used as a base class. +// Adds some behavior to clicks. +class LabelDefaultClickHandle /* not final */ : public UIHandle { - LabelDefaultClickHandle(); LabelDefaultClickHandle(const LabelDefaultClickHandle&) = delete; LabelDefaultClickHandle &operator=(const LabelDefaultClickHandle&) = delete; public: - static LabelDefaultClickHandle& Instance(); + LabelDefaultClickHandle(); virtual ~LabelDefaultClickHandle(); - void DoClick - (const wxMouseEvent &event, AudacityProject *pProject, TrackPanelCell *pCell); - Result Click (const TrackPanelMouseEvent &event, AudacityProject *pProject) override; Result Drag (const TrackPanelMouseEvent &event, AudacityProject *pProject) override; - HitTestPreview Preview - (const TrackPanelMouseEvent &event, const AudacityProject *pProject) - override; + // does not override Preview() Result Release (const TrackPanelMouseEvent &event, AudacityProject *pProject, @@ -48,17 +43,6 @@ public: Result Cancel(AudacityProject *pProject) override; - void DrawExtras - (DrawingPass pass, - wxDC * dc, const wxRegion &updateRegion, const wxRect &panelRect) - override; - - bool StopsOnKeystroke() override; - - void OnProjectChange(AudacityProject *pProject) override; - - UIHandle *mpForward {}; - private: struct LabelState; std::unique_ptr< LabelState > mLabelState; diff --git a/src/tracks/labeltrack/ui/LabelGlyphHandle.cpp b/src/tracks/labeltrack/ui/LabelGlyphHandle.cpp index ba79405ea..1a2ae7a6a 100644 --- a/src/tracks/labeltrack/ui/LabelGlyphHandle.cpp +++ b/src/tracks/labeltrack/ui/LabelGlyphHandle.cpp @@ -8,6 +8,7 @@ Paul Licameli split from TrackPanel.cpp **********************************************************************/ +#include "../../../Audacity.h" #include "LabelGlyphHandle.h" #include "../../../HitTestResult.h" #include "../../../LabelTrack.h" @@ -90,6 +91,8 @@ LabelGlyphHandle::~LabelGlyphHandle() UIHandle::Result LabelGlyphHandle::Click (const TrackPanelMouseEvent &evt, AudacityProject *pProject) { + auto result = LabelDefaultClickHandle::Click( evt, pProject ); + TrackPanelCell *const pCell = evt.pCell; const wxMouseEvent &event = evt.event; const wxRect &rect = evt.rect; @@ -104,11 +107,11 @@ UIHandle::Result LabelGlyphHandle::Click { // The positive hit test should have ensured otherwise //wxASSERT(false); - return RefreshCode::Cancelled; + result |= RefreshCode::Cancelled; } - - // redraw the track. - return RefreshCode::RefreshCell; + else + // redraw the track. + result |= RefreshCode::RefreshCell; // handle shift+ctrl down /*if (event.ShiftDown()) { // && event.ControlDown()) { @@ -117,19 +120,20 @@ UIHandle::Result LabelGlyphHandle::Click return; }*/ - - return RefreshCode::RefreshNone; + return result; } UIHandle::Result LabelGlyphHandle::Drag (const TrackPanelMouseEvent &evt, AudacityProject *pProject) { + auto result = LabelDefaultClickHandle::Drag( evt, pProject ); + const wxMouseEvent &event = evt.event; ViewInfo &viewInfo = pProject->GetViewInfo(); mpLT->HandleGlyphDragRelease(event, mRect, viewInfo, &viewInfo.selectedRegion); // Refresh all so that the change of selection is redrawn in all tracks - return RefreshCode::RefreshAll | RefreshCode::DrawOverlays; + return result | RefreshCode::RefreshAll | RefreshCode::DrawOverlays; } HitTestPreview LabelGlyphHandle::Preview @@ -142,6 +146,7 @@ UIHandle::Result LabelGlyphHandle::Release (const TrackPanelMouseEvent &evt, AudacityProject *pProject, wxWindow *pParent) { + auto result = LabelDefaultClickHandle::Release( evt, pProject, pParent ); mpLT->mOldEdge = 0; const wxMouseEvent &event = evt.event; @@ -153,12 +158,13 @@ UIHandle::Result LabelGlyphHandle::Release } // Refresh all so that the change of selection is redrawn in all tracks - return RefreshCode::RefreshAll | RefreshCode::DrawOverlays; + return result | RefreshCode::RefreshAll | RefreshCode::DrawOverlays; } UIHandle::Result LabelGlyphHandle::Cancel(AudacityProject *pProject) { mpLT->mOldEdge = 0; pProject->RollbackState(); - return RefreshCode::RefreshAll; + auto result = LabelDefaultClickHandle::Cancel( pProject ); + return result | RefreshCode::RefreshAll; } diff --git a/src/tracks/labeltrack/ui/LabelGlyphHandle.h b/src/tracks/labeltrack/ui/LabelGlyphHandle.h index f2f5baf48..50b9887b6 100644 --- a/src/tracks/labeltrack/ui/LabelGlyphHandle.h +++ b/src/tracks/labeltrack/ui/LabelGlyphHandle.h @@ -11,14 +11,14 @@ Paul Licameli split from TrackPanel.cpp #ifndef __AUDACITY_LABEL_GLYPH_HANDLE__ #define __AUDACITY_LABEL_GLYPH_HANDLE__ -#include "../../../UIHandle.h" +#include "LabelDefaultClickHandle.h" #include class wxMouseEvent; struct HitTestResult; class LabelTrack; -class LabelGlyphHandle final : public UIHandle +class LabelGlyphHandle final : public LabelDefaultClickHandle { LabelGlyphHandle(); LabelGlyphHandle(const LabelGlyphHandle&) = delete; diff --git a/src/tracks/labeltrack/ui/LabelTextHandle.cpp b/src/tracks/labeltrack/ui/LabelTextHandle.cpp index ac879ef5b..548b3dab5 100644 --- a/src/tracks/labeltrack/ui/LabelTextHandle.cpp +++ b/src/tracks/labeltrack/ui/LabelTextHandle.cpp @@ -45,6 +45,8 @@ LabelTextHandle::~LabelTextHandle() UIHandle::Result LabelTextHandle::Click (const TrackPanelMouseEvent &evt, AudacityProject *pProject) { + auto result = LabelDefaultClickHandle::Click( evt, pProject ); + auto &selectionState = pProject->GetSelectionState(); TrackList *const tracks = pProject->GetTracks(); mChanger = @@ -98,14 +100,14 @@ UIHandle::Result LabelTextHandle::Click if (!unsafe) pProject->ModifyState(false); - return RefreshCode::RefreshCell | RefreshCode::UpdateSelection; + return result | RefreshCode::RefreshCell | RefreshCode::UpdateSelection; } UIHandle::Result LabelTextHandle::Drag (const TrackPanelMouseEvent &evt, AudacityProject *pProject) { using namespace RefreshCode; - Result result = RefreshNone; + auto result = LabelDefaultClickHandle::Drag( evt, pProject ); const wxMouseEvent &event = evt.event; auto pLT = mpLT.lock(); @@ -145,6 +147,8 @@ UIHandle::Result LabelTextHandle::Release (const TrackPanelMouseEvent &evt, AudacityProject *pProject, wxWindow *pParent) { + auto result = LabelDefaultClickHandle::Release( evt, pProject, pParent ); + // Only selected a part of a text string and changed track selectedness. // No undoable effects. @@ -162,7 +166,7 @@ UIHandle::Result LabelTextHandle::Release if (event.LeftUp()) mLabelTrackStartXPos = -1; - return RefreshCode::RefreshNone; + return result | RefreshCode::RefreshNone; } UIHandle::Result LabelTextHandle::Cancel( AudacityProject *pProject ) @@ -173,5 +177,6 @@ UIHandle::Result LabelTextHandle::Cancel( AudacityProject *pProject ) mChanger.release(); ViewInfo &viewInfo = pProject->GetViewInfo(); viewInfo.selectedRegion = mSelectedRegion; - return RefreshCode::RefreshAll; + auto result = LabelDefaultClickHandle::Cancel( pProject ); + return result | RefreshCode::RefreshAll; } diff --git a/src/tracks/labeltrack/ui/LabelTextHandle.h b/src/tracks/labeltrack/ui/LabelTextHandle.h index 4fb6760e9..1a80f155d 100644 --- a/src/tracks/labeltrack/ui/LabelTextHandle.h +++ b/src/tracks/labeltrack/ui/LabelTextHandle.h @@ -11,7 +11,7 @@ Paul Licameli split from TrackPanel.cpp #ifndef __AUDACITY_LABEL_TEXT_HANDLE__ #define __AUDACITY_LABEL_TEXT_HANDLE__ -#include "../../../UIHandle.h" +#include "LabelDefaultClickHandle.h" #include "../../../MemoryX.h" #include "../../../SelectedRegion.h" #include @@ -21,7 +21,7 @@ struct HitTestResult; class LabelTrack; class SelectionStateChanger; -class LabelTextHandle final : public UIHandle +class LabelTextHandle final : public LabelDefaultClickHandle { LabelTextHandle(); LabelTextHandle(const LabelTextHandle&) = delete; diff --git a/src/tracks/labeltrack/ui/LabelTrackUI.cpp b/src/tracks/labeltrack/ui/LabelTrackUI.cpp index fe6735b1a..317f26e47 100644 --- a/src/tracks/labeltrack/ui/LabelTrackUI.cpp +++ b/src/tracks/labeltrack/ui/LabelTrackUI.cpp @@ -26,44 +26,26 @@ HitTestResult LabelTrack::HitTest (const TrackPanelMouseEvent &evt, const AudacityProject *pProject) { - // PRL: Maybe I did too much work to preserve old behavior, but anyway, - // this unusually combines parts of two or more hit test results. - HitTestResult result; const wxMouseEvent &event = evt.event; // Try label movement handles first result = LabelGlyphHandle::HitTest(event, this); - // Hit test may request refresh even if a miss - auto refreshResult = result.preview.refreshCode; + + if ( !result.handle ) + // Missed glyph, try text box + result = LabelTextHandle::HitTest(event, this); + + if ( !result.handle ) + result = Track::HitTest(evt, pProject); if ( !result.handle ) { - // Missed glyph, try text box - // This hit test does not define its own messages or cursor - HitTestResult defaultResult = Track::HitTest(evt, pProject); - if (!defaultResult.handle) { - // In case of multi tool, default to selection. - const ToolsToolBar *const pTtb = pProject->GetToolsToolBar(); - if (pTtb->IsDown(multiTool)) - defaultResult = SelectHandle::HitTest(evt, pProject, this); - } - result = LabelTextHandle::HitTest(event, this); - if (result.handle) - // Use any cursor or status message change from catchall, - // But let the text ui handle pass - result.preview = defaultResult.preview; - else - result = defaultResult; + // In case of multi tool, default to selection. + const ToolsToolBar *const pTtb = pProject->GetToolsToolBar(); + if (pTtb->IsDown(multiTool)) + result = SelectHandle::HitTest(evt, pProject, this); } - // Now attach some common extra work to the click action - LabelDefaultClickHandle::Instance().mpForward = result.handle; - result.handle = &LabelDefaultClickHandle::Instance(); - - // Don't lose the refresh result side effect of the glyph - // hit test - result.preview.refreshCode |= refreshResult; - return result; } From 24ebf27bb66d10992340937885a2e1d8d89ae83b Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sun, 18 Jun 2017 14:29:08 -0400 Subject: [PATCH 2/4] Cursor change and status message for mouse over label text boxes --- src/tracks/labeltrack/ui/LabelTextHandle.cpp | 15 +++++++++++++-- src/tracks/labeltrack/ui/LabelTextHandle.h | 2 ++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/tracks/labeltrack/ui/LabelTextHandle.cpp b/src/tracks/labeltrack/ui/LabelTextHandle.cpp index 548b3dab5..e5c908774 100644 --- a/src/tracks/labeltrack/ui/LabelTextHandle.cpp +++ b/src/tracks/labeltrack/ui/LabelTextHandle.cpp @@ -16,6 +16,7 @@ Paul Licameli split from TrackPanel.cpp #include "../../../RefreshCode.h" #include "../../../TrackPanelMouseEvent.h" #include "../../../ViewInfo.h" +#include "../../../images/Cursors.h" LabelTextHandle::LabelTextHandle() { @@ -27,13 +28,23 @@ LabelTextHandle &LabelTextHandle::Instance() return instance; } +HitTestPreview LabelTextHandle::HitPreview() +{ + static auto ibeamCursor = + ::MakeCursor(wxCURSOR_IBEAM, IBeamCursorXpm, 17, 16); + return { + _("Click to edit label text"), + ibeamCursor.get() + }; +} + HitTestResult LabelTextHandle::HitTest(const wxMouseEvent &event, LabelTrack *pLT) { // If Control is down, let the select handle be hit instead if (!event.ControlDown() && pLT->OverATextBox(event.m_x, event.m_y) >= 0) // There was no cursor change or status message for mousing over a label text box - return { {}, &Instance() }; + return { HitPreview(), &Instance() }; return {}; } @@ -140,7 +151,7 @@ UIHandle::Result LabelTextHandle::Drag HitTestPreview LabelTextHandle::Preview (const TrackPanelMouseEvent &evt, const AudacityProject *pProject) { - return {}; + return HitPreview(); } UIHandle::Result LabelTextHandle::Release diff --git a/src/tracks/labeltrack/ui/LabelTextHandle.h b/src/tracks/labeltrack/ui/LabelTextHandle.h index 1a80f155d..69c0a392a 100644 --- a/src/tracks/labeltrack/ui/LabelTextHandle.h +++ b/src/tracks/labeltrack/ui/LabelTextHandle.h @@ -28,6 +28,8 @@ class LabelTextHandle final : public LabelDefaultClickHandle LabelTextHandle &operator=(const LabelTextHandle&) = delete; static LabelTextHandle& Instance(); + static HitTestPreview HitPreview(); + public: static HitTestResult HitTest(const wxMouseEvent &event, LabelTrack *pLT); From 9e0010ec5fed046daf14d025be1b8579e84674a2 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Tue, 20 Jun 2017 21:50:02 -0400 Subject: [PATCH 3/4] Time track editing requires click on the curve in multi tool... ... and it will also work so, later, in all tools besides Zoom. --- .../wavetrack/ui/WaveTrackUI.cpp | 2 +- src/tracks/timetrack/ui/TimeTrackUI.cpp | 9 +-- src/tracks/ui/EnvelopeHandle.cpp | 76 +++++++++++++------ src/tracks/ui/EnvelopeHandle.h | 13 +++- 4 files changed, 69 insertions(+), 31 deletions(-) diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackUI.cpp b/src/tracks/playabletrack/wavetrack/ui/WaveTrackUI.cpp index f58590acb..aec70ac1d 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveTrackUI.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackUI.cpp @@ -48,7 +48,7 @@ HitTestResult WaveTrack::HitTest if (event.event.CmdDown()) result = TimeShiftHandle::HitAnywhere(pProject); else if (NULL != - (result = EnvelopeHandle::WaveTrackHitTest(event.event, event.rect, pProject, this)) + (result = EnvelopeHandle::WaveTrackHitTest(event.event, event.rect, pProject, *this)) .preview.cursor) ; else if (NULL != (result = diff --git a/src/tracks/timetrack/ui/TimeTrackUI.cpp b/src/tracks/timetrack/ui/TimeTrackUI.cpp index db313119c..1473f40b5 100644 --- a/src/tracks/timetrack/ui/TimeTrackUI.cpp +++ b/src/tracks/timetrack/ui/TimeTrackUI.cpp @@ -13,6 +13,7 @@ Paul Licameli split from TrackPanel.cpp #include "TimeTrackVRulerControls.h" #include "../../../HitTestResult.h" +#include "../../../TrackPanelMouseEvent.h" #include "../../../Project.h" #include "../../../toolbars/ToolsToolBar.h" @@ -26,12 +27,8 @@ HitTestResult TimeTrack::HitTest if (result.preview.cursor) return result; - const ToolsToolBar *const pTtb = pProject->GetToolsToolBar(); - if (pTtb->IsDown(multiTool)) - // No hit test --unconditional availability. - result = EnvelopeHandle::HitAnywhere(pProject); - - return result; + return EnvelopeHandle::TimeTrackHitTest + ( event.event, event.rect, pProject, *this ); } std::shared_ptr TimeTrack::GetControls() diff --git a/src/tracks/ui/EnvelopeHandle.cpp b/src/tracks/ui/EnvelopeHandle.cpp index 33939b4aa..a30a6e290 100644 --- a/src/tracks/ui/EnvelopeHandle.cpp +++ b/src/tracks/ui/EnvelopeHandle.cpp @@ -62,39 +62,76 @@ HitTestResult EnvelopeHandle::HitAnywhere(const AudacityProject *pProject) }; } +namespace { + void GetTimeTrackData + (const AudacityProject &project, const TimeTrack &tt, + double &dBRange, bool &dB, float &zoomMin, float &zoomMax) + { + const auto &viewInfo = project.GetViewInfo(); + dBRange = viewInfo.dBr; + dB = tt.GetDisplayLog(); + zoomMin = tt.GetRangeLower(), zoomMax = tt.GetRangeUpper(); + if (dB) { + // MB: silly way to undo the work of GetWaveYPos while still getting a logarithmic scale + zoomMin = LINEAR_TO_DB(std::max(1.0e-7, double(dBRange))) / dBRange + 1.0; + zoomMax = LINEAR_TO_DB(std::max(1.0e-7, double(zoomMax))) / dBRange + 1.0; + } + } +} + +HitTestResult EnvelopeHandle::TimeTrackHitTest +(const wxMouseEvent &event, const wxRect &rect, + const AudacityProject *pProject, TimeTrack &tt) +{ + auto envelope = tt.GetEnvelope(); + if (!envelope) + return {}; + bool dB; + double dBRange; + float zoomMin, zoomMax; + GetTimeTrackData( *pProject, tt, dBRange, dB, zoomMin, zoomMax); + return EnvelopeHandle::HitEnvelope + (event, rect, pProject, envelope, zoomMin, zoomMax, dB, dBRange); +} + HitTestResult EnvelopeHandle::WaveTrackHitTest (const wxMouseEvent &event, const wxRect &rect, - const AudacityProject *pProject, Cell *pCell) + const AudacityProject *pProject, WaveTrack &wt) { - const ViewInfo &viewInfo = pProject->GetViewInfo(); - Track *const pTrack = static_cast(pCell); - /// method that tells us if the mouse event landed on an /// envelope boundary. - if (pTrack->GetKind() != Track::Wave) - return {}; - - WaveTrack *const wavetrack = static_cast(pTrack); - Envelope *const envelope = wavetrack->GetEnvelopeAtX(event.GetX()); + const Envelope *const envelope = wt.GetEnvelopeAtX(event.GetX()); if (!envelope) return {}; - const int displayType = wavetrack->GetDisplay(); + const int displayType = wt.GetDisplay(); // Not an envelope hit, unless we're using a type of wavetrack display // suitable for envelopes operations, ie one of the Wave displays. if (displayType != WaveTrack::Waveform) return {}; // No envelope, not a hit, so return. // Get envelope point, range 0.0 to 1.0 - const bool dB = !wavetrack->GetWaveformSettings().isLinear(); - const double envValue = - envelope->GetValue(viewInfo.PositionToTime(event.m_x, rect.x)); + const bool dB = !wt.GetWaveformSettings().isLinear(); float zoomMin, zoomMax; - wavetrack->GetDisplayBounds(&zoomMin, &zoomMax); + wt.GetDisplayBounds(&zoomMin, &zoomMax); - const float dBRange = wavetrack->GetWaveformSettings().dBRange; + const float dBRange = wt.GetWaveformSettings().dBRange; + + return EnvelopeHandle::HitEnvelope + (event, rect, pProject, envelope, zoomMin, zoomMax, dB, dBRange); +} + +HitTestResult EnvelopeHandle::HitEnvelope +(const wxMouseEvent &event, const wxRect &rect, const AudacityProject *pProject, + const Envelope *envelope, float zoomMin, float zoomMax, + bool dB, float dBRange) +{ + const ViewInfo &viewInfo = pProject->GetViewInfo(); + + const double envValue = + envelope->GetValue(viewInfo.PositionToTime(event.m_x, rect.x)); // Get y position of envelope point. int yValue = GetWaveYPos(envValue, @@ -187,14 +224,7 @@ UIHandle::Result EnvelopeHandle::Click auto clickedEnvelope = tt->GetEnvelope(); if (!clickedEnvelope) return Cancelled; - mLog = tt->GetDisplayLog(); - mLower = tt->GetRangeLower(), mUpper = tt->GetRangeUpper(); - if (mLog) { - // MB: silly way to undo the work of GetWaveYPos while still getting a logarithmic scale - mdBRange = viewInfo.dBr; - mLower = LINEAR_TO_DB(std::max(1.0e-7, double(mLower))) / mdBRange + 1.0; - mUpper = LINEAR_TO_DB(std::max(1.0e-7, double(mUpper))) / mdBRange + 1.0; - } + GetTimeTrackData( *pProject, *tt, mdBRange, mLog, mLower, mUpper); mEnvelopeEditor = std::make_unique< EnvelopeEditor >( *clickedEnvelope, false ); mEnvelopeEditorRight.reset(); diff --git a/src/tracks/ui/EnvelopeHandle.h b/src/tracks/ui/EnvelopeHandle.h index fe426888b..25c8f9cf9 100644 --- a/src/tracks/ui/EnvelopeHandle.h +++ b/src/tracks/ui/EnvelopeHandle.h @@ -17,9 +17,11 @@ Paul Licameli split from TrackPanel.cpp class wxMouseEvent; #include +class Envelope; class EnvelopeEditor; struct HitTestResult; class ViewInfo; +class TimeTrack; class WaveTrack; class EnvelopeHandle final : public UIHandle @@ -30,11 +32,20 @@ class EnvelopeHandle final : public UIHandle static EnvelopeHandle& Instance(); static HitTestPreview HitPreview(const AudacityProject *pProject, bool unsafe); + static HitTestResult HitEnvelope + (const wxMouseEvent &event, const wxRect &rect, + const AudacityProject *pProject, + const Envelope *envelope, float zoomMin, float zoomMax, + bool dB, float dBRange); + public: static HitTestResult HitAnywhere(const AudacityProject *pProject); + static HitTestResult TimeTrackHitTest + (const wxMouseEvent &event, const wxRect &rect, + const AudacityProject *pProject, TimeTrack &tt); static HitTestResult WaveTrackHitTest (const wxMouseEvent &event, const wxRect &rect, - const AudacityProject *pProject, Cell *pCell); + const AudacityProject *pProject, WaveTrack &wt); virtual ~EnvelopeHandle(); From bce3571372f6c6665198d0bf01393cbc283945f3 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Mon, 19 Jun 2017 15:02:45 -0400 Subject: [PATCH 4/4] Redo hit test priorities in TrackPanel... Zoom tool takes precedence; Otherwise do special hits appropriate to the track subclass -- and only WaveTrack here uses Tools toolbar state, and now disallows clicks on things when they are not drawn because the view is spectrogram; Finally, default to right button zooming in Multi tool, or to time shift in that tool, or to selection adjustment in Multi or in Select tool. --- src/LabelTrack.h | 5 +- src/NoteTrack.h | 5 +- src/TimeTrack.h | 5 +- src/Track.h | 18 +++- src/WaveTrack.h | 5 +- src/tracks/labeltrack/ui/LabelTrackUI.cpp | 15 +--- .../notetrack/ui/NoteTrackUI.cpp | 34 +------- .../wavetrack/ui/WaveTrackUI.cpp | 87 +++++++++++-------- src/tracks/timetrack/ui/TimeTrackUI.cpp | 9 +- src/tracks/ui/TrackUI.cpp | 44 +++++----- 10 files changed, 109 insertions(+), 118 deletions(-) diff --git a/src/LabelTrack.h b/src/LabelTrack.h index 5f44ed179..cd9cc6e37 100644 --- a/src/LabelTrack.h +++ b/src/LabelTrack.h @@ -121,9 +121,10 @@ class AUDACITY_DLL_API LabelTrack final : public Track virtual ~ LabelTrack(); - HitTestResult HitTest + HitTestResult DetailedHitTest (const TrackPanelMouseEvent &event, - const AudacityProject *pProject) override; + const AudacityProject *pProject, int currentTool, bool bMultiTool) + override; bool DoCaptureKey(wxKeyEvent &event); unsigned CaptureKey diff --git a/src/NoteTrack.h b/src/NoteTrack.h index e4efb4619..84ff4dd79 100644 --- a/src/NoteTrack.h +++ b/src/NoteTrack.h @@ -68,9 +68,10 @@ class AUDACITY_DLL_API NoteTrack final NoteTrack(const std::shared_ptr &projDirManager); virtual ~NoteTrack(); - HitTestResult HitTest + HitTestResult DetailedHitTest (const TrackPanelMouseEvent &event, - const AudacityProject *pProject) override; + const AudacityProject *pProject, int currentTool, bool bMultiTool) + override; using Holder = std::unique_ptr; Track::Holder Duplicate() const override; diff --git a/src/TimeTrack.h b/src/TimeTrack.h index 4883cba39..450ec5eaa 100644 --- a/src/TimeTrack.h +++ b/src/TimeTrack.h @@ -49,9 +49,10 @@ class TimeTrack final : public Track { void Silence(double t0, double t1) override; void InsertSilence(double t, double len) override; - HitTestResult HitTest + HitTestResult DetailedHitTest (const TrackPanelMouseEvent &event, - const AudacityProject *pProject) override; + const AudacityProject *pProject, int currentTool, bool bMultiTool) + override; // Identifying the type of track int GetKind() const override { return Time; } diff --git a/src/Track.h b/src/Track.h index e33af36ee..41a78281b 100644 --- a/src/Track.h +++ b/src/Track.h @@ -108,7 +108,6 @@ class AUDACITY_DLL_API Track /* not final */ bool mMinimized; public: - mutable wxSize vrulerSize; // Given a bare pointer, find a shared_ptr. But this is not possible for // a track not owned by any list, so the result can be null. @@ -131,11 +130,22 @@ class AUDACITY_DLL_API Track /* not final */ return {}; } - // An implementation is defined for call-through from subclasses, but - // the inherited method is still marked pure virtual + // Cause certain overriding tool modes (Zoom; future ones?) to behave + // uniformly in all tracks, disregarding track contents. + // Do not further override this... HitTestResult HitTest (const TrackPanelMouseEvent &, const AudacityProject *pProject) - override = 0; + final; + + public: + + // Rather override this for subclasses: + virtual HitTestResult DetailedHitTest + (const TrackPanelMouseEvent &, + const AudacityProject *pProject, int currentTool, bool bMultiTool) + = 0; + + mutable wxSize vrulerSize; // Return another, associated TrackPanelCell object that implements the // drop-down, close and minimize buttons, etc. diff --git a/src/WaveTrack.h b/src/WaveTrack.h index 9efdde3a8..416c73982 100644 --- a/src/WaveTrack.h +++ b/src/WaveTrack.h @@ -103,9 +103,10 @@ class AUDACITY_DLL_API WaveTrack final : public PlayableTrack { virtual ~WaveTrack(); - HitTestResult HitTest + HitTestResult DetailedHitTest (const TrackPanelMouseEvent &event, - const AudacityProject *pProject) override; + const AudacityProject *pProject, int currentTool, bool bMultiTool) + override; double GetOffset() const override; void SetOffset(double o) override; diff --git a/src/tracks/labeltrack/ui/LabelTrackUI.cpp b/src/tracks/labeltrack/ui/LabelTrackUI.cpp index 317f26e47..f74a68852 100644 --- a/src/tracks/labeltrack/ui/LabelTrackUI.cpp +++ b/src/tracks/labeltrack/ui/LabelTrackUI.cpp @@ -20,11 +20,10 @@ Paul Licameli split from TrackPanel.cpp #include "../../../HitTestResult.h" #include "../../../Project.h" #include "../../../TrackPanelMouseEvent.h" -#include "../../../toolbars/ToolsToolBar.h" -HitTestResult LabelTrack::HitTest +HitTestResult LabelTrack::DetailedHitTest (const TrackPanelMouseEvent &evt, - const AudacityProject *pProject) + const AudacityProject *pProject, int, bool) { HitTestResult result; const wxMouseEvent &event = evt.event; @@ -36,16 +35,6 @@ HitTestResult LabelTrack::HitTest // Missed glyph, try text box result = LabelTextHandle::HitTest(event, this); - if ( !result.handle ) - result = Track::HitTest(evt, pProject); - - if ( !result.handle ) { - // In case of multi tool, default to selection. - const ToolsToolBar *const pTtb = pProject->GetToolsToolBar(); - if (pTtb->IsDown(multiTool)) - result = SelectHandle::HitTest(evt, pProject, this); - } - return result; } diff --git a/src/tracks/playabletrack/notetrack/ui/NoteTrackUI.cpp b/src/tracks/playabletrack/notetrack/ui/NoteTrackUI.cpp index e4d2de76a..5c72c3e7f 100644 --- a/src/tracks/playabletrack/notetrack/ui/NoteTrackUI.cpp +++ b/src/tracks/playabletrack/notetrack/ui/NoteTrackUI.cpp @@ -21,44 +21,18 @@ Paul Licameli split from TrackPanel.cpp #include "../../../../TrackPanelMouseEvent.h" #include "../../../ui/SelectHandle.h" #include "StretchHandle.h" -#include "../../../../toolbars/ToolsToolBar.h" -HitTestResult NoteTrack::HitTest +HitTestResult NoteTrack::DetailedHitTest (const TrackPanelMouseEvent &event, - const AudacityProject *pProject) + const AudacityProject *pProject, int, bool ) { - const ToolsToolBar *const pTtb = pProject->GetToolsToolBar(); - - // If the cursor is in the hot zone for stretching, that takes precedence - // over selection, but it didn't take precedence over other hits. - // That was the old logic, and maybe I tried too hard to preserve it just so. - // PRL. - // Eligible for stretch? - HitTestResult result1; + HitTestResult result; #ifdef USE_MIDI StretchHandle::StretchState state; - result1 = StretchHandle::HitTest( event, pProject, this, state ); + result = StretchHandle::HitTest( event, pProject, this, state ); #endif - // But some other non-select tool like zoom may take priority. - HitTestResult result = Track::HitTest(event, pProject); - if (result.preview.cursor && - !(result1.preview.cursor && pTtb->GetCurrentTool() == selectTool)) - return result; - - if (pTtb->IsDown(multiTool)) { - // Default to selection - if (!result1.preview.cursor && - NULL != (result = - SelectHandle::HitTest(event, pProject, this)).preview.cursor) - return result; - } - - // Do stretch! - if (result1.preview.cursor) - return result1; - return result; } diff --git a/src/tracks/playabletrack/wavetrack/ui/WaveTrackUI.cpp b/src/tracks/playabletrack/wavetrack/ui/WaveTrackUI.cpp index aec70ac1d..bee5e50ef 100644 --- a/src/tracks/playabletrack/wavetrack/ui/WaveTrackUI.cpp +++ b/src/tracks/playabletrack/wavetrack/ui/WaveTrackUI.cpp @@ -23,47 +23,66 @@ Paul Licameli split from TrackPanel.cpp #include "SampleHandle.h" #include "../../../ui/TimeShiftHandle.h" -HitTestResult WaveTrack::HitTest +HitTestResult WaveTrack::DetailedHitTest (const TrackPanelMouseEvent &event, - const AudacityProject *pProject) + const AudacityProject *pProject, int currentTool, bool bMultiTool) { - // FIXME: Should similar logic apply to NoteTrack (#if defined(USE_MIDI)) ? - // From here on the order in which we hit test determines - // which tool takes priority in the rare cases where it - // could be more than one. + // This is the only override of Track::DetailedHitTest that still + // depends on the state of the Tools toolbar. + // If that toolbar were eliminated, this could simplify to a sequence of + // hit test routines describable by a table. - // This hit was always tested first no matter which tool: - HitTestResult result = CutlineHandle::HitTest(event.event, event.rect, pProject, this); - if (result.preview.cursor) - return result; + WaveTrack *const wavetrack = static_cast(event.pCell); + bool isWaveform = (wavetrack->GetDisplay() == WaveTrack::Waveform); - result = Track::HitTest(event, pProject); - if (result.preview.cursor) - return result; + if (bMultiTool && event.event.CmdDown()) + // Ctrl modifier key in multi-tool overrides everything else + // (But this does not do the time shift constrained to the vertical only, + // which is what happens when you hold Ctrl in the Time Shift tool mode) + return TimeShiftHandle::HitAnywhere(pProject); - const ToolsToolBar *const pTtb = pProject->GetToolsToolBar(); - if (pTtb->IsDown(multiTool)) { - // Replicate some of the logic of TrackPanel::DetermineToolToUse - int currentTool = -1; - if (event.event.CmdDown()) - result = TimeShiftHandle::HitAnywhere(pProject); - else if (NULL != - (result = EnvelopeHandle::WaveTrackHitTest(event.event, event.rect, pProject, *this)) - .preview.cursor) - ; - else if (NULL != (result = - TimeShiftHandle::HitTest(event.event, event.rect, pProject)).preview.cursor) - ; - else if (NULL != (result = - SampleHandle::HitTest(event.event, event.rect, pProject, this)).preview.cursor) - ; - else if (NULL != (result = - SelectHandle::HitTest(event, pProject, this)).preview.cursor) - // default of all other hit tests - ; + // Some special targets are not drawn in spectrogram, + // so don't hit them in such views. + else if (isWaveform) { + HitTestResult result; + if (NULL != + (result = CutlineHandle::HitTest + (event.event, event.rect, pProject, this)) + .preview.cursor) + // This overriding test applies in all tools + return result; + else if (bMultiTool) { + // Conditional hit tests + // If Tools toolbar were eliminated, we would keep these + // The priority of these, in case more than one might apply at one + // point, seems arbitrary + if (NULL != (result = EnvelopeHandle::WaveTrackHitTest + (event.event, event.rect, pProject, *this)) + .preview.cursor) + ; + else if (NULL != (result = TimeShiftHandle::HitTest + (event.event, event.rect, pProject)).preview.cursor) + // This is the hit test on the "grips" drawn left and + // right in Multi only + ; + else if (NULL != (result = SampleHandle::HitTest + (event.event, event.rect, pProject, this)).preview.cursor) + ; + return result; + } + else switch ( currentTool ) { + // Unconditional hits appropriate to the tool + // If tools toolbar were eliminated, we would eliminate these + case envelopeTool: + return EnvelopeHandle::HitAnywhere(pProject); + case drawTool: + return SampleHandle::HitAnywhere(event.event, pProject); + default: + break; + } } - return result; + return {}; } std::shared_ptr WaveTrack::GetControls() diff --git a/src/tracks/timetrack/ui/TimeTrackUI.cpp b/src/tracks/timetrack/ui/TimeTrackUI.cpp index 1473f40b5..bb3986ca9 100644 --- a/src/tracks/timetrack/ui/TimeTrackUI.cpp +++ b/src/tracks/timetrack/ui/TimeTrackUI.cpp @@ -15,18 +15,13 @@ Paul Licameli split from TrackPanel.cpp #include "../../../HitTestResult.h" #include "../../../TrackPanelMouseEvent.h" #include "../../../Project.h" -#include "../../../toolbars/ToolsToolBar.h" #include "../../ui/EnvelopeHandle.h" -HitTestResult TimeTrack::HitTest +HitTestResult TimeTrack::DetailedHitTest (const TrackPanelMouseEvent &event, - const AudacityProject *pProject) + const AudacityProject *pProject, int, bool) { - HitTestResult result = Track::HitTest(event, pProject); - if (result.preview.cursor) - return result; - return EnvelopeHandle::TimeTrackHitTest ( event.event, event.rect, pProject, *this ); } diff --git a/src/tracks/ui/TrackUI.cpp b/src/tracks/ui/TrackUI.cpp index b9ba63f7d..b1ffeb557 100644 --- a/src/tracks/ui/TrackUI.cpp +++ b/src/tracks/ui/TrackUI.cpp @@ -28,34 +28,34 @@ HitTestResult Track::HitTest const AudacityProject *pProject) { const ToolsToolBar * pTtb = pProject->GetToolsToolBar(); - // Unless in Multimode keep using the current tool. const bool isMultiTool = pTtb->IsDown(multiTool); - if (!isMultiTool) { - switch (pTtb->GetCurrentTool()) { - case envelopeTool: - // Pass "false" for unsafe -- let the tool decide to cancel itself - return EnvelopeHandle::HitAnywhere(pProject); - case drawTool: - return SampleHandle::HitAnywhere(event.event, pProject); - case zoomTool: - return ZoomHandle::HitAnywhere(event.event, pProject); - case slideTool: - return TimeShiftHandle::HitAnywhere(pProject); - case selectTool: - return SelectHandle::HitTest(event, pProject, this); + const auto currentTool = pTtb->GetCurrentTool(); - default: - // fallthru - ; - } - } + if ( !isMultiTool && currentTool == zoomTool ) + // Zoom tool is a non-selecting tool that takes precedence in all tracks + // over all other tools, no matter what detail you point at. + return ZoomHandle::HitAnywhere(event.event, pProject); - // Replicate some of the logic of TrackPanel::DetermineToolToUse - HitTestResult result; + // In other tools, let subclasses determine detailed hits. + HitTestResult result = + DetailedHitTest( event, pProject, currentTool, isMultiTool ); - if (isMultiTool) + // If there is no detailed hit for the subclass, there are still some + // general cases. + + // Sliding applies in more than one track type. + if ( !result.handle && !isMultiTool && currentTool == slideTool ) + result = TimeShiftHandle::HitAnywhere(pProject); + + // Let the multi-tool right-click handler apply only in default of all + // other detailed hits. + if ( !result.handle && isMultiTool ) result = ZoomHandle::HitTest(event.event, pProject); + // Finally, default of all is adjustment of the selection box. + if ( !result.handle && ( isMultiTool || currentTool == selectTool) ) + result = SelectHandle::HitTest(event, pProject, this); + return result; }