forked from quickshell/quickshell
core/window: backing windows can now be destroyed and recreated
This fixes a crash in layershells and the setVisible crash on nvidia.
This commit is contained in:
parent
b6dc6967a1
commit
3a0381dcbe
16 changed files with 257 additions and 112 deletions
|
@ -23,51 +23,81 @@ ProxyWindowBase::ProxyWindowBase(QObject* parent)
|
|||
QQmlEngine::setObjectOwnership(this->mContentItem, QQmlEngine::CppOwnership);
|
||||
this->mContentItem->setParent(this);
|
||||
|
||||
// clang-format off
|
||||
QObject::connect(this, &ProxyWindowBase::widthChanged, this, &ProxyWindowBase::onWidthChanged);
|
||||
QObject::connect(this, &ProxyWindowBase::heightChanged, this, &ProxyWindowBase::onHeightChanged);
|
||||
|
||||
QObject::connect(this, &ProxyWindowBase::maskChanged, this, &ProxyWindowBase::onMaskChanged);
|
||||
QObject::connect(this, &ProxyWindowBase::widthChanged, this, &ProxyWindowBase::onMaskChanged);
|
||||
QObject::connect(this, &ProxyWindowBase::heightChanged, this, &ProxyWindowBase::onMaskChanged);
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
ProxyWindowBase::~ProxyWindowBase() {
|
||||
if (this->window != nullptr) {
|
||||
this->window->deleteLater();
|
||||
ProxyWindowBase::~ProxyWindowBase() { this->deleteWindow(); }
|
||||
|
||||
void ProxyWindowBase::onReload(QObject* oldInstance) {
|
||||
this->window = this->retrieveWindow(oldInstance);
|
||||
auto wasVisible = this->window != nullptr && this->window->isVisible();
|
||||
if (this->window == nullptr) this->window = new QQuickWindow();
|
||||
|
||||
Reloadable::reloadRecursive(this->mContentItem, oldInstance);
|
||||
|
||||
this->connectWindow();
|
||||
this->completeWindow();
|
||||
|
||||
emit this->windowConnected();
|
||||
this->postCompleteWindow();
|
||||
|
||||
if (wasVisible && this->isVisibleDirect()) emit this->backerVisibilityChanged();
|
||||
}
|
||||
|
||||
void ProxyWindowBase::postCompleteWindow() { this->setVisible(this->mVisible); }
|
||||
|
||||
QQuickWindow* ProxyWindowBase::createQQuickWindow() { return new QQuickWindow(); }
|
||||
|
||||
void ProxyWindowBase::createWindow() {
|
||||
if (this->window != nullptr) return;
|
||||
this->window = this->createQQuickWindow();
|
||||
|
||||
this->connectWindow();
|
||||
this->completeWindow();
|
||||
emit this->windowConnected();
|
||||
}
|
||||
|
||||
void ProxyWindowBase::deleteWindow() {
|
||||
if (auto* window = this->disownWindow()) {
|
||||
if (auto* generation = EngineGeneration::findObjectGeneration(this)) {
|
||||
generation->deregisterIncubationController(window->incubationController());
|
||||
}
|
||||
|
||||
window->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
void ProxyWindowBase::onReload(QObject* oldInstance) {
|
||||
this->window = this->createWindow(oldInstance);
|
||||
QQuickWindow* ProxyWindowBase::disownWindow() {
|
||||
if (this->window == nullptr) return nullptr;
|
||||
|
||||
QObject::disconnect(this->window, nullptr, this, nullptr);
|
||||
|
||||
this->mContentItem->setParentItem(nullptr);
|
||||
|
||||
auto* window = this->window;
|
||||
this->window = nullptr;
|
||||
return window;
|
||||
}
|
||||
|
||||
QQuickWindow* ProxyWindowBase::retrieveWindow(QObject* oldInstance) {
|
||||
auto* old = qobject_cast<ProxyWindowBase*>(oldInstance);
|
||||
return old == nullptr ? nullptr : old->disownWindow();
|
||||
}
|
||||
|
||||
void ProxyWindowBase::connectWindow() {
|
||||
if (auto* generation = EngineGeneration::findObjectGeneration(this)) {
|
||||
// All windows have effectively the same incubation controller so it dosen't matter
|
||||
// which window it belongs to. We do want to replace the delay one though.
|
||||
generation->registerIncubationController(this->window->incubationController());
|
||||
}
|
||||
|
||||
this->setupWindow();
|
||||
|
||||
Reloadable::reloadRecursive(this->mContentItem, oldInstance);
|
||||
|
||||
this->mContentItem->setParentItem(this->window->contentItem());
|
||||
this->mContentItem->setWidth(this->width());
|
||||
this->mContentItem->setHeight(this->height());
|
||||
|
||||
// without this the dangling screen pointer wont be updated to a real screen
|
||||
emit this->screenChanged();
|
||||
|
||||
emit this->windowConnected();
|
||||
this->window->setVisible(this->mVisible);
|
||||
}
|
||||
|
||||
QQuickWindow* ProxyWindowBase::createWindow(QObject* oldInstance) {
|
||||
auto* old = qobject_cast<ProxyWindowBase*>(oldInstance);
|
||||
|
||||
if (old == nullptr || old->window == nullptr) {
|
||||
return new QQuickWindow();
|
||||
} else {
|
||||
return old->disownWindow();
|
||||
}
|
||||
}
|
||||
|
||||
void ProxyWindowBase::setupWindow() {
|
||||
// clang-format off
|
||||
QObject::connect(this->window, &QWindow::visibilityChanged, this, &ProxyWindowBase::visibleChanged);
|
||||
QObject::connect(this->window, &QWindow::xChanged, this, &ProxyWindowBase::xChanged);
|
||||
|
@ -76,12 +106,10 @@ void ProxyWindowBase::setupWindow() {
|
|||
QObject::connect(this->window, &QWindow::heightChanged, this, &ProxyWindowBase::heightChanged);
|
||||
QObject::connect(this->window, &QWindow::screenChanged, this, &ProxyWindowBase::screenChanged);
|
||||
QObject::connect(this->window, &QQuickWindow::colorChanged, this, &ProxyWindowBase::colorChanged);
|
||||
|
||||
QObject::connect(this, &ProxyWindowBase::maskChanged, this, &ProxyWindowBase::onMaskChanged);
|
||||
QObject::connect(this, &ProxyWindowBase::widthChanged, this, &ProxyWindowBase::onMaskChanged);
|
||||
QObject::connect(this, &ProxyWindowBase::heightChanged, this, &ProxyWindowBase::onMaskChanged);
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
void ProxyWindowBase::completeWindow() {
|
||||
if (this->mScreen != nullptr && this->window->screen() != this->mScreen) {
|
||||
if (this->window->isVisible()) this->window->setVisible(false);
|
||||
this->window->setScreen(this->mScreen);
|
||||
|
@ -95,16 +123,24 @@ void ProxyWindowBase::setupWindow() {
|
|||
// notify initial x and y positions
|
||||
emit this->xChanged();
|
||||
emit this->yChanged();
|
||||
|
||||
this->mContentItem->setParentItem(this->window->contentItem());
|
||||
this->mContentItem->setWidth(this->width());
|
||||
this->mContentItem->setHeight(this->height());
|
||||
|
||||
// without this the dangling screen pointer wont be updated to a real screen
|
||||
emit this->screenChanged();
|
||||
}
|
||||
|
||||
QQuickWindow* ProxyWindowBase::disownWindow() {
|
||||
QObject::disconnect(this->window, nullptr, this, nullptr);
|
||||
|
||||
this->mContentItem->setParentItem(nullptr);
|
||||
|
||||
auto* window = this->window;
|
||||
this->window = nullptr;
|
||||
return window;
|
||||
bool ProxyWindowBase::deleteOnInvisible() const {
|
||||
#ifdef NVIDIA_COMPAT
|
||||
// Nvidia drivers and Qt do not play nice when hiding and showing a window
|
||||
// so for nvidia compatibility we can never reuse windows if they have been
|
||||
// hidden.
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
QQuickWindow* ProxyWindowBase::backingWindow() const { return this->window; }
|
||||
|
@ -112,14 +148,38 @@ QQuickItem* ProxyWindowBase::contentItem() const { return this->mContentItem; }
|
|||
|
||||
bool ProxyWindowBase::isVisible() const {
|
||||
if (this->window == nullptr) return this->mVisible;
|
||||
else return this->isVisibleDirect();
|
||||
}
|
||||
|
||||
bool ProxyWindowBase::isVisibleDirect() const {
|
||||
if (this->window == nullptr) return false;
|
||||
else return this->window->isVisible();
|
||||
}
|
||||
|
||||
void ProxyWindowBase::setVisible(bool visible) {
|
||||
if (this->window == nullptr) {
|
||||
this->mVisible = visible;
|
||||
emit this->visibleChanged();
|
||||
} else this->window->setVisible(visible);
|
||||
this->mVisible = visible;
|
||||
this->setVisibleDirect(visible);
|
||||
}
|
||||
|
||||
void ProxyWindowBase::setVisibleDirect(bool visible) {
|
||||
if (this->deleteOnInvisible()) {
|
||||
if (visible == this->isVisibleDirect()) return;
|
||||
|
||||
if (visible) {
|
||||
this->createWindow();
|
||||
this->window->setVisible(true);
|
||||
emit this->backerVisibilityChanged();
|
||||
} else {
|
||||
if (this->window != nullptr) {
|
||||
this->window->setVisible(false);
|
||||
emit this->backerVisibilityChanged();
|
||||
this->deleteWindow();
|
||||
}
|
||||
}
|
||||
} else if (this->window != nullptr) {
|
||||
this->window->setVisible(visible);
|
||||
emit this->backerVisibilityChanged();
|
||||
}
|
||||
}
|
||||
|
||||
qint32 ProxyWindowBase::x() const {
|
||||
|
@ -138,8 +198,8 @@ qint32 ProxyWindowBase::width() const {
|
|||
}
|
||||
|
||||
void ProxyWindowBase::setWidth(qint32 width) {
|
||||
this->mWidth = width;
|
||||
if (this->window == nullptr) {
|
||||
this->mWidth = width;
|
||||
emit this->widthChanged();
|
||||
} else this->window->setWidth(width);
|
||||
}
|
||||
|
@ -150,8 +210,8 @@ qint32 ProxyWindowBase::height() const {
|
|||
}
|
||||
|
||||
void ProxyWindowBase::setHeight(qint32 height) {
|
||||
this->mHeight = height;
|
||||
if (this->window == nullptr) {
|
||||
this->mHeight = height;
|
||||
emit this->heightChanged();
|
||||
} else this->window->setHeight(height);
|
||||
}
|
||||
|
@ -170,10 +230,10 @@ void ProxyWindowBase::setScreen(QuickshellScreenInfo* screen) {
|
|||
this->mScreen = qscreen;
|
||||
emit this->screenChanged();
|
||||
} else {
|
||||
auto reshow = this->window->isVisible();
|
||||
if (reshow) this->window->setVisible(false);
|
||||
this->window->setScreen(qscreen);
|
||||
if (reshow) this->window->setVisible(true);
|
||||
auto reshow = this->isVisibleDirect();
|
||||
if (reshow) this->setVisibleDirect(false);
|
||||
if (this->window != nullptr) this->window->setScreen(qscreen);
|
||||
if (reshow) this->setVisibleDirect(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue