Merge branch 'main' into feat/llm-chat-tab
C++ / fmt (pull_request) Failing after 4s
C++ / build (pull_request) Failing after 12s
C++ / clang-tidy (pull_request) Failing after 12s
JS/TS / fmt (pull_request) Successful in 8s
JS/TS / lint (pull_request) Successful in 9s
Python / static (pull_request) Successful in 31s
Rust / build (pull_request) Successful in 49s
Python / verify (pull_request) Successful in 1m32s
Rust / fmt (pull_request) Successful in 51s
Rust / clippy (pull_request) Successful in 1m1s

This commit is contained in:
2026-10-12 15:54:52 +02:00
16 changed files with 222 additions and 185 deletions
+2
View File
@@ -4,6 +4,7 @@ qml_module(ZShell-components
lazylistview.hpp lazylistview.cpp
chatlistview.hpp chatlistview.cpp
wavyline.hpp wavyline.cpp
wavylinerenderer.hpp wavylinerenderer.cpp
buttonrow.hpp buttonrow.cpp
carouselview.hpp carouselview.cpp
sparklinecontent.hpp sparklinecontent.cpp
@@ -13,5 +14,6 @@ qml_module(ZShell-components
LIBRARIES
Qt::Quick
Qt::QuickPrivate
Qt::CanvasPainter
ZShell-internal
)
+9 -96
View File
@@ -1,12 +1,10 @@
#include "wavyline.hpp"
#include <qpainter.h>
#include <qpainterpath.h>
#include "wavylinerenderer.hpp"
namespace ZShell::controls {
WavyLine::WavyLine(QQuickItem* parent)
: QQuickPaintedItem(parent)
: QCanvasPainterItem(parent)
, m_lineWidth(4)
, m_amplitudeMultiplier(0.5)
, m_frequency(6)
@@ -18,10 +16,13 @@ WavyLine::WavyLine(QQuickItem* parent)
, m_startAngle(0)
, m_fullAngle(360)
, m_radius(-1)
, m_value(1)
, m_startAngleRad(0)
, m_fullAngleRad(2 * M_PI) {
setAntialiasing(true);
, m_value(1) {
setFillColor(Qt::transparent);
setAlphaBlending(true);
}
QCanvasPainterItemRenderer* WavyLine::createItemRenderer() const {
return new WavyLineRenderer;
}
int WavyLine::lineWidth() const {
@@ -127,7 +128,6 @@ qreal WavyLine::startAngle() const {
void WavyLine::setStartAngle(qreal startAngle) {
if (!qFuzzyCompare(m_startAngle + 1.0, startAngle + 1.0)) {
m_startAngle = startAngle;
m_startAngleRad = startAngle * M_PI / 180.0;
emit startAngleChanged();
update();
}
@@ -140,7 +140,6 @@ qreal WavyLine::fullAngle() const {
void WavyLine::setFullAngle(qreal fullAngle) {
if (!qFuzzyCompare(m_fullAngle + 1.0, fullAngle + 1.0)) {
m_fullAngle = fullAngle;
m_fullAngleRad = fullAngle * M_PI / 180.0;
emit fullAngleChanged();
update();
}
@@ -170,90 +169,4 @@ void WavyLine::setValue(qreal value) {
}
}
void WavyLine::paint(QPainter* painter) {
painter->setRenderHint(QPainter::Antialiasing);
painter->setPen(QPen(m_color, m_lineWidth, Qt::SolidLine, Qt::RoundCap));
if (m_pathType == Arc) {
paintArc(painter);
} else {
paintLinear(painter);
}
}
void WavyLine::paintLinear(QPainter* painter) {
const auto amplitude = m_lineWidth * m_amplitudeMultiplier;
const auto phase = m_waveProgress * 2 * M_PI;
const auto centerY = height() / 2;
const auto len = m_fullLength > 0 ? m_fullLength : 1;
const auto start = m_lineWidth / 2.0;
const auto fullEnd = width() - m_lineWidth / 2.0;
const auto drawEnd = start + (fullEnd - start) * m_value;
QPainterPath path;
bool first = true;
for (int x = m_lineWidth / 2; x <= drawEnd; ++x) {
const auto theta =
m_frequency * 2 * M_PI * (x + m_startX) / len + phase;
const auto waveY = centerY + amplitude * qSin(theta);
if (first) {
path.moveTo(x, waveY);
first = false;
} else {
path.lineTo(x, waveY);
}
}
painter->drawPath(path);
}
void WavyLine::paintArc(QPainter* painter) {
if (m_fullAngleRad <= 0) {
return;
}
const auto amplitude = m_lineWidth * m_amplitudeMultiplier;
const auto cx = width() / 2.0;
const auto cy = height() / 2.0;
const auto radius =
m_radius > 0
? m_radius
: (qMin(width(), height()) - m_lineWidth - 2 * amplitude) / 2.0;
if (radius <= 0) {
return;
}
const auto phase = m_waveProgress * 2 * M_PI;
const auto arcLen = radius * m_fullAngleRad;
const auto len = m_fullLength > 0 ? m_fullLength : arcLen;
const auto drawAngleRad = m_fullAngleRad * m_value;
if (drawAngleRad <= 0) {
return;
}
const auto N = qMax(64, qCeil(radius * drawAngleRad));
const auto dTheta = drawAngleRad / N;
QPainterPath path;
for (int i = 0; i <= N; ++i) {
const auto theta = m_startAngleRad + i * dTheta;
const auto s = i * dTheta * radius;
const auto phi = m_frequency * 2 * M_PI * (s + m_startX) / len + phase;
const auto r = radius + amplitude * qSin(phi);
const auto px = cx + r * qCos(theta);
const auto py = cy + r * qSin(theta);
if (i == 0) {
path.moveTo(px, py);
} else {
path.lineTo(px, py);
}
}
painter->drawPath(path);
}
} // namespace ZShell::controls
+11 -9
View File
@@ -1,11 +1,14 @@
#pragma once
#include <qqmlintegration.h>
#include <qquickpainteditem.h>
#include <QCanvasPainterItem>
#include <QColor>
namespace ZShell::controls {
class WavyLine : public QQuickPaintedItem {
class WavyLineRenderer;
class WavyLine : public QCanvasPainterItem {
Q_OBJECT
QML_ELEMENT
@@ -82,9 +85,7 @@ class WavyLine : public QQuickPaintedItem {
[[nodiscard]] qreal value() const;
void setValue(qreal value);
void paint(QPainter* painter) override;
signals:
signals:
void lineWidthChanged();
void amplitudeMultiplierChanged();
void frequencyChanged();
@@ -98,9 +99,12 @@ class WavyLine : public QQuickPaintedItem {
void radiusChanged();
void valueChanged();
protected:
[[nodiscard]] QCanvasPainterItemRenderer* createItemRenderer()
const override;
private:
void paintLinear(QPainter* painter);
void paintArc(QPainter* painter);
friend class WavyLineRenderer;
int m_lineWidth;
qreal m_amplitudeMultiplier;
@@ -114,8 +118,6 @@ class WavyLine : public QQuickPaintedItem {
qreal m_fullAngle;
qreal m_radius;
qreal m_value;
qreal m_startAngleRad;
qreal m_fullAngleRad;
};
} // namespace ZShell::controls
@@ -0,0 +1,116 @@
#include "wavylinerenderer.hpp"
#include "wavyline.hpp"
#include <qcanvaspainter.h>
namespace ZShell::controls {
void WavyLineRenderer::synchronizeData(QCanvasPainterItem* item) {
auto* wavyLine = static_cast<WavyLine*>(item);
m_lineWidth = wavyLine->m_lineWidth;
m_amplitudeMultiplier = wavyLine->m_amplitudeMultiplier;
m_frequency = wavyLine->m_frequency;
m_startX = wavyLine->m_startX;
m_fullLength = wavyLine->m_fullLength;
m_color = wavyLine->m_color;
m_waveProgress = wavyLine->m_waveProgress;
m_pathType = wavyLine->m_pathType;
m_startAngle = wavyLine->m_startAngle;
m_fullAngle = wavyLine->m_fullAngle;
m_radius = wavyLine->m_radius;
m_value = wavyLine->m_value;
}
void WavyLineRenderer::paint(QCanvasPainter* painter) {
painter->clearRect(0, 0, width(), height());
painter->setStrokeStyle(m_color);
painter->setLineWidth(m_lineWidth);
painter->setLineCap(QCanvasPainter::LineCap::Round);
if (m_pathType == WavyLine::Arc) {
paintArc(painter);
} else {
paintLinear(painter);
}
}
void WavyLineRenderer::paintLinear(QCanvasPainter* painter) {
const auto amplitude = m_lineWidth * m_amplitudeMultiplier;
const auto phase = m_waveProgress * 2 * M_PI;
const auto centerY = height() / 2;
const auto len = m_fullLength > 0 ? m_fullLength : 1;
const auto start = m_lineWidth / 2.0;
const auto fullEnd = width() - m_lineWidth / 2.0;
const auto drawEnd = start + (fullEnd - start) * m_value;
painter->beginPath();
bool first = true;
for (int x = m_lineWidth / 2; x <= drawEnd; ++x) {
const auto theta =
m_frequency * 2 * M_PI * (x + m_startX) / len + phase;
const auto waveY = centerY + amplitude * qSin(theta);
if (first) {
painter->moveTo(x, waveY);
first = false;
} else {
painter->lineTo(x, waveY);
}
}
painter->stroke();
}
void WavyLineRenderer::paintArc(QCanvasPainter* painter) {
if (m_fullAngle <= 0) {
return;
}
const auto startAngleRad = m_startAngle * M_PI / 180.0;
const auto fullAngleRad = m_fullAngle * M_PI / 180.0;
const auto amplitude = m_lineWidth * m_amplitudeMultiplier;
const auto cx = width() / 2.0;
const auto cy = height() / 2.0;
const auto radius =
m_radius > 0
? m_radius
: (qMin(width(), height()) - m_lineWidth - 2 * amplitude) / 2.0;
if (radius <= 0) {
return;
}
const auto phase = m_waveProgress * 2 * M_PI;
const auto arcLen = radius * fullAngleRad;
const auto len = m_fullLength > 0 ? m_fullLength : arcLen;
const auto drawAngleRad = fullAngleRad * m_value;
if (drawAngleRad <= 0) {
return;
}
const auto N = qMax(64, qCeil(radius * drawAngleRad));
const auto dTheta = drawAngleRad / N;
painter->beginPath();
for (int i = 0; i <= N; ++i) {
const auto theta = startAngleRad + i * dTheta;
const auto s = i * dTheta * radius;
const auto phi = m_frequency * 2 * M_PI * (s + m_startX) / len + phase;
const auto r = radius + amplitude * qSin(phi);
const auto px = cx + r * qCos(theta);
const auto py = cy + r * qSin(theta);
if (i == 0) {
painter->moveTo(px, py);
} else {
painter->lineTo(px, py);
}
}
painter->stroke();
}
} // namespace ZShell::controls
@@ -0,0 +1,32 @@
#pragma once
#include <QCanvasPainterItemRenderer>
#include <QColor>
#include "wavyline.hpp"
namespace ZShell::controls {
class WavyLineRenderer final : public QCanvasPainterItemRenderer {
public:
void synchronizeData(QCanvasPainterItem* item) override;
void paint(QCanvasPainter* painter) override;
private:
void paintLinear(QCanvasPainter* painter);
void paintArc(QCanvasPainter* painter);
int m_lineWidth;
qreal m_amplitudeMultiplier;
int m_frequency;
qreal m_startX;
qreal m_fullLength;
QColor m_color;
qreal m_waveProgress;
WavyLine::PathType m_pathType;
qreal m_startAngle;
qreal m_fullAngle;
qreal m_radius;
qreal m_value;
};
} // namespace ZShell::controls