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
AccessModifierOffset: 0
AlignAfterOpenBracket: Align
AlignAfterOpenBracket: AlwaysBreak
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignOperands: Align
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortFunctionsOnASingleLine: Inline
AllowShortIfStatementsOnASingleLine: Never
AllowShortIfStatementsOnASingleLine: WithoutElse
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes
AlwaysBreakTemplateDeclarations: MultiLine
BinPackArguments: false
BinPackParameters: false
BreakBeforeBraces: Attach
+17 -21
View File
@@ -2,9 +2,7 @@
#include "blobinvertedrect.hpp"
#include "blobshape.hpp"
BlobGroup::BlobGroup(QObject* parent)
: QObject(parent) {
}
BlobGroup::BlobGroup(QObject* parent) : QObject(parent) {}
BlobGroup::~BlobGroup() {
for (auto* shape : std::as_const(m_shapes))
@@ -14,24 +12,21 @@ BlobGroup::~BlobGroup() {
}
void BlobGroup::setSmoothing(qreal s) {
if (qFuzzyCompare(m_smoothing, s))
return;
if (qFuzzyCompare(m_smoothing, s)) return;
m_smoothing = s;
emit smoothingChanged();
markDirty();
}
void BlobGroup::setColor(const QColor& c) {
if (m_color == c)
return;
if (m_color == c) return;
m_color = c;
emit colorChanged();
markDirty();
}
void BlobGroup::addShape(BlobShape* shape) {
if (!shape || m_shapes.contains(shape))
return;
if (!shape || m_shapes.contains(shape)) return;
m_shapes.append(shape);
markDirty();
}
@@ -42,15 +37,13 @@ void BlobGroup::removeShape(BlobShape* shape) {
}
void BlobGroup::setInvertedRect(BlobInvertedRect* rect) {
if (m_invertedRect == rect)
return;
if (m_invertedRect == rect) return;
m_invertedRect = rect;
markDirty();
}
void BlobGroup::clearInvertedRect(BlobInvertedRect* rect) {
if (m_invertedRect != rect)
return;
if (m_invertedRect != rect) return;
m_invertedRect = nullptr;
markDirty();
}
@@ -75,15 +68,19 @@ void BlobGroup::markShapeDirty(BlobShape* source) {
// Use cached padded rects to find spatial neighbors
const float pad = static_cast<float>(m_smoothing) * 2.0f;
const QRectF srcRect(static_cast<double>(source->m_cachedPaddedX - pad),
static_cast<double>(source->m_cachedPaddedY - pad), static_cast<double>(source->m_cachedPaddedW + pad * 2.0f),
const QRectF srcRect(
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));
for (auto* shape : std::as_const(m_shapes)) {
if (shape == source)
continue;
const QRectF otherRect(static_cast<double>(shape->m_cachedPaddedX), static_cast<double>(shape->m_cachedPaddedY),
static_cast<double>(shape->m_cachedPaddedW), static_cast<double>(shape->m_cachedPaddedH));
if (shape == source) continue;
const QRectF otherRect(
static_cast<double>(shape->m_cachedPaddedX),
static_cast<double>(shape->m_cachedPaddedY),
static_cast<double>(shape->m_cachedPaddedW),
static_cast<double>(shape->m_cachedPaddedH));
if (srcRect.intersects(otherRect)) {
shape->polish();
shape->update();
@@ -97,8 +94,7 @@ void BlobGroup::markShapeDirty(BlobShape* source) {
}
void BlobGroup::ensurePhysicsUpdated() {
if (m_physicsUpdated)
return;
if (m_physicsUpdated) return;
m_physicsUpdated = true;
for (auto* shape : std::as_const(m_shapes))
shape->updatePhysics();
+32 -36
View File
@@ -9,53 +9,49 @@ class BlobShape;
class BlobInvertedRect;
class BlobGroup : public QObject {
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(qreal smoothing READ smoothing WRITE setSmoothing NOTIFY smoothingChanged)
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(
qreal smoothing READ smoothing WRITE setSmoothing NOTIFY
smoothingChanged)
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
public:
explicit BlobGroup(QObject* parent = nullptr);
~BlobGroup() override;
public:
explicit BlobGroup(QObject* parent = nullptr);
~BlobGroup() override;
[[nodiscard]] qreal smoothing() const {
return m_smoothing;
}
[[nodiscard]] qreal smoothing() const { return m_smoothing; }
void setSmoothing(qreal s);
void setSmoothing(qreal s);
[[nodiscard]] QColor color() const {
return m_color;
}
[[nodiscard]] QColor color() const { return m_color; }
void setColor(const QColor& c);
void setColor(const QColor& c);
void addShape(BlobShape* shape);
void removeShape(BlobShape* shape);
void addShape(BlobShape* shape);
void removeShape(BlobShape* shape);
void setInvertedRect(BlobInvertedRect* rect);
void clearInvertedRect(BlobInvertedRect* rect);
void setInvertedRect(BlobInvertedRect* rect);
void clearInvertedRect(BlobInvertedRect* rect);
[[nodiscard]] const QList<BlobShape*>& shapes() const {
return m_shapes;
}
[[nodiscard]] const QList<BlobShape*>& shapes() const { return m_shapes; }
[[nodiscard]] BlobInvertedRect* invertedRect() const {
[[nodiscard]] BlobInvertedRect* invertedRect() const {
return m_invertedRect;
}
}
void markDirty();
void markShapeDirty(BlobShape* source);
void ensurePhysicsUpdated();
void markDirty();
void markShapeDirty(BlobShape* source);
void ensurePhysicsUpdated();
signals:
void smoothingChanged();
void colorChanged();
signals:
void smoothingChanged();
void colorChanged();
private:
qreal m_smoothing = 32.0;
QColor m_color{ 0x44, 0x88, 0xff };
QList<BlobShape*> m_shapes;
BlobInvertedRect* m_invertedRect = nullptr;
bool m_physicsUpdated = false;
private:
qreal m_smoothing = 32.0;
QColor m_color{0x44, 0x88, 0xff};
QList<BlobShape*> m_shapes;
BlobInvertedRect* m_invertedRect = nullptr;
bool m_physicsUpdated = false;
};
+29 -32
View File
@@ -5,12 +5,9 @@
#include <qsggeometry.h>
#include <qsgnode.h>
#include <algorithm>
#include <cstring>
BlobInvertedRect::BlobInvertedRect(QQuickItem* parent)
: BlobShape(parent) {
}
BlobInvertedRect::BlobInvertedRect(QQuickItem* parent) : BlobShape(parent) {}
static void setFrameIndices(quint16* idx) {
// Top strip: 0-1-4, 1-5-4
@@ -43,7 +40,8 @@ static void setFrameIndices(quint16* idx) {
idx[23] = 7;
}
QSGNode* BlobInvertedRect::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) {
QSGNode* BlobInvertedRect::updatePaintNode(
QSGNode* oldNode, UpdatePaintNodeData*) {
if (!m_group) {
delete oldNode;
return nullptr;
@@ -71,8 +69,11 @@ QSGNode* BlobInvertedRect::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData
delete oldNode;
node = new QSGGeometryNode;
auto* geometry =
new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 8, 24, QSGGeometry::UnsignedShortType);
auto* geometry = new QSGGeometry(
QSGGeometry::defaultAttributes_TexturedPoint2D(),
8,
24,
QSGGeometry::UnsignedShortType);
geometry->setDrawingMode(QSGGeometry::DrawTriangles);
node->setGeometry(geometry);
node->setFlag(QSGNode::OwnsGeometry);
@@ -120,10 +121,17 @@ QSGNode* BlobInvertedRect::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData
material->m_color = m_group->color();
material->m_hasInverted = m_cachedHasInverted ? 1 : 0;
material->m_invertedRadius = m_cachedInvertedRadius;
memcpy(material->m_invertedOuter, m_cachedInvertedOuter, sizeof(m_cachedInvertedOuter));
memcpy(material->m_invertedInner, m_cachedInvertedInner, sizeof(m_cachedInvertedInner));
memcpy(
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;
for (int i = 0; i < count; ++i)
material->m_rects[i] = m_cachedRects[i];
@@ -134,52 +142,41 @@ QSGNode* BlobInvertedRect::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData
}
BlobInvertedRect::~BlobInvertedRect() {
if (m_group)
m_group->clearInvertedRect(this);
if (m_group) m_group->clearInvertedRect(this);
}
void BlobInvertedRect::setBorderLeft(qreal v) {
if (qFuzzyCompare(m_borderLeft, v))
return;
if (qFuzzyCompare(m_borderLeft, v)) return;
m_borderLeft = v;
emit borderLeftChanged();
if (m_group)
m_group->markDirty();
if (m_group) m_group->markDirty();
}
void BlobInvertedRect::setBorderRight(qreal v) {
if (qFuzzyCompare(m_borderRight, v))
return;
if (qFuzzyCompare(m_borderRight, v)) return;
m_borderRight = v;
emit borderRightChanged();
if (m_group)
m_group->markDirty();
if (m_group) m_group->markDirty();
}
void BlobInvertedRect::setBorderTop(qreal v) {
if (qFuzzyCompare(m_borderTop, v))
return;
if (qFuzzyCompare(m_borderTop, v)) return;
m_borderTop = v;
emit borderTopChanged();
if (m_group)
m_group->markDirty();
if (m_group) m_group->markDirty();
}
void BlobInvertedRect::setBorderBottom(qreal v) {
if (qFuzzyCompare(m_borderBottom, v))
return;
if (qFuzzyCompare(m_borderBottom, v)) return;
m_borderBottom = v;
emit borderBottomChanged();
if (m_group)
m_group->markDirty();
if (m_group) m_group->markDirty();
}
void BlobInvertedRect::registerWithGroup() {
if (m_group)
m_group->setInvertedRect(this);
if (m_group) m_group->setInvertedRect(this);
}
void BlobInvertedRect::unregisterFromGroup() {
if (m_group)
m_group->clearInvertedRect(this);
if (m_group) m_group->clearInvertedRect(this);
}
+40 -42
View File
@@ -5,60 +5,58 @@
#include <qqmlengine.h>
class BlobInvertedRect : public BlobShape {
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(qreal borderLeft READ borderLeft WRITE setBorderLeft NOTIFY borderLeftChanged)
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)
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(
qreal borderLeft READ borderLeft WRITE setBorderLeft NOTIFY
borderLeftChanged)
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:
explicit BlobInvertedRect(QQuickItem* parent = nullptr);
~BlobInvertedRect() override;
public:
explicit BlobInvertedRect(QQuickItem* parent = nullptr);
~BlobInvertedRect() override;
[[nodiscard]] qreal borderLeft() const {
return m_borderLeft;
}
[[nodiscard]] qreal borderLeft() const { return m_borderLeft; }
void setBorderLeft(qreal v);
void setBorderLeft(qreal v);
[[nodiscard]] qreal borderRight() const {
return m_borderRight;
}
[[nodiscard]] qreal borderRight() const { return m_borderRight; }
void setBorderRight(qreal v);
void setBorderRight(qreal v);
[[nodiscard]] qreal borderTop() const {
return m_borderTop;
}
[[nodiscard]] qreal borderTop() const { return m_borderTop; }
void setBorderTop(qreal v);
void setBorderTop(qreal v);
[[nodiscard]] qreal borderBottom() const {
return m_borderBottom;
}
[[nodiscard]] qreal borderBottom() const { return m_borderBottom; }
void setBorderBottom(qreal v);
void setBorderBottom(qreal v);
signals:
void borderLeftChanged();
void borderRightChanged();
void borderTopChanged();
void borderBottomChanged();
signals:
void borderLeftChanged();
void borderRightChanged();
void borderTopChanged();
void borderBottomChanged();
protected:
[[nodiscard]] bool isInvertedRect() const override {
return true;
}
protected:
[[nodiscard]] bool isInvertedRect() const override { return true; }
QSGNode* updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) override;
QSGNode* updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) override;
void registerWithGroup() override;
void unregisterFromGroup() override;
void registerWithGroup() override;
void unregisterFromGroup() override;
private:
qreal m_borderLeft = 0;
qreal m_borderRight = 0;
qreal m_borderTop = 0;
qreal m_borderBottom = 0;
private:
qreal m_borderLeft = 0;
qreal m_borderRight = 0;
qreal m_borderTop = 0;
qreal m_borderBottom = 0;
};
+18 -15
View File
@@ -1,8 +1,11 @@
#include "blobmaterial.hpp"
#include <math.h>
#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");
QSGMaterialType* BlobMaterial::type() const {
@@ -10,15 +13,14 @@ QSGMaterialType* BlobMaterial::type() const {
return &s_type;
}
QSGMaterialShader* BlobMaterial::createShader(QSGRendererInterface::RenderMode) const {
QSGMaterialShader* BlobMaterial::createShader(
QSGRendererInterface::RenderMode) const {
return new BlobMaterialShader;
}
int BlobMaterial::compare(const QSGMaterial* other) const {
if (this < other)
return -1;
if (this > other)
return 1;
if (this < other) return -1;
if (this > other) return 1;
return 0;
}
@@ -27,7 +29,8 @@ BlobMaterialShader::BlobMaterialShader() {
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);
auto* mat = static_cast<BlobMaterial*>(newMaterial);
QByteArray* buf = state.uniformData();
@@ -59,10 +62,10 @@ bool BlobMaterialShader::updateUniformData(RenderState& state, QSGMaterial* newM
// Color as vec4 (offset 96, 16 bytes)
const float color[4] = {
static_cast<float>(mat->m_color.redF()),
static_cast<float>(mat->m_color.greenF()),
static_cast<float>(mat->m_color.blueF()),
static_cast<float>(mat->m_color.alphaF()),
mat->m_color.redF(),
mat->m_color.greenF(),
mat->m_color.blueF(),
mat->m_color.alphaF(),
};
memcpy(buf->data() + 96, color, 16);
@@ -86,11 +89,11 @@ bool BlobMaterialShader::updateUniformData(RenderState& state, QSGMaterial* newM
const auto& r = mat->m_rects[i];
const int base = 160 + i * 80;
// Pack excludeMask into props.x via bit-cast (read in shader with floatBitsToInt)
float maskAsFloat;
float maskAsFloat = NAN;
memcpy(&maskAsFloat, &r.excludeMask, sizeof(float));
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 d3[4] = { r.screenHalfX, r.screenHalfY, 0.0f, 0.0f };
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 d3[4] = {r.screenHalfX, r.screenHalfY, 0.0f, 0.0f};
memcpy(buf->data() + base, d0, 16);
memcpy(buf->data() + base + 16, d1, 16);
memcpy(buf->data() + base + 32, r.invDeform, 16);
+26 -22
View File
@@ -9,39 +9,43 @@ struct BlobRectData {
float offsetX = 0, offsetY = 0;
float minEig = 1.0f;
// Inverse of 2x2 deformation matrix, column-major for GLSL
float invDeform[4] = { 1, 0, 0, 1 };
float invDeform[4] = {1, 0, 0, 1};
// Screen-space AABB half-extents of the deformed rect
float screenHalfX = 0, screenHalfY = 0;
// Effective per-corner radii (tr, br, bl, tl), pre-computed on CPU
float radius[4] = { 0, 0, 0, 0 };
float radius[4] = {0, 0, 0, 0};
// Bitmask of indices in this rect's m_cachedRects that mutually exclude (or are excluded by) this rect.
// Used by the shader to skip smin between excluded pairs.
int excludeMask = 0;
};
class BlobMaterial : public QSGMaterial {
public:
[[nodiscard]] QSGMaterialType* type() const override;
[[nodiscard]] QSGMaterialShader* createShader(QSGRendererInterface::RenderMode) const override;
int compare(const QSGMaterial* other) const override;
public:
[[nodiscard]] QSGMaterialType* type() const override;
[[nodiscard]] QSGMaterialShader* createShader(
QSGRendererInterface::RenderMode) const override;
int compare(const QSGMaterial* other) const override;
float m_paddedX = 0;
float m_paddedY = 0;
float m_paddedW = 0;
float m_paddedH = 0;
float m_smoothFactor = 32.0f;
int m_rectCount = 0;
int m_myIndex = -2;
QColor m_color{ 0x44, 0x88, 0xff };
int m_hasInverted = 0;
float m_invertedRadius = 0;
float m_invertedOuter[4] = {};
float m_invertedInner[4] = {};
BlobRectData m_rects[16] = {};
float m_paddedX = 0;
float m_paddedY = 0;
float m_paddedW = 0;
float m_paddedH = 0;
float m_smoothFactor = 32.0f;
int m_rectCount = 0;
int m_myIndex = -2;
QColor m_color{0x44, 0x88, 0xff};
int m_hasInverted = 0;
float m_invertedRadius = 0;
float m_invertedOuter[4] = {};
float m_invertedInner[4] = {};
BlobRectData m_rects[16] = {};
};
class BlobMaterialShader : public QSGMaterialShader {
public:
BlobMaterialShader();
bool updateUniformData(RenderState& state, QSGMaterial* newMaterial, QSGMaterial* oldMaterial) override;
public:
BlobMaterialShader();
bool updateUniformData(
RenderState& state,
QSGMaterial* newMaterial,
QSGMaterial* oldMaterial) override;
};
+65 -55
View File
@@ -4,19 +4,18 @@
#include <algorithm>
#include <cmath>
BlobRect::BlobRect(QQuickItem* parent)
: BlobShape(parent) {
}
BlobRect::BlobRect(QQuickItem* parent) : BlobShape(parent) {}
BlobRect::~BlobRect() {
if (m_group)
m_group->removeShape(this);
if (m_group) m_group->removeShape(this);
}
void BlobRect::updatePolish() {
if (m_physicsActive) {
float totalDelta = std::abs(m_dm00 - 1.0f) + std::abs(m_dm01) + std::abs(m_dm11 - 1.0f);
float totalVel = std::abs(m_dmVel00) + std::abs(m_dmVel01) + std::abs(m_dmVel11);
float totalDelta = std::abs(m_dm00 - 1.0f) + std::abs(m_dm01) +
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) {
m_dm00 = 1.0f;
@@ -32,8 +31,7 @@ void BlobRect::updatePolish() {
QMetaObject::invokeMethod(
this,
[this]() {
if (m_group)
m_group->markDirty();
if (m_group) m_group->markDirty();
},
Qt::QueuedConnection);
}
@@ -41,8 +39,7 @@ void BlobRect::updatePolish() {
QMetaObject::invokeMethod(
this,
[this]() {
if (m_physicsActive && m_group)
m_group->markDirty();
if (m_physicsActive && m_group) m_group->markDirty();
},
Qt::QueuedConnection);
}
@@ -64,20 +61,20 @@ void BlobRect::updatePhysics() {
const float dt = static_cast<float>(m_elapsed.restart()) / 1000.0f;
if (dt > 0.1f || dt < 0.001f) {
m_prevScenePos = scenePos;
if (m_physicsActive)
checkAtRest(0.0f);
if (m_physicsActive) checkAtRest(0.0f);
return;
}
const float velX = static_cast<float>(scenePos.x() - m_prevScenePos.x()) / dt;
const float velY = static_cast<float>(scenePos.y() - m_prevScenePos.y()) / dt;
const float velX =
static_cast<float>(scenePos.x() - m_prevScenePos.x()) / dt;
const float velY =
static_cast<float>(scenePos.y() - m_prevScenePos.y()) / dt;
m_prevScenePos = scenePos;
const float speed = std::sqrt(velX * velX + velY * velY);
if (!m_physicsActive) {
if (speed < 5.0f)
return;
if (speed < 5.0f) return;
m_physicsActive = true;
}
@@ -89,7 +86,8 @@ void BlobRect::updatePhysics() {
float target11 = 1.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 cosA = velX / speed;
@@ -106,19 +104,23 @@ void BlobRect::updatePhysics() {
const float kStiffness = static_cast<float>(m_stiffness);
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_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_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_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();
updateCenteredDeformMatrix();
@@ -129,8 +131,7 @@ void BlobRect::setTopLeftRadius(qreal r) {
if (!qFuzzyCompare(m_topLeftRadius, r)) {
m_topLeftRadius = r;
emit topLeftRadiusChanged();
if (m_group)
m_group->markDirty();
if (m_group) m_group->markDirty();
}
}
@@ -138,8 +139,7 @@ void BlobRect::setTopRightRadius(qreal r) {
if (!qFuzzyCompare(m_topRightRadius, r)) {
m_topRightRadius = r;
emit topRightRadiusChanged();
if (m_group)
m_group->markDirty();
if (m_group) m_group->markDirty();
}
}
@@ -147,8 +147,7 @@ void BlobRect::setBottomLeftRadius(qreal r) {
if (!qFuzzyCompare(m_bottomLeftRadius, r)) {
m_bottomLeftRadius = r;
emit bottomLeftRadiusChanged();
if (m_group)
m_group->markDirty();
if (m_group) m_group->markDirty();
}
}
@@ -156,38 +155,51 @@ void BlobRect::setBottomRightRadius(qreal r) {
if (!qFuzzyCompare(m_bottomRightRadius, r)) {
m_bottomRightRadius = r;
emit bottomRightRadiusChanged();
if (m_group)
m_group->markDirty();
if (m_group) m_group->markDirty();
}
}
void BlobRect::cornerRadii(float out[4]) const {
const auto maxR = static_cast<float>(std::min(width(), height())) * 0.5f;
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[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);
out[0] = std::min(
m_topRightRadius >= 0 ? static_cast<float>(m_topRightRadius) : 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 {
for (const auto& ptr : m_exclude) {
if (ptr == other)
return true;
if (ptr == other) return true;
}
return false;
}
QQmlListProperty<BlobRect> BlobRect::exclude() {
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) {
auto* self = static_cast<BlobRect*>(prop->object);
self->m_exclude.append(rect);
if (self->m_group)
self->m_group->markDirty();
if (self->m_group) self->m_group->markDirty();
emit self->excludeChanged();
}
@@ -196,44 +208,43 @@ qsizetype BlobRect::excludeCount(QQmlListProperty<BlobRect>* prop) {
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);
return self->m_exclude.at(index);
}
void BlobRect::excludeClear(QQmlListProperty<BlobRect>* prop) {
auto* self = static_cast<BlobRect*>(prop->object);
if (self->m_exclude.isEmpty())
return;
if (self->m_exclude.isEmpty()) return;
self->m_exclude.clear();
if (self->m_group)
self->m_group->markDirty();
if (self->m_group) self->m_group->markDirty();
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);
self->m_exclude[index] = rect;
if (self->m_group)
self->m_group->markDirty();
if (self->m_group) self->m_group->markDirty();
emit self->excludeChanged();
}
void BlobRect::excludeRemoveLast(QQmlListProperty<BlobRect>* prop) {
auto* self = static_cast<BlobRect*>(prop->object);
if (self->m_exclude.isEmpty())
return;
if (self->m_exclude.isEmpty()) return;
self->m_exclude.removeLast();
if (self->m_group)
self->m_group->markDirty();
if (self->m_group) self->m_group->markDirty();
emit self->excludeChanged();
}
void BlobRect::checkAtRest(float speed) {
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_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) {
m_dm00 = 1.0f;
@@ -251,8 +262,7 @@ void BlobRect::checkAtRest(float speed) {
QMetaObject::invokeMethod(
this,
[this]() {
if (m_group)
m_group->markDirty();
if (m_group) m_group->markDirty();
},
Qt::QueuedConnection);
}
+91 -89
View File
@@ -8,133 +8,135 @@
#include <qqmllist.h>
class BlobRect : public BlobShape {
Q_OBJECT
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(
qreal bottomRightRadius READ bottomRightRadius WRITE setBottomRightRadius NOTIFY bottomRightRadiusChanged)
Q_OBJECT
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(
qreal bottomRightRadius READ bottomRightRadius WRITE
setBottomRightRadius NOTIFY bottomRightRadiusChanged)
public:
explicit BlobRect(QQuickItem* parent = nullptr);
~BlobRect() override;
public:
explicit BlobRect(QQuickItem* parent = nullptr);
~BlobRect() override;
[[nodiscard]] qreal stiffness() const {
return m_stiffness;
}
[[nodiscard]] qreal stiffness() const { return m_stiffness; }
void setStiffness(qreal s) {
void setStiffness(qreal s) {
if (!qFuzzyCompare(m_stiffness, s)) {
m_stiffness = s;
emit stiffnessChanged();
}
}
}
[[nodiscard]] qreal damping() const {
return m_damping;
}
[[nodiscard]] qreal damping() const { return m_damping; }
void setDamping(qreal d) {
void setDamping(qreal d) {
if (!qFuzzyCompare(m_damping, d)) {
m_damping = d;
emit dampingChanged();
}
}
}
[[nodiscard]] qreal deformScale() const {
return m_deformScale;
}
[[nodiscard]] qreal deformScale() const { return m_deformScale; }
void setDeformScale(qreal s) {
void setDeformScale(qreal s) {
if (!qFuzzyCompare(m_deformScale, s)) {
m_deformScale = s;
emit deformScaleChanged();
}
}
}
QQmlListProperty<BlobRect> exclude();
QQmlListProperty<BlobRect> exclude();
bool isExcluded(const BlobShape* other) const override;
void cornerRadii(float out[4]) const override;
bool isExcluded(const BlobShape* other) const override;
void cornerRadii(float out[4]) const override;
[[nodiscard]] qreal topLeftRadius() const {
return m_topLeftRadius;
}
[[nodiscard]] qreal topLeftRadius() const { return m_topLeftRadius; }
void setTopLeftRadius(qreal r);
void setTopLeftRadius(qreal r);
[[nodiscard]] qreal topRightRadius() const {
return m_topRightRadius;
}
[[nodiscard]] qreal topRightRadius() const { return m_topRightRadius; }
void setTopRightRadius(qreal r);
void setTopRightRadius(qreal r);
[[nodiscard]] qreal bottomLeftRadius() const {
return m_bottomLeftRadius;
}
[[nodiscard]] qreal bottomLeftRadius() const { return m_bottomLeftRadius; }
void setBottomLeftRadius(qreal r);
void setBottomLeftRadius(qreal r);
[[nodiscard]] qreal bottomRightRadius() const {
[[nodiscard]] qreal bottomRightRadius() const {
return m_bottomRightRadius;
}
}
void setBottomRightRadius(qreal r);
void setBottomRightRadius(qreal r);
signals:
void stiffnessChanged();
void dampingChanged();
void deformScaleChanged();
void excludeChanged();
void topLeftRadiusChanged();
void topRightRadiusChanged();
void bottomLeftRadiusChanged();
void bottomRightRadiusChanged();
signals:
void stiffnessChanged();
void dampingChanged();
void deformScaleChanged();
void excludeChanged();
void topLeftRadiusChanged();
void topRightRadiusChanged();
void bottomLeftRadiusChanged();
void bottomRightRadiusChanged();
protected:
void updatePolish() override;
void updatePhysics() override;
protected:
void updatePolish() override;
void updatePhysics() override;
private:
void checkAtRest(float speed);
private:
void checkAtRest(float speed);
// Physics state
QPointF m_prevScenePos;
QElapsedTimer m_elapsed;
bool m_physicsActive = false;
bool m_hasPrevPos = false;
// Physics state
QPointF m_prevScenePos;
QElapsedTimer m_elapsed;
bool m_physicsActive = false;
bool m_hasPrevPos = false;
// Symmetric 2x2 deformation matrix components (3 independent: m00, m01,
// m11) Rest state is identity: m00=1, m01=0, m11=1
float m_dm00 = 1.0f;
float m_dm01 = 0.0f;
float m_dm11 = 1.0f;
// Symmetric 2x2 deformation matrix components (3 independent: m00, m01,
// m11) Rest state is identity: m00=1, m01=0, m11=1
float m_dm00 = 1.0f;
float m_dm01 = 0.0f;
float m_dm11 = 1.0f;
// Spring velocities for each component
float m_dmVel00 = 0.0f;
float m_dmVel01 = 0.0f;
float m_dmVel11 = 0.0f;
// Spring velocities for each component
float m_dmVel00 = 0.0f;
float m_dmVel01 = 0.0f;
float m_dmVel11 = 0.0f;
qreal m_stiffness = 200.0;
qreal m_damping = 16.0;
qreal m_deformScale = 0.0005;
qreal m_stiffness = 200.0;
qreal m_damping = 16.0;
qreal m_deformScale = 0.0005;
qreal m_topLeftRadius = -1;
qreal m_topRightRadius = -1;
qreal m_bottomLeftRadius = -1;
qreal m_bottomRightRadius = -1;
qreal m_topLeftRadius = -1;
qreal m_topRightRadius = -1;
qreal m_bottomLeftRadius = -1;
qreal m_bottomRightRadius = -1;
QList<QPointer<BlobRect> > m_exclude;
QList<QPointer<BlobRect>> m_exclude;
static void excludeAppend(QQmlListProperty<BlobRect>* prop, BlobRect* rect);
static qsizetype excludeCount(QQmlListProperty<BlobRect>* prop);
static BlobRect* excludeAt(QQmlListProperty<BlobRect>* prop, qsizetype index);
static void excludeClear(QQmlListProperty<BlobRect>* prop);
static void excludeReplace(QQmlListProperty<BlobRect>* prop, qsizetype index, BlobRect* rect);
static void excludeRemoveLast(QQmlListProperty<BlobRect>* prop);
static void excludeAppend(QQmlListProperty<BlobRect>* prop, BlobRect* rect);
static qsizetype excludeCount(QQmlListProperty<BlobRect>* prop);
static BlobRect* excludeAt(
QQmlListProperty<BlobRect>* prop, qsizetype index);
static void excludeClear(QQmlListProperty<BlobRect>* prop);
static void excludeReplace(
QQmlListProperty<BlobRect>* prop, qsizetype index, BlobRect* rect);
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);
}
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 dy = std::abs(py - cy) - hh;
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);
}
BlobShape::BlobShape(QQuickItem* parent)
: QQuickItem(parent) {
BlobShape::BlobShape(QQuickItem* parent) : QQuickItem(parent) {
setFlag(ItemHasContents);
}
void BlobShape::setGroup(BlobGroup* g) {
if (m_group == g)
return;
if (m_group && isComponentComplete())
unregisterFromGroup();
if (m_group == g) return;
if (m_group && isComponentComplete()) unregisterFromGroup();
m_group = g;
if (m_group && isComponentComplete())
registerWithGroup();
if (m_group && isComponentComplete()) registerWithGroup();
emit groupChanged();
if (m_group)
m_group->markDirty();
if (m_group) m_group->markDirty();
}
void BlobShape::setRadius(qreal r) {
if (qFuzzyCompare(m_radius, r))
return;
if (qFuzzyCompare(m_radius, r)) return;
m_radius = r;
emit radiusChanged();
if (m_group)
m_group->markDirty();
if (m_group) m_group->markDirty();
}
void BlobShape::componentComplete() {
QQuickItem::componentComplete();
if (m_group)
registerWithGroup();
if (m_group) registerWithGroup();
}
void BlobShape::geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) {
void BlobShape::geometryChange(
const QRectF& newGeometry, const QRectF& oldGeometry) {
QQuickItem::geometryChange(newGeometry, oldGeometry);
updateCenteredDeformMatrix();
if (m_group) {
m_pendingDx += static_cast<float>(newGeometry.x() - oldGeometry.x());
m_pendingDy += static_cast<float>(newGeometry.y() - oldGeometry.y());
m_pendingDw += static_cast<float>(newGeometry.width() - oldGeometry.width());
m_pendingDh += static_cast<float>(newGeometry.height() - oldGeometry.height());
m_pendingDw +=
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)) +
std::abs(m_deformMatrix(1, 0)) + std::abs(m_deformMatrix(1, 1) - 1.0f);
const float deformMag = std::abs(m_deformMatrix(0, 0) - 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;
if (std::abs(m_pendingDx) > syncThreshold || std::abs(m_pendingDy) > syncThreshold ||
std::abs(m_pendingDw) > syncThreshold || std::abs(m_pendingDh) > syncThreshold) {
if (std::abs(m_pendingDx) > syncThreshold ||
std::abs(m_pendingDy) > syncThreshold ||
std::abs(m_pendingDw) > syncThreshold ||
std::abs(m_pendingDh) > syncThreshold) {
m_pendingDx = 0;
m_pendingDy = 0;
m_pendingDw = 0;
@@ -111,18 +111,15 @@ void BlobShape::cornerRadii(float out[4]) const {
}
void BlobShape::registerWithGroup() {
if (m_group)
m_group->addShape(this);
if (m_group) m_group->addShape(this);
}
void BlobShape::unregisterFromGroup() {
if (m_group)
m_group->removeShape(this);
if (m_group) m_group->removeShape(this);
}
void BlobShape::updatePolish() {
if (!m_group)
return;
if (!m_group) return;
m_group->ensurePhysicsUpdated();
@@ -144,27 +141,30 @@ void BlobShape::updatePolish() {
m_cachedPaddedY = static_cast<float>(scenePos.y()) - totalPad;
m_cachedPaddedW = static_cast<float>(width()) + 2.0f * totalPad;
m_cachedPaddedH = static_cast<float>(height()) + 2.0f * totalPad;
m_localPaddedRect = QRectF(static_cast<double>(-totalPad), static_cast<double>(-totalPad),
width() + 2.0 * static_cast<double>(totalPad), height() + 2.0 * static_cast<double>(totalPad));
m_localPaddedRect = QRectF(
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_cachedMyIndex = -2;
const QRectF myPadded(static_cast<double>(m_cachedPaddedX), static_cast<double>(m_cachedPaddedY),
static_cast<double>(m_cachedPaddedW), static_cast<double>(m_cachedPaddedH));
const QRectF myPadded(
static_cast<double>(m_cachedPaddedX),
static_cast<double>(m_cachedPaddedY),
static_cast<double>(m_cachedPaddedW),
static_cast<double>(m_cachedPaddedH));
QVector<BlobShape*> rectShapes;
rectShapes.reserve(m_group->shapes().size());
for (BlobShape* other : m_group->shapes()) {
if (other->isInvertedRect())
continue;
if (other->isInvertedRect()) continue;
if (other->width() <= 0 || other->height() <= 0)
continue;
if (other->width() <= 0 || other->height() <= 0) continue;
if (isExcluded(other))
continue;
if (isExcluded(other)) continue;
const QPointF otherScene = other->mapToScene(QPointF(0, 0));
@@ -174,9 +174,12 @@ void BlobShape::updatePolish() {
} else {
const float otherHW = static_cast<float>(other->width()) * 0.5f;
const float otherHH = static_cast<float>(other->height()) * 0.5f;
const float otherPad = pad + deformPadding(other->m_deformMatrix, otherHW, otherHH);
const QRectF otherPadded(otherScene.x() - static_cast<double>(otherPad),
otherScene.y() - static_cast<double>(otherPad), other->width() + 2.0 * static_cast<double>(otherPad),
const float otherPad =
pad + deformPadding(other->m_deformMatrix, otherHW, otherHH);
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));
include = myPadded.intersects(otherPadded);
}
@@ -217,19 +220,16 @@ void BlobShape::updatePolish() {
}
}
if (isInvertedRect())
m_cachedMyIndex = -1;
if (isInvertedRect()) m_cachedMyIndex = -1;
const auto cachedCount = m_cachedRects.size();
for (qsizetype i = 0; i < cachedCount; ++i) {
int mask = 0;
BlobShape* si = rectShapes[i];
for (qsizetype j = 0; j < cachedCount; ++j) {
if (j == i)
continue;
if (j == i) continue;
BlobShape* sj = rectShapes[j];
if (si->isExcluded(sj) || sj->isExcluded(si))
mask |= (1 << j);
if (si->isExcluded(sj) || sj->isExcluded(si)) mask |= (1 << j);
}
m_cachedRects[i].excludeMask = mask;
}
@@ -242,15 +242,25 @@ void BlobShape::updatePolish() {
auto* inv = m_group->invertedRect();
if (inv) {
const QPointF invScene = inv->mapToScene(QPointF(0, 0));
const float outerCX = static_cast<float>(invScene.x() + inv->width() / 2.0);
const float outerCY = static_cast<float>(invScene.y() + inv->height() / 2.0);
const float outerCX =
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 outerHH = static_cast<float>(inv->height() / 2.0);
const float innerCX = outerCX + static_cast<float>((inv->borderLeft() - inv->borderRight()) / 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);
const float innerCX =
outerCX +
static_cast<float>((inv->borderLeft() - inv->borderRight()) / 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();
if (!nearBorder) {
@@ -259,8 +269,10 @@ void BlobShape::updatePolish() {
const float myCY = m_cachedPaddedY + m_cachedPaddedH * 0.5f;
const float myHW = m_cachedPaddedW * 0.5f;
const float myHH = m_cachedPaddedH * 0.5f;
nearBorder = (myCX - myHW < innerCX - innerHW + margin) || (myCX + myHW > innerCX + innerHW - margin) ||
(myCY - myHH < innerCY - innerHH + margin) || (myCY + myHH > innerCY + innerHH - margin);
nearBorder = (myCX - myHW < innerCX - innerHW + margin) ||
(myCX + myHW > innerCX + innerHW - margin) ||
(myCY - myHH < innerCY - innerHH + margin) ||
(myCY + myHH > innerCY + innerHH - margin);
}
if (nearBorder) {
@@ -293,15 +305,33 @@ void BlobShape::updatePolish() {
const float cTlX = ri.cx - ri.hw, cTlY = ri.cy - ri.hh;
for (qsizetype j = 0; j < rectCount; ++j) {
if (j == i)
continue;
if (riExcludeMask & (1 << j))
continue;
if (j == i) continue;
if (riExcludeMask & (1 << j)) continue;
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)));
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)));
fTr = std::min(
fTr,
cpuSmoothstep(
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) {
@@ -309,10 +339,30 @@ void BlobShape::updatePolish() {
const float icy = m_cachedInvertedInner[1];
const float ihw = m_cachedInvertedInner[2];
const float ihh = m_cachedInvertedInner[3];
fTr = std::min(fTr, cpuSmoothstep(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)));
fTr = std::min(
fTr,
cpuSmoothstep(
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);
@@ -332,7 +382,8 @@ QSGNode* BlobShape::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) {
if (!node) {
node = new QSGGeometryNode;
auto* geometry = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4);
auto* geometry =
new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4);
geometry->setDrawingMode(QSGGeometry::DrawTriangleStrip);
node->setGeometry(geometry);
node->setFlag(QSGNode::OwnsGeometry);
@@ -368,10 +419,17 @@ QSGNode* BlobShape::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) {
material->m_color = m_group->color();
material->m_hasInverted = m_cachedHasInverted ? 1 : 0;
material->m_invertedRadius = m_cachedInvertedRadius;
memcpy(material->m_invertedOuter, m_cachedInvertedOuter, sizeof(m_cachedInvertedOuter));
memcpy(material->m_invertedInner, m_cachedInvertedInner, sizeof(m_cachedInvertedInner));
memcpy(
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;
for (int i = 0; i < count; ++i)
material->m_rects[i] = m_cachedRects[i];
+56 -63
View File
@@ -9,85 +9,78 @@
class BlobGroup;
class BlobShape : public QQuickItem {
Q_OBJECT
Q_PROPERTY(BlobGroup* group READ group WRITE setGroup NOTIFY groupChanged)
Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged)
Q_PROPERTY(QMatrix4x4 deformMatrix READ deformMatrix NOTIFY deformMatrixChanged)
Q_PROPERTY(QMatrix4x4 rawDeformMatrix READ rawDeformMatrix NOTIFY rawDeformMatrixChanged)
Q_OBJECT
Q_PROPERTY(BlobGroup* group READ group WRITE setGroup NOTIFY groupChanged)
Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged)
Q_PROPERTY(
QMatrix4x4 deformMatrix READ deformMatrix NOTIFY deformMatrixChanged)
Q_PROPERTY(
QMatrix4x4 rawDeformMatrix READ rawDeformMatrix NOTIFY
rawDeformMatrixChanged)
friend class BlobGroup;
friend class BlobGroup;
public:
explicit BlobShape(QQuickItem* parent = nullptr);
~BlobShape() override = default;
public:
explicit BlobShape(QQuickItem* parent = nullptr);
~BlobShape() override = default;
[[nodiscard]] BlobGroup* group() const {
return m_group;
}
[[nodiscard]] BlobGroup* group() const { return m_group; }
void setGroup(BlobGroup* g);
void setGroup(BlobGroup* g);
[[nodiscard]] qreal radius() const {
return m_radius;
}
[[nodiscard]] qreal radius() const { return m_radius; }
void setRadius(qreal r);
void setRadius(qreal r);
[[nodiscard]] QMatrix4x4 deformMatrix() const {
[[nodiscard]] QMatrix4x4 deformMatrix() const {
return m_centeredDeformMatrix;
}
}
[[nodiscard]] QMatrix4x4 rawDeformMatrix() const {
return m_deformMatrix;
}
[[nodiscard]] QMatrix4x4 rawDeformMatrix() const { return m_deformMatrix; }
signals:
void groupChanged();
void radiusChanged();
void deformMatrixChanged();
void rawDeformMatrixChanged();
signals:
void groupChanged();
void radiusChanged();
void deformMatrixChanged();
void rawDeformMatrixChanged();
protected:
void componentComplete() override;
void geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) override;
void updatePolish() override;
QSGNode* updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) override;
protected:
void componentComplete() override;
void geometryChange(
const QRectF& newGeometry, const QRectF& oldGeometry) override;
void updatePolish() override;
QSGNode* updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) override;
[[nodiscard]] virtual bool isInvertedRect() const {
return false;
}
[[nodiscard]] virtual bool isInvertedRect() const { return false; }
virtual bool isExcluded(const BlobShape* /*other*/) const {
return false;
}
virtual bool isExcluded(const BlobShape*) const { 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 unregisterFromGroup();
void updateCenteredDeformMatrix();
virtual void registerWithGroup();
virtual void unregisterFromGroup();
void updateCenteredDeformMatrix();
BlobGroup* m_group = nullptr;
qreal m_radius = 0;
QMatrix4x4 m_deformMatrix;
QMatrix4x4 m_centeredDeformMatrix;
BlobGroup* m_group = nullptr;
qreal m_radius = 0;
QMatrix4x4 m_deformMatrix;
QMatrix4x4 m_centeredDeformMatrix;
float m_cachedPaddedX = 0;
float m_cachedPaddedY = 0;
float m_cachedPaddedW = 0;
float m_cachedPaddedH = 0;
QRectF m_localPaddedRect;
QVector<BlobRectData> m_cachedRects;
int m_cachedMyIndex = -2;
float m_pendingDx = 0;
float m_pendingDy = 0;
float m_pendingDw = 0;
float m_pendingDh = 0;
bool m_cachedHasInverted = false;
float m_cachedInvertedRadius = 0;
float m_cachedInvertedOuter[4] = {};
float m_cachedInvertedInner[4] = {};
float m_cachedPaddedX = 0;
float m_cachedPaddedY = 0;
float m_cachedPaddedW = 0;
float m_cachedPaddedH = 0;
QRectF m_localPaddedRect;
QVector<BlobRectData> m_cachedRects;
int m_cachedMyIndex = -2;
float m_pendingDx = 0;
float m_pendingDy = 0;
float m_pendingDw = 0;
float m_pendingDh = 0;
bool m_cachedHasInverted = false;
float m_cachedInvertedRadius = 0;
float m_cachedInvertedOuter[4] = {};
float m_cachedInvertedInner[4] = {};
};
+38 -24
View File
@@ -3,11 +3,10 @@
namespace ZShell::components {
ButtonRow::ButtonRow(QQuickItem* parent)
: QQuickItem(parent)
, m_dirty(false)
, m_spacing(0.0) {
: QQuickItem(parent), m_dirty(false), m_spacing(0.0) {
setFlag(QQuickItem::ItemHasContents, true);
QObject::connect(this, &ButtonRow::widthChanged, this, &ButtonRow::invalidate);
QObject::connect(
this, &ButtonRow::widthChanged, this, &ButtonRow::invalidate);
}
qreal ButtonRow::spacing() const {
@@ -15,8 +14,7 @@ qreal ButtonRow::spacing() const {
}
void ButtonRow::setSpacing(qreal spacing) {
if (qFuzzyCompare(m_spacing + 1.0, spacing + 1.0))
return;
if (qFuzzyCompare(m_spacing + 1.0, spacing + 1.0)) return;
m_spacing = spacing;
emit spacingChanged();
@@ -24,18 +22,33 @@ void ButtonRow::setSpacing(qreal spacing) {
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) {
auto* const child = data.item;
QObject::connect(child, &QQuickItem::implicitWidthChanged, this, &ButtonRow::invalidate);
QObject::connect(child, &QQuickItem::implicitHeightChanged, this, &ButtonRow::invalidate);
QObject::connect(child, &QQuickItem::visibleChanged, this, &ButtonRow::invalidate);
QObject::connect(
child,
&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 morphSignalIdx = childMeta->indexOfSignal("shapeMorphExpansionChanged()");
const auto morphSignalIdx =
childMeta->indexOfSignal("shapeMorphExpansionChanged()");
if (morphSignalIdx != -1)
QObject::connect(child, childMeta->method(morphSignalIdx), this,
metaObject()->method(metaObject()->indexOfSlot("invalidate()")));
QObject::connect(
child,
childMeta->method(morphSignalIdx),
this,
metaObject()->method(
metaObject()->indexOfSlot("invalidate()")));
invalidate();
} else if (change == QQuickItem::ItemChildRemovedChange) {
@@ -77,8 +90,7 @@ void ButtonRow::relayout() {
maxHeight = qMax(maxHeight, child->implicitHeight());
const auto prop = child->property("fillWidth");
if (!prop.isValid())
continue;
if (!prop.isValid()) continue;
if (prop.toBool()) {
fillWidthCount++;
@@ -88,15 +100,17 @@ void ButtonRow::relayout() {
}
}
if (fillWidthCount == 0)
fillWidthCount = 1; // Avoid divide by 0
if (fillWidthCount == 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;
baseWidths.reserve(nChildren);
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;
for (int i = 0; i < nChildren; ++i) {
@@ -108,12 +122,12 @@ void ButtonRow::relayout() {
// clang-format on
// Items at edges push by full amount, items in middle push by half
if (i > 1)
prevExtraWidth /= 2;
if (i < nChildren - 2)
nextExtraWidth /= 2;
if (i > 1) prevExtraWidth /= 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->setX(accX);
+20 -19
View File
@@ -5,33 +5,34 @@
namespace ZShell::components {
class ButtonRow : public QQuickItem {
Q_OBJECT
QML_ELEMENT
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing NOTIFY spacingChanged)
Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing NOTIFY spacingChanged)
public:
explicit ButtonRow(QQuickItem* parent = nullptr);
public:
explicit ButtonRow(QQuickItem* parent = nullptr);
[[nodiscard]] qreal spacing() const;
void setSpacing(qreal spacing);
[[nodiscard]] qreal spacing() const;
void setSpacing(qreal spacing);
signals:
void spacingChanged();
signals:
void spacingChanged();
protected:
void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData& data) override;
void updatePolish() override;
protected:
void itemChange(
QQuickItem::ItemChange change,
const QQuickItem::ItemChangeData& data) override;
void updatePolish() override;
private slots:
void invalidate();
private slots:
void invalidate();
private:
void relayout();
static qreal getMorphExpansion(const QQuickItem* item);
void relayout();
static qreal getMorphExpansion(const QQuickItem* item);
bool m_dirty;
qreal m_spacing;
bool m_dirty;
qreal m_spacing;
};
} // namespace ZShell::components
+121 -195
View File
@@ -21,8 +21,7 @@ qreal LazyListViewAttached::preferredHeight() const {
}
void LazyListViewAttached::setPreferredHeight(qreal height) {
if (qFuzzyCompare(m_preferredHeight + 1.0, height + 1.0))
return;
if (qFuzzyCompare(m_preferredHeight + 1.0, height + 1.0)) return;
m_preferredHeight = height;
emit preferredHeightChanged();
}
@@ -32,8 +31,7 @@ qreal LazyListViewAttached::visibleHeight() const {
}
void LazyListViewAttached::setVisibleHeight(qreal height) {
if (qFuzzyCompare(m_visibleHeight + 1.0, height + 1.0))
return;
if (qFuzzyCompare(m_visibleHeight + 1.0, height + 1.0)) return;
m_visibleHeight = height;
emit visibleHeightChanged();
}
@@ -43,8 +41,7 @@ bool LazyListViewAttached::ready() const {
}
void LazyListViewAttached::setReady(bool ready) {
if (m_ready == ready)
return;
if (m_ready == ready) return;
m_ready = ready;
emit readyChanged();
}
@@ -54,8 +51,7 @@ bool LazyListViewAttached::adding() const {
}
void LazyListViewAttached::setAdding(bool adding) {
if (m_adding == adding)
return;
if (m_adding == adding) return;
m_adding = adding;
emit addingChanged();
}
@@ -65,8 +61,7 @@ bool LazyListViewAttached::removing() const {
}
void LazyListViewAttached::setRemoving(bool removing) {
if (m_removing == removing)
return;
if (m_removing == removing) return;
m_removing = removing;
emit removingChanged();
}
@@ -76,8 +71,7 @@ bool LazyListViewAttached::trackViewport() const {
}
void LazyListViewAttached::setTrackViewport(bool track) {
if (m_trackViewport == track)
return;
if (m_trackViewport == track) return;
m_trackViewport = track;
emit trackViewportChanged();
}
@@ -102,16 +96,13 @@ QAbstractItemModel* LazyListView::model() const {
}
void LazyListView::setModel(QAbstractItemModel* model) {
if (m_model == model)
return;
if (m_model == model) return;
if (m_model)
disconnectModel();
if (m_model) disconnectModel();
m_model = model;
if (m_model)
connectModel();
if (m_model) connectModel();
resetContent();
emit modelChanged();
@@ -122,8 +113,7 @@ QQmlComponent* LazyListView::delegate() const {
}
void LazyListView::setDelegate(QQmlComponent* delegate) {
if (m_delegate == delegate)
return;
if (m_delegate == delegate) return;
m_delegate = delegate;
resetContent();
@@ -135,8 +125,7 @@ qreal LazyListView::spacing() const {
}
void LazyListView::setSpacing(qreal spacing) {
if (qFuzzyCompare(m_spacing, spacing))
return;
if (qFuzzyCompare(m_spacing, spacing)) return;
m_spacing = spacing;
emit spacingChanged();
polish();
@@ -155,8 +144,7 @@ qreal LazyListView::contentY() const {
}
void LazyListView::setContentY(qreal contentY) {
if (qFuzzyCompare(m_contentY, contentY))
return;
if (qFuzzyCompare(m_contentY, contentY)) return;
m_contentY = contentY;
emit contentYChanged();
polish();
@@ -167,12 +155,10 @@ QRectF LazyListView::viewport() const {
}
void LazyListView::setViewport(const QRectF& viewport) {
if (m_viewport == viewport)
return;
if (m_viewport == viewport) return;
m_viewport = viewport;
emit viewportChanged();
if (m_useCustomViewport)
polish();
if (m_useCustomViewport) polish();
}
bool LazyListView::useCustomViewport() const {
@@ -180,8 +166,7 @@ bool LazyListView::useCustomViewport() const {
}
void LazyListView::setUseCustomViewport(bool use) {
if (m_useCustomViewport == use)
return;
if (m_useCustomViewport == use) return;
m_useCustomViewport = use;
emit useCustomViewportChanged();
polish();
@@ -192,8 +177,7 @@ qreal LazyListView::cacheBuffer() const {
}
void LazyListView::setCacheBuffer(qreal buffer) {
if (qFuzzyCompare(m_cacheBuffer, buffer))
return;
if (qFuzzyCompare(m_cacheBuffer, buffer)) return;
m_cacheBuffer = buffer;
emit cacheBufferChanged();
polish();
@@ -204,8 +188,7 @@ qreal LazyListView::estimatedHeight() const {
}
void LazyListView::setEstimatedHeight(qreal height) {
if (qFuzzyCompare(m_estimatedHeight, height))
return;
if (qFuzzyCompare(m_estimatedHeight, height)) return;
m_estimatedHeight = height;
emit estimatedHeightChanged();
polish();
@@ -216,17 +199,14 @@ bool LazyListView::asynchronous() const {
}
void LazyListView::setAsynchronous(bool async) {
if (m_asynchronous == async)
return;
if (m_asynchronous == async) return;
m_asynchronous = async;
emit asynchronousChanged();
}
qreal LazyListView::effectiveEstimatedHeight() const {
if (m_estimatedHeight >= 0)
return m_estimatedHeight;
if (m_knownHeightCount > 0)
return m_knownHeightSum / m_knownHeightCount;
if (m_estimatedHeight >= 0) return m_estimatedHeight;
if (m_knownHeightCount > 0) return m_knownHeightSum / m_knownHeightCount;
return 40;
}
@@ -241,8 +221,7 @@ void LazyListView::untrackHeight(qreal height) {
}
qreal LazyListView::delegateHeight(QQuickItem* item) {
if (!item)
return 0;
if (!item) return 0;
auto* attached = qobject_cast<LazyListViewAttached*>(
qmlAttachedPropertiesObject<LazyListView>(item, false));
@@ -253,14 +232,12 @@ qreal LazyListView::delegateHeight(QQuickItem* item) {
}
qreal LazyListView::delegateVisibleHeight(QQuickItem* item) {
if (!item)
return 0;
if (!item) return 0;
auto* attached = qobject_cast<LazyListViewAttached*>(
qmlAttachedPropertiesObject<LazyListView>(item, false));
if (attached) {
if (attached->visibleHeight() >= 0)
return attached->visibleHeight();
if (attached->visibleHeight() >= 0) return attached->visibleHeight();
if (attached->preferredHeight() >= 0)
return attached->preferredHeight();
}
@@ -269,8 +246,7 @@ qreal LazyListView::delegateVisibleHeight(QQuickItem* item) {
}
bool LazyListView::isDelegateReady(QQuickItem* item) {
if (!item)
return false;
if (!item) return false;
auto* att = qobject_cast<LazyListViewAttached*>(
qmlAttachedPropertiesObject<LazyListView>(item, false));
return !att || att->ready();
@@ -281,8 +257,7 @@ int LazyListView::removeDuration() const {
}
void LazyListView::setRemoveDuration(int duration) {
if (m_removeDuration == duration)
return;
if (m_removeDuration == duration) return;
m_removeDuration = duration;
emit removeDurationChanged();
}
@@ -292,8 +267,7 @@ int LazyListView::readyDelay() const {
}
void LazyListView::setReadyDelay(int delay) {
if (m_readyDelay == delay)
return;
if (m_readyDelay == delay) return;
m_readyDelay = delay;
emit readyDelayChanged();
}
@@ -308,17 +282,15 @@ void LazyListView::componentComplete() {
resetContent();
}
void LazyListView::geometryChange(const QRectF& newGeometry,
const QRectF& oldGeometry) {
void LazyListView::geometryChange(
const QRectF& newGeometry, const QRectF& oldGeometry) {
QQuickItem::geometryChange(newGeometry, oldGeometry);
if (!m_componentComplete)
return;
if (!m_componentComplete) return;
if (!qFuzzyCompare(newGeometry.width(), oldGeometry.width())) {
for (auto& entry : m_delegates) {
if (entry.item)
entry.item->setWidth(newGeometry.width());
if (entry.item) entry.item->setWidth(newGeometry.width());
}
}
@@ -326,8 +298,7 @@ void LazyListView::geometryChange(const QRectF& newGeometry,
}
void LazyListView::updatePolish() {
if (!m_componentComplete || !m_model || !m_delegate)
return;
if (!m_componentComplete || !m_model || !m_delegate) return;
// Flush pending inserts — make items visible and clear the adding flag
// 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
QList<int> pendingIndices;
for (auto it = m_delegates.constBegin(); it != m_delegates.constEnd(); ++it) {
if (it->pendingInsert && it->item)
pendingIndices.append(it.key());
for (auto it = m_delegates.constBegin(); it != m_delegates.constEnd();
++it) {
if (it->pendingInsert && it->item) pendingIndices.append(it.key());
}
for (int pendingIdx : pendingIndices) {
@@ -349,8 +320,7 @@ void LazyListView::updatePolish() {
auto* item = entry.item;
QTimer::singleShot(m_readyDelay, this, [this, item] {
auto indexIt = m_itemToIndex.find(item);
if (indexIt == m_itemToIndex.end())
return;
if (indexIt == m_itemToIndex.end()) return;
const int idx = indexIt.value();
auto it = m_delegates.find(idx);
if (it == m_delegates.end() || it->item != item ||
@@ -375,14 +345,11 @@ void LazyListView::updatePolish() {
? m_layout[i].height
: effectiveEstimatedHeight();
if (h > 0) {
if (hasVisItem)
visualY += m_spacing;
if (hasVisItem) visualY += m_spacing;
hasVisItem = true;
}
if (i == idx)
break;
if (h > 0)
visualY += h;
if (i == idx) break;
if (h > 0) visualY += h;
}
item->setY(visualY - m_contentY);
}
@@ -397,8 +364,8 @@ void LazyListView::updatePolish() {
// Animate from visual position to layout position
if (idx >= 0 && idx < static_cast<int>(m_layout.size()))
item->setProperty("y",
m_layout[idx].targetY - m_contentY);
item->setProperty(
"y", m_layout[idx].targetY - m_contentY);
polish();
});
@@ -432,8 +399,7 @@ void LazyListView::updatePolish() {
continue;
const int idx = entry.modelIndex;
if (idx < 0 || idx >= layoutSize)
continue;
if (idx < 0 || idx >= layoutSize) continue;
if (m_layout[idx].heightKnown && qFuzzyIsNull(m_layout[idx].height))
continue;
@@ -444,8 +410,6 @@ void LazyListView::updatePolish() {
}
}
// --- Layout Engine ---
void LazyListView::relayout() {
// Layout positioning uses preferredHeight (final/non-animated).
// Only add spacing between items with non-zero height.
@@ -455,8 +419,7 @@ void LazyListView::relayout() {
const qreal layoutH = record.heightKnown ? record.height
: effectiveEstimatedHeight();
if (layoutH > 0) {
if (hasLayoutItem)
y += m_spacing;
if (hasLayoutItem) y += m_spacing;
hasLayoutItem = true;
record.targetY = y;
y += layoutH;
@@ -483,8 +446,7 @@ void LazyListView::relayout() {
h = m_layout[i].heightKnown ? m_layout[i].height
: effectiveEstimatedHeight();
if (h > 0) {
if (hasVisItem)
visY += m_spacing;
if (hasVisItem) visY += m_spacing;
hasVisItem = true;
visY += h;
}
@@ -492,11 +454,9 @@ void LazyListView::relayout() {
// Account for dying delegates still visually present
for (const auto& dying : std::as_const(m_dyingDelegates)) {
if (!dying.item)
continue;
if (!dying.item) continue;
const qreal dyingH = delegateVisibleHeight(dying.item);
if (dyingH > 0)
visY = std::max(visY, dying.item->y() + dyingH);
if (dyingH > 0) visY = std::max(visY, dying.item->y() + dyingH);
}
if (!qFuzzyCompare(m_contentHeight + 1.0, visY + 1.0)) {
@@ -519,8 +479,7 @@ QRectF LazyListView::effectiveViewport() const {
if (!m_useCustomViewport && m_layoutHeight > 0) {
const qreal top = std::min(vp.y(), m_layoutHeight);
const qreal bottom = std::max(vp.y() + vp.height(), 0.0);
if (bottom > top)
vp = QRectF(vp.x(), top, vp.width(), bottom - top);
if (bottom > top) vp = QRectF(vp.x(), top, vp.width(), bottom - top);
}
vp.adjust(0, -m_cacheBuffer, 0, m_cacheBuffer);
@@ -541,12 +500,10 @@ QRectF LazyListView::effectiveViewport() const {
}
std::pair<int, int> LazyListView::computeVisibleRange() const {
if (m_layout.isEmpty())
return {-1, -1};
if (m_layout.isEmpty()) return {-1, -1};
const auto vp = effectiveViewport();
if (vp.isEmpty())
return {-1, -1};
if (vp.isEmpty()) return {-1, -1};
const qreal vpTop = vp.y();
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()))
return {-1, -1};
if (first >= static_cast<int>(m_layout.size())) return {-1, -1};
// Linear scan for last visible item
int last = first;
for (int i = first; i < static_cast<int>(m_layout.size()); ++i) {
if (m_layout[i].targetY > vpBottom)
break;
if (m_layout[i].targetY > vpBottom) break;
last = i;
}
return {first, last};
}
// --- Delegate Lifecycle ---
void LazyListView::syncDelegates() {
const auto [first, last] = computeVisibleRange();
@@ -601,8 +554,7 @@ void LazyListView::syncDelegates() {
const auto vp = effectiveViewport();
QList<int> toRemove;
for (auto it = m_delegates.begin(); it != m_delegates.end(); ++it) {
if (visibleIndices.contains(it.key()))
continue;
if (visibleIndices.contains(it.key())) continue;
if (!it->item || vp.isEmpty()) {
toRemove.append(it.key());
continue;
@@ -622,11 +574,9 @@ void LazyListView::syncDelegates() {
std::min(destroyBudget, static_cast<int>(toRemove.size())));
int destroyed = 0;
for (int idx : toRemove) {
if (destroyed >= destroyBudget)
break;
if (destroyed >= destroyBudget) break;
auto entry = m_delegates.take(idx);
if (entry.item)
m_itemToIndex.remove(entry.item);
if (entry.item) m_itemToIndex.remove(entry.item);
removedEntries.append(std::move(entry));
++destroyed;
}
@@ -637,8 +587,7 @@ void LazyListView::syncDelegates() {
QList<int> toCreate;
if (first >= 0) {
for (int i = first; i <= last; ++i) {
if (!m_delegates.contains(i))
toCreate.append(i);
if (!m_delegates.contains(i)) toCreate.append(i);
}
}
@@ -647,8 +596,7 @@ void LazyListView::syncDelegates() {
: static_cast<int>(toCreate.size());
int created = 0;
for (int i : toCreate) {
if (created >= createBudget)
break;
if (created >= createBudget) break;
auto entry = createDelegate(i);
if (entry.item) {
@@ -674,25 +622,21 @@ LazyListView::DelegateEntry LazyListView::createDelegate(int modelIndex) {
DelegateEntry entry;
entry.modelIndex = modelIndex;
if (!m_delegate || !m_model)
return entry;
if (!m_delegate || !m_model) return entry;
const auto roleNames = m_model->roleNames();
// Use the delegate component's creation context for beginCreate
// so bound components (pragma ComponentBehavior: Bound) are accepted.
auto* compContext = m_delegate->creationContext();
if (!compContext)
compContext = qmlContext(this);
if (!compContext)
return entry;
if (!compContext) compContext = qmlContext(this);
if (!compContext) return entry;
auto* obj = m_delegate->beginCreate(compContext);
entry.item = qobject_cast<QQuickItem*>(obj);
if (!entry.item) {
if (obj)
m_delegate->completeCreate();
if (obj) m_delegate->completeCreate();
delete obj;
return entry;
}
@@ -705,16 +649,15 @@ LazyListView::DelegateEntry LazyListView::createDelegate(int modelIndex) {
for (auto it = roleNames.constBegin(); it != roleNames.constEnd(); ++it) {
const auto name = QString::fromUtf8(it.value());
initialProps.insert(name, m_model->data(index, it.key()));
if (name == QStringLiteral("modelData"))
hasModelData = true;
if (name == QStringLiteral("modelData")) hasModelData = true;
}
initialProps.insert(QStringLiteral("index"), modelIndex);
if (!hasModelData) {
const auto role = roleNames.isEmpty() ? Qt::DisplayRole
: roleNames.constBegin().key();
initialProps.insert(QStringLiteral("modelData"),
m_model->data(index, role));
initialProps.insert(
QStringLiteral("modelData"), m_model->data(index, role));
}
m_delegate->setInitialProperties(entry.item, initialProps);
@@ -728,8 +671,7 @@ LazyListView::DelegateEntry LazyListView::createDelegate(int modelIndex) {
m_layout[modelIndex].isNew) {
auto* addingAttached = qobject_cast<LazyListViewAttached*>(
qmlAttachedPropertiesObject<LazyListView>(entry.item, true));
if (addingAttached)
addingAttached->setAdding(true);
if (addingAttached) addingAttached->setAdding(true);
}
m_delegate->completeCreate();
@@ -740,15 +682,12 @@ LazyListView::DelegateEntry LazyListView::createDelegate(int modelIndex) {
// Height-change handler — uses m_itemToIndex for O(1) lookup.
// Ignored while the delegate is not yet ready.
auto onHeightChanged = [this, item = entry.item] {
if (!isDelegateReady(item))
return;
if (!isDelegateReady(item)) return;
auto indexIt = m_itemToIndex.find(item);
if (indexIt == m_itemToIndex.end())
return;
if (indexIt == m_itemToIndex.end()) return;
const int idx = indexIt.value();
auto delegateIt = m_delegates.find(idx);
if (delegateIt == m_delegates.end() || delegateIt->item != item)
return;
if (delegateIt == m_delegates.end() || delegateIt->item != item) return;
const qreal h = delegateHeight(item);
if (idx < static_cast<int>(m_layout.size()) &&
!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;
m_layout[idx].height = h;
m_layout[idx].heightKnown = true;
if (wasKnown)
untrackHeight(oldH);
if (wasKnown) untrackHeight(oldH);
trackHeight(h);
// If this tracked item is above the viewport, emit a
@@ -785,37 +723,35 @@ LazyListView::DelegateEntry LazyListView::createDelegate(int modelIndex) {
};
// Watch implicitHeight as fallback
connect(entry.item,
&QQuickItem::implicitHeightChanged,
this,
onHeightChanged);
connect(
entry.item, &QQuickItem::implicitHeightChanged, this, onHeightChanged);
// Watch attached properties if the delegate uses them
auto* attached = qobject_cast<LazyListViewAttached*>(
qmlAttachedPropertiesObject<LazyListView>(entry.item, false));
if (attached) {
connect(attached,
connect(
attached,
&LazyListViewAttached::preferredHeightChanged,
this,
onHeightChanged);
connect(attached,
connect(
attached,
&LazyListViewAttached::visibleHeightChanged,
this,
[this] { polish(); });
connect(attached,
connect(
attached,
&LazyListViewAttached::readyChanged,
this,
[this, item = entry.item] {
auto indexIt = m_itemToIndex.find(item);
if (indexIt == m_itemToIndex.end())
return;
if (indexIt == m_itemToIndex.end()) return;
const int idx = indexIt.value();
if (idx >= static_cast<int>(m_layout.size()))
return;
if (idx >= static_cast<int>(m_layout.size())) return;
auto* att = qobject_cast<LazyListViewAttached*>(
qmlAttachedPropertiesObject<LazyListView>(item, false));
if (!att || !att->ready())
return;
if (!att || !att->ready()) return;
const qreal h = delegateHeight(item);
const qreal oldLayoutH = m_layout[idx].heightKnown
@@ -852,8 +788,7 @@ void LazyListView::destroyDelegate(DelegateEntry& entry) {
}
void LazyListView::updateDelegateData(DelegateEntry& entry) {
if (!m_model || !entry.item)
return;
if (!m_model || !entry.item) return;
const auto roleNames = m_model->roleNames();
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) {
const auto name = QString::fromUtf8(it.value());
entry.item->setProperty(name.toUtf8().constData(),
m_model->data(index, it.key()));
if (name == QStringLiteral("modelData"))
hasModelData = true;
entry.item->setProperty(
name.toUtf8().constData(), m_model->data(index, it.key()));
if (name == QStringLiteral("modelData")) hasModelData = true;
}
entry.item->setProperty("index", entry.modelIndex);
@@ -876,38 +810,42 @@ void LazyListView::updateDelegateData(DelegateEntry& entry) {
}
}
// --- Model Connection ---
void LazyListView::connectModel() {
if (!m_model)
return;
if (!m_model) return;
m_modelConnections = {
connect(m_model,
connect(
m_model,
&QAbstractItemModel::rowsInserted,
this,
&LazyListView::onRowsInserted),
connect(m_model,
connect(
m_model,
&QAbstractItemModel::rowsAboutToBeRemoved,
this,
&LazyListView::onRowsAboutToBeRemoved),
connect(m_model,
connect(
m_model,
&QAbstractItemModel::rowsRemoved,
this,
&LazyListView::onRowsRemoved),
connect(m_model,
connect(
m_model,
&QAbstractItemModel::rowsMoved,
this,
&LazyListView::onRowsMoved),
connect(m_model,
connect(
m_model,
&QAbstractItemModel::dataChanged,
this,
&LazyListView::onDataChanged),
connect(m_model,
connect(
m_model,
&QAbstractItemModel::modelReset,
this,
&LazyListView::onModelReset),
connect(m_model,
connect(
m_model,
&QAbstractItemModel::layoutChanged,
this,
[this] {
@@ -915,7 +853,8 @@ void LazyListView::connectModel() {
updateDelegateData(entry);
polish();
}),
connect(m_model,
connect(
m_model,
&QObject::destroyed,
this,
[this] {
@@ -962,11 +901,9 @@ void LazyListView::resetContent() {
polish();
}
void LazyListView::onRowsInserted(const QModelIndex& parent,
int first,
int last) {
if (parent.isValid())
return;
void LazyListView::onRowsInserted(
const QModelIndex& parent, int first, int last) {
if (parent.isValid()) return;
const int insertCount = last - first + 1;
// Insert new layout records
@@ -990,19 +927,15 @@ void LazyListView::onRowsInserted(const QModelIndex& parent,
polish();
}
void LazyListView::onRowsAboutToBeRemoved(const QModelIndex& parent,
int first,
int last) {
if (parent.isValid())
return;
void LazyListView::onRowsAboutToBeRemoved(
const QModelIndex& parent, int first, int last) {
if (parent.isValid()) return;
for (int i = first; i <= last; ++i) {
if (!m_delegates.contains(i))
continue;
if (!m_delegates.contains(i)) continue;
auto entry = m_delegates.take(i);
if (entry.item)
m_itemToIndex.remove(entry.item);
if (entry.item) m_itemToIndex.remove(entry.item);
entry.pendingRemoval = true;
// Never made visible — skip remove animation
@@ -1014,8 +947,7 @@ void LazyListView::onRowsAboutToBeRemoved(const QModelIndex& parent,
if (m_removeDuration > 0 && entry.item) {
auto* attached = qobject_cast<LazyListViewAttached*>(
qmlAttachedPropertiesObject<LazyListView>(entry.item, false));
if (attached)
attached->setRemoving(true);
if (attached) attached->setRemoving(true);
// Schedule destruction after the remove animation duration
auto* item = entry.item;
@@ -1037,18 +969,15 @@ void LazyListView::onRowsAboutToBeRemoved(const QModelIndex& parent,
}
}
void LazyListView::onRowsRemoved(const QModelIndex& parent,
int first,
int last) {
if (parent.isValid())
return;
void LazyListView::onRowsRemoved(
const QModelIndex& parent, int first, int last) {
if (parent.isValid()) return;
const int removeCount = last - first + 1;
// Untrack known heights being removed
for (int i = first; i <= last; ++i) {
if (m_layout[i].heightKnown)
untrackHeight(m_layout[i].height);
if (m_layout[i].heightKnown) untrackHeight(m_layout[i].height);
}
// Remove layout records
@@ -1072,13 +1001,13 @@ void LazyListView::onRowsRemoved(const QModelIndex& parent,
polish();
}
void LazyListView::onRowsMoved(const QModelIndex& parent,
void LazyListView::onRowsMoved(
const QModelIndex& parent,
int start,
int end,
const QModelIndex& destination,
int row) {
if (parent.isValid() || destination.isValid())
return;
if (parent.isValid() || destination.isValid()) return;
const int count = end - start + 1;
const int dest = row > start ? row - count : row;
@@ -1101,10 +1030,8 @@ void LazyListView::onRowsMoved(const QModelIndex& parent,
if (oldIdx >= start && oldIdx <= end) {
newIdx = dest + (oldIdx - start);
} else {
if (oldIdx > end)
newIdx -= count;
if (newIdx >= dest)
newIdx += count;
if (oldIdx > end) newIdx -= count;
if (newIdx >= dest) newIdx += count;
}
auto entry = std::move(it.value());
@@ -1120,17 +1047,16 @@ void LazyListView::onRowsMoved(const QModelIndex& parent,
polish();
}
void LazyListView::onDataChanged(const QModelIndex& topLeft,
void LazyListView::onDataChanged(
const QModelIndex& topLeft,
const QModelIndex& bottomRight,
const QList<int>& roles) {
Q_UNUSED(roles)
if (topLeft.parent().isValid())
return;
if (topLeft.parent().isValid()) return;
for (int i = topLeft.row(); i <= bottomRight.row(); ++i) {
if (m_delegates.contains(i))
updateDelegateData(m_delegates[i]);
if (m_delegates.contains(i)) updateDelegateData(m_delegates[i]);
}
}
+201 -168
View File
@@ -12,232 +12,265 @@
namespace ZShell::components {
class LazyListViewAttached : public QObject {
Q_OBJECT
Q_OBJECT
Q_PROPERTY(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 adding READ adding NOTIFY addingChanged)
Q_PROPERTY(bool removing READ removing NOTIFY removingChanged)
Q_PROPERTY(bool trackViewport READ trackViewport WRITE setTrackViewport NOTIFY trackViewportChanged)
Q_PROPERTY(
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 adding READ adding NOTIFY addingChanged)
Q_PROPERTY(bool removing READ removing NOTIFY removingChanged)
Q_PROPERTY(
bool trackViewport READ trackViewport WRITE setTrackViewport NOTIFY
trackViewportChanged)
public:
explicit LazyListViewAttached(QObject* parent = nullptr);
public:
explicit LazyListViewAttached(QObject* parent = nullptr);
[[nodiscard]] qreal preferredHeight() const;
void setPreferredHeight(qreal height);
[[nodiscard]] qreal preferredHeight() const;
void setPreferredHeight(qreal height);
[[nodiscard]] qreal visibleHeight() const;
void setVisibleHeight(qreal height);
[[nodiscard]] qreal visibleHeight() const;
void setVisibleHeight(qreal height);
[[nodiscard]] bool ready() const;
void setReady(bool ready);
[[nodiscard]] bool ready() const;
void setReady(bool ready);
[[nodiscard]] bool adding() const;
void setAdding(bool adding);
[[nodiscard]] bool adding() const;
void setAdding(bool adding);
[[nodiscard]] bool removing() const;
void setRemoving(bool removing);
[[nodiscard]] bool removing() const;
void setRemoving(bool removing);
[[nodiscard]] bool trackViewport() const;
void setTrackViewport(bool track);
[[nodiscard]] bool trackViewport() const;
void setTrackViewport(bool track);
signals:
void preferredHeightChanged();
void visibleHeightChanged();
void readyChanged();
void addingChanged();
void removingChanged();
void trackViewportChanged();
signals:
void preferredHeightChanged();
void visibleHeightChanged();
void readyChanged();
void addingChanged();
void removingChanged();
void trackViewportChanged();
private:
qreal m_preferredHeight = -1;
qreal m_visibleHeight = -1;
bool m_ready = false;
bool m_adding = false;
bool m_removing = false;
bool m_trackViewport = false;
private:
qreal m_preferredHeight = -1;
qreal m_visibleHeight = -1;
bool m_ready = false;
bool m_adding = false;
bool m_removing = false;
bool m_trackViewport = false;
};
class LazyListView : public QQuickItem {
Q_OBJECT
QML_ELEMENT
QML_ATTACHED(LazyListViewAttached)
Q_OBJECT
QML_ELEMENT
QML_ATTACHED(LazyListViewAttached)
// Model & Delegate
Q_PROPERTY(QAbstractItemModel* model READ model WRITE setModel NOTIFY modelChanged)
Q_PROPERTY(QQmlComponent* delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
// Model & Delegate
Q_PROPERTY(
QAbstractItemModel* model READ model WRITE setModel NOTIFY modelChanged)
Q_PROPERTY(
QQmlComponent* delegate READ delegate WRITE setDelegate NOTIFY
delegateChanged)
// Layout
Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing NOTIFY spacingChanged)
Q_PROPERTY(qreal contentHeight READ contentHeight NOTIFY contentHeightChanged)
Q_PROPERTY(qreal layoutHeight READ layoutHeight NOTIFY layoutHeightChanged)
Q_PROPERTY(qreal contentY READ contentY WRITE setContentY NOTIFY contentYChanged)
// Layout
Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing NOTIFY spacingChanged)
Q_PROPERTY(
qreal contentHeight READ contentHeight NOTIFY contentHeightChanged)
Q_PROPERTY(qreal layoutHeight READ layoutHeight NOTIFY layoutHeightChanged)
Q_PROPERTY(
qreal contentY READ contentY WRITE setContentY NOTIFY contentYChanged)
// Viewport & Lazy Loading
Q_PROPERTY(QRectF viewport READ viewport WRITE setViewport NOTIFY viewportChanged)
Q_PROPERTY(bool useCustomViewport READ useCustomViewport WRITE setUseCustomViewport NOTIFY useCustomViewportChanged)
Q_PROPERTY(qreal cacheBuffer READ cacheBuffer WRITE setCacheBuffer NOTIFY cacheBufferChanged)
// Viewport & Lazy Loading
Q_PROPERTY(
QRectF viewport READ viewport WRITE setViewport NOTIFY viewportChanged)
Q_PROPERTY(
bool useCustomViewport READ useCustomViewport WRITE setUseCustomViewport
NOTIFY useCustomViewportChanged)
Q_PROPERTY(
qreal cacheBuffer READ cacheBuffer WRITE setCacheBuffer NOTIFY
cacheBufferChanged)
// Sizing
Q_PROPERTY(qreal estimatedHeight READ estimatedHeight WRITE setEstimatedHeight NOTIFY estimatedHeightChanged)
// Sizing
Q_PROPERTY(
qreal estimatedHeight READ estimatedHeight WRITE setEstimatedHeight
NOTIFY estimatedHeightChanged)
// Async
Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged)
// Async
Q_PROPERTY(
bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY
asynchronousChanged)
// Animation Durations
Q_PROPERTY(int removeDuration READ removeDuration WRITE setRemoveDuration NOTIFY removeDurationChanged)
Q_PROPERTY(int readyDelay READ readyDelay WRITE setReadyDelay NOTIFY readyDelayChanged)
// Animation Durations
Q_PROPERTY(
int removeDuration READ removeDuration WRITE setRemoveDuration NOTIFY
removeDurationChanged)
Q_PROPERTY(
int readyDelay READ readyDelay WRITE setReadyDelay NOTIFY
readyDelayChanged)
// State
Q_PROPERTY(int count READ count NOTIFY countChanged)
// State
Q_PROPERTY(int count READ count NOTIFY countChanged)
public:
explicit LazyListView(QQuickItem* parent = nullptr);
~LazyListView() override;
public:
explicit LazyListView(QQuickItem* parent = nullptr);
~LazyListView() override;
static LazyListViewAttached* qmlAttachedProperties(QObject* object);
static LazyListViewAttached* qmlAttachedProperties(QObject* object);
// Model & Delegate
[[nodiscard]] QAbstractItemModel* model() const;
void setModel(QAbstractItemModel* model);
// Model & Delegate
[[nodiscard]] QAbstractItemModel* model() const;
void setModel(QAbstractItemModel* model);
[[nodiscard]] QQmlComponent* delegate() const;
void setDelegate(QQmlComponent* delegate);
[[nodiscard]] QQmlComponent* delegate() const;
void setDelegate(QQmlComponent* delegate);
// Layout
[[nodiscard]] qreal spacing() const;
void setSpacing(qreal spacing);
// Layout
[[nodiscard]] qreal spacing() const;
void setSpacing(qreal spacing);
[[nodiscard]] qreal contentHeight() const;
[[nodiscard]] qreal layoutHeight() const;
[[nodiscard]] qreal contentHeight() const;
[[nodiscard]] qreal layoutHeight() const;
[[nodiscard]] qreal contentY() const;
void setContentY(qreal contentY);
[[nodiscard]] qreal contentY() const;
void setContentY(qreal contentY);
// Viewport
[[nodiscard]] QRectF viewport() const;
void setViewport(const QRectF& viewport);
// Viewport
[[nodiscard]] QRectF viewport() const;
void setViewport(const QRectF& viewport);
[[nodiscard]] bool useCustomViewport() const;
void setUseCustomViewport(bool use);
[[nodiscard]] bool useCustomViewport() const;
void setUseCustomViewport(bool use);
[[nodiscard]] qreal cacheBuffer() const;
void setCacheBuffer(qreal buffer);
[[nodiscard]] qreal cacheBuffer() const;
void setCacheBuffer(qreal buffer);
// Sizing
[[nodiscard]] qreal estimatedHeight() const;
void setEstimatedHeight(qreal height);
// Sizing
[[nodiscard]] qreal estimatedHeight() const;
void setEstimatedHeight(qreal height);
// Async
[[nodiscard]] bool asynchronous() const;
void setAsynchronous(bool async);
// Async
[[nodiscard]] bool asynchronous() const;
void setAsynchronous(bool async);
// Animation Durations
[[nodiscard]] int removeDuration() const;
void setRemoveDuration(int duration);
// Animation Durations
[[nodiscard]] int removeDuration() const;
void setRemoveDuration(int duration);
[[nodiscard]] int readyDelay() const;
void setReadyDelay(int delay);
[[nodiscard]] int readyDelay() const;
void setReadyDelay(int delay);
// State
[[nodiscard]] int count() const;
signals:
void modelChanged();
void delegateChanged();
void spacingChanged();
void contentHeightChanged();
void layoutHeightChanged();
void contentYChanged();
void viewportChanged();
void useCustomViewportChanged();
void cacheBufferChanged();
void estimatedHeightChanged();
void asynchronousChanged();
void removeDurationChanged();
void readyDelayChanged();
void countChanged();
void viewportAdjustNeeded(qreal delta);
// State
[[nodiscard]] int count() const;
signals:
void modelChanged();
void delegateChanged();
void spacingChanged();
void contentHeightChanged();
void layoutHeightChanged();
void contentYChanged();
void viewportChanged();
void useCustomViewportChanged();
void cacheBufferChanged();
void estimatedHeightChanged();
void asynchronousChanged();
void removeDurationChanged();
void readyDelayChanged();
void countChanged();
void viewportAdjustNeeded(qreal delta);
protected:
void componentComplete() override;
void geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) override;
void updatePolish() override;
protected:
void componentComplete() override;
void geometryChange(
const QRectF& newGeometry, const QRectF& oldGeometry) override;
void updatePolish() override;
private:
struct ItemRecord {
private:
struct ItemRecord {
qreal targetY = 0;
qreal height = 0;
bool heightKnown = false;
bool isNew = false;
};
};
struct DelegateEntry {
struct DelegateEntry {
int modelIndex = -1;
QQuickItem* item = nullptr;
bool pendingRemoval = false;
bool pendingInsert = false;
bool readyDelayStarted = false;
};
};
// Layout
void relayout();
[[nodiscard]] std::pair<int, int> computeVisibleRange() const;
[[nodiscard]] QRectF effectiveViewport() const;
[[nodiscard]] qreal effectiveEstimatedHeight() const;
[[nodiscard]] static qreal delegateHeight(QQuickItem* item);
[[nodiscard]] static qreal delegateVisibleHeight(QQuickItem* item);
[[nodiscard]] static bool isDelegateReady(QQuickItem* item);
void trackHeight(qreal height);
void untrackHeight(qreal height);
// Layout
void relayout();
[[nodiscard]] std::pair<int, int> computeVisibleRange() const;
[[nodiscard]] QRectF effectiveViewport() const;
[[nodiscard]] qreal effectiveEstimatedHeight() const;
[[nodiscard]] static qreal delegateHeight(QQuickItem* item);
[[nodiscard]] static qreal delegateVisibleHeight(QQuickItem* item);
[[nodiscard]] static bool isDelegateReady(QQuickItem* item);
void trackHeight(qreal height);
void untrackHeight(qreal height);
// Delegate lifecycle
void syncDelegates();
DelegateEntry createDelegate(int modelIndex);
void destroyDelegate(DelegateEntry& entry);
void updateDelegateData(DelegateEntry& entry);
// Delegate lifecycle
void syncDelegates();
DelegateEntry createDelegate(int modelIndex);
void destroyDelegate(DelegateEntry& entry);
void updateDelegateData(DelegateEntry& entry);
// Model connection
void connectModel();
void disconnectModel();
void resetContent();
void onRowsInserted(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 onRowsMoved(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();
// Model connection
void connectModel();
void disconnectModel();
void resetContent();
void onRowsInserted(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 onRowsMoved(
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();
// Members
QAbstractItemModel* m_model = nullptr;
QQmlComponent* m_delegate = nullptr;
// Members
QAbstractItemModel* m_model = nullptr;
QQmlComponent* m_delegate = nullptr;
qreal m_spacing = 0;
qreal m_contentHeight = 0;
qreal m_layoutHeight = 0;
qreal m_contentY = 0;
qreal m_spacing = 0;
qreal m_contentHeight = 0;
qreal m_layoutHeight = 0;
qreal m_contentY = 0;
QRectF m_viewport;
bool m_useCustomViewport = false;
qreal m_cacheBuffer = 0;
QRectF m_viewport;
bool m_useCustomViewport = false;
qreal m_cacheBuffer = 0;
qreal m_estimatedHeight = -1;
qreal m_knownHeightSum = 0;
int m_knownHeightCount = 0;
bool m_asynchronous = false;
qreal m_estimatedHeight = -1;
qreal m_knownHeightSum = 0;
int m_knownHeightCount = 0;
bool m_asynchronous = false;
int m_removeDuration = 300;
int m_readyDelay = 0;
int m_removeDuration = 300;
int m_readyDelay = 0;
QVector<ItemRecord> m_layout;
QHash<int, DelegateEntry> m_delegates;
QHash<QQuickItem*, int> m_itemToIndex;
QVector<DelegateEntry> m_dyingDelegates;
QVector<ItemRecord> m_layout;
QHash<int, DelegateEntry> m_delegates;
QHash<QQuickItem*, int> m_itemToIndex;
QVector<DelegateEntry> m_dyingDelegates;
bool m_componentComplete = false;
bool m_relayoutPending = false;
bool m_componentComplete = false;
bool m_relayoutPending = false;
QList<QMetaObject::Connection> m_modelConnections;
QList<QMetaObject::Connection> m_modelConnections;
};
} // namespace ZShell::components
+6 -2
View File
@@ -194,7 +194,8 @@ void WavyLine::paintLinear(QPainter* painter) {
bool first = true;
for (int x = m_lineWidth / 2; x <= drawEnd; ++x) {
const auto theta = m_frequency * 2 * M_PI * (x + m_startX) / len + phase;
const auto theta =
m_frequency * 2 * M_PI * (x + m_startX) / len + phase;
const auto waveY = centerY + amplitude * qSin(theta);
if (first) {
path.moveTo(x, waveY);
@@ -215,7 +216,10 @@ void WavyLine::paintArc(QPainter* painter) {
const auto amplitude = m_lineWidth * m_amplitudeMultiplier;
const auto cx = width() / 2.0;
const auto cy = height() / 2.0;
const auto radius = m_radius > 0 ? m_radius : (qMin(width(), height()) - m_lineWidth - 2 * amplitude) / 2.0;
const auto radius =
m_radius > 0
? m_radius
: (qMin(width(), height()) - m_lineWidth - 2 * amplitude) / 2.0;
if (radius <= 0) {
return;
+91 -77
View File
@@ -6,102 +6,116 @@
namespace ZShell::controls {
class WavyLine : public QQuickPaintedItem {
Q_OBJECT
QML_ELEMENT
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(int lineWidth READ lineWidth WRITE setLineWidth NOTIFY lineWidthChanged FINAL)
Q_PROPERTY(qreal amplitudeMultiplier READ amplitudeMultiplier WRITE 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(qreal waveProgress READ waveProgress WRITE setWaveProgress NOTIFY waveProgressChanged FINAL)
Q_PROPERTY(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(
int lineWidth READ lineWidth WRITE setLineWidth NOTIFY lineWidthChanged
FINAL)
Q_PROPERTY(
qreal amplitudeMultiplier READ amplitudeMultiplier WRITE
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(
qreal waveProgress READ waveProgress WRITE setWaveProgress NOTIFY
waveProgressChanged FINAL)
Q_PROPERTY(
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)
public:
enum PathType {
Linear,
Arc
};
Q_ENUM(PathType)
public:
enum PathType { Linear, Arc };
Q_ENUM(PathType)
explicit WavyLine(QQuickItem* parent = nullptr);
explicit WavyLine(QQuickItem* parent = nullptr);
[[nodiscard]] int lineWidth() const;
void setLineWidth(int lineWidth);
[[nodiscard]] int lineWidth() const;
void setLineWidth(int lineWidth);
[[nodiscard]] qreal amplitudeMultiplier() const;
void setAmplitudeMultiplier(qreal amplitudeMultiplier);
[[nodiscard]] qreal amplitudeMultiplier() const;
void setAmplitudeMultiplier(qreal amplitudeMultiplier);
[[nodiscard]] int frequency() const;
void setFrequency(int frequency);
[[nodiscard]] int frequency() const;
void setFrequency(int frequency);
[[nodiscard]] qreal startX() const;
void setStartX(qreal startX);
[[nodiscard]] qreal startX() const;
void setStartX(qreal startX);
[[nodiscard]] qreal fullLength() const;
void setFullLength(qreal fullLength);
[[nodiscard]] qreal fullLength() const;
void setFullLength(qreal fullLength);
[[nodiscard]] QColor color() const;
void setColor(const QColor& color);
[[nodiscard]] QColor color() const;
void setColor(const QColor& color);
[[nodiscard]] qreal waveProgress() const;
void setWaveProgress(qreal progress);
[[nodiscard]] qreal waveProgress() const;
void setWaveProgress(qreal progress);
[[nodiscard]] PathType pathType() const;
void setPathType(PathType pathType);
[[nodiscard]] PathType pathType() const;
void setPathType(PathType pathType);
[[nodiscard]] qreal startAngle() const;
void setStartAngle(qreal startAngle);
[[nodiscard]] qreal startAngle() const;
void setStartAngle(qreal startAngle);
[[nodiscard]] qreal fullAngle() const;
void setFullAngle(qreal fullAngle);
[[nodiscard]] qreal fullAngle() const;
void setFullAngle(qreal fullAngle);
[[nodiscard]] qreal radius() const;
void setRadius(qreal radius);
[[nodiscard]] qreal radius() const;
void setRadius(qreal radius);
[[nodiscard]] qreal value() const;
void setValue(qreal value);
[[nodiscard]] qreal value() const;
void setValue(qreal value);
void paint(QPainter* painter) override;
void paint(QPainter* painter) override;
signals:
void lineWidthChanged();
void amplitudeMultiplierChanged();
void frequencyChanged();
void startXChanged();
void fullLengthChanged();
void colorChanged();
void waveProgressChanged();
void pathTypeChanged();
void startAngleChanged();
void fullAngleChanged();
void radiusChanged();
void valueChanged();
signals:
void lineWidthChanged();
void amplitudeMultiplierChanged();
void frequencyChanged();
void startXChanged();
void fullLengthChanged();
void colorChanged();
void waveProgressChanged();
void pathTypeChanged();
void startAngleChanged();
void fullAngleChanged();
void radiusChanged();
void valueChanged();
private:
void paintLinear(QPainter* painter);
void paintArc(QPainter* painter);
private:
void paintLinear(QPainter* painter);
void paintArc(QPainter* painter);
int m_lineWidth;
qreal m_amplitudeMultiplier;
int m_frequency;
qreal m_startX;
qreal m_fullLength;
QColor m_color;
qreal m_waveProgress;
PathType m_pathType;
qreal m_startAngle;
qreal m_fullAngle;
qreal m_radius;
qreal m_value;
qreal m_startAngleRad;
qreal m_fullAngleRad;
int m_lineWidth;
qreal m_amplitudeMultiplier;
int m_frequency;
qreal m_startX;
qreal m_fullLength;
QColor m_color;
qreal m_waveProgress;
PathType m_pathType;
qreal m_startAngle;
qreal m_fullAngle;
qreal m_radius;
qreal m_value;
qreal m_startAngleRad;
qreal m_fullAngleRad;
};
} // namespace ZShell::controls
+9 -15
View File
@@ -6,8 +6,7 @@
namespace ZShell::internal {
ArcGauge::ArcGauge(QQuickItem* parent)
: QQuickPaintedItem(parent) {
ArcGauge::ArcGauge(QQuickItem* parent) : QQuickPaintedItem(parent) {
setAntialiasing(true);
}
@@ -36,7 +35,8 @@ void ArcGauge::paint(QPainter* painter) {
// Draw value arc
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);
valuePen.setCapStyle(Qt::RoundCap);
painter->setPen(valuePen);
@@ -49,8 +49,7 @@ qreal ArcGauge::percentage() const {
}
void ArcGauge::setPercentage(qreal percentage) {
if (qFuzzyCompare(m_percentage, percentage))
return;
if (qFuzzyCompare(m_percentage, percentage)) return;
m_percentage = percentage;
emit percentageChanged();
update();
@@ -61,8 +60,7 @@ QColor ArcGauge::accentColor() const {
}
void ArcGauge::setAccentColor(const QColor& color) {
if (m_accentColor == color)
return;
if (m_accentColor == color) return;
m_accentColor = color;
emit accentColorChanged();
update();
@@ -73,8 +71,7 @@ QColor ArcGauge::trackColor() const {
}
void ArcGauge::setTrackColor(const QColor& color) {
if (m_trackColor == color)
return;
if (m_trackColor == color) return;
m_trackColor = color;
emit trackColorChanged();
update();
@@ -85,8 +82,7 @@ qreal ArcGauge::startAngle() const {
}
void ArcGauge::setStartAngle(qreal angle) {
if (qFuzzyCompare(m_startAngle, angle))
return;
if (qFuzzyCompare(m_startAngle, angle)) return;
m_startAngle = angle;
emit startAngleChanged();
update();
@@ -97,8 +93,7 @@ qreal ArcGauge::sweepAngle() const {
}
void ArcGauge::setSweepAngle(qreal angle) {
if (qFuzzyCompare(m_sweepAngle, angle))
return;
if (qFuzzyCompare(m_sweepAngle, angle)) return;
m_sweepAngle = angle;
emit sweepAngleChanged();
update();
@@ -109,8 +104,7 @@ qreal ArcGauge::lineWidth() const {
}
void ArcGauge::setLineWidth(qreal width) {
if (qFuzzyCompare(m_lineWidth, width))
return;
if (qFuzzyCompare(m_lineWidth, width)) return;
m_lineWidth = width;
emit lineWidthChanged();
update();
+50 -38
View File
@@ -8,54 +8,66 @@
namespace ZShell::internal {
class ArcGauge : public QQuickPaintedItem {
Q_OBJECT
QML_ELEMENT
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(qreal percentage READ percentage WRITE setPercentage NOTIFY percentageChanged)
Q_PROPERTY(QColor accentColor READ accentColor WRITE setAccentColor NOTIFY 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)
Q_PROPERTY(
qreal percentage READ percentage WRITE setPercentage NOTIFY
percentageChanged)
Q_PROPERTY(
QColor accentColor READ accentColor WRITE setAccentColor NOTIFY
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:
explicit ArcGauge(QQuickItem* parent = nullptr);
public:
explicit ArcGauge(QQuickItem* parent = nullptr);
void paint(QPainter* painter) override;
void paint(QPainter* painter) override;
[[nodiscard]] qreal percentage() const;
void setPercentage(qreal percentage);
[[nodiscard]] qreal percentage() const;
void setPercentage(qreal percentage);
[[nodiscard]] QColor accentColor() const;
void setAccentColor(const QColor& color);
[[nodiscard]] QColor accentColor() const;
void setAccentColor(const QColor& color);
[[nodiscard]] QColor trackColor() const;
void setTrackColor(const QColor& color);
[[nodiscard]] QColor trackColor() const;
void setTrackColor(const QColor& color);
[[nodiscard]] qreal startAngle() const;
void setStartAngle(qreal angle);
[[nodiscard]] qreal startAngle() const;
void setStartAngle(qreal angle);
[[nodiscard]] qreal sweepAngle() const;
void setSweepAngle(qreal angle);
[[nodiscard]] qreal sweepAngle() const;
void setSweepAngle(qreal angle);
[[nodiscard]] qreal lineWidth() const;
void setLineWidth(qreal width);
[[nodiscard]] qreal lineWidth() const;
void setLineWidth(qreal width);
signals:
void percentageChanged();
void accentColorChanged();
void trackColorChanged();
void startAngleChanged();
void sweepAngleChanged();
void lineWidthChanged();
signals:
void percentageChanged();
void accentColorChanged();
void trackColorChanged();
void startAngleChanged();
void sweepAngleChanged();
void lineWidthChanged();
private:
qreal m_percentage = 0.0;
QColor m_accentColor;
QColor m_trackColor;
qreal m_startAngle = 0.75 * M_PI;
qreal m_sweepAngle = 1.5 * M_PI;
qreal m_lineWidth = 10.0;
private:
qreal m_percentage = 0.0;
QColor m_accentColor;
QColor m_trackColor;
qreal m_startAngle = 0.75 * M_PI;
qreal m_sweepAngle = 1.5 * M_PI;
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 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);
return size;
}
@@ -53,7 +54,8 @@ void CachingImageManager::setItem(QQuickItem* item) {
m_widthConn = connect(item, &QQuickItem::widthChanged, this, [this]() {
updateSource();
});
m_heightConn = connect(item, &QQuickItem::heightChanged, this, [this]() {
m_heightConn =
connect(item, &QQuickItem::heightChanged, this, [this]() {
updateSource();
});
updateSource();
@@ -105,11 +107,16 @@ void CachingImageManager::updateSource(const QString& 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);
connect(watcher, &QFutureWatcher<QString>::finished, this, [watcher, path, this]() {
connect(
watcher,
&QFutureWatcher<QString>::finished,
this,
[watcher, path, this]() {
if (m_path != path) {
// Object is destroyed or path has changed, ignore
watcher->deleteLater();
@@ -140,7 +147,8 @@ void CachingImageManager::updateSource(const QString& path) {
emit cachePathChanged();
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();
return;
}
@@ -169,31 +177,42 @@ QUrl CachingImageManager::cachePath() const {
}
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] {
QImage image(path);
if (image.isNull()) {
qWarning() << "CachingImageManager::createCache: failed to read" << path;
qWarning() << "CachingImageManager::createCache: failed to read"
<< path;
return;
}
image.convertTo(QImage::Format_ARGB32);
if (fillMode == "PreserveAspectCrop") {
image = image.scaled(size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
image = image.scaled(
size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
} else if (fillMode == "PreserveAspectFit") {
image = image.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
image = image.scaled(
size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
} 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);
canvas.fill(Qt::transparent);
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();
image = canvas;
@@ -201,7 +220,8 @@ void CachingImageManager::createCache(
const QString parent = QFileInfo(cache).absolutePath();
if (!QDir().mkpath(parent) || !image.save(cache)) {
qWarning() << "CachingImageManager::createCache: failed to save to" << cache;
qWarning() << "CachingImageManager::createCache: failed to save to"
<< cache;
}
});
}
+43 -38
View File
@@ -7,60 +7,65 @@
namespace ZShell::internal {
class CachingImageManager : public QObject {
Q_OBJECT
QML_ELEMENT
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(QQuickItem* item READ item WRITE setItem NOTIFY itemChanged REQUIRED)
Q_PROPERTY(QUrl cacheDir READ cacheDir WRITE setCacheDir NOTIFY cacheDirChanged REQUIRED)
Q_PROPERTY(
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(QUrl cachePath READ cachePath NOTIFY cachePathChanged)
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
Q_PROPERTY(QUrl cachePath READ cachePath NOTIFY cachePathChanged)
public:
explicit CachingImageManager(QObject* parent = nullptr)
: QObject(parent)
, m_item(nullptr) {
}
public:
explicit CachingImageManager(QObject* parent = nullptr)
: QObject(parent), m_item(nullptr) {}
[[nodiscard]] QQuickItem* item() const;
void setItem(QQuickItem* item);
[[nodiscard]] QQuickItem* item() const;
void setItem(QQuickItem* item);
[[nodiscard]] QUrl cacheDir() const;
void setCacheDir(const QUrl& cacheDir);
[[nodiscard]] QUrl cacheDir() const;
void setCacheDir(const QUrl& cacheDir);
[[nodiscard]] QString path() const;
void setPath(const QString& path);
[[nodiscard]] QString path() const;
void setPath(const QString& path);
[[nodiscard]] QUrl cachePath() const;
[[nodiscard]] QUrl cachePath() const;
Q_INVOKABLE void updateSource();
Q_INVOKABLE void updateSource(const QString& path);
Q_INVOKABLE void updateSource();
Q_INVOKABLE void updateSource(const QString& path);
signals:
void itemChanged();
void cacheDirChanged();
signals:
void itemChanged();
void cacheDirChanged();
void pathChanged();
void cachePathChanged();
void usingCacheChanged();
void pathChanged();
void cachePathChanged();
void usingCacheChanged();
private:
QString m_shaPath;
private:
QString m_shaPath;
QQuickItem* m_item;
QUrl m_cacheDir;
QQuickItem* m_item;
QUrl m_cacheDir;
QString m_path;
QUrl m_cachePath;
QString m_path;
QUrl m_cachePath;
QMetaObject::Connection m_widthConn;
QMetaObject::Connection m_heightConn;
QMetaObject::Connection m_widthConn;
QMetaObject::Connection m_heightConn;
[[nodiscard]] qreal effectiveScale() const;
[[nodiscard]] QSize effectiveSize() const;
[[nodiscard]] qreal effectiveScale() const;
[[nodiscard]] QSize effectiveSize() const;
void createCache(const QString& path, const QString& cache, const QString& fillMode, const QSize& size) const;
[[nodiscard]] static QString sha256sum(const QString& path);
void createCache(
const QString& path,
const QString& cache,
const QString& fillMode,
const QSize& size) const;
[[nodiscard]] static QString sha256sum(const QString& path);
};
} // namespace ZShell::internal
+9 -16
View File
@@ -4,19 +4,15 @@
namespace ZShell::internal {
CircularBuffer::CircularBuffer(QObject* parent)
: QObject(parent) {
}
CircularBuffer::CircularBuffer(QObject* parent) : QObject(parent) {}
int CircularBuffer::capacity() const {
return m_capacity;
}
void CircularBuffer::setCapacity(int capacity) {
if (capacity < 0)
capacity = 0;
if (m_capacity == capacity)
return;
if (capacity < 0) capacity = 0;
if (m_capacity == capacity) return;
const auto old = values();
@@ -52,8 +48,7 @@ QList<qreal> CircularBuffer::values() const {
}
qreal CircularBuffer::maximum() const {
if (m_count == 0)
return 0.0;
if (m_count == 0) return 0.0;
qreal maxVal = at(0);
for (int i = 1; i < m_count; ++i)
@@ -62,8 +57,7 @@ qreal CircularBuffer::maximum() const {
}
void CircularBuffer::push(qreal value) {
if (m_capacity <= 0)
return;
if (m_capacity <= 0) return;
m_data[m_head] = value;
m_head = (m_head + 1) % m_capacity;
@@ -75,8 +69,7 @@ void CircularBuffer::push(qreal value) {
}
void CircularBuffer::clear() {
if (m_count == 0)
return;
if (m_count == 0) return;
m_head = 0;
m_count = 0;
@@ -85,10 +78,10 @@ void CircularBuffer::clear() {
}
qreal CircularBuffer::at(int index) const {
if (index < 0 || index >= m_count)
return 0.0;
if (index < 0 || index >= m_count) 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];
}
+26 -25
View File
@@ -7,38 +7,39 @@
namespace ZShell::internal {
class CircularBuffer : public QObject {
Q_OBJECT
QML_ELEMENT
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(int capacity READ capacity WRITE setCapacity NOTIFY capacityChanged)
Q_PROPERTY(int count READ count NOTIFY countChanged)
Q_PROPERTY(QList<qreal> values READ values NOTIFY valuesChanged)
Q_PROPERTY(qreal maximum READ maximum NOTIFY valuesChanged)
Q_PROPERTY(
int capacity READ capacity WRITE setCapacity NOTIFY capacityChanged)
Q_PROPERTY(int count READ count NOTIFY countChanged)
Q_PROPERTY(QList<qreal> values READ values NOTIFY valuesChanged)
Q_PROPERTY(qreal maximum READ maximum NOTIFY valuesChanged)
public:
explicit CircularBuffer(QObject* parent = nullptr);
public:
explicit CircularBuffer(QObject* parent = nullptr);
[[nodiscard]] int capacity() const;
void setCapacity(int capacity);
[[nodiscard]] int capacity() const;
void setCapacity(int capacity);
[[nodiscard]] int count() const;
[[nodiscard]] QList<qreal> values() const;
[[nodiscard]] qreal maximum() const;
[[nodiscard]] int count() const;
[[nodiscard]] QList<qreal> values() const;
[[nodiscard]] qreal maximum() const;
Q_INVOKABLE void push(qreal value);
Q_INVOKABLE void clear();
Q_INVOKABLE [[nodiscard]] qreal at(int index) const;
Q_INVOKABLE void push(qreal value);
Q_INVOKABLE void clear();
Q_INVOKABLE [[nodiscard]] qreal at(int index) const;
signals:
void capacityChanged();
void countChanged();
void valuesChanged();
signals:
void capacityChanged();
void countChanged();
void valuesChanged();
private:
QVector<qreal> m_data;
int m_head = 0;
int m_count = 0;
int m_capacity = 0;
private:
QVector<qreal> m_data;
int m_head = 0;
int m_count = 0;
int m_capacity = 0;
};
} // namespace ZShell::internal
@@ -15,8 +15,10 @@ constexpr qint32 TAIL_DEGREES_OFFSET = -20;
constexpr qint32 EXTRA_DEGREES_PER_CYCLE = 250;
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_COLLAPSE_IN_MS = { 667, 2017, 3367, 4717 };
constexpr std::array<qint32, TOTAL_CYCLES> DELAY_TO_EXPAND_IN_MS =
{0, 1350, 2700, 4050};
constexpr std::array<qint32, TOTAL_CYCLES> DELAY_TO_COLLAPSE_IN_MS = {
667, 2017, 3367, 4717};
} // namespace advance
@@ -26,7 +28,7 @@ constexpr qint32 TOTAL_DURATION_IN_MS = 6000;
constexpr qint32 DURATION_SPIN_IN_MS = 500;
constexpr qint32 DURATION_GROW_ACTIVE_IN_MS = 3000;
constexpr qint32 DURATION_SHRINK_ACTIVE_IN_MS = 3000;
constexpr std::array DELAY_SPINS_IN_MS = { 0, 1500, 3000, 4500 };
constexpr std::array DELAY_SPINS_IN_MS = {0, 1500, 3000, 4500};
constexpr qint32 DELAY_GROW_ACTIVE_IN_MS = 0;
constexpr qint32 DELAY_SHRINK_ACTIVE_IN_MS = 3000;
constexpr qint32 DURATION_TO_COMPLETE_END_IN_MS = 500;
@@ -38,7 +40,7 @@ constexpr qint32 CONSTANT_ROTATION_DEGREES = 1080;
// Despite of the constant rotation, there are also 5 extra rotations the entire animation. The
// total degrees that each extra rotation goes by.
constexpr qint32 SPIN_ROTATION_DEGREES = 90;
constexpr std::array<qreal, 2> END_FRACTION_RANGE = { 0.10, 0.87 };
constexpr std::array<qreal, 2> END_FRACTION_RANGE = {0.10, 0.87};
} // namespace retreat
@@ -61,7 +63,7 @@ CircularIndicatorManager::CircularIndicatorManager(QObject* parent)
, m_rotation(0)
, m_completeEndProgress(0) {
// Fast out slow in
m_curve.addCubicBezierSegment({ 0.4, 0.0 }, { 0.2, 1.0 }, { 1.0, 1.0 });
m_curve.addCubicBezierSegment({0.4, 0.0}, {0.2, 1.0}, {1.0, 1.0});
}
qreal CircularIndicatorManager::startFraction() const {
@@ -100,11 +102,13 @@ qreal CircularIndicatorManager::completeEndDuration() const {
}
}
CircularIndicatorManager::IndeterminateAnimationType CircularIndicatorManager::indeterminateAnimationType() const {
CircularIndicatorManager::IndeterminateAnimationType
CircularIndicatorManager::indeterminateAnimationType() const {
return m_type;
}
void CircularIndicatorManager::setIndeterminateAnimationType(IndeterminateAnimationType t) {
void CircularIndicatorManager::setIndeterminateAnimationType(
IndeterminateAnimationType t) {
if (m_type != t) {
m_type = t;
emit indeterminateAnimationTypeChanged();
@@ -150,24 +154,27 @@ void CircularIndicatorManager::updateRetreat(qreal progress) {
// Extra rotation for the faster spinning.
qreal spinRotation = 0;
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;
}
m_rotation = constantRotation + spinRotation;
emit rotationChanged();
// Grow active indicator.
qreal fraction =
m_curve.valueForProgress(getFractionInRange(playtime, DELAY_GROW_ACTIVE_IN_MS, DURATION_GROW_ACTIVE_IN_MS));
fraction -=
m_curve.valueForProgress(getFractionInRange(playtime, DELAY_SHRINK_ACTIVE_IN_MS, DURATION_SHRINK_ACTIVE_IN_MS));
qreal fraction = m_curve.valueForProgress(getFractionInRange(
playtime, DELAY_GROW_ACTIVE_IN_MS, DURATION_GROW_ACTIVE_IN_MS));
fraction -= m_curve.valueForProgress(getFractionInRange(
playtime, DELAY_SHRINK_ACTIVE_IN_MS, DURATION_SHRINK_ACTIVE_IN_MS));
if (!qFuzzyIsNull(m_startFraction)) {
m_startFraction = 0.0;
emit startFractionChanged();
}
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.
if (m_completeEndProgress > 0) {
@@ -184,22 +191,32 @@ void CircularIndicatorManager::updateAdvance(qreal progress) {
const auto playtime = progress * TOTAL_DURATION_IN_MS;
// 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;
// Adds cycle specific rotation to segment positions.
for (size_t cycleIndex = 0; cycleIndex < TOTAL_CYCLES; ++cycleIndex) {
// While expanding.
qreal fraction = getFractionInRange(playtime, DELAY_TO_EXPAND_IN_MS[cycleIndex], DURATION_TO_EXPAND_IN_MS);
m_endFraction += m_curve.valueForProgress(fraction) * EXTRA_DEGREES_PER_CYCLE;
qreal fraction = getFractionInRange(
playtime,
DELAY_TO_EXPAND_IN_MS[cycleIndex],
DURATION_TO_EXPAND_IN_MS);
m_endFraction +=
m_curve.valueForProgress(fraction) * EXTRA_DEGREES_PER_CYCLE;
// While collapsing.
fraction = getFractionInRange(playtime, DELAY_TO_COLLAPSE_IN_MS[cycleIndex], DURATION_TO_COLLAPSE_IN_MS);
m_startFraction += m_curve.valueForProgress(fraction) * EXTRA_DEGREES_PER_CYCLE;
fraction = getFractionInRange(
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.
m_startFraction += (m_endFraction - m_startFraction) * m_completeEndProgress;
m_startFraction +=
(m_endFraction - m_startFraction) * m_completeEndProgress;
m_startFraction /= 360.0;
m_endFraction /= 360.0;
@@ -1,6 +1,5 @@
#pragma once
#include <cstdint>
#include <qeasingcurve.h>
#include <qobject.h>
#include <qqmlintegration.h>
@@ -8,66 +7,71 @@
namespace ZShell::internal {
class CircularIndicatorManager : public QObject {
Q_OBJECT
QML_ELEMENT
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(qreal startFraction READ startFraction NOTIFY startFractionChanged)
Q_PROPERTY(qreal endFraction READ endFraction NOTIFY endFractionChanged)
Q_PROPERTY(qreal rotation READ rotation NOTIFY rotationChanged)
Q_PROPERTY(qreal progress READ progress WRITE setProgress NOTIFY progressChanged)
Q_PROPERTY(qreal completeEndProgress READ completeEndProgress WRITE setCompleteEndProgress NOTIFY
completeEndProgressChanged)
Q_PROPERTY(qreal duration READ duration NOTIFY indeterminateAnimationTypeChanged)
Q_PROPERTY(qreal completeEndDuration READ completeEndDuration NOTIFY indeterminateAnimationTypeChanged)
Q_PROPERTY(IndeterminateAnimationType indeterminateAnimationType READ indeterminateAnimationType WRITE
setIndeterminateAnimationType NOTIFY indeterminateAnimationTypeChanged)
Q_PROPERTY(
qreal startFraction READ startFraction NOTIFY startFractionChanged)
Q_PROPERTY(qreal endFraction READ endFraction NOTIFY endFractionChanged)
Q_PROPERTY(qreal rotation READ rotation NOTIFY rotationChanged)
Q_PROPERTY(
qreal progress READ progress WRITE setProgress NOTIFY progressChanged)
Q_PROPERTY(
qreal completeEndProgress READ completeEndProgress WRITE
setCompleteEndProgress NOTIFY completeEndProgressChanged)
Q_PROPERTY(
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:
explicit CircularIndicatorManager(QObject* parent = nullptr);
public:
explicit CircularIndicatorManager(QObject* parent = nullptr);
enum IndeterminateAnimationType {
Advance = 0,
Retreat
};
Q_ENUM(IndeterminateAnimationType)
enum IndeterminateAnimationType { Advance = 0, Retreat };
Q_ENUM(IndeterminateAnimationType)
[[nodiscard]] qreal startFraction() const;
[[nodiscard]] qreal endFraction() const;
[[nodiscard]] qreal rotation() const;
[[nodiscard]] qreal startFraction() const;
[[nodiscard]] qreal endFraction() const;
[[nodiscard]] qreal rotation() const;
[[nodiscard]] qreal progress() const;
void setProgress(qreal progress);
[[nodiscard]] qreal progress() const;
void setProgress(qreal progress);
[[nodiscard]] qreal completeEndProgress() const;
void setCompleteEndProgress(qreal progress);
[[nodiscard]] qreal completeEndProgress() const;
void setCompleteEndProgress(qreal progress);
[[nodiscard]] qreal duration() const;
[[nodiscard]] qreal completeEndDuration() const;
[[nodiscard]] qreal duration() const;
[[nodiscard]] qreal completeEndDuration() const;
[[nodiscard]] IndeterminateAnimationType indeterminateAnimationType() const;
void setIndeterminateAnimationType(IndeterminateAnimationType t);
[[nodiscard]] IndeterminateAnimationType indeterminateAnimationType() const;
void setIndeterminateAnimationType(IndeterminateAnimationType t);
signals:
void startFractionChanged();
void endFractionChanged();
void rotationChanged();
void progressChanged();
void completeEndProgressChanged();
void indeterminateAnimationTypeChanged();
signals:
void startFractionChanged();
void endFractionChanged();
void rotationChanged();
void progressChanged();
void completeEndProgressChanged();
void indeterminateAnimationTypeChanged();
private:
IndeterminateAnimationType m_type;
QEasingCurve m_curve;
private:
IndeterminateAnimationType m_type;
QEasingCurve m_curve;
qreal m_progress;
qreal m_startFraction;
qreal m_endFraction;
qreal m_rotation;
qreal m_completeEndProgress;
qreal m_progress;
qreal m_startFraction;
qreal m_endFraction;
qreal m_rotation;
qreal m_completeEndProgress;
void update(qreal progress);
void updateAdvance(qreal progress);
void updateRetreat(qreal progress);
void update(qreal progress);
void updateAdvance(qreal progress);
void updateRetreat(qreal progress);
};
} // namespace ZShell::internal
+10 -9
View File
@@ -5,8 +5,7 @@
namespace ZShell::internal::hypr {
HyprKeyboard::HyprKeyboard(QJsonObject ipcObject, QObject* parent)
: QObject(parent)
, m_lastIpcObject(ipcObject) {}
: QObject(parent), m_lastIpcObject(ipcObject) {}
QVariantHash HyprKeyboard::lastIpcObject() const {
return m_lastIpcObject.toVariantHash();
@@ -82,8 +81,7 @@ bool HyprKeyboard::updateLastIpcObject(QJsonObject object) {
return dirty;
}
HyprDevices::HyprDevices(QObject* parent)
: QObject(parent) {}
HyprDevices::HyprDevices(QObject* parent) : QObject(parent) {}
QQmlListProperty<HyprKeyboard> HyprDevices::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();) {
auto* const keyboard = *it;
const auto inNewValues = std::any_of(val.begin(), val.end(), [keyboard](const QJsonValue& o) {
return o.toObject().value("address").toString() == keyboard->address();
const auto inNewValues =
std::any_of(val.begin(), val.end(), [keyboard](const QJsonValue& o) {
return o.toObject().value("address").toString() ==
keyboard->address();
});
if (!inNewValues) {
@@ -112,9 +112,10 @@ bool HyprDevices::updateLastIpcObject(QJsonObject object) {
const auto obj = o.toObject();
const auto addr = obj.value("address").toString();
auto it = std::find_if(m_keyboards.begin(), m_keyboards.end(), [addr](const HyprKeyboard* kb) {
return kb->address() == addr;
});
auto it = std::find_if(
m_keyboards.begin(),
m_keyboards.end(),
[addr](const HyprKeyboard* kb) { return kb->address() == addr; });
if (it != m_keyboards.end()) {
dirty |= (*it)->updateLastIpcObject(obj);
+52 -47
View File
@@ -8,67 +8,72 @@
namespace ZShell::internal::hypr {
class HyprKeyboard : public QObject {
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("HyprKeyboard instances can only be retrieved from a HyprDevices")
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE(
"HyprKeyboard instances can only be retrieved from a HyprDevices")
Q_PROPERTY(QVariantHash lastIpcObject READ lastIpcObject NOTIFY lastIpcObjectChanged)
Q_PROPERTY(QString address READ address NOTIFY addressChanged)
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
Q_PROPERTY(QString layout READ layout NOTIFY layoutChanged)
Q_PROPERTY(QString activeKeymap READ activeKeymap NOTIFY activeKeymapChanged)
Q_PROPERTY(bool capsLock READ capsLock NOTIFY capsLockChanged)
Q_PROPERTY(bool numLock READ numLock NOTIFY numLockChanged)
Q_PROPERTY(bool main READ main NOTIFY mainChanged)
Q_PROPERTY(
QVariantHash lastIpcObject READ lastIpcObject NOTIFY
lastIpcObjectChanged)
Q_PROPERTY(QString address READ address NOTIFY addressChanged)
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
Q_PROPERTY(QString layout READ layout NOTIFY layoutChanged)
Q_PROPERTY(QString activeKeymap READ activeKeymap NOTIFY activeKeymapChanged)
Q_PROPERTY(bool capsLock READ capsLock NOTIFY capsLockChanged)
Q_PROPERTY(bool numLock READ numLock NOTIFY numLockChanged)
Q_PROPERTY(bool main READ main NOTIFY mainChanged)
public:
explicit HyprKeyboard(QJsonObject ipcObject, QObject* parent = nullptr);
public:
explicit HyprKeyboard(QJsonObject ipcObject, QObject* parent = nullptr);
[[nodiscard]] QVariantHash lastIpcObject() const;
[[nodiscard]] QString address() const;
[[nodiscard]] QString name() const;
[[nodiscard]] QString layout() const;
[[nodiscard]] QString activeKeymap() const;
[[nodiscard]] bool capsLock() const;
[[nodiscard]] bool numLock() const;
[[nodiscard]] bool main() const;
[[nodiscard]] QVariantHash lastIpcObject() const;
[[nodiscard]] QString address() const;
[[nodiscard]] QString name() const;
[[nodiscard]] QString layout() const;
[[nodiscard]] QString activeKeymap() const;
[[nodiscard]] bool capsLock() const;
[[nodiscard]] bool numLock() const;
[[nodiscard]] bool main() const;
bool updateLastIpcObject(QJsonObject object);
bool updateLastIpcObject(QJsonObject object);
signals:
void lastIpcObjectChanged();
void addressChanged();
void nameChanged();
void layoutChanged();
void activeKeymapChanged();
void capsLockChanged();
void numLockChanged();
void mainChanged();
signals:
void lastIpcObjectChanged();
void addressChanged();
void nameChanged();
void layoutChanged();
void activeKeymapChanged();
void capsLockChanged();
void numLockChanged();
void mainChanged();
private:
QJsonObject m_lastIpcObject;
private:
QJsonObject m_lastIpcObject;
};
class HyprDevices : public QObject {
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("HyprDevices instances can only be retrieved from a HyprExtras")
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE(
"HyprDevices instances can only be retrieved from a HyprExtras")
Q_PROPERTY(
QQmlListProperty<ZShell::internal::hypr::HyprKeyboard> keyboards READ keyboards NOTIFY keyboardsChanged)
Q_PROPERTY(
QQmlListProperty<ZShell::internal::hypr::HyprKeyboard> keyboards READ
keyboards NOTIFY keyboardsChanged)
public:
explicit HyprDevices(QObject* parent = nullptr);
public:
explicit HyprDevices(QObject* parent = nullptr);
[[nodiscard]] QQmlListProperty<HyprKeyboard> keyboards();
[[nodiscard]] QQmlListProperty<HyprKeyboard> keyboards();
bool updateLastIpcObject(QJsonObject object);
bool updateLastIpcObject(QJsonObject object);
signals:
void keyboardsChanged();
signals:
void keyboardsChanged();
private:
QList<HyprKeyboard*> m_keyboards;
private:
QList<HyprKeyboard*> m_keyboards;
};
} // namespace ZShell::internal::hypr
}
+86 -38
View File
@@ -64,7 +64,8 @@ static QString luaArray(const QVariantList& list) {
parts << luaValue(item);
}
return QLatin1String("{ ") + parts.join(QLatin1String(", ")) + QLatin1String(" }");
return QLatin1String("{ ") + parts.join(QLatin1String(", ")) +
QLatin1String(" }");
}
static QString luaArray(const QStringList& list) {
@@ -75,7 +76,8 @@ static QString luaArray(const QStringList& list) {
parts << luaEscapeString(item);
}
return QLatin1String("{ ") + parts.join(QLatin1String(", ")) + QLatin1String(" }");
return QLatin1String("{ ") + parts.join(QLatin1String(", ")) +
QLatin1String(" }");
}
static QString luaMapFromHash(const QVariantHash& hash) {
@@ -83,10 +85,12 @@ static QString luaMapFromHash(const QVariantHash& hash) {
parts.reserve(hash.size());
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) {
@@ -94,10 +98,12 @@ static QString luaMap(const QVariantMap& map) {
parts.reserve(map.size());
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) {
@@ -143,7 +149,8 @@ static QString normalizeOptionPath(QString key) {
}
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()) {
return {};
}
@@ -189,7 +196,8 @@ static QVariant parseGetOptionValue(const QJsonObject& obj) {
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));
}
@@ -234,7 +242,8 @@ static QVariant parseGetOptionValue(const QJsonObject& obj) {
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()) {
return;
}
@@ -261,16 +270,19 @@ HyprExtras::HyprExtras(QObject* parent)
, m_devices(new HyprDevices(this)) {
const auto his = qEnvironmentVariable("HYPRLAND_INSTANCE_SIGNATURE");
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;
}
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()) {
hyprDir = QStringLiteral("/tmp/hypr/") + his;
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;
}
}
@@ -283,9 +295,15 @@ HyprExtras::HyprExtras(QObject* parent)
m_socket = new QLocalSocket(this);
QObject::connect(m_socket, &QLocalSocket::errorOccurred, this, &HyprExtras::socketError);
QObject::connect(m_socket, &QLocalSocket::stateChanged, this, &HyprExtras::socketStateChanged);
QObject::connect(m_socket, &QLocalSocket::readyRead, this, &HyprExtras::readEvent);
QObject::connect(
m_socket, &QLocalSocket::errorOccurred, this, &HyprExtras::socketError);
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);
}
@@ -305,7 +323,8 @@ void HyprExtras::message(const QString& message) {
makeRequest(message, [](bool success, const QByteArray& res) {
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;
}
makeRequest(QStringLiteral("[[BATCH]]") + messages.join(QLatin1Char(';')),
makeRequest(
QStringLiteral("[[BATCH]]") + messages.join(QLatin1Char(';')),
[](bool success, const QByteArray& res) {
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;
}
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) {
refreshOptions();
} else {
qCWarning(lcHypr) << "applyOptions: request error" << QString::fromUtf8(res);
qCWarning(lcHypr)
<< "applyOptions: request error" << QString::fromUtf8(res);
}
});
}
@@ -372,7 +396,7 @@ void HyprExtras::refreshOptions() {
auto nextOptions = std::make_shared<QVariantMap>();
auto step = std::make_shared<std::function<void(int)> >();
auto step = std::make_shared<std::function<void(int)>>();
*step = [this, generation, nextOptions, step](int index) {
if (generation != m_optionsRefreshGeneration) {
return;
@@ -390,8 +414,8 @@ void HyprExtras::refreshOptions() {
m_optionsRefresh = makeRequestJson(
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();
if (generation != m_optionsRefreshGeneration) {
@@ -399,14 +423,21 @@ void HyprExtras::refreshOptions() {
}
if (success && response.isObject()) {
const QVariant value = parseGetOptionValue(response.object());
const QVariant value =
parseGetOptionValue(response.object());
if (value.isValid()) {
insertNestedValue(*nextOptions, key.split(QLatin1Char(':'), Qt::SkipEmptyParts), value);
insertNestedValue(
*nextOptions,
key.split(QLatin1Char(':'), Qt::SkipEmptyParts),
value);
} else {
qCWarning(lcHypr) << "refreshOptions: getoption returned no usable value for" << key;
qCWarning(lcHypr) << "refreshOptions: getoption "
"returned no usable value for"
<< key;
}
} else if (!success) {
qCWarning(lcHypr) << "refreshOptions: getoption request error for" << key;
qCWarning(lcHypr)
<< "refreshOptions: getoption request error for" << key;
}
(*step)(index + 1);
@@ -421,7 +452,9 @@ void HyprExtras::refreshDevices() {
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();
if (success) {
m_devices->updateLastIpcObject(response.object());
@@ -431,15 +464,19 @@ void HyprExtras::refreshDevices() {
void HyprExtras::socketError(QLocalSocket::LocalSocketError error) const {
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 {
qCWarning(lcHypr) << "socketError: Hyprland event socket error:" << error;
qCWarning(lcHypr) << "socketError: Hyprland event socket error:"
<< error;
}
}
void HyprExtras::socketStateChanged(QLocalSocket::LocalSocketState state) {
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;
@@ -452,7 +489,8 @@ void HyprExtras::readEvent() {
break;
}
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));
}
}
@@ -466,14 +504,18 @@ void HyprExtras::handleEvent(const QString& event) {
}
HyprExtras::SocketPtr HyprExtras::makeRequestJson(
const QString& request, const std::function<void(bool, QJsonDocument)>& callback) {
return makeRequest(QStringLiteral("j/") + request, [callback](bool success, const QByteArray& response) {
const QString& request,
const std::function<void(bool, QJsonDocument)>& callback) {
return makeRequest(
QStringLiteral("j/") + request,
[callback](bool success, const QByteArray& response) {
callback(success, QJsonDocument::fromJson(response));
});
}
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()) {
return SocketPtr();
}
@@ -481,7 +523,8 @@ HyprExtras::SocketPtr HyprExtras::makeRequest(
auto socket = SocketPtr::create(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();
callback(true, std::move(response));
socket->close();
@@ -491,8 +534,13 @@ HyprExtras::SocketPtr HyprExtras::makeRequest(
socket->flush();
});
QObject::connect(socket.data(), &QLocalSocket::errorOccurred, this, [=](QLocalSocket::LocalSocketError err) {
qCWarning(lcHypr) << "makeRequest: error making request:" << err << "| request:" << request;
QObject::connect(
socket.data(),
&QLocalSocket::errorOccurred,
this,
[=](QLocalSocket::LocalSocketError err) {
qCWarning(lcHypr) << "makeRequest: error making request:" << err
<< "| request:" << request;
callback(false, {});
socket->close();
});
+37 -32
View File
@@ -15,51 +15,56 @@ namespace ZShell::internal::hypr {
class HyprDevices;
class HyprExtras : public QObject {
Q_OBJECT
QML_ELEMENT
Q_OBJECT
QML_ELEMENT
Q_MOC_INCLUDE("hyprdevices.hpp")
Q_PROPERTY(QVariantMap options READ options NOTIFY optionsChanged)
Q_PROPERTY(ZShell::internal::hypr::HyprDevices* devices READ devices CONSTANT)
Q_PROPERTY(QVariantMap options READ options NOTIFY optionsChanged)
Q_PROPERTY(
ZShell::internal::hypr::HyprDevices* devices READ devices CONSTANT)
public:
explicit HyprExtras(QObject* parent = nullptr);
public:
explicit HyprExtras(QObject* parent = nullptr);
[[nodiscard]] QVariantMap options() const;
[[nodiscard]] HyprDevices* devices() const;
[[nodiscard]] QVariantMap options() const;
[[nodiscard]] HyprDevices* devices() const;
Q_INVOKABLE void message(const QString& message);
Q_INVOKABLE void batchMessage(const QStringList& messages);
Q_INVOKABLE void applyOptions(const QVariantHash& options);
Q_INVOKABLE void message(const QString& message);
Q_INVOKABLE void batchMessage(const QStringList& messages);
Q_INVOKABLE void applyOptions(const QVariantHash& options);
Q_INVOKABLE void refreshOptions();
Q_INVOKABLE void refreshDevices();
Q_INVOKABLE void refreshOptions();
Q_INVOKABLE void refreshDevices();
signals:
void optionsChanged();
signals:
void optionsChanged();
private:
using SocketPtr = QSharedPointer<QLocalSocket>;
private:
using SocketPtr = QSharedPointer<QLocalSocket>;
QString m_requestSocket;
QString m_eventSocket;
QLocalSocket* m_socket;
bool m_socketValid;
QString m_requestSocket;
QString m_eventSocket;
QLocalSocket* m_socket;
bool m_socketValid;
QVariantMap m_options;
HyprDevices* const m_devices;
QVariantMap m_options;
HyprDevices* const m_devices;
SocketPtr m_optionsRefresh;
SocketPtr m_devicesRefresh;
quint64 m_optionsRefreshGeneration = 0;
SocketPtr m_optionsRefresh;
SocketPtr m_devicesRefresh;
quint64 m_optionsRefreshGeneration = 0;
void socketError(QLocalSocket::LocalSocketError error) const;
void socketStateChanged(QLocalSocket::LocalSocketState state);
void readEvent();
void handleEvent(const QString& event);
void socketError(QLocalSocket::LocalSocketError error) const;
void socketStateChanged(QLocalSocket::LocalSocketState state);
void readEvent();
void handleEvent(const QString& event);
SocketPtr makeRequestJson(const QString& request, const std::function<void(bool, QJsonDocument)>& callback);
SocketPtr makeRequest(const QString& request, const std::function<void(bool, QByteArray)>& callback);
SocketPtr makeRequestJson(
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
+8 -4
View File
@@ -18,7 +18,8 @@ LidWatcher::LidWatcher(QObject* parent) : QObject(parent) {
return;
}
bool ok = bus.connect("org.freedesktop.login1",
bool ok = bus.connect(
"org.freedesktop.login1",
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
"PrepareForSleep",
@@ -31,7 +32,8 @@ LidWatcher::LidWatcher(QObject* parent) : QObject(parent) {
<< bus.lastError().message();
}
QDBusInterface login1("org.freedesktop.login1",
QDBusInterface login1(
"org.freedesktop.login1",
"/org/freedesktop/login1",
"org.freedesktop.login1.Manager",
bus);
@@ -42,7 +44,8 @@ LidWatcher::LidWatcher(QObject* parent) : QObject(parent) {
}
const auto sessionPath = reply.value().path();
ok = bus.connect("org.freedesktop.login1",
ok = bus.connect(
"org.freedesktop.login1",
sessionPath,
"org.freedesktop.login1.Session",
"Lock",
@@ -54,7 +57,8 @@ LidWatcher::LidWatcher(QObject* parent) : QObject(parent) {
<< "Failed to connect to Lock signal:" << bus.lastError().message();
}
ok = bus.connect("org.freedesktop.login1",
ok = bus.connect(
"org.freedesktop.login1",
sessionPath,
"org.freedesktop.login1.Session",
"Unlock",
+13 -13
View File
@@ -6,22 +6,22 @@
namespace ZShell::internal {
class LidWatcher : public QObject {
Q_OBJECT
QML_ELEMENT
Q_OBJECT
QML_ELEMENT
public:
explicit LidWatcher(QObject* parent = nullptr);
public:
explicit LidWatcher(QObject* parent = nullptr);
signals:
void aboutToSleep();
void resumed();
void lockRequested();
void unlockRequested();
signals:
void aboutToSleep();
void resumed();
void lockRequested();
void unlockRequested();
private slots:
void handlePrepareForSleep(bool sleep);
void handleLockRequested();
void handleUnlockRequested();
private slots:
void handlePrepareForSleep(bool sleep);
void handleLockRequested();
void handleUnlockRequested();
};
} // namespace ZShell::internal
@@ -5,12 +5,12 @@
namespace {
constexpr int TOTAL_DURATION_IN_MS = 1800;
constexpr std::array DURATION_TO_MOVE_SEGMENT_ENDS = { 533, 567, 850, 750 };
constexpr std::array DELAY_TO_MOVE_SEGMENT_ENDS = { 1267, 1000, 333, 0 };
constexpr std::array DURATION_TO_MOVE_SEGMENT_ENDS = {533, 567, 850, 750};
constexpr std::array DELAY_TO_MOVE_SEGMENT_ENDS = {1267, 1000, 333, 0};
QEasingCurve curve(const QPointF& c1, const QPointF& c2) {
QEasingCurve curve(QEasingCurve::BezierSpline);
curve.addCubicBezierSegment(c1, c2, { 1.0, 1.0 });
curve.addCubicBezierSegment(c1, c2, {1.0, 1.0});
return curve;
}
@@ -24,11 +24,7 @@ qreal getFractionInRange(qreal playtime, int start, int duration) {
namespace ZShell::controls {
LinearIndicatorSegment::LinearIndicatorSegment(int gap, QObject* parent)
: QObject(parent)
, m_startFraction(0)
, m_endFraction(0)
, m_gapSize(gap) {
}
: QObject(parent), m_startFraction(0), m_endFraction(0), m_gapSize(gap) {}
qreal LinearIndicatorSegment::startFraction() const {
return m_startFraction;
@@ -45,10 +41,10 @@ int LinearIndicatorSegment::gapSize() const {
LinearIndicatorManager::LinearIndicatorManager(QObject* parent)
: QObject(parent)
, m_interpolators({
curve({ 0.2, 0.0 }, { 0.8, 1.0 }),
curve({ 0.4, 0.0 }, { 1.0, 1.0 }),
curve({ 0.0, 0.0 }, { 0.65, 1.0 }),
curve({ 0.1, 0.0 }, { 0.45, 1.0 }),
curve({0.2, 0.0}, {0.8, 1.0}),
curve({0.4, 0.0}, {1.0, 1.0}),
curve({0.0, 0.0}, {0.65, 1.0}),
curve({0.1, 0.0}, {0.45, 1.0}),
})
, m_progress(0)
, m_completeEndProgress(0)
@@ -58,11 +54,15 @@ LinearIndicatorManager::LinearIndicatorManager(QObject* parent)
new LinearIndicatorSegment(m_gap, this),
}) {
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 {
return { m_activeIndicators.cbegin(), m_activeIndicators.cend() };
return {m_activeIndicators.cbegin(), m_activeIndicators.cend()};
}
qreal LinearIndicatorManager::progress() const {
@@ -98,12 +98,19 @@ void LinearIndicatorManager::update(qreal progress) {
const auto di = i * 2;
auto* const indicator = m_activeIndicators[i];
auto fraction = getFractionInRange(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);
auto fraction = getFractionInRange(
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 =
getFractionInRange(playtime, 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);
fraction = getFractionInRange(
playtime,
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;
@@ -11,76 +11,80 @@ namespace ZShell::controls {
class LinearIndicatorManager;
class LinearIndicatorSegment : public QObject {
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("LinearIndicatorSegments can only be retrieved from a "
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE(
"LinearIndicatorSegments can only be retrieved from a "
"LinearIndicatorManager.")
Q_PROPERTY(qreal startFraction READ startFraction NOTIFY updated FINAL)
Q_PROPERTY(qreal endFraction READ endFraction NOTIFY updated FINAL)
Q_PROPERTY(int gapSize READ gapSize NOTIFY updated FINAL)
Q_PROPERTY(qreal startFraction READ startFraction NOTIFY updated FINAL)
Q_PROPERTY(qreal endFraction READ endFraction NOTIFY updated FINAL)
Q_PROPERTY(int gapSize READ gapSize NOTIFY updated FINAL)
public:
explicit LinearIndicatorSegment(int gap, QObject* parent = nullptr);
public:
explicit LinearIndicatorSegment(int gap, QObject* parent = nullptr);
qreal startFraction() const;
qreal endFraction() const;
int gapSize() const;
qreal startFraction() const;
qreal endFraction() const;
int gapSize() const;
signals:
void updated();
signals:
void updated();
private:
qreal m_startFraction;
qreal m_endFraction;
int m_gapSize;
private:
qreal m_startFraction;
qreal m_endFraction;
int m_gapSize;
friend LinearIndicatorManager;
friend LinearIndicatorManager;
};
class LinearIndicatorManager : public QObject {
Q_OBJECT
QML_ELEMENT
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(
QList<ZShell::controls::LinearIndicatorSegment*> activeIndicators READ activeIndicators CONSTANT FINAL)
Q_PROPERTY(
QList<ZShell::controls::LinearIndicatorSegment*> activeIndicators READ
activeIndicators CONSTANT 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(int gap READ gap WRITE setGap 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(int gap READ gap WRITE setGap NOTIFY updated FINAL)
Q_PROPERTY(qreal duration READ duration CONSTANT FINAL)
Q_PROPERTY(qreal completeEndDuration READ completeEndDuration CONSTANT FINAL)
Q_PROPERTY(qreal duration READ duration CONSTANT FINAL)
Q_PROPERTY(qreal completeEndDuration READ completeEndDuration CONSTANT FINAL)
public:
explicit LinearIndicatorManager(QObject* parent = nullptr);
public:
explicit LinearIndicatorManager(QObject* parent = nullptr);
QList<LinearIndicatorSegment*> activeIndicators() const;
QList<LinearIndicatorSegment*> activeIndicators() const;
qreal progress() const;
qreal completeEndProgress() const;
qreal progress() const;
qreal completeEndProgress() const;
int gap() const;
void setGap(int gap);
int gap() const;
void setGap(int gap);
int duration() const;
int completeEndDuration() const;
int duration() const;
int completeEndDuration() const;
void update(qreal progress);
void updateCompleteEndProgress(qreal progress);
void update(qreal progress);
void updateCompleteEndProgress(qreal progress);
signals:
void updated();
signals:
void updated();
private:
static constexpr int SEGMENTS = 2;
private:
static constexpr int SEGMENTS = 2;
std::array<QEasingCurve, 4> m_interpolators;
qreal m_progress;
qreal m_completeEndProgress;
int m_gap;
std::array<QEasingCurve, 4> m_interpolators;
qreal m_progress;
qreal m_completeEndProgress;
int m_gap;
std::array<LinearIndicatorSegment*, SEGMENTS> m_activeIndicators;
std::array<LinearIndicatorSegment*, SEGMENTS> m_activeIndicators;
};
} // namespace ZShell::controls
+26 -40
View File
@@ -7,38 +7,37 @@
namespace ZShell::internal {
SparklineItem::SparklineItem(QQuickItem* parent)
: QQuickPaintedItem(parent) {
SparklineItem::SparklineItem(QQuickItem* parent) : QQuickPaintedItem(parent) {
setAntialiasing(true);
}
void SparklineItem::paint(QPainter* painter) {
const bool has1 = m_line1 && m_line1->count() >= 2;
const bool has2 = m_line2 && m_line2->count() >= 2;
if (!has1 && !has2)
return;
if (!has1 && !has2) return;
painter->setRenderHint(QPainter::Antialiasing, true);
// Draw line1 first (behind), then line2 (in front)
if (has1)
drawLine(painter, m_line1, m_line1Color, m_line1FillAlpha);
if (has2)
drawLine(painter, m_line2, m_line2Color, m_line2FillAlpha);
if (has1) drawLine(painter, m_line1, m_line1Color, m_line1FillAlpha);
if (has2) drawLine(painter, m_line2, m_line2Color, m_line2FillAlpha);
}
void SparklineItem::drawLine(QPainter* painter, CircularBuffer* buffer, const QColor& color, qreal fillAlpha) {
if (m_historyLength < 2)
return;
void SparklineItem::drawLine(
QPainter* painter,
CircularBuffer* buffer,
const QColor& color,
qreal fillAlpha) {
if (m_historyLength < 2) return;
const qreal w = width();
const qreal h = height();
const int len = buffer->count();
if (len < 2 || m_maxValue <= 0.0)
return;
if (len < 2 || m_maxValue <= 0.0) return;
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 curvePad = 3.0;
@@ -93,8 +92,7 @@ void SparklineItem::drawLine(QPainter* painter, CircularBuffer* buffer, const QC
}
void SparklineItem::connectBuffer(CircularBuffer* buffer) {
if (!buffer)
return;
if (!buffer) return;
connect(buffer, &CircularBuffer::valuesChanged, this, [this]() {
update();
@@ -117,10 +115,8 @@ CircularBuffer* SparklineItem::line1() const {
}
void SparklineItem::setLine1(CircularBuffer* buffer) {
if (m_line1 == buffer)
return;
if (m_line1)
disconnect(m_line1, nullptr, this, nullptr);
if (m_line1 == buffer) return;
if (m_line1) disconnect(m_line1, nullptr, this, nullptr);
m_line1 = buffer;
connectBuffer(buffer);
emit line1Changed();
@@ -132,10 +128,8 @@ CircularBuffer* SparklineItem::line2() const {
}
void SparklineItem::setLine2(CircularBuffer* buffer) {
if (m_line2 == buffer)
return;
if (m_line2)
disconnect(m_line2, nullptr, this, nullptr);
if (m_line2 == buffer) return;
if (m_line2) disconnect(m_line2, nullptr, this, nullptr);
m_line2 = buffer;
connectBuffer(buffer);
emit line2Changed();
@@ -147,8 +141,7 @@ QColor SparklineItem::line1Color() const {
}
void SparklineItem::setLine1Color(const QColor& color) {
if (m_line1Color == color)
return;
if (m_line1Color == color) return;
m_line1Color = color;
emit line1ColorChanged();
update();
@@ -159,8 +152,7 @@ QColor SparklineItem::line2Color() const {
}
void SparklineItem::setLine2Color(const QColor& color) {
if (m_line2Color == color)
return;
if (m_line2Color == color) return;
m_line2Color = color;
emit line2ColorChanged();
update();
@@ -171,8 +163,7 @@ qreal SparklineItem::line1FillAlpha() const {
}
void SparklineItem::setLine1FillAlpha(qreal alpha) {
if (qFuzzyCompare(m_line1FillAlpha, alpha))
return;
if (qFuzzyCompare(m_line1FillAlpha, alpha)) return;
m_line1FillAlpha = alpha;
emit line1FillAlphaChanged();
update();
@@ -183,8 +174,7 @@ qreal SparklineItem::line2FillAlpha() const {
}
void SparklineItem::setLine2FillAlpha(qreal alpha) {
if (qFuzzyCompare(m_line2FillAlpha, alpha))
return;
if (qFuzzyCompare(m_line2FillAlpha, alpha)) return;
m_line2FillAlpha = alpha;
emit line2FillAlphaChanged();
update();
@@ -195,8 +185,7 @@ qreal SparklineItem::maxValue() const {
}
void SparklineItem::setMaxValue(qreal value) {
if (qFuzzyCompare(m_maxValue, value))
return;
if (qFuzzyCompare(m_maxValue, value)) return;
m_maxValue = value;
emit maxValueChanged();
update();
@@ -207,8 +196,7 @@ qreal SparklineItem::slideProgress() const {
}
void SparklineItem::setSlideProgress(qreal progress) {
if (qFuzzyCompare(m_slideProgress, progress))
return;
if (qFuzzyCompare(m_slideProgress, progress)) return;
m_slideProgress = progress;
emit slideProgressChanged();
update();
@@ -219,8 +207,7 @@ int SparklineItem::historyLength() const {
}
void SparklineItem::setHistoryLength(int length) {
if (m_historyLength == length)
return;
if (m_historyLength == length) return;
m_historyLength = length;
emit historyLengthChanged();
update();
@@ -231,8 +218,7 @@ qreal SparklineItem::lineWidth() const {
}
void SparklineItem::setLineWidth(qreal width) {
if (qFuzzyCompare(m_lineWidth, width))
return;
if (qFuzzyCompare(m_lineWidth, width)) return;
m_lineWidth = width;
emit lineWidthChanged();
update();
+80 -59
View File
@@ -10,81 +10,102 @@
namespace ZShell::internal {
class SparklineItem : public QQuickPaintedItem {
Q_OBJECT
QML_ELEMENT
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(CircularBuffer* line1 READ line1 WRITE setLine1 NOTIFY line1Changed)
Q_PROPERTY(CircularBuffer* line2 READ line2 WRITE setLine2 NOTIFY line2Changed)
Q_PROPERTY(QColor line1Color READ line1Color WRITE setLine1Color NOTIFY line1ColorChanged)
Q_PROPERTY(QColor line2Color READ line2Color WRITE setLine2Color NOTIFY 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)
Q_PROPERTY(
CircularBuffer* line1 READ line1 WRITE setLine1 NOTIFY line1Changed)
Q_PROPERTY(
CircularBuffer* line2 READ line2 WRITE setLine2 NOTIFY line2Changed)
Q_PROPERTY(
QColor line1Color READ line1Color WRITE setLine1Color NOTIFY
line1ColorChanged)
Q_PROPERTY(
QColor line2Color READ line2Color WRITE setLine2Color NOTIFY
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:
explicit SparklineItem(QQuickItem* parent = nullptr);
public:
explicit SparklineItem(QQuickItem* parent = nullptr);
void paint(QPainter* painter) override;
void paint(QPainter* painter) override;
[[nodiscard]] CircularBuffer* line1() const;
void setLine1(CircularBuffer* buffer);
[[nodiscard]] CircularBuffer* line1() const;
void setLine1(CircularBuffer* buffer);
[[nodiscard]] CircularBuffer* line2() const;
void setLine2(CircularBuffer* buffer);
[[nodiscard]] CircularBuffer* line2() const;
void setLine2(CircularBuffer* buffer);
[[nodiscard]] QColor line1Color() const;
void setLine1Color(const QColor& color);
[[nodiscard]] QColor line1Color() const;
void setLine1Color(const QColor& color);
[[nodiscard]] QColor line2Color() const;
void setLine2Color(const QColor& color);
[[nodiscard]] QColor line2Color() const;
void setLine2Color(const QColor& color);
[[nodiscard]] qreal line1FillAlpha() const;
void setLine1FillAlpha(qreal alpha);
[[nodiscard]] qreal line1FillAlpha() const;
void setLine1FillAlpha(qreal alpha);
[[nodiscard]] qreal line2FillAlpha() const;
void setLine2FillAlpha(qreal alpha);
[[nodiscard]] qreal line2FillAlpha() const;
void setLine2FillAlpha(qreal alpha);
[[nodiscard]] qreal maxValue() const;
void setMaxValue(qreal value);
[[nodiscard]] qreal maxValue() const;
void setMaxValue(qreal value);
[[nodiscard]] qreal slideProgress() const;
void setSlideProgress(qreal progress);
[[nodiscard]] qreal slideProgress() const;
void setSlideProgress(qreal progress);
[[nodiscard]] int historyLength() const;
void setHistoryLength(int length);
[[nodiscard]] int historyLength() const;
void setHistoryLength(int length);
[[nodiscard]] qreal lineWidth() const;
void setLineWidth(qreal width);
[[nodiscard]] qreal lineWidth() const;
void setLineWidth(qreal width);
signals:
void line1Changed();
void line2Changed();
void line1ColorChanged();
void line2ColorChanged();
void line1FillAlphaChanged();
void line2FillAlphaChanged();
void maxValueChanged();
void slideProgressChanged();
void historyLengthChanged();
void lineWidthChanged();
signals:
void line1Changed();
void line2Changed();
void line1ColorChanged();
void line2ColorChanged();
void line1FillAlphaChanged();
void line2FillAlphaChanged();
void maxValueChanged();
void slideProgressChanged();
void historyLengthChanged();
void lineWidthChanged();
private:
void drawLine(QPainter* painter, CircularBuffer* buffer, const QColor& color, qreal fillAlpha);
void connectBuffer(CircularBuffer* buffer);
private:
void drawLine(
QPainter* painter,
CircularBuffer* buffer,
const QColor& color,
qreal fillAlpha);
void connectBuffer(CircularBuffer* buffer);
CircularBuffer* m_line1 = nullptr;
CircularBuffer* m_line2 = nullptr;
QColor m_line1Color;
QColor m_line2Color;
qreal m_line1FillAlpha = 0.15;
qreal m_line2FillAlpha = 0.2;
qreal m_maxValue = 1024.0;
qreal m_slideProgress = 0.0;
int m_historyLength = 30;
qreal m_lineWidth = 2.0;
CircularBuffer* m_line1 = nullptr;
CircularBuffer* m_line2 = nullptr;
QColor m_line1Color;
QColor m_line2Color;
qreal m_line1FillAlpha = 0.15;
qreal m_line2FillAlpha = 0.2;
qreal m_maxValue = 1024.0;
qreal m_slideProgress = 0.0;
int m_historyLength = 30;
qreal m_lineWidth = 2.0;
};
} // namespace ZShell::internal
+12 -23
View File
@@ -9,14 +9,12 @@
namespace ZShell::internal {
VisualizerBars::VisualizerBars(QQuickItem* parent)
: QQuickPaintedItem(parent) {
VisualizerBars::VisualizerBars(QQuickItem* parent) : QQuickPaintedItem(parent) {
setAntialiasing(true);
}
void VisualizerBars::advance(qreal dt) {
if (m_displayValues.isEmpty() || m_settled)
return;
if (m_displayValues.isEmpty() || m_settled) return;
// dt is in seconds (from FrameAnimation.frameTime), convert to ms
const qreal dtMs = dt * 1000.0;
@@ -45,8 +43,7 @@ void VisualizerBars::advance(qreal dt) {
}
void VisualizerBars::paint(QPainter* painter) {
if (m_displayValues.isEmpty())
return;
if (m_displayValues.isEmpty()) return;
painter->setRenderHint(QPainter::Antialiasing, true);
painter->setPen(Qt::NoPen);
@@ -68,15 +65,13 @@ void VisualizerBars::drawSide(QPainter* painter, bool rightSide) {
const qreal h = height();
const auto count = m_displayValues.size();
if (count == 0)
return;
if (count == 0) return;
const qreal sideWidth = w * 0.4;
const qreal slotWidth = sideWidth / static_cast<qreal>(count);
const qreal barWidth = slotWidth - m_spacing;
if (barWidth <= 0)
return;
if (barWidth <= 0) return;
const qreal sideOffset = rightSide ? w * 0.6 : 0;
const qreal maxBarHeight = h * 0.4;
@@ -86,12 +81,11 @@ void VisualizerBars::drawSide(QPainter* painter, bool rightSide) {
const qreal value = std::clamp(m_displayValues[valueIndex], 0.0, 1.0);
const qreal barHeight = value * maxBarHeight;
if (barHeight <= 0)
continue;
if (barHeight <= 0) continue;
const qreal x = static_cast<qreal>(i) * slotWidth + sideOffset;
const qreal y = h - barHeight;
const qreal r = std::min({ m_rounding, barWidth / 2.0, barHeight });
const qreal r = std::min({m_rounding, barWidth / 2.0, barHeight});
QPainterPath path;
path.moveTo(x, h);
@@ -141,8 +135,7 @@ QColor VisualizerBars::primaryColor() const {
}
void VisualizerBars::setPrimaryColor(const QColor& color) {
if (m_primaryColor == color)
return;
if (m_primaryColor == color) return;
m_primaryColor = color;
emit primaryColorChanged();
update();
@@ -153,8 +146,7 @@ QColor VisualizerBars::secondaryColor() const {
}
void VisualizerBars::setSecondaryColor(const QColor& color) {
if (m_secondaryColor == color)
return;
if (m_secondaryColor == color) return;
m_secondaryColor = color;
emit secondaryColorChanged();
update();
@@ -165,8 +157,7 @@ qreal VisualizerBars::rounding() const {
}
void VisualizerBars::setRounding(qreal rounding) {
if (qFuzzyCompare(m_rounding, rounding))
return;
if (qFuzzyCompare(m_rounding, rounding)) return;
m_rounding = rounding;
emit roundingChanged();
update();
@@ -177,8 +168,7 @@ qreal VisualizerBars::spacing() const {
}
void VisualizerBars::setSpacing(qreal spacing) {
if (qFuzzyCompare(m_spacing, spacing))
return;
if (qFuzzyCompare(m_spacing, spacing)) return;
m_spacing = spacing;
emit spacingChanged();
update();
@@ -189,8 +179,7 @@ int VisualizerBars::animationDuration() const {
}
void VisualizerBars::setAnimationDuration(int duration) {
if (m_animationDuration == duration)
return;
if (m_animationDuration == duration) return;
m_animationDuration = duration;
emit animationDurationChanged();
}
+52 -44
View File
@@ -9,64 +9,72 @@
namespace ZShell::internal {
class VisualizerBars : public QQuickPaintedItem {
Q_OBJECT
QML_ELEMENT
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(QVector<double> values READ values WRITE setValues NOTIFY valuesChanged)
Q_PROPERTY(QColor primaryColor READ primaryColor WRITE setPrimaryColor NOTIFY primaryColorChanged)
Q_PROPERTY(QColor secondaryColor READ secondaryColor WRITE setSecondaryColor NOTIFY secondaryColorChanged)
Q_PROPERTY(qreal rounding READ rounding WRITE setRounding NOTIFY roundingChanged)
Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing NOTIFY spacingChanged)
Q_PROPERTY(int animationDuration READ animationDuration WRITE setAnimationDuration NOTIFY animationDurationChanged)
Q_PROPERTY(bool settled READ settled NOTIFY settledChanged)
Q_PROPERTY(
QVector<double> values READ values WRITE setValues NOTIFY valuesChanged)
Q_PROPERTY(
QColor primaryColor READ primaryColor WRITE setPrimaryColor NOTIFY
primaryColorChanged)
Q_PROPERTY(
QColor secondaryColor READ secondaryColor WRITE setSecondaryColor NOTIFY
secondaryColorChanged)
Q_PROPERTY(
qreal rounding READ rounding WRITE setRounding NOTIFY roundingChanged)
Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing NOTIFY spacingChanged)
Q_PROPERTY(
int animationDuration READ animationDuration WRITE setAnimationDuration
NOTIFY animationDurationChanged)
Q_PROPERTY(bool settled READ settled NOTIFY settledChanged)
public:
explicit VisualizerBars(QQuickItem* parent = nullptr);
public:
explicit VisualizerBars(QQuickItem* parent = nullptr);
void paint(QPainter* painter) override;
void paint(QPainter* painter) override;
Q_INVOKABLE void advance(qreal dt);
Q_INVOKABLE void advance(qreal dt);
[[nodiscard]] QVector<double> values() const;
void setValues(const QVector<double>& values);
[[nodiscard]] QVector<double> values() const;
void setValues(const QVector<double>& values);
[[nodiscard]] QColor primaryColor() const;
void setPrimaryColor(const QColor& color);
[[nodiscard]] QColor primaryColor() const;
void setPrimaryColor(const QColor& color);
[[nodiscard]] QColor secondaryColor() const;
void setSecondaryColor(const QColor& color);
[[nodiscard]] QColor secondaryColor() const;
void setSecondaryColor(const QColor& color);
[[nodiscard]] qreal rounding() const;
void setRounding(qreal rounding);
[[nodiscard]] qreal rounding() const;
void setRounding(qreal rounding);
[[nodiscard]] qreal spacing() const;
void setSpacing(qreal spacing);
[[nodiscard]] qreal spacing() const;
void setSpacing(qreal spacing);
[[nodiscard]] int animationDuration() const;
void setAnimationDuration(int duration);
[[nodiscard]] int animationDuration() const;
void setAnimationDuration(int duration);
[[nodiscard]] bool settled() const;
[[nodiscard]] bool settled() const;
signals:
void valuesChanged();
void primaryColorChanged();
void secondaryColorChanged();
void roundingChanged();
void spacingChanged();
void animationDurationChanged();
void settledChanged();
signals:
void valuesChanged();
void primaryColorChanged();
void secondaryColorChanged();
void roundingChanged();
void spacingChanged();
void animationDurationChanged();
void settledChanged();
private:
void drawSide(QPainter* painter, bool rightSide);
private:
void drawSide(QPainter* painter, bool rightSide);
QVector<double> m_targetValues;
QVector<double> m_displayValues;
QColor m_primaryColor;
QColor m_secondaryColor;
qreal m_rounding = 0.0;
qreal m_spacing = 0.0;
int m_animationDuration = 200;
bool m_settled = true;
QVector<double> m_targetValues;
QVector<double> m_displayValues;
QColor m_primaryColor;
QColor m_secondaryColor;
qreal m_rounding = 0.0;
qreal m_spacing = 0.0;
int m_animationDuration = 200;
bool m_settled = true;
};
} // namespace ZShell::internal
+38 -27
View File
@@ -7,15 +7,16 @@
#include <QtConcurrent>
#include <QSGImageNode>
#include <QQuickWindow>
#include <set>
namespace ZShell::internal {
WallpaperImage::WallpaperImage(QQuickItem *parent)
: QQuickItem(parent)
{
WallpaperImage::WallpaperImage(QQuickItem* parent) : QQuickItem(parent) {
setFlag(ItemHasContents, true);
connect(&m_imageWatcher, &QFutureWatcher<QImage>::finished, this, &WallpaperImage::handleImageLoaded);
connect(
&m_imageWatcher,
&QFutureWatcher<QImage>::finished,
this,
&WallpaperImage::handleImageLoaded);
}
WallpaperImage::~WallpaperImage() {
@@ -23,14 +24,13 @@ WallpaperImage::~WallpaperImage() {
}
void WallpaperImage::setStatus(const Status s) {
if (m_status == s)
return;
if (m_status == s) return;
m_status = s;
emit statusChanged();
}
void WallpaperImage::setSource(const QUrl &source) {
void WallpaperImage::setSource(const QUrl& source) {
if (m_source == source) return;
m_source = source;
emit sourceChanged();
@@ -39,7 +39,7 @@ void WallpaperImage::setSource(const QUrl &source) {
loadImage();
}
void WallpaperImage::setScreenResolution(const QSize &screenResolution) {
void WallpaperImage::setScreenResolution(const QSize& screenResolution) {
if (m_screenResolution == screenResolution) return;
m_screenResolution = screenResolution;
emit screenResolutionChanged();
@@ -86,11 +86,16 @@ void WallpaperImage::setCropHeight(qreal h) {
QString WallpaperImage::getCacheFilePath() const {
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);
QString id = m_source.toString() + "_" + QString::number(m_screenResolution.width()) + "x" + QString::number(m_screenResolution.height());
QByteArray hash = QCryptographicHash::hash(id.toUtf8(), QCryptographicHash::Md5).toHex();
QString id = m_source.toString() + "_" +
QString::number(m_screenResolution.width()) + "x" +
QString::number(m_screenResolution.height());
QByteArray hash =
QCryptographicHash::hash(id.toUtf8(), QCryptographicHash::Md5).toHex();
return cachePath + "/" + hash + ".png";
}
@@ -102,7 +107,8 @@ void WallpaperImage::loadImage() {
}
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:/")) {
sourceFile = sourceFile.mid(3);
@@ -110,8 +116,10 @@ void WallpaperImage::loadImage() {
QSize targetRes = m_screenResolution;
QFuture<QImage> future = QtConcurrent::run([sourceFile, cacheFile, targetRes]() -> QImage {
if (!targetRes.isEmpty() && !cacheFile.isEmpty() && QFileInfo::exists(cacheFile)) {
QFuture<QImage> future =
QtConcurrent::run([sourceFile, cacheFile, targetRes]() -> QImage {
if (!targetRes.isEmpty() && !cacheFile.isEmpty() &&
QFileInfo::exists(cacheFile)) {
QImage cached(cacheFile);
if (!cached.isNull()) return cached;
}
@@ -123,8 +131,12 @@ void WallpaperImage::loadImage() {
return original;
}
if (original.width() > targetRes.width() || original.height() > targetRes.height()) {
QImage scaled = original.scaled(targetRes, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
if (original.width() > targetRes.width() ||
original.height() > targetRes.height()) {
QImage scaled = original.scaled(
targetRes,
Qt::KeepAspectRatioByExpanding,
Qt::SmoothTransformation);
if (!cacheFile.isEmpty()) scaled.save(cacheFile, "PNG");
return scaled;
}
@@ -145,8 +157,9 @@ void WallpaperImage::handleImageLoaded() {
update();
}
QSGNode *WallpaperImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) {
auto *node = static_cast<QSGImageNode *>(oldNode);
QSGNode* WallpaperImage::updatePaintNode(
QSGNode* oldNode, UpdatePaintNodeData*) {
auto* node = static_cast<QSGImageNode*>(oldNode);
if (m_image.isNull()) {
delete node;
@@ -159,7 +172,8 @@ QSGNode *WallpaperImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
if (m_textureDirty) {
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;
}
@@ -175,8 +189,7 @@ QSGNode *WallpaperImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
m_cropX * m_texture->textureSize().width(),
m_cropY * m_texture->textureSize().height(),
cW * m_texture->textureSize().width(),
cH * m_texture->textureSize().height()
);
cH * m_texture->textureSize().height());
QRectF bounds = boundingRect();
if (bounds.isEmpty() || reqRect.isEmpty()) return node;
@@ -202,12 +215,11 @@ QSGNode *WallpaperImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
sourceRect.x() / m_texture->textureSize().width(),
sourceRect.y() / m_texture->textureSize().height(),
sourceRect.width() / m_texture->textureSize().width(),
sourceRect.height() / m_texture->textureSize().height()
);
sourceRect.height() / m_texture->textureSize().height());
bool changed = false;
auto updateIfChanged = [&](qreal &dst, qreal value) {
auto updateIfChanged = [&](qreal& dst, qreal value) {
if (!qFuzzyCompare(dst, value)) {
dst = value;
changed = true;
@@ -219,8 +231,7 @@ QSGNode *WallpaperImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
updateIfChanged(m_actualCropWidth, normalizedActual.width());
updateIfChanged(m_actualCropHeight, normalizedActual.height());
if (changed)
emit actualCropChanged();
if (changed) emit actualCropChanged();
node->setSourceRect(sourceRect);
}
+80 -100
View File
@@ -11,129 +11,109 @@
namespace ZShell::internal {
class WallpaperImage : public QQuickItem {
Q_OBJECT
QML_NAMED_ELEMENT(WallpaperImage)
Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
Q_PROPERTY(QSize screenResolution READ screenResolution WRITE setScreenResolution NOTIFY screenResolutionChanged)
Q_PROPERTY(qreal zoom READ zoom WRITE setZoom NOTIFY zoomChanged)
Q_PROPERTY(Status status READ status NOTIFY statusChanged)
Q_OBJECT
QML_NAMED_ELEMENT(WallpaperImage)
Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
Q_PROPERTY(
QSize screenResolution READ screenResolution WRITE setScreenResolution
NOTIFY screenResolutionChanged)
Q_PROPERTY(qreal zoom READ zoom WRITE setZoom NOTIFY zoomChanged)
Q_PROPERTY(Status status READ status NOTIFY statusChanged)
Q_PROPERTY(qreal actualCropX READ actualCropX NOTIFY actualCropChanged)
Q_PROPERTY(qreal actualCropY READ actualCropY NOTIFY actualCropChanged)
Q_PROPERTY(qreal actualCropWidth READ actualCropWidth NOTIFY actualCropChanged)
Q_PROPERTY(qreal actualCropHeight READ actualCropHeight NOTIFY actualCropChanged)
Q_PROPERTY(qreal actualCropX READ actualCropX NOTIFY actualCropChanged)
Q_PROPERTY(qreal actualCropY READ actualCropY NOTIFY actualCropChanged)
Q_PROPERTY(
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 cropY READ cropY WRITE setCropY NOTIFY cropYChanged)
Q_PROPERTY(qreal cropWidth READ cropWidth WRITE setCropWidth NOTIFY cropWidthChanged)
Q_PROPERTY(qreal cropHeight READ cropHeight WRITE setCropHeight NOTIFY cropHeightChanged)
Q_PROPERTY(qreal cropX READ cropX WRITE setCropX NOTIFY cropXChanged)
Q_PROPERTY(qreal cropY READ cropY WRITE setCropY NOTIFY cropYChanged)
Q_PROPERTY(
qreal cropWidth READ cropWidth WRITE setCropWidth NOTIFY
cropWidthChanged)
Q_PROPERTY(
qreal cropHeight READ cropHeight WRITE setCropHeight NOTIFY
cropHeightChanged)
public:
explicit WallpaperImage(QQuickItem *parent = nullptr);
~WallpaperImage() override;
public:
explicit WallpaperImage(QQuickItem* parent = nullptr);
~WallpaperImage() override;
enum Status {
Null,
Ready,
Loading,
Error
};
Q_ENUM(Status)
enum Status { Null, Ready, Loading, Error };
Q_ENUM(Status)
Status status() const {
return m_status;
}
Status status() const { return m_status; }
QUrl source() const {
return m_source;
}
void setSource(const QUrl &source);
QUrl source() const { return m_source; }
void setSource(const QUrl& source);
QSize screenResolution() const {
return m_screenResolution;
}
void setScreenResolution(const QSize &screenResolution);
QSize screenResolution() const { return m_screenResolution; }
void setScreenResolution(const QSize& screenResolution);
qreal zoom() const {
return m_zoom;
}
void setZoom(qreal zoom);
qreal zoom() const { return m_zoom; }
void setZoom(qreal zoom);
qreal actualCropX() const {
return m_actualCropX;
}
qreal actualCropX() const { return m_actualCropX; }
qreal actualCropY() const {
return m_actualCropY;
}
qreal actualCropY() const { return m_actualCropY; }
qreal actualCropWidth() const {
return m_actualCropWidth;
}
qreal actualCropWidth() const { return m_actualCropWidth; }
qreal actualCropHeight() const {
return m_actualCropHeight;
}
qreal actualCropHeight() const { return m_actualCropHeight; }
qreal cropX() const {
return m_cropX;
}
void setCropX(qreal x);
qreal cropX() const { return m_cropX; }
void setCropX(qreal x);
qreal cropY() const {
return m_cropY;
}
void setCropY(qreal y);
qreal cropY() const { return m_cropY; }
void setCropY(qreal y);
qreal cropWidth() const {
return m_cropWidth;
}
void setCropWidth(qreal w);
qreal cropWidth() const { return m_cropWidth; }
void setCropWidth(qreal w);
qreal cropHeight() const {
return m_cropHeight;
}
void setCropHeight(qreal h);
qreal cropHeight() const { return m_cropHeight; }
void setCropHeight(qreal h);
protected:
QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData) override;
protected:
QSGNode* updatePaintNode(
QSGNode* oldNode, UpdatePaintNodeData* updatePaintNodeData) override;
signals:
void sourceChanged();
void screenResolutionChanged();
void zoomChanged();
void actualCropChanged();
void cropXChanged();
void cropYChanged();
void cropWidthChanged();
void cropHeightChanged();
void statusChanged();
signals:
void sourceChanged();
void screenResolutionChanged();
void zoomChanged();
void actualCropChanged();
void cropXChanged();
void cropYChanged();
void cropWidthChanged();
void cropHeightChanged();
void statusChanged();
private:
void loadImage();
void handleImageLoaded();
QString getCacheFilePath() const;
void setStatus(const Status s);
private:
void loadImage();
void handleImageLoaded();
QString getCacheFilePath() const;
void setStatus(const Status s);
Status m_status = Null;
QUrl m_source;
QSize m_screenResolution;
qreal m_zoom = 1.0;
Status m_status = Null;
QUrl m_source;
QSize m_screenResolution;
qreal m_zoom = 1.0;
qreal m_actualCropX = 0.0;
qreal m_actualCropY = 0.0;
qreal m_actualCropWidth = 1.0;
qreal m_actualCropHeight = 1.0;
qreal m_actualCropX = 0.0;
qreal m_actualCropY = 0.0;
qreal m_actualCropWidth = 1.0;
qreal m_actualCropHeight = 1.0;
qreal m_cropX = 0.0;
qreal m_cropY = 0.0;
qreal m_cropWidth = 1.0;
qreal m_cropHeight = 1.0;
qreal m_cropX = 0.0;
qreal m_cropY = 0.0;
qreal m_cropWidth = 1.0;
qreal m_cropHeight = 1.0;
QImage m_image;
QSGTexture *m_texture = nullptr;
bool m_textureDirty = false;
QFutureWatcher<QImage> m_imageWatcher;
QImage m_image;
QSGTexture* m_texture = nullptr;
bool m_textureDirty = false;
QFutureWatcher<QImage> m_imageWatcher;
};
} // namespace ZShell::internal
+57 -19
View File
@@ -6,7 +6,8 @@
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)
, m_fileInfo(path)
, m_path(path)
@@ -78,8 +79,16 @@ FileSystemModel::FileSystemModel(QObject* parent)
, m_watchChanges(true)
, m_showHidden(false)
, m_filter(NoFilter) {
connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &FileSystemModel::watchDirIfRecursive);
connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &FileSystemModel::updateEntriesForDir);
connect(
&m_watcher,
&QFileSystemWatcher::directoryChanged,
this,
&FileSystemModel::watchDirIfRecursive);
connect(
&m_watcher,
&QFileSystemWatcher::directoryChanged,
this,
&FileSystemModel::updateEntriesForDir);
}
int FileSystemModel::rowCount(const QModelIndex& parent) const {
@@ -90,14 +99,15 @@ int FileSystemModel::rowCount(const QModelIndex& parent) 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::fromValue(m_entries.at(index.row()));
}
QHash<int, QByteArray> FileSystemModel::roleNames() const {
return { { Qt::UserRole, "modelData" } };
return {{Qt::UserRole, "modelData"}};
}
QString FileSystemModel::path() const {
@@ -233,9 +243,14 @@ void FileSystemModel::watchDirIfRecursive(const QString& path) {
return dirs;
});
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();
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
m_watcher.addPaths(paths);
}
@@ -295,8 +310,10 @@ void FileSystemModel::updateEntriesForDir(const QString& dir) {
oldPaths << entry->path();
}
const auto future = QtConcurrent::run([=](QPromise<QPair<QSet<QString>, QSet<QString>>>& promise) {
const auto flags = recursive ? QDirIterator::Subdirectories : QDirIterator::NoIteratorFlags;
const auto future = QtConcurrent::run(
[=](QPromise<QPair<QSet<QString>, QSet<QString>>>& promise) {
const auto flags = recursive ? QDirIterator::Subdirectories
: QDirIterator::NoIteratorFlags;
std::optional<QDirIterator> iter;
@@ -357,7 +374,8 @@ void FileSystemModel::updateEntriesForDir(const QString& dir) {
return;
}
promise.addResult(qMakePair(oldPaths - newPaths, newPaths - oldPaths));
promise.addResult(
qMakePair(oldPaths - newPaths, newPaths - oldPaths));
});
if (m_futures.contains(dir)) {
@@ -365,9 +383,14 @@ void FileSystemModel::updateEntriesForDir(const QString& dir) {
}
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);
if (!watcher->future().isResultReadyAt(0)) {
@@ -384,7 +407,8 @@ void FileSystemModel::updateEntriesForDir(const QString& dir) {
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;
for (int i = 0; i < m_entries.size(); ++i) {
if (removedPaths.contains(m_entries[i]->path())) {
@@ -424,9 +448,13 @@ void FileSystemModel::applyChanges(const QSet<QString>& removedPaths, const QSet
// Create new entries
QList<FileSystemEntry*> newEntries;
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);
});
@@ -435,7 +463,10 @@ void FileSystemModel::applyChanges(const QSet<QString>& removedPaths, const QSet
QList<FileSystemEntry*> batchItems;
for (const auto& entry : std::as_const(newEntries)) {
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);
});
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()) {
batchItems << entry;
} 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) {
m_entries.insert(insertStart + i, batchItems[i]);
}
@@ -458,7 +492,10 @@ void FileSystemModel::applyChanges(const QSet<QString>& removedPaths, const QSet
}
}
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) {
m_entries.insert(insertStart + i, batchItems[i]);
}
@@ -468,7 +505,8 @@ void FileSystemModel::applyChanges(const QSet<QString>& removedPaths, const QSet
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()) {
return m_sortReverse ^ a->isDir();
}
+116 -102
View File
@@ -1,6 +1,5 @@
#pragma once
#include <cstdint>
#include <qabstractitemmodel.h>
#include <qdir.h>
#include <qfilesystemwatcher.h>
@@ -14,136 +13,151 @@
namespace ZShell::models {
class FileSystemEntry : public QObject {
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("FileSystemEntry instances can only be retrieved from a FileSystemModel")
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE(
"FileSystemEntry instances can only be retrieved from a "
"FileSystemModel")
Q_PROPERTY(QString path READ path CONSTANT)
Q_PROPERTY(QString relativePath READ relativePath NOTIFY relativePathChanged)
Q_PROPERTY(QString name READ name CONSTANT)
Q_PROPERTY(QString baseName READ baseName CONSTANT)
Q_PROPERTY(QString parentDir READ parentDir CONSTANT)
Q_PROPERTY(QString suffix READ suffix CONSTANT)
Q_PROPERTY(qint64 size READ size CONSTANT)
Q_PROPERTY(bool isDir READ isDir CONSTANT)
Q_PROPERTY(bool isImage READ isImage CONSTANT)
Q_PROPERTY(QString mimeType READ mimeType CONSTANT)
Q_PROPERTY(QString path READ path CONSTANT)
Q_PROPERTY(QString relativePath READ relativePath NOTIFY relativePathChanged)
Q_PROPERTY(QString name READ name CONSTANT)
Q_PROPERTY(QString baseName READ baseName CONSTANT)
Q_PROPERTY(QString parentDir READ parentDir CONSTANT)
Q_PROPERTY(QString suffix READ suffix CONSTANT)
Q_PROPERTY(qint64 size READ size CONSTANT)
Q_PROPERTY(bool isDir READ isDir CONSTANT)
Q_PROPERTY(bool isImage READ isImage CONSTANT)
Q_PROPERTY(QString mimeType READ mimeType CONSTANT)
public:
explicit FileSystemEntry(const QString& path, const QString& relativePath, QObject* parent = nullptr);
public:
explicit FileSystemEntry(
const QString& path,
const QString& relativePath,
QObject* parent = nullptr);
[[nodiscard]] QString path() const;
[[nodiscard]] QString relativePath() const;
[[nodiscard]] QString name() const;
[[nodiscard]] QString baseName() const;
[[nodiscard]] QString parentDir() const;
[[nodiscard]] QString suffix() const;
[[nodiscard]] qint64 size() const;
[[nodiscard]] bool isDir() const;
[[nodiscard]] bool isImage() const;
[[nodiscard]] QString mimeType() const;
[[nodiscard]] QString path() const;
[[nodiscard]] QString relativePath() const;
[[nodiscard]] QString name() const;
[[nodiscard]] QString baseName() const;
[[nodiscard]] QString parentDir() const;
[[nodiscard]] QString suffix() const;
[[nodiscard]] qint64 size() const;
[[nodiscard]] bool isDir() const;
[[nodiscard]] bool isImage() const;
[[nodiscard]] QString mimeType() const;
void updateRelativePath(const QDir& dir);
void updateRelativePath(const QDir& dir);
signals:
void relativePathChanged();
signals:
void relativePathChanged();
private:
const QFileInfo m_fileInfo;
private:
const QFileInfo m_fileInfo;
const QString m_path;
QString m_relativePath;
const QString m_path;
QString m_relativePath;
mutable bool m_isImage;
mutable bool m_isImageInitialised;
mutable bool m_isImage;
mutable bool m_isImageInitialised;
mutable QString m_mimeType;
mutable bool m_mimeTypeInitialised;
mutable QString m_mimeType;
mutable bool m_mimeTypeInitialised;
};
class FileSystemModel : public QAbstractListModel {
Q_OBJECT
QML_ELEMENT
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
Q_PROPERTY(bool recursive READ recursive WRITE setRecursive NOTIFY recursiveChanged)
Q_PROPERTY(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(QStringList nameFilters READ nameFilters WRITE setNameFilters NOTIFY nameFiltersChanged)
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
Q_PROPERTY(
bool recursive READ recursive WRITE setRecursive NOTIFY recursiveChanged)
Q_PROPERTY(
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(
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:
enum Filter {
NoFilter,
Images,
Files,
Dirs
};
Q_ENUM(Filter)
public:
enum Filter { NoFilter, Images, Files, Dirs };
Q_ENUM(Filter)
explicit FileSystemModel(QObject* parent = nullptr);
explicit FileSystemModel(QObject* parent = nullptr);
[[nodiscard]] int rowCount(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]] int rowCount(
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]] QString path() const;
void setPath(const QString& path);
[[nodiscard]] QString path() const;
void setPath(const QString& path);
[[nodiscard]] bool recursive() const;
void setRecursive(bool recursive);
[[nodiscard]] bool recursive() const;
void setRecursive(bool recursive);
[[nodiscard]] bool watchChanges() const;
void setWatchChanges(bool watchChanges);
[[nodiscard]] bool watchChanges() const;
void setWatchChanges(bool watchChanges);
[[nodiscard]] bool showHidden() const;
void setShowHidden(bool showHidden);
[[nodiscard]] bool showHidden() const;
void setShowHidden(bool showHidden);
[[nodiscard]] bool sortReverse() const;
void setSortReverse(bool sortReverse);
[[nodiscard]] bool sortReverse() const;
void setSortReverse(bool sortReverse);
[[nodiscard]] Filter filter() const;
void setFilter(Filter filter);
[[nodiscard]] Filter filter() const;
void setFilter(Filter filter);
[[nodiscard]] QStringList nameFilters() const;
void setNameFilters(const QStringList& nameFilters);
[[nodiscard]] QStringList nameFilters() const;
void setNameFilters(const QStringList& nameFilters);
[[nodiscard]] QQmlListProperty<FileSystemEntry> entries();
[[nodiscard]] QQmlListProperty<FileSystemEntry> entries();
signals:
void pathChanged();
void recursiveChanged();
void watchChangesChanged();
void showHiddenChanged();
void sortReverseChanged();
void filterChanged();
void nameFiltersChanged();
void entriesChanged();
signals:
void pathChanged();
void recursiveChanged();
void watchChangesChanged();
void showHiddenChanged();
void sortReverseChanged();
void filterChanged();
void nameFiltersChanged();
void entriesChanged();
private:
QDir m_dir;
QFileSystemWatcher m_watcher;
QList<FileSystemEntry*> m_entries;
QHash<QString, QFuture<QPair<QSet<QString>, QSet<QString> > > > m_futures;
private:
QDir m_dir;
QFileSystemWatcher m_watcher;
QList<FileSystemEntry*> m_entries;
QHash<QString, QFuture<QPair<QSet<QString>, QSet<QString>>>> m_futures;
QString m_path;
bool m_recursive;
bool m_watchChanges;
bool m_showHidden;
bool m_sortReverse;
Filter m_filter;
QStringList m_nameFilters;
QString m_path;
bool m_recursive;
bool m_watchChanges;
bool m_showHidden;
bool m_sortReverse;
Filter m_filter;
QStringList m_nameFilters;
void watchDirIfRecursive(const QString& path);
void update();
void updateWatcher();
void updateEntries();
void updateEntriesForDir(const QString& dir);
void applyChanges(const QSet<QString>& removedPaths, const QSet<QString>& addedPaths);
[[nodiscard]] bool compareEntries(const FileSystemEntry* a, const FileSystemEntry* b) const;
void watchDirIfRecursive(const QString& path);
void update();
void updateWatcher();
void updateEntries();
void updateEntriesForDir(const QString& dir);
void applyChanges(
const QSet<QString>& removedPaths, const QSet<QString>& addedPaths);
[[nodiscard]] bool compareEntries(
const FileSystemEntry* a, const FileSystemEntry* b) const;
};
} // namespace ZShell::models
+57 -27
View File
@@ -23,20 +23,33 @@ PipeWireWorker::PipeWireWorker(std::stop_token token, AudioCollector* collector)
m_loop = pw_main_loop_new(nullptr);
if (!m_loop) {
qWarning() << "PipeWireWorker::init: failed to create PipeWire main loop";
qWarning()
<< "PipeWireWorker::init: failed to create PipeWire main loop";
pw_deinit();
return;
}
timespec timeout = { 0, 10 * SPA_NSEC_PER_MSEC };
m_timer = 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);
timespec timeout = {0, 10 * SPA_NSEC_PER_MSEC};
m_timer =
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(
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_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_VIRTUAL, "true");
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);
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);
self->streamStateChanged(state);
};
@@ -64,12 +78,18 @@ PipeWireWorker::PipeWireWorker(std::stop_token token, AudioCollector* collector)
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>(
PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS | PW_STREAM_FLAG_RT_PROCESS),
params, 1);
PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS |
PW_STREAM_FLAG_RT_PROCESS),
params,
1);
if (success < 0) {
qWarning() << "PipeWireWorker::init: failed to connect stream";
pw_stream_destroy(m_stream);
@@ -98,8 +118,13 @@ void PipeWireWorker::handleTimeout(void* data, uint64_t expirations) {
self->m_collector->clearBuffer();
} else {
self->m_idle = true;
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);
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);
}
}
}
@@ -108,12 +133,14 @@ void PipeWireWorker::streamStateChanged(pw_stream_state state) {
m_idle = false;
switch (state) {
case PW_STREAM_STATE_PAUSED: {
timespec timeout = { 0, 10 * SPA_NSEC_PER_MSEC };
pw_loop_update_timer(pw_main_loop_get_loop(m_loop), m_timer, &timeout, &timeout, false);
timespec timeout = {0, 10 * SPA_NSEC_PER_MSEC};
pw_loop_update_timer(
pw_main_loop_get_loop(m_loop), m_timer, &timeout, &timeout, false);
break;
}
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;
case PW_STREAM_STATE_ERROR:
pw_main_loop_quit(m_loop);
@@ -171,7 +198,8 @@ void AudioCollector::clearBuffer() {
auto* writeBuffer = m_writeBuffer.load(std::memory_order_relaxed);
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);
}
@@ -181,11 +209,13 @@ void AudioCollector::loadChunk(const qint16* samples, quint32 count) {
}
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;
});
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);
}
@@ -206,9 +236,11 @@ quint32 AudioCollector::readChunk(double* out, quint32 count) {
}
auto* readBuffer = m_readBuffer.load(std::memory_order_acquire);
std::transform(readBuffer->begin(), readBuffer->begin() + count, out, [](float sample) {
return static_cast<double>(sample);
});
std::transform(
readBuffer->begin(),
readBuffer->begin() + count,
out,
[](float sample) { return static_cast<double>(sample); });
return count;
}
@@ -218,8 +250,7 @@ AudioCollector::AudioCollector(QObject* parent)
, m_buffer1(ac::CHUNK_SIZE)
, m_buffer2(ac::CHUNK_SIZE)
, m_readBuffer(&m_buffer1)
, m_writeBuffer(&m_buffer2) {
}
, m_writeBuffer(&m_buffer2) {}
AudioCollector::~AudioCollector() {
stop();
@@ -232,9 +263,8 @@ void AudioCollector::start() {
clearBuffer();
m_thread = std::jthread([this](std::stop_token token) {
PipeWireWorker worker(token, this);
});
m_thread = std::jthread(
[this](std::stop_token token) { PipeWireWorker worker(token, this); });
}
void AudioCollector::stop() {
+35 -35
View File
@@ -22,55 +22,55 @@ constexpr quint32 CHUNK_SIZE = 512;
class AudioCollector;
class PipeWireWorker {
public:
explicit PipeWireWorker(std::stop_token token, AudioCollector* collector);
public:
explicit PipeWireWorker(std::stop_token token, AudioCollector* collector);
void run();
void run();
private:
pw_main_loop* m_loop;
pw_stream* m_stream;
spa_source* m_timer;
bool m_idle;
private:
pw_main_loop* m_loop;
pw_stream* m_stream;
spa_source* m_timer;
bool m_idle;
std::stop_token m_token;
AudioCollector* m_collector;
std::stop_token m_token;
AudioCollector* m_collector;
static void handleTimeout(void* data, uint64_t expirations);
void streamStateChanged(pw_stream_state state);
void processStream();
static void handleTimeout(void* data, uint64_t expirations);
void streamStateChanged(pw_stream_state state);
void processStream();
[[nodiscard]] unsigned int nextPowerOf2(unsigned int n);
[[nodiscard]] unsigned int nextPowerOf2(unsigned int n);
};
class AudioCollector : public Service {
Q_OBJECT
Q_OBJECT
public:
AudioCollector(const AudioCollector&) = delete;
AudioCollector& operator=(const AudioCollector&) = delete;
public:
AudioCollector(const AudioCollector&) = delete;
AudioCollector& operator=(const AudioCollector&) = delete;
static AudioCollector& instance();
static AudioCollector& instance();
void clearBuffer();
void loadChunk(const qint16* samples, quint32 count);
quint32 readChunk(float* out, quint32 count = 0);
quint32 readChunk(double* out, quint32 count = 0);
void clearBuffer();
void loadChunk(const qint16* samples, quint32 count);
quint32 readChunk(float* out, quint32 count = 0);
quint32 readChunk(double* out, quint32 count = 0);
private:
explicit AudioCollector(QObject* parent = nullptr);
~AudioCollector() override;
private:
explicit AudioCollector(QObject* parent = nullptr);
~AudioCollector() override;
std::jthread m_thread;
std::vector<float> m_buffer1;
std::vector<float> m_buffer2;
std::atomic<std::vector<float>*> m_readBuffer;
std::atomic<std::vector<float>*> m_writeBuffer;
quint32 m_sampleCount;
std::jthread m_thread;
std::vector<float> m_buffer1;
std::vector<float> m_buffer2;
std::atomic<std::vector<float>*> m_readBuffer;
std::atomic<std::vector<float>*> m_writeBuffer;
quint32 m_sampleCount;
void reload();
void start() override;
void stop() override;
void reload();
void start() override;
void stop() override;
};
} // namespace ZShell::services
+18 -12
View File
@@ -7,9 +7,7 @@
namespace ZShell::services {
AudioProcessor::AudioProcessor(QObject* parent)
: QObject(parent) {
}
AudioProcessor::AudioProcessor(QObject* parent) : QObject(parent) {}
AudioProcessor::~AudioProcessor() {
stop();
@@ -17,12 +15,17 @@ AudioProcessor::~AudioProcessor() {
void AudioProcessor::init() {
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);
}
void AudioProcessor::start() {
QMetaObject::invokeMethod(&AudioCollector::instance(), &AudioCollector::ref, Qt::QueuedConnection, this);
QMetaObject::invokeMethod(
&AudioCollector::instance(),
&AudioCollector::ref,
Qt::QueuedConnection,
this);
if (m_timer) {
m_timer->start();
}
@@ -32,14 +35,15 @@ void AudioProcessor::stop() {
if (m_timer) {
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)
: Service(parent)
, m_processor(nullptr)
, m_thread(nullptr) {
}
: Service(parent), m_processor(nullptr), m_thread(nullptr) {}
AudioProvider::~AudioProvider() {
if (m_thread) {
@@ -50,7 +54,8 @@ AudioProvider::~AudioProvider() {
void AudioProvider::init() {
if (!m_processor) {
qWarning() << "AudioProvider::init: attempted to init with no processor set";
qWarning()
<< "AudioProvider::init: attempted to init with no processor set";
return;
}
@@ -58,7 +63,8 @@ void AudioProvider::init() {
m_processor->moveToThread(m_thread);
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);
m_thread->start();
+22 -22
View File
@@ -7,41 +7,41 @@
namespace ZShell::services {
class AudioProcessor : public QObject {
Q_OBJECT
Q_OBJECT
public:
explicit AudioProcessor(QObject* parent = nullptr);
~AudioProcessor() override;
public:
explicit AudioProcessor(QObject* parent = nullptr);
~AudioProcessor() override;
void init();
void init();
void start();
void stop();
void start();
void stop();
protected:
virtual void process() = 0;
protected:
virtual void process() = 0;
private:
QTimer* m_timer;
private:
QTimer* m_timer;
};
class AudioProvider : public Service {
Q_OBJECT
Q_OBJECT
public:
explicit AudioProvider(QObject* parent = nullptr);
~AudioProvider() override;
public:
explicit AudioProvider(QObject* parent = nullptr);
~AudioProvider() override;
protected:
AudioProcessor* m_processor;
protected:
AudioProcessor* m_processor;
void init();
void init();
private:
QThread* m_thread;
private:
QThread* m_thread;
void start() override;
void stop() override;
void start() override;
void stop() override;
};
} // namespace ZShell::services
+7 -6
View File
@@ -10,8 +10,7 @@ BeatProcessor::BeatProcessor(QObject* parent)
: AudioProcessor(parent)
, m_tempo(new_aubio_tempo("default", 1024, ac::CHUNK_SIZE, ac::SAMPLE_RATE))
, m_in(new_fvec(ac::CHUNK_SIZE))
, m_out(new_fvec(2)) {
};
, m_out(new_fvec(2)) {};
BeatProcessor::~BeatProcessor() {
if (m_tempo) {
@@ -36,13 +35,15 @@ void BeatProcessor::process() {
}
}
BeatTracker::BeatTracker(QObject* parent)
: AudioProvider(parent)
, m_bpm(120) {
BeatTracker::BeatTracker(QObject* parent) : AudioProvider(parent), m_bpm(120) {
m_processor = new BeatProcessor();
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 {
+24 -24
View File
@@ -7,43 +7,43 @@
namespace ZShell::services {
class BeatProcessor : public AudioProcessor {
Q_OBJECT
Q_OBJECT
public:
explicit BeatProcessor(QObject* parent = nullptr);
~BeatProcessor() override;
public:
explicit BeatProcessor(QObject* parent = nullptr);
~BeatProcessor() override;
signals:
void beat(smpl_t bpm);
signals:
void beat(smpl_t bpm);
protected:
void process() override;
protected:
void process() override;
private:
aubio_tempo_t* m_tempo;
fvec_t* m_in;
fvec_t* m_out;
private:
aubio_tempo_t* m_tempo;
fvec_t* m_in;
fvec_t* m_out;
};
class BeatTracker : public AudioProvider {
Q_OBJECT
QML_ELEMENT
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(smpl_t bpm READ bpm NOTIFY bpmChanged)
Q_PROPERTY(smpl_t bpm READ bpm NOTIFY bpmChanged)
public:
explicit BeatTracker(QObject* parent = nullptr);
public:
explicit BeatTracker(QObject* parent = nullptr);
[[nodiscard]] smpl_t bpm() const;
[[nodiscard]] smpl_t bpm() const;
signals:
void bpmChanged();
void beat(smpl_t bpm);
signals:
void bpmChanged();
void beat(smpl_t bpm);
private:
smpl_t m_bpm;
private:
smpl_t m_bpm;
void updateBpm(smpl_t bpm);
void updateBpm(smpl_t bpm);
};
} // namespace ZShell::services
+18 -12
View File
@@ -2,7 +2,6 @@
#include "audiocollector.hpp"
#include "audioprovider.hpp"
#include <algorithm>
#include <cava/cavacore.h>
#include <cstddef>
#include <qdebug.h>
@@ -14,8 +13,7 @@ CavaProcessor::CavaProcessor(QObject* parent)
, m_plan(nullptr)
, m_in(new double[ac::CHUNK_SIZE])
, m_out(nullptr)
, m_bars(0) {
};
, m_bars(0) {};
CavaProcessor::~CavaProcessor() {
cleanup();
@@ -27,7 +25,8 @@ void CavaProcessor::process() {
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
cava_execute(m_in, count, m_out, m_plan);
@@ -35,7 +34,7 @@ void CavaProcessor::process() {
// Apply monstercat filter
QVector<double> values(m_bars);
for(int i = 0; i < m_bars; ++i) {
for (int i = 0; i < m_bars; ++i) {
values[i] = m_out[i];
}
@@ -63,7 +62,8 @@ void CavaProcessor::process() {
void CavaProcessor::setBars(int bars) {
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;
}
@@ -100,13 +100,15 @@ void CavaProcessor::initCava() {
}
CavaProvider::CavaProvider(QObject* parent)
: AudioProvider(parent)
, m_bars(0)
, m_values(m_bars, 0.0) {
: AudioProvider(parent), m_bars(0), m_values(m_bars, 0.0) {
m_processor = new CavaProcessor();
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 {
@@ -115,7 +117,8 @@ int CavaProvider::bars() const {
void CavaProvider::setBars(int bars) {
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;
}
@@ -129,7 +132,10 @@ void CavaProvider::setBars(int bars) {
emit valuesChanged();
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 {
+34 -34
View File
@@ -7,58 +7,58 @@
namespace ZShell::services {
class CavaProcessor : public AudioProcessor {
Q_OBJECT
Q_OBJECT
public:
explicit CavaProcessor(QObject* parent = nullptr);
~CavaProcessor() override;
public:
explicit CavaProcessor(QObject* parent = nullptr);
~CavaProcessor() override;
void setBars(int bars);
void setBars(int bars);
signals:
void valuesChanged(QVector<double> values);
signals:
void valuesChanged(QVector<double> values);
protected:
void process() override;
protected:
void process() override;
private:
struct cava_plan* m_plan;
double* m_in;
double* m_out;
private:
struct cava_plan* m_plan;
double* m_in;
double* m_out;
int m_bars;
QVector<double> m_values;
int m_bars;
QVector<double> m_values;
void reload();
void initCava();
void cleanup();
void reload();
void initCava();
void cleanup();
};
class CavaProvider : public AudioProvider {
Q_OBJECT
QML_ELEMENT
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(int bars READ bars WRITE setBars NOTIFY barsChanged)
Q_PROPERTY(int bars READ bars WRITE setBars NOTIFY barsChanged)
Q_PROPERTY(QVector<double> values READ values NOTIFY valuesChanged)
Q_PROPERTY(QVector<double> values READ values NOTIFY valuesChanged)
public:
explicit CavaProvider(QObject* parent = nullptr);
public:
explicit CavaProvider(QObject* parent = nullptr);
[[nodiscard]] int bars() const;
void setBars(int bars);
[[nodiscard]] int bars() const;
void setBars(int bars);
[[nodiscard]] QVector<double> values() const;
[[nodiscard]] QVector<double> values() const;
signals:
void barsChanged();
void valuesChanged();
signals:
void barsChanged();
void valuesChanged();
private:
int m_bars;
QVector<double> m_values;
private:
int m_bars;
QVector<double> m_values;
void updateValues(QVector<double> values);
void updateValues(QVector<double> values);
};
} // namespace ZShell::services
+12 -7
View File
@@ -8,8 +8,7 @@
namespace ZShell::services {
Cpu::Cpu(QObject* parent)
: TickingService(parent) {
Cpu::Cpu(QObject* parent) : TickingService(parent) {
readNameOnce();
}
@@ -41,7 +40,8 @@ void Cpu::readNameOnce() {
const QByteArray data = f.readAll();
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));
if (!match.hasMatch()) {
return;
@@ -64,8 +64,9 @@ void Cpu::refreshPercentage() {
const QByteArray data = f.readAll();
f.close();
static const QRegularExpression re(
QStringLiteral("^cpu\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)"));
static const QRegularExpression re(QStringLiteral(
"^cpu\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)"
"\\s+(\\d+)\\s+(\\d+)"));
const auto match = re.match(QString::fromLatin1(data));
if (!match.hasMatch()) {
return;
@@ -83,7 +84,10 @@ void Cpu::refreshPercentage() {
const quint64 totalDiff = total > m_lastTotal ? total - m_lastTotal : 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_lastIdle = idle;
@@ -105,7 +109,8 @@ void Cpu::refreshTemperature() {
QString Cpu::cleanName(QString s) {
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);
static const QRegularExpression spaces(QStringLiteral("\\s+"));
+28 -28
View File
@@ -7,42 +7,42 @@
namespace ZShell::services {
class Cpu : public TickingService {
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
Q_PROPERTY(qreal percentage READ percentage NOTIFY percentageChanged)
Q_PROPERTY(qreal temperature READ temperature NOTIFY temperatureChanged)
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
Q_PROPERTY(qreal percentage READ percentage NOTIFY percentageChanged)
Q_PROPERTY(qreal temperature READ temperature NOTIFY temperatureChanged)
public:
explicit Cpu(QObject* parent = nullptr);
public:
explicit Cpu(QObject* parent = nullptr);
[[nodiscard]] QString name() const;
[[nodiscard]] qreal percentage() const;
[[nodiscard]] qreal temperature() const;
[[nodiscard]] QString name() const;
[[nodiscard]] qreal percentage() const;
[[nodiscard]] qreal temperature() const;
signals:
void nameChanged();
void percentageChanged();
void temperatureChanged();
signals:
void nameChanged();
void percentageChanged();
void temperatureChanged();
protected:
void tick() override;
protected:
void tick() override;
private:
void readNameOnce();
void refreshPercentage();
void refreshTemperature();
private:
void readNameOnce();
void refreshPercentage();
void refreshTemperature();
[[nodiscard]] static QString cleanName(QString s);
[[nodiscard]] static QString cleanName(QString s);
QString m_name;
qreal m_percentage = 0.0;
qreal m_temperature = 0.0;
quint64 m_lastIdle = 0;
quint64 m_lastTotal = 0;
bool m_nameLoaded = false;
QString m_name;
qreal m_percentage = 0.0;
qreal m_temperature = 0.0;
quint64 m_lastIdle = 0;
quint64 m_lastTotal = 0;
bool m_nameLoaded = false;
};
} // namespace ZShell::services
+46 -38
View File
@@ -17,49 +17,57 @@ struct DesktopItem {
};
class DesktopModel : public QAbstractListModel {
Q_OBJECT
QML_ELEMENT
Q_OBJECT
QML_ELEMENT
public:
enum DesktopRoles {
public:
enum DesktopRoles {
FileNameRole = Qt::UserRole + 1,
FilePathRole,
IsDirRole,
GridXRole,
GridYRole
};
explicit DesktopModel(QObject* parent = nullptr);
[[nodiscard]] int rowCount(
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;
Q_INVOKABLE void loadDirectory(const QString& path);
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_PROPERTY(int rows READ rows WRITE setRows NOTIFY rowsChanged)
[[nodiscard]] int rows() const { return m_rows; }
void setRows(int r) {
if (m_rows != r) {
m_rows = r;
emit rowsChanged();
}
}
signals:
void rowsChanged();
private:
int m_rows = 1;
QList<DesktopItem> m_items;
QString m_watchedPath;
QFileSystemWatcher m_watcher;
void saveCurrentLayout();
[[nodiscard]] QPoint getEmptySpot(const QSet<QString>& occupied) const;
void onDirectoryChanged();
};
explicit DesktopModel(QObject *parent = nullptr);
[[nodiscard]] int rowCount(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;
Q_INVOKABLE void loadDirectory(const QString &path);
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_PROPERTY(int rows READ rows WRITE setRows NOTIFY rowsChanged)
public:
[[nodiscard]] int rows() const {
return m_rows;
}
void setRows(int r) {
if (m_rows != r) { m_rows = r; emit rowsChanged(); }
}
signals:
void rowsChanged();
private:
int m_rows = 1;
QList<DesktopItem> m_items;
QString m_watchedPath;
QFileSystemWatcher m_watcher;
void saveCurrentLayout();
[[nodiscard]] QPoint getEmptySpot(const QSet<QString> &occupied) const;
void onDirectoryChanged();
};
};
}; // namespace ZShell::services
@@ -9,11 +9,12 @@
namespace ZShell::services {
DesktopStateManager::DesktopStateManager(QObject *parent) : QObject(parent) {
}
DesktopStateManager::DesktopStateManager(QObject* parent) : QObject(parent) {}
QString DesktopStateManager::getConfigFilePath() const {
QString configDir = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + "/zshell";
QString configDir =
QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) +
"/zshell";
QDir dir(configDir);
if (!dir.exists()) {
dir.mkpath(".");
@@ -30,7 +31,8 @@ void DesktopStateManager::saveLayout(const QVariantMap& layout) {
file.write(doc.toJson(QJsonDocument::Indented));
file.close();
} else {
qWarning() << "zshell: Cannot save desktop layout to" << getConfigFilePath();
qWarning() << "zshell: Cannot save desktop layout to"
<< getConfigFilePath();
}
}
@@ -7,18 +7,18 @@
namespace ZShell::services {
class DesktopStateManager : public QObject {
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
public:
explicit DesktopStateManager(QObject *parent = nullptr);
public:
explicit DesktopStateManager(QObject* parent = nullptr);
Q_INVOKABLE void saveLayout(const QVariantMap& layout);
Q_INVOKABLE QVariantMap getLayout();
Q_INVOKABLE void saveLayout(const QVariantMap& layout);
Q_INVOKABLE QVariantMap getLayout();
private:
[[nodiscard]] QString getConfigFilePath() const;
private:
[[nodiscard]] QString getConfigFilePath() const;
};
} // namespace ZShell::services
+12 -5
View File
@@ -8,13 +8,17 @@ constexpr qreal kKib = 1024.0;
} // 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)
, m_mount(std::move(mount))
, m_usedBytes(usedBytes)
, m_totalBytes(totalBytes)
, m_hasRoot(hasRoot) {
}
, m_hasRoot(hasRoot) {}
QString DiskInfo::mount() const {
return m_mount;
@@ -29,12 +33,15 @@ qreal DiskInfo::total() 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;
}
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 {
+33 -28
View File
@@ -6,41 +6,46 @@
namespace ZShell::services {
class DiskInfo : public QObject {
Q_OBJECT
QML_ELEMENT
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("DiskInfo is created by DiskUsage")
Q_PROPERTY(QString mount READ mount CONSTANT)
Q_PROPERTY(qreal used READ used NOTIFY usedChanged)
Q_PROPERTY(qreal total READ total NOTIFY totalChanged)
Q_PROPERTY(qreal free READ free NOTIFY freeChanged)
Q_PROPERTY(qreal perc READ perc NOTIFY percChanged)
Q_PROPERTY(bool hasRoot READ hasRoot NOTIFY hasRootChanged)
Q_PROPERTY(QString mount READ mount CONSTANT)
Q_PROPERTY(qreal used READ used NOTIFY usedChanged)
Q_PROPERTY(qreal total READ total NOTIFY totalChanged)
Q_PROPERTY(qreal free READ free NOTIFY freeChanged)
Q_PROPERTY(qreal perc READ perc NOTIFY percChanged)
Q_PROPERTY(bool hasRoot READ hasRoot NOTIFY hasRootChanged)
public:
DiskInfo(QString mount, quint64 usedBytes, quint64 totalBytes, bool hasRoot, QObject* parent = nullptr);
public:
DiskInfo(
QString mount,
quint64 usedBytes,
quint64 totalBytes,
bool hasRoot,
QObject* parent = nullptr);
[[nodiscard]] QString mount() const;
[[nodiscard]] qreal used() const;
[[nodiscard]] qreal total() const;
[[nodiscard]] qreal free() const;
[[nodiscard]] qreal perc() const;
[[nodiscard]] bool hasRoot() const;
[[nodiscard]] QString mount() const;
[[nodiscard]] qreal used() const;
[[nodiscard]] qreal total() const;
[[nodiscard]] qreal free() const;
[[nodiscard]] qreal perc() const;
[[nodiscard]] bool hasRoot() const;
void update(quint64 usedBytes, quint64 totalBytes, bool hasRoot);
void update(quint64 usedBytes, quint64 totalBytes, bool hasRoot);
signals:
void usedChanged();
void totalChanged();
void freeChanged();
void percChanged();
void hasRootChanged();
signals:
void usedChanged();
void totalChanged();
void freeChanged();
void percChanged();
void hasRootChanged();
private:
QString m_mount;
quint64 m_usedBytes;
quint64 m_totalBytes;
bool m_hasRoot;
private:
QString m_mount;
quint64 m_usedBytes;
quint64 m_totalBytes;
bool m_hasRoot;
};
} // namespace ZShell::services
+60 -60
View File
@@ -8,81 +8,81 @@
namespace ZShell::services {
class Gpu : public TickingService {
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
public:
enum Type {
public:
enum Type {
Auto, // user override is empty (config "") — defer to detected autoType
None, // no usable GPU
Nvidia, // queried via nvidia-smi
Generic, // queried via /sys/class/drm/card*/device/gpu_busy_percent
};
Q_ENUM(Type)
};
Q_ENUM(Type)
private:
Q_PROPERTY(Type type READ type NOTIFY typeChanged)
Q_PROPERTY(Type userType READ userType NOTIFY userTypeChanged)
Q_PROPERTY(Type autoType READ autoType NOTIFY autoTypeChanged)
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
Q_PROPERTY(qreal percentage READ percentage NOTIFY percentageChanged)
Q_PROPERTY(qreal temperature READ temperature NOTIFY temperatureChanged)
Q_PROPERTY(qreal memoryUsed READ memoryUsed NOTIFY memoryUsedChanged)
Q_PROPERTY(qreal memoryTotal READ memoryTotal NOTIFY memoryTotalChanged)
private:
Q_PROPERTY(Type type READ type NOTIFY typeChanged)
Q_PROPERTY(Type userType READ userType NOTIFY userTypeChanged)
Q_PROPERTY(Type autoType READ autoType NOTIFY autoTypeChanged)
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
Q_PROPERTY(qreal percentage READ percentage NOTIFY percentageChanged)
Q_PROPERTY(qreal temperature READ temperature NOTIFY temperatureChanged)
Q_PROPERTY(qreal memoryUsed READ memoryUsed NOTIFY memoryUsedChanged)
Q_PROPERTY(qreal memoryTotal READ memoryTotal NOTIFY memoryTotalChanged)
public:
explicit Gpu(QObject* parent = nullptr);
public:
explicit Gpu(QObject* parent = nullptr);
[[nodiscard]] Type type() const;
[[nodiscard]] Type userType() const;
[[nodiscard]] Type autoType() const;
[[nodiscard]] QString name() const;
[[nodiscard]] qreal percentage() const;
[[nodiscard]] qreal temperature() const;
[[nodiscard]] qreal memoryUsed() const;
[[nodiscard]] qreal memoryTotal() const;
[[nodiscard]] Type type() const;
[[nodiscard]] Type userType() const;
[[nodiscard]] Type autoType() const;
[[nodiscard]] QString name() const;
[[nodiscard]] qreal percentage() const;
[[nodiscard]] qreal temperature() const;
[[nodiscard]] qreal memoryUsed() const;
[[nodiscard]] qreal memoryTotal() const;
signals:
void typeChanged();
void userTypeChanged();
void autoTypeChanged();
void nameChanged();
void percentageChanged();
void temperatureChanged();
void memoryUsedChanged();
void memoryTotalChanged();
signals:
void typeChanged();
void userTypeChanged();
void autoTypeChanged();
void nameChanged();
void percentageChanged();
void temperatureChanged();
void memoryUsedChanged();
void memoryTotalChanged();
protected:
void tick() override;
protected:
void tick() override;
private:
void detectTypeOnce();
void detectNameOnce();
void readGenericUsage();
void startNvidiaUsage();
void readGpuTemperature();
private:
void detectTypeOnce();
void detectNameOnce();
void readGenericUsage();
void startNvidiaUsage();
void readGpuTemperature();
void setUserType(Type value);
void setAutoType(Type value);
void setName(QString value);
void setMemoryUsed(qreal value);
void setMemoryTotal(qreal value);
void setUserType(Type value);
void setAutoType(Type value);
void setName(QString value);
void setMemoryUsed(qreal value);
void setMemoryTotal(qreal value);
[[nodiscard]] static Type parseType(const QString& s);
[[nodiscard]] static QString cleanName(QString s);
[[nodiscard]] static Type parseType(const QString& s);
[[nodiscard]] static QString cleanName(QString s);
Type m_userType = Auto;
Type m_autoType = None;
QString m_name;
qreal m_percentage = 0.0;
qreal m_temperature = 0.0;
qreal m_memoryUsed = 0.0;
qreal m_memoryTotal = 0.0;
Type m_userType = Auto;
Type m_autoType = None;
QString m_name;
qreal m_percentage = 0.0;
qreal m_temperature = 0.0;
qreal m_memoryUsed = 0.0;
qreal m_memoryTotal = 0.0;
QProcess* m_typeProc = nullptr;
QProcess* m_nameProc = nullptr;
QProcess* m_nvidiaProc = nullptr;
QProcess* m_typeProc = nullptr;
QProcess* m_nameProc = nullptr;
QProcess* m_nvidiaProc = nullptr;
};
} // namespace ZShell::services
+12 -19
View File
@@ -51,16 +51,14 @@ int HyprsunsetManager::temp() const {
}
void HyprsunsetManager::setActiveAuto(bool activeAuto) {
if (activeAuto == m_activeAuto)
return;
if (activeAuto == m_activeAuto) return;
m_activeAuto = activeAuto;
emit activeAutoChanged();
}
void HyprsunsetManager::setManualToggle(bool toggle) {
if (toggle == m_manualToggle)
return;
if (toggle == m_manualToggle) return;
m_manualToggle = toggle;
emit manualToggleChanged();
@@ -69,8 +67,7 @@ void HyprsunsetManager::setManualToggle(bool toggle) {
}
void HyprsunsetManager::setEndTime(const int& time) {
if (time == m_endTime)
return;
if (time == m_endTime) return;
m_endTime = time;
emit endTimeChanged();
@@ -78,8 +75,7 @@ void HyprsunsetManager::setEndTime(const int& time) {
}
void HyprsunsetManager::setStartTime(const int& time) {
if (time == m_startTime)
return;
if (time == m_startTime) return;
m_startTime = time;
emit startTimeChanged();
@@ -87,8 +83,7 @@ void HyprsunsetManager::setStartTime(const int& time) {
}
void HyprsunsetManager::setTemp(const int& temp) {
if (temp == m_temp)
return;
if (temp == m_temp) return;
m_temp = temp;
emit tempChanged();
@@ -104,8 +99,7 @@ void HyprsunsetManager::toggle() {
}
void HyprsunsetManager::start() {
if (m_enabled && m_initialized)
return;
if (m_enabled && m_initialized) return;
m_initialized = true;
m_enabled = true;
@@ -113,13 +107,13 @@ void HyprsunsetManager::start() {
emit enabledChanged();
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();
}
void HyprsunsetManager::end() {
if (!m_enabled && m_initialized)
return;
if (!m_enabled && m_initialized) return;
m_initialized = true;
m_enabled = false;
@@ -132,12 +126,11 @@ void HyprsunsetManager::end() {
}
void HyprsunsetManager::apply() {
if (m_manualToggle || !m_activeAuto || !m_startAllowed)
return;
if (m_manualToggle || !m_activeAuto || !m_startAllowed) return;
const auto current = QTime::currentTime();
const auto currentMin = current.hour() * 60 + current.minute();
bool isDarkTime;
bool isDarkTime = false;
if (m_startTime <= m_endTime) {
isDarkTime = (currentMin >= m_startTime && currentMin < m_endTime);
@@ -152,4 +145,4 @@ void HyprsunsetManager::apply() {
}
}
};
}; // namespace ZShell::services
+51 -46
View File
@@ -10,57 +10,62 @@
namespace ZShell::services {
class HyprsunsetManager : public QObject {
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(bool enabled READ enabled NOTIFY enabledChanged)
Q_PROPERTY(int startTime READ startTime WRITE setStartTime NOTIFY startTimeChanged)
Q_PROPERTY(int endTime READ endTime WRITE setEndTime NOTIFY endTimeChanged)
Q_PROPERTY(int temp READ temp WRITE setTemp NOTIFY tempChanged)
Q_PROPERTY(bool activeAuto READ activeAuto WRITE setActiveAuto NOTIFY activeAutoChanged)
Q_PROPERTY(bool manualToggle READ manualToggle WRITE setManualToggle NOTIFY manualToggleChanged)
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(bool enabled READ enabled NOTIFY enabledChanged)
Q_PROPERTY(
int startTime READ startTime WRITE setStartTime NOTIFY startTimeChanged)
Q_PROPERTY(int endTime READ endTime WRITE setEndTime NOTIFY endTimeChanged)
Q_PROPERTY(int temp READ temp WRITE setTemp NOTIFY tempChanged)
Q_PROPERTY(
bool activeAuto READ activeAuto WRITE setActiveAuto NOTIFY
activeAutoChanged)
Q_PROPERTY(
bool manualToggle READ manualToggle WRITE setManualToggle NOTIFY
manualToggleChanged)
public:
explicit HyprsunsetManager(QObject* parent = nullptr);
public:
explicit HyprsunsetManager(QObject* parent = nullptr);
[[nodiscard]] int startTime() const;
[[nodiscard]] int endTime() const;
[[nodiscard]] bool enabled() const;
[[nodiscard]] int temp() const;
[[nodiscard]] bool activeAuto() const;
[[nodiscard]] bool manualToggle() const;
[[nodiscard]] int startTime() const;
[[nodiscard]] int endTime() const;
[[nodiscard]] bool enabled() const;
[[nodiscard]] int temp() const;
[[nodiscard]] bool activeAuto() const;
[[nodiscard]] bool manualToggle() const;
Q_INVOKABLE void toggle();
Q_INVOKABLE void apply();
Q_INVOKABLE void toggle();
Q_INVOKABLE void apply();
void setStartTime(const int& time);
void setEndTime(const int& time);
void setTemp(const int& temp);
void setActiveAuto(bool activeAuto);
void setManualToggle(bool toggle);
void setStartTime(const int& time);
void setEndTime(const int& time);
void setTemp(const int& temp);
void setActiveAuto(bool activeAuto);
void setManualToggle(bool toggle);
signals:
void enabledChanged();
void startTimeChanged();
void activeAutoChanged();
void endTimeChanged();
void tempChanged();
void manualToggleChanged();
signals:
void enabledChanged();
void startTimeChanged();
void activeAutoChanged();
void endTimeChanged();
void tempChanged();
void manualToggleChanged();
private:
int m_startTime;
int m_endTime;
bool m_enabled = false;
bool m_manualToggle = false;
bool m_activeAuto;
bool m_startAllowed = false;
bool m_initialized = false;
QTimer m_startCooldown;
int m_temp;
QProcess m_process;
QTimer m_timer;
QTimer m_manualTimer;
void start();
void end();
private:
int m_startTime;
int m_endTime;
bool m_enabled = false;
bool m_manualToggle = false;
bool m_activeAuto;
bool m_startAllowed = false;
bool m_initialized = false;
QTimer m_startCooldown;
int m_temp;
QProcess m_process;
QTimer m_timer;
QTimer m_manualTimer;
void start();
void end();
};
};
}; // namespace ZShell::services
+5 -5
View File
@@ -5,9 +5,7 @@
namespace ZShell::services {
Memory::Memory(QObject* parent)
: TickingService(parent) {
}
Memory::Memory(QObject* parent) : TickingService(parent) {}
qreal Memory::used() const {
return m_used;
@@ -29,8 +27,10 @@ void Memory::tick() {
const QByteArray data = f.readAll();
f.close();
static const QRegularExpression reTotal(QStringLiteral("MemTotal: *(\\d+)"));
static const QRegularExpression reAvail(QStringLiteral("MemAvailable: *(\\d+)"));
static const QRegularExpression reTotal(
QStringLiteral("MemTotal: *(\\d+)"));
static const QRegularExpression reAvail(
QStringLiteral("MemAvailable: *(\\d+)"));
const QString text = QString::fromLatin1(data);
const auto totalMatch = reTotal.match(text);
+20 -20
View File
@@ -8,32 +8,32 @@
namespace ZShell::services {
class Memory : public TickingService {
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
Q_PROPERTY(qreal used READ used NOTIFY changed)
Q_PROPERTY(qreal total READ total NOTIFY changed)
Q_PROPERTY(qreal percentage READ percentage NOTIFY changed)
Q_PROPERTY(qreal used READ used NOTIFY changed)
Q_PROPERTY(qreal total READ total NOTIFY changed)
Q_PROPERTY(qreal percentage READ percentage NOTIFY changed)
public:
explicit Memory(QObject* parent = nullptr);
public:
explicit Memory(QObject* parent = nullptr);
[[nodiscard]] qreal used() const;
[[nodiscard]] qreal total() const;
[[nodiscard]] qreal percentage() const;
[[nodiscard]] qreal used() const;
[[nodiscard]] qreal total() const;
[[nodiscard]] qreal percentage() const;
signals:
void changed();
signals:
void changed();
protected:
void tick() override;
protected:
void tick() override;
private:
qreal m_used = 0.0;
qreal m_total = 1.0;
quint64 m_lastUsed = 0;
quint64 m_lastTotal = 0;
private:
qreal m_used = 0.0;
qreal m_total = 1.0;
quint64 m_lastUsed = 0;
quint64 m_lastTotal = 0;
};
} // namespace ZShell::services
+26 -13
View File
@@ -14,7 +14,7 @@ namespace ZShell::services::sensorslib {
namespace {
std::atomic<bool> g_initOk{ false };
std::atomic<bool> g_initOk{false};
std::once_flag g_initFlag;
void doInit() {
@@ -31,8 +31,10 @@ void doInit() {
});
}
[[nodiscard]] std::optional<double> readTempInput(const sensors_chip_name* chip, const sensors_feature* feat) {
const sensors_subfeature* sf = sensors_get_subfeature(chip, feat, SENSORS_SUBFEATURE_TEMP_INPUT);
[[nodiscard]] std::optional<double> readTempInput(
const sensors_chip_name* chip, const sensors_feature* feat) {
const sensors_subfeature* sf =
sensors_get_subfeature(chip, feat, SENSORS_SUBFEATURE_TEMP_INPUT);
if (!sf) {
return std::nullopt;
}
@@ -43,7 +45,8 @@ void doInit() {
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);
if (!raw) {
return {};
@@ -59,7 +62,8 @@ bool labelEquals(const QByteArray& label, const char* literal) {
bool labelStartsWith(const QByteArray& label, const char* 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
@@ -78,9 +82,11 @@ std::optional<double> cpuPackageTemp() {
std::optional<double> fallback; // Tctl
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;
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) {
continue;
}
@@ -89,7 +95,8 @@ std::optional<double> cpuPackageTemp() {
continue;
}
if (labelStartsWith(label, "Package id ") || labelEquals(label, "Tdie")) {
if (labelStartsWith(label, "Package id ") ||
labelEquals(label, "Tdie")) {
if (auto v = readTempInput(chip, feat)) {
primary = v;
}
@@ -116,13 +123,15 @@ std::optional<double> gpuPciAverageTemp() {
int countFallback = 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) {
continue;
}
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) {
continue;
}
@@ -131,10 +140,14 @@ std::optional<double> gpuPciAverageTemp() {
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]));
const bool isPrimary = tempIndexed || labelEquals(label, "GPU core") || labelEquals(label, "edge");
const bool isFallback = labelEquals(label, "junction") || labelEquals(label, "mem");
const bool isPrimary = tempIndexed ||
labelEquals(label, "GPU core") ||
labelEquals(label, "edge");
const bool isFallback = labelEquals(label, "junction") ||
labelEquals(label, "mem");
if (!isPrimary && !isFallback) {
continue;
+1 -3
View File
@@ -5,9 +5,7 @@
namespace ZShell::services {
Service::Service(QObject* parent)
: QObject(parent) {
}
Service::Service(QObject* parent) : QObject(parent) {}
void Service::ref(QObject* sender) {
if (m_refs.isEmpty()) {
+9 -9
View File
@@ -6,19 +6,19 @@
namespace ZShell::services {
class Service : public QObject {
Q_OBJECT
Q_OBJECT
public:
explicit Service(QObject* parent = nullptr);
public:
explicit Service(QObject* parent = nullptr);
void ref(QObject* sender);
void unref(QObject* sender);
void ref(QObject* sender);
void unref(QObject* sender);
private:
QSet<QObject*> m_refs;
private:
QSet<QObject*> m_refs;
virtual void start() = 0;
virtual void stop() = 0;
virtual void start() = 0;
virtual void stop() = 0;
};
} // namespace ZShell::services
+1 -2
View File
@@ -5,8 +5,7 @@
namespace ZShell::services {
ServiceRef::ServiceRef(Service* service, QObject* parent)
: QObject(parent)
, m_service(service) {
: QObject(parent), m_service(service) {
if (m_service) {
m_service->ref(this);
}
+13 -11
View File
@@ -7,22 +7,24 @@
namespace ZShell::services {
class ServiceRef : public QObject {
Q_OBJECT
QML_ELEMENT
Q_OBJECT
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:
explicit ServiceRef(Service* service = nullptr, QObject* parent = nullptr);
public:
explicit ServiceRef(Service* service = nullptr, QObject* parent = nullptr);
[[nodiscard]] Service* service() const;
void setService(Service* service);
[[nodiscard]] Service* service() const;
void setService(Service* service);
signals:
void serviceChanged();
signals:
void serviceChanged();
private:
QPointer<Service> m_service;
private:
QPointer<Service> m_service;
};
} // namespace ZShell::services
+39 -41
View File
@@ -24,12 +24,14 @@ struct Accum {
};
[[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();
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"));
if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
return false;
@@ -50,6 +52,7 @@ struct Accum {
QStringList resolveByDevt(uint major, uint minor, int depth = 0);
// NOLINTNEXTLINE(misc-no-recursion)
QStringList resolveAtNode(const QString& node, int depth) {
if (node.isEmpty() || depth > 8) {
return {};
@@ -62,18 +65,20 @@ QStringList resolveAtNode(const QString& node, int depth) {
if (QFileInfo::exists(node + QStringLiteral("/partition"))) {
const QString diskNode = nodeInfo.path();
return { QFileInfo(diskNode).fileName() };
return {QFileInfo(diskNode).fileName()};
}
const QDir slavesDir(node + QStringLiteral("/slaves"));
if (slavesDir.exists()) {
const QStringList slaves = slavesDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
const QStringList slaves =
slavesDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
if (!slaves.isEmpty()) {
QStringList out;
for (const QString& slave : slaves) {
uint sm = 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)) {
continue;
}
@@ -88,18 +93,17 @@ 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) {
return resolveAtNode(sysfsRealPath(major, minor), depth);
}
} // namespace
Storage::Storage(QObject* parent)
: TickingService(parent) {
}
Storage::Storage(QObject* parent) : TickingService(parent) {}
qreal Storage::percentage() const {
qreal totalUsed = 0.0;
@@ -124,7 +128,8 @@ bool Storage::sameOrder(const QList<DiskInfo*>& a, const QList<DiskInfo*>& b) {
}
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) {
@@ -157,30 +162,11 @@ DiskInfo* Storage::primaryDisk() const {
bool Storage::isPseudoFs(QByteArrayView fsType) {
static constexpr const char* kPseudo[] = {
"tmpfs",
"devtmpfs",
"proc",
"sysfs",
"cgroup",
"cgroup2",
"overlay",
"squashfs",
"devpts",
"mqueue",
"ramfs",
"rpc_pipefs",
"autofs",
"configfs",
"debugfs",
"tracefs",
"securityfs",
"pstore",
"bpf",
"binfmt_misc",
"hugetlbfs",
"fusectl",
"efivarfs",
"selinuxfs",
"tmpfs", "devtmpfs", "proc", "sysfs", "cgroup",
"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) {
if (fsType == QByteArrayView(p)) {
@@ -194,7 +180,7 @@ QStringList Storage::resolveToPhysicalDisks(const QString& devicePath) {
if (devicePath.isEmpty() || !devicePath.startsWith(QLatin1Char('/'))) {
return {};
}
struct stat st {};
struct stat st{};
if (::stat(devicePath.toLocal8Bit().constData(), &st) != 0) {
return {};
}
@@ -232,7 +218,8 @@ void Storage::tick() {
const QByteArray device = v.device();
const auto totalBytes = static_cast<quint64>(v.bytesTotal());
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("/");
DeviceEntry& e = byDevice[device];
@@ -244,7 +231,8 @@ void Storage::tick() {
for (auto it = byDevice.constBegin(); it != byDevice.constEnd(); ++it) {
const DeviceEntry& e = it.value();
const QStringList disks = resolveToPhysicalDisks(QString::fromLocal8Bit(e.device));
const QStringList disks =
resolveToPhysicalDisks(QString::fromLocal8Bit(e.device));
if (disks.isEmpty()) {
continue;
}
@@ -269,14 +257,23 @@ void Storage::tick() {
next.reserve(byDisk.size());
for (auto it = byDisk.constBegin(); it != byDisk.constEnd(); ++it) {
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);
} 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()) {
return a->hasRoot();
}
@@ -284,7 +281,8 @@ void Storage::tick() {
});
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();
manualCleared = true;
}
+37 -30
View File
@@ -12,44 +12,51 @@
namespace ZShell::services {
class Storage : public TickingService {
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
Q_PROPERTY(qreal percentage READ percentage NOTIFY percentageChanged)
Q_PROPERTY(QQmlListProperty<ZShell::services::DiskInfo> disks READ disksProp NOTIFY disksChanged)
Q_PROPERTY(ZShell::services::DiskInfo* manualPrimaryDisk READ manualPrimaryDisk WRITE setManualPrimaryDisk NOTIFY
manualPrimaryDiskChanged)
Q_PROPERTY(ZShell::services::DiskInfo* primaryDisk READ primaryDisk NOTIFY primaryDiskChanged)
Q_PROPERTY(qreal percentage READ percentage NOTIFY percentageChanged)
Q_PROPERTY(
QQmlListProperty<ZShell::services::DiskInfo> disks READ disksProp NOTIFY
disksChanged)
Q_PROPERTY(
ZShell::services::DiskInfo* manualPrimaryDisk READ manualPrimaryDisk
WRITE setManualPrimaryDisk NOTIFY manualPrimaryDiskChanged)
Q_PROPERTY(
ZShell::services::DiskInfo* primaryDisk READ primaryDisk NOTIFY
primaryDiskChanged)
public:
explicit Storage(QObject* parent = nullptr);
public:
explicit Storage(QObject* parent = nullptr);
[[nodiscard]] qreal percentage() const;
[[nodiscard]] QQmlListProperty<DiskInfo> disksProp();
[[nodiscard]] DiskInfo* manualPrimaryDisk() const;
void setManualPrimaryDisk(DiskInfo* disk);
[[nodiscard]] DiskInfo* primaryDisk() const;
[[nodiscard]] qreal percentage() const;
[[nodiscard]] QQmlListProperty<DiskInfo> disksProp();
[[nodiscard]] DiskInfo* manualPrimaryDisk() const;
void setManualPrimaryDisk(DiskInfo* disk);
[[nodiscard]] DiskInfo* primaryDisk() const;
signals:
void disksChanged();
void percentageChanged();
void manualPrimaryDiskChanged();
void primaryDiskChanged();
signals:
void disksChanged();
void percentageChanged();
void manualPrimaryDiskChanged();
void primaryDiskChanged();
protected:
void tick() override;
protected:
void tick() override;
private:
[[nodiscard]] static QStringList resolveToPhysicalDisks(const QString& devicePath);
[[nodiscard]] static bool isPseudoFs(QByteArrayView fsType);
[[nodiscard]] static bool sameOrder(const QList<DiskInfo*>& a, const QList<DiskInfo*>& b);
private:
[[nodiscard]] static QStringList resolveToPhysicalDisks(
const QString& devicePath);
[[nodiscard]] static bool isPseudoFs(QByteArrayView fsType);
[[nodiscard]] static bool sameOrder(
const QList<DiskInfo*>& a, const QList<DiskInfo*>& b);
static qsizetype disksCount(QQmlListProperty<DiskInfo>* prop);
static DiskInfo* disksAt(QQmlListProperty<DiskInfo>* prop, qsizetype i);
static qsizetype disksCount(QQmlListProperty<DiskInfo>* prop);
static DiskInfo* disksAt(QQmlListProperty<DiskInfo>* prop, qsizetype i);
QList<DiskInfo*> m_disks;
QPointer<DiskInfo> m_manualPrimaryDisk;
QList<DiskInfo*> m_disks;
QPointer<DiskInfo> m_manualPrimaryDisk;
};
} // namespace ZShell::services
+17 -16
View File
@@ -6,30 +6,31 @@
namespace ZShell::services {
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:
explicit TickingService(QObject* parent = nullptr);
public:
explicit TickingService(QObject* parent = nullptr);
[[nodiscard]] int updateInterval() const;
[[nodiscard]] int updateInterval() const;
signals:
void updateIntervalChanged();
signals:
void updateIntervalChanged();
protected:
void start() final;
void stop() final;
protected:
void start() final;
void stop() final;
virtual void tick() = 0;
virtual void tick() = 0;
private:
void applyInterval(int ms);
private:
void applyInterval(int ms);
QTimer* m_timer;
int m_interval = 1000;
bool m_running = false;
QTimer* m_timer;
int m_interval = 1000;
bool m_running = false;
};
} // namespace ZShell::services
+5 -5
View File
@@ -16,18 +16,18 @@ namespace ZShell::services::usagefmt {
FormatResult UsageFmt::formatKib(qreal kib, qreal total) const {
if (!finitePositive(kib) || !finitePositive(total)) {
return { 0.0, 0.0, "KiB" };
return {0.0, 0.0, "KiB"};
}
if (total >= kGib) {
return { kib / kGib, total / kGib, "TiB" };
return {kib / kGib, total / kGib, "TiB"};
}
if (total >= kMib) {
return { kib / kMib, total / kMib, "GiB" };
return {kib / kMib, total / kMib, "GiB"};
}
if (total >= kKib) {
return { kib / kKib, total / kKib, "MiB" };
return {kib / kKib, total / kKib, "MiB"};
}
return { kib, total, "KiB" };
return {kib, total, "KiB"};
}
} // namespace ZShell::services::usagefmt
+7 -6
View File
@@ -15,19 +15,20 @@ struct FormatResult {
Q_PROPERTY(qreal total MEMBER total CONSTANT)
Q_PROPERTY(QString unit MEMBER unit CONSTANT)
public:
public:
qreal value;
qreal total;
QString unit;
};
class UsageFmt : public QObject {
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
public:
Q_INVOKABLE [[nodiscard]] FormatResult formatKib(qreal kib, qreal total) const;
public:
Q_INVOKABLE [[nodiscard]] FormatResult formatKib(
qreal kib, qreal total) const;
};
} // namespace ZShell::services::usagefmt
+22 -10
View File
@@ -7,17 +7,22 @@
namespace ZShell {
AppEntry::AppEntry(QObject* entry, unsigned int frequency, QObject* parent)
: QObject(parent)
, m_entry(entry)
, m_frequency(frequency) {
: QObject(parent), m_entry(entry), m_frequency(frequency) {
const auto mo = m_entry->metaObject();
const auto tmo = metaObject();
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 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]() {
@@ -115,7 +120,9 @@ AppDb::AppDb(QObject* parent)
db.open();
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 {
@@ -142,7 +149,9 @@ void AppDb::setPath(const QString& path) {
db.open();
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();
}
@@ -170,7 +179,8 @@ void AppDb::incrementFrequency(const QString& id) {
auto db = QSqlDatabase::database(m_uuid);
QSqlQuery query(db);
query.prepare("INSERT INTO frequencies (id, frequency) "
query.prepare(
"INSERT INTO frequencies (id, frequency) "
"VALUES (:id, 1) "
"ON CONFLICT (id) DO UPDATE SET frequency = frequency + 1");
query.bindValue(":id", id);
@@ -186,13 +196,15 @@ void AppDb::incrementFrequency(const QString& id) {
emit appsChanged();
}
} 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 {
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()) {
return a->frequency() > b->frequency();
}
+73 -69
View File
@@ -9,98 +9,102 @@
namespace ZShell {
class AppEntry : public QObject {
Q_OBJECT
QML_ELEMENT
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("AppEntry instances can only be retrieved from an AppDb")
// The actual DesktopEntry, but we don't have access to the type so it's a QObject
Q_PROPERTY(QObject* entry READ entry CONSTANT)
// The actual DesktopEntry, but we don't have access to the type so it's a QObject
Q_PROPERTY(QObject* entry READ entry CONSTANT)
Q_PROPERTY(quint32 frequency READ frequency NOTIFY frequencyChanged)
Q_PROPERTY(QString id READ id CONSTANT)
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
Q_PROPERTY(QString comment READ comment NOTIFY commentChanged)
Q_PROPERTY(QString execString READ execString NOTIFY execStringChanged)
Q_PROPERTY(QString startupClass READ startupClass NOTIFY startupClassChanged)
Q_PROPERTY(QString genericName READ genericName NOTIFY genericNameChanged)
Q_PROPERTY(QString categories READ categories NOTIFY categoriesChanged)
Q_PROPERTY(QString keywords READ keywords NOTIFY keywordsChanged)
Q_PROPERTY(quint32 frequency READ frequency NOTIFY frequencyChanged)
Q_PROPERTY(QString id READ id CONSTANT)
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
Q_PROPERTY(QString comment READ comment NOTIFY commentChanged)
Q_PROPERTY(QString execString READ execString NOTIFY execStringChanged)
Q_PROPERTY(QString startupClass READ startupClass NOTIFY startupClassChanged)
Q_PROPERTY(QString genericName READ genericName NOTIFY genericNameChanged)
Q_PROPERTY(QString categories READ categories NOTIFY categoriesChanged)
Q_PROPERTY(QString keywords READ keywords NOTIFY keywordsChanged)
public:
explicit AppEntry(QObject* entry, quint32 frequency, QObject* parent = nullptr);
public:
explicit AppEntry(
QObject* entry, quint32 frequency, QObject* parent = nullptr);
[[nodiscard]] QObject* entry() const;
[[nodiscard]] QObject* entry() const;
[[nodiscard]] quint32 frequency() const;
void setFrequency(quint32 frequency);
void incrementFrequency();
[[nodiscard]] quint32 frequency() const;
void setFrequency(quint32 frequency);
void incrementFrequency();
[[nodiscard]] QString id() const;
[[nodiscard]] QString name() const;
[[nodiscard]] QString comment() const;
[[nodiscard]] QString execString() const;
[[nodiscard]] QString startupClass() const;
[[nodiscard]] QString genericName() const;
[[nodiscard]] QString categories() const;
[[nodiscard]] QString keywords() const;
[[nodiscard]] QString id() const;
[[nodiscard]] QString name() const;
[[nodiscard]] QString comment() const;
[[nodiscard]] QString execString() const;
[[nodiscard]] QString startupClass() const;
[[nodiscard]] QString genericName() const;
[[nodiscard]] QString categories() const;
[[nodiscard]] QString keywords() const;
signals:
void frequencyChanged();
void nameChanged();
void commentChanged();
void execStringChanged();
void startupClassChanged();
void genericNameChanged();
void categoriesChanged();
void keywordsChanged();
signals:
void frequencyChanged();
void nameChanged();
void commentChanged();
void execStringChanged();
void startupClassChanged();
void genericNameChanged();
void categoriesChanged();
void keywordsChanged();
private:
QObject* m_entry;
quint32 m_frequency;
private:
QObject* m_entry;
quint32 m_frequency;
};
class AppDb : public QObject {
Q_OBJECT
QML_ELEMENT
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(QString uuid READ uuid CONSTANT)
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged REQUIRED)
Q_PROPERTY(QObjectList entries READ entries WRITE setEntries NOTIFY entriesChanged REQUIRED)
Q_PROPERTY(QQmlListProperty<ZShell::AppEntry> apps READ apps NOTIFY appsChanged)
Q_PROPERTY(QString uuid READ uuid CONSTANT)
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged REQUIRED)
Q_PROPERTY(
QObjectList entries READ entries WRITE setEntries NOTIFY entriesChanged
REQUIRED)
Q_PROPERTY(
QQmlListProperty<ZShell::AppEntry> apps READ apps NOTIFY appsChanged)
public:
explicit AppDb(QObject* parent = nullptr);
public:
explicit AppDb(QObject* parent = nullptr);
[[nodiscard]] QString uuid() const;
[[nodiscard]] QString uuid() const;
[[nodiscard]] QString path() const;
void setPath(const QString& path);
[[nodiscard]] QString path() const;
void setPath(const QString& path);
[[nodiscard]] QObjectList entries() const;
void setEntries(const QObjectList& entries);
[[nodiscard]] QObjectList entries() const;
void setEntries(const QObjectList& entries);
[[nodiscard]] QQmlListProperty<AppEntry> apps();
[[nodiscard]] QQmlListProperty<AppEntry> apps();
Q_INVOKABLE void incrementFrequency(const QString& id);
Q_INVOKABLE void incrementFrequency(const QString& id);
signals:
void pathChanged();
void entriesChanged();
void appsChanged();
signals:
void pathChanged();
void entriesChanged();
void appsChanged();
private:
QTimer* m_timer;
private:
QTimer* m_timer;
const QString m_uuid;
QString m_path;
QObjectList m_entries;
QHash<QString, AppEntry*> m_apps;
mutable QList<AppEntry*> m_sortedApps;
const QString m_uuid;
QString m_path;
QObjectList m_entries;
QHash<QString, AppEntry*> m_apps;
mutable QList<AppEntry*> m_sortedApps;
QList<AppEntry*>& getSortedApps() const;
quint32 getFrequency(const QString& id) const;
void updateAppFrequencies();
void updateApps();
QList<AppEntry*>& getSortedApps() const;
quint32 getFrequency(const QString& id) const;
void updateAppFrequencies();
void updateApps();
};
} // namespace ZShell
+56 -16
View File
@@ -16,7 +16,11 @@ ImageAnalyser::ImageAnalyser(QObject* parent)
, m_rescaleSize(128)
, m_dominantColour(0, 0, 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)) {
return;
}
@@ -101,23 +105,40 @@ void ImageAnalyser::requestUpdate() {
return;
}
if (!m_sourceItem || (m_sourceItem->window() && m_sourceItem->window()->isVisible() && m_sourceItem->width() > 0 &&
m_sourceItem->height() > 0)) {
if (!m_sourceItem ||
(m_sourceItem->window() && m_sourceItem->window()->isVisible() &&
m_sourceItem->width() > 0 && m_sourceItem->height() > 0)) {
update();
} else if (m_sourceItem) {
if (!m_sourceItem->window()) {
QObject::connect(m_sourceItem, &QQuickItem::windowChanged, this, &ImageAnalyser::requestUpdate,
QObject::connect(
m_sourceItem,
&QQuickItem::windowChanged,
this,
&ImageAnalyser::requestUpdate,
Qt::SingleShotConnection);
} 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);
}
if (m_sourceItem->width() <= 0) {
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) {
QObject::connect(m_sourceItem, &QQuickItem::heightChanged, this, &ImageAnalyser::requestUpdate,
QObject::connect(
m_sourceItem,
&QQuickItem::heightChanged,
this,
&ImageAnalyser::requestUpdate,
Qt::SingleShotConnection);
}
}
@@ -133,19 +154,30 @@ void ImageAnalyser::update() {
}
if (m_sourceItem) {
const QSharedPointer<const QQuickItemGrabResult> grabResult = m_sourceItem->grabToImage();
QObject::connect(grabResult.data(), &QQuickItemGrabResult::ready, this, [grabResult, this]() {
m_futureWatcher->setFuture(QtConcurrent::run(&ImageAnalyser::analyse, grabResult->image(), m_rescaleSize));
const QSharedPointer<const QQuickItemGrabResult> grabResult =
m_sourceItem->grabToImage();
QObject::connect(
grabResult.data(),
&QQuickItemGrabResult::ready,
this,
[grabResult, this]() {
m_futureWatcher->setFuture(
QtConcurrent::run(
&ImageAnalyser::analyse,
grabResult->image(),
m_rescaleSize));
});
} else {
m_futureWatcher->setFuture(QtConcurrent::run([=, this](QPromise<AnalyseResult>& promise) {
m_futureWatcher->setFuture(
QtConcurrent::run([=, this](QPromise<AnalyseResult>& promise) {
const QImage image(m_source);
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()) {
qWarning() << "ImageAnalyser::analyse: image is null";
return;
@@ -153,8 +185,13 @@ void ImageAnalyser::analyse(QPromise<AnalyseResult>& promise, const QImage& imag
QImage img = image;
if (rescaleSize > 0 && (img.width() > rescaleSize || img.height() > rescaleSize)) {
img = img.scaled(rescaleSize, rescaleSize, Qt::KeepAspectRatio, Qt::FastTransformation);
if (rescaleSize > 0 &&
(img.width() > rescaleSize || img.height() > rescaleSize)) {
img = img.scaled(
rescaleSize,
rescaleSize,
Qt::KeepAspectRatio,
Qt::FastTransformation);
}
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 g = pixel[1] / 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;
}
}
@@ -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
+40 -34
View File
@@ -9,53 +9,59 @@
namespace ZShell {
class ImageAnalyser : public QObject {
Q_OBJECT
QML_ELEMENT
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(QString source READ source WRITE setSource NOTIFY sourceChanged)
Q_PROPERTY(QQuickItem* sourceItem READ sourceItem WRITE setSourceItem NOTIFY 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(QString source READ source WRITE setSource NOTIFY sourceChanged)
Q_PROPERTY(
QQuickItem* sourceItem READ sourceItem WRITE setSourceItem NOTIFY
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)
public:
explicit ImageAnalyser(QObject* parent = nullptr);
public:
explicit ImageAnalyser(QObject* parent = nullptr);
[[nodiscard]] QString source() const;
void setSource(const QString& source);
[[nodiscard]] QString source() const;
void setSource(const QString& source);
[[nodiscard]] QQuickItem* sourceItem() const;
void setSourceItem(QQuickItem* sourceItem);
[[nodiscard]] QQuickItem* sourceItem() const;
void setSourceItem(QQuickItem* sourceItem);
[[nodiscard]] int rescaleSize() const;
void setRescaleSize(int rescaleSize);
[[nodiscard]] int rescaleSize() const;
void setRescaleSize(int rescaleSize);
[[nodiscard]] QColor dominantColour() const;
[[nodiscard]] qreal luminance() const;
[[nodiscard]] QColor dominantColour() const;
[[nodiscard]] qreal luminance() const;
Q_INVOKABLE void requestUpdate();
Q_INVOKABLE void requestUpdate();
signals:
void sourceChanged();
void sourceItemChanged();
void rescaleSizeChanged();
void dominantColourChanged();
void luminanceChanged();
signals:
void sourceChanged();
void sourceItemChanged();
void rescaleSizeChanged();
void dominantColourChanged();
void luminanceChanged();
private:
using AnalyseResult = QPair<QColor, qreal>;
private:
using AnalyseResult = QPair<QColor, qreal>;
QFutureWatcher<AnalyseResult>* const m_futureWatcher;
QFutureWatcher<AnalyseResult>* const m_futureWatcher;
QString m_source;
QQuickItem* m_sourceItem;
int m_rescaleSize;
QString m_source;
QQuickItem* m_sourceItem;
int m_rescaleSize;
QColor m_dominantColour;
qreal m_luminance;
QColor m_dominantColour;
qreal m_luminance;
void update();
static void analyse(QPromise<AnalyseResult>& promise, const QImage& image, int rescaleSize);
void update();
static void analyse(
QPromise<AnalyseResult>& promise, const QImage& image, int rescaleSize);
};
} // namespace ZShell
+6 -3
View File
@@ -4,8 +4,7 @@
namespace ZShell {
Qalculator::Qalculator(QObject* parent)
: QObject(parent) {
Qalculator::Qalculator(QObject* parent) : QObject(parent) {
if (!CALCULATOR) {
new Calculator();
CALCULATOR->loadExchangeRates();
@@ -24,7 +23,11 @@ QString Qalculator::eval(const QString& expr, bool printExpr) const {
std::string parsed;
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;
while (CALCULATOR->message()) {
+7 -6
View File
@@ -6,14 +6,15 @@
namespace ZShell {
class Qalculator : public QObject {
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
public:
explicit Qalculator(QObject* parent = nullptr);
public:
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
+7 -7
View File
@@ -7,9 +7,7 @@
namespace ZShell {
Requests::Requests(QObject* parent)
: QObject(parent)
, m_manager(new QNetworkAccessManager(this)) {
}
: QObject(parent), m_manager(new QNetworkAccessManager(this)) {}
void Requests::get(const QUrl& url, QJSValue onSuccess, QJSValue onError) const {
if (!onSuccess.isCallable()) {
@@ -20,13 +18,15 @@ void Requests::get(const QUrl& url, QJSValue onSuccess, QJSValue onError) const
QNetworkRequest request(url);
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) {
onSuccess.call({ QString(reply->readAll()) });
onSuccess.call({QString(reply->readAll())});
} else if (onError.isCallable()) {
onError.call({ reply->errorString() });
onError.call({reply->errorString()});
} else {
qWarning() << "Requests::get: request failed with error" << reply->errorString();
qWarning() << "Requests::get: request failed with error"
<< reply->errorString();
}
reply->deleteLater();
+11 -8
View File
@@ -7,17 +7,20 @@
namespace ZShell {
class Requests : public QObject {
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
public:
explicit Requests(QObject* parent = nullptr);
public:
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:
QNetworkAccessManager* m_manager;
private:
QNetworkAccessManager* m_manager;
};
} // namespace ZShell
+14 -5
View File
@@ -6,7 +6,13 @@
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)
, m_closed(false)
, m_title(title)
@@ -94,15 +100,18 @@ void Toast::unlock(QObject* sender) {
}
}
Toaster::Toaster(QObject* parent)
: QObject(parent) {
}
Toaster::Toaster(QObject* parent) : QObject(parent) {}
QQmlListProperty<Toast> Toaster::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);
QObject::connect(toast, &Toast::finishedClose, this, [toast, this]() {
if (m_toasts.removeOne(toast)) {
+56 -51
View File
@@ -8,75 +8,80 @@
namespace ZShell {
class Toast : public QObject {
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("Toast instances can only be retrieved from a Toaster")
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("Toast instances can only be retrieved from a Toaster")
Q_PROPERTY(bool closed READ closed NOTIFY closedChanged)
Q_PROPERTY(QString title READ title CONSTANT)
Q_PROPERTY(QString message READ message CONSTANT)
Q_PROPERTY(QString icon READ icon CONSTANT)
Q_PROPERTY(int timeout READ timeout CONSTANT)
Q_PROPERTY(Type type READ type CONSTANT)
Q_PROPERTY(bool closed READ closed NOTIFY closedChanged)
Q_PROPERTY(QString title READ title CONSTANT)
Q_PROPERTY(QString message READ message CONSTANT)
Q_PROPERTY(QString icon READ icon CONSTANT)
Q_PROPERTY(int timeout READ timeout CONSTANT)
Q_PROPERTY(Type type READ type CONSTANT)
public:
enum class Type {
Info = 0,
Success,
Warning,
Error
};
Q_ENUM(Type)
public:
enum class Type { Info = 0, Success, Warning, Error };
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);
[[nodiscard]] bool closed() const;
[[nodiscard]] QString title() const;
[[nodiscard]] QString message() const;
[[nodiscard]] QString icon() const;
[[nodiscard]] int timeout() const;
[[nodiscard]] Type type() const;
[[nodiscard]] bool closed() const;
[[nodiscard]] QString title() const;
[[nodiscard]] QString message() const;
[[nodiscard]] QString icon() const;
[[nodiscard]] int timeout() const;
[[nodiscard]] Type type() const;
Q_INVOKABLE void close();
Q_INVOKABLE void lock(QObject* sender);
Q_INVOKABLE void unlock(QObject* sender);
Q_INVOKABLE void close();
Q_INVOKABLE void lock(QObject* sender);
Q_INVOKABLE void unlock(QObject* sender);
signals:
void closedChanged();
void finishedClose();
signals:
void closedChanged();
void finishedClose();
private:
QSet<QObject*> m_locks;
private:
QSet<QObject*> m_locks;
bool m_closed;
QString m_title;
QString m_message;
QString m_icon;
Type m_type;
int m_timeout;
bool m_closed;
QString m_title;
QString m_message;
QString m_icon;
Type m_type;
int m_timeout;
};
class Toaster : public QObject {
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
Q_PROPERTY(QQmlListProperty<ZShell::Toast> toasts READ toasts NOTIFY toastsChanged)
Q_PROPERTY(
QQmlListProperty<ZShell::Toast> toasts READ toasts NOTIFY toastsChanged)
public:
explicit Toaster(QObject* parent = nullptr);
public:
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(),
ZShell::Toast::Type type = Toast::Type::Info, int timeout = 5000);
Q_INVOKABLE void toast(
const QString& title,
const QString& message,
const QString& icon = QString(),
ZShell::Toast::Type type = Toast::Type::Info,
int timeout = 5000);
signals:
void toastsChanged();
signals:
void toastsChanged();
private:
QList<Toast*> m_toasts;
private:
QList<Toast*> m_toasts;
};
} // namespace ZShell
+53 -65
View File
@@ -20,15 +20,12 @@
namespace ZShell {
// ============================================================
// saveItem
// ============================================================
void ZShellIo::saveItem(QQuickItem* target, const QUrl& path) {
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());
}
@@ -36,11 +33,13 @@ void ZShellIo::saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved)
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);
}
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());
}
@@ -49,8 +48,7 @@ void ZShellIo::saveItem(
const QUrl& path,
const QRect& rect,
QJSValue onSaved,
QJSValue onFailed
) {
QJSValue onFailed) {
if (!target) {
qWarning() << "ZShellIo::saveItem: a target is required";
return;
@@ -62,8 +60,7 @@ void ZShellIo::saveItem(
}
if (!target->window()) {
qWarning() << "ZShellIo::saveItem: unable to save target"
<< target
qWarning() << "ZShellIo::saveItem: unable to save target" << target
<< "without a window";
return;
}
@@ -77,8 +74,8 @@ void ZShellIo::saveItem(
rect.left() * scale,
rect.top() * scale,
rect.width() * scale,
rect.height() * scale
).toRect();
rect.height() * scale)
.toRect();
}
const QSharedPointer<const QQuickItemGrabResult> grabResult =
@@ -89,7 +86,8 @@ void ZShellIo::saveItem(
&QQuickItemGrabResult::ready,
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();
if (scaledRect.isValid()) {
@@ -116,39 +114,34 @@ void ZShellIo::saveItem(
auto* watcher = new QFutureWatcher<bool>(this);
auto* engine = qmlEngine(this);
QObject::connect(watcher, &QFutureWatcher<bool>::finished, this, [=]() {
QObject::connect(
watcher, &QFutureWatcher<bool>::finished, this, [=]() {
if (watcher->result()) {
if (onSaved.isCallable() && engine) {
onSaved.call({
engine->toScriptValue(path.toLocalFile()),
engine->toScriptValue(path)
});
onSaved.call(
{engine->toScriptValue(path.toLocalFile()),
engine->toScriptValue(path)});
}
} else {
qWarning() << "ZShellIo::saveItem: failed to save" << path;
qWarning()
<< "ZShellIo::saveItem: failed to save" << path;
if (onFailed.isCallable() && engine) {
onFailed.call({
engine->toScriptValue(path)
});
onFailed.call({engine->toScriptValue(path)});
}
}
watcher->deleteLater();
});
watcher->setFuture(future);
}
);
});
}
// ============================================================
// cacheImage
// ============================================================
void ZShellIo::cacheImage(const QUrl& source, const QString& cacheDir) {
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());
}
@@ -156,8 +149,7 @@ void ZShellIo::cacheImage(
const QUrl& source,
const QString& cacheDir,
QJSValue onSaved,
QJSValue onFailed
) {
QJSValue onFailed) {
if (cacheDir.isEmpty()) {
qWarning() << "ZShellIo::cacheImage: cacheDir is empty";
return;
@@ -165,13 +157,13 @@ void ZShellIo::cacheImage(
QImage 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);
if (onFailed.isCallable() && engine) {
onFailed.call({
engine->toScriptValue(source),
engine->toScriptValue(cacheDir)
});
onFailed.call(
{engine->toScriptValue(source),
engine->toScriptValue(cacheDir)});
}
return;
}
@@ -181,12 +173,12 @@ void ZShellIo::cacheImage(
return QString();
}
const QImage normalized = image.convertToFormat(QImage::Format_RGBA8888);
const QImage normalized =
image.convertToFormat(QImage::Format_RGBA8888);
const QByteArray bytes(
reinterpret_cast<const char*>(normalized.constBits()),
qsizetype(normalized.sizeInBytes())
);
normalized.sizeInBytes());
const QByteArray digest =
QCryptographicHash::hash(bytes, QCryptographicHash::Sha256).toHex();
@@ -196,7 +188,8 @@ void ZShellIo::cacheImage(
return QString();
}
const QString finalPath = dir.filePath(QString::fromLatin1(digest) + ".png");
const QString finalPath =
dir.filePath(QString::fromLatin1(digest) + ".png");
if (QFile::exists(finalPath)) {
return finalPath;
@@ -226,18 +219,16 @@ void ZShellIo::cacheImage(
if (!finalPath.isEmpty()) {
if (onSaved.isCallable() && engine) {
onSaved.call({
engine->toScriptValue(finalPath),
engine->toScriptValue(QUrl::fromLocalFile(finalPath))
});
onSaved.call(
{engine->toScriptValue(finalPath),
engine->toScriptValue(QUrl::fromLocalFile(finalPath))});
}
} else {
qWarning() << "ZShellIo::cacheImage: failed to cache" << source;
if (onFailed.isCallable() && engine) {
onFailed.call({
engine->toScriptValue(source),
engine->toScriptValue(cacheDir)
});
onFailed.call(
{engine->toScriptValue(source),
engine->toScriptValue(cacheDir)});
}
}
@@ -247,10 +238,6 @@ void ZShellIo::cacheImage(
watcher->setFuture(future);
}
// ============================================================
// loadSourceImage
// ============================================================
bool ZShellIo::loadSourceImage(const QUrl& source, QImage& image) const {
image = QImage();
@@ -270,8 +257,7 @@ bool ZShellIo::loadSourceImage(const QUrl& source, QImage& image) const {
const QString providerId = source.host();
const QString imageId =
source.path().startsWith('/')
const QString imageId = source.path().startsWith('/')
? source.path().mid(1)
: source.path();
@@ -284,7 +270,8 @@ bool ZShellIo::loadSourceImage(const QUrl& source, QImage& image) const {
auto* provider = dynamic_cast<QQuickImageProvider*>(providerBase);
if (!provider) {
qWarning() << "ZShellIo::loadSourceImage: provider is not a QQuickImageProvider"
qWarning() << "ZShellIo::loadSourceImage: provider is not a "
"QQuickImageProvider"
<< providerId;
return false;
}
@@ -313,17 +300,16 @@ bool ZShellIo::loadSourceImage(const QUrl& source, QImage& image) const {
return false;
}
// ============================================================
// File ops
// ============================================================
bool ZShellIo::copyFile(const QUrl& source, const QUrl& target, bool overwrite) const {
bool ZShellIo::copyFile(
const QUrl& source, const QUrl& target, bool overwrite) const {
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;
}
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;
}
@@ -336,7 +322,8 @@ bool ZShellIo::copyFile(const QUrl& source, const QUrl& target, bool overwrite)
bool ZShellIo::deleteFile(const QUrl& path) const {
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;
}
@@ -345,7 +332,8 @@ bool ZShellIo::deleteFile(const QUrl& path) const {
QString ZShellIo::toLocalFile(const QUrl& url) const {
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();
}
+21 -21
View File
@@ -10,31 +10,31 @@
namespace ZShell {
class ZShellIo : public QObject {
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
public:
// clang-format off
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved, QJSValue onFailed);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved, QJSValue onFailed);
public:
// clang-format off
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved, QJSValue onFailed);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved, QJSValue onFailed);
Q_INVOKABLE void cacheImage(const QUrl& source, const QString& cacheDir);
Q_INVOKABLE void cacheImage(const QUrl& source, const QString& cacheDir, QJSValue onSaved);
Q_INVOKABLE void cacheImage(const QUrl& source, const QString& cacheDir, QJSValue onSaved, QJSValue onFailed);
// clang-format on
Q_INVOKABLE void cacheImage(const QUrl& source, const QString& cacheDir);
Q_INVOKABLE void cacheImage(const QUrl& source, const QString& cacheDir, QJSValue onSaved);
Q_INVOKABLE void cacheImage(const QUrl& source, const QString& cacheDir, QJSValue onSaved, QJSValue onFailed);
// clang-format on
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]] QString toLocalFile(const QUrl& url) 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]] QString toLocalFile(const QUrl& url) const;
private:
bool loadSourceImage(const QUrl& source, QImage& image) const;
private:
bool loadSourceImage(const QUrl& source, QImage& image) const;
};
} // namespace ZShell
+44 -18
View File
@@ -27,15 +27,22 @@ void ZUtils::saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved) {
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);
}
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());
}
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) {
qCWarning(lcZUtils) << "saveItem: a target is required";
return;
@@ -47,20 +54,29 @@ void ZUtils::saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, Q
}
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;
}
auto scaledRect = rect;
const qreal scale = target->window()->devicePixelRatio();
if (rect.isValid() && !qFuzzyCompare(scale + 1.0, 2.0)) {
scaledRect =
QRectF(rect.left() * scale, rect.top() * scale, rect.width() * scale, rect.height() * scale).toRect();
scaledRect = QRectF(
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]() {
const auto future = QtConcurrent::run([=]() {
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* engine = qmlEngine(this);
QObject::connect(watcher, &QFutureWatcher<bool>::finished, this, [=]() {
QObject::connect(
watcher, &QFutureWatcher<bool>::finished, this, [=]() {
if (watcher->result()) {
if (onSaved.isCallable()) {
QJSValueList args = { QJSValue(path.toLocalFile()) };
QJSValueList args = {QJSValue(path.toLocalFile())};
if (engine) {
args << engine->toScriptValue(QVariant::fromValue(path));
args << engine->toScriptValue(
QVariant::fromValue(path));
}
onSaved.call(args);
}
} else {
qCWarning(lcZUtils) << "saveItem: failed to save" << path;
qCWarning(lcZUtils)
<< "saveItem: failed to save" << path;
if (onFailed.isCallable()) {
if (engine) {
onFailed.call({ engine->toScriptValue(QVariant::fromValue(path)) });
onFailed.call({engine->toScriptValue(
QVariant::fromValue(path))});
} else {
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) {
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;
}
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;
}
if (overwrite && QFile::exists(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;
}
}
@@ -124,7 +148,8 @@ bool ZUtils::copyFile(const QUrl& source, const QUrl& target, bool overwrite) {
bool ZUtils::deleteFile(const QUrl& path) {
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;
}
@@ -133,7 +158,8 @@ bool ZUtils::deleteFile(const QUrl& path) {
QString ZUtils::toLocalFile(const QUrl& url) {
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();
}
+19 -18
View File
@@ -8,28 +8,29 @@
namespace ZShell {
class ZUtils : public QObject {
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
public:
// clang-format off
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved, QJSValue onFailed);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved, QJSValue onFailed);
// clang-format on
public:
// clang-format off
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved, QJSValue onFailed);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved);
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved, QJSValue onFailed);
// clang-format on
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 QString toLocalFile(const QUrl& url);
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 QString toLocalFile(const QUrl& url);
Q_INVOKABLE static qreal clamp(qreal value, qreal min, qreal max);
Q_INVOKABLE static qreal clamp(qreal value, qreal min, qreal max);
[[nodiscard]] QString version() const;
[[nodiscard]] QString qtVersion() const;
[[nodiscard]] QString version() const;
[[nodiscard]] QString qtVersion() const;
};
} // namespace ZShell