1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-07-02 17:23:18 +02:00

Merge branch 'master' into scrubbing2

This commit is contained in:
Paul Licameli 2016-05-03 23:45:14 -04:00
commit 3347f2af70
11 changed files with 213 additions and 60 deletions

View File

@ -243,10 +243,31 @@ public:
}
}
void SetScrollbar(int position, int thumbSize,
int range, int pageSize,
bool refresh = true) override;
private:
DECLARE_EVENT_TABLE()
};
void ScrollBar::SetScrollbar(int position, int thumbSize,
int range, int pageSize,
bool refresh)
{
// Mitigate flashing of scrollbars by refreshing only when something really changes.
auto changed =
position != GetThumbPosition() ||
thumbSize != GetThumbSize() ||
range != GetRange() ||
pageSize != GetPageSize();
if (!changed)
return;
wxScrollBar::SetScrollbar(position, thumbSize, range, pageSize, refresh);
}
BEGIN_EVENT_TABLE(ScrollBar, wxScrollBar)
EVT_SET_FOCUS(ScrollBar::OnSetFocus)
END_EVENT_TABLE()
@ -933,14 +954,18 @@ AudacityProject::AudacityProject(wxWindow * parent, wxWindowID id,
mCursorOverlay = std::make_unique<EditCursorOverlay>(this);
#ifdef EXPERIMENTAL_SCRUBBING_BASIC
// This must follow construction of *mIndicatorOverlay, because it must
// attach its timer event handler later (so that its handler is invoked
// earlier)
mScrubOverlay = std::make_unique<ScrubbingOverlay>(this);
mScrubber = std::make_unique<Scrubber>(this);
#endif
// This must follow construction of *mScrubOverlay, because it must
// More order dependencies here...
// This must follow construction of *mIndicatorOverlay, because it must
// attach its timer event handler later (so that its handler is invoked
// earlier)
mPlaybackScroller = std::make_unique<PlaybackScroller>(this);
// This must follow construction of *mPlaybackScroller,
// because it must
// attach its timer event handler later (so that its handler is invoked
// earlier)
this->Connect(EVT_TRACK_PANEL_TIMER,
@ -1771,7 +1796,6 @@ void AudacityProject::FixScrollbars()
mHsbar->SetScrollbar(scaledSbarH + offset, scaledSbarScreen, scaledSbarTotal,
scaledSbarScreen, TRUE);
mHsbar->Refresh();
}
// Vertical scrollbar
@ -1779,7 +1803,6 @@ void AudacityProject::FixScrollbars()
panelHeight / mViewInfo.scrollStep,
totalHeight / mViewInfo.scrollStep,
panelHeight / mViewInfo.scrollStep, TRUE);
mVsbar->Refresh();
if (refresh || (rescroll &&
(GetScreenEndTime() - mViewInfo.h) < mViewInfo.total)) {
@ -5315,3 +5338,46 @@ int AudacityProject::GetEstimatedRecordingMinsLeftOnDisk() {
int iRecMins = (int)(dRecTime / 60.0);
return iRecMins;
}
AudacityProject::PlaybackScroller::PlaybackScroller(AudacityProject *project)
: mProject(project)
{
mProject->Connect(EVT_TRACK_PANEL_TIMER,
wxCommandEventHandler(PlaybackScroller::OnTimer),
NULL,
this);
}
AudacityProject::PlaybackScroller::~PlaybackScroller()
{
mProject->Disconnect(EVT_TRACK_PANEL_TIMER,
wxCommandEventHandler(PlaybackScroller::OnTimer),
NULL,
this);
}
void AudacityProject::PlaybackScroller::OnTimer(wxCommandEvent &event)
{
// Let other listeners get the notification
event.Skip();
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
if (mActive && mProject->IsAudioActive())
{
// Pan the view, so that we center the play indicator.
ViewInfo &viewInfo = mProject->GetViewInfo();
TrackPanel *const trackPanel = mProject->GetTrackPanel();
const int posX = viewInfo.TimeToPosition(viewInfo.mRecentStreamTime);
int width;
trackPanel->GetTracksUsableArea(&width, NULL);
const int deltaX = posX - width / 2;
viewInfo.h =
viewInfo.OffsetTimeByPixels(viewInfo.h, deltaX, true);
if (!viewInfo.bScrollBeyondZero)
// Can't scroll too far left
viewInfo.h = std::max(0.0, viewInfo.h);
trackPanel->Refresh(false);
}
#endif
}

View File

@ -720,6 +720,28 @@ public:
const Scrubber &GetScrubber() const { return *mScrubber; }
#endif
class PlaybackScroller final : public wxEvtHandler
{
public:
explicit PlaybackScroller(AudacityProject *project);
~PlaybackScroller();
void Activate(bool active)
{
mActive = active;
}
private:
void OnTimer(wxCommandEvent &event);
AudacityProject *mProject;
bool mActive { false };
};
std::unique_ptr<PlaybackScroller> mPlaybackScroller;
public:
PlaybackScroller &GetPlaybackScroller() { return *mPlaybackScroller; }
DECLARE_EVENT_TABLE()
};

