diff --git a/src/widgets/marginwrapper.cpp b/src/widgets/marginwrapper.cpp index 0a5fd37f..623a5f95 100644 --- a/src/widgets/marginwrapper.cpp +++ b/src/widgets/marginwrapper.cpp @@ -2,21 +2,12 @@ #include #include -#include -#include #include "wrapper.hpp" namespace qs::widgets { MarginWrapperManager::MarginWrapperManager(QObject* parent): WrapperManager(parent) { - QObject::connect( - this, - &WrapperManager::initializedChildChanged, - this, - &MarginWrapperManager::onChildChanged - ); - this->bTopMargin.setBinding([this] { return this->bExtraMargin + (this->bTopMarginSet.value() ? this->bTopMarginValue : this->bMargin); @@ -37,13 +28,46 @@ MarginWrapperManager::MarginWrapperManager(QObject* parent): WrapperManager(pare + (this->bRightMarginSet.value() ? this->bRightMarginValue : this->bMargin); }); - // Coalesces updates via binding infrastructure - this->bUpdateWatcher.setBinding([this] { - this->bTopMargin.value(); - this->bBottomMargin.value(); - this->bLeftMargin.value(); - this->bRightMargin.value(); - return 0; + this->bChildX.setBinding([this] { + if (this->bResizeChild) return this->bLeftMargin.value(); + + auto total = this->bLeftMargin + this->bRightMargin; + auto mul = total == 0 ? 0.5 : this->bLeftMargin / total; + auto margin = this->bWrapperWidth - this->bChildImplicitWidth; + return margin * mul; + }); + + this->bChildY.setBinding([this] { + if (this->bResizeChild) return this->bTopMargin.value(); + + auto total = this->bTopMargin + this->bBottomMargin; + auto mul = total == 0 ? 0.5 : this->bTopMargin / total; + auto margin = this->bWrapperHeight - this->bChildImplicitHeight; + return margin * mul; + }); + + this->bChildWidth.setBinding([this] { + if (this->bResizeChild) { + return this->bWrapperWidth - (this->bLeftMargin + this->bRightMargin); + } else { + return this->bChildImplicitWidth.value(); + } + }); + + this->bChildHeight.setBinding([this] { + if (this->bResizeChild) { + return this->bWrapperHeight - (this->bTopMargin + this->bBottomMargin); + } else { + return this->bChildImplicitHeight.value(); + } + }); + + this->bWrapperImplicitWidth.setBinding([this] { + return this->bChildImplicitWidth.value() + this->bLeftMargin + this->bRightMargin; + }); + + this->bWrapperImplicitHeight.setBinding([this] { + return this->bChildImplicitHeight.value() + this->bTopMargin + this->bBottomMargin; }); } @@ -51,141 +75,57 @@ void MarginWrapperManager::componentComplete() { this->WrapperManager::componentComplete(); if (this->mWrapper) { - QObject::connect( - this->mWrapper, - &QQuickItem::widthChanged, - this, - &MarginWrapperManager::updateChildX - ); - - QObject::connect( - this->mWrapper, - &QQuickItem::heightChanged, - this, - &MarginWrapperManager::updateChildY - ); + this->bWrapperWidth.setBinding([this] { return this->mWrapper->bindableWidth().value(); }); + this->bWrapperHeight.setBinding([this] { return this->mWrapper->bindableHeight().value(); }); } - - if (!this->mChild) this->updateGeometry(); } -bool MarginWrapperManager::resizeChild() const { return this->mResizeChild; } - -void MarginWrapperManager::setResizeChild(bool resizeChild) { - if (resizeChild == this->mResizeChild) return; - this->mResizeChild = resizeChild; - this->updateGeometry(); - emit this->resizeChildChanged(); +void MarginWrapperManager::disconnectChild() { + this->mChild->bindableX().setValue(0); + this->mChild->bindableY().setValue(0); + this->mChild->bindableWidth().setValue(0); + this->mChild->bindableHeight().setValue(0); } -void MarginWrapperManager::onChildChanged() { +void MarginWrapperManager::connectChild() { // QObject::disconnect in MarginWrapper handles disconnecting old item + this->mChild->bindableX().setBinding([this] { return this->bChildX.value(); }); + this->mChild->bindableY().setBinding([this] { return this->bChildY.value(); }); + this->mChild->bindableWidth().setBinding([this] { return this->bChildWidth.value(); }); + this->mChild->bindableHeight().setBinding([this] { return this->bChildHeight.value(); }); - if (this->mChild) { - QObject::connect( - this->mChild, - &QQuickItem::implicitWidthChanged, - this, - &MarginWrapperManager::onChildImplicitWidthChanged - ); + QObject::connect( + this->mChild, + &QQuickItem::implicitWidthChanged, + this, + &MarginWrapperManager::onChildImplicitWidthChanged + ); - QObject::connect( - this->mChild, - &QQuickItem::implicitHeightChanged, - this, - &MarginWrapperManager::onChildImplicitHeightChanged - ); - } + QObject::connect( + this->mChild, + &QQuickItem::implicitHeightChanged, + this, + &MarginWrapperManager::onChildImplicitHeightChanged + ); - this->updateGeometry(); -} - -qreal MarginWrapperManager::targetChildWidth() const { - auto max = this->mWrapper->width() - (this->bLeftMargin + this->bRightMargin); - - if (this->mResizeChild) return max; - else return this->mChild->implicitWidth(); -} - -qreal MarginWrapperManager::targetChildHeight() const { - auto max = this->mWrapper->height() - (this->bTopMargin + this->bBottomMargin); - - if (this->mResizeChild) return max; - else return this->mChild->implicitHeight(); -} - -qreal MarginWrapperManager::targetChildX() const { - if (this->mResizeChild) return this->bLeftMargin; - else { - auto total = this->bLeftMargin + this->bRightMargin; - auto mul = total == 0 ? 0.5 : this->bLeftMargin / total; - auto margin = this->mWrapper->width() - this->mChild->implicitWidth(); - return margin * mul; - } -} - -qreal MarginWrapperManager::targetChildY() const { - if (this->mResizeChild) return this->bTopMargin; - else { - auto total = this->bTopMargin + this->bBottomMargin; - auto mul = total == 0 ? 0.5 : this->bTopMargin / total; - auto margin = this->mWrapper->height() - this->mChild->implicitHeight(); - return margin * mul; - } -} - -void MarginWrapperManager::updateChildX() { - if (!this->mChild || !this->mWrapper) return; - this->mChild->setX(this->targetChildX()); - this->mChild->setWidth(this->targetChildWidth()); -} - -void MarginWrapperManager::updateChildY() { - if (!this->mChild || !this->mWrapper) return; - this->mChild->setY(this->targetChildY()); - this->mChild->setHeight(this->targetChildHeight()); + this->onChildImplicitWidthChanged(); + this->onChildImplicitHeightChanged(); } void MarginWrapperManager::onChildImplicitWidthChanged() { - if (!this->mChild || !this->mWrapper) return; - this->mWrapper->setImplicitWidth( - this->mChild->implicitWidth() + this->bLeftMargin + this->bRightMargin - ); - - // If the implicit width change does not result in an actual width change, - // this will not be called anywhere else. - this->updateChildX(); + this->bChildImplicitWidth = this->mChild->implicitWidth(); } void MarginWrapperManager::onChildImplicitHeightChanged() { - if (!this->mChild || !this->mWrapper) return; - this->mWrapper->setImplicitHeight( - this->mChild->implicitHeight() + this->bTopMargin + this->bBottomMargin - ); - - // If the implicit height change does not result in an actual height change, - // this will not be called anywhere else. - this->updateChildY(); + this->bChildImplicitHeight = this->mChild->implicitHeight(); } -void MarginWrapperManager::updateGeometry() { - if (!this->mWrapper) return; +void MarginWrapperManager::setWrapperImplicitWidth() { + if (this->mWrapper) this->mWrapper->setImplicitWidth(this->bWrapperImplicitWidth); +} - if (this->mChild) { - this->mWrapper->setImplicitWidth( - this->mChild->implicitWidth() + this->bLeftMargin + this->bRightMargin - ); - this->mWrapper->setImplicitHeight( - this->mChild->implicitHeight() + this->bTopMargin + this->bBottomMargin - ); - this->mChild->setX(this->targetChildX()); - this->mChild->setY(this->targetChildY()); - this->mChild->setWidth(this->targetChildWidth()); - this->mChild->setHeight(this->targetChildHeight()); - } else { - this->mWrapper->setImplicitWidth(this->bLeftMargin + this->bRightMargin); - this->mWrapper->setImplicitHeight(this->bTopMargin + this->bBottomMargin); - } +void MarginWrapperManager::setWrapperImplicitHeight() { + if (this->mWrapper) this->mWrapper->setImplicitHeight(this->bWrapperImplicitHeight); } } // namespace qs::widgets diff --git a/src/widgets/marginwrapper.hpp b/src/widgets/marginwrapper.hpp index 8af6255e..7a98f07e 100644 --- a/src/widgets/marginwrapper.hpp +++ b/src/widgets/marginwrapper.hpp @@ -6,7 +6,6 @@ #include #include -#include "../core/util.hpp" #include "wrapper.hpp" namespace qs::widgets { @@ -69,7 +68,7 @@ class MarginWrapperManager: public WrapperManager { Q_PROPERTY(qreal rightMargin READ rightMargin WRITE setRightMargin RESET resetRightMargin NOTIFY rightMarginChanged FINAL); /// Determines if child item should be resized larger than its implicit size if /// the parent is resized larger than its implicit size. Defaults to false. - Q_PROPERTY(bool resizeChild READ resizeChild WRITE setResizeChild NOTIFY resizeChildChanged FINAL); + Q_PROPERTY(bool resizeChild READ default WRITE default BINDABLE bindableResizeChild NOTIFY resizeChildChanged FINAL); // clang-format on QML_ELEMENT; @@ -109,8 +108,7 @@ public: this->bRightMarginSet = true; } - [[nodiscard]] bool resizeChild() const; - void setResizeChild(bool resizeChild); + [[nodiscard]] QBindable bindableResizeChild() { return &this->bResizeChild; } signals: void marginChanged(); @@ -122,42 +120,50 @@ signals: void resizeChildChanged(); private slots: - void onChildChanged(); - void updateChildX(); - void updateChildY(); void onChildImplicitWidthChanged(); void onChildImplicitHeightChanged(); + void setWrapperImplicitWidth(); + void setWrapperImplicitHeight(); + +protected: + void disconnectChild() override; + void connectChild() override; private: - void updateGeometry(); - - [[nodiscard]] qreal targetChildX() const; - [[nodiscard]] qreal targetChildY() const; - [[nodiscard]] qreal targetChildWidth() const; - [[nodiscard]] qreal targetChildHeight() const; - - bool mResizeChild = false; - // clang-format off + Q_OBJECT_BINDABLE_PROPERTY(MarginWrapperManager, bool, bResizeChild); Q_OBJECT_BINDABLE_PROPERTY(MarginWrapperManager, qreal, bMargin, &MarginWrapperManager::marginChanged); Q_OBJECT_BINDABLE_PROPERTY(MarginWrapperManager, qreal, bExtraMargin, &MarginWrapperManager::baseMarginChanged); - Q_OBJECT_BINDABLE_PROPERTY(MarginWrapperManager, qreal, bTopMarginValue); - Q_OBJECT_BINDABLE_PROPERTY(MarginWrapperManager, qreal, bBottomMarginValue); - Q_OBJECT_BINDABLE_PROPERTY(MarginWrapperManager, qreal, bLeftMarginValue); - Q_OBJECT_BINDABLE_PROPERTY(MarginWrapperManager, qreal, bRightMarginValue); Q_OBJECT_BINDABLE_PROPERTY(MarginWrapperManager, bool, bTopMarginSet); Q_OBJECT_BINDABLE_PROPERTY(MarginWrapperManager, bool, bBottomMarginSet); Q_OBJECT_BINDABLE_PROPERTY(MarginWrapperManager, bool, bLeftMarginSet); Q_OBJECT_BINDABLE_PROPERTY(MarginWrapperManager, bool, bRightMarginSet); + Q_OBJECT_BINDABLE_PROPERTY(MarginWrapperManager, qreal, bTopMarginValue); + Q_OBJECT_BINDABLE_PROPERTY(MarginWrapperManager, qreal, bBottomMarginValue); + Q_OBJECT_BINDABLE_PROPERTY(MarginWrapperManager, qreal, bLeftMarginValue); + Q_OBJECT_BINDABLE_PROPERTY(MarginWrapperManager, qreal, bRightMarginValue); + // computed Q_OBJECT_BINDABLE_PROPERTY(MarginWrapperManager, qreal, bTopMargin, &MarginWrapperManager::topMarginChanged); Q_OBJECT_BINDABLE_PROPERTY(MarginWrapperManager, qreal, bBottomMargin, &MarginWrapperManager::bottomMarginChanged); Q_OBJECT_BINDABLE_PROPERTY(MarginWrapperManager, qreal, bLeftMargin, &MarginWrapperManager::leftMarginChanged); Q_OBJECT_BINDABLE_PROPERTY(MarginWrapperManager, qreal, bRightMargin, &MarginWrapperManager::rightMarginChanged); - Q_OBJECT_BINDABLE_PROPERTY(MarginWrapperManager, int, bUpdateWatcher); - QS_BINDING_SUBSCRIBE_METHOD(MarginWrapperManager, bUpdateWatcher, updateGeometry, subscribe); + // bound + Q_OBJECT_BINDABLE_PROPERTY(MarginWrapperManager, qreal, bWrapperWidth); + Q_OBJECT_BINDABLE_PROPERTY(MarginWrapperManager, qreal, bWrapperHeight); + Q_OBJECT_BINDABLE_PROPERTY(MarginWrapperManager, qreal, bChildImplicitWidth); + Q_OBJECT_BINDABLE_PROPERTY(MarginWrapperManager, qreal, bChildImplicitHeight); + + // computed + Q_OBJECT_BINDABLE_PROPERTY(MarginWrapperManager, qreal, bChildX); + Q_OBJECT_BINDABLE_PROPERTY(MarginWrapperManager, qreal, bChildY); + Q_OBJECT_BINDABLE_PROPERTY(MarginWrapperManager, qreal, bChildWidth); + Q_OBJECT_BINDABLE_PROPERTY(MarginWrapperManager, qreal, bChildHeight); + Q_OBJECT_BINDABLE_PROPERTY(MarginWrapperManager, qreal, bWrapperImplicitWidth, &MarginWrapperManager::setWrapperImplicitWidth); + Q_OBJECT_BINDABLE_PROPERTY(MarginWrapperManager, qreal, bWrapperImplicitHeight, &MarginWrapperManager::setWrapperImplicitHeight); + // clang-format on }; diff --git a/src/widgets/test/manual/marginwrapper.qml b/src/widgets/test/manual/marginwrapper.qml new file mode 100644 index 00000000..c3307abe --- /dev/null +++ b/src/widgets/test/manual/marginwrapper.qml @@ -0,0 +1,150 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import Quickshell +import Quickshell.Widgets + +FloatingWindow { + color: contentItem.palette.window + + ColumnLayout { + anchors.fill: parent + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + + Rectangle { + anchors.centerIn: parent + width: stretchCb.checked ? wrapperWidthSlider.value : implicitWidth + height: stretchCb.checked ? wrapperHeightSlider.value : implicitHeight + border.color: "black" + + MarginWrapperManager { + margin: marginSlider.value + extraMargin: extraMarginSlider.value + resizeChild: resizeCb.checked + topMargin: separateMarginsCb.checked ? topMarginSlider.value : undefined + bottomMargin: separateMarginsCb.checked ? bottomMarginSlider.value : undefined + leftMargin: separateMarginsCb.checked ? leftMarginSlider.value : undefined + rightMargin: separateMarginsCb.checked ? rightMarginSlider.value : undefined + } + + Rectangle { + color: "green" + implicitWidth: implicitWidthSlider.value + implicitHeight: implicitHeightSlider.value + } + } + } + + RowLayout { + Layout.fillWidth: true + + CheckBox { + id: stretchCb + text: "Stretch" + } + + CheckBox { + id: resizeCb + text: "Resize Child" + } + + CheckBox { + id: separateMarginsCb + text: "Individual Margins" + } + } + + RowLayout { + Layout.fillWidth: true + + Label { text: "Stretch Width" } + Slider { + id: wrapperWidthSlider + Layout.fillWidth: true + from: 0; to: 300; value: 200 + } + + Label { text: "Stretch Height" } + Slider { + id: wrapperHeightSlider + Layout.fillWidth: true + from: 0; to: 300; value: 200 + } + } + + RowLayout { + Layout.fillWidth: true + + Label { text: "Implicit Width" } + Slider { + id: implicitWidthSlider + Layout.fillWidth: true + from: 0; to: 200; value: 100 + } + + Label { text: "Implicit Height" } + Slider { + id: implicitHeightSlider + Layout.fillWidth: true + from: 0; to: 200; value: 100 + } + } + + RowLayout { + Layout.fillWidth: true + + Label { text: "Margin" } + Slider { + id: marginSlider + Layout.fillWidth: true + from: -100; to: 200; value: 50 + } + + Label { text: "Extra" } + Slider { + id: extraMarginSlider + Layout.fillWidth: true + from: -100; to: 200; value: 0 + } + } + + RowLayout { + Layout.fillWidth: true + + Label { text: "Top Margin" } + Slider { + id: topMarginSlider + Layout.fillWidth: true + from: -100; to: 200; value: 50 + } + + Label { text: "Bottom Margin" } + Slider { + id: bottomMarginSlider + Layout.fillWidth: true + from: -100; to: 200; value: 50 + } + } + + RowLayout { + Layout.fillWidth: true + + Label { text: "Left Margin" } + Slider { + id: leftMarginSlider + Layout.fillWidth: true + from: -100; to: 200; value: 50 + } + + Label { text: "Right Margin" } + Slider { + id: rightMarginSlider + Layout.fillWidth: true + from: -100; to: 200; value: 50 + } + } + } +} diff --git a/src/widgets/wrapper.cpp b/src/widgets/wrapper.cpp index e45333ad..ee18da4e 100644 --- a/src/widgets/wrapper.cpp +++ b/src/widgets/wrapper.cpp @@ -61,6 +61,7 @@ void WrapperManager::setChild(QQuickItem* child) { if (this->mChild->parentItem() == this->mWrapper) { this->mChild->setParentItem(nullptr); } + this->disconnectChild(); } this->mChild = child; @@ -78,6 +79,8 @@ void WrapperManager::setChild(QQuickItem* child) { if (auto* wrapper = this->mWrapper) { child->setParentItem(wrapper); } + + this->connectChild(); } emit this->initializedChildChanged(); diff --git a/src/widgets/wrapper.hpp b/src/widgets/wrapper.hpp index 993cfd51..d506750c 100644 --- a/src/widgets/wrapper.hpp +++ b/src/widgets/wrapper.hpp @@ -137,6 +137,9 @@ protected: void printChildCountWarning() const; void updateGeometry(); + virtual void disconnectChild() {}; + virtual void connectChild() {}; + QQuickItem* mWrapper = nullptr; QQuickItem* mAssignedWrapper = nullptr; QPointer mDefaultChild;