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
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:
+3
-3
@@ -1,15 +1,15 @@
|
|||||||
---
|
---
|
||||||
BasedOnStyle: LLVM
|
BasedOnStyle: LLVM
|
||||||
AccessModifierOffset: 0
|
AccessModifierOffset: 0
|
||||||
AlignAfterOpenBracket: Align
|
AlignAfterOpenBracket: AlwaysBreak
|
||||||
AlignConsecutiveAssignments: false
|
AlignConsecutiveAssignments: false
|
||||||
AlignConsecutiveDeclarations: false
|
AlignConsecutiveDeclarations: false
|
||||||
AlignOperands: Align
|
AlignOperands: Align
|
||||||
AllowAllParametersOfDeclarationOnNextLine: true
|
AllowAllParametersOfDeclarationOnNextLine: true
|
||||||
AllowShortFunctionsOnASingleLine: Inline
|
AllowShortFunctionsOnASingleLine: Inline
|
||||||
AllowShortIfStatementsOnASingleLine: Never
|
AllowShortIfStatementsOnASingleLine: WithoutElse
|
||||||
AlwaysBreakBeforeMultilineStrings: false
|
AlwaysBreakBeforeMultilineStrings: false
|
||||||
AlwaysBreakTemplateDeclarations: Yes
|
AlwaysBreakTemplateDeclarations: MultiLine
|
||||||
BinPackArguments: false
|
BinPackArguments: false
|
||||||
BinPackParameters: false
|
BinPackParameters: false
|
||||||
BreakBeforeBraces: Attach
|
BreakBeforeBraces: Attach
|
||||||
|
|||||||
@@ -2,9 +2,7 @@
|
|||||||
#include "blobinvertedrect.hpp"
|
#include "blobinvertedrect.hpp"
|
||||||
#include "blobshape.hpp"
|
#include "blobshape.hpp"
|
||||||
|
|
||||||
BlobGroup::BlobGroup(QObject* parent)
|
BlobGroup::BlobGroup(QObject* parent) : QObject(parent) {}
|
||||||
: QObject(parent) {
|
|
||||||
}
|
|
||||||
|
|
||||||
BlobGroup::~BlobGroup() {
|
BlobGroup::~BlobGroup() {
|
||||||
for (auto* shape : std::as_const(m_shapes))
|
for (auto* shape : std::as_const(m_shapes))
|
||||||
@@ -14,24 +12,21 @@ BlobGroup::~BlobGroup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BlobGroup::setSmoothing(qreal s) {
|
void BlobGroup::setSmoothing(qreal s) {
|
||||||
if (qFuzzyCompare(m_smoothing, s))
|
if (qFuzzyCompare(m_smoothing, s)) return;
|
||||||
return;
|
|
||||||
m_smoothing = s;
|
m_smoothing = s;
|
||||||
emit smoothingChanged();
|
emit smoothingChanged();
|
||||||
markDirty();
|
markDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlobGroup::setColor(const QColor& c) {
|
void BlobGroup::setColor(const QColor& c) {
|
||||||
if (m_color == c)
|
if (m_color == c) return;
|
||||||
return;
|
|
||||||
m_color = c;
|
m_color = c;
|
||||||
emit colorChanged();
|
emit colorChanged();
|
||||||
markDirty();
|
markDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlobGroup::addShape(BlobShape* shape) {
|
void BlobGroup::addShape(BlobShape* shape) {
|
||||||
if (!shape || m_shapes.contains(shape))
|
if (!shape || m_shapes.contains(shape)) return;
|
||||||
return;
|
|
||||||
m_shapes.append(shape);
|
m_shapes.append(shape);
|
||||||
markDirty();
|
markDirty();
|
||||||
}
|
}
|
||||||
@@ -42,15 +37,13 @@ void BlobGroup::removeShape(BlobShape* shape) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BlobGroup::setInvertedRect(BlobInvertedRect* rect) {
|
void BlobGroup::setInvertedRect(BlobInvertedRect* rect) {
|
||||||
if (m_invertedRect == rect)
|
if (m_invertedRect == rect) return;
|
||||||
return;
|
|
||||||
m_invertedRect = rect;
|
m_invertedRect = rect;
|
||||||
markDirty();
|
markDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlobGroup::clearInvertedRect(BlobInvertedRect* rect) {
|
void BlobGroup::clearInvertedRect(BlobInvertedRect* rect) {
|
||||||
if (m_invertedRect != rect)
|
if (m_invertedRect != rect) return;
|
||||||
return;
|
|
||||||
m_invertedRect = nullptr;
|
m_invertedRect = nullptr;
|
||||||
markDirty();
|
markDirty();
|
||||||
}
|
}
|
||||||
@@ -75,15 +68,19 @@ void BlobGroup::markShapeDirty(BlobShape* source) {
|
|||||||
|
|
||||||
// Use cached padded rects to find spatial neighbors
|
// Use cached padded rects to find spatial neighbors
|
||||||
const float pad = static_cast<float>(m_smoothing) * 2.0f;
|
const float pad = static_cast<float>(m_smoothing) * 2.0f;
|
||||||
const QRectF srcRect(static_cast<double>(source->m_cachedPaddedX - pad),
|
const QRectF srcRect(
|
||||||
static_cast<double>(source->m_cachedPaddedY - pad), static_cast<double>(source->m_cachedPaddedW + pad * 2.0f),
|
static_cast<double>(source->m_cachedPaddedX - pad),
|
||||||
|
static_cast<double>(source->m_cachedPaddedY - pad),
|
||||||
|
static_cast<double>(source->m_cachedPaddedW + pad * 2.0f),
|
||||||
static_cast<double>(source->m_cachedPaddedH + pad * 2.0f));
|
static_cast<double>(source->m_cachedPaddedH + pad * 2.0f));
|
||||||
|
|
||||||
for (auto* shape : std::as_const(m_shapes)) {
|
for (auto* shape : std::as_const(m_shapes)) {
|
||||||
if (shape == source)
|
if (shape == source) continue;
|
||||||
continue;
|
const QRectF otherRect(
|
||||||
const QRectF otherRect(static_cast<double>(shape->m_cachedPaddedX), static_cast<double>(shape->m_cachedPaddedY),
|
static_cast<double>(shape->m_cachedPaddedX),
|
||||||
static_cast<double>(shape->m_cachedPaddedW), static_cast<double>(shape->m_cachedPaddedH));
|
static_cast<double>(shape->m_cachedPaddedY),
|
||||||
|
static_cast<double>(shape->m_cachedPaddedW),
|
||||||
|
static_cast<double>(shape->m_cachedPaddedH));
|
||||||
if (srcRect.intersects(otherRect)) {
|
if (srcRect.intersects(otherRect)) {
|
||||||
shape->polish();
|
shape->polish();
|
||||||
shape->update();
|
shape->update();
|
||||||
@@ -97,8 +94,7 @@ void BlobGroup::markShapeDirty(BlobShape* source) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BlobGroup::ensurePhysicsUpdated() {
|
void BlobGroup::ensurePhysicsUpdated() {
|
||||||
if (m_physicsUpdated)
|
if (m_physicsUpdated) return;
|
||||||
return;
|
|
||||||
m_physicsUpdated = true;
|
m_physicsUpdated = true;
|
||||||
for (auto* shape : std::as_const(m_shapes))
|
for (auto* shape : std::as_const(m_shapes))
|
||||||
shape->updatePhysics();
|
shape->updatePhysics();
|
||||||
|
|||||||
@@ -11,22 +11,20 @@ class BlobInvertedRect;
|
|||||||
class BlobGroup : public QObject {
|
class BlobGroup : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
Q_PROPERTY(qreal smoothing READ smoothing WRITE setSmoothing NOTIFY smoothingChanged)
|
Q_PROPERTY(
|
||||||
|
qreal smoothing READ smoothing WRITE setSmoothing NOTIFY
|
||||||
|
smoothingChanged)
|
||||||
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
|
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit BlobGroup(QObject* parent = nullptr);
|
explicit BlobGroup(QObject* parent = nullptr);
|
||||||
~BlobGroup() override;
|
~BlobGroup() override;
|
||||||
|
|
||||||
[[nodiscard]] qreal smoothing() const {
|
[[nodiscard]] qreal smoothing() const { return m_smoothing; }
|
||||||
return m_smoothing;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setSmoothing(qreal s);
|
void setSmoothing(qreal s);
|
||||||
|
|
||||||
[[nodiscard]] QColor color() const {
|
[[nodiscard]] QColor color() const { return m_color; }
|
||||||
return m_color;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setColor(const QColor& c);
|
void setColor(const QColor& c);
|
||||||
|
|
||||||
@@ -36,9 +34,7 @@ void removeShape(BlobShape* shape);
|
|||||||
void setInvertedRect(BlobInvertedRect* rect);
|
void setInvertedRect(BlobInvertedRect* rect);
|
||||||
void clearInvertedRect(BlobInvertedRect* rect);
|
void clearInvertedRect(BlobInvertedRect* rect);
|
||||||
|
|
||||||
[[nodiscard]] const QList<BlobShape*>& shapes() const {
|
[[nodiscard]] const QList<BlobShape*>& shapes() const { return m_shapes; }
|
||||||
return m_shapes;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] BlobInvertedRect* invertedRect() const {
|
[[nodiscard]] BlobInvertedRect* invertedRect() const {
|
||||||
return m_invertedRect;
|
return m_invertedRect;
|
||||||
|
|||||||
@@ -5,12 +5,9 @@
|
|||||||
#include <qsggeometry.h>
|
#include <qsggeometry.h>
|
||||||
#include <qsgnode.h>
|
#include <qsgnode.h>
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
BlobInvertedRect::BlobInvertedRect(QQuickItem* parent)
|
BlobInvertedRect::BlobInvertedRect(QQuickItem* parent) : BlobShape(parent) {}
|
||||||
: BlobShape(parent) {
|
|
||||||
}
|
|
||||||
|
|
||||||
static void setFrameIndices(quint16* idx) {
|
static void setFrameIndices(quint16* idx) {
|
||||||
// Top strip: 0-1-4, 1-5-4
|
// Top strip: 0-1-4, 1-5-4
|
||||||
@@ -43,7 +40,8 @@ static void setFrameIndices(quint16* idx) {
|
|||||||
idx[23] = 7;
|
idx[23] = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
QSGNode* BlobInvertedRect::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) {
|
QSGNode* BlobInvertedRect::updatePaintNode(
|
||||||
|
QSGNode* oldNode, UpdatePaintNodeData*) {
|
||||||
if (!m_group) {
|
if (!m_group) {
|
||||||
delete oldNode;
|
delete oldNode;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -71,8 +69,11 @@ QSGNode* BlobInvertedRect::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData
|
|||||||
delete oldNode;
|
delete oldNode;
|
||||||
node = new QSGGeometryNode;
|
node = new QSGGeometryNode;
|
||||||
|
|
||||||
auto* geometry =
|
auto* geometry = new QSGGeometry(
|
||||||
new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 8, 24, QSGGeometry::UnsignedShortType);
|
QSGGeometry::defaultAttributes_TexturedPoint2D(),
|
||||||
|
8,
|
||||||
|
24,
|
||||||
|
QSGGeometry::UnsignedShortType);
|
||||||
geometry->setDrawingMode(QSGGeometry::DrawTriangles);
|
geometry->setDrawingMode(QSGGeometry::DrawTriangles);
|
||||||
node->setGeometry(geometry);
|
node->setGeometry(geometry);
|
||||||
node->setFlag(QSGNode::OwnsGeometry);
|
node->setFlag(QSGNode::OwnsGeometry);
|
||||||
@@ -120,10 +121,17 @@ QSGNode* BlobInvertedRect::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData
|
|||||||
material->m_color = m_group->color();
|
material->m_color = m_group->color();
|
||||||
material->m_hasInverted = m_cachedHasInverted ? 1 : 0;
|
material->m_hasInverted = m_cachedHasInverted ? 1 : 0;
|
||||||
material->m_invertedRadius = m_cachedInvertedRadius;
|
material->m_invertedRadius = m_cachedInvertedRadius;
|
||||||
memcpy(material->m_invertedOuter, m_cachedInvertedOuter, sizeof(m_cachedInvertedOuter));
|
memcpy(
|
||||||
memcpy(material->m_invertedInner, m_cachedInvertedInner, sizeof(m_cachedInvertedInner));
|
material->m_invertedOuter,
|
||||||
|
m_cachedInvertedOuter,
|
||||||
|
sizeof(m_cachedInvertedOuter));
|
||||||
|
memcpy(
|
||||||
|
material->m_invertedInner,
|
||||||
|
m_cachedInvertedInner,
|
||||||
|
sizeof(m_cachedInvertedInner));
|
||||||
|
|
||||||
const int count = static_cast<int>(qMin(m_cachedRects.size(), qsizetype(16)));
|
const int count =
|
||||||
|
static_cast<int>(qMin(m_cachedRects.size(), qsizetype(16)));
|
||||||
material->m_rectCount = count;
|
material->m_rectCount = count;
|
||||||
for (int i = 0; i < count; ++i)
|
for (int i = 0; i < count; ++i)
|
||||||
material->m_rects[i] = m_cachedRects[i];
|
material->m_rects[i] = m_cachedRects[i];
|
||||||
@@ -134,52 +142,41 @@ QSGNode* BlobInvertedRect::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData
|
|||||||
}
|
}
|
||||||
|
|
||||||
BlobInvertedRect::~BlobInvertedRect() {
|
BlobInvertedRect::~BlobInvertedRect() {
|
||||||
if (m_group)
|
if (m_group) m_group->clearInvertedRect(this);
|
||||||
m_group->clearInvertedRect(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlobInvertedRect::setBorderLeft(qreal v) {
|
void BlobInvertedRect::setBorderLeft(qreal v) {
|
||||||
if (qFuzzyCompare(m_borderLeft, v))
|
if (qFuzzyCompare(m_borderLeft, v)) return;
|
||||||
return;
|
|
||||||
m_borderLeft = v;
|
m_borderLeft = v;
|
||||||
emit borderLeftChanged();
|
emit borderLeftChanged();
|
||||||
if (m_group)
|
if (m_group) m_group->markDirty();
|
||||||
m_group->markDirty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlobInvertedRect::setBorderRight(qreal v) {
|
void BlobInvertedRect::setBorderRight(qreal v) {
|
||||||
if (qFuzzyCompare(m_borderRight, v))
|
if (qFuzzyCompare(m_borderRight, v)) return;
|
||||||
return;
|
|
||||||
m_borderRight = v;
|
m_borderRight = v;
|
||||||
emit borderRightChanged();
|
emit borderRightChanged();
|
||||||
if (m_group)
|
if (m_group) m_group->markDirty();
|
||||||
m_group->markDirty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlobInvertedRect::setBorderTop(qreal v) {
|
void BlobInvertedRect::setBorderTop(qreal v) {
|
||||||
if (qFuzzyCompare(m_borderTop, v))
|
if (qFuzzyCompare(m_borderTop, v)) return;
|
||||||
return;
|
|
||||||
m_borderTop = v;
|
m_borderTop = v;
|
||||||
emit borderTopChanged();
|
emit borderTopChanged();
|
||||||
if (m_group)
|
if (m_group) m_group->markDirty();
|
||||||
m_group->markDirty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlobInvertedRect::setBorderBottom(qreal v) {
|
void BlobInvertedRect::setBorderBottom(qreal v) {
|
||||||
if (qFuzzyCompare(m_borderBottom, v))
|
if (qFuzzyCompare(m_borderBottom, v)) return;
|
||||||
return;
|
|
||||||
m_borderBottom = v;
|
m_borderBottom = v;
|
||||||
emit borderBottomChanged();
|
emit borderBottomChanged();
|
||||||
if (m_group)
|
if (m_group) m_group->markDirty();
|
||||||
m_group->markDirty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlobInvertedRect::registerWithGroup() {
|
void BlobInvertedRect::registerWithGroup() {
|
||||||
if (m_group)
|
if (m_group) m_group->setInvertedRect(this);
|
||||||
m_group->setInvertedRect(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlobInvertedRect::unregisterFromGroup() {
|
void BlobInvertedRect::unregisterFromGroup() {
|
||||||
if (m_group)
|
if (m_group) m_group->clearInvertedRect(this);
|
||||||
m_group->clearInvertedRect(this);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,36 +7,36 @@
|
|||||||
class BlobInvertedRect : public BlobShape {
|
class BlobInvertedRect : public BlobShape {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
Q_PROPERTY(qreal borderLeft READ borderLeft WRITE setBorderLeft NOTIFY borderLeftChanged)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(qreal borderRight READ borderRight WRITE setBorderRight NOTIFY borderRightChanged)
|
qreal borderLeft READ borderLeft WRITE setBorderLeft NOTIFY
|
||||||
Q_PROPERTY(qreal borderTop READ borderTop WRITE setBorderTop NOTIFY borderTopChanged)
|
borderLeftChanged)
|
||||||
Q_PROPERTY(qreal borderBottom READ borderBottom WRITE setBorderBottom NOTIFY borderBottomChanged)
|
Q_PROPERTY(
|
||||||
|
qreal borderRight READ borderRight WRITE setBorderRight NOTIFY
|
||||||
|
borderRightChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
qreal borderTop READ borderTop WRITE setBorderTop NOTIFY
|
||||||
|
borderTopChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
qreal borderBottom READ borderBottom WRITE setBorderBottom NOTIFY
|
||||||
|
borderBottomChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit BlobInvertedRect(QQuickItem* parent = nullptr);
|
explicit BlobInvertedRect(QQuickItem* parent = nullptr);
|
||||||
~BlobInvertedRect() override;
|
~BlobInvertedRect() override;
|
||||||
|
|
||||||
[[nodiscard]] qreal borderLeft() const {
|
[[nodiscard]] qreal borderLeft() const { return m_borderLeft; }
|
||||||
return m_borderLeft;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setBorderLeft(qreal v);
|
void setBorderLeft(qreal v);
|
||||||
|
|
||||||
[[nodiscard]] qreal borderRight() const {
|
[[nodiscard]] qreal borderRight() const { return m_borderRight; }
|
||||||
return m_borderRight;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setBorderRight(qreal v);
|
void setBorderRight(qreal v);
|
||||||
|
|
||||||
[[nodiscard]] qreal borderTop() const {
|
[[nodiscard]] qreal borderTop() const { return m_borderTop; }
|
||||||
return m_borderTop;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setBorderTop(qreal v);
|
void setBorderTop(qreal v);
|
||||||
|
|
||||||
[[nodiscard]] qreal borderBottom() const {
|
[[nodiscard]] qreal borderBottom() const { return m_borderBottom; }
|
||||||
return m_borderBottom;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setBorderBottom(qreal v);
|
void setBorderBottom(qreal v);
|
||||||
|
|
||||||
@@ -47,9 +47,7 @@ void borderTopChanged();
|
|||||||
void borderBottomChanged();
|
void borderBottomChanged();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
[[nodiscard]] bool isInvertedRect() const override {
|
[[nodiscard]] bool isInvertedRect() const override { return true; }
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
QSGNode* updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) override;
|
QSGNode* updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) override;
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
#include "blobmaterial.hpp"
|
#include "blobmaterial.hpp"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
static_assert(sizeof(decltype(BlobRectData::excludeMask)) == sizeof(float),
|
static_assert(
|
||||||
|
sizeof(decltype(BlobRectData::excludeMask)) == sizeof(float),
|
||||||
"BlobMaterial packs excludeMask into a float slot via memcpy");
|
"BlobMaterial packs excludeMask into a float slot via memcpy");
|
||||||
|
|
||||||
QSGMaterialType* BlobMaterial::type() const {
|
QSGMaterialType* BlobMaterial::type() const {
|
||||||
@@ -10,15 +13,14 @@ QSGMaterialType* BlobMaterial::type() const {
|
|||||||
return &s_type;
|
return &s_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
QSGMaterialShader* BlobMaterial::createShader(QSGRendererInterface::RenderMode) const {
|
QSGMaterialShader* BlobMaterial::createShader(
|
||||||
|
QSGRendererInterface::RenderMode) const {
|
||||||
return new BlobMaterialShader;
|
return new BlobMaterialShader;
|
||||||
}
|
}
|
||||||
|
|
||||||
int BlobMaterial::compare(const QSGMaterial* other) const {
|
int BlobMaterial::compare(const QSGMaterial* other) const {
|
||||||
if (this < other)
|
if (this < other) return -1;
|
||||||
return -1;
|
if (this > other) return 1;
|
||||||
if (this > other)
|
|
||||||
return 1;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,7 +29,8 @@ BlobMaterialShader::BlobMaterialShader() {
|
|||||||
setShaderFileName(FragmentStage, QStringLiteral(":/shaders/blob.frag.qsb"));
|
setShaderFileName(FragmentStage, QStringLiteral(":/shaders/blob.frag.qsb"));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BlobMaterialShader::updateUniformData(RenderState& state, QSGMaterial* newMaterial, QSGMaterial* oldMaterial) {
|
bool BlobMaterialShader::updateUniformData(
|
||||||
|
RenderState& state, QSGMaterial* newMaterial, QSGMaterial* oldMaterial) {
|
||||||
Q_UNUSED(oldMaterial);
|
Q_UNUSED(oldMaterial);
|
||||||
auto* mat = static_cast<BlobMaterial*>(newMaterial);
|
auto* mat = static_cast<BlobMaterial*>(newMaterial);
|
||||||
QByteArray* buf = state.uniformData();
|
QByteArray* buf = state.uniformData();
|
||||||
@@ -59,10 +62,10 @@ bool BlobMaterialShader::updateUniformData(RenderState& state, QSGMaterial* newM
|
|||||||
|
|
||||||
// Color as vec4 (offset 96, 16 bytes)
|
// Color as vec4 (offset 96, 16 bytes)
|
||||||
const float color[4] = {
|
const float color[4] = {
|
||||||
static_cast<float>(mat->m_color.redF()),
|
mat->m_color.redF(),
|
||||||
static_cast<float>(mat->m_color.greenF()),
|
mat->m_color.greenF(),
|
||||||
static_cast<float>(mat->m_color.blueF()),
|
mat->m_color.blueF(),
|
||||||
static_cast<float>(mat->m_color.alphaF()),
|
mat->m_color.alphaF(),
|
||||||
};
|
};
|
||||||
memcpy(buf->data() + 96, color, 16);
|
memcpy(buf->data() + 96, color, 16);
|
||||||
|
|
||||||
@@ -86,7 +89,7 @@ bool BlobMaterialShader::updateUniformData(RenderState& state, QSGMaterial* newM
|
|||||||
const auto& r = mat->m_rects[i];
|
const auto& r = mat->m_rects[i];
|
||||||
const int base = 160 + i * 80;
|
const int base = 160 + i * 80;
|
||||||
// Pack excludeMask into props.x via bit-cast (read in shader with floatBitsToInt)
|
// Pack excludeMask into props.x via bit-cast (read in shader with floatBitsToInt)
|
||||||
float maskAsFloat;
|
float maskAsFloat = NAN;
|
||||||
memcpy(&maskAsFloat, &r.excludeMask, sizeof(float));
|
memcpy(&maskAsFloat, &r.excludeMask, sizeof(float));
|
||||||
const float d0[4] = {r.cx, r.cy, r.hw, r.hh};
|
const float d0[4] = {r.cx, r.cy, r.hw, r.hh};
|
||||||
const float d1[4] = {maskAsFloat, r.offsetX, r.offsetY, r.minEig};
|
const float d1[4] = {maskAsFloat, r.offsetX, r.offsetY, r.minEig};
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ struct BlobRectData {
|
|||||||
class BlobMaterial : public QSGMaterial {
|
class BlobMaterial : public QSGMaterial {
|
||||||
public:
|
public:
|
||||||
[[nodiscard]] QSGMaterialType* type() const override;
|
[[nodiscard]] QSGMaterialType* type() const override;
|
||||||
[[nodiscard]] QSGMaterialShader* createShader(QSGRendererInterface::RenderMode) const override;
|
[[nodiscard]] QSGMaterialShader* createShader(
|
||||||
|
QSGRendererInterface::RenderMode) const override;
|
||||||
int compare(const QSGMaterial* other) const override;
|
int compare(const QSGMaterial* other) const override;
|
||||||
|
|
||||||
float m_paddedX = 0;
|
float m_paddedX = 0;
|
||||||
@@ -43,5 +44,8 @@ BlobRectData m_rects[16] = {};
|
|||||||
class BlobMaterialShader : public QSGMaterialShader {
|
class BlobMaterialShader : public QSGMaterialShader {
|
||||||
public:
|
public:
|
||||||
BlobMaterialShader();
|
BlobMaterialShader();
|
||||||
bool updateUniformData(RenderState& state, QSGMaterial* newMaterial, QSGMaterial* oldMaterial) override;
|
bool updateUniformData(
|
||||||
|
RenderState& state,
|
||||||
|
QSGMaterial* newMaterial,
|
||||||
|
QSGMaterial* oldMaterial) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,19 +4,18 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
BlobRect::BlobRect(QQuickItem* parent)
|
BlobRect::BlobRect(QQuickItem* parent) : BlobShape(parent) {}
|
||||||
: BlobShape(parent) {
|
|
||||||
}
|
|
||||||
|
|
||||||
BlobRect::~BlobRect() {
|
BlobRect::~BlobRect() {
|
||||||
if (m_group)
|
if (m_group) m_group->removeShape(this);
|
||||||
m_group->removeShape(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlobRect::updatePolish() {
|
void BlobRect::updatePolish() {
|
||||||
if (m_physicsActive) {
|
if (m_physicsActive) {
|
||||||
float totalDelta = std::abs(m_dm00 - 1.0f) + std::abs(m_dm01) + std::abs(m_dm11 - 1.0f);
|
float totalDelta = std::abs(m_dm00 - 1.0f) + std::abs(m_dm01) +
|
||||||
float totalVel = std::abs(m_dmVel00) + std::abs(m_dmVel01) + std::abs(m_dmVel11);
|
std::abs(m_dm11 - 1.0f);
|
||||||
|
float totalVel =
|
||||||
|
std::abs(m_dmVel00) + std::abs(m_dmVel01) + std::abs(m_dmVel11);
|
||||||
|
|
||||||
if (totalDelta < 0.004f && totalVel < 0.05f) {
|
if (totalDelta < 0.004f && totalVel < 0.05f) {
|
||||||
m_dm00 = 1.0f;
|
m_dm00 = 1.0f;
|
||||||
@@ -32,8 +31,7 @@ void BlobRect::updatePolish() {
|
|||||||
QMetaObject::invokeMethod(
|
QMetaObject::invokeMethod(
|
||||||
this,
|
this,
|
||||||
[this]() {
|
[this]() {
|
||||||
if (m_group)
|
if (m_group) m_group->markDirty();
|
||||||
m_group->markDirty();
|
|
||||||
},
|
},
|
||||||
Qt::QueuedConnection);
|
Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
@@ -41,8 +39,7 @@ void BlobRect::updatePolish() {
|
|||||||
QMetaObject::invokeMethod(
|
QMetaObject::invokeMethod(
|
||||||
this,
|
this,
|
||||||
[this]() {
|
[this]() {
|
||||||
if (m_physicsActive && m_group)
|
if (m_physicsActive && m_group) m_group->markDirty();
|
||||||
m_group->markDirty();
|
|
||||||
},
|
},
|
||||||
Qt::QueuedConnection);
|
Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
@@ -64,20 +61,20 @@ void BlobRect::updatePhysics() {
|
|||||||
const float dt = static_cast<float>(m_elapsed.restart()) / 1000.0f;
|
const float dt = static_cast<float>(m_elapsed.restart()) / 1000.0f;
|
||||||
if (dt > 0.1f || dt < 0.001f) {
|
if (dt > 0.1f || dt < 0.001f) {
|
||||||
m_prevScenePos = scenePos;
|
m_prevScenePos = scenePos;
|
||||||
if (m_physicsActive)
|
if (m_physicsActive) checkAtRest(0.0f);
|
||||||
checkAtRest(0.0f);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const float velX = static_cast<float>(scenePos.x() - m_prevScenePos.x()) / dt;
|
const float velX =
|
||||||
const float velY = static_cast<float>(scenePos.y() - m_prevScenePos.y()) / dt;
|
static_cast<float>(scenePos.x() - m_prevScenePos.x()) / dt;
|
||||||
|
const float velY =
|
||||||
|
static_cast<float>(scenePos.y() - m_prevScenePos.y()) / dt;
|
||||||
m_prevScenePos = scenePos;
|
m_prevScenePos = scenePos;
|
||||||
|
|
||||||
const float speed = std::sqrt(velX * velX + velY * velY);
|
const float speed = std::sqrt(velX * velX + velY * velY);
|
||||||
|
|
||||||
if (!m_physicsActive) {
|
if (!m_physicsActive) {
|
||||||
if (speed < 5.0f)
|
if (speed < 5.0f) return;
|
||||||
return;
|
|
||||||
m_physicsActive = true;
|
m_physicsActive = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +86,8 @@ void BlobRect::updatePhysics() {
|
|||||||
float target11 = 1.0f;
|
float target11 = 1.0f;
|
||||||
|
|
||||||
if (speed > 5.0f) {
|
if (speed > 5.0f) {
|
||||||
const float targetStretch = 1.0f + std::min(speed * kStretchFactor, kMaxStretch);
|
const float targetStretch =
|
||||||
|
1.0f + std::min(speed * kStretchFactor, kMaxStretch);
|
||||||
const float targetCompress = 1.0f / targetStretch;
|
const float targetCompress = 1.0f / targetStretch;
|
||||||
|
|
||||||
const float cosA = velX / speed;
|
const float cosA = velX / speed;
|
||||||
@@ -106,19 +104,23 @@ void BlobRect::updatePhysics() {
|
|||||||
const float kStiffness = static_cast<float>(m_stiffness);
|
const float kStiffness = static_cast<float>(m_stiffness);
|
||||||
const float kDamping = static_cast<float>(m_damping);
|
const float kDamping = static_cast<float>(m_damping);
|
||||||
|
|
||||||
const float accel00 = -kStiffness * (m_dm00 - target00) - kDamping * m_dmVel00;
|
const float accel00 =
|
||||||
|
-kStiffness * (m_dm00 - target00) - kDamping * m_dmVel00;
|
||||||
m_dmVel00 += accel00 * dt;
|
m_dmVel00 += accel00 * dt;
|
||||||
m_dm00 += m_dmVel00 * dt;
|
m_dm00 += m_dmVel00 * dt;
|
||||||
|
|
||||||
const float accel01 = -kStiffness * (m_dm01 - target01) - kDamping * m_dmVel01;
|
const float accel01 =
|
||||||
|
-kStiffness * (m_dm01 - target01) - kDamping * m_dmVel01;
|
||||||
m_dmVel01 += accel01 * dt;
|
m_dmVel01 += accel01 * dt;
|
||||||
m_dm01 += m_dmVel01 * dt;
|
m_dm01 += m_dmVel01 * dt;
|
||||||
|
|
||||||
const float accel11 = -kStiffness * (m_dm11 - target11) - kDamping * m_dmVel11;
|
const float accel11 =
|
||||||
|
-kStiffness * (m_dm11 - target11) - kDamping * m_dmVel11;
|
||||||
m_dmVel11 += accel11 * dt;
|
m_dmVel11 += accel11 * dt;
|
||||||
m_dm11 += m_dmVel11 * dt;
|
m_dm11 += m_dmVel11 * dt;
|
||||||
|
|
||||||
m_deformMatrix = QMatrix4x4(m_dm00, m_dm01, 0, 0, m_dm01, m_dm11, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
m_deformMatrix = QMatrix4x4(
|
||||||
|
m_dm00, m_dm01, 0, 0, m_dm01, m_dm11, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
|
||||||
emit rawDeformMatrixChanged();
|
emit rawDeformMatrixChanged();
|
||||||
updateCenteredDeformMatrix();
|
updateCenteredDeformMatrix();
|
||||||
|
|
||||||
@@ -129,8 +131,7 @@ void BlobRect::setTopLeftRadius(qreal r) {
|
|||||||
if (!qFuzzyCompare(m_topLeftRadius, r)) {
|
if (!qFuzzyCompare(m_topLeftRadius, r)) {
|
||||||
m_topLeftRadius = r;
|
m_topLeftRadius = r;
|
||||||
emit topLeftRadiusChanged();
|
emit topLeftRadiusChanged();
|
||||||
if (m_group)
|
if (m_group) m_group->markDirty();
|
||||||
m_group->markDirty();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,8 +139,7 @@ void BlobRect::setTopRightRadius(qreal r) {
|
|||||||
if (!qFuzzyCompare(m_topRightRadius, r)) {
|
if (!qFuzzyCompare(m_topRightRadius, r)) {
|
||||||
m_topRightRadius = r;
|
m_topRightRadius = r;
|
||||||
emit topRightRadiusChanged();
|
emit topRightRadiusChanged();
|
||||||
if (m_group)
|
if (m_group) m_group->markDirty();
|
||||||
m_group->markDirty();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,8 +147,7 @@ void BlobRect::setBottomLeftRadius(qreal r) {
|
|||||||
if (!qFuzzyCompare(m_bottomLeftRadius, r)) {
|
if (!qFuzzyCompare(m_bottomLeftRadius, r)) {
|
||||||
m_bottomLeftRadius = r;
|
m_bottomLeftRadius = r;
|
||||||
emit bottomLeftRadiusChanged();
|
emit bottomLeftRadiusChanged();
|
||||||
if (m_group)
|
if (m_group) m_group->markDirty();
|
||||||
m_group->markDirty();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,38 +155,51 @@ void BlobRect::setBottomRightRadius(qreal r) {
|
|||||||
if (!qFuzzyCompare(m_bottomRightRadius, r)) {
|
if (!qFuzzyCompare(m_bottomRightRadius, r)) {
|
||||||
m_bottomRightRadius = r;
|
m_bottomRightRadius = r;
|
||||||
emit bottomRightRadiusChanged();
|
emit bottomRightRadiusChanged();
|
||||||
if (m_group)
|
if (m_group) m_group->markDirty();
|
||||||
m_group->markDirty();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlobRect::cornerRadii(float out[4]) const {
|
void BlobRect::cornerRadii(float out[4]) const {
|
||||||
const auto maxR = static_cast<float>(std::min(width(), height())) * 0.5f;
|
const auto maxR = static_cast<float>(std::min(width(), height())) * 0.5f;
|
||||||
const auto base = std::min(static_cast<float>(m_radius), maxR);
|
const auto base = std::min(static_cast<float>(m_radius), maxR);
|
||||||
out[0] = std::min(m_topRightRadius >= 0 ? static_cast<float>(m_topRightRadius) : base, maxR);
|
out[0] = std::min(
|
||||||
out[1] = std::min(m_bottomRightRadius >= 0 ? static_cast<float>(m_bottomRightRadius) : base, maxR);
|
m_topRightRadius >= 0 ? static_cast<float>(m_topRightRadius) : base,
|
||||||
out[2] = std::min(m_bottomLeftRadius >= 0 ? static_cast<float>(m_bottomLeftRadius) : base, maxR);
|
maxR);
|
||||||
out[3] = std::min(m_topLeftRadius >= 0 ? static_cast<float>(m_topLeftRadius) : base, maxR);
|
out[1] = std::min(
|
||||||
|
m_bottomRightRadius >= 0 ? static_cast<float>(m_bottomRightRadius)
|
||||||
|
: base,
|
||||||
|
maxR);
|
||||||
|
out[2] = std::min(
|
||||||
|
m_bottomLeftRadius >= 0 ? static_cast<float>(m_bottomLeftRadius) : base,
|
||||||
|
maxR);
|
||||||
|
out[3] = std::min(
|
||||||
|
m_topLeftRadius >= 0 ? static_cast<float>(m_topLeftRadius) : base,
|
||||||
|
maxR);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BlobRect::isExcluded(const BlobShape* other) const {
|
bool BlobRect::isExcluded(const BlobShape* other) const {
|
||||||
for (const auto& ptr : m_exclude) {
|
for (const auto& ptr : m_exclude) {
|
||||||
if (ptr == other)
|
if (ptr == other) return true;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QQmlListProperty<BlobRect> BlobRect::exclude() {
|
QQmlListProperty<BlobRect> BlobRect::exclude() {
|
||||||
return QQmlListProperty<BlobRect>(
|
return QQmlListProperty<BlobRect>(
|
||||||
this, nullptr, &excludeAppend, &excludeCount, &excludeAt, &excludeClear, &excludeReplace, &excludeRemoveLast);
|
this,
|
||||||
|
nullptr,
|
||||||
|
&excludeAppend,
|
||||||
|
&excludeCount,
|
||||||
|
&excludeAt,
|
||||||
|
&excludeClear,
|
||||||
|
&excludeReplace,
|
||||||
|
&excludeRemoveLast);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlobRect::excludeAppend(QQmlListProperty<BlobRect>* prop, BlobRect* rect) {
|
void BlobRect::excludeAppend(QQmlListProperty<BlobRect>* prop, BlobRect* rect) {
|
||||||
auto* self = static_cast<BlobRect*>(prop->object);
|
auto* self = static_cast<BlobRect*>(prop->object);
|
||||||
self->m_exclude.append(rect);
|
self->m_exclude.append(rect);
|
||||||
if (self->m_group)
|
if (self->m_group) self->m_group->markDirty();
|
||||||
self->m_group->markDirty();
|
|
||||||
emit self->excludeChanged();
|
emit self->excludeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,44 +208,43 @@ qsizetype BlobRect::excludeCount(QQmlListProperty<BlobRect>* prop) {
|
|||||||
return self->m_exclude.size();
|
return self->m_exclude.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
BlobRect* BlobRect::excludeAt(QQmlListProperty<BlobRect>* prop, qsizetype index) {
|
BlobRect* BlobRect::excludeAt(
|
||||||
|
QQmlListProperty<BlobRect>* prop, qsizetype index) {
|
||||||
auto* self = static_cast<BlobRect*>(prop->object);
|
auto* self = static_cast<BlobRect*>(prop->object);
|
||||||
return self->m_exclude.at(index);
|
return self->m_exclude.at(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlobRect::excludeClear(QQmlListProperty<BlobRect>* prop) {
|
void BlobRect::excludeClear(QQmlListProperty<BlobRect>* prop) {
|
||||||
auto* self = static_cast<BlobRect*>(prop->object);
|
auto* self = static_cast<BlobRect*>(prop->object);
|
||||||
if (self->m_exclude.isEmpty())
|
if (self->m_exclude.isEmpty()) return;
|
||||||
return;
|
|
||||||
self->m_exclude.clear();
|
self->m_exclude.clear();
|
||||||
if (self->m_group)
|
if (self->m_group) self->m_group->markDirty();
|
||||||
self->m_group->markDirty();
|
|
||||||
emit self->excludeChanged();
|
emit self->excludeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlobRect::excludeReplace(QQmlListProperty<BlobRect>* prop, qsizetype index, BlobRect* rect) {
|
void BlobRect::excludeReplace(
|
||||||
|
QQmlListProperty<BlobRect>* prop, qsizetype index, BlobRect* rect) {
|
||||||
auto* self = static_cast<BlobRect*>(prop->object);
|
auto* self = static_cast<BlobRect*>(prop->object);
|
||||||
self->m_exclude[index] = rect;
|
self->m_exclude[index] = rect;
|
||||||
if (self->m_group)
|
if (self->m_group) self->m_group->markDirty();
|
||||||
self->m_group->markDirty();
|
|
||||||
emit self->excludeChanged();
|
emit self->excludeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlobRect::excludeRemoveLast(QQmlListProperty<BlobRect>* prop) {
|
void BlobRect::excludeRemoveLast(QQmlListProperty<BlobRect>* prop) {
|
||||||
auto* self = static_cast<BlobRect*>(prop->object);
|
auto* self = static_cast<BlobRect*>(prop->object);
|
||||||
if (self->m_exclude.isEmpty())
|
if (self->m_exclude.isEmpty()) return;
|
||||||
return;
|
|
||||||
self->m_exclude.removeLast();
|
self->m_exclude.removeLast();
|
||||||
if (self->m_group)
|
if (self->m_group) self->m_group->markDirty();
|
||||||
self->m_group->markDirty();
|
|
||||||
emit self->excludeChanged();
|
emit self->excludeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlobRect::checkAtRest(float speed) {
|
void BlobRect::checkAtRest(float speed) {
|
||||||
constexpr float kEpsilon = 0.002f;
|
constexpr float kEpsilon = 0.002f;
|
||||||
const bool atRest = std::abs(m_dm00 - 1.0f) < kEpsilon && std::abs(m_dm01) < kEpsilon &&
|
const bool atRest =
|
||||||
|
std::abs(m_dm00 - 1.0f) < kEpsilon && std::abs(m_dm01) < kEpsilon &&
|
||||||
std::abs(m_dm11 - 1.0f) < kEpsilon && std::abs(m_dmVel00) < kEpsilon &&
|
std::abs(m_dm11 - 1.0f) < kEpsilon && std::abs(m_dmVel00) < kEpsilon &&
|
||||||
std::abs(m_dmVel01) < kEpsilon && std::abs(m_dmVel11) < kEpsilon && speed < 5.0f;
|
std::abs(m_dmVel01) < kEpsilon && std::abs(m_dmVel11) < kEpsilon &&
|
||||||
|
speed < 5.0f;
|
||||||
|
|
||||||
if (atRest) {
|
if (atRest) {
|
||||||
m_dm00 = 1.0f;
|
m_dm00 = 1.0f;
|
||||||
@@ -251,8 +262,7 @@ void BlobRect::checkAtRest(float speed) {
|
|||||||
QMetaObject::invokeMethod(
|
QMetaObject::invokeMethod(
|
||||||
this,
|
this,
|
||||||
[this]() {
|
[this]() {
|
||||||
if (m_group)
|
if (m_group) m_group->markDirty();
|
||||||
m_group->markDirty();
|
|
||||||
},
|
},
|
||||||
Qt::QueuedConnection);
|
Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,23 +10,33 @@
|
|||||||
class BlobRect : public BlobShape {
|
class BlobRect : public BlobShape {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
Q_PROPERTY(qreal stiffness READ stiffness WRITE setStiffness NOTIFY stiffnessChanged)
|
|
||||||
Q_PROPERTY(qreal damping READ damping WRITE setDamping NOTIFY dampingChanged)
|
|
||||||
Q_PROPERTY(qreal deformScale READ deformScale WRITE setDeformScale NOTIFY deformScaleChanged)
|
|
||||||
Q_PROPERTY(QQmlListProperty<BlobRect> exclude READ exclude NOTIFY excludeChanged)
|
|
||||||
Q_PROPERTY(qreal topLeftRadius READ topLeftRadius WRITE setTopLeftRadius NOTIFY topLeftRadiusChanged)
|
|
||||||
Q_PROPERTY(qreal topRightRadius READ topRightRadius WRITE setTopRightRadius NOTIFY topRightRadiusChanged)
|
|
||||||
Q_PROPERTY(qreal bottomLeftRadius READ bottomLeftRadius WRITE setBottomLeftRadius NOTIFY bottomLeftRadiusChanged)
|
|
||||||
Q_PROPERTY(
|
Q_PROPERTY(
|
||||||
qreal bottomRightRadius READ bottomRightRadius WRITE setBottomRightRadius NOTIFY bottomRightRadiusChanged)
|
qreal stiffness READ stiffness WRITE setStiffness NOTIFY
|
||||||
|
stiffnessChanged)
|
||||||
|
Q_PROPERTY(qreal damping READ damping WRITE setDamping NOTIFY dampingChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
qreal deformScale READ deformScale WRITE setDeformScale NOTIFY
|
||||||
|
deformScaleChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
QQmlListProperty<BlobRect> exclude READ exclude NOTIFY excludeChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
qreal topLeftRadius READ topLeftRadius WRITE setTopLeftRadius NOTIFY
|
||||||
|
topLeftRadiusChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
qreal topRightRadius READ topRightRadius WRITE setTopRightRadius NOTIFY
|
||||||
|
topRightRadiusChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
qreal bottomLeftRadius READ bottomLeftRadius WRITE setBottomLeftRadius
|
||||||
|
NOTIFY bottomLeftRadiusChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
qreal bottomRightRadius READ bottomRightRadius WRITE
|
||||||
|
setBottomRightRadius NOTIFY bottomRightRadiusChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit BlobRect(QQuickItem* parent = nullptr);
|
explicit BlobRect(QQuickItem* parent = nullptr);
|
||||||
~BlobRect() override;
|
~BlobRect() override;
|
||||||
|
|
||||||
[[nodiscard]] qreal stiffness() const {
|
[[nodiscard]] qreal stiffness() const { return m_stiffness; }
|
||||||
return m_stiffness;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setStiffness(qreal s) {
|
void setStiffness(qreal s) {
|
||||||
if (!qFuzzyCompare(m_stiffness, s)) {
|
if (!qFuzzyCompare(m_stiffness, s)) {
|
||||||
@@ -35,9 +45,7 @@ void setStiffness(qreal s) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] qreal damping() const {
|
[[nodiscard]] qreal damping() const { return m_damping; }
|
||||||
return m_damping;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setDamping(qreal d) {
|
void setDamping(qreal d) {
|
||||||
if (!qFuzzyCompare(m_damping, d)) {
|
if (!qFuzzyCompare(m_damping, d)) {
|
||||||
@@ -46,9 +54,7 @@ void setDamping(qreal d) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] qreal deformScale() const {
|
[[nodiscard]] qreal deformScale() const { return m_deformScale; }
|
||||||
return m_deformScale;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setDeformScale(qreal s) {
|
void setDeformScale(qreal s) {
|
||||||
if (!qFuzzyCompare(m_deformScale, s)) {
|
if (!qFuzzyCompare(m_deformScale, s)) {
|
||||||
@@ -62,21 +68,15 @@ QQmlListProperty<BlobRect> exclude();
|
|||||||
bool isExcluded(const BlobShape* other) const override;
|
bool isExcluded(const BlobShape* other) const override;
|
||||||
void cornerRadii(float out[4]) const override;
|
void cornerRadii(float out[4]) const override;
|
||||||
|
|
||||||
[[nodiscard]] qreal topLeftRadius() const {
|
[[nodiscard]] qreal topLeftRadius() const { return m_topLeftRadius; }
|
||||||
return m_topLeftRadius;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setTopLeftRadius(qreal r);
|
void setTopLeftRadius(qreal r);
|
||||||
|
|
||||||
[[nodiscard]] qreal topRightRadius() const {
|
[[nodiscard]] qreal topRightRadius() const { return m_topRightRadius; }
|
||||||
return m_topRightRadius;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setTopRightRadius(qreal r);
|
void setTopRightRadius(qreal r);
|
||||||
|
|
||||||
[[nodiscard]] qreal bottomLeftRadius() const {
|
[[nodiscard]] qreal bottomLeftRadius() const { return m_bottomLeftRadius; }
|
||||||
return m_bottomLeftRadius;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setBottomLeftRadius(qreal r);
|
void setBottomLeftRadius(qreal r);
|
||||||
|
|
||||||
@@ -133,8 +133,10 @@ QList<QPointer<BlobRect> > m_exclude;
|
|||||||
|
|
||||||
static void excludeAppend(QQmlListProperty<BlobRect>* prop, BlobRect* rect);
|
static void excludeAppend(QQmlListProperty<BlobRect>* prop, BlobRect* rect);
|
||||||
static qsizetype excludeCount(QQmlListProperty<BlobRect>* prop);
|
static qsizetype excludeCount(QQmlListProperty<BlobRect>* prop);
|
||||||
static BlobRect* excludeAt(QQmlListProperty<BlobRect>* prop, qsizetype index);
|
static BlobRect* excludeAt(
|
||||||
|
QQmlListProperty<BlobRect>* prop, qsizetype index);
|
||||||
static void excludeClear(QQmlListProperty<BlobRect>* prop);
|
static void excludeClear(QQmlListProperty<BlobRect>* prop);
|
||||||
static void excludeReplace(QQmlListProperty<BlobRect>* prop, qsizetype index, BlobRect* rect);
|
static void excludeReplace(
|
||||||
|
QQmlListProperty<BlobRect>* prop, qsizetype index, BlobRect* rect);
|
||||||
static void excludeRemoveLast(QQmlListProperty<BlobRect>* prop);
|
static void excludeRemoveLast(QQmlListProperty<BlobRect>* prop);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ static float deformPadding(const QMatrix4x4& dm, float hw, float hh) {
|
|||||||
return std::max(extraX, extraY);
|
return std::max(extraX, extraY);
|
||||||
}
|
}
|
||||||
|
|
||||||
static float cpuSdBox(float px, float py, float cx, float cy, float hw, float hh) {
|
static float cpuSdBox(
|
||||||
|
float px, float py, float cx, float cy, float hw, float hh) {
|
||||||
const float dx = std::abs(px - cx) - hw;
|
const float dx = std::abs(px - cx) - hw;
|
||||||
const float dy = std::abs(py - cy) - hh;
|
const float dy = std::abs(py - cy) - hh;
|
||||||
const float mdx = std::max(dx, 0.0f);
|
const float mdx = std::max(dx, 0.0f);
|
||||||
@@ -31,54 +32,53 @@ static float cpuSmoothstep(float edge0, float edge1, float x) {
|
|||||||
return t * t * (3.0f - 2.0f * t);
|
return t * t * (3.0f - 2.0f * t);
|
||||||
}
|
}
|
||||||
|
|
||||||
BlobShape::BlobShape(QQuickItem* parent)
|
BlobShape::BlobShape(QQuickItem* parent) : QQuickItem(parent) {
|
||||||
: QQuickItem(parent) {
|
|
||||||
setFlag(ItemHasContents);
|
setFlag(ItemHasContents);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlobShape::setGroup(BlobGroup* g) {
|
void BlobShape::setGroup(BlobGroup* g) {
|
||||||
if (m_group == g)
|
if (m_group == g) return;
|
||||||
return;
|
if (m_group && isComponentComplete()) unregisterFromGroup();
|
||||||
if (m_group && isComponentComplete())
|
|
||||||
unregisterFromGroup();
|
|
||||||
m_group = g;
|
m_group = g;
|
||||||
if (m_group && isComponentComplete())
|
if (m_group && isComponentComplete()) registerWithGroup();
|
||||||
registerWithGroup();
|
|
||||||
emit groupChanged();
|
emit groupChanged();
|
||||||
if (m_group)
|
if (m_group) m_group->markDirty();
|
||||||
m_group->markDirty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlobShape::setRadius(qreal r) {
|
void BlobShape::setRadius(qreal r) {
|
||||||
if (qFuzzyCompare(m_radius, r))
|
if (qFuzzyCompare(m_radius, r)) return;
|
||||||
return;
|
|
||||||
m_radius = r;
|
m_radius = r;
|
||||||
emit radiusChanged();
|
emit radiusChanged();
|
||||||
if (m_group)
|
if (m_group) m_group->markDirty();
|
||||||
m_group->markDirty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlobShape::componentComplete() {
|
void BlobShape::componentComplete() {
|
||||||
QQuickItem::componentComplete();
|
QQuickItem::componentComplete();
|
||||||
if (m_group)
|
if (m_group) registerWithGroup();
|
||||||
registerWithGroup();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlobShape::geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) {
|
void BlobShape::geometryChange(
|
||||||
|
const QRectF& newGeometry, const QRectF& oldGeometry) {
|
||||||
QQuickItem::geometryChange(newGeometry, oldGeometry);
|
QQuickItem::geometryChange(newGeometry, oldGeometry);
|
||||||
updateCenteredDeformMatrix();
|
updateCenteredDeformMatrix();
|
||||||
if (m_group) {
|
if (m_group) {
|
||||||
m_pendingDx += static_cast<float>(newGeometry.x() - oldGeometry.x());
|
m_pendingDx += static_cast<float>(newGeometry.x() - oldGeometry.x());
|
||||||
m_pendingDy += static_cast<float>(newGeometry.y() - oldGeometry.y());
|
m_pendingDy += static_cast<float>(newGeometry.y() - oldGeometry.y());
|
||||||
m_pendingDw += static_cast<float>(newGeometry.width() - oldGeometry.width());
|
m_pendingDw +=
|
||||||
m_pendingDh += static_cast<float>(newGeometry.height() - oldGeometry.height());
|
static_cast<float>(newGeometry.width() - oldGeometry.width());
|
||||||
|
m_pendingDh +=
|
||||||
|
static_cast<float>(newGeometry.height() - oldGeometry.height());
|
||||||
|
|
||||||
const float deformMag = std::abs(m_deformMatrix(0, 0) - 1.0f) + std::abs(m_deformMatrix(0, 1)) +
|
const float deformMag = std::abs(m_deformMatrix(0, 0) - 1.0f) +
|
||||||
std::abs(m_deformMatrix(1, 0)) + std::abs(m_deformMatrix(1, 1) - 1.0f);
|
std::abs(m_deformMatrix(0, 1)) +
|
||||||
|
std::abs(m_deformMatrix(1, 0)) +
|
||||||
|
std::abs(m_deformMatrix(1, 1) - 1.0f);
|
||||||
const float syncThreshold = deformMag > 0.001f ? 0.05f : 0.5f;
|
const float syncThreshold = deformMag > 0.001f ? 0.05f : 0.5f;
|
||||||
|
|
||||||
if (std::abs(m_pendingDx) > syncThreshold || std::abs(m_pendingDy) > syncThreshold ||
|
if (std::abs(m_pendingDx) > syncThreshold ||
|
||||||
std::abs(m_pendingDw) > syncThreshold || std::abs(m_pendingDh) > syncThreshold) {
|
std::abs(m_pendingDy) > syncThreshold ||
|
||||||
|
std::abs(m_pendingDw) > syncThreshold ||
|
||||||
|
std::abs(m_pendingDh) > syncThreshold) {
|
||||||
m_pendingDx = 0;
|
m_pendingDx = 0;
|
||||||
m_pendingDy = 0;
|
m_pendingDy = 0;
|
||||||
m_pendingDw = 0;
|
m_pendingDw = 0;
|
||||||
@@ -111,18 +111,15 @@ void BlobShape::cornerRadii(float out[4]) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BlobShape::registerWithGroup() {
|
void BlobShape::registerWithGroup() {
|
||||||
if (m_group)
|
if (m_group) m_group->addShape(this);
|
||||||
m_group->addShape(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlobShape::unregisterFromGroup() {
|
void BlobShape::unregisterFromGroup() {
|
||||||
if (m_group)
|
if (m_group) m_group->removeShape(this);
|
||||||
m_group->removeShape(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlobShape::updatePolish() {
|
void BlobShape::updatePolish() {
|
||||||
if (!m_group)
|
if (!m_group) return;
|
||||||
return;
|
|
||||||
|
|
||||||
m_group->ensurePhysicsUpdated();
|
m_group->ensurePhysicsUpdated();
|
||||||
|
|
||||||
@@ -144,27 +141,30 @@ void BlobShape::updatePolish() {
|
|||||||
m_cachedPaddedY = static_cast<float>(scenePos.y()) - totalPad;
|
m_cachedPaddedY = static_cast<float>(scenePos.y()) - totalPad;
|
||||||
m_cachedPaddedW = static_cast<float>(width()) + 2.0f * totalPad;
|
m_cachedPaddedW = static_cast<float>(width()) + 2.0f * totalPad;
|
||||||
m_cachedPaddedH = static_cast<float>(height()) + 2.0f * totalPad;
|
m_cachedPaddedH = static_cast<float>(height()) + 2.0f * totalPad;
|
||||||
m_localPaddedRect = QRectF(static_cast<double>(-totalPad), static_cast<double>(-totalPad),
|
m_localPaddedRect = QRectF(
|
||||||
width() + 2.0 * static_cast<double>(totalPad), height() + 2.0 * static_cast<double>(totalPad));
|
static_cast<double>(-totalPad),
|
||||||
|
static_cast<double>(-totalPad),
|
||||||
|
width() + 2.0 * static_cast<double>(totalPad),
|
||||||
|
height() + 2.0 * static_cast<double>(totalPad));
|
||||||
}
|
}
|
||||||
|
|
||||||
m_cachedRects.clear();
|
m_cachedRects.clear();
|
||||||
m_cachedMyIndex = -2;
|
m_cachedMyIndex = -2;
|
||||||
const QRectF myPadded(static_cast<double>(m_cachedPaddedX), static_cast<double>(m_cachedPaddedY),
|
const QRectF myPadded(
|
||||||
static_cast<double>(m_cachedPaddedW), static_cast<double>(m_cachedPaddedH));
|
static_cast<double>(m_cachedPaddedX),
|
||||||
|
static_cast<double>(m_cachedPaddedY),
|
||||||
|
static_cast<double>(m_cachedPaddedW),
|
||||||
|
static_cast<double>(m_cachedPaddedH));
|
||||||
|
|
||||||
QVector<BlobShape*> rectShapes;
|
QVector<BlobShape*> rectShapes;
|
||||||
rectShapes.reserve(m_group->shapes().size());
|
rectShapes.reserve(m_group->shapes().size());
|
||||||
|
|
||||||
for (BlobShape* other : m_group->shapes()) {
|
for (BlobShape* other : m_group->shapes()) {
|
||||||
if (other->isInvertedRect())
|
if (other->isInvertedRect()) continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
if (other->width() <= 0 || other->height() <= 0)
|
if (other->width() <= 0 || other->height() <= 0) continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
if (isExcluded(other))
|
if (isExcluded(other)) continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
const QPointF otherScene = other->mapToScene(QPointF(0, 0));
|
const QPointF otherScene = other->mapToScene(QPointF(0, 0));
|
||||||
|
|
||||||
@@ -174,9 +174,12 @@ void BlobShape::updatePolish() {
|
|||||||
} else {
|
} else {
|
||||||
const float otherHW = static_cast<float>(other->width()) * 0.5f;
|
const float otherHW = static_cast<float>(other->width()) * 0.5f;
|
||||||
const float otherHH = static_cast<float>(other->height()) * 0.5f;
|
const float otherHH = static_cast<float>(other->height()) * 0.5f;
|
||||||
const float otherPad = pad + deformPadding(other->m_deformMatrix, otherHW, otherHH);
|
const float otherPad =
|
||||||
const QRectF otherPadded(otherScene.x() - static_cast<double>(otherPad),
|
pad + deformPadding(other->m_deformMatrix, otherHW, otherHH);
|
||||||
otherScene.y() - static_cast<double>(otherPad), other->width() + 2.0 * static_cast<double>(otherPad),
|
const QRectF otherPadded(
|
||||||
|
otherScene.x() - static_cast<double>(otherPad),
|
||||||
|
otherScene.y() - static_cast<double>(otherPad),
|
||||||
|
other->width() + 2.0 * static_cast<double>(otherPad),
|
||||||
other->height() + 2.0 * static_cast<double>(otherPad));
|
other->height() + 2.0 * static_cast<double>(otherPad));
|
||||||
include = myPadded.intersects(otherPadded);
|
include = myPadded.intersects(otherPadded);
|
||||||
}
|
}
|
||||||
@@ -217,19 +220,16 @@ void BlobShape::updatePolish() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isInvertedRect())
|
if (isInvertedRect()) m_cachedMyIndex = -1;
|
||||||
m_cachedMyIndex = -1;
|
|
||||||
|
|
||||||
const auto cachedCount = m_cachedRects.size();
|
const auto cachedCount = m_cachedRects.size();
|
||||||
for (qsizetype i = 0; i < cachedCount; ++i) {
|
for (qsizetype i = 0; i < cachedCount; ++i) {
|
||||||
int mask = 0;
|
int mask = 0;
|
||||||
BlobShape* si = rectShapes[i];
|
BlobShape* si = rectShapes[i];
|
||||||
for (qsizetype j = 0; j < cachedCount; ++j) {
|
for (qsizetype j = 0; j < cachedCount; ++j) {
|
||||||
if (j == i)
|
if (j == i) continue;
|
||||||
continue;
|
|
||||||
BlobShape* sj = rectShapes[j];
|
BlobShape* sj = rectShapes[j];
|
||||||
if (si->isExcluded(sj) || sj->isExcluded(si))
|
if (si->isExcluded(sj) || sj->isExcluded(si)) mask |= (1 << j);
|
||||||
mask |= (1 << j);
|
|
||||||
}
|
}
|
||||||
m_cachedRects[i].excludeMask = mask;
|
m_cachedRects[i].excludeMask = mask;
|
||||||
}
|
}
|
||||||
@@ -242,15 +242,25 @@ void BlobShape::updatePolish() {
|
|||||||
auto* inv = m_group->invertedRect();
|
auto* inv = m_group->invertedRect();
|
||||||
if (inv) {
|
if (inv) {
|
||||||
const QPointF invScene = inv->mapToScene(QPointF(0, 0));
|
const QPointF invScene = inv->mapToScene(QPointF(0, 0));
|
||||||
const float outerCX = static_cast<float>(invScene.x() + inv->width() / 2.0);
|
const float outerCX =
|
||||||
const float outerCY = static_cast<float>(invScene.y() + inv->height() / 2.0);
|
static_cast<float>(invScene.x() + inv->width() / 2.0);
|
||||||
|
const float outerCY =
|
||||||
|
static_cast<float>(invScene.y() + inv->height() / 2.0);
|
||||||
const float outerHW = static_cast<float>(inv->width() / 2.0);
|
const float outerHW = static_cast<float>(inv->width() / 2.0);
|
||||||
const float outerHH = static_cast<float>(inv->height() / 2.0);
|
const float outerHH = static_cast<float>(inv->height() / 2.0);
|
||||||
|
|
||||||
const float innerCX = outerCX + static_cast<float>((inv->borderLeft() - inv->borderRight()) / 2.0);
|
const float innerCX =
|
||||||
const float innerCY = outerCY + static_cast<float>((inv->borderTop() - inv->borderBottom()) / 2.0);
|
outerCX +
|
||||||
const float innerHW = outerHW - static_cast<float>((inv->borderLeft() + inv->borderRight()) / 2.0);
|
static_cast<float>((inv->borderLeft() - inv->borderRight()) / 2.0);
|
||||||
const float innerHH = outerHH - static_cast<float>((inv->borderTop() + inv->borderBottom()) / 2.0);
|
const float innerCY =
|
||||||
|
outerCY +
|
||||||
|
static_cast<float>((inv->borderTop() - inv->borderBottom()) / 2.0);
|
||||||
|
const float innerHW =
|
||||||
|
outerHW -
|
||||||
|
static_cast<float>((inv->borderLeft() + inv->borderRight()) / 2.0);
|
||||||
|
const float innerHH =
|
||||||
|
outerHH -
|
||||||
|
static_cast<float>((inv->borderTop() + inv->borderBottom()) / 2.0);
|
||||||
|
|
||||||
bool nearBorder = isInvertedRect();
|
bool nearBorder = isInvertedRect();
|
||||||
if (!nearBorder) {
|
if (!nearBorder) {
|
||||||
@@ -259,8 +269,10 @@ void BlobShape::updatePolish() {
|
|||||||
const float myCY = m_cachedPaddedY + m_cachedPaddedH * 0.5f;
|
const float myCY = m_cachedPaddedY + m_cachedPaddedH * 0.5f;
|
||||||
const float myHW = m_cachedPaddedW * 0.5f;
|
const float myHW = m_cachedPaddedW * 0.5f;
|
||||||
const float myHH = m_cachedPaddedH * 0.5f;
|
const float myHH = m_cachedPaddedH * 0.5f;
|
||||||
nearBorder = (myCX - myHW < innerCX - innerHW + margin) || (myCX + myHW > innerCX + innerHW - margin) ||
|
nearBorder = (myCX - myHW < innerCX - innerHW + margin) ||
|
||||||
(myCY - myHH < innerCY - innerHH + margin) || (myCY + myHH > innerCY + innerHH - margin);
|
(myCX + myHW > innerCX + innerHW - margin) ||
|
||||||
|
(myCY - myHH < innerCY - innerHH + margin) ||
|
||||||
|
(myCY + myHH > innerCY + innerHH - margin);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nearBorder) {
|
if (nearBorder) {
|
||||||
@@ -293,15 +305,33 @@ void BlobShape::updatePolish() {
|
|||||||
const float cTlX = ri.cx - ri.hw, cTlY = ri.cy - ri.hh;
|
const float cTlX = ri.cx - ri.hw, cTlY = ri.cy - ri.hh;
|
||||||
|
|
||||||
for (qsizetype j = 0; j < rectCount; ++j) {
|
for (qsizetype j = 0; j < rectCount; ++j) {
|
||||||
if (j == i)
|
if (j == i) continue;
|
||||||
continue;
|
if (riExcludeMask & (1 << j)) continue;
|
||||||
if (riExcludeMask & (1 << j))
|
|
||||||
continue;
|
|
||||||
const auto& rj = m_cachedRects[j];
|
const auto& rj = m_cachedRects[j];
|
||||||
fTr = std::min(fTr, cpuSmoothstep(0.0f, smoothFactor, cpuSdBox(cTrX, cTrY, rj.cx, rj.cy, rj.hw, rj.hh)));
|
fTr = std::min(
|
||||||
fBr = std::min(fBr, cpuSmoothstep(0.0f, smoothFactor, cpuSdBox(cBrX, cBrY, rj.cx, rj.cy, rj.hw, rj.hh)));
|
fTr,
|
||||||
fBl = std::min(fBl, cpuSmoothstep(0.0f, smoothFactor, cpuSdBox(cBlX, cBlY, rj.cx, rj.cy, rj.hw, rj.hh)));
|
cpuSmoothstep(
|
||||||
fTl = std::min(fTl, cpuSmoothstep(0.0f, smoothFactor, cpuSdBox(cTlX, cTlY, rj.cx, rj.cy, rj.hw, rj.hh)));
|
0.0f,
|
||||||
|
smoothFactor,
|
||||||
|
cpuSdBox(cTrX, cTrY, rj.cx, rj.cy, rj.hw, rj.hh)));
|
||||||
|
fBr = std::min(
|
||||||
|
fBr,
|
||||||
|
cpuSmoothstep(
|
||||||
|
0.0f,
|
||||||
|
smoothFactor,
|
||||||
|
cpuSdBox(cBrX, cBrY, rj.cx, rj.cy, rj.hw, rj.hh)));
|
||||||
|
fBl = std::min(
|
||||||
|
fBl,
|
||||||
|
cpuSmoothstep(
|
||||||
|
0.0f,
|
||||||
|
smoothFactor,
|
||||||
|
cpuSdBox(cBlX, cBlY, rj.cx, rj.cy, rj.hw, rj.hh)));
|
||||||
|
fTl = std::min(
|
||||||
|
fTl,
|
||||||
|
cpuSmoothstep(
|
||||||
|
0.0f,
|
||||||
|
smoothFactor,
|
||||||
|
cpuSdBox(cTlX, cTlY, rj.cx, rj.cy, rj.hw, rj.hh)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_cachedHasInverted) {
|
if (m_cachedHasInverted) {
|
||||||
@@ -309,10 +339,30 @@ void BlobShape::updatePolish() {
|
|||||||
const float icy = m_cachedInvertedInner[1];
|
const float icy = m_cachedInvertedInner[1];
|
||||||
const float ihw = m_cachedInvertedInner[2];
|
const float ihw = m_cachedInvertedInner[2];
|
||||||
const float ihh = m_cachedInvertedInner[3];
|
const float ihh = m_cachedInvertedInner[3];
|
||||||
fTr = std::min(fTr, cpuSmoothstep(0.0f, smoothFactor, -cpuSdBox(cTrX, cTrY, icx, icy, ihw, ihh)));
|
fTr = std::min(
|
||||||
fBr = std::min(fBr, cpuSmoothstep(0.0f, smoothFactor, -cpuSdBox(cBrX, cBrY, icx, icy, ihw, ihh)));
|
fTr,
|
||||||
fBl = std::min(fBl, cpuSmoothstep(0.0f, smoothFactor, -cpuSdBox(cBlX, cBlY, icx, icy, ihw, ihh)));
|
cpuSmoothstep(
|
||||||
fTl = std::min(fTl, cpuSmoothstep(0.0f, smoothFactor, -cpuSdBox(cTlX, cTlY, icx, icy, ihw, ihh)));
|
0.0f,
|
||||||
|
smoothFactor,
|
||||||
|
-cpuSdBox(cTrX, cTrY, icx, icy, ihw, ihh)));
|
||||||
|
fBr = std::min(
|
||||||
|
fBr,
|
||||||
|
cpuSmoothstep(
|
||||||
|
0.0f,
|
||||||
|
smoothFactor,
|
||||||
|
-cpuSdBox(cBrX, cBrY, icx, icy, ihw, ihh)));
|
||||||
|
fBl = std::min(
|
||||||
|
fBl,
|
||||||
|
cpuSmoothstep(
|
||||||
|
0.0f,
|
||||||
|
smoothFactor,
|
||||||
|
-cpuSdBox(cBlX, cBlY, icx, icy, ihw, ihh)));
|
||||||
|
fTl = std::min(
|
||||||
|
fTl,
|
||||||
|
cpuSmoothstep(
|
||||||
|
0.0f,
|
||||||
|
smoothFactor,
|
||||||
|
-cpuSdBox(cTlX, cTlY, icx, icy, ihw, ihh)));
|
||||||
}
|
}
|
||||||
|
|
||||||
ri.radius[0] = std::max(ri.radius[0] * fTr, minR);
|
ri.radius[0] = std::max(ri.radius[0] * fTr, minR);
|
||||||
@@ -332,7 +382,8 @@ QSGNode* BlobShape::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) {
|
|||||||
if (!node) {
|
if (!node) {
|
||||||
node = new QSGGeometryNode;
|
node = new QSGGeometryNode;
|
||||||
|
|
||||||
auto* geometry = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4);
|
auto* geometry =
|
||||||
|
new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4);
|
||||||
geometry->setDrawingMode(QSGGeometry::DrawTriangleStrip);
|
geometry->setDrawingMode(QSGGeometry::DrawTriangleStrip);
|
||||||
node->setGeometry(geometry);
|
node->setGeometry(geometry);
|
||||||
node->setFlag(QSGNode::OwnsGeometry);
|
node->setFlag(QSGNode::OwnsGeometry);
|
||||||
@@ -368,10 +419,17 @@ QSGNode* BlobShape::updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) {
|
|||||||
material->m_color = m_group->color();
|
material->m_color = m_group->color();
|
||||||
material->m_hasInverted = m_cachedHasInverted ? 1 : 0;
|
material->m_hasInverted = m_cachedHasInverted ? 1 : 0;
|
||||||
material->m_invertedRadius = m_cachedInvertedRadius;
|
material->m_invertedRadius = m_cachedInvertedRadius;
|
||||||
memcpy(material->m_invertedOuter, m_cachedInvertedOuter, sizeof(m_cachedInvertedOuter));
|
memcpy(
|
||||||
memcpy(material->m_invertedInner, m_cachedInvertedInner, sizeof(m_cachedInvertedInner));
|
material->m_invertedOuter,
|
||||||
|
m_cachedInvertedOuter,
|
||||||
|
sizeof(m_cachedInvertedOuter));
|
||||||
|
memcpy(
|
||||||
|
material->m_invertedInner,
|
||||||
|
m_cachedInvertedInner,
|
||||||
|
sizeof(m_cachedInvertedInner));
|
||||||
|
|
||||||
const int count = static_cast<int>(qMin(m_cachedRects.size(), qsizetype(16)));
|
const int count =
|
||||||
|
static_cast<int>(qMin(m_cachedRects.size(), qsizetype(16)));
|
||||||
material->m_rectCount = count;
|
material->m_rectCount = count;
|
||||||
for (int i = 0; i < count; ++i)
|
for (int i = 0; i < count; ++i)
|
||||||
material->m_rects[i] = m_cachedRects[i];
|
material->m_rects[i] = m_cachedRects[i];
|
||||||
|
|||||||
@@ -12,8 +12,11 @@ class BlobShape : public QQuickItem {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(BlobGroup* group READ group WRITE setGroup NOTIFY groupChanged)
|
Q_PROPERTY(BlobGroup* group READ group WRITE setGroup NOTIFY groupChanged)
|
||||||
Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged)
|
Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged)
|
||||||
Q_PROPERTY(QMatrix4x4 deformMatrix READ deformMatrix NOTIFY deformMatrixChanged)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(QMatrix4x4 rawDeformMatrix READ rawDeformMatrix NOTIFY rawDeformMatrixChanged)
|
QMatrix4x4 deformMatrix READ deformMatrix NOTIFY deformMatrixChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
QMatrix4x4 rawDeformMatrix READ rawDeformMatrix NOTIFY
|
||||||
|
rawDeformMatrixChanged)
|
||||||
|
|
||||||
friend class BlobGroup;
|
friend class BlobGroup;
|
||||||
|
|
||||||
@@ -21,15 +24,11 @@ public:
|
|||||||
explicit BlobShape(QQuickItem* parent = nullptr);
|
explicit BlobShape(QQuickItem* parent = nullptr);
|
||||||
~BlobShape() override = default;
|
~BlobShape() override = default;
|
||||||
|
|
||||||
[[nodiscard]] BlobGroup* group() const {
|
[[nodiscard]] BlobGroup* group() const { return m_group; }
|
||||||
return m_group;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setGroup(BlobGroup* g);
|
void setGroup(BlobGroup* g);
|
||||||
|
|
||||||
[[nodiscard]] qreal radius() const {
|
[[nodiscard]] qreal radius() const { return m_radius; }
|
||||||
return m_radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setRadius(qreal r);
|
void setRadius(qreal r);
|
||||||
|
|
||||||
@@ -37,9 +36,7 @@ void setRadius(qreal r);
|
|||||||
return m_centeredDeformMatrix;
|
return m_centeredDeformMatrix;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] QMatrix4x4 rawDeformMatrix() const {
|
[[nodiscard]] QMatrix4x4 rawDeformMatrix() const { return m_deformMatrix; }
|
||||||
return m_deformMatrix;
|
|
||||||
}
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void groupChanged();
|
void groupChanged();
|
||||||
@@ -49,22 +46,18 @@ void rawDeformMatrixChanged();
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
void componentComplete() override;
|
void componentComplete() override;
|
||||||
void geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) override;
|
void geometryChange(
|
||||||
|
const QRectF& newGeometry, const QRectF& oldGeometry) override;
|
||||||
void updatePolish() override;
|
void updatePolish() override;
|
||||||
QSGNode* updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) override;
|
QSGNode* updatePaintNode(QSGNode* oldNode, UpdatePaintNodeData*) override;
|
||||||
|
|
||||||
[[nodiscard]] virtual bool isInvertedRect() const {
|
[[nodiscard]] virtual bool isInvertedRect() const { return false; }
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool isExcluded(const BlobShape* /*other*/) const {
|
virtual bool isExcluded(const BlobShape*) const { return false; }
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void cornerRadii(float out[4]) const;
|
virtual void cornerRadii(float out[4]) const;
|
||||||
|
|
||||||
virtual void updatePhysics() {
|
virtual void updatePhysics() {}
|
||||||
}
|
|
||||||
|
|
||||||
virtual void registerWithGroup();
|
virtual void registerWithGroup();
|
||||||
virtual void unregisterFromGroup();
|
virtual void unregisterFromGroup();
|
||||||
|
|||||||
@@ -3,11 +3,10 @@
|
|||||||
namespace ZShell::components {
|
namespace ZShell::components {
|
||||||
|
|
||||||
ButtonRow::ButtonRow(QQuickItem* parent)
|
ButtonRow::ButtonRow(QQuickItem* parent)
|
||||||
: QQuickItem(parent)
|
: QQuickItem(parent), m_dirty(false), m_spacing(0.0) {
|
||||||
, m_dirty(false)
|
|
||||||
, m_spacing(0.0) {
|
|
||||||
setFlag(QQuickItem::ItemHasContents, true);
|
setFlag(QQuickItem::ItemHasContents, true);
|
||||||
QObject::connect(this, &ButtonRow::widthChanged, this, &ButtonRow::invalidate);
|
QObject::connect(
|
||||||
|
this, &ButtonRow::widthChanged, this, &ButtonRow::invalidate);
|
||||||
}
|
}
|
||||||
|
|
||||||
qreal ButtonRow::spacing() const {
|
qreal ButtonRow::spacing() const {
|
||||||
@@ -15,8 +14,7 @@ qreal ButtonRow::spacing() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ButtonRow::setSpacing(qreal spacing) {
|
void ButtonRow::setSpacing(qreal spacing) {
|
||||||
if (qFuzzyCompare(m_spacing + 1.0, spacing + 1.0))
|
if (qFuzzyCompare(m_spacing + 1.0, spacing + 1.0)) return;
|
||||||
return;
|
|
||||||
|
|
||||||
m_spacing = spacing;
|
m_spacing = spacing;
|
||||||
emit spacingChanged();
|
emit spacingChanged();
|
||||||
@@ -24,18 +22,33 @@ void ButtonRow::setSpacing(qreal spacing) {
|
|||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ButtonRow::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData& data) {
|
void ButtonRow::itemChange(
|
||||||
|
QQuickItem::ItemChange change, const QQuickItem::ItemChangeData& data) {
|
||||||
if (change == QQuickItem::ItemChildAddedChange) {
|
if (change == QQuickItem::ItemChildAddedChange) {
|
||||||
auto* const child = data.item;
|
auto* const child = data.item;
|
||||||
QObject::connect(child, &QQuickItem::implicitWidthChanged, this, &ButtonRow::invalidate);
|
QObject::connect(
|
||||||
QObject::connect(child, &QQuickItem::implicitHeightChanged, this, &ButtonRow::invalidate);
|
child,
|
||||||
QObject::connect(child, &QQuickItem::visibleChanged, this, &ButtonRow::invalidate);
|
&QQuickItem::implicitWidthChanged,
|
||||||
|
this,
|
||||||
|
&ButtonRow::invalidate);
|
||||||
|
QObject::connect(
|
||||||
|
child,
|
||||||
|
&QQuickItem::implicitHeightChanged,
|
||||||
|
this,
|
||||||
|
&ButtonRow::invalidate);
|
||||||
|
QObject::connect(
|
||||||
|
child, &QQuickItem::visibleChanged, this, &ButtonRow::invalidate);
|
||||||
|
|
||||||
const auto* childMeta = child->metaObject();
|
const auto* childMeta = child->metaObject();
|
||||||
const auto morphSignalIdx = childMeta->indexOfSignal("shapeMorphExpansionChanged()");
|
const auto morphSignalIdx =
|
||||||
|
childMeta->indexOfSignal("shapeMorphExpansionChanged()");
|
||||||
if (morphSignalIdx != -1)
|
if (morphSignalIdx != -1)
|
||||||
QObject::connect(child, childMeta->method(morphSignalIdx), this,
|
QObject::connect(
|
||||||
metaObject()->method(metaObject()->indexOfSlot("invalidate()")));
|
child,
|
||||||
|
childMeta->method(morphSignalIdx),
|
||||||
|
this,
|
||||||
|
metaObject()->method(
|
||||||
|
metaObject()->indexOfSlot("invalidate()")));
|
||||||
|
|
||||||
invalidate();
|
invalidate();
|
||||||
} else if (change == QQuickItem::ItemChildRemovedChange) {
|
} else if (change == QQuickItem::ItemChildRemovedChange) {
|
||||||
@@ -77,8 +90,7 @@ void ButtonRow::relayout() {
|
|||||||
maxHeight = qMax(maxHeight, child->implicitHeight());
|
maxHeight = qMax(maxHeight, child->implicitHeight());
|
||||||
|
|
||||||
const auto prop = child->property("fillWidth");
|
const auto prop = child->property("fillWidth");
|
||||||
if (!prop.isValid())
|
if (!prop.isValid()) continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
if (prop.toBool()) {
|
if (prop.toBool()) {
|
||||||
fillWidthCount++;
|
fillWidthCount++;
|
||||||
@@ -88,15 +100,17 @@ void ButtonRow::relayout() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fillWidthCount == 0)
|
if (fillWidthCount == 0) fillWidthCount = 1; // Avoid divide by 0
|
||||||
fillWidthCount = 1; // Avoid divide by 0
|
|
||||||
|
|
||||||
const auto widthPerItem = (width() - totalSpacing - reservedWidth) / static_cast<qreal>(fillWidthCount);
|
const auto widthPerItem = (width() - totalSpacing - reservedWidth) /
|
||||||
|
static_cast<qreal>(fillWidthCount);
|
||||||
|
|
||||||
QList<qreal> baseWidths;
|
QList<qreal> baseWidths;
|
||||||
baseWidths.reserve(nChildren);
|
baseWidths.reserve(nChildren);
|
||||||
for (auto* const child : validChildren)
|
for (auto* const child : validChildren)
|
||||||
baseWidths.append(child->property("fillWidth").toBool() ? widthPerItem : child->implicitWidth());
|
baseWidths.append(
|
||||||
|
child->property("fillWidth").toBool() ? widthPerItem
|
||||||
|
: child->implicitWidth());
|
||||||
|
|
||||||
qreal accX = 0;
|
qreal accX = 0;
|
||||||
for (int i = 0; i < nChildren; ++i) {
|
for (int i = 0; i < nChildren; ++i) {
|
||||||
@@ -108,12 +122,12 @@ void ButtonRow::relayout() {
|
|||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
// Items at edges push by full amount, items in middle push by half
|
// Items at edges push by full amount, items in middle push by half
|
||||||
if (i > 1)
|
if (i > 1) prevExtraWidth /= 2;
|
||||||
prevExtraWidth /= 2;
|
if (i < nChildren - 2) nextExtraWidth /= 2;
|
||||||
if (i < nChildren - 2)
|
|
||||||
nextExtraWidth /= 2;
|
|
||||||
|
|
||||||
child->setWidth(baseWidths[i] + getMorphExpansion(child) - prevExtraWidth - nextExtraWidth);
|
child->setWidth(
|
||||||
|
baseWidths[i] + getMorphExpansion(child) - prevExtraWidth -
|
||||||
|
nextExtraWidth);
|
||||||
child->setHeight(maxHeight);
|
child->setHeight(maxHeight);
|
||||||
|
|
||||||
child->setX(accX);
|
child->setX(accX);
|
||||||
|
|||||||
@@ -20,13 +20,14 @@ signals:
|
|||||||
void spacingChanged();
|
void spacingChanged();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData& data) override;
|
void itemChange(
|
||||||
|
QQuickItem::ItemChange change,
|
||||||
|
const QQuickItem::ItemChangeData& data) override;
|
||||||
void updatePolish() override;
|
void updatePolish() override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void invalidate();
|
void invalidate();
|
||||||
|
|
||||||
private:
|
|
||||||
void relayout();
|
void relayout();
|
||||||
static qreal getMorphExpansion(const QQuickItem* item);
|
static qreal getMorphExpansion(const QQuickItem* item);
|
||||||
|
|
||||||
|
|||||||
@@ -21,8 +21,7 @@ qreal LazyListViewAttached::preferredHeight() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LazyListViewAttached::setPreferredHeight(qreal height) {
|
void LazyListViewAttached::setPreferredHeight(qreal height) {
|
||||||
if (qFuzzyCompare(m_preferredHeight + 1.0, height + 1.0))
|
if (qFuzzyCompare(m_preferredHeight + 1.0, height + 1.0)) return;
|
||||||
return;
|
|
||||||
m_preferredHeight = height;
|
m_preferredHeight = height;
|
||||||
emit preferredHeightChanged();
|
emit preferredHeightChanged();
|
||||||
}
|
}
|
||||||
@@ -32,8 +31,7 @@ qreal LazyListViewAttached::visibleHeight() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LazyListViewAttached::setVisibleHeight(qreal height) {
|
void LazyListViewAttached::setVisibleHeight(qreal height) {
|
||||||
if (qFuzzyCompare(m_visibleHeight + 1.0, height + 1.0))
|
if (qFuzzyCompare(m_visibleHeight + 1.0, height + 1.0)) return;
|
||||||
return;
|
|
||||||
m_visibleHeight = height;
|
m_visibleHeight = height;
|
||||||
emit visibleHeightChanged();
|
emit visibleHeightChanged();
|
||||||
}
|
}
|
||||||
@@ -43,8 +41,7 @@ bool LazyListViewAttached::ready() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LazyListViewAttached::setReady(bool ready) {
|
void LazyListViewAttached::setReady(bool ready) {
|
||||||
if (m_ready == ready)
|
if (m_ready == ready) return;
|
||||||
return;
|
|
||||||
m_ready = ready;
|
m_ready = ready;
|
||||||
emit readyChanged();
|
emit readyChanged();
|
||||||
}
|
}
|
||||||
@@ -54,8 +51,7 @@ bool LazyListViewAttached::adding() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LazyListViewAttached::setAdding(bool adding) {
|
void LazyListViewAttached::setAdding(bool adding) {
|
||||||
if (m_adding == adding)
|
if (m_adding == adding) return;
|
||||||
return;
|
|
||||||
m_adding = adding;
|
m_adding = adding;
|
||||||
emit addingChanged();
|
emit addingChanged();
|
||||||
}
|
}
|
||||||
@@ -65,8 +61,7 @@ bool LazyListViewAttached::removing() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LazyListViewAttached::setRemoving(bool removing) {
|
void LazyListViewAttached::setRemoving(bool removing) {
|
||||||
if (m_removing == removing)
|
if (m_removing == removing) return;
|
||||||
return;
|
|
||||||
m_removing = removing;
|
m_removing = removing;
|
||||||
emit removingChanged();
|
emit removingChanged();
|
||||||
}
|
}
|
||||||
@@ -76,8 +71,7 @@ bool LazyListViewAttached::trackViewport() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LazyListViewAttached::setTrackViewport(bool track) {
|
void LazyListViewAttached::setTrackViewport(bool track) {
|
||||||
if (m_trackViewport == track)
|
if (m_trackViewport == track) return;
|
||||||
return;
|
|
||||||
m_trackViewport = track;
|
m_trackViewport = track;
|
||||||
emit trackViewportChanged();
|
emit trackViewportChanged();
|
||||||
}
|
}
|
||||||
@@ -102,16 +96,13 @@ QAbstractItemModel* LazyListView::model() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LazyListView::setModel(QAbstractItemModel* model) {
|
void LazyListView::setModel(QAbstractItemModel* model) {
|
||||||
if (m_model == model)
|
if (m_model == model) return;
|
||||||
return;
|
|
||||||
|
|
||||||
if (m_model)
|
if (m_model) disconnectModel();
|
||||||
disconnectModel();
|
|
||||||
|
|
||||||
m_model = model;
|
m_model = model;
|
||||||
|
|
||||||
if (m_model)
|
if (m_model) connectModel();
|
||||||
connectModel();
|
|
||||||
|
|
||||||
resetContent();
|
resetContent();
|
||||||
emit modelChanged();
|
emit modelChanged();
|
||||||
@@ -122,8 +113,7 @@ QQmlComponent* LazyListView::delegate() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LazyListView::setDelegate(QQmlComponent* delegate) {
|
void LazyListView::setDelegate(QQmlComponent* delegate) {
|
||||||
if (m_delegate == delegate)
|
if (m_delegate == delegate) return;
|
||||||
return;
|
|
||||||
|
|
||||||
m_delegate = delegate;
|
m_delegate = delegate;
|
||||||
resetContent();
|
resetContent();
|
||||||
@@ -135,8 +125,7 @@ qreal LazyListView::spacing() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LazyListView::setSpacing(qreal spacing) {
|
void LazyListView::setSpacing(qreal spacing) {
|
||||||
if (qFuzzyCompare(m_spacing, spacing))
|
if (qFuzzyCompare(m_spacing, spacing)) return;
|
||||||
return;
|
|
||||||
m_spacing = spacing;
|
m_spacing = spacing;
|
||||||
emit spacingChanged();
|
emit spacingChanged();
|
||||||
polish();
|
polish();
|
||||||
@@ -155,8 +144,7 @@ qreal LazyListView::contentY() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LazyListView::setContentY(qreal contentY) {
|
void LazyListView::setContentY(qreal contentY) {
|
||||||
if (qFuzzyCompare(m_contentY, contentY))
|
if (qFuzzyCompare(m_contentY, contentY)) return;
|
||||||
return;
|
|
||||||
m_contentY = contentY;
|
m_contentY = contentY;
|
||||||
emit contentYChanged();
|
emit contentYChanged();
|
||||||
polish();
|
polish();
|
||||||
@@ -167,12 +155,10 @@ QRectF LazyListView::viewport() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LazyListView::setViewport(const QRectF& viewport) {
|
void LazyListView::setViewport(const QRectF& viewport) {
|
||||||
if (m_viewport == viewport)
|
if (m_viewport == viewport) return;
|
||||||
return;
|
|
||||||
m_viewport = viewport;
|
m_viewport = viewport;
|
||||||
emit viewportChanged();
|
emit viewportChanged();
|
||||||
if (m_useCustomViewport)
|
if (m_useCustomViewport) polish();
|
||||||
polish();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LazyListView::useCustomViewport() const {
|
bool LazyListView::useCustomViewport() const {
|
||||||
@@ -180,8 +166,7 @@ bool LazyListView::useCustomViewport() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LazyListView::setUseCustomViewport(bool use) {
|
void LazyListView::setUseCustomViewport(bool use) {
|
||||||
if (m_useCustomViewport == use)
|
if (m_useCustomViewport == use) return;
|
||||||
return;
|
|
||||||
m_useCustomViewport = use;
|
m_useCustomViewport = use;
|
||||||
emit useCustomViewportChanged();
|
emit useCustomViewportChanged();
|
||||||
polish();
|
polish();
|
||||||
@@ -192,8 +177,7 @@ qreal LazyListView::cacheBuffer() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LazyListView::setCacheBuffer(qreal buffer) {
|
void LazyListView::setCacheBuffer(qreal buffer) {
|
||||||
if (qFuzzyCompare(m_cacheBuffer, buffer))
|
if (qFuzzyCompare(m_cacheBuffer, buffer)) return;
|
||||||
return;
|
|
||||||
m_cacheBuffer = buffer;
|
m_cacheBuffer = buffer;
|
||||||
emit cacheBufferChanged();
|
emit cacheBufferChanged();
|
||||||
polish();
|
polish();
|
||||||
@@ -204,8 +188,7 @@ qreal LazyListView::estimatedHeight() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LazyListView::setEstimatedHeight(qreal height) {
|
void LazyListView::setEstimatedHeight(qreal height) {
|
||||||
if (qFuzzyCompare(m_estimatedHeight, height))
|
if (qFuzzyCompare(m_estimatedHeight, height)) return;
|
||||||
return;
|
|
||||||
m_estimatedHeight = height;
|
m_estimatedHeight = height;
|
||||||
emit estimatedHeightChanged();
|
emit estimatedHeightChanged();
|
||||||
polish();
|
polish();
|
||||||
@@ -216,17 +199,14 @@ bool LazyListView::asynchronous() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LazyListView::setAsynchronous(bool async) {
|
void LazyListView::setAsynchronous(bool async) {
|
||||||
if (m_asynchronous == async)
|
if (m_asynchronous == async) return;
|
||||||
return;
|
|
||||||
m_asynchronous = async;
|
m_asynchronous = async;
|
||||||
emit asynchronousChanged();
|
emit asynchronousChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
qreal LazyListView::effectiveEstimatedHeight() const {
|
qreal LazyListView::effectiveEstimatedHeight() const {
|
||||||
if (m_estimatedHeight >= 0)
|
if (m_estimatedHeight >= 0) return m_estimatedHeight;
|
||||||
return m_estimatedHeight;
|
if (m_knownHeightCount > 0) return m_knownHeightSum / m_knownHeightCount;
|
||||||
if (m_knownHeightCount > 0)
|
|
||||||
return m_knownHeightSum / m_knownHeightCount;
|
|
||||||
return 40;
|
return 40;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,8 +221,7 @@ void LazyListView::untrackHeight(qreal height) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
qreal LazyListView::delegateHeight(QQuickItem* item) {
|
qreal LazyListView::delegateHeight(QQuickItem* item) {
|
||||||
if (!item)
|
if (!item) return 0;
|
||||||
return 0;
|
|
||||||
|
|
||||||
auto* attached = qobject_cast<LazyListViewAttached*>(
|
auto* attached = qobject_cast<LazyListViewAttached*>(
|
||||||
qmlAttachedPropertiesObject<LazyListView>(item, false));
|
qmlAttachedPropertiesObject<LazyListView>(item, false));
|
||||||
@@ -253,14 +232,12 @@ qreal LazyListView::delegateHeight(QQuickItem* item) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
qreal LazyListView::delegateVisibleHeight(QQuickItem* item) {
|
qreal LazyListView::delegateVisibleHeight(QQuickItem* item) {
|
||||||
if (!item)
|
if (!item) return 0;
|
||||||
return 0;
|
|
||||||
|
|
||||||
auto* attached = qobject_cast<LazyListViewAttached*>(
|
auto* attached = qobject_cast<LazyListViewAttached*>(
|
||||||
qmlAttachedPropertiesObject<LazyListView>(item, false));
|
qmlAttachedPropertiesObject<LazyListView>(item, false));
|
||||||
if (attached) {
|
if (attached) {
|
||||||
if (attached->visibleHeight() >= 0)
|
if (attached->visibleHeight() >= 0) return attached->visibleHeight();
|
||||||
return attached->visibleHeight();
|
|
||||||
if (attached->preferredHeight() >= 0)
|
if (attached->preferredHeight() >= 0)
|
||||||
return attached->preferredHeight();
|
return attached->preferredHeight();
|
||||||
}
|
}
|
||||||
@@ -269,8 +246,7 @@ qreal LazyListView::delegateVisibleHeight(QQuickItem* item) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool LazyListView::isDelegateReady(QQuickItem* item) {
|
bool LazyListView::isDelegateReady(QQuickItem* item) {
|
||||||
if (!item)
|
if (!item) return false;
|
||||||
return false;
|
|
||||||
auto* att = qobject_cast<LazyListViewAttached*>(
|
auto* att = qobject_cast<LazyListViewAttached*>(
|
||||||
qmlAttachedPropertiesObject<LazyListView>(item, false));
|
qmlAttachedPropertiesObject<LazyListView>(item, false));
|
||||||
return !att || att->ready();
|
return !att || att->ready();
|
||||||
@@ -281,8 +257,7 @@ int LazyListView::removeDuration() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LazyListView::setRemoveDuration(int duration) {
|
void LazyListView::setRemoveDuration(int duration) {
|
||||||
if (m_removeDuration == duration)
|
if (m_removeDuration == duration) return;
|
||||||
return;
|
|
||||||
m_removeDuration = duration;
|
m_removeDuration = duration;
|
||||||
emit removeDurationChanged();
|
emit removeDurationChanged();
|
||||||
}
|
}
|
||||||
@@ -292,8 +267,7 @@ int LazyListView::readyDelay() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LazyListView::setReadyDelay(int delay) {
|
void LazyListView::setReadyDelay(int delay) {
|
||||||
if (m_readyDelay == delay)
|
if (m_readyDelay == delay) return;
|
||||||
return;
|
|
||||||
m_readyDelay = delay;
|
m_readyDelay = delay;
|
||||||
emit readyDelayChanged();
|
emit readyDelayChanged();
|
||||||
}
|
}
|
||||||
@@ -308,17 +282,15 @@ void LazyListView::componentComplete() {
|
|||||||
resetContent();
|
resetContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LazyListView::geometryChange(const QRectF& newGeometry,
|
void LazyListView::geometryChange(
|
||||||
const QRectF& oldGeometry) {
|
const QRectF& newGeometry, const QRectF& oldGeometry) {
|
||||||
QQuickItem::geometryChange(newGeometry, oldGeometry);
|
QQuickItem::geometryChange(newGeometry, oldGeometry);
|
||||||
|
|
||||||
if (!m_componentComplete)
|
if (!m_componentComplete) return;
|
||||||
return;
|
|
||||||
|
|
||||||
if (!qFuzzyCompare(newGeometry.width(), oldGeometry.width())) {
|
if (!qFuzzyCompare(newGeometry.width(), oldGeometry.width())) {
|
||||||
for (auto& entry : m_delegates) {
|
for (auto& entry : m_delegates) {
|
||||||
if (entry.item)
|
if (entry.item) entry.item->setWidth(newGeometry.width());
|
||||||
entry.item->setWidth(newGeometry.width());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -326,8 +298,7 @@ void LazyListView::geometryChange(const QRectF& newGeometry,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LazyListView::updatePolish() {
|
void LazyListView::updatePolish() {
|
||||||
if (!m_componentComplete || !m_model || !m_delegate)
|
if (!m_componentComplete || !m_model || !m_delegate) return;
|
||||||
return;
|
|
||||||
|
|
||||||
// Flush pending inserts — make items visible and clear the adding flag
|
// Flush pending inserts — make items visible and clear the adding flag
|
||||||
// so enter animations begin. When readyDelay > 0 the entire insert is
|
// so enter animations begin. When readyDelay > 0 the entire insert is
|
||||||
@@ -335,9 +306,9 @@ void LazyListView::updatePolish() {
|
|||||||
|
|
||||||
// Collect pending indices first — avoids scanning the entire hash each frame
|
// Collect pending indices first — avoids scanning the entire hash each frame
|
||||||
QList<int> pendingIndices;
|
QList<int> pendingIndices;
|
||||||
for (auto it = m_delegates.constBegin(); it != m_delegates.constEnd(); ++it) {
|
for (auto it = m_delegates.constBegin(); it != m_delegates.constEnd();
|
||||||
if (it->pendingInsert && it->item)
|
++it) {
|
||||||
pendingIndices.append(it.key());
|
if (it->pendingInsert && it->item) pendingIndices.append(it.key());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int pendingIdx : pendingIndices) {
|
for (int pendingIdx : pendingIndices) {
|
||||||
@@ -349,8 +320,7 @@ void LazyListView::updatePolish() {
|
|||||||
auto* item = entry.item;
|
auto* item = entry.item;
|
||||||
QTimer::singleShot(m_readyDelay, this, [this, item] {
|
QTimer::singleShot(m_readyDelay, this, [this, item] {
|
||||||
auto indexIt = m_itemToIndex.find(item);
|
auto indexIt = m_itemToIndex.find(item);
|
||||||
if (indexIt == m_itemToIndex.end())
|
if (indexIt == m_itemToIndex.end()) return;
|
||||||
return;
|
|
||||||
const int idx = indexIt.value();
|
const int idx = indexIt.value();
|
||||||
auto it = m_delegates.find(idx);
|
auto it = m_delegates.find(idx);
|
||||||
if (it == m_delegates.end() || it->item != item ||
|
if (it == m_delegates.end() || it->item != item ||
|
||||||
@@ -375,14 +345,11 @@ void LazyListView::updatePolish() {
|
|||||||
? m_layout[i].height
|
? m_layout[i].height
|
||||||
: effectiveEstimatedHeight();
|
: effectiveEstimatedHeight();
|
||||||
if (h > 0) {
|
if (h > 0) {
|
||||||
if (hasVisItem)
|
if (hasVisItem) visualY += m_spacing;
|
||||||
visualY += m_spacing;
|
|
||||||
hasVisItem = true;
|
hasVisItem = true;
|
||||||
}
|
}
|
||||||
if (i == idx)
|
if (i == idx) break;
|
||||||
break;
|
if (h > 0) visualY += h;
|
||||||
if (h > 0)
|
|
||||||
visualY += h;
|
|
||||||
}
|
}
|
||||||
item->setY(visualY - m_contentY);
|
item->setY(visualY - m_contentY);
|
||||||
}
|
}
|
||||||
@@ -397,8 +364,8 @@ void LazyListView::updatePolish() {
|
|||||||
|
|
||||||
// Animate from visual position to layout position
|
// Animate from visual position to layout position
|
||||||
if (idx >= 0 && idx < static_cast<int>(m_layout.size()))
|
if (idx >= 0 && idx < static_cast<int>(m_layout.size()))
|
||||||
item->setProperty("y",
|
item->setProperty(
|
||||||
m_layout[idx].targetY - m_contentY);
|
"y", m_layout[idx].targetY - m_contentY);
|
||||||
|
|
||||||
polish();
|
polish();
|
||||||
});
|
});
|
||||||
@@ -432,8 +399,7 @@ void LazyListView::updatePolish() {
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
const int idx = entry.modelIndex;
|
const int idx = entry.modelIndex;
|
||||||
if (idx < 0 || idx >= layoutSize)
|
if (idx < 0 || idx >= layoutSize) continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
if (m_layout[idx].heightKnown && qFuzzyIsNull(m_layout[idx].height))
|
if (m_layout[idx].heightKnown && qFuzzyIsNull(m_layout[idx].height))
|
||||||
continue;
|
continue;
|
||||||
@@ -444,8 +410,6 @@ void LazyListView::updatePolish() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Layout Engine ---
|
|
||||||
|
|
||||||
void LazyListView::relayout() {
|
void LazyListView::relayout() {
|
||||||
// Layout positioning uses preferredHeight (final/non-animated).
|
// Layout positioning uses preferredHeight (final/non-animated).
|
||||||
// Only add spacing between items with non-zero height.
|
// Only add spacing between items with non-zero height.
|
||||||
@@ -455,8 +419,7 @@ void LazyListView::relayout() {
|
|||||||
const qreal layoutH = record.heightKnown ? record.height
|
const qreal layoutH = record.heightKnown ? record.height
|
||||||
: effectiveEstimatedHeight();
|
: effectiveEstimatedHeight();
|
||||||
if (layoutH > 0) {
|
if (layoutH > 0) {
|
||||||
if (hasLayoutItem)
|
if (hasLayoutItem) y += m_spacing;
|
||||||
y += m_spacing;
|
|
||||||
hasLayoutItem = true;
|
hasLayoutItem = true;
|
||||||
record.targetY = y;
|
record.targetY = y;
|
||||||
y += layoutH;
|
y += layoutH;
|
||||||
@@ -483,8 +446,7 @@ void LazyListView::relayout() {
|
|||||||
h = m_layout[i].heightKnown ? m_layout[i].height
|
h = m_layout[i].heightKnown ? m_layout[i].height
|
||||||
: effectiveEstimatedHeight();
|
: effectiveEstimatedHeight();
|
||||||
if (h > 0) {
|
if (h > 0) {
|
||||||
if (hasVisItem)
|
if (hasVisItem) visY += m_spacing;
|
||||||
visY += m_spacing;
|
|
||||||
hasVisItem = true;
|
hasVisItem = true;
|
||||||
visY += h;
|
visY += h;
|
||||||
}
|
}
|
||||||
@@ -492,11 +454,9 @@ void LazyListView::relayout() {
|
|||||||
|
|
||||||
// Account for dying delegates still visually present
|
// Account for dying delegates still visually present
|
||||||
for (const auto& dying : std::as_const(m_dyingDelegates)) {
|
for (const auto& dying : std::as_const(m_dyingDelegates)) {
|
||||||
if (!dying.item)
|
if (!dying.item) continue;
|
||||||
continue;
|
|
||||||
const qreal dyingH = delegateVisibleHeight(dying.item);
|
const qreal dyingH = delegateVisibleHeight(dying.item);
|
||||||
if (dyingH > 0)
|
if (dyingH > 0) visY = std::max(visY, dying.item->y() + dyingH);
|
||||||
visY = std::max(visY, dying.item->y() + dyingH);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!qFuzzyCompare(m_contentHeight + 1.0, visY + 1.0)) {
|
if (!qFuzzyCompare(m_contentHeight + 1.0, visY + 1.0)) {
|
||||||
@@ -519,8 +479,7 @@ QRectF LazyListView::effectiveViewport() const {
|
|||||||
if (!m_useCustomViewport && m_layoutHeight > 0) {
|
if (!m_useCustomViewport && m_layoutHeight > 0) {
|
||||||
const qreal top = std::min(vp.y(), m_layoutHeight);
|
const qreal top = std::min(vp.y(), m_layoutHeight);
|
||||||
const qreal bottom = std::max(vp.y() + vp.height(), 0.0);
|
const qreal bottom = std::max(vp.y() + vp.height(), 0.0);
|
||||||
if (bottom > top)
|
if (bottom > top) vp = QRectF(vp.x(), top, vp.width(), bottom - top);
|
||||||
vp = QRectF(vp.x(), top, vp.width(), bottom - top);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vp.adjust(0, -m_cacheBuffer, 0, m_cacheBuffer);
|
vp.adjust(0, -m_cacheBuffer, 0, m_cacheBuffer);
|
||||||
@@ -541,12 +500,10 @@ QRectF LazyListView::effectiveViewport() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::pair<int, int> LazyListView::computeVisibleRange() const {
|
std::pair<int, int> LazyListView::computeVisibleRange() const {
|
||||||
if (m_layout.isEmpty())
|
if (m_layout.isEmpty()) return {-1, -1};
|
||||||
return {-1, -1};
|
|
||||||
|
|
||||||
const auto vp = effectiveViewport();
|
const auto vp = effectiveViewport();
|
||||||
if (vp.isEmpty())
|
if (vp.isEmpty()) return {-1, -1};
|
||||||
return {-1, -1};
|
|
||||||
|
|
||||||
const qreal vpTop = vp.y();
|
const qreal vpTop = vp.y();
|
||||||
const qreal vpBottom = vp.y() + vp.height();
|
const qreal vpBottom = vp.y() + vp.height();
|
||||||
@@ -571,22 +528,18 @@ std::pair<int, int> LazyListView::computeVisibleRange() const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (first >= static_cast<int>(m_layout.size()))
|
if (first >= static_cast<int>(m_layout.size())) return {-1, -1};
|
||||||
return {-1, -1};
|
|
||||||
|
|
||||||
// Linear scan for last visible item
|
// Linear scan for last visible item
|
||||||
int last = first;
|
int last = first;
|
||||||
for (int i = first; i < static_cast<int>(m_layout.size()); ++i) {
|
for (int i = first; i < static_cast<int>(m_layout.size()); ++i) {
|
||||||
if (m_layout[i].targetY > vpBottom)
|
if (m_layout[i].targetY > vpBottom) break;
|
||||||
break;
|
|
||||||
last = i;
|
last = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {first, last};
|
return {first, last};
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Delegate Lifecycle ---
|
|
||||||
|
|
||||||
void LazyListView::syncDelegates() {
|
void LazyListView::syncDelegates() {
|
||||||
const auto [first, last] = computeVisibleRange();
|
const auto [first, last] = computeVisibleRange();
|
||||||
|
|
||||||
@@ -601,8 +554,7 @@ void LazyListView::syncDelegates() {
|
|||||||
const auto vp = effectiveViewport();
|
const auto vp = effectiveViewport();
|
||||||
QList<int> toRemove;
|
QList<int> toRemove;
|
||||||
for (auto it = m_delegates.begin(); it != m_delegates.end(); ++it) {
|
for (auto it = m_delegates.begin(); it != m_delegates.end(); ++it) {
|
||||||
if (visibleIndices.contains(it.key()))
|
if (visibleIndices.contains(it.key())) continue;
|
||||||
continue;
|
|
||||||
if (!it->item || vp.isEmpty()) {
|
if (!it->item || vp.isEmpty()) {
|
||||||
toRemove.append(it.key());
|
toRemove.append(it.key());
|
||||||
continue;
|
continue;
|
||||||
@@ -622,11 +574,9 @@ void LazyListView::syncDelegates() {
|
|||||||
std::min(destroyBudget, static_cast<int>(toRemove.size())));
|
std::min(destroyBudget, static_cast<int>(toRemove.size())));
|
||||||
int destroyed = 0;
|
int destroyed = 0;
|
||||||
for (int idx : toRemove) {
|
for (int idx : toRemove) {
|
||||||
if (destroyed >= destroyBudget)
|
if (destroyed >= destroyBudget) break;
|
||||||
break;
|
|
||||||
auto entry = m_delegates.take(idx);
|
auto entry = m_delegates.take(idx);
|
||||||
if (entry.item)
|
if (entry.item) m_itemToIndex.remove(entry.item);
|
||||||
m_itemToIndex.remove(entry.item);
|
|
||||||
removedEntries.append(std::move(entry));
|
removedEntries.append(std::move(entry));
|
||||||
++destroyed;
|
++destroyed;
|
||||||
}
|
}
|
||||||
@@ -637,8 +587,7 @@ void LazyListView::syncDelegates() {
|
|||||||
QList<int> toCreate;
|
QList<int> toCreate;
|
||||||
if (first >= 0) {
|
if (first >= 0) {
|
||||||
for (int i = first; i <= last; ++i) {
|
for (int i = first; i <= last; ++i) {
|
||||||
if (!m_delegates.contains(i))
|
if (!m_delegates.contains(i)) toCreate.append(i);
|
||||||
toCreate.append(i);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -647,8 +596,7 @@ void LazyListView::syncDelegates() {
|
|||||||
: static_cast<int>(toCreate.size());
|
: static_cast<int>(toCreate.size());
|
||||||
int created = 0;
|
int created = 0;
|
||||||
for (int i : toCreate) {
|
for (int i : toCreate) {
|
||||||
if (created >= createBudget)
|
if (created >= createBudget) break;
|
||||||
break;
|
|
||||||
|
|
||||||
auto entry = createDelegate(i);
|
auto entry = createDelegate(i);
|
||||||
if (entry.item) {
|
if (entry.item) {
|
||||||
@@ -674,25 +622,21 @@ LazyListView::DelegateEntry LazyListView::createDelegate(int modelIndex) {
|
|||||||
DelegateEntry entry;
|
DelegateEntry entry;
|
||||||
entry.modelIndex = modelIndex;
|
entry.modelIndex = modelIndex;
|
||||||
|
|
||||||
if (!m_delegate || !m_model)
|
if (!m_delegate || !m_model) return entry;
|
||||||
return entry;
|
|
||||||
|
|
||||||
const auto roleNames = m_model->roleNames();
|
const auto roleNames = m_model->roleNames();
|
||||||
|
|
||||||
// Use the delegate component's creation context for beginCreate
|
// Use the delegate component's creation context for beginCreate
|
||||||
// so bound components (pragma ComponentBehavior: Bound) are accepted.
|
// so bound components (pragma ComponentBehavior: Bound) are accepted.
|
||||||
auto* compContext = m_delegate->creationContext();
|
auto* compContext = m_delegate->creationContext();
|
||||||
if (!compContext)
|
if (!compContext) compContext = qmlContext(this);
|
||||||
compContext = qmlContext(this);
|
if (!compContext) return entry;
|
||||||
if (!compContext)
|
|
||||||
return entry;
|
|
||||||
|
|
||||||
auto* obj = m_delegate->beginCreate(compContext);
|
auto* obj = m_delegate->beginCreate(compContext);
|
||||||
entry.item = qobject_cast<QQuickItem*>(obj);
|
entry.item = qobject_cast<QQuickItem*>(obj);
|
||||||
|
|
||||||
if (!entry.item) {
|
if (!entry.item) {
|
||||||
if (obj)
|
if (obj) m_delegate->completeCreate();
|
||||||
m_delegate->completeCreate();
|
|
||||||
delete obj;
|
delete obj;
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
@@ -705,16 +649,15 @@ LazyListView::DelegateEntry LazyListView::createDelegate(int modelIndex) {
|
|||||||
for (auto it = roleNames.constBegin(); it != roleNames.constEnd(); ++it) {
|
for (auto it = roleNames.constBegin(); it != roleNames.constEnd(); ++it) {
|
||||||
const auto name = QString::fromUtf8(it.value());
|
const auto name = QString::fromUtf8(it.value());
|
||||||
initialProps.insert(name, m_model->data(index, it.key()));
|
initialProps.insert(name, m_model->data(index, it.key()));
|
||||||
if (name == QStringLiteral("modelData"))
|
if (name == QStringLiteral("modelData")) hasModelData = true;
|
||||||
hasModelData = true;
|
|
||||||
}
|
}
|
||||||
initialProps.insert(QStringLiteral("index"), modelIndex);
|
initialProps.insert(QStringLiteral("index"), modelIndex);
|
||||||
|
|
||||||
if (!hasModelData) {
|
if (!hasModelData) {
|
||||||
const auto role = roleNames.isEmpty() ? Qt::DisplayRole
|
const auto role = roleNames.isEmpty() ? Qt::DisplayRole
|
||||||
: roleNames.constBegin().key();
|
: roleNames.constBegin().key();
|
||||||
initialProps.insert(QStringLiteral("modelData"),
|
initialProps.insert(
|
||||||
m_model->data(index, role));
|
QStringLiteral("modelData"), m_model->data(index, role));
|
||||||
}
|
}
|
||||||
|
|
||||||
m_delegate->setInitialProperties(entry.item, initialProps);
|
m_delegate->setInitialProperties(entry.item, initialProps);
|
||||||
@@ -728,8 +671,7 @@ LazyListView::DelegateEntry LazyListView::createDelegate(int modelIndex) {
|
|||||||
m_layout[modelIndex].isNew) {
|
m_layout[modelIndex].isNew) {
|
||||||
auto* addingAttached = qobject_cast<LazyListViewAttached*>(
|
auto* addingAttached = qobject_cast<LazyListViewAttached*>(
|
||||||
qmlAttachedPropertiesObject<LazyListView>(entry.item, true));
|
qmlAttachedPropertiesObject<LazyListView>(entry.item, true));
|
||||||
if (addingAttached)
|
if (addingAttached) addingAttached->setAdding(true);
|
||||||
addingAttached->setAdding(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_delegate->completeCreate();
|
m_delegate->completeCreate();
|
||||||
@@ -740,15 +682,12 @@ LazyListView::DelegateEntry LazyListView::createDelegate(int modelIndex) {
|
|||||||
// Height-change handler — uses m_itemToIndex for O(1) lookup.
|
// Height-change handler — uses m_itemToIndex for O(1) lookup.
|
||||||
// Ignored while the delegate is not yet ready.
|
// Ignored while the delegate is not yet ready.
|
||||||
auto onHeightChanged = [this, item = entry.item] {
|
auto onHeightChanged = [this, item = entry.item] {
|
||||||
if (!isDelegateReady(item))
|
if (!isDelegateReady(item)) return;
|
||||||
return;
|
|
||||||
auto indexIt = m_itemToIndex.find(item);
|
auto indexIt = m_itemToIndex.find(item);
|
||||||
if (indexIt == m_itemToIndex.end())
|
if (indexIt == m_itemToIndex.end()) return;
|
||||||
return;
|
|
||||||
const int idx = indexIt.value();
|
const int idx = indexIt.value();
|
||||||
auto delegateIt = m_delegates.find(idx);
|
auto delegateIt = m_delegates.find(idx);
|
||||||
if (delegateIt == m_delegates.end() || delegateIt->item != item)
|
if (delegateIt == m_delegates.end() || delegateIt->item != item) return;
|
||||||
return;
|
|
||||||
const qreal h = delegateHeight(item);
|
const qreal h = delegateHeight(item);
|
||||||
if (idx < static_cast<int>(m_layout.size()) &&
|
if (idx < static_cast<int>(m_layout.size()) &&
|
||||||
!qFuzzyCompare(m_layout[idx].height + 1.0, h + 1.0)) {
|
!qFuzzyCompare(m_layout[idx].height + 1.0, h + 1.0)) {
|
||||||
@@ -756,8 +695,7 @@ LazyListView::DelegateEntry LazyListView::createDelegate(int modelIndex) {
|
|||||||
const bool wasKnown = m_layout[idx].heightKnown;
|
const bool wasKnown = m_layout[idx].heightKnown;
|
||||||
m_layout[idx].height = h;
|
m_layout[idx].height = h;
|
||||||
m_layout[idx].heightKnown = true;
|
m_layout[idx].heightKnown = true;
|
||||||
if (wasKnown)
|
if (wasKnown) untrackHeight(oldH);
|
||||||
untrackHeight(oldH);
|
|
||||||
trackHeight(h);
|
trackHeight(h);
|
||||||
|
|
||||||
// If this tracked item is above the viewport, emit a
|
// If this tracked item is above the viewport, emit a
|
||||||
@@ -785,37 +723,35 @@ LazyListView::DelegateEntry LazyListView::createDelegate(int modelIndex) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Watch implicitHeight as fallback
|
// Watch implicitHeight as fallback
|
||||||
connect(entry.item,
|
connect(
|
||||||
&QQuickItem::implicitHeightChanged,
|
entry.item, &QQuickItem::implicitHeightChanged, this, onHeightChanged);
|
||||||
this,
|
|
||||||
onHeightChanged);
|
|
||||||
|
|
||||||
// Watch attached properties if the delegate uses them
|
// Watch attached properties if the delegate uses them
|
||||||
auto* attached = qobject_cast<LazyListViewAttached*>(
|
auto* attached = qobject_cast<LazyListViewAttached*>(
|
||||||
qmlAttachedPropertiesObject<LazyListView>(entry.item, false));
|
qmlAttachedPropertiesObject<LazyListView>(entry.item, false));
|
||||||
if (attached) {
|
if (attached) {
|
||||||
connect(attached,
|
connect(
|
||||||
|
attached,
|
||||||
&LazyListViewAttached::preferredHeightChanged,
|
&LazyListViewAttached::preferredHeightChanged,
|
||||||
this,
|
this,
|
||||||
onHeightChanged);
|
onHeightChanged);
|
||||||
connect(attached,
|
connect(
|
||||||
|
attached,
|
||||||
&LazyListViewAttached::visibleHeightChanged,
|
&LazyListViewAttached::visibleHeightChanged,
|
||||||
this,
|
this,
|
||||||
[this] { polish(); });
|
[this] { polish(); });
|
||||||
connect(attached,
|
connect(
|
||||||
|
attached,
|
||||||
&LazyListViewAttached::readyChanged,
|
&LazyListViewAttached::readyChanged,
|
||||||
this,
|
this,
|
||||||
[this, item = entry.item] {
|
[this, item = entry.item] {
|
||||||
auto indexIt = m_itemToIndex.find(item);
|
auto indexIt = m_itemToIndex.find(item);
|
||||||
if (indexIt == m_itemToIndex.end())
|
if (indexIt == m_itemToIndex.end()) return;
|
||||||
return;
|
|
||||||
const int idx = indexIt.value();
|
const int idx = indexIt.value();
|
||||||
if (idx >= static_cast<int>(m_layout.size()))
|
if (idx >= static_cast<int>(m_layout.size())) return;
|
||||||
return;
|
|
||||||
auto* att = qobject_cast<LazyListViewAttached*>(
|
auto* att = qobject_cast<LazyListViewAttached*>(
|
||||||
qmlAttachedPropertiesObject<LazyListView>(item, false));
|
qmlAttachedPropertiesObject<LazyListView>(item, false));
|
||||||
if (!att || !att->ready())
|
if (!att || !att->ready()) return;
|
||||||
return;
|
|
||||||
|
|
||||||
const qreal h = delegateHeight(item);
|
const qreal h = delegateHeight(item);
|
||||||
const qreal oldLayoutH = m_layout[idx].heightKnown
|
const qreal oldLayoutH = m_layout[idx].heightKnown
|
||||||
@@ -852,8 +788,7 @@ void LazyListView::destroyDelegate(DelegateEntry& entry) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LazyListView::updateDelegateData(DelegateEntry& entry) {
|
void LazyListView::updateDelegateData(DelegateEntry& entry) {
|
||||||
if (!m_model || !entry.item)
|
if (!m_model || !entry.item) return;
|
||||||
return;
|
|
||||||
|
|
||||||
const auto roleNames = m_model->roleNames();
|
const auto roleNames = m_model->roleNames();
|
||||||
const auto index = m_model->index(entry.modelIndex, 0);
|
const auto index = m_model->index(entry.modelIndex, 0);
|
||||||
@@ -861,10 +796,9 @@ void LazyListView::updateDelegateData(DelegateEntry& entry) {
|
|||||||
|
|
||||||
for (auto it = roleNames.constBegin(); it != roleNames.constEnd(); ++it) {
|
for (auto it = roleNames.constBegin(); it != roleNames.constEnd(); ++it) {
|
||||||
const auto name = QString::fromUtf8(it.value());
|
const auto name = QString::fromUtf8(it.value());
|
||||||
entry.item->setProperty(name.toUtf8().constData(),
|
entry.item->setProperty(
|
||||||
m_model->data(index, it.key()));
|
name.toUtf8().constData(), m_model->data(index, it.key()));
|
||||||
if (name == QStringLiteral("modelData"))
|
if (name == QStringLiteral("modelData")) hasModelData = true;
|
||||||
hasModelData = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.item->setProperty("index", entry.modelIndex);
|
entry.item->setProperty("index", entry.modelIndex);
|
||||||
@@ -876,38 +810,42 @@ void LazyListView::updateDelegateData(DelegateEntry& entry) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Model Connection ---
|
|
||||||
|
|
||||||
void LazyListView::connectModel() {
|
void LazyListView::connectModel() {
|
||||||
if (!m_model)
|
if (!m_model) return;
|
||||||
return;
|
|
||||||
|
|
||||||
m_modelConnections = {
|
m_modelConnections = {
|
||||||
connect(m_model,
|
connect(
|
||||||
|
m_model,
|
||||||
&QAbstractItemModel::rowsInserted,
|
&QAbstractItemModel::rowsInserted,
|
||||||
this,
|
this,
|
||||||
&LazyListView::onRowsInserted),
|
&LazyListView::onRowsInserted),
|
||||||
connect(m_model,
|
connect(
|
||||||
|
m_model,
|
||||||
&QAbstractItemModel::rowsAboutToBeRemoved,
|
&QAbstractItemModel::rowsAboutToBeRemoved,
|
||||||
this,
|
this,
|
||||||
&LazyListView::onRowsAboutToBeRemoved),
|
&LazyListView::onRowsAboutToBeRemoved),
|
||||||
connect(m_model,
|
connect(
|
||||||
|
m_model,
|
||||||
&QAbstractItemModel::rowsRemoved,
|
&QAbstractItemModel::rowsRemoved,
|
||||||
this,
|
this,
|
||||||
&LazyListView::onRowsRemoved),
|
&LazyListView::onRowsRemoved),
|
||||||
connect(m_model,
|
connect(
|
||||||
|
m_model,
|
||||||
&QAbstractItemModel::rowsMoved,
|
&QAbstractItemModel::rowsMoved,
|
||||||
this,
|
this,
|
||||||
&LazyListView::onRowsMoved),
|
&LazyListView::onRowsMoved),
|
||||||
connect(m_model,
|
connect(
|
||||||
|
m_model,
|
||||||
&QAbstractItemModel::dataChanged,
|
&QAbstractItemModel::dataChanged,
|
||||||
this,
|
this,
|
||||||
&LazyListView::onDataChanged),
|
&LazyListView::onDataChanged),
|
||||||
connect(m_model,
|
connect(
|
||||||
|
m_model,
|
||||||
&QAbstractItemModel::modelReset,
|
&QAbstractItemModel::modelReset,
|
||||||
this,
|
this,
|
||||||
&LazyListView::onModelReset),
|
&LazyListView::onModelReset),
|
||||||
connect(m_model,
|
connect(
|
||||||
|
m_model,
|
||||||
&QAbstractItemModel::layoutChanged,
|
&QAbstractItemModel::layoutChanged,
|
||||||
this,
|
this,
|
||||||
[this] {
|
[this] {
|
||||||
@@ -915,7 +853,8 @@ void LazyListView::connectModel() {
|
|||||||
updateDelegateData(entry);
|
updateDelegateData(entry);
|
||||||
polish();
|
polish();
|
||||||
}),
|
}),
|
||||||
connect(m_model,
|
connect(
|
||||||
|
m_model,
|
||||||
&QObject::destroyed,
|
&QObject::destroyed,
|
||||||
this,
|
this,
|
||||||
[this] {
|
[this] {
|
||||||
@@ -962,11 +901,9 @@ void LazyListView::resetContent() {
|
|||||||
polish();
|
polish();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LazyListView::onRowsInserted(const QModelIndex& parent,
|
void LazyListView::onRowsInserted(
|
||||||
int first,
|
const QModelIndex& parent, int first, int last) {
|
||||||
int last) {
|
if (parent.isValid()) return;
|
||||||
if (parent.isValid())
|
|
||||||
return;
|
|
||||||
|
|
||||||
const int insertCount = last - first + 1;
|
const int insertCount = last - first + 1;
|
||||||
// Insert new layout records
|
// Insert new layout records
|
||||||
@@ -990,19 +927,15 @@ void LazyListView::onRowsInserted(const QModelIndex& parent,
|
|||||||
polish();
|
polish();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LazyListView::onRowsAboutToBeRemoved(const QModelIndex& parent,
|
void LazyListView::onRowsAboutToBeRemoved(
|
||||||
int first,
|
const QModelIndex& parent, int first, int last) {
|
||||||
int last) {
|
if (parent.isValid()) return;
|
||||||
if (parent.isValid())
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (int i = first; i <= last; ++i) {
|
for (int i = first; i <= last; ++i) {
|
||||||
if (!m_delegates.contains(i))
|
if (!m_delegates.contains(i)) continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
auto entry = m_delegates.take(i);
|
auto entry = m_delegates.take(i);
|
||||||
if (entry.item)
|
if (entry.item) m_itemToIndex.remove(entry.item);
|
||||||
m_itemToIndex.remove(entry.item);
|
|
||||||
entry.pendingRemoval = true;
|
entry.pendingRemoval = true;
|
||||||
|
|
||||||
// Never made visible — skip remove animation
|
// Never made visible — skip remove animation
|
||||||
@@ -1014,8 +947,7 @@ void LazyListView::onRowsAboutToBeRemoved(const QModelIndex& parent,
|
|||||||
if (m_removeDuration > 0 && entry.item) {
|
if (m_removeDuration > 0 && entry.item) {
|
||||||
auto* attached = qobject_cast<LazyListViewAttached*>(
|
auto* attached = qobject_cast<LazyListViewAttached*>(
|
||||||
qmlAttachedPropertiesObject<LazyListView>(entry.item, false));
|
qmlAttachedPropertiesObject<LazyListView>(entry.item, false));
|
||||||
if (attached)
|
if (attached) attached->setRemoving(true);
|
||||||
attached->setRemoving(true);
|
|
||||||
|
|
||||||
// Schedule destruction after the remove animation duration
|
// Schedule destruction after the remove animation duration
|
||||||
auto* item = entry.item;
|
auto* item = entry.item;
|
||||||
@@ -1037,18 +969,15 @@ void LazyListView::onRowsAboutToBeRemoved(const QModelIndex& parent,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LazyListView::onRowsRemoved(const QModelIndex& parent,
|
void LazyListView::onRowsRemoved(
|
||||||
int first,
|
const QModelIndex& parent, int first, int last) {
|
||||||
int last) {
|
if (parent.isValid()) return;
|
||||||
if (parent.isValid())
|
|
||||||
return;
|
|
||||||
|
|
||||||
const int removeCount = last - first + 1;
|
const int removeCount = last - first + 1;
|
||||||
|
|
||||||
// Untrack known heights being removed
|
// Untrack known heights being removed
|
||||||
for (int i = first; i <= last; ++i) {
|
for (int i = first; i <= last; ++i) {
|
||||||
if (m_layout[i].heightKnown)
|
if (m_layout[i].heightKnown) untrackHeight(m_layout[i].height);
|
||||||
untrackHeight(m_layout[i].height);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove layout records
|
// Remove layout records
|
||||||
@@ -1072,13 +1001,13 @@ void LazyListView::onRowsRemoved(const QModelIndex& parent,
|
|||||||
polish();
|
polish();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LazyListView::onRowsMoved(const QModelIndex& parent,
|
void LazyListView::onRowsMoved(
|
||||||
|
const QModelIndex& parent,
|
||||||
int start,
|
int start,
|
||||||
int end,
|
int end,
|
||||||
const QModelIndex& destination,
|
const QModelIndex& destination,
|
||||||
int row) {
|
int row) {
|
||||||
if (parent.isValid() || destination.isValid())
|
if (parent.isValid() || destination.isValid()) return;
|
||||||
return;
|
|
||||||
|
|
||||||
const int count = end - start + 1;
|
const int count = end - start + 1;
|
||||||
const int dest = row > start ? row - count : row;
|
const int dest = row > start ? row - count : row;
|
||||||
@@ -1101,10 +1030,8 @@ void LazyListView::onRowsMoved(const QModelIndex& parent,
|
|||||||
if (oldIdx >= start && oldIdx <= end) {
|
if (oldIdx >= start && oldIdx <= end) {
|
||||||
newIdx = dest + (oldIdx - start);
|
newIdx = dest + (oldIdx - start);
|
||||||
} else {
|
} else {
|
||||||
if (oldIdx > end)
|
if (oldIdx > end) newIdx -= count;
|
||||||
newIdx -= count;
|
if (newIdx >= dest) newIdx += count;
|
||||||
if (newIdx >= dest)
|
|
||||||
newIdx += count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto entry = std::move(it.value());
|
auto entry = std::move(it.value());
|
||||||
@@ -1120,17 +1047,16 @@ void LazyListView::onRowsMoved(const QModelIndex& parent,
|
|||||||
polish();
|
polish();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LazyListView::onDataChanged(const QModelIndex& topLeft,
|
void LazyListView::onDataChanged(
|
||||||
|
const QModelIndex& topLeft,
|
||||||
const QModelIndex& bottomRight,
|
const QModelIndex& bottomRight,
|
||||||
const QList<int>& roles) {
|
const QList<int>& roles) {
|
||||||
Q_UNUSED(roles)
|
Q_UNUSED(roles)
|
||||||
|
|
||||||
if (topLeft.parent().isValid())
|
if (topLeft.parent().isValid()) return;
|
||||||
return;
|
|
||||||
|
|
||||||
for (int i = topLeft.row(); i <= bottomRight.row(); ++i) {
|
for (int i = topLeft.row(); i <= bottomRight.row(); ++i) {
|
||||||
if (m_delegates.contains(i))
|
if (m_delegates.contains(i)) updateDelegateData(m_delegates[i]);
|
||||||
updateDelegateData(m_delegates[i]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,12 +14,18 @@ namespace ZShell::components {
|
|||||||
class LazyListViewAttached : public QObject {
|
class LazyListViewAttached : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
Q_PROPERTY(qreal preferredHeight READ preferredHeight WRITE setPreferredHeight NOTIFY preferredHeightChanged)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(qreal visibleHeight READ visibleHeight WRITE setVisibleHeight NOTIFY visibleHeightChanged)
|
qreal preferredHeight READ preferredHeight WRITE setPreferredHeight
|
||||||
|
NOTIFY preferredHeightChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
qreal visibleHeight READ visibleHeight WRITE setVisibleHeight NOTIFY
|
||||||
|
visibleHeightChanged)
|
||||||
Q_PROPERTY(bool ready READ ready NOTIFY readyChanged)
|
Q_PROPERTY(bool ready READ ready NOTIFY readyChanged)
|
||||||
Q_PROPERTY(bool adding READ adding NOTIFY addingChanged)
|
Q_PROPERTY(bool adding READ adding NOTIFY addingChanged)
|
||||||
Q_PROPERTY(bool removing READ removing NOTIFY removingChanged)
|
Q_PROPERTY(bool removing READ removing NOTIFY removingChanged)
|
||||||
Q_PROPERTY(bool trackViewport READ trackViewport WRITE setTrackViewport NOTIFY trackViewportChanged)
|
Q_PROPERTY(
|
||||||
|
bool trackViewport READ trackViewport WRITE setTrackViewport NOTIFY
|
||||||
|
trackViewportChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit LazyListViewAttached(QObject* parent = nullptr);
|
explicit LazyListViewAttached(QObject* parent = nullptr);
|
||||||
@@ -65,29 +71,47 @@ QML_ELEMENT
|
|||||||
QML_ATTACHED(LazyListViewAttached)
|
QML_ATTACHED(LazyListViewAttached)
|
||||||
|
|
||||||
// Model & Delegate
|
// Model & Delegate
|
||||||
Q_PROPERTY(QAbstractItemModel* model READ model WRITE setModel NOTIFY modelChanged)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(QQmlComponent* delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
|
QAbstractItemModel* model READ model WRITE setModel NOTIFY modelChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
QQmlComponent* delegate READ delegate WRITE setDelegate NOTIFY
|
||||||
|
delegateChanged)
|
||||||
|
|
||||||
// Layout
|
// Layout
|
||||||
Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing NOTIFY spacingChanged)
|
Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing NOTIFY spacingChanged)
|
||||||
Q_PROPERTY(qreal contentHeight READ contentHeight NOTIFY contentHeightChanged)
|
Q_PROPERTY(
|
||||||
|
qreal contentHeight READ contentHeight NOTIFY contentHeightChanged)
|
||||||
Q_PROPERTY(qreal layoutHeight READ layoutHeight NOTIFY layoutHeightChanged)
|
Q_PROPERTY(qreal layoutHeight READ layoutHeight NOTIFY layoutHeightChanged)
|
||||||
Q_PROPERTY(qreal contentY READ contentY WRITE setContentY NOTIFY contentYChanged)
|
Q_PROPERTY(
|
||||||
|
qreal contentY READ contentY WRITE setContentY NOTIFY contentYChanged)
|
||||||
|
|
||||||
// Viewport & Lazy Loading
|
// Viewport & Lazy Loading
|
||||||
Q_PROPERTY(QRectF viewport READ viewport WRITE setViewport NOTIFY viewportChanged)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(bool useCustomViewport READ useCustomViewport WRITE setUseCustomViewport NOTIFY useCustomViewportChanged)
|
QRectF viewport READ viewport WRITE setViewport NOTIFY viewportChanged)
|
||||||
Q_PROPERTY(qreal cacheBuffer READ cacheBuffer WRITE setCacheBuffer NOTIFY cacheBufferChanged)
|
Q_PROPERTY(
|
||||||
|
bool useCustomViewport READ useCustomViewport WRITE setUseCustomViewport
|
||||||
|
NOTIFY useCustomViewportChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
qreal cacheBuffer READ cacheBuffer WRITE setCacheBuffer NOTIFY
|
||||||
|
cacheBufferChanged)
|
||||||
|
|
||||||
// Sizing
|
// Sizing
|
||||||
Q_PROPERTY(qreal estimatedHeight READ estimatedHeight WRITE setEstimatedHeight NOTIFY estimatedHeightChanged)
|
Q_PROPERTY(
|
||||||
|
qreal estimatedHeight READ estimatedHeight WRITE setEstimatedHeight
|
||||||
|
NOTIFY estimatedHeightChanged)
|
||||||
|
|
||||||
// Async
|
// Async
|
||||||
Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged)
|
Q_PROPERTY(
|
||||||
|
bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY
|
||||||
|
asynchronousChanged)
|
||||||
|
|
||||||
// Animation Durations
|
// Animation Durations
|
||||||
Q_PROPERTY(int removeDuration READ removeDuration WRITE setRemoveDuration NOTIFY removeDurationChanged)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(int readyDelay READ readyDelay WRITE setReadyDelay NOTIFY readyDelayChanged)
|
int removeDuration READ removeDuration WRITE setRemoveDuration NOTIFY
|
||||||
|
removeDurationChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
int readyDelay READ readyDelay WRITE setReadyDelay NOTIFY
|
||||||
|
readyDelayChanged)
|
||||||
|
|
||||||
// State
|
// State
|
||||||
Q_PROPERTY(int count READ count NOTIFY countChanged)
|
Q_PROPERTY(int count READ count NOTIFY countChanged)
|
||||||
@@ -161,7 +185,8 @@ void viewportAdjustNeeded(qreal delta);
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
void componentComplete() override;
|
void componentComplete() override;
|
||||||
void geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) override;
|
void geometryChange(
|
||||||
|
const QRectF& newGeometry, const QRectF& oldGeometry) override;
|
||||||
void updatePolish() override;
|
void updatePolish() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -204,8 +229,16 @@ void resetContent();
|
|||||||
void onRowsInserted(const QModelIndex& parent, int first, int last);
|
void onRowsInserted(const QModelIndex& parent, int first, int last);
|
||||||
void onRowsAboutToBeRemoved(const QModelIndex& parent, int first, int last);
|
void onRowsAboutToBeRemoved(const QModelIndex& parent, int first, int last);
|
||||||
void onRowsRemoved(const QModelIndex& parent, int first, int last);
|
void onRowsRemoved(const QModelIndex& parent, int first, int last);
|
||||||
void onRowsMoved(const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row);
|
void onRowsMoved(
|
||||||
void onDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QList<int>& roles);
|
const QModelIndex& parent,
|
||||||
|
int start,
|
||||||
|
int end,
|
||||||
|
const QModelIndex& destination,
|
||||||
|
int row);
|
||||||
|
void onDataChanged(
|
||||||
|
const QModelIndex& topLeft,
|
||||||
|
const QModelIndex& bottomRight,
|
||||||
|
const QList<int>& roles);
|
||||||
void onModelReset();
|
void onModelReset();
|
||||||
|
|
||||||
// Members
|
// Members
|
||||||
|
|||||||
@@ -194,7 +194,8 @@ void WavyLine::paintLinear(QPainter* painter) {
|
|||||||
bool first = true;
|
bool first = true;
|
||||||
|
|
||||||
for (int x = m_lineWidth / 2; x <= drawEnd; ++x) {
|
for (int x = m_lineWidth / 2; x <= drawEnd; ++x) {
|
||||||
const auto theta = m_frequency * 2 * M_PI * (x + m_startX) / len + phase;
|
const auto theta =
|
||||||
|
m_frequency * 2 * M_PI * (x + m_startX) / len + phase;
|
||||||
const auto waveY = centerY + amplitude * qSin(theta);
|
const auto waveY = centerY + amplitude * qSin(theta);
|
||||||
if (first) {
|
if (first) {
|
||||||
path.moveTo(x, waveY);
|
path.moveTo(x, waveY);
|
||||||
@@ -215,7 +216,10 @@ void WavyLine::paintArc(QPainter* painter) {
|
|||||||
const auto amplitude = m_lineWidth * m_amplitudeMultiplier;
|
const auto amplitude = m_lineWidth * m_amplitudeMultiplier;
|
||||||
const auto cx = width() / 2.0;
|
const auto cx = width() / 2.0;
|
||||||
const auto cy = height() / 2.0;
|
const auto cy = height() / 2.0;
|
||||||
const auto radius = m_radius > 0 ? m_radius : (qMin(width(), height()) - m_lineWidth - 2 * amplitude) / 2.0;
|
const auto radius =
|
||||||
|
m_radius > 0
|
||||||
|
? m_radius
|
||||||
|
: (qMin(width(), height()) - m_lineWidth - 2 * amplitude) / 2.0;
|
||||||
|
|
||||||
if (radius <= 0) {
|
if (radius <= 0) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -9,25 +9,39 @@ class WavyLine : public QQuickPaintedItem {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
|
|
||||||
Q_PROPERTY(int lineWidth READ lineWidth WRITE setLineWidth NOTIFY lineWidthChanged FINAL)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(qreal amplitudeMultiplier READ amplitudeMultiplier WRITE setAmplitudeMultiplier NOTIFY
|
int lineWidth READ lineWidth WRITE setLineWidth NOTIFY lineWidthChanged
|
||||||
amplitudeMultiplierChanged FINAL)
|
FINAL)
|
||||||
Q_PROPERTY(int frequency READ frequency WRITE setFrequency NOTIFY frequencyChanged FINAL)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(qreal startX READ startX WRITE setStartX NOTIFY startXChanged FINAL)
|
qreal amplitudeMultiplier READ amplitudeMultiplier WRITE
|
||||||
Q_PROPERTY(qreal fullLength READ fullLength WRITE setFullLength NOTIFY fullLengthChanged FINAL)
|
setAmplitudeMultiplier NOTIFY amplitudeMultiplierChanged FINAL)
|
||||||
|
Q_PROPERTY(
|
||||||
|
int frequency READ frequency WRITE setFrequency NOTIFY frequencyChanged
|
||||||
|
FINAL)
|
||||||
|
Q_PROPERTY(
|
||||||
|
qreal startX READ startX WRITE setStartX NOTIFY startXChanged FINAL)
|
||||||
|
Q_PROPERTY(
|
||||||
|
qreal fullLength READ fullLength WRITE setFullLength NOTIFY
|
||||||
|
fullLengthChanged FINAL)
|
||||||
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged FINAL)
|
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged FINAL)
|
||||||
Q_PROPERTY(qreal waveProgress READ waveProgress WRITE setWaveProgress NOTIFY waveProgressChanged FINAL)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(PathType pathType READ pathType WRITE setPathType NOTIFY pathTypeChanged FINAL)
|
qreal waveProgress READ waveProgress WRITE setWaveProgress NOTIFY
|
||||||
Q_PROPERTY(qreal startAngle READ startAngle WRITE setStartAngle NOTIFY startAngleChanged FINAL)
|
waveProgressChanged FINAL)
|
||||||
Q_PROPERTY(qreal fullAngle READ fullAngle WRITE setFullAngle NOTIFY fullAngleChanged FINAL)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged FINAL)
|
PathType pathType READ pathType WRITE setPathType NOTIFY pathTypeChanged
|
||||||
|
FINAL)
|
||||||
|
Q_PROPERTY(
|
||||||
|
qreal startAngle READ startAngle WRITE setStartAngle NOTIFY
|
||||||
|
startAngleChanged FINAL)
|
||||||
|
Q_PROPERTY(
|
||||||
|
qreal fullAngle READ fullAngle WRITE setFullAngle NOTIFY
|
||||||
|
fullAngleChanged FINAL)
|
||||||
|
Q_PROPERTY(
|
||||||
|
qreal radius READ radius WRITE setRadius NOTIFY radiusChanged FINAL)
|
||||||
Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY valueChanged FINAL)
|
Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY valueChanged FINAL)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum PathType {
|
enum PathType { Linear, Arc };
|
||||||
Linear,
|
|
||||||
Arc
|
|
||||||
};
|
|
||||||
Q_ENUM(PathType)
|
Q_ENUM(PathType)
|
||||||
|
|
||||||
explicit WavyLine(QQuickItem* parent = nullptr);
|
explicit WavyLine(QQuickItem* parent = nullptr);
|
||||||
|
|||||||
@@ -6,8 +6,7 @@
|
|||||||
|
|
||||||
namespace ZShell::internal {
|
namespace ZShell::internal {
|
||||||
|
|
||||||
ArcGauge::ArcGauge(QQuickItem* parent)
|
ArcGauge::ArcGauge(QQuickItem* parent) : QQuickPaintedItem(parent) {
|
||||||
: QQuickPaintedItem(parent) {
|
|
||||||
setAntialiasing(true);
|
setAntialiasing(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,7 +35,8 @@ void ArcGauge::paint(QPainter* painter) {
|
|||||||
|
|
||||||
// Draw value arc
|
// Draw value arc
|
||||||
if (m_percentage > 0.0) {
|
if (m_percentage > 0.0) {
|
||||||
const int valueSweep16 = qRound(static_cast<qreal>(sweepAngle16) * m_percentage);
|
const int valueSweep16 =
|
||||||
|
qRound(static_cast<qreal>(sweepAngle16) * m_percentage);
|
||||||
QPen valuePen(m_accentColor, m_lineWidth);
|
QPen valuePen(m_accentColor, m_lineWidth);
|
||||||
valuePen.setCapStyle(Qt::RoundCap);
|
valuePen.setCapStyle(Qt::RoundCap);
|
||||||
painter->setPen(valuePen);
|
painter->setPen(valuePen);
|
||||||
@@ -49,8 +49,7 @@ qreal ArcGauge::percentage() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ArcGauge::setPercentage(qreal percentage) {
|
void ArcGauge::setPercentage(qreal percentage) {
|
||||||
if (qFuzzyCompare(m_percentage, percentage))
|
if (qFuzzyCompare(m_percentage, percentage)) return;
|
||||||
return;
|
|
||||||
m_percentage = percentage;
|
m_percentage = percentage;
|
||||||
emit percentageChanged();
|
emit percentageChanged();
|
||||||
update();
|
update();
|
||||||
@@ -61,8 +60,7 @@ QColor ArcGauge::accentColor() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ArcGauge::setAccentColor(const QColor& color) {
|
void ArcGauge::setAccentColor(const QColor& color) {
|
||||||
if (m_accentColor == color)
|
if (m_accentColor == color) return;
|
||||||
return;
|
|
||||||
m_accentColor = color;
|
m_accentColor = color;
|
||||||
emit accentColorChanged();
|
emit accentColorChanged();
|
||||||
update();
|
update();
|
||||||
@@ -73,8 +71,7 @@ QColor ArcGauge::trackColor() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ArcGauge::setTrackColor(const QColor& color) {
|
void ArcGauge::setTrackColor(const QColor& color) {
|
||||||
if (m_trackColor == color)
|
if (m_trackColor == color) return;
|
||||||
return;
|
|
||||||
m_trackColor = color;
|
m_trackColor = color;
|
||||||
emit trackColorChanged();
|
emit trackColorChanged();
|
||||||
update();
|
update();
|
||||||
@@ -85,8 +82,7 @@ qreal ArcGauge::startAngle() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ArcGauge::setStartAngle(qreal angle) {
|
void ArcGauge::setStartAngle(qreal angle) {
|
||||||
if (qFuzzyCompare(m_startAngle, angle))
|
if (qFuzzyCompare(m_startAngle, angle)) return;
|
||||||
return;
|
|
||||||
m_startAngle = angle;
|
m_startAngle = angle;
|
||||||
emit startAngleChanged();
|
emit startAngleChanged();
|
||||||
update();
|
update();
|
||||||
@@ -97,8 +93,7 @@ qreal ArcGauge::sweepAngle() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ArcGauge::setSweepAngle(qreal angle) {
|
void ArcGauge::setSweepAngle(qreal angle) {
|
||||||
if (qFuzzyCompare(m_sweepAngle, angle))
|
if (qFuzzyCompare(m_sweepAngle, angle)) return;
|
||||||
return;
|
|
||||||
m_sweepAngle = angle;
|
m_sweepAngle = angle;
|
||||||
emit sweepAngleChanged();
|
emit sweepAngleChanged();
|
||||||
update();
|
update();
|
||||||
@@ -109,8 +104,7 @@ qreal ArcGauge::lineWidth() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ArcGauge::setLineWidth(qreal width) {
|
void ArcGauge::setLineWidth(qreal width) {
|
||||||
if (qFuzzyCompare(m_lineWidth, width))
|
if (qFuzzyCompare(m_lineWidth, width)) return;
|
||||||
return;
|
|
||||||
m_lineWidth = width;
|
m_lineWidth = width;
|
||||||
emit lineWidthChanged();
|
emit lineWidthChanged();
|
||||||
update();
|
update();
|
||||||
|
|||||||
@@ -11,12 +11,24 @@ class ArcGauge : public QQuickPaintedItem {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
|
|
||||||
Q_PROPERTY(qreal percentage READ percentage WRITE setPercentage NOTIFY percentageChanged)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(QColor accentColor READ accentColor WRITE setAccentColor NOTIFY accentColorChanged)
|
qreal percentage READ percentage WRITE setPercentage NOTIFY
|
||||||
Q_PROPERTY(QColor trackColor READ trackColor WRITE setTrackColor NOTIFY trackColorChanged)
|
percentageChanged)
|
||||||
Q_PROPERTY(qreal startAngle READ startAngle WRITE setStartAngle NOTIFY startAngleChanged)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(qreal sweepAngle READ sweepAngle WRITE setSweepAngle NOTIFY sweepAngleChanged)
|
QColor accentColor READ accentColor WRITE setAccentColor NOTIFY
|
||||||
Q_PROPERTY(qreal lineWidth READ lineWidth WRITE setLineWidth NOTIFY lineWidthChanged)
|
accentColorChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
QColor trackColor READ trackColor WRITE setTrackColor NOTIFY
|
||||||
|
trackColorChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
qreal startAngle READ startAngle WRITE setStartAngle NOTIFY
|
||||||
|
startAngleChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
qreal sweepAngle READ sweepAngle WRITE setSweepAngle NOTIFY
|
||||||
|
sweepAngleChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
qreal lineWidth READ lineWidth WRITE setLineWidth NOTIFY
|
||||||
|
lineWidthChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ArcGauge(QQuickItem* parent = nullptr);
|
explicit ArcGauge(QQuickItem* parent = nullptr);
|
||||||
@@ -58,4 +70,4 @@ qreal m_sweepAngle = 1.5 * M_PI;
|
|||||||
qreal m_lineWidth = 10.0;
|
qreal m_lineWidth = 10.0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ZShell::Internal
|
} // namespace ZShell::internal
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ QSize CachingImageManager::effectiveSize() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const qreal scale = effectiveScale();
|
const qreal scale = effectiveScale();
|
||||||
const QSize size = QSizeF(m_item->width() * scale, m_item->height() * scale).toSize();
|
const QSize size =
|
||||||
|
QSizeF(m_item->width() * scale, m_item->height() * scale).toSize();
|
||||||
m_item->setProperty("sourceSize", size);
|
m_item->setProperty("sourceSize", size);
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
@@ -53,7 +54,8 @@ void CachingImageManager::setItem(QQuickItem* item) {
|
|||||||
m_widthConn = connect(item, &QQuickItem::widthChanged, this, [this]() {
|
m_widthConn = connect(item, &QQuickItem::widthChanged, this, [this]() {
|
||||||
updateSource();
|
updateSource();
|
||||||
});
|
});
|
||||||
m_heightConn = connect(item, &QQuickItem::heightChanged, this, [this]() {
|
m_heightConn =
|
||||||
|
connect(item, &QQuickItem::heightChanged, this, [this]() {
|
||||||
updateSource();
|
updateSource();
|
||||||
});
|
});
|
||||||
updateSource();
|
updateSource();
|
||||||
@@ -105,11 +107,16 @@ void CachingImageManager::updateSource(const QString& path) {
|
|||||||
|
|
||||||
m_shaPath = path;
|
m_shaPath = path;
|
||||||
|
|
||||||
const auto future = QtConcurrent::run(&CachingImageManager::sha256sum, path);
|
const auto future =
|
||||||
|
QtConcurrent::run(&CachingImageManager::sha256sum, path);
|
||||||
|
|
||||||
const auto watcher = new QFutureWatcher<QString>(this);
|
const auto watcher = new QFutureWatcher<QString>(this);
|
||||||
|
|
||||||
connect(watcher, &QFutureWatcher<QString>::finished, this, [watcher, path, this]() {
|
connect(
|
||||||
|
watcher,
|
||||||
|
&QFutureWatcher<QString>::finished,
|
||||||
|
this,
|
||||||
|
[watcher, path, this]() {
|
||||||
if (m_path != path) {
|
if (m_path != path) {
|
||||||
// Object is destroyed or path has changed, ignore
|
// Object is destroyed or path has changed, ignore
|
||||||
watcher->deleteLater();
|
watcher->deleteLater();
|
||||||
@@ -140,7 +147,8 @@ void CachingImageManager::updateSource(const QString& path) {
|
|||||||
emit cachePathChanged();
|
emit cachePathChanged();
|
||||||
|
|
||||||
if (!cache.isLocalFile()) {
|
if (!cache.isLocalFile()) {
|
||||||
qWarning() << "CachingImageManager::updateSource: cachePath" << cache << "is not a local file";
|
qWarning() << "CachingImageManager::updateSource: cachePath"
|
||||||
|
<< cache << "is not a local file";
|
||||||
watcher->deleteLater();
|
watcher->deleteLater();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -169,31 +177,42 @@ QUrl CachingImageManager::cachePath() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CachingImageManager::createCache(
|
void CachingImageManager::createCache(
|
||||||
const QString& path, const QString& cache, const QString& fillMode, const QSize& size) const {
|
const QString& path,
|
||||||
|
const QString& cache,
|
||||||
|
const QString& fillMode,
|
||||||
|
const QSize& size) const {
|
||||||
QThreadPool::globalInstance()->start([path, cache, fillMode, size] {
|
QThreadPool::globalInstance()->start([path, cache, fillMode, size] {
|
||||||
QImage image(path);
|
QImage image(path);
|
||||||
|
|
||||||
if (image.isNull()) {
|
if (image.isNull()) {
|
||||||
qWarning() << "CachingImageManager::createCache: failed to read" << path;
|
qWarning() << "CachingImageManager::createCache: failed to read"
|
||||||
|
<< path;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
image.convertTo(QImage::Format_ARGB32);
|
image.convertTo(QImage::Format_ARGB32);
|
||||||
|
|
||||||
if (fillMode == "PreserveAspectCrop") {
|
if (fillMode == "PreserveAspectCrop") {
|
||||||
image = image.scaled(size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
|
image = image.scaled(
|
||||||
|
size, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
|
||||||
} else if (fillMode == "PreserveAspectFit") {
|
} else if (fillMode == "PreserveAspectFit") {
|
||||||
image = image.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
image = image.scaled(
|
||||||
|
size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
||||||
} else {
|
} else {
|
||||||
image = image.scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
image = image.scaled(
|
||||||
|
size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fillMode == "PreserveAspectCrop" || fillMode == "PreserveAspectFit") {
|
if (fillMode == "PreserveAspectCrop" ||
|
||||||
|
fillMode == "PreserveAspectFit") {
|
||||||
QImage canvas(size, QImage::Format_ARGB32);
|
QImage canvas(size, QImage::Format_ARGB32);
|
||||||
canvas.fill(Qt::transparent);
|
canvas.fill(Qt::transparent);
|
||||||
|
|
||||||
QPainter painter(&canvas);
|
QPainter painter(&canvas);
|
||||||
painter.drawImage((size.width() - image.width()) / 2, (size.height() - image.height()) / 2, image);
|
painter.drawImage(
|
||||||
|
(size.width() - image.width()) / 2,
|
||||||
|
(size.height() - image.height()) / 2,
|
||||||
|
image);
|
||||||
painter.end();
|
painter.end();
|
||||||
|
|
||||||
image = canvas;
|
image = canvas;
|
||||||
@@ -201,7 +220,8 @@ void CachingImageManager::createCache(
|
|||||||
|
|
||||||
const QString parent = QFileInfo(cache).absolutePath();
|
const QString parent = QFileInfo(cache).absolutePath();
|
||||||
if (!QDir().mkpath(parent) || !image.save(cache)) {
|
if (!QDir().mkpath(parent) || !image.save(cache)) {
|
||||||
qWarning() << "CachingImageManager::createCache: failed to save to" << cache;
|
qWarning() << "CachingImageManager::createCache: failed to save to"
|
||||||
|
<< cache;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,17 +10,18 @@ class CachingImageManager : public QObject {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
|
|
||||||
Q_PROPERTY(QQuickItem* item READ item WRITE setItem NOTIFY itemChanged REQUIRED)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(QUrl cacheDir READ cacheDir WRITE setCacheDir NOTIFY cacheDirChanged REQUIRED)
|
QQuickItem* item READ item WRITE setItem NOTIFY itemChanged REQUIRED)
|
||||||
|
Q_PROPERTY(
|
||||||
|
QUrl cacheDir READ cacheDir WRITE setCacheDir NOTIFY cacheDirChanged
|
||||||
|
REQUIRED)
|
||||||
|
|
||||||
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
|
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
|
||||||
Q_PROPERTY(QUrl cachePath READ cachePath NOTIFY cachePathChanged)
|
Q_PROPERTY(QUrl cachePath READ cachePath NOTIFY cachePathChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit CachingImageManager(QObject* parent = nullptr)
|
explicit CachingImageManager(QObject* parent = nullptr)
|
||||||
: QObject(parent)
|
: QObject(parent), m_item(nullptr) {}
|
||||||
, m_item(nullptr) {
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] QQuickItem* item() const;
|
[[nodiscard]] QQuickItem* item() const;
|
||||||
void setItem(QQuickItem* item);
|
void setItem(QQuickItem* item);
|
||||||
@@ -59,7 +60,11 @@ QMetaObject::Connection m_heightConn;
|
|||||||
[[nodiscard]] qreal effectiveScale() const;
|
[[nodiscard]] qreal effectiveScale() const;
|
||||||
[[nodiscard]] QSize effectiveSize() const;
|
[[nodiscard]] QSize effectiveSize() const;
|
||||||
|
|
||||||
void createCache(const QString& path, const QString& cache, const QString& fillMode, const QSize& size) const;
|
void createCache(
|
||||||
|
const QString& path,
|
||||||
|
const QString& cache,
|
||||||
|
const QString& fillMode,
|
||||||
|
const QSize& size) const;
|
||||||
[[nodiscard]] static QString sha256sum(const QString& path);
|
[[nodiscard]] static QString sha256sum(const QString& path);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,19 +4,15 @@
|
|||||||
|
|
||||||
namespace ZShell::internal {
|
namespace ZShell::internal {
|
||||||
|
|
||||||
CircularBuffer::CircularBuffer(QObject* parent)
|
CircularBuffer::CircularBuffer(QObject* parent) : QObject(parent) {}
|
||||||
: QObject(parent) {
|
|
||||||
}
|
|
||||||
|
|
||||||
int CircularBuffer::capacity() const {
|
int CircularBuffer::capacity() const {
|
||||||
return m_capacity;
|
return m_capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CircularBuffer::setCapacity(int capacity) {
|
void CircularBuffer::setCapacity(int capacity) {
|
||||||
if (capacity < 0)
|
if (capacity < 0) capacity = 0;
|
||||||
capacity = 0;
|
if (m_capacity == capacity) return;
|
||||||
if (m_capacity == capacity)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const auto old = values();
|
const auto old = values();
|
||||||
|
|
||||||
@@ -52,8 +48,7 @@ QList<qreal> CircularBuffer::values() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
qreal CircularBuffer::maximum() const {
|
qreal CircularBuffer::maximum() const {
|
||||||
if (m_count == 0)
|
if (m_count == 0) return 0.0;
|
||||||
return 0.0;
|
|
||||||
|
|
||||||
qreal maxVal = at(0);
|
qreal maxVal = at(0);
|
||||||
for (int i = 1; i < m_count; ++i)
|
for (int i = 1; i < m_count; ++i)
|
||||||
@@ -62,8 +57,7 @@ qreal CircularBuffer::maximum() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CircularBuffer::push(qreal value) {
|
void CircularBuffer::push(qreal value) {
|
||||||
if (m_capacity <= 0)
|
if (m_capacity <= 0) return;
|
||||||
return;
|
|
||||||
|
|
||||||
m_data[m_head] = value;
|
m_data[m_head] = value;
|
||||||
m_head = (m_head + 1) % m_capacity;
|
m_head = (m_head + 1) % m_capacity;
|
||||||
@@ -75,8 +69,7 @@ void CircularBuffer::push(qreal value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CircularBuffer::clear() {
|
void CircularBuffer::clear() {
|
||||||
if (m_count == 0)
|
if (m_count == 0) return;
|
||||||
return;
|
|
||||||
|
|
||||||
m_head = 0;
|
m_head = 0;
|
||||||
m_count = 0;
|
m_count = 0;
|
||||||
@@ -85,10 +78,10 @@ void CircularBuffer::clear() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
qreal CircularBuffer::at(int index) const {
|
qreal CircularBuffer::at(int index) const {
|
||||||
if (index < 0 || index >= m_count)
|
if (index < 0 || index >= m_count) return 0.0;
|
||||||
return 0.0;
|
|
||||||
|
|
||||||
const int actualIndex = (m_head - m_count + index + m_capacity) % m_capacity;
|
const int actualIndex =
|
||||||
|
(m_head - m_count + index + m_capacity) % m_capacity;
|
||||||
return m_data[actualIndex];
|
return m_data[actualIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ class CircularBuffer : public QObject {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
|
|
||||||
Q_PROPERTY(int capacity READ capacity WRITE setCapacity NOTIFY capacityChanged)
|
Q_PROPERTY(
|
||||||
|
int capacity READ capacity WRITE setCapacity NOTIFY capacityChanged)
|
||||||
Q_PROPERTY(int count READ count NOTIFY countChanged)
|
Q_PROPERTY(int count READ count NOTIFY countChanged)
|
||||||
Q_PROPERTY(QList<qreal> values READ values NOTIFY valuesChanged)
|
Q_PROPERTY(QList<qreal> values READ values NOTIFY valuesChanged)
|
||||||
Q_PROPERTY(qreal maximum READ maximum NOTIFY valuesChanged)
|
Q_PROPERTY(qreal maximum READ maximum NOTIFY valuesChanged)
|
||||||
|
|||||||
@@ -15,8 +15,10 @@ constexpr qint32 TAIL_DEGREES_OFFSET = -20;
|
|||||||
constexpr qint32 EXTRA_DEGREES_PER_CYCLE = 250;
|
constexpr qint32 EXTRA_DEGREES_PER_CYCLE = 250;
|
||||||
constexpr qint32 CONSTANT_ROTATION_DEGREES = 1520;
|
constexpr qint32 CONSTANT_ROTATION_DEGREES = 1520;
|
||||||
|
|
||||||
constexpr std::array<qint32, TOTAL_CYCLES> DELAY_TO_EXPAND_IN_MS = { 0, 1350, 2700, 4050 };
|
constexpr std::array<qint32, TOTAL_CYCLES> DELAY_TO_EXPAND_IN_MS =
|
||||||
constexpr std::array<qint32, TOTAL_CYCLES> DELAY_TO_COLLAPSE_IN_MS = { 667, 2017, 3367, 4717 };
|
{0, 1350, 2700, 4050};
|
||||||
|
constexpr std::array<qint32, TOTAL_CYCLES> DELAY_TO_COLLAPSE_IN_MS = {
|
||||||
|
667, 2017, 3367, 4717};
|
||||||
|
|
||||||
} // namespace advance
|
} // namespace advance
|
||||||
|
|
||||||
@@ -100,11 +102,13 @@ qreal CircularIndicatorManager::completeEndDuration() const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CircularIndicatorManager::IndeterminateAnimationType CircularIndicatorManager::indeterminateAnimationType() const {
|
CircularIndicatorManager::IndeterminateAnimationType
|
||||||
|
CircularIndicatorManager::indeterminateAnimationType() const {
|
||||||
return m_type;
|
return m_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CircularIndicatorManager::setIndeterminateAnimationType(IndeterminateAnimationType t) {
|
void CircularIndicatorManager::setIndeterminateAnimationType(
|
||||||
|
IndeterminateAnimationType t) {
|
||||||
if (m_type != t) {
|
if (m_type != t) {
|
||||||
m_type = t;
|
m_type = t;
|
||||||
emit indeterminateAnimationTypeChanged();
|
emit indeterminateAnimationTypeChanged();
|
||||||
@@ -150,24 +154,27 @@ void CircularIndicatorManager::updateRetreat(qreal progress) {
|
|||||||
// Extra rotation for the faster spinning.
|
// Extra rotation for the faster spinning.
|
||||||
qreal spinRotation = 0;
|
qreal spinRotation = 0;
|
||||||
for (const int spinDelay : DELAY_SPINS_IN_MS) {
|
for (const int spinDelay : DELAY_SPINS_IN_MS) {
|
||||||
spinRotation += m_curve.valueForProgress(getFractionInRange(playtime, spinDelay, DURATION_SPIN_IN_MS)) *
|
spinRotation +=
|
||||||
|
m_curve.valueForProgress(
|
||||||
|
getFractionInRange(playtime, spinDelay, DURATION_SPIN_IN_MS)) *
|
||||||
SPIN_ROTATION_DEGREES;
|
SPIN_ROTATION_DEGREES;
|
||||||
}
|
}
|
||||||
m_rotation = constantRotation + spinRotation;
|
m_rotation = constantRotation + spinRotation;
|
||||||
emit rotationChanged();
|
emit rotationChanged();
|
||||||
|
|
||||||
// Grow active indicator.
|
// Grow active indicator.
|
||||||
qreal fraction =
|
qreal fraction = m_curve.valueForProgress(getFractionInRange(
|
||||||
m_curve.valueForProgress(getFractionInRange(playtime, DELAY_GROW_ACTIVE_IN_MS, DURATION_GROW_ACTIVE_IN_MS));
|
playtime, DELAY_GROW_ACTIVE_IN_MS, DURATION_GROW_ACTIVE_IN_MS));
|
||||||
fraction -=
|
fraction -= m_curve.valueForProgress(getFractionInRange(
|
||||||
m_curve.valueForProgress(getFractionInRange(playtime, DELAY_SHRINK_ACTIVE_IN_MS, DURATION_SHRINK_ACTIVE_IN_MS));
|
playtime, DELAY_SHRINK_ACTIVE_IN_MS, DURATION_SHRINK_ACTIVE_IN_MS));
|
||||||
|
|
||||||
if (!qFuzzyIsNull(m_startFraction)) {
|
if (!qFuzzyIsNull(m_startFraction)) {
|
||||||
m_startFraction = 0.0;
|
m_startFraction = 0.0;
|
||||||
emit startFractionChanged();
|
emit startFractionChanged();
|
||||||
}
|
}
|
||||||
const auto oldEndFrac = m_endFraction;
|
const auto oldEndFrac = m_endFraction;
|
||||||
m_endFraction = std::lerp(END_FRACTION_RANGE[0], END_FRACTION_RANGE[1], fraction);
|
m_endFraction =
|
||||||
|
std::lerp(END_FRACTION_RANGE[0], END_FRACTION_RANGE[1], fraction);
|
||||||
|
|
||||||
// Completing animation.
|
// Completing animation.
|
||||||
if (m_completeEndProgress > 0) {
|
if (m_completeEndProgress > 0) {
|
||||||
@@ -184,22 +191,32 @@ void CircularIndicatorManager::updateAdvance(qreal progress) {
|
|||||||
const auto playtime = progress * TOTAL_DURATION_IN_MS;
|
const auto playtime = progress * TOTAL_DURATION_IN_MS;
|
||||||
|
|
||||||
// Adds constant rotation to segment positions.
|
// Adds constant rotation to segment positions.
|
||||||
m_startFraction = CONSTANT_ROTATION_DEGREES * progress + TAIL_DEGREES_OFFSET;
|
m_startFraction =
|
||||||
|
CONSTANT_ROTATION_DEGREES * progress + TAIL_DEGREES_OFFSET;
|
||||||
m_endFraction = CONSTANT_ROTATION_DEGREES * progress;
|
m_endFraction = CONSTANT_ROTATION_DEGREES * progress;
|
||||||
|
|
||||||
// Adds cycle specific rotation to segment positions.
|
// Adds cycle specific rotation to segment positions.
|
||||||
for (size_t cycleIndex = 0; cycleIndex < TOTAL_CYCLES; ++cycleIndex) {
|
for (size_t cycleIndex = 0; cycleIndex < TOTAL_CYCLES; ++cycleIndex) {
|
||||||
// While expanding.
|
// While expanding.
|
||||||
qreal fraction = getFractionInRange(playtime, DELAY_TO_EXPAND_IN_MS[cycleIndex], DURATION_TO_EXPAND_IN_MS);
|
qreal fraction = getFractionInRange(
|
||||||
m_endFraction += m_curve.valueForProgress(fraction) * EXTRA_DEGREES_PER_CYCLE;
|
playtime,
|
||||||
|
DELAY_TO_EXPAND_IN_MS[cycleIndex],
|
||||||
|
DURATION_TO_EXPAND_IN_MS);
|
||||||
|
m_endFraction +=
|
||||||
|
m_curve.valueForProgress(fraction) * EXTRA_DEGREES_PER_CYCLE;
|
||||||
|
|
||||||
// While collapsing.
|
// While collapsing.
|
||||||
fraction = getFractionInRange(playtime, DELAY_TO_COLLAPSE_IN_MS[cycleIndex], DURATION_TO_COLLAPSE_IN_MS);
|
fraction = getFractionInRange(
|
||||||
m_startFraction += m_curve.valueForProgress(fraction) * EXTRA_DEGREES_PER_CYCLE;
|
playtime,
|
||||||
|
DELAY_TO_COLLAPSE_IN_MS[cycleIndex],
|
||||||
|
DURATION_TO_COLLAPSE_IN_MS);
|
||||||
|
m_startFraction +=
|
||||||
|
m_curve.valueForProgress(fraction) * EXTRA_DEGREES_PER_CYCLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Closes the gap between head and tail for complete end.
|
// Closes the gap between head and tail for complete end.
|
||||||
m_startFraction += (m_endFraction - m_startFraction) * m_completeEndProgress;
|
m_startFraction +=
|
||||||
|
(m_endFraction - m_startFraction) * m_completeEndProgress;
|
||||||
|
|
||||||
m_startFraction /= 360.0;
|
m_startFraction /= 360.0;
|
||||||
m_endFraction /= 360.0;
|
m_endFraction /= 360.0;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <qeasingcurve.h>
|
#include <qeasingcurve.h>
|
||||||
#include <qobject.h>
|
#include <qobject.h>
|
||||||
#include <qqmlintegration.h>
|
#include <qqmlintegration.h>
|
||||||
@@ -11,24 +10,29 @@ class CircularIndicatorManager : public QObject {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
|
|
||||||
Q_PROPERTY(qreal startFraction READ startFraction NOTIFY startFractionChanged)
|
Q_PROPERTY(
|
||||||
|
qreal startFraction READ startFraction NOTIFY startFractionChanged)
|
||||||
Q_PROPERTY(qreal endFraction READ endFraction NOTIFY endFractionChanged)
|
Q_PROPERTY(qreal endFraction READ endFraction NOTIFY endFractionChanged)
|
||||||
Q_PROPERTY(qreal rotation READ rotation NOTIFY rotationChanged)
|
Q_PROPERTY(qreal rotation READ rotation NOTIFY rotationChanged)
|
||||||
Q_PROPERTY(qreal progress READ progress WRITE setProgress NOTIFY progressChanged)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(qreal completeEndProgress READ completeEndProgress WRITE setCompleteEndProgress NOTIFY
|
qreal progress READ progress WRITE setProgress NOTIFY progressChanged)
|
||||||
completeEndProgressChanged)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(qreal duration READ duration NOTIFY indeterminateAnimationTypeChanged)
|
qreal completeEndProgress READ completeEndProgress WRITE
|
||||||
Q_PROPERTY(qreal completeEndDuration READ completeEndDuration NOTIFY indeterminateAnimationTypeChanged)
|
setCompleteEndProgress NOTIFY completeEndProgressChanged)
|
||||||
Q_PROPERTY(IndeterminateAnimationType indeterminateAnimationType READ indeterminateAnimationType WRITE
|
Q_PROPERTY(
|
||||||
setIndeterminateAnimationType NOTIFY indeterminateAnimationTypeChanged)
|
qreal duration READ duration NOTIFY indeterminateAnimationTypeChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
qreal completeEndDuration READ completeEndDuration NOTIFY
|
||||||
|
indeterminateAnimationTypeChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
IndeterminateAnimationType indeterminateAnimationType READ
|
||||||
|
indeterminateAnimationType WRITE setIndeterminateAnimationType
|
||||||
|
NOTIFY indeterminateAnimationTypeChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit CircularIndicatorManager(QObject* parent = nullptr);
|
explicit CircularIndicatorManager(QObject* parent = nullptr);
|
||||||
|
|
||||||
enum IndeterminateAnimationType {
|
enum IndeterminateAnimationType { Advance = 0, Retreat };
|
||||||
Advance = 0,
|
|
||||||
Retreat
|
|
||||||
};
|
|
||||||
Q_ENUM(IndeterminateAnimationType)
|
Q_ENUM(IndeterminateAnimationType)
|
||||||
|
|
||||||
[[nodiscard]] qreal startFraction() const;
|
[[nodiscard]] qreal startFraction() const;
|
||||||
|
|||||||
@@ -5,8 +5,7 @@
|
|||||||
namespace ZShell::internal::hypr {
|
namespace ZShell::internal::hypr {
|
||||||
|
|
||||||
HyprKeyboard::HyprKeyboard(QJsonObject ipcObject, QObject* parent)
|
HyprKeyboard::HyprKeyboard(QJsonObject ipcObject, QObject* parent)
|
||||||
: QObject(parent)
|
: QObject(parent), m_lastIpcObject(ipcObject) {}
|
||||||
, m_lastIpcObject(ipcObject) {}
|
|
||||||
|
|
||||||
QVariantHash HyprKeyboard::lastIpcObject() const {
|
QVariantHash HyprKeyboard::lastIpcObject() const {
|
||||||
return m_lastIpcObject.toVariantHash();
|
return m_lastIpcObject.toVariantHash();
|
||||||
@@ -82,8 +81,7 @@ bool HyprKeyboard::updateLastIpcObject(QJsonObject object) {
|
|||||||
return dirty;
|
return dirty;
|
||||||
}
|
}
|
||||||
|
|
||||||
HyprDevices::HyprDevices(QObject* parent)
|
HyprDevices::HyprDevices(QObject* parent) : QObject(parent) {}
|
||||||
: QObject(parent) {}
|
|
||||||
|
|
||||||
QQmlListProperty<HyprKeyboard> HyprDevices::keyboards() {
|
QQmlListProperty<HyprKeyboard> HyprDevices::keyboards() {
|
||||||
return QQmlListProperty<HyprKeyboard>(this, &m_keyboards);
|
return QQmlListProperty<HyprKeyboard>(this, &m_keyboards);
|
||||||
@@ -95,8 +93,10 @@ bool HyprDevices::updateLastIpcObject(QJsonObject object) {
|
|||||||
|
|
||||||
for (auto it = m_keyboards.begin(); it != m_keyboards.end();) {
|
for (auto it = m_keyboards.begin(); it != m_keyboards.end();) {
|
||||||
auto* const keyboard = *it;
|
auto* const keyboard = *it;
|
||||||
const auto inNewValues = std::any_of(val.begin(), val.end(), [keyboard](const QJsonValue& o) {
|
const auto inNewValues =
|
||||||
return o.toObject().value("address").toString() == keyboard->address();
|
std::any_of(val.begin(), val.end(), [keyboard](const QJsonValue& o) {
|
||||||
|
return o.toObject().value("address").toString() ==
|
||||||
|
keyboard->address();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!inNewValues) {
|
if (!inNewValues) {
|
||||||
@@ -112,9 +112,10 @@ bool HyprDevices::updateLastIpcObject(QJsonObject object) {
|
|||||||
const auto obj = o.toObject();
|
const auto obj = o.toObject();
|
||||||
const auto addr = obj.value("address").toString();
|
const auto addr = obj.value("address").toString();
|
||||||
|
|
||||||
auto it = std::find_if(m_keyboards.begin(), m_keyboards.end(), [addr](const HyprKeyboard* kb) {
|
auto it = std::find_if(
|
||||||
return kb->address() == addr;
|
m_keyboards.begin(),
|
||||||
});
|
m_keyboards.end(),
|
||||||
|
[addr](const HyprKeyboard* kb) { return kb->address() == addr; });
|
||||||
|
|
||||||
if (it != m_keyboards.end()) {
|
if (it != m_keyboards.end()) {
|
||||||
dirty |= (*it)->updateLastIpcObject(obj);
|
dirty |= (*it)->updateLastIpcObject(obj);
|
||||||
|
|||||||
@@ -10,9 +10,12 @@ namespace ZShell::internal::hypr {
|
|||||||
class HyprKeyboard : public QObject {
|
class HyprKeyboard : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
QML_UNCREATABLE("HyprKeyboard instances can only be retrieved from a HyprDevices")
|
QML_UNCREATABLE(
|
||||||
|
"HyprKeyboard instances can only be retrieved from a HyprDevices")
|
||||||
|
|
||||||
Q_PROPERTY(QVariantHash lastIpcObject READ lastIpcObject NOTIFY lastIpcObjectChanged)
|
Q_PROPERTY(
|
||||||
|
QVariantHash lastIpcObject READ lastIpcObject NOTIFY
|
||||||
|
lastIpcObjectChanged)
|
||||||
Q_PROPERTY(QString address READ address NOTIFY addressChanged)
|
Q_PROPERTY(QString address READ address NOTIFY addressChanged)
|
||||||
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
|
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
|
||||||
Q_PROPERTY(QString layout READ layout NOTIFY layoutChanged)
|
Q_PROPERTY(QString layout READ layout NOTIFY layoutChanged)
|
||||||
@@ -52,10 +55,12 @@ QJsonObject m_lastIpcObject;
|
|||||||
class HyprDevices : public QObject {
|
class HyprDevices : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
QML_UNCREATABLE("HyprDevices instances can only be retrieved from a HyprExtras")
|
QML_UNCREATABLE(
|
||||||
|
"HyprDevices instances can only be retrieved from a HyprExtras")
|
||||||
|
|
||||||
Q_PROPERTY(
|
Q_PROPERTY(
|
||||||
QQmlListProperty<ZShell::internal::hypr::HyprKeyboard> keyboards READ keyboards NOTIFY keyboardsChanged)
|
QQmlListProperty<ZShell::internal::hypr::HyprKeyboard> keyboards READ
|
||||||
|
keyboards NOTIFY keyboardsChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit HyprDevices(QObject* parent = nullptr);
|
explicit HyprDevices(QObject* parent = nullptr);
|
||||||
@@ -71,4 +76,4 @@ private:
|
|||||||
QList<HyprKeyboard*> m_keyboards;
|
QList<HyprKeyboard*> m_keyboards;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ZShell::internal::hypr
|
}
|
||||||
|
|||||||
@@ -64,7 +64,8 @@ static QString luaArray(const QVariantList& list) {
|
|||||||
parts << luaValue(item);
|
parts << luaValue(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
return QLatin1String("{ ") + parts.join(QLatin1String(", ")) + QLatin1String(" }");
|
return QLatin1String("{ ") + parts.join(QLatin1String(", ")) +
|
||||||
|
QLatin1String(" }");
|
||||||
}
|
}
|
||||||
|
|
||||||
static QString luaArray(const QStringList& list) {
|
static QString luaArray(const QStringList& list) {
|
||||||
@@ -75,7 +76,8 @@ static QString luaArray(const QStringList& list) {
|
|||||||
parts << luaEscapeString(item);
|
parts << luaEscapeString(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
return QLatin1String("{ ") + parts.join(QLatin1String(", ")) + QLatin1String(" }");
|
return QLatin1String("{ ") + parts.join(QLatin1String(", ")) +
|
||||||
|
QLatin1String(" }");
|
||||||
}
|
}
|
||||||
|
|
||||||
static QString luaMapFromHash(const QVariantHash& hash) {
|
static QString luaMapFromHash(const QVariantHash& hash) {
|
||||||
@@ -83,10 +85,12 @@ static QString luaMapFromHash(const QVariantHash& hash) {
|
|||||||
parts.reserve(hash.size());
|
parts.reserve(hash.size());
|
||||||
|
|
||||||
for (auto it = hash.cbegin(); it != hash.cend(); ++it) {
|
for (auto it = hash.cbegin(); it != hash.cend(); ++it) {
|
||||||
parts << luaEscapeString(it.key()) + QLatin1String(" = ") + luaValue(it.value());
|
parts << luaEscapeString(it.key()) + QLatin1String(" = ") +
|
||||||
|
luaValue(it.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
return QLatin1String("{ ") + parts.join(QLatin1String(", ")) + QLatin1String(" }");
|
return QLatin1String("{ ") + parts.join(QLatin1String(", ")) +
|
||||||
|
QLatin1String(" }");
|
||||||
}
|
}
|
||||||
|
|
||||||
static QString luaMap(const QVariantMap& map) {
|
static QString luaMap(const QVariantMap& map) {
|
||||||
@@ -94,10 +98,12 @@ static QString luaMap(const QVariantMap& map) {
|
|||||||
parts.reserve(map.size());
|
parts.reserve(map.size());
|
||||||
|
|
||||||
for (auto it = map.cbegin(); it != map.cend(); ++it) {
|
for (auto it = map.cbegin(); it != map.cend(); ++it) {
|
||||||
parts << luaEscapeString(it.key()) + QLatin1String(" = ") + luaValue(it.value());
|
parts << luaEscapeString(it.key()) + QLatin1String(" = ") +
|
||||||
|
luaValue(it.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
return QLatin1String("{ ") + parts.join(QLatin1String(", ")) + QLatin1String(" }");
|
return QLatin1String("{ ") + parts.join(QLatin1String(", ")) +
|
||||||
|
QLatin1String(" }");
|
||||||
}
|
}
|
||||||
|
|
||||||
static QString luaValue(const QVariant& v) {
|
static QString luaValue(const QVariant& v) {
|
||||||
@@ -143,7 +149,8 @@ static QString normalizeOptionPath(QString key) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static QString buildHlConfigCall(const QString& key, const QVariant& value) {
|
static QString buildHlConfigCall(const QString& key, const QVariant& value) {
|
||||||
const auto parts = normalizeOptionPath(key).split(QLatin1Char('.'), Qt::SkipEmptyParts);
|
const auto parts =
|
||||||
|
normalizeOptionPath(key).split(QLatin1Char('.'), Qt::SkipEmptyParts);
|
||||||
if (parts.isEmpty()) {
|
if (parts.isEmpty()) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@@ -189,7 +196,8 @@ static QVariant parseGetOptionValue(const QJsonObject& obj) {
|
|||||||
|
|
||||||
const auto option = obj.value(QStringLiteral("option")).toString();
|
const auto option = obj.value(QStringLiteral("option")).toString();
|
||||||
|
|
||||||
if (option.contains(QStringLiteral("color")) || option.contains(QStringLiteral("col."))) {
|
if (option.contains(QStringLiteral("color")) ||
|
||||||
|
option.contains(QStringLiteral("col."))) {
|
||||||
return colorFromInt(static_cast<quint32>(value));
|
return colorFromInt(static_cast<quint32>(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,7 +242,8 @@ static QVariant parseGetOptionValue(const QJsonObject& obj) {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
static void insertNestedValue(QVariantMap& root, const QStringList& path, const QVariant& value) {
|
static void insertNestedValue(
|
||||||
|
QVariantMap& root, const QStringList& path, const QVariant& value) {
|
||||||
if (path.isEmpty()) {
|
if (path.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -261,16 +270,19 @@ HyprExtras::HyprExtras(QObject* parent)
|
|||||||
, m_devices(new HyprDevices(this)) {
|
, m_devices(new HyprDevices(this)) {
|
||||||
const auto his = qEnvironmentVariable("HYPRLAND_INSTANCE_SIGNATURE");
|
const auto his = qEnvironmentVariable("HYPRLAND_INSTANCE_SIGNATURE");
|
||||||
if (his.isEmpty()) {
|
if (his.isEmpty()) {
|
||||||
qCWarning(lcHypr) << "$HYPRLAND_INSTANCE_SIGNATURE is unset. Unable to connect to Hyprland socket.";
|
qCWarning(lcHypr) << "$HYPRLAND_INSTANCE_SIGNATURE is unset. Unable to "
|
||||||
|
"connect to Hyprland socket.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto hyprDir = QString("%1/hypr/%2").arg(qEnvironmentVariable("XDG_RUNTIME_DIR"), his);
|
auto hyprDir =
|
||||||
|
QString("%1/hypr/%2").arg(qEnvironmentVariable("XDG_RUNTIME_DIR"), his);
|
||||||
if (!QDir(hyprDir).exists()) {
|
if (!QDir(hyprDir).exists()) {
|
||||||
hyprDir = QStringLiteral("/tmp/hypr/") + his;
|
hyprDir = QStringLiteral("/tmp/hypr/") + his;
|
||||||
|
|
||||||
if (!QDir(hyprDir).exists()) {
|
if (!QDir(hyprDir).exists()) {
|
||||||
qCWarning(lcHypr) << "Hyprland socket directory does not exist. Unable to connect to Hyprland socket.";
|
qCWarning(lcHypr) << "Hyprland socket directory does not exist. "
|
||||||
|
"Unable to connect to Hyprland socket.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -283,9 +295,15 @@ HyprExtras::HyprExtras(QObject* parent)
|
|||||||
|
|
||||||
m_socket = new QLocalSocket(this);
|
m_socket = new QLocalSocket(this);
|
||||||
|
|
||||||
QObject::connect(m_socket, &QLocalSocket::errorOccurred, this, &HyprExtras::socketError);
|
QObject::connect(
|
||||||
QObject::connect(m_socket, &QLocalSocket::stateChanged, this, &HyprExtras::socketStateChanged);
|
m_socket, &QLocalSocket::errorOccurred, this, &HyprExtras::socketError);
|
||||||
QObject::connect(m_socket, &QLocalSocket::readyRead, this, &HyprExtras::readEvent);
|
QObject::connect(
|
||||||
|
m_socket,
|
||||||
|
&QLocalSocket::stateChanged,
|
||||||
|
this,
|
||||||
|
&HyprExtras::socketStateChanged);
|
||||||
|
QObject::connect(
|
||||||
|
m_socket, &QLocalSocket::readyRead, this, &HyprExtras::readEvent);
|
||||||
|
|
||||||
m_socket->connectToServer(m_eventSocket, QLocalSocket::ReadOnly);
|
m_socket->connectToServer(m_eventSocket, QLocalSocket::ReadOnly);
|
||||||
}
|
}
|
||||||
@@ -305,7 +323,8 @@ void HyprExtras::message(const QString& message) {
|
|||||||
|
|
||||||
makeRequest(message, [](bool success, const QByteArray& res) {
|
makeRequest(message, [](bool success, const QByteArray& res) {
|
||||||
if (!success) {
|
if (!success) {
|
||||||
qCWarning(lcHypr) << "message: request error:" << QString::fromUtf8(res);
|
qCWarning(lcHypr)
|
||||||
|
<< "message: request error:" << QString::fromUtf8(res);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -315,10 +334,12 @@ void HyprExtras::batchMessage(const QStringList& messages) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
makeRequest(QStringLiteral("[[BATCH]]") + messages.join(QLatin1Char(';')),
|
makeRequest(
|
||||||
|
QStringLiteral("[[BATCH]]") + messages.join(QLatin1Char(';')),
|
||||||
[](bool success, const QByteArray& res) {
|
[](bool success, const QByteArray& res) {
|
||||||
if (!success) {
|
if (!success) {
|
||||||
qCWarning(lcHypr) << "batchMessage: request error:" << QString::fromUtf8(res);
|
qCWarning(lcHypr)
|
||||||
|
<< "batchMessage: request error:" << QString::fromUtf8(res);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -342,11 +363,14 @@ void HyprExtras::applyOptions(const QVariantHash& options) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
makeRequest(QStringLiteral("eval ") + calls.join(QLatin1String("; ")), [this](bool success, const QByteArray& res) {
|
makeRequest(
|
||||||
|
QStringLiteral("eval ") + calls.join(QLatin1String("; ")),
|
||||||
|
[this](bool success, const QByteArray& res) {
|
||||||
if (success) {
|
if (success) {
|
||||||
refreshOptions();
|
refreshOptions();
|
||||||
} else {
|
} else {
|
||||||
qCWarning(lcHypr) << "applyOptions: request error" << QString::fromUtf8(res);
|
qCWarning(lcHypr)
|
||||||
|
<< "applyOptions: request error" << QString::fromUtf8(res);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -390,8 +414,8 @@ void HyprExtras::refreshOptions() {
|
|||||||
|
|
||||||
m_optionsRefresh = makeRequestJson(
|
m_optionsRefresh = makeRequestJson(
|
||||||
QStringLiteral("getoption ") + key,
|
QStringLiteral("getoption ") + key,
|
||||||
[this, generation, nextOptions, step, index, key](bool success, const QJsonDocument& response)
|
[this, generation, nextOptions, step, index, key](
|
||||||
{
|
bool success, const QJsonDocument& response) {
|
||||||
m_optionsRefresh.reset();
|
m_optionsRefresh.reset();
|
||||||
|
|
||||||
if (generation != m_optionsRefreshGeneration) {
|
if (generation != m_optionsRefreshGeneration) {
|
||||||
@@ -399,14 +423,21 @@ void HyprExtras::refreshOptions() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (success && response.isObject()) {
|
if (success && response.isObject()) {
|
||||||
const QVariant value = parseGetOptionValue(response.object());
|
const QVariant value =
|
||||||
|
parseGetOptionValue(response.object());
|
||||||
if (value.isValid()) {
|
if (value.isValid()) {
|
||||||
insertNestedValue(*nextOptions, key.split(QLatin1Char(':'), Qt::SkipEmptyParts), value);
|
insertNestedValue(
|
||||||
|
*nextOptions,
|
||||||
|
key.split(QLatin1Char(':'), Qt::SkipEmptyParts),
|
||||||
|
value);
|
||||||
} else {
|
} else {
|
||||||
qCWarning(lcHypr) << "refreshOptions: getoption returned no usable value for" << key;
|
qCWarning(lcHypr) << "refreshOptions: getoption "
|
||||||
|
"returned no usable value for"
|
||||||
|
<< key;
|
||||||
}
|
}
|
||||||
} else if (!success) {
|
} else if (!success) {
|
||||||
qCWarning(lcHypr) << "refreshOptions: getoption request error for" << key;
|
qCWarning(lcHypr)
|
||||||
|
<< "refreshOptions: getoption request error for" << key;
|
||||||
}
|
}
|
||||||
|
|
||||||
(*step)(index + 1);
|
(*step)(index + 1);
|
||||||
@@ -421,7 +452,9 @@ void HyprExtras::refreshDevices() {
|
|||||||
m_devicesRefresh->close();
|
m_devicesRefresh->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_devicesRefresh = makeRequestJson(QStringLiteral("devices"), [this](bool success, const QJsonDocument& response) {
|
m_devicesRefresh = makeRequestJson(
|
||||||
|
QStringLiteral("devices"),
|
||||||
|
[this](bool success, const QJsonDocument& response) {
|
||||||
m_devicesRefresh.reset();
|
m_devicesRefresh.reset();
|
||||||
if (success) {
|
if (success) {
|
||||||
m_devices->updateLastIpcObject(response.object());
|
m_devices->updateLastIpcObject(response.object());
|
||||||
@@ -431,15 +464,19 @@ void HyprExtras::refreshDevices() {
|
|||||||
|
|
||||||
void HyprExtras::socketError(QLocalSocket::LocalSocketError error) const {
|
void HyprExtras::socketError(QLocalSocket::LocalSocketError error) const {
|
||||||
if (!m_socketValid) {
|
if (!m_socketValid) {
|
||||||
qCWarning(lcHypr) << "socketError: unable to connect to Hyprland event socket:" << error;
|
qCWarning(lcHypr)
|
||||||
|
<< "socketError: unable to connect to Hyprland event socket:"
|
||||||
|
<< error;
|
||||||
} else {
|
} else {
|
||||||
qCWarning(lcHypr) << "socketError: Hyprland event socket error:" << error;
|
qCWarning(lcHypr) << "socketError: Hyprland event socket error:"
|
||||||
|
<< error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HyprExtras::socketStateChanged(QLocalSocket::LocalSocketState state) {
|
void HyprExtras::socketStateChanged(QLocalSocket::LocalSocketState state) {
|
||||||
if (state == QLocalSocket::UnconnectedState && m_socketValid) {
|
if (state == QLocalSocket::UnconnectedState && m_socketValid) {
|
||||||
qCWarning(lcHypr) << "socketStateChanged: Hyprland event socket disconnected.";
|
qCWarning(lcHypr)
|
||||||
|
<< "socketStateChanged: Hyprland event socket disconnected.";
|
||||||
}
|
}
|
||||||
|
|
||||||
m_socketValid = state == QLocalSocket::ConnectedState;
|
m_socketValid = state == QLocalSocket::ConnectedState;
|
||||||
@@ -452,7 +489,8 @@ void HyprExtras::readEvent() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
rawEvent.truncate(rawEvent.length() - 1);
|
rawEvent.truncate(rawEvent.length() - 1);
|
||||||
const auto event = QByteArrayView(rawEvent.data(), rawEvent.indexOf(">>"));
|
const auto event =
|
||||||
|
QByteArrayView(rawEvent.data(), rawEvent.indexOf(">>"));
|
||||||
handleEvent(QString::fromUtf8(event));
|
handleEvent(QString::fromUtf8(event));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -466,14 +504,18 @@ void HyprExtras::handleEvent(const QString& event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
HyprExtras::SocketPtr HyprExtras::makeRequestJson(
|
HyprExtras::SocketPtr HyprExtras::makeRequestJson(
|
||||||
const QString& request, const std::function<void(bool, QJsonDocument)>& callback) {
|
const QString& request,
|
||||||
return makeRequest(QStringLiteral("j/") + request, [callback](bool success, const QByteArray& response) {
|
const std::function<void(bool, QJsonDocument)>& callback) {
|
||||||
|
return makeRequest(
|
||||||
|
QStringLiteral("j/") + request,
|
||||||
|
[callback](bool success, const QByteArray& response) {
|
||||||
callback(success, QJsonDocument::fromJson(response));
|
callback(success, QJsonDocument::fromJson(response));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
HyprExtras::SocketPtr HyprExtras::makeRequest(
|
HyprExtras::SocketPtr HyprExtras::makeRequest(
|
||||||
const QString& request, const std::function<void(bool, QByteArray)>& callback) {
|
const QString& request,
|
||||||
|
const std::function<void(bool, QByteArray)>& callback) {
|
||||||
if (m_requestSocket.isEmpty()) {
|
if (m_requestSocket.isEmpty()) {
|
||||||
return SocketPtr();
|
return SocketPtr();
|
||||||
}
|
}
|
||||||
@@ -481,7 +523,8 @@ HyprExtras::SocketPtr HyprExtras::makeRequest(
|
|||||||
auto socket = SocketPtr::create(this);
|
auto socket = SocketPtr::create(this);
|
||||||
|
|
||||||
QObject::connect(socket.data(), &QLocalSocket::connected, this, [=, this]() {
|
QObject::connect(socket.data(), &QLocalSocket::connected, this, [=, this]() {
|
||||||
QObject::connect(socket.data(), &QLocalSocket::readyRead, this, [socket, callback]() {
|
QObject::connect(
|
||||||
|
socket.data(), &QLocalSocket::readyRead, this, [socket, callback]() {
|
||||||
const auto response = socket->readAll();
|
const auto response = socket->readAll();
|
||||||
callback(true, std::move(response));
|
callback(true, std::move(response));
|
||||||
socket->close();
|
socket->close();
|
||||||
@@ -491,8 +534,13 @@ HyprExtras::SocketPtr HyprExtras::makeRequest(
|
|||||||
socket->flush();
|
socket->flush();
|
||||||
});
|
});
|
||||||
|
|
||||||
QObject::connect(socket.data(), &QLocalSocket::errorOccurred, this, [=](QLocalSocket::LocalSocketError err) {
|
QObject::connect(
|
||||||
qCWarning(lcHypr) << "makeRequest: error making request:" << err << "| request:" << request;
|
socket.data(),
|
||||||
|
&QLocalSocket::errorOccurred,
|
||||||
|
this,
|
||||||
|
[=](QLocalSocket::LocalSocketError err) {
|
||||||
|
qCWarning(lcHypr) << "makeRequest: error making request:" << err
|
||||||
|
<< "| request:" << request;
|
||||||
callback(false, {});
|
callback(false, {});
|
||||||
socket->close();
|
socket->close();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ QML_ELEMENT
|
|||||||
Q_MOC_INCLUDE("hyprdevices.hpp")
|
Q_MOC_INCLUDE("hyprdevices.hpp")
|
||||||
|
|
||||||
Q_PROPERTY(QVariantMap options READ options NOTIFY optionsChanged)
|
Q_PROPERTY(QVariantMap options READ options NOTIFY optionsChanged)
|
||||||
Q_PROPERTY(ZShell::internal::hypr::HyprDevices* devices READ devices CONSTANT)
|
Q_PROPERTY(
|
||||||
|
ZShell::internal::hypr::HyprDevices* devices READ devices CONSTANT)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit HyprExtras(QObject* parent = nullptr);
|
explicit HyprExtras(QObject* parent = nullptr);
|
||||||
@@ -58,8 +59,12 @@ void socketStateChanged(QLocalSocket::LocalSocketState state);
|
|||||||
void readEvent();
|
void readEvent();
|
||||||
void handleEvent(const QString& event);
|
void handleEvent(const QString& event);
|
||||||
|
|
||||||
SocketPtr makeRequestJson(const QString& request, const std::function<void(bool, QJsonDocument)>& callback);
|
SocketPtr makeRequestJson(
|
||||||
SocketPtr makeRequest(const QString& request, const std::function<void(bool, QByteArray)>& callback);
|
const QString& request,
|
||||||
|
const std::function<void(bool, QJsonDocument)>& callback);
|
||||||
|
SocketPtr makeRequest(
|
||||||
|
const QString& request,
|
||||||
|
const std::function<void(bool, QByteArray)>& callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ZShell::internal::hypr
|
} // namespace ZShell::internal::hypr
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ LidWatcher::LidWatcher(QObject* parent) : QObject(parent) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ok = bus.connect("org.freedesktop.login1",
|
bool ok = bus.connect(
|
||||||
|
"org.freedesktop.login1",
|
||||||
"/org/freedesktop/login1",
|
"/org/freedesktop/login1",
|
||||||
"org.freedesktop.login1.Manager",
|
"org.freedesktop.login1.Manager",
|
||||||
"PrepareForSleep",
|
"PrepareForSleep",
|
||||||
@@ -31,7 +32,8 @@ LidWatcher::LidWatcher(QObject* parent) : QObject(parent) {
|
|||||||
<< bus.lastError().message();
|
<< bus.lastError().message();
|
||||||
}
|
}
|
||||||
|
|
||||||
QDBusInterface login1("org.freedesktop.login1",
|
QDBusInterface login1(
|
||||||
|
"org.freedesktop.login1",
|
||||||
"/org/freedesktop/login1",
|
"/org/freedesktop/login1",
|
||||||
"org.freedesktop.login1.Manager",
|
"org.freedesktop.login1.Manager",
|
||||||
bus);
|
bus);
|
||||||
@@ -42,7 +44,8 @@ LidWatcher::LidWatcher(QObject* parent) : QObject(parent) {
|
|||||||
}
|
}
|
||||||
const auto sessionPath = reply.value().path();
|
const auto sessionPath = reply.value().path();
|
||||||
|
|
||||||
ok = bus.connect("org.freedesktop.login1",
|
ok = bus.connect(
|
||||||
|
"org.freedesktop.login1",
|
||||||
sessionPath,
|
sessionPath,
|
||||||
"org.freedesktop.login1.Session",
|
"org.freedesktop.login1.Session",
|
||||||
"Lock",
|
"Lock",
|
||||||
@@ -54,7 +57,8 @@ LidWatcher::LidWatcher(QObject* parent) : QObject(parent) {
|
|||||||
<< "Failed to connect to Lock signal:" << bus.lastError().message();
|
<< "Failed to connect to Lock signal:" << bus.lastError().message();
|
||||||
}
|
}
|
||||||
|
|
||||||
ok = bus.connect("org.freedesktop.login1",
|
ok = bus.connect(
|
||||||
|
"org.freedesktop.login1",
|
||||||
sessionPath,
|
sessionPath,
|
||||||
"org.freedesktop.login1.Session",
|
"org.freedesktop.login1.Session",
|
||||||
"Unlock",
|
"Unlock",
|
||||||
|
|||||||
@@ -24,11 +24,7 @@ qreal getFractionInRange(qreal playtime, int start, int duration) {
|
|||||||
namespace ZShell::controls {
|
namespace ZShell::controls {
|
||||||
|
|
||||||
LinearIndicatorSegment::LinearIndicatorSegment(int gap, QObject* parent)
|
LinearIndicatorSegment::LinearIndicatorSegment(int gap, QObject* parent)
|
||||||
: QObject(parent)
|
: QObject(parent), m_startFraction(0), m_endFraction(0), m_gapSize(gap) {}
|
||||||
, m_startFraction(0)
|
|
||||||
, m_endFraction(0)
|
|
||||||
, m_gapSize(gap) {
|
|
||||||
}
|
|
||||||
|
|
||||||
qreal LinearIndicatorSegment::startFraction() const {
|
qreal LinearIndicatorSegment::startFraction() const {
|
||||||
return m_startFraction;
|
return m_startFraction;
|
||||||
@@ -58,7 +54,11 @@ LinearIndicatorManager::LinearIndicatorManager(QObject* parent)
|
|||||||
new LinearIndicatorSegment(m_gap, this),
|
new LinearIndicatorSegment(m_gap, this),
|
||||||
}) {
|
}) {
|
||||||
for (auto el : m_activeIndicators)
|
for (auto el : m_activeIndicators)
|
||||||
QObject::connect(this, &LinearIndicatorManager::updated, el, &LinearIndicatorSegment::updated);
|
QObject::connect(
|
||||||
|
this,
|
||||||
|
&LinearIndicatorManager::updated,
|
||||||
|
el,
|
||||||
|
&LinearIndicatorSegment::updated);
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<LinearIndicatorSegment*> LinearIndicatorManager::activeIndicators() const {
|
QList<LinearIndicatorSegment*> LinearIndicatorManager::activeIndicators() const {
|
||||||
@@ -98,12 +98,19 @@ void LinearIndicatorManager::update(qreal progress) {
|
|||||||
const auto di = i * 2;
|
const auto di = i * 2;
|
||||||
auto* const indicator = m_activeIndicators[i];
|
auto* const indicator = m_activeIndicators[i];
|
||||||
|
|
||||||
auto fraction = getFractionInRange(playtime, DELAY_TO_MOVE_SEGMENT_ENDS[di], DURATION_TO_MOVE_SEGMENT_ENDS[di]);
|
auto fraction = getFractionInRange(
|
||||||
indicator->m_startFraction = std::clamp(m_interpolators[di].valueForProgress(fraction), 0.0, 1.0);
|
playtime,
|
||||||
|
DELAY_TO_MOVE_SEGMENT_ENDS[di],
|
||||||
|
DURATION_TO_MOVE_SEGMENT_ENDS[di]);
|
||||||
|
indicator->m_startFraction =
|
||||||
|
std::clamp(m_interpolators[di].valueForProgress(fraction), 0.0, 1.0);
|
||||||
|
|
||||||
fraction =
|
fraction = getFractionInRange(
|
||||||
getFractionInRange(playtime, DELAY_TO_MOVE_SEGMENT_ENDS[di + 1], DURATION_TO_MOVE_SEGMENT_ENDS[di + 1]);
|
playtime,
|
||||||
indicator->m_endFraction = std::clamp(m_interpolators[di + 1].valueForProgress(fraction), 0.0, 1.0);
|
DELAY_TO_MOVE_SEGMENT_ENDS[di + 1],
|
||||||
|
DURATION_TO_MOVE_SEGMENT_ENDS[di + 1]);
|
||||||
|
indicator->m_endFraction = std::clamp(
|
||||||
|
m_interpolators[di + 1].valueForProgress(fraction), 0.0, 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_progress = progress;
|
m_progress = progress;
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ class LinearIndicatorManager;
|
|||||||
class LinearIndicatorSegment : public QObject {
|
class LinearIndicatorSegment : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
QML_UNCREATABLE("LinearIndicatorSegments can only be retrieved from a "
|
QML_UNCREATABLE(
|
||||||
|
"LinearIndicatorSegments can only be retrieved from a "
|
||||||
"LinearIndicatorManager.")
|
"LinearIndicatorManager.")
|
||||||
|
|
||||||
Q_PROPERTY(qreal startFraction READ startFraction NOTIFY updated FINAL)
|
Q_PROPERTY(qreal startFraction READ startFraction NOTIFY updated FINAL)
|
||||||
@@ -43,10 +44,13 @@ Q_OBJECT
|
|||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
|
|
||||||
Q_PROPERTY(
|
Q_PROPERTY(
|
||||||
QList<ZShell::controls::LinearIndicatorSegment*> activeIndicators READ activeIndicators CONSTANT FINAL)
|
QList<ZShell::controls::LinearIndicatorSegment*> activeIndicators READ
|
||||||
|
activeIndicators CONSTANT FINAL)
|
||||||
|
|
||||||
Q_PROPERTY(qreal progress READ progress WRITE update NOTIFY updated FINAL)
|
Q_PROPERTY(qreal progress READ progress WRITE update NOTIFY updated FINAL)
|
||||||
Q_PROPERTY(qreal completeEndProgress READ completeEndProgress WRITE updateCompleteEndProgress NOTIFY updated FINAL)
|
Q_PROPERTY(
|
||||||
|
qreal completeEndProgress READ completeEndProgress WRITE
|
||||||
|
updateCompleteEndProgress NOTIFY updated FINAL)
|
||||||
Q_PROPERTY(int gap READ gap WRITE setGap NOTIFY updated FINAL)
|
Q_PROPERTY(int gap READ gap WRITE setGap NOTIFY updated FINAL)
|
||||||
|
|
||||||
Q_PROPERTY(qreal duration READ duration CONSTANT FINAL)
|
Q_PROPERTY(qreal duration READ duration CONSTANT FINAL)
|
||||||
|
|||||||
@@ -7,38 +7,37 @@
|
|||||||
|
|
||||||
namespace ZShell::internal {
|
namespace ZShell::internal {
|
||||||
|
|
||||||
SparklineItem::SparklineItem(QQuickItem* parent)
|
SparklineItem::SparklineItem(QQuickItem* parent) : QQuickPaintedItem(parent) {
|
||||||
: QQuickPaintedItem(parent) {
|
|
||||||
setAntialiasing(true);
|
setAntialiasing(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SparklineItem::paint(QPainter* painter) {
|
void SparklineItem::paint(QPainter* painter) {
|
||||||
const bool has1 = m_line1 && m_line1->count() >= 2;
|
const bool has1 = m_line1 && m_line1->count() >= 2;
|
||||||
const bool has2 = m_line2 && m_line2->count() >= 2;
|
const bool has2 = m_line2 && m_line2->count() >= 2;
|
||||||
if (!has1 && !has2)
|
if (!has1 && !has2) return;
|
||||||
return;
|
|
||||||
|
|
||||||
painter->setRenderHint(QPainter::Antialiasing, true);
|
painter->setRenderHint(QPainter::Antialiasing, true);
|
||||||
|
|
||||||
// Draw line1 first (behind), then line2 (in front)
|
// Draw line1 first (behind), then line2 (in front)
|
||||||
if (has1)
|
if (has1) drawLine(painter, m_line1, m_line1Color, m_line1FillAlpha);
|
||||||
drawLine(painter, m_line1, m_line1Color, m_line1FillAlpha);
|
if (has2) drawLine(painter, m_line2, m_line2Color, m_line2FillAlpha);
|
||||||
if (has2)
|
|
||||||
drawLine(painter, m_line2, m_line2Color, m_line2FillAlpha);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SparklineItem::drawLine(QPainter* painter, CircularBuffer* buffer, const QColor& color, qreal fillAlpha) {
|
void SparklineItem::drawLine(
|
||||||
if (m_historyLength < 2)
|
QPainter* painter,
|
||||||
return;
|
CircularBuffer* buffer,
|
||||||
|
const QColor& color,
|
||||||
|
qreal fillAlpha) {
|
||||||
|
if (m_historyLength < 2) return;
|
||||||
|
|
||||||
const qreal w = width();
|
const qreal w = width();
|
||||||
const qreal h = height();
|
const qreal h = height();
|
||||||
const int len = buffer->count();
|
const int len = buffer->count();
|
||||||
if (len < 2 || m_maxValue <= 0.0)
|
if (len < 2 || m_maxValue <= 0.0) return;
|
||||||
return;
|
|
||||||
|
|
||||||
const qreal stepX = w / static_cast<qreal>(m_historyLength - 1);
|
const qreal stepX = w / static_cast<qreal>(m_historyLength - 1);
|
||||||
const qreal startX = w - (len - 1) * stepX - stepX * m_slideProgress + stepX;
|
const qreal startX =
|
||||||
|
w - (len - 1) * stepX - stepX * m_slideProgress + stepX;
|
||||||
|
|
||||||
const qreal strokePad = qCeil(m_lineWidth / 2);
|
const qreal strokePad = qCeil(m_lineWidth / 2);
|
||||||
const qreal curvePad = 3.0;
|
const qreal curvePad = 3.0;
|
||||||
@@ -93,8 +92,7 @@ void SparklineItem::drawLine(QPainter* painter, CircularBuffer* buffer, const QC
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SparklineItem::connectBuffer(CircularBuffer* buffer) {
|
void SparklineItem::connectBuffer(CircularBuffer* buffer) {
|
||||||
if (!buffer)
|
if (!buffer) return;
|
||||||
return;
|
|
||||||
|
|
||||||
connect(buffer, &CircularBuffer::valuesChanged, this, [this]() {
|
connect(buffer, &CircularBuffer::valuesChanged, this, [this]() {
|
||||||
update();
|
update();
|
||||||
@@ -117,10 +115,8 @@ CircularBuffer* SparklineItem::line1() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SparklineItem::setLine1(CircularBuffer* buffer) {
|
void SparklineItem::setLine1(CircularBuffer* buffer) {
|
||||||
if (m_line1 == buffer)
|
if (m_line1 == buffer) return;
|
||||||
return;
|
if (m_line1) disconnect(m_line1, nullptr, this, nullptr);
|
||||||
if (m_line1)
|
|
||||||
disconnect(m_line1, nullptr, this, nullptr);
|
|
||||||
m_line1 = buffer;
|
m_line1 = buffer;
|
||||||
connectBuffer(buffer);
|
connectBuffer(buffer);
|
||||||
emit line1Changed();
|
emit line1Changed();
|
||||||
@@ -132,10 +128,8 @@ CircularBuffer* SparklineItem::line2() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SparklineItem::setLine2(CircularBuffer* buffer) {
|
void SparklineItem::setLine2(CircularBuffer* buffer) {
|
||||||
if (m_line2 == buffer)
|
if (m_line2 == buffer) return;
|
||||||
return;
|
if (m_line2) disconnect(m_line2, nullptr, this, nullptr);
|
||||||
if (m_line2)
|
|
||||||
disconnect(m_line2, nullptr, this, nullptr);
|
|
||||||
m_line2 = buffer;
|
m_line2 = buffer;
|
||||||
connectBuffer(buffer);
|
connectBuffer(buffer);
|
||||||
emit line2Changed();
|
emit line2Changed();
|
||||||
@@ -147,8 +141,7 @@ QColor SparklineItem::line1Color() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SparklineItem::setLine1Color(const QColor& color) {
|
void SparklineItem::setLine1Color(const QColor& color) {
|
||||||
if (m_line1Color == color)
|
if (m_line1Color == color) return;
|
||||||
return;
|
|
||||||
m_line1Color = color;
|
m_line1Color = color;
|
||||||
emit line1ColorChanged();
|
emit line1ColorChanged();
|
||||||
update();
|
update();
|
||||||
@@ -159,8 +152,7 @@ QColor SparklineItem::line2Color() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SparklineItem::setLine2Color(const QColor& color) {
|
void SparklineItem::setLine2Color(const QColor& color) {
|
||||||
if (m_line2Color == color)
|
if (m_line2Color == color) return;
|
||||||
return;
|
|
||||||
m_line2Color = color;
|
m_line2Color = color;
|
||||||
emit line2ColorChanged();
|
emit line2ColorChanged();
|
||||||
update();
|
update();
|
||||||
@@ -171,8 +163,7 @@ qreal SparklineItem::line1FillAlpha() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SparklineItem::setLine1FillAlpha(qreal alpha) {
|
void SparklineItem::setLine1FillAlpha(qreal alpha) {
|
||||||
if (qFuzzyCompare(m_line1FillAlpha, alpha))
|
if (qFuzzyCompare(m_line1FillAlpha, alpha)) return;
|
||||||
return;
|
|
||||||
m_line1FillAlpha = alpha;
|
m_line1FillAlpha = alpha;
|
||||||
emit line1FillAlphaChanged();
|
emit line1FillAlphaChanged();
|
||||||
update();
|
update();
|
||||||
@@ -183,8 +174,7 @@ qreal SparklineItem::line2FillAlpha() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SparklineItem::setLine2FillAlpha(qreal alpha) {
|
void SparklineItem::setLine2FillAlpha(qreal alpha) {
|
||||||
if (qFuzzyCompare(m_line2FillAlpha, alpha))
|
if (qFuzzyCompare(m_line2FillAlpha, alpha)) return;
|
||||||
return;
|
|
||||||
m_line2FillAlpha = alpha;
|
m_line2FillAlpha = alpha;
|
||||||
emit line2FillAlphaChanged();
|
emit line2FillAlphaChanged();
|
||||||
update();
|
update();
|
||||||
@@ -195,8 +185,7 @@ qreal SparklineItem::maxValue() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SparklineItem::setMaxValue(qreal value) {
|
void SparklineItem::setMaxValue(qreal value) {
|
||||||
if (qFuzzyCompare(m_maxValue, value))
|
if (qFuzzyCompare(m_maxValue, value)) return;
|
||||||
return;
|
|
||||||
m_maxValue = value;
|
m_maxValue = value;
|
||||||
emit maxValueChanged();
|
emit maxValueChanged();
|
||||||
update();
|
update();
|
||||||
@@ -207,8 +196,7 @@ qreal SparklineItem::slideProgress() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SparklineItem::setSlideProgress(qreal progress) {
|
void SparklineItem::setSlideProgress(qreal progress) {
|
||||||
if (qFuzzyCompare(m_slideProgress, progress))
|
if (qFuzzyCompare(m_slideProgress, progress)) return;
|
||||||
return;
|
|
||||||
m_slideProgress = progress;
|
m_slideProgress = progress;
|
||||||
emit slideProgressChanged();
|
emit slideProgressChanged();
|
||||||
update();
|
update();
|
||||||
@@ -219,8 +207,7 @@ int SparklineItem::historyLength() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SparklineItem::setHistoryLength(int length) {
|
void SparklineItem::setHistoryLength(int length) {
|
||||||
if (m_historyLength == length)
|
if (m_historyLength == length) return;
|
||||||
return;
|
|
||||||
m_historyLength = length;
|
m_historyLength = length;
|
||||||
emit historyLengthChanged();
|
emit historyLengthChanged();
|
||||||
update();
|
update();
|
||||||
@@ -231,8 +218,7 @@ qreal SparklineItem::lineWidth() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SparklineItem::setLineWidth(qreal width) {
|
void SparklineItem::setLineWidth(qreal width) {
|
||||||
if (qFuzzyCompare(m_lineWidth, width))
|
if (qFuzzyCompare(m_lineWidth, width)) return;
|
||||||
return;
|
|
||||||
m_lineWidth = width;
|
m_lineWidth = width;
|
||||||
emit lineWidthChanged();
|
emit lineWidthChanged();
|
||||||
update();
|
update();
|
||||||
|
|||||||
@@ -13,16 +13,33 @@ class SparklineItem : public QQuickPaintedItem {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
|
|
||||||
Q_PROPERTY(CircularBuffer* line1 READ line1 WRITE setLine1 NOTIFY line1Changed)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(CircularBuffer* line2 READ line2 WRITE setLine2 NOTIFY line2Changed)
|
CircularBuffer* line1 READ line1 WRITE setLine1 NOTIFY line1Changed)
|
||||||
Q_PROPERTY(QColor line1Color READ line1Color WRITE setLine1Color NOTIFY line1ColorChanged)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(QColor line2Color READ line2Color WRITE setLine2Color NOTIFY line2ColorChanged)
|
CircularBuffer* line2 READ line2 WRITE setLine2 NOTIFY line2Changed)
|
||||||
Q_PROPERTY(qreal line1FillAlpha READ line1FillAlpha WRITE setLine1FillAlpha NOTIFY line1FillAlphaChanged)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(qreal line2FillAlpha READ line2FillAlpha WRITE setLine2FillAlpha NOTIFY line2FillAlphaChanged)
|
QColor line1Color READ line1Color WRITE setLine1Color NOTIFY
|
||||||
Q_PROPERTY(qreal maxValue READ maxValue WRITE setMaxValue NOTIFY maxValueChanged)
|
line1ColorChanged)
|
||||||
Q_PROPERTY(qreal slideProgress READ slideProgress WRITE setSlideProgress NOTIFY slideProgressChanged)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(int historyLength READ historyLength WRITE setHistoryLength NOTIFY historyLengthChanged)
|
QColor line2Color READ line2Color WRITE setLine2Color NOTIFY
|
||||||
Q_PROPERTY(qreal lineWidth READ lineWidth WRITE setLineWidth NOTIFY lineWidthChanged)
|
line2ColorChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
qreal line1FillAlpha READ line1FillAlpha WRITE setLine1FillAlpha NOTIFY
|
||||||
|
line1FillAlphaChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
qreal line2FillAlpha READ line2FillAlpha WRITE setLine2FillAlpha NOTIFY
|
||||||
|
line2FillAlphaChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
qreal maxValue READ maxValue WRITE setMaxValue NOTIFY maxValueChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
qreal slideProgress READ slideProgress WRITE setSlideProgress NOTIFY
|
||||||
|
slideProgressChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
int historyLength READ historyLength WRITE setHistoryLength NOTIFY
|
||||||
|
historyLengthChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
qreal lineWidth READ lineWidth WRITE setLineWidth NOTIFY
|
||||||
|
lineWidthChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit SparklineItem(QQuickItem* parent = nullptr);
|
explicit SparklineItem(QQuickItem* parent = nullptr);
|
||||||
@@ -72,7 +89,11 @@ void historyLengthChanged();
|
|||||||
void lineWidthChanged();
|
void lineWidthChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void drawLine(QPainter* painter, CircularBuffer* buffer, const QColor& color, qreal fillAlpha);
|
void drawLine(
|
||||||
|
QPainter* painter,
|
||||||
|
CircularBuffer* buffer,
|
||||||
|
const QColor& color,
|
||||||
|
qreal fillAlpha);
|
||||||
void connectBuffer(CircularBuffer* buffer);
|
void connectBuffer(CircularBuffer* buffer);
|
||||||
|
|
||||||
CircularBuffer* m_line1 = nullptr;
|
CircularBuffer* m_line1 = nullptr;
|
||||||
|
|||||||
@@ -9,14 +9,12 @@
|
|||||||
|
|
||||||
namespace ZShell::internal {
|
namespace ZShell::internal {
|
||||||
|
|
||||||
VisualizerBars::VisualizerBars(QQuickItem* parent)
|
VisualizerBars::VisualizerBars(QQuickItem* parent) : QQuickPaintedItem(parent) {
|
||||||
: QQuickPaintedItem(parent) {
|
|
||||||
setAntialiasing(true);
|
setAntialiasing(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VisualizerBars::advance(qreal dt) {
|
void VisualizerBars::advance(qreal dt) {
|
||||||
if (m_displayValues.isEmpty() || m_settled)
|
if (m_displayValues.isEmpty() || m_settled) return;
|
||||||
return;
|
|
||||||
|
|
||||||
// dt is in seconds (from FrameAnimation.frameTime), convert to ms
|
// dt is in seconds (from FrameAnimation.frameTime), convert to ms
|
||||||
const qreal dtMs = dt * 1000.0;
|
const qreal dtMs = dt * 1000.0;
|
||||||
@@ -45,8 +43,7 @@ void VisualizerBars::advance(qreal dt) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VisualizerBars::paint(QPainter* painter) {
|
void VisualizerBars::paint(QPainter* painter) {
|
||||||
if (m_displayValues.isEmpty())
|
if (m_displayValues.isEmpty()) return;
|
||||||
return;
|
|
||||||
|
|
||||||
painter->setRenderHint(QPainter::Antialiasing, true);
|
painter->setRenderHint(QPainter::Antialiasing, true);
|
||||||
painter->setPen(Qt::NoPen);
|
painter->setPen(Qt::NoPen);
|
||||||
@@ -68,15 +65,13 @@ void VisualizerBars::drawSide(QPainter* painter, bool rightSide) {
|
|||||||
const qreal h = height();
|
const qreal h = height();
|
||||||
const auto count = m_displayValues.size();
|
const auto count = m_displayValues.size();
|
||||||
|
|
||||||
if (count == 0)
|
if (count == 0) return;
|
||||||
return;
|
|
||||||
|
|
||||||
const qreal sideWidth = w * 0.4;
|
const qreal sideWidth = w * 0.4;
|
||||||
const qreal slotWidth = sideWidth / static_cast<qreal>(count);
|
const qreal slotWidth = sideWidth / static_cast<qreal>(count);
|
||||||
const qreal barWidth = slotWidth - m_spacing;
|
const qreal barWidth = slotWidth - m_spacing;
|
||||||
|
|
||||||
if (barWidth <= 0)
|
if (barWidth <= 0) return;
|
||||||
return;
|
|
||||||
|
|
||||||
const qreal sideOffset = rightSide ? w * 0.6 : 0;
|
const qreal sideOffset = rightSide ? w * 0.6 : 0;
|
||||||
const qreal maxBarHeight = h * 0.4;
|
const qreal maxBarHeight = h * 0.4;
|
||||||
@@ -86,8 +81,7 @@ void VisualizerBars::drawSide(QPainter* painter, bool rightSide) {
|
|||||||
const qreal value = std::clamp(m_displayValues[valueIndex], 0.0, 1.0);
|
const qreal value = std::clamp(m_displayValues[valueIndex], 0.0, 1.0);
|
||||||
const qreal barHeight = value * maxBarHeight;
|
const qreal barHeight = value * maxBarHeight;
|
||||||
|
|
||||||
if (barHeight <= 0)
|
if (barHeight <= 0) continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
const qreal x = static_cast<qreal>(i) * slotWidth + sideOffset;
|
const qreal x = static_cast<qreal>(i) * slotWidth + sideOffset;
|
||||||
const qreal y = h - barHeight;
|
const qreal y = h - barHeight;
|
||||||
@@ -141,8 +135,7 @@ QColor VisualizerBars::primaryColor() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VisualizerBars::setPrimaryColor(const QColor& color) {
|
void VisualizerBars::setPrimaryColor(const QColor& color) {
|
||||||
if (m_primaryColor == color)
|
if (m_primaryColor == color) return;
|
||||||
return;
|
|
||||||
m_primaryColor = color;
|
m_primaryColor = color;
|
||||||
emit primaryColorChanged();
|
emit primaryColorChanged();
|
||||||
update();
|
update();
|
||||||
@@ -153,8 +146,7 @@ QColor VisualizerBars::secondaryColor() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VisualizerBars::setSecondaryColor(const QColor& color) {
|
void VisualizerBars::setSecondaryColor(const QColor& color) {
|
||||||
if (m_secondaryColor == color)
|
if (m_secondaryColor == color) return;
|
||||||
return;
|
|
||||||
m_secondaryColor = color;
|
m_secondaryColor = color;
|
||||||
emit secondaryColorChanged();
|
emit secondaryColorChanged();
|
||||||
update();
|
update();
|
||||||
@@ -165,8 +157,7 @@ qreal VisualizerBars::rounding() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VisualizerBars::setRounding(qreal rounding) {
|
void VisualizerBars::setRounding(qreal rounding) {
|
||||||
if (qFuzzyCompare(m_rounding, rounding))
|
if (qFuzzyCompare(m_rounding, rounding)) return;
|
||||||
return;
|
|
||||||
m_rounding = rounding;
|
m_rounding = rounding;
|
||||||
emit roundingChanged();
|
emit roundingChanged();
|
||||||
update();
|
update();
|
||||||
@@ -177,8 +168,7 @@ qreal VisualizerBars::spacing() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VisualizerBars::setSpacing(qreal spacing) {
|
void VisualizerBars::setSpacing(qreal spacing) {
|
||||||
if (qFuzzyCompare(m_spacing, spacing))
|
if (qFuzzyCompare(m_spacing, spacing)) return;
|
||||||
return;
|
|
||||||
m_spacing = spacing;
|
m_spacing = spacing;
|
||||||
emit spacingChanged();
|
emit spacingChanged();
|
||||||
update();
|
update();
|
||||||
@@ -189,8 +179,7 @@ int VisualizerBars::animationDuration() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VisualizerBars::setAnimationDuration(int duration) {
|
void VisualizerBars::setAnimationDuration(int duration) {
|
||||||
if (m_animationDuration == duration)
|
if (m_animationDuration == duration) return;
|
||||||
return;
|
|
||||||
m_animationDuration = duration;
|
m_animationDuration = duration;
|
||||||
emit animationDurationChanged();
|
emit animationDurationChanged();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,12 +12,20 @@ class VisualizerBars : public QQuickPaintedItem {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
|
|
||||||
Q_PROPERTY(QVector<double> values READ values WRITE setValues NOTIFY valuesChanged)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(QColor primaryColor READ primaryColor WRITE setPrimaryColor NOTIFY primaryColorChanged)
|
QVector<double> values READ values WRITE setValues NOTIFY valuesChanged)
|
||||||
Q_PROPERTY(QColor secondaryColor READ secondaryColor WRITE setSecondaryColor NOTIFY secondaryColorChanged)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(qreal rounding READ rounding WRITE setRounding NOTIFY roundingChanged)
|
QColor primaryColor READ primaryColor WRITE setPrimaryColor NOTIFY
|
||||||
|
primaryColorChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
QColor secondaryColor READ secondaryColor WRITE setSecondaryColor NOTIFY
|
||||||
|
secondaryColorChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
qreal rounding READ rounding WRITE setRounding NOTIFY roundingChanged)
|
||||||
Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing NOTIFY spacingChanged)
|
Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing NOTIFY spacingChanged)
|
||||||
Q_PROPERTY(int animationDuration READ animationDuration WRITE setAnimationDuration NOTIFY animationDurationChanged)
|
Q_PROPERTY(
|
||||||
|
int animationDuration READ animationDuration WRITE setAnimationDuration
|
||||||
|
NOTIFY animationDurationChanged)
|
||||||
Q_PROPERTY(bool settled READ settled NOTIFY settledChanged)
|
Q_PROPERTY(bool settled READ settled NOTIFY settledChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -7,15 +7,16 @@
|
|||||||
#include <QtConcurrent>
|
#include <QtConcurrent>
|
||||||
#include <QSGImageNode>
|
#include <QSGImageNode>
|
||||||
#include <QQuickWindow>
|
#include <QQuickWindow>
|
||||||
#include <set>
|
|
||||||
|
|
||||||
namespace ZShell::internal {
|
namespace ZShell::internal {
|
||||||
|
|
||||||
WallpaperImage::WallpaperImage(QQuickItem *parent)
|
WallpaperImage::WallpaperImage(QQuickItem* parent) : QQuickItem(parent) {
|
||||||
: QQuickItem(parent)
|
|
||||||
{
|
|
||||||
setFlag(ItemHasContents, true);
|
setFlag(ItemHasContents, true);
|
||||||
connect(&m_imageWatcher, &QFutureWatcher<QImage>::finished, this, &WallpaperImage::handleImageLoaded);
|
connect(
|
||||||
|
&m_imageWatcher,
|
||||||
|
&QFutureWatcher<QImage>::finished,
|
||||||
|
this,
|
||||||
|
&WallpaperImage::handleImageLoaded);
|
||||||
}
|
}
|
||||||
|
|
||||||
WallpaperImage::~WallpaperImage() {
|
WallpaperImage::~WallpaperImage() {
|
||||||
@@ -23,8 +24,7 @@ WallpaperImage::~WallpaperImage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WallpaperImage::setStatus(const Status s) {
|
void WallpaperImage::setStatus(const Status s) {
|
||||||
if (m_status == s)
|
if (m_status == s) return;
|
||||||
return;
|
|
||||||
|
|
||||||
m_status = s;
|
m_status = s;
|
||||||
emit statusChanged();
|
emit statusChanged();
|
||||||
@@ -86,11 +86,16 @@ void WallpaperImage::setCropHeight(qreal h) {
|
|||||||
QString WallpaperImage::getCacheFilePath() const {
|
QString WallpaperImage::getCacheFilePath() const {
|
||||||
if (m_source.isEmpty() || m_screenResolution.isEmpty()) return QString();
|
if (m_source.isEmpty() || m_screenResolution.isEmpty()) return QString();
|
||||||
|
|
||||||
QString cachePath = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + "/zshell/imagecache";
|
QString cachePath =
|
||||||
|
QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) +
|
||||||
|
"/zshell/imagecache";
|
||||||
QDir().mkpath(cachePath);
|
QDir().mkpath(cachePath);
|
||||||
|
|
||||||
QString id = m_source.toString() + "_" + QString::number(m_screenResolution.width()) + "x" + QString::number(m_screenResolution.height());
|
QString id = m_source.toString() + "_" +
|
||||||
QByteArray hash = QCryptographicHash::hash(id.toUtf8(), QCryptographicHash::Md5).toHex();
|
QString::number(m_screenResolution.width()) + "x" +
|
||||||
|
QString::number(m_screenResolution.height());
|
||||||
|
QByteArray hash =
|
||||||
|
QCryptographicHash::hash(id.toUtf8(), QCryptographicHash::Md5).toHex();
|
||||||
|
|
||||||
return cachePath + "/" + hash + ".png";
|
return cachePath + "/" + hash + ".png";
|
||||||
}
|
}
|
||||||
@@ -102,7 +107,8 @@ void WallpaperImage::loadImage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
QString cacheFile = getCacheFilePath();
|
QString cacheFile = getCacheFilePath();
|
||||||
QString sourceFile = m_source.isLocalFile() ? m_source.toLocalFile() : m_source.toString();
|
QString sourceFile = m_source.isLocalFile() ? m_source.toLocalFile()
|
||||||
|
: m_source.toString();
|
||||||
|
|
||||||
if (sourceFile.startsWith("qrc:/")) {
|
if (sourceFile.startsWith("qrc:/")) {
|
||||||
sourceFile = sourceFile.mid(3);
|
sourceFile = sourceFile.mid(3);
|
||||||
@@ -110,8 +116,10 @@ void WallpaperImage::loadImage() {
|
|||||||
|
|
||||||
QSize targetRes = m_screenResolution;
|
QSize targetRes = m_screenResolution;
|
||||||
|
|
||||||
QFuture<QImage> future = QtConcurrent::run([sourceFile, cacheFile, targetRes]() -> QImage {
|
QFuture<QImage> future =
|
||||||
if (!targetRes.isEmpty() && !cacheFile.isEmpty() && QFileInfo::exists(cacheFile)) {
|
QtConcurrent::run([sourceFile, cacheFile, targetRes]() -> QImage {
|
||||||
|
if (!targetRes.isEmpty() && !cacheFile.isEmpty() &&
|
||||||
|
QFileInfo::exists(cacheFile)) {
|
||||||
QImage cached(cacheFile);
|
QImage cached(cacheFile);
|
||||||
if (!cached.isNull()) return cached;
|
if (!cached.isNull()) return cached;
|
||||||
}
|
}
|
||||||
@@ -123,8 +131,12 @@ void WallpaperImage::loadImage() {
|
|||||||
return original;
|
return original;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (original.width() > targetRes.width() || original.height() > targetRes.height()) {
|
if (original.width() > targetRes.width() ||
|
||||||
QImage scaled = original.scaled(targetRes, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
|
original.height() > targetRes.height()) {
|
||||||
|
QImage scaled = original.scaled(
|
||||||
|
targetRes,
|
||||||
|
Qt::KeepAspectRatioByExpanding,
|
||||||
|
Qt::SmoothTransformation);
|
||||||
if (!cacheFile.isEmpty()) scaled.save(cacheFile, "PNG");
|
if (!cacheFile.isEmpty()) scaled.save(cacheFile, "PNG");
|
||||||
return scaled;
|
return scaled;
|
||||||
}
|
}
|
||||||
@@ -145,7 +157,8 @@ void WallpaperImage::handleImageLoaded() {
|
|||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
QSGNode *WallpaperImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) {
|
QSGNode* WallpaperImage::updatePaintNode(
|
||||||
|
QSGNode* oldNode, UpdatePaintNodeData*) {
|
||||||
auto* node = static_cast<QSGImageNode*>(oldNode);
|
auto* node = static_cast<QSGImageNode*>(oldNode);
|
||||||
|
|
||||||
if (m_image.isNull()) {
|
if (m_image.isNull()) {
|
||||||
@@ -159,7 +172,8 @@ QSGNode *WallpaperImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
|
|||||||
|
|
||||||
if (m_textureDirty) {
|
if (m_textureDirty) {
|
||||||
if (m_texture) delete m_texture;
|
if (m_texture) delete m_texture;
|
||||||
m_texture = window()->createTextureFromImage(m_image, QQuickWindow::TextureHasAlphaChannel);
|
m_texture = window()->createTextureFromImage(
|
||||||
|
m_image, QQuickWindow::TextureHasAlphaChannel);
|
||||||
m_textureDirty = false;
|
m_textureDirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,8 +189,7 @@ QSGNode *WallpaperImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
|
|||||||
m_cropX * m_texture->textureSize().width(),
|
m_cropX * m_texture->textureSize().width(),
|
||||||
m_cropY * m_texture->textureSize().height(),
|
m_cropY * m_texture->textureSize().height(),
|
||||||
cW * m_texture->textureSize().width(),
|
cW * m_texture->textureSize().width(),
|
||||||
cH * m_texture->textureSize().height()
|
cH * m_texture->textureSize().height());
|
||||||
);
|
|
||||||
|
|
||||||
QRectF bounds = boundingRect();
|
QRectF bounds = boundingRect();
|
||||||
if (bounds.isEmpty() || reqRect.isEmpty()) return node;
|
if (bounds.isEmpty() || reqRect.isEmpty()) return node;
|
||||||
@@ -202,8 +215,7 @@ QSGNode *WallpaperImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
|
|||||||
sourceRect.x() / m_texture->textureSize().width(),
|
sourceRect.x() / m_texture->textureSize().width(),
|
||||||
sourceRect.y() / m_texture->textureSize().height(),
|
sourceRect.y() / m_texture->textureSize().height(),
|
||||||
sourceRect.width() / m_texture->textureSize().width(),
|
sourceRect.width() / m_texture->textureSize().width(),
|
||||||
sourceRect.height() / m_texture->textureSize().height()
|
sourceRect.height() / m_texture->textureSize().height());
|
||||||
);
|
|
||||||
|
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
|
|
||||||
@@ -219,8 +231,7 @@ QSGNode *WallpaperImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *
|
|||||||
updateIfChanged(m_actualCropWidth, normalizedActual.width());
|
updateIfChanged(m_actualCropWidth, normalizedActual.width());
|
||||||
updateIfChanged(m_actualCropHeight, normalizedActual.height());
|
updateIfChanged(m_actualCropHeight, normalizedActual.height());
|
||||||
|
|
||||||
if (changed)
|
if (changed) emit actualCropChanged();
|
||||||
emit actualCropChanged();
|
|
||||||
|
|
||||||
node->setSourceRect(sourceRect);
|
node->setSourceRect(sourceRect);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,89 +14,69 @@ class WallpaperImage : public QQuickItem {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_NAMED_ELEMENT(WallpaperImage)
|
QML_NAMED_ELEMENT(WallpaperImage)
|
||||||
Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
|
Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
|
||||||
Q_PROPERTY(QSize screenResolution READ screenResolution WRITE setScreenResolution NOTIFY screenResolutionChanged)
|
Q_PROPERTY(
|
||||||
|
QSize screenResolution READ screenResolution WRITE setScreenResolution
|
||||||
|
NOTIFY screenResolutionChanged)
|
||||||
Q_PROPERTY(qreal zoom READ zoom WRITE setZoom NOTIFY zoomChanged)
|
Q_PROPERTY(qreal zoom READ zoom WRITE setZoom NOTIFY zoomChanged)
|
||||||
Q_PROPERTY(Status status READ status NOTIFY statusChanged)
|
Q_PROPERTY(Status status READ status NOTIFY statusChanged)
|
||||||
|
|
||||||
Q_PROPERTY(qreal actualCropX READ actualCropX NOTIFY actualCropChanged)
|
Q_PROPERTY(qreal actualCropX READ actualCropX NOTIFY actualCropChanged)
|
||||||
Q_PROPERTY(qreal actualCropY READ actualCropY NOTIFY actualCropChanged)
|
Q_PROPERTY(qreal actualCropY READ actualCropY NOTIFY actualCropChanged)
|
||||||
Q_PROPERTY(qreal actualCropWidth READ actualCropWidth NOTIFY actualCropChanged)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(qreal actualCropHeight READ actualCropHeight NOTIFY actualCropChanged)
|
qreal actualCropWidth READ actualCropWidth NOTIFY actualCropChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
qreal actualCropHeight READ actualCropHeight NOTIFY actualCropChanged)
|
||||||
|
|
||||||
Q_PROPERTY(qreal cropX READ cropX WRITE setCropX NOTIFY cropXChanged)
|
Q_PROPERTY(qreal cropX READ cropX WRITE setCropX NOTIFY cropXChanged)
|
||||||
Q_PROPERTY(qreal cropY READ cropY WRITE setCropY NOTIFY cropYChanged)
|
Q_PROPERTY(qreal cropY READ cropY WRITE setCropY NOTIFY cropYChanged)
|
||||||
Q_PROPERTY(qreal cropWidth READ cropWidth WRITE setCropWidth NOTIFY cropWidthChanged)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(qreal cropHeight READ cropHeight WRITE setCropHeight NOTIFY cropHeightChanged)
|
qreal cropWidth READ cropWidth WRITE setCropWidth NOTIFY
|
||||||
|
cropWidthChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
qreal cropHeight READ cropHeight WRITE setCropHeight NOTIFY
|
||||||
|
cropHeightChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit WallpaperImage(QQuickItem* parent = nullptr);
|
explicit WallpaperImage(QQuickItem* parent = nullptr);
|
||||||
~WallpaperImage() override;
|
~WallpaperImage() override;
|
||||||
|
|
||||||
enum Status {
|
enum Status { Null, Ready, Loading, Error };
|
||||||
Null,
|
|
||||||
Ready,
|
|
||||||
Loading,
|
|
||||||
Error
|
|
||||||
};
|
|
||||||
Q_ENUM(Status)
|
Q_ENUM(Status)
|
||||||
|
|
||||||
Status status() const {
|
Status status() const { return m_status; }
|
||||||
return m_status;
|
|
||||||
}
|
|
||||||
|
|
||||||
QUrl source() const {
|
QUrl source() const { return m_source; }
|
||||||
return m_source;
|
|
||||||
}
|
|
||||||
void setSource(const QUrl& source);
|
void setSource(const QUrl& source);
|
||||||
|
|
||||||
QSize screenResolution() const {
|
QSize screenResolution() const { return m_screenResolution; }
|
||||||
return m_screenResolution;
|
|
||||||
}
|
|
||||||
void setScreenResolution(const QSize& screenResolution);
|
void setScreenResolution(const QSize& screenResolution);
|
||||||
|
|
||||||
qreal zoom() const {
|
qreal zoom() const { return m_zoom; }
|
||||||
return m_zoom;
|
|
||||||
}
|
|
||||||
void setZoom(qreal zoom);
|
void setZoom(qreal zoom);
|
||||||
|
|
||||||
qreal actualCropX() const {
|
qreal actualCropX() const { return m_actualCropX; }
|
||||||
return m_actualCropX;
|
|
||||||
}
|
|
||||||
|
|
||||||
qreal actualCropY() const {
|
qreal actualCropY() const { return m_actualCropY; }
|
||||||
return m_actualCropY;
|
|
||||||
}
|
|
||||||
|
|
||||||
qreal actualCropWidth() const {
|
qreal actualCropWidth() const { return m_actualCropWidth; }
|
||||||
return m_actualCropWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
qreal actualCropHeight() const {
|
qreal actualCropHeight() const { return m_actualCropHeight; }
|
||||||
return m_actualCropHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
qreal cropX() const {
|
qreal cropX() const { return m_cropX; }
|
||||||
return m_cropX;
|
|
||||||
}
|
|
||||||
void setCropX(qreal x);
|
void setCropX(qreal x);
|
||||||
|
|
||||||
qreal cropY() const {
|
qreal cropY() const { return m_cropY; }
|
||||||
return m_cropY;
|
|
||||||
}
|
|
||||||
void setCropY(qreal y);
|
void setCropY(qreal y);
|
||||||
|
|
||||||
qreal cropWidth() const {
|
qreal cropWidth() const { return m_cropWidth; }
|
||||||
return m_cropWidth;
|
|
||||||
}
|
|
||||||
void setCropWidth(qreal w);
|
void setCropWidth(qreal w);
|
||||||
|
|
||||||
qreal cropHeight() const {
|
qreal cropHeight() const { return m_cropHeight; }
|
||||||
return m_cropHeight;
|
|
||||||
}
|
|
||||||
void setCropHeight(qreal h);
|
void setCropHeight(qreal h);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData) override;
|
QSGNode* updatePaintNode(
|
||||||
|
QSGNode* oldNode, UpdatePaintNodeData* updatePaintNodeData) override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void sourceChanged();
|
void sourceChanged();
|
||||||
|
|||||||
@@ -6,7 +6,8 @@
|
|||||||
|
|
||||||
namespace ZShell::models {
|
namespace ZShell::models {
|
||||||
|
|
||||||
FileSystemEntry::FileSystemEntry(const QString& path, const QString& relativePath, QObject* parent)
|
FileSystemEntry::FileSystemEntry(
|
||||||
|
const QString& path, const QString& relativePath, QObject* parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, m_fileInfo(path)
|
, m_fileInfo(path)
|
||||||
, m_path(path)
|
, m_path(path)
|
||||||
@@ -78,8 +79,16 @@ FileSystemModel::FileSystemModel(QObject* parent)
|
|||||||
, m_watchChanges(true)
|
, m_watchChanges(true)
|
||||||
, m_showHidden(false)
|
, m_showHidden(false)
|
||||||
, m_filter(NoFilter) {
|
, m_filter(NoFilter) {
|
||||||
connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &FileSystemModel::watchDirIfRecursive);
|
connect(
|
||||||
connect(&m_watcher, &QFileSystemWatcher::directoryChanged, this, &FileSystemModel::updateEntriesForDir);
|
&m_watcher,
|
||||||
|
&QFileSystemWatcher::directoryChanged,
|
||||||
|
this,
|
||||||
|
&FileSystemModel::watchDirIfRecursive);
|
||||||
|
connect(
|
||||||
|
&m_watcher,
|
||||||
|
&QFileSystemWatcher::directoryChanged,
|
||||||
|
this,
|
||||||
|
&FileSystemModel::updateEntriesForDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
int FileSystemModel::rowCount(const QModelIndex& parent) const {
|
int FileSystemModel::rowCount(const QModelIndex& parent) const {
|
||||||
@@ -90,7 +99,8 @@ int FileSystemModel::rowCount(const QModelIndex& parent) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
QVariant FileSystemModel::data(const QModelIndex& index, int role) const {
|
QVariant FileSystemModel::data(const QModelIndex& index, int role) const {
|
||||||
if (role != Qt::UserRole || !index.isValid() || index.row() >= m_entries.size()) {
|
if (role != Qt::UserRole || !index.isValid() ||
|
||||||
|
index.row() >= m_entries.size()) {
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
return QVariant::fromValue(m_entries.at(index.row()));
|
return QVariant::fromValue(m_entries.at(index.row()));
|
||||||
@@ -233,9 +243,14 @@ void FileSystemModel::watchDirIfRecursive(const QString& path) {
|
|||||||
return dirs;
|
return dirs;
|
||||||
});
|
});
|
||||||
const auto watcher = new QFutureWatcher<QStringList>(this);
|
const auto watcher = new QFutureWatcher<QStringList>(this);
|
||||||
connect(watcher, &QFutureWatcher<QStringList>::finished, this, [currentDir, showHidden, watcher, this]() {
|
connect(
|
||||||
|
watcher,
|
||||||
|
&QFutureWatcher<QStringList>::finished,
|
||||||
|
this,
|
||||||
|
[currentDir, showHidden, watcher, this]() {
|
||||||
const auto paths = watcher->result();
|
const auto paths = watcher->result();
|
||||||
if (currentDir == m_dir && showHidden == m_showHidden && !paths.isEmpty()) {
|
if (currentDir == m_dir && showHidden == m_showHidden &&
|
||||||
|
!paths.isEmpty()) {
|
||||||
// Ignore if dir or showHidden has changed
|
// Ignore if dir or showHidden has changed
|
||||||
m_watcher.addPaths(paths);
|
m_watcher.addPaths(paths);
|
||||||
}
|
}
|
||||||
@@ -295,8 +310,10 @@ void FileSystemModel::updateEntriesForDir(const QString& dir) {
|
|||||||
oldPaths << entry->path();
|
oldPaths << entry->path();
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto future = QtConcurrent::run([=](QPromise<QPair<QSet<QString>, QSet<QString>>>& promise) {
|
const auto future = QtConcurrent::run(
|
||||||
const auto flags = recursive ? QDirIterator::Subdirectories : QDirIterator::NoIteratorFlags;
|
[=](QPromise<QPair<QSet<QString>, QSet<QString>>>& promise) {
|
||||||
|
const auto flags = recursive ? QDirIterator::Subdirectories
|
||||||
|
: QDirIterator::NoIteratorFlags;
|
||||||
|
|
||||||
std::optional<QDirIterator> iter;
|
std::optional<QDirIterator> iter;
|
||||||
|
|
||||||
@@ -357,7 +374,8 @@ void FileSystemModel::updateEntriesForDir(const QString& dir) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
promise.addResult(qMakePair(oldPaths - newPaths, newPaths - oldPaths));
|
promise.addResult(
|
||||||
|
qMakePair(oldPaths - newPaths, newPaths - oldPaths));
|
||||||
});
|
});
|
||||||
|
|
||||||
if (m_futures.contains(dir)) {
|
if (m_futures.contains(dir)) {
|
||||||
@@ -365,9 +383,14 @@ void FileSystemModel::updateEntriesForDir(const QString& dir) {
|
|||||||
}
|
}
|
||||||
m_futures.insert(dir, future);
|
m_futures.insert(dir, future);
|
||||||
|
|
||||||
const auto watcher = new QFutureWatcher<QPair<QSet<QString>, QSet<QString>>>(this);
|
const auto watcher =
|
||||||
|
new QFutureWatcher<QPair<QSet<QString>, QSet<QString>>>(this);
|
||||||
|
|
||||||
connect(watcher, &QFutureWatcher<QPair<QSet<QString>, QSet<QString>>>::finished, this, [dir, watcher, this]() {
|
connect(
|
||||||
|
watcher,
|
||||||
|
&QFutureWatcher<QPair<QSet<QString>, QSet<QString>>>::finished,
|
||||||
|
this,
|
||||||
|
[dir, watcher, this]() {
|
||||||
m_futures.remove(dir);
|
m_futures.remove(dir);
|
||||||
|
|
||||||
if (!watcher->future().isResultReadyAt(0)) {
|
if (!watcher->future().isResultReadyAt(0)) {
|
||||||
@@ -384,7 +407,8 @@ void FileSystemModel::updateEntriesForDir(const QString& dir) {
|
|||||||
watcher->setFuture(future);
|
watcher->setFuture(future);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileSystemModel::applyChanges(const QSet<QString>& removedPaths, const QSet<QString>& addedPaths) {
|
void FileSystemModel::applyChanges(
|
||||||
|
const QSet<QString>& removedPaths, const QSet<QString>& addedPaths) {
|
||||||
QList<int> removedIndices;
|
QList<int> removedIndices;
|
||||||
for (int i = 0; i < m_entries.size(); ++i) {
|
for (int i = 0; i < m_entries.size(); ++i) {
|
||||||
if (removedPaths.contains(m_entries[i]->path())) {
|
if (removedPaths.contains(m_entries[i]->path())) {
|
||||||
@@ -424,9 +448,13 @@ void FileSystemModel::applyChanges(const QSet<QString>& removedPaths, const QSet
|
|||||||
// Create new entries
|
// Create new entries
|
||||||
QList<FileSystemEntry*> newEntries;
|
QList<FileSystemEntry*> newEntries;
|
||||||
for (const auto& path : addedPaths) {
|
for (const auto& path : addedPaths) {
|
||||||
newEntries << new FileSystemEntry(path, m_dir.relativeFilePath(path), this);
|
newEntries << new FileSystemEntry(
|
||||||
|
path, m_dir.relativeFilePath(path), this);
|
||||||
}
|
}
|
||||||
std::sort(newEntries.begin(), newEntries.end(), [this](const FileSystemEntry* a, const FileSystemEntry* b) {
|
std::sort(
|
||||||
|
newEntries.begin(),
|
||||||
|
newEntries.end(),
|
||||||
|
[this](const FileSystemEntry* a, const FileSystemEntry* b) {
|
||||||
return compareEntries(a, b);
|
return compareEntries(a, b);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -435,7 +463,10 @@ void FileSystemModel::applyChanges(const QSet<QString>& removedPaths, const QSet
|
|||||||
QList<FileSystemEntry*> batchItems;
|
QList<FileSystemEntry*> batchItems;
|
||||||
for (const auto& entry : std::as_const(newEntries)) {
|
for (const auto& entry : std::as_const(newEntries)) {
|
||||||
const auto it = std::lower_bound(
|
const auto it = std::lower_bound(
|
||||||
m_entries.begin(), m_entries.end(), entry, [this](const FileSystemEntry* a, const FileSystemEntry* b) {
|
m_entries.begin(),
|
||||||
|
m_entries.end(),
|
||||||
|
entry,
|
||||||
|
[this](const FileSystemEntry* a, const FileSystemEntry* b) {
|
||||||
return compareEntries(a, b);
|
return compareEntries(a, b);
|
||||||
});
|
});
|
||||||
const auto row = static_cast<int>(it - m_entries.begin());
|
const auto row = static_cast<int>(it - m_entries.begin());
|
||||||
@@ -446,7 +477,10 @@ void FileSystemModel::applyChanges(const QSet<QString>& removedPaths, const QSet
|
|||||||
} else if (row == insertStart + batchItems.size()) {
|
} else if (row == insertStart + batchItems.size()) {
|
||||||
batchItems << entry;
|
batchItems << entry;
|
||||||
} else {
|
} else {
|
||||||
beginInsertRows(QModelIndex(), insertStart, insertStart + static_cast<int>(batchItems.size()) - 1);
|
beginInsertRows(
|
||||||
|
QModelIndex(),
|
||||||
|
insertStart,
|
||||||
|
insertStart + static_cast<int>(batchItems.size()) - 1);
|
||||||
for (int i = 0; i < batchItems.size(); ++i) {
|
for (int i = 0; i < batchItems.size(); ++i) {
|
||||||
m_entries.insert(insertStart + i, batchItems[i]);
|
m_entries.insert(insertStart + i, batchItems[i]);
|
||||||
}
|
}
|
||||||
@@ -458,7 +492,10 @@ void FileSystemModel::applyChanges(const QSet<QString>& removedPaths, const QSet
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!batchItems.isEmpty()) {
|
if (!batchItems.isEmpty()) {
|
||||||
beginInsertRows(QModelIndex(), insertStart, insertStart + static_cast<int>(batchItems.size()) - 1);
|
beginInsertRows(
|
||||||
|
QModelIndex(),
|
||||||
|
insertStart,
|
||||||
|
insertStart + static_cast<int>(batchItems.size()) - 1);
|
||||||
for (int i = 0; i < batchItems.size(); ++i) {
|
for (int i = 0; i < batchItems.size(); ++i) {
|
||||||
m_entries.insert(insertStart + i, batchItems[i]);
|
m_entries.insert(insertStart + i, batchItems[i]);
|
||||||
}
|
}
|
||||||
@@ -468,7 +505,8 @@ void FileSystemModel::applyChanges(const QSet<QString>& removedPaths, const QSet
|
|||||||
emit entriesChanged();
|
emit entriesChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileSystemModel::compareEntries(const FileSystemEntry* a, const FileSystemEntry* b) const {
|
bool FileSystemModel::compareEntries(
|
||||||
|
const FileSystemEntry* a, const FileSystemEntry* b) const {
|
||||||
if (a->isDir() != b->isDir()) {
|
if (a->isDir() != b->isDir()) {
|
||||||
return m_sortReverse ^ a->isDir();
|
return m_sortReverse ^ a->isDir();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <qabstractitemmodel.h>
|
#include <qabstractitemmodel.h>
|
||||||
#include <qdir.h>
|
#include <qdir.h>
|
||||||
#include <qfilesystemwatcher.h>
|
#include <qfilesystemwatcher.h>
|
||||||
@@ -16,7 +15,9 @@ namespace ZShell::models {
|
|||||||
class FileSystemEntry : public QObject {
|
class FileSystemEntry : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
QML_UNCREATABLE("FileSystemEntry instances can only be retrieved from a FileSystemModel")
|
QML_UNCREATABLE(
|
||||||
|
"FileSystemEntry instances can only be retrieved from a "
|
||||||
|
"FileSystemModel")
|
||||||
|
|
||||||
Q_PROPERTY(QString path READ path CONSTANT)
|
Q_PROPERTY(QString path READ path CONSTANT)
|
||||||
Q_PROPERTY(QString relativePath READ relativePath NOTIFY relativePathChanged)
|
Q_PROPERTY(QString relativePath READ relativePath NOTIFY relativePathChanged)
|
||||||
@@ -30,7 +31,10 @@ Q_PROPERTY(bool isImage READ isImage CONSTANT)
|
|||||||
Q_PROPERTY(QString mimeType READ mimeType CONSTANT)
|
Q_PROPERTY(QString mimeType READ mimeType CONSTANT)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit FileSystemEntry(const QString& path, const QString& relativePath, QObject* parent = nullptr);
|
explicit FileSystemEntry(
|
||||||
|
const QString& path,
|
||||||
|
const QString& relativePath,
|
||||||
|
QObject* parent = nullptr);
|
||||||
|
|
||||||
[[nodiscard]] QString path() const;
|
[[nodiscard]] QString path() const;
|
||||||
[[nodiscard]] QString relativePath() const;
|
[[nodiscard]] QString relativePath() const;
|
||||||
@@ -66,28 +70,36 @@ Q_OBJECT
|
|||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
|
|
||||||
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
|
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
|
||||||
Q_PROPERTY(bool recursive READ recursive WRITE setRecursive NOTIFY recursiveChanged)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(bool watchChanges READ watchChanges WRITE setWatchChanges NOTIFY watchChangesChanged)
|
bool recursive READ recursive WRITE setRecursive NOTIFY recursiveChanged)
|
||||||
Q_PROPERTY(bool showHidden READ showHidden WRITE setShowHidden NOTIFY showHiddenChanged)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(bool sortReverse READ sortReverse WRITE setSortReverse NOTIFY sortReverseChanged)
|
bool watchChanges READ watchChanges WRITE setWatchChanges NOTIFY
|
||||||
|
watchChangesChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
bool showHidden READ showHidden WRITE setShowHidden NOTIFY
|
||||||
|
showHiddenChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
bool sortReverse READ sortReverse WRITE setSortReverse NOTIFY
|
||||||
|
sortReverseChanged)
|
||||||
Q_PROPERTY(Filter filter READ filter WRITE setFilter NOTIFY filterChanged)
|
Q_PROPERTY(Filter filter READ filter WRITE setFilter NOTIFY filterChanged)
|
||||||
Q_PROPERTY(QStringList nameFilters READ nameFilters WRITE setNameFilters NOTIFY nameFiltersChanged)
|
Q_PROPERTY(
|
||||||
|
QStringList nameFilters READ nameFilters WRITE setNameFilters NOTIFY
|
||||||
|
nameFiltersChanged)
|
||||||
|
|
||||||
Q_PROPERTY(QQmlListProperty<ZShell::models::FileSystemEntry> entries READ entries NOTIFY entriesChanged)
|
Q_PROPERTY(
|
||||||
|
QQmlListProperty<ZShell::models::FileSystemEntry> entries READ entries
|
||||||
|
NOTIFY entriesChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum Filter {
|
enum Filter { NoFilter, Images, Files, Dirs };
|
||||||
NoFilter,
|
|
||||||
Images,
|
|
||||||
Files,
|
|
||||||
Dirs
|
|
||||||
};
|
|
||||||
Q_ENUM(Filter)
|
Q_ENUM(Filter)
|
||||||
|
|
||||||
explicit FileSystemModel(QObject* parent = nullptr);
|
explicit FileSystemModel(QObject* parent = nullptr);
|
||||||
|
|
||||||
[[nodiscard]] int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
[[nodiscard]] int rowCount(
|
||||||
[[nodiscard]] QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
const QModelIndex& parent = QModelIndex()) const override;
|
||||||
|
[[nodiscard]] QVariant data(
|
||||||
|
const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
||||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
[[nodiscard]] QString path() const;
|
[[nodiscard]] QString path() const;
|
||||||
@@ -142,8 +154,10 @@ void update();
|
|||||||
void updateWatcher();
|
void updateWatcher();
|
||||||
void updateEntries();
|
void updateEntries();
|
||||||
void updateEntriesForDir(const QString& dir);
|
void updateEntriesForDir(const QString& dir);
|
||||||
void applyChanges(const QSet<QString>& removedPaths, const QSet<QString>& addedPaths);
|
void applyChanges(
|
||||||
[[nodiscard]] bool compareEntries(const FileSystemEntry* a, const FileSystemEntry* b) const;
|
const QSet<QString>& removedPaths, const QSet<QString>& addedPaths);
|
||||||
|
[[nodiscard]] bool compareEntries(
|
||||||
|
const FileSystemEntry* a, const FileSystemEntry* b) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ZShell::models
|
} // namespace ZShell::models
|
||||||
|
|||||||
@@ -23,20 +23,33 @@ PipeWireWorker::PipeWireWorker(std::stop_token token, AudioCollector* collector)
|
|||||||
|
|
||||||
m_loop = pw_main_loop_new(nullptr);
|
m_loop = pw_main_loop_new(nullptr);
|
||||||
if (!m_loop) {
|
if (!m_loop) {
|
||||||
qWarning() << "PipeWireWorker::init: failed to create PipeWire main loop";
|
qWarning()
|
||||||
|
<< "PipeWireWorker::init: failed to create PipeWire main loop";
|
||||||
pw_deinit();
|
pw_deinit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
timespec timeout = {0, 10 * SPA_NSEC_PER_MSEC};
|
timespec timeout = {0, 10 * SPA_NSEC_PER_MSEC};
|
||||||
m_timer = pw_loop_add_timer(pw_main_loop_get_loop(m_loop), handleTimeout, this);
|
m_timer =
|
||||||
pw_loop_update_timer(pw_main_loop_get_loop(m_loop), m_timer, &timeout, &timeout, false);
|
pw_loop_add_timer(pw_main_loop_get_loop(m_loop), handleTimeout, this);
|
||||||
|
pw_loop_update_timer(
|
||||||
|
pw_main_loop_get_loop(m_loop), m_timer, &timeout, &timeout, false);
|
||||||
|
|
||||||
auto props = pw_properties_new(
|
auto props = pw_properties_new(
|
||||||
PW_KEY_MEDIA_TYPE, "Audio", PW_KEY_MEDIA_CATEGORY, "Capture", PW_KEY_MEDIA_ROLE, "Music", nullptr);
|
PW_KEY_MEDIA_TYPE,
|
||||||
|
"Audio",
|
||||||
|
PW_KEY_MEDIA_CATEGORY,
|
||||||
|
"Capture",
|
||||||
|
PW_KEY_MEDIA_ROLE,
|
||||||
|
"Music",
|
||||||
|
nullptr);
|
||||||
pw_properties_set(props, PW_KEY_STREAM_CAPTURE_SINK, "true");
|
pw_properties_set(props, PW_KEY_STREAM_CAPTURE_SINK, "true");
|
||||||
pw_properties_setf(
|
pw_properties_setf(
|
||||||
props, PW_KEY_NODE_LATENCY, "%u/%u", nextPowerOf2(512 * ac::SAMPLE_RATE / 48000), ac::SAMPLE_RATE);
|
props,
|
||||||
|
PW_KEY_NODE_LATENCY,
|
||||||
|
"%u/%u",
|
||||||
|
nextPowerOf2(512 * ac::SAMPLE_RATE / 48000),
|
||||||
|
ac::SAMPLE_RATE);
|
||||||
pw_properties_set(props, PW_KEY_NODE_PASSIVE, "true");
|
pw_properties_set(props, PW_KEY_NODE_PASSIVE, "true");
|
||||||
pw_properties_set(props, PW_KEY_NODE_VIRTUAL, "true");
|
pw_properties_set(props, PW_KEY_NODE_VIRTUAL, "true");
|
||||||
pw_properties_set(props, PW_KEY_STREAM_DONT_REMIX, "false");
|
pw_properties_set(props, PW_KEY_STREAM_DONT_REMIX, "false");
|
||||||
@@ -55,7 +68,8 @@ PipeWireWorker::PipeWireWorker(std::stop_token token, AudioCollector* collector)
|
|||||||
params[0] = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &info);
|
params[0] = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat, &info);
|
||||||
|
|
||||||
pw_stream_events events{};
|
pw_stream_events events{};
|
||||||
events.state_changed = [](void* data, pw_stream_state, pw_stream_state state, const char*) {
|
events.state_changed =
|
||||||
|
[](void* data, pw_stream_state, pw_stream_state state, const char*) {
|
||||||
auto* self = static_cast<PipeWireWorker*>(data);
|
auto* self = static_cast<PipeWireWorker*>(data);
|
||||||
self->streamStateChanged(state);
|
self->streamStateChanged(state);
|
||||||
};
|
};
|
||||||
@@ -64,12 +78,18 @@ PipeWireWorker::PipeWireWorker(std::stop_token token, AudioCollector* collector)
|
|||||||
self->processStream();
|
self->processStream();
|
||||||
};
|
};
|
||||||
|
|
||||||
m_stream = pw_stream_new_simple(pw_main_loop_get_loop(m_loop), "ZShell-shell", props, &events, this);
|
m_stream = pw_stream_new_simple(
|
||||||
|
pw_main_loop_get_loop(m_loop), "ZShell-shell", props, &events, this);
|
||||||
|
|
||||||
const int success = pw_stream_connect(m_stream, PW_DIRECTION_INPUT, PW_ID_ANY,
|
const int success = pw_stream_connect(
|
||||||
|
m_stream,
|
||||||
|
PW_DIRECTION_INPUT,
|
||||||
|
PW_ID_ANY,
|
||||||
static_cast<pw_stream_flags>(
|
static_cast<pw_stream_flags>(
|
||||||
PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS | PW_STREAM_FLAG_RT_PROCESS),
|
PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS |
|
||||||
params, 1);
|
PW_STREAM_FLAG_RT_PROCESS),
|
||||||
|
params,
|
||||||
|
1);
|
||||||
if (success < 0) {
|
if (success < 0) {
|
||||||
qWarning() << "PipeWireWorker::init: failed to connect stream";
|
qWarning() << "PipeWireWorker::init: failed to connect stream";
|
||||||
pw_stream_destroy(m_stream);
|
pw_stream_destroy(m_stream);
|
||||||
@@ -99,7 +119,12 @@ void PipeWireWorker::handleTimeout(void* data, uint64_t expirations) {
|
|||||||
} else {
|
} else {
|
||||||
self->m_idle = true;
|
self->m_idle = true;
|
||||||
timespec timeout = {0, 500 * SPA_NSEC_PER_MSEC};
|
timespec timeout = {0, 500 * SPA_NSEC_PER_MSEC};
|
||||||
pw_loop_update_timer(pw_main_loop_get_loop(self->m_loop), self->m_timer, &timeout, &timeout, false);
|
pw_loop_update_timer(
|
||||||
|
pw_main_loop_get_loop(self->m_loop),
|
||||||
|
self->m_timer,
|
||||||
|
&timeout,
|
||||||
|
&timeout,
|
||||||
|
false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,11 +134,13 @@ void PipeWireWorker::streamStateChanged(pw_stream_state state) {
|
|||||||
switch (state) {
|
switch (state) {
|
||||||
case PW_STREAM_STATE_PAUSED: {
|
case PW_STREAM_STATE_PAUSED: {
|
||||||
timespec timeout = {0, 10 * SPA_NSEC_PER_MSEC};
|
timespec timeout = {0, 10 * SPA_NSEC_PER_MSEC};
|
||||||
pw_loop_update_timer(pw_main_loop_get_loop(m_loop), m_timer, &timeout, &timeout, false);
|
pw_loop_update_timer(
|
||||||
|
pw_main_loop_get_loop(m_loop), m_timer, &timeout, &timeout, false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PW_STREAM_STATE_STREAMING:
|
case PW_STREAM_STATE_STREAMING:
|
||||||
pw_loop_update_timer(pw_main_loop_get_loop(m_loop), m_timer, nullptr, nullptr, false);
|
pw_loop_update_timer(
|
||||||
|
pw_main_loop_get_loop(m_loop), m_timer, nullptr, nullptr, false);
|
||||||
break;
|
break;
|
||||||
case PW_STREAM_STATE_ERROR:
|
case PW_STREAM_STATE_ERROR:
|
||||||
pw_main_loop_quit(m_loop);
|
pw_main_loop_quit(m_loop);
|
||||||
@@ -171,7 +198,8 @@ void AudioCollector::clearBuffer() {
|
|||||||
auto* writeBuffer = m_writeBuffer.load(std::memory_order_relaxed);
|
auto* writeBuffer = m_writeBuffer.load(std::memory_order_relaxed);
|
||||||
std::fill(writeBuffer->begin(), writeBuffer->end(), 0.0f);
|
std::fill(writeBuffer->begin(), writeBuffer->end(), 0.0f);
|
||||||
|
|
||||||
auto* oldRead = m_readBuffer.exchange(writeBuffer, std::memory_order_acq_rel);
|
auto* oldRead =
|
||||||
|
m_readBuffer.exchange(writeBuffer, std::memory_order_acq_rel);
|
||||||
m_writeBuffer.store(oldRead, std::memory_order_release);
|
m_writeBuffer.store(oldRead, std::memory_order_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,11 +209,13 @@ void AudioCollector::loadChunk(const qint16* samples, quint32 count) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto* writeBuffer = m_writeBuffer.load(std::memory_order_relaxed);
|
auto* writeBuffer = m_writeBuffer.load(std::memory_order_relaxed);
|
||||||
std::transform(samples, samples + count, writeBuffer->begin(), [](qint16 sample) {
|
std::transform(
|
||||||
|
samples, samples + count, writeBuffer->begin(), [](qint16 sample) {
|
||||||
return sample / 32768.0f;
|
return sample / 32768.0f;
|
||||||
});
|
});
|
||||||
|
|
||||||
auto* oldRead = m_readBuffer.exchange(writeBuffer, std::memory_order_acq_rel);
|
auto* oldRead =
|
||||||
|
m_readBuffer.exchange(writeBuffer, std::memory_order_acq_rel);
|
||||||
m_writeBuffer.store(oldRead, std::memory_order_release);
|
m_writeBuffer.store(oldRead, std::memory_order_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,9 +236,11 @@ quint32 AudioCollector::readChunk(double* out, quint32 count) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto* readBuffer = m_readBuffer.load(std::memory_order_acquire);
|
auto* readBuffer = m_readBuffer.load(std::memory_order_acquire);
|
||||||
std::transform(readBuffer->begin(), readBuffer->begin() + count, out, [](float sample) {
|
std::transform(
|
||||||
return static_cast<double>(sample);
|
readBuffer->begin(),
|
||||||
});
|
readBuffer->begin() + count,
|
||||||
|
out,
|
||||||
|
[](float sample) { return static_cast<double>(sample); });
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
@@ -218,8 +250,7 @@ AudioCollector::AudioCollector(QObject* parent)
|
|||||||
, m_buffer1(ac::CHUNK_SIZE)
|
, m_buffer1(ac::CHUNK_SIZE)
|
||||||
, m_buffer2(ac::CHUNK_SIZE)
|
, m_buffer2(ac::CHUNK_SIZE)
|
||||||
, m_readBuffer(&m_buffer1)
|
, m_readBuffer(&m_buffer1)
|
||||||
, m_writeBuffer(&m_buffer2) {
|
, m_writeBuffer(&m_buffer2) {}
|
||||||
}
|
|
||||||
|
|
||||||
AudioCollector::~AudioCollector() {
|
AudioCollector::~AudioCollector() {
|
||||||
stop();
|
stop();
|
||||||
@@ -232,9 +263,8 @@ void AudioCollector::start() {
|
|||||||
|
|
||||||
clearBuffer();
|
clearBuffer();
|
||||||
|
|
||||||
m_thread = std::jthread([this](std::stop_token token) {
|
m_thread = std::jthread(
|
||||||
PipeWireWorker worker(token, this);
|
[this](std::stop_token token) { PipeWireWorker worker(token, this); });
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioCollector::stop() {
|
void AudioCollector::stop() {
|
||||||
|
|||||||
@@ -7,9 +7,7 @@
|
|||||||
|
|
||||||
namespace ZShell::services {
|
namespace ZShell::services {
|
||||||
|
|
||||||
AudioProcessor::AudioProcessor(QObject* parent)
|
AudioProcessor::AudioProcessor(QObject* parent) : QObject(parent) {}
|
||||||
: QObject(parent) {
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioProcessor::~AudioProcessor() {
|
AudioProcessor::~AudioProcessor() {
|
||||||
stop();
|
stop();
|
||||||
@@ -17,12 +15,17 @@ AudioProcessor::~AudioProcessor() {
|
|||||||
|
|
||||||
void AudioProcessor::init() {
|
void AudioProcessor::init() {
|
||||||
m_timer = new QTimer(this);
|
m_timer = new QTimer(this);
|
||||||
m_timer->setInterval(static_cast<int>(ac::CHUNK_SIZE * 1000.0 / ac::SAMPLE_RATE));
|
m_timer->setInterval(
|
||||||
|
static_cast<int>(ac::CHUNK_SIZE * 1000.0 / ac::SAMPLE_RATE));
|
||||||
connect(m_timer, &QTimer::timeout, this, &AudioProcessor::process);
|
connect(m_timer, &QTimer::timeout, this, &AudioProcessor::process);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioProcessor::start() {
|
void AudioProcessor::start() {
|
||||||
QMetaObject::invokeMethod(&AudioCollector::instance(), &AudioCollector::ref, Qt::QueuedConnection, this);
|
QMetaObject::invokeMethod(
|
||||||
|
&AudioCollector::instance(),
|
||||||
|
&AudioCollector::ref,
|
||||||
|
Qt::QueuedConnection,
|
||||||
|
this);
|
||||||
if (m_timer) {
|
if (m_timer) {
|
||||||
m_timer->start();
|
m_timer->start();
|
||||||
}
|
}
|
||||||
@@ -32,14 +35,15 @@ void AudioProcessor::stop() {
|
|||||||
if (m_timer) {
|
if (m_timer) {
|
||||||
m_timer->stop();
|
m_timer->stop();
|
||||||
}
|
}
|
||||||
QMetaObject::invokeMethod(&AudioCollector::instance(), &AudioCollector::unref, Qt::QueuedConnection, this);
|
QMetaObject::invokeMethod(
|
||||||
|
&AudioCollector::instance(),
|
||||||
|
&AudioCollector::unref,
|
||||||
|
Qt::QueuedConnection,
|
||||||
|
this);
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioProvider::AudioProvider(QObject* parent)
|
AudioProvider::AudioProvider(QObject* parent)
|
||||||
: Service(parent)
|
: Service(parent), m_processor(nullptr), m_thread(nullptr) {}
|
||||||
, m_processor(nullptr)
|
|
||||||
, m_thread(nullptr) {
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioProvider::~AudioProvider() {
|
AudioProvider::~AudioProvider() {
|
||||||
if (m_thread) {
|
if (m_thread) {
|
||||||
@@ -50,7 +54,8 @@ AudioProvider::~AudioProvider() {
|
|||||||
|
|
||||||
void AudioProvider::init() {
|
void AudioProvider::init() {
|
||||||
if (!m_processor) {
|
if (!m_processor) {
|
||||||
qWarning() << "AudioProvider::init: attempted to init with no processor set";
|
qWarning()
|
||||||
|
<< "AudioProvider::init: attempted to init with no processor set";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +63,8 @@ void AudioProvider::init() {
|
|||||||
m_processor->moveToThread(m_thread);
|
m_processor->moveToThread(m_thread);
|
||||||
|
|
||||||
connect(m_thread, &QThread::started, m_processor, &AudioProcessor::init);
|
connect(m_thread, &QThread::started, m_processor, &AudioProcessor::init);
|
||||||
connect(m_thread, &QThread::finished, m_processor, &AudioProcessor::deleteLater);
|
connect(
|
||||||
|
m_thread, &QThread::finished, m_processor, &AudioProcessor::deleteLater);
|
||||||
connect(m_thread, &QThread::finished, m_thread, &QThread::deleteLater);
|
connect(m_thread, &QThread::finished, m_thread, &QThread::deleteLater);
|
||||||
|
|
||||||
m_thread->start();
|
m_thread->start();
|
||||||
|
|||||||
@@ -10,8 +10,7 @@ BeatProcessor::BeatProcessor(QObject* parent)
|
|||||||
: AudioProcessor(parent)
|
: AudioProcessor(parent)
|
||||||
, m_tempo(new_aubio_tempo("default", 1024, ac::CHUNK_SIZE, ac::SAMPLE_RATE))
|
, m_tempo(new_aubio_tempo("default", 1024, ac::CHUNK_SIZE, ac::SAMPLE_RATE))
|
||||||
, m_in(new_fvec(ac::CHUNK_SIZE))
|
, m_in(new_fvec(ac::CHUNK_SIZE))
|
||||||
, m_out(new_fvec(2)) {
|
, m_out(new_fvec(2)) {};
|
||||||
};
|
|
||||||
|
|
||||||
BeatProcessor::~BeatProcessor() {
|
BeatProcessor::~BeatProcessor() {
|
||||||
if (m_tempo) {
|
if (m_tempo) {
|
||||||
@@ -36,13 +35,15 @@ void BeatProcessor::process() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BeatTracker::BeatTracker(QObject* parent)
|
BeatTracker::BeatTracker(QObject* parent) : AudioProvider(parent), m_bpm(120) {
|
||||||
: AudioProvider(parent)
|
|
||||||
, m_bpm(120) {
|
|
||||||
m_processor = new BeatProcessor();
|
m_processor = new BeatProcessor();
|
||||||
init();
|
init();
|
||||||
|
|
||||||
connect(static_cast<BeatProcessor*>(m_processor), &BeatProcessor::beat, this, &BeatTracker::updateBpm);
|
connect(
|
||||||
|
static_cast<BeatProcessor*>(m_processor),
|
||||||
|
&BeatProcessor::beat,
|
||||||
|
this,
|
||||||
|
&BeatTracker::updateBpm);
|
||||||
}
|
}
|
||||||
|
|
||||||
smpl_t BeatTracker::bpm() const {
|
smpl_t BeatTracker::bpm() const {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include "audiocollector.hpp"
|
#include "audiocollector.hpp"
|
||||||
#include "audioprovider.hpp"
|
#include "audioprovider.hpp"
|
||||||
#include <algorithm>
|
|
||||||
#include <cava/cavacore.h>
|
#include <cava/cavacore.h>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <qdebug.h>
|
#include <qdebug.h>
|
||||||
@@ -14,8 +13,7 @@ CavaProcessor::CavaProcessor(QObject* parent)
|
|||||||
, m_plan(nullptr)
|
, m_plan(nullptr)
|
||||||
, m_in(new double[ac::CHUNK_SIZE])
|
, m_in(new double[ac::CHUNK_SIZE])
|
||||||
, m_out(nullptr)
|
, m_out(nullptr)
|
||||||
, m_bars(0) {
|
, m_bars(0) {};
|
||||||
};
|
|
||||||
|
|
||||||
CavaProcessor::~CavaProcessor() {
|
CavaProcessor::~CavaProcessor() {
|
||||||
cleanup();
|
cleanup();
|
||||||
@@ -27,7 +25,8 @@ void CavaProcessor::process() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const int count = static_cast<int>(AudioCollector::instance().readChunk(m_in));
|
const int count =
|
||||||
|
static_cast<int>(AudioCollector::instance().readChunk(m_in));
|
||||||
|
|
||||||
// Process in data via cava
|
// Process in data via cava
|
||||||
cava_execute(m_in, count, m_out, m_plan);
|
cava_execute(m_in, count, m_out, m_plan);
|
||||||
@@ -63,7 +62,8 @@ void CavaProcessor::process() {
|
|||||||
|
|
||||||
void CavaProcessor::setBars(int bars) {
|
void CavaProcessor::setBars(int bars) {
|
||||||
if (bars < 0) {
|
if (bars < 0) {
|
||||||
qWarning() << "CavaProcessor::setBars: bars must be greater than 0. Setting to 0.";
|
qWarning() << "CavaProcessor::setBars: bars must be greater than 0. "
|
||||||
|
"Setting to 0.";
|
||||||
bars = 0;
|
bars = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,13 +100,15 @@ void CavaProcessor::initCava() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CavaProvider::CavaProvider(QObject* parent)
|
CavaProvider::CavaProvider(QObject* parent)
|
||||||
: AudioProvider(parent)
|
: AudioProvider(parent), m_bars(0), m_values(m_bars, 0.0) {
|
||||||
, m_bars(0)
|
|
||||||
, m_values(m_bars, 0.0) {
|
|
||||||
m_processor = new CavaProcessor();
|
m_processor = new CavaProcessor();
|
||||||
init();
|
init();
|
||||||
|
|
||||||
connect(static_cast<CavaProcessor*>(m_processor), &CavaProcessor::valuesChanged, this, &CavaProvider::updateValues);
|
connect(
|
||||||
|
static_cast<CavaProcessor*>(m_processor),
|
||||||
|
&CavaProcessor::valuesChanged,
|
||||||
|
this,
|
||||||
|
&CavaProvider::updateValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
int CavaProvider::bars() const {
|
int CavaProvider::bars() const {
|
||||||
@@ -115,7 +117,8 @@ int CavaProvider::bars() const {
|
|||||||
|
|
||||||
void CavaProvider::setBars(int bars) {
|
void CavaProvider::setBars(int bars) {
|
||||||
if (bars < 0) {
|
if (bars < 0) {
|
||||||
qWarning() << "CavaProvider::setBars: bars must be greater than 0. Setting to 0.";
|
qWarning() << "CavaProvider::setBars: bars must be greater than 0. "
|
||||||
|
"Setting to 0.";
|
||||||
bars = 0;
|
bars = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,7 +132,10 @@ void CavaProvider::setBars(int bars) {
|
|||||||
emit valuesChanged();
|
emit valuesChanged();
|
||||||
|
|
||||||
QMetaObject::invokeMethod(
|
QMetaObject::invokeMethod(
|
||||||
static_cast<CavaProcessor*>(m_processor), &CavaProcessor::setBars, Qt::QueuedConnection, bars);
|
static_cast<CavaProcessor*>(m_processor),
|
||||||
|
&CavaProcessor::setBars,
|
||||||
|
Qt::QueuedConnection,
|
||||||
|
bars);
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<double> CavaProvider::values() const {
|
QVector<double> CavaProvider::values() const {
|
||||||
|
|||||||
@@ -8,8 +8,7 @@
|
|||||||
|
|
||||||
namespace ZShell::services {
|
namespace ZShell::services {
|
||||||
|
|
||||||
Cpu::Cpu(QObject* parent)
|
Cpu::Cpu(QObject* parent) : TickingService(parent) {
|
||||||
: TickingService(parent) {
|
|
||||||
readNameOnce();
|
readNameOnce();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,7 +40,8 @@ void Cpu::readNameOnce() {
|
|||||||
const QByteArray data = f.readAll();
|
const QByteArray data = f.readAll();
|
||||||
f.close();
|
f.close();
|
||||||
|
|
||||||
static const QRegularExpression re(QStringLiteral("model name\\s*:\\s*(.+)"));
|
static const QRegularExpression re(
|
||||||
|
QStringLiteral("model name\\s*:\\s*(.+)"));
|
||||||
const auto match = re.match(QString::fromLatin1(data));
|
const auto match = re.match(QString::fromLatin1(data));
|
||||||
if (!match.hasMatch()) {
|
if (!match.hasMatch()) {
|
||||||
return;
|
return;
|
||||||
@@ -64,8 +64,9 @@ void Cpu::refreshPercentage() {
|
|||||||
const QByteArray data = f.readAll();
|
const QByteArray data = f.readAll();
|
||||||
f.close();
|
f.close();
|
||||||
|
|
||||||
static const QRegularExpression re(
|
static const QRegularExpression re(QStringLiteral(
|
||||||
QStringLiteral("^cpu\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)"));
|
"^cpu\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)"
|
||||||
|
"\\s+(\\d+)\\s+(\\d+)"));
|
||||||
const auto match = re.match(QString::fromLatin1(data));
|
const auto match = re.match(QString::fromLatin1(data));
|
||||||
if (!match.hasMatch()) {
|
if (!match.hasMatch()) {
|
||||||
return;
|
return;
|
||||||
@@ -83,7 +84,10 @@ void Cpu::refreshPercentage() {
|
|||||||
|
|
||||||
const quint64 totalDiff = total > m_lastTotal ? total - m_lastTotal : 0;
|
const quint64 totalDiff = total > m_lastTotal ? total - m_lastTotal : 0;
|
||||||
const quint64 idleDiff = idle > m_lastIdle ? idle - m_lastIdle : 0;
|
const quint64 idleDiff = idle > m_lastIdle ? idle - m_lastIdle : 0;
|
||||||
const qreal newPerc = totalDiff > 0 ? 1.0 - static_cast<qreal>(idleDiff) / static_cast<qreal>(totalDiff) : 0.0;
|
const qreal newPerc =
|
||||||
|
totalDiff > 0
|
||||||
|
? 1.0 - static_cast<qreal>(idleDiff) / static_cast<qreal>(totalDiff)
|
||||||
|
: 0.0;
|
||||||
|
|
||||||
m_lastTotal = total;
|
m_lastTotal = total;
|
||||||
m_lastIdle = idle;
|
m_lastIdle = idle;
|
||||||
@@ -105,7 +109,8 @@ void Cpu::refreshTemperature() {
|
|||||||
|
|
||||||
QString Cpu::cleanName(QString s) {
|
QString Cpu::cleanName(QString s) {
|
||||||
static const QRegularExpression noise(
|
static const QRegularExpression noise(
|
||||||
QStringLiteral("\\(R\\)|\\(TM\\)|CPU|\\d+(?:th|nd|rd|st) Gen |Core |Processor"),
|
QStringLiteral(
|
||||||
|
"\\(R\\)|\\(TM\\)|CPU|\\d+(?:th|nd|rd|st) Gen |Core |Processor"),
|
||||||
QRegularExpression::CaseInsensitiveOption);
|
QRegularExpression::CaseInsensitiveOption);
|
||||||
static const QRegularExpression spaces(QStringLiteral("\\s+"));
|
static const QRegularExpression spaces(QStringLiteral("\\s+"));
|
||||||
|
|
||||||
|
|||||||
@@ -31,22 +31,30 @@ enum DesktopRoles {
|
|||||||
|
|
||||||
explicit DesktopModel(QObject* parent = nullptr);
|
explicit DesktopModel(QObject* parent = nullptr);
|
||||||
|
|
||||||
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
[[nodiscard]] int rowCount(
|
||||||
[[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
const QModelIndex& parent = QModelIndex()) const override;
|
||||||
|
[[nodiscard]] QVariant data(
|
||||||
|
const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
||||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
Q_INVOKABLE void loadDirectory(const QString& path);
|
Q_INVOKABLE void loadDirectory(const QString& path);
|
||||||
Q_INVOKABLE void moveIcon(int index, int newX, int newY);
|
Q_INVOKABLE void moveIcon(int index, int newX, int newY);
|
||||||
Q_INVOKABLE void massMove(const QVariantList &selectedPathsList, const QString &leaderPath, int targetX, int targetY, int maxCol, int maxRow);
|
Q_INVOKABLE void massMove(
|
||||||
|
const QVariantList& selectedPathsList,
|
||||||
|
const QString& leaderPath,
|
||||||
|
int targetX,
|
||||||
|
int targetY,
|
||||||
|
int maxCol,
|
||||||
|
int maxRow);
|
||||||
|
|
||||||
Q_PROPERTY(int rows READ rows WRITE setRows NOTIFY rowsChanged)
|
Q_PROPERTY(int rows READ rows WRITE setRows NOTIFY rowsChanged)
|
||||||
|
|
||||||
public:
|
[[nodiscard]] int rows() const { return m_rows; }
|
||||||
[[nodiscard]] int rows() const {
|
|
||||||
return m_rows;
|
|
||||||
}
|
|
||||||
void setRows(int r) {
|
void setRows(int r) {
|
||||||
if (m_rows != r) { m_rows = r; emit rowsChanged(); }
|
if (m_rows != r) {
|
||||||
|
m_rows = r;
|
||||||
|
emit rowsChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
@@ -62,4 +70,4 @@ void saveCurrentLayout();
|
|||||||
void onDirectoryChanged();
|
void onDirectoryChanged();
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
}; // namespace ZShell::services
|
||||||
|
|||||||
@@ -9,11 +9,12 @@
|
|||||||
|
|
||||||
namespace ZShell::services {
|
namespace ZShell::services {
|
||||||
|
|
||||||
DesktopStateManager::DesktopStateManager(QObject *parent) : QObject(parent) {
|
DesktopStateManager::DesktopStateManager(QObject* parent) : QObject(parent) {}
|
||||||
}
|
|
||||||
|
|
||||||
QString DesktopStateManager::getConfigFilePath() const {
|
QString DesktopStateManager::getConfigFilePath() const {
|
||||||
QString configDir = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + "/zshell";
|
QString configDir =
|
||||||
|
QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) +
|
||||||
|
"/zshell";
|
||||||
QDir dir(configDir);
|
QDir dir(configDir);
|
||||||
if (!dir.exists()) {
|
if (!dir.exists()) {
|
||||||
dir.mkpath(".");
|
dir.mkpath(".");
|
||||||
@@ -30,7 +31,8 @@ void DesktopStateManager::saveLayout(const QVariantMap& layout) {
|
|||||||
file.write(doc.toJson(QJsonDocument::Indented));
|
file.write(doc.toJson(QJsonDocument::Indented));
|
||||||
file.close();
|
file.close();
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "zshell: Cannot save desktop layout to" << getConfigFilePath();
|
qWarning() << "zshell: Cannot save desktop layout to"
|
||||||
|
<< getConfigFilePath();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,13 +8,17 @@ constexpr qreal kKib = 1024.0;
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
DiskInfo::DiskInfo(QString mount, quint64 usedBytes, quint64 totalBytes, bool hasRoot, QObject* parent)
|
DiskInfo::DiskInfo(
|
||||||
|
QString mount,
|
||||||
|
quint64 usedBytes,
|
||||||
|
quint64 totalBytes,
|
||||||
|
bool hasRoot,
|
||||||
|
QObject* parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, m_mount(std::move(mount))
|
, m_mount(std::move(mount))
|
||||||
, m_usedBytes(usedBytes)
|
, m_usedBytes(usedBytes)
|
||||||
, m_totalBytes(totalBytes)
|
, m_totalBytes(totalBytes)
|
||||||
, m_hasRoot(hasRoot) {
|
, m_hasRoot(hasRoot) {}
|
||||||
}
|
|
||||||
|
|
||||||
QString DiskInfo::mount() const {
|
QString DiskInfo::mount() const {
|
||||||
return m_mount;
|
return m_mount;
|
||||||
@@ -29,12 +33,15 @@ qreal DiskInfo::total() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
qreal DiskInfo::free() const {
|
qreal DiskInfo::free() const {
|
||||||
const quint64 freeBytes = m_totalBytes > m_usedBytes ? m_totalBytes - m_usedBytes : 0;
|
const quint64 freeBytes =
|
||||||
|
m_totalBytes > m_usedBytes ? m_totalBytes - m_usedBytes : 0;
|
||||||
return static_cast<qreal>(freeBytes) / kKib;
|
return static_cast<qreal>(freeBytes) / kKib;
|
||||||
}
|
}
|
||||||
|
|
||||||
qreal DiskInfo::perc() const {
|
qreal DiskInfo::perc() const {
|
||||||
return m_totalBytes > 0 ? static_cast<qreal>(m_usedBytes) / static_cast<qreal>(m_totalBytes) : 0.0;
|
return m_totalBytes > 0 ? static_cast<qreal>(m_usedBytes) /
|
||||||
|
static_cast<qreal>(m_totalBytes)
|
||||||
|
: 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DiskInfo::hasRoot() const {
|
bool DiskInfo::hasRoot() const {
|
||||||
|
|||||||
@@ -18,7 +18,12 @@ Q_PROPERTY(qreal perc READ perc NOTIFY percChanged)
|
|||||||
Q_PROPERTY(bool hasRoot READ hasRoot NOTIFY hasRootChanged)
|
Q_PROPERTY(bool hasRoot READ hasRoot NOTIFY hasRootChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DiskInfo(QString mount, quint64 usedBytes, quint64 totalBytes, bool hasRoot, QObject* parent = nullptr);
|
DiskInfo(
|
||||||
|
QString mount,
|
||||||
|
quint64 usedBytes,
|
||||||
|
quint64 totalBytes,
|
||||||
|
bool hasRoot,
|
||||||
|
QObject* parent = nullptr);
|
||||||
|
|
||||||
[[nodiscard]] QString mount() const;
|
[[nodiscard]] QString mount() const;
|
||||||
[[nodiscard]] qreal used() const;
|
[[nodiscard]] qreal used() const;
|
||||||
|
|||||||
@@ -51,16 +51,14 @@ int HyprsunsetManager::temp() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void HyprsunsetManager::setActiveAuto(bool activeAuto) {
|
void HyprsunsetManager::setActiveAuto(bool activeAuto) {
|
||||||
if (activeAuto == m_activeAuto)
|
if (activeAuto == m_activeAuto) return;
|
||||||
return;
|
|
||||||
|
|
||||||
m_activeAuto = activeAuto;
|
m_activeAuto = activeAuto;
|
||||||
emit activeAutoChanged();
|
emit activeAutoChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HyprsunsetManager::setManualToggle(bool toggle) {
|
void HyprsunsetManager::setManualToggle(bool toggle) {
|
||||||
if (toggle == m_manualToggle)
|
if (toggle == m_manualToggle) return;
|
||||||
return;
|
|
||||||
|
|
||||||
m_manualToggle = toggle;
|
m_manualToggle = toggle;
|
||||||
emit manualToggleChanged();
|
emit manualToggleChanged();
|
||||||
@@ -69,8 +67,7 @@ void HyprsunsetManager::setManualToggle(bool toggle) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void HyprsunsetManager::setEndTime(const int& time) {
|
void HyprsunsetManager::setEndTime(const int& time) {
|
||||||
if (time == m_endTime)
|
if (time == m_endTime) return;
|
||||||
return;
|
|
||||||
|
|
||||||
m_endTime = time;
|
m_endTime = time;
|
||||||
emit endTimeChanged();
|
emit endTimeChanged();
|
||||||
@@ -78,8 +75,7 @@ void HyprsunsetManager::setEndTime(const int& time) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void HyprsunsetManager::setStartTime(const int& time) {
|
void HyprsunsetManager::setStartTime(const int& time) {
|
||||||
if (time == m_startTime)
|
if (time == m_startTime) return;
|
||||||
return;
|
|
||||||
|
|
||||||
m_startTime = time;
|
m_startTime = time;
|
||||||
emit startTimeChanged();
|
emit startTimeChanged();
|
||||||
@@ -87,8 +83,7 @@ void HyprsunsetManager::setStartTime(const int& time) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void HyprsunsetManager::setTemp(const int& temp) {
|
void HyprsunsetManager::setTemp(const int& temp) {
|
||||||
if (temp == m_temp)
|
if (temp == m_temp) return;
|
||||||
return;
|
|
||||||
|
|
||||||
m_temp = temp;
|
m_temp = temp;
|
||||||
emit tempChanged();
|
emit tempChanged();
|
||||||
@@ -104,8 +99,7 @@ void HyprsunsetManager::toggle() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void HyprsunsetManager::start() {
|
void HyprsunsetManager::start() {
|
||||||
if (m_enabled && m_initialized)
|
if (m_enabled && m_initialized) return;
|
||||||
return;
|
|
||||||
|
|
||||||
m_initialized = true;
|
m_initialized = true;
|
||||||
m_enabled = true;
|
m_enabled = true;
|
||||||
@@ -113,13 +107,13 @@ void HyprsunsetManager::start() {
|
|||||||
emit enabledChanged();
|
emit enabledChanged();
|
||||||
|
|
||||||
m_process.setProgram("hyprctl");
|
m_process.setProgram("hyprctl");
|
||||||
m_process.setArguments({"hyprsunset", "temperature", QString::number(m_temp)});
|
m_process.setArguments(
|
||||||
|
{"hyprsunset", "temperature", QString::number(m_temp)});
|
||||||
m_process.startDetached();
|
m_process.startDetached();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HyprsunsetManager::end() {
|
void HyprsunsetManager::end() {
|
||||||
if (!m_enabled && m_initialized)
|
if (!m_enabled && m_initialized) return;
|
||||||
return;
|
|
||||||
|
|
||||||
m_initialized = true;
|
m_initialized = true;
|
||||||
m_enabled = false;
|
m_enabled = false;
|
||||||
@@ -132,12 +126,11 @@ void HyprsunsetManager::end() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void HyprsunsetManager::apply() {
|
void HyprsunsetManager::apply() {
|
||||||
if (m_manualToggle || !m_activeAuto || !m_startAllowed)
|
if (m_manualToggle || !m_activeAuto || !m_startAllowed) return;
|
||||||
return;
|
|
||||||
|
|
||||||
const auto current = QTime::currentTime();
|
const auto current = QTime::currentTime();
|
||||||
const auto currentMin = current.hour() * 60 + current.minute();
|
const auto currentMin = current.hour() * 60 + current.minute();
|
||||||
bool isDarkTime;
|
bool isDarkTime = false;
|
||||||
|
|
||||||
if (m_startTime <= m_endTime) {
|
if (m_startTime <= m_endTime) {
|
||||||
isDarkTime = (currentMin >= m_startTime && currentMin < m_endTime);
|
isDarkTime = (currentMin >= m_startTime && currentMin < m_endTime);
|
||||||
@@ -152,4 +145,4 @@ void HyprsunsetManager::apply() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
}; // namespace ZShell::services
|
||||||
|
|||||||
@@ -13,11 +13,16 @@ class HyprsunsetManager : public QObject {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
Q_PROPERTY(bool enabled READ enabled NOTIFY enabledChanged)
|
Q_PROPERTY(bool enabled READ enabled NOTIFY enabledChanged)
|
||||||
Q_PROPERTY(int startTime READ startTime WRITE setStartTime NOTIFY startTimeChanged)
|
Q_PROPERTY(
|
||||||
|
int startTime READ startTime WRITE setStartTime NOTIFY startTimeChanged)
|
||||||
Q_PROPERTY(int endTime READ endTime WRITE setEndTime NOTIFY endTimeChanged)
|
Q_PROPERTY(int endTime READ endTime WRITE setEndTime NOTIFY endTimeChanged)
|
||||||
Q_PROPERTY(int temp READ temp WRITE setTemp NOTIFY tempChanged)
|
Q_PROPERTY(int temp READ temp WRITE setTemp NOTIFY tempChanged)
|
||||||
Q_PROPERTY(bool activeAuto READ activeAuto WRITE setActiveAuto NOTIFY activeAutoChanged)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(bool manualToggle READ manualToggle WRITE setManualToggle NOTIFY manualToggleChanged)
|
bool activeAuto READ activeAuto WRITE setActiveAuto NOTIFY
|
||||||
|
activeAutoChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
bool manualToggle READ manualToggle WRITE setManualToggle NOTIFY
|
||||||
|
manualToggleChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit HyprsunsetManager(QObject* parent = nullptr);
|
explicit HyprsunsetManager(QObject* parent = nullptr);
|
||||||
@@ -63,4 +68,4 @@ void start();
|
|||||||
void end();
|
void end();
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
}; // namespace ZShell::services
|
||||||
|
|||||||
@@ -5,9 +5,7 @@
|
|||||||
|
|
||||||
namespace ZShell::services {
|
namespace ZShell::services {
|
||||||
|
|
||||||
Memory::Memory(QObject* parent)
|
Memory::Memory(QObject* parent) : TickingService(parent) {}
|
||||||
: TickingService(parent) {
|
|
||||||
}
|
|
||||||
|
|
||||||
qreal Memory::used() const {
|
qreal Memory::used() const {
|
||||||
return m_used;
|
return m_used;
|
||||||
@@ -29,8 +27,10 @@ void Memory::tick() {
|
|||||||
const QByteArray data = f.readAll();
|
const QByteArray data = f.readAll();
|
||||||
f.close();
|
f.close();
|
||||||
|
|
||||||
static const QRegularExpression reTotal(QStringLiteral("MemTotal: *(\\d+)"));
|
static const QRegularExpression reTotal(
|
||||||
static const QRegularExpression reAvail(QStringLiteral("MemAvailable: *(\\d+)"));
|
QStringLiteral("MemTotal: *(\\d+)"));
|
||||||
|
static const QRegularExpression reAvail(
|
||||||
|
QStringLiteral("MemAvailable: *(\\d+)"));
|
||||||
const QString text = QString::fromLatin1(data);
|
const QString text = QString::fromLatin1(data);
|
||||||
|
|
||||||
const auto totalMatch = reTotal.match(text);
|
const auto totalMatch = reTotal.match(text);
|
||||||
|
|||||||
@@ -31,8 +31,10 @@ void doInit() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::optional<double> readTempInput(const sensors_chip_name* chip, const sensors_feature* feat) {
|
[[nodiscard]] std::optional<double> readTempInput(
|
||||||
const sensors_subfeature* sf = sensors_get_subfeature(chip, feat, SENSORS_SUBFEATURE_TEMP_INPUT);
|
const sensors_chip_name* chip, const sensors_feature* feat) {
|
||||||
|
const sensors_subfeature* sf =
|
||||||
|
sensors_get_subfeature(chip, feat, SENSORS_SUBFEATURE_TEMP_INPUT);
|
||||||
if (!sf) {
|
if (!sf) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
@@ -43,7 +45,8 @@ void doInit() {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] QByteArray featureLabel(const sensors_chip_name* chip, const sensors_feature* feat) {
|
[[nodiscard]] QByteArray featureLabel(
|
||||||
|
const sensors_chip_name* chip, const sensors_feature* feat) {
|
||||||
char* raw = sensors_get_label(chip, feat);
|
char* raw = sensors_get_label(chip, feat);
|
||||||
if (!raw) {
|
if (!raw) {
|
||||||
return {};
|
return {};
|
||||||
@@ -59,7 +62,8 @@ bool labelEquals(const QByteArray& label, const char* literal) {
|
|||||||
|
|
||||||
bool labelStartsWith(const QByteArray& label, const char* prefix) {
|
bool labelStartsWith(const QByteArray& label, const char* prefix) {
|
||||||
const auto n = std::strlen(prefix);
|
const auto n = std::strlen(prefix);
|
||||||
return static_cast<size_t>(label.size()) >= n && std::memcmp(label.constData(), prefix, n) == 0;
|
return static_cast<size_t>(label.size()) >= n &&
|
||||||
|
std::memcmp(label.constData(), prefix, n) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
@@ -78,9 +82,11 @@ std::optional<double> cpuPackageTemp() {
|
|||||||
std::optional<double> fallback; // Tctl
|
std::optional<double> fallback; // Tctl
|
||||||
|
|
||||||
int chipNr = 0;
|
int chipNr = 0;
|
||||||
while (const sensors_chip_name* chip = sensors_get_detected_chips(nullptr, &chipNr)) {
|
while (const sensors_chip_name* chip =
|
||||||
|
sensors_get_detected_chips(nullptr, &chipNr)) {
|
||||||
int featNr = 0;
|
int featNr = 0;
|
||||||
while (const sensors_feature* feat = sensors_get_features(chip, &featNr)) {
|
while (const sensors_feature* feat =
|
||||||
|
sensors_get_features(chip, &featNr)) {
|
||||||
if (feat->type != SENSORS_FEATURE_TEMP) {
|
if (feat->type != SENSORS_FEATURE_TEMP) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -89,7 +95,8 @@ std::optional<double> cpuPackageTemp() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (labelStartsWith(label, "Package id ") || labelEquals(label, "Tdie")) {
|
if (labelStartsWith(label, "Package id ") ||
|
||||||
|
labelEquals(label, "Tdie")) {
|
||||||
if (auto v = readTempInput(chip, feat)) {
|
if (auto v = readTempInput(chip, feat)) {
|
||||||
primary = v;
|
primary = v;
|
||||||
}
|
}
|
||||||
@@ -116,13 +123,15 @@ std::optional<double> gpuPciAverageTemp() {
|
|||||||
int countFallback = 0;
|
int countFallback = 0;
|
||||||
|
|
||||||
int chipNr = 0;
|
int chipNr = 0;
|
||||||
while (const sensors_chip_name* chip = sensors_get_detected_chips(nullptr, &chipNr)) {
|
while (const sensors_chip_name* chip =
|
||||||
|
sensors_get_detected_chips(nullptr, &chipNr)) {
|
||||||
if (chip->bus.type != SENSORS_BUS_TYPE_PCI) {
|
if (chip->bus.type != SENSORS_BUS_TYPE_PCI) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int featNr = 0;
|
int featNr = 0;
|
||||||
while (const sensors_feature* feat = sensors_get_features(chip, &featNr)) {
|
while (const sensors_feature* feat =
|
||||||
|
sensors_get_features(chip, &featNr)) {
|
||||||
if (feat->type != SENSORS_FEATURE_TEMP) {
|
if (feat->type != SENSORS_FEATURE_TEMP) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -131,10 +140,14 @@ std::optional<double> gpuPciAverageTemp() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool tempIndexed = labelStartsWith(label, "temp") && label.size() > 4 &&
|
const bool tempIndexed =
|
||||||
|
labelStartsWith(label, "temp") && label.size() > 4 &&
|
||||||
std::isdigit(static_cast<unsigned char>(label[4]));
|
std::isdigit(static_cast<unsigned char>(label[4]));
|
||||||
const bool isPrimary = tempIndexed || labelEquals(label, "GPU core") || labelEquals(label, "edge");
|
const bool isPrimary = tempIndexed ||
|
||||||
const bool isFallback = labelEquals(label, "junction") || labelEquals(label, "mem");
|
labelEquals(label, "GPU core") ||
|
||||||
|
labelEquals(label, "edge");
|
||||||
|
const bool isFallback = labelEquals(label, "junction") ||
|
||||||
|
labelEquals(label, "mem");
|
||||||
|
|
||||||
if (!isPrimary && !isFallback) {
|
if (!isPrimary && !isFallback) {
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -5,9 +5,7 @@
|
|||||||
|
|
||||||
namespace ZShell::services {
|
namespace ZShell::services {
|
||||||
|
|
||||||
Service::Service(QObject* parent)
|
Service::Service(QObject* parent) : QObject(parent) {}
|
||||||
: QObject(parent) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void Service::ref(QObject* sender) {
|
void Service::ref(QObject* sender) {
|
||||||
if (m_refs.isEmpty()) {
|
if (m_refs.isEmpty()) {
|
||||||
|
|||||||
@@ -5,8 +5,7 @@
|
|||||||
namespace ZShell::services {
|
namespace ZShell::services {
|
||||||
|
|
||||||
ServiceRef::ServiceRef(Service* service, QObject* parent)
|
ServiceRef::ServiceRef(Service* service, QObject* parent)
|
||||||
: QObject(parent)
|
: QObject(parent), m_service(service) {
|
||||||
, m_service(service) {
|
|
||||||
if (m_service) {
|
if (m_service) {
|
||||||
m_service->ref(this);
|
m_service->ref(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ class ServiceRef : public QObject {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
|
|
||||||
Q_PROPERTY(ZShell::services::Service* service READ service WRITE setService NOTIFY serviceChanged)
|
Q_PROPERTY(
|
||||||
|
ZShell::services::Service* service READ service WRITE setService NOTIFY
|
||||||
|
serviceChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ServiceRef(Service* service = nullptr, QObject* parent = nullptr);
|
explicit ServiceRef(Service* service = nullptr, QObject* parent = nullptr);
|
||||||
|
|||||||
@@ -24,12 +24,14 @@ struct Accum {
|
|||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]] QString sysfsRealPath(uint major, uint minor) {
|
[[nodiscard]] QString sysfsRealPath(uint major, uint minor) {
|
||||||
const QString link = QStringLiteral("/sys/dev/block/%1:%2").arg(major).arg(minor);
|
const QString link =
|
||||||
|
QStringLiteral("/sys/dev/block/%1:%2").arg(major).arg(minor);
|
||||||
const QString resolved = QFileInfo(link).canonicalFilePath();
|
const QString resolved = QFileInfo(link).canonicalFilePath();
|
||||||
return resolved;
|
return resolved;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool readDevtFromSysfs(const QString& sysfsBlockDir, uint& major, uint& minor) {
|
[[nodiscard]] bool readDevtFromSysfs(
|
||||||
|
const QString& sysfsBlockDir, uint& major, uint& minor) {
|
||||||
QFile f(sysfsBlockDir + QStringLiteral("/dev"));
|
QFile f(sysfsBlockDir + QStringLiteral("/dev"));
|
||||||
if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -50,6 +52,7 @@ struct Accum {
|
|||||||
|
|
||||||
QStringList resolveByDevt(uint major, uint minor, int depth = 0);
|
QStringList resolveByDevt(uint major, uint minor, int depth = 0);
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(misc-no-recursion)
|
||||||
QStringList resolveAtNode(const QString& node, int depth) {
|
QStringList resolveAtNode(const QString& node, int depth) {
|
||||||
if (node.isEmpty() || depth > 8) {
|
if (node.isEmpty() || depth > 8) {
|
||||||
return {};
|
return {};
|
||||||
@@ -67,13 +70,15 @@ QStringList resolveAtNode(const QString& node, int depth) {
|
|||||||
|
|
||||||
const QDir slavesDir(node + QStringLiteral("/slaves"));
|
const QDir slavesDir(node + QStringLiteral("/slaves"));
|
||||||
if (slavesDir.exists()) {
|
if (slavesDir.exists()) {
|
||||||
const QStringList slaves = slavesDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
|
const QStringList slaves =
|
||||||
|
slavesDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||||
if (!slaves.isEmpty()) {
|
if (!slaves.isEmpty()) {
|
||||||
QStringList out;
|
QStringList out;
|
||||||
for (const QString& slave : slaves) {
|
for (const QString& slave : slaves) {
|
||||||
uint sm = 0;
|
uint sm = 0;
|
||||||
uint sn = 0;
|
uint sn = 0;
|
||||||
const QString slaveDir = QStringLiteral("/sys/class/block/") + slave;
|
const QString slaveDir =
|
||||||
|
QStringLiteral("/sys/class/block/") + slave;
|
||||||
if (!readDevtFromSysfs(slaveDir, sm, sn)) {
|
if (!readDevtFromSysfs(slaveDir, sm, sn)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -91,15 +96,14 @@ QStringList resolveAtNode(const QString& node, int depth) {
|
|||||||
return {nodeInfo.fileName()};
|
return {nodeInfo.fileName()};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(misc-no-recursion)
|
||||||
QStringList resolveByDevt(uint major, uint minor, int depth) {
|
QStringList resolveByDevt(uint major, uint minor, int depth) {
|
||||||
return resolveAtNode(sysfsRealPath(major, minor), depth);
|
return resolveAtNode(sysfsRealPath(major, minor), depth);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
Storage::Storage(QObject* parent)
|
Storage::Storage(QObject* parent) : TickingService(parent) {}
|
||||||
: TickingService(parent) {
|
|
||||||
}
|
|
||||||
|
|
||||||
qreal Storage::percentage() const {
|
qreal Storage::percentage() const {
|
||||||
qreal totalUsed = 0.0;
|
qreal totalUsed = 0.0;
|
||||||
@@ -124,7 +128,8 @@ bool Storage::sameOrder(const QList<DiskInfo*>& a, const QList<DiskInfo*>& b) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
QQmlListProperty<DiskInfo> Storage::disksProp() {
|
QQmlListProperty<DiskInfo> Storage::disksProp() {
|
||||||
return QQmlListProperty<DiskInfo>(this, nullptr, &Storage::disksCount, &Storage::disksAt);
|
return QQmlListProperty<DiskInfo>(
|
||||||
|
this, nullptr, &Storage::disksCount, &Storage::disksAt);
|
||||||
}
|
}
|
||||||
|
|
||||||
qsizetype Storage::disksCount(QQmlListProperty<DiskInfo>* prop) {
|
qsizetype Storage::disksCount(QQmlListProperty<DiskInfo>* prop) {
|
||||||
@@ -157,30 +162,11 @@ DiskInfo* Storage::primaryDisk() const {
|
|||||||
|
|
||||||
bool Storage::isPseudoFs(QByteArrayView fsType) {
|
bool Storage::isPseudoFs(QByteArrayView fsType) {
|
||||||
static constexpr const char* kPseudo[] = {
|
static constexpr const char* kPseudo[] = {
|
||||||
"tmpfs",
|
"tmpfs", "devtmpfs", "proc", "sysfs", "cgroup",
|
||||||
"devtmpfs",
|
"cgroup2", "overlay", "squashfs", "devpts", "mqueue",
|
||||||
"proc",
|
"ramfs", "rpc_pipefs", "autofs", "configfs", "debugfs",
|
||||||
"sysfs",
|
"tracefs", "securityfs", "pstore", "bpf", "binfmt_misc",
|
||||||
"cgroup",
|
"hugetlbfs", "fusectl", "efivarfs", "selinuxfs",
|
||||||
"cgroup2",
|
|
||||||
"overlay",
|
|
||||||
"squashfs",
|
|
||||||
"devpts",
|
|
||||||
"mqueue",
|
|
||||||
"ramfs",
|
|
||||||
"rpc_pipefs",
|
|
||||||
"autofs",
|
|
||||||
"configfs",
|
|
||||||
"debugfs",
|
|
||||||
"tracefs",
|
|
||||||
"securityfs",
|
|
||||||
"pstore",
|
|
||||||
"bpf",
|
|
||||||
"binfmt_misc",
|
|
||||||
"hugetlbfs",
|
|
||||||
"fusectl",
|
|
||||||
"efivarfs",
|
|
||||||
"selinuxfs",
|
|
||||||
};
|
};
|
||||||
for (const char* p : kPseudo) {
|
for (const char* p : kPseudo) {
|
||||||
if (fsType == QByteArrayView(p)) {
|
if (fsType == QByteArrayView(p)) {
|
||||||
@@ -232,7 +218,8 @@ void Storage::tick() {
|
|||||||
const QByteArray device = v.device();
|
const QByteArray device = v.device();
|
||||||
const auto totalBytes = static_cast<quint64>(v.bytesTotal());
|
const auto totalBytes = static_cast<quint64>(v.bytesTotal());
|
||||||
const auto availBytes = static_cast<quint64>(v.bytesAvailable());
|
const auto availBytes = static_cast<quint64>(v.bytesAvailable());
|
||||||
const quint64 usedBytes = totalBytes > availBytes ? totalBytes - availBytes : 0;
|
const quint64 usedBytes =
|
||||||
|
totalBytes > availBytes ? totalBytes - availBytes : 0;
|
||||||
const bool isRoot = v.rootPath() == QStringLiteral("/");
|
const bool isRoot = v.rootPath() == QStringLiteral("/");
|
||||||
|
|
||||||
DeviceEntry& e = byDevice[device];
|
DeviceEntry& e = byDevice[device];
|
||||||
@@ -244,7 +231,8 @@ void Storage::tick() {
|
|||||||
|
|
||||||
for (auto it = byDevice.constBegin(); it != byDevice.constEnd(); ++it) {
|
for (auto it = byDevice.constBegin(); it != byDevice.constEnd(); ++it) {
|
||||||
const DeviceEntry& e = it.value();
|
const DeviceEntry& e = it.value();
|
||||||
const QStringList disks = resolveToPhysicalDisks(QString::fromLocal8Bit(e.device));
|
const QStringList disks =
|
||||||
|
resolveToPhysicalDisks(QString::fromLocal8Bit(e.device));
|
||||||
if (disks.isEmpty()) {
|
if (disks.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -269,14 +257,23 @@ void Storage::tick() {
|
|||||||
next.reserve(byDisk.size());
|
next.reserve(byDisk.size());
|
||||||
for (auto it = byDisk.constBegin(); it != byDisk.constEnd(); ++it) {
|
for (auto it = byDisk.constBegin(); it != byDisk.constEnd(); ++it) {
|
||||||
if (DiskInfo* survivor = existing.take(it.key())) {
|
if (DiskInfo* survivor = existing.take(it.key())) {
|
||||||
survivor->update(it.value().usedBytes, it.value().totalBytes, it.value().hasRoot);
|
survivor->update(
|
||||||
|
it.value().usedBytes,
|
||||||
|
it.value().totalBytes,
|
||||||
|
it.value().hasRoot);
|
||||||
next.append(survivor);
|
next.append(survivor);
|
||||||
} else {
|
} else {
|
||||||
next.append(new DiskInfo(it.key(), it.value().usedBytes, it.value().totalBytes, it.value().hasRoot, this));
|
next.append(new DiskInfo(
|
||||||
|
it.key(),
|
||||||
|
it.value().usedBytes,
|
||||||
|
it.value().totalBytes,
|
||||||
|
it.value().hasRoot,
|
||||||
|
this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::sort(next.begin(), next.end(), [](const DiskInfo* a, const DiskInfo* b) {
|
std::sort(
|
||||||
|
next.begin(), next.end(), [](const DiskInfo* a, const DiskInfo* b) {
|
||||||
if (a->hasRoot() != b->hasRoot()) {
|
if (a->hasRoot() != b->hasRoot()) {
|
||||||
return a->hasRoot();
|
return a->hasRoot();
|
||||||
}
|
}
|
||||||
@@ -284,7 +281,8 @@ void Storage::tick() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
bool manualCleared = false;
|
bool manualCleared = false;
|
||||||
if (DiskInfo* m = m_manualPrimaryDisk.data(); m && existing.contains(m->mount())) {
|
if (DiskInfo* m = m_manualPrimaryDisk.data();
|
||||||
|
m && existing.contains(m->mount())) {
|
||||||
m_manualPrimaryDisk.clear();
|
m_manualPrimaryDisk.clear();
|
||||||
manualCleared = true;
|
manualCleared = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,10 +17,15 @@ QML_ELEMENT
|
|||||||
QML_SINGLETON
|
QML_SINGLETON
|
||||||
|
|
||||||
Q_PROPERTY(qreal percentage READ percentage NOTIFY percentageChanged)
|
Q_PROPERTY(qreal percentage READ percentage NOTIFY percentageChanged)
|
||||||
Q_PROPERTY(QQmlListProperty<ZShell::services::DiskInfo> disks READ disksProp NOTIFY disksChanged)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(ZShell::services::DiskInfo* manualPrimaryDisk READ manualPrimaryDisk WRITE setManualPrimaryDisk NOTIFY
|
QQmlListProperty<ZShell::services::DiskInfo> disks READ disksProp NOTIFY
|
||||||
manualPrimaryDiskChanged)
|
disksChanged)
|
||||||
Q_PROPERTY(ZShell::services::DiskInfo* primaryDisk READ primaryDisk NOTIFY primaryDiskChanged)
|
Q_PROPERTY(
|
||||||
|
ZShell::services::DiskInfo* manualPrimaryDisk READ manualPrimaryDisk
|
||||||
|
WRITE setManualPrimaryDisk NOTIFY manualPrimaryDiskChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
ZShell::services::DiskInfo* primaryDisk READ primaryDisk NOTIFY
|
||||||
|
primaryDiskChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit Storage(QObject* parent = nullptr);
|
explicit Storage(QObject* parent = nullptr);
|
||||||
@@ -41,9 +46,11 @@ protected:
|
|||||||
void tick() override;
|
void tick() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] static QStringList resolveToPhysicalDisks(const QString& devicePath);
|
[[nodiscard]] static QStringList resolveToPhysicalDisks(
|
||||||
|
const QString& devicePath);
|
||||||
[[nodiscard]] static bool isPseudoFs(QByteArrayView fsType);
|
[[nodiscard]] static bool isPseudoFs(QByteArrayView fsType);
|
||||||
[[nodiscard]] static bool sameOrder(const QList<DiskInfo*>& a, const QList<DiskInfo*>& b);
|
[[nodiscard]] static bool sameOrder(
|
||||||
|
const QList<DiskInfo*>& a, const QList<DiskInfo*>& b);
|
||||||
|
|
||||||
static qsizetype disksCount(QQmlListProperty<DiskInfo>* prop);
|
static qsizetype disksCount(QQmlListProperty<DiskInfo>* prop);
|
||||||
static DiskInfo* disksAt(QQmlListProperty<DiskInfo>* prop, qsizetype i);
|
static DiskInfo* disksAt(QQmlListProperty<DiskInfo>* prop, qsizetype i);
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ namespace ZShell::services {
|
|||||||
class TickingService : public Service {
|
class TickingService : public Service {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
Q_PROPERTY(int updateInterval READ updateInterval NOTIFY updateIntervalChanged)
|
Q_PROPERTY(
|
||||||
|
int updateInterval READ updateInterval NOTIFY updateIntervalChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit TickingService(QObject* parent = nullptr);
|
explicit TickingService(QObject* parent = nullptr);
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ QML_ELEMENT
|
|||||||
QML_SINGLETON
|
QML_SINGLETON
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Q_INVOKABLE [[nodiscard]] FormatResult formatKib(qreal kib, qreal total) const;
|
Q_INVOKABLE [[nodiscard]] FormatResult formatKib(
|
||||||
|
qreal kib, qreal total) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ZShell::services::usagefmt
|
} // namespace ZShell::services::usagefmt
|
||||||
|
|||||||
+22
-10
@@ -7,17 +7,22 @@
|
|||||||
namespace ZShell {
|
namespace ZShell {
|
||||||
|
|
||||||
AppEntry::AppEntry(QObject* entry, unsigned int frequency, QObject* parent)
|
AppEntry::AppEntry(QObject* entry, unsigned int frequency, QObject* parent)
|
||||||
: QObject(parent)
|
: QObject(parent), m_entry(entry), m_frequency(frequency) {
|
||||||
, m_entry(entry)
|
|
||||||
, m_frequency(frequency) {
|
|
||||||
const auto mo = m_entry->metaObject();
|
const auto mo = m_entry->metaObject();
|
||||||
const auto tmo = metaObject();
|
const auto tmo = metaObject();
|
||||||
|
|
||||||
for (const auto& prop :
|
for (const auto& prop :
|
||||||
{ "name", "comment", "execString", "startupClass", "genericName", "categories", "keywords" }) {
|
{"name",
|
||||||
|
"comment",
|
||||||
|
"execString",
|
||||||
|
"startupClass",
|
||||||
|
"genericName",
|
||||||
|
"categories",
|
||||||
|
"keywords"}) {
|
||||||
const auto metaProp = mo->property(mo->indexOfProperty(prop));
|
const auto metaProp = mo->property(mo->indexOfProperty(prop));
|
||||||
const auto thisMetaProp = tmo->property(tmo->indexOfProperty(prop));
|
const auto thisMetaProp = tmo->property(tmo->indexOfProperty(prop));
|
||||||
QObject::connect(m_entry, metaProp.notifySignal(), this, thisMetaProp.notifySignal());
|
QObject::connect(
|
||||||
|
m_entry, metaProp.notifySignal(), this, thisMetaProp.notifySignal());
|
||||||
}
|
}
|
||||||
|
|
||||||
QObject::connect(m_entry, &QObject::destroyed, this, [this]() {
|
QObject::connect(m_entry, &QObject::destroyed, this, [this]() {
|
||||||
@@ -115,7 +120,9 @@ AppDb::AppDb(QObject* parent)
|
|||||||
db.open();
|
db.open();
|
||||||
|
|
||||||
QSqlQuery query(db);
|
QSqlQuery query(db);
|
||||||
query.exec("CREATE TABLE IF NOT EXISTS frequencies (id TEXT PRIMARY KEY, frequency INTEGER)");
|
query.exec(
|
||||||
|
"CREATE TABLE IF NOT EXISTS frequencies (id TEXT PRIMARY KEY, "
|
||||||
|
"frequency INTEGER)");
|
||||||
}
|
}
|
||||||
|
|
||||||
QString AppDb::uuid() const {
|
QString AppDb::uuid() const {
|
||||||
@@ -142,7 +149,9 @@ void AppDb::setPath(const QString& path) {
|
|||||||
db.open();
|
db.open();
|
||||||
|
|
||||||
QSqlQuery query(db);
|
QSqlQuery query(db);
|
||||||
query.exec("CREATE TABLE IF NOT EXISTS frequencies (id TEXT PRIMARY KEY, frequency INTEGER)");
|
query.exec(
|
||||||
|
"CREATE TABLE IF NOT EXISTS frequencies (id TEXT PRIMARY KEY, "
|
||||||
|
"frequency INTEGER)");
|
||||||
|
|
||||||
updateAppFrequencies();
|
updateAppFrequencies();
|
||||||
}
|
}
|
||||||
@@ -170,7 +179,8 @@ void AppDb::incrementFrequency(const QString& id) {
|
|||||||
auto db = QSqlDatabase::database(m_uuid);
|
auto db = QSqlDatabase::database(m_uuid);
|
||||||
QSqlQuery query(db);
|
QSqlQuery query(db);
|
||||||
|
|
||||||
query.prepare("INSERT INTO frequencies (id, frequency) "
|
query.prepare(
|
||||||
|
"INSERT INTO frequencies (id, frequency) "
|
||||||
"VALUES (:id, 1) "
|
"VALUES (:id, 1) "
|
||||||
"ON CONFLICT (id) DO UPDATE SET frequency = frequency + 1");
|
"ON CONFLICT (id) DO UPDATE SET frequency = frequency + 1");
|
||||||
query.bindValue(":id", id);
|
query.bindValue(":id", id);
|
||||||
@@ -186,13 +196,15 @@ void AppDb::incrementFrequency(const QString& id) {
|
|||||||
emit appsChanged();
|
emit appsChanged();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "AppDb::incrementFrequency: could not find app with id" << id;
|
qWarning() << "AppDb::incrementFrequency: could not find app with id"
|
||||||
|
<< id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<AppEntry*>& AppDb::getSortedApps() const {
|
QList<AppEntry*>& AppDb::getSortedApps() const {
|
||||||
m_sortedApps = m_apps.values();
|
m_sortedApps = m_apps.values();
|
||||||
std::sort(m_sortedApps.begin(), m_sortedApps.end(), [](AppEntry* a, AppEntry* b) {
|
std::sort(
|
||||||
|
m_sortedApps.begin(), m_sortedApps.end(), [](AppEntry* a, AppEntry* b) {
|
||||||
if (a->frequency() != b->frequency()) {
|
if (a->frequency() != b->frequency()) {
|
||||||
return a->frequency() > b->frequency();
|
return a->frequency() > b->frequency();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ Q_PROPERTY(QString categories READ categories NOTIFY categoriesChanged)
|
|||||||
Q_PROPERTY(QString keywords READ keywords NOTIFY keywordsChanged)
|
Q_PROPERTY(QString keywords READ keywords NOTIFY keywordsChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit AppEntry(QObject* entry, quint32 frequency, QObject* parent = nullptr);
|
explicit AppEntry(
|
||||||
|
QObject* entry, quint32 frequency, QObject* parent = nullptr);
|
||||||
|
|
||||||
[[nodiscard]] QObject* entry() const;
|
[[nodiscard]] QObject* entry() const;
|
||||||
|
|
||||||
@@ -65,8 +66,11 @@ QML_ELEMENT
|
|||||||
|
|
||||||
Q_PROPERTY(QString uuid READ uuid CONSTANT)
|
Q_PROPERTY(QString uuid READ uuid CONSTANT)
|
||||||
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged REQUIRED)
|
Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged REQUIRED)
|
||||||
Q_PROPERTY(QObjectList entries READ entries WRITE setEntries NOTIFY entriesChanged REQUIRED)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(QQmlListProperty<ZShell::AppEntry> apps READ apps NOTIFY appsChanged)
|
QObjectList entries READ entries WRITE setEntries NOTIFY entriesChanged
|
||||||
|
REQUIRED)
|
||||||
|
Q_PROPERTY(
|
||||||
|
QQmlListProperty<ZShell::AppEntry> apps READ apps NOTIFY appsChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit AppDb(QObject* parent = nullptr);
|
explicit AppDb(QObject* parent = nullptr);
|
||||||
|
|||||||
@@ -16,7 +16,11 @@ ImageAnalyser::ImageAnalyser(QObject* parent)
|
|||||||
, m_rescaleSize(128)
|
, m_rescaleSize(128)
|
||||||
, m_dominantColour(0, 0, 0)
|
, m_dominantColour(0, 0, 0)
|
||||||
, m_luminance(0) {
|
, m_luminance(0) {
|
||||||
QObject::connect(m_futureWatcher, &QFutureWatcher<AnalyseResult>::finished, this, [this]() {
|
QObject::connect(
|
||||||
|
m_futureWatcher,
|
||||||
|
&QFutureWatcher<AnalyseResult>::finished,
|
||||||
|
this,
|
||||||
|
[this]() {
|
||||||
if (!m_futureWatcher->future().isResultReadyAt(0)) {
|
if (!m_futureWatcher->future().isResultReadyAt(0)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -101,23 +105,40 @@ void ImageAnalyser::requestUpdate() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_sourceItem || (m_sourceItem->window() && m_sourceItem->window()->isVisible() && m_sourceItem->width() > 0 &&
|
if (!m_sourceItem ||
|
||||||
m_sourceItem->height() > 0)) {
|
(m_sourceItem->window() && m_sourceItem->window()->isVisible() &&
|
||||||
|
m_sourceItem->width() > 0 && m_sourceItem->height() > 0)) {
|
||||||
update();
|
update();
|
||||||
} else if (m_sourceItem) {
|
} else if (m_sourceItem) {
|
||||||
if (!m_sourceItem->window()) {
|
if (!m_sourceItem->window()) {
|
||||||
QObject::connect(m_sourceItem, &QQuickItem::windowChanged, this, &ImageAnalyser::requestUpdate,
|
QObject::connect(
|
||||||
|
m_sourceItem,
|
||||||
|
&QQuickItem::windowChanged,
|
||||||
|
this,
|
||||||
|
&ImageAnalyser::requestUpdate,
|
||||||
Qt::SingleShotConnection);
|
Qt::SingleShotConnection);
|
||||||
} else if (!m_sourceItem->window()->isVisible()) {
|
} else if (!m_sourceItem->window()->isVisible()) {
|
||||||
QObject::connect(m_sourceItem->window(), &QQuickWindow::visibleChanged, this, &ImageAnalyser::requestUpdate,
|
QObject::connect(
|
||||||
|
m_sourceItem->window(),
|
||||||
|
&QQuickWindow::visibleChanged,
|
||||||
|
this,
|
||||||
|
&ImageAnalyser::requestUpdate,
|
||||||
Qt::SingleShotConnection);
|
Qt::SingleShotConnection);
|
||||||
}
|
}
|
||||||
if (m_sourceItem->width() <= 0) {
|
if (m_sourceItem->width() <= 0) {
|
||||||
QObject::connect(
|
QObject::connect(
|
||||||
m_sourceItem, &QQuickItem::widthChanged, this, &ImageAnalyser::requestUpdate, Qt::SingleShotConnection);
|
m_sourceItem,
|
||||||
|
&QQuickItem::widthChanged,
|
||||||
|
this,
|
||||||
|
&ImageAnalyser::requestUpdate,
|
||||||
|
Qt::SingleShotConnection);
|
||||||
}
|
}
|
||||||
if (m_sourceItem->height() <= 0) {
|
if (m_sourceItem->height() <= 0) {
|
||||||
QObject::connect(m_sourceItem, &QQuickItem::heightChanged, this, &ImageAnalyser::requestUpdate,
|
QObject::connect(
|
||||||
|
m_sourceItem,
|
||||||
|
&QQuickItem::heightChanged,
|
||||||
|
this,
|
||||||
|
&ImageAnalyser::requestUpdate,
|
||||||
Qt::SingleShotConnection);
|
Qt::SingleShotConnection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,19 +154,30 @@ void ImageAnalyser::update() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (m_sourceItem) {
|
if (m_sourceItem) {
|
||||||
const QSharedPointer<const QQuickItemGrabResult> grabResult = m_sourceItem->grabToImage();
|
const QSharedPointer<const QQuickItemGrabResult> grabResult =
|
||||||
QObject::connect(grabResult.data(), &QQuickItemGrabResult::ready, this, [grabResult, this]() {
|
m_sourceItem->grabToImage();
|
||||||
m_futureWatcher->setFuture(QtConcurrent::run(&ImageAnalyser::analyse, grabResult->image(), m_rescaleSize));
|
QObject::connect(
|
||||||
|
grabResult.data(),
|
||||||
|
&QQuickItemGrabResult::ready,
|
||||||
|
this,
|
||||||
|
[grabResult, this]() {
|
||||||
|
m_futureWatcher->setFuture(
|
||||||
|
QtConcurrent::run(
|
||||||
|
&ImageAnalyser::analyse,
|
||||||
|
grabResult->image(),
|
||||||
|
m_rescaleSize));
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
m_futureWatcher->setFuture(QtConcurrent::run([=, this](QPromise<AnalyseResult>& promise) {
|
m_futureWatcher->setFuture(
|
||||||
|
QtConcurrent::run([=, this](QPromise<AnalyseResult>& promise) {
|
||||||
const QImage image(m_source);
|
const QImage image(m_source);
|
||||||
analyse(promise, image, m_rescaleSize);
|
analyse(promise, image, m_rescaleSize);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageAnalyser::analyse(QPromise<AnalyseResult>& promise, const QImage& image, int rescaleSize) {
|
void ImageAnalyser::analyse(
|
||||||
|
QPromise<AnalyseResult>& promise, const QImage& image, int rescaleSize) {
|
||||||
if (image.isNull()) {
|
if (image.isNull()) {
|
||||||
qWarning() << "ImageAnalyser::analyse: image is null";
|
qWarning() << "ImageAnalyser::analyse: image is null";
|
||||||
return;
|
return;
|
||||||
@@ -153,8 +185,13 @@ void ImageAnalyser::analyse(QPromise<AnalyseResult>& promise, const QImage& imag
|
|||||||
|
|
||||||
QImage img = image;
|
QImage img = image;
|
||||||
|
|
||||||
if (rescaleSize > 0 && (img.width() > rescaleSize || img.height() > rescaleSize)) {
|
if (rescaleSize > 0 &&
|
||||||
img = img.scaled(rescaleSize, rescaleSize, Qt::KeepAspectRatio, Qt::FastTransformation);
|
(img.width() > rescaleSize || img.height() > rescaleSize)) {
|
||||||
|
img = img.scaled(
|
||||||
|
rescaleSize,
|
||||||
|
rescaleSize,
|
||||||
|
Qt::KeepAspectRatio,
|
||||||
|
Qt::FastTransformation);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (promise.isCanceled()) {
|
if (promise.isCanceled()) {
|
||||||
@@ -199,7 +236,8 @@ void ImageAnalyser::analyse(QPromise<AnalyseResult>& promise, const QImage& imag
|
|||||||
const qreal r = pixel[0] / 255.0;
|
const qreal r = pixel[0] / 255.0;
|
||||||
const qreal g = pixel[1] / 255.0;
|
const qreal g = pixel[1] / 255.0;
|
||||||
const qreal b = pixel[2] / 255.0;
|
const qreal b = pixel[2] / 255.0;
|
||||||
totalLuminance += std::sqrt(0.299 * r * r + 0.587 * g * g + 0.114 * b * b);
|
totalLuminance +=
|
||||||
|
std::sqrt(0.299 * r * r + 0.587 * g * g + 0.114 * b * b);
|
||||||
++count;
|
++count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -217,7 +255,9 @@ void ImageAnalyser::analyse(QPromise<AnalyseResult>& promise, const QImage& imag
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
promise.addResult(qMakePair(QColor((0xFFu << 24) | dominantColour), count == 0 ? 0.0 : totalLuminance / count));
|
promise.addResult(qMakePair(
|
||||||
|
QColor((0xFFu << 24) | dominantColour),
|
||||||
|
count == 0 ? 0.0 : totalLuminance / count));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ZShell
|
} // namespace ZShell
|
||||||
|
|||||||
@@ -13,9 +13,14 @@ Q_OBJECT
|
|||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
|
|
||||||
Q_PROPERTY(QString source READ source WRITE setSource NOTIFY sourceChanged)
|
Q_PROPERTY(QString source READ source WRITE setSource NOTIFY sourceChanged)
|
||||||
Q_PROPERTY(QQuickItem* sourceItem READ sourceItem WRITE setSourceItem NOTIFY sourceItemChanged)
|
Q_PROPERTY(
|
||||||
Q_PROPERTY(int rescaleSize READ rescaleSize WRITE setRescaleSize NOTIFY rescaleSizeChanged)
|
QQuickItem* sourceItem READ sourceItem WRITE setSourceItem NOTIFY
|
||||||
Q_PROPERTY(QColor dominantColour READ dominantColour NOTIFY dominantColourChanged)
|
sourceItemChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
int rescaleSize READ rescaleSize WRITE setRescaleSize NOTIFY
|
||||||
|
rescaleSizeChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
QColor dominantColour READ dominantColour NOTIFY dominantColourChanged)
|
||||||
Q_PROPERTY(qreal luminance READ luminance NOTIFY luminanceChanged)
|
Q_PROPERTY(qreal luminance READ luminance NOTIFY luminanceChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -55,7 +60,8 @@ QColor m_dominantColour;
|
|||||||
qreal m_luminance;
|
qreal m_luminance;
|
||||||
|
|
||||||
void update();
|
void update();
|
||||||
static void analyse(QPromise<AnalyseResult>& promise, const QImage& image, int rescaleSize);
|
static void analyse(
|
||||||
|
QPromise<AnalyseResult>& promise, const QImage& image, int rescaleSize);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ZShell
|
} // namespace ZShell
|
||||||
|
|||||||
@@ -4,8 +4,7 @@
|
|||||||
|
|
||||||
namespace ZShell {
|
namespace ZShell {
|
||||||
|
|
||||||
Qalculator::Qalculator(QObject* parent)
|
Qalculator::Qalculator(QObject* parent) : QObject(parent) {
|
||||||
: QObject(parent) {
|
|
||||||
if (!CALCULATOR) {
|
if (!CALCULATOR) {
|
||||||
new Calculator();
|
new Calculator();
|
||||||
CALCULATOR->loadExchangeRates();
|
CALCULATOR->loadExchangeRates();
|
||||||
@@ -24,7 +23,11 @@ QString Qalculator::eval(const QString& expr, bool printExpr) const {
|
|||||||
|
|
||||||
std::string parsed;
|
std::string parsed;
|
||||||
std::string result = CALCULATOR->calculateAndPrint(
|
std::string result = CALCULATOR->calculateAndPrint(
|
||||||
CALCULATOR->unlocalizeExpression(expr.toStdString(), eo.parse_options), 100, eo, po, &parsed);
|
CALCULATOR->unlocalizeExpression(expr.toStdString(), eo.parse_options),
|
||||||
|
100,
|
||||||
|
eo,
|
||||||
|
po,
|
||||||
|
&parsed);
|
||||||
|
|
||||||
std::string error;
|
std::string error;
|
||||||
while (CALCULATOR->message()) {
|
while (CALCULATOR->message()) {
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ QML_SINGLETON
|
|||||||
public:
|
public:
|
||||||
explicit Qalculator(QObject* parent = nullptr);
|
explicit Qalculator(QObject* parent = nullptr);
|
||||||
|
|
||||||
Q_INVOKABLE [[nodiscard]] QString eval(const QString& expr, bool printExpr = true) const;
|
Q_INVOKABLE [[nodiscard]] QString eval(
|
||||||
|
const QString& expr, bool printExpr = true) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ZShell
|
} // namespace ZShell
|
||||||
|
|||||||
@@ -7,9 +7,7 @@
|
|||||||
namespace ZShell {
|
namespace ZShell {
|
||||||
|
|
||||||
Requests::Requests(QObject* parent)
|
Requests::Requests(QObject* parent)
|
||||||
: QObject(parent)
|
: QObject(parent), m_manager(new QNetworkAccessManager(this)) {}
|
||||||
, m_manager(new QNetworkAccessManager(this)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void Requests::get(const QUrl& url, QJSValue onSuccess, QJSValue onError) const {
|
void Requests::get(const QUrl& url, QJSValue onSuccess, QJSValue onError) const {
|
||||||
if (!onSuccess.isCallable()) {
|
if (!onSuccess.isCallable()) {
|
||||||
@@ -20,13 +18,15 @@ void Requests::get(const QUrl& url, QJSValue onSuccess, QJSValue onError) const
|
|||||||
QNetworkRequest request(url);
|
QNetworkRequest request(url);
|
||||||
auto reply = m_manager->get(request);
|
auto reply = m_manager->get(request);
|
||||||
|
|
||||||
QObject::connect(reply, &QNetworkReply::finished, [reply, onSuccess, onError]() {
|
QObject::connect(
|
||||||
|
reply, &QNetworkReply::finished, [reply, onSuccess, onError]() {
|
||||||
if (reply->error() == QNetworkReply::NoError) {
|
if (reply->error() == QNetworkReply::NoError) {
|
||||||
onSuccess.call({QString(reply->readAll())});
|
onSuccess.call({QString(reply->readAll())});
|
||||||
} else if (onError.isCallable()) {
|
} else if (onError.isCallable()) {
|
||||||
onError.call({reply->errorString()});
|
onError.call({reply->errorString()});
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "Requests::get: request failed with error" << reply->errorString();
|
qWarning() << "Requests::get: request failed with error"
|
||||||
|
<< reply->errorString();
|
||||||
}
|
}
|
||||||
|
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
|
|||||||
@@ -14,7 +14,10 @@ QML_SINGLETON
|
|||||||
public:
|
public:
|
||||||
explicit Requests(QObject* parent = nullptr);
|
explicit Requests(QObject* parent = nullptr);
|
||||||
|
|
||||||
Q_INVOKABLE void get(const QUrl& url, QJSValue callback, QJSValue onError = QJSValue()) const;
|
Q_INVOKABLE void get(
|
||||||
|
const QUrl& url,
|
||||||
|
QJSValue callback,
|
||||||
|
QJSValue onError = QJSValue()) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QNetworkAccessManager* m_manager;
|
QNetworkAccessManager* m_manager;
|
||||||
|
|||||||
@@ -6,7 +6,13 @@
|
|||||||
|
|
||||||
namespace ZShell {
|
namespace ZShell {
|
||||||
|
|
||||||
Toast::Toast(const QString& title, const QString& message, const QString& icon, Type type, int timeout, QObject* parent)
|
Toast::Toast(
|
||||||
|
const QString& title,
|
||||||
|
const QString& message,
|
||||||
|
const QString& icon,
|
||||||
|
Type type,
|
||||||
|
int timeout,
|
||||||
|
QObject* parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, m_closed(false)
|
, m_closed(false)
|
||||||
, m_title(title)
|
, m_title(title)
|
||||||
@@ -94,15 +100,18 @@ void Toast::unlock(QObject* sender) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Toaster::Toaster(QObject* parent)
|
Toaster::Toaster(QObject* parent) : QObject(parent) {}
|
||||||
: QObject(parent) {
|
|
||||||
}
|
|
||||||
|
|
||||||
QQmlListProperty<Toast> Toaster::toasts() {
|
QQmlListProperty<Toast> Toaster::toasts() {
|
||||||
return QQmlListProperty<Toast>(this, &m_toasts);
|
return QQmlListProperty<Toast>(this, &m_toasts);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Toaster::toast(const QString& title, const QString& message, const QString& icon, Toast::Type type, int timeout) {
|
void Toaster::toast(
|
||||||
|
const QString& title,
|
||||||
|
const QString& message,
|
||||||
|
const QString& icon,
|
||||||
|
Toast::Type type,
|
||||||
|
int timeout) {
|
||||||
auto* toast = new Toast(title, message, icon, type, timeout, this);
|
auto* toast = new Toast(title, message, icon, type, timeout, this);
|
||||||
QObject::connect(toast, &Toast::finishedClose, this, [toast, this]() {
|
QObject::connect(toast, &Toast::finishedClose, this, [toast, this]() {
|
||||||
if (m_toasts.removeOne(toast)) {
|
if (m_toasts.removeOne(toast)) {
|
||||||
|
|||||||
+15
-10
@@ -20,15 +20,15 @@ Q_PROPERTY(int timeout READ timeout CONSTANT)
|
|||||||
Q_PROPERTY(Type type READ type CONSTANT)
|
Q_PROPERTY(Type type READ type CONSTANT)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum class Type {
|
enum class Type { Info = 0, Success, Warning, Error };
|
||||||
Info = 0,
|
|
||||||
Success,
|
|
||||||
Warning,
|
|
||||||
Error
|
|
||||||
};
|
|
||||||
Q_ENUM(Type)
|
Q_ENUM(Type)
|
||||||
|
|
||||||
explicit Toast(const QString& title, const QString& message, const QString& icon, Type type, int timeout,
|
explicit Toast(
|
||||||
|
const QString& title,
|
||||||
|
const QString& message,
|
||||||
|
const QString& icon,
|
||||||
|
Type type,
|
||||||
|
int timeout,
|
||||||
QObject* parent = nullptr);
|
QObject* parent = nullptr);
|
||||||
|
|
||||||
[[nodiscard]] bool closed() const;
|
[[nodiscard]] bool closed() const;
|
||||||
@@ -62,15 +62,20 @@ Q_OBJECT
|
|||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
QML_SINGLETON
|
QML_SINGLETON
|
||||||
|
|
||||||
Q_PROPERTY(QQmlListProperty<ZShell::Toast> toasts READ toasts NOTIFY toastsChanged)
|
Q_PROPERTY(
|
||||||
|
QQmlListProperty<ZShell::Toast> toasts READ toasts NOTIFY toastsChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit Toaster(QObject* parent = nullptr);
|
explicit Toaster(QObject* parent = nullptr);
|
||||||
|
|
||||||
[[nodiscard]] QQmlListProperty<Toast> toasts();
|
[[nodiscard]] QQmlListProperty<Toast> toasts();
|
||||||
|
|
||||||
Q_INVOKABLE void toast(const QString& title, const QString& message, const QString& icon = QString(),
|
Q_INVOKABLE void toast(
|
||||||
ZShell::Toast::Type type = Toast::Type::Info, int timeout = 5000);
|
const QString& title,
|
||||||
|
const QString& message,
|
||||||
|
const QString& icon = QString(),
|
||||||
|
ZShell::Toast::Type type = Toast::Type::Info,
|
||||||
|
int timeout = 5000);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void toastsChanged();
|
void toastsChanged();
|
||||||
|
|||||||
@@ -20,15 +20,12 @@
|
|||||||
|
|
||||||
namespace ZShell {
|
namespace ZShell {
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// saveItem
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
void ZShellIo::saveItem(QQuickItem* target, const QUrl& path) {
|
void ZShellIo::saveItem(QQuickItem* target, const QUrl& path) {
|
||||||
this->saveItem(target, path, QRect(), QJSValue(), QJSValue());
|
this->saveItem(target, path, QRect(), QJSValue(), QJSValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZShellIo::saveItem(QQuickItem* target, const QUrl& path, const QRect& rect) {
|
void ZShellIo::saveItem(
|
||||||
|
QQuickItem* target, const QUrl& path, const QRect& rect) {
|
||||||
this->saveItem(target, path, rect, QJSValue(), QJSValue());
|
this->saveItem(target, path, rect, QJSValue(), QJSValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,11 +33,13 @@ void ZShellIo::saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved)
|
|||||||
this->saveItem(target, path, QRect(), onSaved, QJSValue());
|
this->saveItem(target, path, QRect(), onSaved, QJSValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZShellIo::saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved, QJSValue onFailed) {
|
void ZShellIo::saveItem(
|
||||||
|
QQuickItem* target, const QUrl& path, QJSValue onSaved, QJSValue onFailed) {
|
||||||
this->saveItem(target, path, QRect(), onSaved, onFailed);
|
this->saveItem(target, path, QRect(), onSaved, onFailed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZShellIo::saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved) {
|
void ZShellIo::saveItem(
|
||||||
|
QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved) {
|
||||||
this->saveItem(target, path, rect, onSaved, QJSValue());
|
this->saveItem(target, path, rect, onSaved, QJSValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,8 +48,7 @@ void ZShellIo::saveItem(
|
|||||||
const QUrl& path,
|
const QUrl& path,
|
||||||
const QRect& rect,
|
const QRect& rect,
|
||||||
QJSValue onSaved,
|
QJSValue onSaved,
|
||||||
QJSValue onFailed
|
QJSValue onFailed) {
|
||||||
) {
|
|
||||||
if (!target) {
|
if (!target) {
|
||||||
qWarning() << "ZShellIo::saveItem: a target is required";
|
qWarning() << "ZShellIo::saveItem: a target is required";
|
||||||
return;
|
return;
|
||||||
@@ -62,8 +60,7 @@ void ZShellIo::saveItem(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!target->window()) {
|
if (!target->window()) {
|
||||||
qWarning() << "ZShellIo::saveItem: unable to save target"
|
qWarning() << "ZShellIo::saveItem: unable to save target" << target
|
||||||
<< target
|
|
||||||
<< "without a window";
|
<< "without a window";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -77,8 +74,8 @@ void ZShellIo::saveItem(
|
|||||||
rect.left() * scale,
|
rect.left() * scale,
|
||||||
rect.top() * scale,
|
rect.top() * scale,
|
||||||
rect.width() * scale,
|
rect.width() * scale,
|
||||||
rect.height() * scale
|
rect.height() * scale)
|
||||||
).toRect();
|
.toRect();
|
||||||
}
|
}
|
||||||
|
|
||||||
const QSharedPointer<const QQuickItemGrabResult> grabResult =
|
const QSharedPointer<const QQuickItemGrabResult> grabResult =
|
||||||
@@ -89,7 +86,8 @@ void ZShellIo::saveItem(
|
|||||||
&QQuickItemGrabResult::ready,
|
&QQuickItemGrabResult::ready,
|
||||||
this,
|
this,
|
||||||
[grabResult, scaledRect, path, onSaved, onFailed, this]() {
|
[grabResult, scaledRect, path, onSaved, onFailed, this]() {
|
||||||
const auto future = QtConcurrent::run([grabResult, scaledRect, path]() {
|
const auto future =
|
||||||
|
QtConcurrent::run([grabResult, scaledRect, path]() {
|
||||||
QImage image = grabResult->image();
|
QImage image = grabResult->image();
|
||||||
|
|
||||||
if (scaledRect.isValid()) {
|
if (scaledRect.isValid()) {
|
||||||
@@ -116,39 +114,34 @@ void ZShellIo::saveItem(
|
|||||||
auto* watcher = new QFutureWatcher<bool>(this);
|
auto* watcher = new QFutureWatcher<bool>(this);
|
||||||
auto* engine = qmlEngine(this);
|
auto* engine = qmlEngine(this);
|
||||||
|
|
||||||
QObject::connect(watcher, &QFutureWatcher<bool>::finished, this, [=]() {
|
QObject::connect(
|
||||||
|
watcher, &QFutureWatcher<bool>::finished, this, [=]() {
|
||||||
if (watcher->result()) {
|
if (watcher->result()) {
|
||||||
if (onSaved.isCallable() && engine) {
|
if (onSaved.isCallable() && engine) {
|
||||||
onSaved.call({
|
onSaved.call(
|
||||||
engine->toScriptValue(path.toLocalFile()),
|
{engine->toScriptValue(path.toLocalFile()),
|
||||||
engine->toScriptValue(path)
|
engine->toScriptValue(path)});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "ZShellIo::saveItem: failed to save" << path;
|
qWarning()
|
||||||
|
<< "ZShellIo::saveItem: failed to save" << path;
|
||||||
if (onFailed.isCallable() && engine) {
|
if (onFailed.isCallable() && engine) {
|
||||||
onFailed.call({
|
onFailed.call({engine->toScriptValue(path)});
|
||||||
engine->toScriptValue(path)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
watcher->deleteLater();
|
watcher->deleteLater();
|
||||||
});
|
});
|
||||||
|
|
||||||
watcher->setFuture(future);
|
watcher->setFuture(future);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// cacheImage
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
void ZShellIo::cacheImage(const QUrl& source, const QString& cacheDir) {
|
void ZShellIo::cacheImage(const QUrl& source, const QString& cacheDir) {
|
||||||
this->cacheImage(source, cacheDir, QJSValue(), QJSValue());
|
this->cacheImage(source, cacheDir, QJSValue(), QJSValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZShellIo::cacheImage(const QUrl& source, const QString& cacheDir, QJSValue onSaved) {
|
void ZShellIo::cacheImage(
|
||||||
|
const QUrl& source, const QString& cacheDir, QJSValue onSaved) {
|
||||||
this->cacheImage(source, cacheDir, onSaved, QJSValue());
|
this->cacheImage(source, cacheDir, onSaved, QJSValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,8 +149,7 @@ void ZShellIo::cacheImage(
|
|||||||
const QUrl& source,
|
const QUrl& source,
|
||||||
const QString& cacheDir,
|
const QString& cacheDir,
|
||||||
QJSValue onSaved,
|
QJSValue onSaved,
|
||||||
QJSValue onFailed
|
QJSValue onFailed) {
|
||||||
) {
|
|
||||||
if (cacheDir.isEmpty()) {
|
if (cacheDir.isEmpty()) {
|
||||||
qWarning() << "ZShellIo::cacheImage: cacheDir is empty";
|
qWarning() << "ZShellIo::cacheImage: cacheDir is empty";
|
||||||
return;
|
return;
|
||||||
@@ -165,13 +157,13 @@ void ZShellIo::cacheImage(
|
|||||||
|
|
||||||
QImage image;
|
QImage image;
|
||||||
if (!loadSourceImage(source, image)) {
|
if (!loadSourceImage(source, image)) {
|
||||||
qWarning() << "ZShellIo::cacheImage: failed to load source image" << source;
|
qWarning() << "ZShellIo::cacheImage: failed to load source image"
|
||||||
|
<< source;
|
||||||
auto* engine = qmlEngine(this);
|
auto* engine = qmlEngine(this);
|
||||||
if (onFailed.isCallable() && engine) {
|
if (onFailed.isCallable() && engine) {
|
||||||
onFailed.call({
|
onFailed.call(
|
||||||
engine->toScriptValue(source),
|
{engine->toScriptValue(source),
|
||||||
engine->toScriptValue(cacheDir)
|
engine->toScriptValue(cacheDir)});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -181,12 +173,12 @@ void ZShellIo::cacheImage(
|
|||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
const QImage normalized = image.convertToFormat(QImage::Format_RGBA8888);
|
const QImage normalized =
|
||||||
|
image.convertToFormat(QImage::Format_RGBA8888);
|
||||||
|
|
||||||
const QByteArray bytes(
|
const QByteArray bytes(
|
||||||
reinterpret_cast<const char*>(normalized.constBits()),
|
reinterpret_cast<const char*>(normalized.constBits()),
|
||||||
qsizetype(normalized.sizeInBytes())
|
normalized.sizeInBytes());
|
||||||
);
|
|
||||||
|
|
||||||
const QByteArray digest =
|
const QByteArray digest =
|
||||||
QCryptographicHash::hash(bytes, QCryptographicHash::Sha256).toHex();
|
QCryptographicHash::hash(bytes, QCryptographicHash::Sha256).toHex();
|
||||||
@@ -196,7 +188,8 @@ void ZShellIo::cacheImage(
|
|||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString finalPath = dir.filePath(QString::fromLatin1(digest) + ".png");
|
const QString finalPath =
|
||||||
|
dir.filePath(QString::fromLatin1(digest) + ".png");
|
||||||
|
|
||||||
if (QFile::exists(finalPath)) {
|
if (QFile::exists(finalPath)) {
|
||||||
return finalPath;
|
return finalPath;
|
||||||
@@ -226,18 +219,16 @@ void ZShellIo::cacheImage(
|
|||||||
|
|
||||||
if (!finalPath.isEmpty()) {
|
if (!finalPath.isEmpty()) {
|
||||||
if (onSaved.isCallable() && engine) {
|
if (onSaved.isCallable() && engine) {
|
||||||
onSaved.call({
|
onSaved.call(
|
||||||
engine->toScriptValue(finalPath),
|
{engine->toScriptValue(finalPath),
|
||||||
engine->toScriptValue(QUrl::fromLocalFile(finalPath))
|
engine->toScriptValue(QUrl::fromLocalFile(finalPath))});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "ZShellIo::cacheImage: failed to cache" << source;
|
qWarning() << "ZShellIo::cacheImage: failed to cache" << source;
|
||||||
if (onFailed.isCallable() && engine) {
|
if (onFailed.isCallable() && engine) {
|
||||||
onFailed.call({
|
onFailed.call(
|
||||||
engine->toScriptValue(source),
|
{engine->toScriptValue(source),
|
||||||
engine->toScriptValue(cacheDir)
|
engine->toScriptValue(cacheDir)});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,10 +238,6 @@ void ZShellIo::cacheImage(
|
|||||||
watcher->setFuture(future);
|
watcher->setFuture(future);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// loadSourceImage
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
bool ZShellIo::loadSourceImage(const QUrl& source, QImage& image) const {
|
bool ZShellIo::loadSourceImage(const QUrl& source, QImage& image) const {
|
||||||
image = QImage();
|
image = QImage();
|
||||||
|
|
||||||
@@ -270,8 +257,7 @@ bool ZShellIo::loadSourceImage(const QUrl& source, QImage& image) const {
|
|||||||
|
|
||||||
const QString providerId = source.host();
|
const QString providerId = source.host();
|
||||||
|
|
||||||
const QString imageId =
|
const QString imageId = source.path().startsWith('/')
|
||||||
source.path().startsWith('/')
|
|
||||||
? source.path().mid(1)
|
? source.path().mid(1)
|
||||||
: source.path();
|
: source.path();
|
||||||
|
|
||||||
@@ -284,7 +270,8 @@ bool ZShellIo::loadSourceImage(const QUrl& source, QImage& image) const {
|
|||||||
|
|
||||||
auto* provider = dynamic_cast<QQuickImageProvider*>(providerBase);
|
auto* provider = dynamic_cast<QQuickImageProvider*>(providerBase);
|
||||||
if (!provider) {
|
if (!provider) {
|
||||||
qWarning() << "ZShellIo::loadSourceImage: provider is not a QQuickImageProvider"
|
qWarning() << "ZShellIo::loadSourceImage: provider is not a "
|
||||||
|
"QQuickImageProvider"
|
||||||
<< providerId;
|
<< providerId;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -313,17 +300,16 @@ bool ZShellIo::loadSourceImage(const QUrl& source, QImage& image) const {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
bool ZShellIo::copyFile(
|
||||||
// File ops
|
const QUrl& source, const QUrl& target, bool overwrite) const {
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
bool ZShellIo::copyFile(const QUrl& source, const QUrl& target, bool overwrite) const {
|
|
||||||
if (!source.isLocalFile()) {
|
if (!source.isLocalFile()) {
|
||||||
qWarning() << "ZShellIo::copyFile: source" << source << "is not a local file";
|
qWarning() << "ZShellIo::copyFile: source" << source
|
||||||
|
<< "is not a local file";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!target.isLocalFile()) {
|
if (!target.isLocalFile()) {
|
||||||
qWarning() << "ZShellIo::copyFile: target" << target << "is not a local file";
|
qWarning() << "ZShellIo::copyFile: target" << target
|
||||||
|
<< "is not a local file";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -336,7 +322,8 @@ bool ZShellIo::copyFile(const QUrl& source, const QUrl& target, bool overwrite)
|
|||||||
|
|
||||||
bool ZShellIo::deleteFile(const QUrl& path) const {
|
bool ZShellIo::deleteFile(const QUrl& path) const {
|
||||||
if (!path.isLocalFile()) {
|
if (!path.isLocalFile()) {
|
||||||
qWarning() << "ZShellIo::deleteFile: path" << path << "is not a local file";
|
qWarning() << "ZShellIo::deleteFile: path" << path
|
||||||
|
<< "is not a local file";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,7 +332,8 @@ bool ZShellIo::deleteFile(const QUrl& path) const {
|
|||||||
|
|
||||||
QString ZShellIo::toLocalFile(const QUrl& url) const {
|
QString ZShellIo::toLocalFile(const QUrl& url) const {
|
||||||
if (!url.isLocalFile()) {
|
if (!url.isLocalFile()) {
|
||||||
qWarning() << "ZShellIo::toLocalFile: given url is not a local file" << url;
|
qWarning() << "ZShellIo::toLocalFile: given url is not a local file"
|
||||||
|
<< url;
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
namespace ZShell {
|
namespace ZShell {
|
||||||
|
|
||||||
class ZShellIo : public QObject {
|
class ZShellIo : public QObject {
|
||||||
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
QML_ELEMENT
|
QML_ELEMENT
|
||||||
QML_SINGLETON
|
QML_SINGLETON
|
||||||
@@ -29,7 +28,8 @@ Q_INVOKABLE void cacheImage(const QUrl& source, const QString& cacheDir, QJSValu
|
|||||||
Q_INVOKABLE void cacheImage(const QUrl& source, const QString& cacheDir, QJSValue onSaved, QJSValue onFailed);
|
Q_INVOKABLE void cacheImage(const QUrl& source, const QString& cacheDir, QJSValue onSaved, QJSValue onFailed);
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
Q_INVOKABLE [[nodiscard]] bool copyFile(const QUrl& source, const QUrl& target, bool overwrite = true) const;
|
Q_INVOKABLE [[nodiscard]] bool copyFile(
|
||||||
|
const QUrl& source, const QUrl& target, bool overwrite = true) const;
|
||||||
Q_INVOKABLE [[nodiscard]] bool deleteFile(const QUrl& path) const;
|
Q_INVOKABLE [[nodiscard]] bool deleteFile(const QUrl& path) const;
|
||||||
Q_INVOKABLE [[nodiscard]] QString toLocalFile(const QUrl& url) const;
|
Q_INVOKABLE [[nodiscard]] QString toLocalFile(const QUrl& url) const;
|
||||||
|
|
||||||
|
|||||||
+43
-17
@@ -27,15 +27,22 @@ void ZUtils::saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved) {
|
|||||||
this->saveItem(target, path, QRect(), onSaved, QJSValue());
|
this->saveItem(target, path, QRect(), onSaved, QJSValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZUtils::saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved, QJSValue onFailed) {
|
void ZUtils::saveItem(
|
||||||
|
QQuickItem* target, const QUrl& path, QJSValue onSaved, QJSValue onFailed) {
|
||||||
this->saveItem(target, path, QRect(), onSaved, onFailed);
|
this->saveItem(target, path, QRect(), onSaved, onFailed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZUtils::saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved) {
|
void ZUtils::saveItem(
|
||||||
|
QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved) {
|
||||||
this->saveItem(target, path, rect, onSaved, QJSValue());
|
this->saveItem(target, path, rect, onSaved, QJSValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZUtils::saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved, QJSValue onFailed) {
|
void ZUtils::saveItem(
|
||||||
|
QQuickItem* target,
|
||||||
|
const QUrl& path,
|
||||||
|
const QRect& rect,
|
||||||
|
QJSValue onSaved,
|
||||||
|
QJSValue onFailed) {
|
||||||
if (!target) {
|
if (!target) {
|
||||||
qCWarning(lcZUtils) << "saveItem: a target is required";
|
qCWarning(lcZUtils) << "saveItem: a target is required";
|
||||||
return;
|
return;
|
||||||
@@ -47,20 +54,29 @@ void ZUtils::saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, Q
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!target->window()) {
|
if (!target->window()) {
|
||||||
qCWarning(lcZUtils) << "saveItem: unable to save target" << target << "without a window";
|
qCWarning(lcZUtils) << "saveItem: unable to save target" << target
|
||||||
|
<< "without a window";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto scaledRect = rect;
|
auto scaledRect = rect;
|
||||||
const qreal scale = target->window()->devicePixelRatio();
|
const qreal scale = target->window()->devicePixelRatio();
|
||||||
if (rect.isValid() && !qFuzzyCompare(scale + 1.0, 2.0)) {
|
if (rect.isValid() && !qFuzzyCompare(scale + 1.0, 2.0)) {
|
||||||
scaledRect =
|
scaledRect = QRectF(
|
||||||
QRectF(rect.left() * scale, rect.top() * scale, rect.width() * scale, rect.height() * scale).toRect();
|
rect.left() * scale,
|
||||||
|
rect.top() * scale,
|
||||||
|
rect.width() * scale,
|
||||||
|
rect.height() * scale)
|
||||||
|
.toRect();
|
||||||
}
|
}
|
||||||
|
|
||||||
const QSharedPointer<const QQuickItemGrabResult> grabResult = target->grabToImage();
|
const QSharedPointer<const QQuickItemGrabResult> grabResult =
|
||||||
|
target->grabToImage();
|
||||||
|
|
||||||
QObject::connect(grabResult.data(), &QQuickItemGrabResult::ready, this,
|
QObject::connect(
|
||||||
|
grabResult.data(),
|
||||||
|
&QQuickItemGrabResult::ready,
|
||||||
|
this,
|
||||||
[grabResult, scaledRect, path, onSaved, onFailed, this]() {
|
[grabResult, scaledRect, path, onSaved, onFailed, this]() {
|
||||||
const auto future = QtConcurrent::run([=]() {
|
const auto future = QtConcurrent::run([=]() {
|
||||||
QImage image = grabResult->image();
|
QImage image = grabResult->image();
|
||||||
@@ -77,20 +93,24 @@ void ZUtils::saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, Q
|
|||||||
auto* watcher = new QFutureWatcher<bool>(this);
|
auto* watcher = new QFutureWatcher<bool>(this);
|
||||||
auto* engine = qmlEngine(this);
|
auto* engine = qmlEngine(this);
|
||||||
|
|
||||||
QObject::connect(watcher, &QFutureWatcher<bool>::finished, this, [=]() {
|
QObject::connect(
|
||||||
|
watcher, &QFutureWatcher<bool>::finished, this, [=]() {
|
||||||
if (watcher->result()) {
|
if (watcher->result()) {
|
||||||
if (onSaved.isCallable()) {
|
if (onSaved.isCallable()) {
|
||||||
QJSValueList args = {QJSValue(path.toLocalFile())};
|
QJSValueList args = {QJSValue(path.toLocalFile())};
|
||||||
if (engine) {
|
if (engine) {
|
||||||
args << engine->toScriptValue(QVariant::fromValue(path));
|
args << engine->toScriptValue(
|
||||||
|
QVariant::fromValue(path));
|
||||||
}
|
}
|
||||||
onSaved.call(args);
|
onSaved.call(args);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
qCWarning(lcZUtils) << "saveItem: failed to save" << path;
|
qCWarning(lcZUtils)
|
||||||
|
<< "saveItem: failed to save" << path;
|
||||||
if (onFailed.isCallable()) {
|
if (onFailed.isCallable()) {
|
||||||
if (engine) {
|
if (engine) {
|
||||||
onFailed.call({ engine->toScriptValue(QVariant::fromValue(path)) });
|
onFailed.call({engine->toScriptValue(
|
||||||
|
QVariant::fromValue(path))});
|
||||||
} else {
|
} else {
|
||||||
onFailed.call();
|
onFailed.call();
|
||||||
}
|
}
|
||||||
@@ -104,17 +124,21 @@ void ZUtils::saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, Q
|
|||||||
|
|
||||||
bool ZUtils::copyFile(const QUrl& source, const QUrl& target, bool overwrite) {
|
bool ZUtils::copyFile(const QUrl& source, const QUrl& target, bool overwrite) {
|
||||||
if (!source.isLocalFile()) {
|
if (!source.isLocalFile()) {
|
||||||
qCWarning(lcZUtils) << "copyFile: source" << source << "is not a local file";
|
qCWarning(lcZUtils)
|
||||||
|
<< "copyFile: source" << source << "is not a local file";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!target.isLocalFile()) {
|
if (!target.isLocalFile()) {
|
||||||
qCWarning(lcZUtils) << "copyFile: target" << target << "is not a local file";
|
qCWarning(lcZUtils)
|
||||||
|
<< "copyFile: target" << target << "is not a local file";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (overwrite && QFile::exists(target.toLocalFile())) {
|
if (overwrite && QFile::exists(target.toLocalFile())) {
|
||||||
if (!QFile::remove(target.toLocalFile())) {
|
if (!QFile::remove(target.toLocalFile())) {
|
||||||
qCWarning(lcZUtils) << "copyFile: overwrite was specified but failed to remove" << target.toLocalFile();
|
qCWarning(lcZUtils)
|
||||||
|
<< "copyFile: overwrite was specified but failed to remove"
|
||||||
|
<< target.toLocalFile();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -124,7 +148,8 @@ bool ZUtils::copyFile(const QUrl& source, const QUrl& target, bool overwrite) {
|
|||||||
|
|
||||||
bool ZUtils::deleteFile(const QUrl& path) {
|
bool ZUtils::deleteFile(const QUrl& path) {
|
||||||
if (!path.isLocalFile()) {
|
if (!path.isLocalFile()) {
|
||||||
qCWarning(lcZUtils) << "deleteFile: path" << path << "is not a local file";
|
qCWarning(lcZUtils)
|
||||||
|
<< "deleteFile: path" << path << "is not a local file";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +158,8 @@ bool ZUtils::deleteFile(const QUrl& path) {
|
|||||||
|
|
||||||
QString ZUtils::toLocalFile(const QUrl& url) {
|
QString ZUtils::toLocalFile(const QUrl& url) {
|
||||||
if (!url.isLocalFile()) {
|
if (!url.isLocalFile()) {
|
||||||
qCWarning(lcZUtils) << "toLocalFile: given url is not a local file" << url;
|
qCWarning(lcZUtils)
|
||||||
|
<< "toLocalFile: given url is not a local file" << url;
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rec
|
|||||||
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved, QJSValue onFailed);
|
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved, QJSValue onFailed);
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
Q_INVOKABLE static bool copyFile(const QUrl& source, const QUrl& target, bool overwrite = true);
|
Q_INVOKABLE static bool copyFile(
|
||||||
|
const QUrl& source, const QUrl& target, bool overwrite = true);
|
||||||
Q_INVOKABLE static bool deleteFile(const QUrl& path);
|
Q_INVOKABLE static bool deleteFile(const QUrl& path);
|
||||||
Q_INVOKABLE static QString toLocalFile(const QUrl& url);
|
Q_INVOKABLE static QString toLocalFile(const QUrl& url);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user