Skip to content

Commit 85be386

Browse files
committed
io/fileview: add FileView
1 parent 3a1eec0 commit 85be386

File tree

6 files changed

+635
-1
lines changed

6 files changed

+635
-1
lines changed

src/core/util.hpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#pragma once
22
#include <type_traits>
33

4+
#include <qtclasshelpermacros.h>
5+
46
// NOLINTBEGIN
57
#define DROP_EMIT(object, func) \
68
DropEmitter(object, static_cast<void (*)(typeof(object))>([](typeof(object) o) { o->func(); }))
@@ -72,6 +74,8 @@ class DropEmitter {
7274
DECLARE_MEMBER_GET(name); \
7375
DECLARE_MEMBER_SET(name, setter)
7476

77+
#define DECLARE_MEMBER_SETONLY(class, name, setter, member, signal) DECLARE_MEMBER(cl
78+
7579
#define DECLARE_MEMBER_FULL(class, name, setter, member, signal) \
7680
DECLARE_MEMBER(class, name, member, signal); \
7781
DECLARE_MEMBER_GETSET(name, setter)
@@ -123,6 +127,8 @@ public:
123127
#define DEFINE_MEMBER_GETSET(Class, name, setter) \
124128
DEFINE_MEMBER_GET(Class, name) \
125129
DEFINE_MEMBER_SET(Class, name, setter)
130+
131+
#define MEMBER_EMIT(name) std::remove_reference_t<decltype(*this)>::M_##name::emitter(this)
126132
// NOLINTEND
127133

128134
template <typename T>
@@ -154,6 +160,12 @@ class MemberMetadata {
154160
} else {
155161
if (MemberMetadata::get(obj) == value) return DropEmitter();
156162
obj->*member = value;
163+
return MemberMetadata::emitter(obj);
164+
}
165+
}
166+
167+
static Ret emitter(Class* obj) {
168+
if constexpr (signal != nullptr) {
157169
return DropEmitter(obj, &MemberMetadata::emitForObject);
158170
}
159171
}
@@ -170,3 +182,32 @@ class PseudomemberMetadata {
170182
using Ref = const Type&;
171183
using Ret = std::conditional_t<hasSignal, DropEmitter, void>;
172184
};
185+
186+
class GuardedEmitBlocker {
187+
public:
188+
explicit GuardedEmitBlocker(bool* var): var(var) { *this->var = true; }
189+
~GuardedEmitBlocker() { *this->var = false; }
190+
Q_DISABLE_COPY_MOVE(GuardedEmitBlocker);
191+
192+
private:
193+
bool* var;
194+
};
195+
196+
template <auto signal>
197+
class GuardedEmitter {
198+
using Traits = MemberPointerTraits<decltype(signal)>;
199+
using Class = Traits::Class;
200+
201+
bool blocked = false;
202+
203+
public:
204+
GuardedEmitter() = default;
205+
~GuardedEmitter() = default;
206+
Q_DISABLE_COPY_MOVE(GuardedEmitter);
207+
208+
void call(Class* obj) {
209+
if (!this->blocked) (obj->*signal)();
210+
}
211+
212+
GuardedEmitBlocker block() { return GuardedEmitBlocker(&this->blocked); }
213+
};

src/io/CMakeLists.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
qt_add_library(quickshell-io STATIC
22
datastream.cpp
33
process.cpp
4+
fileview.cpp
45
)
56

67
add_library(quickshell-io-init OBJECT init.cpp)
@@ -9,7 +10,12 @@ if (SOCKETS)
910
target_sources(quickshell-io PRIVATE socket.cpp)
1011
endif()
1112

12-
qt_add_qml_module(quickshell-io URI Quickshell.Io VERSION 0.1)
13+
qt_add_qml_module(quickshell-io
14+
URI Quickshell.Io
15+
VERSION 0.1
16+
QML_FILES
17+
FileView.qml
18+
)
1319

1420
target_link_libraries(quickshell-io PRIVATE ${QT_DEPS})
1521
target_link_libraries(quickshell-io-init PRIVATE ${QT_DEPS})

src/io/FileView.qml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import Quickshell.Io
2+
3+
FileViewInternal {
4+
property bool preload: this.__preload;
5+
property bool blockLoading: this.__blockLoading;
6+
property bool blockAllReads: this.__blockAllReads;
7+
property string path: this.__path;
8+
9+
onPreloadChanged: this.__preload = preload;
10+
onBlockLoadingChanged: this.__blockLoading = this.blockLoading;
11+
onBlockAllReadsChanged: this.__blockAllReads = this.blockAllReads;
12+
13+
// Unfortunately path can't be kept as an empty string until the file loads
14+
// without using QQmlPropertyValueInterceptor which is private. If we lean fully
15+
// into using private code in the future, there will be no reason not to do it here.
16+
17+
onPathChanged: {
18+
if (!this.preload) this.__preload = false;
19+
this.__path = this.path;
20+
if (this.preload) this.__preload = true;
21+
}
22+
23+
// The C++ side can't force bindings to be resolved in a specific order so
24+
// its done here. Functions are used to avoid the eager loading aspect of properties.
25+
26+
// Preload is set as it is below to avoid starting an async read from a preload
27+
// if the user wants an initial blocking read.
28+
29+
function text(): string {
30+
if (!this.preload) this.__preload = false;
31+
this.__blockLoading = this.blockLoading;
32+
this.__blockAllReads = this.blockAllReads;
33+
this.__path = this.path;
34+
const text = this.__text;
35+
if (this.preload) this.__preload = true;
36+
return text;
37+
}
38+
39+
function data(): string {
40+
if (!this.preload) this.__preload = false;
41+
this.__blockLoading = this.blockLoading;
42+
this.__blockAllReads = this.blockAllReads;
43+
this.__path = this.path;
44+
const data = this.__data;
45+
if (this.preload) this.__preload = true;
46+
return data;
47+
}
48+
}

0 commit comments

Comments
 (0)