1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-09-18 09:00:52 +02:00

TrackPanel simplifications; improved cursor handling...

... in cases such as:  dragging an envelope point, then hitting space to play,
which forces the drag to finish early.  If you move the mouse, cursor will
remain "ban", even while the mouse button remains down but no drag is really
happening.
This commit is contained in:
Paul Licameli 2017-06-08 17:27:41 -04:00
parent 114f5a4a63
commit d9a91c1431
3 changed files with 139 additions and 159 deletions

View File

@ -661,12 +661,7 @@ void TrackPanel::MakeParentRedrawScrollbars()
void TrackPanel::HandleInterruptedDrag() void TrackPanel::HandleInterruptedDrag()
{ {
bool sendEvent = true; if (mUIHandle && mUIHandle->StopsOnKeystroke() ) {
if (mUIHandle)
sendEvent = mUIHandle->StopsOnKeystroke();
if (sendEvent) {
// The bogus id isn't used anywhere, but may help with debugging. // The bogus id isn't used anywhere, but may help with debugging.
// as this is sending a bogus mouse up. The mouse button is still actually down // as this is sending a bogus mouse up. The mouse button is still actually down
// and may go up again. // and may go up again.
@ -751,6 +746,13 @@ namespace
} }
} }
void TrackPanel::Uncapture(wxMouseEvent *pEvent)
{
if (HasCapture())
ReleaseMouse();
HandleCursor( pEvent );
}
void TrackPanel::CancelDragging() void TrackPanel::CancelDragging()
{ {
if (mUIHandle) { if (mUIHandle) {
@ -760,15 +762,13 @@ void TrackPanel::CancelDragging()
// when the undo stack management of the typical Cancel override // when the undo stack management of the typical Cancel override
// causes it to relocate. That is implement some means to // causes it to relocate. That is implement some means to
// re-fetch the track according to its position in the list. // re-fetch the track according to its position in the list.
// (Or should all Tracks be managed always by std::shared_ptr?)
mpClickedTrack = NULL; mpClickedTrack = NULL;
} }
ProcessUIHandleResult(this, mRuler, mpClickedTrack, NULL, refreshResult); ProcessUIHandleResult(this, mRuler, mpClickedTrack, NULL, refreshResult);
mpClickedTrack = NULL; mpClickedTrack = NULL;
mUIHandle = NULL; mUIHandle = NULL;
if (HasCapture()) Uncapture();
ReleaseMouse();
wxMouseEvent dummy;
HandleCursor(dummy);
} }
} }
@ -817,7 +817,8 @@ void TrackPanel::HandlePageDownKey()
void TrackPanel::HandleCursorForLastMouseEvent() void TrackPanel::HandleCursorForLastMouseEvent()
{ {
HandleCursor(mLastMouseEvent); // Come here on modifier key transitions and change the cursor appropriately.
HandleCursor( &mLastMouseEvent );
} }
bool TrackPanel::IsAudioActive() bool TrackPanel::IsAudioActive()
@ -830,47 +831,71 @@ bool TrackPanel::IsAudioActive()
/// TrackPanel::HandleCursor( ) sets the cursor drawn at the mouse location. /// TrackPanel::HandleCursor( ) sets the cursor drawn at the mouse location.
/// As this procedure checks which region the mouse is over, it is /// As this procedure checks which region the mouse is over, it is
/// appropriate to establish the message in the status bar. /// appropriate to establish the message in the status bar.
void TrackPanel::HandleCursor(wxMouseEvent & event) void TrackPanel::HandleCursor( wxMouseEvent *pEvent )
{ {
mLastMouseEvent = event; wxMouseEvent dummy;
if (!pEvent)
pEvent = &dummy;
else
mLastMouseEvent = *pEvent;
auto &event = *pEvent;
const auto foundCell = FindCell( event.m_x, event.m_y ); const auto foundCell = FindCell( event.m_x, event.m_y );
auto &track = foundCell.pTrack; auto &track = foundCell.pTrack;
auto &rect = foundCell.rect; auto &rect = foundCell.rect;
auto &pCell = foundCell.pCell; auto &pCell = foundCell.pCell;
wxCursor *pCursor = NULL; const auto size = GetSize();
const TrackPanelMouseEvent tpmEvent{ event, rect, size, pCell };
HandleCursor( tpmEvent, foundCell.type );
}
wxString tip; void TrackPanel::HandleCursor
( const TrackPanelMouseEvent &tpmEvent, CellType cellType )
// tip may still be NULL at this point, in which case we go on looking. {
if ( mUIHandle ) {
// Are we within the vertical resize area? // UIHANDLE PREVIEW
// (Add margin back to bottom of the rectangle) // Update status message and cursor during drag
if (track && HitTestPreview preview = mUIHandle->Preview( tpmEvent, GetProject() );
within(event.m_y, rect.GetBottom() + kBorderThickness, TRACK_RESIZE_REGION)) mListener->TP_DisplayStatusMessage( preview.message );
{ if ( preview.cursor )
HitTestPreview preview SetCursor( *preview.cursor );
(TrackPanelResizeHandle::HitPreview(
(foundCell.type != CellType::Label) && track->GetLinked()));
tip = preview.message;
wxCursor *const pCursor = preview.cursor;
if (pCursor)
SetCursor(*pCursor);
} }
else {
wxCursor *pCursor = NULL;
if (pCell && pCursor == NULL && tip == wxString()) { wxString tip;
const auto size = GetSize();
HitTestResult hitTest(pCell->HitTest // Are we within the vertical resize area?
(TrackPanelMouseEvent{ event, rect, size, pCell }, GetProject())); // (Add margin back to bottom of the rectangle)
tip = hitTest.preview.message; auto &event = tpmEvent.event;
ProcessUIHandleResult(this, mRuler, track, track, hitTest.preview.refreshCode); auto pCell = tpmEvent.pCell;
pCursor = hitTest.preview.cursor; auto track = static_cast<CommonTrackPanelCell*>( pCell )->FindTrack();
if (pCursor) auto &rect = tpmEvent.rect;
SetCursor(*pCursor); if (track &&
within(event.m_y, rect.GetBottom() + kBorderThickness, TRACK_RESIZE_REGION))
{
HitTestPreview preview
( TrackPanelResizeHandle::HitPreview(
( cellType != CellType::Label) && track->GetLinked() ) );
tip = preview.message;
wxCursor *const pCursor = preview.cursor;
if (pCursor)
SetCursor(*pCursor);
}
if (pCell && pCursor == NULL && tip == wxString()) {
const auto size = GetSize();
HitTestResult hitTest( pCell->HitTest(tpmEvent, GetProject()) );
tip = hitTest.preview.message;
ProcessUIHandleResult(this, mRuler, track, track, hitTest.preview.refreshCode);
pCursor = hitTest.preview.cursor;
if (pCursor)
SetCursor(*pCursor);
}
if (pCursor != NULL || tip != wxString())
mListener->TP_DisplayStatusMessage(tip);
} }
if (pCursor != NULL || tip != wxString())
mListener->TP_DisplayStatusMessage(tip);
} }
void TrackPanel::UpdateSelectionDisplay() void TrackPanel::UpdateSelectionDisplay()
@ -931,7 +956,7 @@ void TrackPanel::MessageForScreenReader(const wxString& message)
mAx->MessageForScreenReader(message); mAx->MessageForScreenReader(message);
} }
/// Determines if the a modal tool is active /// Determines if a modal tool is active
bool TrackPanel::IsMouseCaptured() bool TrackPanel::IsMouseCaptured()
{ {
return mUIHandle != NULL; return mUIHandle != NULL;
@ -1172,8 +1197,13 @@ bool TrackInfo::HideTopItem( const wxRect &rect, const wxRect &subRect,
} }
/// Handle mouse wheel rotation (for zoom in/out, vertical and horizontal scrolling) /// Handle mouse wheel rotation (for zoom in/out, vertical and horizontal scrolling)
void TrackPanel::HandleWheelRotation(wxMouseEvent & event) void TrackPanel::HandleWheelRotation( TrackPanelMouseEvent &tpmEvent )
{ {
auto pCell = tpmEvent.pCell;
if (!pCell)
return;
auto &event = tpmEvent.event;
double steps {}; double steps {};
#if defined(__WXMAC__) && defined(EVT_MAGNIFY) #if defined(__WXMAC__) && defined(EVT_MAGNIFY)
// PRL: // PRL:
@ -1198,6 +1228,8 @@ void TrackPanel::HandleWheelRotation(wxMouseEvent & event)
steps *= -1; steps *= -1;
} }
tpmEvent.steps = steps;
if(!event.HasAnyModifiers()) { if(!event.HasAnyModifiers()) {
// We will later un-skip if we do anything, but if we don't, // We will later un-skip if we do anything, but if we don't,
// propagate the event up for the sake of the scrubber // propagate the event up for the sake of the scrubber
@ -1205,18 +1237,10 @@ void TrackPanel::HandleWheelRotation(wxMouseEvent & event)
event.ResumePropagation(wxEVENT_PROPAGATE_MAX); event.ResumePropagation(wxEVENT_PROPAGATE_MAX);
} }
// Delegate wheel handling to the cell under the mouse unsigned result =
const auto foundCell = FindCell( event.m_x, event.m_y ); pCell->HandleWheelRotation( tpmEvent, GetProject() );
auto &rect = foundCell.rect; auto pTrack = static_cast<CommonTrackPanelCell*>(pCell)->FindTrack();
auto pCell = foundCell.pCell; ProcessUIHandleResult(this, mRuler, pTrack, pTrack, result);
auto pTrack = foundCell.pTrack;
if (pCell) {
const auto size = GetSize();
unsigned result = pCell->HandleWheelRotation(
TrackPanelMouseEvent{ event, rect, size, pCell, steps },
GetProject() );
ProcessUIHandleResult(this, mRuler, pTrack, pTrack, result);
}
} }
/// Filter captured keys typed into LabelTracks. /// Filter captured keys typed into LabelTracks.
@ -1358,12 +1382,20 @@ void TrackPanel::OnCaptureLost(wxMouseCaptureLostEvent & WXUNUSED(event))
void TrackPanel::OnMouseEvent(wxMouseEvent & event) void TrackPanel::OnMouseEvent(wxMouseEvent & event)
try try
{ {
const auto foundCell = FindCell( event.m_x, event.m_y );
auto &rect = foundCell.rect;
auto &pCell = foundCell.pCell;
auto &pTrack = foundCell.pTrack;
const auto size = GetSize();
TrackPanelMouseEvent tpmEvent{ event, rect, size, pCell };
#if defined(__WXMAC__) && defined(EVT_MAGNIFY) #if defined(__WXMAC__) && defined(EVT_MAGNIFY)
// PRL: // PRL:
// Pinch and spread implemented in wxWidgets 3.1.0, or cherry-picked from // Pinch and spread implemented in wxWidgets 3.1.0, or cherry-picked from
// the future in custom build of 3.0.2 // the future in custom build of 3.0.2
if (event.Magnify()) { if (event.Magnify()) {
HandleWheelRotation(event); HandleWheelRotation( tpmEvent );
} }
#endif #endif
@ -1378,7 +1410,7 @@ try
} }
if (event.m_wheelRotation != 0) if (event.m_wheelRotation != 0)
HandleWheelRotation(event); HandleWheelRotation( tpmEvent );
if (event.LeftDown() || event.LeftIsDown() || event.Moving()) { if (event.LeftDown() || event.LeftIsDown() || event.Moving()) {
// Skip, even if we do something, so that the left click or drag // Skip, even if we do something, so that the left click or drag
@ -1408,10 +1440,6 @@ try
if (event.ButtonDown()) { if (event.ButtonDown()) {
SetFocus(); SetFocus();
} }
if (event.ButtonUp()) {
if (HasCapture())
ReleaseMouse();
}
if (event.Leaving()) if (event.Leaving())
{ {
@ -1435,52 +1463,38 @@ try
} }
if (mUIHandle) { if (mUIHandle) {
const auto foundCell = FindCell( event.m_x, event.m_y );
auto &rect = foundCell.rect;
auto &pCell = foundCell.pCell;
auto &pTrack = foundCell.pTrack;
const auto size = GetSize();
if (event.Dragging()) { if (event.Dragging()) {
// UIHANDLE DRAG // UIHANDLE DRAG
const UIHandle::Result refreshResult = mUIHandle->Drag( const UIHandle::Result refreshResult =
TrackPanelMouseEvent{ event, rect, size, pCell }, GetProject() ); mUIHandle->Drag( tpmEvent, GetProject() );
ProcessUIHandleResult(this, mRuler, mpClickedTrack, pTrack, refreshResult); ProcessUIHandleResult(this, mRuler, mpClickedTrack, pTrack, refreshResult);
if (refreshResult & RefreshCode::Cancelled) { if (refreshResult & RefreshCode::Cancelled) {
// Drag decided to abort itself // Drag decided to abort itself
mUIHandle = NULL; mUIHandle = NULL;
mpClickedTrack = NULL; mpClickedTrack = NULL;
if (HasCapture()) Uncapture( &event );
ReleaseMouse();
// Should this be done? As for cancelling?
// HandleCursor(event);
}
else {
// UIHANDLE PREVIEW
// Update status message and cursor during drag
HitTestPreview preview = mUIHandle->Preview(
TrackPanelMouseEvent{ event, rect, size, pCell }, GetProject() );
mListener->TP_DisplayStatusMessage(preview.message);
if (preview.cursor)
SetCursor(*preview.cursor);
} }
else
HandleCursor( tpmEvent );
} }
else if (event.ButtonUp()) { else if (event.ButtonUp()) {
// UIHANDLE RELEASE // UIHANDLE RELEASE
UIHandle::Result refreshResult = mUIHandle->Release( UIHandle::Result refreshResult =
TrackPanelMouseEvent{ event, rect, size, pCell }, GetProject(), mUIHandle->Release( tpmEvent, GetProject(), this );
this );
ProcessUIHandleResult(this, mRuler, mpClickedTrack, pTrack, refreshResult); ProcessUIHandleResult(this, mRuler, mpClickedTrack, pTrack, refreshResult);
mUIHandle = NULL; mUIHandle = NULL;
mpClickedTrack = NULL; mpClickedTrack = NULL;
// ReleaseMouse() already done above // will also Uncapture() below
// Should this be done? As for cancelling?
// HandleCursor(event);
} }
} }
else { else if ( event.GetEventType() == wxEVT_MOTION )
// This is where most button-downs are detected // Update status message and cursor, not during drag
HandleTrackSpecificMouseEvent(event); // (consider it not a drag, even if button is down during motion, if
// mUIHandle is null, as it becomes during interrupted drag
// (e.g. by hitting space to play while dragging an envelope point)
HandleCursor( &event );
else if ( event.ButtonDown() || event.ButtonDClick() ) {
HandleClick( tpmEvent );
} }
if (event.ButtonDown() && IsMouseCaptured()) { if (event.ButtonDown() && IsMouseCaptured()) {
@ -1490,6 +1504,8 @@ try
//EnsureVisible should be called after the up-click. //EnsureVisible should be called after the up-click.
if (event.ButtonUp()) { if (event.ButtonUp()) {
Uncapture( &event );
wxRect rect; wxRect rect;
const auto foundCell = FindCell(event.m_x, event.m_y); const auto foundCell = FindCell(event.m_x, event.m_y);
@ -1506,24 +1522,18 @@ catch( ... )
if ( HandleEscapeKey( true ) ) if ( HandleEscapeKey( true ) )
; ;
else { else {
// Ensure these steps, if escape handling did nothing Uncapture();
if (HasCapture())
ReleaseMouse();
wxMouseEvent dummy;
HandleCursor(dummy);
Refresh(false); Refresh(false);
} }
throw; throw;
} }
// AS: I don't really understand why this code is sectioned off void TrackPanel::HandleClick( const TrackPanelMouseEvent &tpmEvent )
// from the other OnMouseEvent code.
void TrackPanel::HandleTrackSpecificMouseEvent(wxMouseEvent & event)
{ {
const auto foundCell = FindCell( event.m_x, event.m_y ); const auto &event = tpmEvent.event;
auto &pTrack = foundCell.pTrack; auto pCell = tpmEvent.pCell;
auto &pCell = foundCell.pCell; const auto &rect = tpmEvent.rect;
auto &rect = foundCell.rect; auto pTrack = static_cast<CommonTrackPanelCell *>( pCell )->FindTrack();
// see if I'm over the border area. // see if I'm over the border area.
// TrackPanelResizeHandle is the UIHandle subclass that TrackPanel knows // TrackPanelResizeHandle is the UIHandle subclass that TrackPanel knows
@ -1539,56 +1549,21 @@ void TrackPanel::HandleTrackSpecificMouseEvent(wxMouseEvent & event)
} }
//Determine if user clicked on the track's left-hand label or ruler //Determine if user clicked on the track's left-hand label or ruler
if ( !( foundCell.type == CellType::Track || if ( !mUIHandle && pCell )
foundCell.type == CellType::Background ) ) { mUIHandle =
const auto size = GetSize(); pCell->HitTest( tpmEvent, GetProject() ).handle;
if (!mUIHandle &&
pCell &&
(event.ButtonDown() || event.ButtonDClick()))
mUIHandle = pCell->HitTest(
TrackPanelMouseEvent{ event, rect, size }, GetProject()).handle;
if (mUIHandle) { if (mUIHandle) {
// UIHANDLE CLICK // UIHANDLE CLICK
UIHandle::Result refreshResult = mUIHandle->Click( UIHandle::Result refreshResult =
TrackPanelMouseEvent{ event, rect, size, pCell }, GetProject() ); mUIHandle->Click( tpmEvent, GetProject() );
if (refreshResult & RefreshCode::Cancelled) if (refreshResult & RefreshCode::Cancelled)
mUIHandle = NULL; mUIHandle = NULL;
else else
mpClickedTrack = pTrack; mpClickedTrack = pTrack;
ProcessUIHandleResult(this, mRuler, pTrack, pTrack, refreshResult); ProcessUIHandleResult(this, mRuler, pTrack, pTrack, refreshResult);
} HandleCursor( tpmEvent );
HandleCursor(event);
return;
} }
// To do: remove the following special things
// so that we can coalesce the code for track and non-track clicks
bool handled = false;
if( !handled )
{
const auto size = GetSize();
if (pCell &&
(event.ButtonDown() || event.ButtonDClick()) &&
( mUIHandle ||
NULL != (mUIHandle = pCell->HitTest(
TrackPanelMouseEvent{ event, rect, size }, GetProject()).handle))) {
// UIHANDLE CLICK
UIHandle::Result refreshResult = mUIHandle->Click(
TrackPanelMouseEvent{ event, rect, size, pCell }, GetProject() );
if (refreshResult & RefreshCode::Cancelled)
mUIHandle = NULL;
else
mpClickedTrack = pTrack;
ProcessUIHandleResult(this, mRuler, pTrack, pTrack, refreshResult);
}
}
if ((event.Moving() || event.LeftUp()))
HandleCursor(event);
} }
double TrackPanel::GetMostRecentXPos() double TrackPanel::GetMostRecentXPos()

View File

@ -44,6 +44,7 @@ class MixerBoard;
class AudacityProject; class AudacityProject;
class TrackPanelAx; class TrackPanelAx;
struct TrackPanelMouseEvent;
class ViewInfo; class ViewInfo;
@ -302,6 +303,7 @@ class AUDACITY_DLL_API TrackPanel final : public OverlayPanel {
// void SetSnapTo(int snapto) // void SetSnapTo(int snapto)
void HandleInterruptedDrag(); void HandleInterruptedDrag();
void Uncapture( wxMouseEvent *pEvent = nullptr );
void CancelDragging(); void CancelDragging();
bool HandleEscapeKey(bool down); bool HandleEscapeKey(bool down);
void HandleAltKey(bool down); void HandleAltKey(bool down);
@ -336,7 +338,7 @@ class AUDACITY_DLL_API TrackPanel final : public OverlayPanel {
protected: protected:
bool IsAudioActive(); bool IsAudioActive();
void HandleTrackSpecificMouseEvent(wxMouseEvent & event); void HandleClick( const TrackPanelMouseEvent &tpmEvent );
public: public:
size_t GetTrackCount() const; size_t GetTrackCount() const;
@ -349,10 +351,8 @@ public:
void UpdateAccessibility(); void UpdateAccessibility();
void MessageForScreenReader(const wxString& message); void MessageForScreenReader(const wxString& message);
void HandleCursor(wxMouseEvent & event);
// MM: Handle mouse wheel rotation // MM: Handle mouse wheel rotation
void HandleWheelRotation(wxMouseEvent & event); void HandleWheelRotation( TrackPanelMouseEvent &tpmEvent );
void MakeParentRedrawScrollbars(); void MakeParentRedrawScrollbars();
@ -370,6 +370,11 @@ protected:
}; };
FoundCell FindCell(int mouseX, int mouseY); FoundCell FindCell(int mouseX, int mouseY);
void HandleCursor( wxMouseEvent *pEvent );
void HandleCursor
( const TrackPanelMouseEvent &tpmEvent,
CellType cellType = CellType::Background );
// If label, rectangle includes track control panel only. // If label, rectangle includes track control panel only.
// If !label, rectangle includes all of that, and the vertical ruler, and // If !label, rectangle includes all of that, and the vertical ruler, and
// the proper track area. // the proper track area.

View File

@ -22,12 +22,12 @@ struct TrackPanelMouseEvent
{ {
TrackPanelMouseEvent TrackPanelMouseEvent
( wxMouseEvent &event_, const wxRect &rect_, const wxSize &whole_, ( wxMouseEvent &event_, const wxRect &rect_, const wxSize &whole_,
TrackPanelCell *pCell_ = NULL, double steps_ = 0.0 ) TrackPanelCell *pCell_ )
: event{ event_ } : event{ event_ }
, rect{ rect_ } , rect{ rect_ }
, whole{ whole_ } , whole{ whole_ }
, pCell{ pCell_ } , pCell{ pCell_ }
, steps{ steps_ } , steps{ 0 }
{ {
} }