Skip to content

Commit

Permalink
draw overlapping marks at increasing level
Browse files Browse the repository at this point in the history
  • Loading branch information
m0dB committed Nov 7, 2023
1 parent ad92390 commit 52a275d
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 29 deletions.
3 changes: 1 addition & 2 deletions src/waveform/renderers/allshader/waveformrendermark.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -380,10 +380,9 @@ void allshader::WaveformRenderMark::checkCuesUpdated() {
if (pMark->m_text.isNull() || newLabel != pMark->m_text ||
!pMark->fillColor().isValid() ||
newColor != pMark->fillColor()) {
pMark->m_text = newLabel;
pMark->setText(newLabel);
const int dimBrightThreshold = m_waveformRenderer->getDimBrightThreshold();
pMark->setBaseColor(newColor, dimBrightThreshold);
generateMarkImage(pMark);
}
}
}
Expand Down
53 changes: 44 additions & 9 deletions src/waveform/renderers/waveformmark.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -121,16 +122,45 @@ 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);
};

if (m_pGraphics) {
m_pGraphics->m_obsolete = true;
}
}

void WaveformMark::setText(const QString& text) {
if (m_text == text) {
return;
}

m_text = text;

if (m_pGraphics) {
m_pGraphics->m_obsolete = true;
}
}

void WaveformMark::setLevel(int level) {
if (level == m_level) {
return;
}

m_level = level;

if (m_pGraphics) {
m_pGraphics->m_obsolete = true;
}
}

