optimize notification icon caching by copying image rather than item
This commit is contained in:
+55
-57
@@ -179,6 +179,8 @@ Singleton {
|
||||
property string appIcon
|
||||
property string appName
|
||||
property string body
|
||||
property string cachedImageSource: ""
|
||||
property bool cachingImage: false
|
||||
property bool closed
|
||||
readonly property Connections conn: Connections {
|
||||
function onActionsChanged(): void {
|
||||
@@ -214,9 +216,9 @@ Singleton {
|
||||
}
|
||||
|
||||
function onImageChanged(): void {
|
||||
notif.image = notif.notification.image;
|
||||
if (notif.notification?.image)
|
||||
notif.dummyImageLoader.active = true;
|
||||
notif.imageSource = notif.notification.image || "";
|
||||
notif.image = notif.imageSource;
|
||||
notif.cacheImageIfNeeded();
|
||||
}
|
||||
|
||||
function onResidentChanged(): void {
|
||||
@@ -233,60 +235,12 @@ Singleton {
|
||||
|
||||
target: notif.notification
|
||||
}
|
||||
readonly property LazyLoader dummyImageLoader: LazyLoader {
|
||||
active: false
|
||||
|
||||
PanelWindow {
|
||||
color: "transparent"
|
||||
implicitHeight: Config.notifs.sizes.image
|
||||
implicitWidth: Config.notifs.sizes.image
|
||||
|
||||
mask: Region {
|
||||
}
|
||||
|
||||
Image {
|
||||
function tryCache(): void {
|
||||
if (status !== Image.Ready || width != Config.notifs.sizes.image || height != Config.notifs.sizes.image)
|
||||
return;
|
||||
|
||||
const cacheKey = notif.appName + notif.summary + notif.id;
|
||||
let h1 = 0xdeadbeef, h2 = 0x41c6ce57, ch;
|
||||
for (let i = 0; i < cacheKey.length; i++) {
|
||||
ch = cacheKey.charCodeAt(i);
|
||||
h1 = Math.imul(h1 ^ ch, 2654435761);
|
||||
h2 = Math.imul(h2 ^ ch, 1597334677);
|
||||
}
|
||||
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507);
|
||||
h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);
|
||||
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507);
|
||||
h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909);
|
||||
const hash = (h2 >>> 0).toString(16).padStart(8, 0) + (h1 >>> 0).toString(16).padStart(8, 0);
|
||||
|
||||
const cache = `${Paths.notifimagecache}/${hash}.png`;
|
||||
ZShellIo.saveItem(this, Qt.resolvedUrl(cache), () => {
|
||||
notif.image = cache;
|
||||
notif.dummyImageLoader.active = false;
|
||||
});
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
asynchronous: true
|
||||
cache: false
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
opacity: 0
|
||||
source: Qt.resolvedUrl(notif.image)
|
||||
|
||||
onHeightChanged: tryCache()
|
||||
onStatusChanged: tryCache()
|
||||
onWidthChanged: tryCache()
|
||||
}
|
||||
}
|
||||
}
|
||||
property real expireTimeout: 5
|
||||
property bool hasActionIcons
|
||||
property string id
|
||||
property string image
|
||||
property string imageSource
|
||||
property var locks: new Set()
|
||||
property string notifId
|
||||
property Notification notification
|
||||
property bool popup
|
||||
property bool resident
|
||||
@@ -329,6 +283,35 @@ Singleton {
|
||||
}
|
||||
property int urgency: NotificationUrgency.Normal
|
||||
|
||||
function cacheImageIfNeeded(): void {
|
||||
const source = imageSource;
|
||||
|
||||
if (!source || cachingImage)
|
||||
return;
|
||||
|
||||
if (cachedImageSource === source)
|
||||
return;
|
||||
|
||||
if (source.startsWith("file:")) {
|
||||
cachedImageSource = source;
|
||||
image = source;
|
||||
return;
|
||||
}
|
||||
|
||||
const hash = hashForString(source);
|
||||
const cache = `${Paths.notifimagecache}/${hash}.png`;
|
||||
const cacheUrl = Qt.resolvedUrl(cache);
|
||||
|
||||
cachingImage = true;
|
||||
ZShellIo.saveImage(source, cacheUrl, () => {
|
||||
cachedImageSource = source;
|
||||
image = cache;
|
||||
cachingImage = false;
|
||||
}, () => {
|
||||
cachingImage = false;
|
||||
});
|
||||
}
|
||||
|
||||
function close(): void {
|
||||
closed = true;
|
||||
if (locks.size === 0 && root.list.includes(this)) {
|
||||
@@ -338,6 +321,20 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
function hashForString(s: string): string {
|
||||
let h1 = 0xdeadbeef, h2 = 0x41c6ce57, ch;
|
||||
for (let i = 0; i < s.length; i++) {
|
||||
ch = s.charCodeAt(i);
|
||||
h1 = Math.imul(h1 ^ ch, 2654435761);
|
||||
h2 = Math.imul(h2 ^ ch, 1597334677);
|
||||
}
|
||||
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507);
|
||||
h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909);
|
||||
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507);
|
||||
h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909);
|
||||
return (h2 >>> 0).toString(16).padStart(8, "0") + (h1 >>> 0).toString(16).padStart(8, "0");
|
||||
}
|
||||
|
||||
function lock(item: Item): void {
|
||||
locks.add(item);
|
||||
}
|
||||
@@ -352,14 +349,13 @@ Singleton {
|
||||
if (!notification)
|
||||
return;
|
||||
|
||||
id = notification.id;
|
||||
notifId = notification.id;
|
||||
summary = notification.summary;
|
||||
body = notification.body;
|
||||
appIcon = notification.appIcon;
|
||||
appName = notification.appName;
|
||||
image = notification.image;
|
||||
if (notification?.image)
|
||||
dummyImageLoader.active = true;
|
||||
imageSource = notification.image || "";
|
||||
image = imageSource;
|
||||
expireTimeout = notification.expireTimeout;
|
||||
urgency = notification.urgency;
|
||||
resident = notification.resident;
|
||||
@@ -369,6 +365,8 @@ Singleton {
|
||||
text: a.text,
|
||||
invoke: () => a.invoke()
|
||||
}));
|
||||
|
||||
cacheImageIfNeeded();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,6 +229,7 @@ Variants {
|
||||
id: notifsBg
|
||||
|
||||
panel: panels.notifications
|
||||
radius: Appearance.rounding.normal
|
||||
}
|
||||
|
||||
PanelBg {
|
||||
|
||||
@@ -20,7 +20,7 @@ Item {
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
implicitHeight: content.implicitHeight
|
||||
implicitWidth: content.implicitWidth || 854 // Hard coded fallback for first open
|
||||
implicitWidth: content.implicitWidth || 854
|
||||
opacity: 1 - offsetScale
|
||||
visible: offsetScale < 1
|
||||
|
||||
|
||||
@@ -9,18 +9,17 @@ Item {
|
||||
id: root
|
||||
|
||||
property int contentHeight
|
||||
property real offsetScale: shouldBeActive ? 0 : 1
|
||||
required property var panels
|
||||
required property ShellScreen screen
|
||||
readonly property bool shouldBeActive: visibilities.dock && Config.dock.enable
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
readonly property bool shouldBeActive: visibilities.dock
|
||||
property real offsetScale: shouldBeActive ? 0 : 1
|
||||
|
||||
visible: offsetScale < 1
|
||||
anchors.bottomMargin: (-implicitHeight - 5) * offsetScale
|
||||
implicitHeight: content.implicitHeight
|
||||
implicitWidth: content.implicitWidth || 400
|
||||
opacity: 1 - offsetScale
|
||||
visible: offsetScale < 1
|
||||
|
||||
Behavior on offsetScale {
|
||||
Anim {
|
||||
@@ -32,10 +31,10 @@ Item {
|
||||
Loader {
|
||||
id: content
|
||||
|
||||
active: root.shouldBeActive || root.visible
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
|
||||
active: root.shouldBeActive || root.visible
|
||||
asynchronous: true
|
||||
|
||||
sourceComponent: Content {
|
||||
panels: root.panels
|
||||
|
||||
@@ -47,6 +47,7 @@ Item {
|
||||
active: Qt.binding(() => root.shouldBeActive || root.visible)
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
asynchronous: true
|
||||
height: content.contentItem.height
|
||||
opacity: root.expanded ? 0 : 1
|
||||
|
||||
@@ -65,6 +66,7 @@ Item {
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
asynchronous: true
|
||||
opacity: root.expanded ? 1 : 0
|
||||
|
||||
Behavior on opacity {
|
||||
|
||||
@@ -8,7 +8,7 @@ import QtQuick
|
||||
Item {
|
||||
id: root
|
||||
|
||||
readonly property int padding: 6
|
||||
readonly property int padding: Appearance.padding.smaller
|
||||
required property Item panels
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
@@ -54,7 +54,7 @@ Item {
|
||||
anchors.fill: parent
|
||||
anchors.margins: root.padding
|
||||
color: "transparent"
|
||||
radius: Appearance.rounding.smallest / 2
|
||||
radius: Appearance.rounding.normal - root.padding
|
||||
|
||||
CustomListView {
|
||||
id: list
|
||||
@@ -72,7 +72,7 @@ Item {
|
||||
required property NotifServer.Notif modelData
|
||||
readonly property alias nonAnimHeight: notif.nonAnimHeight
|
||||
|
||||
implicitHeight: notif.implicitHeight + (idx === 0 ? 0 : 8)
|
||||
implicitHeight: notif.implicitHeight + (idx === 0 ? 0 : Appearance.spacing.small)
|
||||
implicitWidth: notif.implicitWidth
|
||||
|
||||
ListView.onRemove: removeAnim.start()
|
||||
@@ -151,48 +151,6 @@ Item {
|
||||
property: "y"
|
||||
}
|
||||
}
|
||||
|
||||
ExtraIndicator {
|
||||
anchors.top: parent.top
|
||||
extra: {
|
||||
const count = list.count;
|
||||
if (count === 0)
|
||||
return 0;
|
||||
|
||||
const scrollY = list.contentY;
|
||||
|
||||
let height = 0;
|
||||
for (let i = 0; i < count; i++) {
|
||||
height += (list.itemAtIndex(i)?.nonAnimHeight ?? 0) + 8;
|
||||
|
||||
if (height - 8 >= scrollY)
|
||||
return i;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
ExtraIndicator {
|
||||
anchors.bottom: parent.bottom
|
||||
extra: {
|
||||
const count = list.count;
|
||||
if (count === 0)
|
||||
return 0;
|
||||
|
||||
const scrollY = list.contentHeight - (list.contentY + list.height);
|
||||
|
||||
let height = 0;
|
||||
for (let i = count - 1; i >= 0; i--) {
|
||||
height += (list.itemAtIndex(i)?.nonAnimHeight ?? 0) + 8;
|
||||
|
||||
if (height - 8 >= scrollY)
|
||||
return count - i - 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ Item {
|
||||
anchors.right: parent.right
|
||||
anchors.top: searchBar.bottom
|
||||
anchors.topMargin: Appearance.spacing.smaller
|
||||
color: DynamicColors.tPalette.m3surfaceContainerLowest
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
radius: Appearance.rounding.normal
|
||||
|
||||
StackView {
|
||||
|
||||
+263
-25
@@ -1,15 +1,24 @@
|
||||
#include "writefile.hpp"
|
||||
|
||||
#include <QtConcurrent/qtconcurrentrun.h>
|
||||
#include <QtQuick/qquickimageprovider.h>
|
||||
#include <QtQuick/qquickitemgrabresult.h>
|
||||
#include <QtQuick/qquickwindow.h>
|
||||
|
||||
#include <qdir.h>
|
||||
#include <qfile.h>
|
||||
#include <qfileinfo.h>
|
||||
#include <qfuturewatcher.h>
|
||||
#include <qimage.h>
|
||||
#include <qjsengine.h>
|
||||
#include <qqmlengine.h>
|
||||
|
||||
namespace ZShell {
|
||||
|
||||
// ============================================================
|
||||
// saveItem
|
||||
// ============================================================
|
||||
|
||||
void ZShellIo::saveItem(QQuickItem* target, const QUrl& path) {
|
||||
this->saveItem(target, path, QRect(), QJSValue(), QJSValue());
|
||||
}
|
||||
@@ -30,7 +39,13 @@ void ZShellIo::saveItem(QQuickItem* target, const QUrl& path, const QRect& rect,
|
||||
this->saveItem(target, path, rect, onSaved, QJSValue());
|
||||
}
|
||||
|
||||
void ZShellIo::saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved, QJSValue onFailed) {
|
||||
void ZShellIo::saveItem(
|
||||
QQuickItem* target,
|
||||
const QUrl& path,
|
||||
const QRect& rect,
|
||||
QJSValue onSaved,
|
||||
QJSValue onFailed
|
||||
) {
|
||||
if (!target) {
|
||||
qWarning() << "ZShellIo::saveItem: a target is required";
|
||||
return;
|
||||
@@ -42,77 +57,298 @@ void ZShellIo::saveItem(QQuickItem* target, const QUrl& path, const QRect& rect,
|
||||
}
|
||||
|
||||
if (!target->window()) {
|
||||
qWarning() << "ZShellIo::saveItem: unable to save target" << target << "without a window";
|
||||
qWarning() << "ZShellIo::saveItem: unable to save target"
|
||||
<< target
|
||||
<< "without a window";
|
||||
return;
|
||||
}
|
||||
|
||||
auto scaledRect = rect;
|
||||
|
||||
const qreal scale = target->window()->devicePixelRatio();
|
||||
|
||||
if (rect.isValid() && !qFuzzyCompare(scale + 1.0, 2.0)) {
|
||||
scaledRect =
|
||||
QRectF(rect.left() * scale, rect.top() * scale, rect.width() * scale, rect.height() * scale).toRect();
|
||||
scaledRect = QRectF(
|
||||
rect.left() * scale,
|
||||
rect.top() * scale,
|
||||
rect.width() * scale,
|
||||
rect.height() * scale
|
||||
).toRect();
|
||||
}
|
||||
|
||||
const QSharedPointer<const QQuickItemGrabResult> grabResult = target->grabToImage();
|
||||
const QSharedPointer<const QQuickItemGrabResult> grabResult =
|
||||
target->grabToImage();
|
||||
|
||||
QObject::connect(grabResult.data(), &QQuickItemGrabResult::ready, this,
|
||||
QObject::connect(
|
||||
grabResult.data(),
|
||||
&QQuickItemGrabResult::ready,
|
||||
this,
|
||||
[grabResult, scaledRect, path, onSaved, onFailed, this]() {
|
||||
const auto future = QtConcurrent::run([=]() {
|
||||
|
||||
QImage image = grabResult->image();
|
||||
|
||||
if (scaledRect.isValid()) {
|
||||
image = image.copy(scaledRect);
|
||||
}
|
||||
|
||||
const auto future = QtConcurrent::run([image, path]() {
|
||||
|
||||
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, [=]() {
|
||||
QObject::connect(
|
||||
watcher,
|
||||
&QFutureWatcher<bool>::finished,
|
||||
this,
|
||||
[=]() {
|
||||
|
||||
if (watcher->result()) {
|
||||
|
||||
if (onSaved.isCallable()) {
|
||||
onSaved.call(
|
||||
{ QJSValue(path.toLocalFile()), engine->toScriptValue(QVariant::fromValue(path)) });
|
||||
onSaved.call({
|
||||
QJSValue(path.toLocalFile()),
|
||||
engine->toScriptValue(QVariant::fromValue(path))
|
||||
});
|
||||
}
|
||||
|
||||
} else {
|
||||
qWarning() << "ZShellIo::saveItem: failed to save" << path;
|
||||
|
||||
qWarning() << "ZShellIo::saveItem: failed to save"
|
||||
<< path;
|
||||
|
||||
if (onFailed.isCallable()) {
|
||||
onFailed.call({ engine->toScriptValue(QVariant::fromValue(path)) });
|
||||
onFailed.call({
|
||||
engine->toScriptValue(QVariant::fromValue(path))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
watcher->deleteLater();
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
watcher->setFuture(future);
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
bool ZShellIo::copyFile(const QUrl& source, const QUrl& target, bool overwrite) const {
|
||||
if (!source.isLocalFile()) {
|
||||
qWarning() << "ZShellIo::copyFile: source" << source << "is not a local file";
|
||||
// ============================================================
|
||||
// saveImage
|
||||
// ============================================================
|
||||
|
||||
void ZShellIo::saveImage(const QUrl& source, const QUrl& target) {
|
||||
this->saveImage(source, target, QJSValue(), QJSValue());
|
||||
}
|
||||
|
||||
void ZShellIo::saveImage(const QUrl& source, const QUrl& target, QJSValue onSaved) {
|
||||
this->saveImage(source, target, onSaved, QJSValue());
|
||||
}
|
||||
|
||||
void ZShellIo::saveImage(
|
||||
const QUrl& source,
|
||||
const QUrl& target,
|
||||
QJSValue onSaved,
|
||||
QJSValue onFailed
|
||||
) {
|
||||
auto* engine = qmlEngine(this);
|
||||
|
||||
const auto future = QtConcurrent::run([this, source, target]() {
|
||||
return this->saveImageInternal(source, target);
|
||||
});
|
||||
|
||||
auto* watcher = new QFutureWatcher<bool>(this);
|
||||
|
||||
QObject::connect(
|
||||
watcher,
|
||||
&QFutureWatcher<bool>::finished,
|
||||
this,
|
||||
[=]() {
|
||||
|
||||
if (watcher->result()) {
|
||||
|
||||
if (onSaved.isCallable()) {
|
||||
onSaved.call({
|
||||
QJSValue(target.toLocalFile()),
|
||||
engine->toScriptValue(QVariant::fromValue(target))
|
||||
});
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
qWarning() << "ZShellIo::saveImage: failed to save"
|
||||
<< source
|
||||
<< "to"
|
||||
<< target;
|
||||
|
||||
if (onFailed.isCallable()) {
|
||||
onFailed.call({
|
||||
engine->toScriptValue(QVariant::fromValue(target))
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
watcher->deleteLater();
|
||||
}
|
||||
);
|
||||
|
||||
watcher->setFuture(future);
|
||||
}
|
||||
|
||||
bool ZShellIo::saveImageInternal(const QUrl& source, const QUrl& target) const {
|
||||
|
||||
if (!target.isLocalFile()) {
|
||||
qWarning() << "ZShellIo::saveImage: target"
|
||||
<< target
|
||||
<< "is not a local file";
|
||||
return false;
|
||||
}
|
||||
|
||||
const QString targetFile = target.toLocalFile();
|
||||
|
||||
if (!QDir().mkpath(QFileInfo(targetFile).absolutePath())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ========================================================
|
||||
// Local file path
|
||||
// ========================================================
|
||||
|
||||
if (source.isLocalFile()) {
|
||||
|
||||
QFile::remove(targetFile);
|
||||
|
||||
return QFile::copy(
|
||||
source.toLocalFile(),
|
||||
targetFile
|
||||
);
|
||||
}
|
||||
|
||||
// ========================================================
|
||||
// image:// provider path
|
||||
// ========================================================
|
||||
|
||||
if (source.scheme() == "image") {
|
||||
|
||||
auto* engine = qmlEngine(const_cast<ZShellIo*>(this));
|
||||
|
||||
if (!engine) {
|
||||
qWarning() << "ZShellIo::saveImage: no QQmlEngine";
|
||||
return false;
|
||||
}
|
||||
|
||||
const QString providerId = source.host();
|
||||
|
||||
const QString imageId =
|
||||
source.path().startsWith('/')
|
||||
? source.path().mid(1)
|
||||
: source.path();
|
||||
|
||||
auto* providerBase =
|
||||
engine->imageProvider(providerId);
|
||||
|
||||
if (!providerBase) {
|
||||
qWarning() << "ZShellIo::saveImage: provider not found"
|
||||
<< providerId;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* provider =
|
||||
dynamic_cast<QQuickImageProvider*>(providerBase);
|
||||
|
||||
if (!provider) {
|
||||
qWarning() << "ZShellIo::saveImage: provider is not a QQuickImageProvider"
|
||||
<< providerId;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!provider) {
|
||||
qWarning() << "ZShellIo::saveImage: provider not found"
|
||||
<< providerId;
|
||||
return false;
|
||||
}
|
||||
|
||||
QSize size;
|
||||
QImage image;
|
||||
|
||||
switch (provider->imageType()) {
|
||||
|
||||
case QQuickImageProvider::Image:
|
||||
image = provider->requestImage(
|
||||
imageId,
|
||||
&size,
|
||||
QSize()
|
||||
);
|
||||
break;
|
||||
|
||||
case QQuickImageProvider::Pixmap:
|
||||
image = provider->requestPixmap(
|
||||
imageId,
|
||||
&size,
|
||||
QSize()
|
||||
).toImage();
|
||||
break;
|
||||
|
||||
default:
|
||||
qWarning() << "ZShellIo::saveImage: unsupported provider type";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (image.isNull()) {
|
||||
qWarning() << "ZShellIo::saveImage: provider returned null image";
|
||||
return false;
|
||||
}
|
||||
|
||||
return image.save(targetFile);
|
||||
}
|
||||
|
||||
qWarning() << "ZShellIo::saveImage: unsupported source"
|
||||
<< source;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// File ops
|
||||
// ============================================================
|
||||
|
||||
bool ZShellIo::copyFile(const QUrl& source, const QUrl& target, bool overwrite) const {
|
||||
|
||||
if (!source.isLocalFile()) {
|
||||
qWarning() << "ZShellIo::copyFile: source"
|
||||
<< source
|
||||
<< "is not a local file";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!target.isLocalFile()) {
|
||||
qWarning() << "ZShellIo::copyFile: target" << target << "is not a local file";
|
||||
qWarning() << "ZShellIo::copyFile: target"
|
||||
<< target
|
||||
<< "is not a local file";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (overwrite) {
|
||||
if (!QFile::remove(target.toLocalFile())) {
|
||||
qWarning() << "ZShellIo::copyFile: overwrite was specified but failed to remove" << target.toLocalFile();
|
||||
return false;
|
||||
}
|
||||
QFile::remove(target.toLocalFile());
|
||||
}
|
||||
|
||||
return QFile::copy(source.toLocalFile(), target.toLocalFile());
|
||||
return QFile::copy(
|
||||
source.toLocalFile(),
|
||||
target.toLocalFile()
|
||||
);
|
||||
}
|
||||
|
||||
bool ZShellIo::deleteFile(const QUrl& path) const {
|
||||
|
||||
if (!path.isLocalFile()) {
|
||||
qWarning() << "ZShellIo::deleteFile: path" << path << "is not a local file";
|
||||
qWarning() << "ZShellIo::deleteFile: path"
|
||||
<< path
|
||||
<< "is not a local file";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -120,8 +356,10 @@ bool ZShellIo::deleteFile(const QUrl& path) const {
|
||||
}
|
||||
|
||||
QString ZShellIo::toLocalFile(const QUrl& url) const {
|
||||
|
||||
if (!url.isLocalFile()) {
|
||||
qWarning() << "ZShellIo::toLocalFile: given url is not a local file" << url;
|
||||
qWarning() << "ZShellIo::toLocalFile: given url is not a local file"
|
||||
<< url;
|
||||
return QString();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,31 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <QtQuick/qquickitem.h>
|
||||
#include <qjsvalue.h>
|
||||
#include <qobject.h>
|
||||
#include <qqmlintegration.h>
|
||||
#include <qurl.h>
|
||||
|
||||
namespace ZShell {
|
||||
|
||||
class ZShellIo : public QObject {
|
||||
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_SINGLETON
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
QML_SINGLETON
|
||||
|
||||
public:
|
||||
// clang-format off
|
||||
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path);
|
||||
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect);
|
||||
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved);
|
||||
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved, QJSValue onFailed);
|
||||
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved);
|
||||
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved, QJSValue onFailed);
|
||||
// clang-format on
|
||||
// clang-format off
|
||||
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path);
|
||||
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect);
|
||||
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved);
|
||||
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, QJSValue onSaved, QJSValue onFailed);
|
||||
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved);
|
||||
Q_INVOKABLE void saveItem(QQuickItem* target, const QUrl& path, const QRect& rect, QJSValue onSaved, QJSValue onFailed);
|
||||
|
||||
Q_INVOKABLE bool copyFile(const QUrl& source, const QUrl& target, bool overwrite = true) const;
|
||||
Q_INVOKABLE bool deleteFile(const QUrl& path) const;
|
||||
Q_INVOKABLE QString toLocalFile(const QUrl& url) const;
|
||||
Q_INVOKABLE void saveImage(const QUrl& source, const QUrl& target);
|
||||
Q_INVOKABLE void saveImage(const QUrl& source, const QUrl& target, QJSValue onSaved);
|
||||
Q_INVOKABLE void saveImage(const QUrl& source, const QUrl& target, QJSValue onSaved, QJSValue onFailed);
|
||||
// clang-format on
|
||||
|
||||
Q_INVOKABLE bool copyFile(const QUrl& source, const QUrl& target, bool overwrite = true) const;
|
||||
Q_INVOKABLE bool deleteFile(const QUrl& path) const;
|
||||
Q_INVOKABLE QString toLocalFile(const QUrl& url) const;
|
||||
|
||||
private:
|
||||
bool saveImageInternal(const QUrl& source, const QUrl& target) const;
|
||||
};
|
||||
|
||||
|
||||
} // namespace ZShell
|
||||
|
||||
Reference in New Issue
Block a user