1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-05-02 16:49:41 +02:00

Implement scrub and seek buttons on ruler; redo scrub menu items

This commit is contained in:
Paul Licameli 2016-06-02 15:19:43 -04:00
parent ed7e52da44
commit 6a3613e8e9
6 changed files with 153 additions and 118 deletions

View File

@ -2384,7 +2384,6 @@ void AudacityProject::OnTogglePinnedHead()
auto ruler = GetRulerPanel();
if (ruler)
// Update button image
ruler->UpdateButtonStates();
auto &scrubber = GetScrubber();

View File

@ -890,7 +890,7 @@ AudacityProject::AudacityProject(wxWindow * parent, wxWindowID id,
mRuler = safenew AdornedRulerPanel( this,
wxID_ANY,
wxDefaultPosition,
wxSize( -1, AdornedRulerPanel::GetRulerHeight() ),
wxSize( -1, AdornedRulerPanel::GetRulerHeight(false) ),
&mViewInfo );
//

View File

@ -33,9 +33,6 @@ Paul Licameli split from TrackPanel.cpp
#include <wx/dc.h>
// Conditional compilation switch for making scrub menu items checkable
#define CHECKABLE_SCRUB_MENU_ITEMS
enum {
// PRL:
// Mouse must move at least this far to distinguish ctrl-drag to scrub
@ -221,24 +218,27 @@ namespace {
wxString status;
void (Scrubber::*memFn)(wxCommandEvent&);
bool seek;
bool (Scrubber::*StatusTest)() const;
const wxString &GetStatus() const { return status; }
} menuItems[] = {
/* i18n-hint: These commands assist the user in finding a sound by ear. ...
"Scrubbing" is variable-speed playback, ...
"Seeking" is normal speed playback but with skips, ...
"Scrolling" keeps the playback position at a fixed place on screen while the waveform moves
*/
{ wxT("Scrub"), XO("&Scrub"), XO("Scrubbing"),
&Scrubber::OnScrub, false },
&Scrubber::OnScrub, false, &Scrubber::Scrubs },
{ wxT("Seek"), XO("See&k"), XO("Seeking"),
&Scrubber::OnSeek, true },
&Scrubber::OnSeek, true, &Scrubber::Seeks },
{ wxT("StartScrubSeek"), XO("Star&t"), XO(""),
&Scrubber::OnStart, true, nullptr },
};
enum { nMenuItems = sizeof(menuItems) / sizeof(*menuItems) };
enum { nMenuItems = sizeof(menuItems) / sizeof(*menuItems), StartMenuItem = 2 };
// This never finds the last item:
inline const MenuItem &FindMenuItem(bool seek)
{
return *std::find_if(menuItems, menuItems + nMenuItems,
@ -252,16 +252,13 @@ namespace {
void Scrubber::MarkScrubStart(
// Assume xx is relative to the left edge of TrackPanel!
wxCoord xx, bool smoothScrolling, bool alwaysSeeking
wxCoord xx, bool smoothScrolling
)
{
UncheckAllMenuItems();
// Don't actually start scrubbing, but collect some information
// needed for the decision to start scrubbing later when handling
// drag events.
mSmoothScrollingScrub = smoothScrolling;
mAlwaysSeeking = alwaysSeeking;
ControlToolBar * const ctb = mProject->GetControlToolBar();
@ -278,8 +275,6 @@ void Scrubber::MarkScrubStart(
mScrubStartPosition = xx;
mOptions.startClockTimeMillis = ::wxGetLocalTimeMillis();
CheckMenuItem();
}
#ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
@ -468,12 +463,12 @@ void Scrubber::ContinueScrubbingUI()
{
// Show the correct status for seeking.
bool backup = mAlwaysSeeking;
mAlwaysSeeking = seek;
bool backup = mSeeking;
mSeeking = seek;
const auto ctb = mProject->GetControlToolBar();
if (ctb)
ctb->UpdateStatusBar(mProject);
mAlwaysSeeking = backup;
mSeeking = backup;
}
if (seek)
@ -498,8 +493,6 @@ void Scrubber::StopScrubbing()
mPoller->Stop();
UncheckAllMenuItems();
mScrubStartPosition = -1;
mDragging = false;
@ -770,13 +763,12 @@ Scrubber &ScrubbingOverlay::GetScrubber()
bool Scrubber::PollIsSeeking()
{
return mDragging || (mAlwaysSeeking || ::wxGetMouseState().LeftIsDown());
return mDragging || (mSeeking || ::wxGetMouseState().LeftIsDown());
}
void Scrubber::DoScrub(bool seek)
void Scrubber::DoScrub()
{
const bool wasScrubbing = IsScrubbing();
const bool match = (seek == mAlwaysSeeking);
const bool scroll = PlaybackPrefs::GetPinnedHeadPreference();
if (!wasScrubbing) {
auto tp = mProject->GetTrackPanel();
@ -788,33 +780,43 @@ void Scrubber::DoScrub(bool seek)
const auto offset = tp->GetLeftOffset();
xx = (std::max(offset, std::min(offset + width - 1, xx)));
MarkScrubStart(xx, scroll, seek);
MarkScrubStart(xx, scroll);
}
else if(!match) {
mSmoothScrollingScrub = scroll;
mAlwaysSeeking = seek;
UncheckAllMenuItems();
CheckMenuItem();
}
void Scrubber::OnScrubOrSeek(bool &toToggle, bool &other)
{
toToggle = !toToggle;
if (toToggle)
other = false;
if (HasStartedScrubbing()) {
// Show the correct status.
const auto ctb = mProject->GetControlToolBar();
ctb->UpdateStatusBar(mProject);
}
else {
// This will call back to Scrubber::StopScrubbing
const auto ctb = mProject->GetControlToolBar();
ctb->StopPlaying();
}
auto ruler = mProject->GetRulerPanel();
if (ruler)
// Update button images
ruler->UpdateButtonStates();
CheckMenuItem();
}
void Scrubber::OnScrub(wxCommandEvent&)
{
DoScrub(false);
OnScrubOrSeek(mScrubbing, mSeeking);
}
void Scrubber::OnSeek(wxCommandEvent&)
{
DoScrub(true);
OnScrubOrSeek(mSeeking, mScrubbing);
}
void Scrubber::OnStart(wxCommandEvent&)
{
DoScrub();
}
enum { CMD_ID = 8000 };
@ -828,14 +830,14 @@ BEGIN_EVENT_TABLE(Scrubber::Forwarder, wxEvtHandler)
EVT_MOUSE_EVENTS(Scrubber::Forwarder::OnMouse)
END_EVENT_TABLE()
static_assert(nMenuItems == 2, "wrong number of items");
static_assert(nMenuItems == 3, "wrong number of items");
const wxString &Scrubber::GetUntranslatedStateString() const
{
static wxString empty;
if (HasStartedScrubbing()) {
auto &item = FindMenuItem(mAlwaysSeeking);
auto &item = FindMenuItem(mSeeking);
return item.status;
}
else
@ -846,15 +848,19 @@ std::vector<wxString> Scrubber::GetAllUntranslatedStatusStrings()
{
using namespace std;
vector<wxString> results;
transform(menuItems, menuItems + nMenuItems, back_inserter(results),
mem_fun_ref(&MenuItem::GetStatus));
for (const auto &item : menuItems) {
const auto &status = item.GetStatus();
if (!status.empty())
results.push_back(status);
}
return move(results);
}
bool Scrubber::CanScrub() const
{
// Return the enabled state for the menu item that really launches the scrub or seek.
auto cm = mProject->GetCommandManager();
return cm->GetEnabled(menuItems[0].name);
return cm->GetEnabled(menuItems[StartMenuItem].name);
}
void Scrubber::AddMenuItems()
@ -865,15 +871,14 @@ void Scrubber::AddMenuItems()
cm->BeginSubMenu(_("Scru&bbing"));
for (const auto &item : menuItems) {
#ifdef CHECKABLE_SCRUB_MENU_ITEMS
cm->AddCheck(item.name, wxGetTranslation(item.label),
FNT(Scrubber, this, item.memFn),
false, flags, mask);
#else
cm->AddItem(item.name, wxGetTranslation(item.label),
FNT(Scrubber, this, item.memFn),
flags, mask);
#endif
if (!item.GetStatus().empty())
cm->AddCheck(item.name, wxGetTranslation(item.label),
FNT(Scrubber, this, item.memFn),
false, flags, mask);
else
cm->AddItem(item.name, wxGetTranslation(item.label),
FNT(Scrubber, this, item.memFn),
flags, mask);
}
cm->EndSubMenu();
CheckMenuItem();
@ -885,40 +890,23 @@ void Scrubber::PopulateMenu(wxMenu &menu)
auto cm = mProject->GetCommandManager();
const MenuItem *checkedItem =
HasStartedScrubbing()
? &FindMenuItem(mAlwaysSeeking)
? &FindMenuItem(mSeeking)
: nullptr;
for (const auto &item : menuItems) {
if (cm->GetEnabled(item.name)) {
#ifdef CHECKABLE_SCRUB_MENU_ITEMS
menu.AppendCheckItem(id, item.label);
if(&item == checkedItem)
menu.FindItem(id)->Check();
#else
menu.Append(id, item.label);
#endif
}
++id;
}
}
void Scrubber::UncheckAllMenuItems()
{
#ifdef CHECKABLE_SCRUB_MENU_ITEMS
auto cm = mProject->GetCommandManager();
for (const auto &item : menuItems)
cm->Check(item.name, false);
#endif
}
void Scrubber::CheckMenuItem()
{
#ifdef CHECKABLE_SCRUB_MENU_ITEMS
if(HasStartedScrubbing()) {
auto cm = mProject->GetCommandManager();
auto item = FindMenuItem(mAlwaysSeeking);
cm->Check(item.name, true);
}
#endif
auto cm = mProject->GetCommandManager();
cm->Check(menuItems[0].name, mScrubbing);
cm->Check(menuItems[1].name, mSeeking);
}
#endif

View File

@ -71,11 +71,7 @@ public:
~Scrubber();
// Assume xx is relative to the left edge of TrackPanel!
void MarkScrubStart(
wxCoord xx, bool smoothScrolling,
bool alwaysSeeking // if false, can switch seeking or scrubbing
// by mouse button state
);
void MarkScrubStart(wxCoord xx, bool smoothScrolling);
// Returns true iff the event should be considered consumed by this:
// Assume xx is relative to the left edge of TrackPanel!
@ -101,8 +97,11 @@ public:
void SetScrollScrubbing(bool value)
{ mSmoothScrollingScrub = value; }
bool IsAlwaysSeeking() const
{ return mAlwaysSeeking; }
bool Seeks() const
{ return mSeeking; }
bool Scrubs() const
{ return mScrubbing; }
bool ShouldDrawScrubSpeed();
double FindScrubSpeed(bool seeking, double time) const;
@ -120,10 +119,13 @@ public:
// For popup
void PopulateMenu(wxMenu &menu);
void OnScrubOrSeek(bool &toToggle, bool &other);
void OnScrub(wxCommandEvent&);
void OnSeek(wxCommandEvent&);
void OnStart(wxCommandEvent&);
// A string to put in the leftmost part of the status bar.
// A string to put in the leftmost part of the status bar
// when scrub or seek is in progress, or else empty.
const wxString &GetUntranslatedStateString() const;
// All possible status strings.
@ -133,9 +135,8 @@ public:
bool IsPaused() const;
private:
void DoScrub(bool seek);
void DoScrub();
void OnActivateOrDeactivateApp(wxActivateEvent & event);
void UncheckAllMenuItems();
void CheckMenuItem();
// I need this because I can't push the scrubber as an event handler
@ -158,7 +159,12 @@ private:
wxCoord mLastScrubPosition {};
bool mScrubSeekPress;
bool mSmoothScrollingScrub;
bool mAlwaysSeeking {};
// These hold the three-way choice among click-to-scrub, click-to-seek, or disabled.
// Not both true.
bool mScrubbing {};
bool mSeeking {};
bool mDragging {};
#ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL

View File

@ -1918,8 +1918,8 @@ enum {
OnLockPlayRegionID,
OnTogglePinnedStateID,
OnShowHideScrubbingID,
OnScrubID,
OnSeekID,
};
BEGIN_EVENT_TABLE(AdornedRulerPanel, OverlayPanel)
@ -1935,15 +1935,18 @@ BEGIN_EVENT_TABLE(AdornedRulerPanel, OverlayPanel)
EVT_MENU(OnAutoScrollID, AdornedRulerPanel::OnAutoScroll)
EVT_MENU(OnLockPlayRegionID, AdornedRulerPanel::OnLockPlayRegion)
// Scrub bar menu commands
EVT_MENU(OnShowHideScrubbingID, AdornedRulerPanel::OnToggleScrubbing)
// Pop up menus on Windows
EVT_CONTEXT_MENU(AdornedRulerPanel::OnContextMenu)
EVT_COMMAND( OnTogglePinnedStateID,
wxEVT_COMMAND_BUTTON_CLICKED,
AdornedRulerPanel::OnTogglePinnedState )
wxEVT_COMMAND_BUTTON_CLICKED,
AdornedRulerPanel::OnTogglePinnedState )
EVT_COMMAND( OnScrubID,
wxEVT_COMMAND_BUTTON_CLICKED,
AdornedRulerPanel::OnScrub )
EVT_COMMAND( OnSeekID,
wxEVT_COMMAND_BUTTON_CLICKED,
AdornedRulerPanel::OnSeek )
END_EVENT_TABLE()
@ -2026,6 +2029,7 @@ AdornedRulerPanel::~AdornedRulerPanel()
this);
}
#if 0
namespace {
static const wxChar *scrubEnabledPrefName = wxT("/QuickPlay/ScrubbingEnabled");
@ -2041,6 +2045,7 @@ namespace {
gPrefs->Write(scrubEnabledPrefName, value);
}
}
#endif
void AdornedRulerPanel::UpdatePrefs()
{
@ -2058,7 +2063,7 @@ void AdornedRulerPanel::UpdatePrefs()
#endif
#endif
mShowScrubbing = ReadScrubEnabledPref();
// mShowScrubbing = ReadScrubEnabledPref();
// Affected by the last
UpdateRects();
@ -2088,9 +2093,10 @@ void AdornedRulerPanel::ReCreateButtons()
// Make the short row of time ruler pushbottons.
// Don't bother with sizers. Their sizes and positions are fixed.
wxPoint position{ FocusBorderLeft, FocusBorderTop };
wxPoint position{ FocusBorderLeft, 0 };
size_t iButton = 0;
const auto size = theTheme.ImageSize( bmpRecoloredUpSmall );
auto size = theTheme.ImageSize( bmpRecoloredUpSmall );
size.y = std::min(size.y, GetRulerHeight(false));
auto buttonMaker = [&]
(wxWindowID id, teBmps bitmap, bool toggle)
@ -2113,6 +2119,8 @@ void AdornedRulerPanel::ReCreateButtons()
bmpRecoloredUpSmall, bmpRecoloredDownSmall, bmpRecoloredHiliteSmall,
bmpUnpinnedPlayRecordHead, bmpUnpinnedPlayRecordHead, bmpUnpinnedPlayRecordHead,
size);
buttonMaker(OnScrubID, bmpScrub, true);
buttonMaker(OnSeekID, bmpSeek, true);
UpdateButtonStates();
}
@ -2421,7 +2429,7 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt)
}
else if (!HasCapture() && inScrubZone) {
if (evt.LeftDown()) {
scrubber.MarkScrubStart(evt.m_x, PlaybackPrefs::GetPinnedHeadPreference(), false);
scrubber.MarkScrubStart(evt.m_x, PlaybackPrefs::GetPinnedHeadPreference());
UpdateStatusBarAndTooltips(StatusChoice::EnteringScrubZone);
}
ShowQuickPlayIndicator();
@ -2730,12 +2738,10 @@ void AdornedRulerPanel::UpdateStatusBarAndTooltips(StatusChoice choice)
case StatusChoice::EnteringScrubZone:
{
if (scrubbing) {
if(!scrubber.IsAlwaysSeeking())
message = _("Click or drag to seek");
}
else
message = _("Click to scrub, Double-Click to scroll, Drag to seek");
if(scrubber.Seeks())
message = _("Click or drag to seek");
else if(scrubber.Scrubs())
message = _("Click or drag to scrub");
}
break;
@ -2749,10 +2755,10 @@ void AdornedRulerPanel::UpdateStatusBarAndTooltips(StatusChoice choice)
RegenerateTooltips(choice);
}
void AdornedRulerPanel::OnToggleScrubbing(wxCommandEvent&)
void AdornedRulerPanel::OnToggleScrubbing(/*wxCommandEvent&*/)
{
mShowScrubbing = !mShowScrubbing;
WriteScrubEnabledPref(mShowScrubbing);
//WriteScrubEnabledPref(mShowScrubbing);
gPrefs->Flush();
wxSize size { GetSize().GetWidth(), GetRulerHeight(mShowScrubbing) };
SetSize(size);
@ -2767,6 +2773,13 @@ void AdornedRulerPanel::OnContextMenu(wxContextMenuEvent & WXUNUSED(event))
void AdornedRulerPanel::UpdateButtonStates()
{
auto common = [this]
(wxWindow *button, const wxString &commandName, const wxString &label){
const auto &fullLabel = ComposeButtonLabel(*mProject, commandName, label);
button->SetLabel(fullLabel);
button->SetToolTip(fullLabel);
};
{
bool state = PlaybackPrefs::GetPinnedHeadPreference();
auto pinButton = static_cast<AButton*>(FindWindow(OnTogglePinnedStateID));
@ -2777,10 +2790,38 @@ void AdornedRulerPanel::UpdateButtonStates()
// (which is, to toggle the state)
? _("Pinned play/record Head")
: _("Unpinned play/record Head");
const auto &fullLabel = ComposeButtonLabel(*mProject, wxT("PinnedHead"), label);
pinButton->SetLabel(fullLabel);
pinButton->SetToolTip(fullLabel);
common(pinButton, wxT("PinnedHead"), label);
}
const auto scrubber = &mProject->GetScrubber();
{
const auto scrubButton = static_cast<AButton*>(FindWindow(OnScrubID));
/* i18n-hint: These commands assist the user in finding a sound by ear. ...
"Scrubbing" is variable-speed playback
*/
common(scrubButton, wxT("Scrub"), _("Scrub"));
if (scrubber && scrubber->Scrubs())
scrubButton->PushDown();
else
scrubButton->PopUp();
}
{
const auto seekButton = static_cast<AButton*>(FindWindow(OnSeekID));
/* i18n-hint: These commands assist the user in finding a sound by ear. ...
"Seeking" is normal speed playback but with skips
*/
common(seekButton, wxT("Seek"), _("Seek"));
if (scrubber && scrubber->Seeks())
seekButton->PushDown();
else
seekButton->PopUp();
}
if(scrubber &&
mShowScrubbing != (scrubber->Scrubs() || scrubber->Seeks()))
OnToggleScrubbing();
}
void AdornedRulerPanel::OnTogglePinnedState(wxCommandEvent & event)
@ -2789,6 +2830,16 @@ void AdornedRulerPanel::OnTogglePinnedState(wxCommandEvent & event)
UpdateButtonStates();
}
void AdornedRulerPanel::OnSeek(wxCommandEvent & event)
{
mProject->GetScrubber().OnSeek(event);
}
void AdornedRulerPanel::OnScrub(wxCommandEvent & event)
{
mProject->GetScrubber().OnScrub(event);
}
void AdornedRulerPanel::OnCaptureLost(wxMouseCaptureLostEvent & WXUNUSED(evt))
{
HideQuickPlayIndicator();
@ -2858,12 +2909,6 @@ void AdornedRulerPanel::ShowScrubMenu(const wxPoint & pos)
auto cleanup = finally([this]{ PopEventHandler(); });
wxMenu rulerMenu;
rulerMenu.AppendCheckItem(OnShowHideScrubbingID, _("Scrub Bar"));
if(mShowScrubbing)
rulerMenu.FindItem(OnShowHideScrubbingID)->Check();
rulerMenu.AppendSeparator();
mProject->GetScrubber().PopulateMenu(rulerMenu);
PopupMenu(&rulerMenu, pos);
}
@ -3104,11 +3149,6 @@ void AdornedRulerPanel::DoDrawSelection(wxDC * dc)
dc->DrawRectangle( r );
}
int AdornedRulerPanel::GetRulerHeight()
{
return GetRulerHeight(ReadScrubEnabledPref());
}
int AdornedRulerPanel::GetRulerHeight(bool showScrubBar)
{
return ProperRulerHeight + (showScrubBar ? ScrubHeight : 0);

View File

@ -298,7 +298,7 @@ public:
#endif
public:
static int GetRulerHeight();
int GetRulerHeight() { return GetRulerHeight(mShowScrubbing); }
static int GetRulerHeight(bool showScrubBar);
wxRect GetInnerRect() const { return mInner; }
@ -413,7 +413,9 @@ private:
void OnAutoScroll(wxCommandEvent &evt);
void OnLockPlayRegion(wxCommandEvent &evt);
void OnToggleScrubbing(wxCommandEvent&);
void OnToggleScrubbing(/*wxCommandEvent&*/);
void OnScrub(wxCommandEvent&);
void OnSeek(wxCommandEvent&);
void OnContextMenu(wxContextMenuEvent & WXUNUSED(event));
@ -446,7 +448,7 @@ private:
friend QuickPlayRulerOverlay;
wxWindow *mButtons[1];
wxWindow *mButtons[3];
bool mNeedButtonUpdate { true };
};