Kirigami2

headerfooterlayout.cpp
1/*
2 * SPDX-FileCopyrightText: 2023 Marco Martin <mart@kde.org>
3 * SPDX-FileCopyrightText: 2023 ivan tkachenko <me@ratijas.tk>
4 *
5 * SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7
8#include "headerfooterlayout.h"
9
10#include <QDebug>
11#include <QTimer>
12
13HeaderFooterLayout::HeaderFooterLayout(QQuickItem *parent)
14 : QQuickItem(parent)
15 , m_isDirty(false)
16 , m_performingLayout(false)
17{
18}
19
20HeaderFooterLayout::~HeaderFooterLayout()
21{
22 disconnectItem(m_header);
23 disconnectItem(m_contentItem);
24 disconnectItem(m_footer);
25};
26
27void HeaderFooterLayout::setHeader(QQuickItem *item)
28{
29 if (m_header == item) {
30 return;
31 }
32
33 if (m_header) {
34 disconnectItem(m_header);
35 m_header->setParentItem(nullptr);
36 }
37
38 m_header = item;
39
40 if (m_header) {
41 m_header->setParentItem(this);
42 if (m_header->z() == 0) {
43 m_header->setZ(1);
44 }
45
46 connect(m_header, &QQuickItem::implicitWidthChanged, this, &HeaderFooterLayout::markAsDirty);
47 connect(m_header, &QQuickItem::implicitHeightChanged, this, &HeaderFooterLayout::markAsDirty);
48 connect(m_header, &QQuickItem::visibleChanged, this, &HeaderFooterLayout::markAsDirty);
49
50 if (m_header->inherits("QQuickTabBar") || m_header->inherits("QQuickToolBar") || m_header->inherits("QQuickDialogButtonBox")) {
51 // Assume 0 is Header for all 3 types
52 m_header->setProperty("position", 0);
53 }
54 }
55
56 markAsDirty();
57
58 Q_EMIT headerChanged();
59}
60
62{
63 return m_header;
64}
65
66void HeaderFooterLayout::setContentItem(QQuickItem *item)
67{
68 if (m_contentItem == item) {
69 return;
70 }
71
72 if (m_contentItem) {
73 disconnectItem(m_contentItem);
74 m_contentItem->setParentItem(nullptr);
75 }
76
77 m_contentItem = item;
78
79 if (m_contentItem) {
80 m_contentItem->setParentItem(this);
81 connect(m_contentItem, &QQuickItem::implicitWidthChanged, this, &HeaderFooterLayout::markAsDirty);
82 connect(m_contentItem, &QQuickItem::implicitHeightChanged, this, &HeaderFooterLayout::markAsDirty);
83 connect(m_contentItem, &QQuickItem::visibleChanged, this, &HeaderFooterLayout::markAsDirty);
84 }
85
86 markAsDirty();
87
88 Q_EMIT contentItemChanged();
89}
90
92{
93 return m_contentItem;
94}
95
96void HeaderFooterLayout::setFooter(QQuickItem *item)
97{
98 if (m_footer == item) {
99 return;
100 }
101
102 if (m_footer) {
103 disconnectItem(m_footer);
104 m_footer->setParentItem(nullptr);
105 }
106
107 m_footer = item;
108
109 if (m_footer) {
110 m_footer->setParentItem(this);
111 if (m_footer->z() == 0) {
112 m_footer->setZ(1);
113 }
114
115 connect(m_footer, &QQuickItem::implicitWidthChanged, this, &HeaderFooterLayout::markAsDirty);
116 connect(m_footer, &QQuickItem::implicitHeightChanged, this, &HeaderFooterLayout::markAsDirty);
117 connect(m_footer, &QQuickItem::visibleChanged, this, &HeaderFooterLayout::markAsDirty);
118
119 if (m_footer->inherits("QQuickTabBar") || m_footer->inherits("QQuickToolBar") || m_footer->inherits("QQuickDialogButtonBox")) {
120 // Assume 1 is Footer for all 3 types
121 m_footer->setProperty("position", 1);
122 }
123 }
124
125 markAsDirty();
126
127 Q_EMIT footerChanged();
128}
129
131{
132 return m_footer;
133}
134
135void HeaderFooterLayout::setSpacing(qreal spacing)
136{
137 if (spacing == m_spacing) {
138 return;
139 }
140
141 m_spacing = spacing;
142
143 markAsDirty();
144
145 Q_EMIT spacingChanged();
146}
147
148qreal HeaderFooterLayout::spacing() const
149{
150 return m_spacing;
151}
152
154{
155 updatePolish();
156}
157
158void HeaderFooterLayout::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
159{
160 if (newGeometry != oldGeometry) {
161 markAsDirty();
162 }
163
164 QQuickItem::geometryChange(newGeometry, oldGeometry);
165}
166
167void HeaderFooterLayout::componentComplete()
168{
170 if (m_isDirty) {
171 performLayout();
172 }
173}
174
175void HeaderFooterLayout::updatePolish()
176{
177 if (m_isDirty) {
178 performLayout();
179 }
180}
181
182void HeaderFooterLayout::markAsDirty()
183{
184 if (!m_isDirty) {
185 m_isDirty = true;
186 polish();
187 }
188}
189
190void HeaderFooterLayout::performLayout()
191{
192 if (!isComponentComplete() || m_performingLayout) {
193 return;
194 }
195
196 m_isDirty = false;
197 m_performingLayout = true;
198
199 // Implicit size has to be updated first, as it may propagate to the
200 // actual size which will be used below during layouting.
201 updateImplicitSize();
202
203 const QSizeF newSize = size();
204 qreal headerHeight = 0;
205 qreal footerHeight = 0;
206
207 if (m_header) {
208 m_header->setWidth(newSize.width());
209 if (m_header->isVisible()) {
210 headerHeight = m_header->height() + m_spacing;
211 }
212 }
213 if (m_footer) {
214 m_footer->setY(newSize.height() - m_footer->height());
215 m_footer->setWidth(newSize.width());
216 if (m_footer->isVisible()) {
217 footerHeight = m_footer->height() + m_spacing;
218 }
219 }
220 if (m_contentItem) {
221 m_contentItem->setY(headerHeight);
222 m_contentItem->setWidth(newSize.width());
223 m_contentItem->setHeight(newSize.height() - headerHeight - footerHeight);
224 }
225
226 m_performingLayout = false;
227}
228
229void HeaderFooterLayout::updateImplicitSize()
230{
231 qreal impWidth = 0;
232 qreal impHeight = 0;
233
234 if (m_header && m_header->isVisible()) {
235 impWidth = std::max(impWidth, m_header->implicitWidth());
236 impHeight += m_header->implicitHeight() + m_spacing;
237 }
238 if (m_footer && m_footer->isVisible()) {
239 impWidth = std::max(impWidth, m_footer->implicitWidth());
240 impHeight += m_footer->implicitHeight() + m_spacing;
241 }
242 if (m_contentItem && m_contentItem->isVisible()) {
243 impWidth = std::max(impWidth, m_contentItem->implicitWidth());
244 impHeight += m_contentItem->implicitHeight();
245 }
246 setImplicitSize(impWidth, impHeight);
247}
248
249void HeaderFooterLayout::disconnectItem(QQuickItem *item)
250{
251 if (item) {
252 disconnect(item, &QQuickItem::implicitWidthChanged, this, &HeaderFooterLayout::markAsDirty);
253 disconnect(item, &QQuickItem::implicitHeightChanged, this, &HeaderFooterLayout::markAsDirty);
254 disconnect(item, &QQuickItem::visibleChanged, this, &HeaderFooterLayout::markAsDirty);
255 }
256}
257
258#include "moc_headerfooterlayout.cpp"
QQuickItem * footer
This property holds the page footer item.
QML_ELEMENTQQuickItem * header
This property holds the page header item.
Q_INVOKABLE void forceLayout()
HeaderFooterLayout normally positions its header, footer and contentItem once per frame (at polish ev...
qreal spacing
Space between contentItem and the header and footer items.
QQuickItem * contentItem
This property holds the visual content Item.
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
virtual void componentComplete() override
virtual void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
void implicitHeightChanged()
void implicitWidthChanged()
bool isComponentComplete() const const
void polish()
QSizeF size() const const
void visibleChanged()
qreal height() const const
qreal width() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Apr 11 2025 11:49:27 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.