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

Implement drag-scrub, compatibly with the existing move-scrub...

Also fix scroll-scrub and remove obsolete mouse preferences messages
This commit is contained in:
Paul Licameli 2016-05-10 09:40:06 -04:00
commit 9f8e34ad0f
12 changed files with 150 additions and 83 deletions

View File

@ -402,6 +402,14 @@ struct AudioIO::ScrubQueue
} }
~ScrubQueue() {} ~ScrubQueue() {}
double LastTimeInQueue() const
{
// Needed by the main thread sometimes
wxCriticalSectionLocker locker(mUpdating);
const Entry &previous = mEntries[(mLeadingIdx + Size - 1) % Size];
return previous.mS1 / mRate;
}
bool Producer(double end, double maxSpeed, bool bySpeed, bool maySkip) bool Producer(double end, double maxSpeed, bool bySpeed, bool maySkip)
{ {
// Main thread indicates a scrubbing interval // Main thread indicates a scrubbing interval
@ -670,7 +678,7 @@ private:
const double mRate; const double mRate;
const long mMinStutter; const long mMinStutter;
wxLongLong mLastScrubTimeMillis; wxLongLong mLastScrubTimeMillis;
wxCriticalSection mUpdating; mutable wxCriticalSection mUpdating;
}; };
#endif #endif
@ -2426,6 +2434,15 @@ bool AudioIO::EnqueueScrubBySignedSpeed(double speed, double maxSpeed, bool mayS
else else
return false; return false;
} }
double AudioIO::GetLastTimeInScrubQueue() const
{
if (mScrubQueue)
return mScrubQueue->LastTimeInQueue();
else
return -1.0;
}
#endif #endif
bool AudioIO::IsBusy() bool AudioIO::IsBusy()

View File

