parent
1f3656c2f6
commit
3bdffca061
4 changed files with 285 additions and 75 deletions
|
|
@ -3,7 +3,7 @@ pragma ComponentBehavior: Bound
|
|||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import Caelestia.Internal
|
||||
import Caelestia.Services
|
||||
import qs.components
|
||||
import qs.services
|
||||
|
|
@ -55,25 +55,29 @@ Item {
|
|||
service: Audio.cava
|
||||
}
|
||||
|
||||
Item {
|
||||
id: content
|
||||
VisualiserBars {
|
||||
id: bars
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Config.border.thickness
|
||||
anchors.leftMargin: Visibilities.bars.get(root.screen).exclusiveZone + Appearance.spacing.small * Config.background.visualiser.spacing
|
||||
|
||||
Side {
|
||||
content: content
|
||||
}
|
||||
Side {
|
||||
content: content
|
||||
isRight: true
|
||||
}
|
||||
values: Audio.cava.values
|
||||
primaryColor: Qt.alpha(Colours.palette.m3primary, 0.7)
|
||||
secondaryColor: Qt.alpha(Colours.palette.m3inversePrimary, 0.7)
|
||||
rounding: Appearance.rounding.small * Config.background.visualiser.rounding
|
||||
spacing: Appearance.spacing.small * Config.background.visualiser.spacing
|
||||
animationDuration: Appearance.anim.durations.normal
|
||||
|
||||
Behavior on anchors.leftMargin {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
|
||||
FrameAnimation {
|
||||
running: root.opacity > 0 && !bars.settled
|
||||
onTriggered: bars.advance(frameTime)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -85,69 +89,4 @@ Item {
|
|||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
component Side: Repeater {
|
||||
id: side
|
||||
|
||||
required property Item content
|
||||
property bool isRight
|
||||
|
||||
model: Config.services.visualiserBars
|
||||
|
||||
ClippingRectangle {
|
||||
id: bar
|
||||
|
||||
required property int modelData
|
||||
property real value: Math.max(0, Math.min(1, Audio.cava.values[side.isRight ? modelData : side.count - modelData - 1]))
|
||||
|
||||
clip: true
|
||||
|
||||
x: modelData * ((side.content.width * 0.4) / Config.services.visualiserBars) + (side.isRight ? side.content.width * 0.6 : 0)
|
||||
implicitWidth: (side.content.width * 0.4) / Config.services.visualiserBars - Appearance.spacing.small * Config.background.visualiser.spacing
|
||||
|
||||
y: side.content.height - height
|
||||
implicitHeight: bar.value * side.content.height * 0.4
|
||||
|
||||
color: "transparent"
|
||||
topLeftRadius: Appearance.rounding.small * Config.background.visualiser.rounding
|
||||
topRightRadius: Appearance.rounding.small * Config.background.visualiser.rounding
|
||||
|
||||
Rectangle {
|
||||
topLeftRadius: parent.topLeftRadius
|
||||
topRightRadius: parent.topRightRadius
|
||||
|
||||
gradient: Gradient {
|
||||
orientation: Gradient.Vertical
|
||||
|
||||
GradientStop {
|
||||
position: 0
|
||||
color: Qt.alpha(Colours.palette.m3primary, 0.7)
|
||||
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
}
|
||||
}
|
||||
GradientStop {
|
||||
position: 1
|
||||
color: Qt.alpha(Colours.palette.m3inversePrimary, 0.7)
|
||||
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
y: parent.height - height
|
||||
implicitHeight: side.content.height * 0.4
|
||||
}
|
||||
|
||||
Behavior on value {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.small
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ qml_module(caelestia-internal
|
|||
hyprextras.hpp hyprextras.cpp
|
||||
logindmanager.hpp logindmanager.cpp
|
||||
sparklineitem.hpp sparklineitem.cpp
|
||||
visualiserbars.hpp visualiserbars.cpp
|
||||
LIBRARIES
|
||||
Qt::Gui
|
||||
Qt::Quick
|
||||
|
|
|
|||
198
plugin/src/Caelestia/Internal/visualiserbars.cpp
Normal file
198
plugin/src/Caelestia/Internal/visualiserbars.cpp
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
#include "visualiserbars.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <qbrush.h>
|
||||
#include <qpainter.h>
|
||||
#include <qpainterpath.h>
|
||||
#include <qpen.h>
|
||||
|
||||
namespace caelestia::internal {
|
||||
|
||||
VisualiserBars::VisualiserBars(QQuickItem* parent)
|
||||
: QQuickPaintedItem(parent) {
|
||||
setAntialiasing(true);
|
||||
}
|
||||
|
||||
void VisualiserBars::advance(qreal dt) {
|
||||
if (m_displayValues.isEmpty() || m_settled)
|
||||
return;
|
||||
|
||||
// dt is in seconds (from FrameAnimation.frameTime), convert to ms
|
||||
const qreal dtMs = dt * 1000.0;
|
||||
const qreal tau = m_animationDuration / 3.0;
|
||||
const qreal alpha = 1.0 - std::exp(-dtMs / tau);
|
||||
|
||||
bool allSettled = true;
|
||||
|
||||
for (qsizetype i = 0; i < m_displayValues.size(); ++i) {
|
||||
const double diff = m_targetValues[i] - m_displayValues[i];
|
||||
|
||||
if (std::abs(diff) > 0.001) {
|
||||
m_displayValues[i] += diff * alpha;
|
||||
allSettled = false;
|
||||
} else {
|
||||
m_displayValues[i] = m_targetValues[i];
|
||||
}
|
||||
}
|
||||
|
||||
update();
|
||||
|
||||
if (allSettled && !m_settled) {
|
||||
m_settled = true;
|
||||
emit settledChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void VisualiserBars::paint(QPainter* painter) {
|
||||
if (m_displayValues.isEmpty())
|
||||
return;
|
||||
|
||||
painter->setRenderHint(QPainter::Antialiasing, true);
|
||||
painter->setPen(Qt::NoPen);
|
||||
|
||||
const qreal h = height();
|
||||
const qreal maxBarHeight = h * 0.4;
|
||||
|
||||
QLinearGradient gradient(0, h - maxBarHeight, 0, h);
|
||||
gradient.setColorAt(0, m_primaryColor);
|
||||
gradient.setColorAt(1, m_secondaryColor);
|
||||
painter->setBrush(gradient);
|
||||
|
||||
drawSide(painter, false);
|
||||
drawSide(painter, true);
|
||||
}
|
||||
|
||||
void VisualiserBars::drawSide(QPainter* painter, bool rightSide) {
|
||||
const qreal w = width();
|
||||
const qreal h = height();
|
||||
const auto count = m_displayValues.size();
|
||||
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
const qreal sideWidth = w * 0.4;
|
||||
const qreal slotWidth = sideWidth / static_cast<qreal>(count);
|
||||
const qreal barWidth = slotWidth - m_spacing;
|
||||
|
||||
if (barWidth <= 0)
|
||||
return;
|
||||
|
||||
const qreal sideOffset = rightSide ? w * 0.6 : 0;
|
||||
const qreal maxBarHeight = h * 0.4;
|
||||
|
||||
for (qsizetype i = 0; i < count; ++i) {
|
||||
const qsizetype valueIndex = rightSide ? i : (count - i - 1);
|
||||
const qreal value = std::clamp(m_displayValues[valueIndex], 0.0, 1.0);
|
||||
const qreal barHeight = value * maxBarHeight;
|
||||
|
||||
if (barHeight <= 0)
|
||||
continue;
|
||||
|
||||
const qreal x = static_cast<qreal>(i) * slotWidth + sideOffset;
|
||||
const qreal y = h - barHeight;
|
||||
const qreal r = std::min({ m_rounding, barWidth / 2.0, barHeight });
|
||||
|
||||
QPainterPath path;
|
||||
path.moveTo(x, h);
|
||||
path.lineTo(x, y + r);
|
||||
|
||||
if (r > 0) {
|
||||
path.arcTo(x, y, r * 2, r * 2, 180, -90);
|
||||
path.lineTo(x + barWidth - r, y);
|
||||
path.arcTo(x + barWidth - r * 2, y, r * 2, r * 2, 90, -90);
|
||||
} else {
|
||||
path.lineTo(x, y);
|
||||
path.lineTo(x + barWidth, y);
|
||||
}
|
||||
|
||||
path.lineTo(x + barWidth, h);
|
||||
path.closeSubpath();
|
||||
|
||||
painter->drawPath(path);
|
||||
}
|
||||
}
|
||||
|
||||
QVector<double> VisualiserBars::values() const {
|
||||
return m_targetValues;
|
||||
}
|
||||
|
||||
void VisualiserBars::setValues(const QVector<double>& values) {
|
||||
m_targetValues = values;
|
||||
|
||||
if (m_displayValues.size() != values.size()) {
|
||||
m_displayValues.resize(values.size(), 0.0);
|
||||
}
|
||||
|
||||
if (m_settled) {
|
||||
m_settled = false;
|
||||
emit settledChanged();
|
||||
}
|
||||
|
||||
emit valuesChanged();
|
||||
}
|
||||
|
||||
bool VisualiserBars::settled() const {
|
||||
return m_settled;
|
||||
}
|
||||
|
||||
QColor VisualiserBars::primaryColor() const {
|
||||
return m_primaryColor;
|
||||
}
|
||||
|
||||
void VisualiserBars::setPrimaryColor(const QColor& color) {
|
||||
if (m_primaryColor == color)
|
||||
return;
|
||||
m_primaryColor = color;
|
||||
emit primaryColorChanged();
|
||||
update();
|
||||
}
|
||||
|
||||
QColor VisualiserBars::secondaryColor() const {
|
||||
return m_secondaryColor;
|
||||
}
|
||||
|
||||
void VisualiserBars::setSecondaryColor(const QColor& color) {
|
||||
if (m_secondaryColor == color)
|
||||
return;
|
||||
m_secondaryColor = color;
|
||||
emit secondaryColorChanged();
|
||||
update();
|
||||
}
|
||||
|
||||
qreal VisualiserBars::rounding() const {
|
||||
return m_rounding;
|
||||
}
|
||||
|
||||
void VisualiserBars::setRounding(qreal rounding) {
|
||||
if (qFuzzyCompare(m_rounding, rounding))
|
||||
return;
|
||||
m_rounding = rounding;
|
||||
emit roundingChanged();
|
||||
update();
|
||||
}
|
||||
|
||||
qreal VisualiserBars::spacing() const {
|
||||
return m_spacing;
|
||||
}
|
||||
|
||||
void VisualiserBars::setSpacing(qreal spacing) {
|
||||
if (qFuzzyCompare(m_spacing, spacing))
|
||||
return;
|
||||
m_spacing = spacing;
|
||||
emit spacingChanged();
|
||||
update();
|
||||
}
|
||||
|
||||
int VisualiserBars::animationDuration() const {
|
||||
return m_animationDuration;
|
||||
}
|
||||
|
||||
void VisualiserBars::setAnimationDuration(int duration) {
|
||||
if (m_animationDuration == duration)
|
||||
return;
|
||||
m_animationDuration = duration;
|
||||
emit animationDurationChanged();
|
||||
}
|
||||
|
||||
} // namespace caelestia::internal
|
||||
72
plugin/src/Caelestia/Internal/visualiserbars.hpp
Normal file
72
plugin/src/Caelestia/Internal/visualiserbars.hpp
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
#pragma once
|
||||
|
||||
#include <qcolor.h>
|
||||
#include <qobject.h>
|
||||
#include <qqmlintegration.h>
|
||||
#include <qquickpainteditem.h>
|
||||
#include <qvector.h>
|
||||
|
||||
namespace caelestia::internal {
|
||||
|
||||
class VisualiserBars : public QQuickPaintedItem {
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
|
||||
Q_PROPERTY(QVector<double> values READ values WRITE setValues NOTIFY valuesChanged)
|
||||
Q_PROPERTY(QColor primaryColor READ primaryColor WRITE setPrimaryColor NOTIFY primaryColorChanged)
|
||||
Q_PROPERTY(QColor secondaryColor READ secondaryColor WRITE setSecondaryColor NOTIFY secondaryColorChanged)
|
||||
Q_PROPERTY(qreal rounding READ rounding WRITE setRounding NOTIFY roundingChanged)
|
||||
Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing NOTIFY spacingChanged)
|
||||
Q_PROPERTY(int animationDuration READ animationDuration WRITE setAnimationDuration NOTIFY animationDurationChanged)
|
||||
Q_PROPERTY(bool settled READ settled NOTIFY settledChanged)
|
||||
|
||||
public:
|
||||
explicit VisualiserBars(QQuickItem* parent = nullptr);
|
||||
|
||||
void paint(QPainter* painter) override;
|
||||
|
||||
Q_INVOKABLE void advance(qreal dt);
|
||||
|
||||
[[nodiscard]] QVector<double> values() const;
|
||||
void setValues(const QVector<double>& values);
|
||||
|
||||
[[nodiscard]] QColor primaryColor() const;
|
||||
void setPrimaryColor(const QColor& color);
|
||||
|
||||
[[nodiscard]] QColor secondaryColor() const;
|
||||
void setSecondaryColor(const QColor& color);
|
||||
|
||||
[[nodiscard]] qreal rounding() const;
|
||||
void setRounding(qreal rounding);
|
||||
|
||||
[[nodiscard]] qreal spacing() const;
|
||||
void setSpacing(qreal spacing);
|
||||
|
||||
[[nodiscard]] int animationDuration() const;
|
||||
void setAnimationDuration(int duration);
|
||||
|
||||
[[nodiscard]] bool settled() const;
|
||||
|
||||
signals:
|
||||
void valuesChanged();
|
||||
void primaryColorChanged();
|
||||
void secondaryColorChanged();
|
||||
void roundingChanged();
|
||||
void spacingChanged();
|
||||
void animationDurationChanged();
|
||||
void settledChanged();
|
||||
|
||||
private:
|
||||
void drawSide(QPainter* painter, bool rightSide);
|
||||
|
||||
QVector<double> m_targetValues;
|
||||
QVector<double> m_displayValues;
|
||||
QColor m_primaryColor;
|
||||
QColor m_secondaryColor;
|
||||
qreal m_rounding = 0.0;
|
||||
qreal m_spacing = 0.0;
|
||||
int m_animationDuration = 200;
|
||||
bool m_settled = true;
|
||||
};
|
||||
|
||||
} // namespace caelestia::internal
|
||||
Loading…
Add table
Reference in a new issue