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

Merge branch 'master' into scrubbing

This commit is contained in:
Paul Licameli 2016-04-27 11:50:27 -04:00
commit d8fd3dd09b
16 changed files with 383 additions and 202 deletions

View File

@ -689,8 +689,6 @@ struct Final_action {
Final_action(F f) : clean{ f } {}
~Final_action() { clean(); }
F clean;
Final_action(const Final_action&) PROHIBITED;
Final_action& operator= (const Final_action&) PROHIBITED;
};
// Function template with type deduction lets you construct Final_action

View File

@ -1613,7 +1613,7 @@ void AudacityProject::TP_ScrollWindow(double scrollto)
// handler in Track Panel. A positive argument makes the window
// scroll down, while a negative argument scrolls up.
//
void AudacityProject::TP_ScrollUpDown(int delta)
bool AudacityProject::TP_ScrollUpDown(int delta)
{
int oldPos = mVsbar->GetThumbPosition();
int pos = oldPos + delta;
@ -1634,7 +1634,10 @@ void AudacityProject::TP_ScrollUpDown(int delta)
wxScrollEvent dummy;
OnScroll(dummy);
return true;
}
else
return false;
}
void AudacityProject::FixScrollbars()

View File

@ -430,7 +430,7 @@ class AUDACITY_DLL_API AudacityProject final : public wxFrame,
void TP_ScrollLeft() override;
void TP_ScrollRight() override;
void TP_ScrollWindow(double scrollto) override;
void TP_ScrollUpDown(int delta) override;
bool TP_ScrollUpDown(int delta) override;
void TP_HandleResize() override;
// ToolBar

View File

