From 5a5775c1c1c5079a15bf09f30d8afc51b9b900f9 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Wed, 27 Apr 2016 18:39:47 -0400 Subject: [PATCH] Implement showing and hiding of a srub bar portion of the ruler... ... activated by clicking near the left end of the ruler, then using the context menu. This is not finished work, but a proof of concept for a possible new scrubbing UI. --- src/TrackPanel.cpp | 14 --- src/TrackPanel.h | 18 ++++ src/widgets/Ruler.cpp | 197 ++++++++++++++++++++++++++++++++---------- src/widgets/Ruler.h | 16 +++- 4 files changed, 182 insertions(+), 63 deletions(-) diff --git a/src/TrackPanel.cpp b/src/TrackPanel.cpp index ef8b5a38a..390d474ce 100644 --- a/src/TrackPanel.cpp +++ b/src/TrackPanel.cpp @@ -261,17 +261,6 @@ right and top insets | |+-Border---- ... ----- ... --------------------- ... ... -Border-+|| | | | Shadow---- ... ----- ... --------------------- ... ... --Shadow-+| | */ -enum { - kLeftInset = 4, - kRightInset = kLeftInset, - kTopInset = 4, - kShadowThickness = 1, - kBorderThickness = 1, - kTopMargin = kTopInset + kBorderThickness, - kBottomMargin = kShadowThickness + kBorderThickness, - kLeftMargin = kLeftInset + kBorderThickness, - kRightMargin = kRightInset + kShadowThickness + kBorderThickness, -}; // Is the distance between A and B less than D? template < class A, class B, class DIST > bool within(A a, B b, DIST d) @@ -8811,9 +8800,6 @@ TrackInfo::~TrackInfo() delete mPan; } -static const int kTrackInfoWidth = 100; -static const int kTrackInfoBtnSize = 16; // widely used dimension, usually height - int TrackInfo::GetTrackInfoWidth() const { return kTrackInfoWidth; diff --git a/src/TrackPanel.h b/src/TrackPanel.h index 5d7a4c2af..2e69f0bc3 100644 --- a/src/TrackPanel.h +++ b/src/TrackPanel.h @@ -818,6 +818,24 @@ protected: DECLARE_EVENT_TABLE() }; +// See big pictorial comment in TrackPanel for explanation of these numbers +enum : int { + kLeftInset = 4, + kRightInset = kLeftInset, + kTopInset = 4, + kShadowThickness = 1, + kBorderThickness = 1, + kTopMargin = kTopInset + kBorderThickness, + kBottomMargin = kShadowThickness + kBorderThickness, + kLeftMargin = kLeftInset + kBorderThickness, + kRightMargin = kRightInset + kShadowThickness + kBorderThickness, +}; + +enum : int { + kTrackInfoWidth = 100, + kTrackInfoBtnSize = 16 // widely used dimension, usually height +}; + #ifdef _MSC_VER #pragma warning( pop ) #endif diff --git a/src/widgets/Ruler.cpp b/src/widgets/Ruler.cpp index d481326a3..1b4fb4425 100644 --- a/src/widgets/Ruler.cpp +++ b/src/widgets/Ruler.cpp @@ -94,8 +94,6 @@ using std::max; #define PLAY_REGION_RECT_HEIGHT 3 #define PLAY_REGION_GLOBAL_OFFSET_Y 7 -#define kTopInset 4 - wxColour Ruler::mTickColour{ 153, 153, 153 }; // @@ -1758,7 +1756,9 @@ enum { OnSyncQuickPlaySelID, OnTimelineToolTipID, OnAutoScrollID, - OnLockPlayRegionID + OnLockPlayRegionID, + + OnShowHideScrubbingID, }; BEGIN_EVENT_TABLE(AdornedRulerPanel, wxPanel) @@ -1766,11 +1766,17 @@ BEGIN_EVENT_TABLE(AdornedRulerPanel, wxPanel) EVT_SIZE(AdornedRulerPanel::OnSize) EVT_MOUSE_EVENTS(AdornedRulerPanel::OnMouseEvents) EVT_MOUSE_CAPTURE_LOST(AdornedRulerPanel::OnCaptureLost) + + // Context menu commands EVT_MENU(OnToggleQuickPlayID, AdornedRulerPanel::OnToggleQuickPlay) EVT_MENU(OnSyncQuickPlaySelID, AdornedRulerPanel::OnSyncSelToQuickPlay) EVT_MENU(OnTimelineToolTipID, AdornedRulerPanel::OnTimelineToolTips) EVT_MENU(OnAutoScrollID, AdornedRulerPanel::OnAutoScroll) EVT_MENU(OnLockPlayRegionID, AdornedRulerPanel::OnLockPlayRegion) + + // Main menu commands + EVT_MENU(OnShowHideScrubbingID, AdornedRulerPanel::OnShowHideScrubbing) + END_EVENT_TABLE() AdornedRulerPanel::AdornedRulerPanel(AudacityProject* parent, @@ -1808,17 +1814,7 @@ AdornedRulerPanel::AdornedRulerPanel(AudacityProject* parent, mOuter = GetClientRect(); - mInner = mOuter; - mInner.x += 1; // +1 for left bevel - mInner.y += 1; // +1 for top bevel - mInner.width -= 2; // -2 for left and right bevels - mInner.height -= 3; // -3 for top and bottom bevels and bottom line - mRuler.SetUseZoomInfo(mLeftOffset, mViewInfo); - mRuler.SetBounds(mInner.GetLeft(), - mInner.GetTop(), - mInner.GetRight(), - mInner.GetBottom()); mRuler.SetLabelEdges( false ); mRuler.SetFormat( Ruler::TimeFormat ); @@ -1867,6 +1863,22 @@ AdornedRulerPanel::~AdornedRulerPanel() } } +namespace { + static const wxChar *scrubEnabledPrefName = wxT("/QuickPlay/ScrubbingEnabled"); + + bool ReadScrubEnabledPref() + { + bool result {}; + gPrefs->Read(scrubEnabledPrefName, &result, true); + return result; + } + + void WriteScrubEnabledPref(bool value) + { + gPrefs->Write(scrubEnabledPrefName, value); + } +} + void AdornedRulerPanel::UpdatePrefs() { #ifdef EXPERIMENTAL_SCROLLING_LIMITS @@ -1878,6 +1890,11 @@ void AdornedRulerPanel::UpdatePrefs() } #endif #endif + + mShowScrubbing = ReadScrubEnabledPref(); + // Affected by the last + UpdateRects(); + RegenerateTooltips(); } @@ -1935,18 +1952,23 @@ enum : int { IndicatorSmallWidth = 9, IndicatorMediumWidth = 13, IndicatorOffset = 1, + + TopMargin = 1, + BottomMargin = 2, // for bottom bevel and bottom line + LeftMargin = 1, + RightMargin = 1, }; inline int IndicatorHeightForWidth(int width) { - return ((width / 2) * 3) / 2 + 1; + return ((width / 2) * 3) / 2; } inline int IndicatorWidthForHeight(int height) { // Not an exact inverse of the above, with rounding, but good enough return std::max(static_cast(IndicatorSmallWidth), - (((height + 1) * 2) / 3) * 2 + (((height) * 2) / 3) * 2 ); } @@ -1955,9 +1977,15 @@ int AdornedRulerPanel::IndicatorBigWidth() return IndicatorWidthForHeight(IndicatorBigHeight()); } +enum { + ScrubHeight = 14, + RulerHeight = 28 +}; + int AdornedRulerPanel::IndicatorBigHeight() { - return this->GetSize().GetHeight() / 2; + return std::max(int(ScrubHeight - TopMargin), + int(IndicatorMediumWidth)); } void AdornedRulerPanel::OnPaint(wxPaintEvent & WXUNUSED(evt)) @@ -2014,18 +2042,41 @@ void AdornedRulerPanel::OnSize(wxSizeEvent & WXUNUSED(evt)) return; } + UpdateRects(); + + Refresh(); +} + +void AdornedRulerPanel::UpdateRects() +{ mInner = mOuter; - mInner.x += 1; // +1 for left bevel - mInner.y += 1; // +1 for top bevel - mInner.width -= 2; // -2 for left and right bevels - mInner.height -= 3; // -3 for top and bottom bevels and bottom line + mInner.x += LeftMargin; + mInner.width -= (LeftMargin + RightMargin); + + wxRect *top = &mInner; + + if (mShowScrubbing) { + mScrubZone = mInner; + auto scrubHeight = std::min(mScrubZone.height, int(ScrubHeight)); + mScrubZone.height = scrubHeight; + mInner.height -= scrubHeight; + mInner.y += scrubHeight; + top = &mScrubZone; + } + + top->y += TopMargin; + top->height -= TopMargin; + + mInner.height -= BottomMargin; + + if (!mShowScrubbing) + mScrubZone = mInner; mRuler.SetBounds(mInner.GetLeft(), mInner.GetTop(), mInner.GetRight(), mInner.GetBottom()); - Refresh(); } double AdornedRulerPanel::Pos2Time(int p, bool ignoreFisheye) @@ -2069,11 +2120,28 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt) const bool changeInScrubZone = (inScrubZone != mPrevInScrubZone); mPrevInScrubZone = inScrubZone; + double t0 = mTracks->GetStartTime(); + double t1 = mTracks->GetEndTime(); + double sel0 = mProject->GetSel0(); + double sel1 = mProject->GetSel1(); + + wxCoord xx = evt.GetX(); + wxCoord mousePosX = xx; + UpdateQuickPlayPos(mousePosX); + + // If not looping, restrict selection to end of project + if (!inScrubZone && !evt.ShiftDown()) { + mQuickPlayPos = std::min(t1, mQuickPlayPos); + } + + // If position was adjusted right, we are over menu + const bool overMenu = (xx < mousePosX); + auto &scrubber = mProject->GetScrubber(); // Handle status bar messages UpdateStatusBar ( - evt.Leaving() + overMenu || evt.Leaving() ? StatusChoice::Leaving : evt.Entering() || changeInScrubZone ? inScrubZone @@ -2082,18 +2150,10 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt) : StatusChoice::NoChange ); - - double t0 = mTracks->GetStartTime(); - double t1 = mTracks->GetEndTime(); - double sel0 = mProject->GetSel0(); - double sel1 = mProject->GetSel1(); - - wxCoord mousePosX = evt.GetX(); - UpdateQuickPlayPos(mousePosX); - - // If not looping, restrict selection to end of project - if (!inScrubZone && !evt.ShiftDown()) { - mQuickPlayPos = std::min(t1, mQuickPlayPos); + if (overMenu && evt.Button(wxMOUSE_BTN_ANY)) { + if(evt.ButtonDown()) + DoMainMenu(); + return; } if (scrubber.HasStartedScrubbing()) { @@ -2464,6 +2524,32 @@ void AdornedRulerPanel::UpdateStatusBar(StatusChoice choice) mProject->TP_DisplayStatusMessage(message); } +void AdornedRulerPanel::DoMainMenu() +{ + wxMenu menu; + + menu.AppendCheckItem(OnShowHideScrubbingID, _("Scrub Bar")); + menu.Check(OnShowHideScrubbingID, mShowScrubbing); + + // Position the popup similarly to the track control panel menus + wxPoint pos { + kLeftInset + kTrackInfoBtnSize + 1, + GetSize().GetHeight() + 1 + }; + PopupMenu(&menu, pos); +} + +void AdornedRulerPanel::OnShowHideScrubbing(wxCommandEvent&) +{ + mShowScrubbing = !mShowScrubbing; + WriteScrubEnabledPref(mShowScrubbing); + gPrefs->Flush(); + wxSize size { GetSize().GetWidth(), GetRulerHeight(mShowScrubbing) }; + SetSize(size); + SetMinSize(size); + PostSizeEventToParent(); +} + void AdornedRulerPanel::OnCaptureLost(wxMouseCaptureLostEvent & WXUNUSED(evt)) { DrawQuickPlayIndicator(NULL); @@ -2617,7 +2703,7 @@ void AdornedRulerPanel::DoDrawPlayRegion(wxDC * dc) { int x1 = Time2Pos(start) + 1; int x2 = Time2Pos(end); - int y = mInner.height/2; + int y = mInner.y - TopMargin + mInner.height/2; bool isLocked = mProject->IsPlayRegionLocked(); AColor::PlayRegionColor(dc, isLocked); @@ -2670,9 +2756,16 @@ void AdornedRulerPanel::DoDrawBorder(wxDC * dc) AColor::MediumTrackInfo( dc, false ); dc->DrawRectangle( mInner ); + if (mShowScrubbing) { + // Let's distinguish the scrubbing area by using the same gray as for + // selected track control panel. + AColor::Medium(&mBackDC, true); + mBackDC.DrawRectangle(mScrubZone); + } + wxRect r = mOuter; - r.width -= 1; // -1 for bevel - r.height -= 2; // -2 for bevel and for bottom line + r.width -= RightMargin; + r.height -= BottomMargin; AColor::BevelTrackInfo( *dc, true, r ); dc->SetPen( *wxBLACK_PEN ); @@ -2710,12 +2803,22 @@ void AdornedRulerPanel::DoDrawSelection(wxDC * dc) wxRect r; r.x = p0; - r.y = 1; + r.y = mInner.y; r.width = p1 - p0 - 1; r.height = mInner.height; dc->DrawRectangle( r ); } +int AdornedRulerPanel::GetRulerHeight() +{ + return GetRulerHeight(ReadScrubEnabledPref()); +} + +int AdornedRulerPanel::GetRulerHeight(bool showScrubBar) +{ + return RulerHeight + (showScrubBar ? ScrubHeight : 0); +} + void AdornedRulerPanel::SetLeftOffset(int offset) { mLeftOffset = offset; @@ -2727,7 +2830,7 @@ void AdornedRulerPanel::DoDrawCursor(wxDC * dc) const int x = Time2Pos(mViewInfo->selectedRegion.t0()); // Draw cursor in ruler - dc->DrawLine( x, 1, x, mInner.height ); + dc->DrawLine( x, mInner.y, x, mInner.y + mInner.height ); } // @@ -2771,11 +2874,11 @@ void AdornedRulerPanel::DoDrawIndicator // Double headed, left-right tri[ 0 ].x = x - IndicatorOffset; - tri[ 0 ].y = 1; + tri[ 0 ].y = mScrubZone.y; tri[ 1 ].x = x - IndicatorOffset; - tri[ 1 ].y = height; + tri[ 1 ].y = mScrubZone.y + height; tri[ 2 ].x = x - IndicatorHalfWidth; - tri[ 2 ].y = height / 2; + tri[ 2 ].y = mScrubZone.y + height / 2; dc->DrawPolygon( 3, tri ); tri[ 0 ].x = tri[ 1 ].x = x + IndicatorOffset; tri[ 2 ].x = x + IndicatorHalfWidth; @@ -2786,11 +2889,11 @@ void AdornedRulerPanel::DoDrawIndicator auto height = IndicatorHeightForWidth(width); const int IndicatorHalfWidth = width / 2; tri[ 0 ].x = x - IndicatorHalfWidth; - tri[ 0 ].y = 1; + tri[ 0 ].y = mScrubZone.y; tri[ 1 ].x = x + IndicatorHalfWidth; - tri[ 1 ].y = 1; + tri[ 1 ].y = mScrubZone.y; tri[ 2 ].x = x; - tri[ 2 ].y = height; + tri[ 2 ].y = mScrubZone.y + height; dc->DrawPolygon( 3, tri ); } } @@ -2808,9 +2911,9 @@ void AdornedRulerPanel::DoEraseIndicator(wxDC *dc, int x) // Restore the background, but make it a little oversized to make // it happy OSX. dc->Blit(x - indsize - 1, - 0, + mScrubZone.y - 1, indsize * 2 + 1 + 2, - height + 2, + mScrubZone.y + height + 2, &mBackDC, x - indsize - 1, 0); diff --git a/src/widgets/Ruler.h b/src/widgets/Ruler.h index 0b2b5dfc3..085f9ff3d 100644 --- a/src/widgets/Ruler.h +++ b/src/widgets/Ruler.h @@ -251,7 +251,7 @@ class AUDACITY_DLL_API RulerPanel final : public wxPanel { void DoSetSize(int x, int y, int width, int height, - int sizeFlags = wxSIZE_AUTO); + int sizeFlags = wxSIZE_AUTO) override; void OnErase(wxEraseEvent &evt); void OnPaint(wxPaintEvent &evt); @@ -291,7 +291,9 @@ public: bool AcceptsFocus() const override { return false; }; public: - static int GetRulerHeight() { return 28; } + static int GetRulerHeight(); + static int GetRulerHeight(bool showScrubBar); + void SetLeftOffset(int offset); void DrawIndicator(double time, bool rec); @@ -317,6 +319,7 @@ private: void OnCapture(wxCommandEvent & evt); void OnPaint(wxPaintEvent &evt); void OnSize(wxSizeEvent &evt); + void UpdateRects(); void OnMouseEvents(wxMouseEvent &evt); enum class StatusChoice { @@ -327,6 +330,8 @@ private: }; void UpdateStatusBar(StatusChoice choice); + void DoMainMenu(); + void OnCaptureLost(wxMouseCaptureLostEvent &evt); void DoDrawBorder(wxDC * dc); @@ -363,6 +368,7 @@ private: wxMemoryDC mBackDC; wxRect mOuter; + wxRect mScrubZone; wxRect mInner; int mLeftOffset; // Number of pixels before we hit the 'zero position'. @@ -398,6 +404,11 @@ private: void OnAutoScroll(wxCommandEvent &evt); void OnLockPlayRegion(wxCommandEvent &evt); + // + // Main menu + // + void OnShowHideScrubbing(wxCommandEvent &evt); + bool mPlayRegionDragsSelection; bool mTimelineToolTip; bool mQuickPlayEnabled; @@ -419,6 +430,7 @@ private: std::unique_ptr mOverlay; bool mPrevInScrubZone{}; + bool mShowScrubbing { true }; DECLARE_EVENT_TABLE() };