bool WaveformMark::contains(QPoint point, Qt::Orientation orientation) const {
// Without some padding, the user would only have a single pixel width that
Expand All @@ -148,13 +178,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();
Expand Down Expand Up @@ -240,9 +274,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 {
Expand Down Expand Up @@ -292,7 +327,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, breadth, m_level);

m_label.setAreaRect(markerGeometry.m_labelRect);

Expand Down
31 changes: 16 additions & 15 deletions src/waveform/renderers/waveformmark.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class WaveformMark {
const QString& group,
const QDomNode& node,
const SkinContext& context,
int priority,
const WaveformSignalColors& signalColors,
int hotCue = Cue::kNoHotCue);
~WaveformMark();
Expand All @@ -44,6 +45,9 @@ class WaveformMark {
int getHotCue() const {
return m_iHotCue;
};
int getPriority() const {
return m_iPriority;
};

// The m_pPositionCO related function
bool isValid() const {
Expand Down Expand Up @@ -89,6 +93,8 @@ class WaveformMark {
m_pVisibleCO->connectValueChanged(receiver, slot, Qt::AutoConnection);
}

void setText(const QString& text);

// Sets the appropriate mark colors based on the base color
void setBaseColor(QColor baseColor, int dimBrightThreshold);
QColor fillColor() const {
Expand All @@ -101,6 +107,8 @@ class WaveformMark {
return m_labelColor;
}

void setLevel(int level);

// Check if a point (in image coordinates) lies on drawn image.
bool contains(QPoint point, Qt::Orientation orientation) const;

Expand All @@ -114,6 +122,7 @@ class WaveformMark {

float m_linePosition;
int m_breadth;
int m_level;

WaveformMarkLabel m_label;

Expand All @@ -126,6 +135,7 @@ class WaveformMark {

std::unique_ptr<Graphics> m_pGraphics;

int m_iPriority;
int m_iHotCue;

QColor m_fillColor;
Expand All @@ -144,27 +154,18 @@ typedef QSharedPointer<WaveformMark> 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;
};
69 changes: 66 additions & 3 deletions src/waveform/renderers/waveformmarkset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,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();
Expand All @@ -54,7 +57,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);
}
Expand All @@ -69,3 +73,62 @@ WaveformMarkPointer WaveformMarkSet::getHotCueMark(int hotCue) const {
WaveformMarkPointer WaveformMarkSet::getDefaultMark() const {
return m_pDefaultMark;
}

void WaveformMarkSet::update() {

Check failure on line 77 in src/waveform/renderers/waveformmarkset.cpp

View workflow job for this annotation

GitHub Actions / clazy

out-of-line definition of 'update' does not match any declaration in 'WaveformMarkSet'

Check failure on line 77 in src/waveform/renderers/waveformmarkset.cpp

View workflow job for this annotation

GitHub Actions / Ubuntu 22.04 (gcc)

no declaration matches ‘void WaveformMarkSet::update()’

Check failure on line 77 in src/waveform/renderers/waveformmarkset.cpp

View workflow job for this annotation

GitHub Actions / Windows 2019 (MSVC)

'update': is not a member of 'WaveformMarkSet'
std::map<WaveformMarkSortKey, WaveformMarkPointer> map;
for (const auto& pMark : m_marks) {

Check failure on line 79 in src/waveform/renderers/waveformmarkset.cpp

View workflow job for this annotation

GitHub Actions / Windows 2019 (MSVC)

'm_marks': undeclared identifier

Check failure on line 79 in src/waveform/renderers/waveformmarkset.cpp

View workflow job for this annotation

GitHub Actions / Windows 2019 (MSVC)

'begin': no matching overloaded function found

Check failure on line 79 in src/waveform/renderers/waveformmarkset.cpp

View workflow job for this annotation

GitHub Actions / Windows 2019 (MSVC)

Failed to specialize function template 'unknown-type std::begin(_Container &)'

Check failure on line 79 in src/waveform/renderers/waveformmarkset.cpp

View workflow job for this annotation

GitHub Actions / Windows 2019 (MSVC)

'const _Elem *std::begin(std::initializer_list<_Elem>) noexcept': could not deduce template argument for 'std::initializer_list<_Elem>' from 'unknown-type'

Check failure on line 79 in src/waveform/renderers/waveformmarkset.cpp

View workflow job for this annotation

GitHub Actions / Windows 2019 (MSVC)

'end': no matching overloaded function found

Check failure on line 79 in src/waveform/renderers/waveformmarkset.cpp

View workflow job for this annotation

GitHub Actions / Windows 2019 (MSVC)

Failed to specialize function template 'unknown-type std::end(_Container &)'

Check failure on line 79 in src/waveform/renderers/waveformmarkset.cpp

View workflow job for this annotation

GitHub Actions / Windows 2019 (MSVC)

'const _Elem *std::end(std::initializer_list<_Elem>) noexcept': could not deduce template argument for 'std::initializer_list<_Elem>' from 'unknown-type'

Check failure on line 79 in src/waveform/renderers/waveformmarkset.cpp

View workflow job for this annotation

GitHub Actions / Windows 2019 (MSVC)

'<begin>$L0': cannot be used before it is initialized

Check failure on line 79 in src/waveform/renderers/waveformmarkset.cpp

View workflow job for this annotation

GitHub Actions / Windows 2019 (MSVC)

'<end>$L0': cannot be used before it is initialized
if (pMark->isValid() && pMark->isVisible()) {
double samplePosition = pMark->getSamplePosition();
if (samplePosition != Cue::kNoPosition) {
// 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->getPriority());
map.emplace(key, pMark);
}
}
}

m_marksToRender.clear();

Check failure on line 92 in src/waveform/renderers/waveformmarkset.cpp

View workflow job for this annotation

GitHub Actions / clazy

use of undeclared identifier 'm_marksToRender'
m_marksToRender.reserve(static_cast<int>(map.size()));

Check failure on line 93 in src/waveform/renderers/waveformmarkset.cpp

View workflow job for this annotation

GitHub Actions / clazy

use of undeclared identifier 'm_marksToRender'
std::transform(map.begin(),
map.end(),
std::back_inserter(m_marksToRender),

Check failure on line 96 in src/waveform/renderers/waveformmarkset.cpp

View workflow job for this annotation

GitHub Actions / clazy

use of undeclared identifier 'm_marksToRender'
[](auto const& pair) { return pair.second; });

double prevSamplePosition = Cue::kNoPosition;
int levelTop = 0;
int levelBottom = 0;
for (auto& pMark : m_marksToRender) {

Check failure on line 102 in src/waveform/renderers/waveformmarkset.cpp

View workflow job for this annotation

GitHub Actions / clazy

use of undeclared identifier 'm_marksToRender'
if (pMark->getSamplePosition() != prevSamplePosition) {
prevSamplePosition = pMark->getSamplePosition();
levelTop = 0;
levelBottom = 0;
}
const Qt::Alignment alignV = pMark->m_align & Qt::AlignVertical_Mask;
if (alignV == Qt::AlignVCenter) {
pMark->setLevel(0);
} else if (alignV == Qt::AlignBottom) {
pMark->setLevel(levelBottom++);
} else {
pMark->setLevel(levelTop++);
}
}
}

WaveformMarkPointer WaveformMarkSet::findHoveredMark(

Check failure on line 119 in src/waveform/renderers/waveformmarkset.cpp

View workflow job for this annotation

GitHub Actions / clazy

out-of-line definition of 'findHoveredMark' does not match any declaration in 'WaveformMarkSet'

Check failure on line 119 in src/waveform/renderers/waveformmarkset.cpp

View workflow job for this annotation

GitHub Actions / Ubuntu 22.04 (gcc)

no declaration matches ‘WaveformMarkPointer WaveformMarkSet::findHoveredMark(QPoint, Qt::Orientation) const’
QPoint pos, Qt::Orientation orientation) const {
// 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 CueMenuPopup. 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) {

Check failure on line 127 in src/waveform/renderers/waveformmarkset.cpp

View workflow job for this annotation

GitHub Actions / clazy

use of undeclared identifier 'm_marksToRender'

Check failure on line 127 in src/waveform/renderers/waveformmarkset.cpp

View workflow job for this annotation

GitHub Actions / clazy

use of undeclared identifier 'm_marksToRender'
const WaveformMarkPointer& pMark = *it;
if (pMark->contains(pos, orientation)) {
return pMark;
}
}
return nullptr;
}

0 comments on commit 52a275d

Please sign in to comment.