forked from quickshell/quickshell
io/fileview: add support for watching changes
This commit is contained in:
parent
ccf885081c
commit
69d13967c9
2 changed files with 71 additions and 3 deletions
|
@ -6,6 +6,7 @@
|
||||||
#include <qdir.h>
|
#include <qdir.h>
|
||||||
#include <qfiledevice.h>
|
#include <qfiledevice.h>
|
||||||
#include <qfileinfo.h>
|
#include <qfileinfo.h>
|
||||||
|
#include <qfilesystemwatcher.h>
|
||||||
#include <qlogging.h>
|
#include <qlogging.h>
|
||||||
#include <qloggingcategory.h>
|
#include <qloggingcategory.h>
|
||||||
#include <qmutex.h>
|
#include <qmutex.h>
|
||||||
|
@ -280,7 +281,6 @@ void FileViewWriter::write(
|
||||||
if (shouldCancel.loadAcquire()) return;
|
if (shouldCancel.loadAcquire()) return;
|
||||||
|
|
||||||
if (doAtomicWrite) {
|
if (doAtomicWrite) {
|
||||||
qDebug() << "Atomic commit";
|
|
||||||
if (!reinterpret_cast<QSaveFile*>(file.get())->commit()) { // NOLINT
|
if (!reinterpret_cast<QSaveFile*>(file.get())->commit()) { // NOLINT
|
||||||
qmlWarning(view) << "Write of " << state.path << " failed: Atomic commit failed.";
|
qmlWarning(view) << "Write of " << state.path << " failed: Atomic commit failed.";
|
||||||
}
|
}
|
||||||
|
@ -477,6 +477,49 @@ void FileView::updatePath() {
|
||||||
} else {
|
} else {
|
||||||
this->emitDataChanged();
|
this->emitDataChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->updateWatchedFiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileView::updateWatchedFiles() {
|
||||||
|
delete this->watcher;
|
||||||
|
|
||||||
|
if (!this->targetPath.isEmpty() && this->bWatchChanges) {
|
||||||
|
qCDebug(logFileView) << "Creating watcher for" << this << "at" << this->targetPath;
|
||||||
|
this->watcher = new QFileSystemWatcher(this);
|
||||||
|
this->watcher->addPath(this->targetPath);
|
||||||
|
this->watcher->addPath(QDir(this->targetPath).dirName());
|
||||||
|
|
||||||
|
QObject::connect(
|
||||||
|
this->watcher,
|
||||||
|
&QFileSystemWatcher::fileChanged,
|
||||||
|
this,
|
||||||
|
&FileView::onWatchedFileChanged
|
||||||
|
);
|
||||||
|
|
||||||
|
QObject::connect(
|
||||||
|
this->watcher,
|
||||||
|
&QFileSystemWatcher::directoryChanged,
|
||||||
|
this,
|
||||||
|
&FileView::onWatchedDirectoryChanged
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileView::onWatchedFileChanged() {
|
||||||
|
if (!this->watcher->files().contains(this->targetPath)) {
|
||||||
|
this->watcher->addPath(this->targetPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit this->fileChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileView::onWatchedDirectoryChanged() {
|
||||||
|
if (!this->watcher->files().contains(this->targetPath) && QFileInfo(this->targetPath).exists()) {
|
||||||
|
// the file was just created
|
||||||
|
this->watcher->addPath(this->targetPath);
|
||||||
|
emit this->fileChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileView::shouldBlockRead() const {
|
bool FileView::shouldBlockRead() const {
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include <qatomic.h>
|
#include <qatomic.h>
|
||||||
#include <qdebug.h>
|
#include <qdebug.h>
|
||||||
|
#include <qfilesystemwatcher.h>
|
||||||
#include <qlogging.h>
|
#include <qlogging.h>
|
||||||
#include <qmutex.h>
|
#include <qmutex.h>
|
||||||
#include <qobject.h>
|
#include <qobject.h>
|
||||||
|
@ -187,13 +188,13 @@ class FileView: public QObject {
|
||||||
/// > [!WARNING] We cannot think of a valid use case for this.
|
/// > [!WARNING] We cannot think of a valid use case for this.
|
||||||
/// > You almost definitely want @@blockLoading.
|
/// > You almost definitely want @@blockLoading.
|
||||||
QSDOC_PROPERTY_OVERRIDE(bool blockAllReads READ blockAllReads WRITE setBlockAllReads NOTIFY blockAllReadsChanged);
|
QSDOC_PROPERTY_OVERRIDE(bool blockAllReads READ blockAllReads WRITE setBlockAllReads NOTIFY blockAllReadsChanged);
|
||||||
/// If true (default false), all calls to @@setText or @@setData will block the
|
/// If true (default false), all calls to @@setText() or @@setData() will block the
|
||||||
/// UI thread until the write succeeds or fails.
|
/// UI thread until the write succeeds or fails.
|
||||||
///
|
///
|
||||||
/// > [!WARNING] Blocking operations should be used carefully to avoid stutters and other performance
|
/// > [!WARNING] Blocking operations should be used carefully to avoid stutters and other performance
|
||||||
/// > degradations. Blocking means that your interface **WILL NOT FUNCTION** during the call.
|
/// > degradations. Blocking means that your interface **WILL NOT FUNCTION** during the call.
|
||||||
Q_PROPERTY(bool blockWrites READ default WRITE default NOTIFY blockWritesChanged BINDABLE bindableBlockWrites);
|
Q_PROPERTY(bool blockWrites READ default WRITE default NOTIFY blockWritesChanged BINDABLE bindableBlockWrites);
|
||||||
/// If true (default), all calls to @@setText or @@setData will be performed atomically,
|
/// If true (default), all calls to @@setText() or @@setData() will be performed atomically,
|
||||||
/// meaning if the write fails for any reason, the file will not be modified.
|
/// meaning if the write fails for any reason, the file will not be modified.
|
||||||
///
|
///
|
||||||
/// > [!NOTE] This works by creating another file with the desired content, and renaming
|
/// > [!NOTE] This works by creating another file with the desired content, and renaming
|
||||||
|
@ -202,6 +203,18 @@ class FileView: public QObject {
|
||||||
/// If true (default), read or write errors will be printed to the quickshell logs.
|
/// If true (default), read or write errors will be printed to the quickshell logs.
|
||||||
/// If false, all known errors will not be printed.
|
/// If false, all known errors will not be printed.
|
||||||
QSDOC_PROPERTY_OVERRIDE(bool printErrors READ default WRITE default NOTIFY printErrorsChanged);
|
QSDOC_PROPERTY_OVERRIDE(bool printErrors READ default WRITE default NOTIFY printErrorsChanged);
|
||||||
|
/// If true (defaule false), @@fileChanged() will be called whenever the content of the file
|
||||||
|
/// changes on disk, including when @@setText() or @@setData() are used.
|
||||||
|
///
|
||||||
|
/// > [!NOTE] You can reload the file's content whenever it changes on disk like so:
|
||||||
|
/// > ```qml
|
||||||
|
/// > FileView {
|
||||||
|
/// > // ...
|
||||||
|
/// > watchChanges: true
|
||||||
|
/// > onFileChanged: this.reload()
|
||||||
|
/// > }
|
||||||
|
/// > ```
|
||||||
|
Q_PROPERTY(bool watchChanges READ default WRITE default NOTIFY watchChangesChanged BINDABLE bindableWatchChanges);
|
||||||
|
|
||||||
QSDOC_HIDE Q_PROPERTY(QString __path READ path WRITE setPath NOTIFY pathChanged);
|
QSDOC_HIDE Q_PROPERTY(QString __path READ path WRITE setPath NOTIFY pathChanged);
|
||||||
QSDOC_HIDE Q_PROPERTY(QString __text READ text NOTIFY internalTextChanged);
|
QSDOC_HIDE Q_PROPERTY(QString __text READ text NOTIFY internalTextChanged);
|
||||||
|
@ -297,6 +310,7 @@ public:
|
||||||
[[nodiscard]] QBindable<bool> bindableAtomicWrites() { return &this->bAtomicWrites; }
|
[[nodiscard]] QBindable<bool> bindableAtomicWrites() { return &this->bAtomicWrites; }
|
||||||
|
|
||||||
[[nodiscard]] QBindable<bool> bindablePrintErrors() { return &this->bPrintErrors; }
|
[[nodiscard]] QBindable<bool> bindablePrintErrors() { return &this->bPrintErrors; }
|
||||||
|
[[nodiscard]] QBindable<bool> bindableWatchChanges() { return &this->bWatchChanges; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
/// Emitted if the file was loaded successfully.
|
/// Emitted if the file was loaded successfully.
|
||||||
|
@ -307,6 +321,8 @@ signals:
|
||||||
void saved();
|
void saved();
|
||||||
/// Emitted if the file failed to save.
|
/// Emitted if the file failed to save.
|
||||||
void saveFailed(qs::io::FileViewError::Enum error);
|
void saveFailed(qs::io::FileViewError::Enum error);
|
||||||
|
/// Emitted if the file changes on disk and @@watchChanges is true.
|
||||||
|
void fileChanged();
|
||||||
|
|
||||||
void pathChanged();
|
void pathChanged();
|
||||||
QSDOC_HIDE void internalTextChanged();
|
QSDOC_HIDE void internalTextChanged();
|
||||||
|
@ -320,6 +336,7 @@ signals:
|
||||||
void blockWritesChanged();
|
void blockWritesChanged();
|
||||||
void atomicWritesChanged();
|
void atomicWritesChanged();
|
||||||
void printErrorsChanged();
|
void printErrorsChanged();
|
||||||
|
void watchChangesChanged();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void operationFinished();
|
void operationFinished();
|
||||||
|
@ -332,6 +349,9 @@ private:
|
||||||
void saveSync();
|
void saveSync();
|
||||||
void updateState(FileViewState& newState);
|
void updateState(FileViewState& newState);
|
||||||
void updatePath();
|
void updatePath();
|
||||||
|
void updateWatchedFiles();
|
||||||
|
void onWatchedFileChanged();
|
||||||
|
void onWatchedDirectoryChanged();
|
||||||
|
|
||||||
[[nodiscard]] bool shouldBlockRead() const;
|
[[nodiscard]] bool shouldBlockRead() const;
|
||||||
[[nodiscard]] FileViewReader* liveReader() const;
|
[[nodiscard]] FileViewReader* liveReader() const;
|
||||||
|
@ -353,6 +373,8 @@ private:
|
||||||
bool mBlockLoading = false;
|
bool mBlockLoading = false;
|
||||||
bool mBlockAllReads = false;
|
bool mBlockAllReads = false;
|
||||||
|
|
||||||
|
QFileSystemWatcher* watcher = nullptr;
|
||||||
|
|
||||||
GuardedEmitter<&FileView::internalTextChanged> textChangedEmitter;
|
GuardedEmitter<&FileView::internalTextChanged> textChangedEmitter;
|
||||||
GuardedEmitter<&FileView::internalDataChanged> dataChangedEmitter;
|
GuardedEmitter<&FileView::internalDataChanged> dataChangedEmitter;
|
||||||
void emitDataChanged();
|
void emitDataChanged();
|
||||||
|
@ -374,8 +396,11 @@ public:
|
||||||
Q_OBJECT_BINDABLE_PROPERTY(FileView, bool, bBlockWrites, &FileView::blockWritesChanged);
|
Q_OBJECT_BINDABLE_PROPERTY(FileView, bool, bBlockWrites, &FileView::blockWritesChanged);
|
||||||
Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(FileView, bool, bAtomicWrites, true, &FileView::atomicWritesChanged);
|
Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(FileView, bool, bAtomicWrites, true, &FileView::atomicWritesChanged);
|
||||||
Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(FileView, bool, bPrintErrors, true, &FileView::printErrorsChanged);
|
Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(FileView, bool, bPrintErrors, true, &FileView::printErrorsChanged);
|
||||||
|
Q_OBJECT_BINDABLE_PROPERTY(FileView, bool, bWatchChanges, &FileView::watchChangesChanged);
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
QS_BINDING_SUBSCRIBE_METHOD(FileView, bWatchChanges, updateWatchedFiles, onValueChanged);
|
||||||
|
|
||||||
void setPreload(bool preload);
|
void setPreload(bool preload);
|
||||||
void setBlockLoading(bool blockLoading);
|
void setBlockLoading(bool blockLoading);
|
||||||
void setBlockAllReads(bool blockAllReads);
|
void setBlockAllReads(bool blockAllReads);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue