updated components
This commit is contained in:
@@ -45,6 +45,7 @@ qml_module(ZShell
|
||||
requests.hpp requests.cpp
|
||||
toaster.hpp toaster.cpp
|
||||
qalculator.hpp qalculator.cpp
|
||||
zutils.hpp zutils.cpp
|
||||
LIBRARIES
|
||||
Qt::Gui
|
||||
Qt::Quick
|
||||
|
||||
@@ -2,6 +2,7 @@ qml_module(ZShell-components
|
||||
URI ZShell.Components
|
||||
SOURCES
|
||||
lazylistview.hpp lazylistview.cpp
|
||||
wavyline.hpp wavyline.cpp
|
||||
LIBRARIES
|
||||
Qt::Quick
|
||||
)
|
||||
|
||||
@@ -0,0 +1,255 @@
|
||||
#include "wavyline.hpp"
|
||||
|
||||
#include <qpainter.h>
|
||||
#include <qpainterpath.h>
|
||||
|
||||
namespace ZShell::controls {
|
||||
|
||||
WavyLine::WavyLine(QQuickItem* parent)
|
||||
: QQuickPaintedItem(parent)
|
||||
, m_lineWidth(4)
|
||||
, m_amplitudeMultiplier(0.5)
|
||||
, m_frequency(6)
|
||||
, m_startX(0)
|
||||
, m_fullLength(0)
|
||||
, m_color(Qt::white)
|
||||
, m_waveProgress(0)
|
||||
, m_pathType(Linear)
|
||||
, m_startAngle(0)
|
||||
, m_fullAngle(360)
|
||||
, m_radius(-1)
|
||||
, m_value(1)
|
||||
, m_startAngleRad(0)
|
||||
, m_fullAngleRad(2 * M_PI) {
|
||||
setAntialiasing(true);
|
||||
}
|
||||
|
||||
int WavyLine::lineWidth() const {
|
||||
return m_lineWidth;
|
||||
}
|
||||
|
||||
void WavyLine::setLineWidth(int lineWidth) {
|
||||
if (m_lineWidth != lineWidth) {
|
||||
m_lineWidth = lineWidth;
|
||||
emit lineWidthChanged();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
qreal WavyLine::amplitudeMultiplier() const {
|
||||
return m_amplitudeMultiplier;
|
||||
}
|
||||
|
||||
void WavyLine::setAmplitudeMultiplier(qreal amplitudeMultiplier) {
|
||||
if (!qFuzzyCompare(m_amplitudeMultiplier + 1.0, amplitudeMultiplier + 1.0)) {
|
||||
m_amplitudeMultiplier = amplitudeMultiplier;
|
||||
emit amplitudeMultiplierChanged();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
int WavyLine::frequency() const {
|
||||
return m_frequency;
|
||||
}
|
||||
|
||||
void WavyLine::setFrequency(int frequency) {
|
||||
if (m_frequency != frequency) {
|
||||
m_frequency = frequency;
|
||||
emit frequencyChanged();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
qreal WavyLine::startX() const {
|
||||
return m_startX;
|
||||
}
|
||||
|
||||
void WavyLine::setStartX(qreal startX) {
|
||||
if (!qFuzzyCompare(m_startX + 1.0, startX + 1.0)) {
|
||||
m_startX = startX;
|
||||
emit startXChanged();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
qreal WavyLine::fullLength() const {
|
||||
return m_fullLength;
|
||||
}
|
||||
|
||||
void WavyLine::setFullLength(qreal fullLength) {
|
||||
if (!qFuzzyCompare(m_fullLength + 1.0, fullLength + 1.0)) {
|
||||
m_fullLength = fullLength;
|
||||
emit fullLengthChanged();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
QColor WavyLine::color() const {
|
||||
return m_color;
|
||||
}
|
||||
|
||||
void WavyLine::setColor(const QColor& color) {
|
||||
if (m_color != color) {
|
||||
m_color = color;
|
||||
emit colorChanged();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
qreal WavyLine::waveProgress() const {
|
||||
return m_waveProgress;
|
||||
}
|
||||
|
||||
void WavyLine::setWaveProgress(qreal progress) {
|
||||
if (!qFuzzyCompare(m_waveProgress + 1.0, progress + 1.0)) {
|
||||
m_waveProgress = progress;
|
||||
emit waveProgressChanged();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
WavyLine::PathType WavyLine::pathType() const {
|
||||
return m_pathType;
|
||||
}
|
||||
|
||||
void WavyLine::setPathType(PathType pathType) {
|
||||
if (m_pathType != pathType) {
|
||||
m_pathType = pathType;
|
||||
emit pathTypeChanged();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
qreal WavyLine::startAngle() const {
|
||||
return m_startAngle;
|
||||
}
|
||||
|
||||
void WavyLine::setStartAngle(qreal startAngle) {
|
||||
if (!qFuzzyCompare(m_startAngle + 1.0, startAngle + 1.0)) {
|
||||
m_startAngle = startAngle;
|
||||
m_startAngleRad = startAngle * M_PI / 180.0;
|
||||
emit startAngleChanged();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
qreal WavyLine::fullAngle() const {
|
||||
return m_fullAngle;
|
||||
}
|
||||
|
||||
void WavyLine::setFullAngle(qreal fullAngle) {
|
||||
if (!qFuzzyCompare(m_fullAngle + 1.0, fullAngle + 1.0)) {
|
||||
m_fullAngle = fullAngle;
|
||||
m_fullAngleRad = fullAngle * M_PI / 180.0;
|
||||
emit fullAngleChanged();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
qreal WavyLine::radius() const {
|
||||
return m_radius;
|
||||
}
|
||||
|
||||
void WavyLine::setRadius(qreal radius) {
|
||||
if (!qFuzzyCompare(m_radius + 1.0, radius + 1.0)) {
|
||||
m_radius = radius;
|
||||
emit radiusChanged();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
qreal WavyLine::value() const {
|
||||
return m_value;
|
||||
}
|
||||
|
||||
void WavyLine::setValue(qreal value) {
|
||||
if (!qFuzzyCompare(m_value + 1.0, value + 1.0)) {
|
||||
m_value = value;
|
||||
emit valueChanged();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void WavyLine::paint(QPainter* painter) {
|
||||
painter->setRenderHint(QPainter::Antialiasing);
|
||||
painter->setPen(QPen(m_color, m_lineWidth, Qt::SolidLine, Qt::RoundCap));
|
||||
|
||||
if (m_pathType == Arc) {
|
||||
paintArc(painter);
|
||||
} else {
|
||||
paintLinear(painter);
|
||||
}
|
||||
}
|
||||
|
||||
void WavyLine::paintLinear(QPainter* painter) {
|
||||
const auto amplitude = m_lineWidth * m_amplitudeMultiplier;
|
||||
const auto phase = m_waveProgress * 2 * M_PI;
|
||||
const auto centerY = height() / 2;
|
||||
const auto len = m_fullLength > 0 ? m_fullLength : 1;
|
||||
const auto start = m_lineWidth / 2.0;
|
||||
const auto fullEnd = width() - m_lineWidth / 2.0;
|
||||
const auto drawEnd = start + (fullEnd - start) * m_value;
|
||||
|
||||
QPainterPath path;
|
||||
bool first = true;
|
||||
|
||||
for (int x = m_lineWidth / 2; x <= drawEnd; ++x) {
|
||||
const auto theta = m_frequency * 2 * M_PI * (x + m_startX) / len + phase;
|
||||
const auto waveY = centerY + amplitude * qSin(theta);
|
||||
if (first) {
|
||||
path.moveTo(x, waveY);
|
||||
first = false;
|
||||
} else {
|
||||
path.lineTo(x, waveY);
|
||||
}
|
||||
}
|
||||
|
||||
painter->drawPath(path);
|
||||
}
|
||||
|
||||
void WavyLine::paintArc(QPainter* painter) {
|
||||
if (m_fullAngleRad <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto amplitude = m_lineWidth * m_amplitudeMultiplier;
|
||||
const auto cx = width() / 2.0;
|
||||
const auto cy = height() / 2.0;
|
||||
const auto radius = m_radius > 0 ? m_radius : (qMin(width(), height()) - m_lineWidth - 2 * amplitude) / 2.0;
|
||||
|
||||
if (radius <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto phase = m_waveProgress * 2 * M_PI;
|
||||
const auto arcLen = radius * m_fullAngleRad;
|
||||
const auto len = m_fullLength > 0 ? m_fullLength : arcLen;
|
||||
const auto drawAngleRad = m_fullAngleRad * m_value;
|
||||
|
||||
if (drawAngleRad <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto N = qMax(64, qCeil(radius * drawAngleRad));
|
||||
const auto dTheta = drawAngleRad / N;
|
||||
|
||||
QPainterPath path;
|
||||
|
||||
for (int i = 0; i <= N; ++i) {
|
||||
const auto theta = m_startAngleRad + i * dTheta;
|
||||
const auto s = i * dTheta * radius;
|
||||
const auto phi = m_frequency * 2 * M_PI * (s + m_startX) / len + phase;
|
||||
const auto r = radius + amplitude * qSin(phi);
|
||||
const auto px = cx + r * qCos(theta);
|
||||
const auto py = cy + r * qSin(theta);
|
||||
if (i == 0) {
|
||||
path.moveTo(px, py);
|
||||
} else {
|
||||
path.lineTo(px, py);
|
||||
}
|
||||
}
|
||||
|
||||
painter->drawPath(path);
|
||||
}
|
||||
|
||||
} // namespace ZShell::controls
|
||||
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include <qqmlintegration.h>
|
||||
#include <qquickpainteditem.h>
|
||||
|
||||
namespace ZShell::controls {
|
||||
|
||||
class WavyLine : public QQuickPaintedItem {
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
|
||||
Q_PROPERTY(int lineWidth READ lineWidth WRITE setLineWidth NOTIFY lineWidthChanged FINAL)
|
||||
Q_PROPERTY(qreal amplitudeMultiplier READ amplitudeMultiplier WRITE setAmplitudeMultiplier NOTIFY
|
||||
amplitudeMultiplierChanged FINAL)
|
||||
Q_PROPERTY(int frequency READ frequency WRITE setFrequency NOTIFY frequencyChanged FINAL)
|
||||
Q_PROPERTY(qreal startX READ startX WRITE setStartX NOTIFY startXChanged FINAL)
|
||||
Q_PROPERTY(qreal fullLength READ fullLength WRITE setFullLength NOTIFY fullLengthChanged FINAL)
|
||||
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged FINAL)
|
||||
Q_PROPERTY(qreal waveProgress READ waveProgress WRITE setWaveProgress NOTIFY waveProgressChanged FINAL)
|
||||
Q_PROPERTY(PathType pathType READ pathType WRITE setPathType NOTIFY pathTypeChanged FINAL)
|
||||
Q_PROPERTY(qreal startAngle READ startAngle WRITE setStartAngle NOTIFY startAngleChanged FINAL)
|
||||
Q_PROPERTY(qreal fullAngle READ fullAngle WRITE setFullAngle NOTIFY fullAngleChanged FINAL)
|
||||
Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged FINAL)
|
||||
Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY valueChanged FINAL)
|
||||
|
||||
public:
|
||||
enum PathType {
|
||||
Linear,
|
||||
Arc
|
||||
};
|
||||
Q_ENUM(PathType)
|
||||
|
||||
explicit WavyLine(QQuickItem* parent = nullptr);
|
||||
|
||||
[[nodiscard]] int lineWidth() const;
|
||||
void setLineWidth(int lineWidth);
|
||||
|
||||
[[nodiscard]] qreal amplitudeMultiplier() const;
|
||||
void setAmplitudeMultiplier(qreal amplitudeMultiplier);
|
||||
|
||||
[[nodiscard]] int frequency() const;
|
||||
void setFrequency(int frequency);
|
||||
|
||||
[[nodiscard]] qreal startX() const;
|
||||
void setStartX(qreal startX);
|
||||
|
||||
[[nodiscard]] qreal fullLength() const;
|
||||
void setFullLength(qreal fullLength);
|
||||
|
||||
[[nodiscard]] QColor color() const;
|
||||
void setColor(const QColor& color);
|
||||
|
||||
[[nodiscard]] qreal waveProgress() const;
|
||||
void setWaveProgress(qreal progress);
|
||||
|
||||
[[nodiscard]] PathType pathType() const;
|
||||
void setPathType(PathType pathType);
|
||||
|
||||
[[nodiscard]] qreal startAngle() const;
|
||||
void setStartAngle(qreal startAngle);
|
||||
|
||||
[[nodiscard]] qreal fullAngle() const;
|
||||
void setFullAngle(qreal fullAngle);
|
||||
|
||||
[[nodiscard]] qreal radius() const;
|
||||
void setRadius(qreal radius);
|
||||
|
||||
[[nodiscard]] qreal value() const;
|
||||
void setValue(qreal value);
|
||||
|
||||
void paint(QPainter* painter) override;
|
||||
|
||||
signals:
|
||||
void lineWidthChanged();
|
||||
void amplitudeMultiplierChanged();
|
||||
void frequencyChanged();
|
||||
void startXChanged();
|
||||
void fullLengthChanged();
|
||||
void colorChanged();
|
||||
void waveProgressChanged();
|
||||
void pathTypeChanged();
|
||||
void startAngleChanged();
|
||||
void fullAngleChanged();
|
||||
void radiusChanged();
|
||||
void valueChanged();
|
||||
|
||||
private:
|
||||
void paintLinear(QPainter* painter);
|
||||
void paintArc(QPainter* painter);
|
||||
|
||||
int m_lineWidth;
|
||||
qreal m_amplitudeMultiplier;
|
||||
int m_frequency;
|
||||
qreal m_startX;
|
||||
qreal m_fullLength;
|
||||
QColor m_color;
|
||||
qreal m_waveProgress;
|
||||
PathType m_pathType;
|
||||
qreal m_startAngle;
|
||||
qreal m_fullAngle;
|
||||
qreal m_radius;
|
||||
qreal m_value;
|
||||
qreal m_startAngleRad;
|
||||
qreal m_fullAngleRad;
|
||||
};
|
||||
|
||||
} // namespace ZShell::controls
|
||||
@@ -0,0 +1,145 @@
|
||||
#include "zutils.hpp"
|
||||
|
||||
#include <QtConcurrent/qtconcurrentrun.h>
|
||||
#include <QtQuick/qquickitemgrabresult.h>
|
||||
#include <QtQuick/qquickwindow.h>
|
||||
#include <qdir.h>
|
||||
#include <qfileinfo.h>
|
||||
#include <qfuturewatcher.h>
|
||||
#include <qloggingcategory.h>
|
||||
#include <qqmlengine.h>
|
||||
|
||||
Q_LOGGING_CATEGORY(lcZUtils, "ZShell.cutils", QtInfoMsg)
|
||||
|
||||
namespace ZShell {
|
||||
|
||||
void ZUtils::saveItem(QQuickItem* target, const QUrl& path) {
|
||||
this->saveItem(target, path, QRect(), QJSValue(), QJSValue());
|
||||
}
|
||||
|
||||
void ZUtils::saveItem(QQuickItem* target, const QUrl& path, const QRect& rect) {
|
||||
this->saveItem(target, path, rect, QJSValue(), QJSValue());
|
||||
}
|
||||
|
||||
void ZUtils::saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved) {
|
||||
this->saveItem(target, path, QRect(), onSaved, QJSValue());
|
||||
}
|
||||
|
||||
void ZUtils::saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved, QJSValue onFailed) {
|
||||
this->saveItem(target, path, QRect(), onSaved, onFailed);
|
||||
}
|
||||
|
||||
void ZUtils::saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved) {
|
||||
this->saveItem(target, path, rect, onSaved, QJSValue());
|
||||
}
|
||||
|
||||
void ZUtils::saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved, QJSValue onFailed) {
|
||||
if (!target) {
|
||||
qCWarning(lcZUtils) << "saveItem: a target is required";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!path.isLocalFile()) {
|
||||
qCWarning(lcZUtils) << "saveItem:" << path << "is not a local file";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!target->window()) {
|
||||
qCWarning(lcZUtils) << "saveItem: unable to save target" << target << "without a window";
|
||||
return;
|
||||
}
|
||||
|
||||
auto scaledRect = rect;
|
||||
const qreal scale = target->window()->devicePixelRatio();
|
||||
if (rect.isValid() && !qFuzzyCompare(scale + 1.0, 2.0)) {
|
||||
scaledRect =
|
||||
QRectF(rect.left() * scale, rect.top() * scale, rect.width() * scale, rect.height() * scale).toRect();
|
||||
}
|
||||
|
||||
const QSharedPointer<const QQuickItemGrabResult> grabResult = target->grabToImage();
|
||||
|
||||
QObject::connect(grabResult.data(), &QQuickItemGrabResult::ready, this,
|
||||
[grabResult, scaledRect, path, onSaved, onFailed, this]() {
|
||||
const auto future = QtConcurrent::run([=]() {
|
||||
QImage image = grabResult->image();
|
||||
|
||||
if (scaledRect.isValid()) {
|
||||
image = image.copy(scaledRect);
|
||||
}
|
||||
|
||||
const QString file = path.toLocalFile();
|
||||
const QString parent = QFileInfo(file).absolutePath();
|
||||
return QDir().mkpath(parent) && image.save(file);
|
||||
});
|
||||
|
||||
auto* watcher = new QFutureWatcher<bool>(this);
|
||||
auto* engine = qmlEngine(this);
|
||||
|
||||
QObject::connect(watcher, &QFutureWatcher<bool>::finished, this, [=]() {
|
||||
if (watcher->result()) {
|
||||
if (onSaved.isCallable()) {
|
||||
QJSValueList args = { QJSValue(path.toLocalFile()) };
|
||||
if (engine) {
|
||||
args << engine->toScriptValue(QVariant::fromValue(path));
|
||||
}
|
||||
onSaved.call(args);
|
||||
}
|
||||
} else {
|
||||
qCWarning(lcZUtils) << "saveItem: failed to save" << path;
|
||||
if (onFailed.isCallable()) {
|
||||
if (engine) {
|
||||
onFailed.call({ engine->toScriptValue(QVariant::fromValue(path)) });
|
||||
} else {
|
||||
onFailed.call();
|
||||
}
|
||||
}
|
||||
}
|
||||
watcher->deleteLater();
|
||||
});
|
||||
watcher->setFuture(future);
|
||||
});
|
||||
}
|
||||
|
||||
bool ZUtils::copyFile(const QUrl& source, const QUrl& target, bool overwrite) {
|
||||
if (!source.isLocalFile()) {
|
||||
qCWarning(lcZUtils) << "copyFile: source" << source << "is not a local file";
|
||||
return false;
|
||||
}
|
||||
if (!target.isLocalFile()) {
|
||||
qCWarning(lcZUtils) << "copyFile: target" << target << "is not a local file";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (overwrite && QFile::exists(target.toLocalFile())) {
|
||||
if (!QFile::remove(target.toLocalFile())) {
|
||||
qCWarning(lcZUtils) << "copyFile: overwrite was specified but failed to remove" << target.toLocalFile();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return QFile::copy(source.toLocalFile(), target.toLocalFile());
|
||||
}
|
||||
|
||||
bool ZUtils::deleteFile(const QUrl& path) {
|
||||
if (!path.isLocalFile()) {
|
||||
qCWarning(lcZUtils) << "deleteFile: path" << path << "is not a local file";
|
||||
return false;
|
||||
}
|
||||
|
||||
return QFile::remove(path.toLocalFile());
|
||||
}
|
||||
|
||||
QString ZUtils::toLocalFile(const QUrl& url) {
|
||||
if (!url.isLocalFile()) {
|
||||
qCWarning(lcZUtils) << "toLocalFile: given url is not a local file" << url;
|
||||
return QString();
|
||||
}
|
||||
|
||||
return url.toLocalFile();
|
||||
}
|
||||
|
||||
qreal ZUtils::clamp(qreal value, qreal min, qreal max) {
|
||||
return qBound(min, value, max);
|
||||
}
|
||||
|
||||
} // namespace ZShell
|
||||
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <QtQuick/qquickitem.h>
|
||||
#include <qobject.h>
|
||||
#include <qqmlintegration.h>
|
||||
|
||||
namespace ZShell {
|
||||
|
||||
class ZUtils : public QObject {
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_SINGLETON
|
||||
|
||||
public:
|
||||
// clang-format off
|
||||
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path);
|
||||
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect);
|
||||
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved);
|
||||
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved, QJSValue onFailed);
|
||||
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved);
|
||||
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved, QJSValue onFailed);
|
||||
// clang-format on
|
||||
|
||||
Q_INVOKABLE static bool copyFile(const QUrl& source, const QUrl& target, bool overwrite = true);
|
||||
Q_INVOKABLE static bool deleteFile(const QUrl& path);
|
||||
Q_INVOKABLE static QString toLocalFile(const QUrl& url);
|
||||
|
||||
Q_INVOKABLE static qreal clamp(qreal value, qreal min, qreal max);
|
||||
};
|
||||
|
||||
} // namespace ZShell
|
||||
Reference in New Issue
Block a user