Skip to content

Commit fee4942

Browse files
committed
io/fileview: add adapter support and JsonAdapter
1 parent cb69c2d commit fee4942

File tree

6 files changed

+487
-2
lines changed

6 files changed

+487
-2
lines changed

src/io/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ qt_add_library(quickshell-io STATIC
22
datastream.cpp
33
process.cpp
44
fileview.cpp
5+
jsonadapter.cpp
56
ipccomm.cpp
67
ipc.cpp
78
ipchandler.cpp

src/io/fileview.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,12 @@ void FileViewWriter::write(
289289
}
290290
}
291291

292+
FileView::~FileView() {
293+
if (this->mAdapter) {
294+
this->mAdapter->setFileView(nullptr);
295+
}
296+
}
297+
292298
void FileView::loadAsync(bool doStringConversion) {
293299
// Writes update via operationFinished, making a read both invalid and outdated.
294300
if (!this->liveOperation || this->pathInFlight != this->targetPath) {
@@ -636,4 +642,55 @@ void FileView::setBlockAllReads(bool blockAllReads) {
636642
}
637643
}
638644

645+
FileViewAdapter* FileView::adapter() const { return this->mAdapter; }
646+
647+
void FileView::setAdapter(FileViewAdapter* adapter) {
648+
if (adapter == this->mAdapter) return;
649+
650+
if (this->mAdapter) {
651+
this->mAdapter->setFileView(nullptr);
652+
QObject::disconnect(this->mAdapter, nullptr, this, nullptr);
653+
}
654+
655+
this->mAdapter = adapter;
656+
657+
if (adapter) {
658+
this->mAdapter->setFileView(this);
659+
QObject::connect(adapter, &FileViewAdapter::adapterUpdated, this, &FileView::adapterUpdated);
660+
QObject::connect(adapter, &QObject::destroyed, this, &FileView::onAdapterDestroyed);
661+
}
662+
663+
emit this->adapterChanged();
664+
}
665+
666+
void FileView::writeAdapter() {
667+
if (!this->mAdapter) {
668+
qmlWarning(this) << "Cannot call writeAdapter without an adapter.";
669+
return;
670+
}
671+
672+
this->setData(this->mAdapter->serializeAdapter());
673+
}
674+
675+
void FileView::onAdapterDestroyed() { this->mAdapter = nullptr; }
676+
677+
void FileViewAdapter::setFileView(FileView* fileView) {
678+
if (fileView == this->mFileView) return;
679+
680+
if (this->mFileView) {
681+
QObject::disconnect(this->mFileView, nullptr, this, nullptr);
682+
}
683+
684+
this->mFileView = fileView;
685+
686+
if (fileView) {
687+
QObject::connect(fileView, &FileView::dataChanged, this, &FileViewAdapter::onDataChanged);
688+
this->setFileView(fileView);
689+
} else {
690+
this->setFileView(nullptr);
691+
}
692+
}
693+
694+
void FileViewAdapter::onDataChanged() { this->deserializeAdapter(this->mFileView->data()); }
695+
639696
} // namespace qs::io

src/io/fileview.hpp

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <qqmlparserstatus.h>
1515
#include <qrunnable.h>
1616
#include <qstringview.h>
17+
#include <qtclasshelpermacros.h>
1718
#include <qtmetamacros.h>
1819

1920
#include "../core/doc.hpp"
@@ -140,11 +141,13 @@ class FileViewWriter: public FileViewOperation {
140141
bool doAtomicWrite;
141142
};
142143

