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

Control scrub with motion, click, drag, wheel almost anywhere in main window...

... if the event is not handled and skipped by sub-windows first, such as for
toolbar button clicks.

(But track panel clicks are skipped even after doing something, so they may
also cause seeking besides other responses.  So click can seek AND set cursor.)

This is meant to make drag to seek and wheel for change of speed easier,
without needing to keep the mouse in the narrow time ruler.

Also lets you click in the ruler, then move in any direction, and not miss the
motion event that should start the scrub playback.

The event handling is a bit of a hack, using propagation.  It does not use
capture.
This commit is contained in:
Paul Licameli 2016-04-21 20:59:38 -04:00
parent 3d222bcd87
commit 9ab0e42f29
13 changed files with 120 additions and 25 deletions

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

@ -5473,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
@ -5485,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;
}
}
@ -5574,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);
}
}
}
@ -5862,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;

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

@ -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

@ -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(this);
}
Scrubber::~Scrubber()
{
mProject->PopEventHandler();
if (wxTheApp)
wxTheApp->Disconnect
(wxEVT_ACTIVATE_APP,
@ -180,6 +183,7 @@ namespace {
}
void Scrubber::MarkScrubStart(
// Assume xx is relative to the left edge of TrackPanel!
wxCoord xx
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
, bool smoothScrolling
@ -207,7 +211,8 @@ void Scrubber::MarkScrubStart(
}
#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;
@ -222,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();
@ -450,6 +455,40 @@ void Scrubber::OnActivateOrDeactivateApp(wxActivateEvent &event)
event.Skip();
}
void Scrubber::OnMouse(wxMouseEvent &event)
{
auto isScrubbing = IsScrubbing();
if (!isScrubbing && HasStartedScrubbing()) {
if (!event.HasAnyModifiers() &&
event.GetEventType() == wxEVT_MOTION) {
// Really start scrub if motion is far enough
auto ruler = mProject->GetRulerPanel();
auto xx = ruler->ScreenToClient(::wxGetMousePosition()).x;
MaybeStartScrubbing(xx
);
}
}
else if (isScrubbing && !event.HasAnyModifiers()) {
if(event.LeftDown() ||
(event.LeftIsDown() && event.Dragging())) {
mScrubSeekPress = true;
auto ruler = 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);
HandleScrollWheel(steps);
}
else
event.Skip();
}
else
event.Skip();
}
///////////////////////////////////////////////////////////////////////////////
// class ScrubbingOverlay is responsible for drawing the speed numbers
@ -661,6 +700,9 @@ BEGIN_EVENT_TABLE(Scrubber, wxEvtHandler)
EVT_MENU(CMD_ID + 1, Scrubber::OnScrollScrub)
EVT_MENU(CMD_ID + 2, Scrubber::OnSeek)
EVT_MENU(CMD_ID + 3, Scrubber::OnScrollSeek)
EVT_MOUSE_EVENTS(Scrubber::OnMouse)
END_EVENT_TABLE()
static_assert(nMenuItems == 4, "wrong number of items");

View File

@ -27,6 +27,7 @@ public:
Scrubber(AudacityProject *project);
~Scrubber();
// Assume xx is relative to the left edge of TrackPanel!
void MarkScrubStart(
wxCoord xx
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
@ -35,8 +36,11 @@ public:
, 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
@ -61,7 +65,6 @@ public:
void HandleScrollWheel(int steps);
void SetSeeking() { mScrubSeekPress = true; }
bool PollIsSeeking();
// This returns the same as the enabled state of the menu items:
@ -88,6 +91,7 @@ private:
void OnActivateOrDeactivateApp(wxActivateEvent & event);
void UncheckAllMenuItems();
void CheckMenuItem();
void OnMouse(wxMouseEvent &event);
private:
int mScrubToken;

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

@ -2080,20 +2080,15 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt)
: StatusChoice::NoChange
);
// 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);
double t0 = mTracks->GetStartTime();
double t1 = mTracks->GetEndTime();
double sel0 = mProject->GetSel0();
double sel1 = mProject->GetSel1();
mLastMouseX = mousePosX;
mQuickPlayPos = Pos2Time(mousePosX);
wxCoord mousePosX = evt.GetX();
UpdateQuickPlayPos(mousePosX);
// If not looping, restrict selection to end of project
if (!inScrubZone && !evt.ShiftDown()) {
mQuickPlayPos = std::min(t1, mQuickPlayPos);
@ -2103,18 +2098,16 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt)
// If already clicked for scrub, preempt the usual event handling,
// no matter what the y coordinate.
if (scrubber.IsScrubbing()) {
if(evt.LeftDown() || evt.Dragging())
// Cause scrub in progress to jump
scrubber.SetSeeking();
}
// 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 if (!evt.Button(wxMOUSE_BTN_ANY)) {
// Really start scrub if motion is far enough
scrubber.MaybeStartScrubbing(evt);
}
else
evt.Skip();
mQuickPlayInd = true;
wxClientDC dc(this);
@ -2478,6 +2471,19 @@ void AdornedRulerPanel::OnCaptureLost(wxMouseCaptureLostEvent & WXUNUSED(evt))
OnMouseEvents(e);
}
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)

View File

@ -312,6 +312,8 @@ public:
void RegenerateTooltips();
void HideQuickPlayIndicator();
void UpdateQuickPlayPos(wxCoord &mousPosX);
private:
void OnCapture(wxCommandEvent & evt);
void OnPaint(wxPaintEvent &evt);