quickshell/src/core/transformwatcher.cpp
2024-06-18 20:46:58 -07:00

183 lines
4.9 KiB
C++

#include "transformwatcher.hpp"
#include <qcontainerfwd.h>
#include <qdebug.h>
#include <qlist.h>
#include <qlogging.h>
#include <qobject.h>
#include <qquickitem.h>
#include <qquickwindow.h>
#include <qtmetamacros.h>
void TransformWatcher::resolveChains(QQuickItem* a, QQuickItem* b, QQuickItem* commonParent) {
if (a == nullptr || b == nullptr) return;
auto aChain = QVector<QQuickItem*>();
auto bChain = QVector<QQuickItem*>();
auto* aParent = a;
auto* bParent = b;
// resolve the parent chain of b. if a is in the chain break early
while (bParent != nullptr) {
bChain.push_back(bParent);
if (bParent == a) {
aChain.push_back(a);
goto chainResolved;
}
if (bParent == commonParent) break;
bParent = bParent->parentItem();
}
// resolve the parent chain of a, breaking as soon as b is found
while (aParent != nullptr) {
aChain.push_back(aParent);
for (auto bParent = bChain.begin(); bParent != bChain.end(); bParent++) {
if (*bParent == aParent) {
bParent++;
bChain.erase(bParent, bChain.end());
goto chainResolved;
}
}
if (aParent == commonParent) break;
aParent = aParent->parentItem();
}
if (commonParent != nullptr && aParent == commonParent) {
qWarning() << this << "failed to find a common parent between" << a << "and" << b
<< "due to incorrectly set commonParent" << commonParent;
return;
}
chainResolved:
this->parentChain = aChain;
if (bChain.last() == aChain.last()) bChain.removeLast();
this->childChain = bChain;
if (a->window() != b->window()) {
this->parentWindow = a->window();
this->childWindow = b->window();
} else {
this->parentWindow = nullptr;
this->childWindow = nullptr;
}
}
void TransformWatcher::resolveChains() {
this->resolveChains(this->mA, this->mB, this->mCommonParent);
}
void TransformWatcher::linkItem(QQuickItem* item) const {
QObject::connect(item, &QQuickItem::xChanged, this, &TransformWatcher::transformChanged);
QObject::connect(item, &QQuickItem::yChanged, this, &TransformWatcher::transformChanged);
QObject::connect(item, &QQuickItem::widthChanged, this, &TransformWatcher::transformChanged);
QObject::connect(item, &QQuickItem::heightChanged, this, &TransformWatcher::transformChanged);
QObject::connect(item, &QQuickItem::scaleChanged, this, &TransformWatcher::transformChanged);
QObject::connect(item, &QQuickItem::rotationChanged, this, &TransformWatcher::transformChanged);
QObject::connect(item, &QQuickItem::parentChanged, this, &TransformWatcher::recalcChains);
QObject::connect(item, &QQuickItem::windowChanged, this, &TransformWatcher::recalcChains);
if (item != this->mA && item != this->mB) {
QObject::connect(item, &QObject::destroyed, this, &TransformWatcher::itemDestroyed);
}
}
void TransformWatcher::linkChains() {
for (auto* item: this->parentChain) {
this->linkItem(item);
}
for (auto* item: this->childChain) {
this->linkItem(item);
}
}
void TransformWatcher::unlinkChains() {
for (auto* item: this->parentChain) {
QObject::disconnect(item, nullptr, this, nullptr);
}
for (auto* item: this->childChain) {
QObject::disconnect(item, nullptr, this, nullptr);
}
// relink a and b destruction notifications
if (this->mA != nullptr) {
QObject::connect(this->mA, &QObject::destroyed, this, &TransformWatcher::aDestroyed);
}
if (this->mB != nullptr) {
QObject::connect(this->mB, &QObject::destroyed, this, &TransformWatcher::bDestroyed);
}
this->parentChain.clear();
this->childChain.clear();
}
void TransformWatcher::recalcChains() {
this->unlinkChains();
this->resolveChains();
this->linkChains();
}
void TransformWatcher::itemDestroyed() {
auto destroyed =
this->parentChain.removeOne(this->sender()) || this->childChain.removeOne(this->sender());
if (destroyed) this->recalcChains();
}
QQuickItem* TransformWatcher::a() const { return this->mA; }
void TransformWatcher::setA(QQuickItem* a) {
if (this->mA == a) return;
if (this->mA != nullptr) QObject::disconnect(this->mA, nullptr, this, nullptr);
this->mA = a;
if (this->mA != nullptr) {
QObject::connect(this->mA, &QObject::destroyed, this, &TransformWatcher::aDestroyed);
}
this->recalcChains();
}
void TransformWatcher::aDestroyed() {
this->mA = nullptr;
this->unlinkChains();
emit this->aChanged();
}
QQuickItem* TransformWatcher::b() const { return this->mB; }
void TransformWatcher::setB(QQuickItem* b) {
if (this->mB == b) return;
if (this->mB != nullptr) QObject::disconnect(this->mB, nullptr, this, nullptr);
this->mB = b;
if (this->mB != nullptr) {
QObject::connect(this->mB, &QObject::destroyed, this, &TransformWatcher::bDestroyed);
}
this->recalcChains();
}
void TransformWatcher::bDestroyed() {
this->mB = nullptr;
this->unlinkChains();
emit this->bChanged();
}
QQuickItem* TransformWatcher::commonParent() const { return this->mCommonParent; }
void TransformWatcher::setCommonParent(QQuickItem* commonParent) {
if (this->mCommonParent == commonParent) return;
this->mCommonParent = commonParent;
this->recalcChains();
}