tidy&format(all): all files formatted and relevant warnings resolved
C++ / build (pull_request) Failing after 15s
Lint & Format (JS/TS) / lint-format (pull_request) Successful in 28s
Python / lint-format (pull_request) Successful in 45s
Python / test (pull_request) Successful in 38s
Lint & Format (Rust) / lint-format (pull_request) Successful in 1m45s

This commit is contained in:
2026-06-30 02:04:24 +02:00
parent fed9372b35
commit 467e44bb9f
85 changed files with 4139 additions and 3738 deletions
+3 -3
View File
@@ -1,15 +1,15 @@
--- ---
BasedOnStyle: LLVM BasedOnStyle: LLVM
AccessModifierOffset: 0 AccessModifierOffset: 0
AlignAfterOpenBracket: Align AlignAfterOpenBracket: AlwaysBreak
AlignConsecutiveAssignments: false AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false AlignConsecutiveDeclarations: false
AlignOperands: Align AlignOperands: Align
AllowAllParametersOfDeclarationOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: true
AllowShortFunctionsOnASingleLine: Inline AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: Never AllowShortIfStatementsOnASingleLine: WithoutElse
AlwaysBreakBeforeMultilineStrings: false AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes AlwaysBreakTemplateDeclarations: MultiLine
BinPackArguments: false BinPackArguments: false
BinPackParameters: false BinPackParameters: false
BreakBeforeBraces: Attach BreakBeforeBraces: Attach
+17 -21
View File
@@ -2,9 +2,7 @@
#include "blobinvertedrect.hpp" #include "blobinvertedrect.hpp"
#include "blobshape.hpp" #include "blobshape.hpp"
BlobGroup::BlobGroup(QObject* parent) BlobGroup::BlobGroup(QObject* parent) : QObject(parent) {}
: QObject(parent) {
}
BlobGroup::~BlobGroup() { BlobGroup::~BlobGroup() {
for (auto* shape : std::as_const(m_shapes)) for (auto* shape : std::as_const(m_shapes))
@@ -14,24 +12,21 @@ BlobGroup::~BlobGroup() {
} }
void BlobGroup::setSmoothing(qreal s) { void BlobGroup::setSmoothing(qreal s) {
if (qFuzzyCompare(m_smoothing, s)) if (qFuzzyCompare(m_smoothing, s)) return;
return;
m_smoothing = s; m_smoothing = s;
emit smoothingChanged(); emit smoothingChanged();
markDirty(); markDirty();
} }
void BlobGroup::setColor(const QColor& c) { void BlobGroup::setColor(const QColor& c) {
if (m_color == c) if (m_color == c) return;
return;
m_color = c; m_color = c;
emit colorChanged(); emit colorChanged();
markDirty(); markDirty();
} }
void BlobGroup::addShape(BlobShape* shape) { void BlobGroup::addShape(BlobShape* shape) {
if (!shape || m_shapes.contains(shape)) if (!shape || m_shapes.contains(shape)) return;
return;
m_shapes.append(shape); m_shapes.append(shape);
markDirty(); markDirty();
} }
@@ -42,15 +37,13 @@ void BlobGroup::removeShape(BlobShape* shape) {
} }
void BlobGroup::setInvertedRect(BlobInvertedRect* rect) { void BlobGroup::setInvertedRect(BlobInvertedRect* rect) {
if (m_invertedRect == rect) if (m_invertedRect == rect) return;
return;
m_invertedRect = rect; m_invertedRect = rect;
markDirty(); markDirty();
} }
void BlobGroup::clearInvertedRect(BlobInvertedRect* rect) { void BlobGroup::clearInvertedRect(BlobInvertedRect* rect) {
if (m_invertedRect != rect) if (m_invertedRect != rect) return;
return;
m_invertedRect = nullptr; m_invertedRect = nullptr;
markDirty(); markDirty();
} }
@@ -75,15 +68,19 @@ void BlobGroup::markShapeDirty(BlobShape* source) {
// Use cached padded rects to find spatial neighbors // Use cached padded rects to find spatial neighbors
const float pad = static_cast<float>(m_smoothing) * 2.0f; const float pad = static_cast<float>(m_smoothing) * 2.0f;
const QRectF srcRect(static_cast<double>(source->m_cachedPaddedX - pad), const QRectF srcRect(
static_cast<double>(source->m_cachedPaddedY - pad), static_cast<double>(source->m_cachedPaddedW + pad * 2.0f), static_cast<double>(source->m_cachedPaddedX - pad),
static_cast<double>(source->m_cachedPaddedY - pad),
static_cast<double>(source->m_cachedPaddedW + pad * 2.0f),
static_cast<double>(source->m_cachedPaddedH + pad * 2.0f)); static_cast<double>(source->m_cachedPaddedH + pad * 2.0f));
for (auto* shape : std::as_const(m_shapes)) { for (auto* shape : std::as_const(m_shapes)) {
if (shape == source) if (shape == source) continue;
continue; const QRectF otherRect(
const QRectF otherRect(static_cast<double>(shape->m_cachedPaddedX), static_cast<double>(shape->m_cachedPaddedY), static_cast<double>(shape->m_cachedPaddedX),
static_cast<double>(shape->m_cachedPaddedW), static_cast<double>(shape->m_cachedPaddedH)); static_cast<double>(shape->m_cachedPaddedY),
static_cast<double>(shape->m_cachedPaddedW),
static_cast<double>(shape->m_cachedPaddedH));
if (srcRect.intersects(otherRect)) { if (srcRect.intersects(otherRect)) {
shape->polish(); shape->polish();
shape->update(); shape->update();
@@ -97,8 +94,7 @@ void BlobGroup::markShapeDirty(BlobShape* source) {
} }
void BlobGroup::ensurePhysicsUpdated() { void BlobGroup::ensurePhysicsUpdated() {
if (m_physicsUpdated) if (m_physicsUpdated) return;
return;
m_physicsUpdated = true; m_physicsUpdated = true;
for (auto* shape : std::as_const(m_shapes)) for (auto* shape : std::as_const(m_shapes))
shape->updatePhysics(); shape->updatePhysics();
+6 -10
View File
@@ -11,22 +11,20 @@ class BlobInvertedRect;
class BlobGroup : public QObject { class BlobGroup : public QObject {
Q_OBJECT Q_OBJECT
QML_ELEMENT QML_ELEMENT
Q_PROPERTY(qreal smoothing READ smoothing WRITE setSmoothing NOTIFY smoothingChanged) Q_PROPERTY(
qreal smoothing READ smoothing WRITE setSmoothing NOTIFY
smoothingChanged)
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
public: public:
explicit BlobGroup(QObject* parent = nullptr); explicit BlobGroup(QObject* parent = nullptr);
~BlobGroup() override; ~BlobGroup() override;
[[nodiscard]] qreal smoothing() const { [[nodiscard]] qreal smoothing() const { return m_smoothing; }
return m_smoothing;
}
void setSmoothing(qreal s); void setSmoothing(qreal s);
[[nodiscard]] QColor color() const { [[nodiscard]] QColor color() const { return m_color; }
return m_color;
}
void setColor(const QColor& c); void setColor(const QColor& c);
@@ -36,9 +34,7 @@ void removeShape(BlobShape* shape);
void setInvertedRect(BlobInvertedRect* rect); void setInvertedRect(BlobInvertedRect* rect);
void clearInvertedRect(BlobInvertedRect* rect); void clearInvertedRect(BlobInvertedRect* rect);
[[nodiscard]] const QList<BlobShape*>& shapes() const { [[nodiscard]] const QList<BlobShape*>& shapes() const { return m_shapes; }
return m_shapes;
}
[[nodiscard]] BlobInvertedRect* invertedRect() const { [[nodiscard]] BlobInvertedRect* invertedRect() const {
return m_invertedRect; return m_invertedRect;
+29 -32
View File
@@ -5,12 +5,9 @@
#include <qsggeometry.h> #include <qsggeometry.h>
#include <qsgnode.h> #include <qsgnode.h>
#include <algorithm>
#include <cstring> #include <cstring>
BlobInvertedRect::BlobInvertedRect(QQuickItem* parent) BlobInvertedRect::BlobInvertedRect(QQuickItem* parent) : BlobShape(parent) {}
: BlobShape(parent) {
}
static void setFrameIndices(quint16* idx) { static void setFrameIndices(quint16* idx) {
// Top strip: 0-1-4, 1-5-4 // Top strip: 0-1-4, 1-5-4
@@ -43,7 +40,8 @@ static void setFrameIndices(quint16* idx) {
idx[23] = 7; idx[23] = 7;
} }
QSGNode* BlobInvertedRect::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) { QSGNode* BlobInvertedRect::updatePaintNode(
QSGNode* oldNode, UpdatePaintNodeData*) {
if (!m_group) { if (!m_group) {
delete oldNode; delete oldNode;
return nullptr; return nullptr;
@@ -71,8 +69,11 @@ QSGNode* BlobInvertedRect::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData
delete oldNode; delete oldNode;
node = new QSGGeometryNode; node = new QSGGeometryNode;
auto* geometry = auto* geometry = new QSGGeometry(
new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 8, 24, QSGGeometry::UnsignedShortType); QSGGeometry::defaultAttributes_TexturedPoint2D(),
8,
24,
QSGGeometry::UnsignedShortType);
geometry->setDrawingMode(QSGGeometry::DrawTriangles); geometry->setDrawingMode(QSGGeometry::DrawTriangles);
node->setGeometry(geometry); node->setGeometry(geometry);
node->setFlag(QSGNode::OwnsGeometry); node->setFlag(QSGNode::OwnsGeometry);
@@ -120,10 +121,17 @@ QSGNode* BlobInvertedRect::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData
material->m_color = m_group->color(); material->m_color = m_group->color();
material->m_hasInverted = m_cachedHasInverted ? 1 : 0; material->m_hasInverted = m_cachedHasInverted ? 1 : 0;
material->m_invertedRadius = m_cachedInvertedRadius; material->m_invertedRadius = m_cachedInvertedRadius;
memcpy(material->m_invertedOuter, m_cachedInvertedOuter, sizeof(m_cachedInvertedOuter)); memcpy(
memcpy(material->m_invertedInner, m_cachedInvertedInner, sizeof(m_cachedInvertedInner)); material->m_invertedOuter,
m_cachedInvertedOuter,
sizeof(m_cachedInvertedOuter));
memcpy(
material->m_invertedInner,
m_cachedInvertedInner,
sizeof(m_cachedInvertedInner));
const int count = static_cast<int>(qMin(m_cachedRects.size(), qsizetype(16))); const int count =
static_cast<int>(qMin(m_cachedRects.size(), qsizetype(16)));
material->m_rectCount = count; material->m_rectCount = count;
for (int i = 0; i < count; ++i) for (int i = 0; i < count; ++i)
material->m_rects[i] = m_cachedRects[i]; material->m_rects[i] = m_cachedRects[i];
@@ -134,52 +142,41 @@ QSGNode* BlobInvertedRect::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData
} }
BlobInvertedRect::~BlobInvertedRect() { BlobInvertedRect::~BlobInvertedRect() {
if (m_group) if (m_group) m_group->clearInvertedRect(this);
m_group->clearInvertedRect(this);
} }
void BlobInvertedRect::setBorderLeft(qreal v) { void BlobInvertedRect::setBorderLeft(qreal v) {
if (qFuzzyCompare(m_borderLeft, v)) if (qFuzzyCompare(m_borderLeft, v)) return;
return;
m_borderLeft = v; m_borderLeft = v;
emit borderLeftChanged(); emit borderLeftChanged();
if (m_group) if (m_group) m_group->markDirty();
m_group->markDirty();
} }
void BlobInvertedRect::setBorderRight(qreal v) { void BlobInvertedRect::setBorderRight(qreal v) {
if (qFuzzyCompare(m_borderRight, v)) if (qFuzzyCompare(m_borderRight, v)) return;
return;
m_borderRight = v; m_borderRight = v;
emit borderRightChanged(); emit borderRightChanged();
if (m_group) if (m_group) m_group->markDirty();
m_group->markDirty();
} }
void BlobInvertedRect::setBorderTop(qreal v) { void BlobInvertedRect::setBorderTop(qreal v) {
if (qFuzzyCompare(m_borderTop, v)) if (qFuzzyCompare(m_borderTop, v)) return;
return;
m_borderTop = v; m_borderTop = v;
emit borderTopChanged(); emit borderTopChanged();
if (m_group) if (m_group) m_group->markDirty();
m_group->markDirty();
} }
void BlobInvertedRect::setBorderBottom(qreal v) { void BlobInvertedRect::setBorderBottom(qreal v) {
if (qFuzzyCompare(m_borderBottom, v)) if (qFuzzyCompare(m_borderBottom, v)) return;
return;
m_borderBottom = v; m_borderBottom = v;
emit borderBottomChanged(); emit borderBottomChanged();
if (m_group) if (m_group) m_group->markDirty();
m_group->markDirty();
} }
void BlobInvertedRect::registerWithGroup() { void BlobInvertedRect::registerWithGroup() {
if (m_group) if (m_group) m_group->setInvertedRect(this);
m_group->setInvertedRect(this);
} }
void BlobInvertedRect::unregisterFromGroup() { void BlobInvertedRect::unregisterFromGroup() {
if (m_group) if (m_group) m_group->clearInvertedRect(this);
m_group->clearInvertedRect(this);
} }
+17 -19
View File
@@ -7,36 +7,36 @@
class BlobInvertedRect : public BlobShape { class BlobInvertedRect : public BlobShape {
Q_OBJECT Q_OBJECT
QML_ELEMENT QML_ELEMENT
Q_PROPERTY(qreal borderLeft READ borderLeft WRITE setBorderLeft NOTIFY borderLeftChanged) Q_PROPERTY(
Q_PROPERTY(qreal borderRight READ borderRight WRITE setBorderRight NOTIFY borderRightChanged) qreal borderLeft READ borderLeft WRITE setBorderLeft NOTIFY
Q_PROPERTY(qreal borderTop READ borderTop WRITE setBorderTop NOTIFY borderTopChanged) borderLeftChanged)
Q_PROPERTY(qreal borderBottom READ borderBottom WRITE setBorderBottom NOTIFY borderBottomChanged) Q_PROPERTY(
qreal borderRight READ borderRight WRITE setBorderRight NOTIFY
borderRightChanged)
Q_PROPERTY(
qreal borderTop READ borderTop WRITE setBorderTop NOTIFY
borderTopChanged)
Q_PROPERTY(
qreal borderBottom READ borderBottom WRITE setBorderBottom NOTIFY
borderBottomChanged)
public: public:
explicit BlobInvertedRect(QQuickItem* parent = nullptr); explicit BlobInvertedRect(QQuickItem* parent = nullptr);
~BlobInvertedRect() override; ~BlobInvertedRect() override;
[[nodiscard]] qreal borderLeft() const { [[nodiscard]] qreal borderLeft() const { return m_borderLeft; }
return m_borderLeft;
}
void setBorderLeft(qreal v); void setBorderLeft(qreal v);
[[nodiscard]] qreal borderRight() const { [[nodiscard]] qreal borderRight() const { return m_borderRight; }
return m_borderRight;
}
void setBorderRight(qreal v); void setBorderRight(qreal v);
[[nodiscard]] qreal borderTop() const { [[nodiscard]] qreal borderTop() const { return m_borderTop; }
return m_borderTop;
}
void setBorderTop(qreal v); void setBorderTop(qreal v);
[[nodiscard]] qreal borderBottom() const { [[nodiscard]] qreal borderBottom() const { return m_borderBottom; }
return m_borderBottom;
}
void setBorderBottom(qreal v); void setBorderBottom(qreal v);
@@ -47,9 +47,7 @@ void borderTopChanged();
void borderBottomChanged(); void borderBottomChanged();
protected: protected:
[[nodiscard]] bool isInvertedRect() const override { [[nodiscard]] bool isInvertedRect() const override { return true; }
return true;
}
QSGNode* updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) override; QSGNode* updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) override;
+15 -12
View File
@@ -1,8 +1,11 @@
#include "blobmaterial.hpp" #include "blobmaterial.hpp"
#include <math.h>
#include <cstring> #include <cstring>
static_assert(sizeof(decltype(BlobRectData::excludeMask)) == sizeof(float), static_assert(
sizeof(decltype(BlobRectData::excludeMask)) == sizeof(float),
"BlobMaterial packs excludeMask into a float slot via memcpy"); "BlobMaterial packs excludeMask into a float slot via memcpy");
QSGMaterialType* BlobMaterial::type() const { QSGMaterialType* BlobMaterial::type() const {
@@ -10,15 +13,14 @@ QSGMaterialType* BlobMaterial::type() const {
return &s_type; return &s_type;
} }
QSGMaterialShader* BlobMaterial::createShader(QSGRendererInterface::RenderMode) const { QSGMaterialShader* BlobMaterial::createShader(
QSGRendererInterface::RenderMode) const {
return new BlobMaterialShader; return new BlobMaterialShader;
} }
int BlobMaterial::compare(const QSGMaterial* other) const { int BlobMaterial::compare(const QSGMaterial* other) const {
if (this < other) if (this < other) return -1;
return -1; if (this > other) return 1;
if (this > other)
return 1;
return 0; return 0;
} }
@@ -27,7 +29,8 @@ BlobMaterialShader::BlobMaterialShader() {
setShaderFileName(FragmentStage, QStringLiteral(":/shaders/blob.frag.qsb")); setShaderFileName(FragmentStage, QStringLiteral(":/shaders/blob.frag.qsb"));
} }
bool BlobMaterialShader::updateUniformData(RenderState& state, QSGMaterial* newMaterial, QSGMaterial* oldMaterial) { bool BlobMaterialShader::updateUniformData(
RenderState& state, QSGMaterial* newMaterial, QSGMaterial* oldMaterial) {
Q_UNUSED(oldMaterial); Q_UNUSED(oldMaterial);
auto* mat = static_cast<BlobMaterial*>(newMaterial); auto* mat = static_cast<BlobMaterial*>(newMaterial);
QByteArray* buf = state.uniformData(); QByteArray* buf = state.uniformData();
@@ -59,10 +62,10 @@ bool BlobMaterialShader::updateUniformData(RenderState& state, QSGMaterial* newM
// Color as vec4 (offset 96, 16 bytes) // Color as vec4 (offset 96, 16 bytes)
const float color[4] = { const float color[4] = {
static_cast<float>(mat->m_color.redF()), mat->m_color.redF(),
static_cast<float>(mat->m_color.greenF()), mat->m_color.greenF(),
static_cast<float>(mat->m_color.blueF()), mat->m_color.blueF(),
static_cast<float>(mat->m_color.alphaF()), mat->m_color.alphaF(),
}; };
memcpy(buf->data() + 96, color, 16); memcpy(buf->data() + 96, color, 16);
@@ -86,7 +89,7 @@ bool BlobMaterialShader::updateUniformData(RenderState& state, QSGMaterial* newM
const auto& r = mat->m_rects[i]; const auto& r = mat->m_rects[i];
const int base = 160 + i * 80; const int base = 160 + i * 80;
// Pack excludeMask into props.x via bit-cast (read in shader with floatBitsToInt) // Pack excludeMask into props.x via bit-cast (read in shader with floatBitsToInt)
float maskAsFloat; float maskAsFloat = NAN;
memcpy(&maskAsFloat, &r.excludeMask, sizeof(float)); memcpy(&maskAsFloat, &r.excludeMask, sizeof(float));
const float d0[4] = {r.cx, r.cy, r.hw, r.hh}; const float d0[4] = {r.cx, r.cy, r.hw, r.hh};
const float d1[4] = {maskAsFloat, r.offsetX, r.offsetY, r.minEig}; const float d1[4] = {maskAsFloat, r.offsetX, r.offsetY, r.minEig};
+6 -2
View File
@@ -22,7 +22,8 @@ struct BlobRectData {
class BlobMaterial : public QSGMaterial { class BlobMaterial : public QSGMaterial {
public: public:
[[nodiscard]] QSGMaterialType* type() const override; [[nodiscard]] QSGMaterialType* type() const override;
[[nodiscard]] QSGMaterialShader* createShader(QSGRendererInterface::RenderMode) const override; [[nodiscard]] QSGMaterialShader* createShader(
QSGRendererInterface::RenderMode) const override;
int compare(const QSGMaterial* other) const override; int compare(const QSGMaterial* other) const override;
float m_paddedX = 0; float m_paddedX = 0;
@@ -43,5 +44,8 @@ BlobRectData m_rects[16] = {};
class BlobMaterialShader : public QSGMaterialShader { class BlobMaterialShader : public QSGMaterialShader {
public: public:
BlobMaterialShader(); BlobMaterialShader();
bool updateUniformData(RenderState& state, QSGMaterial* newMaterial, QSGMaterial* oldMaterial) override; bool updateUniformData(
RenderState& state,
QSGMaterial* newMaterial,
QSGMaterial* oldMaterial) override;
}; };
+65 -55
View File
@@ -4,19 +4,18 @@
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
BlobRect::BlobRect(QQuickItem* parent) BlobRect::BlobRect(QQuickItem* parent) : BlobShape(parent) {}
: BlobShape(parent) {
}
BlobRect::~BlobRect() { BlobRect::~BlobRect() {
if (m_group) if (m_group) m_group->removeShape(this);
m_group->removeShape(this);
} }
void BlobRect::updatePolish() { void BlobRect::updatePolish() {
if (m_physicsActive) { if (m_physicsActive) {
float totalDelta = std::abs(m_dm00 - 1.0f) + std::abs(m_dm01) + std::abs(m_dm11 - 1.0f); float totalDelta = std::abs(m_dm00 - 1.0f) + std::abs(m_dm01) +
float totalVel = std::abs(m_dmVel00) + std::abs(m_dmVel01) + std::abs(m_dmVel11); std::abs(m_dm11 - 1.0f);
float totalVel =
std::abs(m_dmVel00) + std::abs(m_dmVel01) + std::abs(m_dmVel11);
if (totalDelta < 0.004f && totalVel < 0.05f) { if (totalDelta < 0.004f && totalVel < 0.05f) {
m_dm00 = 1.0f; m_dm00 = 1.0f;
@@ -32,8 +31,7 @@ void BlobRect::updatePolish() {
QMetaObject::invokeMethod( QMetaObject::invokeMethod(
this, this,
[this]() { [this]() {
if (m_group) if (m_group) m_group->markDirty();
m_group->markDirty();
}, },
Qt::QueuedConnection); Qt::QueuedConnection);
} }
@@ -41,8 +39,7 @@ void BlobRect::updatePolish() {
QMetaObject::invokeMethod( QMetaObject::invokeMethod(
this, this,
[this]() { [this]() {
if (m_physicsActive && m_group) if (m_physicsActive && m_group) m_group->markDirty();
m_group->markDirty();
}, },
Qt::QueuedConnection); Qt::QueuedConnection);
} }
@@ -64,20 +61,20 @@ void BlobRect::updatePhysics() {
const float dt = static_cast<float>(m_elapsed.restart()) / 1000.0f; const float dt = static_cast<float>(m_elapsed.restart()) / 1000.0f;
if (dt > 0.1f || dt < 0.001f) { if (dt > 0.1f || dt < 0.001f) {
m_prevScenePos = scenePos; m_prevScenePos = scenePos;
if (m_physicsActive) if (m_physicsActive) checkAtRest(0.0f);
checkAtRest(0.0f);
return; return;
} }
const float velX = static_cast<float>(scenePos.x() - m_prevScenePos.x()) / dt; const float velX =
const float velY = static_cast<float>(scenePos.y() - m_prevScenePos.y()) / dt; static_cast<float>(scenePos.x() - m_prevScenePos.x()) / dt;
const float velY =
static_cast<float>(scenePos.y() - m_prevScenePos.y()) / dt;
m_prevScenePos = scenePos; m_prevScenePos = scenePos;
const float speed = std::sqrt(velX * velX + velY * velY); const float speed = std::sqrt(velX * velX + velY * velY);
if (!m_physicsActive) { if (!m_physicsActive) {
if (speed < 5.0f) if (speed < 5.0f) return;
return;
m_physicsActive = true; m_physicsActive = true;
} }
@@ -89,7 +86,8 @@ void BlobRect::updatePhysics() {
float target11 = 1.0f; float target11 = 1.0f;
if (speed > 5.0f) { if (speed > 5.0f) {
const float targetStretch = 1.0f + std::min(speed * kStretchFactor, kMaxStretch); const float targetStretch =
1.0f + std::min(speed * kStretchFactor, kMaxStretch);
const float targetCompress = 1.0f / targetStretch; const float targetCompress = 1.0f / targetStretch;
const float cosA = velX / speed; const float cosA = velX / speed;
@@ -106,19 +104,23 @@ void BlobRect::updatePhysics() {
const float kStiffness = static_cast<float>(m_stiffness); const float kStiffness = static_cast<float>(m_stiffness);
const float kDamping = static_cast<float>(m_damping); const float kDamping = static_cast<float>(m_damping);
const float accel00 = -kStiffness * (m_dm00 - target00) - kDamping * m_dmVel00; const float accel00 =
-kStiffness * (m_dm00 - target00) - kDamping * m_dmVel00;
m_dmVel00 += accel00 * dt; m_dmVel00 += accel00 * dt;
m_dm00 += m_dmVel00 * dt; m_dm00 += m_dmVel00 * dt;
const float accel01 = -kStiffness * (m_dm01 - target01) - kDamping * m_dmVel01; const float accel01 =
-kStiffness * (m_dm01 - target01) - kDamping * m_dmVel01;
m_dmVel01 += accel01 * dt; m_dmVel01 += accel01 * dt;
m_dm01 += m_dmVel01 * dt; m_dm01 += m_dmVel01 * dt;
const float accel11 = -kStiffness * (m_dm11 - target11) - kDamping * m_dmVel11; const float accel11 =
-kStiffness * (m_dm11 - target11) - kDamping * m_dmVel11;
m_dmVel11 += accel11 * dt; m_dmVel11 += accel11 * dt;
m_dm11 += m_dmVel11 * dt; m_dm11 += m_dmVel11 * dt;
m_deformMatrix = QMatrix4x4(m_dm00, m_dm01, 0, 0, m_dm01, m_dm11, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); m_deformMatrix = QMatrix4x4(
m_dm00, m_dm01, 0, 0, m_dm01, m_dm11, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
emit rawDeformMatrixChanged(); emit rawDeformMatrixChanged();
updateCenteredDeformMatrix(); updateCenteredDeformMatrix();
@@ -129,8 +131,7 @@ void BlobRect::setTopLeftRadius(qreal r) {
if (!qFuzzyCompare(m_topLeftRadius, r)) { if (!qFuzzyCompare(m_topLeftRadius, r)) {
m_topLeftRadius = r; m_topLeftRadius = r;
emit topLeftRadiusChanged(); emit topLeftRadiusChanged();
if (m_group) if (m_group) m_group->markDirty();
m_group->markDirty();
} }
} }
@@ -138,8 +139,7 @@ void BlobRect::setTopRightRadius(qreal r) {
if (!qFuzzyCompare(m_topRightRadius, r)) { if (!qFuzzyCompare(m_topRightRadius, r)) {
m_topRightRadius = r; m_topRightRadius = r;
emit topRightRadiusChanged(); emit topRightRadiusChanged();
if (m_group) if (m_group) m_group->markDirty();
m_group->markDirty();
} }
} }
@@ -147,8 +147,7 @@ void BlobRect::setBottomLeftRadius(qreal r) {
if (!qFuzzyCompare(m_bottomLeftRadius, r)) { if (!qFuzzyCompare(m_bottomLeftRadius, r)) {
m_bottomLeftRadius = r; m_bottomLeftRadius = r;
emit bottomLeftRadiusChanged(); emit bottomLeftRadiusChanged();
if (m_group) if (m_group) m_group->markDirty();
m_group->markDirty();
} }
} }
@@ -156,38 +155,51 @@ void BlobRect::setBottomRightRadius(qreal r) {
if (!qFuzzyCompare(m_bottomRightRadius, r)) { if (!qFuzzyCompare(m_bottomRightRadius, r)) {
m_bottomRightRadius = r; m_bottomRightRadius = r;
emit bottomRightRadiusChanged(); emit bottomRightRadiusChanged();
if (m_group) if (m_group) m_group->markDirty();
m_group->markDirty();
} }
} }
void BlobRect::cornerRadii(float out[4]) const { void BlobRect::cornerRadii(float out[4]) const {
const auto maxR = static_cast<float>(std::min(width(), height())) * 0.5f; const auto maxR = static_cast<float>(std::min(width(), height())) * 0.5f;
const auto base = std::min(static_cast<float>(m_radius), maxR); const auto base = std::min(static_cast<float>(m_radius), maxR);
out[0] = std::min(m_topRightRadius >= 0 ? static_cast<float>(m_topRightRadius) : base, maxR); out[0] = std::min(
out[1] = std::min(m_bottomRightRadius >= 0 ? static_cast<float>(m_bottomRightRadius) : base, maxR); m_topRightRadius >= 0 ? static_cast<float>(m_topRightRadius) : base,
out[2] = std::min(m_bottomLeftRadius >= 0 ? static_cast<float>(m_bottomLeftRadius) : base, maxR); maxR);
out[3] = std::min(m_topLeftRadius >= 0 ? static_cast<float>(m_topLeftRadius) : base, maxR); out[1] = std::min(
m_bottomRightRadius >= 0 ? static_cast<float>(m_bottomRightRadius)
: base,
maxR);
out[2] = std::min(
m_bottomLeftRadius >= 0 ? static_cast<float>(m_bottomLeftRadius) : base,
maxR);
out[3] = std::min(
m_topLeftRadius >= 0 ? static_cast<float>(m_topLeftRadius) : base,
maxR);
} }
bool BlobRect::isExcluded(const BlobShape* other) const { bool BlobRect::isExcluded(const BlobShape* other) const {
for (const auto& ptr : m_exclude) { for (const auto& ptr : m_exclude) {
if (ptr == other) if (ptr == other) return true;
return true;
} }
return false; return false;
} }
QQmlListProperty<BlobRect> BlobRect::exclude() { QQmlListProperty<BlobRect> BlobRect::exclude() {
return QQmlListProperty<BlobRect>( return QQmlListProperty<BlobRect>(
this, nullptr, &excludeAppend, &excludeCount, &excludeAt, &excludeClear, &excludeReplace, &excludeRemoveLast); this,
nullptr,
&excludeAppend,
&excludeCount,
&excludeAt,
&excludeClear,
&excludeReplace,
&excludeRemoveLast);
} }
void BlobRect::excludeAppend(QQmlListProperty<BlobRect>* prop, BlobRect* rect) { void BlobRect::excludeAppend(QQmlListProperty<BlobRect>* prop, BlobRect* rect) {
auto* self = static_cast<BlobRect*>(prop->object); auto* self = static_cast<BlobRect*>(prop->object);
self->m_exclude.append(rect); self->m_exclude.append(rect);
if (self->m_group) if (self->m_group) self->m_group->markDirty();
self->m_group->markDirty();
emit self->excludeChanged(); emit self->excludeChanged();
} }
@@ -196,44 +208,43 @@ qsizetype BlobRect::excludeCount(QQmlListProperty<BlobRect>* prop) {
return self->m_exclude.size(); return self->m_exclude.size();
} }
BlobRect* BlobRect::excludeAt(QQmlListProperty<BlobRect>* prop, qsizetype index) { BlobRect* BlobRect::excludeAt(
QQmlListProperty<BlobRect>* prop, qsizetype index) {
auto* self = static_cast<BlobRect*>(prop->object); auto* self = static_cast<BlobRect*>(prop->object);
return self->m_exclude.at(index); return self->m_exclude.at(index);
} }
void BlobRect::excludeClear(QQmlListProperty<BlobRect>* prop) { void BlobRect::excludeClear(QQmlListProperty<BlobRect>* prop) {
auto* self = static_cast<BlobRect*>(prop->object); auto* self = static_cast<BlobRect*>(prop->object);
if (self->m_exclude.isEmpty()) if (self->m_exclude.isEmpty()) return;
return;
self->m_exclude.clear(); self->m_exclude.clear();
if (self->m_group) if (self->m_group) self->m_group->markDirty();
self->m_group->markDirty();
emit self->excludeChanged(); emit self->excludeChanged();
} }
void BlobRect::excludeReplace(QQmlListProperty<BlobRect>* prop, qsizetype index, BlobRect* rect) { void BlobRect::excludeReplace(
QQmlListProperty<BlobRect>* prop, qsizetype index, BlobRect* rect) {
auto* self = static_cast<BlobRect*>(prop->object); auto* self = static_cast<BlobRect*>(prop->object);
self->m_exclude[index] = rect; self->m_exclude[index] = rect;
if (self->m_group) if (self->m_group) self->m_group->markDirty();
self->m_group->markDirty();
emit self->excludeChanged(); emit self->excludeChanged();
} }
void BlobRect::excludeRemoveLast(QQmlListProperty<BlobRect>* prop) { void BlobRect::excludeRemoveLast(QQmlListProperty<BlobRect>* prop) {
auto* self = static_cast<BlobRect*>(prop->object); auto* self = static_cast<BlobRect*>(prop->object);
if (self->m_exclude.isEmpty()) if (self->m_exclude.isEmpty()) return;
return;
self->m_exclude.removeLast(); self->m_exclude.removeLast();
if (self->m_group) if (self->m_group) self->m_group->markDirty();
self->m_group->markDirty();
emit self->excludeChanged(); emit self->excludeChanged();
} }
void BlobRect::checkAtRest(float speed) { void BlobRect::checkAtRest(float speed) {
constexpr float kEpsilon = 0.002f; constexpr float kEpsilon = 0.002f;
const bool atRest = std::abs(m_dm00 - 1.0f) < kEpsilon && std::abs(m_dm01) < kEpsilon && const bool atRest =
std::abs(m_dm00 - 1.0f) < kEpsilon && std::abs(m_dm01) < kEpsilon &&
std::abs(m_dm11 - 1.0f) < kEpsilon && std::abs(m_dmVel00) < kEpsilon && std::abs(m_dm11 - 1.0f) < kEpsilon && std::abs(m_dmVel00) < kEpsilon &&
std::abs(m_dmVel01) < kEpsilon && std::abs(m_dmVel11) < kEpsilon && speed < 5.0f; std::abs(m_dmVel01) < kEpsilon && std::abs(m_dmVel11) < kEpsilon &&
speed < 5.0f;
if (atRest) { if (atRest) {
m_dm00 = 1.0f; m_dm00 = 1.0f;
@@ -251,8 +262,7 @@ void BlobRect::checkAtRest(float speed) {
QMetaObject::invokeMethod( QMetaObject::invokeMethod(
this, this,
[this]() { [this]() {
if (m_group) if (m_group) m_group->markDirty();
m_group->markDirty();
}, },
Qt::QueuedConnection); Qt::QueuedConnection);
} }
+30 -28
View File
@@ -10,23 +10,33 @@
class BlobRect : public BlobShape { class BlobRect : public BlobShape {
Q_OBJECT Q_OBJECT
QML_ELEMENT QML_ELEMENT
Q_PROPERTY(qreal stiffness READ stiffness WRITE setStiffness NOTIFY stiffnessChanged)
Q_PROPERTY(qreal damping READ damping WRITE setDamping NOTIFY dampingChanged)
Q_PROPERTY(qreal deformScale READ deformScale WRITE setDeformScale NOTIFY deformScaleChanged)
Q_PROPERTY(QQmlListProperty<BlobRect> exclude READ exclude NOTIFY excludeChanged)
Q_PROPERTY(qreal topLeftRadius READ topLeftRadius WRITE setTopLeftRadius NOTIFY topLeftRadiusChanged)
Q_PROPERTY(qreal topRightRadius READ topRightRadius WRITE setTopRightRadius NOTIFY topRightRadiusChanged)
Q_PROPERTY(qreal bottomLeftRadius READ bottomLeftRadius WRITE setBottomLeftRadius NOTIFY bottomLeftRadiusChanged)
Q_PROPERTY( Q_PROPERTY(
qreal bottomRightRadius READ bottomRightRadius WRITE setBottomRightRadius NOTIFY bottomRightRadiusChanged) qreal stiffness READ stiffness WRITE setStiffness NOTIFY
stiffnessChanged)
Q_PROPERTY(qreal damping READ damping WRITE setDamping NOTIFY dampingChanged)
Q_PROPERTY(
qreal deformScale READ deformScale WRITE setDeformScale NOTIFY
deformScaleChanged)
Q_PROPERTY(
QQmlListProperty<BlobRect> exclude READ exclude NOTIFY excludeChanged)
Q_PROPERTY(
qreal topLeftRadius READ topLeftRadius WRITE setTopLeftRadius NOTIFY
topLeftRadiusChanged)
Q_PROPERTY(
qreal topRightRadius READ topRightRadius WRITE setTopRightRadius NOTIFY
topRightRadiusChanged)
Q_PROPERTY(
qreal bottomLeftRadius READ bottomLeftRadius WRITE setBottomLeftRadius
NOTIFY bottomLeftRadiusChanged)
Q_PROPERTY(
qreal bottomRightRadius READ bottomRightRadius WRITE
setBottomRightRadius NOTIFY bottomRightRadiusChanged)
public: public:
explicit BlobRect(QQuickItem* parent = nullptr); explicit BlobRect(QQuickItem* parent = nullptr);
~BlobRect() override; ~BlobRect() override;
[[nodiscard]] qreal stiffness() const { [[nodiscard]] qreal stiffness() const { return m_stiffness; }
return m_stiffness;
}
void setStiffness(qreal s) { void setStiffness(qreal s) {
if (!qFuzzyCompare(m_stiffness, s)) { if (!qFuzzyCompare(m_stiffness, s)) {
@@ -35,9 +45,7 @@ void setStiffness(qreal s) {
} }
} }
[[nodiscard]] qreal damping() const { [[nodiscard]] qreal damping() const { return m_damping; }
return m_damping;
}
void setDamping(qreal d) { void setDamping(qreal d) {
if (!qFuzzyCompare(m_damping, d)) { if (!qFuzzyCompare(m_damping, d)) {
@@ -46,9 +54,7 @@ void setDamping(qreal d) {
} }
} }
[[nodiscard]] qreal deformScale() const { [[nodiscard]] qreal deformScale() const { return m_deformScale; }
return m_deformScale;
}
void setDeformScale(qreal s) { void setDeformScale(qreal s) {
if (!qFuzzyCompare(m_deformScale, s)) { if (!qFuzzyCompare(m_deformScale, s)) {
@@ -62,21 +68,15 @@ QQmlListProperty<BlobRect> exclude();
bool isExcluded(const BlobShape* other) const override; bool isExcluded(const BlobShape* other) const override;
void cornerRadii(float out[4]) const override; void cornerRadii(float out[4]) const override;
[[nodiscard]] qreal topLeftRadius() const { [[nodiscard]] qreal topLeftRadius() const { return m_topLeftRadius; }
return m_topLeftRadius;
}
void setTopLeftRadius(qreal r); void setTopLeftRadius(qreal r);
[[nodiscard]] qreal topRightRadius() const { [[nodiscard]] qreal topRightRadius() const { return m_topRightRadius; }
return m_topRightRadius;
}
void setTopRightRadius(qreal r); void setTopRightRadius(qreal r);
[[nodiscard]] qreal bottomLeftRadius() const { [[nodiscard]] qreal bottomLeftRadius() const { return m_bottomLeftRadius; }
return m_bottomLeftRadius;
}
void setBottomLeftRadius(qreal r); void setBottomLeftRadius(qreal r);
@@ -133,8 +133,10 @@ QList<QPointer<BlobRect> > m_exclude;
static void excludeAppend(QQmlListProperty<BlobRect>* prop, BlobRect* rect); static void excludeAppend(QQmlListProperty<BlobRect>* prop, BlobRect* rect);
static qsizetype excludeCount(QQmlListProperty<BlobRect>* prop); static qsizetype excludeCount(QQmlListProperty<BlobRect>* prop);
static BlobRect* excludeAt(QQmlListProperty<BlobRect>* prop, qsizetype index); static BlobRect* excludeAt(
QQmlListProperty<BlobRect>* prop, qsizetype index);
static void excludeClear(QQmlListProperty<BlobRect>* prop); static void excludeClear(QQmlListProperty<BlobRect>* prop);
static void excludeReplace(QQmlListProperty<BlobRect>* prop, qsizetype index, BlobRect* rect); static void excludeReplace(
QQmlListProperty<BlobRect>* prop, qsizetype index, BlobRect* rect);
static void excludeRemoveLast(QQmlListProperty<BlobRect>* prop); static void excludeRemoveLast(QQmlListProperty<BlobRect>* prop);
}; };
+131 -73
View File
@@ -18,7 +18,8 @@ static float deformPadding(const QMatrix4x4& dm, float hw, float hh) {
return std::max(extraX, extraY); return std::max(extraX, extraY);
} }
static float cpuSdBox(float px, float py, float cx, float cy, float hw, float hh) { static float cpuSdBox(
float px, float py, float cx, float cy, float hw, float hh) {
const float dx = std::abs(px - cx) - hw; const float dx = std::abs(px - cx) - hw;
const float dy = std::abs(py - cy) - hh; const float dy = std::abs(py - cy) - hh;
const float mdx = std::max(dx, 0.0f); const float mdx = std::max(dx, 0.0f);
@@ -31,54 +32,53 @@ static float cpuSmoothstep(float edge0, float edge1, float x) {
return t * t * (3.0f - 2.0f * t); return t * t * (3.0f - 2.0f * t);
} }
BlobShape::BlobShape(QQuickItem* parent) BlobShape::BlobShape(QQuickItem* parent) : QQuickItem(parent) {
: QQuickItem(parent) {
setFlag(ItemHasContents); setFlag(ItemHasContents);
} }
void BlobShape::setGroup(BlobGroup* g) { void BlobShape::setGroup(BlobGroup* g) {
if (m_group == g) if (m_group == g) return;
return; if (m_group && isComponentComplete()) unregisterFromGroup();
if (m_group && isComponentComplete())
unregisterFromGroup();
m_group = g; m_group = g;
if (m_group && isComponentComplete()) if (m_group && isComponentComplete()) registerWithGroup();
registerWithGroup();
emit groupChanged(); emit groupChanged();
if (m_group) if (m_group) m_group->markDirty();
m_group->markDirty();
} }
void BlobShape::setRadius(qreal r) { void BlobShape::setRadius(qreal r) {
if (qFuzzyCompare(m_radius, r)) if (qFuzzyCompare(m_radius, r)) return;
return;
m_radius = r; m_radius = r;
emit radiusChanged(); emit radiusChanged();
if (m_group) if (m_group) m_group->markDirty();
m_group->markDirty();
} }
void BlobShape::componentComplete() { void BlobShape::componentComplete() {
QQuickItem::componentComplete(); QQuickItem::componentComplete();
if (m_group) if (m_group) registerWithGroup();
registerWithGroup();
} }
void BlobShape::geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) { void BlobShape::geometryChange(
const QRectF& newGeometry, const QRectF& oldGeometry) {
QQuickItem::geometryChange(newGeometry, oldGeometry); QQuickItem::geometryChange(newGeometry, oldGeometry);
updateCenteredDeformMatrix(); updateCenteredDeformMatrix();
if (m_group) { if (m_group) {
m_pendingDx += static_cast<float>(newGeometry.x() - oldGeometry.x()); m_pendingDx += static_cast<float>(newGeometry.x() - oldGeometry.x());
m_pendingDy += static_cast<float>(newGeometry.y() - oldGeometry.y()); m_pendingDy += static_cast<float>(newGeometry.y() - oldGeometry.y());
m_pendingDw += static_cast<float>(newGeometry.width() - oldGeometry.width()); m_pendingDw +=
m_pendingDh += static_cast<float>(newGeometry.height() - oldGeometry.height()); static_cast<float>(newGeometry.width() - oldGeometry.width());
m_pendingDh +=
static_cast<float>(newGeometry.height() - oldGeometry.height());
const float deformMag = std::abs(m_deformMatrix(0, 0) - 1.0f) + std::abs(m_deformMatrix(0, 1)) + const float deformMag = std::abs(m_deformMatrix(0, 0) - 1.0f) +
std::abs(m_deformMatrix(1, 0)) + std::abs(m_deformMatrix(1, 1) - 1.0f); std::abs(m_deformMatrix(0, 1)) +
std::abs(m_deformMatrix(1, 0)) +
std::abs(m_deformMatrix(1, 1) - 1.0f);
const float syncThreshold = deformMag > 0.001f ? 0.05f : 0.5f; const float syncThreshold = deformMag > 0.001f ? 0.05f : 0.5f;
if (std::abs(m_pendingDx) > syncThreshold || std::abs(m_pendingDy) > syncThreshold || if (std::abs(m_pendingDx) > syncThreshold ||
std::abs(m_pendingDw) > syncThreshold || std::abs(m_pendingDh) > syncThreshold) { std::abs(m_pendingDy) > syncThreshold ||
std::abs(m_pendingDw) > syncThreshold ||
std::abs(m_pendingDh) > syncThreshold) {
m_pendingDx = 0; m_pendingDx = 0;
m_pendingDy = 0; m_pendingDy = 0;
m_pendingDw = 0; m_pendingDw = 0;
@@ -111,18 +111,15 @@ void BlobShape::cornerRadii(float out[4]) const {
} }
void BlobShape::registerWithGroup() { void BlobShape::registerWithGroup() {
if (m_group) if (m_group) m_group->addShape(this);
m_group->addShape(this);
} }
void BlobShape::unregisterFromGroup() { void BlobShape::unregisterFromGroup() {
if (m_group) if (m_group) m_group->removeShape(this);
m_group->removeShape(this);
} }
void BlobShape::updatePolish() { void BlobShape::updatePolish() {
if (!m_group) if (!m_group) return;
return;
m_group->ensurePhysicsUpdated(); m_group->ensurePhysicsUpdated();
@@ -144,27 +141,30 @@ void BlobShape::updatePolish() {
m_cachedPaddedY = static_cast<float>(scenePos.y()) - totalPad; m_cachedPaddedY = static_cast<float>(scenePos.y()) - totalPad;
m_cachedPaddedW = static_cast<float>(width()) + 2.0f * totalPad; m_cachedPaddedW = static_cast<float>(width()) + 2.0f * totalPad;
m_cachedPaddedH = static_cast<float>(height()) + 2.0f * totalPad; m_cachedPaddedH = static_cast<float>(height()) + 2.0f * totalPad;
m_localPaddedRect = QRectF(static_cast<double>(-totalPad), static_cast<double>(-totalPad), m_localPaddedRect = QRectF(
width() + 2.0 * static_cast<double>(totalPad), height() + 2.0 * static_cast<double>(totalPad)); static_cast<double>(-totalPad),
static_cast<double>(-totalPad),
width() + 2.0 * static_cast<double>(totalPad),
height() + 2.0 * static_cast<double>(totalPad));
} }
m_cachedRects.clear(); m_cachedRects.clear();
m_cachedMyIndex = -2; m_cachedMyIndex = -2;
const QRectF myPadded(static_cast<double>(m_cachedPaddedX), static_cast<double>(m_cachedPaddedY), const QRectF myPadded(
static_cast<double>(m_cachedPaddedW), static_cast<double>(m_cachedPaddedH)); static_cast<double>(m_cachedPaddedX),
static_cast<double>(m_cachedPaddedY),
static_cast<double>(m_cachedPaddedW),
static_cast<double>(m_cachedPaddedH));
QVector<BlobShape*> rectShapes; QVector<BlobShape*> rectShapes;
rectShapes.reserve(m_group->shapes().size()); rectShapes.reserve(m_group->shapes().size());
for (BlobShape* other : m_group->shapes()) { for (BlobShape* other : m_group->shapes()) {
if (other->isInvertedRect()) if (other->isInvertedRect()) continue;
continue;
if (other->width() <= 0 || other->height() <= 0) if (other->width() <= 0 || other->height() <= 0) continue;
continue;
if (isExcluded(other)) if (isExcluded(other)) continue;
continue;
const QPointF otherScene = other->mapToScene(QPointF(0, 0)); const QPointF otherScene = other->mapToScene(QPointF(0, 0));
@@ -174,9 +174,12 @@ void BlobShape::updatePolish() {
} else { } else {
const float otherHW = static_cast<float>(other->width()) * 0.5f; const float otherHW = static_cast<float>(other->width()) * 0.5f;
const float otherHH = static_cast<float>(other->height()) * 0.5f; const float otherHH = static_cast<float>(other->height()) * 0.5f;
const float otherPad = pad + deformPadding(other->m_deformMatrix, otherHW, otherHH); const float otherPad =
const QRectF otherPadded(otherScene.x() - static_cast<double>(otherPad), pad + deformPadding(other->m_deformMatrix, otherHW, otherHH);
otherScene.y() - static_cast<double>(otherPad), other->width() + 2.0 * static_cast<double>(otherPad), const QRectF otherPadded(
otherScene.x() - static_cast<double>(otherPad),
otherScene.y() - static_cast<double>(otherPad),
other->width() + 2.0 * static_cast<double>(otherPad),
other->height() + 2.0 * static_cast<double>(otherPad)); other->height() + 2.0 * static_cast<double>(otherPad));
include = myPadded.intersects(otherPadded); include = myPadded.intersects(otherPadded);
} }
@@ -217,19 +220,16 @@ void BlobShape::updatePolish() {
} }
} }
if (isInvertedRect()) if (isInvertedRect()) m_cachedMyIndex = -1;
m_cachedMyIndex = -1;
const auto cachedCount = m_cachedRects.size(); const auto cachedCount = m_cachedRects.size();
for (qsizetype i = 0; i < cachedCount; ++i) { for (qsizetype i = 0; i < cachedCount; ++i) {
int mask = 0; int mask = 0;
BlobShape* si = rectShapes[i]; BlobShape* si = rectShapes[i];
for (qsizetype j = 0; j < cachedCount; ++j) { for (qsizetype j = 0; j < cachedCount; ++j) {
if (j == i) if (j == i) continue;
continue;
BlobShape* sj = rectShapes[j]; BlobShape* sj = rectShapes[j];
if (si->isExcluded(sj) || sj->isExcluded(si)) if (si->isExcluded(sj) || sj->isExcluded(si)) mask |= (1 << j);
mask |= (1 << j);
} }
m_cachedRects[i].excludeMask = mask; m_cachedRects[i].excludeMask = mask;
} }
@@ -242,15 +242,25 @@ void BlobShape::updatePolish() {
auto* inv = m_group->invertedRect(); auto* inv = m_group->invertedRect();
if (inv) { if (inv) {
const QPointF invScene = inv->mapToScene(QPointF(0, 0)); const QPointF invScene = inv->mapToScene(QPointF(0, 0));
const float outerCX = static_cast<float>(invScene.x() + inv->width() / 2.0); const float outerCX =
const float outerCY = static_cast<float>(invScene.y() + inv->height() / 2.0); static_cast<float>(invScene.x() + inv->width() / 2.0);
const float outerCY =
static_cast<float>(invScene.y() + inv->height() / 2.0);
const float outerHW = static_cast<float>(inv->width() / 2.0); const float outerHW = static_cast<float>(inv->width() / 2.0);
const float outerHH = static_cast<float>(inv->height() / 2.0); const float outerHH = static_cast<float>(inv->height() / 2.0);
const float innerCX = outerCX + static_cast<float>((inv->borderLeft() - inv->borderRight()) / 2.0); const float innerCX =
const float innerCY = outerCY + static_cast<float>((inv->borderTop() - inv->borderBottom()) / 2.0); outerCX +
const float innerHW = outerHW - static_cast<float>((inv->borderLeft() + inv->borderRight()) / 2.0); static_cast<float>((inv->borderLeft() - inv->borderRight()) / 2.0);
const float innerHH = outerHH - static_cast<float>((inv->borderTop() + inv->borderBottom()) / 2.0); const float innerCY =
outerCY +
static_cast<float>((inv->borderTop() - inv->borderBottom()) / 2.0);
const float innerHW =
outerHW -
static_cast<float>((inv->borderLeft() + inv->borderRight()) / 2.0);
const float innerHH =
outerHH -
static_cast<float>((inv->borderTop() + inv->borderBottom()) / 2.0);
bool nearBorder = isInvertedRect(); bool nearBorder = isInvertedRect();
if (!nearBorder) { if (!nearBorder) {
@@ -259,8 +269,10 @@ void BlobShape::updatePolish() {
const float myCY = m_cachedPaddedY + m_cachedPaddedH * 0.5f; const float myCY = m_cachedPaddedY + m_cachedPaddedH * 0.5f;
const float myHW = m_cachedPaddedW * 0.5f; const float myHW = m_cachedPaddedW * 0.5f;
const float myHH = m_cachedPaddedH * 0.5f; const float myHH = m_cachedPaddedH * 0.5f;
nearBorder = (myCX - myHW < innerCX - innerHW + margin) || (myCX + myHW > innerCX + innerHW - margin) || nearBorder = (myCX - myHW < innerCX - innerHW + margin) ||
(myCY - myHH < innerCY - innerHH + margin) || (myCY + myHH > innerCY + innerHH - margin); (myCX + myHW > innerCX + innerHW - margin) ||
(myCY - myHH < innerCY - innerHH + margin) ||
(myCY + myHH > innerCY + innerHH - margin);
} }
if (nearBorder) { if (nearBorder) {
@@ -293,15 +305,33 @@ void BlobShape::updatePolish() {
const float cTlX = ri.cx - ri.hw, cTlY = ri.cy - ri.hh; const float cTlX = ri.cx - ri.hw, cTlY = ri.cy - ri.hh;
for (qsizetype j = 0; j < rectCount; ++j) { for (qsizetype j = 0; j < rectCount; ++j) {
if (j == i) if (j == i) continue;
continue; if (riExcludeMask & (1 << j)) continue;
if (riExcludeMask & (1 << j))
continue;
const auto& rj = m_cachedRects[j]; const auto& rj = m_cachedRects[j];
fTr = std::min(fTr, cpuSmoothstep(0.0f, smoothFactor, cpuSdBox(cTrX, cTrY, rj.cx, rj.cy, rj.hw, rj.hh))); fTr = std::min(
fBr = std::min(fBr, cpuSmoothstep(0.0f, smoothFactor, cpuSdBox(cBrX, cBrY, rj.cx, rj.cy, rj.hw, rj.hh))); fTr,
fBl = std::min(fBl, cpuSmoothstep(0.0f, smoothFactor, cpuSdBox(cBlX, cBlY, rj.cx, rj.cy, rj.hw, rj.hh))); cpuSmoothstep(
fTl = std::min(fTl, cpuSmoothstep(0.0f, smoothFactor, cpuSdBox(cTlX, cTlY, rj.cx, rj.cy, rj.hw, rj.hh))); 0.0f,
smoothFactor,
cpuSdBox(cTrX, cTrY, rj.cx, rj.cy, rj.hw, rj.hh)));
fBr = std::min(
fBr,
cpuSmoothstep(
0.0f,
smoothFactor,
cpuSdBox(cBrX, cBrY, rj.cx, rj.cy, rj.hw, rj.hh)));
fBl = std::min(
fBl,
cpuSmoothstep(
0.0f,
smoothFactor,
cpuSdBox(cBlX, cBlY, rj.cx, rj.cy, rj.hw, rj.hh)));
fTl = std::min(
fTl,
cpuSmoothstep(
0.0f,
smoothFactor,
cpuSdBox(cTlX, cTlY, rj.cx, rj.cy, rj.hw, rj.hh)));
} }
if (m_cachedHasInverted) { if (m_cachedHasInverted) {
@@ -309,10 +339,30 @@ void BlobShape::updatePolish() {
const float icy = m_cachedInvertedInner[1]; const float icy = m_cachedInvertedInner[1];
const float ihw = m_cachedInvertedInner[2]; const float ihw = m_cachedInvertedInner[2];
const float ihh = m_cachedInvertedInner[3]; const float ihh = m_cachedInvertedInner[3];
fTr = std::min(fTr, cpuSmoothstep(0.0f, smoothFactor, -cpuSdBox(cTrX, cTrY, icx, icy, ihw, ihh))); fTr = std::min(
fBr = std::min(fBr, cpuSmoothstep(0.0f, smoothFactor, -cpuSdBox(cBrX, cBrY, icx, icy, ihw, ihh))); fTr,
fBl = std::min(fBl, cpuSmoothstep(0.0f, smoothFactor, -cpuSdBox(cBlX, cBlY, icx, icy, ihw, ihh))); cpuSmoothstep(
fTl = std::min(fTl, cpuSmoothstep(0.0f, smoothFactor, -cpuSdBox(cTlX, cTlY, icx, icy, ihw, ihh))); 0.0f,
smoothFactor,
-cpuSdBox(cTrX, cTrY, icx, icy, ihw, ihh)));
fBr = std::min(
fBr,
cpuSmoothstep(
0.0f,
smoothFactor,
-cpuSdBox(cBrX, cBrY, icx, icy, ihw, ihh)));
fBl = std::min(
fBl,
cpuSmoothstep(
0.0f,
smoothFactor,
-cpuSdBox(cBlX, cBlY, icx, icy, ihw, ihh)));
fTl = std::min(
fTl,
cpuSmoothstep(
0.0f,
smoothFactor,
-cpuSdBox(cTlX, cTlY, icx, icy, ihw, ihh)));
} }
ri.radius[0] = std::max(ri.radius[0] * fTr, minR); ri.radius[0] = std::max(ri.radius[0] * fTr, minR);
@@ -332,7 +382,8 @@ QSGNode* BlobShape::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) {
if (!node) { if (!node) {
node = new QSGGeometryNode; node = new QSGGeometryNode;
auto* geometry = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4); auto* geometry =
new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4);
geometry->setDrawingMode(QSGGeometry::DrawTriangleStrip); geometry->setDrawingMode(QSGGeometry::DrawTriangleStrip);
node->setGeometry(geometry); node->setGeometry(geometry);
node->setFlag(QSGNode::OwnsGeometry); node->setFlag(QSGNode::OwnsGeometry);
@@ -368,10 +419,17 @@ QSGNode* BlobShape::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) {
material->m_color = m_group->color(); material->m_color = m_group->color();
material->m_hasInverted = m_cachedHasInverted ? 1 : 0; material->m_hasInverted = m_cachedHasInverted ? 1 : 0;
material->m_invertedRadius = m_cachedInvertedRadius; material->m_invertedRadius = m_cachedInvertedRadius;
memcpy(material->m_invertedOuter, m_cachedInvertedOuter, sizeof(m_cachedInvertedOuter)); memcpy(
memcpy(material->m_invertedInner, m_cachedInvertedInner, sizeof(m_cachedInvertedInner)); material->m_invertedOuter,
m_cachedInvertedOuter,
sizeof(m_cachedInvertedOuter));
memcpy(
material->m_invertedInner,
m_cachedInvertedInner,
sizeof(m_cachedInvertedInner));
const int count = static_cast<int>(qMin(m_cachedRects.size(), qsizetype(16))); const int count =
static_cast<int>(qMin(m_cachedRects.size(), qsizetype(16)));
material->m_rectCount = count; material->m_rectCount = count;
for (int i = 0; i < count; ++i) for (int i = 0; i < count; ++i)
material->m_rects[i] = m_cachedRects[i]; material->m_rects[i] = m_cachedRects[i];
+13 -20
View File
@@ -12,8 +12,11 @@ class BlobShape : public QQuickItem {
Q_OBJECT Q_OBJECT
Q_PROPERTY(BlobGroup* group READ group WRITE setGroup NOTIFY groupChanged) Q_PROPERTY(BlobGroup* group READ group WRITE setGroup NOTIFY groupChanged)
Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged) Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged)
Q_PROPERTY(QMatrix4x4 deformMatrix READ deformMatrix NOTIFY deformMatrixChanged) Q_PROPERTY(
Q_PROPERTY(QMatrix4x4 rawDeformMatrix READ rawDeformMatrix NOTIFY rawDeformMatrixChanged) QMatrix4x4 deformMatrix READ deformMatrix NOTIFY deformMatrixChanged)
Q_PROPERTY(
QMatrix4x4 rawDeformMatrix READ rawDeformMatrix NOTIFY
rawDeformMatrixChanged)
friend class BlobGroup; friend class BlobGroup;
@@ -21,15 +24,11 @@ public:
explicit BlobShape(QQuickItem* parent = nullptr); explicit BlobShape(QQuickItem* parent = nullptr);
~BlobShape() override = default; ~BlobShape() override = default;
[[nodiscard]] BlobGroup* group() const { [[nodiscard]] BlobGroup* group() const { return m_group; }
return m_group;
}
void setGroup(BlobGroup* g); void setGroup(BlobGroup* g);
[[nodiscard]] qreal radius() const { [[nodiscard]] qreal radius() const { return m_radius; }
return m_radius;
}
void setRadius(qreal r); void setRadius(qreal r);
@@ -37,9 +36,7 @@ void setRadius(qreal r);
return m_centeredDeformMatrix; return m_centeredDeformMatrix;
} }
[[nodiscard]] QMatrix4x4 rawDeformMatrix() const { [[nodiscard]] QMatrix4x4 rawDeformMatrix() const { return m_deformMatrix; }
return m_deformMatrix;
}
signals: signals:
void groupChanged(); void groupChanged();
@@ -49,22 +46,18 @@ void rawDeformMatrixChanged();
protected: protected:
void componentComplete() override; void componentComplete() override;
void geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) override; void geometryChange(
const QRectF& newGeometry, const QRectF& oldGeometry) override;
void updatePolish() override; void updatePolish() override;
QSGNode* updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) override; QSGNode* updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) override;
[[nodiscard]] virtual bool isInvertedRect() const { [[nodiscard]] virtual bool isInvertedRect() const { return false; }
return false;
}
virtual bool isExcluded(const BlobShape* /*other*/) const { virtual bool isExcluded(const BlobShape*) const { return false; }
return false;
}
virtual void cornerRadii(float out[4]) const; virtual void cornerRadii(float out[4]) const;
virtual void updatePhysics() { virtual void updatePhysics() {}
}
virtual void registerWithGroup(); virtual void registerWithGroup();
virtual void unregisterFromGroup(); virtual void unregisterFromGroup();
+38 -24
View File
@@ -3,11 +3,10 @@
namespace ZShell::components { namespace ZShell::components {
ButtonRow::ButtonRow(QQuickItem* parent) ButtonRow::ButtonRow(QQuickItem* parent)
: QQuickItem(parent) : QQuickItem(parent), m_dirty(false), m_spacing(0.0) {
, m_dirty(false)
, m_spacing(0.0) {
setFlag(QQuickItem::ItemHasContents, true); setFlag(QQuickItem::ItemHasContents, true);
QObject::connect(this, &ButtonRow::widthChanged, this, &ButtonRow::invalidate); QObject::connect(
this, &ButtonRow::widthChanged, this, &ButtonRow::invalidate);
} }
qreal ButtonRow::spacing() const { qreal ButtonRow::spacing() const {
@@ -15,8 +14,7 @@ qreal ButtonRow::spacing() const {
} }
void ButtonRow::setSpacing(qreal spacing) { void ButtonRow::setSpacing(qreal spacing) {
if (qFuzzyCompare(m_spacing + 1.0, spacing + 1.0)) if (qFuzzyCompare(m_spacing + 1.0, spacing + 1.0)) return;
return;
m_spacing = spacing; m_spacing = spacing;
emit spacingChanged(); emit spacingChanged();
@@ -24,18 +22,33 @@ void ButtonRow::setSpacing(qreal spacing) {
invalidate(); invalidate();
} }
void ButtonRow::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData& data) { void ButtonRow::itemChange(
QQuickItem::ItemChange change, const QQuickItem::ItemChangeData& data) {
if (change == QQuickItem::ItemChildAddedChange) { if (change == QQuickItem::ItemChildAddedChange) {
auto* const child = data.item; auto* const child = data.item;
QObject::connect(child, &QQuickItem::implicitWidthChanged, this, &ButtonRow::invalidate); QObject::connect(
QObject::connect(child, &QQuickItem::implicitHeightChanged, this, &ButtonRow::invalidate); child,
QObject::connect(child, &QQuickItem::visibleChanged, this, &ButtonRow::invalidate); &QQuickItem::implicitWidthChanged,
this,
&ButtonRow::invalidate);
QObject::connect(
child,
&QQuickItem::implicitHeightChanged,
this,
&ButtonRow::invalidate);
QObject::connect(
child, &QQuickItem::visibleChanged, this, &ButtonRow::invalidate);
const auto* childMeta = child->metaObject(); const auto* childMeta = child->metaObject();
const auto morphSignalIdx = childMeta->indexOfSignal("shapeMorphExpansionChanged()"); const auto morphSignalIdx =
childMeta->indexOfSignal("shapeMorphExpansionChanged()");
if (morphSignalIdx != -1) if (morphSignalIdx != -1)
QObject::connect(child, childMeta->method(morphSignalIdx), this, QObject::connect(
metaObject()->method(metaObject()->indexOfSlot("invalidate()"))); child,
childMeta->method(morphSignalIdx),
this,
metaObject()->method(
metaObject()->indexOfSlot("invalidate()")));
invalidate(); invalidate();
} else if (change == QQuickItem::ItemChildRemovedChange) { } else if (change == QQuickItem::ItemChildRemovedChange) {
@@ -77,8 +90,7 @@ void ButtonRow::relayout() {
maxHeight = qMax(maxHeight, child->implicitHeight()); maxHeight = qMax(maxHeight, child->implicitHeight());
const auto prop = child->property("fillWidth"); const auto prop = child->property("fillWidth");
if (!prop.isValid()) if (!prop.isValid()) continue;
continue;
if (prop.toBool()) { if (prop.toBool()) {
fillWidthCount++; fillWidthCount++;
@@ -88,15 +100,17 @@ void ButtonRow::relayout() {
} }
} }
if (fillWidthCount == 0) if (fillWidthCount == 0) fillWidthCount = 1; // Avoid divide by 0
fillWidthCount = 1; // Avoid divide by 0
const auto widthPerItem = (width() - totalSpacing - reservedWidth) / static_cast<qreal>(fillWidthCount); const auto widthPerItem = (width() - totalSpacing - reservedWidth) /
static_cast<qreal>(fillWidthCount);
QList<qreal> baseWidths; QList<qreal> baseWidths;
baseWidths.reserve(nChildren); baseWidths.reserve(nChildren);
for (auto* const child : validChildren) for (auto* const child : validChildren)
baseWidths.append(child->property("fillWidth").toBool() ? widthPerItem : child->implicitWidth()); baseWidths.append(
child->property("fillWidth").toBool() ? widthPerItem
: child->implicitWidth());
qreal accX = 0; qreal accX = 0;
for (int i = 0; i < nChildren; ++i) { for (int i = 0; i < nChildren; ++i) {
@@ -108,12 +122,12 @@ void ButtonRow::relayout() {
// clang-format on // clang-format on
// Items at edges push by full amount, items in middle push by half // Items at edges push by full amount, items in middle push by half
if (i > 1) if (i > 1) prevExtraWidth /= 2;
prevExtraWidth /= 2; if (i < nChildren - 2) nextExtraWidth /= 2;
if (i < nChildren - 2)
nextExtraWidth /= 2;
child->setWidth(baseWidths[i] + getMorphExpansion(child) - prevExtraWidth - nextExtraWidth); child->setWidth(
baseWidths[i] + getMorphExpansion(child) - prevExtraWidth -
nextExtraWidth);
child->setHeight(maxHeight); child->setHeight(maxHeight);
child->setX(accX); child->setX(accX);
+3 -2
View File
@@ -20,13 +20,14 @@ signals:
void spacingChanged(); void spacingChanged();
protected: protected:
void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData& data) override; void itemChange(
QQuickItem::ItemChange change,
const QQuickItem::ItemChangeData& data) override;
void updatePolish() override; void updatePolish() override;
private slots: private slots:
void invalidate(); void invalidate();
private:
void relayout(); void relayout();
static qreal getMorphExpansion(const QQuickItem* item); static qreal getMorphExpansion(const QQuickItem* item);
+121 -195
View File
@@ -21,8 +21,7 @@ qreal LazyListViewAttached::preferredHeight() const {
} }
void LazyListViewAttached::setPreferredHeight(qreal height) { void LazyListViewAttached::setPreferredHeight(qreal height) {
if (qFuzzyCompare(m_preferredHeight + 1.0, height + 1.0)) if (qFuzzyCompare(m_preferredHeight + 1.0, height + 1.0)) return;
return;
m_preferredHeight = height; m_preferredHeight = height;
emit preferredHeightChanged(); emit preferredHeightChanged();
} }
@@ -32,8 +31,7 @@ qreal LazyListViewAttached::visibleHeight() const {
} }
void LazyListViewAttached::setVisibleHeight(qreal height) { void LazyListViewAttached::setVisibleHeight(qreal height) {
if (qFuzzyCompare(m_visibleHeight + 1.0, height + 1.0)) if (qFuzzyCompare(m_visibleHeight + 1.0, height + 1.0)) return;
return;
m_visibleHeight = height; m_visibleHeight = height;
emit visibleHeightChanged(); emit visibleHeightChanged();
} }
@@ -43,8 +41,7 @@ bool LazyListViewAttached::ready() const {
} }
void LazyListViewAttached::setReady(bool ready) { void LazyListViewAttached::setReady(bool ready) {
if (m_ready == ready) if (m_ready == ready) return;
return;
m_ready = ready; m_ready = ready;
emit readyChanged(); emit readyChanged();
} }
@@ -54,8 +51,7 @@ bool LazyListViewAttached::adding() const {
} }
void LazyListViewAttached::setAdding(bool adding) { void LazyListViewAttached::setAdding(bool adding) {
if (m_adding == adding) if (m_adding == adding) return;
return;
m_adding = adding; m_adding = adding;
emit addingChanged(); emit addingChanged();
} }
@@ -65,8 +61,7 @@ bool LazyListViewAttached::removing() const {
} }
void LazyListViewAttached::setRemoving(bool removing) { void LazyListViewAttached::setRemoving(bool removing) {
if (m_removing == removing) if (m_removing == removing) return;
return;
m_removing = removing; m_removing = removing;
emit removingChanged(); emit removingChanged();
} }
@@ -76,8 +71,7 @@ bool LazyListViewAttached::trackViewport() const {
} }
void LazyListViewAttached::setTrackViewport(bool track) { void LazyListViewAttached::setTrackViewport(bool track) {
if (m_trackViewport == track) if (m_trackViewport == track) return;
return;
m_trackViewport = track; m_trackViewport = track;
emit trackViewportChanged(); emit trackViewportChanged();
} }
@@ -102,16 +96,13 @@ QAbstractItemModel* LazyListView::model() const {
} }
void LazyListView::setModel(QAbstractItemModel* model) { void LazyListView::setModel(QAbstractItemModel* model) {
if (m_model == model) if (m_model == model) return;
return;
if (m_model) if (m_model) disconnectModel();
disconnectModel();
m_model = model; m_model = model;
if (m_model) if (m_model) connectModel();
connectModel();
resetContent(); resetContent();
emit modelChanged(); emit modelChanged();
@@ -122,8 +113,7 @@ QQmlComponent* LazyListView::delegate() const {
} }
void LazyListView::setDelegate(QQmlComponent* delegate) { void LazyListView::setDelegate(QQmlComponent* delegate) {
if (m_delegate == delegate) if (m_delegate == delegate) return;
return;
m_delegate = delegate; m_delegate = delegate;
resetContent(); resetContent();
@@ -135,8 +125,7 @@ qreal LazyListView::spacing() const {
} }
void LazyListView::setSpacing(qreal spacing) { void LazyListView::setSpacing(qreal spacing) {
if (qFuzzyCompare(m_spacing, spacing)) if (qFuzzyCompare(m_spacing, spacing)) return;
return;
m_spacing = spacing; m_spacing = spacing;
emit spacingChanged(); emit spacingChanged();
polish(); polish();
@@ -155,8 +144,7 @@ qreal LazyListView::contentY() const {
} }
void LazyListView::setContentY(qreal contentY) { void LazyListView::setContentY(qreal contentY) {
if (qFuzzyCompare(m_contentY, contentY)) if (qFuzzyCompare(m_contentY, contentY)) return;
return;
m_contentY = contentY; m_contentY = contentY;
emit contentYChanged(); emit contentYChanged();
polish(); polish();
@@ -167,12 +155,10 @@ QRectF LazyListView::viewport() const {
} }
void LazyListView::setViewport(const QRectF& viewport) { void LazyListView::setViewport(const QRectF& viewport) {
if (m_viewport == viewport) if (m_viewport == viewport) return;
return;
m_viewport = viewport; m_viewport = viewport;
emit viewportChanged(); emit viewportChanged();
if (m_useCustomViewport) if (m_useCustomViewport) polish();
polish();
} }
bool LazyListView::useCustomViewport() const { bool LazyListView::useCustomViewport() const {
@@ -180,8 +166,7 @@ bool LazyListView::useCustomViewport() const {
} }
void LazyListView::setUseCustomViewport(bool use) { void LazyListView::setUseCustomViewport(bool use) {
if (m_useCustomViewport == use) if (m_useCustomViewport == use) return;
return;
m_useCustomViewport = use; m_useCustomViewport = use;
emit useCustomViewportChanged(); emit useCustomViewportChanged();
polish(); polish();
@@ -192,8 +177,7 @@ qreal LazyListView::cacheBuffer() const {
} }
void LazyListView::setCacheBuffer(qreal buffer) { void LazyListView::setCacheBuffer(qreal buffer) {
if (qFuzzyCompare(m_cacheBuffer, buffer)) if (qFuzzyCompare(m_cacheBuffer, buffer)) return;
return;
m_cacheBuffer = buffer; m_cacheBuffer = buffer;
emit cacheBufferChanged(); emit cacheBufferChanged();
polish(); polish();
@@ -204,8 +188,7 @@ qreal LazyListView::estimatedHeight() const {
} }
void LazyListView::setEstimatedHeight(qreal height) { void LazyListView::setEstimatedHeight(qreal height) {
if (qFuzzyCompare(m_estimatedHeight, height)) if (qFuzzyCompare(m_estimatedHeight, height)) return;
return;
m_estimatedHeight = height; m_estimatedHeight = height;
emit estimatedHeightChanged(); emit estimatedHeightChanged();
polish(); polish();
@@ -216,17 +199,14 @@ bool LazyListView::asynchronous() const {
} }
void LazyListView::setAsynchronous(bool async) { void LazyListView::setAsynchronous(bool async) {
if (m_asynchronous == async) if (m_asynchronous == async) return;
return;
m_asynchronous = async; m_asynchronous = async;
emit asynchronousChanged(); emit asynchronousChanged();
} }
qreal LazyListView::effectiveEstimatedHeight() const { qreal LazyListView::effectiveEstimatedHeight() const {
if (m_estimatedHeight >= 0) if (m_estimatedHeight >= 0) return m_estimatedHeight;
return m_estimatedHeight; if (m_knownHeightCount > 0) return m_knownHeightSum / m_knownHeightCount;
if (m_knownHeightCount > 0)
return m_knownHeightSum / m_knownHeightCount;
return 40; return 40;
} }
@@ -241,8 +221,7 @@ void LazyListView::untrackHeight(qreal height) {
} }
qreal LazyListView::delegateHeight(QQuickItem* item) { qreal LazyListView::delegateHeight(QQuickItem* item) {
if (!item) if (!item) return 0;
return 0;
auto* attached = qobject_cast<LazyListViewAttached*>( auto* attached = qobject_cast<LazyListViewAttached*>(
qmlAttachedPropertiesObject<LazyListView>(item, false)); qmlAttachedPropertiesObject<LazyListView>(item, false));
@@ -253,14 +232,12 @@ qreal LazyListView::delegateHeight(QQuickItem* item) {
} }
qreal LazyListView::delegateVisibleHeight(QQuickItem* item) { qreal LazyListView::delegateVisibleHeight(QQuickItem* item) {
if (!item) if (!item) return 0;
return 0;
auto* attached = qobject_cast<LazyListViewAttached*>( auto* attached = qobject_cast<LazyListViewAttached*>(
qmlAttachedPropertiesObject<LazyListView>(item, false)); qmlAttachedPropertiesObject<LazyListView>(item, false));
if (attached) { if (attached) {
if (attached->visibleHeight() >= 0) if (attached->visibleHeight() >= 0) return attached->visibleHeight();
return attached->visibleHeight();
if (attached->preferredHeight() >= 0) if (attached->preferredHeight() >= 0)
return attached->preferredHeight(); return attached->preferredHeight();
} }
@@ -269,8 +246,7 @@ qreal LazyListView::delegateVisibleHeight(QQuickItem* item) {
} }
bool LazyListView::isDelegateReady(QQuickItem* item) { bool LazyListView::isDelegateReady(QQuickItem* item) {
if (!item) if (!item) return false;
return false;
auto* att = qobject_cast<LazyListViewAttached*>( auto* att = qobject_cast<LazyListViewAttached*>(
qmlAttachedPropertiesObject<LazyListView>(item, false)); qmlAttachedPropertiesObject<LazyListView>(item, false));
return !att || att->ready(); return !att || att->ready();
@@ -281,8 +257,7 @@ int LazyListView::removeDuration() const {
} }
void LazyListView::setRemoveDuration(int duration) { void LazyListView::setRemoveDuration(int duration) {
if (m_removeDuration == duration) if (m_removeDuration == duration) return;
return;
m_removeDuration = duration; m_removeDuration = duration;
emit removeDurationChanged(); emit removeDurationChanged();
} }
@@ -292,8 +267,7 @@ int LazyListView::readyDelay() const {
} }
void LazyListView::setReadyDelay(int delay) { void LazyListView::setReadyDelay(int delay) {
if (m_readyDelay == delay) if (m_readyDelay == delay) return;
return;
m_readyDelay = delay; m_readyDelay = delay;
emit readyDelayChanged(); emit readyDelayChanged();
} }
@@ -308,17 +282,15 @@ void LazyListView::componentComplete() {
resetContent(); resetContent();
} }
void LazyListView::geometryChange(const QRectF& newGeometry, void LazyListView::geometryChange(
const QRectF& oldGeometry) { const QRectF& newGeometry, const QRectF& oldGeometry) {
QQuickItem::geometryChange(newGeometry, oldGeometry); QQuickItem::geometryChange(newGeometry, oldGeometry);
if (!m_componentComplete) if (!m_componentComplete) return;
return;
if (!qFuzzyCompare(newGeometry.width(), oldGeometry.width())) { if (!qFuzzyCompare(newGeometry.width(), oldGeometry.width())) {
for (auto& entry : m_delegates) { for (auto& entry : m_delegates) {
if (entry.item) if (entry.item) entry.item->setWidth(newGeometry.width());
entry.item->setWidth(newGeometry.width());
} }
} }
@@ -326,8 +298,7 @@ void LazyListView::geometryChange(const QRectF& newGeometry,
} }
void LazyListView::updatePolish() { void LazyListView::updatePolish() {
if (!m_componentComplete || !m_model || !m_delegate) if (!m_componentComplete || !m_model || !m_delegate) return;
return;
// Flush pending inserts — make items visible and clear the adding flag // Flush pending inserts — make items visible and clear the adding flag
// so enter animations begin. When readyDelay > 0 the entire insert is // so enter animations begin. When readyDelay > 0 the entire insert is
@@ -335,9 +306,9 @@ void LazyListView::updatePolish() {
// Collect pending indices first — avoids scanning the entire hash each frame // Collect pending indices first — avoids scanning the entire hash each frame
QList<int> pendingIndices; QList<int> pendingIndices;
for (auto it = m_delegates.constBegin(); it != m_delegates.constEnd(); ++it) { for (auto it = m_delegates.constBegin(); it != m_delegates.constEnd();
if (it->pendingInsert && it->item) ++it) {
pendingIndices.append(it.key()); if (it->pendingInsert && it->item) pendingIndices.append(it.key());
} }
for (int pendingIdx : pendingIndices) { for (int pendingIdx : pendingIndices) {
@@ -349,8 +320,7 @@ void LazyListView::updatePolish() {
auto* item = entry.item; auto* item = entry.item;
QTimer::singleShot(m_readyDelay, this, [this, item] { QTimer::singleShot(m_readyDelay, this, [this, item] {
auto indexIt = m_itemToIndex.find(item); auto indexIt = m_itemToIndex.find(item);
if (indexIt == m_itemToIndex.end()) if (indexIt == m_itemToIndex.end()) return;
return;
const int idx = indexIt.value(); const int idx = indexIt.value();
auto it = m_delegates.find(idx); auto it = m_delegates.find(idx);
if (it == m_delegates.end() || it->item != item || if (it == m_delegates.end() || it->item != item ||
@@ -375,14 +345,11 @@ void LazyListView::updatePolish() {
? m_layout[i].height ? m_layout[i].height
: effectiveEstimatedHeight(); : effectiveEstimatedHeight();
if (h > 0) { if (h > 0) {
if (hasVisItem) if (hasVisItem) visualY += m_spacing;
visualY += m_spacing;
hasVisItem = true; hasVisItem = true;
} }
if (i == idx) if (i == idx) break;
break; if (h > 0) visualY += h;
if (h > 0)
visualY += h;
} }
item->setY(visualY - m_contentY); item->setY(visualY - m_contentY);
} }
@@ -397,8 +364,8 @@ void LazyListView::updatePolish() {
// Animate from visual position to layout position // Animate from visual position to layout position
if (idx >= 0 && idx < static_cast<int>(m_layout.size())) if (idx >= 0 && idx < static_cast<int>(m_layout.size()))
item->setProperty("y", item->setProperty(
m_layout[idx].targetY - m_contentY); "y", m_layout[idx].targetY - m_contentY);
polish(); polish();
}); });
@@ -432,8 +399,7 @@ void LazyListView::updatePolish() {
continue; continue;
const int idx = entry.modelIndex; const int idx = entry.modelIndex;
if (idx < 0 || idx >= layoutSize) if (idx < 0 || idx >= layoutSize) continue;
continue;
if (m_layout[idx].heightKnown && qFuzzyIsNull(m_layout[idx].height)) if (m_layout[idx].heightKnown && qFuzzyIsNull(m_layout[idx].height))
continue; continue;
@@ -444,8 +410,6 @@ void LazyListView::updatePolish() {
} }
} }
// --- Layout Engine ---
void LazyListView::relayout() { void LazyListView::relayout() {
// Layout positioning uses preferredHeight (final/non-animated). // Layout positioning uses preferredHeight (final/non-animated).
// Only add spacing between items with non-zero height. // Only add spacing between items with non-zero height.
@@ -455,8 +419,7 @@ void LazyListView::relayout() {
const qreal layoutH = record.heightKnown ? record.height const qreal layoutH = record.heightKnown ? record.height
: effectiveEstimatedHeight(); : effectiveEstimatedHeight();
if (layoutH > 0) { if (layoutH > 0) {
if (hasLayoutItem) if (hasLayoutItem) y += m_spacing;
y += m_spacing;
hasLayoutItem = true; hasLayoutItem = true;
record.targetY = y; record.targetY = y;
y += layoutH; y += layoutH;
@@ -483,8 +446,7 @@ void LazyListView::relayout() {
h = m_layout[i].heightKnown ? m_layout[i].height h = m_layout[i].heightKnown ? m_layout[i].height
: effectiveEstimatedHeight(); : effectiveEstimatedHeight();
if (h > 0) { if (h > 0) {
if (hasVisItem) if (hasVisItem) visY += m_spacing;
visY += m_spacing;
hasVisItem = true; hasVisItem = true;
visY += h; visY += h;
} }
@@ -492,11 +454,9 @@ void LazyListView::relayout() {
// Account for dying delegates still visually present // Account for dying delegates still visually present
for (const auto& dying : std::as_const(m_dyingDelegates)) { for (const auto& dying : std::as_const(m_dyingDelegates)) {
if (!dying.item) if (!dying.item) continue;
continue;
const qreal dyingH = delegateVisibleHeight(dying.item); const qreal dyingH = delegateVisibleHeight(dying.item);
if (dyingH > 0) if (dyingH > 0) visY = std::max(visY, dying.item->y() + dyingH);
visY = std::max(visY, dying.item->y() + dyingH);
} }
if (!qFuzzyCompare(m_contentHeight + 1.0, visY + 1.0)) { if (!qFuzzyCompare(m_contentHeight + 1.0, visY + 1.0)) {
@@ -519,8 +479,7 @@ QRectF LazyListView::effectiveViewport() const {
if (!m_useCustomViewport && m_layoutHeight > 0) { if (!m_useCustomViewport && m_layoutHeight > 0) {
const qreal top = std::min(vp.y(), m_layoutHeight); const qreal top = std::min(vp.y(), m_layoutHeight);
const qreal bottom = std::max(vp.y() + vp.height(), 0.0); const qreal bottom = std::max(vp.y() + vp.height(), 0.0);
if (bottom > top) if (bottom > top) vp = QRectF(vp.x(), top, vp.width(), bottom - top);
vp = QRectF(vp.x(), top, vp.width(), bottom - top);
} }
vp.adjust(0, -m_cacheBuffer, 0, m_cacheBuffer); vp.adjust(0, -m_cacheBuffer, 0, m_cacheBuffer);
@@ -541,12 +500,10 @@ QRectF LazyListView::effectiveViewport() const {
} }
std::pair<int, int> LazyListView::computeVisibleRange() const { std::pair<int, int> LazyListView::computeVisibleRange() const {
if (m_layout.isEmpty()) if (m_layout.isEmpty()) return {-1, -1};
return {-1, -1};
const auto vp = effectiveViewport(); const auto vp = effectiveViewport();
if (vp.isEmpty()) if (vp.isEmpty()) return {-1, -1};
return {-1, -1};
const qreal vpTop = vp.y(); const qreal vpTop = vp.y();
const qreal vpBottom = vp.y() + vp.height(); const qreal vpBottom = vp.y() + vp.height();
@@ -571,22 +528,18 @@ std::pair<int, int> LazyListView::computeVisibleRange() const {
} }
} }
if (first >= static_cast<int>(m_layout.size())) if (first >= static_cast<int>(m_layout.size())) return {-1, -1};
return {-1, -1};
// Linear scan for last visible item // Linear scan for last visible item
int last = first; int last = first;
for (int i = first; i < static_cast<int>(m_layout.size()); ++i) { for (int i = first; i < static_cast<int>(m_layout.size()); ++i) {
if (m_layout[i].targetY > vpBottom) if (m_layout[i].targetY > vpBottom) break;
break;
last = i; last = i;
} }
return {first, last}; return {first, last};
} }
// --- Delegate Lifecycle ---
void LazyListView::syncDelegates() { void LazyListView::syncDelegates() {
const auto [first, last] = computeVisibleRange(); const auto [first, last] = computeVisibleRange();
@@ -601,8 +554,7 @@ void LazyListView::syncDelegates() {
const auto vp = effectiveViewport(); const auto vp = effectiveViewport();
QList<int> toRemove; QList<int> toRemove;
for (auto it = m_delegates.begin(); it != m_delegates.end(); ++it) { for (auto it = m_delegates.begin(); it != m_delegates.end(); ++it) {
if (visibleIndices.contains(it.key())) if (visibleIndices.contains(it.key())) continue;
continue;
if (!it->item || vp.isEmpty()) { if (!it->item || vp.isEmpty()) {
toRemove.append(it.key()); toRemove.append(it.key());
continue; continue;
@@ -622,11 +574,9 @@ void LazyListView::syncDelegates() {
std::min(destroyBudget, static_cast<int>(toRemove.size()))); std::min(destroyBudget, static_cast<int>(toRemove.size())));
int destroyed = 0; int destroyed = 0;
for (int idx : toRemove) { for (int idx : toRemove) {
if (destroyed >= destroyBudget) if (destroyed >= destroyBudget) break;
break;
auto entry = m_delegates.take(idx); auto entry = m_delegates.take(idx);
if (entry.item) if (entry.item) m_itemToIndex.remove(entry.item);
m_itemToIndex.remove(entry.item);
removedEntries.append(std::move(entry)); removedEntries.append(std::move(entry));
++destroyed; ++destroyed;
} }
@@ -637,8 +587,7 @@ void LazyListView::syncDelegates() {
QList<int> toCreate; QList<int> toCreate;
if (first >= 0) { if (first >= 0) {
for (int i = first; i <= last; ++i) { for (int i = first; i <= last; ++i) {
if (!m_delegates.contains(i)) if (!m_delegates.contains(i)) toCreate.append(i);
toCreate.append(i);
} }
} }
@@ -647,8 +596,7 @@ void LazyListView::syncDelegates() {
: static_cast<int>(toCreate.size()); : static_cast<int>(toCreate.size());
int created = 0; int created = 0;
for (int i : toCreate) { for (int i : toCreate) {
if (created >= createBudget) if (created >= createBudget) break;
break;
auto entry = createDelegate(i); auto entry = createDelegate(i);
if (entry.item) { if (entry.item) {
@@ -674,25 +622,21 @@ LazyListView::DelegateEntry LazyListView::createDelegate(int modelIndex) {
DelegateEntry entry; DelegateEntry entry;
entry.modelIndex = modelIndex; entry.modelIndex = modelIndex;
if (!m_delegate || !m_model) if (!m_delegate || !m_model) return entry;
return entry;
const auto roleNames = m_model->roleNames(); const auto roleNames = m_model->roleNames();
// Use the delegate component's creation context for beginCreate // Use the delegate component's creation context for beginCreate
// so bound components (pragma ComponentBehavior: Bound) are accepted. // so bound components (pragma ComponentBehavior: Bound) are accepted.
auto* compContext = m_delegate->creationContext(); auto* compContext = m_delegate->creationContext();
if (!compContext) if (!compContext) compContext = qmlContext(this);
compContext = qmlContext(this); if (!compContext) return entry;
if (!compContext)
return entry;
auto* obj = m_delegate->beginCreate(compContext); auto* obj = m_delegate->beginCreate(compContext);
entry.item = qobject_cast<QQuickItem*>(obj); entry.item = qobject_cast<QQuickItem*>(obj);
if (!entry.item) { if (!entry.item) {
if (obj) if (obj) m_delegate->completeCreate();
m_delegate->completeCreate();
delete obj; delete obj;
return entry; return entry;
} }
@@ -705,16 +649,15 @@ LazyListView::DelegateEntry LazyListView::createDelegate(int modelIndex) {
for (auto it = roleNames.constBegin(); it != roleNames.constEnd(); ++it) { for (auto it = roleNames.constBegin(); it != roleNames.constEnd(); ++it) {
const auto name = QString::fromUtf8(it.value()); const auto name = QString::fromUtf8(it.value());
initialProps.insert(name, m_model->data(index, it.key())); initialProps.insert(name, m_model->data(index, it.key()));
if (name == QStringLiteral("modelData")) if (name == QStringLiteral("modelData")) hasModelData = true;
hasModelData = true;
} }
initialProps.insert(QStringLiteral("index"), modelIndex); initialProps.insert(QStringLiteral("index"), modelIndex);
if (!hasModelData) { if (!hasModelData) {
const auto role = roleNames.isEmpty() ? Qt::DisplayRole const auto role = roleNames.isEmpty() ? Qt::DisplayRole
: roleNames.constBegin().key(); : roleNames.constBegin().key();
initialProps.insert(QStringLiteral("modelData"), initialProps.insert(
m_model->data(index, role)); QStringLiteral("modelData"), m_model->data(index, role));
} }
m_delegate->setInitialProperties(entry.item, initialProps); m_delegate->setInitialProperties(entry.item, initialProps);
@@ -728,8 +671,7 @@ LazyListView::DelegateEntry LazyListView::createDelegate(int modelIndex) {
m_layout[modelIndex].isNew) { m_layout[modelIndex].isNew) {
auto* addingAttached = qobject_cast<LazyListViewAttached*>( auto* addingAttached = qobject_cast<LazyListViewAttached*>(
qmlAttachedPropertiesObject<LazyListView>(entry.item, true)); qmlAttachedPropertiesObject<LazyListView>(entry.item, true));
if (addingAttached) if (addingAttached) addingAttached->setAdding(true);
addingAttached->setAdding(true);
} }
m_delegate->completeCreate(); m_delegate->completeCreate();
@@ -740,15 +682,12 @@ LazyListView::DelegateEntry LazyListView::createDelegate(int modelIndex) {
// Height-change handler — uses m_itemToIndex for O(1) lookup. // Height-change handler — uses m_itemToIndex for O(1) lookup.
// Ignored while the delegate is not yet ready. // Ignored while the delegate is not yet ready.
auto onHeightChanged = [this, item = entry.item] { auto onHeightChanged = [this, item = entry.item] {
if (!isDelegateReady(item)) if (!isDelegateReady(item)) return;
return;
auto indexIt = m_itemToIndex.find(item); auto indexIt = m_itemToIndex.find(item);
if (indexIt == m_itemToIndex.end()) if (indexIt == m_itemToIndex.end()) return;
return;
const int idx = indexIt.value(); const int idx = indexIt.value();
auto delegateIt = m_delegates.find(idx); auto delegateIt = m_delegates.find(idx);
if (delegateIt == m_delegates.end() || delegateIt->item != item) if (delegateIt == m_delegates.end() || delegateIt->item != item) return;
return;
const qreal h = delegateHeight(item); const qreal h = delegateHeight(item);
if (idx < static_cast<int>(m_layout.size()) && if (idx < static_cast<int>(m_layout.size()) &&
!qFuzzyCompare(m_layout[idx].height + 1.0, h + 1.0)) { !qFuzzyCompare(m_layout[idx].height + 1.0, h + 1.0)) {
@@ -756,8 +695,7 @@ LazyListView::DelegateEntry LazyListView::createDelegate(int modelIndex) {
const bool wasKnown = m_layout[idx].heightKnown; const bool wasKnown = m_layout[idx].heightKnown;
m_layout[idx].height = h; m_layout[idx].height = h;
m_layout[idx].heightKnown = true; m_layout[idx].heightKnown = true;
if (wasKnown) if (wasKnown) untrackHeight(oldH);
untrackHeight(oldH);
trackHeight(h); trackHeight(h);
// If this tracked item is above the viewport, emit a // If this tracked item is above the viewport, emit a
@@ -785,37 +723,35 @@ LazyListView::DelegateEntry LazyListView::createDelegate(int modelIndex) {
}; };
// Watch implicitHeight as fallback // Watch implicitHeight as fallback
connect(entry.item, connect(
&QQuickItem::implicitHeightChanged, entry.item, &QQuickItem::implicitHeightChanged, this, onHeightChanged);
this,
onHeightChanged);
// Watch attached properties if the delegate uses them // Watch attached properties if the delegate uses them
auto* attached = qobject_cast<LazyListViewAttached*>( auto* attached = qobject_cast<LazyListViewAttached*>(
qmlAttachedPropertiesObject<LazyListView>(entry.item, false)); qmlAttachedPropertiesObject<LazyListView>(entry.item, false));
if (attached) { if (attached) {
connect(attached, connect(
attached,
&LazyListViewAttached::preferredHeightChanged, &LazyListViewAttached::preferredHeightChanged,
this, this,
onHeightChanged); onHeightChanged);
connect(attached, connect(
attached,
&LazyListViewAttached::visibleHeightChanged, &LazyListViewAttached::visibleHeightChanged,
this, this,
[this] { polish(); }); [this] { polish(); });
connect(attached, connect(
attached,
&LazyListViewAttached::readyChanged, &LazyListViewAttached::readyChanged,
this, this,
[this, item = entry.item] { [this, item = entry.item] {
auto indexIt = m_itemToIndex.find(item); auto indexIt = m_itemToIndex.find(item);
if (indexIt == m_itemToIndex.end()) if (indexIt == m_itemToIndex.end()) return;
return;
const int idx = indexIt.value(); const int idx = indexIt.value();
if (idx >= static_cast<int>(m_layout.size())) if (idx >= static_cast<int>(m_layout.size())) return;
return;
auto* att = qobject_cast<LazyListViewAttached*>( auto* att = qobject_cast<LazyListViewAttached*>(
qmlAttachedPropertiesObject<LazyListView>(item, false)); qmlAttachedPropertiesObject<LazyListView>(item, false));
if (!att || !att->ready()) if (!att || !att->ready()) return;
return;
const qreal h = delegateHeight(item); const qreal h = delegateHeight(item);
const qreal oldLayoutH = m_layout[idx].heightKnown const qreal oldLayoutH = m_layout[idx].heightKnown
@@ -852,8 +788,7 @@ void LazyListView::destroyDelegate(DelegateEntry& entry) {
} }
void LazyListView::updateDelegateData(DelegateEntry& entry) { void LazyListView::updateDelegateData(DelegateEntry& entry) {
if (!m_model || !entry.item) if (!m_model || !entry.item) return;
return;
const auto roleNames = m_model->roleNames(); const auto roleNames = m_model->roleNames();
const auto index = m_model->index(entry.modelIndex, 0); const auto index = m_model->index(entry.modelIndex, 0);
@@ -861,10 +796,9 @@ void LazyListView::updateDelegateData(DelegateEntry& entry) {
for (auto it = roleNames.constBegin(); it != roleNames.constEnd(); ++it) { for (auto it = roleNames.constBegin(); it != roleNames.constEnd(); ++it) {
const auto name = QString::fromUtf8(it.value()); const auto name = QString::fromUtf8(it.value());
entry.item->setProperty(name.toUtf8().constData(), entry.item->setProperty(
m_model->data(index, it.key())); name.toUtf8().constData(), m_model->data(index, it.key()));
if (name == QStringLiteral("modelData")) if (name == QStringLiteral("modelData")) hasModelData = true;
hasModelData = true;
} }
entry.item->setProperty("index", entry.modelIndex); entry.item->setProperty("index", entry.modelIndex);
@@ -876,38 +810,42 @@ void LazyListView::updateDelegateData(DelegateEntry& entry) {
} }
} }
// --- Model Connection ---
void LazyListView::connectModel() { void LazyListView::connectModel() {
if (!m_model) if (!m_model) return;
return;
m_modelConnections = { m_modelConnections = {
connect(m_model, connect(
m_model,
&QAbstractItemModel::rowsInserted, &QAbstractItemModel::rowsInserted,
this, this,
&LazyListView::onRowsInserted), &LazyListView::onRowsInserted),
connect(m_model, connect(
m_model,
&QAbstractItemModel::rowsAboutToBeRemoved, &QAbstractItemModel::rowsAboutToBeRemoved,
this, this,
&LazyListView::onRowsAboutToBeRemoved), &LazyListView::onRowsAboutToBeRemoved),
connect(m_model, connect(
m_model,
&QAbstractItemModel::rowsRemoved, &QAbstractItemModel::rowsRemoved,
this, this,
&LazyListView::onRowsRemoved), &LazyListView::onRowsRemoved),
connect(m_model, connect(
m_model,
&QAbstractItemModel::rowsMoved, &QAbstractItemModel::rowsMoved,
this, this,
&LazyListView::onRowsMoved), &LazyListView::onRowsMoved),
connect(m_model, connect(
m_model,
&QAbstractItemModel::dataChanged, &QAbstractItemModel::dataChanged,
this, this,
&LazyListView::onDataChanged), &LazyListView::onDataChanged),
connect(m_model, connect(
m_model,
&QAbstractItemModel::modelReset, &QAbstractItemModel::modelReset,
this, this,
&LazyListView::onModelReset), &LazyListView::onModelReset),
connect(m_model, connect(
m_model,
&QAbstractItemModel::layoutChanged, &QAbstractItemModel::layoutChanged,
this, this,
[this] { [this] {
@@ -915,7 +853,8 @@ void LazyListView::connectModel() {
updateDelegateData(entry); updateDelegateData(entry);
polish(); polish();
}), }),
connect(m_model, connect(
m_model,
&QObject::destroyed, &QObject::destroyed,
this, this,
[this] { [this] {
@@ -962,11 +901,9 @@ void LazyListView::resetContent() {
polish(); polish();
} }
void LazyListView::onRowsInserted(const QModelIndex& parent, void LazyListView::onRowsInserted(
int first, const QModelIndex& parent, int first, int last) {
int last) { if (parent.isValid()) return;
if (parent.isValid())
return;
const int insertCount = last - first + 1; const int insertCount = last - first + 1;
// Insert new layout records // Insert new layout records
@@ -990,19 +927,15 @@ void LazyListView::onRowsInserted(const QModelIndex& parent,
polish(); polish();
} }
void LazyListView::onRowsAboutToBeRemoved(const QModelIndex& parent, void LazyListView::onRowsAboutToBeRemoved(
int first, const QModelIndex& parent, int first, int last) {
int last) { if (parent.isValid()) return;
if (parent.isValid())
return;
for (int i = first; i <= last; ++i) { for (int i = first; i <= last; ++i) {
if (!m_delegates.contains(i)) if (!m_delegates.contains(i)) continue;
continue;
auto entry = m_delegates.take(i); auto entry = m_delegates.take(i);
if (entry.item) if (entry.item) m_itemToIndex.remove(entry.item);
m_itemToIndex.remove(entry.item);
entry.pendingRemoval = true; entry.pendingRemoval = true;
// Never made visible — skip remove animation // Never made visible — skip remove animation
@@ -1014,8 +947,7 @@ void LazyListView::onRowsAboutToBeRemoved(const QModelIndex& parent,
if (m_removeDuration > 0 && entry.item) { if (m_removeDuration > 0 && entry.item) {
auto* attached = qobject_cast<LazyListViewAttached*>( auto* attached = qobject_cast<LazyListViewAttached*>(
qmlAttachedPropertiesObject<LazyListView>(entry.item, false)); qmlAttachedPropertiesObject<LazyListView>(entry.item, false));
if (attached) if (attached) attached->setRemoving(true);
attached->setRemoving(true);
// Schedule destruction after the remove animation duration // Schedule destruction after the remove animation duration
auto* item = entry.item; auto* item = entry.item;
@@ -1037,18 +969,15 @@ void LazyListView::onRowsAboutToBeRemoved(const QModelIndex& parent,
} }
} }
void LazyListView::onRowsRemoved(const QModelIndex& parent, void LazyListView::onRowsRemoved(
int first, const QModelIndex& parent, int first, int last) {
int last) { if (parent.isValid()) return;
if (parent.isValid())
return;
const int removeCount = last - first + 1; const int removeCount = last - first + 1;
// Untrack known heights being removed // Untrack known heights being removed
for (int i = first; i <= last; ++i) { for (int i = first; i <= last; ++i) {
if (m_layout[i].heightKnown) if (m_layout[i].heightKnown) untrackHeight(m_layout[i].height);
untrackHeight(m_layout[i].height);
} }
// Remove layout records // Remove layout records
@@ -1072,13 +1001,13 @@ void LazyListView::onRowsRemoved(const QModelIndex& parent,
polish(); polish();
} }
void LazyListView::onRowsMoved(const QModelIndex& parent, void LazyListView::onRowsMoved(
const QModelIndex& parent,
int start, int start,
int end, int end,
const QModelIndex& destination, const QModelIndex& destination,
int row) { int row) {
if (parent.isValid() || destination.isValid()) if (parent.isValid() || destination.isValid()) return;
return;
const int count = end - start + 1; const int count = end - start + 1;
const int dest = row > start ? row - count : row; const int dest = row > start ? row - count : row;
@@ -1101,10 +1030,8 @@ void LazyListView::onRowsMoved(const QModelIndex& parent,
if (oldIdx >= start && oldIdx <= end) { if (oldIdx >= start && oldIdx <= end) {
newIdx = dest + (oldIdx - start); newIdx = dest + (oldIdx - start);
} else { } else {
if (oldIdx > end) if (oldIdx > end) newIdx -= count;
newIdx -= count; if (newIdx >= dest) newIdx += count;
if (newIdx >= dest)
newIdx += count;
} }
auto entry = std::move(it.value()); auto entry = std::move(it.value());
@@ -1120,17 +1047,16 @@ void LazyListView::onRowsMoved(const QModelIndex& parent,
polish(); polish();
} }
void LazyListView::onDataChanged(const QModelIndex& topLeft, void LazyListView::onDataChanged(
const QModelIndex& topLeft,
const QModelIndex& bottomRight, const QModelIndex& bottomRight,
const QList<int>& roles) { const QList<int>& roles) {
Q_UNUSED(roles) Q_UNUSED(roles)
if (topLeft.parent().isValid()) if (topLeft.parent().isValid()) return;
return;
for (int i = topLeft.row(); i <= bottomRight.row(); ++i) { for (int i = topLeft.row(); i <= bottomRight.row(); ++i) {
if (m_delegates.contains(i)) if (m_delegates.contains(i)) updateDelegateData(m_delegates[i]);
updateDelegateData(m_delegates[i]);
} }
} }
+50 -17
View File
@@ -14,12 +14,18 @@ namespace ZShell::components {
class LazyListViewAttached : public QObject { class LazyListViewAttached : public QObject {
Q_OBJECT Q_OBJECT
Q_PROPERTY(qreal preferredHeight READ preferredHeight WRITE setPreferredHeight NOTIFY preferredHeightChanged) Q_PROPERTY(
Q_PROPERTY(qreal visibleHeight READ visibleHeight WRITE setVisibleHeight NOTIFY visibleHeightChanged) qreal preferredHeight READ preferredHeight WRITE setPreferredHeight
NOTIFY preferredHeightChanged)
Q_PROPERTY(
qreal visibleHeight READ visibleHeight WRITE setVisibleHeight NOTIFY
visibleHeightChanged)
Q_PROPERTY(bool ready READ ready NOTIFY readyChanged) Q_PROPERTY(bool ready READ ready NOTIFY readyChanged)
Q_PROPERTY(bool adding READ adding NOTIFY addingChanged) Q_PROPERTY(bool adding READ adding NOTIFY addingChanged)
Q_PROPERTY(bool removing READ removing NOTIFY removingChanged) Q_PROPERTY(bool removing READ removing NOTIFY removingChanged)
Q_PROPERTY(bool trackViewport READ trackViewport WRITE setTrackViewport NOTIFY trackViewportChanged) Q_PROPERTY(
bool trackViewport READ trackViewport WRITE setTrackViewport NOTIFY
trackViewportChanged)
public: public:
explicit LazyListViewAttached(QObject* parent = nullptr); explicit LazyListViewAttached(QObject* parent = nullptr);
@@ -65,29 +71,47 @@ QML_ELEMENT
QML_ATTACHED(LazyListViewAttached) QML_ATTACHED(LazyListViewAttached)
// Model & Delegate // Model & Delegate
Q_PROPERTY(QAbstractItemModel* model READ model WRITE setModel NOTIFY modelChanged) Q_PROPERTY(
Q_PROPERTY(QQmlComponent* delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) QAbstractItemModel* model READ model WRITE setModel NOTIFY modelChanged)
Q_PROPERTY(
QQmlComponent* delegate READ delegate WRITE setDelegate NOTIFY
delegateChanged)
// Layout // Layout
Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing NOTIFY spacingChanged) Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing NOTIFY spacingChanged)
Q_PROPERTY(qreal contentHeight READ contentHeight NOTIFY contentHeightChanged) Q_PROPERTY(
qreal contentHeight READ contentHeight NOTIFY contentHeightChanged)
Q_PROPERTY(qreal layoutHeight READ layoutHeight NOTIFY layoutHeightChanged) Q_PROPERTY(qreal layoutHeight READ layoutHeight NOTIFY layoutHeightChanged)
Q_PROPERTY(qreal contentY READ contentY WRITE setContentY NOTIFY contentYChanged) Q_PROPERTY(
qreal contentY READ contentY WRITE setContentY NOTIFY contentYChanged)
// Viewport & Lazy Loading // Viewport & Lazy Loading
Q_PROPERTY(QRectF viewport READ viewport WRITE setViewport NOTIFY viewportChanged) Q_PROPERTY(
Q_PROPERTY(bool useCustomViewport READ useCustomViewport WRITE setUseCustomViewport NOTIFY useCustomViewportChanged) QRectF viewport READ viewport WRITE setViewport NOTIFY viewportChanged)
Q_PROPERTY(qreal cacheBuffer READ cacheBuffer WRITE setCacheBuffer NOTIFY cacheBufferChanged) Q_PROPERTY(
bool useCustomViewport READ useCustomViewport WRITE setUseCustomViewport
NOTIFY useCustomViewportChanged)
Q_PROPERTY(
qreal cacheBuffer READ cacheBuffer WRITE setCacheBuffer NOTIFY
cacheBufferChanged)
// Sizing // Sizing
Q_PROPERTY(qreal estimatedHeight READ estimatedHeight WRITE setEstimatedHeight NOTIFY estimatedHeightChanged) Q_PROPERTY(
qreal estimatedHeight READ estimatedHeight WRITE setEstimatedHeight
NOTIFY estimatedHeightChanged)
// Async // Async
Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged) Q_PROPERTY(
bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY
asynchronousChanged)
// Animation Durations // Animation Durations
Q_PROPERTY(int removeDuration READ removeDuration WRITE setRemoveDuration NOTIFY removeDurationChanged) Q_PROPERTY(
Q_PROPERTY(int readyDelay READ readyDelay WRITE setReadyDelay NOTIFY readyDelayChanged) int removeDuration READ removeDuration WRITE setRemoveDuration NOTIFY
removeDurationChanged)
Q_PROPERTY(
int readyDelay READ readyDelay WRITE setReadyDelay NOTIFY
readyDelayChanged)
// State // State
Q_PROPERTY(int count READ count NOTIFY countChanged) Q_PROPERTY(int count READ count NOTIFY countChanged)
@@ -161,7 +185,8 @@ void viewportAdjustNeeded(qreal delta);
protected: protected:
void componentComplete() override; void componentComplete() override;
void geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) override; void geometryChange(
const QRectF& newGeometry, const QRectF& oldGeometry) override;
void updatePolish() override; void updatePolish() override;
private: private:
@@ -204,8 +229,16 @@ void resetContent();
void onRowsInserted(const QModelIndex& parent, int first, int last); void onRowsInserted(const QModelIndex& parent, int first, int last);
void onRowsAboutToBeRemoved(const QModelIndex& parent, int first, int last); void onRowsAboutToBeRemoved(const QModelIndex& parent, int first, int last);
void onRowsRemoved(const QModelIndex& parent, int first, int last); void onRowsRemoved(const QModelIndex& parent, int first, int last);
void onRowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row); void onRowsMoved(
void onDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QList<int>& roles); const QModelIndex& parent,
int start,
int end,
const QModelIndex& destination,
int row);
void onDataChanged(
const QModelIndex& topLeft,
const QModelIndex& bottomRight,
const QList<int>& roles);
void onModelReset(); void onModelReset();
// Members // Members
+6 -2
View File
@@ -194,7 +194,8 @@ void WavyLine::paintLinear(QPainter* painter) {
bool first = true; bool first = true;
for (int x = m_lineWidth / 2; x <= drawEnd; ++x) { for (int x = m_lineWidth / 2; x <= drawEnd; ++x) {
const auto theta = m_frequency * 2 * M_PI * (x + m_startX) / len + phase; const auto theta =
m_frequency * 2 * M_PI * (x + m_startX) / len + phase;
const auto waveY = centerY + amplitude * qSin(theta); const auto waveY = centerY + amplitude * qSin(theta);
if (first) { if (first) {
path.moveTo(x, waveY); path.moveTo(x, waveY);
@@ -215,7 +216,10 @@ void WavyLine::paintArc(QPainter* painter) {
const auto amplitude = m_lineWidth * m_amplitudeMultiplier; const auto amplitude = m_lineWidth * m_amplitudeMultiplier;
const auto cx = width() / 2.0; const auto cx = width() / 2.0;
const auto cy = height() / 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; const auto radius =
m_radius > 0
? m_radius
: (qMin(width(), height()) - m_lineWidth - 2 * amplitude) / 2.0;
if (radius <= 0) { if (radius <= 0) {
return; return;
+29 -15
View File
@@ -9,25 +9,39 @@ class WavyLine : public QQuickPaintedItem {
Q_OBJECT Q_OBJECT
QML_ELEMENT QML_ELEMENT
Q_PROPERTY(int lineWidth READ lineWidth WRITE setLineWidth NOTIFY lineWidthChanged FINAL) Q_PROPERTY(
Q_PROPERTY(qreal amplitudeMultiplier READ amplitudeMultiplier WRITE setAmplitudeMultiplier NOTIFY int lineWidth READ lineWidth WRITE setLineWidth NOTIFY lineWidthChanged
amplitudeMultiplierChanged FINAL) FINAL)
Q_PROPERTY(int frequency READ frequency WRITE setFrequency NOTIFY frequencyChanged FINAL) Q_PROPERTY(
Q_PROPERTY(qreal startX READ startX WRITE setStartX NOTIFY startXChanged FINAL) qreal amplitudeMultiplier READ amplitudeMultiplier WRITE
Q_PROPERTY(qreal fullLength READ fullLength WRITE setFullLength NOTIFY fullLengthChanged FINAL) setAmplitudeMultiplier NOTIFY amplitudeMultiplierChanged FINAL)
Q_PROPERTY(
int frequency READ frequency WRITE setFrequency NOTIFY frequencyChanged
FINAL)
Q_PROPERTY(
qreal startX READ startX WRITE setStartX NOTIFY startXChanged FINAL)
Q_PROPERTY(
qreal fullLength READ fullLength WRITE setFullLength NOTIFY
fullLengthChanged FINAL)
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged FINAL) Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged FINAL)
Q_PROPERTY(qreal waveProgress READ waveProgress WRITE setWaveProgress NOTIFY waveProgressChanged FINAL) Q_PROPERTY(
Q_PROPERTY(PathType pathType READ pathType WRITE setPathType NOTIFY pathTypeChanged FINAL) qreal waveProgress READ waveProgress WRITE setWaveProgress NOTIFY
Q_PROPERTY(qreal startAngle READ startAngle WRITE setStartAngle NOTIFY startAngleChanged FINAL) waveProgressChanged FINAL)
Q_PROPERTY(qreal fullAngle READ fullAngle WRITE setFullAngle NOTIFY fullAngleChanged FINAL) Q_PROPERTY(
Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged FINAL) PathType pathType READ pathType WRITE setPathType NOTIFY pathTypeChanged
FINAL)
Q_PROPERTY(
qreal startAngle READ startAngle WRITE setStartAngle NOTIFY
startAngleChanged FINAL)
Q_PROPERTY(
qreal fullAngle READ fullAngle WRITE setFullAngle NOTIFY
fullAngleChanged FINAL)
Q_PROPERTY(
qreal radius READ radius WRITE setRadius NOTIFY radiusChanged FINAL)
Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY valueChanged FINAL) Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY valueChanged FINAL)
public: public:
enum PathType { enum PathType { Linear, Arc };
Linear,
Arc
};
Q_ENUM(PathType) Q_ENUM(PathType)
explicit WavyLine(QQuickItem* parent = nullptr); explicit WavyLine(QQuickItem* parent = nullptr);
+9 -15
View File
@@ -6,8 +6,7 @@
namespace ZShell::internal { namespace ZShell::internal {
ArcGauge::ArcGauge(QQuickItem* parent) ArcGauge::ArcGauge(QQuickItem* parent) : QQuickPaintedItem(parent) {
: QQuickPaintedItem(parent) {
setAntialiasing(true); setAntialiasing(true);
} }
@@ -36,7 +35,8 @@ void ArcGauge::paint(QPainter* painter) {
// Draw value arc // Draw value arc
if (m_percentage > 0.0) { if (m_percentage > 0.0) {
const int valueSweep16 = qRound(static_cast<qreal>(sweepAngle16) * m_percentage); const int valueSweep16 =
qRound(static_cast<qreal>(sweepAngle16) * m_percentage);
QPen valuePen(m_accentColor, m_lineWidth); QPen valuePen(m_accentColor, m_lineWidth);
valuePen.setCapStyle(Qt::RoundCap); valuePen.setCapStyle(Qt::RoundCap);
painter->setPen(valuePen); painter->setPen(valuePen);
@@ -49,8 +49,7 @@ qreal ArcGauge::percentage() const {
} }
void ArcGauge::setPercentage(qreal percentage) { void ArcGauge::setPercentage(qreal percentage) {
if (qFuzzyCompare(m_percentage, percentage)) if (qFuzzyCompare(m_percentage, percentage)) return;
return;
m_percentage = percentage; m_percentage = percentage;
emit percentageChanged(); emit percentageChanged();
update(); update();
@@ -61,8 +60,7 @@ QColor ArcGauge::accentColor() const {
} }
void ArcGauge::setAccentColor(const QColor& color) { void ArcGauge::setAccentColor(const QColor& color) {
if (m_accentColor == color) if (m_accentColor == color) return;
return;
m_accentColor = color; m_accentColor = color;
emit accentColorChanged(); emit accentColorChanged();
update(); update();
@@ -73,8 +71,7 @@ QColor ArcGauge::trackColor() const {
} }
void ArcGauge::setTrackColor(const QColor& color) { void ArcGauge::setTrackColor(const QColor& color) {
if (m_trackColor == color) if (m_trackColor == color) return;
return;
m_trackColor = color; m_trackColor = color;
emit trackColorChanged(); emit trackColorChanged();
update(); update();
@@ -85,8 +82,7 @@ qreal ArcGauge::startAngle() const {
} }
void ArcGauge::setStartAngle(qreal angle) { void ArcGauge::setStartAngle(qreal angle) {
if (qFuzzyCompare(m_startAngle, angle)) if (qFuzzyCompare(m_startAngle, angle)) return;
return;
m_startAngle = angle; m_startAngle = angle;
emit startAngleChanged(); emit startAngleChanged();
update(); update();
@@ -97,8 +93,7 @@ qreal ArcGauge::sweepAngle() const {
} }
void ArcGauge::setSweepAngle(qreal angle) { void ArcGauge::setSweepAngle(qreal angle) {
if (qFuzzyCompare(m_sweepAngle, angle)) if (qFuzzyCompare(m_sweepAngle, angle)) return;
return;
m_sweepAngle = angle; m_sweepAngle = angle;
emit sweepAngleChanged(); emit sweepAngleChanged();
update(); update();
@@ -109,8 +104,7 @@ qreal ArcGauge::lineWidth() const {
} }
void ArcGauge::setLineWidth(qreal width) { void ArcGauge::setLineWidth(qreal width) {
if (qFuzzyCompare(m_lineWidth, width)) if (qFuzzyCompare(m_lineWidth, width)) return;
return;
m_lineWidth = width; m_lineWidth = width;
emit lineWidthChanged(); emit lineWidthChanged();
update(); update();
+19 -7
View File
@@ -11,12 +11,24 @@ class ArcGauge : public QQuickPaintedItem {
Q_OBJECT Q_OBJECT
QML_ELEMENT QML_ELEMENT
Q_PROPERTY(qreal percentage READ percentage WRITE setPercentage NOTIFY percentageChanged) Q_PROPERTY(
Q_PROPERTY(QColor accentColor READ accentColor WRITE setAccentColor NOTIFY accentColorChanged) qreal percentage READ percentage WRITE setPercentage NOTIFY
Q_PROPERTY(QColor trackColor READ trackColor WRITE setTrackColor NOTIFY trackColorChanged) percentageChanged)
Q_PROPERTY(qreal startAngle READ startAngle WRITE setStartAngle NOTIFY startAngleChanged) Q_PROPERTY(
Q_PROPERTY(qreal sweepAngle READ sweepAngle WRITE setSweepAngle NOTIFY sweepAngleChanged) QColor accentColor READ accentColor WRITE setAccentColor NOTIFY
Q_PROPERTY(qreal lineWidth READ lineWidth WRITE setLineWidth NOTIFY lineWidthChanged) accentColorChanged)
Q_PROPERTY(
QColor trackColor READ trackColor WRITE setTrackColor NOTIFY
trackColorChanged)
Q_PROPERTY(
qreal startAngle READ startAngle WRITE setStartAngle NOTIFY
startAngleChanged)
Q_PROPERTY(
qreal sweepAngle READ sweepAngle WRITE setSweepAngle NOTIFY
sweepAngleChanged)
Q_PROPERTY(
qreal lineWidth READ lineWidth WRITE setLineWidth NOTIFY
lineWidthChanged)
public: public:
explicit ArcGauge(QQuickItem* parent = nullptr); explicit ArcGauge(QQuickItem* parent = nullptr);
@@ -58,4 +70,4 @@ qreal m_sweepAngle = 1.5 * M_PI;
qreal m_lineWidth = 10.0; qreal m_lineWidth = 10.0;
}; };
} // namespace ZShell::Internal } // namespace ZShell::internal
+33 -13
View File
@@ -25,7 +25,8 @@ QSize CachingImageManager::effectiveSize() const {
} }
const qreal scale = effectiveScale(); const qreal scale = effectiveScale();
const QSize size = QSizeF(m_item->width() * scale, m_item->height() * scale).toSize(); const QSize size =
QSizeF(m_item->width() * scale, m_item->height() * scale).toSize();
m_item->setProperty("sourceSize", size); m_item->setProperty("sourceSize", size);
return size; return size;
} }
@@ -53,7 +54,8 @@ void CachingImageManager::setItem(QQuickItem* item) {
m_widthConn = connect(item, &QQuickItem::widthChanged, this, [this]() { m_widthConn = connect(item, &QQuickItem::widthChanged, this, [this]() {
updateSource(); updateSource();
}); });
m_heightConn = connect(item, &QQuickItem::heightChanged, this, [this]() { m_heightConn =
connect(item, &QQuickItem::heightChanged, this, [this]() {
updateSource(); updateSource();
}); });
updateSource(); updateSource();
@@ -105,11 +107,16 @@ void CachingImageManager::updateSource(const QString& path) {
m_shaPath = path; m_shaPath = path;
const auto future = QtConcurrent::run(&CachingImageManager::sha256sum, path); const auto future =
QtConcurrent::run(&CachingImageManager::sha256sum, path);
const auto watcher = new QFutureWatcher<QString>(this); const auto watcher = new QFutureWatcher<QString>(this);
connect(watcher, &QFutureWatcher<QString>::finished, this, [watcher, path, this]() { connect(
watcher,
&QFutureWatcher<QString>::finished,
this,
[watcher, path, this]() {
if (m_path != path) { if (m_path != path) {
// Object is destroyed or path has changed, ignore // Object is destroyed or path has changed, ignore
watcher->deleteLater(); watcher->deleteLater();
@@ -140,7 +147,8 @@ void CachingImageManager::updateSource(const QString& path) {
emit cachePathChanged(); emit cachePathChanged();
if (!cache.isLocalFile()) { if (!cache.isLocalFile()) {
qWarning() << "CachingImageManager::updateSource: cachePath" << cache << "is not a local file"; qWarning() << "CachingImageManager::updateSource: cachePath"
<< cache << "is not a local file";
watcher->deleteLater(); watcher->deleteLater();
return; return;
} }
@@ -169,31 +177,42 @@ QUrl CachingImageManager::cachePath() const {
} }
void CachingImageManager::createCache( void CachingImageManager::createCache(
const QString& path, const QString& cache, const QString& fillMode, const QSize& size) const { const QString& path,
const QString& cache,
const QString& fillMode,
const QSize& size) const {
QThreadPool::globalInstance()->start([path, cache, fillMode, size] { QThreadPool::globalInstance()->start([path, cache, fillMode, size] {
QImage image(path); QImage image(path);
if (image.isNull()) { if (image.isNull()) {
qWarning() << "CachingImageManager::createCache: failed to read" << path; qWarning() << "CachingImageManager::createCache: failed to read"
<< path;
return; return;
} }
image.convertTo(QImage::Format_ARGB32); image.convertTo(QImage::Format_ARGB32);
if (fillMode == "PreserveAspectCrop") { if (fillMode == "PreserveAspectCrop") {
image = image.scaled(size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); image = image.scaled(
size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
} else if (fillMode == "PreserveAspectFit") { } else if (fillMode == "PreserveAspectFit") {
image = image.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation); image = image.scaled(
size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
} else { } else {
image = image.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); image = image.scaled(
size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
} }
if (fillMode == "PreserveAspectCrop" || fillMode == "PreserveAspectFit") { if (fillMode == "PreserveAspectCrop" ||
fillMode == "PreserveAspectFit") {
QImage canvas(size, QImage::Format_ARGB32); QImage canvas(size, QImage::Format_ARGB32);
canvas.fill(Qt::transparent); canvas.fill(Qt::transparent);
QPainter painter(&canvas); QPainter painter(&canvas);
painter.drawImage((size.width() - image.width()) / 2, (size.height() - image.height()) / 2, image); painter.drawImage(
(size.width() - image.width()) / 2,
(size.height() - image.height()) / 2,
image);
painter.end(); painter.end();
image = canvas; image = canvas;
@@ -201,7 +220,8 @@ void CachingImageManager::createCache(
const QString parent = QFileInfo(cache).absolutePath(); const QString parent = QFileInfo(cache).absolutePath();
if (!QDir().mkpath(parent) || !image.save(cache)) { if (!QDir().mkpath(parent) || !image.save(cache)) {
qWarning() << "CachingImageManager::createCache: failed to save to" << cache; qWarning() << "CachingImageManager::createCache: failed to save to"
<< cache;
} }
}); });
} }
@@ -10,17 +10,18 @@ class CachingImageManager : public QObject {
Q_OBJECT Q_OBJECT
QML_ELEMENT QML_ELEMENT
Q_PROPERTY(QQuickItem* item READ item WRITE setItem NOTIFY itemChanged REQUIRED) Q_PROPERTY(
Q_PROPERTY(QUrl cacheDir READ cacheDir WRITE setCacheDir NOTIFY cacheDirChanged REQUIRED) QQuickItem* item READ item WRITE setItem NOTIFY itemChanged REQUIRED)
Q_PROPERTY(
QUrl cacheDir READ cacheDir WRITE setCacheDir NOTIFY cacheDirChanged
REQUIRED)
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged) Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
Q_PROPERTY(QUrl cachePath READ cachePath NOTIFY cachePathChanged) Q_PROPERTY(QUrl cachePath READ cachePath NOTIFY cachePathChanged)
public: public:
explicit CachingImageManager(QObject* parent = nullptr) explicit CachingImageManager(QObject* parent = nullptr)
: QObject(parent) : QObject(parent), m_item(nullptr) {}
, m_item(nullptr) {
}
[[nodiscard]] QQuickItem* item() const; [[nodiscard]] QQuickItem* item() const;
void setItem(QQuickItem* item); void setItem(QQuickItem* item);
@@ -59,7 +60,11 @@ QMetaObject::Connection m_heightConn;
[[nodiscard]] qreal effectiveScale() const; [[nodiscard]] qreal effectiveScale() const;
[[nodiscard]] QSize effectiveSize() const; [[nodiscard]] QSize effectiveSize() const;
void createCache(const QString& path, const QString& cache, const QString& fillMode, const QSize& size) const; void createCache(
const QString& path,
const QString& cache,
const QString& fillMode,
const QSize& size) const;
[[nodiscard]] static QString sha256sum(const QString& path); [[nodiscard]] static QString sha256sum(const QString& path);
}; };
+9 -16
View File
@@ -4,19 +4,15 @@
namespace ZShell::internal { namespace ZShell::internal {
CircularBuffer::CircularBuffer(QObject* parent) CircularBuffer::CircularBuffer(QObject* parent) : QObject(parent) {}
: QObject(parent) {
}
int CircularBuffer::capacity() const { int CircularBuffer::capacity() const {
return m_capacity; return m_capacity;
} }
void CircularBuffer::setCapacity(int capacity) { void CircularBuffer::setCapacity(int capacity) {
if (capacity < 0) if (capacity < 0) capacity = 0;
capacity = 0; if (m_capacity == capacity) return;
if (m_capacity == capacity)
return;
const auto old = values(); const auto old = values();
@@ -52,8 +48,7 @@ QList<qreal> CircularBuffer::values() const {
} }
qreal CircularBuffer::maximum() const { qreal CircularBuffer::maximum() const {
if (m_count == 0) if (m_count == 0) return 0.0;
return 0.0;
qreal maxVal = at(0); qreal maxVal = at(0);
for (int i = 1; i < m_count; ++i) for (int i = 1; i < m_count; ++i)
@@ -62,8 +57,7 @@ qreal CircularBuffer::maximum() const {
} }
void CircularBuffer::push(qreal value) { void CircularBuffer::push(qreal value) {
if (m_capacity <= 0) if (m_capacity <= 0) return;
return;
m_data[m_head] = value; m_data[m_head] = value;
m_head = (m_head + 1) % m_capacity; m_head = (m_head + 1) % m_capacity;
@@ -75,8 +69,7 @@ void CircularBuffer::push(qreal value) {
} }
void CircularBuffer::clear() { void CircularBuffer::clear() {
if (m_count == 0) if (m_count == 0) return;
return;
m_head = 0; m_head = 0;
m_count = 0; m_count = 0;
@@ -85,10 +78,10 @@ void CircularBuffer::clear() {
} }
qreal CircularBuffer::at(int index) const { qreal CircularBuffer::at(int index) const {
if (index < 0 || index >= m_count) if (index < 0 || index >= m_count) return 0.0;
return 0.0;
const int actualIndex = (m_head - m_count + index + m_capacity) % m_capacity; const int actualIndex =
(m_head - m_count + index + m_capacity) % m_capacity;
return m_data[actualIndex]; return m_data[actualIndex];
} }
+2 -1
View File
@@ -10,7 +10,8 @@ class CircularBuffer : public QObject {
Q_OBJECT Q_OBJECT
QML_ELEMENT QML_ELEMENT
Q_PROPERTY(int capacity READ capacity WRITE setCapacity NOTIFY capacityChanged) Q_PROPERTY(
int capacity READ capacity WRITE setCapacity NOTIFY capacityChanged)
Q_PROPERTY(int count READ count NOTIFY countChanged) Q_PROPERTY(int count READ count NOTIFY countChanged)
Q_PROPERTY(QList<qreal> values READ values NOTIFY valuesChanged) Q_PROPERTY(QList<qreal> values READ values NOTIFY valuesChanged)
Q_PROPERTY(qreal maximum READ maximum NOTIFY valuesChanged) Q_PROPERTY(qreal maximum READ maximum NOTIFY valuesChanged)
@@ -15,8 +15,10 @@ constexpr qint32 TAIL_DEGREES_OFFSET = -20;
constexpr qint32 EXTRA_DEGREES_PER_CYCLE = 250; constexpr qint32 EXTRA_DEGREES_PER_CYCLE = 250;
constexpr qint32 CONSTANT_ROTATION_DEGREES = 1520; constexpr qint32 CONSTANT_ROTATION_DEGREES = 1520;
constexpr std::array<qint32, TOTAL_CYCLES> DELAY_TO_EXPAND_IN_MS = { 0, 1350, 2700, 4050 }; constexpr std::array<qint32, TOTAL_CYCLES> DELAY_TO_EXPAND_IN_MS =
constexpr std::array<qint32, TOTAL_CYCLES> DELAY_TO_COLLAPSE_IN_MS = { 667, 2017, 3367, 4717 }; {0, 1350, 2700, 4050};
constexpr std::array<qint32, TOTAL_CYCLES> DELAY_TO_COLLAPSE_IN_MS = {
667, 2017, 3367, 4717};
} // namespace advance } // namespace advance
@@ -100,11 +102,13 @@ qreal CircularIndicatorManager::completeEndDuration() const {
} }
} }
CircularIndicatorManager::IndeterminateAnimationType CircularIndicatorManager::indeterminateAnimationType() const { CircularIndicatorManager::IndeterminateAnimationType
CircularIndicatorManager::indeterminateAnimationType() const {
return m_type; return m_type;
} }
void CircularIndicatorManager::setIndeterminateAnimationType(IndeterminateAnimationType t) { void CircularIndicatorManager::setIndeterminateAnimationType(
IndeterminateAnimationType t) {
if (m_type != t) { if (m_type != t) {
m_type = t; m_type = t;
emit indeterminateAnimationTypeChanged(); emit indeterminateAnimationTypeChanged();
@@ -150,24 +154,27 @@ void CircularIndicatorManager::updateRetreat(qreal progress) {
// Extra rotation for the faster spinning. // Extra rotation for the faster spinning.
qreal spinRotation = 0; qreal spinRotation = 0;
for (const int spinDelay : DELAY_SPINS_IN_MS) { for (const int spinDelay : DELAY_SPINS_IN_MS) {
spinRotation += m_curve.valueForProgress(getFractionInRange(playtime, spinDelay, DURATION_SPIN_IN_MS)) * spinRotation +=
m_curve.valueForProgress(
getFractionInRange(playtime, spinDelay, DURATION_SPIN_IN_MS)) *
SPIN_ROTATION_DEGREES; SPIN_ROTATION_DEGREES;
} }
m_rotation = constantRotation + spinRotation; m_rotation = constantRotation + spinRotation;
emit rotationChanged(); emit rotationChanged();
// Grow active indicator. // Grow active indicator.
qreal fraction = qreal fraction = m_curve.valueForProgress(getFractionInRange(
m_curve.valueForProgress(getFractionInRange(playtime, DELAY_GROW_ACTIVE_IN_MS, DURATION_GROW_ACTIVE_IN_MS)); playtime, DELAY_GROW_ACTIVE_IN_MS, DURATION_GROW_ACTIVE_IN_MS));
fraction -= fraction -= m_curve.valueForProgress(getFractionInRange(
m_curve.valueForProgress(getFractionInRange(playtime, DELAY_SHRINK_ACTIVE_IN_MS, DURATION_SHRINK_ACTIVE_IN_MS)); playtime, DELAY_SHRINK_ACTIVE_IN_MS, DURATION_SHRINK_ACTIVE_IN_MS));
if (!qFuzzyIsNull(m_startFraction)) { if (!qFuzzyIsNull(m_startFraction)) {
m_startFraction = 0.0; m_startFraction = 0.0;
emit startFractionChanged(); emit startFractionChanged();
} }
const auto oldEndFrac = m_endFraction; const auto oldEndFrac = m_endFraction;
m_endFraction = std::lerp(END_FRACTION_RANGE[0], END_FRACTION_RANGE[1], fraction); m_endFraction =
std::lerp(END_FRACTION_RANGE[0], END_FRACTION_RANGE[1], fraction);
// Completing animation. // Completing animation.
if (m_completeEndProgress > 0) { if (m_completeEndProgress > 0) {
@@ -184,22 +191,32 @@ void CircularIndicatorManager::updateAdvance(qreal progress) {
const auto playtime = progress * TOTAL_DURATION_IN_MS; const auto playtime = progress * TOTAL_DURATION_IN_MS;
// Adds constant rotation to segment positions. // Adds constant rotation to segment positions.
m_startFraction = CONSTANT_ROTATION_DEGREES * progress + TAIL_DEGREES_OFFSET; m_startFraction =
CONSTANT_ROTATION_DEGREES * progress + TAIL_DEGREES_OFFSET;
m_endFraction = CONSTANT_ROTATION_DEGREES * progress; m_endFraction = CONSTANT_ROTATION_DEGREES * progress;
// Adds cycle specific rotation to segment positions. // Adds cycle specific rotation to segment positions.
for (size_t cycleIndex = 0; cycleIndex < TOTAL_CYCLES; ++cycleIndex) { for (size_t cycleIndex = 0; cycleIndex < TOTAL_CYCLES; ++cycleIndex) {
// While expanding. // While expanding.
qreal fraction = getFractionInRange(playtime, DELAY_TO_EXPAND_IN_MS[cycleIndex], DURATION_TO_EXPAND_IN_MS); qreal fraction = getFractionInRange(
m_endFraction += m_curve.valueForProgress(fraction) * EXTRA_DEGREES_PER_CYCLE; playtime,
DELAY_TO_EXPAND_IN_MS[cycleIndex],
DURATION_TO_EXPAND_IN_MS);
m_endFraction +=
m_curve.valueForProgress(fraction) * EXTRA_DEGREES_PER_CYCLE;
// While collapsing. // While collapsing.
fraction = getFractionInRange(playtime, DELAY_TO_COLLAPSE_IN_MS[cycleIndex], DURATION_TO_COLLAPSE_IN_MS); fraction = getFractionInRange(
m_startFraction += m_curve.valueForProgress(fraction) * EXTRA_DEGREES_PER_CYCLE; playtime,
DELAY_TO_COLLAPSE_IN_MS[cycleIndex],
DURATION_TO_COLLAPSE_IN_MS);
m_startFraction +=
m_curve.valueForProgress(fraction) * EXTRA_DEGREES_PER_CYCLE;
} }
// Closes the gap between head and tail for complete end. // Closes the gap between head and tail for complete end.
m_startFraction += (m_endFraction - m_startFraction) * m_completeEndProgress; m_startFraction +=
(m_endFraction - m_startFraction) * m_completeEndProgress;
m_startFraction /= 360.0; m_startFraction /= 360.0;
m_endFraction /= 360.0; m_endFraction /= 360.0;
@@ -1,6 +1,5 @@
#pragma once #pragma once
#include <cstdint>
#include <qeasingcurve.h> #include <qeasingcurve.h>
#include <qobject.h> #include <qobject.h>
#include <qqmlintegration.h> #include <qqmlintegration.h>
@@ -11,24 +10,29 @@ class CircularIndicatorManager : public QObject {
Q_OBJECT Q_OBJECT
QML_ELEMENT QML_ELEMENT
Q_PROPERTY(qreal startFraction READ startFraction NOTIFY startFractionChanged) Q_PROPERTY(
qreal startFraction READ startFraction NOTIFY startFractionChanged)
Q_PROPERTY(qreal endFraction READ endFraction NOTIFY endFractionChanged) Q_PROPERTY(qreal endFraction READ endFraction NOTIFY endFractionChanged)
Q_PROPERTY(qreal rotation READ rotation NOTIFY rotationChanged) Q_PROPERTY(qreal rotation READ rotation NOTIFY rotationChanged)
Q_PROPERTY(qreal progress READ progress WRITE setProgress NOTIFY progressChanged) Q_PROPERTY(
Q_PROPERTY(qreal completeEndProgress READ completeEndProgress WRITE setCompleteEndProgress NOTIFY qreal progress READ progress WRITE setProgress NOTIFY progressChanged)
completeEndProgressChanged) Q_PROPERTY(
Q_PROPERTY(qreal duration READ duration NOTIFY indeterminateAnimationTypeChanged) qreal completeEndProgress READ completeEndProgress WRITE
Q_PROPERTY(qreal completeEndDuration READ completeEndDuration NOTIFY indeterminateAnimationTypeChanged) setCompleteEndProgress NOTIFY completeEndProgressChanged)
Q_PROPERTY(IndeterminateAnimationType indeterminateAnimationType READ indeterminateAnimationType WRITE Q_PROPERTY(
setIndeterminateAnimationType NOTIFY indeterminateAnimationTypeChanged) qreal duration READ duration NOTIFY indeterminateAnimationTypeChanged)
Q_PROPERTY(
qreal completeEndDuration READ completeEndDuration NOTIFY
indeterminateAnimationTypeChanged)
Q_PROPERTY(
IndeterminateAnimationType indeterminateAnimationType READ
indeterminateAnimationType WRITE setIndeterminateAnimationType
NOTIFY indeterminateAnimationTypeChanged)
public: public:
explicit CircularIndicatorManager(QObject* parent = nullptr); explicit CircularIndicatorManager(QObject* parent = nullptr);
enum IndeterminateAnimationType { enum IndeterminateAnimationType { Advance = 0, Retreat };
Advance = 0,
Retreat
};
Q_ENUM(IndeterminateAnimationType) Q_ENUM(IndeterminateAnimationType)
[[nodiscard]] qreal startFraction() const; [[nodiscard]] qreal startFraction() const;
+10 -9
View File
@@ -5,8 +5,7 @@
namespace ZShell::internal::hypr { namespace ZShell::internal::hypr {
HyprKeyboard::HyprKeyboard(QJsonObject ipcObject, QObject* parent) HyprKeyboard::HyprKeyboard(QJsonObject ipcObject, QObject* parent)
: QObject(parent) : QObject(parent), m_lastIpcObject(ipcObject) {}
, m_lastIpcObject(ipcObject) {}
QVariantHash HyprKeyboard::lastIpcObject() const { QVariantHash HyprKeyboard::lastIpcObject() const {
return m_lastIpcObject.toVariantHash(); return m_lastIpcObject.toVariantHash();
@@ -82,8 +81,7 @@ bool HyprKeyboard::updateLastIpcObject(QJsonObject object) {
return dirty; return dirty;
} }
HyprDevices::HyprDevices(QObject* parent) HyprDevices::HyprDevices(QObject* parent) : QObject(parent) {}
: QObject(parent) {}
QQmlListProperty<HyprKeyboard> HyprDevices::keyboards() { QQmlListProperty<HyprKeyboard> HyprDevices::keyboards() {
return QQmlListProperty<HyprKeyboard>(this, &m_keyboards); return QQmlListProperty<HyprKeyboard>(this, &m_keyboards);
@@ -95,8 +93,10 @@ bool HyprDevices::updateLastIpcObject(QJsonObject object) {
for (auto it = m_keyboards.begin(); it != m_keyboards.end();) { for (auto it = m_keyboards.begin(); it != m_keyboards.end();) {
auto* const keyboard = *it; auto* const keyboard = *it;
const auto inNewValues = std::any_of(val.begin(), val.end(), [keyboard](const QJsonValue& o) { const auto inNewValues =
return o.toObject().value("address").toString() == keyboard->address(); std::any_of(val.begin(), val.end(), [keyboard](const QJsonValue& o) {
return o.toObject().value("address").toString() ==
keyboard->address();
}); });
if (!inNewValues) { if (!inNewValues) {
@@ -112,9 +112,10 @@ bool HyprDevices::updateLastIpcObject(QJsonObject object) {
const auto obj = o.toObject(); const auto obj = o.toObject();
const auto addr = obj.value("address").toString(); const auto addr = obj.value("address").toString();
auto it = std::find_if(m_keyboards.begin(), m_keyboards.end(), [addr](const HyprKeyboard* kb) { auto it = std::find_if(
return kb->address() == addr; m_keyboards.begin(),
}); m_keyboards.end(),
[addr](const HyprKeyboard* kb) { return kb->address() == addr; });
if (it != m_keyboards.end()) { if (it != m_keyboards.end()) {
dirty |= (*it)->updateLastIpcObject(obj); dirty |= (*it)->updateLastIpcObject(obj);
+10 -5
View File
@@ -10,9 +10,12 @@ namespace ZShell::internal::hypr {
class HyprKeyboard : public QObject { class HyprKeyboard : public QObject {
Q_OBJECT Q_OBJECT
QML_ELEMENT QML_ELEMENT
QML_UNCREATABLE("HyprKeyboard instances can only be retrieved from a HyprDevices") QML_UNCREATABLE(
"HyprKeyboard instances can only be retrieved from a HyprDevices")
Q_PROPERTY(QVariantHash lastIpcObject READ lastIpcObject NOTIFY lastIpcObjectChanged) Q_PROPERTY(
QVariantHash lastIpcObject READ lastIpcObject NOTIFY
lastIpcObjectChanged)
Q_PROPERTY(QString address READ address NOTIFY addressChanged) Q_PROPERTY(QString address READ address NOTIFY addressChanged)
Q_PROPERTY(QString name READ name NOTIFY nameChanged) Q_PROPERTY(QString name READ name NOTIFY nameChanged)
Q_PROPERTY(QString layout READ layout NOTIFY layoutChanged) Q_PROPERTY(QString layout READ layout NOTIFY layoutChanged)
@@ -52,10 +55,12 @@ QJsonObject m_lastIpcObject;
class HyprDevices : public QObject { class HyprDevices : public QObject {
Q_OBJECT Q_OBJECT
QML_ELEMENT QML_ELEMENT
QML_UNCREATABLE("HyprDevices instances can only be retrieved from a HyprExtras") QML_UNCREATABLE(
"HyprDevices instances can only be retrieved from a HyprExtras")
Q_PROPERTY( Q_PROPERTY(
QQmlListProperty<ZShell::internal::hypr::HyprKeyboard> keyboards READ keyboards NOTIFY keyboardsChanged) QQmlListProperty<ZShell::internal::hypr::HyprKeyboard> keyboards READ
keyboards NOTIFY keyboardsChanged)
public: public:
explicit HyprDevices(QObject* parent = nullptr); explicit HyprDevices(QObject* parent = nullptr);
@@ -71,4 +76,4 @@ private:
QList<HyprKeyboard*> m_keyboards; QList<HyprKeyboard*> m_keyboards;
}; };
} // namespace ZShell::internal::hypr }
+85 -37
View File
@@ -64,7 +64,8 @@ static QString luaArray(const QVariantList& list) {
parts << luaValue(item); parts << luaValue(item);
} }
return QLatin1String("{ ") + parts.join(QLatin1String(", ")) + QLatin1String(" }"); return QLatin1String("{ ") + parts.join(QLatin1String(", ")) +
QLatin1String(" }");
} }
static QString luaArray(const QStringList& list) { static QString luaArray(const QStringList& list) {
@@ -75,7 +76,8 @@ static QString luaArray(const QStringList& list) {
parts << luaEscapeString(item); parts << luaEscapeString(item);
} }
return QLatin1String("{ ") + parts.join(QLatin1String(", ")) + QLatin1String(" }"); return QLatin1String("{ ") + parts.join(QLatin1String(", ")) +
QLatin1String(" }");
} }
static QString luaMapFromHash(const QVariantHash& hash) { static QString luaMapFromHash(const QVariantHash& hash) {
@@ -83,10 +85,12 @@ static QString luaMapFromHash(const QVariantHash& hash) {
parts.reserve(hash.size()); parts.reserve(hash.size());
for (auto it = hash.cbegin(); it != hash.cend(); ++it) { for (auto it = hash.cbegin(); it != hash.cend(); ++it) {
parts << luaEscapeString(it.key()) + QLatin1String(" = ") + luaValue(it.value()); parts << luaEscapeString(it.key()) + QLatin1String(" = ") +
luaValue(it.value());
} }
return QLatin1String("{ ") + parts.join(QLatin1String(", ")) + QLatin1String(" }"); return QLatin1String("{ ") + parts.join(QLatin1String(", ")) +
QLatin1String(" }");
} }
static QString luaMap(const QVariantMap& map) { static QString luaMap(const QVariantMap& map) {
@@ -94,10 +98,12 @@ static QString luaMap(const QVariantMap& map) {
parts.reserve(map.size()); parts.reserve(map.size());
for (auto it = map.cbegin(); it != map.cend(); ++it) { for (auto it = map.cbegin(); it != map.cend(); ++it) {
parts << luaEscapeString(it.key()) + QLatin1String(" = ") + luaValue(it.value()); parts << luaEscapeString(it.key()) + QLatin1String(" = ") +
luaValue(it.value());
} }
return QLatin1String("{ ") + parts.join(QLatin1String(", ")) + QLatin1String(" }"); return QLatin1String("{ ") + parts.join(QLatin1String(", ")) +
QLatin1String(" }");
} }
static QString luaValue(const QVariant& v) { static QString luaValue(const QVariant& v) {
@@ -143,7 +149,8 @@ static QString normalizeOptionPath(QString key) {
} }
static QString buildHlConfigCall(const QString& key, const QVariant& value) { static QString buildHlConfigCall(const QString& key, const QVariant& value) {
const auto parts = normalizeOptionPath(key).split(QLatin1Char('.'), Qt::SkipEmptyParts); const auto parts =
normalizeOptionPath(key).split(QLatin1Char('.'), Qt::SkipEmptyParts);
if (parts.isEmpty()) { if (parts.isEmpty()) {
return {}; return {};
} }
@@ -189,7 +196,8 @@ static QVariant parseGetOptionValue(const QJsonObject& obj) {
const auto option = obj.value(QStringLiteral("option")).toString(); const auto option = obj.value(QStringLiteral("option")).toString();
if (option.contains(QStringLiteral("color")) || option.contains(QStringLiteral("col."))) { if (option.contains(QStringLiteral("color")) ||
option.contains(QStringLiteral("col."))) {
return colorFromInt(static_cast<quint32>(value)); return colorFromInt(static_cast<quint32>(value));
} }
@@ -234,7 +242,8 @@ static QVariant parseGetOptionValue(const QJsonObject& obj) {
return {}; return {};
} }
static void insertNestedValue(QVariantMap& root, const QStringList& path, const QVariant& value) { static void insertNestedValue(
QVariantMap& root, const QStringList& path, const QVariant& value) {
if (path.isEmpty()) { if (path.isEmpty()) {
return; return;
} }
@@ -261,16 +270,19 @@ HyprExtras::HyprExtras(QObject* parent)
, m_devices(new HyprDevices(this)) { , m_devices(new HyprDevices(this)) {
const auto his = qEnvironmentVariable("HYPRLAND_INSTANCE_SIGNATURE"); const auto his = qEnvironmentVariable("HYPRLAND_INSTANCE_SIGNATURE");
if (his.isEmpty()) { if (his.isEmpty()) {
qCWarning(lcHypr) << "$HYPRLAND_INSTANCE_SIGNATURE is unset. Unable to connect to Hyprland socket."; qCWarning(lcHypr) << "$HYPRLAND_INSTANCE_SIGNATURE is unset. Unable to "
"connect to Hyprland socket.";
return; return;
} }
auto hyprDir = QString("%1/hypr/%2").arg(qEnvironmentVariable("XDG_RUNTIME_DIR"), his); auto hyprDir =
QString("%1/hypr/%2").arg(qEnvironmentVariable("XDG_RUNTIME_DIR"), his);
if (!QDir(hyprDir).exists()) { if (!QDir(hyprDir).exists()) {
hyprDir = QStringLiteral("/tmp/hypr/") + his; hyprDir = QStringLiteral("/tmp/hypr/") + his;
if (!QDir(hyprDir).exists()) { if (!QDir(hyprDir).exists()) {
qCWarning(lcHypr) << "Hyprland socket directory does not exist. Unable to connect to Hyprland socket."; qCWarning(lcHypr) << "Hyprland socket directory does not exist. "
"Unable to connect to Hyprland socket.";
return; return;
} }
} }
@@ -283,9 +295,15 @@ HyprExtras::HyprExtras(QObject* parent)
m_socket = new QLocalSocket(this); m_socket = new QLocalSocket(this);
QObject::connect(m_socket, &QLocalSocket::errorOccurred, this, &HyprExtras::socketError); QObject::connect(
QObject::connect(m_socket, &QLocalSocket::stateChanged, this, &HyprExtras::socketStateChanged); m_socket, &QLocalSocket::errorOccurred, this, &HyprExtras::socketError);
QObject::connect(m_socket, &QLocalSocket::readyRead, this, &HyprExtras::readEvent); QObject::connect(
m_socket,
&QLocalSocket::stateChanged,
this,
&HyprExtras::socketStateChanged);
QObject::connect(
m_socket, &QLocalSocket::readyRead, this, &HyprExtras::readEvent);
m_socket->connectToServer(m_eventSocket, QLocalSocket::ReadOnly); m_socket->connectToServer(m_eventSocket, QLocalSocket::ReadOnly);
} }
@@ -305,7 +323,8 @@ void HyprExtras::message(const QString& message) {
makeRequest(message, [](bool success, const QByteArray& res) { makeRequest(message, [](bool success, const QByteArray& res) {
if (!success) { if (!success) {
qCWarning(lcHypr) << "message: request error:" << QString::fromUtf8(res); qCWarning(lcHypr)
<< "message: request error:" << QString::fromUtf8(res);
} }
}); });
} }
@@ -315,10 +334,12 @@ void HyprExtras::batchMessage(const QStringList& messages) {
return; return;
} }
makeRequest(QStringLiteral("[[BATCH]]") + messages.join(QLatin1Char(';')), makeRequest(
QStringLiteral("[[BATCH]]") + messages.join(QLatin1Char(';')),
[](bool success, const QByteArray& res) { [](bool success, const QByteArray& res) {
if (!success) { if (!success) {
qCWarning(lcHypr) << "batchMessage: request error:" << QString::fromUtf8(res); qCWarning(lcHypr)
<< "batchMessage: request error:" << QString::fromUtf8(res);
} }
}); });
} }
@@ -342,11 +363,14 @@ void HyprExtras::applyOptions(const QVariantHash& options) {
return; return;
} }
makeRequest(QStringLiteral("eval ") + calls.join(QLatin1String("; ")), [this](bool success, const QByteArray& res) { makeRequest(
QStringLiteral("eval ") + calls.join(QLatin1String("; ")),
[this](bool success, const QByteArray& res) {
if (success) { if (success) {
refreshOptions(); refreshOptions();
} else { } else {
qCWarning(lcHypr) << "applyOptions: request error" << QString::fromUtf8(res); qCWarning(lcHypr)
<< "applyOptions: request error" << QString::fromUtf8(res);
} }
}); });
} }
@@ -390,8 +414,8 @@ void HyprExtras::refreshOptions() {
m_optionsRefresh = makeRequestJson( m_optionsRefresh = makeRequestJson(
QStringLiteral("getoption ") + key, QStringLiteral("getoption ") + key,
[this, generation, nextOptions, step, index, key](bool success, const QJsonDocument& response) [this, generation, nextOptions, step, index, key](
{ bool success, const QJsonDocument& response) {
m_optionsRefresh.reset(); m_optionsRefresh.reset();
if (generation != m_optionsRefreshGeneration) { if (generation != m_optionsRefreshGeneration) {
@@ -399,14 +423,21 @@ void HyprExtras::refreshOptions() {
} }
if (success && response.isObject()) { if (success && response.isObject()) {
const QVariant value = parseGetOptionValue(response.object()); const QVariant value =
parseGetOptionValue(response.object());
if (value.isValid()) { if (value.isValid()) {
insertNestedValue(*nextOptions, key.split(QLatin1Char(':'), Qt::SkipEmptyParts), value); insertNestedValue(
*nextOptions,
key.split(QLatin1Char(':'), Qt::SkipEmptyParts),
value);
} else { } else {
qCWarning(lcHypr) << "refreshOptions: getoption returned no usable value for" << key; qCWarning(lcHypr) << "refreshOptions: getoption "
"returned no usable value for"
<< key;
} }
} else if (!success) { } else if (!success) {
qCWarning(lcHypr) << "refreshOptions: getoption request error for" << key; qCWarning(lcHypr)
<< "refreshOptions: getoption request error for" << key;
} }
(*step)(index + 1); (*step)(index + 1);
@@ -421,7 +452,9 @@ void HyprExtras::refreshDevices() {
m_devicesRefresh->close(); m_devicesRefresh->close();
} }
m_devicesRefresh = makeRequestJson(QStringLiteral("devices"), [this](bool success, const QJsonDocument& response) { m_devicesRefresh = makeRequestJson(
QStringLiteral("devices"),
[this](bool success, const QJsonDocument& response) {
m_devicesRefresh.reset(); m_devicesRefresh.reset();
if (success) { if (success) {
m_devices->updateLastIpcObject(response.object()); m_devices->updateLastIpcObject(response.object());
@@ -431,15 +464,19 @@ void HyprExtras::refreshDevices() {
void HyprExtras::socketError(QLocalSocket::LocalSocketError error) const { void HyprExtras::socketError(QLocalSocket::LocalSocketError error) const {
if (!m_socketValid) { if (!m_socketValid) {
qCWarning(lcHypr) << "socketError: unable to connect to Hyprland event socket:" << error; qCWarning(lcHypr)
<< "socketError: unable to connect to Hyprland event socket:"
<< error;
} else { } else {
qCWarning(lcHypr) << "socketError: Hyprland event socket error:" << error; qCWarning(lcHypr) << "socketError: Hyprland event socket error:"
<< error;
} }
} }
void HyprExtras::socketStateChanged(QLocalSocket::LocalSocketState state) { void HyprExtras::socketStateChanged(QLocalSocket::LocalSocketState state) {
if (state == QLocalSocket::UnconnectedState && m_socketValid) { if (state == QLocalSocket::UnconnectedState && m_socketValid) {
qCWarning(lcHypr) << "socketStateChanged: Hyprland event socket disconnected."; qCWarning(lcHypr)
<< "socketStateChanged: Hyprland event socket disconnected.";
} }
m_socketValid = state == QLocalSocket::ConnectedState; m_socketValid = state == QLocalSocket::ConnectedState;
@@ -452,7 +489,8 @@ void HyprExtras::readEvent() {
break; break;
} }
rawEvent.truncate(rawEvent.length() - 1); rawEvent.truncate(rawEvent.length() - 1);
const auto event = QByteArrayView(rawEvent.data(), rawEvent.indexOf(">>")); const auto event =
QByteArrayView(rawEvent.data(), rawEvent.indexOf(">>"));
handleEvent(QString::fromUtf8(event)); handleEvent(QString::fromUtf8(event));
} }
} }
@@ -466,14 +504,18 @@ void HyprExtras::handleEvent(const QString& event) {
} }
HyprExtras::SocketPtr HyprExtras::makeRequestJson( HyprExtras::SocketPtr HyprExtras::makeRequestJson(
const QString& request, const std::function<void(bool, QJsonDocument)>& callback) { const QString& request,
return makeRequest(QStringLiteral("j/") + request, [callback](bool success, const QByteArray& response) { const std::function<void(bool, QJsonDocument)>& callback) {
return makeRequest(
QStringLiteral("j/") + request,
[callback](bool success, const QByteArray& response) {
callback(success, QJsonDocument::fromJson(response)); callback(success, QJsonDocument::fromJson(response));
}); });
} }
HyprExtras::SocketPtr HyprExtras::makeRequest( HyprExtras::SocketPtr HyprExtras::makeRequest(
const QString& request, const std::function<void(bool, QByteArray)>& callback) { const QString& request,
const std::function<void(bool, QByteArray)>& callback) {
if (m_requestSocket.isEmpty()) { if (m_requestSocket.isEmpty()) {
return SocketPtr(); return SocketPtr();
} }
@@ -481,7 +523,8 @@ HyprExtras::SocketPtr HyprExtras::makeRequest(
auto socket = SocketPtr::create(this); auto socket = SocketPtr::create(this);
QObject::connect(socket.data(), &QLocalSocket::connected, this, [=, this]() { QObject::connect(socket.data(), &QLocalSocket::connected, this, [=, this]() {
QObject::connect(socket.data(), &QLocalSocket::readyRead, this, [socket, callback]() { QObject::connect(
socket.data(), &QLocalSocket::readyRead, this, [socket, callback]() {
const auto response = socket->readAll(); const auto response = socket->readAll();
callback(true, std::move(response)); callback(true, std::move(response));
socket->close(); socket->close();
@@ -491,8 +534,13 @@ HyprExtras::SocketPtr HyprExtras::makeRequest(
socket->flush(); socket->flush();
}); });
QObject::connect(socket.data(), &QLocalSocket::errorOccurred, this, [=](QLocalSocket::LocalSocketError err) { QObject::connect(
qCWarning(lcHypr) << "makeRequest: error making request:" << err << "| request:" << request; socket.data(),
&QLocalSocket::errorOccurred,
this,
[=](QLocalSocket::LocalSocketError err) {
qCWarning(lcHypr) << "makeRequest: error making request:" << err
<< "| request:" << request;
callback(false, {}); callback(false, {});
socket->close(); socket->close();
}); });
+8 -3
View File
@@ -20,7 +20,8 @@ QML_ELEMENT
Q_MOC_INCLUDE("hyprdevices.hpp") Q_MOC_INCLUDE("hyprdevices.hpp")
Q_PROPERTY(QVariantMap options READ options NOTIFY optionsChanged) Q_PROPERTY(QVariantMap options READ options NOTIFY optionsChanged)
Q_PROPERTY(ZShell::internal::hypr::HyprDevices* devices READ devices CONSTANT) Q_PROPERTY(
ZShell::internal::hypr::HyprDevices* devices READ devices CONSTANT)
public: public:
explicit HyprExtras(QObject* parent = nullptr); explicit HyprExtras(QObject* parent = nullptr);
@@ -58,8 +59,12 @@ void socketStateChanged(QLocalSocket::LocalSocketState state);
void readEvent(); void readEvent();
void handleEvent(const QString& event); void handleEvent(const QString& event);
SocketPtr makeRequestJson(const QString& request, const std::function<void(bool, QJsonDocument)>& callback); SocketPtr makeRequestJson(
SocketPtr makeRequest(const QString& request, const std::function<void(bool, QByteArray)>& callback); const QString& request,
const std::function<void(bool, QJsonDocument)>& callback);
SocketPtr makeRequest(
const QString& request,
const std::function<void(bool, QByteArray)>& callback);
}; };
} // namespace ZShell::internal::hypr } // namespace ZShell::internal::hypr
+8 -4
View File
@@ -18,7 +18,8 @@ LidWatcher::LidWatcher(QObject* parent) : QObject(parent) {
return; return;
} }
bool ok = bus.connect("org.freedesktop.login1", bool ok = bus.connect(
"org.freedesktop.login1",
"/org/freedesktop/login1", "/org/freedesktop/login1",
"org.freedesktop.login1.Manager", "org.freedesktop.login1.Manager",
"PrepareForSleep", "PrepareForSleep",
@@ -31,7 +32,8 @@ LidWatcher::LidWatcher(QObject* parent) : QObject(parent) {
<< bus.lastError().message(); << bus.lastError().message();
} }
QDBusInterface login1("org.freedesktop.login1", QDBusInterface login1(
"org.freedesktop.login1",
"/org/freedesktop/login1", "/org/freedesktop/login1",
"org.freedesktop.login1.Manager", "org.freedesktop.login1.Manager",
bus); bus);
@@ -42,7 +44,8 @@ LidWatcher::LidWatcher(QObject* parent) : QObject(parent) {
} }
const auto sessionPath = reply.value().path(); const auto sessionPath = reply.value().path();
ok = bus.connect("org.freedesktop.login1", ok = bus.connect(
"org.freedesktop.login1",
sessionPath, sessionPath,
"org.freedesktop.login1.Session", "org.freedesktop.login1.Session",
"Lock", "Lock",
@@ -54,7 +57,8 @@ LidWatcher::LidWatcher(QObject* parent) : QObject(parent) {
<< "Failed to connect to Lock signal:" << bus.lastError().message(); << "Failed to connect to Lock signal:" << bus.lastError().message();
} }
ok = bus.connect("org.freedesktop.login1", ok = bus.connect(
"org.freedesktop.login1",
sessionPath, sessionPath,
"org.freedesktop.login1.Session", "org.freedesktop.login1.Session",
"Unlock", "Unlock",
@@ -24,11 +24,7 @@ qreal getFractionInRange(qreal playtime, int start, int duration) {
namespace ZShell::controls { namespace ZShell::controls {
LinearIndicatorSegment::LinearIndicatorSegment(int gap, QObject* parent) LinearIndicatorSegment::LinearIndicatorSegment(int gap, QObject* parent)
: QObject(parent) : QObject(parent), m_startFraction(0), m_endFraction(0), m_gapSize(gap) {}
, m_startFraction(0)
, m_endFraction(0)
, m_gapSize(gap) {
}
qreal LinearIndicatorSegment::startFraction() const { qreal LinearIndicatorSegment::startFraction() const {
return m_startFraction; return m_startFraction;
@@ -58,7 +54,11 @@ LinearIndicatorManager::LinearIndicatorManager(QObject* parent)
new LinearIndicatorSegment(m_gap, this), new LinearIndicatorSegment(m_gap, this),
}) { }) {
for (auto el : m_activeIndicators) for (auto el : m_activeIndicators)
QObject::connect(this, &LinearIndicatorManager::updated, el, &LinearIndicatorSegment::updated); QObject::connect(
this,
&LinearIndicatorManager::updated,
el,
&LinearIndicatorSegment::updated);
} }
QList<LinearIndicatorSegment*> LinearIndicatorManager::activeIndicators() const { QList<LinearIndicatorSegment*> LinearIndicatorManager::activeIndicators() const {
@@ -98,12 +98,19 @@ void LinearIndicatorManager::update(qreal progress) {
const auto di = i * 2; const auto di = i * 2;
auto* const indicator = m_activeIndicators[i]; auto* const indicator = m_activeIndicators[i];
auto fraction = getFractionInRange(playtime, DELAY_TO_MOVE_SEGMENT_ENDS[di], DURATION_TO_MOVE_SEGMENT_ENDS[di]); auto fraction = getFractionInRange(
indicator->m_startFraction = std::clamp(m_interpolators[di].valueForProgress(fraction), 0.0, 1.0); playtime,
DELAY_TO_MOVE_SEGMENT_ENDS[di],
DURATION_TO_MOVE_SEGMENT_ENDS[di]);
indicator->m_startFraction =
std::clamp(m_interpolators[di].valueForProgress(fraction), 0.0, 1.0);
fraction = fraction = getFractionInRange(
getFractionInRange(playtime, DELAY_TO_MOVE_SEGMENT_ENDS[di + 1], DURATION_TO_MOVE_SEGMENT_ENDS[di + 1]); playtime,
indicator->m_endFraction = std::clamp(m_interpolators[di + 1].valueForProgress(fraction), 0.0, 1.0); DELAY_TO_MOVE_SEGMENT_ENDS[di + 1],
DURATION_TO_MOVE_SEGMENT_ENDS[di + 1]);
indicator->m_endFraction = std::clamp(
m_interpolators[di + 1].valueForProgress(fraction), 0.0, 1.0);
} }
m_progress = progress; m_progress = progress;
@@ -13,7 +13,8 @@ class LinearIndicatorManager;
class LinearIndicatorSegment : public QObject { class LinearIndicatorSegment : public QObject {
Q_OBJECT Q_OBJECT
QML_ELEMENT QML_ELEMENT
QML_UNCREATABLE("LinearIndicatorSegments can only be retrieved from a " QML_UNCREATABLE(
"LinearIndicatorSegments can only be retrieved from a "
"LinearIndicatorManager.") "LinearIndicatorManager.")
Q_PROPERTY(qreal startFraction READ startFraction NOTIFY updated FINAL) Q_PROPERTY(qreal startFraction READ startFraction NOTIFY updated FINAL)
@@ -43,10 +44,13 @@ Q_OBJECT
QML_ELEMENT QML_ELEMENT
Q_PROPERTY( Q_PROPERTY(
QList<ZShell::controls::LinearIndicatorSegment*> activeIndicators READ activeIndicators CONSTANT FINAL) QList<ZShell::controls::LinearIndicatorSegment*> activeIndicators READ
activeIndicators CONSTANT FINAL)
Q_PROPERTY(qreal progress READ progress WRITE update NOTIFY updated FINAL) Q_PROPERTY(qreal progress READ progress WRITE update NOTIFY updated FINAL)
Q_PROPERTY(qreal completeEndProgress READ completeEndProgress WRITE updateCompleteEndProgress NOTIFY updated FINAL) Q_PROPERTY(
qreal completeEndProgress READ completeEndProgress WRITE
updateCompleteEndProgress NOTIFY updated FINAL)
Q_PROPERTY(int gap READ gap WRITE setGap NOTIFY updated FINAL) Q_PROPERTY(int gap READ gap WRITE setGap NOTIFY updated FINAL)
Q_PROPERTY(qreal duration READ duration CONSTANT FINAL) Q_PROPERTY(qreal duration READ duration CONSTANT FINAL)
+26 -40
View File
@@ -7,38 +7,37 @@
namespace ZShell::internal { namespace ZShell::internal {
SparklineItem::SparklineItem(QQuickItem* parent) SparklineItem::SparklineItem(QQuickItem* parent) : QQuickPaintedItem(parent) {
: QQuickPaintedItem(parent) {
setAntialiasing(true); setAntialiasing(true);
} }
void SparklineItem::paint(QPainter* painter) { void SparklineItem::paint(QPainter* painter) {
const bool has1 = m_line1 && m_line1->count() >= 2; const bool has1 = m_line1 && m_line1->count() >= 2;
const bool has2 = m_line2 && m_line2->count() >= 2; const bool has2 = m_line2 && m_line2->count() >= 2;
if (!has1 && !has2) if (!has1 && !has2) return;
return;
painter->setRenderHint(QPainter::Antialiasing, true); painter->setRenderHint(QPainter::Antialiasing, true);
// Draw line1 first (behind), then line2 (in front) // Draw line1 first (behind), then line2 (in front)
if (has1) if (has1) drawLine(painter, m_line1, m_line1Color, m_line1FillAlpha);
drawLine(painter, m_line1, m_line1Color, m_line1FillAlpha); if (has2) drawLine(painter, m_line2, m_line2Color, m_line2FillAlpha);
if (has2)
drawLine(painter, m_line2, m_line2Color, m_line2FillAlpha);
} }
void SparklineItem::drawLine(QPainter* painter, CircularBuffer* buffer, const QColor& color, qreal fillAlpha) { void SparklineItem::drawLine(
if (m_historyLength < 2) QPainter* painter,
return; CircularBuffer* buffer,
const QColor& color,
qreal fillAlpha) {
if (m_historyLength < 2) return;
const qreal w = width(); const qreal w = width();
const qreal h = height(); const qreal h = height();
const int len = buffer->count(); const int len = buffer->count();
if (len < 2 || m_maxValue <= 0.0) if (len < 2 || m_maxValue <= 0.0) return;
return;
const qreal stepX = w / static_cast<qreal>(m_historyLength - 1); const qreal stepX = w / static_cast<qreal>(m_historyLength - 1);
const qreal startX = w - (len - 1) * stepX - stepX * m_slideProgress + stepX; const qreal startX =
w - (len - 1) * stepX - stepX * m_slideProgress + stepX;
const qreal strokePad = qCeil(m_lineWidth / 2); const qreal strokePad = qCeil(m_lineWidth / 2);
const qreal curvePad = 3.0; const qreal curvePad = 3.0;
@@ -93,8 +92,7 @@ void SparklineItem::drawLine(QPainter* painter, CircularBuffer* buffer, const QC
} }
void SparklineItem::connectBuffer(CircularBuffer* buffer) { void SparklineItem::connectBuffer(CircularBuffer* buffer) {
if (!buffer) if (!buffer) return;
return;
connect(buffer, &CircularBuffer::valuesChanged, this, [this]() { connect(buffer, &CircularBuffer::valuesChanged, this, [this]() {
update(); update();
@@ -117,10 +115,8 @@ CircularBuffer* SparklineItem::line1() const {
} }
void SparklineItem::setLine1(CircularBuffer* buffer) { void SparklineItem::setLine1(CircularBuffer* buffer) {
if (m_line1 == buffer) if (m_line1 == buffer) return;
return; if (m_line1) disconnect(m_line1, nullptr, this, nullptr);
if (m_line1)
disconnect(m_line1, nullptr, this, nullptr);
m_line1 = buffer; m_line1 = buffer;
connectBuffer(buffer); connectBuffer(buffer);
emit line1Changed(); emit line1Changed();
@@ -132,10 +128,8 @@ CircularBuffer* SparklineItem::line2() const {
} }
void SparklineItem::setLine2(CircularBuffer* buffer) { void SparklineItem::setLine2(CircularBuffer* buffer) {
if (m_line2 == buffer) if (m_line2 == buffer) return;
return; if (m_line2) disconnect(m_line2, nullptr, this, nullptr);
if (m_line2)
disconnect(m_line2, nullptr, this, nullptr);
m_line2 = buffer; m_line2 = buffer;
connectBuffer(buffer); connectBuffer(buffer);
emit line2Changed(); emit line2Changed();
@@ -147,8 +141,7 @@ QColor SparklineItem::line1Color() const {
} }
void SparklineItem::setLine1Color(const QColor& color) { void SparklineItem::setLine1Color(const QColor& color) {
if (m_line1Color == color) if (m_line1Color == color) return;
return;
m_line1Color = color; m_line1Color = color;
emit line1ColorChanged(); emit line1ColorChanged();
update(); update();
@@ -159,8 +152,7 @@ QColor SparklineItem::line2Color() const {
} }
void SparklineItem::setLine2Color(const QColor& color) { void SparklineItem::setLine2Color(const QColor& color) {
if (m_line2Color == color) if (m_line2Color == color) return;
return;
m_line2Color = color; m_line2Color = color;
emit line2ColorChanged(); emit line2ColorChanged();
update(); update();
@@ -171,8 +163,7 @@ qreal SparklineItem::line1FillAlpha() const {
} }
void SparklineItem::setLine1FillAlpha(qreal alpha) { void SparklineItem::setLine1FillAlpha(qreal alpha) {
if (qFuzzyCompare(m_line1FillAlpha, alpha)) if (qFuzzyCompare(m_line1FillAlpha, alpha)) return;
return;
m_line1FillAlpha = alpha; m_line1FillAlpha = alpha;
emit line1FillAlphaChanged(); emit line1FillAlphaChanged();
update(); update();
@@ -183,8 +174,7 @@ qreal SparklineItem::line2FillAlpha() const {
} }
void SparklineItem::setLine2FillAlpha(qreal alpha) { void SparklineItem::setLine2FillAlpha(qreal alpha) {
if (qFuzzyCompare(m_line2FillAlpha, alpha)) if (qFuzzyCompare(m_line2FillAlpha, alpha)) return;
return;
m_line2FillAlpha = alpha; m_line2FillAlpha = alpha;
emit line2FillAlphaChanged(); emit line2FillAlphaChanged();
update(); update();
@@ -195,8 +185,7 @@ qreal SparklineItem::maxValue() const {
} }
void SparklineItem::setMaxValue(qreal value) { void SparklineItem::setMaxValue(qreal value) {
if (qFuzzyCompare(m_maxValue, value)) if (qFuzzyCompare(m_maxValue, value)) return;
return;
m_maxValue = value; m_maxValue = value;
emit maxValueChanged(); emit maxValueChanged();
update(); update();
@@ -207,8 +196,7 @@ qreal SparklineItem::slideProgress() const {
} }
void SparklineItem::setSlideProgress(qreal progress) { void SparklineItem::setSlideProgress(qreal progress) {
if (qFuzzyCompare(m_slideProgress, progress)) if (qFuzzyCompare(m_slideProgress, progress)) return;
return;
m_slideProgress = progress; m_slideProgress = progress;
emit slideProgressChanged(); emit slideProgressChanged();
update(); update();
@@ -219,8 +207,7 @@ int SparklineItem::historyLength() const {
} }
void SparklineItem::setHistoryLength(int length) { void SparklineItem::setHistoryLength(int length) {
if (m_historyLength == length) if (m_historyLength == length) return;
return;
m_historyLength = length; m_historyLength = length;
emit historyLengthChanged(); emit historyLengthChanged();
update(); update();
@@ -231,8 +218,7 @@ qreal SparklineItem::lineWidth() const {
} }
void SparklineItem::setLineWidth(qreal width) { void SparklineItem::setLineWidth(qreal width) {
if (qFuzzyCompare(m_lineWidth, width)) if (qFuzzyCompare(m_lineWidth, width)) return;
return;
m_lineWidth = width; m_lineWidth = width;
emit lineWidthChanged(); emit lineWidthChanged();
update(); update();
+32 -11
View File
@@ -13,16 +13,33 @@ class SparklineItem : public QQuickPaintedItem {
Q_OBJECT Q_OBJECT
QML_ELEMENT QML_ELEMENT
Q_PROPERTY(CircularBuffer* line1 READ line1 WRITE setLine1 NOTIFY line1Changed) Q_PROPERTY(
Q_PROPERTY(CircularBuffer* line2 READ line2 WRITE setLine2 NOTIFY line2Changed) CircularBuffer* line1 READ line1 WRITE setLine1 NOTIFY line1Changed)
Q_PROPERTY(QColor line1Color READ line1Color WRITE setLine1Color NOTIFY line1ColorChanged) Q_PROPERTY(
Q_PROPERTY(QColor line2Color READ line2Color WRITE setLine2Color NOTIFY line2ColorChanged) CircularBuffer* line2 READ line2 WRITE setLine2 NOTIFY line2Changed)
Q_PROPERTY(qreal line1FillAlpha READ line1FillAlpha WRITE setLine1FillAlpha NOTIFY line1FillAlphaChanged) Q_PROPERTY(
Q_PROPERTY(qreal line2FillAlpha READ line2FillAlpha WRITE setLine2FillAlpha NOTIFY line2FillAlphaChanged) QColor line1Color READ line1Color WRITE setLine1Color NOTIFY
Q_PROPERTY(qreal maxValue READ maxValue WRITE setMaxValue NOTIFY maxValueChanged) line1ColorChanged)
Q_PROPERTY(qreal slideProgress READ slideProgress WRITE setSlideProgress NOTIFY slideProgressChanged) Q_PROPERTY(
Q_PROPERTY(int historyLength READ historyLength WRITE setHistoryLength NOTIFY historyLengthChanged) QColor line2Color READ line2Color WRITE setLine2Color NOTIFY
Q_PROPERTY(qreal lineWidth READ lineWidth WRITE setLineWidth NOTIFY lineWidthChanged) line2ColorChanged)
Q_PROPERTY(
qreal line1FillAlpha READ line1FillAlpha WRITE setLine1FillAlpha NOTIFY
line1FillAlphaChanged)
Q_PROPERTY(
qreal line2FillAlpha READ line2FillAlpha WRITE setLine2FillAlpha NOTIFY
line2FillAlphaChanged)
Q_PROPERTY(
qreal maxValue READ maxValue WRITE setMaxValue NOTIFY maxValueChanged)
Q_PROPERTY(
qreal slideProgress READ slideProgress WRITE setSlideProgress NOTIFY
slideProgressChanged)
Q_PROPERTY(
int historyLength READ historyLength WRITE setHistoryLength NOTIFY
historyLengthChanged)
Q_PROPERTY(
qreal lineWidth READ lineWidth WRITE setLineWidth NOTIFY
lineWidthChanged)
public: public:
explicit SparklineItem(QQuickItem* parent = nullptr); explicit SparklineItem(QQuickItem* parent = nullptr);
@@ -72,7 +89,11 @@ void historyLengthChanged();
void lineWidthChanged(); void lineWidthChanged();
private: private:
void drawLine(QPainter* painter, CircularBuffer* buffer, const QColor& color, qreal fillAlpha); void drawLine(
QPainter* painter,
CircularBuffer* buffer,
const QColor& color,
qreal fillAlpha);
void connectBuffer(CircularBuffer* buffer); void connectBuffer(CircularBuffer* buffer);
CircularBuffer* m_line1 = nullptr; CircularBuffer* m_line1 = nullptr;
+11 -22
View File
@@ -9,14 +9,12 @@
namespace ZShell::internal { namespace ZShell::internal {
VisualizerBars::VisualizerBars(QQuickItem* parent) VisualizerBars::VisualizerBars(QQuickItem* parent) : QQuickPaintedItem(parent) {
: QQuickPaintedItem(parent) {
setAntialiasing(true); setAntialiasing(true);
} }
void VisualizerBars::advance(qreal dt) { void VisualizerBars::advance(qreal dt) {
if (m_displayValues.isEmpty() || m_settled) if (m_displayValues.isEmpty() || m_settled) return;
return;
// dt is in seconds (from FrameAnimation.frameTime), convert to ms // dt is in seconds (from FrameAnimation.frameTime), convert to ms
const qreal dtMs = dt * 1000.0; const qreal dtMs = dt * 1000.0;
@@ -45,8 +43,7 @@ void VisualizerBars::advance(qreal dt) {
} }
void VisualizerBars::paint(QPainter* painter) { void VisualizerBars::paint(QPainter* painter) {
if (m_displayValues.isEmpty()) if (m_displayValues.isEmpty()) return;
return;
painter->setRenderHint(QPainter::Antialiasing, true); painter->setRenderHint(QPainter::Antialiasing, true);
painter->setPen(Qt::NoPen); painter->setPen(Qt::NoPen);
@@ -68,15 +65,13 @@ void VisualizerBars::drawSide(QPainter* painter, bool rightSide) {
const qreal h = height(); const qreal h = height();
const auto count = m_displayValues.size(); const auto count = m_displayValues.size();
if (count == 0) if (count == 0) return;
return;
const qreal sideWidth = w * 0.4; const qreal sideWidth = w * 0.4;
const qreal slotWidth = sideWidth / static_cast<qreal>(count); const qreal slotWidth = sideWidth / static_cast<qreal>(count);
const qreal barWidth = slotWidth - m_spacing; const qreal barWidth = slotWidth - m_spacing;
if (barWidth <= 0) if (barWidth <= 0) return;
return;
const qreal sideOffset = rightSide ? w * 0.6 : 0; const qreal sideOffset = rightSide ? w * 0.6 : 0;
const qreal maxBarHeight = h * 0.4; const qreal maxBarHeight = h * 0.4;
@@ -86,8 +81,7 @@ void VisualizerBars::drawSide(QPainter* painter, bool rightSide) {
const qreal value = std::clamp(m_displayValues[valueIndex], 0.0, 1.0); const qreal value = std::clamp(m_displayValues[valueIndex], 0.0, 1.0);
const qreal barHeight = value * maxBarHeight; const qreal barHeight = value * maxBarHeight;
if (barHeight <= 0) if (barHeight <= 0) continue;
continue;
const qreal x = static_cast<qreal>(i) * slotWidth + sideOffset; const qreal x = static_cast<qreal>(i) * slotWidth + sideOffset;
const qreal y = h - barHeight; const qreal y = h - barHeight;
@@ -141,8 +135,7 @@ QColor VisualizerBars::primaryColor() const {
} }
void VisualizerBars::setPrimaryColor(const QColor& color) { void VisualizerBars::setPrimaryColor(const QColor& color) {
if (m_primaryColor == color) if (m_primaryColor == color) return;
return;
m_primaryColor = color; m_primaryColor = color;
emit primaryColorChanged(); emit primaryColorChanged();
update(); update();
@@ -153,8 +146,7 @@ QColor VisualizerBars::secondaryColor() const {
} }
void VisualizerBars::setSecondaryColor(const QColor& color) { void VisualizerBars::setSecondaryColor(const QColor& color) {
if (m_secondaryColor == color) if (m_secondaryColor == color) return;
return;
m_secondaryColor = color; m_secondaryColor = color;
emit secondaryColorChanged(); emit secondaryColorChanged();
update(); update();
@@ -165,8 +157,7 @@ qreal VisualizerBars::rounding() const {
} }
void VisualizerBars::setRounding(qreal rounding) { void VisualizerBars::setRounding(qreal rounding) {
if (qFuzzyCompare(m_rounding, rounding)) if (qFuzzyCompare(m_rounding, rounding)) return;
return;
m_rounding = rounding; m_rounding = rounding;
emit roundingChanged(); emit roundingChanged();
update(); update();
@@ -177,8 +168,7 @@ qreal VisualizerBars::spacing() const {
} }
void VisualizerBars::setSpacing(qreal spacing) { void VisualizerBars::setSpacing(qreal spacing) {
if (qFuzzyCompare(m_spacing, spacing)) if (qFuzzyCompare(m_spacing, spacing)) return;
return;
m_spacing = spacing; m_spacing = spacing;
emit spacingChanged(); emit spacingChanged();
update(); update();
@@ -189,8 +179,7 @@ int VisualizerBars::animationDuration() const {
} }
void VisualizerBars::setAnimationDuration(int duration) { void VisualizerBars::setAnimationDuration(int duration) {
if (m_animationDuration == duration) if (m_animationDuration == duration) return;
return;
m_animationDuration = duration; m_animationDuration = duration;
emit animationDurationChanged(); emit animationDurationChanged();
} }
+13 -5
View File
@@ -12,12 +12,20 @@ class VisualizerBars : public QQuickPaintedItem {
Q_OBJECT Q_OBJECT
QML_ELEMENT QML_ELEMENT
Q_PROPERTY(QVector<double> values READ values WRITE setValues NOTIFY valuesChanged) Q_PROPERTY(
Q_PROPERTY(QColor primaryColor READ primaryColor WRITE setPrimaryColor NOTIFY primaryColorChanged) QVector<double> values READ values WRITE setValues NOTIFY valuesChanged)
Q_PROPERTY(QColor secondaryColor READ secondaryColor WRITE setSecondaryColor NOTIFY secondaryColorChanged) Q_PROPERTY(
Q_PROPERTY(qreal rounding READ rounding WRITE setRounding NOTIFY roundingChanged) 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(qreal spacing READ spacing WRITE setSpacing NOTIFY spacingChanged)
Q_PROPERTY(int animationDuration READ animationDuration WRITE setAnimationDuration NOTIFY animationDurationChanged) Q_PROPERTY(
int animationDuration READ animationDuration WRITE setAnimationDuration
NOTIFY animationDurationChanged)
Q_PROPERTY(bool settled READ settled NOTIFY settledChanged) Q_PROPERTY(bool settled READ settled NOTIFY settledChanged)
public: public:
+34 -23
View File
@@ -7,15 +7,16 @@
#include <QtConcurrent> #include <QtConcurrent>
#include <QSGImageNode> #include <QSGImageNode>
#include <QQuickWindow> #include <QQuickWindow>
#include <set>
namespace ZShell::internal { namespace ZShell::internal {
WallpaperImage::WallpaperImage(QQuickItem *parent) WallpaperImage::WallpaperImage(QQuickItem* parent) : QQuickItem(parent) {
: QQuickItem(parent)
{
setFlag(ItemHasContents, true); setFlag(ItemHasContents, true);
connect(&m_imageWatcher, &QFutureWatcher<QImage>::finished, this, &WallpaperImage::handleImageLoaded); connect(
&m_imageWatcher,
&QFutureWatcher<QImage>::finished,
this,
&WallpaperImage::handleImageLoaded);
} }
WallpaperImage::~WallpaperImage() { WallpaperImage::~WallpaperImage() {
@@ -23,8 +24,7 @@ WallpaperImage::~WallpaperImage() {
} }
void WallpaperImage::setStatus(const Status s) { void WallpaperImage::setStatus(const Status s) {
if (m_status == s) if (m_status == s) return;
return;
m_status = s; m_status = s;
emit statusChanged(); emit statusChanged();
@@ -86,11 +86,16 @@ void WallpaperImage::setCropHeight(qreal h) {
QString WallpaperImage::getCacheFilePath() const { QString WallpaperImage::getCacheFilePath() const {
if (m_source.isEmpty() || m_screenResolution.isEmpty()) return QString(); if (m_source.isEmpty() || m_screenResolution.isEmpty()) return QString();
QString cachePath = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + "/zshell/imagecache"; QString cachePath =
QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) +
"/zshell/imagecache";
QDir().mkpath(cachePath); QDir().mkpath(cachePath);
QString id = m_source.toString() + "_" + QString::number(m_screenResolution.width()) + "x" + QString::number(m_screenResolution.height()); QString id = m_source.toString() + "_" +
QByteArray hash = QCryptographicHash::hash(id.toUtf8(), QCryptographicHash::Md5).toHex(); QString::number(m_screenResolution.width()) + "x" +
QString::number(m_screenResolution.height());
QByteArray hash =
QCryptographicHash::hash(id.toUtf8(), QCryptographicHash::Md5).toHex();
return cachePath + "/" + hash + ".png"; return cachePath + "/" + hash + ".png";
} }
@@ -102,7 +107,8 @@ void WallpaperImage::loadImage() {
} }
QString cacheFile = getCacheFilePath(); QString cacheFile = getCacheFilePath();
QString sourceFile = m_source.isLocalFile() ? m_source.toLocalFile() : m_source.toString(); QString sourceFile = m_source.isLocalFile() ? m_source.toLocalFile()
: m_source.toString();
if (sourceFile.startsWith("qrc:/")) { if (sourceFile.startsWith("qrc:/")) {
sourceFile = sourceFile.mid(3); sourceFile = sourceFile.mid(3);
@@ -110,8 +116,10 @@ void WallpaperImage::loadImage() {
QSize targetRes = m_screenResolution; QSize targetRes = m_screenResolution;
QFuture<QImage> future = QtConcurrent::run([sourceFile, cacheFile, targetRes]() -> QImage { QFuture<QImage> future =
if (!targetRes.isEmpty() && !cacheFile.isEmpty() && QFileInfo::exists(cacheFile)) { QtConcurrent::run([sourceFile, cacheFile, targetRes]() -> QImage {
if (!targetRes.isEmpty() && !cacheFile.isEmpty() &&
QFileInfo::exists(cacheFile)) {
QImage cached(cacheFile); QImage cached(cacheFile);
if (!cached.isNull()) return cached; if (!cached.isNull()) return cached;
} }
@@ -123,8 +131,12 @@ void WallpaperImage::loadImage() {
return original; return original;
} }
if (original.width() > targetRes.width() || original.height() > targetRes.height()) { if (original.width() > targetRes.width() ||
QImage scaled = original.scaled(targetRes, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); original.height() > targetRes.height()) {
QImage scaled = original.scaled(
targetRes,
Qt::KeepAspectRatioByExpanding,
Qt::SmoothTransformation);
if (!cacheFile.isEmpty()) scaled.save(cacheFile, "PNG"); if (!cacheFile.isEmpty()) scaled.save(cacheFile, "PNG");
return scaled; return scaled;
} }
@@ -145,7 +157,8 @@ void WallpaperImage::handleImageLoaded() {
update(); update();
} }
QSGNode *WallpaperImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) { QSGNode* WallpaperImage::updatePaintNode(
QSGNode* oldNode, UpdatePaintNodeData*) {
auto* node = static_cast<QSGImageNode*>(oldNode); auto* node = static_cast<QSGImageNode*>(oldNode);
if (m_image.isNull()) { if (m_image.isNull()) {
@@ -159,7 +172,8 @@ QSGNode *WallpaperImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
if (m_textureDirty) { if (m_textureDirty) {
if (m_texture) delete m_texture; if (m_texture) delete m_texture;
m_texture = window()->createTextureFromImage(m_image, QQuickWindow::TextureHasAlphaChannel); m_texture = window()->createTextureFromImage(
m_image, QQuickWindow::TextureHasAlphaChannel);
m_textureDirty = false; m_textureDirty = false;
} }
@@ -175,8 +189,7 @@ QSGNode *WallpaperImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
m_cropX * m_texture->textureSize().width(), m_cropX * m_texture->textureSize().width(),
m_cropY * m_texture->textureSize().height(), m_cropY * m_texture->textureSize().height(),
cW * m_texture->textureSize().width(), cW * m_texture->textureSize().width(),
cH * m_texture->textureSize().height() cH * m_texture->textureSize().height());
);
QRectF bounds = boundingRect(); QRectF bounds = boundingRect();
if (bounds.isEmpty() || reqRect.isEmpty()) return node; if (bounds.isEmpty() || reqRect.isEmpty()) return node;
@@ -202,8 +215,7 @@ QSGNode *WallpaperImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
sourceRect.x() / m_texture->textureSize().width(), sourceRect.x() / m_texture->textureSize().width(),
sourceRect.y() / m_texture->textureSize().height(), sourceRect.y() / m_texture->textureSize().height(),
sourceRect.width() / m_texture->textureSize().width(), sourceRect.width() / m_texture->textureSize().width(),
sourceRect.height() / m_texture->textureSize().height() sourceRect.height() / m_texture->textureSize().height());
);
bool changed = false; bool changed = false;
@@ -219,8 +231,7 @@ QSGNode *WallpaperImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
updateIfChanged(m_actualCropWidth, normalizedActual.width()); updateIfChanged(m_actualCropWidth, normalizedActual.width());
updateIfChanged(m_actualCropHeight, normalizedActual.height()); updateIfChanged(m_actualCropHeight, normalizedActual.height());
if (changed) if (changed) emit actualCropChanged();
emit actualCropChanged();
node->setSourceRect(sourceRect); node->setSourceRect(sourceRect);
} }
+28 -48
View File
@@ -14,89 +14,69 @@ class WallpaperImage : public QQuickItem {
Q_OBJECT Q_OBJECT
QML_NAMED_ELEMENT(WallpaperImage) QML_NAMED_ELEMENT(WallpaperImage)
Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
Q_PROPERTY(QSize screenResolution READ screenResolution WRITE setScreenResolution NOTIFY screenResolutionChanged) Q_PROPERTY(
QSize screenResolution READ screenResolution WRITE setScreenResolution
NOTIFY screenResolutionChanged)
Q_PROPERTY(qreal zoom READ zoom WRITE setZoom NOTIFY zoomChanged) Q_PROPERTY(qreal zoom READ zoom WRITE setZoom NOTIFY zoomChanged)
Q_PROPERTY(Status status READ status NOTIFY statusChanged) Q_PROPERTY(Status status READ status NOTIFY statusChanged)
Q_PROPERTY(qreal actualCropX READ actualCropX NOTIFY actualCropChanged) Q_PROPERTY(qreal actualCropX READ actualCropX NOTIFY actualCropChanged)
Q_PROPERTY(qreal actualCropY READ actualCropY NOTIFY actualCropChanged) Q_PROPERTY(qreal actualCropY READ actualCropY NOTIFY actualCropChanged)
Q_PROPERTY(qreal actualCropWidth READ actualCropWidth NOTIFY actualCropChanged) Q_PROPERTY(
Q_PROPERTY(qreal actualCropHeight READ actualCropHeight NOTIFY actualCropChanged) qreal actualCropWidth READ actualCropWidth NOTIFY actualCropChanged)
Q_PROPERTY(
qreal actualCropHeight READ actualCropHeight NOTIFY actualCropChanged)
Q_PROPERTY(qreal cropX READ cropX WRITE setCropX NOTIFY cropXChanged) Q_PROPERTY(qreal cropX READ cropX WRITE setCropX NOTIFY cropXChanged)
Q_PROPERTY(qreal cropY READ cropY WRITE setCropY NOTIFY cropYChanged) Q_PROPERTY(qreal cropY READ cropY WRITE setCropY NOTIFY cropYChanged)
Q_PROPERTY(qreal cropWidth READ cropWidth WRITE setCropWidth NOTIFY cropWidthChanged) Q_PROPERTY(
Q_PROPERTY(qreal cropHeight READ cropHeight WRITE setCropHeight NOTIFY cropHeightChanged) qreal cropWidth READ cropWidth WRITE setCropWidth NOTIFY
cropWidthChanged)
Q_PROPERTY(
qreal cropHeight READ cropHeight WRITE setCropHeight NOTIFY
cropHeightChanged)
public: public:
explicit WallpaperImage(QQuickItem* parent = nullptr); explicit WallpaperImage(QQuickItem* parent = nullptr);
~WallpaperImage() override; ~WallpaperImage() override;
enum Status { enum Status { Null, Ready, Loading, Error };
Null,
Ready,
Loading,
Error
};
Q_ENUM(Status) Q_ENUM(Status)
Status status() const { Status status() const { return m_status; }
return m_status;
}
QUrl source() const { QUrl source() const { return m_source; }
return m_source;
}
void setSource(const QUrl& source); void setSource(const QUrl& source);
QSize screenResolution() const { QSize screenResolution() const { return m_screenResolution; }
return m_screenResolution;
}
void setScreenResolution(const QSize& screenResolution); void setScreenResolution(const QSize& screenResolution);
qreal zoom() const { qreal zoom() const { return m_zoom; }
return m_zoom;
}
void setZoom(qreal zoom); void setZoom(qreal zoom);
qreal actualCropX() const { qreal actualCropX() const { return m_actualCropX; }
return m_actualCropX;
}
qreal actualCropY() const { qreal actualCropY() const { return m_actualCropY; }
return m_actualCropY;
}
qreal actualCropWidth() const { qreal actualCropWidth() const { return m_actualCropWidth; }
return m_actualCropWidth;
}
qreal actualCropHeight() const { qreal actualCropHeight() const { return m_actualCropHeight; }
return m_actualCropHeight;
}
qreal cropX() const { qreal cropX() const { return m_cropX; }
return m_cropX;
}
void setCropX(qreal x); void setCropX(qreal x);
qreal cropY() const { qreal cropY() const { return m_cropY; }
return m_cropY;
}
void setCropY(qreal y); void setCropY(qreal y);
qreal cropWidth() const { qreal cropWidth() const { return m_cropWidth; }
return m_cropWidth;
}
void setCropWidth(qreal w); void setCropWidth(qreal w);
qreal cropHeight() const { qreal cropHeight() const { return m_cropHeight; }
return m_cropHeight;
}
void setCropHeight(qreal h); void setCropHeight(qreal h);
protected: protected:
QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData) override; QSGNode* updatePaintNode(
QSGNode* oldNode, UpdatePaintNodeData* updatePaintNodeData) override;
signals: signals:
void sourceChanged(); void sourceChanged();
+56 -18
View File
@@ -6,7 +6,8 @@
namespace ZShell::models { namespace ZShell::models {
FileSystemEntry::FileSystemEntry(const QString& path, const QString& relativePath, QObject* parent) FileSystemEntry::FileSystemEntry(
const QString& path, const QString& relativePath, QObject* parent)
: QObject(parent) : QObject(parent)
, m_fileInfo(path) , m_fileInfo(path)
, m_path(path) , m_path(path)
@@ -78,8 +79,16 @@ FileSystemModel::FileSystemModel(QObject* parent)
, m_watchChanges(true) , m_watchChanges(true)
, m_showHidden(false) , m_showHidden(false)
, m_filter(NoFilter) { , m_filter(NoFilter) {
connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &FileSystemModel::watchDirIfRecursive); connect(
connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &FileSystemModel::updateEntriesForDir); &m_watcher,
&QFileSystemWatcher::directoryChanged,
this,
&FileSystemModel::watchDirIfRecursive);
connect(
&m_watcher,
&QFileSystemWatcher::directoryChanged,
this,
&FileSystemModel::updateEntriesForDir);
} }
int FileSystemModel::rowCount(const QModelIndex& parent) const { int FileSystemModel::rowCount(const QModelIndex& parent) const {
@@ -90,7 +99,8 @@ int FileSystemModel::rowCount(const QModelIndex& parent) const {
} }
QVariant FileSystemModel::data(const QModelIndex& index, int role) const { QVariant FileSystemModel::data(const QModelIndex& index, int role) const {
if (role != Qt::UserRole || !index.isValid() || index.row() >= m_entries.size()) { if (role != Qt::UserRole || !index.isValid() ||
index.row() >= m_entries.size()) {
return QVariant(); return QVariant();
} }
return QVariant::fromValue(m_entries.at(index.row())); return QVariant::fromValue(m_entries.at(index.row()));
@@ -233,9 +243,14 @@ void FileSystemModel::watchDirIfRecursive(const QString& path) {
return dirs; return dirs;
}); });
const auto watcher = new QFutureWatcher<QStringList>(this); const auto watcher = new QFutureWatcher<QStringList>(this);
connect(watcher, &QFutureWatcher<QStringList>::finished, this, [currentDir, showHidden, watcher, this]() { connect(
watcher,
&QFutureWatcher<QStringList>::finished,
this,
[currentDir, showHidden, watcher, this]() {
const auto paths = watcher->result(); const auto paths = watcher->result();
if (currentDir == m_dir && showHidden == m_showHidden && !paths.isEmpty()) { if (currentDir == m_dir && showHidden == m_showHidden &&
!paths.isEmpty()) {
// Ignore if dir or showHidden has changed // Ignore if dir or showHidden has changed
m_watcher.addPaths(paths); m_watcher.addPaths(paths);
} }
@@ -295,8 +310,10 @@ void FileSystemModel::updateEntriesForDir(const QString& dir) {
oldPaths << entry->path(); oldPaths << entry->path();
} }
const auto future = QtConcurrent::run([=](QPromise<QPair<QSet<QString>, QSet<QString>>>& promise) { const auto future = QtConcurrent::run(
const auto flags = recursive ? QDirIterator::Subdirectories : QDirIterator::NoIteratorFlags; [=](QPromise<QPair<QSet<QString>, QSet<QString>>>& promise) {
const auto flags = recursive ? QDirIterator::Subdirectories
: QDirIterator::NoIteratorFlags;
std::optional<QDirIterator> iter; std::optional<QDirIterator> iter;
@@ -357,7 +374,8 @@ void FileSystemModel::updateEntriesForDir(const QString& dir) {
return; return;
} }
promise.addResult(qMakePair(oldPaths - newPaths, newPaths - oldPaths)); promise.addResult(
qMakePair(oldPaths - newPaths, newPaths - oldPaths));
}); });
if (m_futures.contains(dir)) { if (m_futures.contains(dir)) {
@@ -365,9 +383,14 @@ void FileSystemModel::updateEntriesForDir(const QString& dir) {
} }
m_futures.insert(dir, future); m_futures.insert(dir, future);
const auto watcher = new QFutureWatcher<QPair<QSet<QString>, QSet<QString>>>(this); const auto watcher =
new QFutureWatcher<QPair<QSet<QString>, QSet<QString>>>(this);
connect(watcher, &QFutureWatcher<QPair<QSet<QString>, QSet<QString>>>::finished, this, [dir, watcher, this]() { connect(
watcher,
&QFutureWatcher<QPair<QSet<QString>, QSet<QString>>>::finished,
this,
[dir, watcher, this]() {
m_futures.remove(dir); m_futures.remove(dir);
if (!watcher->future().isResultReadyAt(0)) { if (!watcher->future().isResultReadyAt(0)) {
@@ -384,7 +407,8 @@ void FileSystemModel::updateEntriesForDir(const QString& dir) {
watcher->setFuture(future); watcher->setFuture(future);
} }
void FileSystemModel::applyChanges(const QSet<QString>& removedPaths, const QSet<QString>& addedPaths) { void FileSystemModel::applyChanges(
const QSet<QString>& removedPaths, const QSet<QString>& addedPaths) {
QList<int> removedIndices; QList<int> removedIndices;
for (int i = 0; i < m_entries.size(); ++i) { for (int i = 0; i < m_entries.size(); ++i) {
if (removedPaths.contains(m_entries[i]->path())) { if (removedPaths.contains(m_entries[i]->path())) {
@@ -424,9 +448,13 @@ void FileSystemModel::applyChanges(const QSet<QString>& removedPaths, const QSet
// Create new entries // Create new entries
QList<FileSystemEntry*> newEntries; QList<FileSystemEntry*> newEntries;
for (const auto& path : addedPaths) { for (const auto& path : addedPaths) {
newEntries << new FileSystemEntry(path, m_dir.relativeFilePath(path), this); newEntries << new FileSystemEntry(
path, m_dir.relativeFilePath(path), this);
} }
std::sort(newEntries.begin(), newEntries.end(), [this](const FileSystemEntry* a, const FileSystemEntry* b) { std::sort(
newEntries.begin(),
newEntries.end(),
[this](const FileSystemEntry* a, const FileSystemEntry* b) {
return compareEntries(a, b); return compareEntries(a, b);
}); });
@@ -435,7 +463,10 @@ void FileSystemModel::applyChanges(const QSet<QString>& removedPaths, const QSet
QList<FileSystemEntry*> batchItems; QList<FileSystemEntry*> batchItems;
for (const auto& entry : std::as_const(newEntries)) { for (const auto& entry : std::as_const(newEntries)) {
const auto it = std::lower_bound( const auto it = std::lower_bound(
m_entries.begin(), m_entries.end(), entry, [this](const FileSystemEntry* a, const FileSystemEntry* b) { m_entries.begin(),
m_entries.end(),
entry,
[this](const FileSystemEntry* a, const FileSystemEntry* b) {
return compareEntries(a, b); return compareEntries(a, b);
}); });
const auto row = static_cast<int>(it - m_entries.begin()); const auto row = static_cast<int>(it - m_entries.begin());
@@ -446,7 +477,10 @@ void FileSystemModel::applyChanges(const QSet<QString>& removedPaths, const QSet
} else if (row == insertStart + batchItems.size()) { } else if (row == insertStart + batchItems.size()) {
batchItems << entry; batchItems << entry;
} else { } else {
beginInsertRows(QModelIndex(), insertStart, insertStart + static_cast<int>(batchItems.size()) - 1); beginInsertRows(
QModelIndex(),
insertStart,
insertStart + static_cast<int>(batchItems.size()) - 1);
for (int i = 0; i < batchItems.size(); ++i) { for (int i = 0; i < batchItems.size(); ++i) {
m_entries.insert(insertStart + i, batchItems[i]); m_entries.insert(insertStart + i, batchItems[i]);
} }
@@ -458,7 +492,10 @@ void FileSystemModel::applyChanges(const QSet<QString>& removedPaths, const QSet
} }
} }
if (!batchItems.isEmpty()) { if (!batchItems.isEmpty()) {
beginInsertRows(QModelIndex(), insertStart, insertStart + static_cast<int>(batchItems.size()) - 1); beginInsertRows(
QModelIndex(),
insertStart,
insertStart + static_cast<int>(batchItems.size()) - 1);
for (int i = 0; i < batchItems.size(); ++i) { for (int i = 0; i < batchItems.size(); ++i) {
m_entries.insert(insertStart + i, batchItems[i]); m_entries.insert(insertStart + i, batchItems[i]);
} }
@@ -468,7 +505,8 @@ void FileSystemModel::applyChanges(const QSet<QString>& removedPaths, const QSet
emit entriesChanged(); emit entriesChanged();
} }
bool FileSystemModel::compareEntries(const FileSystemEntry* a, const FileSystemEntry* b) const { bool FileSystemModel::compareEntries(
const FileSystemEntry* a, const FileSystemEntry* b) const {
if (a->isDir() != b->isDir()) { if (a->isDir() != b->isDir()) {
return m_sortReverse ^ a->isDir(); return m_sortReverse ^ a->isDir();
} }
+33 -19
View File
@@ -1,6 +1,5 @@
#pragma once #pragma once
#include <cstdint>
#include <qabstractitemmodel.h> #include <qabstractitemmodel.h>
#include <qdir.h> #include <qdir.h>
#include <qfilesystemwatcher.h> #include <qfilesystemwatcher.h>
@@ -16,7 +15,9 @@ namespace ZShell::models {
class FileSystemEntry : public QObject { class FileSystemEntry : public QObject {
Q_OBJECT Q_OBJECT
QML_ELEMENT QML_ELEMENT
QML_UNCREATABLE("FileSystemEntry instances can only be retrieved from a FileSystemModel") QML_UNCREATABLE(
"FileSystemEntry instances can only be retrieved from a "
"FileSystemModel")
Q_PROPERTY(QString path READ path CONSTANT) Q_PROPERTY(QString path READ path CONSTANT)
Q_PROPERTY(QString relativePath READ relativePath NOTIFY relativePathChanged) Q_PROPERTY(QString relativePath READ relativePath NOTIFY relativePathChanged)
@@ -30,7 +31,10 @@ Q_PROPERTY(bool isImage READ isImage CONSTANT)
Q_PROPERTY(QString mimeType READ mimeType CONSTANT) Q_PROPERTY(QString mimeType READ mimeType CONSTANT)
public: public:
explicit FileSystemEntry(const QString& path, const QString& relativePath, QObject* parent = nullptr); explicit FileSystemEntry(
const QString& path,
const QString& relativePath,
QObject* parent = nullptr);
[[nodiscard]] QString path() const; [[nodiscard]] QString path() const;
[[nodiscard]] QString relativePath() const; [[nodiscard]] QString relativePath() const;
@@ -66,28 +70,36 @@ Q_OBJECT
QML_ELEMENT QML_ELEMENT
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged) Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
Q_PROPERTY(bool recursive READ recursive WRITE setRecursive NOTIFY recursiveChanged) Q_PROPERTY(
Q_PROPERTY(bool watchChanges READ watchChanges WRITE setWatchChanges NOTIFY watchChangesChanged) bool recursive READ recursive WRITE setRecursive NOTIFY recursiveChanged)
Q_PROPERTY(bool showHidden READ showHidden WRITE setShowHidden NOTIFY showHiddenChanged) Q_PROPERTY(
Q_PROPERTY(bool sortReverse READ sortReverse WRITE setSortReverse NOTIFY sortReverseChanged) bool watchChanges READ watchChanges WRITE setWatchChanges NOTIFY
watchChangesChanged)
Q_PROPERTY(
bool showHidden READ showHidden WRITE setShowHidden NOTIFY
showHiddenChanged)
Q_PROPERTY(
bool sortReverse READ sortReverse WRITE setSortReverse NOTIFY
sortReverseChanged)
Q_PROPERTY(Filter filter READ filter WRITE setFilter NOTIFY filterChanged) Q_PROPERTY(Filter filter READ filter WRITE setFilter NOTIFY filterChanged)
Q_PROPERTY(QStringList nameFilters READ nameFilters WRITE setNameFilters NOTIFY nameFiltersChanged) Q_PROPERTY(
QStringList nameFilters READ nameFilters WRITE setNameFilters NOTIFY
nameFiltersChanged)
Q_PROPERTY(QQmlListProperty<ZShell::models::FileSystemEntry> entries READ entries NOTIFY entriesChanged) Q_PROPERTY(
QQmlListProperty<ZShell::models::FileSystemEntry> entries READ entries
NOTIFY entriesChanged)
public: public:
enum Filter { enum Filter { NoFilter, Images, Files, Dirs };
NoFilter,
Images,
Files,
Dirs
};
Q_ENUM(Filter) Q_ENUM(Filter)
explicit FileSystemModel(QObject* parent = nullptr); explicit FileSystemModel(QObject* parent = nullptr);
[[nodiscard]] int rowCount(const QModelIndex& parent = QModelIndex()) const override; [[nodiscard]] int rowCount(
[[nodiscard]] QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; const QModelIndex& parent = QModelIndex()) const override;
[[nodiscard]] QVariant data(
const QModelIndex& index, int role = Qt::DisplayRole) const override;
[[nodiscard]] QHash<int, QByteArray> roleNames() const override; [[nodiscard]] QHash<int, QByteArray> roleNames() const override;
[[nodiscard]] QString path() const; [[nodiscard]] QString path() const;
@@ -142,8 +154,10 @@ void update();
void updateWatcher(); void updateWatcher();
void updateEntries(); void updateEntries();
void updateEntriesForDir(const QString& dir); void updateEntriesForDir(const QString& dir);
void applyChanges(const QSet<QString>& removedPaths, const QSet<QString>& addedPaths); void applyChanges(
[[nodiscard]] bool compareEntries(const FileSystemEntry* a, const FileSystemEntry* b) const; const QSet<QString>& removedPaths, const QSet<QString>& addedPaths);
[[nodiscard]] bool compareEntries(
const FileSystemEntry* a, const FileSystemEntry* b) const;
}; };
} // namespace ZShell::models } // namespace ZShell::models
+54 -24
View File
@@ -23,20 +23,33 @@ PipeWireWorker::PipeWireWorker(std::stop_token token, AudioCollector* collector)
m_loop = pw_main_loop_new(nullptr); m_loop = pw_main_loop_new(nullptr);
if (!m_loop) { if (!m_loop) {
qWarning() << "PipeWireWorker::init: failed to create PipeWire main loop"; qWarning()
<< "PipeWireWorker::init: failed to create PipeWire main loop";
pw_deinit(); pw_deinit();
return; return;
} }
timespec timeout = {0, 10 * SPA_NSEC_PER_MSEC}; timespec timeout = {0, 10 * SPA_NSEC_PER_MSEC};
m_timer = pw_loop_add_timer(pw_main_loop_get_loop(m_loop), handleTimeout, this); m_timer =
pw_loop_update_timer(pw_main_loop_get_loop(m_loop), m_timer, &timeout, &timeout, false); pw_loop_add_timer(pw_main_loop_get_loop(m_loop), handleTimeout, this);
pw_loop_update_timer(
pw_main_loop_get_loop(m_loop), m_timer, &timeout, &timeout, false);
auto props = pw_properties_new( auto props = pw_properties_new(
PW_KEY_MEDIA_TYPE, "Audio", PW_KEY_MEDIA_CATEGORY, "Capture", PW_KEY_MEDIA_ROLE, "Music", nullptr); PW_KEY_MEDIA_TYPE,
"Audio",
PW_KEY_MEDIA_CATEGORY,
"Capture",
PW_KEY_MEDIA_ROLE,
"Music",
nullptr);
pw_properties_set(props, PW_KEY_STREAM_CAPTURE_SINK, "true"); pw_properties_set(props, PW_KEY_STREAM_CAPTURE_SINK, "true");
pw_properties_setf( pw_properties_setf(
props, PW_KEY_NODE_LATENCY, "%u/%u", nextPowerOf2(512 * ac::SAMPLE_RATE / 48000), ac::SAMPLE_RATE); props,
PW_KEY_NODE_LATENCY,
"%u/%u",
nextPowerOf2(512 * ac::SAMPLE_RATE / 48000),
ac::SAMPLE_RATE);
pw_properties_set(props, PW_KEY_NODE_PASSIVE, "true"); pw_properties_set(props, PW_KEY_NODE_PASSIVE, "true");
pw_properties_set(props, PW_KEY_NODE_VIRTUAL, "true"); pw_properties_set(props, PW_KEY_NODE_VIRTUAL, "true");
pw_properties_set(props, PW_KEY_STREAM_DONT_REMIX, "false"); pw_properties_set(props, PW_KEY_STREAM_DONT_REMIX, "false");
@@ -55,7 +68,8 @@ PipeWireWorker::PipeWireWorker(std::stop_token token, AudioCollector* collector)
params[0] = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &info); params[0] = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &info);
pw_stream_events events{}; pw_stream_events events{};
events.state_changed = [](void* data, pw_stream_state, pw_stream_state state, const char*) { events.state_changed =
[](void* data, pw_stream_state, pw_stream_state state, const char*) {
auto* self = static_cast<PipeWireWorker*>(data); auto* self = static_cast<PipeWireWorker*>(data);
self->streamStateChanged(state); self->streamStateChanged(state);
}; };
@@ -64,12 +78,18 @@ PipeWireWorker::PipeWireWorker(std::stop_token token, AudioCollector* collector)
self->processStream(); self->processStream();
}; };
m_stream = pw_stream_new_simple(pw_main_loop_get_loop(m_loop), "ZShell-shell", props, &events, this); m_stream = pw_stream_new_simple(
pw_main_loop_get_loop(m_loop), "ZShell-shell", props, &events, this);
const int success = pw_stream_connect(m_stream, PW_DIRECTION_INPUT, PW_ID_ANY, const int success = pw_stream_connect(
m_stream,
PW_DIRECTION_INPUT,
PW_ID_ANY,
static_cast<pw_stream_flags>( static_cast<pw_stream_flags>(
PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS | PW_STREAM_FLAG_RT_PROCESS), PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS |
params, 1); PW_STREAM_FLAG_RT_PROCESS),
params,
1);
if (success < 0) { if (success < 0) {
qWarning() << "PipeWireWorker::init: failed to connect stream"; qWarning() << "PipeWireWorker::init: failed to connect stream";
pw_stream_destroy(m_stream); pw_stream_destroy(m_stream);
@@ -99,7 +119,12 @@ void PipeWireWorker::handleTimeout(void* data, uint64_t expirations) {
} else { } else {
self->m_idle = true; self->m_idle = true;
timespec timeout = {0, 500 * SPA_NSEC_PER_MSEC}; timespec timeout = {0, 500 * SPA_NSEC_PER_MSEC};
pw_loop_update_timer(pw_main_loop_get_loop(self->m_loop), self->m_timer, &timeout, &timeout, false); pw_loop_update_timer(
pw_main_loop_get_loop(self->m_loop),
self->m_timer,
&timeout,
&timeout,
false);
} }
} }
} }
@@ -109,11 +134,13 @@ void PipeWireWorker::streamStateChanged(pw_stream_state state) {
switch (state) { switch (state) {
case PW_STREAM_STATE_PAUSED: { case PW_STREAM_STATE_PAUSED: {
timespec timeout = {0, 10 * SPA_NSEC_PER_MSEC}; timespec timeout = {0, 10 * SPA_NSEC_PER_MSEC};
pw_loop_update_timer(pw_main_loop_get_loop(m_loop), m_timer, &timeout, &timeout, false); pw_loop_update_timer(
pw_main_loop_get_loop(m_loop), m_timer, &timeout, &timeout, false);
break; break;
} }
case PW_STREAM_STATE_STREAMING: case PW_STREAM_STATE_STREAMING:
pw_loop_update_timer(pw_main_loop_get_loop(m_loop), m_timer, nullptr, nullptr, false); pw_loop_update_timer(
pw_main_loop_get_loop(m_loop), m_timer, nullptr, nullptr, false);
break; break;
case PW_STREAM_STATE_ERROR: case PW_STREAM_STATE_ERROR:
pw_main_loop_quit(m_loop); pw_main_loop_quit(m_loop);
@@ -171,7 +198,8 @@ void AudioCollector::clearBuffer() {
auto* writeBuffer = m_writeBuffer.load(std::memory_order_relaxed); auto* writeBuffer = m_writeBuffer.load(std::memory_order_relaxed);
std::fill(writeBuffer->begin(), writeBuffer->end(), 0.0f); std::fill(writeBuffer->begin(), writeBuffer->end(), 0.0f);
auto* oldRead = m_readBuffer.exchange(writeBuffer, std::memory_order_acq_rel); auto* oldRead =
m_readBuffer.exchange(writeBuffer, std::memory_order_acq_rel);
m_writeBuffer.store(oldRead, std::memory_order_release); m_writeBuffer.store(oldRead, std::memory_order_release);
} }
@@ -181,11 +209,13 @@ void AudioCollector::loadChunk(const qint16* samples, quint32 count) {
} }
auto* writeBuffer = m_writeBuffer.load(std::memory_order_relaxed); auto* writeBuffer = m_writeBuffer.load(std::memory_order_relaxed);
std::transform(samples, samples + count, writeBuffer->begin(), [](qint16 sample) { std::transform(
samples, samples + count, writeBuffer->begin(), [](qint16 sample) {
return sample / 32768.0f; return sample / 32768.0f;
}); });
auto* oldRead = m_readBuffer.exchange(writeBuffer, std::memory_order_acq_rel); auto* oldRead =
m_readBuffer.exchange(writeBuffer, std::memory_order_acq_rel);
m_writeBuffer.store(oldRead, std::memory_order_release); m_writeBuffer.store(oldRead, std::memory_order_release);
} }
@@ -206,9 +236,11 @@ quint32 AudioCollector::readChunk(double* out, quint32 count) {
} }
auto* readBuffer = m_readBuffer.load(std::memory_order_acquire); auto* readBuffer = m_readBuffer.load(std::memory_order_acquire);
std::transform(readBuffer->begin(), readBuffer->begin() + count, out, [](float sample) { std::transform(
return static_cast<double>(sample); readBuffer->begin(),
}); readBuffer->begin() + count,
out,
[](float sample) { return static_cast<double>(sample); });
return count; return count;
} }
@@ -218,8 +250,7 @@ AudioCollector::AudioCollector(QObject* parent)
, m_buffer1(ac::CHUNK_SIZE) , m_buffer1(ac::CHUNK_SIZE)
, m_buffer2(ac::CHUNK_SIZE) , m_buffer2(ac::CHUNK_SIZE)
, m_readBuffer(&m_buffer1) , m_readBuffer(&m_buffer1)
, m_writeBuffer(&m_buffer2) { , m_writeBuffer(&m_buffer2) {}
}
AudioCollector::~AudioCollector() { AudioCollector::~AudioCollector() {
stop(); stop();
@@ -232,9 +263,8 @@ void AudioCollector::start() {
clearBuffer(); clearBuffer();
m_thread = std::jthread([this](std::stop_token token) { m_thread = std::jthread(
PipeWireWorker worker(token, this); [this](std::stop_token token) { PipeWireWorker worker(token, this); });
});
} }
void AudioCollector::stop() { void AudioCollector::stop() {
+18 -12
View File
@@ -7,9 +7,7 @@
namespace ZShell::services { namespace ZShell::services {
AudioProcessor::AudioProcessor(QObject* parent) AudioProcessor::AudioProcessor(QObject* parent) : QObject(parent) {}
: QObject(parent) {
}
AudioProcessor::~AudioProcessor() { AudioProcessor::~AudioProcessor() {
stop(); stop();
@@ -17,12 +15,17 @@ AudioProcessor::~AudioProcessor() {
void AudioProcessor::init() { void AudioProcessor::init() {
m_timer = new QTimer(this); m_timer = new QTimer(this);
m_timer->setInterval(static_cast<int>(ac::CHUNK_SIZE * 1000.0 / ac::SAMPLE_RATE)); m_timer->setInterval(
static_cast<int>(ac::CHUNK_SIZE * 1000.0 / ac::SAMPLE_RATE));
connect(m_timer, &QTimer::timeout, this, &AudioProcessor::process); connect(m_timer, &QTimer::timeout, this, &AudioProcessor::process);
} }
void AudioProcessor::start() { void AudioProcessor::start() {
QMetaObject::invokeMethod(&AudioCollector::instance(), &AudioCollector::ref, Qt::QueuedConnection, this); QMetaObject::invokeMethod(
&AudioCollector::instance(),
&AudioCollector::ref,
Qt::QueuedConnection,
this);
if (m_timer) { if (m_timer) {
m_timer->start(); m_timer->start();
} }
@@ -32,14 +35,15 @@ void AudioProcessor::stop() {
if (m_timer) { if (m_timer) {
m_timer->stop(); m_timer->stop();
} }
QMetaObject::invokeMethod(&AudioCollector::instance(), &AudioCollector::unref, Qt::QueuedConnection, this); QMetaObject::invokeMethod(
&AudioCollector::instance(),
&AudioCollector::unref,
Qt::QueuedConnection,
this);
} }
AudioProvider::AudioProvider(QObject* parent) AudioProvider::AudioProvider(QObject* parent)
: Service(parent) : Service(parent), m_processor(nullptr), m_thread(nullptr) {}
, m_processor(nullptr)
, m_thread(nullptr) {
}
AudioProvider::~AudioProvider() { AudioProvider::~AudioProvider() {
if (m_thread) { if (m_thread) {
@@ -50,7 +54,8 @@ AudioProvider::~AudioProvider() {
void AudioProvider::init() { void AudioProvider::init() {
if (!m_processor) { if (!m_processor) {
qWarning() << "AudioProvider::init: attempted to init with no processor set"; qWarning()
<< "AudioProvider::init: attempted to init with no processor set";
return; return;
} }
@@ -58,7 +63,8 @@ void AudioProvider::init() {
m_processor->moveToThread(m_thread); m_processor->moveToThread(m_thread);
connect(m_thread, &QThread::started, m_processor, &AudioProcessor::init); connect(m_thread, &QThread::started, m_processor, &AudioProcessor::init);
connect(m_thread, &QThread::finished, m_processor, &AudioProcessor::deleteLater); connect(
m_thread, &QThread::finished, m_processor, &AudioProcessor::deleteLater);
connect(m_thread, &QThread::finished, m_thread, &QThread::deleteLater); connect(m_thread, &QThread::finished, m_thread, &QThread::deleteLater);
m_thread->start(); m_thread->start();
+7 -6
View File
@@ -10,8 +10,7 @@ BeatProcessor::BeatProcessor(QObject* parent)
: AudioProcessor(parent) : AudioProcessor(parent)
, m_tempo(new_aubio_tempo("default", 1024, ac::CHUNK_SIZE, ac::SAMPLE_RATE)) , m_tempo(new_aubio_tempo("default", 1024, ac::CHUNK_SIZE, ac::SAMPLE_RATE))
, m_in(new_fvec(ac::CHUNK_SIZE)) , m_in(new_fvec(ac::CHUNK_SIZE))
, m_out(new_fvec(2)) { , m_out(new_fvec(2)) {};
};
BeatProcessor::~BeatProcessor() { BeatProcessor::~BeatProcessor() {
if (m_tempo) { if (m_tempo) {
@@ -36,13 +35,15 @@ void BeatProcessor::process() {
} }
} }
BeatTracker::BeatTracker(QObject* parent) BeatTracker::BeatTracker(QObject* parent) : AudioProvider(parent), m_bpm(120) {
: AudioProvider(parent)
, m_bpm(120) {
m_processor = new BeatProcessor(); m_processor = new BeatProcessor();
init(); init();
connect(static_cast<BeatProcessor*>(m_processor), &BeatProcessor::beat, this, &BeatTracker::updateBpm); connect(
static_cast<BeatProcessor*>(m_processor),
&BeatProcessor::beat,
this,
&BeatTracker::updateBpm);
} }
smpl_t BeatTracker::bpm() const { smpl_t BeatTracker::bpm() const {
+17 -11
View File
@@ -2,7 +2,6 @@
#include "audiocollector.hpp" #include "audiocollector.hpp"
#include "audioprovider.hpp" #include "audioprovider.hpp"
#include <algorithm>
#include <cava/cavacore.h> #include <cava/cavacore.h>
#include <cstddef> #include <cstddef>
#include <qdebug.h> #include <qdebug.h>
@@ -14,8 +13,7 @@ CavaProcessor::CavaProcessor(QObject* parent)
, m_plan(nullptr) , m_plan(nullptr)
, m_in(new double[ac::CHUNK_SIZE]) , m_in(new double[ac::CHUNK_SIZE])
, m_out(nullptr) , m_out(nullptr)
, m_bars(0) { , m_bars(0) {};
};
CavaProcessor::~CavaProcessor() { CavaProcessor::~CavaProcessor() {
cleanup(); cleanup();
@@ -27,7 +25,8 @@ void CavaProcessor::process() {
return; return;
} }
const int count = static_cast<int>(AudioCollector::instance().readChunk(m_in)); const int count =
static_cast<int>(AudioCollector::instance().readChunk(m_in));
// Process in data via cava // Process in data via cava
cava_execute(m_in, count, m_out, m_plan); cava_execute(m_in, count, m_out, m_plan);
@@ -63,7 +62,8 @@ void CavaProcessor::process() {
void CavaProcessor::setBars(int bars) { void CavaProcessor::setBars(int bars) {
if (bars < 0) { if (bars < 0) {
qWarning() << "CavaProcessor::setBars: bars must be greater than 0. Setting to 0."; qWarning() << "CavaProcessor::setBars: bars must be greater than 0. "
"Setting to 0.";
bars = 0; bars = 0;
} }
@@ -100,13 +100,15 @@ void CavaProcessor::initCava() {
} }
CavaProvider::CavaProvider(QObject* parent) CavaProvider::CavaProvider(QObject* parent)
: AudioProvider(parent) : AudioProvider(parent), m_bars(0), m_values(m_bars, 0.0) {
, m_bars(0)
, m_values(m_bars, 0.0) {
m_processor = new CavaProcessor(); m_processor = new CavaProcessor();
init(); init();
connect(static_cast<CavaProcessor*>(m_processor), &CavaProcessor::valuesChanged, this, &CavaProvider::updateValues); connect(
static_cast<CavaProcessor*>(m_processor),
&CavaProcessor::valuesChanged,
this,
&CavaProvider::updateValues);
} }
int CavaProvider::bars() const { int CavaProvider::bars() const {
@@ -115,7 +117,8 @@ int CavaProvider::bars() const {
void CavaProvider::setBars(int bars) { void CavaProvider::setBars(int bars) {
if (bars < 0) { if (bars < 0) {
qWarning() << "CavaProvider::setBars: bars must be greater than 0. Setting to 0."; qWarning() << "CavaProvider::setBars: bars must be greater than 0. "
"Setting to 0.";
bars = 0; bars = 0;
} }
@@ -129,7 +132,10 @@ void CavaProvider::setBars(int bars) {
emit valuesChanged(); emit valuesChanged();
QMetaObject::invokeMethod( QMetaObject::invokeMethod(
static_cast<CavaProcessor*>(m_processor), &CavaProcessor::setBars, Qt::QueuedConnection, bars); static_cast<CavaProcessor*>(m_processor),
&CavaProcessor::setBars,
Qt::QueuedConnection,
bars);
} }
QVector<double> CavaProvider::values() const { QVector<double> CavaProvider::values() const {
+12 -7
View File
@@ -8,8 +8,7 @@
namespace ZShell::services { namespace ZShell::services {
Cpu::Cpu(QObject* parent) Cpu::Cpu(QObject* parent) : TickingService(parent) {
: TickingService(parent) {
readNameOnce(); readNameOnce();
} }
@@ -41,7 +40,8 @@ void Cpu::readNameOnce() {
const QByteArray data = f.readAll(); const QByteArray data = f.readAll();
f.close(); f.close();
static const QRegularExpression re(QStringLiteral("model name\\s*:\\s*(.+)")); static const QRegularExpression re(
QStringLiteral("model name\\s*:\\s*(.+)"));
const auto match = re.match(QString::fromLatin1(data)); const auto match = re.match(QString::fromLatin1(data));
if (!match.hasMatch()) { if (!match.hasMatch()) {
return; return;
@@ -64,8 +64,9 @@ void Cpu::refreshPercentage() {
const QByteArray data = f.readAll(); const QByteArray data = f.readAll();
f.close(); f.close();
static const QRegularExpression re( static const QRegularExpression re(QStringLiteral(
QStringLiteral("^cpu\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)")); "^cpu\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)"
"\\s+(\\d+)\\s+(\\d+)"));
const auto match = re.match(QString::fromLatin1(data)); const auto match = re.match(QString::fromLatin1(data));
if (!match.hasMatch()) { if (!match.hasMatch()) {
return; return;
@@ -83,7 +84,10 @@ void Cpu::refreshPercentage() {
const quint64 totalDiff = total > m_lastTotal ? total - m_lastTotal : 0; const quint64 totalDiff = total > m_lastTotal ? total - m_lastTotal : 0;
const quint64 idleDiff = idle > m_lastIdle ? idle - m_lastIdle : 0; const quint64 idleDiff = idle > m_lastIdle ? idle - m_lastIdle : 0;
const qreal newPerc = totalDiff > 0 ? 1.0 - static_cast<qreal>(idleDiff) / static_cast<qreal>(totalDiff) : 0.0; const qreal newPerc =
totalDiff > 0
? 1.0 - static_cast<qreal>(idleDiff) / static_cast<qreal>(totalDiff)
: 0.0;
m_lastTotal = total; m_lastTotal = total;
m_lastIdle = idle; m_lastIdle = idle;
@@ -105,7 +109,8 @@ void Cpu::refreshTemperature() {
QString Cpu::cleanName(QString s) { QString Cpu::cleanName(QString s) {
static const QRegularExpression noise( static const QRegularExpression noise(
QStringLiteral("\\(R\\)|\\(TM\\)|CPU|\\d+(?:th|nd|rd|st) Gen |Core |Processor"), QStringLiteral(
"\\(R\\)|\\(TM\\)|CPU|\\d+(?:th|nd|rd|st) Gen |Core |Processor"),
QRegularExpression::CaseInsensitiveOption); QRegularExpression::CaseInsensitiveOption);
static const QRegularExpression spaces(QStringLiteral("\\s+")); static const QRegularExpression spaces(QStringLiteral("\\s+"));
+17 -9
View File
@@ -31,22 +31,30 @@ enum DesktopRoles {
explicit DesktopModel(QObject* parent = nullptr); explicit DesktopModel(QObject* parent = nullptr);
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override; [[nodiscard]] int rowCount(
[[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; const QModelIndex& parent = QModelIndex()) const override;
[[nodiscard]] QVariant data(
const QModelIndex& index, int role = Qt::DisplayRole) const override;
[[nodiscard]] QHash<int, QByteArray> roleNames() const override; [[nodiscard]] QHash<int, QByteArray> roleNames() const override;
Q_INVOKABLE void loadDirectory(const QString& path); Q_INVOKABLE void loadDirectory(const QString& path);
Q_INVOKABLE void moveIcon(int index, int newX, int newY); Q_INVOKABLE void moveIcon(int index, int newX, int newY);
Q_INVOKABLE void massMove(const QVariantList &selectedPathsList, const QString &leaderPath, int targetX, int targetY, int maxCol, int maxRow); Q_INVOKABLE void massMove(
const QVariantList& selectedPathsList,
const QString& leaderPath,
int targetX,
int targetY,
int maxCol,
int maxRow);
Q_PROPERTY(int rows READ rows WRITE setRows NOTIFY rowsChanged) Q_PROPERTY(int rows READ rows WRITE setRows NOTIFY rowsChanged)
public: [[nodiscard]] int rows() const { return m_rows; }
[[nodiscard]] int rows() const {
return m_rows;
}
void setRows(int r) { void setRows(int r) {
if (m_rows != r) { m_rows = r; emit rowsChanged(); } if (m_rows != r) {
m_rows = r;
emit rowsChanged();
}
} }
signals: signals:
@@ -62,4 +70,4 @@ void saveCurrentLayout();
void onDirectoryChanged(); void onDirectoryChanged();
}; };
}; }; // namespace ZShell::services
@@ -9,11 +9,12 @@
namespace ZShell::services { namespace ZShell::services {
DesktopStateManager::DesktopStateManager(QObject *parent) : QObject(parent) { DesktopStateManager::DesktopStateManager(QObject* parent) : QObject(parent) {}
}
QString DesktopStateManager::getConfigFilePath() const { QString DesktopStateManager::getConfigFilePath() const {
QString configDir = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + "/zshell"; QString configDir =
QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) +
"/zshell";
QDir dir(configDir); QDir dir(configDir);
if (!dir.exists()) { if (!dir.exists()) {
dir.mkpath("."); dir.mkpath(".");
@@ -30,7 +31,8 @@ void DesktopStateManager::saveLayout(const QVariantMap& layout) {
file.write(doc.toJson(QJsonDocument::Indented)); file.write(doc.toJson(QJsonDocument::Indented));
file.close(); file.close();
} else { } else {
qWarning() << "zshell: Cannot save desktop layout to" << getConfigFilePath(); qWarning() << "zshell: Cannot save desktop layout to"
<< getConfigFilePath();
} }
} }
+12 -5
View File
@@ -8,13 +8,17 @@ constexpr qreal kKib = 1024.0;
} // namespace } // namespace
DiskInfo::DiskInfo(QString mount, quint64 usedBytes, quint64 totalBytes, bool hasRoot, QObject* parent) DiskInfo::DiskInfo(
QString mount,
quint64 usedBytes,
quint64 totalBytes,
bool hasRoot,
QObject* parent)
: QObject(parent) : QObject(parent)
, m_mount(std::move(mount)) , m_mount(std::move(mount))
, m_usedBytes(usedBytes) , m_usedBytes(usedBytes)
, m_totalBytes(totalBytes) , m_totalBytes(totalBytes)
, m_hasRoot(hasRoot) { , m_hasRoot(hasRoot) {}
}
QString DiskInfo::mount() const { QString DiskInfo::mount() const {
return m_mount; return m_mount;
@@ -29,12 +33,15 @@ qreal DiskInfo::total() const {
} }
qreal DiskInfo::free() const { qreal DiskInfo::free() const {
const quint64 freeBytes = m_totalBytes > m_usedBytes ? m_totalBytes - m_usedBytes : 0; const quint64 freeBytes =
m_totalBytes > m_usedBytes ? m_totalBytes - m_usedBytes : 0;
return static_cast<qreal>(freeBytes) / kKib; return static_cast<qreal>(freeBytes) / kKib;
} }
qreal DiskInfo::perc() const { qreal DiskInfo::perc() const {
return m_totalBytes > 0 ? static_cast<qreal>(m_usedBytes) / static_cast<qreal>(m_totalBytes) : 0.0; return m_totalBytes > 0 ? static_cast<qreal>(m_usedBytes) /
static_cast<qreal>(m_totalBytes)
: 0.0;
} }
bool DiskInfo::hasRoot() const { bool DiskInfo::hasRoot() const {
+6 -1
View File
@@ -18,7 +18,12 @@ Q_PROPERTY(qreal perc READ perc NOTIFY percChanged)
Q_PROPERTY(bool hasRoot READ hasRoot NOTIFY hasRootChanged) Q_PROPERTY(bool hasRoot READ hasRoot NOTIFY hasRootChanged)
public: public:
DiskInfo(QString mount, quint64 usedBytes, quint64 totalBytes, bool hasRoot, QObject* parent = nullptr); DiskInfo(
QString mount,
quint64 usedBytes,
quint64 totalBytes,
bool hasRoot,
QObject* parent = nullptr);
[[nodiscard]] QString mount() const; [[nodiscard]] QString mount() const;
[[nodiscard]] qreal used() const; [[nodiscard]] qreal used() const;
+12 -19
View File
@@ -51,16 +51,14 @@ int HyprsunsetManager::temp() const {
} }
void HyprsunsetManager::setActiveAuto(bool activeAuto) { void HyprsunsetManager::setActiveAuto(bool activeAuto) {
if (activeAuto == m_activeAuto) if (activeAuto == m_activeAuto) return;
return;
m_activeAuto = activeAuto; m_activeAuto = activeAuto;
emit activeAutoChanged(); emit activeAutoChanged();
} }
void HyprsunsetManager::setManualToggle(bool toggle) { void HyprsunsetManager::setManualToggle(bool toggle) {
if (toggle == m_manualToggle) if (toggle == m_manualToggle) return;
return;
m_manualToggle = toggle; m_manualToggle = toggle;
emit manualToggleChanged(); emit manualToggleChanged();
@@ -69,8 +67,7 @@ void HyprsunsetManager::setManualToggle(bool toggle) {
} }
void HyprsunsetManager::setEndTime(const int& time) { void HyprsunsetManager::setEndTime(const int& time) {
if (time == m_endTime) if (time == m_endTime) return;
return;
m_endTime = time; m_endTime = time;
emit endTimeChanged(); emit endTimeChanged();
@@ -78,8 +75,7 @@ void HyprsunsetManager::setEndTime(const int& time) {
} }
void HyprsunsetManager::setStartTime(const int& time) { void HyprsunsetManager::setStartTime(const int& time) {
if (time == m_startTime) if (time == m_startTime) return;
return;
m_startTime = time; m_startTime = time;
emit startTimeChanged(); emit startTimeChanged();
@@ -87,8 +83,7 @@ void HyprsunsetManager::setStartTime(const int& time) {
} }
void HyprsunsetManager::setTemp(const int& temp) { void HyprsunsetManager::setTemp(const int& temp) {
if (temp == m_temp) if (temp == m_temp) return;
return;
m_temp = temp; m_temp = temp;
emit tempChanged(); emit tempChanged();
@@ -104,8 +99,7 @@ void HyprsunsetManager::toggle() {
} }
void HyprsunsetManager::start() { void HyprsunsetManager::start() {
if (m_enabled && m_initialized) if (m_enabled && m_initialized) return;
return;
m_initialized = true; m_initialized = true;
m_enabled = true; m_enabled = true;
@@ -113,13 +107,13 @@ void HyprsunsetManager::start() {
emit enabledChanged(); emit enabledChanged();
m_process.setProgram("hyprctl"); m_process.setProgram("hyprctl");
m_process.setArguments({"hyprsunset", "temperature", QString::number(m_temp)}); m_process.setArguments(
{"hyprsunset", "temperature", QString::number(m_temp)});
m_process.startDetached(); m_process.startDetached();
} }
void HyprsunsetManager::end() { void HyprsunsetManager::end() {
if (!m_enabled && m_initialized) if (!m_enabled && m_initialized) return;
return;
m_initialized = true; m_initialized = true;
m_enabled = false; m_enabled = false;
@@ -132,12 +126,11 @@ void HyprsunsetManager::end() {
} }
void HyprsunsetManager::apply() { void HyprsunsetManager::apply() {
if (m_manualToggle || !m_activeAuto || !m_startAllowed) if (m_manualToggle || !m_activeAuto || !m_startAllowed) return;
return;
const auto current = QTime::currentTime(); const auto current = QTime::currentTime();
const auto currentMin = current.hour() * 60 + current.minute(); const auto currentMin = current.hour() * 60 + current.minute();
bool isDarkTime; bool isDarkTime = false;
if (m_startTime <= m_endTime) { if (m_startTime <= m_endTime) {
isDarkTime = (currentMin >= m_startTime && currentMin < m_endTime); isDarkTime = (currentMin >= m_startTime && currentMin < m_endTime);
@@ -152,4 +145,4 @@ void HyprsunsetManager::apply() {
} }
} }
}; }; // namespace ZShell::services
@@ -13,11 +13,16 @@ class HyprsunsetManager : public QObject {
Q_OBJECT Q_OBJECT
QML_ELEMENT QML_ELEMENT
Q_PROPERTY(bool enabled READ enabled NOTIFY enabledChanged) Q_PROPERTY(bool enabled READ enabled NOTIFY enabledChanged)
Q_PROPERTY(int startTime READ startTime WRITE setStartTime NOTIFY startTimeChanged) Q_PROPERTY(
int startTime READ startTime WRITE setStartTime NOTIFY startTimeChanged)
Q_PROPERTY(int endTime READ endTime WRITE setEndTime NOTIFY endTimeChanged) Q_PROPERTY(int endTime READ endTime WRITE setEndTime NOTIFY endTimeChanged)
Q_PROPERTY(int temp READ temp WRITE setTemp NOTIFY tempChanged) Q_PROPERTY(int temp READ temp WRITE setTemp NOTIFY tempChanged)
Q_PROPERTY(bool activeAuto READ activeAuto WRITE setActiveAuto NOTIFY activeAutoChanged) Q_PROPERTY(
Q_PROPERTY(bool manualToggle READ manualToggle WRITE setManualToggle NOTIFY manualToggleChanged) bool activeAuto READ activeAuto WRITE setActiveAuto NOTIFY
activeAutoChanged)
Q_PROPERTY(
bool manualToggle READ manualToggle WRITE setManualToggle NOTIFY
manualToggleChanged)
public: public:
explicit HyprsunsetManager(QObject* parent = nullptr); explicit HyprsunsetManager(QObject* parent = nullptr);
@@ -63,4 +68,4 @@ void start();
void end(); void end();
}; };
}; }; // namespace ZShell::services
+5 -5
View File
@@ -5,9 +5,7 @@
namespace ZShell::services { namespace ZShell::services {
Memory::Memory(QObject* parent) Memory::Memory(QObject* parent) : TickingService(parent) {}
: TickingService(parent) {
}
qreal Memory::used() const { qreal Memory::used() const {
return m_used; return m_used;
@@ -29,8 +27,10 @@ void Memory::tick() {
const QByteArray data = f.readAll(); const QByteArray data = f.readAll();
f.close(); f.close();
static const QRegularExpression reTotal(QStringLiteral("MemTotal: *(\\d+)")); static const QRegularExpression reTotal(
static const QRegularExpression reAvail(QStringLiteral("MemAvailable: *(\\d+)")); QStringLiteral("MemTotal: *(\\d+)"));
static const QRegularExpression reAvail(
QStringLiteral("MemAvailable: *(\\d+)"));
const QString text = QString::fromLatin1(data); const QString text = QString::fromLatin1(data);
const auto totalMatch = reTotal.match(text); const auto totalMatch = reTotal.match(text);
+25 -12
View File
@@ -31,8 +31,10 @@ void doInit() {
}); });
} }
[[nodiscard]] std::optional<double> readTempInput(const sensors_chip_name* chip, const sensors_feature* feat) { [[nodiscard]] std::optional<double> readTempInput(
const sensors_subfeature* sf = sensors_get_subfeature(chip, feat, SENSORS_SUBFEATURE_TEMP_INPUT); const sensors_chip_name* chip, const sensors_feature* feat) {
const sensors_subfeature* sf =
sensors_get_subfeature(chip, feat, SENSORS_SUBFEATURE_TEMP_INPUT);
if (!sf) { if (!sf) {
return std::nullopt; return std::nullopt;
} }
@@ -43,7 +45,8 @@ void doInit() {
return value; return value;
} }
[[nodiscard]] QByteArray featureLabel(const sensors_chip_name* chip, const sensors_feature* feat) { [[nodiscard]] QByteArray featureLabel(
const sensors_chip_name* chip, const sensors_feature* feat) {
char* raw = sensors_get_label(chip, feat); char* raw = sensors_get_label(chip, feat);
if (!raw) { if (!raw) {
return {}; return {};
@@ -59,7 +62,8 @@ bool labelEquals(const QByteArray& label, const char* literal) {
bool labelStartsWith(const QByteArray& label, const char* prefix) { bool labelStartsWith(const QByteArray& label, const char* prefix) {
const auto n = std::strlen(prefix); const auto n = std::strlen(prefix);
return static_cast<size_t>(label.size()) >= n && std::memcmp(label.constData(), prefix, n) == 0; return static_cast<size_t>(label.size()) >= n &&
std::memcmp(label.constData(), prefix, n) == 0;
} }
} // namespace } // namespace
@@ -78,9 +82,11 @@ std::optional<double> cpuPackageTemp() {
std::optional<double> fallback; // Tctl std::optional<double> fallback; // Tctl
int chipNr = 0; int chipNr = 0;
while (const sensors_chip_name* chip = sensors_get_detected_chips(nullptr, &chipNr)) { while (const sensors_chip_name* chip =
sensors_get_detected_chips(nullptr, &chipNr)) {
int featNr = 0; int featNr = 0;
while (const sensors_feature* feat = sensors_get_features(chip, &featNr)) { while (const sensors_feature* feat =
sensors_get_features(chip, &featNr)) {
if (feat->type != SENSORS_FEATURE_TEMP) { if (feat->type != SENSORS_FEATURE_TEMP) {
continue; continue;
} }
@@ -89,7 +95,8 @@ std::optional<double> cpuPackageTemp() {
continue; continue;
} }
if (labelStartsWith(label, "Package id ") || labelEquals(label, "Tdie")) { if (labelStartsWith(label, "Package id ") ||
labelEquals(label, "Tdie")) {
if (auto v = readTempInput(chip, feat)) { if (auto v = readTempInput(chip, feat)) {
primary = v; primary = v;
} }
@@ -116,13 +123,15 @@ std::optional<double> gpuPciAverageTemp() {
int countFallback = 0; int countFallback = 0;
int chipNr = 0; int chipNr = 0;
while (const sensors_chip_name* chip = sensors_get_detected_chips(nullptr, &chipNr)) { while (const sensors_chip_name* chip =
sensors_get_detected_chips(nullptr, &chipNr)) {
if (chip->bus.type != SENSORS_BUS_TYPE_PCI) { if (chip->bus.type != SENSORS_BUS_TYPE_PCI) {
continue; continue;
} }
int featNr = 0; int featNr = 0;
while (const sensors_feature* feat = sensors_get_features(chip, &featNr)) { while (const sensors_feature* feat =
sensors_get_features(chip, &featNr)) {
if (feat->type != SENSORS_FEATURE_TEMP) { if (feat->type != SENSORS_FEATURE_TEMP) {
continue; continue;
} }
@@ -131,10 +140,14 @@ std::optional<double> gpuPciAverageTemp() {
continue; continue;
} }
const bool tempIndexed = labelStartsWith(label, "temp") && label.size() > 4 && const bool tempIndexed =
labelStartsWith(label, "temp") && label.size() > 4 &&
std::isdigit(static_cast<unsigned char>(label[4])); std::isdigit(static_cast<unsigned char>(label[4]));
const bool isPrimary = tempIndexed || labelEquals(label, "GPU core") || labelEquals(label, "edge"); const bool isPrimary = tempIndexed ||
const bool isFallback = labelEquals(label, "junction") || labelEquals(label, "mem"); labelEquals(label, "GPU core") ||
labelEquals(label, "edge");
const bool isFallback = labelEquals(label, "junction") ||
labelEquals(label, "mem");
if (!isPrimary && !isFallback) { if (!isPrimary && !isFallback) {
continue; continue;
+1 -3
View File
@@ -5,9 +5,7 @@
namespace ZShell::services { namespace ZShell::services {
Service::Service(QObject* parent) Service::Service(QObject* parent) : QObject(parent) {}
: QObject(parent) {
}
void Service::ref(QObject* sender) { void Service::ref(QObject* sender) {
if (m_refs.isEmpty()) { if (m_refs.isEmpty()) {
+1 -2
View File
@@ -5,8 +5,7 @@
namespace ZShell::services { namespace ZShell::services {
ServiceRef::ServiceRef(Service* service, QObject* parent) ServiceRef::ServiceRef(Service* service, QObject* parent)
: QObject(parent) : QObject(parent), m_service(service) {
, m_service(service) {
if (m_service) { if (m_service) {
m_service->ref(this); m_service->ref(this);
} }
+3 -1
View File
@@ -10,7 +10,9 @@ class ServiceRef : public QObject {
Q_OBJECT Q_OBJECT
QML_ELEMENT QML_ELEMENT
Q_PROPERTY(ZShell::services::Service* service READ service WRITE setService NOTIFY serviceChanged) Q_PROPERTY(
ZShell::services::Service* service READ service WRITE setService NOTIFY
serviceChanged)
public: public:
explicit ServiceRef(Service* service = nullptr, QObject* parent = nullptr); explicit ServiceRef(Service* service = nullptr, QObject* parent = nullptr);
+36 -38
View File
@@ -24,12 +24,14 @@ struct Accum {
}; };
[[nodiscard]] QString sysfsRealPath(uint major, uint minor) { [[nodiscard]] QString sysfsRealPath(uint major, uint minor) {
const QString link = QStringLiteral("/sys/dev/block/%1:%2").arg(major).arg(minor); const QString link =
QStringLiteral("/sys/dev/block/%1:%2").arg(major).arg(minor);
const QString resolved = QFileInfo(link).canonicalFilePath(); const QString resolved = QFileInfo(link).canonicalFilePath();
return resolved; return resolved;
} }
[[nodiscard]] bool readDevtFromSysfs(const QString& sysfsBlockDir, uint& major, uint& minor) { [[nodiscard]] bool readDevtFromSysfs(
const QString& sysfsBlockDir, uint& major, uint& minor) {
QFile f(sysfsBlockDir + QStringLiteral("/dev")); QFile f(sysfsBlockDir + QStringLiteral("/dev"));
if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) { if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
return false; return false;
@@ -50,6 +52,7 @@ struct Accum {
QStringList resolveByDevt(uint major, uint minor, int depth = 0); QStringList resolveByDevt(uint major, uint minor, int depth = 0);
// NOLINTNEXTLINE(misc-no-recursion)
QStringList resolveAtNode(const QString& node, int depth) { QStringList resolveAtNode(const QString& node, int depth) {
if (node.isEmpty() || depth > 8) { if (node.isEmpty() || depth > 8) {
return {}; return {};
@@ -67,13 +70,15 @@ QStringList resolveAtNode(const QString& node, int depth) {
const QDir slavesDir(node + QStringLiteral("/slaves")); const QDir slavesDir(node + QStringLiteral("/slaves"));
if (slavesDir.exists()) { if (slavesDir.exists()) {
const QStringList slaves = slavesDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); const QStringList slaves =
slavesDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
if (!slaves.isEmpty()) { if (!slaves.isEmpty()) {
QStringList out; QStringList out;
for (const QString& slave : slaves) { for (const QString& slave : slaves) {
uint sm = 0; uint sm = 0;
uint sn = 0; uint sn = 0;
const QString slaveDir = QStringLiteral("/sys/class/block/") + slave; const QString slaveDir =
QStringLiteral("/sys/class/block/") + slave;
if (!readDevtFromSysfs(slaveDir, sm, sn)) { if (!readDevtFromSysfs(slaveDir, sm, sn)) {
continue; continue;
} }
@@ -91,15 +96,14 @@ QStringList resolveAtNode(const QString& node, int depth) {
return {nodeInfo.fileName()}; return {nodeInfo.fileName()};
} }
// NOLINTNEXTLINE(misc-no-recursion)
QStringList resolveByDevt(uint major, uint minor, int depth) { QStringList resolveByDevt(uint major, uint minor, int depth) {
return resolveAtNode(sysfsRealPath(major, minor), depth); return resolveAtNode(sysfsRealPath(major, minor), depth);
} }
} // namespace } // namespace
Storage::Storage(QObject* parent) Storage::Storage(QObject* parent) : TickingService(parent) {}
: TickingService(parent) {
}
qreal Storage::percentage() const { qreal Storage::percentage() const {
qreal totalUsed = 0.0; qreal totalUsed = 0.0;
@@ -124,7 +128,8 @@ bool Storage::sameOrder(const QList<DiskInfo*>& a, const QList<DiskInfo*>& b) {
} }
QQmlListProperty<DiskInfo> Storage::disksProp() { QQmlListProperty<DiskInfo> Storage::disksProp() {
return QQmlListProperty<DiskInfo>(this, nullptr, &Storage::disksCount, &Storage::disksAt); return QQmlListProperty<DiskInfo>(
this, nullptr, &Storage::disksCount, &Storage::disksAt);
} }
qsizetype Storage::disksCount(QQmlListProperty<DiskInfo>* prop) { qsizetype Storage::disksCount(QQmlListProperty<DiskInfo>* prop) {
@@ -157,30 +162,11 @@ DiskInfo* Storage::primaryDisk() const {
bool Storage::isPseudoFs(QByteArrayView fsType) { bool Storage::isPseudoFs(QByteArrayView fsType) {
static constexpr const char* kPseudo[] = { static constexpr const char* kPseudo[] = {
"tmpfs", "tmpfs", "devtmpfs", "proc", "sysfs", "cgroup",
"devtmpfs", "cgroup2", "overlay", "squashfs", "devpts", "mqueue",
"proc", "ramfs", "rpc_pipefs", "autofs", "configfs", "debugfs",
"sysfs", "tracefs", "securityfs", "pstore", "bpf", "binfmt_misc",
"cgroup", "hugetlbfs", "fusectl", "efivarfs", "selinuxfs",
"cgroup2",
"overlay",
"squashfs",
"devpts",
"mqueue",
"ramfs",
"rpc_pipefs",
"autofs",
"configfs",
"debugfs",
"tracefs",
"securityfs",
"pstore",
"bpf",
"binfmt_misc",
"hugetlbfs",
"fusectl",
"efivarfs",
"selinuxfs",
}; };
for (const char* p : kPseudo) { for (const char* p : kPseudo) {
if (fsType == QByteArrayView(p)) { if (fsType == QByteArrayView(p)) {
@@ -232,7 +218,8 @@ void Storage::tick() {
const QByteArray device = v.device(); const QByteArray device = v.device();
const auto totalBytes = static_cast<quint64>(v.bytesTotal()); const auto totalBytes = static_cast<quint64>(v.bytesTotal());
const auto availBytes = static_cast<quint64>(v.bytesAvailable()); const auto availBytes = static_cast<quint64>(v.bytesAvailable());
const quint64 usedBytes = totalBytes > availBytes ? totalBytes - availBytes : 0; const quint64 usedBytes =
totalBytes > availBytes ? totalBytes - availBytes : 0;
const bool isRoot = v.rootPath() == QStringLiteral("/"); const bool isRoot = v.rootPath() == QStringLiteral("/");
DeviceEntry& e = byDevice[device]; DeviceEntry& e = byDevice[device];
@@ -244,7 +231,8 @@ void Storage::tick() {
for (auto it = byDevice.constBegin(); it != byDevice.constEnd(); ++it) { for (auto it = byDevice.constBegin(); it != byDevice.constEnd(); ++it) {
const DeviceEntry& e = it.value(); const DeviceEntry& e = it.value();
const QStringList disks = resolveToPhysicalDisks(QString::fromLocal8Bit(e.device)); const QStringList disks =
resolveToPhysicalDisks(QString::fromLocal8Bit(e.device));
if (disks.isEmpty()) { if (disks.isEmpty()) {
continue; continue;
} }
@@ -269,14 +257,23 @@ void Storage::tick() {
next.reserve(byDisk.size()); next.reserve(byDisk.size());
for (auto it = byDisk.constBegin(); it != byDisk.constEnd(); ++it) { for (auto it = byDisk.constBegin(); it != byDisk.constEnd(); ++it) {
if (DiskInfo* survivor = existing.take(it.key())) { if (DiskInfo* survivor = existing.take(it.key())) {
survivor->update(it.value().usedBytes, it.value().totalBytes, it.value().hasRoot); survivor->update(
it.value().usedBytes,
it.value().totalBytes,
it.value().hasRoot);
next.append(survivor); next.append(survivor);
} else { } else {
next.append(new DiskInfo(it.key(), it.value().usedBytes, it.value().totalBytes, it.value().hasRoot, this)); next.append(new DiskInfo(
it.key(),
it.value().usedBytes,
it.value().totalBytes,
it.value().hasRoot,
this));
} }
} }
std::sort(next.begin(), next.end(), [](const DiskInfo* a, const DiskInfo* b) { std::sort(
next.begin(), next.end(), [](const DiskInfo* a, const DiskInfo* b) {
if (a->hasRoot() != b->hasRoot()) { if (a->hasRoot() != b->hasRoot()) {
return a->hasRoot(); return a->hasRoot();
} }
@@ -284,7 +281,8 @@ void Storage::tick() {
}); });
bool manualCleared = false; bool manualCleared = false;
if (DiskInfo* m = m_manualPrimaryDisk.data(); m && existing.contains(m->mount())) { if (DiskInfo* m = m_manualPrimaryDisk.data();
m && existing.contains(m->mount())) {
m_manualPrimaryDisk.clear(); m_manualPrimaryDisk.clear();
manualCleared = true; manualCleared = true;
} }
+13 -6
View File
@@ -17,10 +17,15 @@ QML_ELEMENT
QML_SINGLETON QML_SINGLETON
Q_PROPERTY(qreal percentage READ percentage NOTIFY percentageChanged) Q_PROPERTY(qreal percentage READ percentage NOTIFY percentageChanged)
Q_PROPERTY(QQmlListProperty<ZShell::services::DiskInfo> disks READ disksProp NOTIFY disksChanged) Q_PROPERTY(
Q_PROPERTY(ZShell::services::DiskInfo* manualPrimaryDisk READ manualPrimaryDisk WRITE setManualPrimaryDisk NOTIFY QQmlListProperty<ZShell::services::DiskInfo> disks READ disksProp NOTIFY
manualPrimaryDiskChanged) disksChanged)
Q_PROPERTY(ZShell::services::DiskInfo* primaryDisk READ primaryDisk NOTIFY primaryDiskChanged) Q_PROPERTY(
ZShell::services::DiskInfo* manualPrimaryDisk READ manualPrimaryDisk
WRITE setManualPrimaryDisk NOTIFY manualPrimaryDiskChanged)
Q_PROPERTY(
ZShell::services::DiskInfo* primaryDisk READ primaryDisk NOTIFY
primaryDiskChanged)
public: public:
explicit Storage(QObject* parent = nullptr); explicit Storage(QObject* parent = nullptr);
@@ -41,9 +46,11 @@ protected:
void tick() override; void tick() override;
private: private:
[[nodiscard]] static QStringList resolveToPhysicalDisks(const QString& devicePath); [[nodiscard]] static QStringList resolveToPhysicalDisks(
const QString& devicePath);
[[nodiscard]] static bool isPseudoFs(QByteArrayView fsType); [[nodiscard]] static bool isPseudoFs(QByteArrayView fsType);
[[nodiscard]] static bool sameOrder(const QList<DiskInfo*>& a, const QList<DiskInfo*>& b); [[nodiscard]] static bool sameOrder(
const QList<DiskInfo*>& a, const QList<DiskInfo*>& b);
static qsizetype disksCount(QQmlListProperty<DiskInfo>* prop); static qsizetype disksCount(QQmlListProperty<DiskInfo>* prop);
static DiskInfo* disksAt(QQmlListProperty<DiskInfo>* prop, qsizetype i); static DiskInfo* disksAt(QQmlListProperty<DiskInfo>* prop, qsizetype i);
+2 -1
View File
@@ -8,7 +8,8 @@ namespace ZShell::services {
class TickingService : public Service { class TickingService : public Service {
Q_OBJECT Q_OBJECT
Q_PROPERTY(int updateInterval READ updateInterval NOTIFY updateIntervalChanged) Q_PROPERTY(
int updateInterval READ updateInterval NOTIFY updateIntervalChanged)
public: public:
explicit TickingService(QObject* parent = nullptr); explicit TickingService(QObject* parent = nullptr);
+2 -1
View File
@@ -27,7 +27,8 @@ QML_ELEMENT
QML_SINGLETON QML_SINGLETON
public: public:
Q_INVOKABLE [[nodiscard]] FormatResult formatKib(qreal kib, qreal total) const; Q_INVOKABLE [[nodiscard]] FormatResult formatKib(
qreal kib, qreal total) const;
}; };
} // namespace ZShell::services::usagefmt } // namespace ZShell::services::usagefmt
+22 -10
View File
@@ -7,17 +7,22 @@
namespace ZShell { namespace ZShell {
AppEntry::AppEntry(QObject* entry, unsigned int frequency, QObject* parent) AppEntry::AppEntry(QObject* entry, unsigned int frequency, QObject* parent)
: QObject(parent) : QObject(parent), m_entry(entry), m_frequency(frequency) {
, m_entry(entry)
, m_frequency(frequency) {
const auto mo = m_entry->metaObject(); const auto mo = m_entry->metaObject();
const auto tmo = metaObject(); const auto tmo = metaObject();
for (const auto& prop : for (const auto& prop :
{ "name", "comment", "execString", "startupClass", "genericName", "categories", "keywords" }) { {"name",
"comment",
"execString",
"startupClass",
"genericName",
"categories",
"keywords"}) {
const auto metaProp = mo->property(mo->indexOfProperty(prop)); const auto metaProp = mo->property(mo->indexOfProperty(prop));
const auto thisMetaProp = tmo->property(tmo->indexOfProperty(prop)); const auto thisMetaProp = tmo->property(tmo->indexOfProperty(prop));
QObject::connect(m_entry, metaProp.notifySignal(), this, thisMetaProp.notifySignal()); QObject::connect(
m_entry, metaProp.notifySignal(), this, thisMetaProp.notifySignal());
} }
QObject::connect(m_entry, &QObject::destroyed, this, [this]() { QObject::connect(m_entry, &QObject::destroyed, this, [this]() {
@@ -115,7 +120,9 @@ AppDb::AppDb(QObject* parent)
db.open(); db.open();
QSqlQuery query(db); QSqlQuery query(db);
query.exec("CREATE TABLE IF NOT EXISTS frequencies (id TEXT PRIMARY KEY, frequency INTEGER)"); query.exec(
"CREATE TABLE IF NOT EXISTS frequencies (id TEXT PRIMARY KEY, "
"frequency INTEGER)");
} }
QString AppDb::uuid() const { QString AppDb::uuid() const {
@@ -142,7 +149,9 @@ void AppDb::setPath(const QString& path) {
db.open(); db.open();
QSqlQuery query(db); QSqlQuery query(db);
query.exec("CREATE TABLE IF NOT EXISTS frequencies (id TEXT PRIMARY KEY, frequency INTEGER)"); query.exec(
"CREATE TABLE IF NOT EXISTS frequencies (id TEXT PRIMARY KEY, "
"frequency INTEGER)");
updateAppFrequencies(); updateAppFrequencies();
} }
@@ -170,7 +179,8 @@ void AppDb::incrementFrequency(const QString& id) {
auto db = QSqlDatabase::database(m_uuid); auto db = QSqlDatabase::database(m_uuid);
QSqlQuery query(db); QSqlQuery query(db);
query.prepare("INSERT INTO frequencies (id, frequency) " query.prepare(
"INSERT INTO frequencies (id, frequency) "
"VALUES (:id, 1) " "VALUES (:id, 1) "
"ON CONFLICT (id) DO UPDATE SET frequency = frequency + 1"); "ON CONFLICT (id) DO UPDATE SET frequency = frequency + 1");
query.bindValue(":id", id); query.bindValue(":id", id);
@@ -186,13 +196,15 @@ void AppDb::incrementFrequency(const QString& id) {
emit appsChanged(); emit appsChanged();
} }
} else { } else {
qWarning() << "AppDb::incrementFrequency: could not find app with id" << id; qWarning() << "AppDb::incrementFrequency: could not find app with id"
<< id;
} }
} }
QList<AppEntry*>& AppDb::getSortedApps() const { QList<AppEntry*>& AppDb::getSortedApps() const {
m_sortedApps = m_apps.values(); m_sortedApps = m_apps.values();
std::sort(m_sortedApps.begin(), m_sortedApps.end(), [](AppEntry* a, AppEntry* b) { std::sort(
m_sortedApps.begin(), m_sortedApps.end(), [](AppEntry* a, AppEntry* b) {
if (a->frequency() != b->frequency()) { if (a->frequency() != b->frequency()) {
return a->frequency() > b->frequency(); return a->frequency() > b->frequency();
} }
+7 -3
View File
@@ -27,7 +27,8 @@ Q_PROPERTY(QString categories READ categories NOTIFY categoriesChanged)
Q_PROPERTY(QString keywords READ keywords NOTIFY keywordsChanged) Q_PROPERTY(QString keywords READ keywords NOTIFY keywordsChanged)
public: public:
explicit AppEntry(QObject* entry, quint32 frequency, QObject* parent = nullptr); explicit AppEntry(
QObject* entry, quint32 frequency, QObject* parent = nullptr);
[[nodiscard]] QObject* entry() const; [[nodiscard]] QObject* entry() const;
@@ -65,8 +66,11 @@ QML_ELEMENT
Q_PROPERTY(QString uuid READ uuid CONSTANT) Q_PROPERTY(QString uuid READ uuid CONSTANT)
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged REQUIRED) Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged REQUIRED)
Q_PROPERTY(QObjectList entries READ entries WRITE setEntries NOTIFY entriesChanged REQUIRED) Q_PROPERTY(
Q_PROPERTY(QQmlListProperty<ZShell::AppEntry> apps READ apps NOTIFY appsChanged) QObjectList entries READ entries WRITE setEntries NOTIFY entriesChanged
REQUIRED)
Q_PROPERTY(
QQmlListProperty<ZShell::AppEntry> apps READ apps NOTIFY appsChanged)
public: public:
explicit AppDb(QObject* parent = nullptr); explicit AppDb(QObject* parent = nullptr);
+56 -16
View File
@@ -16,7 +16,11 @@ ImageAnalyser::ImageAnalyser(QObject* parent)
, m_rescaleSize(128) , m_rescaleSize(128)
, m_dominantColour(0, 0, 0) , m_dominantColour(0, 0, 0)
, m_luminance(0) { , m_luminance(0) {
QObject::connect(m_futureWatcher, &QFutureWatcher<AnalyseResult>::finished, this, [this]() { QObject::connect(
m_futureWatcher,
&QFutureWatcher<AnalyseResult>::finished,
this,
[this]() {
if (!m_futureWatcher->future().isResultReadyAt(0)) { if (!m_futureWatcher->future().isResultReadyAt(0)) {
return; return;
} }
@@ -101,23 +105,40 @@ void ImageAnalyser::requestUpdate() {
return; return;
} }
if (!m_sourceItem || (m_sourceItem->window() && m_sourceItem->window()->isVisible() && m_sourceItem->width() > 0 && if (!m_sourceItem ||
m_sourceItem->height() > 0)) { (m_sourceItem->window() && m_sourceItem->window()->isVisible() &&
m_sourceItem->width() > 0 && m_sourceItem->height() > 0)) {
update(); update();
} else if (m_sourceItem) { } else if (m_sourceItem) {
if (!m_sourceItem->window()) { if (!m_sourceItem->window()) {
QObject::connect(m_sourceItem, &QQuickItem::windowChanged, this, &ImageAnalyser::requestUpdate, QObject::connect(
m_sourceItem,
&QQuickItem::windowChanged,
this,
&ImageAnalyser::requestUpdate,
Qt::SingleShotConnection); Qt::SingleShotConnection);
} else if (!m_sourceItem->window()->isVisible()) { } else if (!m_sourceItem->window()->isVisible()) {
QObject::connect(m_sourceItem->window(), &QQuickWindow::visibleChanged, this, &ImageAnalyser::requestUpdate, QObject::connect(
m_sourceItem->window(),
&QQuickWindow::visibleChanged,
this,
&ImageAnalyser::requestUpdate,
Qt::SingleShotConnection); Qt::SingleShotConnection);
} }
if (m_sourceItem->width() <= 0) { if (m_sourceItem->width() <= 0) {
QObject::connect( QObject::connect(
m_sourceItem, &QQuickItem::widthChanged, this, &ImageAnalyser::requestUpdate, Qt::SingleShotConnection); m_sourceItem,
&QQuickItem::widthChanged,
this,
&ImageAnalyser::requestUpdate,
Qt::SingleShotConnection);
} }
if (m_sourceItem->height() <= 0) { if (m_sourceItem->height() <= 0) {
QObject::connect(m_sourceItem, &QQuickItem::heightChanged, this, &ImageAnalyser::requestUpdate, QObject::connect(
m_sourceItem,
&QQuickItem::heightChanged,
this,
&ImageAnalyser::requestUpdate,
Qt::SingleShotConnection); Qt::SingleShotConnection);
} }
} }
@@ -133,19 +154,30 @@ void ImageAnalyser::update() {
} }
if (m_sourceItem) { if (m_sourceItem) {
const QSharedPointer<const QQuickItemGrabResult> grabResult = m_sourceItem->grabToImage(); const QSharedPointer<const QQuickItemGrabResult> grabResult =
QObject::connect(grabResult.data(), &QQuickItemGrabResult::ready, this, [grabResult, this]() { m_sourceItem->grabToImage();
m_futureWatcher->setFuture(QtConcurrent::run(&ImageAnalyser::analyse, grabResult->image(), m_rescaleSize)); QObject::connect(
grabResult.data(),
&QQuickItemGrabResult::ready,
this,
[grabResult, this]() {
m_futureWatcher->setFuture(
QtConcurrent::run(
&ImageAnalyser::analyse,
grabResult->image(),
m_rescaleSize));
}); });
} else { } else {
m_futureWatcher->setFuture(QtConcurrent::run([=, this](QPromise<AnalyseResult>& promise) { m_futureWatcher->setFuture(
QtConcurrent::run([=, this](QPromise<AnalyseResult>& promise) {
const QImage image(m_source); const QImage image(m_source);
analyse(promise, image, m_rescaleSize); analyse(promise, image, m_rescaleSize);
})); }));
} }
} }
void ImageAnalyser::analyse(QPromise<AnalyseResult>& promise, const QImage& image, int rescaleSize) { void ImageAnalyser::analyse(
QPromise<AnalyseResult>& promise, const QImage& image, int rescaleSize) {
if (image.isNull()) { if (image.isNull()) {
qWarning() << "ImageAnalyser::analyse: image is null"; qWarning() << "ImageAnalyser::analyse: image is null";
return; return;
@@ -153,8 +185,13 @@ void ImageAnalyser::analyse(QPromise<AnalyseResult>& promise, const QImage& imag
QImage img = image; QImage img = image;
if (rescaleSize > 0 && (img.width() > rescaleSize || img.height() > rescaleSize)) { if (rescaleSize > 0 &&
img = img.scaled(rescaleSize, rescaleSize, Qt::KeepAspectRatio, Qt::FastTransformation); (img.width() > rescaleSize || img.height() > rescaleSize)) {
img = img.scaled(
rescaleSize,
rescaleSize,
Qt::KeepAspectRatio,
Qt::FastTransformation);
} }
if (promise.isCanceled()) { if (promise.isCanceled()) {
@@ -199,7 +236,8 @@ void ImageAnalyser::analyse(QPromise<AnalyseResult>& promise, const QImage& imag
const qreal r = pixel[0] / 255.0; const qreal r = pixel[0] / 255.0;
const qreal g = pixel[1] / 255.0; const qreal g = pixel[1] / 255.0;
const qreal b = pixel[2] / 255.0; const qreal b = pixel[2] / 255.0;
totalLuminance += std::sqrt(0.299 * r * r + 0.587 * g * g + 0.114 * b * b); totalLuminance +=
std::sqrt(0.299 * r * r + 0.587 * g * g + 0.114 * b * b);
++count; ++count;
} }
} }
@@ -217,7 +255,9 @@ void ImageAnalyser::analyse(QPromise<AnalyseResult>& promise, const QImage& imag
} }
} }
promise.addResult(qMakePair(QColor((0xFFu << 24) | dominantColour), count == 0 ? 0.0 : totalLuminance / count)); promise.addResult(qMakePair(
QColor((0xFFu << 24) | dominantColour),
count == 0 ? 0.0 : totalLuminance / count));
} }
} // namespace ZShell } // namespace ZShell
+10 -4
View File
@@ -13,9 +13,14 @@ Q_OBJECT
QML_ELEMENT QML_ELEMENT
Q_PROPERTY(QString source READ source WRITE setSource NOTIFY sourceChanged) Q_PROPERTY(QString source READ source WRITE setSource NOTIFY sourceChanged)
Q_PROPERTY(QQuickItem* sourceItem READ sourceItem WRITE setSourceItem NOTIFY sourceItemChanged) Q_PROPERTY(
Q_PROPERTY(int rescaleSize READ rescaleSize WRITE setRescaleSize NOTIFY rescaleSizeChanged) QQuickItem* sourceItem READ sourceItem WRITE setSourceItem NOTIFY
Q_PROPERTY(QColor dominantColour READ dominantColour NOTIFY dominantColourChanged) sourceItemChanged)
Q_PROPERTY(
int rescaleSize READ rescaleSize WRITE setRescaleSize NOTIFY
rescaleSizeChanged)
Q_PROPERTY(
QColor dominantColour READ dominantColour NOTIFY dominantColourChanged)
Q_PROPERTY(qreal luminance READ luminance NOTIFY luminanceChanged) Q_PROPERTY(qreal luminance READ luminance NOTIFY luminanceChanged)
public: public:
@@ -55,7 +60,8 @@ QColor m_dominantColour;
qreal m_luminance; qreal m_luminance;
void update(); void update();
static void analyse(QPromise<AnalyseResult>& promise, const QImage& image, int rescaleSize); static void analyse(
QPromise<AnalyseResult>& promise, const QImage& image, int rescaleSize);
}; };
} // namespace ZShell } // namespace ZShell
+6 -3
View File
@@ -4,8 +4,7 @@
namespace ZShell { namespace ZShell {
Qalculator::Qalculator(QObject* parent) Qalculator::Qalculator(QObject* parent) : QObject(parent) {
: QObject(parent) {
if (!CALCULATOR) { if (!CALCULATOR) {
new Calculator(); new Calculator();
CALCULATOR->loadExchangeRates(); CALCULATOR->loadExchangeRates();
@@ -24,7 +23,11 @@ QString Qalculator::eval(const QString& expr, bool printExpr) const {
std::string parsed; std::string parsed;
std::string result = CALCULATOR->calculateAndPrint( std::string result = CALCULATOR->calculateAndPrint(
CALCULATOR->unlocalizeExpression(expr.toStdString(), eo.parse_options), 100, eo, po, &parsed); CALCULATOR->unlocalizeExpression(expr.toStdString(), eo.parse_options),
100,
eo,
po,
&parsed);
std::string error; std::string error;
while (CALCULATOR->message()) { while (CALCULATOR->message()) {
+2 -1
View File
@@ -13,7 +13,8 @@ QML_SINGLETON
public: public:
explicit Qalculator(QObject* parent = nullptr); explicit Qalculator(QObject* parent = nullptr);
Q_INVOKABLE [[nodiscard]] QString eval(const QString& expr, bool printExpr = true) const; Q_INVOKABLE [[nodiscard]] QString eval(
const QString& expr, bool printExpr = true) const;
}; };
} // namespace ZShell } // namespace ZShell
+5 -5
View File
@@ -7,9 +7,7 @@
namespace ZShell { namespace ZShell {
Requests::Requests(QObject* parent) Requests::Requests(QObject* parent)
: QObject(parent) : QObject(parent), m_manager(new QNetworkAccessManager(this)) {}
, m_manager(new QNetworkAccessManager(this)) {
}
void Requests::get(const QUrl& url, QJSValue onSuccess, QJSValue onError) const { void Requests::get(const QUrl& url, QJSValue onSuccess, QJSValue onError) const {
if (!onSuccess.isCallable()) { if (!onSuccess.isCallable()) {
@@ -20,13 +18,15 @@ void Requests::get(const QUrl& url, QJSValue onSuccess, QJSValue onError) const
QNetworkRequest request(url); QNetworkRequest request(url);
auto reply = m_manager->get(request); auto reply = m_manager->get(request);
QObject::connect(reply, &QNetworkReply::finished, [reply, onSuccess, onError]() { QObject::connect(
reply, &QNetworkReply::finished, [reply, onSuccess, onError]() {
if (reply->error() == QNetworkReply::NoError) { if (reply->error() == QNetworkReply::NoError) {
onSuccess.call({QString(reply->readAll())}); onSuccess.call({QString(reply->readAll())});
} else if (onError.isCallable()) { } else if (onError.isCallable()) {
onError.call({reply->errorString()}); onError.call({reply->errorString()});
} else { } else {
qWarning() << "Requests::get: request failed with error" << reply->errorString(); qWarning() << "Requests::get: request failed with error"
<< reply->errorString();
} }
reply->deleteLater(); reply->deleteLater();
+4 -1
View File
@@ -14,7 +14,10 @@ QML_SINGLETON
public: public:
explicit Requests(QObject* parent = nullptr); explicit Requests(QObject* parent = nullptr);
Q_INVOKABLE void get(const QUrl& url, QJSValue callback, QJSValue onError = QJSValue()) const; Q_INVOKABLE void get(
const QUrl& url,
QJSValue callback,
QJSValue onError = QJSValue()) const;
private: private:
QNetworkAccessManager* m_manager; QNetworkAccessManager* m_manager;
+14 -5
View File
@@ -6,7 +6,13 @@
namespace ZShell { namespace ZShell {
Toast::Toast(const QString& title, const QString& message, const QString& icon, Type type, int timeout, QObject* parent) Toast::Toast(
const QString& title,
const QString& message,
const QString& icon,
Type type,
int timeout,
QObject* parent)
: QObject(parent) : QObject(parent)
, m_closed(false) , m_closed(false)
, m_title(title) , m_title(title)
@@ -94,15 +100,18 @@ void Toast::unlock(QObject* sender) {
} }
} }
Toaster::Toaster(QObject* parent) Toaster::Toaster(QObject* parent) : QObject(parent) {}
: QObject(parent) {
}
QQmlListProperty<Toast> Toaster::toasts() { QQmlListProperty<Toast> Toaster::toasts() {
return QQmlListProperty<Toast>(this, &m_toasts); return QQmlListProperty<Toast>(this, &m_toasts);
} }
void Toaster::toast(const QString& title, const QString& message, const QString& icon, Toast::Type type, int timeout) { void Toaster::toast(
const QString& title,
const QString& message,
const QString& icon,
Toast::Type type,
int timeout) {
auto* toast = new Toast(title, message, icon, type, timeout, this); auto* toast = new Toast(title, message, icon, type, timeout, this);
QObject::connect(toast, &Toast::finishedClose, this, [toast, this]() { QObject::connect(toast, &Toast::finishedClose, this, [toast, this]() {
if (m_toasts.removeOne(toast)) { if (m_toasts.removeOne(toast)) {
+15 -10
View File
@@ -20,15 +20,15 @@ Q_PROPERTY(int timeout READ timeout CONSTANT)
Q_PROPERTY(Type type READ type CONSTANT) Q_PROPERTY(Type type READ type CONSTANT)
public: public:
enum class Type { enum class Type { Info = 0, Success, Warning, Error };
Info = 0,
Success,
Warning,
Error
};
Q_ENUM(Type) Q_ENUM(Type)
explicit Toast(const QString& title, const QString& message, const QString& icon, Type type, int timeout, explicit Toast(
const QString& title,
const QString& message,
const QString& icon,
Type type,
int timeout,
QObject* parent = nullptr); QObject* parent = nullptr);
[[nodiscard]] bool closed() const; [[nodiscard]] bool closed() const;
@@ -62,15 +62,20 @@ Q_OBJECT
QML_ELEMENT QML_ELEMENT
QML_SINGLETON QML_SINGLETON
Q_PROPERTY(QQmlListProperty<ZShell::Toast> toasts READ toasts NOTIFY toastsChanged) Q_PROPERTY(
QQmlListProperty<ZShell::Toast> toasts READ toasts NOTIFY toastsChanged)
public: public:
explicit Toaster(QObject* parent = nullptr); explicit Toaster(QObject* parent = nullptr);
[[nodiscard]] QQmlListProperty<Toast> toasts(); [[nodiscard]] QQmlListProperty<Toast> toasts();
Q_INVOKABLE void toast(const QString& title, const QString& message, const QString& icon = QString(), Q_INVOKABLE void toast(
ZShell::Toast::Type type = Toast::Type::Info, int timeout = 5000); const QString& title,
const QString& message,
const QString& icon = QString(),
ZShell::Toast::Type type = Toast::Type::Info,
int timeout = 5000);
signals: signals:
void toastsChanged(); void toastsChanged();
+53 -65
View File
@@ -20,15 +20,12 @@
namespace ZShell { namespace ZShell {
// ============================================================
// saveItem
// ============================================================
void ZShellIo::saveItem(QQuickItem* target, const QUrl& path) { void ZShellIo::saveItem(QQuickItem* target, const QUrl& path) {
this->saveItem(target, path, QRect(), QJSValue(), QJSValue()); this->saveItem(target, path, QRect(), QJSValue(), QJSValue());
} }
void ZShellIo::saveItem(QQuickItem* target, const QUrl& path, const QRect& rect) { void ZShellIo::saveItem(
QQuickItem* target, const QUrl& path, const QRect& rect) {
this->saveItem(target, path, rect, QJSValue(), QJSValue()); this->saveItem(target, path, rect, QJSValue(), QJSValue());
} }
@@ -36,11 +33,13 @@ void ZShellIo::saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved)
this->saveItem(target, path, QRect(), onSaved, QJSValue()); this->saveItem(target, path, QRect(), onSaved, QJSValue());
} }
void ZShellIo::saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved, QJSValue onFailed) { void ZShellIo::saveItem(
QQuickItem* target, const QUrl& path, QJSValue onSaved, QJSValue onFailed) {
this->saveItem(target, path, QRect(), onSaved, onFailed); this->saveItem(target, path, QRect(), onSaved, onFailed);
} }
void ZShellIo::saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved) { void ZShellIo::saveItem(
QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved) {
this->saveItem(target, path, rect, onSaved, QJSValue()); this->saveItem(target, path, rect, onSaved, QJSValue());
} }
@@ -49,8 +48,7 @@ void ZShellIo::saveItem(
const QUrl& path, const QUrl& path,
const QRect& rect, const QRect& rect,
QJSValue onSaved, QJSValue onSaved,
QJSValue onFailed QJSValue onFailed) {
) {
if (!target) { if (!target) {
qWarning() << "ZShellIo::saveItem: a target is required"; qWarning() << "ZShellIo::saveItem: a target is required";
return; return;
@@ -62,8 +60,7 @@ void ZShellIo::saveItem(
} }
if (!target->window()) { if (!target->window()) {
qWarning() << "ZShellIo::saveItem: unable to save target" qWarning() << "ZShellIo::saveItem: unable to save target" << target
<< target
<< "without a window"; << "without a window";
return; return;
} }
@@ -77,8 +74,8 @@ void ZShellIo::saveItem(
rect.left() * scale, rect.left() * scale,
rect.top() * scale, rect.top() * scale,
rect.width() * scale, rect.width() * scale,
rect.height() * scale rect.height() * scale)
).toRect(); .toRect();
} }
const QSharedPointer<const QQuickItemGrabResult> grabResult = const QSharedPointer<const QQuickItemGrabResult> grabResult =
@@ -89,7 +86,8 @@ void ZShellIo::saveItem(
&QQuickItemGrabResult::ready, &QQuickItemGrabResult::ready,
this, this,
[grabResult, scaledRect, path, onSaved, onFailed, this]() { [grabResult, scaledRect, path, onSaved, onFailed, this]() {
const auto future = QtConcurrent::run([grabResult, scaledRect, path]() { const auto future =
QtConcurrent::run([grabResult, scaledRect, path]() {
QImage image = grabResult->image(); QImage image = grabResult->image();
if (scaledRect.isValid()) { if (scaledRect.isValid()) {
@@ -116,39 +114,34 @@ void ZShellIo::saveItem(
auto* watcher = new QFutureWatcher<bool>(this); auto* watcher = new QFutureWatcher<bool>(this);
auto* engine = qmlEngine(this); auto* engine = qmlEngine(this);
QObject::connect(watcher, &QFutureWatcher<bool>::finished, this, [=]() { QObject::connect(
watcher, &QFutureWatcher<bool>::finished, this, [=]() {
if (watcher->result()) { if (watcher->result()) {
if (onSaved.isCallable() && engine) { if (onSaved.isCallable() && engine) {
onSaved.call({ onSaved.call(
engine->toScriptValue(path.toLocalFile()), {engine->toScriptValue(path.toLocalFile()),
engine->toScriptValue(path) engine->toScriptValue(path)});
});
} }
} else { } else {
qWarning() << "ZShellIo::saveItem: failed to save" << path; qWarning()
<< "ZShellIo::saveItem: failed to save" << path;
if (onFailed.isCallable() && engine) { if (onFailed.isCallable() && engine) {
onFailed.call({ onFailed.call({engine->toScriptValue(path)});
engine->toScriptValue(path)
});
} }
} }
watcher->deleteLater(); watcher->deleteLater();
}); });
watcher->setFuture(future); watcher->setFuture(future);
});
} }
);
}
// ============================================================
// cacheImage
// ============================================================
void ZShellIo::cacheImage(const QUrl& source, const QString& cacheDir) { void ZShellIo::cacheImage(const QUrl& source, const QString& cacheDir) {
this->cacheImage(source, cacheDir, QJSValue(), QJSValue()); this->cacheImage(source, cacheDir, QJSValue(), QJSValue());
} }
void ZShellIo::cacheImage(const QUrl& source, const QString& cacheDir, QJSValue onSaved) { void ZShellIo::cacheImage(
const QUrl& source, const QString& cacheDir, QJSValue onSaved) {
this->cacheImage(source, cacheDir, onSaved, QJSValue()); this->cacheImage(source, cacheDir, onSaved, QJSValue());
} }
@@ -156,8 +149,7 @@ void ZShellIo::cacheImage(
const QUrl& source, const QUrl& source,
const QString& cacheDir, const QString& cacheDir,
QJSValue onSaved, QJSValue onSaved,
QJSValue onFailed QJSValue onFailed) {
) {
if (cacheDir.isEmpty()) { if (cacheDir.isEmpty()) {
qWarning() << "ZShellIo::cacheImage: cacheDir is empty"; qWarning() << "ZShellIo::cacheImage: cacheDir is empty";
return; return;
@@ -165,13 +157,13 @@ void ZShellIo::cacheImage(
QImage image; QImage image;
if (!loadSourceImage(source, image)) { if (!loadSourceImage(source, image)) {
qWarning() << "ZShellIo::cacheImage: failed to load source image" << source; qWarning() << "ZShellIo::cacheImage: failed to load source image"
<< source;
auto* engine = qmlEngine(this); auto* engine = qmlEngine(this);
if (onFailed.isCallable() && engine) { if (onFailed.isCallable() && engine) {
onFailed.call({ onFailed.call(
engine->toScriptValue(source), {engine->toScriptValue(source),
engine->toScriptValue(cacheDir) engine->toScriptValue(cacheDir)});
});
} }
return; return;
} }
@@ -181,12 +173,12 @@ void ZShellIo::cacheImage(
return QString(); return QString();
} }
const QImage normalized = image.convertToFormat(QImage::Format_RGBA8888); const QImage normalized =
image.convertToFormat(QImage::Format_RGBA8888);
const QByteArray bytes( const QByteArray bytes(
reinterpret_cast<const char*>(normalized.constBits()), reinterpret_cast<const char*>(normalized.constBits()),
qsizetype(normalized.sizeInBytes()) normalized.sizeInBytes());
);
const QByteArray digest = const QByteArray digest =
QCryptographicHash::hash(bytes, QCryptographicHash::Sha256).toHex(); QCryptographicHash::hash(bytes, QCryptographicHash::Sha256).toHex();
@@ -196,7 +188,8 @@ void ZShellIo::cacheImage(
return QString(); return QString();
} }
const QString finalPath = dir.filePath(QString::fromLatin1(digest) + ".png"); const QString finalPath =
dir.filePath(QString::fromLatin1(digest) + ".png");
if (QFile::exists(finalPath)) { if (QFile::exists(finalPath)) {
return finalPath; return finalPath;
@@ -226,18 +219,16 @@ void ZShellIo::cacheImage(
if (!finalPath.isEmpty()) { if (!finalPath.isEmpty()) {
if (onSaved.isCallable() && engine) { if (onSaved.isCallable() && engine) {
onSaved.call({ onSaved.call(
engine->toScriptValue(finalPath), {engine->toScriptValue(finalPath),
engine->toScriptValue(QUrl::fromLocalFile(finalPath)) engine->toScriptValue(QUrl::fromLocalFile(finalPath))});
});
} }
} else { } else {
qWarning() << "ZShellIo::cacheImage: failed to cache" << source; qWarning() << "ZShellIo::cacheImage: failed to cache" << source;
if (onFailed.isCallable() && engine) { if (onFailed.isCallable() && engine) {
onFailed.call({ onFailed.call(
engine->toScriptValue(source), {engine->toScriptValue(source),
engine->toScriptValue(cacheDir) engine->toScriptValue(cacheDir)});
});
} }
} }
@@ -247,10 +238,6 @@ void ZShellIo::cacheImage(
watcher->setFuture(future); watcher->setFuture(future);
} }
// ============================================================
// loadSourceImage
// ============================================================
bool ZShellIo::loadSourceImage(const QUrl& source, QImage& image) const { bool ZShellIo::loadSourceImage(const QUrl& source, QImage& image) const {
image = QImage(); image = QImage();
@@ -270,8 +257,7 @@ bool ZShellIo::loadSourceImage(const QUrl& source, QImage& image) const {
const QString providerId = source.host(); const QString providerId = source.host();
const QString imageId = const QString imageId = source.path().startsWith('/')
source.path().startsWith('/')
? source.path().mid(1) ? source.path().mid(1)
: source.path(); : source.path();
@@ -284,7 +270,8 @@ bool ZShellIo::loadSourceImage(const QUrl& source, QImage& image) const {
auto* provider = dynamic_cast<QQuickImageProvider*>(providerBase); auto* provider = dynamic_cast<QQuickImageProvider*>(providerBase);
if (!provider) { if (!provider) {
qWarning() << "ZShellIo::loadSourceImage: provider is not a QQuickImageProvider" qWarning() << "ZShellIo::loadSourceImage: provider is not a "
"QQuickImageProvider"
<< providerId; << providerId;
return false; return false;
} }
@@ -313,17 +300,16 @@ bool ZShellIo::loadSourceImage(const QUrl& source, QImage& image) const {
return false; return false;
} }
// ============================================================ bool ZShellIo::copyFile(
// File ops const QUrl& source, const QUrl& target, bool overwrite) const {
// ============================================================
bool ZShellIo::copyFile(const QUrl& source, const QUrl& target, bool overwrite) const {
if (!source.isLocalFile()) { if (!source.isLocalFile()) {
qWarning() << "ZShellIo::copyFile: source" << source << "is not a local file"; qWarning() << "ZShellIo::copyFile: source" << source
<< "is not a local file";
return false; return false;
} }
if (!target.isLocalFile()) { if (!target.isLocalFile()) {
qWarning() << "ZShellIo::copyFile: target" << target << "is not a local file"; qWarning() << "ZShellIo::copyFile: target" << target
<< "is not a local file";
return false; return false;
} }
@@ -336,7 +322,8 @@ bool ZShellIo::copyFile(const QUrl& source, const QUrl& target, bool overwrite)
bool ZShellIo::deleteFile(const QUrl& path) const { bool ZShellIo::deleteFile(const QUrl& path) const {
if (!path.isLocalFile()) { if (!path.isLocalFile()) {
qWarning() << "ZShellIo::deleteFile: path" << path << "is not a local file"; qWarning() << "ZShellIo::deleteFile: path" << path
<< "is not a local file";
return false; return false;
} }
@@ -345,7 +332,8 @@ bool ZShellIo::deleteFile(const QUrl& path) const {
QString ZShellIo::toLocalFile(const QUrl& url) const { QString ZShellIo::toLocalFile(const QUrl& url) const {
if (!url.isLocalFile()) { if (!url.isLocalFile()) {
qWarning() << "ZShellIo::toLocalFile: given url is not a local file" << url; qWarning() << "ZShellIo::toLocalFile: given url is not a local file"
<< url;
return QString(); return QString();
} }
+2 -2
View File
@@ -10,7 +10,6 @@
namespace ZShell { namespace ZShell {
class ZShellIo : public QObject { class ZShellIo : public QObject {
Q_OBJECT Q_OBJECT
QML_ELEMENT QML_ELEMENT
QML_SINGLETON QML_SINGLETON
@@ -29,7 +28,8 @@ Q_INVOKABLE void cacheImage(const QUrl& source, const QString& cacheDir, QJSValu
Q_INVOKABLE void cacheImage(const QUrl& source, const QString& cacheDir, QJSValue onSaved, QJSValue onFailed); Q_INVOKABLE void cacheImage(const QUrl& source, const QString& cacheDir, QJSValue onSaved, QJSValue onFailed);
// clang-format on // clang-format on
Q_INVOKABLE [[nodiscard]] bool copyFile(const QUrl& source, const QUrl& target, bool overwrite = true) const; Q_INVOKABLE [[nodiscard]] bool copyFile(
const QUrl& source, const QUrl& target, bool overwrite = true) const;
Q_INVOKABLE [[nodiscard]] bool deleteFile(const QUrl& path) const; Q_INVOKABLE [[nodiscard]] bool deleteFile(const QUrl& path) const;
Q_INVOKABLE [[nodiscard]] QString toLocalFile(const QUrl& url) const; Q_INVOKABLE [[nodiscard]] QString toLocalFile(const QUrl& url) const;
+43 -17
View File
@@ -27,15 +27,22 @@ void ZUtils::saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved) {
this->saveItem(target, path, QRect(), onSaved, QJSValue()); this->saveItem(target, path, QRect(), onSaved, QJSValue());
} }
void ZUtils::saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved, QJSValue onFailed) { void ZUtils::saveItem(
QQuickItem* target, const QUrl& path, QJSValue onSaved, QJSValue onFailed) {
this->saveItem(target, path, QRect(), onSaved, onFailed); this->saveItem(target, path, QRect(), onSaved, onFailed);
} }
void ZUtils::saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved) { void ZUtils::saveItem(
QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved) {
this->saveItem(target, path, rect, onSaved, QJSValue()); this->saveItem(target, path, rect, onSaved, QJSValue());
} }
void ZUtils::saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved, QJSValue onFailed) { void ZUtils::saveItem(
QQuickItem* target,
const QUrl& path,
const QRect& rect,
QJSValue onSaved,
QJSValue onFailed) {
if (!target) { if (!target) {
qCWarning(lcZUtils) << "saveItem: a target is required"; qCWarning(lcZUtils) << "saveItem: a target is required";
return; return;
@@ -47,20 +54,29 @@ void ZUtils::saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, Q
} }
if (!target->window()) { if (!target->window()) {
qCWarning(lcZUtils) << "saveItem: unable to save target" << target << "without a window"; qCWarning(lcZUtils) << "saveItem: unable to save target" << target
<< "without a window";
return; return;
} }
auto scaledRect = rect; auto scaledRect = rect;
const qreal scale = target->window()->devicePixelRatio(); const qreal scale = target->window()->devicePixelRatio();
if (rect.isValid() && !qFuzzyCompare(scale + 1.0, 2.0)) { if (rect.isValid() && !qFuzzyCompare(scale + 1.0, 2.0)) {
scaledRect = scaledRect = QRectF(
QRectF(rect.left() * scale, rect.top() * scale, rect.width() * scale, rect.height() * scale).toRect(); rect.left() * scale,
rect.top() * scale,
rect.width() * scale,
rect.height() * scale)
.toRect();
} }
const QSharedPointer<const QQuickItemGrabResult> grabResult = target->grabToImage(); const QSharedPointer<const QQuickItemGrabResult> grabResult =
target->grabToImage();
QObject::connect(grabResult.data(), &QQuickItemGrabResult::ready, this, QObject::connect(
grabResult.data(),
&QQuickItemGrabResult::ready,
this,
[grabResult, scaledRect, path, onSaved, onFailed, this]() { [grabResult, scaledRect, path, onSaved, onFailed, this]() {
const auto future = QtConcurrent::run([=]() { const auto future = QtConcurrent::run([=]() {
QImage image = grabResult->image(); QImage image = grabResult->image();
@@ -77,20 +93,24 @@ void ZUtils::saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, Q
auto* watcher = new QFutureWatcher<bool>(this); auto* watcher = new QFutureWatcher<bool>(this);
auto* engine = qmlEngine(this); auto* engine = qmlEngine(this);
QObject::connect(watcher, &QFutureWatcher<bool>::finished, this, [=]() { QObject::connect(
watcher, &QFutureWatcher<bool>::finished, this, [=]() {
if (watcher->result()) { if (watcher->result()) {
if (onSaved.isCallable()) { if (onSaved.isCallable()) {
QJSValueList args = {QJSValue(path.toLocalFile())}; QJSValueList args = {QJSValue(path.toLocalFile())};
if (engine) { if (engine) {
args << engine->toScriptValue(QVariant::fromValue(path)); args << engine->toScriptValue(
QVariant::fromValue(path));
} }
onSaved.call(args); onSaved.call(args);
} }
} else { } else {
qCWarning(lcZUtils) << "saveItem: failed to save" << path; qCWarning(lcZUtils)
<< "saveItem: failed to save" << path;
if (onFailed.isCallable()) { if (onFailed.isCallable()) {
if (engine) { if (engine) {
onFailed.call({ engine->toScriptValue(QVariant::fromValue(path)) }); onFailed.call({engine->toScriptValue(
QVariant::fromValue(path))});
} else { } else {
onFailed.call(); onFailed.call();
} }
@@ -104,17 +124,21 @@ void ZUtils::saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, Q
bool ZUtils::copyFile(const QUrl& source, const QUrl& target, bool overwrite) { bool ZUtils::copyFile(const QUrl& source, const QUrl& target, bool overwrite) {
if (!source.isLocalFile()) { if (!source.isLocalFile()) {
qCWarning(lcZUtils) << "copyFile: source" << source << "is not a local file"; qCWarning(lcZUtils)
<< "copyFile: source" << source << "is not a local file";
return false; return false;
} }
if (!target.isLocalFile()) { if (!target.isLocalFile()) {
qCWarning(lcZUtils) << "copyFile: target" << target << "is not a local file"; qCWarning(lcZUtils)
<< "copyFile: target" << target << "is not a local file";
return false; return false;
} }
if (overwrite && QFile::exists(target.toLocalFile())) { if (overwrite && QFile::exists(target.toLocalFile())) {
if (!QFile::remove(target.toLocalFile())) { if (!QFile::remove(target.toLocalFile())) {
qCWarning(lcZUtils) << "copyFile: overwrite was specified but failed to remove" << target.toLocalFile(); qCWarning(lcZUtils)
<< "copyFile: overwrite was specified but failed to remove"
<< target.toLocalFile();
return false; return false;
} }
} }
@@ -124,7 +148,8 @@ bool ZUtils::copyFile(const QUrl& source, const QUrl& target, bool overwrite) {
bool ZUtils::deleteFile(const QUrl& path) { bool ZUtils::deleteFile(const QUrl& path) {
if (!path.isLocalFile()) { if (!path.isLocalFile()) {
qCWarning(lcZUtils) << "deleteFile: path" << path << "is not a local file"; qCWarning(lcZUtils)
<< "deleteFile: path" << path << "is not a local file";
return false; return false;
} }
@@ -133,7 +158,8 @@ bool ZUtils::deleteFile(const QUrl& path) {
QString ZUtils::toLocalFile(const QUrl& url) { QString ZUtils::toLocalFile(const QUrl& url) {
if (!url.isLocalFile()) { if (!url.isLocalFile()) {
qCWarning(lcZUtils) << "toLocalFile: given url is not a local file" << url; qCWarning(lcZUtils)
<< "toLocalFile: given url is not a local file" << url;
return QString(); return QString();
} }
+2 -1
View File
@@ -22,7 +22,8 @@ Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rec
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved, QJSValue onFailed); Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved, QJSValue onFailed);
// clang-format on // clang-format on
Q_INVOKABLE static bool copyFile(const QUrl& source, const QUrl& target, bool overwrite = true); Q_INVOKABLE static bool copyFile(
const QUrl& source, const QUrl& target, bool overwrite = true);
Q_INVOKABLE static bool deleteFile(const QUrl& path); Q_INVOKABLE static bool deleteFile(const QUrl& path);
Q_INVOKABLE static QString toLocalFile(const QUrl& url); Q_INVOKABLE static QString toLocalFile(const QUrl& url);