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