#include "zutils.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "util/metaenum.hpp" 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 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(this); auto* engine = qmlEngine(this); QObject::connect( watcher, &QFutureWatcher::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); } QString ZUtils::enumToString( QObject* target, const QString& property, const QVariant& value) { if (!target) { qCWarning(lcZUtils) << "enumToString: a target is required"; return {}; } const auto* meta = target->metaObject(); const auto index = meta->indexOfProperty(property.toUtf8().constData()); if (index < 0) { qCWarning(lcZUtils) << "enumToString:" << target << "has no property" << property; return {}; } const auto prop = meta->property(index); const auto metaEnum = prop.isEnumType() ? prop.enumerator() : util::metaEnumFor(prop.metaType()); if (!metaEnum.isValid() || metaEnum.is64Bit()) { qCWarning(lcZUtils) << "enumToString: property" << property << "of" << target << "is not a supported enum"; return {}; } const auto val = value.isValid() ? value : prop.read(target); const auto* key = util::enumKeyFor(metaEnum, val); if (!key) { qCWarning( lcZUtils, "enumToString: no enumerator of %s::%s has the value %lld", metaEnum.scope(), metaEnum.name(), val.toLongLong()); return {}; } return QString::fromUtf8(key); } namespace { template QQuickItem* findChildDfs(QQuickItem* root, Predicate&& match) { const auto children = root->childItems(); for (QQuickItem* const child : children) { if (match(child)) { return child; } if (QQuickItem* const found = findChildDfs(child, match)) { return found; } } return nullptr; } template void findChildrenDfs( QQuickItem* root, Predicate&& match, QList& out) { const auto children = root->childItems(); for (QQuickItem* const child : children) { if (match(child)) { out.append(child); } findChildrenDfs(child, match, out); } } } // namespace QQuickItem* ZUtils::findChild(QQuickItem* root, const QString& name) { if (!root) { return nullptr; } return findChildDfs(root, [&name](const QQuickItem* item) { return item->objectName() == name; }); } QList ZUtils::findChildren(QQuickItem* root, const QString& name) { QList children; if (root) { findChildrenDfs( root, [&name](const QQuickItem* item) { return item->objectName() == name; }, children); } return children; } QList ZUtils::findChildrenMatching( QQuickItem* root, const QString& pattern) { QList children; if (root) { const QRegularExpression re(pattern); findChildrenDfs( root, [&re](const QQuickItem* item) { return re.match(item->objectName()).hasMatch(); }, children); } return children; } #ifndef ZSHELL_VERSION #define ZSHELL_VERSION "" #endif QString ZUtils::version() const { return QStringLiteral(ZSHELL_VERSION); } QString ZUtils::qtVersion() const { return QStringLiteral(QT_VERSION_STR); } QString ZUtils::gitRevision() { #ifdef GIT_REVISION return QStringLiteral(GIT_REVISION); #else return QString(); #endif } QString ZUtils::readTextFile(const QString& path) { QFile file(path); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { return QString(); } return QString::fromUtf8(file.readAll()); } bool ZUtils::writeTextFile(const QString& path, const QString& text) { const QFileInfo info(path); if (!QDir().mkpath(info.absolutePath())) { qCWarning(lcZUtils) << "Failed to create directory for" << path; return false; } QFile file(path); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { qCWarning(lcZUtils) << "Failed to open" << path << "for writing"; return false; } return file.write(text.toUtf8()) >= 0; } QStringList ZUtils::listFiles(const QString& dir, const QString& suffix) { QStringList out; QDirIterator it(dir, QDirIterator::Subdirectories); while (it.hasNext()) { const QString path = it.next(); if (it.fileInfo().isFile() && path.endsWith(suffix)) { out.append(path); } } return out; } } // namespace ZShell