@ -1525,13 +1525,7 @@ void TrackPanel::SetCursorAndTipWhenSelectTool( Track * t,
// If not shift-down and not snapping center, then
// choose boundaries only in snapping tolerance,
// and may choose center.
// But don't change the cursor when scrubbing.
SelectionBoundary boundary =
#ifdef EXPERIMENTAL_SCRUBBING_BASIC
GetProject()->GetScrubber().IsScrubbing()
? SBNone
:
#endif
ChooseBoundary(event, t, rect, !bShiftDown, !bShiftDown);
#ifdef USE_MIDI
@ -1705,14 +1699,7 @@ void TrackPanel::HandleCursor(const wxMouseEvent & event)
tip = ttb->GetMessageForTool(tool);
const auto &scrubber = GetProject()->GetScrubber();
if (scrubber.HasStartedScrubbing()) {
if (scrubber.IsScrollScrubbing())
tip = _("Move to adjust speed, click to skip, ESC to stop.");
else
tip = _("Move to scrub, click to seek, ESC to stop.");
}
else if( tool != selectTool )
if( tool != selectTool )
{
// We don't include the select tool in
// SetCursorAndTipByTool() because it's more complex than
@ -1773,11 +1760,7 @@ void TrackPanel::HandleSelect(wxMouseEvent & event)
mFreqSelMode = FREQ_SEL_INVALID;
#endif
} else if (event.LeftDClick() && !event.ShiftDown()
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
&& !event.CmdDown()
#endif
) {
} else if (event.LeftDClick() && !event.ShiftDown()) {
if (!mCapturedTrack) {
wxRect rect;
mCapturedTrack =
@ -1950,29 +1933,9 @@ void TrackPanel::SelectionHandleClick(wxMouseEvent & event,
#endif
) {
#ifdef EXPERIMENTAL_SCRUBBING_BASIC
if (
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
event.LeftDClick() ||
#endif
event.LeftDown()) {
SetCapturedTrack(nullptr, IsUncaptured);
GetProject()->GetScrubber().MarkScrubStart(
event
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
, event.LeftDClick()
#endif
, false
);
return;
}
#else
// Used to jump the play head, but it is redundant with timeline quick play
// StartOrJumpPlayback(event);
#endif
// Not starting a drag
SetCapturedTrack(NULL, IsUncaptured);
return;
@ -2693,14 +2656,6 @@ void TrackPanel::Stretch(int mouseXCoordinate, int trackLeftEdge,
/// handle it here.
void TrackPanel::SelectionHandleDrag(wxMouseEvent & event, Track *clickedTrack)
{
#ifdef EXPERIMENTAL_SCRUBBING_BASIC
Scrubber &scrubber = GetProject()->GetScrubber();
if (scrubber.IsScrubbing() ||
GetProject()->GetScrubber().MaybeStartScrubbing(event))
// Do nothing more, don't change selection
return;
#endif
// AS: If we're not in the process of selecting (set in
// the SelectionHandleClick above), fuhggeddaboudit.
if (mMouseCapture!=IsSelecting)
@ -5518,6 +5473,13 @@ void TrackPanel::HandleResize(wxMouseEvent & event)
/// Handle mouse wheel rotation (for zoom in/out, vertical and horizontal scrolling)
void TrackPanel::HandleWheelRotation(wxMouseEvent & event)
{
if(!event.HasAnyModifiers()) {
// We will later un-skip if we do anything, but if we don't,
// propagate the event up for the sake of the scrubber
event.Skip();
event.ResumePropagation(wxEVENT_PROPAGATE_MAX);
}
if (GetTracks()->IsEmpty())
// Scrolling and Zoom in and out commands are disabled when there are no tracks.
// This should be disabled too for consistency. Otherwise
@ -5530,6 +5492,9 @@ void TrackPanel::HandleWheelRotation(wxMouseEvent & event)
Track *const pTrack = FindTrack(event.m_x, event.m_y, true, false, &rect);
if (pTrack && event.m_x >= GetVRulerOffset()) {
HandleWheelRotationInVRuler(event, pTrack, rect);
// Always stop propagation even if the ruler didn't change. The ruler
// is a narrow enough target.
event.Skip(false);
return;
}
}
@ -5619,7 +5584,8 @@ void TrackPanel::HandleWheelRotation(wxMouseEvent & event)
double lines = steps * 4 + mVertScrollRemainder;
mVertScrollRemainder = lines - floor(lines);
lines = floor(lines);
mListener->TP_ScrollUpDown((int)-lines);
const bool didSomething = mListener->TP_ScrollUpDown((int)-lines);
event.Skip(!didSomething);
}
}
}
@ -5907,6 +5873,13 @@ void TrackPanel::OnMouseEvent(wxMouseEvent & event)
if (event.m_wheelRotation != 0)
HandleWheelRotation(event);
if (event.LeftDown() || event.LeftIsDown() || event.Moving()) {
// Skip, even if we do something, so that the left click or drag
// may have an additional effect in the scrubber.
event.Skip();
event.ResumePropagation(wxEVENT_PROPAGATE_MAX);
}
if (!mAutoScrolling) {
mMouseMostRecentX = event.m_x;
mMouseMostRecentY = event.m_y;
@ -6366,20 +6339,6 @@ void TrackPanel::HandleTrackSpecificMouseEvent(wxMouseEvent & event)
return;
}
#ifdef EXPERIMENTAL_SCRUBBING_BASIC
if (GetProject()->GetScrubber().IsScrubbing() &&
GetRect().Contains(event.GetPosition()) &&
(!pTrack ||
pTrack->GetKind() == Track::Wave)) {
if (event.LeftDown()) {
GetProject()->GetScrubber().SetSeeking();
return;
}
else if (event.LeftIsDown())
return;
}
#endif
bool handled = false;
if (pTrack && (pTrack->GetKind() == Track::Wave) &&

View File

@ -34,7 +34,7 @@ class AUDACITY_DLL_API TrackPanelListener /* not final */ {
virtual void TP_ScrollLeft() = 0;
virtual void TP_ScrollRight() = 0;
virtual void TP_ScrollWindow(double scrollto) = 0;
virtual void TP_ScrollUpDown(int delta) = 0;
virtual bool TP_ScrollUpDown(int delta) = 0;
virtual void TP_HandleResize() = 0;
};

View File

@ -10,6 +10,7 @@
#define __AUDACITY_COMMAND_FUNCTORS__
#include <wx/string.h>
#include <wx/event.h>
#include "../MemoryX.h"
class wxEvent;
@ -61,6 +62,24 @@ private:
const audCommandKeyFunction<OBJ> mCommandKeyFunction;
};
// This allows functions to be used either by command manager or by a wxMenu popup,
// but the functions MUST ignore the argument!
template<typename OBJ>
using audCommandPopupFunction = void (OBJ::*)(wxCommandEvent&);
template<typename OBJ>
class PopupFunctor final : public CommandFunctor
{
public:
explicit PopupFunctor(OBJ *This, audCommandPopupFunction<OBJ> pfn)
: mThis{ This }, mCommandPopupFunction{ pfn } {}
void operator () (int, const wxEvent *) override
{ wxCommandEvent dummy; (mThis->*mCommandPopupFunction) (dummy); }
private:
OBJ *const mThis;
const audCommandPopupFunction<OBJ> mCommandPopupFunction;
};
template<typename OBJ>
using audCommandListFunction = void (OBJ::*)(int);
@ -108,6 +127,11 @@ inline CommandFunctorPointer MakeFunctor(OBJ *This,
audCommandKeyFunction<OBJ> pfn)
{ return CommandFunctorPointer{ safenew KeyFunctor<OBJ>{ This, pfn } }; }
template<typename OBJ>
inline CommandFunctorPointer MakeFunctor(OBJ *This,
audCommandPopupFunction<OBJ> pfn)
{ return CommandFunctorPointer{ safenew PopupFunctor<OBJ>{ This, pfn } }; }
template<typename OBJ>
inline CommandFunctorPointer MakeFunctor(OBJ *This,
audCommandListFunction<OBJ> pfn)

View File

@ -276,6 +276,7 @@ DEFINE_EVENT_TYPE(EVT_TOOLBAR_UPDATED)
BEGIN_EVENT_TABLE( ToolBar, wxPanel )
EVT_PAINT( ToolBar::OnPaint )
EVT_ERASE_BACKGROUND( ToolBar::OnErase )
EVT_MOUSE_EVENTS( ToolBar::OnMouseEvents )
END_EVENT_TABLE()
//
@ -841,6 +842,13 @@ void ToolBar::OnPaint( wxPaintEvent & event )
#endif
}
void ToolBar::OnMouseEvents(wxMouseEvent &event)
{
// Do this hack so scrubber can detect mouse drags anywhere
event.ResumePropagation(wxEVENT_PROPAGATE_MAX);
event.Skip();
}
int ToolBar::GetResizeGrabberWidth()
{
return RWIDTH;

View File

@ -188,6 +188,7 @@ class ToolBar /* not final */ : public wxPanel
void OnErase(wxEraseEvent & event);
void OnPaint(wxPaintEvent & event);
void OnMouseEvents(wxMouseEvent &event);
protected:
wxString mLabel;

View File

@ -64,6 +64,7 @@ BEGIN_EVENT_TABLE( ToolDock, wxPanel )
EVT_ERASE_BACKGROUND( ToolDock::OnErase )
EVT_PAINT( ToolDock::OnPaint )
EVT_SIZE( ToolDock::OnSize )
EVT_MOUSE_EVENTS( ToolDock::OnMouseEvents )
END_EVENT_TABLE()
//
@ -556,3 +557,10 @@ void ToolDock::OnPaint( wxPaintEvent & WXUNUSED(event) )
}
}
}
void ToolDock::OnMouseEvents(wxMouseEvent &event)
{
// Do this hack so scrubber can detect mouse drags anywhere
event.ResumePropagation(wxEVENT_PROPAGATE_MAX);
event.Skip();
}