View File

@ -5901,16 +5901,26 @@ void TrackPanel::OnMouseEvent(wxMouseEvent & event)
ReleaseMouse();
}
if (event.Leaving() && !event.ButtonIsDown(wxMOUSE_BTN_ANY))
if (event.Leaving())
{
// PRL: was this test really needed? It interfered with my refactoring
// that tried to eliminate those enum values.
// I think it was never true, that mouse capture was pan or gain sliding,
// but no mouse button was down.
// if (mMouseCapture != IsPanSliding && mMouseCapture != IsGainSliding)
{
auto buttons =
// Bug 1325: button state in Leaving events is unreliable on Mac.
// Poll the global state instead.
// event.ButtonIsDown(wxMOUSE_BTN_ANY);
::wxGetMouseState().ButtonIsDown(wxMOUSE_BTN_ANY);
if(!buttons) {
SetCapturedTrack(NULL);
#if defined(__WXMAC__)
// We must install the cursor ourselves since the window under
// the mouse is no longer this one and wx2.8.12 makes that check.
// Should re-evaluate with wx3.

View File

@ -738,16 +738,24 @@ void ControlToolBar::OnKeyEvent(wxKeyEvent & event)
void ControlToolBar::OnPlay(wxCommandEvent & WXUNUSED(evt))
{
if (!CanStopAudioStream())
return;
auto doubleClicked = mPlay->IsDoubleClicked();
mPlay->ClearDoubleClicked();
StopPlaying();
auto p = GetActiveProject();
AudacityProject *p = GetActiveProject();
if (p) p->TP_DisplaySelection();
if (doubleClicked)
p->GetPlaybackScroller().Activate(true);
else {
if (!CanStopAudioStream())
return;
PlayDefault();
UpdateStatusBar(GetActiveProject());
StopPlaying();
if (p) p->TP_DisplaySelection();
PlayDefault();
UpdateStatusBar(p);
}
}
void ControlToolBar::OnStop(wxCommandEvent & WXUNUSED(evt))
@ -778,9 +786,11 @@ void ControlToolBar::StopPlaying(bool stopStream /* = true*/)
{
AudacityProject *project = GetActiveProject();
if(project)
if(project) {
project->GetPlaybackScroller().Activate(false);
// Let scrubbing code do some appearance change
project->GetScrubber().StopScrubbing();
}
if (!CanStopAudioStream())
return;

View File

@ -474,11 +474,24 @@ void TranscriptionToolBar::PlayAtSpeed(bool looped, bool cutPreview)
// Come here from button clicks only
void TranscriptionToolBar::OnPlaySpeed(wxCommandEvent & WXUNUSED(event))
{
// Let control have precedence over shift
const bool cutPreview = mButtons[TTB_PlaySpeed]->WasControlDown();
const bool looped = !cutPreview &&
mButtons[TTB_PlaySpeed]->WasShiftDown();
PlayAtSpeed(looped, cutPreview);
auto button = mButtons[TTB_PlaySpeed];
auto doubleClicked = button->IsDoubleClicked();
button->ClearDoubleClicked();
if (doubleClicked) {
GetActiveProject()->GetPlaybackScroller().Activate(true);
// Pop up the button
SetButton(false, button);
}
else {
// Let control have precedence over shift
const bool cutPreview = mButtons[TTB_PlaySpeed]->WasControlDown();
const bool looped = !cutPreview &&
button->WasShiftDown();
PlayAtSpeed(looped, cutPreview);
}
}
void TranscriptionToolBar::OnSpeedSlider(wxCommandEvent& WXUNUSED(event))

View File

@ -200,7 +200,7 @@ void Scrubber::MarkScrubStart(
// needed for the decision to start scrubbing later when handling
// drag events.
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
mSmoothScrollingScrub = smoothScrolling;
SetScrollScrubbing (smoothScrolling);
#endif
mAlwaysSeeking = alwaysSeeking;
mScrubStartPosition = xx;
@ -356,25 +356,6 @@ void Scrubber::ContinueScrubbing()
if (mScrubSpeedDisplayCountdown > 0)
--mScrubSpeedDisplayCountdown;
}
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
if (mSmoothScrollingScrub) {
// Pan the view, so that we center the play indicator.
ViewInfo &viewInfo = mProject->GetViewInfo();
TrackPanel *const trackPanel = mProject->GetTrackPanel();
const int posX = viewInfo.TimeToPosition(viewInfo.mRecentStreamTime);
int width;
trackPanel->GetTracksUsableArea(&width, NULL);
const int deltaX = posX - width / 2;
viewInfo.h =
viewInfo.OffsetTimeByPixels(viewInfo.h, deltaX, true);
if (!viewInfo.bScrollBeyondZero)
// Can't scroll too far left
viewInfo.h = std::max(0.0, viewInfo.h);
trackPanel->Refresh(false);
}
#endif
}
void Scrubber::StopScrubbing()
@ -382,7 +363,7 @@ void Scrubber::StopScrubbing()
UncheckAllMenuItems();
mScrubStartPosition = -1;
mSmoothScrollingScrub = false;
SetScrollScrubbing (false);
if (!IsScrubbing())
{
@ -393,6 +374,12 @@ void Scrubber::StopScrubbing()
}
}
void Scrubber::SetScrollScrubbing(bool scrollScrubbing)
{
mSmoothScrollingScrub = scrollScrubbing;
mProject->GetPlaybackScroller().Activate(scrollScrubbing);
}
bool Scrubber::IsScrubbing() const
{
if (mScrubToken <= 0)
@ -403,6 +390,7 @@ bool Scrubber::IsScrubbing() const
const_cast<Scrubber&>(*this).mScrubToken = -1;
const_cast<Scrubber&>(*this).mScrubStartPosition = -1;
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
// Don't call SetScrollScrubbing
const_cast<Scrubber&>(*this).mSmoothScrollingScrub = false;
#endif
return false;
@ -557,7 +545,6 @@ void ScrubbingOverlay::Draw
dc.DrawText(mLastScrubSpeedText, mLastScrubRect.GetX(), mLastScrubRect.GetY());
}
void ScrubbingOverlay::OnTimer(wxCommandEvent &event)
{
// Let other listeners get the notification
@ -666,7 +653,7 @@ void Scrubber::DoScrub(bool scroll, bool seek)
MarkScrubStart(xx, scroll, seek);
}
else if(!match) {
mSmoothScrollingScrub = scroll;
SetScrollScrubbing(scroll);
mAlwaysSeeking = seek;
UncheckAllMenuItems();
CheckMenuItem();

View File

@ -54,8 +54,11 @@ public:
bool HasStartedScrubbing() const
{ return GetScrubStartPosition() >= 0; }
bool IsScrubbing() const;
bool IsScrollScrubbing() const // If true, implies HasStartedScrubbing()
{ return mSmoothScrollingScrub; }
void SetScrollScrubbing(bool scrollScrubbing);
bool IsAlwaysSeeking() const
{ return mAlwaysSeeking; }

View File

@ -407,6 +407,8 @@ void AButton::OnMouseEvent(wxMouseEvent & event)
if (mEnabled && event.IsButton()) {
if (event.ButtonIsDown(wxMOUSE_BTN_ANY)) {
mIsClicking = true;
if (event.ButtonDClick())
mIsDoubleClicked = true;
if( !HasCapture() )
CaptureMouse();
}

View File

@ -109,6 +109,11 @@ class AButton final : public wxWindow {
bool WasControlDown(); // returns true if control was held down
// the last time the button was clicked
bool IsDown(){ return mButtonIsDown;}
// Double click is detected, but not automatically cleared.
bool IsDoubleClicked() const { return mIsDoubleClicked; }
void ClearDoubleClicked() { mIsDoubleClicked = false; }
void SetButtonToggles( bool toggler ){ mToggle = toggler;}
void Toggle(){ mButtonIsDown ? PopUp() : PushDown();}
void Click();
@ -157,6 +162,7 @@ class AButton final : public wxWindow {
bool mIsClicking;
bool mEnabled;
bool mUseDisabledAsDownHiliteImage;
bool mIsDoubleClicked {};
struct ImageArr { ImageRoll mArr[4]; };
std::vector<ImageArr> mImages;

View File

@ -85,6 +85,7 @@ array of Ruler::Label.
#include "../tracks/ui/Scrubbing.h"
//#define SCRUB_ABOVE
#define RULER_DOUBLE_CLICK
using std::min;
using std::max;
@ -2024,6 +2025,9 @@ void AdornedRulerPanel::OnCapture(wxCommandEvent & evt)
// if recording is initiated by a modal window (Timer Record).
SetCursor(mCursorDefault);
mIsRecording = true;
// The quick play indicator is useless during recording
HideQuickPlayIndicator();
}
else {
SetCursor(mCursorHand);
@ -2364,7 +2368,15 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt)
}
}
#ifdef RULER_DOUBLE_CLICK
if (evt.LeftDClick()) {
mDoubleClick = true;
HandleQPDoubleClick(evt, mousePosX);
}
else
#endif
if (evt.LeftDown()) {
mDoubleClick = false;
HandleQPClick(evt, mousePosX);
HandleQPDrag(evt, mousePosX);
}
@ -2379,6 +2391,11 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt)
}
}
void AdornedRulerPanel::HandleQPDoubleClick(wxMouseEvent &evt, wxCoord mousePosX)
{
mProject->GetPlaybackScroller().Activate(true);
}
void AdornedRulerPanel::HandleQPClick(wxMouseEvent &evt, wxCoord mousePosX)
{
// Temporarily unlock locked play region
@ -2512,6 +2529,9 @@ void AdornedRulerPanel::HandleQPDrag(wxMouseEvent &event, wxCoord mousePosX)
void AdornedRulerPanel::HandleQPRelease(wxMouseEvent &evt)
{
if (mDoubleClick)
return;
HideQuickPlayIndicator();
if (HasCapture())
@ -2552,6 +2572,28 @@ void AdornedRulerPanel::HandleQPRelease(wxMouseEvent &evt)
ClearPlayRegion();
}
StartQPPlay(evt.ShiftDown(), evt.ControlDown());
mMouseEventState = mesNone;
mIsDragging = false;
mLeftDownClick = -1;
if (mPlayRegionLock) {
// Restore Locked Play region
SetPlayRegion(mOldPlayRegionStart, mOldPlayRegionEnd);
mProject->OnLockPlayRegion();
// and release local lock
mPlayRegionLock = false;
}
}
void AdornedRulerPanel::StartQPPlay(bool looped, bool cutPreview)
{
const double t0 = mTracks->GetStartTime();
const double t1 = mTracks->GetEndTime();
const double sel0 = mProject->GetSel0();
const double sel1 = mProject->GetSel1();
// Start / Restart playback on left click.
bool startPlaying = (mPlayRegionStart >= 0);
@ -2562,7 +2604,7 @@ void AdornedRulerPanel::HandleQPRelease(wxMouseEvent &evt)
bool loopEnabled = true;
double start, end;
if ((mPlayRegionEnd - mPlayRegionStart == 0.0) && evt.ShiftDown()) {
if ((mPlayRegionEnd - mPlayRegionStart == 0.0) && looped) {
// Loop play a point will loop either a selection or the project.
if ((mPlayRegionStart > sel0) && (mPlayRegionStart < sel1)) {
// we are in a selection, so use the selection
@ -2582,15 +2624,15 @@ void AdornedRulerPanel::HandleQPRelease(wxMouseEvent &evt)
loopEnabled = ((end - start) > 0.001)? true : false;
AudioIOStartStreamOptions options(mProject->GetDefaultPlayOptions());
options.playLooped = (loopEnabled && evt.ShiftDown());
options.playLooped = (loopEnabled && looped);
if (!evt.ControlDown())
if (!cutPreview)
options.pStartTime = &mPlayRegionStart;
else
options.timeTrack = NULL;
ControlToolBar::PlayAppearance appearance =
evt.ControlDown() ? ControlToolBar::PlayAppearance::CutPreview
cutPreview ? ControlToolBar::PlayAppearance::CutPreview
: options.playLooped ? ControlToolBar::PlayAppearance::Looped
: ControlToolBar::PlayAppearance::Straight;
ctb->PlayPlayRegion((SelectedRegion(start, end)),
@ -2603,18 +2645,6 @@ void AdornedRulerPanel::HandleQPRelease(wxMouseEvent &evt)
mPlayRegionEnd = end;
Refresh();
}
mMouseEventState = mesNone;
mIsDragging = false;
mLeftDownClick = -1;
if (mPlayRegionLock) {
// Restore Locked Play region
SetPlayRegion(mOldPlayRegionStart, mOldPlayRegionEnd);
mProject->OnLockPlayRegion();
// and release local lock
mPlayRegionLock = false;
}
}
void AdornedRulerPanel::UpdateStatusBarAndTooltips(StatusChoice choice)

View File

@ -342,9 +342,11 @@ private:
void OnSize(wxSizeEvent &evt);
void UpdateRects();
void OnMouseEvents(wxMouseEvent &evt);
void HandleQPDoubleClick(wxMouseEvent &event, wxCoord mousePosX);
void HandleQPClick(wxMouseEvent &event, wxCoord mousePosX);
void HandleQPDrag(wxMouseEvent &event, wxCoord mousePosX);
void HandleQPRelease(wxMouseEvent &event);
void StartQPPlay(bool looped, bool cutPreview);
static inline bool IsButton(StatusChoice choice)
{
@ -488,6 +490,8 @@ private:
mutable int mButtonFontSize { -1 };
mutable wxFont mButtonFont;
bool mDoubleClick {};
DECLARE_EVENT_TABLE()
};