forked from quickshell/quickshell
feat: completely redesign hot reloader
The hot reloader previously attempted to figure out which parent a component would attach to as it loaded. This was fairly error prone as it was heuristic based and didn't work as soon as you split definitions into multiple QML files. The new hot reloader functions by first completely building the widget tree, then applying the old tree to the first tree and pulling out usable values. Proxy windows now wait to appear until being reloaded. Additionally added support for `reloadableId` to help match a Reloadable to its value in the previous widget tree.
This commit is contained in:
parent
d6ed717c39
commit
1da43be6c0
|
@ -34,6 +34,7 @@ Checks: >
|
||||||
-readability-uppercase-literal-suffix,
|
-readability-uppercase-literal-suffix,
|
||||||
-readability-braces-around-statements,
|
-readability-braces-around-statements,
|
||||||
-readability-redundant-access-specifiers,
|
-readability-redundant-access-specifiers,
|
||||||
|
-readability-else-after-return,
|
||||||
tidyfox-*,
|
tidyfox-*,
|
||||||
CheckOptions:
|
CheckOptions:
|
||||||
performance-for-range-copy.WarnOnAllAutoCopies: true
|
performance-for-range-copy.WarnOnAllAutoCopies: true
|
||||||
|
|
|
@ -33,7 +33,7 @@ qt_add_executable(quickshell
|
||||||
src/cpp/variants.cpp
|
src/cpp/variants.cpp
|
||||||
src/cpp/rootwrapper.cpp
|
src/cpp/rootwrapper.cpp
|
||||||
src/cpp/proxywindow.cpp
|
src/cpp/proxywindow.cpp
|
||||||
src/cpp/scavenge.cpp
|
src/cpp/reload.cpp
|
||||||
src/cpp/rootwrapper.cpp
|
src/cpp/rootwrapper.cpp
|
||||||
src/cpp/qmlglobal.cpp
|
src/cpp/qmlglobal.cpp
|
||||||
src/cpp/qmlscreen.cpp
|
src/cpp/qmlscreen.cpp
|
||||||
|
|
|
@ -14,18 +14,11 @@
|
||||||
#include "proxywindow.hpp"
|
#include "proxywindow.hpp"
|
||||||
#include "qmlscreen.hpp"
|
#include "qmlscreen.hpp"
|
||||||
|
|
||||||
void ProxyShellWindow::earlyInit(QObject* old) {
|
void ProxyShellWindow::setupWindow() {
|
||||||
this->ProxyWindowBase::earlyInit(old);
|
|
||||||
|
|
||||||
QObject::connect(this->window, &QWindow::screenChanged, this, &ProxyShellWindow::screenChanged);
|
QObject::connect(this->window, &QWindow::screenChanged, this, &ProxyShellWindow::screenChanged);
|
||||||
|
|
||||||
this->shellWindow = LayerShellQt::Window::get(this->window);
|
this->shellWindow = LayerShellQt::Window::get(this->window);
|
||||||
|
|
||||||
// dont want to steal focus unless actually configured to
|
this->ProxyWindowBase::setupWindow();
|
||||||
this->shellWindow->setKeyboardInteractivity(LayerShellQt::Window::KeyboardInteractivityNone);
|
|
||||||
|
|
||||||
// this dosent reset if its unset
|
|
||||||
this->shellWindow->setExclusiveZone(0);
|
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
QObject::connect(this->shellWindow, &LayerShellQt::Window::anchorsChanged, this, &ProxyShellWindow::anchorsChanged);
|
QObject::connect(this->shellWindow, &LayerShellQt::Window::anchorsChanged, this, &ProxyShellWindow::anchorsChanged);
|
||||||
|
@ -38,26 +31,16 @@ void ProxyShellWindow::earlyInit(QObject* old) {
|
||||||
QObject::connect(this, &ProxyShellWindow::anchorsChanged, this, &ProxyShellWindow::updateExclusionZone);
|
QObject::connect(this, &ProxyShellWindow::anchorsChanged, this, &ProxyShellWindow::updateExclusionZone);
|
||||||
QObject::connect(this, &ProxyShellWindow::marginsChanged, this, &ProxyShellWindow::updateExclusionZone);
|
QObject::connect(this, &ProxyShellWindow::marginsChanged, this, &ProxyShellWindow::updateExclusionZone);
|
||||||
// clang-format on
|
// clang-format on
|
||||||
}
|
|
||||||
|
|
||||||
void ProxyShellWindow::componentComplete() {
|
this->window->setScreen(this->mScreen);
|
||||||
this->complete = true;
|
this->setAnchors(this->mAnchors);
|
||||||
|
this->setMargins(this->mMargins);
|
||||||
|
this->setExclusionMode(this->mExclusionMode); // also sets exclusion zone
|
||||||
|
this->setLayer(this->mLayer);
|
||||||
|
this->shellWindow->setScope(this->mScope);
|
||||||
|
this->setKeyboardFocus(this->mKeyboardFocus);
|
||||||
|
|
||||||
// The default anchor settings are a hazard because they cover the entire screen.
|
this->connected = true;
|
||||||
// We opt for 0 anchors by default to avoid blocking user input.
|
|
||||||
this->setAnchors(this->stagingAnchors);
|
|
||||||
this->updateExclusionZone();
|
|
||||||
|
|
||||||
// Make sure we signal changes from anchors, but only if this is a reload.
|
|
||||||
// If we do it on first load then it sends an extra change at 0px.
|
|
||||||
if (this->stagingAnchors.mLeft && this->stagingAnchors.mRight && this->width() != 0)
|
|
||||||
this->widthChanged(this->width());
|
|
||||||
if (this->stagingAnchors.mTop && this->stagingAnchors.mBottom && this->height() != 0)
|
|
||||||
this->heightChanged(this->height());
|
|
||||||
|
|
||||||
this->window->setVisible(this->stagingVisible);
|
|
||||||
|
|
||||||
this->ProxyWindowBase::componentComplete();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QQuickWindow* ProxyShellWindow::disownWindow() {
|
QQuickWindow* ProxyShellWindow::disownWindow() {
|
||||||
|
@ -65,54 +48,56 @@ QQuickWindow* ProxyShellWindow::disownWindow() {
|
||||||
return this->ProxyWindowBase::disownWindow();
|
return this->ProxyWindowBase::disownWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxyShellWindow::setVisible(bool visible) {
|
|
||||||
if (!this->complete) this->stagingVisible = visible;
|
|
||||||
else this->ProxyWindowBase::setVisible(visible);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ProxyShellWindow::isVisible() {
|
|
||||||
return this->complete ? this->ProxyWindowBase::isVisible() : this->stagingVisible;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProxyShellWindow::setWidth(qint32 width) {
|
void ProxyShellWindow::setWidth(qint32 width) {
|
||||||
this->requestedWidth = width;
|
this->mWidth = width;
|
||||||
|
|
||||||
// only update the actual size if not blocked by anchors
|
// only update the actual size if not blocked by anchors
|
||||||
auto anchors = this->anchors();
|
auto anchors = this->anchors();
|
||||||
if (this->complete && (!anchors.mLeft || !anchors.mRight)) this->ProxyWindowBase::setWidth(width);
|
if (!anchors.mLeft || !anchors.mRight) this->ProxyWindowBase::setWidth(width);
|
||||||
}
|
|
||||||
|
|
||||||
qint32 ProxyShellWindow::width() {
|
|
||||||
return this->complete ? this->ProxyWindowBase::width() : this->requestedWidth;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxyShellWindow::setHeight(qint32 height) {
|
void ProxyShellWindow::setHeight(qint32 height) {
|
||||||
this->requestedHeight = height;
|
this->mHeight = height;
|
||||||
|
|
||||||
// only update the actual size if not blocked by anchors
|
// only update the actual size if not blocked by anchors
|
||||||
auto anchors = this->anchors();
|
auto anchors = this->anchors();
|
||||||
if (this->complete && (!anchors.mTop || !anchors.mBottom))
|
if (!anchors.mTop || !anchors.mBottom) this->ProxyWindowBase::setHeight(height);
|
||||||
this->ProxyWindowBase::setHeight(height);
|
|
||||||
}
|
|
||||||
|
|
||||||
qint32 ProxyShellWindow::height() {
|
|
||||||
return this->complete ? this->ProxyWindowBase::height() : this->requestedHeight;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxyShellWindow::setScreen(QuickShellScreenInfo* screen) {
|
void ProxyShellWindow::setScreen(QuickShellScreenInfo* screen) {
|
||||||
this->window->setScreen(screen->screen);
|
if (this->mScreen != nullptr) {
|
||||||
|
QObject::disconnect(this->mScreen, nullptr, this, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* qscreen = screen == nullptr ? nullptr : screen->screen;
|
||||||
|
if (qscreen != nullptr) {
|
||||||
|
QObject::connect(qscreen, &QObject::destroyed, this, &ProxyShellWindow::onScreenDestroyed);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->window == nullptr) this->mScreen = qscreen;
|
||||||
|
else this->window->setScreen(qscreen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProxyShellWindow::onScreenDestroyed() { this->mScreen = nullptr; }
|
||||||
|
|
||||||
QuickShellScreenInfo* ProxyShellWindow::screen() const {
|
QuickShellScreenInfo* ProxyShellWindow::screen() const {
|
||||||
|
QScreen* qscreen = nullptr;
|
||||||
|
|
||||||
|
if (this->window == nullptr) {
|
||||||
|
if (this->mScreen != nullptr) qscreen = this->mScreen;
|
||||||
|
} else {
|
||||||
|
qscreen = this->window->screen();
|
||||||
|
}
|
||||||
|
|
||||||
return new QuickShellScreenInfo(
|
return new QuickShellScreenInfo(
|
||||||
const_cast<ProxyShellWindow*>(this), // NOLINT
|
const_cast<ProxyShellWindow*>(this), // NOLINT
|
||||||
this->window->screen()
|
qscreen
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxyShellWindow::setAnchors(Anchors anchors) {
|
void ProxyShellWindow::setAnchors(Anchors anchors) {
|
||||||
if (!this->complete) {
|
if (this->window == nullptr) {
|
||||||
this->stagingAnchors = anchors;
|
this->mAnchors = anchors;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,14 +107,14 @@ void ProxyShellWindow::setAnchors(Anchors anchors) {
|
||||||
if (anchors.mTop) lsAnchors |= LayerShellQt::Window::AnchorTop;
|
if (anchors.mTop) lsAnchors |= LayerShellQt::Window::AnchorTop;
|
||||||
if (anchors.mBottom) lsAnchors |= LayerShellQt::Window::AnchorBottom;
|
if (anchors.mBottom) lsAnchors |= LayerShellQt::Window::AnchorBottom;
|
||||||
|
|
||||||
if (!anchors.mLeft || !anchors.mRight) this->ProxyWindowBase::setWidth(this->requestedWidth);
|
if (!anchors.mLeft || !anchors.mRight) this->ProxyWindowBase::setWidth(this->mWidth);
|
||||||
if (!anchors.mTop || !anchors.mBottom) this->ProxyWindowBase::setHeight(this->requestedHeight);
|
if (!anchors.mTop || !anchors.mBottom) this->ProxyWindowBase::setHeight(this->mHeight);
|
||||||
|
|
||||||
this->shellWindow->setAnchors(lsAnchors);
|
this->shellWindow->setAnchors(lsAnchors);
|
||||||
}
|
}
|
||||||
|
|
||||||
Anchors ProxyShellWindow::anchors() const {
|
Anchors ProxyShellWindow::anchors() const {
|
||||||
if (!this->complete) return this->stagingAnchors;
|
if (this->window == nullptr) return this->mAnchors;
|
||||||
|
|
||||||
auto lsAnchors = this->shellWindow->anchors();
|
auto lsAnchors = this->shellWindow->anchors();
|
||||||
|
|
||||||
|
@ -144,25 +129,29 @@ Anchors ProxyShellWindow::anchors() const {
|
||||||
|
|
||||||
void ProxyShellWindow::setExclusiveZone(qint32 zone) {
|
void ProxyShellWindow::setExclusiveZone(qint32 zone) {
|
||||||
if (zone < 0) zone = 0;
|
if (zone < 0) zone = 0;
|
||||||
if (zone == this->requestedExclusionZone) return;
|
if (this->connected && zone == this->mExclusionZone) return;
|
||||||
this->requestedExclusionZone = zone;
|
this->mExclusionZone = zone;
|
||||||
|
|
||||||
if (this->exclusionMode() == ExclusionMode::Normal) {
|
if (this->window != nullptr && this->exclusionMode() == ExclusionMode::Normal) {
|
||||||
this->shellWindow->setExclusiveZone(zone);
|
this->shellWindow->setExclusiveZone(zone);
|
||||||
emit this->exclusionZoneChanged();
|
emit this->exclusionZoneChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qint32 ProxyShellWindow::exclusiveZone() const { return this->shellWindow->exclusionZone(); }
|
qint32 ProxyShellWindow::exclusiveZone() const {
|
||||||
|
if (this->window == nullptr) return this->mExclusionZone;
|
||||||
|
else return this->shellWindow->exclusionZone();
|
||||||
|
}
|
||||||
|
|
||||||
ExclusionMode::Enum ProxyShellWindow::exclusionMode() const { return this->mExclusionMode; }
|
ExclusionMode::Enum ProxyShellWindow::exclusionMode() const { return this->mExclusionMode; }
|
||||||
|
|
||||||
void ProxyShellWindow::setExclusionMode(ExclusionMode::Enum exclusionMode) {
|
void ProxyShellWindow::setExclusionMode(ExclusionMode::Enum exclusionMode) {
|
||||||
if (exclusionMode == this->mExclusionMode) return;
|
if (this->connected && exclusionMode == this->mExclusionMode) return;
|
||||||
this->mExclusionMode = exclusionMode;
|
this->mExclusionMode = exclusionMode;
|
||||||
|
|
||||||
|
if (this->window != nullptr) {
|
||||||
if (exclusionMode == ExclusionMode::Normal) {
|
if (exclusionMode == ExclusionMode::Normal) {
|
||||||
this->shellWindow->setExclusiveZone(this->requestedExclusionZone);
|
this->shellWindow->setExclusiveZone(this->mExclusionZone);
|
||||||
emit this->exclusionZoneChanged();
|
emit this->exclusionZoneChanged();
|
||||||
} else if (exclusionMode == ExclusionMode::Ignore) {
|
} else if (exclusionMode == ExclusionMode::Ignore) {
|
||||||
this->shellWindow->setExclusiveZone(-1);
|
this->shellWindow->setExclusiveZone(-1);
|
||||||
|
@ -170,14 +159,19 @@ void ProxyShellWindow::setExclusionMode(ExclusionMode::Enum exclusionMode) {
|
||||||
} else {
|
} else {
|
||||||
this->updateExclusionZone();
|
this->updateExclusionZone();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxyShellWindow::setMargins(Margins margins) {
|
void ProxyShellWindow::setMargins(Margins margins) {
|
||||||
|
if (this->window == nullptr) this->mMargins = margins;
|
||||||
|
else {
|
||||||
auto lsMargins = QMargins(margins.mLeft, margins.mTop, margins.mRight, margins.mBottom);
|
auto lsMargins = QMargins(margins.mLeft, margins.mTop, margins.mRight, margins.mBottom);
|
||||||
this->shellWindow->setMargins(lsMargins);
|
this->shellWindow->setMargins(lsMargins);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Margins ProxyShellWindow::margins() const {
|
Margins ProxyShellWindow::margins() const {
|
||||||
|
if (this->window == nullptr) return this->mMargins;
|
||||||
auto lsMargins = this->shellWindow->margins();
|
auto lsMargins = this->shellWindow->margins();
|
||||||
|
|
||||||
auto margins = Margins();
|
auto margins = Margins();
|
||||||
|
@ -190,6 +184,11 @@ Margins ProxyShellWindow::margins() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxyShellWindow::setLayer(Layer::Enum layer) {
|
void ProxyShellWindow::setLayer(Layer::Enum layer) {
|
||||||
|
if (this->window == nullptr) {
|
||||||
|
this->mLayer = layer;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto lsLayer = LayerShellQt::Window::LayerBackground;
|
auto lsLayer = LayerShellQt::Window::LayerBackground;
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
@ -205,6 +204,8 @@ void ProxyShellWindow::setLayer(Layer::Enum layer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Layer::Enum ProxyShellWindow::layer() const {
|
Layer::Enum ProxyShellWindow::layer() const {
|
||||||
|
if (this->window == nullptr) return this->mLayer;
|
||||||
|
|
||||||
auto layer = Layer::Top;
|
auto layer = Layer::Top;
|
||||||
auto lsLayer = this->shellWindow->layer();
|
auto lsLayer = this->shellWindow->layer();
|
||||||
|
|
||||||
|
@ -220,11 +221,22 @@ Layer::Enum ProxyShellWindow::layer() const {
|
||||||
return layer;
|
return layer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxyShellWindow::setScope(const QString& scope) { this->shellWindow->setScope(scope); }
|
void ProxyShellWindow::setScope(const QString& scope) {
|
||||||
|
if (this->window == nullptr) this->mScope = scope;
|
||||||
|
else this->shellWindow->setScope(scope);
|
||||||
|
}
|
||||||
|
|
||||||
QString ProxyShellWindow::scope() const { return this->shellWindow->scope(); }
|
QString ProxyShellWindow::scope() const {
|
||||||
|
if (this->window == nullptr) return this->mScope;
|
||||||
|
else return this->shellWindow->scope();
|
||||||
|
}
|
||||||
|
|
||||||
void ProxyShellWindow::setKeyboardFocus(KeyboardFocus::Enum focus) {
|
void ProxyShellWindow::setKeyboardFocus(KeyboardFocus::Enum focus) {
|
||||||
|
if (this->window == nullptr) {
|
||||||
|
this->mKeyboardFocus = focus;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto lsFocus = LayerShellQt::Window::KeyboardInteractivityNone;
|
auto lsFocus = LayerShellQt::Window::KeyboardInteractivityNone;
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
@ -239,6 +251,8 @@ void ProxyShellWindow::setKeyboardFocus(KeyboardFocus::Enum focus) {
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyboardFocus::Enum ProxyShellWindow::keyboardFocus() const {
|
KeyboardFocus::Enum ProxyShellWindow::keyboardFocus() const {
|
||||||
|
if (this->window == nullptr) return this->mKeyboardFocus;
|
||||||
|
|
||||||
auto focus = KeyboardFocus::None;
|
auto focus = KeyboardFocus::None;
|
||||||
auto lsFocus = this->shellWindow->keyboardInteractivity();
|
auto lsFocus = this->shellWindow->keyboardInteractivity();
|
||||||
|
|
||||||
|
@ -254,6 +268,11 @@ KeyboardFocus::Enum ProxyShellWindow::keyboardFocus() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxyShellWindow::setScreenConfiguration(ScreenConfiguration::Enum configuration) {
|
void ProxyShellWindow::setScreenConfiguration(ScreenConfiguration::Enum configuration) {
|
||||||
|
if (this->window == nullptr) {
|
||||||
|
this->mScreenConfiguration = configuration;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto lsConfiguration = LayerShellQt::Window::ScreenFromQWindow;
|
auto lsConfiguration = LayerShellQt::Window::ScreenFromQWindow;
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
@ -267,6 +286,8 @@ void ProxyShellWindow::setScreenConfiguration(ScreenConfiguration::Enum configur
|
||||||
}
|
}
|
||||||
|
|
||||||
ScreenConfiguration::Enum ProxyShellWindow::screenConfiguration() const {
|
ScreenConfiguration::Enum ProxyShellWindow::screenConfiguration() const {
|
||||||
|
if (this->window == nullptr) return this->mScreenConfiguration;
|
||||||
|
|
||||||
auto configuration = ScreenConfiguration::Window;
|
auto configuration = ScreenConfiguration::Window;
|
||||||
auto lsConfiguration = this->shellWindow->screenConfiguration();
|
auto lsConfiguration = this->shellWindow->screenConfiguration();
|
||||||
|
|
||||||
|
@ -280,14 +301,8 @@ ScreenConfiguration::Enum ProxyShellWindow::screenConfiguration() const {
|
||||||
return configuration;
|
return configuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxyShellWindow::setCloseOnDismissed(bool close) {
|
|
||||||
this->shellWindow->setCloseOnDismissed(close);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ProxyShellWindow::closeOnDismissed() const { return this->shellWindow->closeOnDismissed(); }
|
|
||||||
|
|
||||||
void ProxyShellWindow::updateExclusionZone() {
|
void ProxyShellWindow::updateExclusionZone() {
|
||||||
if (this->exclusionMode() == ExclusionMode::Auto) {
|
if (this->window != nullptr && this->exclusionMode() == ExclusionMode::Auto) {
|
||||||
auto anchors = this->anchors();
|
auto anchors = this->anchors();
|
||||||
|
|
||||||
auto zone = -1;
|
auto zone = -1;
|
||||||
|
|
|
@ -162,27 +162,18 @@ class ProxyShellWindow: public ProxyWindowBase {
|
||||||
/// The degree of keyboard focus taken. Defaults to `KeyboardFocus.None`.
|
/// The degree of keyboard focus taken. Defaults to `KeyboardFocus.None`.
|
||||||
Q_PROPERTY(KeyboardFocus::Enum keyboardFocus READ keyboardFocus WRITE setKeyboardFocus NOTIFY keyboardFocusChanged);
|
Q_PROPERTY(KeyboardFocus::Enum keyboardFocus READ keyboardFocus WRITE setKeyboardFocus NOTIFY keyboardFocusChanged);
|
||||||
Q_PROPERTY(ScreenConfiguration::Enum screenConfiguration READ screenConfiguration WRITE setScreenConfiguration);
|
Q_PROPERTY(ScreenConfiguration::Enum screenConfiguration READ screenConfiguration WRITE setScreenConfiguration);
|
||||||
Q_PROPERTY(bool closeOnDismissed READ closeOnDismissed WRITE setCloseOnDismissed);
|
|
||||||
QML_ELEMENT;
|
QML_ELEMENT;
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
protected:
|
|
||||||
void earlyInit(QObject* old) override;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void componentComplete() override;
|
void setupWindow() override;
|
||||||
QQuickWindow* disownWindow() override;
|
QQuickWindow* disownWindow() override;
|
||||||
|
|
||||||
QQmlListProperty<QObject> data();
|
QQmlListProperty<QObject> data();
|
||||||
|
|
||||||
void setVisible(bool visible) override;
|
|
||||||
bool isVisible() override;
|
|
||||||
|
|
||||||
void setWidth(qint32 width) override;
|
void setWidth(qint32 width) override;
|
||||||
qint32 width() override;
|
|
||||||
|
|
||||||
void setHeight(qint32 height) override;
|
void setHeight(qint32 height) override;
|
||||||
qint32 height() override;
|
|
||||||
|
|
||||||
void setScreen(QuickShellScreenInfo* screen);
|
void setScreen(QuickShellScreenInfo* screen);
|
||||||
[[nodiscard]] QuickShellScreenInfo* screen() const;
|
[[nodiscard]] QuickShellScreenInfo* screen() const;
|
||||||
|
@ -211,9 +202,6 @@ public:
|
||||||
void setScreenConfiguration(ScreenConfiguration::Enum configuration);
|
void setScreenConfiguration(ScreenConfiguration::Enum configuration);
|
||||||
[[nodiscard]] ScreenConfiguration::Enum screenConfiguration() const;
|
[[nodiscard]] ScreenConfiguration::Enum screenConfiguration() const;
|
||||||
|
|
||||||
void setCloseOnDismissed(bool close);
|
|
||||||
[[nodiscard]] bool closeOnDismissed() const;
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void screenChanged();
|
void screenChanged();
|
||||||
void anchorsChanged();
|
void anchorsChanged();
|
||||||
|
@ -225,20 +213,19 @@ signals:
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void updateExclusionZone();
|
void updateExclusionZone();
|
||||||
|
void onScreenDestroyed();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
LayerShellQt::Window* shellWindow = nullptr;
|
LayerShellQt::Window* shellWindow = nullptr;
|
||||||
bool anchorsInitialized = false;
|
QScreen* mScreen = nullptr;
|
||||||
ExclusionMode::Enum mExclusionMode = ExclusionMode::Normal;
|
ExclusionMode::Enum mExclusionMode = ExclusionMode::Normal;
|
||||||
qint32 requestedExclusionZone = 0;
|
qint32 mExclusionZone = 0;
|
||||||
|
Anchors mAnchors;
|
||||||
|
Margins mMargins;
|
||||||
|
Layer::Enum mLayer = Layer::Top;
|
||||||
|
QString mScope;
|
||||||
|
KeyboardFocus::Enum mKeyboardFocus = KeyboardFocus::None;
|
||||||
|
ScreenConfiguration::Enum mScreenConfiguration = ScreenConfiguration::Window;
|
||||||
|
|
||||||
// needed to ensure size dosent fuck up when changing layershell attachments
|
bool connected = false;
|
||||||
// along with setWidth and setHeight overrides
|
|
||||||
qint32 requestedWidth = 100;
|
|
||||||
qint32 requestedHeight = 100;
|
|
||||||
|
|
||||||
// width/height must be set before anchors, so we have to track anchors and apply them late
|
|
||||||
bool complete = false;
|
|
||||||
bool stagingVisible = false;
|
|
||||||
Anchors stagingAnchors;
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,7 +3,7 @@ description = "Core QuickShell types"
|
||||||
headers = [
|
headers = [
|
||||||
"qmlglobal.hpp",
|
"qmlglobal.hpp",
|
||||||
"qmlscreen.hpp",
|
"qmlscreen.hpp",
|
||||||
"scavenge.hpp",
|
"reload.hpp",
|
||||||
"shell.hpp",
|
"shell.hpp",
|
||||||
"variants.hpp",
|
"variants.hpp",
|
||||||
"proxywindow.hpp",
|
"proxywindow.hpp",
|
||||||
|
|
|
@ -17,17 +17,29 @@ ProxyWindowBase::~ProxyWindowBase() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxyWindowBase::earlyInit(QObject* old) {
|
void ProxyWindowBase::onReload(QObject* oldInstance) {
|
||||||
auto* oldpw = qobject_cast<ProxyWindowBase*>(old);
|
auto* old = qobject_cast<ProxyWindowBase*>(oldInstance);
|
||||||
|
|
||||||
if (oldpw == nullptr || oldpw->window == nullptr) {
|
if (old == nullptr || old->window == nullptr) {
|
||||||
this->window = new QQuickWindow();
|
this->window = new QQuickWindow();
|
||||||
} else {
|
} else {
|
||||||
this->window = oldpw->disownWindow();
|
this->window = old->disownWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
this->window->setMask(QRegion());
|
this->setupWindow();
|
||||||
|
|
||||||
|
auto backer = this->dataBacker();
|
||||||
|
for (auto* child: this->pendingChildren) {
|
||||||
|
backer.append(&backer, child);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->pendingChildren.clear();
|
||||||
|
|
||||||
|
emit this->windowConnected();
|
||||||
|
this->window->setVisible(this->mVisible);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProxyWindowBase::setupWindow() {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
QObject::connect(this->window, &QWindow::visibilityChanged, this, &ProxyWindowBase::visibleChanged);
|
QObject::connect(this->window, &QWindow::visibilityChanged, this, &ProxyWindowBase::visibleChanged);
|
||||||
QObject::connect(this->window, &QWindow::widthChanged, this, &ProxyWindowBase::widthChanged);
|
QObject::connect(this->window, &QWindow::widthChanged, this, &ProxyWindowBase::widthChanged);
|
||||||
|
@ -38,6 +50,11 @@ void ProxyWindowBase::earlyInit(QObject* old) {
|
||||||
QObject::connect(this, &ProxyWindowBase::widthChanged, this, &ProxyWindowBase::onMaskChanged);
|
QObject::connect(this, &ProxyWindowBase::widthChanged, this, &ProxyWindowBase::onMaskChanged);
|
||||||
QObject::connect(this, &ProxyWindowBase::heightChanged, this, &ProxyWindowBase::onMaskChanged);
|
QObject::connect(this, &ProxyWindowBase::heightChanged, this, &ProxyWindowBase::onMaskChanged);
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
this->setWidth(this->mWidth);
|
||||||
|
this->setHeight(this->mHeight);
|
||||||
|
this->setColor(this->mColor);
|
||||||
|
this->updateMask();
|
||||||
}
|
}
|
||||||
|
|
||||||
QQuickWindow* ProxyWindowBase::disownWindow() {
|
QQuickWindow* ProxyWindowBase::disownWindow() {
|
||||||
|
@ -52,20 +69,57 @@ QQuickWindow* ProxyWindowBase::disownWindow() {
|
||||||
return window;
|
return window;
|
||||||
}
|
}
|
||||||
|
|
||||||
QQuickWindow* ProxyWindowBase::backingWindow() { return this->window; }
|
QQuickWindow* ProxyWindowBase::backingWindow() const { return this->window; }
|
||||||
QQuickItem* ProxyWindowBase::item() { return this->window->contentItem(); }
|
|
||||||
|
|
||||||
// NOLINTNEXTLINE
|
bool ProxyWindowBase::isVisible() const {
|
||||||
#define PROXYPROP(type, get, set) \
|
if (this->window == nullptr) return this->mVisible;
|
||||||
type ProxyWindowBase::get() { return this->window->get(); } \
|
else return this->window->isVisible();
|
||||||
void ProxyWindowBase::set(type value) { this->window->set(value); }
|
}
|
||||||
|
|
||||||
PROXYPROP(bool, isVisible, setVisible);
|
void ProxyWindowBase::setVisible(bool visible) {
|
||||||
PROXYPROP(qint32, width, setWidth);
|
if (this->window == nullptr) {
|
||||||
PROXYPROP(qint32, height, setHeight);
|
this->mVisible = visible;
|
||||||
PROXYPROP(QColor, color, setColor);
|
emit this->visibleChanged();
|
||||||
|
} else this->window->setVisible(visible);
|
||||||
|
}
|
||||||
|
|
||||||
PendingRegion* ProxyWindowBase::mask() { return this->mMask; }
|
qint32 ProxyWindowBase::width() const {
|
||||||
|
if (this->window == nullptr) return this->mWidth;
|
||||||
|
else return this->window->width();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProxyWindowBase::setWidth(qint32 width) {
|
||||||
|
if (this->window == nullptr) {
|
||||||
|
this->mWidth = width;
|
||||||
|
emit this->widthChanged();
|
||||||
|
} else this->window->setWidth(width);
|
||||||
|
}
|
||||||
|
|
||||||
|
qint32 ProxyWindowBase::height() const {
|
||||||
|
if (this->window == nullptr) return this->mHeight;
|
||||||
|
else return this->window->height();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProxyWindowBase::setHeight(qint32 height) {
|
||||||
|
if (this->window == nullptr) {
|
||||||
|
this->mHeight = height;
|
||||||
|
emit this->heightChanged();
|
||||||
|
} else this->window->setHeight(height);
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor ProxyWindowBase::color() const {
|
||||||
|
if (this->window == nullptr) return this->mColor;
|
||||||
|
else return this->window->color();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProxyWindowBase::setColor(QColor color) {
|
||||||
|
if (this->window == nullptr) {
|
||||||
|
this->mColor = color;
|
||||||
|
emit this->colorChanged();
|
||||||
|
} else this->window->setColor(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
PendingRegion* ProxyWindowBase::mask() const { return this->mMask; }
|
||||||
|
|
||||||
void ProxyWindowBase::setMask(PendingRegion* mask) {
|
void ProxyWindowBase::setMask(PendingRegion* mask) {
|
||||||
if (this->mMask != nullptr) {
|
if (this->mMask != nullptr) {
|
||||||
|
@ -81,6 +135,10 @@ void ProxyWindowBase::setMask(PendingRegion* mask) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxyWindowBase::onMaskChanged() {
|
void ProxyWindowBase::onMaskChanged() {
|
||||||
|
if (this->window != nullptr) this->updateMask();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProxyWindowBase::updateMask() {
|
||||||
QRegion mask;
|
QRegion mask;
|
||||||
if (this->mMask != nullptr) {
|
if (this->mMask != nullptr) {
|
||||||
// if left as the default, dont combine it with the whole window area, leave it as is.
|
// if left as the default, dont combine it with the whole window area, leave it as is.
|
||||||
|
@ -114,59 +172,87 @@ QQmlListProperty<QObject> ProxyWindowBase::data() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
QQmlListProperty<QObject> ProxyWindowBase::dataBacker(QQmlListProperty<QObject>* prop) {
|
QQmlListProperty<QObject> ProxyWindowBase::dataBacker() {
|
||||||
auto* that = static_cast<ProxyWindowBase*>(prop->object); // NOLINT
|
return this->window->property("data").value<QQmlListProperty<QObject>>();
|
||||||
return that->window->property("data").value<QQmlListProperty<QObject>>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxyWindowBase::dataAppend(QQmlListProperty<QObject>* prop, QObject* obj) {
|
void ProxyWindowBase::dataAppend(QQmlListProperty<QObject>* prop, QObject* obj) {
|
||||||
auto backer = dataBacker(prop);
|
auto* self = static_cast<ProxyWindowBase*>(prop->object); // NOLINT
|
||||||
|
|
||||||
|
if (self->window == nullptr) {
|
||||||
|
if (obj != nullptr) {
|
||||||
|
obj->setParent(self);
|
||||||
|
self->pendingChildren.append(obj);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto backer = self->dataBacker();
|
||||||
backer.append(&backer, obj);
|
backer.append(&backer, obj);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qsizetype ProxyWindowBase::dataCount(QQmlListProperty<QObject>* prop) {
|
qsizetype ProxyWindowBase::dataCount(QQmlListProperty<QObject>* prop) {
|
||||||
auto backer = dataBacker(prop);
|
auto* self = static_cast<ProxyWindowBase*>(prop->object); // NOLINT
|
||||||
|
|
||||||
|
if (self->window == nullptr) {
|
||||||
|
return self->pendingChildren.count();
|
||||||
|
} else {
|
||||||
|
auto backer = self->dataBacker();
|
||||||
return backer.count(&backer);
|
return backer.count(&backer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QObject* ProxyWindowBase::dataAt(QQmlListProperty<QObject>* prop, qsizetype i) {
|
QObject* ProxyWindowBase::dataAt(QQmlListProperty<QObject>* prop, qsizetype i) {
|
||||||
auto backer = dataBacker(prop);
|
auto* self = static_cast<ProxyWindowBase*>(prop->object); // NOLINT
|
||||||
|
|
||||||
|
if (self->window == nullptr) {
|
||||||
|
return self->pendingChildren.at(i);
|
||||||
|
} else {
|
||||||
|
auto backer = self->dataBacker();
|
||||||
return backer.at(&backer, i);
|
return backer.at(&backer, i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxyWindowBase::dataClear(QQmlListProperty<QObject>* prop) {
|
void ProxyWindowBase::dataClear(QQmlListProperty<QObject>* prop) {
|
||||||
auto backer = dataBacker(prop);
|
auto* self = static_cast<ProxyWindowBase*>(prop->object); // NOLINT
|
||||||
|
|
||||||
|
if (self->window == nullptr) {
|
||||||
|
self->pendingChildren.clear();
|
||||||
|
} else {
|
||||||
|
auto backer = self->dataBacker();
|
||||||
backer.clear(&backer);
|
backer.clear(&backer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxyWindowBase::dataReplace(QQmlListProperty<QObject>* prop, qsizetype i, QObject* obj) {
|
void ProxyWindowBase::dataReplace(QQmlListProperty<QObject>* prop, qsizetype i, QObject* obj) {
|
||||||
auto backer = dataBacker(prop);
|
auto* self = static_cast<ProxyWindowBase*>(prop->object); // NOLINT
|
||||||
|
|
||||||
|
if (self->window == nullptr) {
|
||||||
|
if (obj != nullptr) {
|
||||||
|
obj->setParent(self);
|
||||||
|
self->pendingChildren.replace(i, obj);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto backer = self->dataBacker();
|
||||||
backer.replace(&backer, i, obj);
|
backer.replace(&backer, i, obj);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxyWindowBase::dataRemoveLast(QQmlListProperty<QObject>* prop) {
|
void ProxyWindowBase::dataRemoveLast(QQmlListProperty<QObject>* prop) {
|
||||||
auto backer = dataBacker(prop);
|
auto* self = static_cast<ProxyWindowBase*>(prop->object); // NOLINT
|
||||||
|
|
||||||
|
if (self->window == nullptr) {
|
||||||
|
self->pendingChildren.removeLast();
|
||||||
|
} else {
|
||||||
|
auto backer = self->dataBacker();
|
||||||
backer.removeLast(&backer);
|
backer.removeLast(&backer);
|
||||||
}
|
|
||||||
|
|
||||||
void ProxyFloatingWindow::earlyInit(QObject* old) {
|
|
||||||
this->ProxyWindowBase::earlyInit(old);
|
|
||||||
this->geometryLocked = this->window->isVisible();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProxyFloatingWindow::componentComplete() {
|
|
||||||
this->ProxyWindowBase::componentComplete();
|
|
||||||
this->geometryLocked = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProxyFloatingWindow::setWidth(qint32 value) {
|
|
||||||
if (!this->geometryLocked) {
|
|
||||||
this->ProxyWindowBase::setWidth(value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProxyFloatingWindow::setHeight(qint32 value) {
|
void ProxyFloatingWindow::setWidth(qint32 width) {
|
||||||
if (!this->geometryLocked) {
|
if (this->window == nullptr || !this->window->isVisible()) this->ProxyWindowBase::setWidth(width);
|
||||||
this->ProxyWindowBase::setHeight(value);
|
}
|
||||||
}
|
|
||||||
|
void ProxyFloatingWindow::setHeight(qint32 height) {
|
||||||
|
if (this->window == nullptr || !this->window->isVisible())
|
||||||
|
this->ProxyWindowBase::setHeight(height);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <qcolor.h>
|
#include <qcolor.h>
|
||||||
|
#include <qcontainerfwd.h>
|
||||||
#include <qevent.h>
|
#include <qevent.h>
|
||||||
#include <qobject.h>
|
#include <qobject.h>
|
||||||
#include <qqmllist.h>
|
#include <qqmllist.h>
|
||||||
|
@ -11,7 +12,7 @@
|
||||||
#include <qtypes.h>
|
#include <qtypes.h>
|
||||||
|
|
||||||
#include "region.hpp"
|
#include "region.hpp"
|
||||||
#include "scavenge.hpp"
|
#include "reload.hpp"
|
||||||
|
|
||||||
// Proxy to an actual window exposing a limited property set with the ability to
|
// Proxy to an actual window exposing a limited property set with the ability to
|
||||||
// transfer it to a new window.
|
// transfer it to a new window.
|
||||||
|
@ -19,7 +20,7 @@
|
||||||
//
|
//
|
||||||
// NOTE: setting an `id` in qml will point to the proxy window and not the real window so things
|
// NOTE: setting an `id` in qml will point to the proxy window and not the real window so things
|
||||||
// like anchors must use `item`.
|
// like anchors must use `item`.
|
||||||
class ProxyWindowBase: public Scavenger {
|
class ProxyWindowBase: public Reloadable {
|
||||||
Q_OBJECT;
|
Q_OBJECT;
|
||||||
/// The QtQuick window backing this window.
|
/// The QtQuick window backing this window.
|
||||||
///
|
///
|
||||||
|
@ -29,8 +30,6 @@ class ProxyWindowBase: public Scavenger {
|
||||||
/// >
|
/// >
|
||||||
/// > Use **only** if you know what you are doing.
|
/// > Use **only** if you know what you are doing.
|
||||||
Q_PROPERTY(QQuickWindow* _backingWindow READ backingWindow);
|
Q_PROPERTY(QQuickWindow* _backingWindow READ backingWindow);
|
||||||
/// The content item of the window.
|
|
||||||
Q_PROPERTY(QQuickItem* item READ item CONSTANT);
|
|
||||||
/// The visibility of the window.
|
/// The visibility of the window.
|
||||||
///
|
///
|
||||||
/// > [!INFO] Windows are not visible by default so you will need to set this to make the window
|
/// > [!INFO] Windows are not visible by default so you will need to set this to make the window
|
||||||
|
@ -99,12 +98,8 @@ class ProxyWindowBase: public Scavenger {
|
||||||
Q_PROPERTY(QQmlListProperty<QObject> data READ data);
|
Q_PROPERTY(QQmlListProperty<QObject> data READ data);
|
||||||
Q_CLASSINFO("DefaultProperty", "data");
|
Q_CLASSINFO("DefaultProperty", "data");
|
||||||
|
|
||||||
protected:
|
|
||||||
void earlyInit(QObject* old) override;
|
|
||||||
QQuickWindow* window = nullptr;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ProxyWindowBase(QObject* parent = nullptr): Scavenger(parent) {}
|
explicit ProxyWindowBase(QObject* parent = nullptr): Reloadable(parent) {}
|
||||||
~ProxyWindowBase() override;
|
~ProxyWindowBase() override;
|
||||||
|
|
||||||
ProxyWindowBase(ProxyWindowBase&) = delete;
|
ProxyWindowBase(ProxyWindowBase&) = delete;
|
||||||
|
@ -112,41 +107,55 @@ public:
|
||||||
void operator=(ProxyWindowBase&) = delete;
|
void operator=(ProxyWindowBase&) = delete;
|
||||||
void operator=(ProxyWindowBase&&) = delete;
|
void operator=(ProxyWindowBase&&) = delete;
|
||||||
|
|
||||||
|
void onReload(QObject* oldInstance) override;
|
||||||
|
|
||||||
|
virtual void setupWindow();
|
||||||
|
|
||||||
// Disown the backing window and delete all its children.
|
// Disown the backing window and delete all its children.
|
||||||
virtual QQuickWindow* disownWindow();
|
virtual QQuickWindow* disownWindow();
|
||||||
|
|
||||||
QQuickWindow* backingWindow();
|
[[nodiscard]] QQuickWindow* backingWindow() const;
|
||||||
QQuickItem* item();
|
|
||||||
|
|
||||||
virtual bool isVisible();
|
[[nodiscard]] virtual bool isVisible() const;
|
||||||
virtual void setVisible(bool value);
|
virtual void setVisible(bool visible);
|
||||||
|
|
||||||
virtual qint32 width();
|
[[nodiscard]] virtual qint32 width() const;
|
||||||
virtual void setWidth(qint32 value);
|
virtual void setWidth(qint32 width);
|
||||||
|
|
||||||
virtual qint32 height();
|
[[nodiscard]] virtual qint32 height() const;
|
||||||
virtual void setHeight(qint32 value);
|
virtual void setHeight(qint32 height);
|
||||||
|
|
||||||
QColor color();
|
[[nodiscard]] QColor color() const;
|
||||||
void setColor(QColor value);
|
void setColor(QColor color);
|
||||||
|
|
||||||
PendingRegion* mask();
|
[[nodiscard]] PendingRegion* mask() const;
|
||||||
void setMask(PendingRegion* mask);
|
void setMask(PendingRegion* mask);
|
||||||
|
|
||||||
QQmlListProperty<QObject> data();
|
QQmlListProperty<QObject> data();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void visibleChanged(bool visible);
|
void windowConnected();
|
||||||
void widthChanged(qint32 width);
|
void visibleChanged();
|
||||||
void heightChanged(qint32 width);
|
void widthChanged();
|
||||||
void colorChanged(QColor color);
|
void heightChanged();
|
||||||
|
void colorChanged();
|
||||||
void maskChanged();
|
void maskChanged();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onMaskChanged();
|
void onMaskChanged();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool mVisible = false;
|
||||||
|
qint32 mWidth = 100;
|
||||||
|
qint32 mHeight = 100;
|
||||||
|
QColor mColor;
|
||||||
|
PendingRegion* mMask = nullptr;
|
||||||
|
QQuickWindow* window = nullptr;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static QQmlListProperty<QObject> dataBacker(QQmlListProperty<QObject>* prop);
|
void updateMask();
|
||||||
|
QQmlListProperty<QObject> dataBacker();
|
||||||
|
|
||||||
static void dataAppend(QQmlListProperty<QObject>* prop, QObject* obj);
|
static void dataAppend(QQmlListProperty<QObject>* prop, QObject* obj);
|
||||||
static qsizetype dataCount(QQmlListProperty<QObject>* prop);
|
static qsizetype dataCount(QQmlListProperty<QObject>* prop);
|
||||||
static QObject* dataAt(QQmlListProperty<QObject>* prop, qsizetype i);
|
static QObject* dataAt(QQmlListProperty<QObject>* prop, qsizetype i);
|
||||||
|
@ -154,7 +163,7 @@ private:
|
||||||
static void dataReplace(QQmlListProperty<QObject>* prop, qsizetype i, QObject* obj);
|
static void dataReplace(QQmlListProperty<QObject>* prop, qsizetype i, QObject* obj);
|
||||||
static void dataRemoveLast(QQmlListProperty<QObject>* prop);
|
static void dataRemoveLast(QQmlListProperty<QObject>* prop);
|
||||||
|
|
||||||
PendingRegion* mMask = nullptr;
|
QVector<QObject*> pendingChildren;
|
||||||
};
|
};
|
||||||
|
|
||||||
// qt attempts to resize the window but fails because wayland
|
// qt attempts to resize the window but fails because wayland
|
||||||
|
@ -164,12 +173,8 @@ class ProxyFloatingWindow: public ProxyWindowBase {
|
||||||
QML_ELEMENT;
|
QML_ELEMENT;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void earlyInit(QObject* old) override;
|
// Setting geometry while the window is visible makes the content item shrink but not the window
|
||||||
void componentComplete() override;
|
// which is awful so we disable it for floating windows.
|
||||||
|
void setWidth(qint32 width) override;
|
||||||
void setWidth(qint32 value) override;
|
void setHeight(qint32 height) override;
|
||||||
void setHeight(qint32 value) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool geometryLocked = false;
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -45,36 +45,9 @@ public:
|
||||||
/// `hard` - perform a hard reload. If this is false, QuickShell will attempt to reuse windows
|
/// `hard` - perform a hard reload. If this is false, QuickShell will attempt to reuse windows
|
||||||
/// that already exist. If true windows will be recreated.
|
/// that already exist. If true windows will be recreated.
|
||||||
///
|
///
|
||||||
/// > [!INFO] QuickShell can only reuse windows that are in a hierarchy of elements known
|
/// See [Reloadable] for more information on what can be reloaded and how.
|
||||||
/// > internally as `Scavengeable`. These types are [ShellRoot] and [Variants].
|
///
|
||||||
/// >
|
/// [Reloadable]: ../reloadable
|
||||||
/// > ```qml
|
|
||||||
/// > // this will reuse the window on reload
|
|
||||||
/// > ShellRoot {
|
|
||||||
/// > Varaints {
|
|
||||||
/// > ProxyShellWindow {
|
|
||||||
/// > // ...
|
|
||||||
/// > }
|
|
||||||
/// >
|
|
||||||
/// > // ...
|
|
||||||
/// > }
|
|
||||||
/// > }
|
|
||||||
/// >
|
|
||||||
/// > // this will NOT reuse the window on reload,
|
|
||||||
/// > // and will destroy the old one / create a new one every time
|
|
||||||
/// > ShellRoot {
|
|
||||||
/// > AnyNonScavengeableType {
|
|
||||||
/// > ProxyShellWindow {
|
|
||||||
/// > // ...
|
|
||||||
/// > }
|
|
||||||
/// >
|
|
||||||
/// > // ...
|
|
||||||
/// > }
|
|
||||||
/// > }
|
|
||||||
/// > ```
|
|
||||||
/// >
|
|
||||||
/// > [ShellRoot]: ../shellroot
|
|
||||||
/// > [Variants]: ../variants
|
|
||||||
Q_INVOKABLE void reload(bool hard);
|
Q_INVOKABLE void reload(bool hard);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
77
src/cpp/reload.cpp
Normal file
77
src/cpp/reload.cpp
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
#include "reload.hpp"
|
||||||
|
|
||||||
|
#include <qcontainerfwd.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qqmllist.h>
|
||||||
|
|
||||||
|
void ReloadPropagator::onReload(QObject* oldInstance) {
|
||||||
|
auto* old = qobject_cast<ReloadPropagator*>(oldInstance);
|
||||||
|
|
||||||
|
for (auto i = 0; i < this->mChildren.length(); i++) {
|
||||||
|
auto* newChild = qobject_cast<Reloadable*>(this->mChildren.at(i));
|
||||||
|
if (newChild != nullptr) {
|
||||||
|
auto* oldChild = old == nullptr || old->mChildren.length() <= i
|
||||||
|
? nullptr
|
||||||
|
: qobject_cast<Reloadable*>(old->mChildren.at(i));
|
||||||
|
newChild->onReload(oldChild);
|
||||||
|
} else {
|
||||||
|
Reloadable::reloadRecursive(newChild, oldInstance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QQmlListProperty<QObject> ReloadPropagator::data() {
|
||||||
|
return QQmlListProperty<QObject>(
|
||||||
|
this,
|
||||||
|
nullptr,
|
||||||
|
&ReloadPropagator::appendComponent,
|
||||||
|
nullptr,
|
||||||
|
nullptr,
|
||||||
|
nullptr,
|
||||||
|
nullptr,
|
||||||
|
nullptr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReloadPropagator::appendComponent(QQmlListProperty<QObject>* list, QObject* obj) {
|
||||||
|
auto* self = static_cast<ReloadPropagator*>(list->object); // NOLINT
|
||||||
|
obj->setParent(self);
|
||||||
|
self->mChildren.append(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reloadable::reloadRecursive(QObject* newObj, QObject* oldRoot) {
|
||||||
|
auto* reloadable = qobject_cast<Reloadable*>(newObj);
|
||||||
|
if (reloadable != nullptr) {
|
||||||
|
QObject* oldInstance = nullptr;
|
||||||
|
if (oldRoot != nullptr && !reloadable->mReloadableId.isEmpty()) {
|
||||||
|
oldInstance = Reloadable::getChildByReloadId(oldRoot, reloadable->mReloadableId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// pass handling to the child's onReload, which should call back into reloadRecursive,
|
||||||
|
// with its oldInstance becoming the new oldRoot.
|
||||||
|
reloadable->onReload(oldInstance);
|
||||||
|
} else if (newObj != nullptr) {
|
||||||
|
Reloadable::reloadChildrenRecursive(newObj, oldRoot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reloadable::reloadChildrenRecursive(QObject* newRoot, QObject* oldRoot) {
|
||||||
|
for (auto* child: newRoot->children()) {
|
||||||
|
Reloadable::reloadRecursive(child, oldRoot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QObject* Reloadable::getChildByReloadId(QObject* parent, const QString& reloadId) {
|
||||||
|
for (auto* child: parent->children()) {
|
||||||
|
auto* reloadable = qobject_cast<Reloadable*>(child);
|
||||||
|
if (reloadable != nullptr) {
|
||||||
|
if (reloadable->mReloadableId == reloadId) return reloadable;
|
||||||
|
// if not then don't check its children as thats a seperate reload scope.
|
||||||
|
} else {
|
||||||
|
auto* reloadable = Reloadable::getChildByReloadId(child, reloadId);
|
||||||
|
if (reloadable != nullptr) return reloadable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
107
src/cpp/reload.hpp
Normal file
107
src/cpp/reload.hpp
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qqmlcomponent.h>
|
||||||
|
#include <qqmlintegration.h>
|
||||||
|
#include <qqmllist.h>
|
||||||
|
#include <qqmlparserstatus.h>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
|
||||||
|
///! The base class of all types that can be reloaded.
|
||||||
|
/// Reloadables will attempt to take specific state from previous config revisions if possible.
|
||||||
|
/// Some examples are `ProxyShellWindow` and `ProxyFloatingWindow` which will attempt to find the
|
||||||
|
/// windows assigned to them in the previous configuration.
|
||||||
|
class Reloadable: public QObject, public QQmlParserStatus {
|
||||||
|
Q_OBJECT;
|
||||||
|
Q_INTERFACES(QQmlParserStatus);
|
||||||
|
/// An additional identifier that can be used to try to match a reloadable object to its
|
||||||
|
/// previous state.
|
||||||
|
///
|
||||||
|
/// Simply keeping a stable identifier across config versions (saves) is
|
||||||
|
/// enough to help the reloader figure out which object in the old revision corrosponds to
|
||||||
|
/// this object in the current revision, and facilitate smoother reloading.
|
||||||
|
///
|
||||||
|
/// Note that identifiers are scoped, and will try to do the right thing in context.
|
||||||
|
/// For example if you have a `Variants` wrapping an object with an identified element inside,
|
||||||
|
/// a scope is created at the variant level.
|
||||||
|
///
|
||||||
|
/// ```qml
|
||||||
|
/// Variants {
|
||||||
|
/// // multiple variants of the same object tree
|
||||||
|
/// variants: [ { foo: 1 }, { foo: 2 } ]
|
||||||
|
///
|
||||||
|
/// // any non `Reloadable` object
|
||||||
|
/// QtObject {
|
||||||
|
/// ProxyFloatingWindow {
|
||||||
|
/// // this ProxyFloatingWindow will now be matched to the same one in the previous
|
||||||
|
/// // widget tree for its variant. "myFloatingWindow" refers to both the variant in
|
||||||
|
/// // `foo: 1` and `foo: 2` for each tree.
|
||||||
|
/// reloadableId: "myFloatingWindow"
|
||||||
|
///
|
||||||
|
/// // ...
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
Q_PROPERTY(QString reloadableId MEMBER mReloadableId);
|
||||||
|
QML_ELEMENT;
|
||||||
|
QML_UNCREATABLE(
|
||||||
|
"Reloadable is the base class of reloadable types and cannot be created on its own."
|
||||||
|
);
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit Reloadable(QObject* parent = nullptr): QObject(parent) {}
|
||||||
|
|
||||||
|
/// called unconditionally in the reload phase, with nullptr if no source could be determined
|
||||||
|
virtual void onReload(QObject* oldInstance) = 0;
|
||||||
|
|
||||||
|
// TODO: onReload runs after initialization for reloadable objects created late
|
||||||
|
void classBegin() override {}
|
||||||
|
void componentComplete() override {}
|
||||||
|
|
||||||
|
// Reload objects in the parent->child graph recursively.
|
||||||
|
static void reloadRecursive(QObject* newObj, QObject* oldRoot);
|
||||||
|
// Same as above but does not reload the passed object, only its children.
|
||||||
|
static void reloadChildrenRecursive(QObject* newRoot, QObject* oldRoot);
|
||||||
|
|
||||||
|
QString mReloadableId;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static QObject* getChildByReloadId(QObject* parent, const QString& reloadId);
|
||||||
|
};
|
||||||
|
|
||||||
|
///! Basic type that propagates reloads to child items in order.
|
||||||
|
/// Convenience type equivalent to setting `reloadableId` on properties in a
|
||||||
|
/// QtObject instance.
|
||||||
|
///
|
||||||
|
/// Note that this does not work for visible `Item`s (all widgets).
|
||||||
|
///
|
||||||
|
/// ```qml
|
||||||
|
/// ShellRoot {
|
||||||
|
/// Variants {
|
||||||
|
/// variants: ...
|
||||||
|
///
|
||||||
|
/// ReloadPropagator {
|
||||||
|
/// // everything in here behaves the same as if it was defined
|
||||||
|
/// // directly in `Variants` reload-wise.
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
class ReloadPropagator: public Reloadable {
|
||||||
|
Q_OBJECT;
|
||||||
|
Q_PROPERTY(QQmlListProperty<QObject> children READ data);
|
||||||
|
Q_CLASSINFO("DefaultProperty", "children");
|
||||||
|
QML_ELEMENT;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ReloadPropagator(QObject* parent = nullptr): Reloadable(parent) {}
|
||||||
|
|
||||||
|
void onReload(QObject* oldInstance) override;
|
||||||
|
|
||||||
|
QQmlListProperty<QObject> data();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void appendComponent(QQmlListProperty<QObject>* list, QObject* obj);
|
||||||
|
|
||||||
|
QList<QObject*> mChildren;
|
||||||
|
};
|
|
@ -9,7 +9,6 @@
|
||||||
#include <qqmlengine.h>
|
#include <qqmlengine.h>
|
||||||
#include <qurl.h>
|
#include <qurl.h>
|
||||||
|
|
||||||
#include "scavenge.hpp"
|
|
||||||
#include "shell.hpp"
|
#include "shell.hpp"
|
||||||
#include "watcher.hpp"
|
#include "watcher.hpp"
|
||||||
|
|
||||||
|
@ -23,8 +22,6 @@ RootWrapper::RootWrapper(QString rootPath):
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QObject* RootWrapper::scavengeTargetFor(QObject* /* child */) { return this->root; }
|
|
||||||
|
|
||||||
void RootWrapper::reloadGraph(bool hard) {
|
void RootWrapper::reloadGraph(bool hard) {
|
||||||
if (this->root != nullptr) {
|
if (this->root != nullptr) {
|
||||||
this->engine.clearComponentCache();
|
this->engine.clearComponentCache();
|
||||||
|
@ -32,9 +29,7 @@ void RootWrapper::reloadGraph(bool hard) {
|
||||||
|
|
||||||
auto component = QQmlComponent(&this->engine, QUrl::fromLocalFile(this->rootPath));
|
auto component = QQmlComponent(&this->engine, QUrl::fromLocalFile(this->rootPath));
|
||||||
|
|
||||||
SCAVENGE_PARENT = hard ? nullptr : this;
|
|
||||||
auto* obj = component.beginCreate(this->engine.rootContext());
|
auto* obj = component.beginCreate(this->engine.rootContext());
|
||||||
SCAVENGE_PARENT = nullptr;
|
|
||||||
|
|
||||||
if (obj == nullptr) {
|
if (obj == nullptr) {
|
||||||
qWarning() << component.errorString().toStdString().c_str();
|
qWarning() << component.errorString().toStdString().c_str();
|
||||||
|
@ -51,6 +46,8 @@ void RootWrapper::reloadGraph(bool hard) {
|
||||||
|
|
||||||
component.completeCreate();
|
component.completeCreate();
|
||||||
|
|
||||||
|
newRoot->onReload(hard ? nullptr : this->root);
|
||||||
|
|
||||||
if (this->root != nullptr) {
|
if (this->root != nullptr) {
|
||||||
this->root->deleteLater();
|
this->root->deleteLater();
|
||||||
this->root = nullptr;
|
this->root = nullptr;
|
||||||
|
|
|
@ -5,18 +5,15 @@
|
||||||
#include <qtmetamacros.h>
|
#include <qtmetamacros.h>
|
||||||
#include <qurl.h>
|
#include <qurl.h>
|
||||||
|
|
||||||
#include "scavenge.hpp"
|
|
||||||
#include "shell.hpp"
|
#include "shell.hpp"
|
||||||
#include "watcher.hpp"
|
#include "watcher.hpp"
|
||||||
|
|
||||||
class RootWrapper: public QObject, virtual public Scavengeable {
|
class RootWrapper: public QObject {
|
||||||
Q_OBJECT;
|
Q_OBJECT;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit RootWrapper(QString rootPath);
|
explicit RootWrapper(QString rootPath);
|
||||||
|
|
||||||
QObject* scavengeTargetFor(QObject* child) override;
|
|
||||||
|
|
||||||
void reloadGraph(bool hard);
|
void reloadGraph(bool hard);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
|
|
@ -1,87 +0,0 @@
|
||||||
#include "scavenge.hpp"
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include <qcontainerfwd.h>
|
|
||||||
#include <qlogging.h>
|
|
||||||
#include <qobject.h>
|
|
||||||
#include <qqmlcomponent.h>
|
|
||||||
#include <qqmlengine.h>
|
|
||||||
#include <qqmllist.h>
|
|
||||||
|
|
||||||
// FIXME: there are core problems with SCAVENGE_PARENT due to the qml engine liking to set parents really late.
|
|
||||||
// this should instead be handled by proxying all property values until a possible target is ready or definitely not coming.
|
|
||||||
// The parent should probably be stable in componentComplete() but should be tested.
|
|
||||||
|
|
||||||
QObject* SCAVENGE_PARENT = nullptr; // NOLINT
|
|
||||||
|
|
||||||
void Scavenger::classBegin() {
|
|
||||||
// prayers
|
|
||||||
if (this->parent() == nullptr) {
|
|
||||||
this->setParent(SCAVENGE_PARENT);
|
|
||||||
SCAVENGE_PARENT = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* parent = dynamic_cast<Scavengeable*>(this->parent());
|
|
||||||
|
|
||||||
QObject* old = nullptr;
|
|
||||||
if (parent != nullptr) {
|
|
||||||
old = parent->scavengeTargetFor(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
this->earlyInit(old);
|
|
||||||
}
|
|
||||||
|
|
||||||
QObject* createComponentScavengeable(
|
|
||||||
QObject& parent,
|
|
||||||
QQmlComponent& component,
|
|
||||||
QVariantMap& initialProperties
|
|
||||||
) {
|
|
||||||
SCAVENGE_PARENT = &parent;
|
|
||||||
auto* instance = component.beginCreate(QQmlEngine::contextForObject(&parent));
|
|
||||||
SCAVENGE_PARENT = nullptr;
|
|
||||||
if (instance == nullptr) return nullptr;
|
|
||||||
if (instance->parent() != nullptr) instance->setParent(&parent);
|
|
||||||
component.setInitialProperties(instance, initialProperties);
|
|
||||||
component.completeCreate();
|
|
||||||
|
|
||||||
if (instance == nullptr) {
|
|
||||||
qWarning() << component.errorString().toStdString().c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ScavengeableScope::earlyInit(QObject* old) {
|
|
||||||
auto* oldshell = qobject_cast<ScavengeableScope*>(old);
|
|
||||||
|
|
||||||
if (oldshell != nullptr) {
|
|
||||||
this->scavengeableData = std::move(oldshell->mData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QObject* ScavengeableScope::scavengeTargetFor(QObject* /* child */) {
|
|
||||||
if (this->scavengeableData.length() > this->mData.length()) {
|
|
||||||
return this->scavengeableData[this->mData.length()];
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
QQmlListProperty<QObject> ScavengeableScope::data() {
|
|
||||||
return QQmlListProperty<QObject>(
|
|
||||||
this,
|
|
||||||
nullptr,
|
|
||||||
&ScavengeableScope::appendComponent,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
nullptr
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ScavengeableScope::appendComponent(QQmlListProperty<QObject>* list, QObject* component) {
|
|
||||||
auto* self = static_cast<ScavengeableScope*>(list->object); // NOLINT
|
|
||||||
component->setParent(self);
|
|
||||||
self->mData.append(component);
|
|
||||||
}
|
|
|
@ -1,79 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <qobject.h>
|
|
||||||
#include <qqmlcomponent.h>
|
|
||||||
#include <qqmlintegration.h>
|
|
||||||
#include <qqmllist.h>
|
|
||||||
#include <qqmlparserstatus.h>
|
|
||||||
#include <qtmetamacros.h>
|
|
||||||
|
|
||||||
extern QObject* SCAVENGE_PARENT; // NOLINT
|
|
||||||
|
|
||||||
class Scavenger: public QObject, public QQmlParserStatus {
|
|
||||||
Q_OBJECT;
|
|
||||||
Q_INTERFACES(QQmlParserStatus);
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit Scavenger(QObject* parent = nullptr): QObject(parent) {}
|
|
||||||
~Scavenger() override = default;
|
|
||||||
|
|
||||||
Scavenger(Scavenger&) = delete;
|
|
||||||
Scavenger(Scavenger&&) = delete;
|
|
||||||
void operator=(Scavenger&) = delete;
|
|
||||||
void operator=(Scavenger&&) = delete;
|
|
||||||
|
|
||||||
void classBegin() override;
|
|
||||||
void componentComplete() override {}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// do early init, sometimes with a scavengeable target
|
|
||||||
virtual void earlyInit(QObject* old) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Scavengeable {
|
|
||||||
public:
|
|
||||||
explicit Scavengeable() = default;
|
|
||||||
virtual ~Scavengeable() = default;
|
|
||||||
|
|
||||||
Scavengeable(Scavengeable&) = delete;
|
|
||||||
Scavengeable(Scavengeable&&) = delete;
|
|
||||||
void operator=(Scavengeable&) = delete;
|
|
||||||
void operator=(Scavengeable&&) = delete;
|
|
||||||
|
|
||||||
// return an old object that might have salvageable resources
|
|
||||||
virtual QObject* scavengeTargetFor(QObject* child) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
QObject* createComponentScavengeable(
|
|
||||||
QObject& parent,
|
|
||||||
QQmlComponent& component,
|
|
||||||
QVariantMap& initialProperties
|
|
||||||
);
|
|
||||||
|
|
||||||
///! Reloader connection scope
|
|
||||||
/// Attempts to maintain scavengeable connections.
|
|
||||||
/// This is mostly useful to split a scavengeable component slot (e.g. `Variants`)
|
|
||||||
/// into multiple slots.
|
|
||||||
///
|
|
||||||
/// If you don't know what that means you probably don't need it.
|
|
||||||
class ScavengeableScope: public Scavenger, virtual public Scavengeable {
|
|
||||||
Q_OBJECT;
|
|
||||||
Q_PROPERTY(QQmlListProperty<QObject> data READ data);
|
|
||||||
Q_CLASSINFO("DefaultProperty", "data");
|
|
||||||
QML_ELEMENT;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit ScavengeableScope(QObject* parent = nullptr): Scavenger(parent) {}
|
|
||||||
|
|
||||||
void earlyInit(QObject* old) override;
|
|
||||||
QObject* scavengeTargetFor(QObject* child) override;
|
|
||||||
|
|
||||||
QQmlListProperty<QObject> data();
|
|
||||||
|
|
||||||
private:
|
|
||||||
static void appendComponent(QQmlListProperty<QObject>* list, QObject* component);
|
|
||||||
|
|
||||||
// track only the children assigned to `data` in order
|
|
||||||
QList<QObject*> mData;
|
|
||||||
QList<QObject*> scavengeableData;
|
|
||||||
};
|
|
|
@ -5,7 +5,7 @@
|
||||||
#include <qqmlengine.h>
|
#include <qqmlengine.h>
|
||||||
#include <qtmetamacros.h>
|
#include <qtmetamacros.h>
|
||||||
|
|
||||||
#include "scavenge.hpp"
|
#include "reload.hpp"
|
||||||
|
|
||||||
class ShellConfig {
|
class ShellConfig {
|
||||||
Q_GADGET;
|
Q_GADGET;
|
||||||
|
@ -16,7 +16,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
///! Root config element
|
///! Root config element
|
||||||
class ShellRoot: public ScavengeableScope {
|
class ShellRoot: public ReloadPropagator {
|
||||||
Q_OBJECT;
|
Q_OBJECT;
|
||||||
/// If `config.watchFiles` is true the configuration will be reloaded whenever it changes.
|
/// If `config.watchFiles` is true the configuration will be reloaded whenever it changes.
|
||||||
/// Defaults to true.
|
/// Defaults to true.
|
||||||
|
@ -24,7 +24,7 @@ class ShellRoot: public ScavengeableScope {
|
||||||
QML_ELEMENT;
|
QML_ELEMENT;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ShellRoot(QObject* parent = nullptr): ScavengeableScope(parent) {}
|
explicit ShellRoot(QObject* parent = nullptr): ReloadPropagator(parent) {}
|
||||||
|
|
||||||
void setConfig(ShellConfig config);
|
void setConfig(ShellConfig config);
|
||||||
[[nodiscard]] ShellConfig config() const;
|
[[nodiscard]] ShellConfig config() const;
|
||||||
|
|
|
@ -5,31 +5,24 @@
|
||||||
#include <qcontainerfwd.h>
|
#include <qcontainerfwd.h>
|
||||||
#include <qlogging.h>
|
#include <qlogging.h>
|
||||||
#include <qobject.h>
|
#include <qobject.h>
|
||||||
|
#include <qqmlengine.h>
|
||||||
|
|
||||||
#include "scavenge.hpp"
|
#include "reload.hpp"
|
||||||
|
|
||||||
void Variants::earlyInit(QObject* old) {
|
void Variants::onReload(QObject* oldInstance) {
|
||||||
auto* oldv = qobject_cast<Variants*>(old);
|
auto* old = qobject_cast<Variants*>(oldInstance);
|
||||||
if (oldv != nullptr) {
|
|
||||||
this->scavengeableInstances = std::move(oldv->instances);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QObject* Variants::scavengeTargetFor(QObject* /* child */) {
|
for (auto& [variant, instanceObj]: this->instances.values) {
|
||||||
// Attempt to find the set that most closely matches the current one.
|
QObject* oldInstance = nullptr;
|
||||||
// This is biased to the order of the scavenge list which should help in
|
if (old != nullptr) {
|
||||||
// case of conflicts as long as variants have not been reordered.
|
auto& values = old->instances.values;
|
||||||
|
|
||||||
if (this->activeScavengeVariant != nullptr) {
|
|
||||||
auto& values = this->scavengeableInstances.values;
|
|
||||||
if (values.empty()) return nullptr;
|
|
||||||
|
|
||||||
int matchcount = 0;
|
int matchcount = 0;
|
||||||
int matchi = 0;
|
int matchi = 0;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (auto& [valueSet, _]: values) {
|
for (auto& [valueSet, _]: values) {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (auto& [k, v]: this->activeScavengeVariant->toStdMap()) {
|
for (auto& [k, v]: variant.toStdMap()) {
|
||||||
if (valueSet.contains(k) && valueSet.value(k) == v) {
|
if (valueSet.contains(k) && valueSet.value(k) == v) {
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
@ -44,11 +37,15 @@ QObject* Variants::scavengeTargetFor(QObject* /* child */) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matchcount > 0) {
|
if (matchcount > 0) {
|
||||||
return values.takeAt(matchi).second;
|
oldInstance = values.takeAt(matchi).second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
auto* instance = qobject_cast<Reloadable*>(instanceObj);
|
||||||
|
|
||||||
|
if (instance != nullptr) instance->onReload(oldInstance);
|
||||||
|
else Reloadable::reloadChildrenRecursive(instanceObj, oldInstance);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Variants::setVariants(QVariantList variants) {
|
void Variants::setVariants(QVariantList variants) {
|
||||||
|
@ -57,7 +54,7 @@ void Variants::setVariants(QVariantList variants) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Variants::componentComplete() {
|
void Variants::componentComplete() {
|
||||||
this->Scavenger::componentComplete();
|
this->Reloadable::componentComplete();
|
||||||
this->updateVariants();
|
this->updateVariants();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,14 +93,18 @@ void Variants::updateVariants() {
|
||||||
continue; // we dont need to recreate this one
|
continue; // we dont need to recreate this one
|
||||||
}
|
}
|
||||||
|
|
||||||
this->activeScavengeVariant = &variant;
|
auto* instance = this->mComponent->createWithInitialProperties(
|
||||||
auto* instance = createComponentScavengeable(*this, *this->mComponent, variant);
|
variant,
|
||||||
|
QQmlEngine::contextForObject(this)
|
||||||
|
);
|
||||||
|
|
||||||
if (instance == nullptr) {
|
if (instance == nullptr) {
|
||||||
|
qWarning() << this->mComponent->errorString().toStdString().c_str();
|
||||||
qWarning() << "failed to create variant with object" << variant;
|
qWarning() << "failed to create variant with object" << variant;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
instance->setParent(this);
|
||||||
this->instances.insert(variant, instance);
|
this->instances.insert(variant, instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,9 @@
|
||||||
#include <qobject.h>
|
#include <qobject.h>
|
||||||
#include <qqmlcomponent.h>
|
#include <qqmlcomponent.h>
|
||||||
#include <qqmlparserstatus.h>
|
#include <qqmlparserstatus.h>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
|
||||||
#include "scavenge.hpp"
|
#include "reload.hpp"
|
||||||
|
|
||||||
// extremely inefficient map
|
// extremely inefficient map
|
||||||
template <typename K, typename V>
|
template <typename K, typename V>
|
||||||
|
@ -28,7 +29,7 @@ public:
|
||||||
/// screen.
|
/// screen.
|
||||||
///
|
///
|
||||||
/// [QuickShell.screens]: ../quickshell#prop.screens
|
/// [QuickShell.screens]: ../quickshell#prop.screens
|
||||||
class Variants: public Scavenger, virtual public Scavengeable {
|
class Variants: public Reloadable {
|
||||||
Q_OBJECT;
|
Q_OBJECT;
|
||||||
/// The component to create instances of
|
/// The component to create instances of
|
||||||
Q_PROPERTY(QQmlComponent* component MEMBER mComponent);
|
Q_PROPERTY(QQmlComponent* component MEMBER mComponent);
|
||||||
|
@ -39,10 +40,9 @@ class Variants: public Scavenger, virtual public Scavengeable {
|
||||||
QML_ELEMENT;
|
QML_ELEMENT;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit Variants(QObject* parent = nullptr): Scavenger(parent) {}
|
explicit Variants(QObject* parent = nullptr): Reloadable(parent) {}
|
||||||
|
|
||||||
void earlyInit(QObject* old) override;
|
void onReload(QObject* oldInstance) override;
|
||||||
QObject* scavengeTargetFor(QObject* child) override;
|
|
||||||
|
|
||||||
void componentComplete() override;
|
void componentComplete() override;
|
||||||
|
|
||||||
|
@ -53,8 +53,4 @@ private:
|
||||||
QQmlComponent* mComponent = nullptr;
|
QQmlComponent* mComponent = nullptr;
|
||||||
QVariantList mVariants;
|
QVariantList mVariants;
|
||||||
AwfulMap<QVariantMap, QObject*> instances;
|
AwfulMap<QVariantMap, QObject*> instances;
|
||||||
|
|
||||||
// pointers may die post componentComplete.
|
|
||||||
AwfulMap<QVariantMap, QObject*> scavengeableInstances;
|
|
||||||
QVariantMap* activeScavengeVariant = nullptr;
|
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue