fix(visualizer): refine scopes and seekbar
Make immersive scopes responsive and derive readable colors from album artwork. Use distinct full-size bar and compact contour renderers for the seek waveform. cx
This commit is contained in:
8 files changed
+178
-38
No files matched your search
@@ -109,7 +109,18 @@ QVector<QPointF> mapPoints(const QVariantList &source,
|
||||
points.push_back(QPointF(pad + (0.5 + side * 0.47) * drawWidth,
|
||||
pad + (0.5 - mid * 0.47) * drawHeight));
|
||||
} else {
|
||||
const qreal value = qBound<qreal>(-1.0, source[index].toReal() * gain, 1.0);
|
||||
// The DSP publishes two pitch-locked periods. Repeat that stable
|
||||
// window according to the item's aspect ratio so a wide monitor
|
||||
// gains temporal detail instead of stretching two enormous
|
||||
// lobes. A square panel remains the original two-period scope.
|
||||
const int repeats = qBound(1, qRound(width / qMax<qreal>(1.0, height)), 4);
|
||||
const qreal wrapped = std::fmod(index * repeats, count - 1.0);
|
||||
const int first = qBound(0, static_cast<int>(std::floor(wrapped)), count - 1);
|
||||
const int second = qMin(first + 1, count - 1);
|
||||
const qreal fraction = wrapped - first;
|
||||
const qreal sample = source[first].toReal() * (1.0 - fraction)
|
||||
+ source[second].toReal() * fraction;
|
||||
const qreal value = qBound<qreal>(-1.0, sample * gain, 1.0);
|
||||
points.push_back(QPointF(pad + index * drawWidth / qMax(1, count - 1),
|
||||
pad + (0.5 - value * 0.47) * drawHeight));
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ public:
|
||||
QColor baseGeometryColor;
|
||||
QColor cacheGeometryColor;
|
||||
QColor playedGeometryColor;
|
||||
int geometryRenderMode = -1;
|
||||
};
|
||||
|
||||
void setVertex(QSGGeometry::ColoredPoint2D &vertex,
|
||||
@@ -100,17 +101,70 @@ void setLayerGeometry(QSGGeometryNode *node,
|
||||
const std::vector<float> &values,
|
||||
qreal width,
|
||||
qreal height,
|
||||
const QColor &color)
|
||||
const QColor &color,
|
||||
int renderMode)
|
||||
{
|
||||
auto *geometry = node->geometry();
|
||||
const int count = static_cast<int>(values.size());
|
||||
const float centre = static_cast<float>(height * 0.5);
|
||||
const float halfHeight = std::max(1.0f, static_cast<float>(height * 0.47));
|
||||
|
||||
if (renderMode == 1) {
|
||||
// Small NPB: the RMS document has no signed samples, so present its
|
||||
// loudness contour as one continuous, centre-crossing curve. A few
|
||||
// dozen well-spaced segments read as a waveform at 11 px; hundreds
|
||||
// of sub-pixel bars only read as a rough blue rail.
|
||||
geometry->allocate(6 + std::max(0, count - 1) * 6);
|
||||
geometry->setDrawingMode(QSGGeometry::DrawTriangles);
|
||||
auto *vertices = geometry->vertexDataAsColoredPoint2D();
|
||||
const float railHalf = std::min(0.42f, halfHeight * 0.12f);
|
||||
setQuad(vertices, 0.0f, centre - railHalf, static_cast<float>(width),
|
||||
centre + railHalf, color, 0.32f, 0.32f);
|
||||
if (count < 2) {
|
||||
node->markDirty(QSGNode::DirtyGeometry);
|
||||
return;
|
||||
}
|
||||
|
||||
const float lineHalf = std::clamp(static_cast<float>(height * 0.085), 0.62f, 1.15f);
|
||||
const float swing = static_cast<float>(height * 0.36);
|
||||
auto pointAt = [&](int index) {
|
||||
const float x = index * static_cast<float>(width) / (count - 1);
|
||||
const float y = centre + (0.5f - values[index]) * swing * 2.0f;
|
||||
return QPointF(x, y);
|
||||
};
|
||||
int vertex = 6;
|
||||
for (int index = 0; index + 1 < count; ++index) {
|
||||
const QPointF first = pointAt(index);
|
||||
const QPointF second = pointAt(index + 1);
|
||||
const QPointF delta = second - first;
|
||||
const qreal length = std::hypot(delta.x(), delta.y());
|
||||
const QPointF normal = length > 0.0001
|
||||
? QPointF(-delta.y() / length * lineHalf,
|
||||
delta.x() / length * lineHalf)
|
||||
: QPointF(0.0, lineHalf);
|
||||
setVertex(vertices[vertex], static_cast<float>((first + normal).x()),
|
||||
static_cast<float>((first + normal).y()), color, 0.92f);
|
||||
setVertex(vertices[vertex + 1], static_cast<float>((first - normal).x()),
|
||||
static_cast<float>((first - normal).y()), color, 0.92f);
|
||||
setVertex(vertices[vertex + 2], static_cast<float>((second + normal).x()),
|
||||
static_cast<float>((second + normal).y()), color, 0.92f);
|
||||
setVertex(vertices[vertex + 3], static_cast<float>((second + normal).x()),
|
||||
static_cast<float>((second + normal).y()), color, 0.92f);
|
||||
setVertex(vertices[vertex + 4], static_cast<float>((first - normal).x()),
|
||||
static_cast<float>((first - normal).y()), color, 0.92f);
|
||||
setVertex(vertices[vertex + 5], static_cast<float>((second - normal).x()),
|
||||
static_cast<float>((second - normal).y()), color, 0.92f);
|
||||
vertex += 6;
|
||||
}
|
||||
node->markDirty(QSGNode::DirtyGeometry);
|
||||
return;
|
||||
}
|
||||
|
||||
const int visibleBars = static_cast<int>(std::count_if(values.begin(), values.end(),
|
||||
[](float value) { return value > 0.0005f; }));
|
||||
geometry->allocate(6 + visibleBars * 12);
|
||||
geometry->setDrawingMode(QSGGeometry::DrawTriangles);
|
||||
auto *vertices = geometry->vertexDataAsColoredPoint2D();
|
||||
const float centre = static_cast<float>(height * 0.5);
|
||||
const float halfHeight = std::max(1.0f, static_cast<float>(height * 0.47));
|
||||
|
||||
// A permanent hairline makes the temporal axis readable before the whole
|
||||
// track has been analysed. Its three clipped color layers retain the
|
||||
@@ -127,10 +181,10 @@ void setLayerGeometry(QSGGeometryNode *node,
|
||||
constexpr int groupSize = 8;
|
||||
const int groupBreaks = (count - 1) / groupSize;
|
||||
const float nominalSlot = static_cast<float>(width) / count;
|
||||
const float groupGap = std::min(1.7f, std::max(0.65f, nominalSlot * 0.48f));
|
||||
const float groupGap = std::min(2.8f, std::max(1.1f, nominalSlot * 0.52f));
|
||||
const float usableWidth = std::max(1.0f, static_cast<float>(width) - groupBreaks * groupGap);
|
||||
const float slot = usableWidth / count;
|
||||
const float barWidth = std::max(0.65f, std::min(slot * 0.66f, slot - 0.18f));
|
||||
const float barWidth = std::max(1.2f, std::min(slot * 0.74f, slot - 0.35f));
|
||||
int vertex = 6;
|
||||
|
||||
for (int index = 0; index < count; ++index) {
|
||||
@@ -152,7 +206,7 @@ void setLayerGeometry(QSGGeometryNode *node,
|
||||
node->markDirty(QSGNode::DirtyGeometry);
|
||||
}
|
||||
|
||||
std::vector<float> renderValues(const QVariantList &source, int columns)
|
||||
std::vector<float> renderValues(const QVariantList &source, int columns, float gamma)
|
||||
{
|
||||
if (source.isEmpty() || columns < 2)
|
||||
return {};
|
||||
@@ -165,7 +219,7 @@ std::vector<float> renderValues(const QVariantList &source, int columns)
|
||||
float peak = 0.0f;
|
||||
for (int index = begin; index < std::min(end, sourceCount); ++index)
|
||||
peak = std::max(peak, std::clamp(source[index].toFloat(), 0.0f, 1.0f));
|
||||
values[column] = std::pow(peak, 1.42f);
|
||||
values[column] = std::pow(peak, gamma);
|
||||
}
|
||||
|
||||
std::vector<float> scratch(columns, 0.0f);
|
||||
@@ -242,6 +296,15 @@ void SeekWaveformItem::setPlayedColor(const QColor &color)
|
||||
update();
|
||||
}
|
||||
|
||||
void SeekWaveformItem::setRenderMode(int mode)
|
||||
{
|
||||
mode = mode == 1 ? 1 : 0;
|
||||
if (m_renderMode == mode)
|
||||
return;
|
||||
m_renderMode = mode;
|
||||
update();
|
||||
}
|
||||
|
||||
QSGNode *SeekWaveformItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
|
||||
{
|
||||
if (m_values.isEmpty() || width() <= 0.0 || height() <= 0.0) {
|
||||
@@ -262,17 +325,20 @@ QSGNode *SeekWaveformItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
|
||||
|| root->cacheGeometryColor != m_cacheColor
|
||||
|| root->playedGeometryColor != m_playedColor;
|
||||
if (root->geometryRevision != m_valuesRevision || root->geometrySize != size()
|
||||
|| colorsChanged) {
|
||||
const int columns = qBound(24, static_cast<int>(std::ceil(width() / 2.45)), 512);
|
||||
const auto values = renderValues(m_values, columns);
|
||||
setLayerGeometry(root->baseLayer, values, width(), height(), m_baseColor);
|
||||
setLayerGeometry(root->cacheLayer, values, width(), height(), m_cacheColor);
|
||||
setLayerGeometry(root->playedLayer, values, width(), height(), m_playedColor);
|
||||
|| colorsChanged || root->geometryRenderMode != m_renderMode) {
|
||||
const int columns = m_renderMode == 1
|
||||
? qBound(32, static_cast<int>(std::ceil(width() / 9.0)), 192)
|
||||
: qBound(40, static_cast<int>(std::ceil(width() / 5.5)), 256);
|
||||
const auto values = renderValues(m_values, columns, m_renderMode == 1 ? 0.9f : 0.78f);
|
||||
setLayerGeometry(root->baseLayer, values, width(), height(), m_baseColor, m_renderMode);
|
||||
setLayerGeometry(root->cacheLayer, values, width(), height(), m_cacheColor, m_renderMode);
|
||||
setLayerGeometry(root->playedLayer, values, width(), height(), m_playedColor, m_renderMode);
|
||||
root->geometryRevision = m_valuesRevision;
|
||||
root->geometrySize = size();
|
||||
root->baseGeometryColor = m_baseColor;
|
||||
root->cacheGeometryColor = m_cacheColor;
|
||||
root->playedGeometryColor = m_playedColor;
|
||||
root->geometryRenderMode = m_renderMode;
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ class SeekWaveformItem : public QQuickItem
|
||||
Q_PROPERTY(QColor baseColor READ baseColor WRITE setBaseColor)
|
||||
Q_PROPERTY(QColor cacheColor READ cacheColor WRITE setCacheColor)
|
||||
Q_PROPERTY(QColor playedColor READ playedColor WRITE setPlayedColor)
|
||||
Q_PROPERTY(int renderMode READ renderMode WRITE setRenderMode)
|
||||
|
||||
public:
|
||||
explicit SeekWaveformItem(QQuickItem *parent = nullptr);
|
||||
@@ -30,6 +31,8 @@ public:
|
||||
void setCacheColor(const QColor &color);
|
||||
QColor playedColor() const { return m_playedColor; }
|
||||
void setPlayedColor(const QColor &color);
|
||||
int renderMode() const { return m_renderMode; }
|
||||
void setRenderMode(int mode);
|
||||
|
||||
protected:
|
||||
QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) override;
|
||||
@@ -41,6 +44,7 @@ private:
|
||||
QColor m_baseColor{ 64, 68, 74 };
|
||||
QColor m_cacheColor{ 128, 132, 140 };
|
||||
QColor m_playedColor{ 63, 169, 232 };
|
||||
int m_renderMode = 0;
|
||||
quint64 m_valuesRevision = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -10,7 +10,40 @@ Item {
|
||||
// 0 = goniometer, 1 = pitch-locked oscilloscope.
|
||||
property int scopeMode: 0
|
||||
property color traceColor: theme.accent
|
||||
// Immersive scopes choose the brightest colour extracted from the
|
||||
// current cover. Compact Large-NPB scopes leave this false and receive
|
||||
// the same reactive primary colour as its other three visualizers.
|
||||
property bool albumContrast: false
|
||||
property bool compact: false
|
||||
readonly property color albumPrimary: QbzShell.ambientPrimary
|
||||
readonly property color albumSecondary: QbzShell.ambientSecondary
|
||||
readonly property color albumAccent: QbzShell.ambientAccent
|
||||
|
||||
function relativeLuminance(c) {
|
||||
const r = c.r <= 0.03928 ? c.r / 12.92 : Math.pow((c.r + 0.055) / 1.055, 2.4)
|
||||
const g = c.g <= 0.03928 ? c.g / 12.92 : Math.pow((c.g + 0.055) / 1.055, 2.4)
|
||||
const b = c.b <= 0.03928 ? c.b / 12.92 : Math.pow((c.b + 0.055) / 1.055, 2.4)
|
||||
return 0.2126 * r + 0.7152 * g + 0.0722 * b
|
||||
}
|
||||
function brightestAlbumColor() {
|
||||
const candidates = [root.albumPrimary, root.albumSecondary,
|
||||
root.albumAccent]
|
||||
var best = candidates[0]
|
||||
var bestLuminance = relativeLuminance(best)
|
||||
for (var i = 1; i < candidates.length; ++i) {
|
||||
const luminance = relativeLuminance(candidates[i])
|
||||
if (luminance > bestLuminance) {
|
||||
best = candidates[i]
|
||||
bestLuminance = luminance
|
||||
}
|
||||
}
|
||||
return best
|
||||
}
|
||||
readonly property color effectiveTraceColor:
|
||||
root.albumContrast ? root.brightestAlbumColor() : root.traceColor
|
||||
readonly property color guideColor: Qt.rgba(
|
||||
root.effectiveTraceColor.r, root.effectiveTraceColor.g,
|
||||
root.effectiveTraceColor.b, root.scopeMode === 0 ? 0.19 : 0.12)
|
||||
|
||||
// Instrument guides stay deliberately quieter than the trace. They are
|
||||
// static scene-graph nodes; only the native trace item changes per frame.
|
||||
@@ -19,16 +52,14 @@ Item {
|
||||
y: root.compact ? 4 : parent.height * 0.08
|
||||
width: 1
|
||||
height: root.compact ? parent.height - 8 : parent.height * 0.84
|
||||
color: Qt.rgba(theme.textMuted.r, theme.textMuted.g, theme.textMuted.b,
|
||||
root.scopeMode === 0 ? 0.22 : 0.12)
|
||||
color: root.guideColor
|
||||
}
|
||||
Rectangle {
|
||||
x: root.compact ? 4 : parent.width * 0.08
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: root.compact ? parent.width - 8 : parent.width * 0.84
|
||||
height: 1
|
||||
color: Qt.rgba(theme.textMuted.r, theme.textMuted.g, theme.textMuted.b,
|
||||
root.scopeMode === 0 ? 0.22 : 0.15)
|
||||
color: root.guideColor
|
||||
}
|
||||
|
||||
Repeater {
|
||||
@@ -39,7 +70,8 @@ Item {
|
||||
y: parent.height * 0.14
|
||||
width: 1
|
||||
height: parent.height * 0.72
|
||||
color: Qt.rgba(theme.textMuted.r, theme.textMuted.g, theme.textMuted.b, 0.08)
|
||||
color: Qt.rgba(root.effectiveTraceColor.r, root.effectiveTraceColor.g,
|
||||
root.effectiveTraceColor.b, 0.07)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +82,8 @@ Item {
|
||||
height: width
|
||||
color: "transparent"
|
||||
border.width: 1
|
||||
border.color: Qt.rgba(theme.textMuted.r, theme.textMuted.g, theme.textMuted.b, 0.18)
|
||||
border.color: Qt.rgba(root.effectiveTraceColor.r, root.effectiveTraceColor.g,
|
||||
root.effectiveTraceColor.b, 0.18)
|
||||
radius: width / 2
|
||||
}
|
||||
Rectangle {
|
||||
@@ -60,7 +93,8 @@ Item {
|
||||
height: width
|
||||
color: "transparent"
|
||||
border.width: 1
|
||||
border.color: Qt.rgba(theme.textMuted.r, theme.textMuted.g, theme.textMuted.b, 0.09)
|
||||
border.color: Qt.rgba(root.effectiveTraceColor.r, root.effectiveTraceColor.g,
|
||||
root.effectiveTraceColor.b, 0.09)
|
||||
radius: width / 2
|
||||
}
|
||||
Repeater {
|
||||
@@ -71,7 +105,8 @@ Item {
|
||||
width: parent.width * 0.76
|
||||
height: 1
|
||||
rotation: index === 0 ? 45 : -45
|
||||
color: Qt.rgba(theme.textMuted.r, theme.textMuted.g, theme.textMuted.b, 0.08)
|
||||
color: Qt.rgba(root.effectiveTraceColor.r, root.effectiveTraceColor.g,
|
||||
root.effectiveTraceColor.b, 0.08)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +115,7 @@ Item {
|
||||
anchors.margins: root.compact ? 3 : 8
|
||||
mode: root.scopeMode
|
||||
points: root.scopeMode === 0 ? QbzViz.goniometer : QbzViz.oscilloscope
|
||||
traceColor: root.traceColor
|
||||
traceColor: root.effectiveTraceColor
|
||||
lineWidth: root.compact ? 1.35 : 2.2
|
||||
trailDepth: root.scopeMode === 0 ? (root.compact ? 3 : 5) : (root.compact ? 2 : 4)
|
||||
fillOpacity: root.compact ? 0.08 : 0.16
|
||||
@@ -101,7 +136,8 @@ Item {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
height: 2
|
||||
radius: 1
|
||||
color: Qt.rgba(theme.textMuted.r, theme.textMuted.g, theme.textMuted.b, 0.22)
|
||||
color: Qt.rgba(root.effectiveTraceColor.r, root.effectiveTraceColor.g,
|
||||
root.effectiveTraceColor.b, 0.22)
|
||||
Rectangle {
|
||||
width: 4
|
||||
height: 10
|
||||
@@ -109,7 +145,7 @@ Item {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
x: (parent.width - width) * Math.max(0, Math.min(1,
|
||||
(QbzViz.stereoCorrelation + 1) * 0.5))
|
||||
color: root.traceColor
|
||||
color: root.effectiveTraceColor
|
||||
}
|
||||
}
|
||||
Text {
|
||||
@@ -117,7 +153,8 @@ Item {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: QbzViz.stereoCorrelation.toFixed(2)
|
||||
color: theme.textMuted
|
||||
color: Qt.rgba(root.effectiveTraceColor.r, root.effectiveTraceColor.g,
|
||||
root.effectiveTraceColor.b, 0.72)
|
||||
font.pixelSize: 11
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,9 +145,11 @@ Item {
|
||||
ImmersiveAtmosphere {
|
||||
anchors.fill: parent
|
||||
visible: QbzShaderScene.scene === 0
|
||||
&& !(QbzImmersive.viewMode === 0
|
||||
&& (QbzImmersive.mode === 7 || QbzImmersive.mode === 8))
|
||||
source: QbzImmersive.atmosphereUrl
|
||||
fallbackSource: QbzPlayer.npArtworkPath
|
||||
animated: QbzPlayer.npPlaying && QbzImmersive.open
|
||||
animated: visible && QbzPlayer.npPlaying && QbzImmersive.open
|
||||
dim: 0.15
|
||||
}
|
||||
|
||||
@@ -308,6 +310,20 @@ Item {
|
||||
&& QbzImmersive.viewMode === 0 && QbzImmersive.mode === 7
|
||||
visible: active
|
||||
sourceComponent: Item {
|
||||
// A single host-generated blurred cover, without the animated
|
||||
// four-layer atmosphere. The dark veil makes one album-derived
|
||||
// high-luminance trace colour hold over the whole viewport.
|
||||
Rectangle { anchors.fill: parent; color: "#090909" }
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
source: QbzImmersive.atmosphereUrl
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
asynchronous: true
|
||||
smooth: true
|
||||
mipmap: true
|
||||
opacity: status === Image.Ready ? 0.78 : 0.0
|
||||
}
|
||||
Rectangle { anchors.fill: parent; color: "#66000000" }
|
||||
ScopePanel {
|
||||
width: Math.max(1, Math.min(parent.width - 96, parent.height - 196))
|
||||
height: width
|
||||
@@ -315,7 +331,7 @@ Item {
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 132
|
||||
scopeMode: 0
|
||||
traceColor: theme.accent
|
||||
albumContrast: true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -325,14 +341,18 @@ Item {
|
||||
&& QbzImmersive.viewMode === 0 && QbzImmersive.mode === 8
|
||||
visible: active
|
||||
sourceComponent: Item {
|
||||
Rectangle { anchors.fill: parent; color: "#000000" }
|
||||
ScopePanel {
|
||||
width: Math.max(1, Math.min(parent.width - 96, parent.height - 196))
|
||||
height: width
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.leftMargin: 48
|
||||
anchors.rightMargin: 48
|
||||
anchors.topMargin: 96
|
||||
anchors.bottomMargin: 132
|
||||
scopeMode: 1
|
||||
traceColor: theme.accent
|
||||
albumContrast: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,6 +234,7 @@ Rectangle {
|
||||
cacheColor: Qt.rgba(theme.textMuted.r, theme.textMuted.g,
|
||||
theme.textMuted.b, 0.35)
|
||||
playedColor: theme.accent
|
||||
renderMode: 1
|
||||
}
|
||||
|
||||
// Buffered / cache line.
|
||||
|
||||
@@ -308,7 +308,7 @@ Rectangle {
|
||||
// dock (AppShell.slint's large-active padding).
|
||||
Item {
|
||||
width: parent.width
|
||||
height: 20
|
||||
height: 32
|
||||
Text {
|
||||
x: root.dockWidth > 0 ? root.dockWidth + 6 : 6
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
@@ -323,7 +323,7 @@ Rectangle {
|
||||
anchors.right: parent.right
|
||||
anchors.leftMargin: (root.dockWidth > 0 ? root.dockWidth + 6 : 6) + (QbzPlayer.npHasTrack ? 46 : 0)
|
||||
anchors.rightMargin: 6 + (QbzPlayer.npHasTrack ? 46 : 0)
|
||||
height: root.waveformVisible ? 18 : 4
|
||||
height: root.waveformVisible ? 30 : 4
|
||||
radius: 2
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: root.waveformVisible ? "transparent" : theme.surfaceElevated
|
||||
@@ -337,6 +337,7 @@ Rectangle {
|
||||
cacheColor: Qt.rgba(theme.textMuted.r, theme.textMuted.g,
|
||||
theme.textMuted.b, 0.35)
|
||||
playedColor: theme.accent
|
||||
renderMode: 0
|
||||
}
|
||||
// Buffered / cache line — SeekBar.slint:58 paints it
|
||||
// text-muted @0.35, NOT border-muted: border-muted is an
|
||||
@@ -406,13 +407,13 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
Item { width: 1; height: 6 }
|
||||
Item { width: 1; height: 2 }
|
||||
|
||||
// --- Controls: the responsive symmetric zones -----------------------
|
||||
Item {
|
||||
id: controlsLayout
|
||||
width: parent.width
|
||||
height: parent.height - 38
|
||||
height: parent.height - 40
|
||||
|
||||
// The three zones keep PLAY centred and the right cluster pinned,
|
||||
// but the SongCard is sized against the controls themselves, not
|
||||
|
||||
@@ -446,7 +446,7 @@ Rectangle {
|
||||
sourceComponent: ScopePanel {
|
||||
compact: true
|
||||
scopeMode: 0
|
||||
traceColor: theme.accent
|
||||
traceColor: band.topColor
|
||||
}
|
||||
}
|
||||
Loader {
|
||||
@@ -455,7 +455,7 @@ Rectangle {
|
||||
sourceComponent: ScopePanel {
|
||||
compact: true
|
||||
scopeMode: 1
|
||||
traceColor: theme.accent
|
||||
traceColor: band.topColor
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user