From 0277e3c4f90b9d6637f7a8ae59eab2c07a9fb879 Mon Sep 17 00:00:00 2001 From: m0dB <79429057+m0dB@users.noreply.github.com> Date: Fri, 17 Nov 2023 01:40:02 +0100 Subject: [PATCH 1/6] draw overlapping marks at increasing level, both in legacy and glsl waveform renderers. reduced code duplications, fix setting the initial size of the waveform renderers --- CMakeLists.txt | 1 + .../allshader/waveformrendermark.cpp | 116 ++++-------------- .../renderers/allshader/waveformrendermark.h | 36 +++--- src/waveform/renderers/waveformmark.cpp | 35 ++++-- src/waveform/renderers/waveformmark.h | 74 +++++++---- src/waveform/renderers/waveformmarkset.cpp | 28 ++++- src/waveform/renderers/waveformmarkset.h | 2 + src/waveform/renderers/waveformrendermark.cpp | 83 +------------ src/waveform/renderers/waveformrendermark.h | 27 +--- .../renderers/waveformrendermarkbase.cpp | 95 ++++++++++++++ .../renderers/waveformrendermarkbase.h | 51 ++++++++ .../renderers/waveformwidgetrenderer.cpp | 2 +- .../renderers/waveformwidgetrenderer.h | 3 +- src/waveform/widgets/glslwaveformwidget.cpp | 4 +- src/waveform/widgets/glslwaveformwidget.h | 2 +- .../widgets/waveformwidgetabstract.cpp | 2 +- src/widget/wwaveformviewer.cpp | 15 ++- src/widget/wwaveformviewer.h | 1 + 18 files changed, 317 insertions(+), 260 deletions(-) create mode 100644 src/waveform/renderers/waveformrendermarkbase.cpp create mode 100644 src/waveform/renderers/waveformrendermarkbase.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 42cd3f9f02a..1acf469d1cd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1405,6 +1405,7 @@ else() src/waveform/renderers/waveformrendererrgb.cpp src/waveform/renderers/waveformrenderersignalbase.cpp src/waveform/renderers/waveformrendermark.cpp + src/waveform/renderers/waveformrendermarkbase.cpp src/waveform/renderers/waveformrendermarkrange.cpp src/waveform/renderers/waveformsignalcolors.cpp src/waveform/renderers/waveformwidgetrenderer.cpp diff --git a/src/waveform/renderers/allshader/waveformrendermark.cpp b/src/waveform/renderers/allshader/waveformrendermark.cpp index 34c35f8b1bd..de17cdf2c80 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.cpp +++ b/src/waveform/renderers/allshader/waveformrendermark.cpp @@ -1,20 +1,13 @@ #include "waveform/renderers/allshader/waveformrendermark.h" -#include #include #include -#include "track/track.h" -#include "util/color/color.h" -#include "util/colorcomponents.h" #include "util/texture.h" #include "waveform/renderers/allshader/matrixforwidgetgeometry.h" -#include "waveform/renderers/allshader/moc_waveformrendermark.cpp" #include "waveform/renderers/allshader/rgbadata.h" #include "waveform/renderers/allshader/vertexdata.h" -#include "waveform/renderers/waveformsignalcolors.h" #include "waveform/renderers/waveformwidgetrenderer.h" -#include "widget/wimagestore.h" // On the use of QPainter: // @@ -37,25 +30,30 @@ class TextureGraphics : public WaveformMark::Graphics { } }; -allshader::WaveformRenderMark::WaveformRenderMark(WaveformWidgetRenderer* waveformWidget) - : WaveformRenderer(waveformWidget), - m_bCuesUpdates(false) { -} +// Both allshader::WaveformRenderMark and the non-GL ::WaveformRenderMark derive +// from WaveformRenderMarkBase. The base-class takes care of updating the marks +// when needed and flagging them when their image needs to be updated (resizing, +// cue changes, position changes) +// +// While in the case of ::WaveformRenderMark those images can be updated immediately, +// in the case of allshader::WaveformRenderMark we need to do that when we have an +// openGL context, as we create new textures. +// +// The boolean argument for the WaveformRenderMarkBase constructor indicates +// that updateMarkImages should not be called immediately. -void allshader::WaveformRenderMark::setup(const QDomNode& node, const SkinContext& context) { - WaveformSignalColors signalColors = *m_waveformRenderer->getWaveformSignalColors(); - m_marks.setup(m_waveformRenderer->getGroup(), node, context, signalColors); +allshader::WaveformRenderMark::WaveformRenderMark(WaveformWidgetRenderer* waveformWidget) + : ::WaveformRenderMarkBase(waveformWidget, false) { } void allshader::WaveformRenderMark::initializeGL() { - WaveformRenderer::initializeGL(); + allshader::WaveformRendererAbstract::initializeGL(); m_rgbaShader.init(); m_textureShader.init(); - for (const auto& pMark : std::as_const(m_marks)) { - generateMarkImage(pMark); - } - generatePlayPosMarkTexture(); + // Will create textures so requires OpenGL context + updateMarkImages(); + updatePlayPosMarkTexture(); } void allshader::WaveformRenderMark::drawTexture(float x, float y, QOpenGLTexture* texture) { @@ -157,16 +155,13 @@ void allshader::WaveformRenderMark::paintGL() { const float devicePixelRatio = m_waveformRenderer->getDevicePixelRatio(); QList marksOnScreen; - checkCuesUpdated(); - glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - for (const auto& pMark : std::as_const(m_marks)) { - if (!pMark->m_pGraphics || pMark->m_pGraphics->m_obsolete) { - generateMarkImage(pMark); - } + // Will create textures so requires OpenGL context + updateMarkImages(); + for (const auto& pMark : std::as_const(m_marks)) { QOpenGLTexture* pTexture = static_cast(pMark->m_pGraphics.get()) ->texture(); @@ -238,7 +233,7 @@ void allshader::WaveformRenderMark::paintGL() { // Generate the texture used to draw the play position marker. // Note that in the legacy waveform widgets this is drawn directly // in the WaveformWidgetRenderer itself. Doing it here is cleaner. -void allshader::WaveformRenderMark::generatePlayPosMarkTexture() { +void allshader::WaveformRenderMark::updatePlayPosMarkTexture() { float imgwidth; float imgheight; @@ -317,71 +312,12 @@ void allshader::WaveformRenderMark::drawTriangle(QPainter* painter, } void allshader::WaveformRenderMark::resizeGL(int, int) { - for (const auto& pMark : std::as_const(m_marks)) { - generateMarkImage(pMark); - } - generatePlayPosMarkTexture(); -} - -void allshader::WaveformRenderMark::onSetTrack() { - slotCuesUpdated(); - - TrackPointer trackInfo = m_waveformRenderer->getTrackInfo(); - if (!trackInfo) { - return; - } - connect(trackInfo.get(), - &Track::cuesUpdated, - this, - &allshader::WaveformRenderMark::slotCuesUpdated); -} - -void allshader::WaveformRenderMark::slotCuesUpdated() { - m_bCuesUpdates = true; -} - -void allshader::WaveformRenderMark::checkCuesUpdated() { - if (!m_bCuesUpdates) { - return; - } - m_bCuesUpdates = false; - - TrackPointer trackInfo = m_waveformRenderer->getTrackInfo(); - if (!trackInfo) { - return; - } - - QList loadedCues = trackInfo->getCuePoints(); - for (const CuePointer& pCue : loadedCues) { - const int hotCue = pCue->getHotCue(); - if (hotCue == Cue::kNoHotCue) { - continue; - } - - // Here we assume no two cues can have the same hotcue assigned, - // because WaveformMarkSet stores one mark for each hotcue. - WaveformMarkPointer pMark = m_marks.getHotCueMark(hotCue); - if (pMark.isNull()) { - continue; - } - - QString newLabel = pCue->getLabel(); - QColor newColor = mixxx::RgbColor::toQColor(pCue->getColor()); - if (pMark->m_text.isNull() || newLabel != pMark->m_text || - !pMark->fillColor().isValid() || - newColor != pMark->fillColor()) { - pMark->m_text = newLabel; - const int dimBrightThreshold = m_waveformRenderer->getDimBrightThreshold(); - pMark->setBaseColor(newColor, dimBrightThreshold); - generateMarkImage(pMark); - } - } - - m_marks.update(); + // Will create textures so requires OpenGL context + updateMarkImages(); + updatePlayPosMarkTexture(); } -void allshader::WaveformRenderMark::generateMarkImage(WaveformMarkPointer pMark) { +void allshader::WaveformRenderMark::updateMarkImage(WaveformMarkPointer pMark) { pMark->m_pGraphics = std::make_unique( - createTexture(pMark->generateImage(m_waveformRenderer->getBreadth(), - m_waveformRenderer->getDevicePixelRatio()))); + createTexture(pMark->generateImage(m_waveformRenderer->getDevicePixelRatio()))); } diff --git a/src/waveform/renderers/allshader/waveformrendermark.h b/src/waveform/renderers/allshader/waveformrendermark.h index 101a96a0126..a06474cfc07 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.h +++ b/src/waveform/renderers/allshader/waveformrendermark.h @@ -1,13 +1,11 @@ #pragma once #include -#include #include "shaders/rgbashader.h" #include "shaders/textureshader.h" -#include "util/class.h" -#include "waveform/renderers/allshader/waveformrenderer.h" -#include "waveform/renderers/waveformmarkset.h" +#include "waveform/renderers/allshader/waveformrendererabstract.h" +#include "waveform/renderers/waveformrendermarkbase.h" class QDomNode; class SkinContext; @@ -17,32 +15,28 @@ namespace allshader { class WaveformRenderMark; } -class allshader::WaveformRenderMark final : public QObject, public allshader::WaveformRenderer { - Q_OBJECT +class allshader::WaveformRenderMark : public ::WaveformRenderMarkBase, + public allshader::WaveformRendererAbstract { public: explicit WaveformRenderMark(WaveformWidgetRenderer* waveformWidget); - void setup(const QDomNode& node, const SkinContext& context) override; + void draw(QPainter* painter, QPaintEvent* event) override { + Q_UNUSED(painter); + Q_UNUSED(event); + } + + allshader::WaveformRendererAbstract* allshaderWaveformRenderer() override { + return this; + } void initializeGL() override; void paintGL() override; void resizeGL(int w, int h) override; - // Called when a new track is loaded. - void onSetTrack() override; - - public slots: - // Called when the loaded track's cues are added, deleted or modified and - // when a new track is loaded. - // It updates the marks' names and regenerates their image if needed. - // This method is used for hotcues. - void slotCuesUpdated(); - private: - void checkCuesUpdated(); + void updateMarkImage(WaveformMarkPointer pMark) override; - void generateMarkImage(WaveformMarkPointer pMark); - void generatePlayPosMarkTexture(); + void updatePlayPosMarkTexture(); void drawTriangle(QPainter* painter, const QBrush& fillColor, @@ -50,11 +44,9 @@ class allshader::WaveformRenderMark final : public QObject, public allshader::Wa QPointF p2, QPointF p3); - WaveformMarkSet m_marks; mixxx::RGBAShader m_rgbaShader; mixxx::TextureShader m_textureShader; std::unique_ptr m_pPlayPosMarkTexture; - bool m_bCuesUpdates; void drawMark(const QRectF& rect, QColor color); void drawTexture(float x, float y, QOpenGLTexture* texture); diff --git a/src/waveform/renderers/waveformmark.cpp b/src/waveform/renderers/waveformmark.cpp index 6bc63af5cce..e2d5c71c085 100644 --- a/src/waveform/renderers/waveformmark.cpp +++ b/src/waveform/renderers/waveformmark.cpp @@ -56,9 +56,10 @@ Qt::Alignment decodeAlignmentFlags(const QString& alignString, Qt::Alignment def WaveformMark::WaveformMark(const QString& group, const QDomNode& node, const SkinContext& context, + int priority, const WaveformSignalColors& signalColors, int hotCue) - : m_linePosition{}, m_breadth{}, m_iHotCue{hotCue} { + : m_linePosition{}, m_breadth{}, m_level{}, m_iPriority(priority), m_iHotCue(hotCue) { QString positionControl; QString endPositionControl; if (hotCue != Cue::kNoHotCue) { @@ -121,23 +122,26 @@ WaveformMark::WaveformMark(const QString& group, WaveformMark::~WaveformMark() = default; void WaveformMark::setBaseColor(QColor baseColor, int dimBrightThreshold) { - if (m_pGraphics) { - m_pGraphics->m_obsolete = true; + if (m_fillColor == baseColor) { + return; } + m_fillColor = baseColor; m_borderColor = Color::chooseContrastColor(baseColor, dimBrightThreshold); m_labelColor = Color::chooseColorByBrightness(baseColor, QColor(255, 255, 255, 255), QColor(0, 0, 0, 255), dimBrightThreshold); -}; + + setNeedsImageUpdate(); +} bool WaveformMark::contains(QPoint point, Qt::Orientation orientation) const { // Without some padding, the user would only have a single pixel width that // would count as hovering over the WaveformMark. float lineHoverPadding = 5.0; if (orientation == Qt::Vertical) { - point = QPoint(point.y(), m_breadth - point.x()); + point = QPoint(point.y(), static_cast(m_breadth) - point.x()); } bool lineHovered = m_linePosition >= point.x() - lineHoverPadding && m_linePosition <= point.x() + lineHoverPadding; @@ -148,13 +152,17 @@ bool WaveformMark::contains(QPoint point, Qt::Orientation orientation) const { // Helper struct to calculate the geometry and fontsize needed by generateImage // to draw the label and text struct MarkerGeometry { - bool m_isSymbol; // it the label normal text or a single symbol (e.g. open circle arrow) + bool m_isSymbol; // is the label normal text or a single symbol (e.g. open circle arrow) QFont m_font; QRectF m_contentRect; QRectF m_labelRect; QSizeF m_imageSize; - MarkerGeometry(const QString& label, bool useIcon, Qt::Alignment align, float breadth) { + MarkerGeometry(const QString& label, + bool useIcon, + Qt::Alignment align, + float breadth, + int level) { // If the label is 1 character long, and this character isn't a letter or a number, // we can assume it's a special symbol m_isSymbol = !useIcon && label.length() == 1 && !label[0].isLetterOrNumber(); @@ -240,9 +248,10 @@ struct MarkerGeometry { if (alignV == Qt::AlignVCenter) { m_labelRect.moveTop((m_imageSize.height() - m_labelRect.height()) / 2.f); } else if (alignV == Qt::AlignBottom) { - m_labelRect.moveBottom(m_imageSize.height() - 0.5f); + m_labelRect.moveBottom(m_imageSize.height() - 0.5f - + level * (m_labelRect.height() + 2.f)); } else { - m_labelRect.moveTop(0.5f); + m_labelRect.moveTop(0.5f + level * (m_labelRect.height() + 2.f)); } } QSize getImageSize(float devicePixelRatio) const { @@ -251,12 +260,12 @@ struct MarkerGeometry { } }; -QImage WaveformMark::generateImage(float breadth, float devicePixelRatio) { +QImage WaveformMark::generateImage(float devicePixelRatio) { + assert(needsImageUpdate()); + // Load the pixmap from file. // If that succeeds loading the text and stroke is skipped. - m_breadth = static_cast(breadth); - if (!m_pixmapPath.isEmpty()) { QString path = m_pixmapPath; // Use devicePixelRatio to properly scale the image @@ -292,7 +301,7 @@ QImage WaveformMark::generateImage(float breadth, float devicePixelRatio) { const bool useIcon = m_iconPath != ""; // Determine drawing geometries - const MarkerGeometry markerGeometry(label, useIcon, m_align, breadth); + const MarkerGeometry markerGeometry(label, useIcon, m_align, m_breadth, m_level); m_label.setAreaRect(markerGeometry.m_labelRect); diff --git a/src/waveform/renderers/waveformmark.h b/src/waveform/renderers/waveformmark.h index e0548384629..5e7a5431b34 100644 --- a/src/waveform/renderers/waveformmark.h +++ b/src/waveform/renderers/waveformmark.h @@ -19,16 +19,16 @@ class WaveformMark { public: class Graphics { public: - Graphics() - : m_obsolete{false} { + Graphics() { } - bool m_obsolete; + bool m_obsolete{}; }; WaveformMark( const QString& group, const QDomNode& node, const SkinContext& context, + int priority, const WaveformSignalColors& signalColors, int hotCue = Cue::kNoHotCue); ~WaveformMark(); @@ -40,6 +40,9 @@ class WaveformMark { int getHotCue() const { return m_iHotCue; }; + int getPriority() const { + return m_iPriority; + }; // The m_pPositionCO related function bool isValid() const { @@ -85,22 +88,56 @@ class WaveformMark { m_pVisibleCO->connectValueChanged(receiver, slot, Qt::AutoConnection); } + void setText(const QString& text) { + if (m_text != text) { + m_text = text; + setNeedsImageUpdate(); + } + } + // Sets the appropriate mark colors based on the base color void setBaseColor(QColor baseColor, int dimBrightThreshold); + QColor fillColor() const { return m_fillColor; } + QColor borderColor() const { return m_borderColor; } + QColor labelColor() const { return m_labelColor; } + void setNeedsImageUpdate() { + if (m_pGraphics) { + m_pGraphics->m_obsolete = true; + } + } + + bool needsImageUpdate() const { + return !m_pGraphics || m_pGraphics->m_obsolete; + } + + void setBreadth(float breadth) { + if (m_breadth != breadth) { + m_breadth = breadth; + setNeedsImageUpdate(); + } + } + + void setLevel(int level) { + if (m_level != level) { + m_level = level; + setNeedsImageUpdate(); + } + } + // Check if a point (in image coordinates) lies on drawn image. bool contains(QPoint point, Qt::Orientation orientation) const; - QImage generateImage(float breath, float devicePixelRatio); + QImage generateImage(float devicePixelRatio); QColor m_textColor; QString m_text; @@ -109,7 +146,8 @@ class WaveformMark { QString m_iconPath; float m_linePosition; - int m_breadth; + float m_breadth; + int m_level; WaveformMarkLabel m_label; @@ -118,10 +156,9 @@ class WaveformMark { std::unique_ptr m_pEndPositionCO; std::unique_ptr m_pVisibleCO; - friend class allshader::WaveformRenderMark; - std::unique_ptr m_pGraphics; + int m_iPriority; int m_iHotCue; QColor m_fillColor; @@ -129,6 +166,8 @@ class WaveformMark { QColor m_labelColor; friend class WaveformRenderMark; + friend class WaveformRenderMarkBase; + friend class allshader::WaveformRenderMark; }; typedef QSharedPointer WaveformMarkPointer; @@ -140,27 +179,18 @@ typedef QSharedPointer WaveformMarkPointer; // temporarily incorrect sort order is acceptable. class WaveformMarkSortKey { public: - WaveformMarkSortKey(double samplePosition, int hotcue) + WaveformMarkSortKey(double samplePosition, int priority) : m_samplePosition(samplePosition), - m_hotcue(hotcue) { + m_priority(priority) { } bool operator<(const WaveformMarkSortKey& other) const { - if (m_samplePosition == other.m_samplePosition) { - // Sort WaveformMarks without hotcues before those with hotcues; - // if both have hotcues, sort numerically by hotcue number. - if (m_hotcue == Cue::kNoHotCue && other.m_hotcue != Cue::kNoHotCue) { - return true; - } else if (m_hotcue != Cue::kNoHotCue && other.m_hotcue == Cue::kNoHotCue) { - return false; - } else { - return m_hotcue < other.m_hotcue; - } - } - return m_samplePosition < other.m_samplePosition; + return m_samplePosition == other.m_samplePosition + ? m_priority < other.m_priority + : m_samplePosition < other.m_samplePosition; } private: double m_samplePosition; - int m_hotcue; + int m_priority; }; diff --git a/src/waveform/renderers/waveformmarkset.cpp b/src/waveform/renderers/waveformmarkset.cpp index a861df33ec7..ab7d6083ce4 100644 --- a/src/waveform/renderers/waveformmarkset.cpp +++ b/src/waveform/renderers/waveformmarkset.cpp @@ -23,13 +23,16 @@ void WaveformMarkSet::setup(const QString& group, const QDomNode& node, QDomNode child = node.firstChild(); QDomNode defaultChild; + int priority = 0; while (!child.isNull()) { if (child.nodeName() == "DefaultMark") { - m_pDefaultMark = WaveformMarkPointer(new WaveformMark(group, child, context, signalColors)); + m_pDefaultMark = WaveformMarkPointer(new WaveformMark( + group, child, context, --priority, signalColors)); hasDefaultMark = true; defaultChild = child; } else if (child.nodeName() == "Mark") { - WaveformMarkPointer pMark(new WaveformMark(group, child, context, signalColors)); + WaveformMarkPointer pMark(new WaveformMark( + group, child, context, --priority, signalColors)); if (pMark->isValid()) { // guarantee uniqueness even if there is a misdesigned skin QString item = pMark->getItem(); @@ -52,7 +55,8 @@ void WaveformMarkSet::setup(const QString& group, const QDomNode& node, for (int i = 0; i < NUM_HOT_CUES; ++i) { if (m_hotCueMarks.value(i).isNull()) { //qDebug() << "WaveformRenderMark::setup - Automatic mark" << hotCueControlItem; - WaveformMarkPointer pMark(new WaveformMark(group, defaultChild, context, signalColors, i)); + WaveformMarkPointer pMark(new WaveformMark( + group, defaultChild, context, i, signalColors, i)); m_marks.push_front(pMark); m_hotCueMarks.insert(pMark->getHotCue(), pMark); } @@ -68,6 +72,12 @@ WaveformMarkPointer WaveformMarkSet::getDefaultMark() const { return m_pDefaultMark; } +void WaveformMarkSet::setBreadth(float breadth) { + for (auto& pMark : m_marks) { + pMark->setBreadth(breadth); + } +} + void WaveformMarkSet::update() { std::map map; for (const auto& pMark : std::as_const(m_marks)) { @@ -77,7 +87,7 @@ void WaveformMarkSet::update() { // Create a stable key for sorting, because the WaveformMark's samplePosition is a // ControlObject which can change at any time by other threads. Such a change causes // another updateCues() call, rebuilding map. - auto key = WaveformMarkSortKey(samplePosition, pMark->getHotCue()); + auto key = WaveformMarkSortKey(samplePosition, pMark->getPriority()); map.emplace(key, pMark); } } @@ -89,6 +99,16 @@ void WaveformMarkSet::update() { map.end(), std::back_inserter(m_marksToRender), [](auto const& pair) { return pair.second; }); + + double prevSamplePosition = Cue::kNoPosition; + std::map levels; + for (auto& pMark : m_marksToRender) { + if (pMark->getSamplePosition() != prevSamplePosition) { + prevSamplePosition = pMark->getSamplePosition(); + levels.clear(); + } + pMark->setLevel(levels[pMark->m_align]++); + } } WaveformMarkPointer WaveformMarkSet::findHoveredMark( diff --git a/src/waveform/renderers/waveformmarkset.h b/src/waveform/renderers/waveformmarkset.h index faadf3a37fc..2ab37568f2f 100644 --- a/src/waveform/renderers/waveformmarkset.h +++ b/src/waveform/renderers/waveformmarkset.h @@ -65,6 +65,8 @@ class WaveformMarkSet { void update(); + void setBreadth(float breadth); + private: void clear() { m_marks.clear(); diff --git a/src/waveform/renderers/waveformrendermark.cpp b/src/waveform/renderers/waveformrendermark.cpp index e7aeb8474ab..1b8aeb469aa 100644 --- a/src/waveform/renderers/waveformrendermark.cpp +++ b/src/waveform/renderers/waveformrendermark.cpp @@ -1,10 +1,7 @@ #include "waveform/renderers/waveformrendermark.h" -#include "moc_waveformrendermark.cpp" -#include "track/track.h" #include "util/painterscope.h" #include "waveform/renderers/waveformwidgetrenderer.h" -#include "widget/wimagestore.h" class ImageGraphics : public WaveformMark::Graphics { QImage m_image; @@ -20,13 +17,8 @@ class ImageGraphics : public WaveformMark::Graphics { }; WaveformRenderMark::WaveformRenderMark( - WaveformWidgetRenderer* waveformWidgetRenderer) : - WaveformRendererAbstract(waveformWidgetRenderer) { -} - -void WaveformRenderMark::setup(const QDomNode& node, const SkinContext& context) { - WaveformSignalColors signalColors = *m_waveformRenderer->getWaveformSignalColors(); - m_marks.setup(m_waveformRenderer->getGroup(), node, context, signalColors); + WaveformWidgetRenderer* waveformWidgetRenderer) + : WaveformRenderMarkBase(waveformWidgetRenderer, true) { } void WaveformRenderMark::draw(QPainter* painter, QPaintEvent* /*event*/) { @@ -37,12 +29,6 @@ void WaveformRenderMark::draw(QPainter* painter, QPaintEvent* /*event*/) { painter->setWorldMatrixEnabled(false); for (const auto& pMark : std::as_const(m_marks)) { - // Generate image on first paint can't be done in setup since we need to - // wait for the render widget to be resized yet. - if (!pMark->m_pGraphics || pMark->m_pGraphics->m_obsolete) { - generateMarkImage(pMark); - } - const QImage& image = static_cast(pMark->m_pGraphics.get())->image(); const double samplePosition = pMark->getSamplePosition(); @@ -149,72 +135,13 @@ void WaveformRenderMark::draw(QPainter* painter, QPaintEvent* /*event*/) { m_waveformRenderer->setMarkPositions(marksOnScreen); } -void WaveformRenderMark::onResize() { - // Flag that the mark image has to be updated. New images will be created on next paint. - for (const auto& pMark : std::as_const(m_marks)) { - if (pMark->m_pGraphics) { - pMark->m_pGraphics->m_obsolete = true; - } - } -} - -void WaveformRenderMark::onSetTrack() { - slotCuesUpdated(); - - const TrackPointer pTrackInfo = m_waveformRenderer->getTrackInfo(); - if (!pTrackInfo) { - return; - } - connect(pTrackInfo.get(), - &Track::cuesUpdated, - this, - &WaveformRenderMark::slotCuesUpdated); -} - -void WaveformRenderMark::slotCuesUpdated() { - const TrackPointer pTrackInfo = m_waveformRenderer->getTrackInfo(); - if (!pTrackInfo) { - return; - } - - QList loadedCues = pTrackInfo->getCuePoints(); - for (const CuePointer& pCue : loadedCues) { - int hotCue = pCue->getHotCue(); - if (hotCue == Cue::kNoHotCue) { - continue; - } - - // Here we assume no two cues can have the same hotcue assigned, - // because WaveformMarkSet stores one mark for each hotcue. - WaveformMarkPointer pMark = m_marks.getHotCueMark(hotCue); - if (pMark.isNull()) { - continue; - } - - QString newLabel = pCue->getLabel(); - QColor newColor = mixxx::RgbColor::toQColor(pCue->getColor()); - if (pMark->m_text.isNull() || newLabel != pMark->m_text || - !pMark->fillColor().isValid() || - newColor != pMark->fillColor()) { - pMark->m_text = newLabel; - int dimBrightThreshold = m_waveformRenderer->getDimBrightThreshold(); - pMark->setBaseColor(newColor, dimBrightThreshold); - generateMarkImage(pMark); - } - } - - m_marks.update(); -} - -void WaveformRenderMark::generateMarkImage(WaveformMarkPointer pMark) { +void WaveformRenderMark::updateMarkImage(WaveformMarkPointer pMark) { if (m_waveformRenderer->getOrientation() == Qt::Horizontal) { pMark->m_pGraphics = std::make_unique( - pMark->generateImage(m_waveformRenderer->getBreadth(), - m_waveformRenderer->getDevicePixelRatio())); + pMark->generateImage(m_waveformRenderer->getDevicePixelRatio())); } else { pMark->m_pGraphics = std::make_unique( - pMark->generateImage(m_waveformRenderer->getBreadth(), - m_waveformRenderer->getDevicePixelRatio()) + pMark->generateImage(m_waveformRenderer->getDevicePixelRatio()) .transformed(QTransform().rotate(90))); } } diff --git a/src/waveform/renderers/waveformrendermark.h b/src/waveform/renderers/waveformrendermark.h index 1419194e89b..2bf284223f1 100644 --- a/src/waveform/renderers/waveformrendermark.h +++ b/src/waveform/renderers/waveformrendermark.h @@ -1,36 +1,15 @@ #pragma once -#include - -#include "skin/legacy/skincontext.h" -#include "util/class.h" -#include "waveform/renderers/waveformmarkset.h" -#include "waveform/renderers/waveformrendererabstract.h" - -class WaveformRenderMark : public QObject, public WaveformRendererAbstract { - Q_OBJECT +#include "waveform/renderers/waveformrendermarkbase.h" +class WaveformRenderMark : public WaveformRenderMarkBase { public: explicit WaveformRenderMark(WaveformWidgetRenderer* waveformWidgetRenderer); - void setup(const QDomNode& node, const SkinContext& context) override; void draw(QPainter* painter, QPaintEvent* event) override; - void onResize() override; - - // Called when a new track is loaded. - void onSetTrack() override; - - public slots: - // Called when the loaded track's cues are added, deleted or modified and - // when a new track is loaded. - // It updates the marks' names and regenerates their image if needed. - // This method is used for hotcues. - void slotCuesUpdated(); - private: - void generateMarkImage(WaveformMarkPointer pMark); + void updateMarkImage(WaveformMarkPointer pMark) override; - WaveformMarkSet m_marks; DISALLOW_COPY_AND_ASSIGN(WaveformRenderMark); }; diff --git a/src/waveform/renderers/waveformrendermarkbase.cpp b/src/waveform/renderers/waveformrendermarkbase.cpp new file mode 100644 index 00000000000..4b49840a36f --- /dev/null +++ b/src/waveform/renderers/waveformrendermarkbase.cpp @@ -0,0 +1,95 @@ +#include "waveform/renderers/waveformrendermarkbase.h" +#include "moc_waveformrendermarkbase.cpp" +#include "track/track.h" +#include "waveform/renderers/waveformwidgetrenderer.h" + +WaveformRenderMarkBase::WaveformRenderMarkBase( + WaveformWidgetRenderer* waveformWidgetRenderer, + bool updateImagesImmediately) + : WaveformRendererAbstract(waveformWidgetRenderer), + m_updateImagesImmediately(updateImagesImmediately) { +} + +void WaveformRenderMarkBase::setup(const QDomNode& node, const SkinContext& context) { + WaveformSignalColors signalColors = *m_waveformRenderer->getWaveformSignalColors(); + m_marks.setup(m_waveformRenderer->getGroup(), node, context, signalColors); + m_marks.connectSamplePositionChanged(this, &WaveformRenderMarkBase::onMarkChanged); + m_marks.connectSampleEndPositionChanged(this, &WaveformRenderMarkBase::onMarkChanged); + m_marks.connectVisibleChanged(this, &WaveformRenderMarkBase::onMarkChanged); +} + +void WaveformRenderMarkBase::onSetTrack() { + updateMarksFromCues(); + + const TrackPointer pTrackInfo = m_waveformRenderer->getTrackInfo(); + if (!pTrackInfo) { + return; + } + + connect(pTrackInfo.get(), + &Track::cuesUpdated, + this, + &WaveformRenderMarkBase::slotCuesUpdated); +} + +void WaveformRenderMarkBase::onResize() { + m_marks.setBreadth(m_waveformRenderer->getBreadth()); + if (m_updateImagesImmediately) { + updateMarkImages(); + } +} + +void WaveformRenderMarkBase::onMarkChanged(double v) { + Q_UNUSED(v); + + updateMarks(); +} + +void WaveformRenderMarkBase::slotCuesUpdated() { + updateMarksFromCues(); +} + +void WaveformRenderMarkBase::updateMarksFromCues() { + const TrackPointer pTrackInfo = m_waveformRenderer->getTrackInfo(); + if (!pTrackInfo) { + return; + } + + const int dimBrightThreshold = m_waveformRenderer->getDimBrightThreshold(); + QList loadedCues = pTrackInfo->getCuePoints(); + for (const CuePointer& pCue : loadedCues) { + const int hotCue = pCue->getHotCue(); + if (hotCue == Cue::kNoHotCue) { + continue; + } + + // Here we assume no two cues can have the same hotcue assigned, + // because WaveformMarkSet stores one mark for each hotcue. + WaveformMarkPointer pMark = m_marks.getHotCueMark(hotCue); + if (pMark.isNull()) { + continue; + } + + QString newLabel = pCue->getLabel(); + QColor newColor = mixxx::RgbColor::toQColor(pCue->getColor()); + pMark->setText(newLabel); + pMark->setBaseColor(newColor, dimBrightThreshold); + } + + updateMarks(); +} + +void WaveformRenderMarkBase::updateMarks() { + m_marks.update(); + if (m_updateImagesImmediately) { + updateMarkImages(); + } +} + +void WaveformRenderMarkBase::updateMarkImages() { + for (auto& pMark : m_marks) { + if (pMark->needsImageUpdate()) { + updateMarkImage(pMark); + } + } +} diff --git a/src/waveform/renderers/waveformrendermarkbase.h b/src/waveform/renderers/waveformrendermarkbase.h new file mode 100644 index 00000000000..b5e4d51dd25 --- /dev/null +++ b/src/waveform/renderers/waveformrendermarkbase.h @@ -0,0 +1,51 @@ +#pragma once + +#include + +#include "skin/legacy/skincontext.h" +#include "util/class.h" +#include "waveform/renderers/waveformmarkset.h" +#include "waveform/renderers/waveformrendererabstract.h" + +class WaveformRenderMarkBase : public QObject, public WaveformRendererAbstract { + Q_OBJECT + + public: + explicit WaveformRenderMarkBase( + WaveformWidgetRenderer* waveformWidgetRenderer, + bool updateImagesImmediately); + + void setup(const QDomNode& node, const SkinContext& context) override; + + // Called when a new track is loaded. + void onSetTrack() override; + + void onResize() override; + + public slots: + // Called when the loaded track's cues are added, deleted or modified and + // when a new track is loaded. + // It updates the marks' names and flags the need for an image update. + // This method is used for hotcues. + void slotCuesUpdated(); + + private slots: + // Called when a mark position or visibility changes + void onMarkChanged(double v); + + protected: + WaveformMarkSet m_marks; + + void updateMarkImages(); + + private: + const bool m_updateImagesImmediately; + + void updateMarksFromCues(); + void updateMarks(); + + private: + virtual void updateMarkImage(WaveformMarkPointer pMark) = 0; + + DISALLOW_COPY_AND_ASSIGN(WaveformRenderMarkBase); +}; diff --git a/src/waveform/renderers/waveformwidgetrenderer.cpp b/src/waveform/renderers/waveformwidgetrenderer.cpp index dc93dd34bfd..81685e2b7a5 100644 --- a/src/waveform/renderers/waveformwidgetrenderer.cpp +++ b/src/waveform/renderers/waveformwidgetrenderer.cpp @@ -350,7 +350,7 @@ void WaveformWidgetRenderer::setPassThroughEnabled(bool enabled) { } } -void WaveformWidgetRenderer::resize(int width, int height, float devicePixelRatio) { +void WaveformWidgetRenderer::resizeRenderer(int width, int height, float devicePixelRatio) { m_width = width; m_height = height; m_devicePixelRatio = devicePixelRatio; diff --git a/src/waveform/renderers/waveformwidgetrenderer.h b/src/waveform/renderers/waveformwidgetrenderer.h index 927bce8ad5c..0928ff9b130 100644 --- a/src/waveform/renderers/waveformwidgetrenderer.h +++ b/src/waveform/renderers/waveformwidgetrenderer.h @@ -115,7 +115,8 @@ class WaveformWidgetRenderer { return m_alphaBeatGrid; } - void resize(int width, int height, float devicePixelRatio); + virtual void resizeRenderer(int width, int height, float devicePixelRatio); + int getHeight() const { return m_height; } diff --git a/src/waveform/widgets/glslwaveformwidget.cpp b/src/waveform/widgets/glslwaveformwidget.cpp index 65fc318b726..1790c143ad2 100644 --- a/src/waveform/widgets/glslwaveformwidget.cpp +++ b/src/waveform/widgets/glslwaveformwidget.cpp @@ -84,10 +84,10 @@ mixxx::Duration GLSLWaveformWidget::render() { return t1; // return timer for painter setup } -void GLSLWaveformWidget::resize(int width, int height) { +void GLSLWaveformWidget::resizeRenderer(int width, int height, float devicePixelRatio) { // NOTE: (vrince) this is needed since we allocation buffer on resize // and the Gl Context should be properly set makeCurrentIfNeeded(); - WaveformWidgetAbstract::resize(width, height); + WaveformWidgetRenderer::resizeRenderer(width, height, devicePixelRatio); doneCurrent(); } diff --git a/src/waveform/widgets/glslwaveformwidget.h b/src/waveform/widgets/glslwaveformwidget.h index ccfbf3169ae..126c82e2221 100644 --- a/src/waveform/widgets/glslwaveformwidget.h +++ b/src/waveform/widgets/glslwaveformwidget.h @@ -17,7 +17,7 @@ class GLSLWaveformWidget : public GLWaveformWidgetAbstract { GlslType type); ~GLSLWaveformWidget() override = default; - void resize(int width, int height) override; + void resizeRenderer(int width, int height, float devicePixelRatio) override; protected: void castToQWidget() override; diff --git a/src/waveform/widgets/waveformwidgetabstract.cpp b/src/waveform/widgets/waveformwidgetabstract.cpp index f3dc9166cf8..d678d4635f4 100644 --- a/src/waveform/widgets/waveformwidgetabstract.cpp +++ b/src/waveform/widgets/waveformwidgetabstract.cpp @@ -43,5 +43,5 @@ void WaveformWidgetAbstract::resize(int width, int height) { m_widget->resize(width, height); devicePixelRatio = m_widget->devicePixelRatioF(); } - WaveformWidgetRenderer::resize(width, height, static_cast(devicePixelRatio)); + resizeRenderer(width, height, static_cast(devicePixelRatio)); } diff --git a/src/widget/wwaveformviewer.cpp b/src/widget/wwaveformviewer.cpp index 2c400ec5150..34ff9027bdd 100644 --- a/src/widget/wwaveformviewer.cpp +++ b/src/widget/wwaveformviewer.cpp @@ -52,17 +52,30 @@ WWaveformViewer::~WWaveformViewer() { void WWaveformViewer::setup(const QDomNode& node, const SkinContext& context) { if (m_waveformWidget) { m_waveformWidget->setup(node, context); + m_dimBrightThreshold = m_waveformWidget->getDimBrightThreshold(); } - m_dimBrightThreshold = m_waveformWidget->getDimBrightThreshold(); } void WWaveformViewer::resizeEvent(QResizeEvent* event) { Q_UNUSED(event); if (m_waveformWidget) { + // Note m_waveformWidget is a WaveformWidgetAbstract, + // so this calls the method of WaveformWidgetAbstract, + // note of the derived waveform widgets which are also + // a QWidget, though that will be called directly. m_waveformWidget->resize(width(), height()); } } +void WWaveformViewer::showEvent(QShowEvent* event) { + if (m_waveformWidget) { + // We leave it up to Qt to set the size of the derived + // waveform widget, but we still need to set the size + // of the renderer. + m_waveformWidget->resizeRenderer(width(), height(), devicePixelRatioF()); + } +} + void WWaveformViewer::mousePressEvent(QMouseEvent* event) { if (!m_waveformWidget) { return; diff --git a/src/widget/wwaveformviewer.h b/src/widget/wwaveformviewer.h index 505288ed974..fddf802bca5 100644 --- a/src/widget/wwaveformviewer.h +++ b/src/widget/wwaveformviewer.h @@ -46,6 +46,7 @@ class WWaveformViewer : public WWidget, public TrackDropTarget { void slotLoadingTrack(TrackPointer pNewTrack, TrackPointer pOldTrack); protected: + void showEvent(QShowEvent* event) override; void resizeEvent(QResizeEvent *event) override; void wheelEvent(QWheelEvent *event) override; From e3c585b1f1878d5e992dfe0d90e1886bf385b7e4 Mon Sep 17 00:00:00 2001 From: m0dB <79429057+m0dB@users.noreply.github.com> Date: Sat, 18 Nov 2023 12:24:01 +0100 Subject: [PATCH 2/6] fix gcc compilation --- src/widget/wwaveformviewer.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/widget/wwaveformviewer.cpp b/src/widget/wwaveformviewer.cpp index 34ff9027bdd..240fee3ba85 100644 --- a/src/widget/wwaveformviewer.cpp +++ b/src/widget/wwaveformviewer.cpp @@ -68,11 +68,13 @@ void WWaveformViewer::resizeEvent(QResizeEvent* event) { } void WWaveformViewer::showEvent(QShowEvent* event) { + Q_UNUSED(event); if (m_waveformWidget) { // We leave it up to Qt to set the size of the derived // waveform widget, but we still need to set the size // of the renderer. - m_waveformWidget->resizeRenderer(width(), height(), devicePixelRatioF()); + m_waveformWidget->resizeRenderer( + width(), height(), static_cast(devicePixelRatioF())); } } From d16dcc8d9abdd5c4f3f2e226cf67226e55878647 Mon Sep 17 00:00:00 2001 From: m0dB <79429057+m0dB@users.noreply.github.com> Date: Sat, 18 Nov 2023 12:32:32 +0100 Subject: [PATCH 3/6] formatting --- src/waveform/renderers/waveformrendermarkbase.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/waveform/renderers/waveformrendermarkbase.cpp b/src/waveform/renderers/waveformrendermarkbase.cpp index 4b49840a36f..0c8904bb3bf 100644 --- a/src/waveform/renderers/waveformrendermarkbase.cpp +++ b/src/waveform/renderers/waveformrendermarkbase.cpp @@ -1,4 +1,5 @@ #include "waveform/renderers/waveformrendermarkbase.h" + #include "moc_waveformrendermarkbase.cpp" #include "track/track.h" #include "waveform/renderers/waveformwidgetrenderer.h" From ed37eda7f001523f4a5398f0ae987d442b1a717c Mon Sep 17 00:00:00 2001 From: m0dB <79429057+m0dB@users.noreply.github.com> Date: Sun, 26 Nov 2023 11:32:54 +0100 Subject: [PATCH 4/6] missing include --- src/waveform/renderers/allshader/waveformrendermark.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/waveform/renderers/allshader/waveformrendermark.cpp b/src/waveform/renderers/allshader/waveformrendermark.cpp index de17cdf2c80..3c643e73178 100644 --- a/src/waveform/renderers/allshader/waveformrendermark.cpp +++ b/src/waveform/renderers/allshader/waveformrendermark.cpp @@ -3,6 +3,7 @@ #include #include +#include "util/colorcomponents.h" #include "util/texture.h" #include "waveform/renderers/allshader/matrixforwidgetgeometry.h" #include "waveform/renderers/allshader/rgbadata.h" From 887dea6a880963978ba2c2c0964873d4307733fa Mon Sep 17 00:00:00 2001 From: m0dB <79429057+m0dB@users.noreply.github.com> Date: Sun, 26 Nov 2023 11:33:28 +0100 Subject: [PATCH 5/6] slightly improved hovering detection --- src/waveform/renderers/waveformmark.cpp | 11 ++++++++--- src/waveform/renderers/waveformmark.h | 2 ++ src/waveform/renderers/waveformmarkset.cpp | 6 ++++++ .../renderers/waveformwidgetrenderer.cpp | 18 ++++++++++++++++++ 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/waveform/renderers/waveformmark.cpp b/src/waveform/renderers/waveformmark.cpp index e2d5c71c085..3a1e2651f68 100644 --- a/src/waveform/renderers/waveformmark.cpp +++ b/src/waveform/renderers/waveformmark.cpp @@ -136,17 +136,22 @@ void WaveformMark::setBaseColor(QColor baseColor, int dimBrightThreshold) { setNeedsImageUpdate(); } -bool WaveformMark::contains(QPoint point, Qt::Orientation orientation) const { +bool WaveformMark::lineHovered(QPoint point, Qt::Orientation orientation) const { // Without some padding, the user would only have a single pixel width that // would count as hovering over the WaveformMark. float lineHoverPadding = 5.0; if (orientation == Qt::Vertical) { point = QPoint(point.y(), static_cast(m_breadth) - point.x()); } - bool lineHovered = m_linePosition >= point.x() - lineHoverPadding && + return m_linePosition >= point.x() - lineHoverPadding && m_linePosition <= point.x() + lineHoverPadding; +} - return m_label.area().contains(point) || lineHovered; +bool WaveformMark::contains(QPoint point, Qt::Orientation orientation) const { + if (orientation == Qt::Vertical) { + point = QPoint(point.y(), static_cast(m_breadth) - point.x()); + } + return m_label.area().contains(point); } // Helper struct to calculate the geometry and fontsize needed by generateImage diff --git a/src/waveform/renderers/waveformmark.h b/src/waveform/renderers/waveformmark.h index 5e7a5431b34..abb738b3d79 100644 --- a/src/waveform/renderers/waveformmark.h +++ b/src/waveform/renderers/waveformmark.h @@ -134,6 +134,8 @@ class WaveformMark { } } + // Check if a point (in image coordinates) lies on the line + bool lineHovered(QPoint point, Qt::Orientation orientation) const; // Check if a point (in image coordinates) lies on drawn image. bool contains(QPoint point, Qt::Orientation orientation) const; diff --git a/src/waveform/renderers/waveformmarkset.cpp b/src/waveform/renderers/waveformmarkset.cpp index ab7d6083ce4..2d600744405 100644 --- a/src/waveform/renderers/waveformmarkset.cpp +++ b/src/waveform/renderers/waveformmarkset.cpp @@ -125,5 +125,11 @@ WaveformMarkPointer WaveformMarkSet::findHoveredMark( return pMark; } } + for (auto it = m_marksToRender.crbegin(); it != m_marksToRender.crend(); ++it) { + const WaveformMarkPointer& pMark = *it; + if (pMark->lineHovered(pos, orientation)) { + return pMark; + } + } return nullptr; } diff --git a/src/waveform/renderers/waveformwidgetrenderer.cpp b/src/waveform/renderers/waveformwidgetrenderer.cpp index 81685e2b7a5..6def0215d5b 100644 --- a/src/waveform/renderers/waveformwidgetrenderer.cpp +++ b/src/waveform/renderers/waveformwidgetrenderer.cpp @@ -437,6 +437,24 @@ WaveformMarkPointer WaveformWidgetRenderer::getCueMarkAtPoint(QPoint point) cons return pMark; } } + for (auto it = m_markPositions.crbegin(); it != m_markPositions.crend(); ++it) { + const WaveformMarkPointer& pMark = it->m_pMark; + VERIFY_OR_DEBUG_ASSERT(pMark) { + continue; + } + + int markImagePositionInWidgetSpace = it->m_offsetOnScreen; + QPoint pointInImageSpace; + if (getOrientation() == Qt::Horizontal) { + pointInImageSpace = QPoint(point.x() - markImagePositionInWidgetSpace, point.y()); + } else { + DEBUG_ASSERT(getOrientation() == Qt::Vertical); + pointInImageSpace = QPoint(point.x(), point.y() - markImagePositionInWidgetSpace); + } + if (pMark->lineHovered(pointInImageSpace, getOrientation())) { + return pMark; + } + } return nullptr; } From f7e7cd5c78404e3dd5a37b0b9024708785f32b89 Mon Sep 17 00:00:00 2001 From: m0dB Date: Fri, 8 Dec 2023 23:02:38 +0100 Subject: [PATCH 6/6] added comments and minor changed based on review --- src/waveform/renderers/waveformmark.cpp | 11 +++++++---- src/waveform/renderers/waveformmark.h | 8 ++++++-- src/waveform/renderers/waveformmarkset.cpp | 4 ++++ src/waveform/renderers/waveformrendermarkbase.cpp | 6 +++--- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/waveform/renderers/waveformmark.cpp b/src/waveform/renderers/waveformmark.cpp index 3a1e2651f68..4088895620f 100644 --- a/src/waveform/renderers/waveformmark.cpp +++ b/src/waveform/renderers/waveformmark.cpp @@ -10,6 +10,11 @@ #include "widget/wskincolor.h" namespace { + +// Without some padding, the user would only have a single pixel width that +// would count as hovering over the WaveformMark. +constexpr float lineHoverPadding = 5.0; + Qt::Alignment decodeAlignmentFlags(const QString& alignString, Qt::Alignment defaultFlags) { QStringList stringFlags = alignString.toLower() .split('|', @@ -137,10 +142,8 @@ void WaveformMark::setBaseColor(QColor baseColor, int dimBrightThreshold) { } bool WaveformMark::lineHovered(QPoint point, Qt::Orientation orientation) const { - // Without some padding, the user would only have a single pixel width that - // would count as hovering over the WaveformMark. - float lineHoverPadding = 5.0; if (orientation == Qt::Vertical) { + // Note that for vertical orientation, breadth is set to the width. point = QPoint(point.y(), static_cast(m_breadth) - point.x()); } return m_linePosition >= point.x() - lineHoverPadding && @@ -306,7 +309,7 @@ QImage WaveformMark::generateImage(float devicePixelRatio) { const bool useIcon = m_iconPath != ""; // Determine drawing geometries - const MarkerGeometry markerGeometry(label, useIcon, m_align, m_breadth, m_level); + const MarkerGeometry markerGeometry{label, useIcon, m_align, m_breadth, m_level}; m_label.setAreaRect(markerGeometry.m_labelRect); diff --git a/src/waveform/renderers/waveformmark.h b/src/waveform/renderers/waveformmark.h index abb738b3d79..8929ba74cb2 100644 --- a/src/waveform/renderers/waveformmark.h +++ b/src/waveform/renderers/waveformmark.h @@ -19,8 +19,8 @@ class WaveformMark { public: class Graphics { public: - Graphics() { - } + // To indicate that the image for the mark needs to be regenerated, + // when the text, color, breadth or level are changed. bool m_obsolete{}; }; @@ -149,6 +149,10 @@ class WaveformMark { float m_linePosition; float m_breadth; + + // When there are overlapping marks, level is increased for each overlapping mark, + // so that we can draw them at different positions: The marks at the top go lower + // when the level increased, the marks at the bottom higher. int m_level; WaveformMarkLabel m_label; diff --git a/src/waveform/renderers/waveformmarkset.cpp b/src/waveform/renderers/waveformmarkset.cpp index 2d600744405..8fe24730146 100644 --- a/src/waveform/renderers/waveformmarkset.cpp +++ b/src/waveform/renderers/waveformmarkset.cpp @@ -101,6 +101,10 @@ void WaveformMarkSet::update() { [](auto const& pair) { return pair.second; }); double prevSamplePosition = Cue::kNoPosition; + + // Avoid overlapping marks by increasing the level per alignment. + // We take this into account when drawing the marks aligned at: + // left top, right top, left bottom, right bottom. std::map levels; for (auto& pMark : m_marksToRender) { if (pMark->getSamplePosition() != prevSamplePosition) { diff --git a/src/waveform/renderers/waveformrendermarkbase.cpp b/src/waveform/renderers/waveformrendermarkbase.cpp index 0c8904bb3bf..2471dbf7044 100644 --- a/src/waveform/renderers/waveformrendermarkbase.cpp +++ b/src/waveform/renderers/waveformrendermarkbase.cpp @@ -5,9 +5,9 @@ #include "waveform/renderers/waveformwidgetrenderer.h" WaveformRenderMarkBase::WaveformRenderMarkBase( - WaveformWidgetRenderer* waveformWidgetRenderer, + WaveformWidgetRenderer* pWaveformWidgetRenderer, bool updateImagesImmediately) - : WaveformRendererAbstract(waveformWidgetRenderer), + : WaveformRendererAbstract(pWaveformWidgetRenderer), m_updateImagesImmediately(updateImagesImmediately) { } @@ -88,7 +88,7 @@ void WaveformRenderMarkBase::updateMarks() { } void WaveformRenderMarkBase::updateMarkImages() { - for (auto& pMark : m_marks) { + for (const auto& pMark : m_marks) { if (pMark->needsImageUpdate()) { updateMarkImage(pMark); }