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 @@
+
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;
-
};