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:
commit
9f8e34ad0f
@ -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()
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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();
|
||||||
|
@ -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;
|
||||||
|
@ -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{};
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user