From dc7c7e33ab17c63a3ff74e4f2f58cce5a055d7d5 Mon Sep 17 00:00:00 2001 From: Be Date: Tue, 13 Aug 2019 11:09:15 -0500 Subject: [PATCH 01/95] WOverview: render hotcue text labels --- src/waveform/renderers/waveformmarkproperties.cpp | 5 ++--- src/waveform/renderers/waveformrendermark.cpp | 2 +- src/widget/woverview.cpp | 15 +++++++++++++++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/waveform/renderers/waveformmarkproperties.cpp b/src/waveform/renderers/waveformmarkproperties.cpp index 18ee9195e49..8685ade6147 100644 --- a/src/waveform/renderers/waveformmarkproperties.cpp +++ b/src/waveform/renderers/waveformmarkproperties.cpp @@ -66,9 +66,8 @@ WaveformMarkProperties::WaveformMarkProperties(const QDomNode& node, QString markAlign = context.selectString(node, "Align"); m_align = decodeAlignmentFlags(markAlign, Qt::AlignBottom | Qt::AlignHCenter); - if (WaveformMark::kNoHotCue != hotCue) { - m_text = context.selectString(node, "Text").arg(hotCue + 1); - } else { + // Hotcue text is set by the cue's label in the database, not by the skin. + if (hotCue == WaveformMark::kNoHotCue) { m_text = context.selectString(node, "Text"); } m_pixmapPath = context.selectString(node, "Pixmap"); diff --git a/src/waveform/renderers/waveformrendermark.cpp b/src/waveform/renderers/waveformrendermark.cpp index f6a2638969e..4c78a96af2f 100644 --- a/src/waveform/renderers/waveformrendermark.cpp +++ b/src/waveform/renderers/waveformrendermark.cpp @@ -123,7 +123,7 @@ void WaveformRenderMark::slotCuesUpdated() { // because WaveformMarkSet stores one mark for each hotcue. WaveformMarkPointer pMark = m_marks.getHotCueMark(hotCue); if (pMark.isNull()) { - continue; + continue; } WaveformMarkProperties markProperties = pMark->getProperties(); diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index fcd9e4c47cd..672dc0588c7 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -302,6 +302,21 @@ void WOverview::updateCues(const QList &loadedCues) { markProperties.setBaseColor(newColor); currentMark->setProperties(markProperties); } + + int hotcueNumber = currentCue->getHotCue(); + if (currentCue->getType() == Cue::CUE && hotcueNumber != WaveformMark::kNoHotCue) { + QString newLabel = currentCue->getLabel(); + if (newLabel.isEmpty()) { + newLabel = QString::number(hotcueNumber + 1); + } else { + newLabel = QString("%1: %2").arg(hotcueNumber + 1).arg(newLabel); + } + + if (markProperties.m_text != newLabel) { + markProperties.m_text = newLabel; + currentMark->setProperties(markProperties); + } + } } } } From d9d963f217a8b9de071045af8aa1077e0323b39e Mon Sep 17 00:00:00 2001 From: Be Date: Tue, 13 Aug 2019 13:21:45 -0500 Subject: [PATCH 02/95] WOverview: elide long cue labels, but show them on mouse hover --- .../renderers/waveformmarkproperties.cpp | 4 +- .../renderers/waveformmarkproperties.h | 4 ++ src/widget/woverview.cpp | 59 ++++++++++++++++--- src/widget/woverview.h | 1 + 4 files changed, 58 insertions(+), 10 deletions(-) diff --git a/src/waveform/renderers/waveformmarkproperties.cpp b/src/waveform/renderers/waveformmarkproperties.cpp index 8685ade6147..c7a278b1ff5 100644 --- a/src/waveform/renderers/waveformmarkproperties.cpp +++ b/src/waveform/renderers/waveformmarkproperties.cpp @@ -45,7 +45,9 @@ Qt::Alignment decodeAlignmentFlags(QString alignString, Qt::Alignment defaultFla WaveformMarkProperties::WaveformMarkProperties(const QDomNode& node, const SkinContext& context, const WaveformSignalColors& signalColors, - int hotCue) { + int hotCue) + : m_renderedArea(QRectF()), + m_bMouseHovering(false) { QColor color(context.selectString(node, "Color")); if (!color.isValid()) { // As a fallback, grab the color from the parent's AxesColor diff --git a/src/waveform/renderers/waveformmarkproperties.h b/src/waveform/renderers/waveformmarkproperties.h index 6f3cf0ea4c2..4f38087add2 100644 --- a/src/waveform/renderers/waveformmarkproperties.h +++ b/src/waveform/renderers/waveformmarkproperties.h @@ -3,6 +3,7 @@ #include #include +#include class SkinContext; class WaveformSignalColors; @@ -26,6 +27,9 @@ class WaveformMarkProperties final { Qt::Alignment m_align; QString m_pixmapPath; + QRectF m_renderedArea; + bool m_bMouseHovering; + private: QColor m_fillColor; QColor m_borderColor; diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 672dc0588c7..a5e49ca5dff 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -76,6 +76,8 @@ WOverview::WOverview( new ControlProxy(m_group, "track_samples", this); setAcceptDrops(true); + setMouseTracking(true); + connect(pPlayerManager, &PlayerManager::trackAnalyzerProgress, this, &WOverview::onTrackAnalyzerProgress); } @@ -328,11 +330,24 @@ void WOverview::receiveCuesUpdated() { } void WOverview::mouseMoveEvent(QMouseEvent* e) { - if (m_orientation == Qt::Horizontal) { - m_iPos = math_clamp(e->x(), 0, width() - 1); - } else { - m_iPos = math_clamp(e->y(), 0, height() - 1); + if (m_bDrag) { + if (m_orientation == Qt::Horizontal) { + m_iPos = math_clamp(e->x(), 0, width() - 1); + } else { + m_iPos = math_clamp(e->y(), 0, height() - 1); + } + } + + for (auto& mark : m_marks) { + WaveformMarkProperties markProperties = mark->getProperties(); + if (markProperties.m_renderedArea.contains(e->pos())) { + markProperties.m_bMouseHovering = true; + } else { + markProperties.m_bMouseHovering = false; + } + mark->setProperties(markProperties); } + //qDebug() << "WOverview::mouseMoveEvent" << e->pos() << m_iPos; update(); } @@ -352,6 +367,17 @@ void WOverview::mousePressEvent(QMouseEvent* e) { m_bDrag = true; } +void WOverview::leaveEvent(QEvent* e) { + Q_UNUSED(e); + for (auto& mark : m_marks) { + WaveformMarkProperties markProperties = mark->getProperties(); + markProperties.m_bMouseHovering = false; + mark->setProperties(markProperties); + } + m_bDrag = false; + update(); +} + void WOverview::paintEvent(QPaintEvent * /*unused*/) { ScopedTimer t("WOverview::paintEvent"); @@ -571,8 +597,8 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga shadowFont.setWeight(99); shadowFont.setPixelSize(10 * m_scaleFactor); - for (const auto& currentMark : m_marks) { - const WaveformMarkProperties& markProperties = currentMark->getProperties(); + for (auto& currentMark : m_marks) { + WaveformMarkProperties markProperties = currentMark->getProperties(); if (currentMark->isValid() && currentMark->getSamplePosition() >= 0.0) { // Marks are visible by default. if (currentMark->hasVisible() && !currentMark->isVisible()) { @@ -602,8 +628,15 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga if (!markProperties.m_text.isEmpty()) { Qt::Alignment halign = markProperties.m_align & Qt::AlignHorizontal_Mask; Qt::Alignment valign = markProperties.m_align & Qt::AlignVertical_Mask; + QFontMetricsF metric(markerFont); - QRectF textRect = metric.tightBoundingRect(markProperties.m_text); + QString text = markProperties.m_text; + if (!markProperties.m_bMouseHovering) { + // 40 pixels is an arbitrary limit + text = metric.elidedText(text, Qt::ElideRight, 40); + } + + QRectF textRect = metric.boundingRect(text); QPointF textPoint; if (m_orientation == Qt::Horizontal) { if (halign == Qt::AlignLeft) { @@ -641,12 +674,20 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga pPainter->setPen(shadowPen); pPainter->setFont(shadowFont); - pPainter->drawText(textPoint, markProperties.m_text); + pPainter->drawText(textPoint, text); pPainter->setPen(markProperties.m_textColor); pPainter->setFont(markerFont); - pPainter->drawText(textPoint, markProperties.m_text); + pPainter->drawText(textPoint, text); + + // QPainter::drawText starts drawing with the given QPointF as + // the bottom left of the text, but QRectF::moveTo takes the new + // top left of the QRectF. + QPointF textTopLeft = QPointF(textPoint.x(), textPoint.y() - metric.height()); + textRect.moveTo(textTopLeft); + markProperties.m_renderedArea = textRect; } + currentMark->setProperties(markProperties); } } } diff --git a/src/widget/woverview.h b/src/widget/woverview.h index bae516ed550..ae136fd2cef 100644 --- a/src/widget/woverview.h +++ b/src/widget/woverview.h @@ -59,6 +59,7 @@ class WOverview : public WWidget, public TrackDropTarget { void mouseMoveEvent(QMouseEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override; void mousePressEvent(QMouseEvent *e) override; + void leaveEvent(QEvent* event) override; void paintEvent(QPaintEvent * /*unused*/) override; void resizeEvent(QResizeEvent * /*unused*/) override; void dragEnterEvent(QDragEnterEvent* event) override; From bc19ef32c06e52e2616791d8a80226423cfb954a Mon Sep 17 00:00:00 2001 From: Be Date: Tue, 13 Aug 2019 18:02:26 -0500 Subject: [PATCH 03/95] WOverview: let cue labels expand until they reach the next label This prevents labels from overlapping without enforcing an arbitrary limit on the width of the label. The user can hover the mouse cursor over an elided label to show its full text. In this case, subsequent labels are temporarily hidden until the mouse cursor is moved away. --- src/waveform/renderers/waveformmark.h | 11 +- src/widget/woverview.cpp | 194 ++++++++++++++++---------- 2 files changed, 127 insertions(+), 78 deletions(-) diff --git a/src/waveform/renderers/waveformmark.h b/src/waveform/renderers/waveformmark.h index c3075ec076f..ab05851db8d 100644 --- a/src/waveform/renderers/waveformmark.h +++ b/src/waveform/renderers/waveformmark.h @@ -57,7 +57,12 @@ class WaveformMark { // The m_pVisibleCos related function bool hasVisible() const { return m_pVisibleCos && m_pVisibleCos->valid(); } - bool isVisible() const { return m_pVisibleCos->get(); } + bool isVisible() const { + if (!hasVisible()) { + return true; + } + return m_pVisibleCos->get(); + } template void connectVisibleChanged(Receiver receiver, Slot slot) const { @@ -76,4 +81,8 @@ class WaveformMark { typedef QSharedPointer WaveformMarkPointer; +inline bool operator<(const WaveformMarkPointer& lhs, const WaveformMarkPointer& rhs) { + return lhs->getSamplePosition() < rhs->getSamplePosition(); +} + #endif // WAVEFORMMARK_H diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index a5e49ca5dff..6e93d81b103 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -597,97 +597,137 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga shadowFont.setWeight(99); shadowFont.setPixelSize(10 * m_scaleFactor); - for (auto& currentMark : m_marks) { - WaveformMarkProperties markProperties = currentMark->getProperties(); - if (currentMark->isValid() && currentMark->getSamplePosition() >= 0.0) { - // Marks are visible by default. - if (currentMark->hasVisible() && !currentMark->isVisible()) { - continue; - } + // Text labels are rendered so they do not overlap with other WaveformMark's + // labels. If the text would be too wide, it is elided. However, the user + // can hover the mouse cursor over a label to show the whole label text, + // temporarily hiding any following labels that would be drawn over it. + // This requires looping over the WaveformMarks twice and the marks must be + // sorted in the order they appear on the waveform. + // In the first loop, the lines are drawn and the text to render plus its + // location are calculated. The text must be drawn in the second loop to + // prevent the lines of following WaveformMarks getting drawn over it. + QList marksToRender; + for (WaveformMarkPointer mark : m_marks) { + if (mark->isValid() && mark->getSamplePosition() >= 0.0 && mark->isVisible()) { + marksToRender.append(mark); + } + } + std::sort(marksToRender.begin(), marksToRender.end()); + QList textToRender; + QRectF expandedLabelRect; + int firstHoveredIndex = -1; - PainterScope painterScope(pPainter); + for (int i = 0; i < marksToRender.size(); ++i) { + WaveformMarkProperties markProperties = marksToRender.at(i)->getProperties(); - //const float markPosition = 1.0 + - // (currentMark.m_pointControl->get() / (float)m_trackSamplesControl->get()) * (float)(width()-2); - const float markPosition = offset + currentMark->getSamplePosition() * gain; + PainterScope painterScope(pPainter); - QPen shadowPen(QBrush(markProperties.borderColor()), 2.5 * m_scaleFactor); + //const float markPosition = 1.0 + + // (marksToRender.at(i).m_pointControl->get() / (float)m_trackSamplesControl->get()) * (float)(width()-2); + const float markPosition = offset + marksToRender.at(i)->getSamplePosition() * gain; - QLineF line; - if (m_orientation == Qt::Horizontal) { - line.setLine(markPosition, 0.0, markPosition, static_cast(height())); - } else { - line.setLine(0.0, markPosition, static_cast(width()), markPosition); - } - pPainter->setPen(shadowPen); - pPainter->drawLine(line); + QPen shadowPen(QBrush(markProperties.borderColor()), 2.5 * m_scaleFactor); - pPainter->setPen(markProperties.fillColor()); - pPainter->drawLine(line); + QLineF line; + if (m_orientation == Qt::Horizontal) { + line.setLine(markPosition, 0.0, markPosition, static_cast(height())); + } else { + line.setLine(0.0, markPosition, static_cast(width()), markPosition); + } + pPainter->setPen(shadowPen); + pPainter->drawLine(line); - if (!markProperties.m_text.isEmpty()) { - Qt::Alignment halign = markProperties.m_align & Qt::AlignHorizontal_Mask; - Qt::Alignment valign = markProperties.m_align & Qt::AlignVertical_Mask; + pPainter->setPen(markProperties.fillColor()); + pPainter->drawLine(line); - QFontMetricsF metric(markerFont); - QString text = markProperties.m_text; - if (!markProperties.m_bMouseHovering) { - // 40 pixels is an arbitrary limit - text = metric.elidedText(text, Qt::ElideRight, 40); + if (!markProperties.m_text.isEmpty()) { + Qt::Alignment halign = markProperties.m_align & Qt::AlignHorizontal_Mask; + Qt::Alignment valign = markProperties.m_align & Qt::AlignVertical_Mask; + + QFontMetricsF metric(markerFont); + QString text = markProperties.m_text; + + // Only allow the text to overlap the following mark if the mouse is + // hovering over it. Otherwise, elide it if it would render over + // the next label. + if (!markProperties.m_bMouseHovering && i < marksToRender.size()-1) { + const float nextMarkPosition = offset + marksToRender.at(i+1)->getSamplePosition() * gain; + text = metric.elidedText(text, Qt::ElideRight, nextMarkPosition - markPosition - 5); + } + textToRender.append(text); + + // Without tracking the first hovered WaveformMark, the user could + // hover one mark, then drag the cursor over another, and the second + // one's label text would be drawn under the first. + if (markProperties.m_bMouseHovering && firstHoveredIndex == -1) { + firstHoveredIndex = i; + } + + QRectF textRect = metric.boundingRect(text); + QPointF textPoint; + if (m_orientation == Qt::Horizontal) { + if (halign == Qt::AlignLeft) { + textPoint.setX(markPosition - textRect.width()); + } else if (halign == Qt::AlignHCenter) { + textPoint.setX(markPosition - textRect.width() / 2); + } else { // AlignRight + textPoint.setX(markPosition + 0.5f); } - QRectF textRect = metric.boundingRect(text); - QPointF textPoint; - if (m_orientation == Qt::Horizontal) { - if (halign == Qt::AlignLeft) { - textPoint.setX(markPosition - textRect.width()); - } else if (halign == Qt::AlignHCenter) { - textPoint.setX(markPosition - textRect.width() / 2); - } else { // AlignRight - textPoint.setX(markPosition + 0.5f); - } - - if (valign == Qt::AlignTop) { - textPoint.setY(textRect.height() + 0.5f); - } else if (valign == Qt::AlignVCenter) { - textPoint.setY((textRect.height() + height()) / 2); - } else { // AlignBottom - textPoint.setY(float(height()) - 0.5f); - } - } else { // Vertical - if (halign == Qt::AlignLeft) { - textPoint.setX(1.0f); - } else if (halign == Qt::AlignHCenter) { - textPoint.setX((width() - textRect.width()) / 2); - } else { // AlignRight - textPoint.setX(width() - textRect.width()); - } - - if (valign == Qt::AlignTop) { - textPoint.setY(markPosition - 1.0f); - } else if (valign == Qt::AlignVCenter) { - textPoint.setY(markPosition + textRect.height() / 2); - } else { // AlignBottom - textPoint.setY(markPosition + metric.ascent()); - } + if (valign == Qt::AlignTop) { + textPoint.setY(textRect.height() + 0.5f); + } else if (valign == Qt::AlignVCenter) { + textPoint.setY((textRect.height() + height()) / 2); + } else { // AlignBottom + textPoint.setY(float(height()) - 0.5f); + } + } else { // Vertical + if (halign == Qt::AlignLeft) { + textPoint.setX(1.0f); + } else if (halign == Qt::AlignHCenter) { + textPoint.setX((width() - textRect.width()) / 2); + } else { // AlignRight + textPoint.setX(width() - textRect.width()); } - pPainter->setPen(shadowPen); - pPainter->setFont(shadowFont); - pPainter->drawText(textPoint, text); + if (valign == Qt::AlignTop) { + textPoint.setY(markPosition - 1.0f); + } else if (valign == Qt::AlignVCenter) { + textPoint.setY(markPosition + textRect.height() / 2); + } else { // AlignBottom + textPoint.setY(markPosition + metric.ascent()); + } + } - pPainter->setPen(markProperties.m_textColor); - pPainter->setFont(markerFont); - pPainter->drawText(textPoint, text); + // QPainter::drawText starts drawing with the given QPointF as + // the bottom left of the text, but QRectF::moveTo takes the new + // top left of the QRectF. + QPointF textTopLeft = QPointF(textPoint.x(), textPoint.y() - metric.height()); + textRect.moveTo(textTopLeft); + markProperties.m_renderedArea = textRect; - // QPainter::drawText starts drawing with the given QPointF as - // the bottom left of the text, but QRectF::moveTo takes the new - // top left of the QRectF. - QPointF textTopLeft = QPointF(textPoint.x(), textPoint.y() - metric.height()); - textRect.moveTo(textTopLeft); - markProperties.m_renderedArea = textRect; + if (markProperties.m_bMouseHovering) { + expandedLabelRect = textRect; } - currentMark->setProperties(markProperties); + } else { + // Placeholder to keep order + textToRender.append(QString()); + } + marksToRender.at(i)->setProperties(markProperties); + } + + for (int n = 0; n < marksToRender.size(); ++n) { + WaveformMarkProperties markProperties = marksToRender.at(n)->getProperties(); + QPen shadowPen(QBrush(markProperties.borderColor()), 2.5 * m_scaleFactor); + if (!markProperties.m_renderedArea.intersects(expandedLabelRect) + || (markProperties.m_bMouseHovering && firstHoveredIndex == n)) { + pPainter->setPen(shadowPen); + pPainter->setFont(shadowFont); + pPainter->drawText(markProperties.m_renderedArea.bottomLeft(), textToRender.at(n)); + + pPainter->setPen(markProperties.m_textColor); + pPainter->setFont(markerFont); + pPainter->drawText(markProperties.m_renderedArea.bottomLeft(), textToRender.at(n)); } } } From 2222550459e96aa156d250b453781e831e2c63df Mon Sep 17 00:00:00 2001 From: Be Date: Wed, 14 Aug 2019 07:52:33 -0500 Subject: [PATCH 04/95] WOverview: sort cues when they are updated, not when drawing for efficiency --- src/widget/woverview.cpp | 34 +++++++++++++++++----------------- src/widget/woverview.h | 3 +++ 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 6e93d81b103..3d17d21e2c1 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -294,10 +294,12 @@ void WOverview::onRateSliderChange(double /*v*/) { // currently only updates the mark color but it could be easily extended. void WOverview::updateCues(const QList &loadedCues) { + m_marksToRender.clear(); for (CuePointer currentCue: loadedCues) { const WaveformMarkPointer currentMark = m_marks.getHotCueMark(currentCue->getHotCue()); - if (currentMark && currentMark->isValid()) { + if (currentMark && currentMark->isValid() && currentMark->isVisible() + && currentMark->getSamplePosition() >= 0.0) { WaveformMarkProperties markProperties = currentMark->getProperties(); QColor newColor = m_predefinedColorsRepresentation.representationFor(currentCue->getColor()); if (newColor != markProperties.fillColor() || newColor != markProperties.m_textColor) { @@ -307,6 +309,7 @@ void WOverview::updateCues(const QList &loadedCues) { int hotcueNumber = currentCue->getHotCue(); if (currentCue->getType() == Cue::CUE && hotcueNumber != WaveformMark::kNoHotCue) { + // Prepend the hotcue number to hotcues' labels QString newLabel = currentCue->getLabel(); if (newLabel.isEmpty()) { newLabel = QString::number(hotcueNumber + 1); @@ -319,8 +322,11 @@ void WOverview::updateCues(const QList &loadedCues) { currentMark->setProperties(markProperties); } } + + m_marksToRender.append(currentMark); } } + std::sort(m_marksToRender.begin(), m_marksToRender.end()); } // connecting the tracks cuesUpdated and onMarkChanged is not possible @@ -606,25 +612,19 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga // In the first loop, the lines are drawn and the text to render plus its // location are calculated. The text must be drawn in the second loop to // prevent the lines of following WaveformMarks getting drawn over it. - QList marksToRender; - for (WaveformMarkPointer mark : m_marks) { - if (mark->isValid() && mark->getSamplePosition() >= 0.0 && mark->isVisible()) { - marksToRender.append(mark); - } - } - std::sort(marksToRender.begin(), marksToRender.end()); + QList textToRender; QRectF expandedLabelRect; int firstHoveredIndex = -1; - for (int i = 0; i < marksToRender.size(); ++i) { - WaveformMarkProperties markProperties = marksToRender.at(i)->getProperties(); + for (int i = 0; i < m_marksToRender.size(); ++i) { + WaveformMarkProperties markProperties = m_marksToRender.at(i)->getProperties(); PainterScope painterScope(pPainter); //const float markPosition = 1.0 + - // (marksToRender.at(i).m_pointControl->get() / (float)m_trackSamplesControl->get()) * (float)(width()-2); - const float markPosition = offset + marksToRender.at(i)->getSamplePosition() * gain; + // (m_marksToRender.at(i).m_pointControl->get() / (float)m_trackSamplesControl->get()) * (float)(width()-2); + const float markPosition = offset + m_marksToRender.at(i)->getSamplePosition() * gain; QPen shadowPen(QBrush(markProperties.borderColor()), 2.5 * m_scaleFactor); @@ -650,8 +650,8 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga // Only allow the text to overlap the following mark if the mouse is // hovering over it. Otherwise, elide it if it would render over // the next label. - if (!markProperties.m_bMouseHovering && i < marksToRender.size()-1) { - const float nextMarkPosition = offset + marksToRender.at(i+1)->getSamplePosition() * gain; + if (!markProperties.m_bMouseHovering && i < m_marksToRender.size()-1) { + const float nextMarkPosition = offset + m_marksToRender.at(i+1)->getSamplePosition() * gain; text = metric.elidedText(text, Qt::ElideRight, nextMarkPosition - markPosition - 5); } textToRender.append(text); @@ -713,11 +713,11 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga // Placeholder to keep order textToRender.append(QString()); } - marksToRender.at(i)->setProperties(markProperties); + m_marksToRender.at(i)->setProperties(markProperties); } - for (int n = 0; n < marksToRender.size(); ++n) { - WaveformMarkProperties markProperties = marksToRender.at(n)->getProperties(); + for (int n = 0; n < m_marksToRender.size(); ++n) { + WaveformMarkProperties markProperties = m_marksToRender.at(n)->getProperties(); QPen shadowPen(QBrush(markProperties.borderColor()), 2.5 * m_scaleFactor); if (!markProperties.m_renderedArea.intersects(expandedLabelRect) || (markProperties.m_bMouseHovering && firstHoveredIndex == n)) { diff --git a/src/widget/woverview.h b/src/widget/woverview.h index ae136fd2cef..16e91fb5cea 100644 --- a/src/widget/woverview.h +++ b/src/widget/woverview.h @@ -150,7 +150,10 @@ class WOverview : public WWidget, public TrackDropTarget { QColor m_endOfTrackColor; PredefinedColorsRepresentation m_predefinedColorsRepresentation; + // All WaveformMarks WaveformMarkSet m_marks; + // List of visible WaveformMarks sorted by the order they appear in the track + QList m_marksToRender; std::vector m_markRanges; // Coefficient value-position linear transposition From 8554b353f3449dbbc7e0816c99c9a6ba7b980c86 Mon Sep 17 00:00:00 2001 From: Be Date: Wed, 14 Aug 2019 07:52:52 -0500 Subject: [PATCH 05/95] use const auto& in for loops --- src/widget/woverview.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 3d17d21e2c1..3ced5f61626 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -344,7 +344,7 @@ void WOverview::mouseMoveEvent(QMouseEvent* e) { } } - for (auto& mark : m_marks) { + for (const auto& mark : m_marks) { WaveformMarkProperties markProperties = mark->getProperties(); if (markProperties.m_renderedArea.contains(e->pos())) { markProperties.m_bMouseHovering = true; @@ -375,7 +375,7 @@ void WOverview::mousePressEvent(QMouseEvent* e) { void WOverview::leaveEvent(QEvent* e) { Q_UNUSED(e); - for (auto& mark : m_marks) { + for (const auto& mark : m_marks) { WaveformMarkProperties markProperties = mark->getProperties(); markProperties.m_bMouseHovering = false; mark->setProperties(markProperties); From dcbd145b1738099799bc6021c5f68618c889c80d Mon Sep 17 00:00:00 2001 From: Be Date: Wed, 14 Aug 2019 08:23:07 -0500 Subject: [PATCH 06/95] WOverview: move tracking of first hovered mark to mouseMoveEvent to simplify drawMarks a bit --- src/widget/woverview.cpp | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 3ced5f61626..ce2a5b0e8f6 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -344,10 +344,15 @@ void WOverview::mouseMoveEvent(QMouseEvent* e) { } } - for (const auto& mark : m_marks) { + // Without tracking the first hovered WaveformMark, the user could hover one + // mark, then drag the cursor over another, and the second one's label text + // would be drawn under the first. + bool firstMarkHovered = false; + for (const auto& mark : m_marksToRender) { WaveformMarkProperties markProperties = mark->getProperties(); - if (markProperties.m_renderedArea.contains(e->pos())) { + if (markProperties.m_renderedArea.contains(e->pos()) && !firstMarkHovered) { markProperties.m_bMouseHovering = true; + firstMarkHovered = true; } else { markProperties.m_bMouseHovering = false; } @@ -615,7 +620,6 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga QList textToRender; QRectF expandedLabelRect; - int firstHoveredIndex = -1; for (int i = 0; i < m_marksToRender.size(); ++i) { WaveformMarkProperties markProperties = m_marksToRender.at(i)->getProperties(); @@ -656,13 +660,6 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga } textToRender.append(text); - // Without tracking the first hovered WaveformMark, the user could - // hover one mark, then drag the cursor over another, and the second - // one's label text would be drawn under the first. - if (markProperties.m_bMouseHovering && firstHoveredIndex == -1) { - firstHoveredIndex = i; - } - QRectF textRect = metric.boundingRect(text); QPointF textPoint; if (m_orientation == Qt::Horizontal) { @@ -720,7 +717,7 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga WaveformMarkProperties markProperties = m_marksToRender.at(n)->getProperties(); QPen shadowPen(QBrush(markProperties.borderColor()), 2.5 * m_scaleFactor); if (!markProperties.m_renderedArea.intersects(expandedLabelRect) - || (markProperties.m_bMouseHovering && firstHoveredIndex == n)) { + || markProperties.m_bMouseHovering) { pPainter->setPen(shadowPen); pPainter->setFont(shadowFont); pPainter->drawText(markProperties.m_renderedArea.bottomLeft(), textToRender.at(n)); From e2c80bce0c1145a993301a4b0ad4d4a448bfbc1a Mon Sep 17 00:00:00 2001 From: Be Date: Thu, 15 Aug 2019 10:33:57 -0500 Subject: [PATCH 07/95] WOverview: show position of hotcues on mouse hover --- src/widget/woverview.cpp | 32 +++++++++++++++++++++++++++++--- src/widget/woverview.h | 1 + 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index ce2a5b0e8f6..f7a5d5b59c7 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -571,9 +571,8 @@ void WOverview::drawRangeMarks(QPainter* pPainter, const float& offset, const fl // draw duration of range if (markRange.showDuration()) { - // TODO: replace with rate_ratio in PR #1765 - double rateRatio = 1.0 + m_pRateDirControl->get() * m_pRateRangeControl->get() * m_pRateSliderControl->get(); - QString duration = mixxx::Duration::formatTime((endValue - startValue) / m_trackSampleRateControl->get() / mixxx::kEngineChannelCount / rateRatio); + QString duration = mixxx::Duration::formatTime( + samplePositionToSeconds(endValue - startValue)); QFontMetrics fm(pPainter->font()); int textWidth = fm.width(duration); @@ -726,6 +725,25 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga pPainter->setFont(markerFont); pPainter->drawText(markProperties.m_renderedArea.bottomLeft(), textToRender.at(n)); } + if (markProperties.m_bMouseHovering) { + // Show cue position when hovered + // TODO: hide duration of intro/outro if the cue position text would + // overlap + double markPosition = m_marksToRender.at(n)->getSamplePosition(); + double markTime = samplePositionToSeconds(markPosition); + QFontMetricsF metric(markerFont); + Qt::Alignment valign = markProperties.m_align & Qt::AlignVertical_Mask; + QPointF textPoint(markProperties.m_renderedArea.bottomLeft()); + if (valign == Qt::AlignTop) { + textPoint.setY(float(height()) - 0.5f); + } else { + textPoint.setY(metric.height()); + } + + pPainter->setPen(markProperties.m_textColor); + pPainter->setFont(markerFont); + pPainter->drawText(textPoint, mixxx::Duration::formatTime(markTime)); + } } } @@ -781,6 +799,14 @@ void WOverview::paintText(const QString& text, QPainter* pPainter) { pPainter->resetTransform(); } +double WOverview::samplePositionToSeconds(double sample) { + // TODO: replace with rate_ratio in PR #1765 + double rateRatio = 1.0 + m_pRateDirControl->get() + * m_pRateRangeControl->get() * m_pRateSliderControl->get(); + return sample / m_trackSampleRateControl->get() + / mixxx::kEngineChannelCount / rateRatio; +} + void WOverview::resizeEvent(QResizeEvent * /*unused*/) { // Play-position potmeters range from 0 to 1 but they allow out-of-range // sets. This is to give VC access to the pre-roll area. diff --git a/src/widget/woverview.h b/src/widget/woverview.h index 16e91fb5cea..4577f1fadeb 100644 --- a/src/widget/woverview.h +++ b/src/widget/woverview.h @@ -114,6 +114,7 @@ class WOverview : public WWidget, public TrackDropTarget { void drawMarks(QPainter* pPainter, const float offset, const float gain); void drawCurrentPosition(QPainter* pPainter); void paintText(const QString& text, QPainter* pPainter); + double samplePositionToSeconds(double sample); inline int valueToPosition(double value) const { return static_cast(m_a * value - m_b); } From 65d279032b1c3a696481b2ca1592c64c03821aa1 Mon Sep 17 00:00:00 2001 From: Be Date: Thu, 15 Aug 2019 13:22:08 -0500 Subject: [PATCH 08/95] merge WaveformMarkProperties into WaveformMark class I do not understand why these were separate classes in the first place. --- build/depends.py | 1 - src/waveform/renderers/waveformmark.cpp | 106 ++++++++++++++---- src/waveform/renderers/waveformmark.h | 42 ++++--- .../renderers/waveformmarkproperties.cpp | 96 ---------------- .../renderers/waveformmarkproperties.h | 39 ------- src/waveform/renderers/waveformmarkset.h | 2 + src/waveform/renderers/waveformrendermark.cpp | 71 ++++++------ src/waveform/renderers/waveformrendermark.h | 2 +- src/widget/woverview.cpp | 82 ++++++-------- 9 files changed, 184 insertions(+), 257 deletions(-) delete mode 100644 src/waveform/renderers/waveformmarkproperties.cpp delete mode 100644 src/waveform/renderers/waveformmarkproperties.h diff --git a/build/depends.py b/build/depends.py index dde433a95c6..87076cabf2f 100644 --- a/build/depends.py +++ b/build/depends.py @@ -1111,7 +1111,6 @@ def sources(self, build): "src/waveform/renderers/waveformrenderersignalbase.cpp", "src/waveform/renderers/waveformmark.cpp", - "src/waveform/renderers/waveformmarkproperties.cpp", "src/waveform/renderers/waveformmarkset.cpp", "src/waveform/renderers/waveformmarkrange.cpp", "src/waveform/renderers/glwaveformrenderersimplesignal.cpp", diff --git a/src/waveform/renderers/waveformmark.cpp b/src/waveform/renderers/waveformmark.cpp index d1b98a03fe5..0f2db81adad 100644 --- a/src/waveform/renderers/waveformmark.cpp +++ b/src/waveform/renderers/waveformmark.cpp @@ -1,29 +1,49 @@ #include #include "skin/skincontext.h" -#include "waveform/renderers/waveformmarkproperties.h" +#include "waveform/renderers/waveformsignalcolors.h" +#include "widget/wskincolor.h" #include "waveformmark.h" -WaveformMark::WaveformMark(const QString& group, - const QDomNode& node, - const SkinContext& context, - const WaveformSignalColors& signalColors) - : m_iHotCue(kNoHotCue) { - QString item = context.selectString(node, "Control"); - if (!item.isEmpty()) { - m_pPointCos = std::make_unique(group, item); - if (item.startsWith("hotcue_") && item.endsWith("_position")) { - m_iHotCue = item.midRef(7, item.count() - 16).toInt() - 1; +namespace { +Qt::Alignment decodeAlignmentFlags(QString alignString, Qt::Alignment defaultFlags) { + QStringList stringFlags = alignString.toLower() + .split("|", QString::SkipEmptyParts); + + Qt::Alignment hflags = 0L; + Qt::Alignment vflags = 0L; + + for (auto stringFlag : stringFlags) { + if (stringFlag == "center") { + hflags |= Qt::AlignHCenter; + vflags |= Qt::AlignVCenter; + } else if (stringFlag == "left") { + hflags |= Qt::AlignLeft; + } else if (stringFlag == "hcenter") { + hflags |= Qt::AlignHCenter; + } else if (stringFlag == "right") { + hflags |= Qt::AlignRight; + } else if (stringFlag == "top") { + vflags |= Qt::AlignTop; + } else if (stringFlag == "vcenter") { + vflags |= Qt::AlignVCenter; + } else if (stringFlag == "bottom") { + vflags |= Qt::AlignBottom; } } - QString visibilityControl = context.selectString(node, "VisibilityControl"); - if (!visibilityControl.isEmpty()) { - ConfigKey key = ConfigKey::parseCommaSeparated(visibilityControl); - m_pVisibleCos = std::make_unique(key); + + if (hflags != Qt::AlignLeft && hflags != Qt::AlignHCenter && hflags != Qt::AlignRight) { + hflags = defaultFlags & Qt::AlignHorizontal_Mask; + } + + if (vflags != Qt::AlignTop && vflags != Qt::AlignVCenter && vflags != Qt::AlignBottom) { + vflags = defaultFlags & Qt::AlignVertical_Mask; } - m_properties = WaveformMarkProperties(node, context, signalColors, m_iHotCue); + + return hflags | vflags; } +} // anonymous namespace WaveformMark::WaveformMark(const QString& group, const QDomNode& node, @@ -31,9 +51,55 @@ WaveformMark::WaveformMark(const QString& group, const WaveformSignalColors& signalColors, int hotCue) : m_iHotCue(hotCue) { - if (hotCue >= 0) { - QString item = "hotcue_" + QString::number(hotCue + 1) + "_position"; - m_pPointCos = std::make_unique(group, item); + QString control; + if (hotCue != kNoHotCue) { + control = "hotcue_" + QString::number(hotCue + 1) + "_position"; + } else { + control = context.selectString(node, "Control"); + } + if (!control.isEmpty()) { + m_pPointCos = std::make_unique(group, control); + } + + QString visibilityControl = context.selectString(node, "VisibilityControl"); + if (!visibilityControl.isEmpty()) { + ConfigKey key = ConfigKey::parseCommaSeparated(visibilityControl); + m_pVisibleCos = std::make_unique(key); + } + + QColor color(context.selectString(node, "Color")); + if (!color.isValid()) { + // As a fallback, grab the color from the parent's AxesColor + color = signalColors.getAxesColor(); + qDebug() << "Didn't get mark , using parent's :" << color; + } else { + color = WSkinColor::getCorrectColor(color); + } + setBaseColor(color); + + m_textColor = context.selectString(node, "TextColor"); + if (!m_textColor.isValid()) { + // Read the text color, otherwise use the parent's BgColor. + m_textColor = signalColors.getBgColor(); + qDebug() << "Didn't get mark , using parent's :" << m_textColor; + } + + QString markAlign = context.selectString(node, "Align"); + m_align = decodeAlignmentFlags(markAlign, Qt::AlignBottom | Qt::AlignHCenter); + + // Hotcue text is set by the cue's label in the database, not by the skin. + if (hotCue == WaveformMark::kNoHotCue) { + m_text = context.selectString(node, "Text"); + } + + m_pixmapPath = context.selectString(node, "Pixmap"); + if (!m_pixmapPath.isEmpty()) { + m_pixmapPath = context.makeSkinPath(m_pixmapPath); } - m_properties = WaveformMarkProperties(node, context, signalColors, hotCue); } + +void WaveformMark::setBaseColor(QColor baseColor) { + m_fillColor = baseColor; + m_borderColor = Color::chooseContrastColor(baseColor); + m_labelColor = Color::chooseColorByBrightness(baseColor, QColor(255,255,255,255), QColor(0,0,0,255)); +}; diff --git a/src/waveform/renderers/waveformmark.h b/src/waveform/renderers/waveformmark.h index ab05851db8d..fc6f7fde4bf 100644 --- a/src/waveform/renderers/waveformmark.h +++ b/src/waveform/renderers/waveformmark.h @@ -7,7 +7,6 @@ #include "control/controlproxy.h" #include "util/memory.h" -#include "waveform/renderers/waveformmarkproperties.h" #include "control/controlobject.h" class SkinContext; @@ -18,31 +17,17 @@ class WOverview; class WaveformMark { public: static const int kNoHotCue = -1; - WaveformMark( - const QString& group, - const QDomNode& node, - const SkinContext& context, - const WaveformSignalColors& signalColors); - WaveformMark( const QString& group, const QDomNode& node, const SkinContext& context, const WaveformSignalColors& signalColors, - int hotCue); - + int hotCue = kNoHotCue); // Disable copying WaveformMark(const WaveformMark&) = delete; WaveformMark& operator=(const WaveformMark&) = delete; - const WaveformMarkProperties& getProperties() const { - return m_properties; - }; - void setProperties(const WaveformMarkProperties& properties) { - m_properties = properties; - }; - int getHotCue() const { return m_iHotCue; }; //The m_pPointCos related function @@ -69,13 +54,36 @@ class WaveformMark { m_pVisibleCos->connectValueChanged(receiver, slot, Qt::AutoConnection); } + // Sets the appropriate mark colors based on the base color + void setBaseColor(QColor baseColor); + QColor fillColor() const { + return m_fillColor; + } + QColor borderColor() const { + return m_borderColor; + } + QColor labelColor() const { + return m_labelColor; + } + + QColor m_textColor; + QString m_text; + Qt::Alignment m_align; + QString m_pixmapPath; + + QRectF m_renderedArea; + bool m_bMouseHovering; + private: std::unique_ptr m_pPointCos; std::unique_ptr m_pVisibleCos; - WaveformMarkProperties m_properties; int m_iHotCue; QImage m_image; + QColor m_fillColor; + QColor m_borderColor; + QColor m_labelColor; + friend class WaveformRenderMark; }; diff --git a/src/waveform/renderers/waveformmarkproperties.cpp b/src/waveform/renderers/waveformmarkproperties.cpp deleted file mode 100644 index c7a278b1ff5..00000000000 --- a/src/waveform/renderers/waveformmarkproperties.cpp +++ /dev/null @@ -1,96 +0,0 @@ -#include "skin/skincontext.h" -#include "waveform/renderers/waveformsignalcolors.h" -#include "widget/wskincolor.h" - -#include "waveform/renderers/waveformmarkproperties.h" -#include "waveform/renderers/waveformmark.h" - -Qt::Alignment decodeAlignmentFlags(QString alignString, Qt::Alignment defaultFlags) { - QStringList stringFlags = alignString.toLower() - .split("|", QString::SkipEmptyParts); - - Qt::Alignment hflags = 0L; - Qt::Alignment vflags = 0L; - - for (auto stringFlag : stringFlags) { - if (stringFlag == "center") { - hflags |= Qt::AlignHCenter; - vflags |= Qt::AlignVCenter; - } else if (stringFlag == "left") { - hflags |= Qt::AlignLeft; - } else if (stringFlag == "hcenter") { - hflags |= Qt::AlignHCenter; - } else if (stringFlag == "right") { - hflags |= Qt::AlignRight; - } else if (stringFlag == "top") { - vflags |= Qt::AlignTop; - } else if (stringFlag == "vcenter") { - vflags |= Qt::AlignVCenter; - } else if (stringFlag == "bottom") { - vflags |= Qt::AlignBottom; - } - } - - if (hflags != Qt::AlignLeft && hflags != Qt::AlignHCenter && hflags != Qt::AlignRight) { - hflags = defaultFlags & Qt::AlignHorizontal_Mask; - } - - if (vflags != Qt::AlignTop && vflags != Qt::AlignVCenter && vflags != Qt::AlignBottom) { - vflags = defaultFlags & Qt::AlignVertical_Mask; - } - - return hflags | vflags; -} - -WaveformMarkProperties::WaveformMarkProperties(const QDomNode& node, - const SkinContext& context, - const WaveformSignalColors& signalColors, - int hotCue) - : m_renderedArea(QRectF()), - m_bMouseHovering(false) { - QColor color(context.selectString(node, "Color")); - if (!color.isValid()) { - // As a fallback, grab the color from the parent's AxesColor - color = signalColors.getAxesColor(); - qDebug() << "Didn't get mark , using parent's :" << color; - } else { - color = WSkinColor::getCorrectColor(color); - } - setBaseColor(color); - - m_textColor = context.selectString(node, "TextColor"); - if (!m_textColor.isValid()) { - // Read the text color, otherwise use the parent's BgColor. - m_textColor = signalColors.getBgColor(); - qDebug() << "Didn't get mark , using parent's :" << m_textColor; - } - - QString markAlign = context.selectString(node, "Align"); - m_align = decodeAlignmentFlags(markAlign, Qt::AlignBottom | Qt::AlignHCenter); - - // Hotcue text is set by the cue's label in the database, not by the skin. - if (hotCue == WaveformMark::kNoHotCue) { - m_text = context.selectString(node, "Text"); - } - m_pixmapPath = context.selectString(node, "Pixmap"); - if (!m_pixmapPath.isEmpty()) { - m_pixmapPath = context.makeSkinPath(m_pixmapPath); - } -} - -void WaveformMarkProperties::setBaseColor(QColor baseColor) { - m_fillColor = baseColor; - m_borderColor = Color::chooseContrastColor(baseColor); - m_labelColor = Color::chooseColorByBrightness(baseColor, QColor(255,255,255,255), QColor(0,0,0,255)); -} - -QColor WaveformMarkProperties::fillColor() const { - return m_fillColor; -} - -QColor WaveformMarkProperties::borderColor() const { - return m_borderColor; -} -QColor WaveformMarkProperties::labelColor() const { - return m_labelColor; -} diff --git a/src/waveform/renderers/waveformmarkproperties.h b/src/waveform/renderers/waveformmarkproperties.h deleted file mode 100644 index 4f38087add2..00000000000 --- a/src/waveform/renderers/waveformmarkproperties.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef WAVEFORMMARKPROPERTIES_H -#define WAVEFORMMARKPROPERTIES_H - -#include -#include -#include - -class SkinContext; -class WaveformSignalColors; - -class WaveformMarkProperties final { - public: - WaveformMarkProperties() = default; - WaveformMarkProperties(const QDomNode& node, - const SkinContext& context, - const WaveformSignalColors& signalColors, - int hotCue); - - // Sets the appropriate mark colors based on the base color - void setBaseColor(QColor baseColor); - QColor fillColor() const; - QColor borderColor() const; - QColor labelColor() const; - - QColor m_textColor; - QString m_text; - Qt::Alignment m_align; - QString m_pixmapPath; - - QRectF m_renderedArea; - bool m_bMouseHovering; - - private: - QColor m_fillColor; - QColor m_borderColor; - QColor m_labelColor; -}; - -#endif // WAVEFORMMARKPROPERTIES_H diff --git a/src/waveform/renderers/waveformmarkset.h b/src/waveform/renderers/waveformmarkset.h index 198d6876620..9997eeffc7e 100644 --- a/src/waveform/renderers/waveformmarkset.h +++ b/src/waveform/renderers/waveformmarkset.h @@ -9,6 +9,8 @@ class WaveformWidgetRenderer; +// This class helps share code between the WaveformRenderMark and WOverview +// constructors. class WaveformMarkSet { public: WaveformMarkSet(); diff --git a/src/waveform/renderers/waveformrendermark.cpp b/src/waveform/renderers/waveformrendermark.cpp index 4c78a96af2f..5f239b4229f 100644 --- a/src/waveform/renderers/waveformrendermark.cpp +++ b/src/waveform/renderers/waveformrendermark.cpp @@ -5,6 +5,7 @@ #include "waveform/renderers/waveformrendermark.h" #include "control/controlproxy.h" +#include "engine/controls/cuecontrol.h" #include "track/track.h" #include "util/color/color.h" #include "waveform/renderers/waveformwidgetrenderer.h" @@ -27,7 +28,7 @@ void WaveformRenderMark::setup(const QDomNode& node, const SkinContext& context) m_marks.setup(m_waveformRenderer->getGroup(), node, context, signalColors); WaveformMarkPointer defaultMark(m_marks.getDefaultMark()); QColor defaultColor = defaultMark - ? defaultMark->getProperties().fillColor() + ? defaultMark->fillColor() : signalColors.getAxesColor(); m_predefinedColorsRepresentation = context.getCueColorRepresentation(node, defaultColor); } @@ -45,7 +46,7 @@ void WaveformRenderMark::draw(QPainter* painter, QPaintEvent* /*event*/) { painter->setWorldMatrixEnabled(false); - for (const auto& pMark: m_marks) { + for (auto& pMark: m_marks) { if (!pMark->isValid()) continue; @@ -56,7 +57,7 @@ void WaveformRenderMark::draw(QPainter* painter, QPaintEvent* /*event*/) { // Generate image on first paint can't be done in setup since we need // render widget to be resized yet ... if (pMark->m_image.isNull()) { - generateMarkImage(pMark.data()); + generateMarkImage(pMark); } double samplePosition = pMark->getSamplePosition(); @@ -115,7 +116,7 @@ void WaveformRenderMark::slotCuesUpdated() { QList loadedCues = trackInfo->getCuePoints(); for (const CuePointer pCue: loadedCues) { int hotCue = pCue->getHotCue(); - if (hotCue < 0) { + if (hotCue == WaveformMark::kNoHotCue) { continue; } @@ -126,25 +127,21 @@ void WaveformRenderMark::slotCuesUpdated() { continue; } - WaveformMarkProperties markProperties = pMark->getProperties(); QString newLabel = pCue->getLabel(); QColor newColor = m_predefinedColorsRepresentation.representationFor(pCue->getColor()); - if (markProperties.m_text.isNull() || newLabel != markProperties.m_text || - !markProperties.fillColor().isValid() || newColor != markProperties.fillColor()) { - markProperties.m_text = newLabel; - markProperties.setBaseColor(newColor); - pMark->setProperties(markProperties); - generateMarkImage(pMark.data()); + if (pMark->m_text.isNull() || newLabel != pMark->m_text || + !pMark->fillColor().isValid() || newColor != pMark->fillColor()) { + pMark->m_text = newLabel; + pMark->setBaseColor(newColor); + generateMarkImage(pMark); } } } -void WaveformRenderMark::generateMarkImage(WaveformMark* pMark) { - const WaveformMarkProperties& markProperties = pMark->getProperties(); - +void WaveformRenderMark::generateMarkImage(WaveformMarkPointer pMark) { // Load the pixmap from file -- takes precedence over text. - if (!markProperties.m_pixmapPath.isEmpty()) { - QString path = markProperties.m_pixmapPath; + if (!pMark->m_pixmapPath.isEmpty()) { + QString path = pMark->m_pixmapPath; QImage image = *WImageStore::getImage(path, scaleFactor()); //QImage image = QImage(path); // If loading the image didn't fail, then we're done. Otherwise fall @@ -159,9 +156,9 @@ void WaveformRenderMark::generateMarkImage(WaveformMark* pMark) { QPainter painter; // If no text is provided, leave m_markImage as a null image - if (!markProperties.m_text.isNull()) { + if (!pMark->m_text.isNull()) { // Determine mark text. - QString label = markProperties.m_text; + QString label = pMark->m_text; if (pMark->getHotCue() >= 0) { if (!label.isEmpty()) { label.prepend(": "); @@ -210,8 +207,8 @@ void WaveformRenderMark::generateMarkImage(WaveformMark* pMark) { pMark->m_image = QImage(width, height, QImage::Format_ARGB32_Premultiplied); - Qt::Alignment markAlignH = markProperties.m_align & Qt::AlignHorizontal_Mask; - Qt::Alignment markAlignV = markProperties.m_align & Qt::AlignVertical_Mask; + Qt::Alignment markAlignH = pMark->m_align & Qt::AlignHorizontal_Mask; + Qt::Alignment markAlignV = pMark->m_align & Qt::AlignVertical_Mask; if (markAlignH == Qt::AlignHCenter) { labelRect.moveLeft((width - labelRectWidth) / 2); @@ -238,27 +235,27 @@ void WaveformRenderMark::generateMarkImage(WaveformMark* pMark) { int middle = width / 2; if (markAlignH == Qt::AlignHCenter) { if (labelRect.top() > 0) { - painter.setPen(markProperties.fillColor()); + painter.setPen(pMark->fillColor()); painter.drawLine(middle, 0, middle, labelRect.top()); - painter.setPen(markProperties.borderColor()); + painter.setPen(pMark->borderColor()); painter.drawLine(middle - 1, 0, middle - 1, labelRect.top()); painter.drawLine(middle + 1, 0, middle + 1, labelRect.top()); } if (labelRect.bottom() < height) { - painter.setPen(markProperties.fillColor()); + painter.setPen(pMark->fillColor()); painter.drawLine(middle, labelRect.bottom(), middle, height); - painter.setPen(markProperties.borderColor()); + painter.setPen(pMark->borderColor()); painter.drawLine(middle - 1, labelRect.bottom(), middle - 1, height); painter.drawLine(middle + 1, labelRect.bottom(), middle + 1, height); } } else { // AlignLeft || AlignRight - painter.setPen(markProperties.fillColor()); + painter.setPen(pMark->fillColor()); painter.drawLine(middle, 0, middle, height); - painter.setPen(markProperties.borderColor()); + painter.setPen(pMark->borderColor()); painter.drawLine(middle - 1, 0, middle - 1, height); painter.drawLine(middle + 1, 0, middle + 1, height); } @@ -266,41 +263,41 @@ void WaveformRenderMark::generateMarkImage(WaveformMark* pMark) { int middle = height / 2; if (markAlignV == Qt::AlignVCenter) { if (labelRect.left() > 0) { - painter.setPen(markProperties.fillColor()); + painter.setPen(pMark->fillColor()); painter.drawLine(0, middle, labelRect.left(), middle); - painter.setPen(markProperties.borderColor()); + painter.setPen(pMark->borderColor()); painter.drawLine(0, middle - 1, labelRect.left(), middle - 1); painter.drawLine(0, middle + 1, labelRect.left(), middle + 1); } if (labelRect.right() < width) { - painter.setPen(markProperties.fillColor()); + painter.setPen(pMark->fillColor()); painter.drawLine(labelRect.right(), middle, width, middle); - painter.setPen(markProperties.borderColor()); + painter.setPen(pMark->borderColor()); painter.drawLine(labelRect.right(), middle - 1, width, middle - 1); painter.drawLine(labelRect.right(), middle + 1, width, middle + 1); } } else { // AlignTop || AlignBottom - painter.setPen(markProperties.fillColor()); + painter.setPen(pMark->fillColor()); painter.drawLine(0, middle, width, middle); - painter.setPen(markProperties.borderColor()); + painter.setPen(pMark->borderColor()); painter.drawLine(0, middle - 1, width, middle - 1); painter.drawLine(0, middle + 1, width, middle + 1); } } // Draw the label rect - painter.setPen(markProperties.borderColor()); - painter.setBrush(QBrush(markProperties.fillColor())); + painter.setPen(pMark->borderColor()); + painter.setBrush(QBrush(pMark->fillColor())); painter.drawRoundedRect(labelRect, 2.0, 2.0); // Draw text painter.setBrush(QBrush(QColor(0,0,0,0))); painter.setFont(font); - painter.setPen(markProperties.labelColor()); + painter.setPen(pMark->labelColor()); painter.drawText(labelRect, Qt::AlignCenter, label); } else //no text draw triangle @@ -332,7 +329,7 @@ void WaveformRenderMark::generateMarkImage(WaveformMark* pMark) { painter.setTransform(QTransform(0, 1, 1, 0, 0, 0)); } - QColor triangleColor = markProperties.fillColor(); + QColor triangleColor = pMark->fillColor(); painter.setPen(QColor(0,0,0,0)); painter.setBrush(QBrush(triangleColor)); @@ -355,7 +352,7 @@ void WaveformRenderMark::generateMarkImage(WaveformMark* pMark) { //TODO vRince duplicated code make a method //draw line - QColor lineColor = markProperties.fillColor(); + QColor lineColor = pMark->fillColor(); painter.setPen(lineColor); float middle = markLength / 2.0; diff --git a/src/waveform/renderers/waveformrendermark.h b/src/waveform/renderers/waveformrendermark.h index 265da9d1af3..01c975fe3c1 100644 --- a/src/waveform/renderers/waveformrendermark.h +++ b/src/waveform/renderers/waveformrendermark.h @@ -33,7 +33,7 @@ class WaveformRenderMark : public QObject, public WaveformRendererAbstract { void slotCuesUpdated(); private: - void generateMarkImage(WaveformMark* pMark); + void generateMarkImage(WaveformMarkPointer pMark); PredefinedColorsRepresentation m_predefinedColorsRepresentation; diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index f7a5d5b59c7..b542b6ccd59 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -108,7 +108,7 @@ void WOverview::setup(const QDomNode& node, const SkinContext& context) { m_marks.setup(m_group, node, context, m_signalColors); WaveformMarkPointer defaultMark(m_marks.getDefaultMark()); QColor defaultColor = defaultMark - ? defaultMark->getProperties().fillColor() + ? defaultMark->fillColor() : m_signalColors.getAxesColor(); m_predefinedColorsRepresentation = context.getCueColorRepresentation(node, defaultColor); @@ -292,19 +292,16 @@ void WOverview::onRateSliderChange(double /*v*/) { update(); } -// currently only updates the mark color but it could be easily extended. void WOverview::updateCues(const QList &loadedCues) { m_marksToRender.clear(); for (CuePointer currentCue: loadedCues) { - const WaveformMarkPointer currentMark = m_marks.getHotCueMark(currentCue->getHotCue()); + const WaveformMarkPointer pMark = m_marks.getHotCueMark(currentCue->getHotCue()); - if (currentMark && currentMark->isValid() && currentMark->isVisible() - && currentMark->getSamplePosition() >= 0.0) { - WaveformMarkProperties markProperties = currentMark->getProperties(); + if (pMark != nullptr && pMark->isValid() && pMark->isVisible() + && pMark->getSamplePosition() >= 0.0) { QColor newColor = m_predefinedColorsRepresentation.representationFor(currentCue->getColor()); - if (newColor != markProperties.fillColor() || newColor != markProperties.m_textColor) { - markProperties.setBaseColor(newColor); - currentMark->setProperties(markProperties); + if (newColor != pMark->fillColor() || newColor != pMark->m_textColor) { + pMark->setBaseColor(newColor); } int hotcueNumber = currentCue->getHotCue(); @@ -317,13 +314,12 @@ void WOverview::updateCues(const QList &loadedCues) { newLabel = QString("%1: %2").arg(hotcueNumber + 1).arg(newLabel); } - if (markProperties.m_text != newLabel) { - markProperties.m_text = newLabel; - currentMark->setProperties(markProperties); + if (pMark->m_text != newLabel) { + pMark->m_text = newLabel; } } - m_marksToRender.append(currentMark); + m_marksToRender.append(pMark); } } std::sort(m_marksToRender.begin(), m_marksToRender.end()); @@ -348,15 +344,13 @@ void WOverview::mouseMoveEvent(QMouseEvent* e) { // mark, then drag the cursor over another, and the second one's label text // would be drawn under the first. bool firstMarkHovered = false; - for (const auto& mark : m_marksToRender) { - WaveformMarkProperties markProperties = mark->getProperties(); - if (markProperties.m_renderedArea.contains(e->pos()) && !firstMarkHovered) { - markProperties.m_bMouseHovering = true; + for (const auto& pMark : m_marksToRender) { + if (pMark->m_renderedArea.contains(e->pos()) && !firstMarkHovered) { + pMark->m_bMouseHovering = true; firstMarkHovered = true; } else { - markProperties.m_bMouseHovering = false; + pMark->m_bMouseHovering = false; } - mark->setProperties(markProperties); } //qDebug() << "WOverview::mouseMoveEvent" << e->pos() << m_iPos; @@ -380,10 +374,8 @@ void WOverview::mousePressEvent(QMouseEvent* e) { void WOverview::leaveEvent(QEvent* e) { Q_UNUSED(e); - for (const auto& mark : m_marks) { - WaveformMarkProperties markProperties = mark->getProperties(); - markProperties.m_bMouseHovering = false; - mark->setProperties(markProperties); + for (const auto& pMark : m_marks) { + pMark->m_bMouseHovering = false; } m_bDrag = false; update(); @@ -621,15 +613,14 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga QRectF expandedLabelRect; for (int i = 0; i < m_marksToRender.size(); ++i) { - WaveformMarkProperties markProperties = m_marksToRender.at(i)->getProperties(); - + WaveformMarkPointer pMark = m_marksToRender.at(i); PainterScope painterScope(pPainter); //const float markPosition = 1.0 + // (m_marksToRender.at(i).m_pointControl->get() / (float)m_trackSamplesControl->get()) * (float)(width()-2); const float markPosition = offset + m_marksToRender.at(i)->getSamplePosition() * gain; - QPen shadowPen(QBrush(markProperties.borderColor()), 2.5 * m_scaleFactor); + QPen shadowPen(QBrush(pMark->borderColor()), 2.5 * m_scaleFactor); QLineF line; if (m_orientation == Qt::Horizontal) { @@ -640,20 +631,20 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga pPainter->setPen(shadowPen); pPainter->drawLine(line); - pPainter->setPen(markProperties.fillColor()); + pPainter->setPen(pMark->fillColor()); pPainter->drawLine(line); - if (!markProperties.m_text.isEmpty()) { - Qt::Alignment halign = markProperties.m_align & Qt::AlignHorizontal_Mask; - Qt::Alignment valign = markProperties.m_align & Qt::AlignVertical_Mask; + if (!pMark->m_text.isEmpty()) { + Qt::Alignment halign = pMark->m_align & Qt::AlignHorizontal_Mask; + Qt::Alignment valign = pMark->m_align & Qt::AlignVertical_Mask; QFontMetricsF metric(markerFont); - QString text = markProperties.m_text; + QString text = pMark->m_text; // Only allow the text to overlap the following mark if the mouse is // hovering over it. Otherwise, elide it if it would render over // the next label. - if (!markProperties.m_bMouseHovering && i < m_marksToRender.size()-1) { + if (!pMark->m_bMouseHovering && i < m_marksToRender.size()-1) { const float nextMarkPosition = offset + m_marksToRender.at(i+1)->getSamplePosition() * gain; text = metric.elidedText(text, Qt::ElideRight, nextMarkPosition - markPosition - 5); } @@ -700,47 +691,46 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga // top left of the QRectF. QPointF textTopLeft = QPointF(textPoint.x(), textPoint.y() - metric.height()); textRect.moveTo(textTopLeft); - markProperties.m_renderedArea = textRect; + pMark->m_renderedArea = textRect; - if (markProperties.m_bMouseHovering) { + if (pMark->m_bMouseHovering) { expandedLabelRect = textRect; } } else { // Placeholder to keep order textToRender.append(QString()); } - m_marksToRender.at(i)->setProperties(markProperties); } for (int n = 0; n < m_marksToRender.size(); ++n) { - WaveformMarkProperties markProperties = m_marksToRender.at(n)->getProperties(); - QPen shadowPen(QBrush(markProperties.borderColor()), 2.5 * m_scaleFactor); - if (!markProperties.m_renderedArea.intersects(expandedLabelRect) - || markProperties.m_bMouseHovering) { + WaveformMarkPointer pMark = m_marksToRender.at(n); + QPen shadowPen(QBrush(pMark->borderColor()), 2.5 * m_scaleFactor); + if (!pMark->m_renderedArea.intersects(expandedLabelRect) + || pMark->m_bMouseHovering) { pPainter->setPen(shadowPen); pPainter->setFont(shadowFont); - pPainter->drawText(markProperties.m_renderedArea.bottomLeft(), textToRender.at(n)); + pPainter->drawText(pMark->m_renderedArea.bottomLeft(), textToRender.at(n)); - pPainter->setPen(markProperties.m_textColor); + pPainter->setPen(pMark->m_textColor); pPainter->setFont(markerFont); - pPainter->drawText(markProperties.m_renderedArea.bottomLeft(), textToRender.at(n)); + pPainter->drawText(pMark->m_renderedArea.bottomLeft(), textToRender.at(n)); } - if (markProperties.m_bMouseHovering) { + if (pMark->m_bMouseHovering) { // Show cue position when hovered // TODO: hide duration of intro/outro if the cue position text would // overlap double markPosition = m_marksToRender.at(n)->getSamplePosition(); double markTime = samplePositionToSeconds(markPosition); QFontMetricsF metric(markerFont); - Qt::Alignment valign = markProperties.m_align & Qt::AlignVertical_Mask; - QPointF textPoint(markProperties.m_renderedArea.bottomLeft()); + Qt::Alignment valign = pMark->m_align & Qt::AlignVertical_Mask; + QPointF textPoint(pMark->m_renderedArea.bottomLeft()); if (valign == Qt::AlignTop) { textPoint.setY(float(height()) - 0.5f); } else { textPoint.setY(metric.height()); } - pPainter->setPen(markProperties.m_textColor); + pPainter->setPen(pMark->m_textColor); pPainter->setFont(markerFont); pPainter->drawText(textPoint, mixxx::Duration::formatTime(markTime)); } From f9a540fcd8c0af2d2496a59e374e21e99c840d7c Mon Sep 17 00:00:00 2001 From: Be Date: Thu, 15 Aug 2019 15:21:55 -0500 Subject: [PATCH 09/95] add right click menu on WOverview to edit hotcue label & color This is much easier to use and discover than finding the track in the library, right clicking it, opening the track Properties window, and going to the Cuepoints tab. --- build/depends.py | 1 + src/widget/cuemenu.cpp | 78 ++++++++++++++++++++++++++++++++++++++++ src/widget/cuemenu.h | 37 +++++++++++++++++++ src/widget/woverview.cpp | 39 ++++++++++++++++++-- src/widget/woverview.h | 3 ++ 5 files changed, 155 insertions(+), 3 deletions(-) create mode 100644 src/widget/cuemenu.cpp create mode 100644 src/widget/cuemenu.h diff --git a/build/depends.py b/build/depends.py index 87076cabf2f..589e87b26d5 100644 --- a/build/depends.py +++ b/build/depends.py @@ -907,6 +907,7 @@ def sources(self, build): "src/sources/soundsourceproxy.cpp", "src/widget/controlwidgetconnection.cpp", + "src/widget/cuemenu.cpp", "src/widget/wbasewidget.cpp", "src/widget/wwidget.cpp", "src/widget/wwidgetgroup.cpp", diff --git a/src/widget/cuemenu.cpp b/src/widget/cuemenu.cpp new file mode 100644 index 00000000000..b2ff5896519 --- /dev/null +++ b/src/widget/cuemenu.cpp @@ -0,0 +1,78 @@ +#include + +#include "widget/cuemenu.h" +#include "util/color/color.h" + +CueMenu::CueMenu(QWidget *parent) + : QMenu(parent) { + m_pEditLabel = new QAction(tr("Edit label")); + addAction(m_pEditLabel); + connect(m_pEditLabel, &QAction::triggered, this, &CueMenu::slotEditLabel); + + m_pColorMenu = new QMenu(this); + m_pColorMenu->setTitle(tr("Set color")); + addMenu(m_pColorMenu); + + for (const auto& pColor : Color::kPredefinedColorsSet.allColors) { + if (*pColor == *Color::kPredefinedColorsSet.noColor) { + continue; + } + + QAction* pColorAction = new QAction(pColor->m_sDisplayName); + QPixmap pixmap(80, 80); + pixmap.fill(pColor->m_defaultRgba); + pColorAction->setIcon(QIcon(pixmap)); + + m_pColorMenuActions.append(pColorAction); + m_pColorMenu->addAction(pColorAction); + connect(pColorAction, &QAction::triggered, this, [pColor, this]() { + changeCueColor(pColor); + }); + } + + m_pRemoveCue = new QAction(tr("Remove")); + addAction(m_pRemoveCue); + connect(m_pRemoveCue, &QAction::triggered, this, &CueMenu::slotRemoveCue); +} + +CueMenu::~CueMenu() { + delete m_pEditLabel; + for (auto& pAction : m_pColorMenuActions) { + delete pAction; + } + delete m_pColorMenu; + delete m_pRemoveCue; +} + +void CueMenu::slotEditLabel() { + VERIFY_OR_DEBUG_ASSERT(m_pCue != nullptr) { + return; + } + bool okay = false; + QString newLabel = QInputDialog::getText(this, tr("Edit cue label"), + tr("Cue label"), QLineEdit::Normal, + m_pCue->getLabel(), &okay); + if (okay) { + m_pCue->setLabel(newLabel); + } +} + +void CueMenu::changeCueColor(PredefinedColorPointer pColor) { + VERIFY_OR_DEBUG_ASSERT(m_pCue != nullptr) { + return; + } + VERIFY_OR_DEBUG_ASSERT(pColor != nullptr) { + return; + } + m_pCue->setColor(pColor); +} + +void CueMenu::slotRemoveCue() { + VERIFY_OR_DEBUG_ASSERT(m_pCue != nullptr) { + return; + } + VERIFY_OR_DEBUG_ASSERT(m_pTrack != nullptr) { + return; + } + m_pTrack->removeCue(m_pCue); +} diff --git a/src/widget/cuemenu.h b/src/widget/cuemenu.h new file mode 100644 index 00000000000..dca4685e633 --- /dev/null +++ b/src/widget/cuemenu.h @@ -0,0 +1,37 @@ +#pragma once + +#include + +#include "track/track.h" +#include "track/cue.h" + +class CueMenu : public QMenu { + Q_OBJECT + public: + CueMenu(QWidget *parent = nullptr); + ~CueMenu() override; + + void setCue(CuePointer pCue) { + m_pCue = pCue; + } + + void setTrack(TrackPointer pTrack) { + m_pTrack = pTrack; + } + + private slots: + void slotEditLabel(); + void slotRemoveCue(); + + private: + // This is not a Qt slot because it is connected via a lambda. + void changeCueColor(PredefinedColorPointer pColor); + + CuePointer m_pCue; + TrackPointer m_pTrack; + + QAction* m_pEditLabel; + QMenu* m_pColorMenu; + QList m_pColorMenuActions; + QAction* m_pRemoveCue; +}; diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index b542b6ccd59..65d792f51aa 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -54,6 +54,7 @@ WOverview::WOverview( m_group(group), m_pConfig(pConfig), m_endOfTrack(false), + m_pCueMenu(std::make_unique(this)), m_bDrag(false), m_iPos(0), m_orientation(Qt::Horizontal), @@ -362,14 +363,46 @@ void WOverview::mouseReleaseEvent(QMouseEvent* e) { double dValue = positionToValue(m_iPos); //qDebug() << "WOverview::mouseReleaseEvent" << e->pos() << m_iPos << ">>" << dValue; - setControlParameterUp(dValue); - m_bDrag = false; + if (e->button() == Qt::LeftButton) { + setControlParameterUp(dValue); + m_bDrag = false; + // Do not seek when releasing a right click. This is important to + // prevent accidental seeking when trying to right click a hotcue. + } } void WOverview::mousePressEvent(QMouseEvent* e) { //qDebug() << "WOverview::mousePressEvent" << e->pos(); mouseMoveEvent(e); - m_bDrag = true; + if (e->button() == Qt::LeftButton) { + m_bDrag = true; + } else { + if (m_pCurrentTrack != nullptr) { + QList cueList = m_pCurrentTrack->getCuePoints(); + for (const auto& pMark : m_marksToRender) { + if (pMark->m_bMouseHovering) { + // Currently the only way WaveformMarks can be associated + // with their respective Cue objects is by using the hotcue + // number. If cues without assigned hotcue are drawn on + // WOverview in the future, another way to associate + // WaveformMarks with Cues will need to be implemented. + CuePointer pHoveredCue; + for (const auto& pCue : cueList) { + if (pCue->getHotCue() == pMark->getHotCue()) { + pHoveredCue = pCue; + break; + } + } + VERIFY_OR_DEBUG_ASSERT(pHoveredCue != nullptr) { + continue; + } + m_pCueMenu->setCue(pHoveredCue); + m_pCueMenu->setTrack(m_pCurrentTrack); + m_pCueMenu->popup(e->globalPos()); + } + } + } + } } void WOverview::leaveEvent(QEvent* e) { diff --git a/src/widget/woverview.h b/src/widget/woverview.h index 4577f1fadeb..6de95166fdf 100644 --- a/src/widget/woverview.h +++ b/src/widget/woverview.h @@ -19,6 +19,7 @@ #include #include "track/track.h" +#include "widget/cuemenu.h" #include "widget/trackdroptarget.h" #include "widget/wwidget.h" #include "analyzer/analyzerprogress.h" @@ -138,6 +139,8 @@ class WOverview : public WWidget, public TrackDropTarget { TrackPointer m_pCurrentTrack; ConstWaveformPointer m_pWaveform; + std::unique_ptr m_pCueMenu; + // True if slider is dragged. Only used when m_bEventWhileDrag is false bool m_bDrag; // Internal storage of slider position in pixels From d2971e5b3b69e8b17b8b9d852e0bba393f0f3e08 Mon Sep 17 00:00:00 2001 From: Be Date: Thu, 15 Aug 2019 15:50:05 -0500 Subject: [PATCH 10/95] WOverview: jump to hotcue when clicking its label instead of the point under the cursor --- src/widget/woverview.cpp | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 65d792f51aa..b4aaa88b3e2 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -364,6 +364,14 @@ void WOverview::mouseReleaseEvent(QMouseEvent* e) { //qDebug() << "WOverview::mouseReleaseEvent" << e->pos() << m_iPos << ">>" << dValue; if (e->button() == Qt::LeftButton) { + // If a hotcue label is being hovered, jump to it instead of the point + // under the cursor. + for (const auto& pMark : m_marksToRender) { + if (pMark->m_bMouseHovering) { + dValue = pMark->getSamplePosition() / m_trackSamplesControl->get(); + break; + } + } setControlParameterUp(dValue); m_bDrag = false; // Do not seek when releasing a right click. This is important to @@ -374,13 +382,14 @@ void WOverview::mouseReleaseEvent(QMouseEvent* e) { void WOverview::mousePressEvent(QMouseEvent* e) { //qDebug() << "WOverview::mousePressEvent" << e->pos(); mouseMoveEvent(e); - if (e->button() == Qt::LeftButton) { - m_bDrag = true; - } else { - if (m_pCurrentTrack != nullptr) { - QList cueList = m_pCurrentTrack->getCuePoints(); - for (const auto& pMark : m_marksToRender) { - if (pMark->m_bMouseHovering) { + bool dragging = true; + if (m_pCurrentTrack != nullptr) { + QList cueList = m_pCurrentTrack->getCuePoints(); + for (const auto& pMark : m_marksToRender) { + if (pMark->m_bMouseHovering) { + if (e->button() == Qt::LeftButton) { + dragging = false; + } else { // Currently the only way WaveformMarks can be associated // with their respective Cue objects is by using the hotcue // number. If cues without assigned hotcue are drawn on @@ -403,6 +412,9 @@ void WOverview::mousePressEvent(QMouseEvent* e) { } } } + if (e->button() == Qt::LeftButton) { + m_bDrag = dragging; + } } void WOverview::leaveEvent(QEvent* e) { From 6e07761efc66e35e37c8d5b0ea717a81fc72e3fe Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 16 Aug 2019 15:33:40 -0500 Subject: [PATCH 11/95] mention hotcue right click menu in overview & hotcue tooltips --- src/skin/tooltips.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/skin/tooltips.cpp b/src/skin/tooltips.cpp index 1314c0f1195..bfb8c97476a 100644 --- a/src/skin/tooltips.cpp +++ b/src/skin/tooltips.cpp @@ -36,8 +36,9 @@ void Tooltips::addStandardTooltips() { add("waveform_overview") << tr("Waveform Overview") - << tr("Shows information about the track currently loaded in this channel.") + << tr("Shows information about the track currently loaded in this deck.") << tr("Jump around in the track by clicking anywhere on the waveform.") + << tr("Right click hotcues to edit their labels and colors.") << dropTracksHere; QString scratchMouse = tr("Use the mouse to scratch, spin-back or throw tracks."); @@ -538,7 +539,8 @@ void Tooltips::addStandardTooltips() { << QString("%1: %2").arg(leftClick, tr("If hotcue is set, jumps to the hotcue.")) << tr("If hotcue is not set, sets the hotcue to the current play position.") << quantizeSnap - << QString("%1: %2").arg(rightClick, tr("If hotcue is set, clears the hotcue.")); + << QString("%1: %2").arg(rightClick, tr("If hotcue is set, clears the hotcue.")) + << tr("Right click hotcues on the overview waveform to edit their labels and colors."); // Status displays and toggle buttons add("toggle_recording") From 5a4abb80984e58aa68d0413b0c6590bac44fe59b Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 16 Aug 2019 15:50:29 -0500 Subject: [PATCH 12/95] WOverview: draw playhead marker under cue labels --- src/widget/woverview.cpp | 81 +++++++++++++++++++++++----------------- src/widget/woverview.h | 3 ++ 2 files changed, 50 insertions(+), 34 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index b4aaa88b3e2..661c9067078 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -453,6 +453,7 @@ void WOverview::paintEvent(QPaintEvent * /*unused*/) { drawRangeMarks(&painter, offset, gain); drawMarks(&painter, offset, gain); drawCurrentPosition(&painter); + drawMarkLabels(&painter); } } } @@ -652,10 +653,13 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga // sorted in the order they appear on the waveform. // In the first loop, the lines are drawn and the text to render plus its // location are calculated. The text must be drawn in the second loop to - // prevent the lines of following WaveformMarks getting drawn over it. + // prevent the lines of following WaveformMarks getting drawn over it. The + // second loop is in the separate drawMarkLabels function so it can be + // called after drawCurrentPosition so the view of labels is not obscured + // by the playhead. - QList textToRender; - QRectF expandedLabelRect; + m_markLabelText.clear(); + m_expandedLabelRect = QRectF(); for (int i = 0; i < m_marksToRender.size(); ++i) { WaveformMarkPointer pMark = m_marksToRender.at(i); @@ -693,7 +697,7 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga const float nextMarkPosition = offset + m_marksToRender.at(i+1)->getSamplePosition() * gain; text = metric.elidedText(text, Qt::ElideRight, nextMarkPosition - markPosition - 5); } - textToRender.append(text); + m_markLabelText.append(text); QRectF textRect = metric.boundingRect(text); QPointF textPoint; @@ -739,26 +743,60 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga pMark->m_renderedArea = textRect; if (pMark->m_bMouseHovering) { - expandedLabelRect = textRect; + m_expandedLabelRect = textRect; } } else { // Placeholder to keep order - textToRender.append(QString()); + m_markLabelText.append(QString()); } } +} + +void WOverview::drawCurrentPosition(QPainter* pPainter) { + PainterScope painterScope(pPainter); + + if (m_orientation == Qt::Vertical) { + pPainter->setTransform(QTransform(0, 1, 1, 0, 0, 0)); + } + + pPainter->setPen(QPen(QBrush(m_qColorBackground), 1 * m_scaleFactor)); + pPainter->setOpacity(0.5); + pPainter->drawLine(m_iPos + 1, 0, m_iPos + 1, breadth()); + pPainter->drawLine(m_iPos - 1, 0, m_iPos - 1, breadth()); + + pPainter->setPen(QPen(m_signalColors.getPlayPosColor(), 1 * m_scaleFactor)); + pPainter->setOpacity(1.0); + pPainter->drawLine(m_iPos, 0, m_iPos, breadth()); + + pPainter->drawLine(m_iPos - 2, 0, m_iPos, 2); + pPainter->drawLine(m_iPos, 2, m_iPos + 2, 0); + pPainter->drawLine(m_iPos - 2, 0, m_iPos + 2, 0); + + pPainter->drawLine(m_iPos - 2, breadth() - 1, m_iPos, breadth() - 3); + pPainter->drawLine(m_iPos, breadth() - 3, m_iPos + 2, breadth() - 1); + pPainter->drawLine(m_iPos - 2, breadth() - 1, m_iPos + 2, breadth() - 1); +} + +void WOverview::drawMarkLabels(QPainter* pPainter) { + QFont markerFont = pPainter->font(); + markerFont.setPixelSize(10 * m_scaleFactor); + + QFont shadowFont = pPainter->font(); + shadowFont.setWeight(99); + shadowFont.setPixelSize(10 * m_scaleFactor); for (int n = 0; n < m_marksToRender.size(); ++n) { WaveformMarkPointer pMark = m_marksToRender.at(n); QPen shadowPen(QBrush(pMark->borderColor()), 2.5 * m_scaleFactor); - if (!pMark->m_renderedArea.intersects(expandedLabelRect) + if (!pMark->m_renderedArea.intersects(m_expandedLabelRect) || pMark->m_bMouseHovering) { pPainter->setPen(shadowPen); pPainter->setFont(shadowFont); - pPainter->drawText(pMark->m_renderedArea.bottomLeft(), textToRender.at(n)); + pPainter->drawText(pMark->m_renderedArea.bottomLeft(), m_markLabelText.at(n)); pPainter->setPen(pMark->m_textColor); pPainter->setFont(markerFont); - pPainter->drawText(pMark->m_renderedArea.bottomLeft(), textToRender.at(n)); + pPainter->drawText(pMark->m_renderedArea.bottomLeft(), m_markLabelText.at(n)); } if (pMark->m_bMouseHovering) { // Show cue position when hovered @@ -782,31 +820,6 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga } } -void WOverview::drawCurrentPosition(QPainter* pPainter) { - PainterScope painterScope(pPainter); - - if (m_orientation == Qt::Vertical) { - pPainter->setTransform(QTransform(0, 1, 1, 0, 0, 0)); - } - - pPainter->setPen(QPen(QBrush(m_qColorBackground), 1 * m_scaleFactor)); - pPainter->setOpacity(0.5); - pPainter->drawLine(m_iPos + 1, 0, m_iPos + 1, breadth()); - pPainter->drawLine(m_iPos - 1, 0, m_iPos - 1, breadth()); - - pPainter->setPen(QPen(m_signalColors.getPlayPosColor(), 1 * m_scaleFactor)); - pPainter->setOpacity(1.0); - pPainter->drawLine(m_iPos, 0, m_iPos, breadth()); - - pPainter->drawLine(m_iPos - 2, 0, m_iPos, 2); - pPainter->drawLine(m_iPos, 2, m_iPos + 2, 0); - pPainter->drawLine(m_iPos - 2, 0, m_iPos + 2, 0); - - pPainter->drawLine(m_iPos - 2, breadth() - 1, m_iPos, breadth() - 3); - pPainter->drawLine(m_iPos, breadth() - 3, m_iPos + 2, breadth() - 1); - pPainter->drawLine(m_iPos - 2, breadth() - 1, m_iPos + 2, breadth() - 1); -} - void WOverview::paintText(const QString& text, QPainter* pPainter) { PainterScope painterScope(pPainter); QColor lowColor = m_signalColors.getLowColor(); diff --git a/src/widget/woverview.h b/src/widget/woverview.h index 6de95166fdf..08c8c785f9a 100644 --- a/src/widget/woverview.h +++ b/src/widget/woverview.h @@ -114,6 +114,7 @@ class WOverview : public WWidget, public TrackDropTarget { void drawRangeMarks(QPainter* pPainter, const float& offset, const float& gain); void drawMarks(QPainter* pPainter, const float offset, const float gain); void drawCurrentPosition(QPainter* pPainter); + void drawMarkLabels(QPainter* pPainter); void paintText(const QString& text, QPainter* pPainter); double samplePositionToSeconds(double sample); inline int valueToPosition(double value) const { @@ -159,6 +160,8 @@ class WOverview : public WWidget, public TrackDropTarget { // List of visible WaveformMarks sorted by the order they appear in the track QList m_marksToRender; std::vector m_markRanges; + QList m_markLabelText; + QRectF m_expandedLabelRect; // Coefficient value-position linear transposition double m_a; From d2d6fc37d507b5adfa8ec206038bde8fe741ab73 Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 16 Aug 2019 16:41:32 -0500 Subject: [PATCH 13/95] add named constant for undefined cue point positions --- src/track/cue.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/track/cue.h b/src/track/cue.h index 6843dab1974..d4b3c343a47 100644 --- a/src/track/cue.h +++ b/src/track/cue.h @@ -34,6 +34,8 @@ class Cue : public QObject { OUTRO = 7, }; + static constexpr double kPositionNotDefined = -1.0; + ~Cue() override = default; bool isDirty() const; From acf83fe92d5dddba04be09f60346582a23c6e7b1 Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 16 Aug 2019 16:42:03 -0500 Subject: [PATCH 14/95] WOverview: fix non-hotcue WaveformMarks not rendering --- src/widget/woverview.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 661c9067078..dc2ce60f75b 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -323,6 +323,14 @@ void WOverview::updateCues(const QList &loadedCues) { m_marksToRender.append(pMark); } } + + // The loop above only adds WaveformMarks for hotcues to m_marksToRender. + for (const auto& pMark : m_marks) { + if (!m_marksToRender.contains(pMark) + && pMark->getSamplePosition() != Cue::kPositionNotDefined) { + m_marksToRender.append(pMark); + } + } std::sort(m_marksToRender.begin(), m_marksToRender.end()); } From 9f4423e9a73095402886af40f03e72b24df7be63 Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 16 Aug 2019 16:42:48 -0500 Subject: [PATCH 15/95] WOverview: hide WaveformMarkRange durations if they overlap cue position --- src/widget/woverview.cpp | 100 +++++++++++++++++++++++++-------------- src/widget/woverview.h | 2 +- 2 files changed, 65 insertions(+), 37 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index dc2ce60f75b..9e154464264 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -312,7 +312,7 @@ void WOverview::updateCues(const QList &loadedCues) { if (newLabel.isEmpty()) { newLabel = QString::number(hotcueNumber + 1); } else { - newLabel = QString("%1: %2").arg(hotcueNumber + 1).arg(newLabel); + newLabel = QString("%1: %2").arg(hotcueNumber + 1).arg(newLabel); } if (pMark->m_text != newLabel) { @@ -461,7 +461,7 @@ void WOverview::paintEvent(QPaintEvent * /*unused*/) { drawRangeMarks(&painter, offset, gain); drawMarks(&painter, offset, gain); drawCurrentPosition(&painter); - drawMarkLabels(&painter); + drawMarkLabels(&painter, offset, gain); } } } @@ -614,34 +614,6 @@ void WOverview::drawRangeMarks(QPainter* pPainter, const float& offset, const fl pPainter->drawRect(QRectF(QPointF(-2.0, startPosition), QPointF(width() + 1.0, endPosition))); } - - // draw duration of range - if (markRange.showDuration()) { - QString duration = mixxx::Duration::formatTime( - samplePositionToSeconds(endValue - startValue)); - - QFontMetrics fm(pPainter->font()); - int textWidth = fm.width(duration); - float padding = 3.0; - float x; - - WaveformMarkRange::DurationTextLocation textLocation = markRange.durationTextLocation(); - if (textLocation == WaveformMarkRange::DurationTextLocation::Before) { - x = startPosition - textWidth - padding; - } else { - x = endPosition + padding; - } - - // Ensure the right end of the text does not get cut off by - // the end of the track - if (x + textWidth > width()) { - x = width() - textWidth; - } - - pPainter->setOpacity(1.0); - pPainter->setPen(markRange.m_durationTextColor); - pPainter->drawText(QPointF(x, fm.ascent()), duration); - } } } @@ -785,14 +757,17 @@ void WOverview::drawCurrentPosition(QPainter* pPainter) { pPainter->drawLine(m_iPos - 2, breadth() - 1, m_iPos + 2, breadth() - 1); } -void WOverview::drawMarkLabels(QPainter* pPainter) { +void WOverview::drawMarkLabels(QPainter* pPainter, const float offset, const float gain) { QFont markerFont = pPainter->font(); markerFont.setPixelSize(10 * m_scaleFactor); + QFontMetricsF fontMetrics(markerFont); QFont shadowFont = pPainter->font(); shadowFont.setWeight(99); shadowFont.setPixelSize(10 * m_scaleFactor); + QRectF cuePositionRect; + for (int n = 0; n < m_marksToRender.size(); ++n) { WaveformMarkPointer pMark = m_marksToRender.at(n); QPen shadowPen(QBrush(pMark->borderColor()), 2.5 * m_scaleFactor); @@ -808,22 +783,75 @@ void WOverview::drawMarkLabels(QPainter* pPainter) { } if (pMark->m_bMouseHovering) { // Show cue position when hovered - // TODO: hide duration of intro/outro if the cue position text would - // overlap double markPosition = m_marksToRender.at(n)->getSamplePosition(); double markTime = samplePositionToSeconds(markPosition); - QFontMetricsF metric(markerFont); Qt::Alignment valign = pMark->m_align & Qt::AlignVertical_Mask; QPointF textPoint(pMark->m_renderedArea.bottomLeft()); if (valign == Qt::AlignTop) { textPoint.setY(float(height()) - 0.5f); } else { - textPoint.setY(metric.height()); + textPoint.setY(fontMetrics.height()); } + QString positionString = mixxx::Duration::formatTime(markTime); + pPainter->setPen(pMark->m_textColor); pPainter->setFont(markerFont); - pPainter->drawText(textPoint, mixxx::Duration::formatTime(markTime)); + pPainter->drawText(textPoint, positionString); + + cuePositionRect = fontMetrics.boundingRect(positionString); + // QPainter::drawText starts drawing with the given QPointF as + // the bottom left of the text, but QRectF::moveTo takes the new + // top left of the QRectF. + QPointF textTopLeft = QPointF(textPoint.x(), textPoint.y() - fontMetrics.height()); + cuePositionRect.moveTo(textTopLeft); + } + } + + // draw duration of ranges + for (auto&& markRange : m_markRanges) { + if (markRange.showDuration()) { + // Active mark ranges by definition have starts/ends that are not + // disabled. + const double startValue = markRange.start(); + const double endValue = markRange.end(); + + const float startPosition = offset + startValue * gain; + const float endPosition = offset + endValue * gain; + + if (startPosition < 0.0 && endPosition < 0.0) { + continue; + } + QString duration = mixxx::Duration::formatTime( + samplePositionToSeconds(endValue - startValue)); + + QRectF durationRect = fontMetrics.boundingRect(duration); + float padding = 3.0; + float x; + + WaveformMarkRange::DurationTextLocation textLocation = markRange.durationTextLocation(); + if (textLocation == WaveformMarkRange::DurationTextLocation::Before) { + x = startPosition - durationRect.width() - padding; + } else { + x = endPosition + padding; + } + + // Ensure the right end of the text does not get cut off by + // the end of the track + if (x + durationRect.width() > width()) { + x = width() - durationRect.width(); + } + + pPainter->setOpacity(1.0); + pPainter->setPen(markRange.m_durationTextColor); + // QPainter::drawText starts drawing with the given QPointF as + // the bottom left of the text, but QRectF::moveTo takes the new + // top left of the QRectF. + QPointF textTopLeft = QPointF(x, fontMetrics.ascent()); + durationRect.moveTo(textTopLeft); + if (!durationRect.intersects(cuePositionRect)) { + pPainter->drawText(QPointF(x, fontMetrics.ascent()), duration); + } } } } diff --git a/src/widget/woverview.h b/src/widget/woverview.h index 08c8c785f9a..b23017f3e61 100644 --- a/src/widget/woverview.h +++ b/src/widget/woverview.h @@ -114,7 +114,7 @@ class WOverview : public WWidget, public TrackDropTarget { void drawRangeMarks(QPainter* pPainter, const float& offset, const float& gain); void drawMarks(QPainter* pPainter, const float offset, const float gain); void drawCurrentPosition(QPainter* pPainter); - void drawMarkLabels(QPainter* pPainter); + void drawMarkLabels(QPainter* pPainter, const float offset, const float gain); void paintText(const QString& text, QPainter* pPainter); double samplePositionToSeconds(double sample); inline int valueToPosition(double value) const { From ee9e7453b6b3bf6fa20e06258479b7024dbc777e Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 16 Aug 2019 17:37:05 -0500 Subject: [PATCH 16/95] WOverview: don't render invisible WaveformMarks --- src/widget/woverview.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 9e154464264..ef08322dee7 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -327,7 +327,9 @@ void WOverview::updateCues(const QList &loadedCues) { // The loop above only adds WaveformMarks for hotcues to m_marksToRender. for (const auto& pMark : m_marks) { if (!m_marksToRender.contains(pMark) - && pMark->getSamplePosition() != Cue::kPositionNotDefined) { + && pMark->isValid() + && pMark->getSamplePosition() != Cue::kPositionNotDefined + && pMark->isVisible()) { m_marksToRender.append(pMark); } } From e4b041181484da66d354994534c7e848f28edb99 Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 16 Aug 2019 17:41:58 -0500 Subject: [PATCH 17/95] WOverview: avoid overlapping cue labels & cue position text --- src/widget/woverview.cpp | 76 ++++++++++++++++++++++++---------------- src/widget/woverview.h | 2 ++ 2 files changed, 47 insertions(+), 31 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index ef08322dee7..cacb8dbf1d8 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -642,6 +642,7 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga m_markLabelText.clear(); m_expandedLabelRect = QRectF(); + m_cuePositionRect = QRectF(); for (int i = 0; i < m_marksToRender.size(); ++i) { WaveformMarkPointer pMark = m_marksToRender.at(i); @@ -669,7 +670,7 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga Qt::Alignment halign = pMark->m_align & Qt::AlignHorizontal_Mask; Qt::Alignment valign = pMark->m_align & Qt::AlignVertical_Mask; - QFontMetricsF metric(markerFont); + QFontMetricsF fontMetrics(markerFont); QString text = pMark->m_text; // Only allow the text to overlap the following mark if the mouse is @@ -677,11 +678,11 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga // the next label. if (!pMark->m_bMouseHovering && i < m_marksToRender.size()-1) { const float nextMarkPosition = offset + m_marksToRender.at(i+1)->getSamplePosition() * gain; - text = metric.elidedText(text, Qt::ElideRight, nextMarkPosition - markPosition - 5); + text = fontMetrics.elidedText(text, Qt::ElideRight, nextMarkPosition - markPosition - 5); } m_markLabelText.append(text); - QRectF textRect = metric.boundingRect(text); + QRectF textRect = fontMetrics.boundingRect(text); QPointF textPoint; if (m_orientation == Qt::Horizontal) { if (halign == Qt::AlignLeft) { @@ -713,19 +714,51 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga } else if (valign == Qt::AlignVCenter) { textPoint.setY(markPosition + textRect.height() / 2); } else { // AlignBottom - textPoint.setY(markPosition + metric.ascent()); + textPoint.setY(markPosition + fontMetrics.ascent()); } } // QPainter::drawText starts drawing with the given QPointF as // the bottom left of the text, but QRectF::moveTo takes the new // top left of the QRectF. - QPointF textTopLeft = QPointF(textPoint.x(), textPoint.y() - metric.height()); + QPointF textTopLeft = QPointF(textPoint.x(), textPoint.y() - fontMetrics.height()); textRect.moveTo(textTopLeft); pMark->m_renderedArea = textRect; if (pMark->m_bMouseHovering) { m_expandedLabelRect = textRect; + + // Show cue position when hovered + // The area it will be drawn in needs to be calculated here + // before drawMarkLabels so drawMarkLabels can avoid drawing + // labels over the cue position. + // This can happen for example if the user shows the cue position + // of a hotcue which is near the intro end position because the + // intro_end_position WaveformMark label is drawn at the top. + // However, the drawing of this text needs to happen in + // drawMarkLabels so none of the WaveformMark lines are drawn + // on top of the position text. + + // WaveformMark::m_align refers to the alignment of the label, + // so if the label is on bottom draw the duration on top and + // vice versa. + Qt::Alignment valign = pMark->m_align & Qt::AlignVertical_Mask; + QPointF positionTextPoint(textRect.bottomLeft()); + if (valign == Qt::AlignTop) { + positionTextPoint.setY(float(height()) - 0.5f); + } else { + positionTextPoint.setY(fontMetrics.height()); + } + + double markTime = samplePositionToSeconds(pMark->getSamplePosition()); + m_hoveredCuePositionText = mixxx::Duration::formatTime(markTime); + m_cuePositionRect = fontMetrics.boundingRect(m_hoveredCuePositionText); + // QPainter::drawText starts drawing with the given QPointF as + // the bottom left of the text, but QRectF::moveTo takes the new + // top left of the QRectF. + QPointF textTopLeft = QPointF(positionTextPoint.x(), + positionTextPoint.y() - fontMetrics.height()); + m_cuePositionRect.moveTo(textTopLeft); } } else { // Placeholder to keep order @@ -768,12 +801,12 @@ void WOverview::drawMarkLabels(QPainter* pPainter, const float offset, const flo shadowFont.setWeight(99); shadowFont.setPixelSize(10 * m_scaleFactor); - QRectF cuePositionRect; - + // Draw WaveformMark labels for (int n = 0; n < m_marksToRender.size(); ++n) { WaveformMarkPointer pMark = m_marksToRender.at(n); QPen shadowPen(QBrush(pMark->borderColor()), 2.5 * m_scaleFactor); - if (!pMark->m_renderedArea.intersects(m_expandedLabelRect) + if ((!pMark->m_renderedArea.intersects(m_expandedLabelRect) + && !pMark->m_renderedArea.intersects(m_cuePositionRect)) || pMark->m_bMouseHovering) { pPainter->setPen(shadowPen); pPainter->setFont(shadowFont); @@ -783,34 +816,15 @@ void WOverview::drawMarkLabels(QPainter* pPainter, const float offset, const flo pPainter->setFont(markerFont); pPainter->drawText(pMark->m_renderedArea.bottomLeft(), m_markLabelText.at(n)); } + // Draw the position text if (pMark->m_bMouseHovering) { - // Show cue position when hovered - double markPosition = m_marksToRender.at(n)->getSamplePosition(); - double markTime = samplePositionToSeconds(markPosition); - Qt::Alignment valign = pMark->m_align & Qt::AlignVertical_Mask; - QPointF textPoint(pMark->m_renderedArea.bottomLeft()); - if (valign == Qt::AlignTop) { - textPoint.setY(float(height()) - 0.5f); - } else { - textPoint.setY(fontMetrics.height()); - } - - QString positionString = mixxx::Duration::formatTime(markTime); - pPainter->setPen(pMark->m_textColor); pPainter->setFont(markerFont); - pPainter->drawText(textPoint, positionString); - - cuePositionRect = fontMetrics.boundingRect(positionString); - // QPainter::drawText starts drawing with the given QPointF as - // the bottom left of the text, but QRectF::moveTo takes the new - // top left of the QRectF. - QPointF textTopLeft = QPointF(textPoint.x(), textPoint.y() - fontMetrics.height()); - cuePositionRect.moveTo(textTopLeft); + pPainter->drawText(m_cuePositionRect.bottomLeft(), m_hoveredCuePositionText); } } - // draw duration of ranges + // draw duration of WaveformMarkRanges for (auto&& markRange : m_markRanges) { if (markRange.showDuration()) { // Active mark ranges by definition have starts/ends that are not @@ -851,7 +865,7 @@ void WOverview::drawMarkLabels(QPainter* pPainter, const float offset, const flo // top left of the QRectF. QPointF textTopLeft = QPointF(x, fontMetrics.ascent()); durationRect.moveTo(textTopLeft); - if (!durationRect.intersects(cuePositionRect)) { + if (!durationRect.intersects(m_cuePositionRect)) { pPainter->drawText(QPointF(x, fontMetrics.ascent()), duration); } } diff --git a/src/widget/woverview.h b/src/widget/woverview.h index b23017f3e61..93c978ec3f1 100644 --- a/src/widget/woverview.h +++ b/src/widget/woverview.h @@ -162,6 +162,8 @@ class WOverview : public WWidget, public TrackDropTarget { std::vector m_markRanges; QList m_markLabelText; QRectF m_expandedLabelRect; + QString m_hoveredCuePositionText; + QRectF m_cuePositionRect; // Coefficient value-position linear transposition double m_a; From 90e6112b84881ebabc9236f56ac060d31b39f0b5 Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 16 Aug 2019 18:49:26 -0500 Subject: [PATCH 18/95] WOverview: show cue position & label when hovering line, not just label --- src/waveform/renderers/waveformmark.h | 3 ++- src/widget/woverview.cpp | 28 +++++++++++++++++++++------ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/waveform/renderers/waveformmark.h b/src/waveform/renderers/waveformmark.h index fc6f7fde4bf..4b4d0dbaf4b 100644 --- a/src/waveform/renderers/waveformmark.h +++ b/src/waveform/renderers/waveformmark.h @@ -71,7 +71,8 @@ class WaveformMark { Qt::Alignment m_align; QString m_pixmapPath; - QRectF m_renderedArea; + QRectF m_labelArea; + float m_linePosition; bool m_bMouseHovering; private: diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index cacb8dbf1d8..b66a11cd69c 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -355,8 +355,23 @@ void WOverview::mouseMoveEvent(QMouseEvent* e) { // mark, then drag the cursor over another, and the second one's label text // would be drawn under the first. bool firstMarkHovered = false; + // Without some padding, the user would only have a single pixel width that + // would count as hovering over the WaveformMark. + float lineHoverPadding = 3.0; for (const auto& pMark : m_marksToRender) { - if (pMark->m_renderedArea.contains(e->pos()) && !firstMarkHovered) { + int hoveredPosition; + if (m_orientation == Qt::Horizontal) { + hoveredPosition = e->x(); + } else { + hoveredPosition = e->y(); + } + bool lineHovered = + pMark->m_linePosition >= hoveredPosition - lineHoverPadding + && pMark->m_linePosition <= hoveredPosition + lineHoverPadding; + + if ((pMark->m_labelArea.contains(e->pos()) + || lineHovered) + && !firstMarkHovered) { pMark->m_bMouseHovering = true; firstMarkHovered = true; } else { @@ -651,6 +666,7 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga //const float markPosition = 1.0 + // (m_marksToRender.at(i).m_pointControl->get() / (float)m_trackSamplesControl->get()) * (float)(width()-2); const float markPosition = offset + m_marksToRender.at(i)->getSamplePosition() * gain; + pMark->m_linePosition = markPosition; QPen shadowPen(QBrush(pMark->borderColor()), 2.5 * m_scaleFactor); @@ -723,7 +739,7 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga // top left of the QRectF. QPointF textTopLeft = QPointF(textPoint.x(), textPoint.y() - fontMetrics.height()); textRect.moveTo(textTopLeft); - pMark->m_renderedArea = textRect; + pMark->m_labelArea = textRect; if (pMark->m_bMouseHovering) { m_expandedLabelRect = textRect; @@ -805,16 +821,16 @@ void WOverview::drawMarkLabels(QPainter* pPainter, const float offset, const flo for (int n = 0; n < m_marksToRender.size(); ++n) { WaveformMarkPointer pMark = m_marksToRender.at(n); QPen shadowPen(QBrush(pMark->borderColor()), 2.5 * m_scaleFactor); - if ((!pMark->m_renderedArea.intersects(m_expandedLabelRect) - && !pMark->m_renderedArea.intersects(m_cuePositionRect)) + if ((!pMark->m_labelArea.intersects(m_expandedLabelRect) + && !pMark->m_labelArea.intersects(m_cuePositionRect)) || pMark->m_bMouseHovering) { pPainter->setPen(shadowPen); pPainter->setFont(shadowFont); - pPainter->drawText(pMark->m_renderedArea.bottomLeft(), m_markLabelText.at(n)); + pPainter->drawText(pMark->m_labelArea.bottomLeft(), m_markLabelText.at(n)); pPainter->setPen(pMark->m_textColor); pPainter->setFont(markerFont); - pPainter->drawText(pMark->m_renderedArea.bottomLeft(), m_markLabelText.at(n)); + pPainter->drawText(pMark->m_labelArea.bottomLeft(), m_markLabelText.at(n)); } // Draw the position text if (pMark->m_bMouseHovering) { From 2c1dd9551481b031cf7a6505842caee63841bfae Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 16 Aug 2019 19:20:29 -0500 Subject: [PATCH 19/95] WOverview: make WaveformMarks without text labels hoverable --- src/widget/woverview.cpp | 68 +++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index b66a11cd69c..e6f03e63d59 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -637,6 +637,7 @@ void WOverview::drawRangeMarks(QPainter* pPainter, const float& offset, const fl void WOverview::drawMarks(QPainter* pPainter, const float offset, const float gain) { QFont markerFont = pPainter->font(); markerFont.setPixelSize(10 * m_scaleFactor); + QFontMetricsF fontMetrics(markerFont); QFont shadowFont = pPainter->font(); shadowFont.setWeight(99); @@ -686,7 +687,6 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga Qt::Alignment halign = pMark->m_align & Qt::AlignHorizontal_Mask; Qt::Alignment valign = pMark->m_align & Qt::AlignVertical_Mask; - QFontMetricsF fontMetrics(markerFont); QString text = pMark->m_text; // Only allow the text to overlap the following mark if the mouse is @@ -743,43 +743,45 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga if (pMark->m_bMouseHovering) { m_expandedLabelRect = textRect; - - // Show cue position when hovered - // The area it will be drawn in needs to be calculated here - // before drawMarkLabels so drawMarkLabels can avoid drawing - // labels over the cue position. - // This can happen for example if the user shows the cue position - // of a hotcue which is near the intro end position because the - // intro_end_position WaveformMark label is drawn at the top. - // However, the drawing of this text needs to happen in - // drawMarkLabels so none of the WaveformMark lines are drawn - // on top of the position text. - - // WaveformMark::m_align refers to the alignment of the label, - // so if the label is on bottom draw the duration on top and - // vice versa. - Qt::Alignment valign = pMark->m_align & Qt::AlignVertical_Mask; - QPointF positionTextPoint(textRect.bottomLeft()); - if (valign == Qt::AlignTop) { - positionTextPoint.setY(float(height()) - 0.5f); - } else { - positionTextPoint.setY(fontMetrics.height()); - } - - double markTime = samplePositionToSeconds(pMark->getSamplePosition()); - m_hoveredCuePositionText = mixxx::Duration::formatTime(markTime); - m_cuePositionRect = fontMetrics.boundingRect(m_hoveredCuePositionText); - // QPainter::drawText starts drawing with the given QPointF as - // the bottom left of the text, but QRectF::moveTo takes the new - // top left of the QRectF. - QPointF textTopLeft = QPointF(positionTextPoint.x(), - positionTextPoint.y() - fontMetrics.height()); - m_cuePositionRect.moveTo(textTopLeft); } } else { // Placeholder to keep order m_markLabelText.append(QString()); } + + // Show cue position when hovered + // The area it will be drawn in needs to be calculated here + // before drawMarkLabels so drawMarkLabels can avoid drawing + // labels over the cue position. + // This can happen for example if the user shows the cue position + // of a hotcue which is near the intro end position because the + // intro_end_position WaveformMark label is drawn at the top. + // However, the drawing of this text needs to happen in + // drawMarkLabels so none of the WaveformMark lines are drawn + // on top of the position text. + + // WaveformMark::m_align refers to the alignment of the label, + // so if the label is on bottom draw the duration on top and + // vice versa. + if (pMark->m_bMouseHovering) { + Qt::Alignment valign = pMark->m_align & Qt::AlignVertical_Mask; + QPointF positionTextPoint(markPosition, 0); + if (valign == Qt::AlignTop) { + positionTextPoint.setY(float(height()) - 0.5f); + } else { + positionTextPoint.setY(fontMetrics.height()); + } + + double markTime = samplePositionToSeconds(pMark->getSamplePosition()); + m_hoveredCuePositionText = mixxx::Duration::formatTime(markTime); + m_cuePositionRect = fontMetrics.boundingRect(m_hoveredCuePositionText); + // QPainter::drawText starts drawing with the given QPointF as + // the bottom left of the text, but QRectF::moveTo takes the new + // top left of the QRectF. + QPointF textTopLeft = QPointF(positionTextPoint.x(), + positionTextPoint.y() - fontMetrics.height()); + m_cuePositionRect.moveTo(textTopLeft); + } } } From 0079d64dbcf51862dd14f03d04f92932e0b06bb9 Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 16 Aug 2019 19:34:12 -0500 Subject: [PATCH 20/95] WOverview: only show hotcue right click menu for hotcues --- src/widget/woverview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index e6f03e63d59..50852825893 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -414,7 +414,7 @@ void WOverview::mousePressEvent(QMouseEvent* e) { if (pMark->m_bMouseHovering) { if (e->button() == Qt::LeftButton) { dragging = false; - } else { + } else if (pMark->getHotCue() != WaveformMark::kNoHotCue) { // Currently the only way WaveformMarks can be associated // with their respective Cue objects is by using the hotcue // number. If cues without assigned hotcue are drawn on From 7539a2f4e2c7b3e4c3a1030f512754aa7e3cef23 Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 16 Aug 2019 19:59:11 -0500 Subject: [PATCH 21/95] WOverview: always show at least the hotcue number if label elided --- src/widget/woverview.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 50852825893..8d5d8d85798 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -696,6 +696,13 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga const float nextMarkPosition = offset + m_marksToRender.at(i+1)->getSamplePosition() * gain; text = fontMetrics.elidedText(text, Qt::ElideRight, nextMarkPosition - markPosition - 5); } + // Sometimes QFontMetrics::elidedText turns the QString into just an + // elipsis character, so always show at least the hotcue number if + // the label does not fit. + if ((text.isEmpty() || text == "…") + && pMark->getHotCue() != WaveformMark::kNoHotCue) { + text = QString::number(pMark->getHotCue()+1); + } m_markLabelText.append(text); QRectF textRect = fontMetrics.boundingRect(text); From bb2bc08cffd3be26f9067db668060e41d369fd3d Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 16 Aug 2019 20:10:50 -0500 Subject: [PATCH 22/95] WOverview: shift cue labels left if they would exceed width of widget --- src/widget/woverview.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 8d5d8d85798..fcd3e4e885b 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -746,6 +746,11 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga // top left of the QRectF. QPointF textTopLeft = QPointF(textPoint.x(), textPoint.y() - fontMetrics.height()); textRect.moveTo(textTopLeft); + // If the right end of the label would get cut off, shift the label + // left so it fits. + if (textRect.right() > width()) { + textRect.setLeft(width() - textRect.width()); + } pMark->m_labelArea = textRect; if (pMark->m_bMouseHovering) { @@ -833,6 +838,8 @@ void WOverview::drawMarkLabels(QPainter* pPainter, const float offset, const flo if ((!pMark->m_labelArea.intersects(m_expandedLabelRect) && !pMark->m_labelArea.intersects(m_cuePositionRect)) || pMark->m_bMouseHovering) { + + pPainter->setPen(shadowPen); pPainter->setFont(shadowFont); pPainter->drawText(pMark->m_labelArea.bottomLeft(), m_markLabelText.at(n)); From 9a30992a15f1245406186e5233c9e6f8bdf82382 Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 16 Aug 2019 20:47:08 -0500 Subject: [PATCH 23/95] WOverview: shift cue position text left if it would get cut off --- src/widget/woverview.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index fcd3e4e885b..575a02e654e 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -793,6 +793,12 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga QPointF textTopLeft = QPointF(positionTextPoint.x(), positionTextPoint.y() - fontMetrics.height()); m_cuePositionRect.moveTo(textTopLeft); + + // If the right end of the text would get cut off, shift text left + // so it fits. + if (m_cuePositionRect.right() > width()) { + m_cuePositionRect.setLeft(width() - m_cuePositionRect.width()); + } } } } From 35efc63067051902ae00481191ce58b5009b2a8d Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 16 Aug 2019 20:47:25 -0500 Subject: [PATCH 24/95] WOverview: avoid drawing overlapping cue labels --- src/widget/woverview.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 575a02e654e..d255e642cd2 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -837,6 +837,7 @@ void WOverview::drawMarkLabels(QPainter* pPainter, const float offset, const flo shadowFont.setWeight(99); shadowFont.setPixelSize(10 * m_scaleFactor); + bool firstOverlappingLabelRendered = false; // Draw WaveformMark labels for (int n = 0; n < m_marksToRender.size(); ++n) { WaveformMarkPointer pMark = m_marksToRender.at(n); @@ -845,6 +846,25 @@ void WOverview::drawMarkLabels(QPainter* pPainter, const float offset, const flo && !pMark->m_labelArea.intersects(m_cuePositionRect)) || pMark->m_bMouseHovering) { + // If labels would overlap, only draw the first one. + bool skip = false; + for (const auto& otherMark : m_marksToRender) { + if (otherMark != pMark + && pMark->m_labelArea.intersects(otherMark->m_labelArea)) { + + if (firstOverlappingLabelRendered) { + skip = true; + break; + } else { + skip = false; + firstOverlappingLabelRendered = true; + break; + } + } + } + if (skip) { + continue; + } pPainter->setPen(shadowPen); pPainter->setFont(shadowFont); From 885efe769ee9de6c66cfb4e9f4a34224b7b468f2 Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 16 Aug 2019 20:54:21 -0500 Subject: [PATCH 25/95] WOverview: remove code that is now redundant --- src/widget/woverview.cpp | 8 +------- src/widget/woverview.h | 1 - 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index d255e642cd2..35aae2d09ad 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -657,7 +657,6 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga // by the playhead. m_markLabelText.clear(); - m_expandedLabelRect = QRectF(); m_cuePositionRect = QRectF(); for (int i = 0; i < m_marksToRender.size(); ++i) { @@ -752,10 +751,6 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga textRect.setLeft(width() - textRect.width()); } pMark->m_labelArea = textRect; - - if (pMark->m_bMouseHovering) { - m_expandedLabelRect = textRect; - } } else { // Placeholder to keep order m_markLabelText.append(QString()); @@ -842,8 +837,7 @@ void WOverview::drawMarkLabels(QPainter* pPainter, const float offset, const flo for (int n = 0; n < m_marksToRender.size(); ++n) { WaveformMarkPointer pMark = m_marksToRender.at(n); QPen shadowPen(QBrush(pMark->borderColor()), 2.5 * m_scaleFactor); - if ((!pMark->m_labelArea.intersects(m_expandedLabelRect) - && !pMark->m_labelArea.intersects(m_cuePositionRect)) + if (!pMark->m_labelArea.intersects(m_cuePositionRect) || pMark->m_bMouseHovering) { // If labels would overlap, only draw the first one. diff --git a/src/widget/woverview.h b/src/widget/woverview.h index 93c978ec3f1..c8d63ac152b 100644 --- a/src/widget/woverview.h +++ b/src/widget/woverview.h @@ -161,7 +161,6 @@ class WOverview : public WWidget, public TrackDropTarget { QList m_marksToRender; std::vector m_markRanges; QList m_markLabelText; - QRectF m_expandedLabelRect; QString m_hoveredCuePositionText; QRectF m_cuePositionRect; From 0fb3b36605a31ff1018ed2b87fe47fefb79fb116 Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 16 Aug 2019 20:59:36 -0500 Subject: [PATCH 26/95] fix typo in comment --- src/widget/woverview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 35aae2d09ad..07e662f33c8 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -768,7 +768,7 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga // on top of the position text. // WaveformMark::m_align refers to the alignment of the label, - // so if the label is on bottom draw the duration on top and + // so if the label is on bottom draw the position text on top and // vice versa. if (pMark->m_bMouseHovering) { Qt::Alignment valign = pMark->m_align & Qt::AlignVertical_Mask; From 462e3bdaa9824c183bd8c3e763bb243946f6d772 Mon Sep 17 00:00:00 2001 From: Be Date: Sat, 17 Aug 2019 00:17:27 -0500 Subject: [PATCH 27/95] WOverview: show time until cue when hovering --- src/widget/woverview.cpp | 33 ++++++++++++++++++++++++++++----- src/widget/woverview.h | 5 ++++- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 07e662f33c8..d4a10122611 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -75,6 +75,7 @@ WOverview::WOverview( m_trackSampleRateControl = new ControlProxy(m_group, "track_samplerate", this); m_trackSamplesControl = new ControlProxy(m_group, "track_samples", this); + m_playpositionControl = new ControlProxy(m_group, "playposition", this); setAcceptDrops(true); setMouseTracking(true); @@ -658,6 +659,7 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga m_markLabelText.clear(); m_cuePositionRect = QRectF(); + m_cueTimeDistanceRect = QRectF(); for (int i = 0; i < m_marksToRender.size(); ++i) { WaveformMarkPointer pMark = m_marksToRender.at(i); @@ -779,9 +781,21 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga positionTextPoint.setY(fontMetrics.height()); } - double markTime = samplePositionToSeconds(pMark->getSamplePosition()); - m_hoveredCuePositionText = mixxx::Duration::formatTime(markTime); - m_cuePositionRect = fontMetrics.boundingRect(m_hoveredCuePositionText); + double markSamples = pMark->getSamplePosition(); + double currentPositionSamples = m_playpositionControl->get() * m_trackSamplesControl->get(); + double markTime = samplePositionToSeconds(markSamples); + double markTimeDistance = samplePositionToSeconds(markSamples - currentPositionSamples); + m_cuePositionText = mixxx::Duration::formatTime(markTime); + // Do not show the time until the cue point if the playhead is past + // the cue point. + if (markTimeDistance > 0) { + m_cueTimeDistanceText = mixxx::Duration::formatTime(markTimeDistance); + } else { + m_cueTimeDistanceText = QString(); + } + m_cuePositionRect = fontMetrics.boundingRect(m_cuePositionText); + m_cueTimeDistanceRect = fontMetrics.boundingRect(m_cueTimeDistanceText); + // QPainter::drawText starts drawing with the given QPointF as // the bottom left of the text, but QRectF::moveTo takes the new // top left of the QRectF. @@ -789,11 +803,18 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga positionTextPoint.y() - fontMetrics.height()); m_cuePositionRect.moveTo(textTopLeft); + QPointF distanceTextTopLeft(markPosition, + (height() / 2) - fontMetrics.height()); + m_cueTimeDistanceRect.moveTo(distanceTextTopLeft); + // If the right end of the text would get cut off, shift text left // so it fits. if (m_cuePositionRect.right() > width()) { m_cuePositionRect.setLeft(width() - m_cuePositionRect.width()); } + if (m_cueTimeDistanceRect.right() > width()) { + m_cueTimeDistanceRect.setLeft(width() - m_cueTimeDistanceRect.width()); + } } } } @@ -837,7 +858,8 @@ void WOverview::drawMarkLabels(QPainter* pPainter, const float offset, const flo for (int n = 0; n < m_marksToRender.size(); ++n) { WaveformMarkPointer pMark = m_marksToRender.at(n); QPen shadowPen(QBrush(pMark->borderColor()), 2.5 * m_scaleFactor); - if (!pMark->m_labelArea.intersects(m_cuePositionRect) + if ((!pMark->m_labelArea.intersects(m_cuePositionRect) + && !pMark->m_labelArea.intersects(m_cueTimeDistanceRect)) || pMark->m_bMouseHovering) { // If labels would overlap, only draw the first one. @@ -872,7 +894,8 @@ void WOverview::drawMarkLabels(QPainter* pPainter, const float offset, const flo if (pMark->m_bMouseHovering) { pPainter->setPen(pMark->m_textColor); pPainter->setFont(markerFont); - pPainter->drawText(m_cuePositionRect.bottomLeft(), m_hoveredCuePositionText); + pPainter->drawText(m_cuePositionRect.bottomLeft(), m_cuePositionText); + pPainter->drawText(m_cueTimeDistanceRect.bottomLeft(), m_cueTimeDistanceText); } } diff --git a/src/widget/woverview.h b/src/widget/woverview.h index c8d63ac152b..50eaaa7faa1 100644 --- a/src/widget/woverview.h +++ b/src/widget/woverview.h @@ -135,6 +135,7 @@ class WOverview : public WWidget, public TrackDropTarget { ControlProxy* m_pRateSliderControl; ControlProxy* m_trackSampleRateControl; ControlProxy* m_trackSamplesControl; + ControlProxy* m_playpositionControl; // Current active track TrackPointer m_pCurrentTrack; @@ -161,8 +162,10 @@ class WOverview : public WWidget, public TrackDropTarget { QList m_marksToRender; std::vector m_markRanges; QList m_markLabelText; - QString m_hoveredCuePositionText; + QString m_cuePositionText; QRectF m_cuePositionRect; + QString m_cueTimeDistanceText; + QRectF m_cueTimeDistanceRect; // Coefficient value-position linear transposition double m_a; From 31c3814f9f1caeae5e491ef25ad77867ce6b4338 Mon Sep 17 00:00:00 2001 From: Be Date: Sat, 17 Aug 2019 01:57:47 -0500 Subject: [PATCH 28/95] WOverview: show time position on right click --- src/widget/woverview.cpp | 142 +++++++++++++++++++++++++++++++++------ src/widget/woverview.h | 7 ++ 2 files changed, 127 insertions(+), 22 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index d4a10122611..9219ea23be2 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -57,6 +57,7 @@ WOverview::WOverview( m_pCueMenu(std::make_unique(this)), m_bDrag(false), m_iPos(0), + m_bTimeRulerActive(false), m_orientation(Qt::Horizontal), m_a(1.0), m_b(0.0), @@ -352,6 +353,14 @@ void WOverview::mouseMoveEvent(QMouseEvent* e) { } } + // Do not activate cue hovering while right click is held down and the + // button down event was not on a cue. + if (m_bTimeRulerActive) { + m_timeRulerPos = e->pos(); + update(); + return; + } + // Without tracking the first hovered WaveformMark, the user could hover one // mark, then drag the cursor over another, and the second one's label text // would be drawn under the first. @@ -402,6 +411,8 @@ void WOverview::mouseReleaseEvent(QMouseEvent* e) { m_bDrag = false; // Do not seek when releasing a right click. This is important to // prevent accidental seeking when trying to right click a hotcue. + } else if (e->button() == Qt::RightButton) { + m_bTimeRulerActive = false; } } @@ -409,6 +420,7 @@ void WOverview::mousePressEvent(QMouseEvent* e) { //qDebug() << "WOverview::mousePressEvent" << e->pos(); mouseMoveEvent(e); bool dragging = true; + bool hotcueRightClicked = false; if (m_pCurrentTrack != nullptr) { QList cueList = m_pCurrentTrack->getCuePoints(); for (const auto& pMark : m_marksToRender) { @@ -425,6 +437,7 @@ void WOverview::mousePressEvent(QMouseEvent* e) { for (const auto& pCue : cueList) { if (pCue->getHotCue() == pMark->getHotCue()) { pHoveredCue = pCue; + hotcueRightClicked = true; break; } } @@ -440,6 +453,9 @@ void WOverview::mousePressEvent(QMouseEvent* e) { } if (e->button() == Qt::LeftButton) { m_bDrag = dragging; + } else if (e->button() == Qt::RightButton && !hotcueRightClicked) { + m_bTimeRulerActive = true; + m_timeRulerPos = e->pos(); } } @@ -449,6 +465,7 @@ void WOverview::leaveEvent(QEvent* e) { pMark->m_bMouseHovering = false; } m_bDrag = false; + m_bTimeRulerActive = false; update(); } @@ -479,6 +496,7 @@ void WOverview::paintEvent(QPaintEvent * /*unused*/) { drawRangeMarks(&painter, offset, gain); drawMarks(&painter, offset, gain); drawCurrentPosition(&painter); + drawTimeRuler(&painter); drawMarkLabels(&painter, offset, gain); } } @@ -747,11 +765,7 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga // top left of the QRectF. QPointF textTopLeft = QPointF(textPoint.x(), textPoint.y() - fontMetrics.height()); textRect.moveTo(textTopLeft); - // If the right end of the label would get cut off, shift the label - // left so it fits. - if (textRect.right() > width()) { - textRect.setLeft(width() - textRect.width()); - } + shiftRectIfCutOff(&textRect); pMark->m_labelArea = textRect; } else { // Placeholder to keep order @@ -807,14 +821,8 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga (height() / 2) - fontMetrics.height()); m_cueTimeDistanceRect.moveTo(distanceTextTopLeft); - // If the right end of the text would get cut off, shift text left - // so it fits. - if (m_cuePositionRect.right() > width()) { - m_cuePositionRect.setLeft(width() - m_cuePositionRect.width()); - } - if (m_cueTimeDistanceRect.right() > width()) { - m_cueTimeDistanceRect.setLeft(width() - m_cueTimeDistanceRect.width()); - } + shiftRectIfCutOff(&m_cuePositionRect); + shiftRectIfCutOff(&m_cueTimeDistanceRect); } } } @@ -844,6 +852,84 @@ void WOverview::drawCurrentPosition(QPainter* pPainter) { pPainter->drawLine(m_iPos - 2, breadth() - 1, m_iPos + 2, breadth() - 1); } +void WOverview::drawTimeRuler(QPainter* pPainter) { + QFont markerFont = pPainter->font(); + markerFont.setPixelSize(10 * m_scaleFactor); + QFontMetricsF fontMetrics(markerFont); + + QFont shadowFont = pPainter->font(); + shadowFont.setWeight(99); + shadowFont.setPixelSize(10 * m_scaleFactor); + QPen shadowPen(Qt::black, 2.5 * m_scaleFactor); + + m_timeRulerTextArea = QRectF(); + m_timeRulerDistanceTextArea = QRectF(); + + if (m_bTimeRulerActive) { + QLineF line; + if (m_orientation == Qt::Horizontal) { + line.setLine(m_timeRulerPos.x(), 0.0, m_timeRulerPos.x(), static_cast(height())); + } else { + line.setLine(0.0, m_timeRulerPos.x(), static_cast(width()), m_timeRulerPos.x()); + } + pPainter->setPen(shadowPen); + pPainter->drawLine(line); + + pPainter->setPen(Qt::green); + pPainter->drawLine(line); + + QPointF textPoint = m_timeRulerPos; + QPointF textPointDistance = m_timeRulerPos; + double widgetPositionFraction; + double padding = 1.0; // spacing between line and text + if (m_orientation == Qt::Horizontal) { + textPoint = QPointF(textPoint.x() + padding, fontMetrics.height()); + textPointDistance = QPointF(textPointDistance.x() + padding, height() / 2); + widgetPositionFraction = m_timeRulerPos.x() / width(); + } else { + textPoint.setX(0); + textPointDistance.setX(0); + widgetPositionFraction = m_timeRulerPos.y() / height(); + } + double trackSamples = m_trackSamplesControl->get(); + double timePosition = samplePositionToSeconds( + widgetPositionFraction * trackSamples); + double timePositionTillEnd = samplePositionToSeconds( + (1 - widgetPositionFraction) * trackSamples); + double timeDistance = samplePositionToSeconds( + (widgetPositionFraction - m_playpositionControl->get()) * trackSamples); + + QString timeText = mixxx::Duration::formatTime(timePosition) + + " -" + mixxx::Duration::formatTime(timePositionTillEnd); + m_timeRulerTextArea = fontMetrics.boundingRect(timeText); + QString timeDistanceText; + if (timeDistance > 0) { + timeDistanceText = mixxx::Duration::formatTime(timeDistance); + m_timeRulerDistanceTextArea = fontMetrics.boundingRect(timeDistanceText); + } + + QPointF timeTextTopLeft = QPointF(textPoint.x(), + textPoint.y() - fontMetrics.height()); + m_timeRulerTextArea.moveTo(timeTextTopLeft); + QPointF timeDistanceTopLeft = QPointF(textPointDistance.x(), + textPointDistance.y() - fontMetrics.height()); + m_timeRulerDistanceTextArea.moveTo(timeDistanceTopLeft); + + shiftRectIfCutOff(&m_timeRulerTextArea); + shiftRectIfCutOff(&m_timeRulerDistanceTextArea); + + pPainter->setPen(shadowPen); + pPainter->setFont(shadowFont); + pPainter->drawText(m_timeRulerTextArea.bottomLeft(), timeText); + pPainter->drawText(m_timeRulerDistanceTextArea.bottomLeft(), timeDistanceText); + + pPainter->setPen(Qt::white); + pPainter->setFont(markerFont); + pPainter->drawText(m_timeRulerTextArea.bottomLeft(), timeText); + pPainter->drawText(m_timeRulerDistanceTextArea.bottomLeft(), timeDistanceText); + } +} + void WOverview::drawMarkLabels(QPainter* pPainter, const float offset, const float gain) { QFont markerFont = pPainter->font(); markerFont.setPixelSize(10 * m_scaleFactor); @@ -858,9 +944,11 @@ void WOverview::drawMarkLabels(QPainter* pPainter, const float offset, const flo for (int n = 0; n < m_marksToRender.size(); ++n) { WaveformMarkPointer pMark = m_marksToRender.at(n); QPen shadowPen(QBrush(pMark->borderColor()), 2.5 * m_scaleFactor); - if ((!pMark->m_labelArea.intersects(m_cuePositionRect) - && !pMark->m_labelArea.intersects(m_cueTimeDistanceRect)) - || pMark->m_bMouseHovering) { + if (pMark->m_bMouseHovering || + !(pMark->m_labelArea.intersects(m_cuePositionRect) + || pMark->m_labelArea.intersects(m_cueTimeDistanceRect) + || pMark->m_labelArea.intersects(m_timeRulerTextArea) + || pMark->m_labelArea.intersects(m_timeRulerDistanceTextArea))) { // If labels would overlap, only draw the first one. bool skip = false; @@ -892,6 +980,11 @@ void WOverview::drawMarkLabels(QPainter* pPainter, const float offset, const flo } // Draw the position text if (pMark->m_bMouseHovering) { + pPainter->setPen(shadowPen); + pPainter->setFont(shadowFont); + pPainter->drawText(m_cuePositionRect.bottomLeft(), m_cuePositionText); + pPainter->drawText(m_cueTimeDistanceRect.bottomLeft(), m_cueTimeDistanceText); + pPainter->setPen(pMark->m_textColor); pPainter->setFont(markerFont); pPainter->drawText(m_cuePositionRect.bottomLeft(), m_cuePositionText); @@ -927,11 +1020,7 @@ void WOverview::drawMarkLabels(QPainter* pPainter, const float offset, const flo x = endPosition + padding; } - // Ensure the right end of the text does not get cut off by - // the end of the track - if (x + durationRect.width() > width()) { - x = width() - durationRect.width(); - } + shiftRectIfCutOff(&durationRect); pPainter->setOpacity(1.0); pPainter->setPen(markRange.m_durationTextColor); @@ -940,7 +1029,10 @@ void WOverview::drawMarkLabels(QPainter* pPainter, const float offset, const flo // top left of the QRectF. QPointF textTopLeft = QPointF(x, fontMetrics.ascent()); durationRect.moveTo(textTopLeft); - if (!durationRect.intersects(m_cuePositionRect)) { + if (!(durationRect.intersects(m_cuePositionRect) + || durationRect.intersects(m_cueTimeDistanceRect) + || durationRect.intersects(m_timeRulerTextArea) + || durationRect.intersects(m_timeRulerDistanceTextArea))) { pPainter->drawText(QPointF(x, fontMetrics.ascent()), duration); } } @@ -982,6 +1074,12 @@ double WOverview::samplePositionToSeconds(double sample) { / mixxx::kEngineChannelCount / rateRatio; } +void WOverview::shiftRectIfCutOff(QRectF* pRect) { + if (pRect->right() > width()) { + pRect->setLeft(width() - pRect->width()); + } +} + void WOverview::resizeEvent(QResizeEvent * /*unused*/) { // Play-position potmeters range from 0 to 1 but they allow out-of-range // sets. This is to give VC access to the pre-roll area. diff --git a/src/widget/woverview.h b/src/widget/woverview.h index 50eaaa7faa1..8de8f542980 100644 --- a/src/widget/woverview.h +++ b/src/widget/woverview.h @@ -114,6 +114,7 @@ class WOverview : public WWidget, public TrackDropTarget { void drawRangeMarks(QPainter* pPainter, const float& offset, const float& gain); void drawMarks(QPainter* pPainter, const float offset, const float gain); void drawCurrentPosition(QPainter* pPainter); + void drawTimeRuler(QPainter* pPainter); void drawMarkLabels(QPainter* pPainter, const float offset, const float gain); void paintText(const QString& text, QPainter* pPainter); double samplePositionToSeconds(double sample); @@ -123,6 +124,7 @@ class WOverview : public WWidget, public TrackDropTarget { inline double positionToValue(int position) const { return (static_cast(position) + m_b) / m_a; } + void shiftRectIfCutOff(QRectF* pRect); void updateCues(const QList &loadedCues); @@ -148,6 +150,11 @@ class WOverview : public WWidget, public TrackDropTarget { // Internal storage of slider position in pixels int m_iPos; + bool m_bTimeRulerActive; + QPointF m_timeRulerPos; + QRectF m_timeRulerTextArea; + QRectF m_timeRulerDistanceTextArea; + Qt::Orientation m_orientation; QPixmap m_backgroundPixmap; From 6403d3042469f0aacd3d24367c8965b94b30592c Mon Sep 17 00:00:00 2001 From: Be Date: Sat, 17 Aug 2019 16:13:27 -0500 Subject: [PATCH 29/95] split ColorMenu class from CueMenu This color menu may be useful to reuse. --- build/depends.py | 1 + src/widget/colormenu.cpp | 31 +++++++++++++++++++++++++++++++ src/widget/colormenu.h | 18 ++++++++++++++++++ src/widget/cuemenu.cpp | 26 +++----------------------- src/widget/cuemenu.h | 8 +++----- 5 files changed, 56 insertions(+), 28 deletions(-) create mode 100644 src/widget/colormenu.cpp create mode 100644 src/widget/colormenu.h diff --git a/build/depends.py b/build/depends.py index 589e87b26d5..5487861f08b 100644 --- a/build/depends.py +++ b/build/depends.py @@ -906,6 +906,7 @@ def sources(self, build): "src/sources/soundsourceproviderregistry.cpp", "src/sources/soundsourceproxy.cpp", + "src/widget/colormenu.cpp", "src/widget/controlwidgetconnection.cpp", "src/widget/cuemenu.cpp", "src/widget/wbasewidget.cpp", diff --git a/src/widget/colormenu.cpp b/src/widget/colormenu.cpp new file mode 100644 index 00000000000..87ec3a88fff --- /dev/null +++ b/src/widget/colormenu.cpp @@ -0,0 +1,31 @@ +#include "widget/colormenu.h" +#include "util/color/color.h" + +ColorMenu::ColorMenu(QWidget *parent) + : QMenu(parent) { + // If another title would be more appropriate in some context, setTitle + // can be called again after construction. + setTitle(tr("Set color")); + for (const auto& pColor : Color::kPredefinedColorsSet.allColors) { + if (*pColor == *Color::kPredefinedColorsSet.noColor) { + continue; + } + + QAction* pColorAction = new QAction(pColor->m_sDisplayName); + QPixmap pixmap(80, 80); + pixmap.fill(pColor->m_defaultRgba); + pColorAction->setIcon(QIcon(pixmap)); + + m_pColorActions.append(pColorAction); + addAction(pColorAction); + connect(pColorAction, &QAction::triggered, this, [pColor, this]() { + emit(colorPicked(pColor)); + }); + } +} + +ColorMenu::~ColorMenu() { + for (auto& pAction : m_pColorActions) { + delete pAction; + } +} diff --git a/src/widget/colormenu.h b/src/widget/colormenu.h new file mode 100644 index 00000000000..4ccd8c8a5c8 --- /dev/null +++ b/src/widget/colormenu.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +#include "util/color/color.h" + +class ColorMenu : public QMenu { + Q_OBJECT + public: + ColorMenu(QWidget *parent = nullptr); + ~ColorMenu() override; + + signals: + void colorPicked(PredefinedColorPointer pColor); + + private: + QList m_pColorActions; +}; diff --git a/src/widget/cuemenu.cpp b/src/widget/cuemenu.cpp index b2ff5896519..274bbd259f0 100644 --- a/src/widget/cuemenu.cpp +++ b/src/widget/cuemenu.cpp @@ -9,27 +9,10 @@ CueMenu::CueMenu(QWidget *parent) addAction(m_pEditLabel); connect(m_pEditLabel, &QAction::triggered, this, &CueMenu::slotEditLabel); - m_pColorMenu = new QMenu(this); - m_pColorMenu->setTitle(tr("Set color")); + m_pColorMenu = new ColorMenu(this); + connect(m_pColorMenu, &ColorMenu::colorPicked, this, &CueMenu::slotChangeCueColor); addMenu(m_pColorMenu); - for (const auto& pColor : Color::kPredefinedColorsSet.allColors) { - if (*pColor == *Color::kPredefinedColorsSet.noColor) { - continue; - } - - QAction* pColorAction = new QAction(pColor->m_sDisplayName); - QPixmap pixmap(80, 80); - pixmap.fill(pColor->m_defaultRgba); - pColorAction->setIcon(QIcon(pixmap)); - - m_pColorMenuActions.append(pColorAction); - m_pColorMenu->addAction(pColorAction); - connect(pColorAction, &QAction::triggered, this, [pColor, this]() { - changeCueColor(pColor); - }); - } - m_pRemoveCue = new QAction(tr("Remove")); addAction(m_pRemoveCue); connect(m_pRemoveCue, &QAction::triggered, this, &CueMenu::slotRemoveCue); @@ -37,9 +20,6 @@ CueMenu::CueMenu(QWidget *parent) CueMenu::~CueMenu() { delete m_pEditLabel; - for (auto& pAction : m_pColorMenuActions) { - delete pAction; - } delete m_pColorMenu; delete m_pRemoveCue; } @@ -57,7 +37,7 @@ void CueMenu::slotEditLabel() { } } -void CueMenu::changeCueColor(PredefinedColorPointer pColor) { +void CueMenu::slotChangeCueColor(PredefinedColorPointer pColor) { VERIFY_OR_DEBUG_ASSERT(m_pCue != nullptr) { return; } diff --git a/src/widget/cuemenu.h b/src/widget/cuemenu.h index dca4685e633..ec90727dd3d 100644 --- a/src/widget/cuemenu.h +++ b/src/widget/cuemenu.h @@ -4,6 +4,7 @@ #include "track/track.h" #include "track/cue.h" +#include "widget/colormenu.h" class CueMenu : public QMenu { Q_OBJECT @@ -22,16 +23,13 @@ class CueMenu : public QMenu { private slots: void slotEditLabel(); void slotRemoveCue(); + void slotChangeCueColor(PredefinedColorPointer pColor); private: - // This is not a Qt slot because it is connected via a lambda. - void changeCueColor(PredefinedColorPointer pColor); - CuePointer m_pCue; TrackPointer m_pTrack; QAction* m_pEditLabel; - QMenu* m_pColorMenu; - QList m_pColorMenuActions; + ColorMenu* m_pColorMenu; QAction* m_pRemoveCue; }; From 3af842cb030e0ca988fd5123ef3bd6a38791c202 Mon Sep 17 00:00:00 2001 From: Be Date: Sat, 17 Aug 2019 17:15:02 -0500 Subject: [PATCH 30/95] ColorMenu: use cue colors specified by skin --- src/widget/colormenu.cpp | 24 ++++++++++++++++++++---- src/widget/colormenu.h | 6 +++++- src/widget/cuemenu.cpp | 4 ++-- src/widget/cuemenu.h | 9 ++++++++- src/widget/woverview.cpp | 1 + 5 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/widget/colormenu.cpp b/src/widget/colormenu.cpp index 87ec3a88fff..ff83abe7d24 100644 --- a/src/widget/colormenu.cpp +++ b/src/widget/colormenu.cpp @@ -1,11 +1,16 @@ #include "widget/colormenu.h" #include "util/color/color.h" -ColorMenu::ColorMenu(QWidget *parent) +ColorMenu::ColorMenu(QWidget *parent, PredefinedColorsRepresentation* pColorRepresentation) : QMenu(parent) { // If another title would be more appropriate in some context, setTitle // can be called again after construction. setTitle(tr("Set color")); + useColorSet(pColorRepresentation); +} + +void ColorMenu::useColorSet(PredefinedColorsRepresentation* pColorRepresentation) { + clear(); for (const auto& pColor : Color::kPredefinedColorsSet.allColors) { if (*pColor == *Color::kPredefinedColorsSet.noColor) { continue; @@ -13,7 +18,11 @@ ColorMenu::ColorMenu(QWidget *parent) QAction* pColorAction = new QAction(pColor->m_sDisplayName); QPixmap pixmap(80, 80); - pixmap.fill(pColor->m_defaultRgba); + if (pColorRepresentation == nullptr) { + pixmap.fill(pColor->m_defaultRgba); + } else { + pixmap.fill(pColorRepresentation->representationFor(pColor)); + } pColorAction->setIcon(QIcon(pixmap)); m_pColorActions.append(pColorAction); @@ -24,8 +33,15 @@ ColorMenu::ColorMenu(QWidget *parent) } } -ColorMenu::~ColorMenu() { +void ColorMenu::clear() { for (auto& pAction : m_pColorActions) { - delete pAction; + if (pAction != nullptr) { + delete pAction; + } } + m_pColorActions.clear(); +} + +ColorMenu::~ColorMenu() { + clear(); } diff --git a/src/widget/colormenu.h b/src/widget/colormenu.h index 4ccd8c8a5c8..c6a30bf2241 100644 --- a/src/widget/colormenu.h +++ b/src/widget/colormenu.h @@ -7,12 +7,16 @@ class ColorMenu : public QMenu { Q_OBJECT public: - ColorMenu(QWidget *parent = nullptr); + ColorMenu(QWidget *parent = nullptr, + PredefinedColorsRepresentation* pColorRepresentation = nullptr); ~ColorMenu() override; + void useColorSet(PredefinedColorsRepresentation* pColorRepresentation); + signals: void colorPicked(PredefinedColorPointer pColor); private: + void clear(); QList m_pColorActions; }; diff --git a/src/widget/cuemenu.cpp b/src/widget/cuemenu.cpp index 274bbd259f0..c828d027885 100644 --- a/src/widget/cuemenu.cpp +++ b/src/widget/cuemenu.cpp @@ -3,13 +3,13 @@ #include "widget/cuemenu.h" #include "util/color/color.h" -CueMenu::CueMenu(QWidget *parent) +CueMenu::CueMenu(QWidget *parent, PredefinedColorsRepresentation* pColorRepresentation) : QMenu(parent) { m_pEditLabel = new QAction(tr("Edit label")); addAction(m_pEditLabel); connect(m_pEditLabel, &QAction::triggered, this, &CueMenu::slotEditLabel); - m_pColorMenu = new ColorMenu(this); + m_pColorMenu = new ColorMenu(this, pColorRepresentation); connect(m_pColorMenu, &ColorMenu::colorPicked, this, &CueMenu::slotChangeCueColor); addMenu(m_pColorMenu); diff --git a/src/widget/cuemenu.h b/src/widget/cuemenu.h index ec90727dd3d..cdd209a07ec 100644 --- a/src/widget/cuemenu.h +++ b/src/widget/cuemenu.h @@ -9,7 +9,8 @@ class CueMenu : public QMenu { Q_OBJECT public: - CueMenu(QWidget *parent = nullptr); + CueMenu(QWidget *parent = nullptr, + PredefinedColorsRepresentation* pColorRepresentation = nullptr); ~CueMenu() override; void setCue(CuePointer pCue) { @@ -20,6 +21,12 @@ class CueMenu : public QMenu { m_pTrack = pTrack; } + void useColorSet(PredefinedColorsRepresentation* pColorRepresentation) { + if (m_pColorMenu != nullptr) { + m_pColorMenu->useColorSet(pColorRepresentation); + } + } + private slots: void slotEditLabel(); void slotRemoveCue(); diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 9219ea23be2..53f87f01709 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -114,6 +114,7 @@ void WOverview::setup(const QDomNode& node, const SkinContext& context) { ? defaultMark->fillColor() : m_signalColors.getAxesColor(); m_predefinedColorsRepresentation = context.getCueColorRepresentation(node, defaultColor); + m_pCueMenu->useColorSet(&m_predefinedColorsRepresentation); for (const auto& pMark: m_marks) { if (pMark->isValid()) { From 4af33c6a82af8be0da51b00b9d48f1d7d9cb9c3c Mon Sep 17 00:00:00 2001 From: Be Date: Sat, 17 Aug 2019 17:25:29 -0500 Subject: [PATCH 31/95] WOverview: show cues that are beyond the limits of the track --- src/widget/woverview.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 53f87f01709..f58bc4e37b3 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -302,7 +302,7 @@ void WOverview::updateCues(const QList &loadedCues) { const WaveformMarkPointer pMark = m_marks.getHotCueMark(currentCue->getHotCue()); if (pMark != nullptr && pMark->isValid() && pMark->isVisible() - && pMark->getSamplePosition() >= 0.0) { + && pMark->getSamplePosition() != Cue::kPositionNotDefined) { QColor newColor = m_predefinedColorsRepresentation.representationFor(currentCue->getColor()); if (newColor != pMark->fillColor() || newColor != pMark->m_textColor) { pMark->setBaseColor(newColor); @@ -686,7 +686,9 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga //const float markPosition = 1.0 + // (m_marksToRender.at(i).m_pointControl->get() / (float)m_trackSamplesControl->get()) * (float)(width()-2); - const float markPosition = offset + m_marksToRender.at(i)->getSamplePosition() * gain; + const float markPosition = math_clamp( + offset + m_marksToRender.at(i)->getSamplePosition() * gain, + 0.0, static_cast(width())); pMark->m_linePosition = markPosition; QPen shadowPen(QBrush(pMark->borderColor()), 2.5 * m_scaleFactor); From 85b84857e29917e11d19e4d0d2e6f88e41ce3c03 Mon Sep 17 00:00:00 2001 From: Be Date: Sun, 18 Aug 2019 06:46:22 -0500 Subject: [PATCH 32/95] WOverview: prevent showing out-of-bounds times on right click drag --- src/widget/woverview.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index f58bc4e37b3..0bd65dc21e3 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -358,6 +358,13 @@ void WOverview::mouseMoveEvent(QMouseEvent* e) { // button down event was not on a cue. if (m_bTimeRulerActive) { m_timeRulerPos = e->pos(); + // Prevent showing times beyond the boundaries of the track when the + // cursor is dragged outside this widget before releasing right click. + if (m_timeRulerPos.x() > width()) { + m_timeRulerPos.setX(width()); + } else if (m_timeRulerDistanceTextArea.y() > height()) { + m_timeRulerPos.setY(height()); + } update(); return; } From a87655d86d010ee2102e0fe3ebef5b899e56d6d0 Mon Sep 17 00:00:00 2001 From: Be Date: Tue, 20 Aug 2019 19:43:23 -0500 Subject: [PATCH 33/95] factor out redundant code to new WaveformMarkLabel class Also draw rounded rectangle backgrounds for them to ensure text is legible over the waveform. The capability for showing icons with text labels is there for the future too. --- build/depends.py | 1 + src/waveform/renderers/waveformmark.h | 4 +- src/waveform/renderers/waveformmarkrange.h | 3 + src/waveform/waveformmarklabel.cpp | 61 ++++++++ src/waveform/waveformmarklabel.h | 50 ++++++ src/widget/woverview.cpp | 169 ++++++++------------- src/widget/woverview.h | 12 +- 7 files changed, 184 insertions(+), 116 deletions(-) create mode 100644 src/waveform/waveformmarklabel.cpp create mode 100644 src/waveform/waveformmarklabel.h diff --git a/build/depends.py b/build/depends.py index 5487861f08b..ed42e796a3e 100644 --- a/build/depends.py +++ b/build/depends.py @@ -1121,6 +1121,7 @@ def sources(self, build): "src/waveform/renderers/glslwaveformrenderersignal.cpp", "src/waveform/renderers/glvsynctestrenderer.cpp", + "src/waveform/waveformmarklabel.cpp", "src/waveform/widgets/waveformwidgetabstract.cpp", "src/waveform/widgets/emptywaveformwidget.cpp", "src/waveform/widgets/softwarewaveformwidget.cpp", diff --git a/src/waveform/renderers/waveformmark.h b/src/waveform/renderers/waveformmark.h index 4b4d0dbaf4b..3147fe0816a 100644 --- a/src/waveform/renderers/waveformmark.h +++ b/src/waveform/renderers/waveformmark.h @@ -4,6 +4,7 @@ #include #include +#include "waveform/waveformmarklabel.h" #include "control/controlproxy.h" #include "util/memory.h" @@ -71,10 +72,11 @@ class WaveformMark { Qt::Alignment m_align; QString m_pixmapPath; - QRectF m_labelArea; float m_linePosition; bool m_bMouseHovering; + WaveformMarkLabel m_label; + private: std::unique_ptr m_pPointCos; std::unique_ptr m_pVisibleCos; diff --git a/src/waveform/renderers/waveformmarkrange.h b/src/waveform/renderers/waveformmarkrange.h index 0b2201c4a72..deb3f09251a 100644 --- a/src/waveform/renderers/waveformmarkrange.h +++ b/src/waveform/renderers/waveformmarkrange.h @@ -6,6 +6,7 @@ #include "control/controlproxy.h" #include "util/memory.h" +#include "waveform/waveformmarklabel.h" class QDomNode; class SkinContext; @@ -47,6 +48,8 @@ class WaveformMarkRange { return m_durationTextLocation; } + WaveformMarkLabel m_durationLabel; + private: void generateImage(int weidth, int height); diff --git a/src/waveform/waveformmarklabel.cpp b/src/waveform/waveformmarklabel.cpp new file mode 100644 index 00000000000..4334f55c223 --- /dev/null +++ b/src/waveform/waveformmarklabel.cpp @@ -0,0 +1,61 @@ +#include "waveform/waveformmarklabel.h" +#include "util/math.h" + +void WaveformMarkLabel::prerender(QPointF bottomLeft, QPixmap icon, QString text, + QFont font, QColor textColor, QColor backgroundColor, + float widgetWidth, double scaleFactor) { + if (text.isEmpty() && icon.isNull()) { + clear(); + return; + } + + m_text = text; + QFontMetrics fontMetrics(font); + const int padding = 2; + + QRectF pixmapRect; + pixmapRect = fontMetrics.boundingRect(text); + if (icon.isNull()) { + pixmapRect.setWidth(padding + pixmapRect.width() + padding); + } else { + pixmapRect.setWidth(padding + icon.width() + padding + pixmapRect.width() + padding); + } + pixmapRect.setHeight(math_max(fontMetrics.height(), icon.height())); + + // pixmapRect has a top left of (0,0) for rendering to m_pixmap. + // m_areaRect is the same size but shifted to the coordinates of the widget. + m_areaRect = pixmapRect; + QPointF topLeft = QPointF(bottomLeft.x(), + bottomLeft.y() - pixmapRect.height()); + m_areaRect.moveTo(topLeft); + + if (m_areaRect.right() > widgetWidth) { + m_areaRect.setLeft(widgetWidth - m_areaRect.width()); + } + + m_pixmap = QPixmap(pixmapRect.width() * scaleFactor, pixmapRect.height() * scaleFactor); + m_pixmap.setDevicePixelRatio(scaleFactor); + m_pixmap.fill(Qt::transparent); + + QPainter painter(&m_pixmap); + + painter.setPen(backgroundColor); + painter.setBrush(QBrush(backgroundColor)); + painter.drawRoundedRect(0, 0, pixmapRect.width(), pixmapRect.height(), 5.0, 5.0); + + QPointF iconTopLeft = pixmapRect.topLeft(); + iconTopLeft.setX(iconTopLeft.x() + padding); + painter.drawPixmap(iconTopLeft, icon); + + // QPainter::drawText draws from the bottom left point. + QPointF textPoint; + textPoint.setX(icon.width() + padding); + textPoint.setY(fontMetrics.ascent()); + painter.setFont(font); + painter.setPen(textColor); + painter.drawText(textPoint, text); +}; + +void WaveformMarkLabel::draw(QPainter* pPainter) { + pPainter->drawPixmap(m_areaRect.topLeft(), m_pixmap); +} diff --git a/src/waveform/waveformmarklabel.h b/src/waveform/waveformmarklabel.h new file mode 100644 index 00000000000..d8e21571682 --- /dev/null +++ b/src/waveform/waveformmarklabel.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +class WaveformMarkLabel { + public: + WaveformMarkLabel() {}; + + // Render the pixmap to an internal buffer + void prerender(QPointF bottomLeft, QPixmap icon, QString text, + QFont font, QColor textColor, QColor backgroundColor, + float widgetWidth, double scaleFactor); + + // Draw the prerendered pixmap + void draw(QPainter* pPainter); + + QRectF area() const { + return m_areaRect; + }; + + bool intersects(const QRectF& other) const { + return m_areaRect.intersects(other); + } + + bool intersects(const WaveformMarkLabel& other) const { + return intersects(other.area()); + } + + void clear() { + m_text = QString(); + m_pixmap = QPixmap(); + m_areaRect = QRectF(); + } + + private: + QPixmap m_icon; + QString m_text; + QFont m_font; + QColor m_textColor; + QColor m_backgroundColor; + + QPixmap m_pixmap; + QRectF m_areaRect; +}; diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 0bd65dc21e3..3e793c0446f 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -362,7 +362,7 @@ void WOverview::mouseMoveEvent(QMouseEvent* e) { // cursor is dragged outside this widget before releasing right click. if (m_timeRulerPos.x() > width()) { m_timeRulerPos.setX(width()); - } else if (m_timeRulerDistanceTextArea.y() > height()) { + } else if (m_timeRulerPos.y() > height()) { m_timeRulerPos.setY(height()); } update(); @@ -387,7 +387,7 @@ void WOverview::mouseMoveEvent(QMouseEvent* e) { pMark->m_linePosition >= hoveredPosition - lineHoverPadding && pMark->m_linePosition <= hoveredPosition + lineHoverPadding; - if ((pMark->m_labelArea.contains(e->pos()) + if ((pMark->m_label.area().contains(e->pos()) || lineHovered) && !firstMarkHovered) { pMark->m_bMouseHovering = true; @@ -683,10 +683,7 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga // called after drawCurrentPosition so the view of labels is not obscured // by the playhead. - m_markLabelText.clear(); - m_cuePositionRect = QRectF(); - m_cueTimeDistanceRect = QRectF(); - + bool markHovered = false; for (int i = 0; i < m_marksToRender.size(); ++i) { WaveformMarkPointer pMark = m_marksToRender.at(i); PainterScope painterScope(pPainter); @@ -732,7 +729,6 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga && pMark->getHotCue() != WaveformMark::kNoHotCue) { text = QString::number(pMark->getHotCue()+1); } - m_markLabelText.append(text); QRectF textRect = fontMetrics.boundingRect(text); QPointF textPoint; @@ -770,16 +766,16 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga } } - // QPainter::drawText starts drawing with the given QPointF as - // the bottom left of the text, but QRectF::moveTo takes the new - // top left of the QRectF. - QPointF textTopLeft = QPointF(textPoint.x(), textPoint.y() - fontMetrics.height()); - textRect.moveTo(textTopLeft); - shiftRectIfCutOff(&textRect); - pMark->m_labelArea = textRect; - } else { - // Placeholder to keep order - m_markLabelText.append(QString()); + QColor bgColor = m_qColorBackground; + if (pMark->getHotCue() == WaveformMark::kNoHotCue) { + bgColor = Qt::transparent; + } else { + bgColor.setAlpha(128); + } + + pMark->m_label.prerender(textPoint, QPixmap(), text, + markerFont, pMark->m_textColor, bgColor, + width(), getDevicePixelRatioF(this)); } // Show cue position when hovered @@ -809,32 +805,31 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga double currentPositionSamples = m_playpositionControl->get() * m_trackSamplesControl->get(); double markTime = samplePositionToSeconds(markSamples); double markTimeDistance = samplePositionToSeconds(markSamples - currentPositionSamples); - m_cuePositionText = mixxx::Duration::formatTime(markTime); + QString cuePositionText = mixxx::Duration::formatTime(markTime); + QString cueTimeDistanceText; // Do not show the time until the cue point if the playhead is past // the cue point. if (markTimeDistance > 0) { - m_cueTimeDistanceText = mixxx::Duration::formatTime(markTimeDistance); - } else { - m_cueTimeDistanceText = QString(); + cueTimeDistanceText = mixxx::Duration::formatTime(markTimeDistance); } - m_cuePositionRect = fontMetrics.boundingRect(m_cuePositionText); - m_cueTimeDistanceRect = fontMetrics.boundingRect(m_cueTimeDistanceText); - - // QPainter::drawText starts drawing with the given QPointF as - // the bottom left of the text, but QRectF::moveTo takes the new - // top left of the QRectF. - QPointF textTopLeft = QPointF(positionTextPoint.x(), - positionTextPoint.y() - fontMetrics.height()); - m_cuePositionRect.moveTo(textTopLeft); - - QPointF distanceTextTopLeft(markPosition, - (height() / 2) - fontMetrics.height()); - m_cueTimeDistanceRect.moveTo(distanceTextTopLeft); - - shiftRectIfCutOff(&m_cuePositionRect); - shiftRectIfCutOff(&m_cueTimeDistanceRect); + + QColor bgColor = m_qColorBackground; + bgColor.setAlpha(128); + m_cuePositionLabel.prerender(positionTextPoint, QPixmap(), cuePositionText, + markerFont, Qt::white, bgColor, width(), getDevicePixelRatioF(this)); + m_cuePositionLabel.draw(pPainter); + + QPointF timeDistancePoint(markPosition, height() / 2); + m_cueTimeDistanceLabel.prerender(timeDistancePoint, QPixmap(), cueTimeDistanceText, + markerFont, Qt::white, bgColor, width(), getDevicePixelRatioF(this)); + m_cueTimeDistanceLabel.draw(pPainter); + markHovered = true; } } + if (!markHovered) { + m_cuePositionLabel.clear(); + m_cueTimeDistanceLabel.clear(); + } } void WOverview::drawCurrentPosition(QPainter* pPainter) { @@ -872,9 +867,6 @@ void WOverview::drawTimeRuler(QPainter* pPainter) { shadowFont.setPixelSize(10 * m_scaleFactor); QPen shadowPen(Qt::black, 2.5 * m_scaleFactor); - m_timeRulerTextArea = QRectF(); - m_timeRulerDistanceTextArea = QRectF(); - if (m_bTimeRulerActive) { QLineF line; if (m_orientation == Qt::Horizontal) { @@ -911,32 +903,23 @@ void WOverview::drawTimeRuler(QPainter* pPainter) { QString timeText = mixxx::Duration::formatTime(timePosition) + " -" + mixxx::Duration::formatTime(timePositionTillEnd); - m_timeRulerTextArea = fontMetrics.boundingRect(timeText); + + QColor bgColor = m_qColorBackground; + bgColor.setAlpha(128); + m_timeRulerPositionLabel.prerender(textPoint, QPixmap(), timeText, + markerFont, Qt::white, bgColor, width(), getDevicePixelRatioF(this)); + m_timeRulerPositionLabel.draw(pPainter); + QString timeDistanceText; if (timeDistance > 0) { timeDistanceText = mixxx::Duration::formatTime(timeDistance); - m_timeRulerDistanceTextArea = fontMetrics.boundingRect(timeDistanceText); } - - QPointF timeTextTopLeft = QPointF(textPoint.x(), - textPoint.y() - fontMetrics.height()); - m_timeRulerTextArea.moveTo(timeTextTopLeft); - QPointF timeDistanceTopLeft = QPointF(textPointDistance.x(), - textPointDistance.y() - fontMetrics.height()); - m_timeRulerDistanceTextArea.moveTo(timeDistanceTopLeft); - - shiftRectIfCutOff(&m_timeRulerTextArea); - shiftRectIfCutOff(&m_timeRulerDistanceTextArea); - - pPainter->setPen(shadowPen); - pPainter->setFont(shadowFont); - pPainter->drawText(m_timeRulerTextArea.bottomLeft(), timeText); - pPainter->drawText(m_timeRulerDistanceTextArea.bottomLeft(), timeDistanceText); - - pPainter->setPen(Qt::white); - pPainter->setFont(markerFont); - pPainter->drawText(m_timeRulerTextArea.bottomLeft(), timeText); - pPainter->drawText(m_timeRulerDistanceTextArea.bottomLeft(), timeDistanceText); + m_timeRulerDistanceLabel.prerender(textPointDistance, QPixmap(), timeDistanceText, + markerFont, Qt::white, bgColor, width(), getDevicePixelRatioF(this)); + m_timeRulerDistanceLabel.draw(pPainter); + } else { + m_timeRulerPositionLabel.clear(); + m_timeRulerDistanceLabel.clear(); } } @@ -953,18 +936,17 @@ void WOverview::drawMarkLabels(QPainter* pPainter, const float offset, const flo // Draw WaveformMark labels for (int n = 0; n < m_marksToRender.size(); ++n) { WaveformMarkPointer pMark = m_marksToRender.at(n); - QPen shadowPen(QBrush(pMark->borderColor()), 2.5 * m_scaleFactor); if (pMark->m_bMouseHovering || - !(pMark->m_labelArea.intersects(m_cuePositionRect) - || pMark->m_labelArea.intersects(m_cueTimeDistanceRect) - || pMark->m_labelArea.intersects(m_timeRulerTextArea) - || pMark->m_labelArea.intersects(m_timeRulerDistanceTextArea))) { + !(pMark->m_label.intersects(m_cuePositionLabel) + || pMark->m_label.intersects(m_cueTimeDistanceLabel) + || pMark->m_label.intersects(m_timeRulerPositionLabel) + || pMark->m_label.intersects(m_timeRulerDistanceLabel))) { // If labels would overlap, only draw the first one. bool skip = false; for (const auto& otherMark : m_marksToRender) { if (otherMark != pMark - && pMark->m_labelArea.intersects(otherMark->m_labelArea)) { + && pMark->m_label.intersects(otherMark->m_label)) { if (firstOverlappingLabelRendered) { skip = true; @@ -980,25 +962,7 @@ void WOverview::drawMarkLabels(QPainter* pPainter, const float offset, const flo continue; } - pPainter->setPen(shadowPen); - pPainter->setFont(shadowFont); - pPainter->drawText(pMark->m_labelArea.bottomLeft(), m_markLabelText.at(n)); - - pPainter->setPen(pMark->m_textColor); - pPainter->setFont(markerFont); - pPainter->drawText(pMark->m_labelArea.bottomLeft(), m_markLabelText.at(n)); - } - // Draw the position text - if (pMark->m_bMouseHovering) { - pPainter->setPen(shadowPen); - pPainter->setFont(shadowFont); - pPainter->drawText(m_cuePositionRect.bottomLeft(), m_cuePositionText); - pPainter->drawText(m_cueTimeDistanceRect.bottomLeft(), m_cueTimeDistanceText); - - pPainter->setPen(pMark->m_textColor); - pPainter->setFont(markerFont); - pPainter->drawText(m_cuePositionRect.bottomLeft(), m_cuePositionText); - pPainter->drawText(m_cueTimeDistanceRect.bottomLeft(), m_cueTimeDistanceText); + pMark->m_label.draw(pPainter); } } @@ -1030,20 +994,17 @@ void WOverview::drawMarkLabels(QPainter* pPainter, const float offset, const flo x = endPosition + padding; } - shiftRectIfCutOff(&durationRect); - - pPainter->setOpacity(1.0); - pPainter->setPen(markRange.m_durationTextColor); - // QPainter::drawText starts drawing with the given QPointF as - // the bottom left of the text, but QRectF::moveTo takes the new - // top left of the QRectF. - QPointF textTopLeft = QPointF(x, fontMetrics.ascent()); - durationRect.moveTo(textTopLeft); - if (!(durationRect.intersects(m_cuePositionRect) - || durationRect.intersects(m_cueTimeDistanceRect) - || durationRect.intersects(m_timeRulerTextArea) - || durationRect.intersects(m_timeRulerDistanceTextArea))) { - pPainter->drawText(QPointF(x, fontMetrics.ascent()), duration); + QPointF durationBottomLeft(x, fontMetrics.ascent()); + + markRange.m_durationLabel.prerender(durationBottomLeft, QPixmap(), + duration, markerFont, markRange.m_durationTextColor, + Qt::transparent, width(), getDevicePixelRatioF(this)); + + if (!(markRange.m_durationLabel.intersects(m_cuePositionLabel) + || markRange.m_durationLabel.intersects(m_cueTimeDistanceLabel) + || markRange.m_durationLabel.intersects(m_timeRulerPositionLabel) + || markRange.m_durationLabel.intersects(m_timeRulerDistanceLabel))) { + markRange.m_durationLabel.draw(pPainter); } } } @@ -1084,12 +1045,6 @@ double WOverview::samplePositionToSeconds(double sample) { / mixxx::kEngineChannelCount / rateRatio; } -void WOverview::shiftRectIfCutOff(QRectF* pRect) { - if (pRect->right() > width()) { - pRect->setLeft(width() - pRect->width()); - } -} - void WOverview::resizeEvent(QResizeEvent * /*unused*/) { // Play-position potmeters range from 0 to 1 but they allow out-of-range // sets. This is to give VC access to the pre-roll area. diff --git a/src/widget/woverview.h b/src/widget/woverview.h index 8de8f542980..a13c69093bb 100644 --- a/src/widget/woverview.h +++ b/src/widget/woverview.h @@ -124,7 +124,6 @@ class WOverview : public WWidget, public TrackDropTarget { inline double positionToValue(int position) const { return (static_cast(position) + m_b) / m_a; } - void shiftRectIfCutOff(QRectF* pRect); void updateCues(const QList &loadedCues); @@ -152,8 +151,8 @@ class WOverview : public WWidget, public TrackDropTarget { bool m_bTimeRulerActive; QPointF m_timeRulerPos; - QRectF m_timeRulerTextArea; - QRectF m_timeRulerDistanceTextArea; + WaveformMarkLabel m_timeRulerPositionLabel; + WaveformMarkLabel m_timeRulerDistanceLabel; Qt::Orientation m_orientation; @@ -168,11 +167,8 @@ class WOverview : public WWidget, public TrackDropTarget { // List of visible WaveformMarks sorted by the order they appear in the track QList m_marksToRender; std::vector m_markRanges; - QList m_markLabelText; - QString m_cuePositionText; - QRectF m_cuePositionRect; - QString m_cueTimeDistanceText; - QRectF m_cueTimeDistanceRect; + WaveformMarkLabel m_cuePositionLabel; + WaveformMarkLabel m_cueTimeDistanceLabel; // Coefficient value-position linear transposition double m_a; From b3a55e9657b9edf83703df22bfd2557788766623 Mon Sep 17 00:00:00 2001 From: Be Date: Tue, 20 Aug 2019 23:53:51 -0500 Subject: [PATCH 34/95] mention right clicking to show time in overview tooltip --- src/skin/tooltips.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/skin/tooltips.cpp b/src/skin/tooltips.cpp index bfb8c97476a..25df8da92b3 100644 --- a/src/skin/tooltips.cpp +++ b/src/skin/tooltips.cpp @@ -39,6 +39,7 @@ void Tooltips::addStandardTooltips() { << tr("Shows information about the track currently loaded in this deck.") << tr("Jump around in the track by clicking anywhere on the waveform.") << tr("Right click hotcues to edit their labels and colors.") + << tr("Right click anywhere else to show the time at that point.") << dropTracksHere; QString scratchMouse = tr("Use the mouse to scratch, spin-back or throw tracks."); From 4f9a5da1c3d552357253f080ee5650f225141aed Mon Sep 17 00:00:00 2001 From: Be Date: Wed, 21 Aug 2019 00:31:01 -0500 Subject: [PATCH 35/95] WOverview: draw background for non-hotcue cue marks This ensures their labels are legible no matter how the waveform underneath looks. --- src/widget/woverview.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 3e793c0446f..71dc66c501a 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -767,11 +767,7 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga } QColor bgColor = m_qColorBackground; - if (pMark->getHotCue() == WaveformMark::kNoHotCue) { - bgColor = Qt::transparent; - } else { - bgColor.setAlpha(128); - } + bgColor.setAlpha(128); pMark->m_label.prerender(textPoint, QPixmap(), text, markerFont, pMark->m_textColor, bgColor, From 2378a881e85ae9aab759a702a1fbc06f497bdc8b Mon Sep 17 00:00:00 2001 From: Be Date: Wed, 21 Aug 2019 00:39:33 -0500 Subject: [PATCH 36/95] elaborate in comments --- src/waveform/waveformmarklabel.h | 5 ++++- src/widget/woverview.cpp | 12 ++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/waveform/waveformmarklabel.h b/src/waveform/waveformmarklabel.h index d8e21571682..02ff40ca797 100644 --- a/src/waveform/waveformmarklabel.h +++ b/src/waveform/waveformmarklabel.h @@ -8,11 +8,14 @@ #include #include +// WaveformMarkLabel renders the label for a WaveformMark to an offscreen buffer +// and calculates its area. This allows the areas of all WaveformMarkLabels +// to be compared so overlapping labels are not drawn. class WaveformMarkLabel { public: WaveformMarkLabel() {}; - // Render the pixmap to an internal buffer + // Render the label to an internal QPixmap buffer void prerender(QPointF bottomLeft, QPixmap icon, QString text, QFont font, QColor textColor, QColor backgroundColor, float widgetWidth, double scaleFactor); diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 71dc66c501a..405f41b8d58 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -670,18 +670,18 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga shadowFont.setWeight(99); shadowFont.setPixelSize(10 * m_scaleFactor); - // Text labels are rendered so they do not overlap with other WaveformMark's + // Text labels are rendered so they do not overlap with other WaveformMarks' // labels. If the text would be too wide, it is elided. However, the user // can hover the mouse cursor over a label to show the whole label text, // temporarily hiding any following labels that would be drawn over it. // This requires looping over the WaveformMarks twice and the marks must be // sorted in the order they appear on the waveform. // In the first loop, the lines are drawn and the text to render plus its - // location are calculated. The text must be drawn in the second loop to - // prevent the lines of following WaveformMarks getting drawn over it. The - // second loop is in the separate drawMarkLabels function so it can be - // called after drawCurrentPosition so the view of labels is not obscured - // by the playhead. + // location are calculated then stored in a WaveformMarkLabel. The text must + // be drawn in the second loop to prevent the lines of following + // WaveformMarks getting drawn over it. The second loop is in the separate + // drawMarkLabels function so it can be called after drawCurrentPosition so + // the view of labels is not obscured by the playhead. bool markHovered = false; for (int i = 0; i < m_marksToRender.size(); ++i) { From 0903d1ec62c90006b970b645491f1e6975eb9b4b Mon Sep 17 00:00:00 2001 From: Be Date: Thu, 22 Aug 2019 11:08:38 -0500 Subject: [PATCH 37/95] WOverview: calculate label background color only once for efficiency --- src/widget/woverview.cpp | 8 ++++---- src/widget/woverview.h | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 405f41b8d58..59dfdc218b8 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -90,6 +90,8 @@ void WOverview::setup(const QDomNode& node, const SkinContext& context) { m_signalColors.setup(node, context); m_qColorBackground = m_signalColors.getBgColor(); + m_labelBackgroundColor = m_qColorBackground; + m_labelBackgroundColor.setAlpha(128); // Clear the background pixmap, if it exists. m_backgroundPixmap = QPixmap(); @@ -900,10 +902,8 @@ void WOverview::drawTimeRuler(QPainter* pPainter) { QString timeText = mixxx::Duration::formatTime(timePosition) + " -" + mixxx::Duration::formatTime(timePositionTillEnd); - QColor bgColor = m_qColorBackground; - bgColor.setAlpha(128); m_timeRulerPositionLabel.prerender(textPoint, QPixmap(), timeText, - markerFont, Qt::white, bgColor, width(), getDevicePixelRatioF(this)); + markerFont, Qt::white, m_labelBackgroundColor, width(), getDevicePixelRatioF(this)); m_timeRulerPositionLabel.draw(pPainter); QString timeDistanceText; @@ -911,7 +911,7 @@ void WOverview::drawTimeRuler(QPainter* pPainter) { timeDistanceText = mixxx::Duration::formatTime(timeDistance); } m_timeRulerDistanceLabel.prerender(textPointDistance, QPixmap(), timeDistanceText, - markerFont, Qt::white, bgColor, width(), getDevicePixelRatioF(this)); + markerFont, Qt::white, m_labelBackgroundColor, width(), getDevicePixelRatioF(this)); m_timeRulerDistanceLabel.draw(pPainter); } else { m_timeRulerPositionLabel.clear(); diff --git a/src/widget/woverview.h b/src/widget/woverview.h index a13c69093bb..6716781ecef 100644 --- a/src/widget/woverview.h +++ b/src/widget/woverview.h @@ -159,6 +159,7 @@ class WOverview : public WWidget, public TrackDropTarget { QPixmap m_backgroundPixmap; QString m_backgroundPixmapPath; QColor m_qColorBackground; + QColor m_labelBackgroundColor; QColor m_endOfTrackColor; PredefinedColorsRepresentation m_predefinedColorsRepresentation; From c038eed6058cab6df1c95e17b44b80f1a758ab3d Mon Sep 17 00:00:00 2001 From: Be Date: Thu, 22 Aug 2019 12:13:26 -0500 Subject: [PATCH 38/95] WOverview: draw translucent backgrounds for WaveformMarkRange label to ensure legibility regardless of the waveform image it is drawn over --- src/widget/woverview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 59dfdc218b8..18ee1e9c7c7 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -994,7 +994,7 @@ void WOverview::drawMarkLabels(QPainter* pPainter, const float offset, const flo markRange.m_durationLabel.prerender(durationBottomLeft, QPixmap(), duration, markerFont, markRange.m_durationTextColor, - Qt::transparent, width(), getDevicePixelRatioF(this)); + m_labelBackgroundColor, width(), getDevicePixelRatioF(this)); if (!(markRange.m_durationLabel.intersects(m_cuePositionLabel) || markRange.m_durationLabel.intersects(m_cueTimeDistanceLabel) From 0cdfbc5f30bdcb1894251ccec726a7c1bdb79eea Mon Sep 17 00:00:00 2001 From: Be Date: Thu, 22 Aug 2019 12:18:45 -0500 Subject: [PATCH 39/95] WOverview: show time till end of track when hovering cues --- src/widget/woverview.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 18ee1e9c7c7..3aeb78deaf7 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -800,10 +800,13 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga } double markSamples = pMark->getSamplePosition(); - double currentPositionSamples = m_playpositionControl->get() * m_trackSamplesControl->get(); + double trackSamples = m_trackSamplesControl->get(); + double currentPositionSamples = m_playpositionControl->get() * trackSamples; double markTime = samplePositionToSeconds(markSamples); + double markTimeRemaining = samplePositionToSeconds(trackSamples - markSamples); double markTimeDistance = samplePositionToSeconds(markSamples - currentPositionSamples); - QString cuePositionText = mixxx::Duration::formatTime(markTime); + QString cuePositionText = mixxx::Duration::formatTime(markTime) + " -" + + mixxx::Duration::formatTime(markTimeRemaining); QString cueTimeDistanceText; // Do not show the time until the cue point if the playhead is past // the cue point. From 2fcf8fc2ed4d4450a718e41467b95bbe87a9523b Mon Sep 17 00:00:00 2001 From: Be Date: Thu, 22 Aug 2019 14:11:04 -0500 Subject: [PATCH 40/95] WOverview: fix cue position text getting drawn under other cues --- src/widget/woverview.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 3aeb78deaf7..bbe6ec38fed 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -818,12 +818,10 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga bgColor.setAlpha(128); m_cuePositionLabel.prerender(positionTextPoint, QPixmap(), cuePositionText, markerFont, Qt::white, bgColor, width(), getDevicePixelRatioF(this)); - m_cuePositionLabel.draw(pPainter); QPointF timeDistancePoint(markPosition, height() / 2); m_cueTimeDistanceLabel.prerender(timeDistancePoint, QPixmap(), cueTimeDistanceText, markerFont, Qt::white, bgColor, width(), getDevicePixelRatioF(this)); - m_cueTimeDistanceLabel.draw(pPainter); markHovered = true; } } @@ -965,6 +963,9 @@ void WOverview::drawMarkLabels(QPainter* pPainter, const float offset, const flo } } + m_cuePositionLabel.draw(pPainter); + m_cueTimeDistanceLabel.draw(pPainter); + // draw duration of WaveformMarkRanges for (auto&& markRange : m_markRanges) { if (markRange.showDuration()) { From e65f56a67699f64abe8e90f4553532d729129220 Mon Sep 17 00:00:00 2001 From: Be Date: Thu, 22 Aug 2019 14:38:24 -0500 Subject: [PATCH 41/95] WOverview: fix regression with hovered cue labels not getting drawn --- src/widget/woverview.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index bbe6ec38fed..2c65458880b 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -931,10 +931,8 @@ void WOverview::drawMarkLabels(QPainter* pPainter, const float offset, const flo bool firstOverlappingLabelRendered = false; // Draw WaveformMark labels - for (int n = 0; n < m_marksToRender.size(); ++n) { - WaveformMarkPointer pMark = m_marksToRender.at(n); - if (pMark->m_bMouseHovering || - !(pMark->m_label.intersects(m_cuePositionLabel) + for (const auto& pMark : m_marksToRender) { + if (!(pMark->m_label.intersects(m_cuePositionLabel) || pMark->m_label.intersects(m_cueTimeDistanceLabel) || pMark->m_label.intersects(m_timeRulerPositionLabel) || pMark->m_label.intersects(m_timeRulerDistanceLabel))) { @@ -948,7 +946,7 @@ void WOverview::drawMarkLabels(QPainter* pPainter, const float offset, const flo if (firstOverlappingLabelRendered) { skip = true; break; - } else { + } else if (pMark->m_bMouseHovering) { skip = false; firstOverlappingLabelRendered = true; break; From 05ed063266352de9ef2c266bf945e55f3938cc2d Mon Sep 17 00:00:00 2001 From: Be Date: Thu, 22 Aug 2019 15:45:08 -0500 Subject: [PATCH 42/95] WOverview: don't draw overlapping labels when hovered label is shifted right --- src/waveform/renderers/waveformmark.h | 1 - src/widget/woverview.cpp | 106 ++++++++++---------------- src/widget/woverview.h | 1 + 3 files changed, 40 insertions(+), 68 deletions(-) diff --git a/src/waveform/renderers/waveformmark.h b/src/waveform/renderers/waveformmark.h index 3147fe0816a..e1c4dfd0c4b 100644 --- a/src/waveform/renderers/waveformmark.h +++ b/src/waveform/renderers/waveformmark.h @@ -73,7 +73,6 @@ class WaveformMark { QString m_pixmapPath; float m_linePosition; - bool m_bMouseHovering; WaveformMarkLabel m_label; diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 2c65458880b..ee939aa56ff 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -57,6 +57,7 @@ WOverview::WOverview( m_pCueMenu(std::make_unique(this)), m_bDrag(false), m_iPos(0), + m_pHoveredMark(nullptr), m_bTimeRulerActive(false), m_orientation(Qt::Horizontal), m_a(1.0), @@ -371,10 +372,7 @@ void WOverview::mouseMoveEvent(QMouseEvent* e) { return; } - // Without tracking the first hovered WaveformMark, the user could hover one - // mark, then drag the cursor over another, and the second one's label text - // would be drawn under the first. - bool firstMarkHovered = false; + m_pHoveredMark = nullptr; // Without some padding, the user would only have a single pixel width that // would count as hovering over the WaveformMark. float lineHoverPadding = 3.0; @@ -389,13 +387,9 @@ void WOverview::mouseMoveEvent(QMouseEvent* e) { pMark->m_linePosition >= hoveredPosition - lineHoverPadding && pMark->m_linePosition <= hoveredPosition + lineHoverPadding; - if ((pMark->m_label.area().contains(e->pos()) - || lineHovered) - && !firstMarkHovered) { - pMark->m_bMouseHovering = true; - firstMarkHovered = true; - } else { - pMark->m_bMouseHovering = false; + if (pMark->m_label.area().contains(e->pos()) || lineHovered) { + m_pHoveredMark = pMark; + break; } } @@ -411,11 +405,8 @@ void WOverview::mouseReleaseEvent(QMouseEvent* e) { if (e->button() == Qt::LeftButton) { // If a hotcue label is being hovered, jump to it instead of the point // under the cursor. - for (const auto& pMark : m_marksToRender) { - if (pMark->m_bMouseHovering) { - dValue = pMark->getSamplePosition() / m_trackSamplesControl->get(); - break; - } + if (m_pHoveredMark != nullptr) { + dValue = m_pHoveredMark->getSamplePosition() / m_trackSamplesControl->get(); } setControlParameterUp(dValue); m_bDrag = false; @@ -433,27 +424,25 @@ void WOverview::mousePressEvent(QMouseEvent* e) { bool hotcueRightClicked = false; if (m_pCurrentTrack != nullptr) { QList cueList = m_pCurrentTrack->getCuePoints(); - for (const auto& pMark : m_marksToRender) { - if (pMark->m_bMouseHovering) { - if (e->button() == Qt::LeftButton) { - dragging = false; - } else if (pMark->getHotCue() != WaveformMark::kNoHotCue) { - // Currently the only way WaveformMarks can be associated - // with their respective Cue objects is by using the hotcue - // number. If cues without assigned hotcue are drawn on - // WOverview in the future, another way to associate - // WaveformMarks with Cues will need to be implemented. - CuePointer pHoveredCue; - for (const auto& pCue : cueList) { - if (pCue->getHotCue() == pMark->getHotCue()) { - pHoveredCue = pCue; - hotcueRightClicked = true; - break; - } - } - VERIFY_OR_DEBUG_ASSERT(pHoveredCue != nullptr) { - continue; + if (m_pHoveredMark != nullptr) { + if (e->button() == Qt::LeftButton) { + dragging = false; + } else if (e->button() == Qt::RightButton + && m_pHoveredMark->getHotCue() != WaveformMark::kNoHotCue) { + // Currently the only way WaveformMarks can be associated + // with their respective Cue objects is by using the hotcue + // number. If cues without assigned hotcue are drawn on + // WOverview in the future, another way to associate + // WaveformMarks with Cues will need to be implemented. + CuePointer pHoveredCue; + for (const auto& pCue : cueList) { + if (pCue->getHotCue() == m_pHoveredMark->getHotCue()) { + pHoveredCue = pCue; + hotcueRightClicked = true; + break; } + } + if (pHoveredCue != nullptr) { m_pCueMenu->setCue(pHoveredCue); m_pCueMenu->setTrack(m_pCurrentTrack); m_pCueMenu->popup(e->globalPos()); @@ -471,9 +460,7 @@ void WOverview::mousePressEvent(QMouseEvent* e) { void WOverview::leaveEvent(QEvent* e) { Q_UNUSED(e); - for (const auto& pMark : m_marks) { - pMark->m_bMouseHovering = false; - } + m_pHoveredMark = nullptr; m_bDrag = false; m_bTimeRulerActive = false; update(); @@ -720,7 +707,7 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga // Only allow the text to overlap the following mark if the mouse is // hovering over it. Otherwise, elide it if it would render over // the next label. - if (!pMark->m_bMouseHovering && i < m_marksToRender.size()-1) { + if (pMark != m_pHoveredMark && i < m_marksToRender.size()-1) { const float nextMarkPosition = offset + m_marksToRender.at(i+1)->getSamplePosition() * gain; text = fontMetrics.elidedText(text, Qt::ElideRight, nextMarkPosition - markPosition - 5); } @@ -790,7 +777,7 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga // WaveformMark::m_align refers to the alignment of the label, // so if the label is on bottom draw the position text on top and // vice versa. - if (pMark->m_bMouseHovering) { + if (pMark == m_pHoveredMark) { Qt::Alignment valign = pMark->m_align & Qt::AlignVertical_Mask; QPointF positionTextPoint(markPosition, 0); if (valign == Qt::AlignTop) { @@ -929,36 +916,21 @@ void WOverview::drawMarkLabels(QPainter* pPainter, const float offset, const flo shadowFont.setWeight(99); shadowFont.setPixelSize(10 * m_scaleFactor); - bool firstOverlappingLabelRendered = false; // Draw WaveformMark labels for (const auto& pMark : m_marksToRender) { - if (!(pMark->m_label.intersects(m_cuePositionLabel) - || pMark->m_label.intersects(m_cueTimeDistanceLabel) - || pMark->m_label.intersects(m_timeRulerPositionLabel) - || pMark->m_label.intersects(m_timeRulerDistanceLabel))) { - - // If labels would overlap, only draw the first one. - bool skip = false; - for (const auto& otherMark : m_marksToRender) { - if (otherMark != pMark - && pMark->m_label.intersects(otherMark->m_label)) { - - if (firstOverlappingLabelRendered) { - skip = true; - break; - } else if (pMark->m_bMouseHovering) { - skip = false; - firstOverlappingLabelRendered = true; - break; - } - } - } - if (skip) { - continue; + if (m_pHoveredMark != nullptr && pMark != m_pHoveredMark) { + if (pMark->m_label.intersects(m_pHoveredMark->m_label)) { + continue; } - - pMark->m_label.draw(pPainter); } + if (pMark->m_label.intersects(m_cuePositionLabel) + || pMark->m_label.intersects(m_cueTimeDistanceLabel) + || pMark->m_label.intersects(m_timeRulerPositionLabel) + || pMark->m_label.intersects(m_timeRulerDistanceLabel)) { + continue; + } + + pMark->m_label.draw(pPainter); } m_cuePositionLabel.draw(pPainter); diff --git a/src/widget/woverview.h b/src/widget/woverview.h index 6716781ecef..858a422023d 100644 --- a/src/widget/woverview.h +++ b/src/widget/woverview.h @@ -149,6 +149,7 @@ class WOverview : public WWidget, public TrackDropTarget { // Internal storage of slider position in pixels int m_iPos; + WaveformMarkPointer m_pHoveredMark; bool m_bTimeRulerActive; QPointF m_timeRulerPos; WaveformMarkLabel m_timeRulerPositionLabel; From cb5cecfa1c55790c02e58af9f11f7efa8a3cb209 Mon Sep 17 00:00:00 2001 From: Be Date: Thu, 22 Aug 2019 17:34:10 -0500 Subject: [PATCH 43/95] WOverview: make vertical alignment of labels more consistent --- src/widget/woverview.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index ee939aa56ff..0b82d3c6f57 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -806,7 +806,7 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga m_cuePositionLabel.prerender(positionTextPoint, QPixmap(), cuePositionText, markerFont, Qt::white, bgColor, width(), getDevicePixelRatioF(this)); - QPointF timeDistancePoint(markPosition, height() / 2); + QPointF timeDistancePoint(markPosition, (fontMetrics.height() + height()) / 2); m_cueTimeDistanceLabel.prerender(timeDistancePoint, QPixmap(), cueTimeDistanceText, markerFont, Qt::white, bgColor, width(), getDevicePixelRatioF(this)); markHovered = true; @@ -872,7 +872,8 @@ void WOverview::drawTimeRuler(QPainter* pPainter) { double padding = 1.0; // spacing between line and text if (m_orientation == Qt::Horizontal) { textPoint = QPointF(textPoint.x() + padding, fontMetrics.height()); - textPointDistance = QPointF(textPointDistance.x() + padding, height() / 2); + textPointDistance = QPointF(textPointDistance.x() + padding, + (fontMetrics.height() + height()) / 2); widgetPositionFraction = m_timeRulerPos.x() / width(); } else { textPoint.setX(0); @@ -964,7 +965,7 @@ void WOverview::drawMarkLabels(QPainter* pPainter, const float offset, const flo x = endPosition + padding; } - QPointF durationBottomLeft(x, fontMetrics.ascent()); + QPointF durationBottomLeft(x, fontMetrics.height()); markRange.m_durationLabel.prerender(durationBottomLeft, QPixmap(), duration, markerFont, markRange.m_durationTextColor, From 8af156df6aafcfaf8fcf1334746e3df13fd86503 Mon Sep 17 00:00:00 2001 From: Be Date: Thu, 22 Aug 2019 17:50:10 -0500 Subject: [PATCH 44/95] ColorMenu: set parent of QActions The implicit nullptr parent is not available in older Qt versions. --- src/widget/colormenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widget/colormenu.cpp b/src/widget/colormenu.cpp index ff83abe7d24..b93140ffb65 100644 --- a/src/widget/colormenu.cpp +++ b/src/widget/colormenu.cpp @@ -16,7 +16,7 @@ void ColorMenu::useColorSet(PredefinedColorsRepresentation* pColorRepresentation continue; } - QAction* pColorAction = new QAction(pColor->m_sDisplayName); + QAction* pColorAction = new QAction(pColor->m_sDisplayName, this); QPixmap pixmap(80, 80); if (pColorRepresentation == nullptr) { pixmap.fill(pColor->m_defaultRgba); From a654ebf3dba9c35b2b5f2fb4e32f9d6f18f0197a Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 23 Aug 2019 07:44:36 -0500 Subject: [PATCH 45/95] WaveformMarkLabel: skip code when it won't do anything --- src/waveform/waveformmarklabel.cpp | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/waveform/waveformmarklabel.cpp b/src/waveform/waveformmarklabel.cpp index 4334f55c223..135273c80ae 100644 --- a/src/waveform/waveformmarklabel.cpp +++ b/src/waveform/waveformmarklabel.cpp @@ -43,17 +43,21 @@ void WaveformMarkLabel::prerender(QPointF bottomLeft, QPixmap icon, QString text painter.setBrush(QBrush(backgroundColor)); painter.drawRoundedRect(0, 0, pixmapRect.width(), pixmapRect.height(), 5.0, 5.0); - QPointF iconTopLeft = pixmapRect.topLeft(); - iconTopLeft.setX(iconTopLeft.x() + padding); - painter.drawPixmap(iconTopLeft, icon); - - // QPainter::drawText draws from the bottom left point. - QPointF textPoint; - textPoint.setX(icon.width() + padding); - textPoint.setY(fontMetrics.ascent()); - painter.setFont(font); - painter.setPen(textColor); - painter.drawText(textPoint, text); + if (!icon.isNull()) { + QPointF iconTopLeft = pixmapRect.topLeft(); + iconTopLeft.setX(iconTopLeft.x() + padding); + painter.drawPixmap(iconTopLeft, icon); + } + + if (!text.isEmpty()) { + // QPainter::drawText draws from the bottom left point. + QPointF textPoint; + textPoint.setX(icon.width() + padding); + textPoint.setY(fontMetrics.ascent()); + painter.setFont(font); + painter.setPen(textColor); + painter.drawText(textPoint, text); + } }; void WaveformMarkLabel::draw(QPainter* pPainter) { From 38feaf6caa86eb55ddcf82f1438c9d18c9914a36 Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 23 Aug 2019 07:46:49 -0500 Subject: [PATCH 46/95] ColorMenu: remove useColorSet call from constructor --- src/widget/colormenu.cpp | 1 - src/widget/colormenu.h | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widget/colormenu.cpp b/src/widget/colormenu.cpp index b93140ffb65..05260189f15 100644 --- a/src/widget/colormenu.cpp +++ b/src/widget/colormenu.cpp @@ -6,7 +6,6 @@ ColorMenu::ColorMenu(QWidget *parent, PredefinedColorsRepresentation* pColorRepr // If another title would be more appropriate in some context, setTitle // can be called again after construction. setTitle(tr("Set color")); - useColorSet(pColorRepresentation); } void ColorMenu::useColorSet(PredefinedColorsRepresentation* pColorRepresentation) { diff --git a/src/widget/colormenu.h b/src/widget/colormenu.h index c6a30bf2241..a6f4135b43c 100644 --- a/src/widget/colormenu.h +++ b/src/widget/colormenu.h @@ -11,6 +11,7 @@ class ColorMenu : public QMenu { PredefinedColorsRepresentation* pColorRepresentation = nullptr); ~ColorMenu() override; + // NOTE: This must be called before showing this menu. void useColorSet(PredefinedColorsRepresentation* pColorRepresentation); signals: From 27047e2a5499ce79ebaef596b9a683b76c6e9936 Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 23 Aug 2019 08:15:37 -0500 Subject: [PATCH 47/95] WOverview: explain magic number --- src/widget/woverview.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 0b82d3c6f57..4230d143931 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -92,7 +92,7 @@ void WOverview::setup(const QDomNode& node, const SkinContext& context) { m_qColorBackground = m_signalColors.getBgColor(); m_labelBackgroundColor = m_qColorBackground; - m_labelBackgroundColor.setAlpha(128); + m_labelBackgroundColor.setAlpha(255 / 2); // 255 == fully transparent // Clear the background pixmap, if it exists. m_backgroundPixmap = QPixmap(); @@ -755,11 +755,8 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga } } - QColor bgColor = m_qColorBackground; - bgColor.setAlpha(128); - pMark->m_label.prerender(textPoint, QPixmap(), text, - markerFont, pMark->m_textColor, bgColor, + markerFont, pMark->m_textColor, m_labelBackgroundColor, width(), getDevicePixelRatioF(this)); } @@ -801,14 +798,12 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga cueTimeDistanceText = mixxx::Duration::formatTime(markTimeDistance); } - QColor bgColor = m_qColorBackground; - bgColor.setAlpha(128); m_cuePositionLabel.prerender(positionTextPoint, QPixmap(), cuePositionText, - markerFont, Qt::white, bgColor, width(), getDevicePixelRatioF(this)); + markerFont, Qt::white, m_labelBackgroundColor, width(), getDevicePixelRatioF(this)); QPointF timeDistancePoint(markPosition, (fontMetrics.height() + height()) / 2); m_cueTimeDistanceLabel.prerender(timeDistancePoint, QPixmap(), cueTimeDistanceText, - markerFont, Qt::white, bgColor, width(), getDevicePixelRatioF(this)); + markerFont, Qt::white, m_labelBackgroundColor, width(), getDevicePixelRatioF(this)); markHovered = true; } } From e8d3de897bf797b75fbbedaf7a4a0ba9006e692a Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 23 Aug 2019 11:04:05 -0500 Subject: [PATCH 48/95] WOverview: use qreal and remove superfluous explicit casts --- src/widget/woverview.cpp | 44 ++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 4230d143931..78c9622d0d8 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -617,11 +617,11 @@ void WOverview::drawRangeMarks(QPainter* pPainter, const float& offset, const fl // Active mark ranges by definition have starts/ends that are not // disabled. - const double startValue = markRange.start(); - const double endValue = markRange.end(); + const qreal startValue = markRange.start(); + const qreal endValue = markRange.end(); - const float startPosition = offset + startValue * gain; - const float endPosition = offset + endValue * gain; + const qreal startPosition = offset + startValue * gain; + const qreal endPosition = offset + endValue * gain; if (startPosition < 0.0 && endPosition < 0.0) { continue; @@ -679,18 +679,18 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga //const float markPosition = 1.0 + // (m_marksToRender.at(i).m_pointControl->get() / (float)m_trackSamplesControl->get()) * (float)(width()-2); - const float markPosition = math_clamp( + const qreal markPosition = math_clamp( offset + m_marksToRender.at(i)->getSamplePosition() * gain, - 0.0, static_cast(width())); + 0.0, static_cast(width())); pMark->m_linePosition = markPosition; QPen shadowPen(QBrush(pMark->borderColor()), 2.5 * m_scaleFactor); QLineF line; if (m_orientation == Qt::Horizontal) { - line.setLine(markPosition, 0.0, markPosition, static_cast(height())); + line.setLine(markPosition, 0.0, markPosition, height()); } else { - line.setLine(0.0, markPosition, static_cast(width()), markPosition); + line.setLine(0.0, markPosition, width(), markPosition); } pPainter->setPen(shadowPen); pPainter->drawLine(line); @@ -851,9 +851,9 @@ void WOverview::drawTimeRuler(QPainter* pPainter) { if (m_bTimeRulerActive) { QLineF line; if (m_orientation == Qt::Horizontal) { - line.setLine(m_timeRulerPos.x(), 0.0, m_timeRulerPos.x(), static_cast(height())); + line.setLine(m_timeRulerPos.x(), 0.0, m_timeRulerPos.x(), height()); } else { - line.setLine(0.0, m_timeRulerPos.x(), static_cast(width()), m_timeRulerPos.x()); + line.setLine(0.0, m_timeRulerPos.x(), width(), m_timeRulerPos.x()); } pPainter->setPen(shadowPen); pPainter->drawLine(line); @@ -863,8 +863,8 @@ void WOverview::drawTimeRuler(QPainter* pPainter) { QPointF textPoint = m_timeRulerPos; QPointF textPointDistance = m_timeRulerPos; - double widgetPositionFraction; - double padding = 1.0; // spacing between line and text + qreal widgetPositionFraction; + qreal padding = 1.0; // spacing between line and text if (m_orientation == Qt::Horizontal) { textPoint = QPointF(textPoint.x() + padding, fontMetrics.height()); textPointDistance = QPointF(textPointDistance.x() + padding, @@ -875,12 +875,12 @@ void WOverview::drawTimeRuler(QPainter* pPainter) { textPointDistance.setX(0); widgetPositionFraction = m_timeRulerPos.y() / height(); } - double trackSamples = m_trackSamplesControl->get(); - double timePosition = samplePositionToSeconds( + qreal trackSamples = m_trackSamplesControl->get(); + qreal timePosition = samplePositionToSeconds( widgetPositionFraction * trackSamples); - double timePositionTillEnd = samplePositionToSeconds( + qreal timePositionTillEnd = samplePositionToSeconds( (1 - widgetPositionFraction) * trackSamples); - double timeDistance = samplePositionToSeconds( + qreal timeDistance = samplePositionToSeconds( (widgetPositionFraction - m_playpositionControl->get()) * trackSamples); QString timeText = mixxx::Duration::formatTime(timePosition) @@ -937,11 +937,11 @@ void WOverview::drawMarkLabels(QPainter* pPainter, const float offset, const flo if (markRange.showDuration()) { // Active mark ranges by definition have starts/ends that are not // disabled. - const double startValue = markRange.start(); - const double endValue = markRange.end(); + const qreal startValue = markRange.start(); + const qreal endValue = markRange.end(); - const float startPosition = offset + startValue * gain; - const float endPosition = offset + endValue * gain; + const qreal startPosition = offset + startValue * gain; + const qreal endPosition = offset + endValue * gain; if (startPosition < 0.0 && endPosition < 0.0) { continue; @@ -950,8 +950,8 @@ void WOverview::drawMarkLabels(QPainter* pPainter, const float offset, const flo samplePositionToSeconds(endValue - startValue)); QRectF durationRect = fontMetrics.boundingRect(duration); - float padding = 3.0; - float x; + qreal padding = 3.0; + qreal x; WaveformMarkRange::DurationTextLocation textLocation = markRange.durationTextLocation(); if (textLocation == WaveformMarkRange::DurationTextLocation::Before) { From 7056bc3d3eab1b0f74ef5e25a30477ce53f17a58 Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 23 Aug 2019 11:19:18 -0500 Subject: [PATCH 49/95] double indent for long `if` conditions --- src/widget/woverview.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 78c9622d0d8..36d6c2610be 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -920,9 +920,9 @@ void WOverview::drawMarkLabels(QPainter* pPainter, const float offset, const flo } } if (pMark->m_label.intersects(m_cuePositionLabel) - || pMark->m_label.intersects(m_cueTimeDistanceLabel) - || pMark->m_label.intersects(m_timeRulerPositionLabel) - || pMark->m_label.intersects(m_timeRulerDistanceLabel)) { + || pMark->m_label.intersects(m_cueTimeDistanceLabel) + || pMark->m_label.intersects(m_timeRulerPositionLabel) + || pMark->m_label.intersects(m_timeRulerDistanceLabel)) { continue; } @@ -967,9 +967,9 @@ void WOverview::drawMarkLabels(QPainter* pPainter, const float offset, const flo m_labelBackgroundColor, width(), getDevicePixelRatioF(this)); if (!(markRange.m_durationLabel.intersects(m_cuePositionLabel) - || markRange.m_durationLabel.intersects(m_cueTimeDistanceLabel) - || markRange.m_durationLabel.intersects(m_timeRulerPositionLabel) - || markRange.m_durationLabel.intersects(m_timeRulerDistanceLabel))) { + || markRange.m_durationLabel.intersects(m_cueTimeDistanceLabel) + || markRange.m_durationLabel.intersects(m_timeRulerPositionLabel) + || markRange.m_durationLabel.intersects(m_timeRulerDistanceLabel))) { markRange.m_durationLabel.draw(pPainter); } } From 5d3159ff2499016434b10bbd2d1d1007b0c4fab6 Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 23 Aug 2019 11:19:28 -0500 Subject: [PATCH 50/95] remove commented code --- src/widget/woverview.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 36d6c2610be..4cf111081b6 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -677,8 +677,6 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga WaveformMarkPointer pMark = m_marksToRender.at(i); PainterScope painterScope(pPainter); - //const float markPosition = 1.0 + - // (m_marksToRender.at(i).m_pointControl->get() / (float)m_trackSamplesControl->get()) * (float)(width()-2); const qreal markPosition = math_clamp( offset + m_marksToRender.at(i)->getSamplePosition() * gain, 0.0, static_cast(width())); From 641250db22c12c0055648c0d8c003733b37fe9ef Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 23 Aug 2019 11:30:43 -0500 Subject: [PATCH 51/95] WaveformMarkRender: remove unnecessary check for null text --- src/waveform/renderers/waveformrendermark.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/waveform/renderers/waveformrendermark.cpp b/src/waveform/renderers/waveformrendermark.cpp index 5f239b4229f..df5cb87b64b 100644 --- a/src/waveform/renderers/waveformrendermark.cpp +++ b/src/waveform/renderers/waveformrendermark.cpp @@ -129,8 +129,8 @@ void WaveformRenderMark::slotCuesUpdated() { QString newLabel = pCue->getLabel(); QColor newColor = m_predefinedColorsRepresentation.representationFor(pCue->getColor()); - if (pMark->m_text.isNull() || newLabel != pMark->m_text || - !pMark->fillColor().isValid() || newColor != pMark->fillColor()) { + if (newLabel != pMark->m_text || newColor != pMark->fillColor() + || !pMark->fillColor().isValid()) { pMark->m_text = newLabel; pMark->setBaseColor(newColor); generateMarkImage(pMark); From 106e1a6685a17a9b45c357f20cac214dabf1b451 Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 23 Aug 2019 11:59:08 -0500 Subject: [PATCH 52/95] ColorMenu: refactor to avoid deleting/creating new QActions --- src/widget/colormenu.cpp | 33 +++++++++++++-------------------- src/widget/colormenu.h | 8 ++------ src/widget/cuemenu.cpp | 4 ++-- src/widget/cuemenu.h | 3 +-- 4 files changed, 18 insertions(+), 30 deletions(-) diff --git a/src/widget/colormenu.cpp b/src/widget/colormenu.cpp index 05260189f15..543b2710842 100644 --- a/src/widget/colormenu.cpp +++ b/src/widget/colormenu.cpp @@ -1,15 +1,11 @@ #include "widget/colormenu.h" #include "util/color/color.h" -ColorMenu::ColorMenu(QWidget *parent, PredefinedColorsRepresentation* pColorRepresentation) +ColorMenu::ColorMenu(QWidget *parent) : QMenu(parent) { // If another title would be more appropriate in some context, setTitle // can be called again after construction. setTitle(tr("Set color")); -} - -void ColorMenu::useColorSet(PredefinedColorsRepresentation* pColorRepresentation) { - clear(); for (const auto& pColor : Color::kPredefinedColorsSet.allColors) { if (*pColor == *Color::kPredefinedColorsSet.noColor) { continue; @@ -17,14 +13,10 @@ void ColorMenu::useColorSet(PredefinedColorsRepresentation* pColorRepresentation QAction* pColorAction = new QAction(pColor->m_sDisplayName, this); QPixmap pixmap(80, 80); - if (pColorRepresentation == nullptr) { - pixmap.fill(pColor->m_defaultRgba); - } else { - pixmap.fill(pColorRepresentation->representationFor(pColor)); - } + pixmap.fill(pColor->m_defaultRgba); pColorAction->setIcon(QIcon(pixmap)); - m_pColorActions.append(pColorAction); + m_pColorActions.insert(pColor, pColorAction); addAction(pColorAction); connect(pColorAction, &QAction::triggered, this, [pColor, this]() { emit(colorPicked(pColor)); @@ -32,15 +24,16 @@ void ColorMenu::useColorSet(PredefinedColorsRepresentation* pColorRepresentation } } -void ColorMenu::clear() { - for (auto& pAction : m_pColorActions) { - if (pAction != nullptr) { - delete pAction; +void ColorMenu::useColorSet(PredefinedColorsRepresentation* pColorRepresentation) { + QMapIterator i(m_pColorActions); + while (i.hasNext()) { + i.next(); + QPixmap pixmap(80, 80); + if (pColorRepresentation == nullptr) { + pixmap.fill(i.key()->m_defaultRgba); + } else { + pixmap.fill(pColorRepresentation->representationFor(i.key())); } + i.value()->setIcon(QIcon(pixmap)); } - m_pColorActions.clear(); -} - -ColorMenu::~ColorMenu() { - clear(); } diff --git a/src/widget/colormenu.h b/src/widget/colormenu.h index a6f4135b43c..4c82943cbb8 100644 --- a/src/widget/colormenu.h +++ b/src/widget/colormenu.h @@ -7,17 +7,13 @@ class ColorMenu : public QMenu { Q_OBJECT public: - ColorMenu(QWidget *parent = nullptr, - PredefinedColorsRepresentation* pColorRepresentation = nullptr); - ~ColorMenu() override; + ColorMenu(QWidget *parent = nullptr); - // NOTE: This must be called before showing this menu. void useColorSet(PredefinedColorsRepresentation* pColorRepresentation); signals: void colorPicked(PredefinedColorPointer pColor); private: - void clear(); - QList m_pColorActions; + QMap m_pColorActions; }; diff --git a/src/widget/cuemenu.cpp b/src/widget/cuemenu.cpp index c828d027885..274bbd259f0 100644 --- a/src/widget/cuemenu.cpp +++ b/src/widget/cuemenu.cpp @@ -3,13 +3,13 @@ #include "widget/cuemenu.h" #include "util/color/color.h" -CueMenu::CueMenu(QWidget *parent, PredefinedColorsRepresentation* pColorRepresentation) +CueMenu::CueMenu(QWidget *parent) : QMenu(parent) { m_pEditLabel = new QAction(tr("Edit label")); addAction(m_pEditLabel); connect(m_pEditLabel, &QAction::triggered, this, &CueMenu::slotEditLabel); - m_pColorMenu = new ColorMenu(this, pColorRepresentation); + m_pColorMenu = new ColorMenu(this); connect(m_pColorMenu, &ColorMenu::colorPicked, this, &CueMenu::slotChangeCueColor); addMenu(m_pColorMenu); diff --git a/src/widget/cuemenu.h b/src/widget/cuemenu.h index cdd209a07ec..3802292e44a 100644 --- a/src/widget/cuemenu.h +++ b/src/widget/cuemenu.h @@ -9,8 +9,7 @@ class CueMenu : public QMenu { Q_OBJECT public: - CueMenu(QWidget *parent = nullptr, - PredefinedColorsRepresentation* pColorRepresentation = nullptr); + CueMenu(QWidget *parent = nullptr); ~CueMenu() override; void setCue(CuePointer pCue) { From 7cd7d49d47fd3df638f530eed536f67a64dc3d46 Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 23 Aug 2019 16:45:49 -0500 Subject: [PATCH 53/95] WOverview: seek on left mouse button press --- src/widget/woverview.cpp | 111 +++++++++++++++++---------------------- src/widget/woverview.h | 2 - 2 files changed, 47 insertions(+), 66 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 4cf111081b6..4340cbfd32f 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -55,7 +55,6 @@ WOverview::WOverview( m_pConfig(pConfig), m_endOfTrack(false), m_pCueMenu(std::make_unique(this)), - m_bDrag(false), m_iPos(0), m_pHoveredMark(nullptr), m_bTimeRulerActive(false), @@ -180,17 +179,15 @@ void WOverview::setup(const QDomNode& node, const SkinContext& context) { void WOverview::onConnectedControlChanged(double dParameter, double dValue) { Q_UNUSED(dValue); - if (!m_bDrag) { - // Calculate handle position. Clamp the value within 0-1 because that's - // all we represent with this widget. - dParameter = math_clamp(dParameter, 0.0, 1.0); - - int iPos = valueToPosition(dParameter); - if (iPos != m_iPos) { - m_iPos = iPos; - //qDebug() << "WOverview::onConnectedControlChanged" << dParameter << ">>" << m_iPos; - update(); - } + // Calculate handle position. Clamp the value within 0-1 because that's + // all we represent with this widget. + dParameter = math_clamp(dParameter, 0.0, 1.0); + + int iPos = valueToPosition(dParameter); + if (iPos != m_iPos) { + m_iPos = iPos; + //qDebug() << "WOverview::onConnectedControlChanged" << dParameter << ">>" << m_iPos; + update(); } } @@ -349,14 +346,6 @@ void WOverview::receiveCuesUpdated() { } void WOverview::mouseMoveEvent(QMouseEvent* e) { - if (m_bDrag) { - if (m_orientation == Qt::Horizontal) { - m_iPos = math_clamp(e->x(), 0, width() - 1); - } else { - m_iPos = math_clamp(e->y(), 0, height() - 1); - } - } - // Do not activate cue hovering while right click is held down and the // button down event was not on a cue. if (m_bTimeRulerActive) { @@ -399,20 +388,9 @@ void WOverview::mouseMoveEvent(QMouseEvent* e) { void WOverview::mouseReleaseEvent(QMouseEvent* e) { mouseMoveEvent(e); - double dValue = positionToValue(m_iPos); //qDebug() << "WOverview::mouseReleaseEvent" << e->pos() << m_iPos << ">>" << dValue; - if (e->button() == Qt::LeftButton) { - // If a hotcue label is being hovered, jump to it instead of the point - // under the cursor. - if (m_pHoveredMark != nullptr) { - dValue = m_pHoveredMark->getSamplePosition() / m_trackSamplesControl->get(); - } - setControlParameterUp(dValue); - m_bDrag = false; - // Do not seek when releasing a right click. This is important to - // prevent accidental seeking when trying to right click a hotcue. - } else if (e->button() == Qt::RightButton) { + if (e->button() == Qt::RightButton) { m_bTimeRulerActive = false; } } @@ -420,48 +398,53 @@ void WOverview::mouseReleaseEvent(QMouseEvent* e) { void WOverview::mousePressEvent(QMouseEvent* e) { //qDebug() << "WOverview::mousePressEvent" << e->pos(); mouseMoveEvent(e); - bool dragging = true; - bool hotcueRightClicked = false; - if (m_pCurrentTrack != nullptr) { - QList cueList = m_pCurrentTrack->getCuePoints(); + if (m_pCurrentTrack == nullptr) { + return; + } + + if (e->button() == Qt::LeftButton) { + if (m_orientation == Qt::Horizontal) { + m_iPos = math_clamp(e->x(), 0, width() - 1); + } else { + m_iPos = math_clamp(e->y(), 0, height() - 1); + } + + double dValue = positionToValue(m_iPos); if (m_pHoveredMark != nullptr) { - if (e->button() == Qt::LeftButton) { - dragging = false; - } else if (e->button() == Qt::RightButton - && m_pHoveredMark->getHotCue() != WaveformMark::kNoHotCue) { - // Currently the only way WaveformMarks can be associated - // with their respective Cue objects is by using the hotcue - // number. If cues without assigned hotcue are drawn on - // WOverview in the future, another way to associate - // WaveformMarks with Cues will need to be implemented. - CuePointer pHoveredCue; - for (const auto& pCue : cueList) { - if (pCue->getHotCue() == m_pHoveredMark->getHotCue()) { - pHoveredCue = pCue; - hotcueRightClicked = true; - break; - } - } - if (pHoveredCue != nullptr) { - m_pCueMenu->setCue(pHoveredCue); - m_pCueMenu->setTrack(m_pCurrentTrack); - m_pCueMenu->popup(e->globalPos()); + dValue = m_pHoveredMark->getSamplePosition() / m_trackSamplesControl->get(); + m_iPos = valueToPosition(dValue); + } + setControlParameterUp(dValue); + } else if (e->button() == Qt::RightButton) { + if (m_pHoveredMark == nullptr) { + m_bTimeRulerActive = true; + m_timeRulerPos = e->pos(); + } else if (m_pHoveredMark->getHotCue() != WaveformMark::kNoHotCue) { + // Currently the only way WaveformMarks can be associated + // with their respective Cue objects is by using the hotcue + // number. If cues without assigned hotcue are drawn on + // WOverview in the future, another way to associate + // WaveformMarks with Cues will need to be implemented. + CuePointer pHoveredCue; + QList cueList = m_pCurrentTrack->getCuePoints(); + for (const auto& pCue : cueList) { + if (pCue->getHotCue() == m_pHoveredMark->getHotCue()) { + pHoveredCue = pCue; + break; } } + if (pHoveredCue != nullptr) { + m_pCueMenu->setCue(pHoveredCue); + m_pCueMenu->setTrack(m_pCurrentTrack); + m_pCueMenu->popup(e->globalPos()); + } } } - if (e->button() == Qt::LeftButton) { - m_bDrag = dragging; - } else if (e->button() == Qt::RightButton && !hotcueRightClicked) { - m_bTimeRulerActive = true; - m_timeRulerPos = e->pos(); - } } void WOverview::leaveEvent(QEvent* e) { Q_UNUSED(e); m_pHoveredMark = nullptr; - m_bDrag = false; m_bTimeRulerActive = false; update(); } diff --git a/src/widget/woverview.h b/src/widget/woverview.h index 858a422023d..a1e8a65fce0 100644 --- a/src/widget/woverview.h +++ b/src/widget/woverview.h @@ -144,8 +144,6 @@ class WOverview : public WWidget, public TrackDropTarget { std::unique_ptr m_pCueMenu; - // True if slider is dragged. Only used when m_bEventWhileDrag is false - bool m_bDrag; // Internal storage of slider position in pixels int m_iPos; From 52f520e97cc447737a31e4939798e8cc433b0c6c Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 23 Aug 2019 17:12:13 -0500 Subject: [PATCH 54/95] WOverview: show negative times for cue/right click behind playhead --- src/widget/woverview.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 4340cbfd32f..547635b29c9 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -772,11 +772,11 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga double markTimeDistance = samplePositionToSeconds(markSamples - currentPositionSamples); QString cuePositionText = mixxx::Duration::formatTime(markTime) + " -" + mixxx::Duration::formatTime(markTimeRemaining); - QString cueTimeDistanceText; - // Do not show the time until the cue point if the playhead is past - // the cue point. - if (markTimeDistance > 0) { - cueTimeDistanceText = mixxx::Duration::formatTime(markTimeDistance); + QString cueTimeDistanceText = mixxx::Duration::formatTime(fabs(markTimeDistance)); + // Cast to int to avoid confusingly switching from -0:00 to 0:00 as + // the playhead passes the cue + if (static_cast(markTimeDistance) < 0) { + cueTimeDistanceText = "-" + cueTimeDistanceText; } m_cuePositionLabel.prerender(positionTextPoint, QPixmap(), cuePositionText, @@ -871,9 +871,11 @@ void WOverview::drawTimeRuler(QPainter* pPainter) { markerFont, Qt::white, m_labelBackgroundColor, width(), getDevicePixelRatioF(this)); m_timeRulerPositionLabel.draw(pPainter); - QString timeDistanceText; - if (timeDistance > 0) { - timeDistanceText = mixxx::Duration::formatTime(timeDistance); + QString timeDistanceText = mixxx::Duration::formatTime(fabs(timeDistance)); + // Cast to int to avoid confusingly switching from -0:00 to 0:00 as + // the playhead passes the point + if (static_cast(timeDistance) < 0) { + timeDistanceText = "-" + timeDistanceText; } m_timeRulerDistanceLabel.prerender(textPointDistance, QPixmap(), timeDistanceText, markerFont, Qt::white, m_labelBackgroundColor, width(), getDevicePixelRatioF(this)); From 9e53801d372fa014f20867be736e43bb054cb723 Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 23 Aug 2019 17:31:37 -0500 Subject: [PATCH 55/95] WOverview: update at least once per second --- src/widget/woverview.cpp | 15 +++++++++++---- src/widget/woverview.h | 1 + 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 547635b29c9..cae48c2314d 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -55,6 +55,7 @@ WOverview::WOverview( m_pConfig(pConfig), m_endOfTrack(false), m_pCueMenu(std::make_unique(this)), + m_iPosSeconds(0), m_iPos(0), m_pHoveredMark(nullptr), m_bTimeRulerActive(false), @@ -183,10 +184,16 @@ void WOverview::onConnectedControlChanged(double dParameter, double dValue) { // all we represent with this widget. dParameter = math_clamp(dParameter, 0.0, 1.0); - int iPos = valueToPosition(dParameter); - if (iPos != m_iPos) { - m_iPos = iPos; - //qDebug() << "WOverview::onConnectedControlChanged" << dParameter << ">>" << m_iPos; + int oldPos = m_iPos; + m_iPos = valueToPosition(dParameter); + + // In case the user is hovering a cue point or holding right click, the + // calculated time between the playhead and cue/cursor should be updated at + // least once per second, regardless of m_iPos which depends on the length + // of the widget. + int oldPositionSeconds = m_iPosSeconds; + m_iPosSeconds = static_cast(dParameter * m_trackSamplesControl->get()); + if (oldPositionSeconds != m_iPosSeconds || oldPos != m_iPos) { update(); } } diff --git a/src/widget/woverview.h b/src/widget/woverview.h index a1e8a65fce0..da171c4bf26 100644 --- a/src/widget/woverview.h +++ b/src/widget/woverview.h @@ -144,6 +144,7 @@ class WOverview : public WWidget, public TrackDropTarget { std::unique_ptr m_pCueMenu; + int m_iPosSeconds; // Internal storage of slider position in pixels int m_iPos; From b81d73700a0766ead9f7df9186e31e881e366b2c Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 23 Aug 2019 17:39:22 -0500 Subject: [PATCH 56/95] WOverview: increasing padding width for hovering cue lines --- src/widget/woverview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index cae48c2314d..3a120253dd9 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -371,7 +371,7 @@ void WOverview::mouseMoveEvent(QMouseEvent* e) { m_pHoveredMark = nullptr; // Without some padding, the user would only have a single pixel width that // would count as hovering over the WaveformMark. - float lineHoverPadding = 3.0; + float lineHoverPadding = 5.0; for (const auto& pMark : m_marksToRender) { int hoveredPosition; if (m_orientation == Qt::Horizontal) { From 65dab6b7cf607e7c761d7235c8aeed115cc51a59 Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 23 Aug 2019 17:49:00 -0500 Subject: [PATCH 57/95] WOverview: only update >= once per second if that is actually required --- src/widget/woverview.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 3a120253dd9..4afdc9b5e6f 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -184,8 +184,12 @@ void WOverview::onConnectedControlChanged(double dParameter, double dValue) { // all we represent with this widget. dParameter = math_clamp(dParameter, 0.0, 1.0); + bool redraw = false; int oldPos = m_iPos; m_iPos = valueToPosition(dParameter); + if (oldPos != m_iPos) { + redraw = true; + } // In case the user is hovering a cue point or holding right click, the // calculated time between the playhead and cue/cursor should be updated at @@ -193,7 +197,12 @@ void WOverview::onConnectedControlChanged(double dParameter, double dValue) { // of the widget. int oldPositionSeconds = m_iPosSeconds; m_iPosSeconds = static_cast(dParameter * m_trackSamplesControl->get()); - if (oldPositionSeconds != m_iPosSeconds || oldPos != m_iPos) { + if ((m_bTimeRulerActive || m_pHoveredMark != nullptr) + && oldPositionSeconds != m_iPosSeconds) { + redraw = true; + } + + if (redraw) { update(); } } From 37175773fb0652c4b814e7e5b87add88100a56a2 Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 23 Aug 2019 18:01:10 -0500 Subject: [PATCH 58/95] WOverview: clamp dragged mouse position when moved left of widget --- src/widget/woverview.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 4afdc9b5e6f..d142180fd92 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -365,14 +365,10 @@ void WOverview::mouseMoveEvent(QMouseEvent* e) { // Do not activate cue hovering while right click is held down and the // button down event was not on a cue. if (m_bTimeRulerActive) { - m_timeRulerPos = e->pos(); // Prevent showing times beyond the boundaries of the track when the // cursor is dragged outside this widget before releasing right click. - if (m_timeRulerPos.x() > width()) { - m_timeRulerPos.setX(width()); - } else if (m_timeRulerPos.y() > height()) { - m_timeRulerPos.setY(height()); - } + m_timeRulerPos.setX(math_clamp(e->pos().x(), 0, width())); + m_timeRulerPos.setY(math_clamp(e->pos().y(), 0, height())); update(); return; } From 6afd934251e5672d905f56227d5c6e6699f49d11 Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 23 Aug 2019 18:01:26 -0500 Subject: [PATCH 59/95] Deere: let hotcue labels show lowercase letters --- res/skins/Deere/style.qss | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/res/skins/Deere/style.qss b/res/skins/Deere/style.qss index 6f224f18b68..8d79865105a 100644 --- a/res/skins/Deere/style.qss +++ b/res/skins/Deere/style.qss @@ -740,6 +740,11 @@ WWidget, QLabel { text-transform: uppercase; } +WOverview { + /* This affects the labels for cue points. */ + text-transform: none; +} + /* Start spacing for Deck overview row (small waveform, option grid) */ #OptionGrid, #ButtonGrid { background-color: #333333; From 94604cdf16af8255b01d08cbe24e9d790329cb87 Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 23 Aug 2019 18:01:48 -0500 Subject: [PATCH 60/95] Deere: increase default font size to 12px --- res/skins/Deere/style.qss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/skins/Deere/style.qss b/res/skins/Deere/style.qss index 8d79865105a..0c358384f76 100644 --- a/res/skins/Deere/style.qss +++ b/res/skins/Deere/style.qss @@ -736,7 +736,7 @@ QAbstractScrollArea::corner { WWidget, QLabel { font-family: "Open Sans"; - font-size: 8px; + font-size: 12px; text-transform: uppercase; } From e6f012e1ff3fd861909fa6b7f451dc9e9c736a08 Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 23 Aug 2019 18:15:21 -0500 Subject: [PATCH 61/95] WaveformMarkLabel: elide text that is wider than widget --- src/waveform/waveformmarklabel.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/waveform/waveformmarklabel.cpp b/src/waveform/waveformmarklabel.cpp index 135273c80ae..f04a9c87c27 100644 --- a/src/waveform/waveformmarklabel.cpp +++ b/src/waveform/waveformmarklabel.cpp @@ -15,10 +15,18 @@ void WaveformMarkLabel::prerender(QPointF bottomLeft, QPixmap icon, QString text QRectF pixmapRect; pixmapRect = fontMetrics.boundingRect(text); + int availableWidthForText; if (icon.isNull()) { pixmapRect.setWidth(padding + pixmapRect.width() + padding); + availableWidthForText = widgetWidth - padding * 2; } else { pixmapRect.setWidth(padding + icon.width() + padding + pixmapRect.width() + padding); + availableWidthForText = widgetWidth - padding * 3; + } + // Elide extremely long labels + if (pixmapRect.width() > widgetWidth) { + text = fontMetrics.elidedText(text, Qt::ElideRight, availableWidthForText); + pixmapRect.setWidth(widgetWidth); } pixmapRect.setHeight(math_max(fontMetrics.height(), icon.height())); From 52968ffea867c3488d9522a73328b9ea7e00f681 Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 23 Aug 2019 18:32:04 -0500 Subject: [PATCH 62/95] WaveformMarkLabel: rename textPoint -> textBottomLeft --- src/waveform/waveformmarklabel.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/waveform/waveformmarklabel.cpp b/src/waveform/waveformmarklabel.cpp index f04a9c87c27..2c4cf0b8b00 100644 --- a/src/waveform/waveformmarklabel.cpp +++ b/src/waveform/waveformmarklabel.cpp @@ -58,13 +58,12 @@ void WaveformMarkLabel::prerender(QPointF bottomLeft, QPixmap icon, QString text } if (!text.isEmpty()) { - // QPainter::drawText draws from the bottom left point. - QPointF textPoint; - textPoint.setX(icon.width() + padding); - textPoint.setY(fontMetrics.ascent()); + QPointF textBottomLeft; + textBottomLeft.setX(icon.width() + padding); + textBottomLeft.setY(fontMetrics.ascent()); painter.setFont(font); painter.setPen(textColor); - painter.drawText(textPoint, text); + painter.drawText(textBottomLeft, text); } }; From f52069300d4a5ab329d3c385828a663b4c3f2aae Mon Sep 17 00:00:00 2001 From: Be Date: Fri, 23 Aug 2019 18:40:36 -0500 Subject: [PATCH 63/95] fix indentation --- src/widget/woverview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index d142180fd92..8320c28033f 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -911,7 +911,7 @@ void WOverview::drawMarkLabels(QPainter* pPainter, const float offset, const flo for (const auto& pMark : m_marksToRender) { if (m_pHoveredMark != nullptr && pMark != m_pHoveredMark) { if (pMark->m_label.intersects(m_pHoveredMark->m_label)) { - continue; + continue; } } if (pMark->m_label.intersects(m_cuePositionLabel) From f1877370b142a65f3d5075d2bedc3ae2d8ee8aa0 Mon Sep 17 00:00:00 2001 From: Be Date: Mon, 26 Aug 2019 13:30:26 -0500 Subject: [PATCH 64/95] fix build issues with old Qt versions --- src/widget/cuemenu.cpp | 4 ++-- src/widget/woverview.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/widget/cuemenu.cpp b/src/widget/cuemenu.cpp index 274bbd259f0..331b180145f 100644 --- a/src/widget/cuemenu.cpp +++ b/src/widget/cuemenu.cpp @@ -5,7 +5,7 @@ CueMenu::CueMenu(QWidget *parent) : QMenu(parent) { - m_pEditLabel = new QAction(tr("Edit label")); + m_pEditLabel = new QAction(tr("Edit label"), this); addAction(m_pEditLabel); connect(m_pEditLabel, &QAction::triggered, this, &CueMenu::slotEditLabel); @@ -13,7 +13,7 @@ CueMenu::CueMenu(QWidget *parent) connect(m_pColorMenu, &ColorMenu::colorPicked, this, &CueMenu::slotChangeCueColor); addMenu(m_pColorMenu); - m_pRemoveCue = new QAction(tr("Remove")); + m_pRemoveCue = new QAction(tr("Remove"), this); addAction(m_pRemoveCue); connect(m_pRemoveCue, &QAction::triggered, this, &CueMenu::slotRemoveCue); } diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 8320c28033f..10c64e9dedf 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -373,7 +373,7 @@ void WOverview::mouseMoveEvent(QMouseEvent* e) { return; } - m_pHoveredMark = nullptr; + m_pHoveredMark.clear(); // Without some padding, the user would only have a single pixel width that // would count as hovering over the WaveformMark. float lineHoverPadding = 5.0; @@ -456,7 +456,7 @@ void WOverview::mousePressEvent(QMouseEvent* e) { void WOverview::leaveEvent(QEvent* e) { Q_UNUSED(e); - m_pHoveredMark = nullptr; + m_pHoveredMark.clear(); m_bTimeRulerActive = false; update(); } From 529be488ae4abfaa4e83410f117afc9cc67d29f1 Mon Sep 17 00:00:00 2001 From: Be Date: Mon, 26 Aug 2019 18:44:06 -0500 Subject: [PATCH 65/95] LateNight: don't capitalize cue labels on overview waveform --- res/skins/LateNight/style.qss | 1 + 1 file changed, 1 insertion(+) diff --git a/res/skins/LateNight/style.qss b/res/skins/LateNight/style.qss index f9caa116516..29975f52f52 100644 --- a/res/skins/LateNight/style.qss +++ b/res/skins/LateNight/style.qss @@ -30,6 +30,7 @@ WBeatSpinBox, QSpinBox, WOverview { font-family: "Open Sans"; + text-transform: none; } WPushButton, QPushButton { From 058f0d4f5a98593df97396ed49f873d65145d840 Mon Sep 17 00:00:00 2001 From: Be Date: Mon, 26 Aug 2019 19:04:18 -0500 Subject: [PATCH 66/95] remove unused code --- src/widget/woverview.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 10c64e9dedf..6e9eb608467 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -650,10 +650,6 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga markerFont.setPixelSize(10 * m_scaleFactor); QFontMetricsF fontMetrics(markerFont); - QFont shadowFont = pPainter->font(); - shadowFont.setWeight(99); - shadowFont.setPixelSize(10 * m_scaleFactor); - // Text labels are rendered so they do not overlap with other WaveformMarks' // labels. If the text would be too wide, it is elided. However, the user // can hover the mouse cursor over a label to show the whole label text, @@ -903,10 +899,6 @@ void WOverview::drawMarkLabels(QPainter* pPainter, const float offset, const flo markerFont.setPixelSize(10 * m_scaleFactor); QFontMetricsF fontMetrics(markerFont); - QFont shadowFont = pPainter->font(); - shadowFont.setWeight(99); - shadowFont.setPixelSize(10 * m_scaleFactor); - // Draw WaveformMark labels for (const auto& pMark : m_marksToRender) { if (m_pHoveredMark != nullptr && pMark != m_pHoveredMark) { From 4439406ad548be2804d05da53572e4cd156f5838 Mon Sep 17 00:00:00 2001 From: Be Date: Mon, 26 Aug 2019 19:23:38 -0500 Subject: [PATCH 67/95] WOverview: let skins specify label background, text color, and font size --- src/widget/woverview.cpp | 39 +++++++++++++++++++++++++++------------ src/widget/woverview.h | 2 ++ 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 6e9eb608467..19091fadd97 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -91,8 +91,23 @@ void WOverview::setup(const QDomNode& node, const SkinContext& context) { m_signalColors.setup(node, context); m_qColorBackground = m_signalColors.getBgColor(); - m_labelBackgroundColor = m_qColorBackground; - m_labelBackgroundColor.setAlpha(255 / 2); // 255 == fully transparent + + m_labelBackgroundColor = context.selectColor(node, "LabelBackgroundColor"); + if (!m_labelBackgroundColor.isValid()) { + m_labelBackgroundColor = m_qColorBackground; + m_labelBackgroundColor.setAlpha(255 / 2); // 255 == fully transparent + } + + m_labelTextColor = context.selectColor(node, "LabelTextColor"); + if (!m_labelTextColor.isValid()) { + m_labelTextColor = Qt::white; + } + + bool okay = false; + m_iLabelFontSize = context.selectInt(node, "LabelFontSize", &okay); + if (!okay) { + m_iLabelFontSize = 10; + } // Clear the background pixmap, if it exists. m_backgroundPixmap = QPixmap(); @@ -647,7 +662,7 @@ void WOverview::drawRangeMarks(QPainter* pPainter, const float& offset, const fl void WOverview::drawMarks(QPainter* pPainter, const float offset, const float gain) { QFont markerFont = pPainter->font(); - markerFont.setPixelSize(10 * m_scaleFactor); + markerFont.setPixelSize(m_iLabelFontSize * m_scaleFactor); QFontMetricsF fontMetrics(markerFont); // Text labels are rendered so they do not overlap with other WaveformMarks' @@ -745,7 +760,7 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga } pMark->m_label.prerender(textPoint, QPixmap(), text, - markerFont, pMark->m_textColor, m_labelBackgroundColor, + markerFont, m_labelTextColor, m_labelBackgroundColor, width(), getDevicePixelRatioF(this)); } @@ -788,11 +803,11 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga } m_cuePositionLabel.prerender(positionTextPoint, QPixmap(), cuePositionText, - markerFont, Qt::white, m_labelBackgroundColor, width(), getDevicePixelRatioF(this)); + markerFont, m_labelTextColor, m_labelBackgroundColor, width(), getDevicePixelRatioF(this)); QPointF timeDistancePoint(markPosition, (fontMetrics.height() + height()) / 2); m_cueTimeDistanceLabel.prerender(timeDistancePoint, QPixmap(), cueTimeDistanceText, - markerFont, Qt::white, m_labelBackgroundColor, width(), getDevicePixelRatioF(this)); + markerFont, m_labelTextColor, m_labelBackgroundColor, width(), getDevicePixelRatioF(this)); markHovered = true; } } @@ -829,12 +844,12 @@ void WOverview::drawCurrentPosition(QPainter* pPainter) { void WOverview::drawTimeRuler(QPainter* pPainter) { QFont markerFont = pPainter->font(); - markerFont.setPixelSize(10 * m_scaleFactor); + markerFont.setPixelSize(m_iLabelFontSize * m_scaleFactor); QFontMetricsF fontMetrics(markerFont); QFont shadowFont = pPainter->font(); shadowFont.setWeight(99); - shadowFont.setPixelSize(10 * m_scaleFactor); + shadowFont.setPixelSize(m_iLabelFontSize * m_scaleFactor); QPen shadowPen(Qt::black, 2.5 * m_scaleFactor); if (m_bTimeRulerActive) { @@ -876,7 +891,7 @@ void WOverview::drawTimeRuler(QPainter* pPainter) { + " -" + mixxx::Duration::formatTime(timePositionTillEnd); m_timeRulerPositionLabel.prerender(textPoint, QPixmap(), timeText, - markerFont, Qt::white, m_labelBackgroundColor, width(), getDevicePixelRatioF(this)); + markerFont, m_labelTextColor, m_labelBackgroundColor, width(), getDevicePixelRatioF(this)); m_timeRulerPositionLabel.draw(pPainter); QString timeDistanceText = mixxx::Duration::formatTime(fabs(timeDistance)); @@ -886,7 +901,7 @@ void WOverview::drawTimeRuler(QPainter* pPainter) { timeDistanceText = "-" + timeDistanceText; } m_timeRulerDistanceLabel.prerender(textPointDistance, QPixmap(), timeDistanceText, - markerFont, Qt::white, m_labelBackgroundColor, width(), getDevicePixelRatioF(this)); + markerFont, m_labelTextColor, m_labelBackgroundColor, width(), getDevicePixelRatioF(this)); m_timeRulerDistanceLabel.draw(pPainter); } else { m_timeRulerPositionLabel.clear(); @@ -896,7 +911,7 @@ void WOverview::drawTimeRuler(QPainter* pPainter) { void WOverview::drawMarkLabels(QPainter* pPainter, const float offset, const float gain) { QFont markerFont = pPainter->font(); - markerFont.setPixelSize(10 * m_scaleFactor); + markerFont.setPixelSize(m_iLabelFontSize * m_scaleFactor); QFontMetricsF fontMetrics(markerFont); // Draw WaveformMark labels @@ -950,7 +965,7 @@ void WOverview::drawMarkLabels(QPainter* pPainter, const float offset, const flo QPointF durationBottomLeft(x, fontMetrics.height()); markRange.m_durationLabel.prerender(durationBottomLeft, QPixmap(), - duration, markerFont, markRange.m_durationTextColor, + duration, markerFont, m_labelTextColor, m_labelBackgroundColor, width(), getDevicePixelRatioF(this)); if (!(markRange.m_durationLabel.intersects(m_cuePositionLabel) diff --git a/src/widget/woverview.h b/src/widget/woverview.h index da171c4bf26..4fc6ddbdf91 100644 --- a/src/widget/woverview.h +++ b/src/widget/woverview.h @@ -159,6 +159,8 @@ class WOverview : public WWidget, public TrackDropTarget { QPixmap m_backgroundPixmap; QString m_backgroundPixmapPath; QColor m_qColorBackground; + int m_iLabelFontSize; + QColor m_labelTextColor; QColor m_labelBackgroundColor; QColor m_endOfTrackColor; From 25a653efebc8bbb77e47a0c7ef8ec3ac0ba889e2 Mon Sep 17 00:00:00 2001 From: Be Date: Mon, 26 Aug 2019 19:28:12 -0500 Subject: [PATCH 68/95] Shade: specify overview label colors and font size --- res/skins/Shade/deck_overview.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/res/skins/Shade/deck_overview.xml b/res/skins/Shade/deck_overview.xml index 875b722b36f..a625535e1a1 100644 --- a/res/skins/Shade/deck_overview.xml +++ b/res/skins/Shade/deck_overview.xml @@ -14,6 +14,9 @@ #00FF00 #EA0000 #60000000 + #80000000 + #ffffff + 9 bottom|right #FD0564 From ed45d458cf0d59941f39dfa5eddbdaccc09b572b Mon Sep 17 00:00:00 2001 From: Be Date: Sun, 1 Sep 2019 13:05:18 -0500 Subject: [PATCH 69/95] WOverview: only consider labels at same height for eliding width --- src/widget/woverview.cpp | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 19091fadd97..9fa3d39c693 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -709,11 +709,25 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga QString text = pMark->m_text; // Only allow the text to overlap the following mark if the mouse is - // hovering over it. Otherwise, elide it if it would render over - // the next label. + // hovering over it. Elide it if it would render over the next + // label, but do not elide it if the next mark's label is not at the + // same vertical position. if (pMark != m_pHoveredMark && i < m_marksToRender.size()-1) { - const float nextMarkPosition = offset + m_marksToRender.at(i+1)->getSamplePosition() * gain; - text = fontMetrics.elidedText(text, Qt::ElideRight, nextMarkPosition - markPosition - 5); + float nextMarkPosition = -1.0; + for (int m = i + 1; m < m_marksToRender.size() - 1; ++m) { + WaveformMarkPointer otherMark = m_marksToRender.at(m); + bool otherAtSameHeight = valign == (otherMark->m_align & Qt::AlignVertical_Mask); + // Hotcues always show at least their number. + bool otherHasLabel = !otherMark->m_text.isEmpty() + || otherMark->getHotCue() != WaveformMark::kNoHotCue; + if (otherAtSameHeight && otherHasLabel) { + nextMarkPosition = offset + otherMark->getSamplePosition() * gain; + break; + } + } + if (nextMarkPosition != -1.0) { + text = fontMetrics.elidedText(text, Qt::ElideRight, nextMarkPosition - markPosition - 5); + } } // Sometimes QFontMetrics::elidedText turns the QString into just an // elipsis character, so always show at least the hotcue number if From 2e1e11aabe7443b112583fa9af82a7c78111af74 Mon Sep 17 00:00:00 2001 From: Be Date: Sun, 1 Sep 2019 14:01:48 -0500 Subject: [PATCH 70/95] WOverview: show MarkRange and cue labels at consistent height --- src/widget/woverview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 9fa3d39c693..78133d9369a 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -749,7 +749,7 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga } if (valign == Qt::AlignTop) { - textPoint.setY(textRect.height() + 0.5f); + textPoint.setY(fontMetrics.height()); } else if (valign == Qt::AlignVCenter) { textPoint.setY((textRect.height() + height()) / 2); } else { // AlignBottom From ef726090a31235615ab8d0351e7dc1722bf4ff41 Mon Sep 17 00:00:00 2001 From: Be Date: Mon, 2 Sep 2019 07:19:20 -0500 Subject: [PATCH 71/95] WOverview: don't draw MarkRange labels when they should be hidden --- src/widget/woverview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 78133d9369a..fb9311a41e9 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -950,7 +950,7 @@ void WOverview::drawMarkLabels(QPainter* pPainter, const float offset, const flo // draw duration of WaveformMarkRanges for (auto&& markRange : m_markRanges) { - if (markRange.showDuration()) { + if (markRange.showDuration() && markRange.active() && markRange.visible()) { // Active mark ranges by definition have starts/ends that are not // disabled. const qreal startValue = markRange.start(); From cbb7bffa716ab68ffe3f344a92dcafb71dd6e343 Mon Sep 17 00:00:00 2001 From: Be Date: Mon, 2 Sep 2019 07:22:45 -0500 Subject: [PATCH 72/95] Revert "WaveformMarkRender: remove unnecessary check for null text" This reverts commit 641250db22c12c0055648c0d8c003733b37fe9ef. This commit introduced a regression where hotcues without labels or colors were not drawn on the scrolling waveform. --- src/waveform/renderers/waveformrendermark.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/waveform/renderers/waveformrendermark.cpp b/src/waveform/renderers/waveformrendermark.cpp index df5cb87b64b..5f239b4229f 100644 --- a/src/waveform/renderers/waveformrendermark.cpp +++ b/src/waveform/renderers/waveformrendermark.cpp @@ -129,8 +129,8 @@ void WaveformRenderMark::slotCuesUpdated() { QString newLabel = pCue->getLabel(); QColor newColor = m_predefinedColorsRepresentation.representationFor(pCue->getColor()); - if (newLabel != pMark->m_text || newColor != pMark->fillColor() - || !pMark->fillColor().isValid()) { + if (pMark->m_text.isNull() || newLabel != pMark->m_text || + !pMark->fillColor().isValid() || newColor != pMark->fillColor()) { pMark->m_text = newLabel; pMark->setBaseColor(newColor); generateMarkImage(pMark); From 367eb1d08b38d90d9bac090cd0345ac5932972c3 Mon Sep 17 00:00:00 2001 From: Be Date: Mon, 2 Sep 2019 07:36:41 -0500 Subject: [PATCH 73/95] WOverview: don't overlap right-aligned cue labels and cue lines --- src/widget/woverview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index fb9311a41e9..7a87d246089 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -745,7 +745,7 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga } else if (halign == Qt::AlignHCenter) { textPoint.setX(markPosition - textRect.width() / 2); } else { // AlignRight - textPoint.setX(markPosition + 0.5f); + textPoint.setX(markPosition + 1.5); } if (valign == Qt::AlignTop) { From d3b6b4ecf4b717b52c3c19a196d7a82cae23b0cc Mon Sep 17 00:00:00 2001 From: Be Date: Mon, 2 Sep 2019 08:25:51 -0500 Subject: [PATCH 74/95] WOverview: avoid overlapping labels with cue lines --- src/widget/woverview.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 7a87d246089..ba5c638decb 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -741,7 +741,7 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga QPointF textPoint; if (m_orientation == Qt::Horizontal) { if (halign == Qt::AlignLeft) { - textPoint.setX(markPosition - textRect.width()); + textPoint.setX(markPosition - textRect.width() - 5.5); } else if (halign == Qt::AlignHCenter) { textPoint.setX(markPosition - textRect.width() / 2); } else { // AlignRight @@ -794,7 +794,7 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga // vice versa. if (pMark == m_pHoveredMark) { Qt::Alignment valign = pMark->m_align & Qt::AlignVertical_Mask; - QPointF positionTextPoint(markPosition, 0); + QPointF positionTextPoint(markPosition + 1.5, 0); if (valign == Qt::AlignTop) { positionTextPoint.setY(float(height()) - 0.5f); } else { @@ -819,7 +819,7 @@ void WOverview::drawMarks(QPainter* pPainter, const float offset, const float ga m_cuePositionLabel.prerender(positionTextPoint, QPixmap(), cuePositionText, markerFont, m_labelTextColor, m_labelBackgroundColor, width(), getDevicePixelRatioF(this)); - QPointF timeDistancePoint(markPosition, (fontMetrics.height() + height()) / 2); + QPointF timeDistancePoint(positionTextPoint.x(), (fontMetrics.height() + height()) / 2); m_cueTimeDistanceLabel.prerender(timeDistancePoint, QPixmap(), cueTimeDistanceText, markerFont, m_labelTextColor, m_labelBackgroundColor, width(), getDevicePixelRatioF(this)); markHovered = true; @@ -966,14 +966,13 @@ void WOverview::drawMarkLabels(QPainter* pPainter, const float offset, const flo samplePositionToSeconds(endValue - startValue)); QRectF durationRect = fontMetrics.boundingRect(duration); - qreal padding = 3.0; qreal x; WaveformMarkRange::DurationTextLocation textLocation = markRange.durationTextLocation(); if (textLocation == WaveformMarkRange::DurationTextLocation::Before) { - x = startPosition - durationRect.width() - padding; + x = startPosition - durationRect.width() - 5.5; } else { - x = endPosition + padding; + x = endPosition + 1.5; } QPointF durationBottomLeft(x, fontMetrics.height()); From ba03e61ba2cc1e5c1c1e7075dc37794c10d090da Mon Sep 17 00:00:00 2001 From: Be Date: Mon, 2 Sep 2019 10:15:44 -0500 Subject: [PATCH 75/95] WOverview: keep hotcue label showing when right click menu is shown --- src/widget/woverview.cpp | 15 ++++++++++++++- src/widget/woverview.h | 3 +++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index ba5c638decb..4f2edc6dcb7 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -58,6 +58,7 @@ WOverview::WOverview( m_iPosSeconds(0), m_iPos(0), m_pHoveredMark(nullptr), + m_bHotcueMenuShowing(false), m_bTimeRulerActive(false), m_orientation(Qt::Horizontal), m_a(1.0), @@ -84,6 +85,9 @@ WOverview::WOverview( connect(pPlayerManager, &PlayerManager::trackAnalyzerProgress, this, &WOverview::onTrackAnalyzerProgress); + + connect(m_pCueMenu.get(), &QMenu::aboutToHide, + this, &WOverview::slotCueMenuAboutToHide); } void WOverview::setup(const QDomNode& node, const SkinContext& context) { @@ -464,14 +468,23 @@ void WOverview::mousePressEvent(QMouseEvent* e) { m_pCueMenu->setCue(pHoveredCue); m_pCueMenu->setTrack(m_pCurrentTrack); m_pCueMenu->popup(e->globalPos()); + m_bHotcueMenuShowing = true; } } } } +void WOverview::slotCueMenuAboutToHide() { + m_bHotcueMenuShowing = false; + m_pHoveredMark.clear(); + update(); +} + void WOverview::leaveEvent(QEvent* e) { Q_UNUSED(e); - m_pHoveredMark.clear(); + if (!m_bHotcueMenuShowing) { + m_pHoveredMark.clear(); + } m_bTimeRulerActive = false; update(); } diff --git a/src/widget/woverview.h b/src/widget/woverview.h index 4fc6ddbdf91..e8eeaf1d64a 100644 --- a/src/widget/woverview.h +++ b/src/widget/woverview.h @@ -101,6 +101,8 @@ class WOverview : public WWidget, public TrackDropTarget { void receiveCuesUpdated(); void slotWaveformSummaryUpdated(); + void slotCueMenuAboutToHide(); + private: // Append the waveform overview pixmap according to available data @@ -149,6 +151,7 @@ class WOverview : public WWidget, public TrackDropTarget { int m_iPos; WaveformMarkPointer m_pHoveredMark; + bool m_bHotcueMenuShowing; bool m_bTimeRulerActive; QPointF m_timeRulerPos; WaveformMarkLabel m_timeRulerPositionLabel; From 10e211439b8d088858ee559cab9e8054f7b02ad2 Mon Sep 17 00:00:00 2001 From: Be Date: Mon, 2 Sep 2019 11:51:30 -0500 Subject: [PATCH 76/95] WaveformMark: sort marks without hotcues before those with hotcues This ensures that when a hotcue overlaps a WaveformMark without a hotcue (for example, placing a hotcue at the loop in point), the hotcue mark is drawn over the non-hotcue mark. --- src/waveform/renderers/waveformmark.h | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/waveform/renderers/waveformmark.h b/src/waveform/renderers/waveformmark.h index e1c4dfd0c4b..ab11862df18 100644 --- a/src/waveform/renderers/waveformmark.h +++ b/src/waveform/renderers/waveformmark.h @@ -92,7 +92,22 @@ class WaveformMark { typedef QSharedPointer WaveformMarkPointer; inline bool operator<(const WaveformMarkPointer& lhs, const WaveformMarkPointer& rhs) { - return lhs->getSamplePosition() < rhs->getSamplePosition(); + double leftPosition = lhs->getSamplePosition(); + int leftHotcue = lhs->getHotCue(); + double rightPosition = rhs->getSamplePosition(); + int rightHotcue = rhs->getHotCue(); + if (leftPosition == rightPosition) { + // Sort WaveformMarks without hotcues before those with hotcues; + // if both have hotcues, sort numerically by hotcue number. + if (leftHotcue == WaveformMark::kNoHotCue && rightHotcue != WaveformMark::kNoHotCue) { + return true; + } else if (leftHotcue != WaveformMark::kNoHotCue && rightHotcue == WaveformMark::kNoHotCue) { + return false; + } else { + return leftHotcue < rightHotcue; + } + } + return leftPosition < rightPosition; } #endif // WAVEFORMMARK_H From 6f85553807378dc1ef15be48e0121b655a8fb38f Mon Sep 17 00:00:00 2001 From: Be Date: Thu, 12 Sep 2019 21:26:18 -0500 Subject: [PATCH 77/95] add line break to overview waveform tooltip --- src/skin/tooltips.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/skin/tooltips.cpp b/src/skin/tooltips.cpp index 25df8da92b3..c69c9894daa 100644 --- a/src/skin/tooltips.cpp +++ b/src/skin/tooltips.cpp @@ -36,7 +36,7 @@ void Tooltips::addStandardTooltips() { add("waveform_overview") << tr("Waveform Overview") - << tr("Shows information about the track currently loaded in this deck.") + << tr("Shows information about the track currently loaded in this deck.") << "\n" << tr("Jump around in the track by clicking anywhere on the waveform.") << tr("Right click hotcues to edit their labels and colors.") << tr("Right click anywhere else to show the time at that point.") From 48f30801041ee800c50e87c22b364bda01a5f785 Mon Sep 17 00:00:00 2001 From: Be Date: Thu, 12 Sep 2019 21:47:22 -0500 Subject: [PATCH 78/95] edit overview waveform tooltip --- src/skin/tooltips.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/skin/tooltips.cpp b/src/skin/tooltips.cpp index c69c9894daa..538ae438384 100644 --- a/src/skin/tooltips.cpp +++ b/src/skin/tooltips.cpp @@ -37,7 +37,7 @@ void Tooltips::addStandardTooltips() { add("waveform_overview") << tr("Waveform Overview") << tr("Shows information about the track currently loaded in this deck.") << "\n" - << tr("Jump around in the track by clicking anywhere on the waveform.") + << tr("Left click to jump around in the track.") << tr("Right click hotcues to edit their labels and colors.") << tr("Right click anywhere else to show the time at that point.") << dropTracksHere; From 5c5705cee845b735ba6c34ba970310ee9a2bdc0b Mon Sep 17 00:00:00 2001 From: Be Date: Thu, 12 Sep 2019 21:49:47 -0500 Subject: [PATCH 79/95] CueMenu: "Cue label" -> "New cue label" --- src/widget/cuemenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widget/cuemenu.cpp b/src/widget/cuemenu.cpp index 331b180145f..b9d62a30911 100644 --- a/src/widget/cuemenu.cpp +++ b/src/widget/cuemenu.cpp @@ -30,7 +30,7 @@ void CueMenu::slotEditLabel() { } bool okay = false; QString newLabel = QInputDialog::getText(this, tr("Edit cue label"), - tr("Cue label"), QLineEdit::Normal, + tr("New cue label"), QLineEdit::Normal, m_pCue->getLabel(), &okay); if (okay) { m_pCue->setLabel(newLabel); From 6c3ce9afde21d548ee1d5b6106558010af28ce0a Mon Sep 17 00:00:00 2001 From: Be Date: Sat, 14 Sep 2019 11:09:06 -0500 Subject: [PATCH 80/95] WOverview: show CueMenu for hotcue when it is drawn over a non-hotcue --- src/widget/woverview.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 4f2edc6dcb7..d08a9b00b4f 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -396,7 +396,14 @@ void WOverview::mouseMoveEvent(QMouseEvent* e) { // Without some padding, the user would only have a single pixel width that // would count as hovering over the WaveformMark. float lineHoverPadding = 5.0; - for (const auto& pMark : m_marksToRender) { + // Non-hotcue marks (intro/outro cues, main cue, loop in/out) are sorted + // before hotcues in m_marksToRender so if there is a hotcue in the same + // location, the hotcue gets rendered on top. When right clicking, the + // the hotcue rendered on top must be assigned to m_pHoveredMark to show + // the CueMenu. To accomplish this, m_marksToRender is iterated in reverse + // and the loop breaks as soon as m_pHoveredMark is set. + for (auto it = m_marksToRender.crbegin(); it != m_marksToRender.crend(); ++it) { + auto pMark = *it; int hoveredPosition; if (m_orientation == Qt::Horizontal) { hoveredPosition = e->x(); From a26999ea2cee18addb3440db4eace7e718ed86bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Wed, 18 Sep 2019 23:22:13 +0200 Subject: [PATCH 81/95] Fix building on Xenial --- src/widget/woverview.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index d08a9b00b4f..165e2750a57 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -402,8 +402,8 @@ void WOverview::mouseMoveEvent(QMouseEvent* e) { // the hotcue rendered on top must be assigned to m_pHoveredMark to show // the CueMenu. To accomplish this, m_marksToRender is iterated in reverse // and the loop breaks as soon as m_pHoveredMark is set. - for (auto it = m_marksToRender.crbegin(); it != m_marksToRender.crend(); ++it) { - auto pMark = *it; + for (int i = m_marksToRender.size() - 1; i >= 0 ; --i) { + WaveformMarkPointer pMark = m_marksToRender.at(i); int hoveredPosition; if (m_orientation == Qt::Horizontal) { hoveredPosition = e->x(); From 7c320fa052ba60b830b47c366a2b37411ce18992 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Wed, 18 Sep 2019 23:23:32 +0200 Subject: [PATCH 82/95] Remove border of cue labels and increase font size --- src/waveform/waveformmarklabel.cpp | 4 ++-- src/widget/woverview.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/waveform/waveformmarklabel.cpp b/src/waveform/waveformmarklabel.cpp index 2c4cf0b8b00..47928dfdc84 100644 --- a/src/waveform/waveformmarklabel.cpp +++ b/src/waveform/waveformmarklabel.cpp @@ -47,9 +47,9 @@ void WaveformMarkLabel::prerender(QPointF bottomLeft, QPixmap icon, QString text QPainter painter(&m_pixmap); - painter.setPen(backgroundColor); + painter.setPen(QColor(Qt::transparent)); painter.setBrush(QBrush(backgroundColor)); - painter.drawRoundedRect(0, 0, pixmapRect.width(), pixmapRect.height(), 5.0, 5.0); + painter.drawRoundedRect(0, 0, pixmapRect.width(), pixmapRect.height(), 2.0, 2.0); if (!icon.isNull()) { QPointF iconTopLeft = pixmapRect.topLeft(); diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 165e2750a57..36c077cdbda 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -99,7 +99,7 @@ void WOverview::setup(const QDomNode& node, const SkinContext& context) { m_labelBackgroundColor = context.selectColor(node, "LabelBackgroundColor"); if (!m_labelBackgroundColor.isValid()) { m_labelBackgroundColor = m_qColorBackground; - m_labelBackgroundColor.setAlpha(255 / 2); // 255 == fully transparent + m_labelBackgroundColor.setAlpha(255 / 2); // 0 == fully transparent } m_labelTextColor = context.selectColor(node, "LabelTextColor"); @@ -110,7 +110,7 @@ void WOverview::setup(const QDomNode& node, const SkinContext& context) { bool okay = false; m_iLabelFontSize = context.selectInt(node, "LabelFontSize", &okay); if (!okay) { - m_iLabelFontSize = 10; + m_iLabelFontSize = 11; } // Clear the background pixmap, if it exists. From 7c304d1277c821b38383d1da5696f748e0a09f37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Wed, 18 Sep 2019 23:24:15 +0200 Subject: [PATCH 83/95] Shade: set a overview bg color to alter the cue label background with the color schemes. --- res/skins/Shade/deck_overview.xml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/res/skins/Shade/deck_overview.xml b/res/skins/Shade/deck_overview.xml index a625535e1a1..cd0f3a0a586 100644 --- a/res/skins/Shade/deck_overview.xml +++ b/res/skins/Shade/deck_overview.xml @@ -5,7 +5,7 @@ 220e,30me - + #008d98a3 @@ -14,9 +14,6 @@ #00FF00 #EA0000 #60000000 - #80000000 - #ffffff - 9 bottom|right #FD0564 From c32960da77b06c8b6ed71d3a7d5b2f0711ae3f1f Mon Sep 17 00:00:00 2001 From: ronso0 Date: Sat, 21 Sep 2019 14:11:59 +0200 Subject: [PATCH 84/95] Shade: remove useless channel_PeakIndicator - skin:/style/volume_clipping_microphone_over.png skin:/style/volume_clipping_microphone.png 99,6 @@ -28,7 +26,6 @@ --> channel_VuMeter - skin:/style/volume_display_microphone_over.png skin:/style/volume_display_microphone.png 99,14 @@ -43,7 +40,6 @@ pregain - 64 knobs/knob_rotary_s%1.png 66,22 @@ -54,7 +50,6 @@ orientation - 3 0 @@ -81,7 +76,6 @@ - 2 0 diff --git a/res/skins/Shade/deck.xml b/res/skins/Shade/deck.xml index 440c17f973e..84957defe72 100644 --- a/res/skins/Shade/deck.xml +++ b/res/skins/Shade/deck.xml @@ -2,7 +2,6 @@ 0e,0min horizontal - DeckLeftBorder @@ -13,31 +12,26 @@ DeckLeft 0e,100min vertical - DeckLeft 0e,165f horizontal - DeckLeft 0e,165f vertical - DeckTopLeftMidPart 0e,45f horizontal - DeckTopLeftPart 40f,45f style/style_bg_deck_top_left.png - 2,7 @@ -53,7 +47,6 @@ i,45f vertical style/style_bg_deck_top_mid.png - track_title @@ -120,21 +113,18 @@ DeckUpperLeftMidPart 0e,81f horizontal - DeckUpperMidPart 0e,81f style/style_bg_deck_pane.png horizontal - DeckWaveformVinylControlSpinny 0e,81f horizontal - show_effects - 2 0 @@ -30,7 +28,6 @@ EffectUnit_deck_enabled - 2 0 @@ -51,7 +48,6 @@ EffectUnit_headphones_enabled - 2 0 @@ -72,7 +68,6 @@ EffectUnit__enabled - 2 0 @@ -95,12 +90,10 @@ 17f,5f 53,14 skin:/btn/btn_mix.png - EffectUnit_mix - 63 knobs/knob_rotary_s%1.png 49,21 @@ -111,7 +104,6 @@ EffectUnit_mix_mode - 2 0 @@ -134,12 +126,10 @@ 29f,5f 76,14 skin:/btn/btn_super.png - EffectUnit_super1 - 63 knobs/knob_rotary_s%1.png 77,21 diff --git a/res/skins/Shade/deck_overview.xml b/res/skins/Shade/deck_overview.xml index a625535e1a1..104f66e3218 100644 --- a/res/skins/Shade/deck_overview.xml +++ b/res/skins/Shade/deck_overview.xml @@ -1,7 +1,6 @@