1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-21 23:00:06 +02:00

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.
This commit is contained in:
Paul Licameli 2016-04-27 18:39:47 -04:00
parent b7dc2561b6
commit 5a5775c1c1
4 changed files with 182 additions and 63 deletions

View File

@ -261,17 +261,6 @@ right and top insets
| |+-Border---- ... ----- ... --------------------- ... ... -Border-+|| | | |+-Border---- ... ----- ... --------------------- ... ... -Border-+|| |
| | Shadow---- ... ----- ... --------------------- ... ... --Shadow-+| | | | 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? // 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) template < class A, class B, class DIST > bool within(A a, B b, DIST d)
@ -8811,9 +8800,6 @@ TrackInfo::~TrackInfo()
delete mPan; delete mPan;
} }
static const int kTrackInfoWidth = 100;
static const int kTrackInfoBtnSize = 16; // widely used dimension, usually height
int TrackInfo::GetTrackInfoWidth() const int TrackInfo::GetTrackInfoWidth() const
{ {
return kTrackInfoWidth; return kTrackInfoWidth;

View File

@ -818,6 +818,24 @@ protected:
DECLARE_EVENT_TABLE() 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 #ifdef _MSC_VER
#pragma warning( pop ) #pragma warning( pop )
#endif #endif

View File

@ -94,8 +94,6 @@ using std::max;
#define PLAY_REGION_RECT_HEIGHT 3 #define PLAY_REGION_RECT_HEIGHT 3
#define PLAY_REGION_GLOBAL_OFFSET_Y 7 #define PLAY_REGION_GLOBAL_OFFSET_Y 7
#define kTopInset 4
wxColour Ruler::mTickColour{ 153, 153, 153 }; wxColour Ruler::mTickColour{ 153, 153, 153 };
// //
@ -1758,7 +1756,9 @@ enum {
OnSyncQuickPlaySelID, OnSyncQuickPlaySelID,
OnTimelineToolTipID, OnTimelineToolTipID,
OnAutoScrollID, OnAutoScrollID,
OnLockPlayRegionID OnLockPlayRegionID,
OnShowHideScrubbingID,
}; };
BEGIN_EVENT_TABLE(AdornedRulerPanel, wxPanel) BEGIN_EVENT_TABLE(AdornedRulerPanel, wxPanel)
@ -1766,11 +1766,17 @@ BEGIN_EVENT_TABLE(AdornedRulerPanel, wxPanel)
EVT_SIZE(AdornedRulerPanel::OnSize) EVT_SIZE(AdornedRulerPanel::OnSize)
EVT_MOUSE_EVENTS(AdornedRulerPanel::OnMouseEvents) EVT_MOUSE_EVENTS(AdornedRulerPanel::OnMouseEvents)
EVT_MOUSE_CAPTURE_LOST(AdornedRulerPanel::OnCaptureLost) EVT_MOUSE_CAPTURE_LOST(AdornedRulerPanel::OnCaptureLost)
// Context menu commands
EVT_MENU(OnToggleQuickPlayID, AdornedRulerPanel::OnToggleQuickPlay) EVT_MENU(OnToggleQuickPlayID, AdornedRulerPanel::OnToggleQuickPlay)
EVT_MENU(OnSyncQuickPlaySelID, AdornedRulerPanel::OnSyncSelToQuickPlay) EVT_MENU(OnSyncQuickPlaySelID, AdornedRulerPanel::OnSyncSelToQuickPlay)
EVT_MENU(OnTimelineToolTipID, AdornedRulerPanel::OnTimelineToolTips) EVT_MENU(OnTimelineToolTipID, AdornedRulerPanel::OnTimelineToolTips)
EVT_MENU(OnAutoScrollID, AdornedRulerPanel::OnAutoScroll) EVT_MENU(OnAutoScrollID, AdornedRulerPanel::OnAutoScroll)
EVT_MENU(OnLockPlayRegionID, AdornedRulerPanel::OnLockPlayRegion) EVT_MENU(OnLockPlayRegionID, AdornedRulerPanel::OnLockPlayRegion)
// Main menu commands
EVT_MENU(OnShowHideScrubbingID, AdornedRulerPanel::OnShowHideScrubbing)
END_EVENT_TABLE() END_EVENT_TABLE()
AdornedRulerPanel::AdornedRulerPanel(AudacityProject* parent, AdornedRulerPanel::AdornedRulerPanel(AudacityProject* parent,
@ -1808,17 +1814,7 @@ AdornedRulerPanel::AdornedRulerPanel(AudacityProject* parent,
mOuter = GetClientRect(); 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.SetUseZoomInfo(mLeftOffset, mViewInfo);
mRuler.SetBounds(mInner.GetLeft(),
mInner.GetTop(),
mInner.GetRight(),
mInner.GetBottom());
mRuler.SetLabelEdges( false ); mRuler.SetLabelEdges( false );
mRuler.SetFormat( Ruler::TimeFormat ); 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() void AdornedRulerPanel::UpdatePrefs()
{ {
#ifdef EXPERIMENTAL_SCROLLING_LIMITS #ifdef EXPERIMENTAL_SCROLLING_LIMITS
@ -1878,6 +1890,11 @@ void AdornedRulerPanel::UpdatePrefs()
} }
#endif #endif
#endif #endif
mShowScrubbing = ReadScrubEnabledPref();
// Affected by the last
UpdateRects();
RegenerateTooltips(); RegenerateTooltips();
} }
@ -1935,18 +1952,23 @@ enum : int {
IndicatorSmallWidth = 9, IndicatorSmallWidth = 9,
IndicatorMediumWidth = 13, IndicatorMediumWidth = 13,
IndicatorOffset = 1, IndicatorOffset = 1,
TopMargin = 1,
BottomMargin = 2, // for bottom bevel and bottom line
LeftMargin = 1,
RightMargin = 1,
}; };
inline int IndicatorHeightForWidth(int width) inline int IndicatorHeightForWidth(int width)
{ {
return ((width / 2) * 3) / 2 + 1; return ((width / 2) * 3) / 2;
} }
inline int IndicatorWidthForHeight(int height) inline int IndicatorWidthForHeight(int height)
{ {
// Not an exact inverse of the above, with rounding, but good enough // Not an exact inverse of the above, with rounding, but good enough
return std::max(static_cast<int>(IndicatorSmallWidth), return std::max(static_cast<int>(IndicatorSmallWidth),
(((height + 1) * 2) / 3) * 2 (((height) * 2) / 3) * 2
); );
} }
@ -1955,9 +1977,15 @@ int AdornedRulerPanel::IndicatorBigWidth()
return IndicatorWidthForHeight(IndicatorBigHeight()); return IndicatorWidthForHeight(IndicatorBigHeight());
} }
enum {
ScrubHeight = 14,
RulerHeight = 28
};
int AdornedRulerPanel::IndicatorBigHeight() int AdornedRulerPanel::IndicatorBigHeight()
{ {
return this->GetSize().GetHeight() / 2; return std::max(int(ScrubHeight - TopMargin),
int(IndicatorMediumWidth));
} }
void AdornedRulerPanel::OnPaint(wxPaintEvent & WXUNUSED(evt)) void AdornedRulerPanel::OnPaint(wxPaintEvent & WXUNUSED(evt))
@ -2014,18 +2042,41 @@ void AdornedRulerPanel::OnSize(wxSizeEvent & WXUNUSED(evt))
return; return;
} }
UpdateRects();
Refresh();
}
void AdornedRulerPanel::UpdateRects()
{
mInner = mOuter; mInner = mOuter;
mInner.x += 1; // +1 for left bevel mInner.x += LeftMargin;
mInner.y += 1; // +1 for top bevel mInner.width -= (LeftMargin + RightMargin);
mInner.width -= 2; // -2 for left and right bevels
mInner.height -= 3; // -3 for top and bottom bevels and bottom line 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(), mRuler.SetBounds(mInner.GetLeft(),
mInner.GetTop(), mInner.GetTop(),
mInner.GetRight(), mInner.GetRight(),
mInner.GetBottom()); mInner.GetBottom());
Refresh();
} }
double AdornedRulerPanel::Pos2Time(int p, bool ignoreFisheye) double AdornedRulerPanel::Pos2Time(int p, bool ignoreFisheye)
@ -2069,11 +2120,28 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt)
const bool changeInScrubZone = (inScrubZone != mPrevInScrubZone); const bool changeInScrubZone = (inScrubZone != mPrevInScrubZone);
mPrevInScrubZone = inScrubZone; 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(); auto &scrubber = mProject->GetScrubber();
// Handle status bar messages // Handle status bar messages
UpdateStatusBar ( UpdateStatusBar (
evt.Leaving() overMenu || evt.Leaving()
? StatusChoice::Leaving ? StatusChoice::Leaving
: evt.Entering() || changeInScrubZone : evt.Entering() || changeInScrubZone
? inScrubZone ? inScrubZone
@ -2082,18 +2150,10 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt)
: StatusChoice::NoChange : StatusChoice::NoChange
); );
if (overMenu && evt.Button(wxMOUSE_BTN_ANY)) {
double t0 = mTracks->GetStartTime(); if(evt.ButtonDown())
double t1 = mTracks->GetEndTime(); DoMainMenu();
double sel0 = mProject->GetSel0(); return;
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 (scrubber.HasStartedScrubbing()) { if (scrubber.HasStartedScrubbing()) {
@ -2464,6 +2524,32 @@ void AdornedRulerPanel::UpdateStatusBar(StatusChoice choice)
mProject->TP_DisplayStatusMessage(message); 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)) void AdornedRulerPanel::OnCaptureLost(wxMouseCaptureLostEvent & WXUNUSED(evt))
{ {
DrawQuickPlayIndicator(NULL); DrawQuickPlayIndicator(NULL);
@ -2617,7 +2703,7 @@ void AdornedRulerPanel::DoDrawPlayRegion(wxDC * dc)
{ {
int x1 = Time2Pos(start) + 1; int x1 = Time2Pos(start) + 1;
int x2 = Time2Pos(end); int x2 = Time2Pos(end);
int y = mInner.height/2; int y = mInner.y - TopMargin + mInner.height/2;
bool isLocked = mProject->IsPlayRegionLocked(); bool isLocked = mProject->IsPlayRegionLocked();
AColor::PlayRegionColor(dc, isLocked); AColor::PlayRegionColor(dc, isLocked);
@ -2670,9 +2756,16 @@ void AdornedRulerPanel::DoDrawBorder(wxDC * dc)
AColor::MediumTrackInfo( dc, false ); AColor::MediumTrackInfo( dc, false );
dc->DrawRectangle( mInner ); 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; wxRect r = mOuter;
r.width -= 1; // -1 for bevel r.width -= RightMargin;
r.height -= 2; // -2 for bevel and for bottom line r.height -= BottomMargin;
AColor::BevelTrackInfo( *dc, true, r ); AColor::BevelTrackInfo( *dc, true, r );
dc->SetPen( *wxBLACK_PEN ); dc->SetPen( *wxBLACK_PEN );
@ -2710,12 +2803,22 @@ void AdornedRulerPanel::DoDrawSelection(wxDC * dc)
wxRect r; wxRect r;
r.x = p0; r.x = p0;
r.y = 1; r.y = mInner.y;
r.width = p1 - p0 - 1; r.width = p1 - p0 - 1;
r.height = mInner.height; r.height = mInner.height;
dc->DrawRectangle( r ); dc->DrawRectangle( r );
} }
int AdornedRulerPanel::GetRulerHeight()
{
return GetRulerHeight(ReadScrubEnabledPref());
}
int AdornedRulerPanel::GetRulerHeight(bool showScrubBar)
{
return RulerHeight + (showScrubBar ? ScrubHeight : 0);
}
void AdornedRulerPanel::SetLeftOffset(int offset) void AdornedRulerPanel::SetLeftOffset(int offset)
{ {
mLeftOffset = offset; mLeftOffset = offset;
@ -2727,7 +2830,7 @@ void AdornedRulerPanel::DoDrawCursor(wxDC * dc)
const int x = Time2Pos(mViewInfo->selectedRegion.t0()); const int x = Time2Pos(mViewInfo->selectedRegion.t0());
// Draw cursor in ruler // 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 // Double headed, left-right
tri[ 0 ].x = x - IndicatorOffset; tri[ 0 ].x = x - IndicatorOffset;
tri[ 0 ].y = 1; tri[ 0 ].y = mScrubZone.y;
tri[ 1 ].x = x - IndicatorOffset; tri[ 1 ].x = x - IndicatorOffset;
tri[ 1 ].y = height; tri[ 1 ].y = mScrubZone.y + height;
tri[ 2 ].x = x - IndicatorHalfWidth; tri[ 2 ].x = x - IndicatorHalfWidth;
tri[ 2 ].y = height / 2; tri[ 2 ].y = mScrubZone.y + height / 2;
dc->DrawPolygon( 3, tri ); dc->DrawPolygon( 3, tri );
tri[ 0 ].x = tri[ 1 ].x = x + IndicatorOffset; tri[ 0 ].x = tri[ 1 ].x = x + IndicatorOffset;
tri[ 2 ].x = x + IndicatorHalfWidth; tri[ 2 ].x = x + IndicatorHalfWidth;
@ -2786,11 +2889,11 @@ void AdornedRulerPanel::DoDrawIndicator
auto height = IndicatorHeightForWidth(width); auto height = IndicatorHeightForWidth(width);
const int IndicatorHalfWidth = width / 2; const int IndicatorHalfWidth = width / 2;
tri[ 0 ].x = x - IndicatorHalfWidth; tri[ 0 ].x = x - IndicatorHalfWidth;
tri[ 0 ].y = 1; tri[ 0 ].y = mScrubZone.y;
tri[ 1 ].x = x + IndicatorHalfWidth; tri[ 1 ].x = x + IndicatorHalfWidth;
tri[ 1 ].y = 1; tri[ 1 ].y = mScrubZone.y;
tri[ 2 ].x = x; tri[ 2 ].x = x;
tri[ 2 ].y = height; tri[ 2 ].y = mScrubZone.y + height;
dc->DrawPolygon( 3, tri ); 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 // Restore the background, but make it a little oversized to make
// it happy OSX. // it happy OSX.
dc->Blit(x - indsize - 1, dc->Blit(x - indsize - 1,
0, mScrubZone.y - 1,
indsize * 2 + 1 + 2, indsize * 2 + 1 + 2,
height + 2, mScrubZone.y + height + 2,
&mBackDC, &mBackDC,
x - indsize - 1, x - indsize - 1,
0); 0);

View File

@ -251,7 +251,7 @@ class AUDACITY_DLL_API RulerPanel final : public wxPanel {
void DoSetSize(int x, int y, void DoSetSize(int x, int y,
int width, int height, int width, int height,
int sizeFlags = wxSIZE_AUTO); int sizeFlags = wxSIZE_AUTO) override;
void OnErase(wxEraseEvent &evt); void OnErase(wxEraseEvent &evt);
void OnPaint(wxPaintEvent &evt); void OnPaint(wxPaintEvent &evt);
@ -291,7 +291,9 @@ public:
bool AcceptsFocus() const override { return false; }; bool AcceptsFocus() const override { return false; };
public: public:
static int GetRulerHeight() { return 28; } static int GetRulerHeight();
static int GetRulerHeight(bool showScrubBar);
void SetLeftOffset(int offset); void SetLeftOffset(int offset);
void DrawIndicator(double time, bool rec); void DrawIndicator(double time, bool rec);
@ -317,6 +319,7 @@ private:
void OnCapture(wxCommandEvent & evt); void OnCapture(wxCommandEvent & evt);
void OnPaint(wxPaintEvent &evt); void OnPaint(wxPaintEvent &evt);
void OnSize(wxSizeEvent &evt); void OnSize(wxSizeEvent &evt);
void UpdateRects();
void OnMouseEvents(wxMouseEvent &evt); void OnMouseEvents(wxMouseEvent &evt);
enum class StatusChoice { enum class StatusChoice {
@ -327,6 +330,8 @@ private:
}; };
void UpdateStatusBar(StatusChoice choice); void UpdateStatusBar(StatusChoice choice);
void DoMainMenu();
void OnCaptureLost(wxMouseCaptureLostEvent &evt); void OnCaptureLost(wxMouseCaptureLostEvent &evt);
void DoDrawBorder(wxDC * dc); void DoDrawBorder(wxDC * dc);
@ -363,6 +368,7 @@ private:
wxMemoryDC mBackDC; wxMemoryDC mBackDC;
wxRect mOuter; wxRect mOuter;
wxRect mScrubZone;
wxRect mInner; wxRect mInner;
int mLeftOffset; // Number of pixels before we hit the 'zero position'. int mLeftOffset; // Number of pixels before we hit the 'zero position'.
@ -398,6 +404,11 @@ private:
void OnAutoScroll(wxCommandEvent &evt); void OnAutoScroll(wxCommandEvent &evt);
void OnLockPlayRegion(wxCommandEvent &evt); void OnLockPlayRegion(wxCommandEvent &evt);
//
// Main menu
//
void OnShowHideScrubbing(wxCommandEvent &evt);
bool mPlayRegionDragsSelection; bool mPlayRegionDragsSelection;
bool mTimelineToolTip; bool mTimelineToolTip;
bool mQuickPlayEnabled; bool mQuickPlayEnabled;
@ -419,6 +430,7 @@ private:
std::unique_ptr<QuickPlayIndicatorOverlay> mOverlay; std::unique_ptr<QuickPlayIndicatorOverlay> mOverlay;
bool mPrevInScrubZone{}; bool mPrevInScrubZone{};
bool mShowScrubbing { true };
DECLARE_EVENT_TABLE() DECLARE_EVENT_TABLE()
}; };