143-
///! Simplified reader for small files.
144+
class FileViewAdapter;
145+
146+
///! Simple accessor for small files.
144147
/// A reader for small to medium files that don't need seeking/cursor access,
145148
/// suitable for most text files.
146149
///
147-
/// #### Example: Reading a JSON
150+
/// #### Example: Reading a JSON as text
148151
/// ```qml
149152
/// FileView {
150153
/// id: jsonFile
@@ -156,6 +159,8 @@ class FileViewWriter: public FileViewOperation {
156159
///
157160
/// readonly property var jsonData: JSON.parse(jsonFile.text())
158161
/// ```
162+
///
163+
/// Also see @@JsonAdapter for an alternative way to handle reading and writing JSON files.
159164
class FileView: public QObject {
160165
Q_OBJECT;
161166
// clang-format off
@@ -215,6 +220,14 @@ class FileView: public QObject {
215220
/// > }
216221
/// > ```
217222
Q_PROPERTY(bool watchChanges READ default WRITE default NOTIFY watchChangesChanged BINDABLE bindableWatchChanges);
223+
/// In addition to directly reading/writing the file as text, *adapters* can be used to
224+
/// expose a file's content in new ways.
225+
///
226+
/// An adapter will automatically be given the loaded file's content.
227+
/// Its state may be saved with @@writeAdapter().
228+
///
229+
/// Currently the only adapter is @@JsonAdapter.
230+
Q_PROPERTY(FileViewAdapter* adapter READ adapter WRITE setAdapter NOTIFY adapterChanged);
218231

219232
QSDOC_HIDE Q_PROPERTY(QString __path READ path WRITE setPath NOTIFY pathChanged);
220233
QSDOC_HIDE Q_PROPERTY(QString __text READ text NOTIFY internalTextChanged);
@@ -230,11 +243,14 @@ class FileView: public QObject {
230243
QSDOC_HIDE Q_PROPERTY(bool __blockAllReads READ blockAllReads WRITE setBlockAllReads NOTIFY blockAllReadsChanged);
231244
QSDOC_HIDE Q_PROPERTY(bool __printErrors READ default WRITE default NOTIFY printErrorsChanged BINDABLE bindablePrintErrors);
232245
// clang-format on
246+
Q_CLASSINFO("DefaultProperty", "adapter");
233247
QML_NAMED_ELEMENT(FileViewInternal);
234248
QSDOC_NAMED_ELEMENT(FileView);
235249

236250
public:
237251
explicit FileView(QObject* parent = nullptr): QObject(parent) {}
252+
~FileView() override;
253+
Q_DISABLE_COPY_MOVE(FileView);
238254

239255
/// Returns the data of the file specified by @@path as text.
240256
///
@@ -280,6 +296,8 @@ class FileView: public QObject {
280296
/// This will not block if @@blockLoading is set, only if @@blockAllReads is true.
281297
/// It acts the same as changing @@path to a new file, except loading the same file.
282298
Q_INVOKABLE void reload();
299+
/// Write the content of the current @@adapter to the selected file.
300+
Q_INVOKABLE void writeAdapter();
283301

284302
[[nodiscard]] QString path() const;
285303
void setPath(const QString& path);
@@ -312,6 +330,9 @@ class FileView: public QObject {
312330
[[nodiscard]] QBindable<bool> bindablePrintErrors() { return &this->bPrintErrors; }
313331
[[nodiscard]] QBindable<bool> bindableWatchChanges() { return &this->bWatchChanges; }
314332

333+
[[nodiscard]] FileViewAdapter* adapter() const;
334+
void setAdapter(FileViewAdapter* adapter);
335+
315336
signals:
316337
/// Emitted if the file was loaded successfully.
317338
void loaded();
@@ -323,6 +344,8 @@ class FileView: public QObject {
323344
void saveFailed(qs::io::FileViewError::Enum error);
324345
/// Emitted if the file changes on disk and @@watchChanges is true.
325346
void fileChanged();
347+
/// Emitted when the active @@adapter$'s data is changed.
348+
void adapterUpdated();
326349

327350
void pathChanged();
328351
QSDOC_HIDE void internalTextChanged();
@@ -337,9 +360,11 @@ class FileView: public QObject {
337360
void atomicWritesChanged();
338361
void printErrorsChanged();
339362
void watchChangesChanged();
363+
void adapterChanged();
340364

341365
private slots:
342366
void operationFinished();
367+
void onAdapterDestroyed();
343368

344369
private:
345370
void loadAsync(bool doStringConversion);
@@ -373,6 +398,7 @@ private slots:
373398
bool mBlockLoading = false;
374399
bool mBlockAllReads = false;
375400

401+
FileViewAdapter* mAdapter = nullptr;
376402
QFileSystemWatcher* watcher = nullptr;
377403

378404
GuardedEmitter<&FileView::internalTextChanged> textChangedEmitter;
@@ -406,4 +432,26 @@ private slots:
406432
void setBlockAllReads(bool blockAllReads);
407433
};
408434

435+
/// See @@FileView.adapter.
436+
class FileViewAdapter: public QObject {
437+
Q_OBJECT;
438+
QML_ELEMENT;
439+
QML_UNCREATABLE("");
440+
441+
public:
442+
void setFileView(FileView* fileView);
443+
virtual void deserializeAdapter(const QByteArray& data) = 0;
444+
[[nodiscard]] virtual QByteArray serializeAdapter() = 0;
445+
446+
signals:
447+
/// This signal is fired when data in the adapter changes, and triggers @@FileView.adapterUpdated(s).
448+
void adapterUpdated();
449+
450+
private slots:
451+
void onDataChanged();
452+
453+
protected:
454+
FileView* mFileView = nullptr;
455+
};
456+
409457
} // namespace qs::io

0 commit comments

Comments
 (0)