View File

@ -70,6 +70,7 @@ class ToolDock final : public wxPanel
void OnSize( wxSizeEvent & event );
void OnPaint( wxPaintEvent & event );
void OnGrabber( GrabberEvent & event );
void OnMouseEvents(wxMouseEvent &event);
private:

View File

@ -55,9 +55,6 @@
#include "../Theme.h"
#include "../Experimental.h"
#ifdef EXPERIMENTAL_SCRUBBING_BASIC
#include "../tracks/ui/Scrubbing.h"
#endif
#include "../widgets/AButton.h"
@ -87,21 +84,7 @@ ToolsToolBar::ToolsToolBar()
wxASSERT( drawTool == drawTool - firstTool );
wxASSERT( multiTool == multiTool - firstTool );
{
#ifdef EXPERIMENTAL_SCRUBBING_BASIC
mMessageOfTool[selectTool] =
#if defined(__WXMAC__)
_("Click and drag to select audio, Command-Click to scrub, Command-Double-Click to scroll-scrub, Command-drag to seek")
#else
_("Click and drag to select audio, Ctrl-Click to scrub, Ctrl-Double-Click to scroll-scrub, Ctrl-drag to seek")
#endif
;
#else
mMessageOfTool[selectTool] = _("Click and drag to select audio");
#endif
}
mMessageOfTool[selectTool] = _("Click and drag to select audio");
mMessageOfTool[envelopeTool] = _("Click and drag to edit the amplitude envelope");
mMessageOfTool[drawTool] = _("Click and drag to edit the samples");
@ -219,14 +202,6 @@ void ToolsToolBar::SetCurrentTool(int tool, bool show)
//In multi-mode the current tool is shown by the
//cursor icon. The buttons are not updated.
#ifdef EXPERIMENTAL_SCRUBBING_BASIC
if (tool != selectTool) {
AudacityProject *const p = GetActiveProject();
if (p)
p->GetScrubber().StopScrubbing();
}
#endif
bool leavingMulticlipMode =
IsDown(multiTool) && show && tool != multiTool;
@ -290,14 +265,6 @@ void ToolsToolBar::OnTool(wxCommandEvent & evt)
else
mTool[i]->PopUp();
#ifdef EXPERIMENTAL_SCRUBBING_BASIC
if (0 != mCurrentTool) {
AudacityProject *const p = GetActiveProject();
if (p)
p->GetScrubber().StopScrubbing();
}
#endif
RedrawAllProjects();
gPrefs->Write(wxT("/GUI/ToolBars/Tools/MultiToolActive"),

View File

@ -19,6 +19,7 @@ Paul Licameli split from TrackPanel.cpp
#include "../../TrackPanelCellIterator.h"
#include "../../commands/CommandFunctors.h"
#include "../../toolbars/ControlToolBar.h"
#include "../../widgets/Ruler.h"
#include <algorithm>
@ -126,10 +127,12 @@ Scrubber::Scrubber(AudacityProject *project)
wxTheApp->Connect
(wxEVT_ACTIVATE_APP,
wxActivateEventHandler(Scrubber::OnActivateOrDeactivateApp), NULL, this);
mProject->PushEventHandler(&mForwarder);
}
Scrubber::~Scrubber()
{
mProject->PopEventHandler();
if (wxTheApp)
wxTheApp->Disconnect
(wxEVT_ACTIVATE_APP,
@ -141,7 +144,7 @@ namespace {
wxString name;
wxString label;
wxString status;
void (Scrubber::*memFn)();
void (Scrubber::*memFn)(wxCommandEvent&);
bool scroll;
bool seek;
@ -180,7 +183,8 @@ namespace {
}
void Scrubber::MarkScrubStart(
const wxMouseEvent &event
// Assume xx is relative to the left edge of TrackPanel!
wxCoord xx
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
, bool smoothScrolling
#endif
@ -189,8 +193,6 @@ void Scrubber::MarkScrubStart(
{
UncheckAllMenuItems();
const wxCoord xx = event.m_x;
// Don't actually start scrubbing, but collect some information
// needed for the decision to start scrubbing later when handling
// drag events.
@ -204,13 +206,13 @@ void Scrubber::MarkScrubStart(
ControlToolBar * const ctb = mProject->GetControlToolBar();
ctb->SetPlay(true, ControlToolBar::PlayAppearance::Scrub);
ctb->UpdateStatusBar(mProject);
mProject->GetTrackPanel()->HandleCursor(event);
CheckMenuItem();
}
#ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
bool Scrubber::MaybeStartScrubbing(const wxMouseEvent &event)
// Assume xx is relative to the left edge of TrackPanel!
bool Scrubber::MaybeStartScrubbing(wxCoord xx)
{
if (mScrubStartPosition < 0)
return false;
@ -225,7 +227,7 @@ bool Scrubber::MaybeStartScrubbing(const wxMouseEvent &event)
return false;
}
wxCoord position = event.m_x;
wxCoord position = xx;
if (abs(mScrubStartPosition - position) >= SCRUBBING_PIXEL_TOLERANCE) {
const ViewInfo &viewInfo = mProject->GetViewInfo();
TrackPanel *const trackPanel = mProject->GetTrackPanel();
@ -299,9 +301,11 @@ void Scrubber::ContinueScrubbing()
// Seek only when the pointer is in the panel. Else, scrub.
const wxMouseState state(::wxGetMouseState());
TrackPanel *const trackPanel = mProject->GetTrackPanel();
const wxPoint position = trackPanel->ScreenToClient(state.GetPosition());
const bool inPanel = trackPanel->GetRect().Contains(position);
const bool seek = inPanel && (mScrubSeekPress || PollIsSeeking());
// Decide whether to skip play, because either mouse is down now,
// or there was a left click event. (This is then a delayed reaction, in a
// timer callback, to a left click event detected elsewhere.)
const bool seek = PollIsSeeking() || mScrubSeekPress;
{
// Show the correct status for seeking.
@ -312,6 +316,7 @@ void Scrubber::ContinueScrubbing()
mAlwaysSeeking = backup;
}
const wxPoint position = trackPanel->ScreenToClient(state.GetPosition());
// When we don't have focus, enqueue silent scrubs until we regain focus.
bool result = false;
if (!mScrubHasFocus)
@ -450,6 +455,39 @@ void Scrubber::OnActivateOrDeactivateApp(wxActivateEvent &event)
event.Skip();
}
void Scrubber::Forwarder::OnMouse(wxMouseEvent &event)
{
auto isScrubbing = scrubber.IsScrubbing();
if (!isScrubbing && scrubber.HasStartedScrubbing()) {
if (!event.HasAnyModifiers() &&
event.GetEventType() == wxEVT_MOTION) {
// Really start scrub if motion is far enough
auto ruler = scrubber.mProject->GetRulerPanel();
auto xx = ruler->ScreenToClient(::wxGetMousePosition()).x;
scrubber.MaybeStartScrubbing(xx);
}
}
else if (isScrubbing && !event.HasAnyModifiers()) {
if(event.LeftDown() ||
(event.LeftIsDown() && event.Dragging())) {
scrubber.mScrubSeekPress = true;
auto ruler = scrubber.mProject->GetRulerPanel();
auto xx = ruler->ScreenToClient(::wxGetMousePosition()).x;
ruler->UpdateQuickPlayPos(xx);
}
else if (event.m_wheelRotation) {
double steps = event.m_wheelRotation /
(event.m_wheelDelta > 0 ? (double)event.m_wheelDelta : 120.0);
scrubber.HandleScrollWheel(steps);
}
else
event.Skip();
}
else
event.Skip();
}
///////////////////////////////////////////////////////////////////////////////
// class ScrubbingOverlay is responsible for drawing the speed numbers
@ -615,9 +653,7 @@ void Scrubber::DoScrub(bool scroll, bool seek)
if (!wasScrubbing) {
auto tp = mProject->GetTrackPanel();
wxCoord xx = tp->ScreenToClient(::wxGetMouseState().GetPosition()).x;
wxMouseEvent evt;
evt.SetX(xx);
MarkScrubStart(evt, scroll, seek);
MarkScrubStart(xx, scroll, seek);
}
else if(!match) {
mSmoothScrollingScrub = scroll;
@ -636,26 +672,41 @@ void Scrubber::DoScrub(bool scroll, bool seek)
}
}
void Scrubber::OnScrub()
void Scrubber::OnScrub(wxCommandEvent&)
{
DoScrub(false, false);
}
void Scrubber::OnScrollScrub()
void Scrubber::OnScrollScrub(wxCommandEvent&)
{
DoScrub(true, false);
}
void Scrubber::OnSeek()
void Scrubber::OnSeek(wxCommandEvent&)
{
DoScrub(false, true);
}
void Scrubber::OnScrollSeek()
void Scrubber::OnScrollSeek(wxCommandEvent&)
{
DoScrub(true, true);
}
enum { CMD_ID = 8000 };
BEGIN_EVENT_TABLE(Scrubber, wxEvtHandler)
EVT_MENU(CMD_ID, Scrubber::OnScrub)
EVT_MENU(CMD_ID + 1, Scrubber::OnScrollScrub)
EVT_MENU(CMD_ID + 2, Scrubber::OnSeek)
EVT_MENU(CMD_ID + 3, Scrubber::OnScrollSeek)
END_EVENT_TABLE()
BEGIN_EVENT_TABLE(Scrubber::Forwarder, wxEvtHandler)
EVT_MOUSE_EVENTS(Scrubber::Forwarder::OnMouse)
END_EVENT_TABLE()
static_assert(nMenuItems == 4, "wrong number of items");
const wxString &Scrubber::GetUntranslatedStateString() const
{
static wxString empty;
@ -677,6 +728,12 @@ std::vector<wxString> Scrubber::GetAllUntranslatedStatusStrings()
return move(results);
}
bool Scrubber::CanScrub() const
{
auto cm = mProject->GetCommandManager();
return cm->GetEnabled(menuItems[0].name);
}
void Scrubber::AddMenuItems()
{
auto cm = mProject->GetCommandManager();
@ -693,6 +750,17 @@ void Scrubber::AddMenuItems()
CheckMenuItem();
}
void Scrubber::PopulateMenu(wxMenu &menu)
{
int id = CMD_ID;
auto cm = mProject->GetCommandManager();
for (const auto &item : menuItems) {
if (cm->GetEnabled(item.name))
menu.Append(id, item.label);
++id;
}
}
void Scrubber::UncheckAllMenuItems()
{
auto cm = mProject->GetCommandManager();

View File

@ -27,16 +27,20 @@ public:
Scrubber(AudacityProject *project);
~Scrubber();
// Assume xx is relative to the left edge of TrackPanel!
void MarkScrubStart(
const wxMouseEvent &event
wxCoord xx
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
, bool smoothScrolling
#endif
, bool alwaysSeeking // if false, can switch seeking or scrubbing
// by mouse button state
);
// Returns true iff the event should be considered consumed by this:
bool MaybeStartScrubbing(const wxMouseEvent &event);
// Assume xx is relative to the left edge of TrackPanel!
bool MaybeStartScrubbing(wxCoord xx);
void ContinueScrubbing();
// This is meant to be called only from ControlToolBar
@ -52,6 +56,8 @@ public:
bool IsScrubbing() const;
bool IsScrollScrubbing() const // If true, implies HasStartedScrubbing()
{ return mSmoothScrollingScrub; }
bool IsAlwaysSeeking() const
{ return mAlwaysSeeking; }
bool ShouldDrawScrubSpeed();
double FindScrubSpeed(bool seeking, double time) const;
@ -59,15 +65,20 @@ public:
void HandleScrollWheel(int steps);
void SetSeeking() { mScrubSeekPress = true; }
bool PollIsSeeking();
void AddMenuItems();
// This returns the same as the enabled state of the menu items:
bool CanScrub() const;
void OnScrub();
void OnScrollScrub();
void OnSeek();
void OnScrollSeek();
// For the toolbar
void AddMenuItems();
// For popup
void PopulateMenu(wxMenu &menu);
void OnScrub(wxCommandEvent&);
void OnScrollScrub(wxCommandEvent&);
void OnSeek(wxCommandEvent&);
void OnScrollSeek(wxCommandEvent&);
// A string to put in the leftmost part of the status bar.
const wxString &GetUntranslatedStateString() const;
@ -81,6 +92,18 @@ private:
void UncheckAllMenuItems();
void CheckMenuItem();
// I need this because I can't push the scrubber as an event handler
// in two places at once.
struct Forwarder : public wxEvtHandler {
Forwarder(Scrubber &scrubber_) : scrubber{ scrubber_ } {}
Scrubber &scrubber;
void OnMouse(wxMouseEvent &event);
DECLARE_EVENT_TABLE()
};
Forwarder mForwarder{ *this };
private:
int mScrubToken;
wxLongLong mScrubStartClockTimeMillis;
@ -97,6 +120,8 @@ private:
#endif
AudacityProject *mProject;
DECLARE_EVENT_TABLE()
};
// Specialist in drawing the scrub speed, and listening for certain events

View File

@ -452,6 +452,8 @@ void AButton::OnMouseEvent(wxMouseEvent & event)
GetActiveProject()->TP_DisplayStatusMessage(wxT(""));
}
}
else
event.Skip();
}
void AButton::OnCaptureLost(wxMouseCaptureLostEvent & WXUNUSED(event))

View File

@ -96,6 +96,8 @@ using std::max;
#define kTopInset 4
wxColour Ruler::mTickColour{ 153, 153, 153 };
//
// Ruler
//
@ -120,7 +122,6 @@ Ruler::Ruler()
mBottom = -1;
mbTicksOnly = true;
mbTicksAtExtremes = false;
mTickColour = wxColour(153,153,153);
mPen.SetColour(mTickColour);
// Note: the font size is now adjusted automatically whenever
@ -1572,7 +1573,7 @@ void Ruler::Label::Draw(wxDC&dc, bool twoTone) const
#ifdef EXPERIMENTAL_THEMING
// TODO: handle color distinction
mDC->SetTextForeground(mTickColour);
dc.SetTextForeground(mTickColour);
#else
dc.SetTextForeground(altColor ? *wxBLUE : *wxBLACK);
#endif
@ -1717,8 +1718,6 @@ void QuickPlayIndicatorOverlay::Draw
if (mOldQPIndicatorPos >= 0) {
mOldPreviewingScrub
? AColor::IndicatorColor(&dc, true) // Draw green line for preview.
// Drawing during actual scrub not by this class,
// but by PlayIndicatorOverlay
: mOldQPIndicatorSnapped
? AColor::SnapGuidePen(&dc)
: AColor::Light(&dc, false)
@ -2061,14 +2060,61 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt)
if (mIsRecording)
return;
const bool inScrubZone =
// only if scrubbing is allowed now
mProject->GetScrubber().CanScrub() &&
evt.m_y < IndicatorBigHeight();
const bool changeInScrubZone = (inScrubZone != mPrevInScrubZone);
mPrevInScrubZone = inScrubZone;
auto &scrubber = mProject->GetScrubber();
// Handle status bar messages
if(evt.Leaving()) {
mProject->TP_DisplayStatusMessage(wxT(""));
UpdateStatusBar (
evt.Leaving()
? StatusChoice::Leaving
: evt.Entering() || changeInScrubZone
? inScrubZone
? StatusChoice::EnteringScrubZone
: StatusChoice::EnteringQP
: StatusChoice::NoChange
);
double t0 = mTracks->GetStartTime();
double t1 = mTracks->GetEndTime();
double sel0 = mProject->GetSel0();
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);
}
else if(evt.Entering()) {
// Insert timeline status bar messages here
mProject->TP_DisplayStatusMessage
(wxT(""));
if (scrubber.HasStartedScrubbing()) {
// If already clicked for scrub, preempt the usual event handling,
// no matter what the y coordinate.
// Do this hack so scrubber can detect mouse drags anywhere
evt.ResumePropagation(wxEVENT_PROPAGATE_MAX);
if (scrubber.IsScrubbing())
evt.Skip();
else if (evt.LeftDClick())
// On the second button down, switch the pending scrub to scrolling
scrubber.MarkScrubStart(evt.m_x, true, false);
else
evt.Skip();
mQuickPlayInd = true;
wxClientDC dc(this);
DrawQuickPlayIndicator(&dc);
return;
}
// Store the initial play region state
@ -2078,34 +2124,18 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt)
mPlayRegionLock = mProject->IsPlayRegionLocked();
}
// Keep Quick-Play within usable track area.
TrackPanel *tp = mProject->GetTrackPanel();
int mousePosX, width;
tp->GetTracksUsableArea(&width, NULL);
mousePosX = std::max(evt.GetX(), tp->GetLeftOffset());
mousePosX = std::min(mousePosX, tp->GetLeftOffset() + width - 1);
bool isWithinStart = IsWithinMarker(mousePosX, mOldPlayRegionStart);
bool isWithinEnd = IsWithinMarker(mousePosX, mOldPlayRegionEnd);
bool isWithinClick = (mLeftDownClick >= 0) && IsWithinMarker(mousePosX, mLeftDownClick);
bool canDragSel = !mPlayRegionLock && mPlayRegionDragsSelection;
double t0 = mTracks->GetStartTime();
double t1 = mTracks->GetEndTime();
double sel0 = mProject->GetSel0();
double sel1 = mProject->GetSel1();
mLastMouseX = mousePosX;
mQuickPlayPos = Pos2Time(mousePosX);
// If not looping, restrict selection to end of project
if (!evt.ShiftDown()) {
mQuickPlayPos = std::min(t1, mQuickPlayPos);
}
if (evt.Leaving()) {
mQuickPlayInd = false;
DrawQuickPlayIndicator(NULL);
Refresh();
// Handle entering and leaving of the bar, or movement from
// one portion (quick play or scrub) to the other
if (evt.Leaving() || (changeInScrubZone && inScrubZone)) {
if (evt.Leaving()) {
// Erase the line
HideQuickPlayIndicator();
}
SetCursor(mCursorDefault);
mIsWE = false;
@ -2114,19 +2144,37 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt)
delete mSnapManager;
mSnapManager = NULL;
}
return;
if(evt.Leaving())
return;
// else, may detect a scrub click below
}
else if (evt.Entering()) {
else if (evt.Entering() || (changeInScrubZone && !inScrubZone)) {
SetCursor(mCursorHand);
mQuickPlayInd = false;
DrawQuickPlayIndicator(NULL);
HideQuickPlayIndicator();
return;
}
if (evt.RightDown() && !(evt.LeftIsDown())) {
ShowMenu(evt.GetPosition());
if(inScrubZone)
ShowScrubMenu(evt.GetPosition());
else
ShowMenu(evt.GetPosition());
// dismiss and clear Quick-Play indicator
HideQuickPlayIndicator();
if (HasCapture())
ReleaseMouse();
return;
}
else if (inScrubZone) {
if (evt.LeftDown())
scrubber.MarkScrubStart(evt.m_x, false, false);
UpdateStatusBar(StatusChoice::EnteringScrubZone);
wxClientDC dc(this);
DrawQuickPlayIndicator(&dc);
return;
}
if (!mQuickPlayEnabled)
@ -2277,8 +2325,7 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt)
if (evt.LeftUp())
{
mQuickPlayInd = false;
DrawQuickPlayIndicator(NULL);
HideQuickPlayIndicator();
if (HasCapture())
ReleaseMouse();
@ -2377,6 +2424,45 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt)
}
}
void AdornedRulerPanel::UpdateStatusBar(StatusChoice choice)
{
if (choice == StatusChoice::NoChange)
return;
const auto &scrubber = mProject->GetScrubber();
const bool scrubbing = scrubber.HasStartedScrubbing();
if (scrubbing && choice != StatusChoice::Leaving)
// Don't distinguish zones
choice = StatusChoice::EnteringScrubZone;
wxString message{};
switch (choice) {
case StatusChoice::EnteringQP:
{
// message = Insert timeline status bar message here
}
break;
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");
}
break;
case StatusChoice::Leaving:
default:
break;
}
// Display a message, or empty message
mProject->TP_DisplayStatusMessage(message);
}
void AdornedRulerPanel::OnCaptureLost(wxMouseCaptureLostEvent & WXUNUSED(evt))
{
DrawQuickPlayIndicator(NULL);
@ -2386,52 +2472,68 @@ void AdornedRulerPanel::OnCaptureLost(wxMouseCaptureLostEvent & WXUNUSED(evt))
OnMouseEvents(e);
}
// Pop-up menu
void AdornedRulerPanel::UpdateQuickPlayPos(wxCoord &mousePosX)
{
// Keep Quick-Play within usable track area.
TrackPanel *tp = mProject->GetTrackPanel();
int width;
tp->GetTracksUsableArea(&width, NULL);
mousePosX = std::max(mousePosX, tp->GetLeftOffset());
mousePosX = std::min(mousePosX, tp->GetLeftOffset() + width - 1);
mLastMouseX = mousePosX;
mQuickPlayPos = Pos2Time(mousePosX);
}
// Pop-up menus
void AdornedRulerPanel::ShowMenu(const wxPoint & pos)
{
{
wxMenu rulerMenu;
wxMenu rulerMenu;
if (mQuickPlayEnabled)
rulerMenu.Append(OnToggleQuickPlayID, _("Disable Quick-Play"));
else
rulerMenu.Append(OnToggleQuickPlayID, _("Enable Quick-Play"));
if (mQuickPlayEnabled)
rulerMenu.Append(OnToggleQuickPlayID, _("Disable Quick-Play"));
else
rulerMenu.Append(OnToggleQuickPlayID, _("Enable Quick-Play"));
wxMenuItem *dragitem;
if (mPlayRegionDragsSelection && !mProject->IsPlayRegionLocked())
dragitem = rulerMenu.Append(OnSyncQuickPlaySelID, _("Disable dragging selection"));
else
dragitem = rulerMenu.Append(OnSyncQuickPlaySelID, _("Enable dragging selection"));
dragitem->Enable(mQuickPlayEnabled && !mProject->IsPlayRegionLocked());
wxMenuItem *dragitem;
if (mPlayRegionDragsSelection && !mProject->IsPlayRegionLocked())
dragitem = rulerMenu.Append(OnSyncQuickPlaySelID, _("Disable dragging selection"));
else
dragitem = rulerMenu.Append(OnSyncQuickPlaySelID, _("Enable dragging selection"));
dragitem->Enable(mQuickPlayEnabled && !mProject->IsPlayRegionLocked());
#if wxUSE_TOOLTIPS
if (mTimelineToolTip)
rulerMenu.Append(OnTimelineToolTipID, _("Disable Timeline Tooltips"));
else
rulerMenu.Append(OnTimelineToolTipID, _("Enable Timeline Tooltips"));
if (mTimelineToolTip)
rulerMenu.Append(OnTimelineToolTipID, _("Disable Timeline Tooltips"));
else
rulerMenu.Append(OnTimelineToolTipID, _("Enable Timeline Tooltips"));
#endif
if (mViewInfo->bUpdateTrackIndicator)
rulerMenu.Append(OnAutoScrollID, _("Do not scroll while playing"));
else
rulerMenu.Append(OnAutoScrollID, _("Update display while playing"));
if (mViewInfo->bUpdateTrackIndicator)
rulerMenu.Append(OnAutoScrollID, _("Do not scroll while playing"));
else
rulerMenu.Append(OnAutoScrollID, _("Update display while playing"));
wxMenuItem *prlitem;
if (!mProject->IsPlayRegionLocked())
prlitem = rulerMenu.Append(OnLockPlayRegionID, _("Lock Play Region"));
else
prlitem = rulerMenu.Append(OnLockPlayRegionID, _("Unlock Play Region"));
prlitem->Enable(mProject->IsPlayRegionLocked() || (mPlayRegionStart != mPlayRegionEnd));
wxMenuItem *prlitem;
if (!mProject->IsPlayRegionLocked())
prlitem = rulerMenu.Append(OnLockPlayRegionID, _("Lock Play Region"));
else
prlitem = rulerMenu.Append(OnLockPlayRegionID, _("Unlock Play Region"));
prlitem->Enable(mProject->IsPlayRegionLocked() || (mPlayRegionStart != mPlayRegionEnd));
PopupMenu(&rulerMenu, pos);
}
PopupMenu(&rulerMenu, pos);
}
// dismiss and clear Quick-Play indicator
mQuickPlayInd = false;
DrawQuickPlayIndicator(NULL);
void AdornedRulerPanel::ShowScrubMenu(const wxPoint & pos)
{
auto &scrubber = mProject->GetScrubber();
PushEventHandler(&scrubber);
auto cleanup = finally([this]{ PopEventHandler(); });
Refresh();
wxMenu rulerMenu;
mProject->GetScrubber().PopulateMenu(rulerMenu);
PopupMenu(&rulerMenu, pos);
}
void AdornedRulerPanel::OnToggleQuickPlay(wxCommandEvent&)
@ -2745,7 +2847,10 @@ void AdornedRulerPanel::DrawQuickPlayIndicator(wxDC * dc)
}
const int x = Time2Pos(mQuickPlayPos);
GetOverlay()->Update(x, mIsSnapped, mPrevInScrubZone);
bool previewScrub =
mPrevInScrubZone &&
!mProject->GetScrubber().IsScrubbing();
GetOverlay()->Update(x, mIsSnapped, previewScrub);
DoEraseIndicator(dc, mLastQuickPlayX);
mLastQuickPlayX = x;

View File

@ -168,7 +168,7 @@ public:
wxRect mRect;
private:
wxColour mTickColour;
static wxColour mTickColour;
wxPen mPen;
int mMaxWidth, mMaxHeight;
@ -312,11 +312,22 @@ public:
void RegenerateTooltips();
void HideQuickPlayIndicator();
void UpdateQuickPlayPos(wxCoord &mousPosX);
private:
void OnCapture(wxCommandEvent & evt);
void OnPaint(wxPaintEvent &evt);
void OnSize(wxSizeEvent &evt);
void OnMouseEvents(wxMouseEvent &evt);
enum class StatusChoice {
EnteringQP,
EnteringScrubZone,
Leaving,
NoChange
};
void UpdateStatusBar(StatusChoice choice);
void OnCaptureLost(wxMouseCaptureLostEvent &evt);
void DoDrawBorder(wxDC * dc);
@ -381,6 +392,7 @@ private:
// Pop-up menu
//
void ShowMenu(const wxPoint & pos);
void ShowScrubMenu(const wxPoint & pos);
void DragSelection();
void HandleSnapping();
void OnToggleQuickPlay(wxCommandEvent &evt);