diff --git a/res/images/cross_circle_red.svg b/res/images/cross_circle_red.svg new file mode 100644 index 00000000000..300ea50059a --- /dev/null +++ b/res/images/cross_circle_red.svg @@ -0,0 +1,5 @@ + + Mixxx 1.12+ iconset + + + diff --git a/res/mixxx.qrc b/res/mixxx.qrc index 8011fe39f94..257dc4cb8d6 100644 --- a/res/mixxx.qrc +++ b/res/mixxx.qrc @@ -2,7 +2,10 @@ ../LICENSE + images/library/ic_library_drag_and_drop.svg + + images/cross_circle_red.svg images/library/ic_library_locked.svg images/library/ic_library_unlocked.svg diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index dd44cb74159..8d35828b83f 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -26,6 +26,12 @@ #include "widget/controlwidgetconnection.h" #include "wskincolor.h" +namespace { +// Horizontal and vertical margin around the widget where we accept play pos dragging. +constexpr int kDragOutsideLimitX = 100; +constexpr int kDragOutsideLimitY = 50; +} // anonymous namespace + WOverview::WOverview( const QString& group, PlayerManager* pPlayerManager, @@ -50,6 +56,8 @@ WOverview::WOverview( m_iPlayPos(0), m_bTimeRulerActive(false), m_orientation(Qt::Horizontal), + m_dragMarginH(kDragOutsideLimitX), + m_dragMarginV(kDragOutsideLimitY), m_iLabelFontSize(10), m_a(1.0), m_b(0.0), @@ -115,6 +123,12 @@ WOverview::WOverview( this, &WOverview::onTrackAnalyzerProgress); connect(m_pCueMenuPopup.get(), &WCueMenuPopup::aboutToHide, this, &WOverview::slotCueMenuPopupAboutToHide); + + // Style the cursor we show when the cursor leaves the valid + // play pos dragging area. + QPixmap abortPixmap(32, 32); + abortPixmap.load(":/images/cross_circle_red.svg"); + m_dragAbortCursor = QCursor(abortPixmap); } void WOverview::setup(const QDomNode& node, const SkinContext& context) { @@ -226,6 +240,8 @@ void WOverview::setup(const QDomNode& node, const SkinContext& context) { QString orientationString = context.selectString(node, "Orientation").toLower(); if (orientationString == "vertical") { m_orientation = Qt::Vertical; + m_dragMarginH = kDragOutsideLimitY; + m_dragMarginV = kDragOutsideLimitX; } else { m_orientation = Qt::Horizontal; } @@ -487,6 +503,31 @@ void WOverview::receiveCuesUpdated() { void WOverview::mouseMoveEvent(QMouseEvent* e) { if (m_bLeftClickDragging) { + // Check if we're inside the valid dragging area + int x = 0, y = 0; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + x = static_cast(e->position().x()); + y = static_cast(e->position().y()); +#else + x = static_cast(e->x()); + y = static_cast(e->y()); +#endif + if (!isPosWithinPositionDragZone(x, y)) { + // Remove the time ruler to indicate dragging position is invalid + m_iPickupPos = m_iPlayPos; + m_bTimeRulerActive = false; + + setCursor(m_dragAbortCursor); + // Remember to restore cursor everywhere where we cancel dragging + // Update immediately + update(); + return; + } else { + m_bTimeRulerActive = true; + m_timeRulerPos = e->pos(); + unsetCursor(); + } + if (m_orientation == Qt::Horizontal) { #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) m_iPickupPos = math_clamp(static_cast(e->position().x()), 0, width() - 1); @@ -523,12 +564,32 @@ void WOverview::mouseReleaseEvent(QMouseEvent* e) { mouseMoveEvent(e); if (m_bPassthroughEnabled) { m_bLeftClickDragging = false; + // We may be dragging, and we may be outside the valid dragging area + // (we'd show show the 'invalid drag' cursor then), so restore the cursor. + unsetCursor(); return; } //qDebug() << "WOverview::mouseReleaseEvent" << e->pos() << m_iPos << ">>" << dValue; if (e->button() == Qt::LeftButton) { if (m_bLeftClickDragging) { + unsetCursor(); + // Abort dragging if we are way outside the widget. + int x = 0, y = 0; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + x = static_cast(e->position().x()); + y = static_cast(e->position().y()); +#else + x = static_cast(e->x()); + y = static_cast(e->y()); +#endif + if (!isPosWithinPositionDragZone(x, y)) { + m_iPickupPos = m_iPlayPos; + m_bLeftClickDragging = false; + m_bTimeRulerActive = false; + return; + } + m_iPlayPos = m_iPickupPos; double dValue = positionToValue(m_iPickupPos); setControlParameterUp(dValue); @@ -584,6 +645,7 @@ void WOverview::mousePressEvent(QMouseEvent* e) { m_iPickupPos = m_iPlayPos; m_bLeftClickDragging = false; m_bTimeRulerActive = false; + unsetCursor(); } else if (m_pHoveredMark == nullptr) { m_bTimeRulerActive = true; m_timeRulerPos = e->pos(); diff --git a/src/widget/woverview.h b/src/widget/woverview.h index 242802d057f..08b04a73956 100644 --- a/src/widget/woverview.h +++ b/src/widget/woverview.h @@ -124,6 +124,15 @@ class WOverview : public WWidget, public TrackDropTarget { return m_orientation == Qt::Horizontal ? height() : width(); } + inline bool isPosWithinPositionDragZone(int x, int y) { + const QRect dragZone = rect().marginsAdded(QMargins( + m_dragMarginH, + m_dragMarginV, + m_dragMarginH, + m_dragMarginV)); + return dragZone.contains(x, y); + } + ConstWaveformPointer getWaveform() const { return m_pWaveform; } @@ -164,6 +173,8 @@ class WOverview : public WWidget, public TrackDropTarget { int m_iPlayPos; bool m_bTimeRulerActive; Qt::Orientation m_orientation; + int m_dragMarginH; + int m_dragMarginV; int m_iLabelFontSize; // Coefficient value-position linear transposition @@ -211,10 +222,10 @@ class WOverview : public WWidget, public TrackDropTarget { QColor m_lowColor; int m_dimBrightThreshold; parented_ptr m_pPassthroughLabel; + QCursor m_dragAbortCursor; WaveformMarkSet m_marks; std::vector m_markRanges; WaveformMarkLabel m_cuePositionLabel; WaveformMarkLabel m_cueTimeDistanceLabel; - };