forked from quickshell/quickshell
widgets: add wrapper components and managers
This commit is contained in:
parent
79fca3cab8
commit
401ee4cec6
8 changed files with 578 additions and 0 deletions
|
@ -1,5 +1,7 @@
|
||||||
qt_add_library(quickshell-widgets STATIC
|
qt_add_library(quickshell-widgets STATIC
|
||||||
cliprect.cpp
|
cliprect.cpp
|
||||||
|
wrapper.cpp
|
||||||
|
marginwrapper.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
qt_add_qml_module(quickshell-widgets
|
qt_add_qml_module(quickshell-widgets
|
||||||
|
@ -8,6 +10,8 @@ qt_add_qml_module(quickshell-widgets
|
||||||
QML_FILES
|
QML_FILES
|
||||||
IconImage.qml
|
IconImage.qml
|
||||||
ClippingRectangle.qml
|
ClippingRectangle.qml
|
||||||
|
WrapperItem.qml
|
||||||
|
WrapperRectangle.qml
|
||||||
)
|
)
|
||||||
|
|
||||||
qt6_add_shaders(quickshell-widgets "widgets-cliprect"
|
qt6_add_shaders(quickshell-widgets "widgets-cliprect"
|
||||||
|
|
44
src/widgets/WrapperItem.qml
Normal file
44
src/widgets/WrapperItem.qml
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Widgets
|
||||||
|
|
||||||
|
///! Item that handles sizes and positioning for a single visual child.
|
||||||
|
/// This component is useful when you need to wrap a single component in
|
||||||
|
/// an item, or give a single component a margin. See [QtQuick.Layouts]
|
||||||
|
/// for positioning multiple items.
|
||||||
|
///
|
||||||
|
/// > [!NOTE] WrapperItem is a @@MarginWrapperManager based component.
|
||||||
|
/// > You should read its documentation as well.
|
||||||
|
///
|
||||||
|
/// ### Example: Adding a margin to an item
|
||||||
|
/// The snippet below adds a 10px margin to all sides of the @@QtQuick.Text item.
|
||||||
|
///
|
||||||
|
/// ```qml
|
||||||
|
/// WrapperItem {
|
||||||
|
/// margin: 10
|
||||||
|
///
|
||||||
|
/// @@QtQuick.Text { text: "Hello!" }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// > [!NOTE] The child item can be specified by writing it inline in the wrapper,
|
||||||
|
/// > as in the example above, or by using the @@child property. See
|
||||||
|
/// > @@WrapperManager.child for details.
|
||||||
|
///
|
||||||
|
/// > [!WARNING] You should not set @@Item.x, @@Item.y, @@Item.width,
|
||||||
|
/// > @@Item.height or @@Item.anchors on the child item, as they are used
|
||||||
|
/// > by WrapperItem to position it. Instead set @@Item.implicitWidth and
|
||||||
|
/// > @@Item.implicitHeight.
|
||||||
|
///
|
||||||
|
/// [QtQuick.Layouts]: https://doc.qt.io/qt-6/qtquicklayouts-index.html
|
||||||
|
Item {
|
||||||
|
/// The minimum margin between the child item and the WrapperItem's edges.
|
||||||
|
/// Defaults to 0.
|
||||||
|
property /*real*/alias margin: manager.margin
|
||||||
|
/// If the child item should be resized larger than its implicit size if
|
||||||
|
/// the WrapperItem is resized larger than its implicit size. Defaults to false.
|
||||||
|
property /*bool*/alias resizeChild: manager.resizeChild
|
||||||
|
/// See @@WrapperManager.child for details.
|
||||||
|
property /*Item*/alias child: manager.child
|
||||||
|
|
||||||
|
MarginWrapperManager { id: manager }
|
||||||
|
}
|
35
src/widgets/WrapperRectangle.qml
Normal file
35
src/widgets/WrapperRectangle.qml
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import QtQuick
|
||||||
|
import Quickshell.Widgets
|
||||||
|
|
||||||
|
///! Rectangle that handles sizes and positioning for a single visual child.
|
||||||
|
/// This component is useful for adding a border or background rectangle to
|
||||||
|
/// a child item.
|
||||||
|
///
|
||||||
|
/// > [!NOTE] WrapperRectangle is a @@MarginWrapperManager based component.
|
||||||
|
/// > You should read its documentation as well.
|
||||||
|
///
|
||||||
|
/// > [!WARNING] You should not set @@Item.x, @@Item.y, @@Item.width,
|
||||||
|
/// > @@Item.height or @@Item.anchors on the child item, as they are used
|
||||||
|
/// > by WrapperItem to position it. Instead set @@Item.implicitWidth and
|
||||||
|
/// > @@Item.implicitHeight.
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
/// If true (default), the rectangle's border width will be added
|
||||||
|
/// to the margin.
|
||||||
|
property bool contentInsideBorder: true
|
||||||
|
/// The minimum margin between the child item and the WrapperRectangle's
|
||||||
|
/// edges. If @@contentInsideBorder is true, this excludes the border,
|
||||||
|
/// otherwise it includes it. Defaults to 0.
|
||||||
|
property real margin: 0
|
||||||
|
/// If the child item should be resized larger than its implicit size if
|
||||||
|
/// the WrapperRectangle is resized larger than its implicit size. Defaults to false.
|
||||||
|
property /*bool*/alias resizeChild: manager.resizeChild
|
||||||
|
/// See @@WrapperManager.child for details.
|
||||||
|
property alias child: manager.child
|
||||||
|
|
||||||
|
MarginWrapperManager {
|
||||||
|
id: manager
|
||||||
|
margin: (root.contentInsideBorder ? root.border.width : 0) + root.margin
|
||||||
|
}
|
||||||
|
}
|
146
src/widgets/marginwrapper.cpp
Normal file
146
src/widgets/marginwrapper.cpp
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
#include "marginwrapper.hpp"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qquickitem.h>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
#include <qtypes.h>
|
||||||
|
|
||||||
|
#include "wrapper.hpp"
|
||||||
|
|
||||||
|
namespace qs::widgets {
|
||||||
|
|
||||||
|
MarginWrapperManager::MarginWrapperManager(QObject* parent): WrapperManager(parent) {
|
||||||
|
QObject::connect(
|
||||||
|
this,
|
||||||
|
&WrapperManager::initializedChildChanged,
|
||||||
|
this,
|
||||||
|
&MarginWrapperManager::onChildChanged
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarginWrapperManager::componentComplete() {
|
||||||
|
if (this->mWrapper) {
|
||||||
|
QObject::connect(
|
||||||
|
this->mWrapper,
|
||||||
|
&QQuickItem::widthChanged,
|
||||||
|
this,
|
||||||
|
&MarginWrapperManager::onWrapperWidthChanged
|
||||||
|
);
|
||||||
|
|
||||||
|
QObject::connect(
|
||||||
|
this->mWrapper,
|
||||||
|
&QQuickItem::heightChanged,
|
||||||
|
this,
|
||||||
|
&MarginWrapperManager::onWrapperHeightChanged
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->WrapperManager::componentComplete();
|
||||||
|
|
||||||
|
if (!this->mChild) this->updateGeometry();
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal MarginWrapperManager::margin() const { return this->mMargin; }
|
||||||
|
|
||||||
|
void MarginWrapperManager::setMargin(qreal margin) {
|
||||||
|
if (margin == this->mMargin) return;
|
||||||
|
this->mMargin = margin;
|
||||||
|
this->updateGeometry();
|
||||||
|
emit this->marginChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
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::onChildChanged() {
|
||||||
|
// QObject::disconnect in MarginWrapper handles disconnecting old item
|
||||||
|
|
||||||
|
if (this->mChild) {
|
||||||
|
QObject::connect(
|
||||||
|
this->mChild,
|
||||||
|
&QQuickItem::implicitWidthChanged,
|
||||||
|
this,
|
||||||
|
&MarginWrapperManager::onChildImplicitWidthChanged
|
||||||
|
);
|
||||||
|
|
||||||
|
QObject::connect(
|
||||||
|
this->mChild,
|
||||||
|
&QQuickItem::implicitHeightChanged,
|
||||||
|
this,
|
||||||
|
&MarginWrapperManager::onChildImplicitHeightChanged
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->updateGeometry();
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal MarginWrapperManager::targetChildWidth() const {
|
||||||
|
auto max = this->mWrapper->width() - this->mMargin * 2;
|
||||||
|
|
||||||
|
if (this->mResizeChild) return max;
|
||||||
|
else return std::min(this->mChild->implicitWidth(), max);
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal MarginWrapperManager::targetChildHeight() const {
|
||||||
|
auto max = this->mWrapper->height() - this->mMargin * 2;
|
||||||
|
|
||||||
|
if (this->mResizeChild) return max;
|
||||||
|
else return std::min(this->mChild->implicitHeight(), max);
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal MarginWrapperManager::targetChildX() const {
|
||||||
|
if (this->mResizeChild) return this->mMargin;
|
||||||
|
else return this->mWrapper->width() / 2 - this->mChild->implicitWidth() / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
qreal MarginWrapperManager::targetChildY() const {
|
||||||
|
if (this->mResizeChild) return this->mMargin;
|
||||||
|
else return this->mWrapper->height() / 2 - this->mChild->implicitHeight() / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarginWrapperManager::onWrapperWidthChanged() {
|
||||||
|
if (!this->mChild || !this->mWrapper) return;
|
||||||
|
this->mChild->setX(this->targetChildX());
|
||||||
|
this->mChild->setWidth(this->targetChildWidth());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarginWrapperManager::onWrapperHeightChanged() {
|
||||||
|
if (!this->mChild || !this->mWrapper) return;
|
||||||
|
this->mChild->setY(this->targetChildY());
|
||||||
|
this->mChild->setHeight(this->targetChildHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarginWrapperManager::onChildImplicitWidthChanged() {
|
||||||
|
if (!this->mChild || !this->mWrapper) return;
|
||||||
|
this->mWrapper->setImplicitWidth(this->mChild->implicitWidth() + this->mMargin * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarginWrapperManager::onChildImplicitHeightChanged() {
|
||||||
|
if (!this->mChild || !this->mWrapper) return;
|
||||||
|
this->mWrapper->setImplicitHeight(this->mChild->implicitHeight() + this->mMargin * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarginWrapperManager::updateGeometry() {
|
||||||
|
if (!this->mWrapper) return;
|
||||||
|
|
||||||
|
if (this->mChild) {
|
||||||
|
this->mWrapper->setImplicitWidth(this->mChild->implicitWidth() + this->mMargin * 2);
|
||||||
|
this->mWrapper->setImplicitHeight(this->mChild->implicitHeight() + this->mMargin * 2);
|
||||||
|
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->mMargin * 2);
|
||||||
|
this->mWrapper->setImplicitHeight(this->mMargin * 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace qs::widgets
|
75
src/widgets/marginwrapper.hpp
Normal file
75
src/widgets/marginwrapper.hpp
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qqmlintegration.h>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
#include <qtypes.h>
|
||||||
|
|
||||||
|
#include "wrapper.hpp"
|
||||||
|
|
||||||
|
namespace qs::widgets {
|
||||||
|
|
||||||
|
///! Helper object for applying sizes and margins to a single child item.
|
||||||
|
/// > [!NOTE] MarginWrapperManager is an extension of @@WrapperManager.
|
||||||
|
/// > You should read its documentation to understand wrapper types.
|
||||||
|
///
|
||||||
|
/// MarginWrapperManager can be used to apply margins to a child item,
|
||||||
|
/// in addition to handling the size / implicit size relationship
|
||||||
|
/// between the parent and the child. @@WrapperItem and @@WrapperRectangle
|
||||||
|
/// exist for Item and Rectangle implementations respectively.
|
||||||
|
///
|
||||||
|
/// > [!WARNING] MarginWrapperManager based types set the child item's
|
||||||
|
/// > @@QtQuick.Item.x, @@QtQuick.Item.y, @@QtQuick.Item.width, @@QtQuick.Item.height
|
||||||
|
/// > or @@QtQuick.Item.anchors properties. Do not set them yourself,
|
||||||
|
/// > instead set @@Item.implicitWidth and @@Item.implicitHeight.
|
||||||
|
///
|
||||||
|
/// ### Implementing a margin wrapper type
|
||||||
|
/// Follow the directions in @@WrapperManager$'s documentation, and or
|
||||||
|
/// alias the @@margin property if you wish to expose it.
|
||||||
|
class MarginWrapperManager: public WrapperManager {
|
||||||
|
Q_OBJECT;
|
||||||
|
// clang-format off
|
||||||
|
/// The minimum margin between the child item and the parent item's edges.
|
||||||
|
/// Defaults to 0.
|
||||||
|
Q_PROPERTY(qreal margin READ margin WRITE setMargin NOTIFY marginChanged FINAL);
|
||||||
|
/// If the 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);
|
||||||
|
// clang-format on
|
||||||
|
QML_ELEMENT;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit MarginWrapperManager(QObject* parent = nullptr);
|
||||||
|
|
||||||
|
void componentComplete() override;
|
||||||
|
|
||||||
|
[[nodiscard]] qreal margin() const;
|
||||||
|
void setMargin(qreal margin);
|
||||||
|
|
||||||
|
[[nodiscard]] bool resizeChild() const;
|
||||||
|
void setResizeChild(bool resizeChild);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void marginChanged();
|
||||||
|
void resizeChildChanged();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onChildChanged();
|
||||||
|
void onWrapperWidthChanged();
|
||||||
|
void onWrapperHeightChanged();
|
||||||
|
void onChildImplicitWidthChanged();
|
||||||
|
void onChildImplicitHeightChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateGeometry();
|
||||||
|
|
||||||
|
[[nodiscard]] qreal targetChildX() const;
|
||||||
|
[[nodiscard]] qreal targetChildY() const;
|
||||||
|
[[nodiscard]] qreal targetChildWidth() const;
|
||||||
|
[[nodiscard]] qreal targetChildHeight() const;
|
||||||
|
|
||||||
|
qreal mMargin = 0;
|
||||||
|
bool mResizeChild = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace qs::widgets
|
|
@ -1,7 +1,15 @@
|
||||||
name = "Quickshell.Widgets"
|
name = "Quickshell.Widgets"
|
||||||
description = "Bundled widgets"
|
description = "Bundled widgets"
|
||||||
|
|
||||||
|
headers = [
|
||||||
|
"wrapper.hpp",
|
||||||
|
"marginwrapper.hpp",
|
||||||
|
]
|
||||||
|
|
||||||
qml_files = [
|
qml_files = [
|
||||||
"IconImage.qml",
|
"IconImage.qml",
|
||||||
"ClippingRectangle.qml",
|
"ClippingRectangle.qml",
|
||||||
|
"WrapperItem.qml",
|
||||||
|
"WrapperRectangle.qml",
|
||||||
]
|
]
|
||||||
-----
|
-----
|
||||||
|
|
127
src/widgets/wrapper.cpp
Normal file
127
src/widgets/wrapper.cpp
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
#include "wrapper.hpp"
|
||||||
|
|
||||||
|
#include <QtQml/qqmlinfo.h>
|
||||||
|
#include <QtQml/qqmllist.h>
|
||||||
|
#include <qlogging.h>
|
||||||
|
#include <qnamespace.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qquickitem.h>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
|
||||||
|
namespace qs::widgets {
|
||||||
|
|
||||||
|
void WrapperManager::componentComplete() {
|
||||||
|
this->mWrapper = qobject_cast<QQuickItem*>(this->parent());
|
||||||
|
|
||||||
|
if (!this->mWrapper) {
|
||||||
|
QString pstr;
|
||||||
|
QDebug(&pstr) << this->parent();
|
||||||
|
|
||||||
|
qmlWarning(this) << "Parent of WrapperManager is not a QQuickItem. Parent: " << pstr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QQuickItem* child = this->mChild;
|
||||||
|
this->mChild = nullptr; // avoids checks for the old item in setChild.
|
||||||
|
|
||||||
|
const auto& childItems = this->mWrapper->childItems();
|
||||||
|
|
||||||
|
if (childItems.length() == 1) {
|
||||||
|
this->mDefaultChild = childItems.first();
|
||||||
|
} else if (childItems.length() != 0) {
|
||||||
|
this->flags.setFlag(WrapperManager::HasMultipleChildren);
|
||||||
|
|
||||||
|
if (!child && !this->flags.testFlags(WrapperManager::NullChild)) {
|
||||||
|
this->printChildCountWarning();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto* item: childItems) {
|
||||||
|
if (item != child) item->setParentItem(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (child && !this->flags.testFlag(WrapperManager::NullChild)) {
|
||||||
|
this->setChild(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QQuickItem* WrapperManager::child() const { return this->mChild; }
|
||||||
|
|
||||||
|
void WrapperManager::setChild(QQuickItem* child) {
|
||||||
|
if (child && child == this->mChild) return;
|
||||||
|
|
||||||
|
if (this->mChild != nullptr) {
|
||||||
|
QObject::disconnect(this->mChild, nullptr, this, nullptr);
|
||||||
|
|
||||||
|
if (this->mChild->parentItem() == this->mWrapper) {
|
||||||
|
this->mChild->setParentItem(nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->mChild = child;
|
||||||
|
this->flags.setFlag(WrapperManager::NullChild, child == nullptr);
|
||||||
|
|
||||||
|
if (child) {
|
||||||
|
QObject::connect(
|
||||||
|
child,
|
||||||
|
&QObject::destroyed,
|
||||||
|
this,
|
||||||
|
&WrapperManager::onChildDestroyed,
|
||||||
|
Qt::UniqueConnection
|
||||||
|
);
|
||||||
|
|
||||||
|
if (auto* wrapper = this->mWrapper) {
|
||||||
|
child->setParentItem(wrapper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emit this->initializedChildChanged();
|
||||||
|
emit this->childChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WrapperManager::setProspectiveChild(QQuickItem* child) {
|
||||||
|
if (child && child == this->mChild) return;
|
||||||
|
|
||||||
|
if (!this->mWrapper) {
|
||||||
|
if (this->mChild) {
|
||||||
|
QObject::disconnect(this->mChild, nullptr, this, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->mChild = child;
|
||||||
|
this->flags.setFlag(WrapperManager::NullChild, child == nullptr);
|
||||||
|
|
||||||
|
if (child) {
|
||||||
|
QObject::connect(child, &QObject::destroyed, this, &WrapperManager::onChildDestroyed);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this->setChild(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WrapperManager::unsetChild() {
|
||||||
|
if (!this->mWrapper) {
|
||||||
|
this->setProspectiveChild(nullptr);
|
||||||
|
} else {
|
||||||
|
this->setChild(this->mDefaultChild);
|
||||||
|
|
||||||
|
if (!this->mDefaultChild && this->flags.testFlag(WrapperManager::HasMultipleChildren)) {
|
||||||
|
this->printChildCountWarning();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->flags.setFlag(WrapperManager::NullChild, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WrapperManager::onChildDestroyed() {
|
||||||
|
this->mChild = nullptr;
|
||||||
|
this->unsetChild();
|
||||||
|
emit this->childChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WrapperManager::printChildCountWarning() const {
|
||||||
|
qmlWarning(this->mWrapper) << "Wrapper component cannot have more than one visual child.";
|
||||||
|
qmlWarning(this->mWrapper) << "Remove all additional children, or pick a specific component "
|
||||||
|
"to wrap using the child property.";
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace qs::widgets
|
139
src/widgets/wrapper.hpp
Normal file
139
src/widgets/wrapper.hpp
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <qflags.h>
|
||||||
|
#include <qobject.h>
|
||||||
|
#include <qpointer.h>
|
||||||
|
#include <qqmlintegration.h>
|
||||||
|
#include <qqmllist.h>
|
||||||
|
#include <qqmlparserstatus.h>
|
||||||
|
#include <qquickitem.h>
|
||||||
|
#include <qtmetamacros.h>
|
||||||
|
#include <qtypes.h>
|
||||||
|
|
||||||
|
#include "../core/doc.hpp"
|
||||||
|
|
||||||
|
namespace qs::widgets {
|
||||||
|
|
||||||
|
///! Helper object for creating components with a single visual child.
|
||||||
|
/// WrapperManager determines which child of an Item should be its visual
|
||||||
|
/// child, and exposes it for further operations. See @@MarginWrapperManager
|
||||||
|
/// for a subclass that implements automatic sizing and margins.
|
||||||
|
///
|
||||||
|
/// ### Using wrapper types
|
||||||
|
/// WrapperManager based types have a single visual child item.
|
||||||
|
/// You can specify the child item using the default property, or by
|
||||||
|
/// setting the @@child property. You must use the @@child property if
|
||||||
|
/// the widget has more than one @@QtQuick.Item based child.
|
||||||
|
///
|
||||||
|
/// #### Example using the default property
|
||||||
|
/// ```qml
|
||||||
|
/// WrapperWidget { // a widget that uses WrapperManager
|
||||||
|
/// // Putting the item inline uses the default property of WrapperWidget.
|
||||||
|
/// @@QtQuick.Text { text: "Hello" }
|
||||||
|
///
|
||||||
|
/// // Scope does not extend Item, so it can be placed in the
|
||||||
|
/// // default property without issue.
|
||||||
|
/// @@Quickshell.Scope {}
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// #### Example using the child property
|
||||||
|
/// ```qml
|
||||||
|
/// WrapperWidget {
|
||||||
|
/// @@QtQuick.Text {
|
||||||
|
/// id: text
|
||||||
|
/// text: "Hello"
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// @@QtQuick.Text {
|
||||||
|
/// id: otherText
|
||||||
|
/// text: "Other Text"
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// // Both text and otherText extend Item, so one must be specified.
|
||||||
|
/// child: text
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// See @@child for more details on how the child property can be used.
|
||||||
|
///
|
||||||
|
/// ### Implementing wrapper types
|
||||||
|
/// In addition to the bundled wrapper types, you can make your own using
|
||||||
|
/// WrapperManager. To implement a wrapper, create a WrapperManager inside
|
||||||
|
/// your wrapper component 's default property, then alias a new property
|
||||||
|
/// to the WrapperManager's @@child property.
|
||||||
|
///
|
||||||
|
/// #### Example
|
||||||
|
/// ```qml
|
||||||
|
/// Item { // your wrapper component
|
||||||
|
/// WrapperManager { id: wrapperManager }
|
||||||
|
///
|
||||||
|
/// // Allows consumers of your wrapper component to use the child property.
|
||||||
|
/// property alias child: wrapperManager.child
|
||||||
|
///
|
||||||
|
/// // The rest of your component logic. You can use
|
||||||
|
/// // `wrapperManager.child` or `this.child` to refer to the selected child.
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ### See also
|
||||||
|
/// - @@WrapperItem - A @@MarginWrapperManager based component that sizes itself
|
||||||
|
/// to its child.
|
||||||
|
/// - @@WrapperRectangle - A @@MarginWrapperManager based component that sizes
|
||||||
|
/// itself to its child, and provides an option to use its border as an inset.
|
||||||
|
class WrapperManager
|
||||||
|
: public QObject
|
||||||
|
, public QQmlParserStatus {
|
||||||
|
Q_OBJECT;
|
||||||
|
// clang-format off
|
||||||
|
/// The wrapper component's selected child.
|
||||||
|
///
|
||||||
|
/// Setting this property override's WrapperManager's default selection,
|
||||||
|
/// and resolve ambiguity when more than one visual child is present.
|
||||||
|
/// The property can additionally be defined inline or reference a component
|
||||||
|
/// that is not already a child of the wrapper, in which case it will be
|
||||||
|
/// reparented to the wrapper. Setting child to `null` will select no child,
|
||||||
|
/// and `undefined` will restore the default child.
|
||||||
|
///
|
||||||
|
/// When read, `child` will always return the (potentially null) selected child,
|
||||||
|
/// and not `undefined`.
|
||||||
|
Q_PROPERTY(QQuickItem* child READ child WRITE setProspectiveChild RESET unsetChild NOTIFY childChanged FINAL);
|
||||||
|
// clang-format on
|
||||||
|
QML_ELEMENT;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit WrapperManager(QObject* parent = nullptr): QObject(parent) {}
|
||||||
|
|
||||||
|
void classBegin() override {}
|
||||||
|
void componentComplete() override;
|
||||||
|
|
||||||
|
[[nodiscard]] QQuickItem* child() const;
|
||||||
|
void setChild(QQuickItem* child);
|
||||||
|
void setProspectiveChild(QQuickItem* child);
|
||||||
|
void unsetChild();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void childChanged();
|
||||||
|
QSDOC_HIDE void initializedChildChanged();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onChildDestroyed();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
enum Flag : quint8 {
|
||||||
|
NoFlags = 0x0,
|
||||||
|
NullChild = 0x1,
|
||||||
|
HasMultipleChildren = 0x2,
|
||||||
|
};
|
||||||
|
Q_DECLARE_FLAGS(Flags, Flag);
|
||||||
|
|
||||||
|
void printChildCountWarning() const;
|
||||||
|
void updateGeometry();
|
||||||
|
|
||||||
|
QQuickItem* mWrapper = nullptr;
|
||||||
|
QPointer<QQuickItem> mDefaultChild;
|
||||||
|
QQuickItem* mChild = nullptr;
|
||||||
|
Flags flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace qs::widgets
|
Loading…
Add table
Add a link
Reference in a new issue