@ -208,6 +208,10 @@ class AUDACITY_DLL_API AudioIO final {
* Return true if some work was really enqueued. * Return true if some work was really enqueued.
*/ */
bool EnqueueScrubBySignedSpeed(double speed, double maxSpeed, bool maySkip); bool EnqueueScrubBySignedSpeed(double speed, double maxSpeed, bool maySkip);
/** \brief return the ending time of the last enqueued scrub interval.
*/
double GetLastTimeInScrubQueue() const;
#endif #endif
/** \brief Returns true if audio i/o is busy starting, stopping, playing, /** \brief Returns true if audio i/o is busy starting, stopping, playing,

View File

@ -173,7 +173,6 @@
// The following enable parts of the scrubbing user interface. // The following enable parts of the scrubbing user interface.
#define EXPERIMENTAL_SCRUBBING_BASIC #define EXPERIMENTAL_SCRUBBING_BASIC
#ifdef EXPERIMENTAL_SCRUBBING_BASIC #ifdef EXPERIMENTAL_SCRUBBING_BASIC
#define EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
#define EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL #define EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL
#endif #endif
#endif #endif

View File

@ -2281,8 +2281,13 @@ void AudacityProject::OnRecordAppend()
GetControlToolBar()->OnRecord(evt); GetControlToolBar()->OnRecord(evt);
} }
// The code for "OnPlayStopSelect" is simply the code of "OnPlayStop" and "OnStopSelect" merged.
void AudacityProject::OnPlayStopSelect() void AudacityProject::OnPlayStopSelect()
{
DoPlayStopSelect(false, false);
}
// The code for "OnPlayStopSelect" is simply the code of "OnPlayStop" and "OnStopSelect" merged.
void AudacityProject::DoPlayStopSelect(bool click, bool shift)
{ {
wxCommandEvent evt; wxCommandEvent evt;
ControlToolBar *toolbar = GetControlToolBar(); ControlToolBar *toolbar = GetControlToolBar();
@ -2291,7 +2296,36 @@ void AudacityProject::OnPlayStopSelect()
if (gAudioIO->IsStreamActive(GetAudioIOToken())) { if (gAudioIO->IsStreamActive(GetAudioIOToken())) {
toolbar->SetPlay(false); //Pops toolbar->SetPlay(false); //Pops
toolbar->SetStop(true); //Pushes stop down toolbar->SetStop(true); //Pushes stop down
mViewInfo.selectedRegion.setT0(gAudioIO->GetStreamTime(), false);
// change the selection
auto time = gAudioIO->GetStreamTime();
auto &selection = mViewInfo.selectedRegion;
if (shift && click) {
// Change the region selection, as if by shift-click at the play head
auto t0 = selection.t0(), t1 = selection.t1();
if (time < t0)
// Grow selection
t0 = time;
else if (time > t1)
// Grow selection
t1 = time;
else {
// Shrink selection, changing the nearer boundary
if (fabs(t0 - time) < fabs(t1 - time))
t0 = time;
else
t1 = time;
}
selection.setTimes(t0, t1);
}
else if (click)
// Set a point selection, as if by a click at the play head
selection.setTimes(time, time);
else
// How stop and set cursor always worked
// -- change t0, collapsing to point only if t1 was greater
selection.setT0(time, false);
ModifyState(false); // without bWantsAutoSave ModifyState(false); // without bWantsAutoSave
toolbar->OnStop(evt); toolbar->OnStop(evt);
} }

View File

@ -80,6 +80,7 @@ void OnSeekRightLong();
bool MakeReadyToPlay(bool loop = false, bool cutpreview = false); // Helper function that sets button states etc. bool MakeReadyToPlay(bool loop = false, bool cutpreview = false); // Helper function that sets button states etc.
void OnPlayStop(); void OnPlayStop();
void DoPlayStopSelect(bool click, bool shift);
void OnPlayStopSelect(); void OnPlayStopSelect();
void OnPlayOneSecond(); void OnPlayOneSecond();
void OnPlayToSelection(); void OnPlayToSelection();

View File

@ -5364,7 +5364,6 @@ void AudacityProject::PlaybackScroller::OnTimer(wxCommandEvent &event)
// Let other listeners get the notification // Let other listeners get the notification
event.Skip(); event.Skip();
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
if (mActive && mProject->IsAudioActive()) if (mActive && mProject->IsAudioActive())
{ {
// Pan the view, so that we center the play indicator. // Pan the view, so that we center the play indicator.
@ -5382,5 +5381,4 @@ void AudacityProject::PlaybackScroller::OnTimer(wxCommandEvent &event)
viewInfo.h = std::max(0.0, viewInfo.h); viewInfo.h = std::max(0.0, viewInfo.h);
trackPanel->Refresh(false); trackPanel->Refresh(false);
} }
#endif
} }

View File

@ -5472,12 +5472,9 @@ void TrackPanel::HandleWheelRotation(wxMouseEvent & event)
(event.m_wheelDelta > 0 ? (double)event.m_wheelDelta : 120.0); (event.m_wheelDelta > 0 ? (double)event.m_wheelDelta : 120.0);
if (event.ShiftDown() if (event.ShiftDown()
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
// Don't pan during smooth scrolling. That would conflict with keeping // Don't pan during smooth scrolling. That would conflict with keeping
// the play indicator centered. // the play indicator centered.
&& !GetProject()->GetScrubber().IsScrollScrubbing() && !GetProject()->GetScrubber().IsScrollScrubbing())
#endif
)
{ {
// MM: Scroll left/right when used with Shift key down // MM: Scroll left/right when used with Shift key down
mListener->TP_ScrollWindow( mListener->TP_ScrollWindow(
@ -5506,15 +5503,12 @@ void TrackPanel::HandleWheelRotation(wxMouseEvent & event)
// Time corresponding to mouse position // Time corresponding to mouse position
wxCoord xx; wxCoord xx;
double center_h; double center_h;
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
if (GetProject()->GetScrubber().IsScrollScrubbing()) { if (GetProject()->GetScrubber().IsScrollScrubbing()) {
// Expand or contract about the center, ignoring mouse position // Expand or contract about the center, ignoring mouse position
center_h = mViewInfo->h + (GetScreenEndTime() - mViewInfo->h) / 2.0; center_h = mViewInfo->h + (GetScreenEndTime() - mViewInfo->h) / 2.0;
xx = mViewInfo->TimeToPosition(center_h, trackLeftEdge); xx = mViewInfo->TimeToPosition(center_h, trackLeftEdge);
} }
else else {
#endif
{
xx = event.m_x; xx = event.m_x;
center_h = mViewInfo->PositionToTime(xx, trackLeftEdge); center_h = mViewInfo->PositionToTime(xx, trackLeftEdge);
} }

View File

@ -110,13 +110,6 @@ void MousePrefs::CreateList()
AddItem(_("Left-Drag"), _("Select"), _("Set Selection Range")); AddItem(_("Left-Drag"), _("Select"), _("Set Selection Range"));
AddItem(_("Shift-Left-Click"), _("Select"), _("Extend Selection Range")); AddItem(_("Shift-Left-Click"), _("Select"), _("Extend Selection Range"));
AddItem(_("Left-Double-Click"), _("Select"), _("Select Clip or Entire Track")); AddItem(_("Left-Double-Click"), _("Select"), _("Select Clip or Entire Track"));
#ifdef EXPERIMENTAL_SCRUBBING_BASIC
AddItem(CTRL + _("-Left-Click"), _("Select"), _("Scrub"));
AddItem(CTRL + _("-Left-Drag"), _("Select"), _("Seek"));
#endif
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
AddItem(CTRL + _("-Left-Double-Click"), _("Select"), _("Scroll-scrub"));
#endif
#ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL #ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL
AddItem(_("Wheel-Rotate"), _("Select"), _("Change scrub speed")); AddItem(_("Wheel-Rotate"), _("Select"), _("Change scrub speed"));
#endif #endif

View File

@ -188,11 +188,7 @@ namespace {
void Scrubber::MarkScrubStart( void Scrubber::MarkScrubStart(
// Assume xx is relative to the left edge of TrackPanel! // Assume xx is relative to the left edge of TrackPanel!
wxCoord xx wxCoord xx, bool smoothScrolling, bool alwaysSeeking
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
, bool smoothScrolling
#endif
, bool alwaysSeeking
) )
{ {
UncheckAllMenuItems(); UncheckAllMenuItems();
@ -200,9 +196,7 @@ void Scrubber::MarkScrubStart(
// Don't actually start scrubbing, but collect some information // Don't actually start scrubbing, but collect some information
// needed for the decision to start scrubbing later when handling // needed for the decision to start scrubbing later when handling
// drag events. // drag events.
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL mSmoothScrollingScrub = smoothScrolling;
SetScrollScrubbing (smoothScrolling);
#endif
mAlwaysSeeking = alwaysSeeking; mAlwaysSeeking = alwaysSeeking;
ControlToolBar * const ctb = mProject->GetControlToolBar(); ControlToolBar * const ctb = mProject->GetControlToolBar();
@ -236,6 +230,9 @@ bool Scrubber::MaybeStartScrubbing(wxCoord xx)
if (IsScrubbing()) if (IsScrubbing())
return false; return false;
else { else {
const auto state = ::wxGetMouseState();
mDragging = state.LeftIsDown();
const bool busy = gAudioIO->IsBusy(); const bool busy = gAudioIO->IsBusy();
if (busy && gAudioIO->GetNumCaptureChannels() > 0) { if (busy && gAudioIO->GetNumCaptureChannels() > 0) {
// Do not stop recording, and don't try to start scrubbing after // Do not stop recording, and don't try to start scrubbing after
@ -265,6 +262,14 @@ bool Scrubber::MaybeStartScrubbing(wxCoord xx)
mScrubStartPosition = position; mScrubStartPosition = position;
} }
if (mDragging && mSmoothScrollingScrub) {
auto delta = time0 - time1;
time0 = std::max(0.0, std::min(maxTime,
(viewInfo.h + mProject->GetScreenEndTime()) / 2
));
time1 = time0 + delta;
}
AudioIOStartStreamOptions options(mProject->GetDefaultPlayOptions()); AudioIOStartStreamOptions options(mProject->GetDefaultPlayOptions());
options.timeTrack = NULL; options.timeTrack = NULL;
options.scrubDelay = (kTimerInterval / 1000.0); options.scrubDelay = (kTimerInterval / 1000.0);
@ -278,8 +283,10 @@ bool Scrubber::MaybeStartScrubbing(wxCoord xx)
p->GetTranscriptionToolBar()->GetPlaySpeed(); p->GetTranscriptionToolBar()->GetPlaySpeed();
} }
#else #else
// That idea seems unpopular... just make it one // That idea seems unpopular... just make it one for move-scrub,
mMaxScrubSpeed = options.maxScrubSpeed = 1.0; // but big for drag-scrub
mMaxScrubSpeed = options.maxScrubSpeed =
mDragging ? AudioIO::GetMaxScrubSpeed() : 1.0;
#endif #endif
options.maxScrubTime = mProject->GetTracks()->GetEndTime(); options.maxScrubTime = mProject->GetTracks()->GetEndTime();
ControlToolBar::PlayAppearance appearance = ControlToolBar::PlayAppearance appearance =
@ -303,8 +310,11 @@ bool Scrubber::MaybeStartScrubbing(wxCoord xx)
// Wait to test again // Wait to test again
mScrubStartClockTimeMillis = ::wxGetLocalTimeMillis(); mScrubStartClockTimeMillis = ::wxGetLocalTimeMillis();
if (IsScrubbing()) if (IsScrubbing()) {
mProject->GetPlaybackScroller().Activate(mSmoothScrollingScrub);
mScrubHasFocus = true; mScrubHasFocus = true;
mLastScrubPosition = xx;
}
// Return true whether we started scrub, or are still waiting to decide. // Return true whether we started scrub, or are still waiting to decide.
return true; return true;
@ -313,13 +323,19 @@ bool Scrubber::MaybeStartScrubbing(wxCoord xx)
void Scrubber::ContinueScrubbing() void Scrubber::ContinueScrubbing()
{ {
const wxMouseState state(::wxGetMouseState());
if (mDragging && !state.LeftIsDown()) {
// Stop and set cursor
mProject->DoPlayStopSelect(true, state.ShiftDown());
return;
}
// Thus scrubbing relies mostly on periodic polling of mouse and keys, // Thus scrubbing relies mostly on periodic polling of mouse and keys,
// not event notifications. But there are a few event handlers that // not event notifications. But there are a few event handlers that
// leave messages for this routine, in mScrubSeekPress and in mScrubHasFocus. // leave messages for this routine, in mScrubSeekPress and in mScrubHasFocus.
// Seek only when the pointer is in the panel. Else, scrub. // Seek only when the pointer is in the panel. Else, scrub.
const wxMouseState state(::wxGetMouseState());
TrackPanel *const trackPanel = mProject->GetTrackPanel(); TrackPanel *const trackPanel = mProject->GetTrackPanel();
// Decide whether to skip play, because either mouse is down now, // Decide whether to skip play, because either mouse is down now,
@ -337,26 +353,32 @@ void Scrubber::ContinueScrubbing()
} }
const wxPoint position = trackPanel->ScreenToClient(state.GetPosition()); const wxPoint position = trackPanel->ScreenToClient(state.GetPosition());
// When we don't have focus, enqueue silent scrubs until we regain focus. const auto &viewInfo = mProject->GetViewInfo();
bool result = false; bool result = false;
if (!mScrubHasFocus) if (!mScrubHasFocus)
// When we don't have focus, enqueue silent scrubs until we regain focus.
result = gAudioIO->EnqueueScrubBySignedSpeed(0, mMaxScrubSpeed, false); result = gAudioIO->EnqueueScrubBySignedSpeed(0, mMaxScrubSpeed, false);
else if (mDragging && mSmoothScrollingScrub) {
const auto lastTime = gAudioIO->GetLastTimeInScrubQueue();
const auto delta = mLastScrubPosition - position.x;
const double time = viewInfo.OffsetTimeByPixels(lastTime, delta);
result = gAudioIO->EnqueueScrubByPosition(time, mMaxScrubSpeed, false);
mLastScrubPosition = position.x;
}
else { else {
const double time = mProject->GetViewInfo().PositionToTime(position.x, trackPanel->GetLeftOffset()); const double time = viewInfo.PositionToTime(position.x, trackPanel->GetLeftOffset());
if (seek) if (seek)
// Cause OnTimer() to suppress the speed display // Cause OnTimer() to suppress the speed display
mScrubSpeedDisplayCountdown = 1; mScrubSpeedDisplayCountdown = 1;
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
if (mSmoothScrollingScrub) { if (mSmoothScrollingScrub) {
const double speed = FindScrubSpeed(seek, time); const double speed = FindScrubSpeed(seek, time);
result = gAudioIO->EnqueueScrubBySignedSpeed(speed, mMaxScrubSpeed, seek); result = gAudioIO->EnqueueScrubBySignedSpeed(speed, mMaxScrubSpeed, seek);
} }
else else
#endif
result = gAudioIO->EnqueueScrubByPosition result = gAudioIO->EnqueueScrubByPosition
(time, seek ? 1.0 : mMaxScrubSpeed, seek); (time, seek ? 1.0 : mMaxScrubSpeed, seek);
} }
if (result) if (result)
@ -364,12 +386,9 @@ void Scrubber::ContinueScrubbing()
// else, if seek requested, try again at a later time when we might // else, if seek requested, try again at a later time when we might
// enqueue a long enough stutter // enqueue a long enough stutter
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
if (mSmoothScrollingScrub) if (mSmoothScrollingScrub)
; ;
else else {
#endif
{
if (mScrubSpeedDisplayCountdown > 0) if (mScrubSpeedDisplayCountdown > 0)
--mScrubSpeedDisplayCountdown; --mScrubSpeedDisplayCountdown;
} }
@ -380,7 +399,8 @@ void Scrubber::StopScrubbing()
UncheckAllMenuItems(); UncheckAllMenuItems();
mScrubStartPosition = -1; mScrubStartPosition = -1;
SetScrollScrubbing (false); mProject->GetPlaybackScroller().Activate(false);
mDragging = false;
if (!IsScrubbing()) if (!IsScrubbing())
{ {
@ -391,12 +411,11 @@ void Scrubber::StopScrubbing()
} }
mProject->GetRulerPanel()->HideQuickPlayIndicator(); mProject->GetRulerPanel()->HideQuickPlayIndicator();
}
void Scrubber::SetScrollScrubbing(bool scrollScrubbing) // Need this in case ruler gets the mouse-up event after escaping scrubbing:
{ // prevent reappearance of the
mSmoothScrollingScrub = scrollScrubbing; // quick play guideline
mProject->GetPlaybackScroller().Activate(scrollScrubbing); mProject->GetRulerPanel()->IgnoreMouseUp();
} }
bool Scrubber::IsScrubbing() const bool Scrubber::IsScrubbing() const
@ -408,24 +427,22 @@ bool Scrubber::IsScrubbing() const
else { else {
const_cast<Scrubber&>(*this).mScrubToken = -1; const_cast<Scrubber&>(*this).mScrubToken = -1;
const_cast<Scrubber&>(*this).mScrubStartPosition = -1; const_cast<Scrubber&>(*this).mScrubStartPosition = -1;
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
// Don't call SetScrollScrubbing
const_cast<Scrubber&>(*this).mSmoothScrollingScrub = false; const_cast<Scrubber&>(*this).mSmoothScrollingScrub = false;
#endif
return false; return false;
} }
} }
bool Scrubber::ShouldDrawScrubSpeed() bool Scrubber::ShouldDrawScrubSpeed()
{ {
if (mDragging)
return false;
return IsScrubbing() && return IsScrubbing() &&
mScrubHasFocus && ( mScrubHasFocus && (
// Draw for (non-scroll) scrub, sometimes, but never for seek // Draw for (non-scroll) scrub, sometimes, but never for seek
(!PollIsSeeking() && mScrubSpeedDisplayCountdown > 0) (!PollIsSeeking() && mScrubSpeedDisplayCountdown > 0)
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
// Draw always for scroll-scrub and for scroll-seek // Draw always for scroll-scrub and for scroll-seek
|| mSmoothScrollingScrub || mSmoothScrollingScrub
#endif
); );
} }
@ -439,6 +456,10 @@ double Scrubber::FindScrubSpeed(bool seeking, double time) const
void Scrubber::HandleScrollWheel(int steps) void Scrubber::HandleScrollWheel(int steps)
{ {
if (mDragging)
// Not likely you would spin it with the left button down, but...
return;
const int newLogMaxScrubSpeed = mLogMaxScrubSpeed + steps; const int newLogMaxScrubSpeed = mLogMaxScrubSpeed + steps;
static const double maxScrubSpeedBase = static const double maxScrubSpeedBase =
pow(2.0, 1.0 / ScrubSpeedStepsPerOctave); pow(2.0, 1.0 / ScrubSpeedStepsPerOctave);
@ -447,9 +468,7 @@ void Scrubber::HandleScrollWheel(int steps)
newSpeed <= AudioIO::GetMaxScrubSpeed()) { newSpeed <= AudioIO::GetMaxScrubSpeed()) {
mLogMaxScrubSpeed = newLogMaxScrubSpeed; mLogMaxScrubSpeed = newLogMaxScrubSpeed;
mMaxScrubSpeed = newSpeed; mMaxScrubSpeed = newSpeed;
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
if (!mSmoothScrollingScrub) if (!mSmoothScrollingScrub)
#endif
// Show the speed for one second // Show the speed for one second
mScrubSpeedDisplayCountdown = kOneSecondCountdown + 1; mScrubSpeedDisplayCountdown = kOneSecondCountdown + 1;
} }
@ -472,7 +491,8 @@ void Scrubber::Forwarder::OnMouse(wxMouseEvent &event)
if (isScrubbing && !event.HasAnyModifiers()) { if (isScrubbing && !event.HasAnyModifiers()) {
if(event.LeftDown() || if(event.LeftDown() ||
(event.LeftIsDown() && event.Dragging())) { (event.LeftIsDown() && event.Dragging())) {
scrubber.mScrubSeekPress = true; if (!scrubber.mDragging)
scrubber.mScrubSeekPress = true;
auto xx = ruler->ScreenToClient(::wxGetMousePosition()).x; auto xx = ruler->ScreenToClient(::wxGetMousePosition()).x;
ruler->UpdateQuickPlayPos(xx); ruler->UpdateQuickPlayPos(xx);
ruler->ShowQuickPlayIndicator(); ruler->ShowQuickPlayIndicator();
@ -544,11 +564,9 @@ void ScrubbingOverlay::Draw(OverlayPanel &, wxDC &dc)
// (b) Error alerts // (b) Error alerts
// So they were changed to 'orange' and 'lime'. // So they were changed to 'orange' and 'lime'.
static const wxColour clrNoScroll(215, 162, 0), clrScroll(0, 204, 153); static const wxColour clrNoScroll(215, 162, 0), clrScroll(0, 204, 153);
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
if (scrubber.IsScrollScrubbing()) if (scrubber.IsScrollScrubbing())
dc.SetTextForeground(clrScroll); dc.SetTextForeground(clrScroll);
else else
#endif
dc.SetTextForeground(clrNoScroll); dc.SetTextForeground(clrNoScroll);
dc.DrawText(mLastScrubSpeedText, mLastScrubRect.GetX(), mLastScrubRect.GetY()); dc.DrawText(mLastScrubSpeedText, mLastScrubRect.GetX(), mLastScrubRect.GetY());
@ -565,12 +583,13 @@ void ScrubbingOverlay::OnTimer(wxCommandEvent &event)
auto position = ::wxGetMousePosition(); auto position = ::wxGetMousePosition();
{ {
auto xx = ruler->ScreenToClient(position).x; if(scrubber.HasStartedScrubbing()) {
ruler->UpdateQuickPlayPos(xx); auto xx = ruler->ScreenToClient(position).x;
ruler->UpdateQuickPlayPos(xx);
if(!isScrubbing && scrubber.HasStartedScrubbing()) { if (!isScrubbing)
// Really start scrub if motion is far enough // Really start scrub if motion is far enough
scrubber.MaybeStartScrubbing(xx); scrubber.MaybeStartScrubbing(xx);
} }
if (!isScrubbing) { if (!isScrubbing) {
@ -603,23 +622,17 @@ void ScrubbingOverlay::OnTimer(wxCommandEvent &event)
// Find the text // Find the text
const double maxScrubSpeed = GetScrubber().GetMaxScrubSpeed(); const double maxScrubSpeed = GetScrubber().GetMaxScrubSpeed();
const double speed = const double speed =
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
scrubber.IsScrollScrubbing() scrubber.IsScrollScrubbing()
? scrubber.FindScrubSpeed ? scrubber.FindScrubSpeed
(seeking, mProject->GetViewInfo().PositionToTime(position.x, trackPanel->GetLeftOffset())) (seeking, mProject->GetViewInfo().PositionToTime(position.x, trackPanel->GetLeftOffset()))
: : maxScrubSpeed;
#endif
maxScrubSpeed;
const wxChar *format = const wxChar *format =
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
scrubber.IsScrollScrubbing() scrubber.IsScrollScrubbing()
? seeking ? seeking
? wxT("%+.2fX") ? wxT("%+.2fX")
: wxT("%+.2f") : wxT("%+.2f")
: : wxT("%.2f");
#endif
wxT("%.2f");
mNextScrubSpeedText = wxString::Format(format, speed); mNextScrubSpeedText = wxString::Format(format, speed);
@ -657,7 +670,7 @@ Scrubber &ScrubbingOverlay::GetScrubber()
bool Scrubber::PollIsSeeking() bool Scrubber::PollIsSeeking()
{ {
return mAlwaysSeeking || ::wxGetMouseState().LeftIsDown(); return !mDragging && (mAlwaysSeeking || ::wxGetMouseState().LeftIsDown());
} }
void Scrubber::DoScrub(bool scroll, bool seek) void Scrubber::DoScrub(bool scroll, bool seek)
@ -677,7 +690,8 @@ void Scrubber::DoScrub(bool scroll, bool seek)
MarkScrubStart(xx, scroll, seek); MarkScrubStart(xx, scroll, seek);
} }
else if(!match) { else if(!match) {
SetScrollScrubbing(scroll); mSmoothScrollingScrub = scroll;
mProject->GetPlaybackScroller().Activate(scroll);
mAlwaysSeeking = seek; mAlwaysSeeking = seek;
UncheckAllMenuItems(); UncheckAllMenuItems();
CheckMenuItem(); CheckMenuItem();

View File

@ -29,11 +29,8 @@ public:
// Assume xx is relative to the left edge of TrackPanel! // Assume xx is relative to the left edge of TrackPanel!
void MarkScrubStart( void MarkScrubStart(
wxCoord xx wxCoord xx, bool smoothScrolling,
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL bool alwaysSeeking // if false, can switch seeking or scrubbing
, bool smoothScrolling
#endif
, bool alwaysSeeking // if false, can switch seeking or scrubbing
// by mouse button state // by mouse button state
); );
@ -57,7 +54,6 @@ public:
bool IsScrollScrubbing() const // If true, implies HasStartedScrubbing() bool IsScrollScrubbing() const // If true, implies HasStartedScrubbing()
{ return mSmoothScrollingScrub; } { return mSmoothScrollingScrub; }
void SetScrollScrubbing(bool scrollScrubbing);
bool IsAlwaysSeeking() const bool IsAlwaysSeeking() const
{ return mAlwaysSeeking; } { return mAlwaysSeeking; }
@ -113,10 +109,12 @@ private:
bool mScrubHasFocus; bool mScrubHasFocus;
int mScrubSpeedDisplayCountdown; int mScrubSpeedDisplayCountdown;
wxCoord mScrubStartPosition; wxCoord mScrubStartPosition;
wxCoord mLastScrubPosition {};
double mMaxScrubSpeed; double mMaxScrubSpeed;
bool mScrubSeekPress; bool mScrubSeekPress;
bool mSmoothScrollingScrub; bool mSmoothScrollingScrub;
bool mAlwaysSeeking{}; bool mAlwaysSeeking {};
bool mDragging {};
#ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL #ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL
int mLogMaxScrubSpeed; int mLogMaxScrubSpeed;

View File

@ -2326,6 +2326,17 @@ bool AdornedRulerPanel::IsWithinMarker(int mousePosX, double markerTime)
void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt) void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt)
{ {
if (mIgnoreMouseUp) {
if (evt.Dragging())
return;
else if (evt.ButtonUp()) {
mIgnoreMouseUp = false;
return;
}
else
mIgnoreMouseUp = false;
}
// PRL: why do I need these two lines on Windows but not on Mac? // PRL: why do I need these two lines on Windows but not on Mac?
if (evt.ButtonDown(wxMOUSE_BTN_ANY)) if (evt.ButtonDown(wxMOUSE_BTN_ANY))
SetFocus(); SetFocus();
@ -2654,10 +2665,12 @@ void AdornedRulerPanel::HandleQPRelease(wxMouseEvent &evt)
if (mDoubleClick) if (mDoubleClick)
return; return;
HideQuickPlayIndicator();
if (HasCapture()) if (HasCapture())
ReleaseMouse(); ReleaseMouse();
else
return;
HideQuickPlayIndicator();
mCaptureState = CaptureState{}; mCaptureState = CaptureState{};

View File

@ -350,6 +350,7 @@ public:
void ShowQuickPlayIndicator(); void ShowQuickPlayIndicator();
void HideQuickPlayIndicator(); void HideQuickPlayIndicator();
void UpdateQuickPlayPos(wxCoord &mousPosX); void UpdateQuickPlayPos(wxCoord &mousPosX);
void IgnoreMouseUp() { mIgnoreMouseUp = true; }
private: private:
void OnCapture(wxCommandEvent & evt); void OnCapture(wxCommandEvent & evt);
@ -531,6 +532,7 @@ private:
mutable wxFont mButtonFont; mutable wxFont mButtonFont;
bool mDoubleClick {}; bool mDoubleClick {};
bool mIgnoreMouseUp {};
DECLARE_EVENT_TABLE() DECLARE_EVENT_TABLE()