File indexing completed on 2024-04-21 03:55:59

0001 /*
0002  *  SPDX-FileCopyrightText: 2023 Marco Martin <mart@kde.org>
0003  *  SPDX-FileCopyrightText: 2023 ivan tkachenko <me@ratijas.tk>
0004  *
0005  *  SPDX-License-Identifier: LGPL-2.0-or-later
0006  */
0007 
0008 #include "headerfooterlayout.h"
0009 
0010 #include <QDebug>
0011 #include <QTimer>
0012 
0013 HeaderFooterLayout::HeaderFooterLayout(QQuickItem *parent)
0014     : QQuickItem(parent)
0015     , m_isDirty(false)
0016     , m_performingLayout(false)
0017 {
0018 }
0019 
0020 HeaderFooterLayout::~HeaderFooterLayout()
0021 {
0022     disconnectItem(m_header);
0023     disconnectItem(m_contentItem);
0024     disconnectItem(m_footer);
0025 };
0026 
0027 void HeaderFooterLayout::setHeader(QQuickItem *item)
0028 {
0029     if (m_header == item) {
0030         return;
0031     }
0032 
0033     if (m_header) {
0034         disconnectItem(m_header);
0035         m_header->setParentItem(nullptr);
0036     }
0037 
0038     m_header = item;
0039 
0040     if (m_header) {
0041         m_header->setParentItem(this);
0042         if (m_header->z() == 0) {
0043             m_header->setZ(1);
0044         }
0045 
0046         connect(m_header, &QQuickItem::implicitWidthChanged, this, &HeaderFooterLayout::markAsDirty);
0047         connect(m_header, &QQuickItem::implicitHeightChanged, this, &HeaderFooterLayout::markAsDirty);
0048         connect(m_header, &QQuickItem::visibleChanged, this, &HeaderFooterLayout::markAsDirty);
0049 
0050         if (m_header->inherits("QQuickTabBar") || m_header->inherits("QQuickToolBar") || m_header->inherits("QQuickDialogButtonBox")) {
0051             // Assume 0 is Header for all 3 types
0052             m_header->setProperty("position", 0);
0053         }
0054     }
0055 
0056     markAsDirty();
0057 
0058     Q_EMIT headerChanged();
0059 }
0060 
0061 QQuickItem *HeaderFooterLayout::header()
0062 {
0063     return m_header;
0064 }
0065 
0066 void HeaderFooterLayout::setContentItem(QQuickItem *item)
0067 {
0068     if (m_contentItem == item) {
0069         return;
0070     }
0071 
0072     if (m_contentItem) {
0073         disconnectItem(m_contentItem);
0074         m_contentItem->setParentItem(nullptr);
0075     }
0076 
0077     m_contentItem = item;
0078 
0079     if (m_contentItem) {
0080         m_contentItem->setParentItem(this);
0081         connect(m_contentItem, &QQuickItem::implicitWidthChanged, this, &HeaderFooterLayout::markAsDirty);
0082         connect(m_contentItem, &QQuickItem::implicitHeightChanged, this, &HeaderFooterLayout::markAsDirty);
0083         connect(m_contentItem, &QQuickItem::visibleChanged, this, &HeaderFooterLayout::markAsDirty);
0084     }
0085 
0086     markAsDirty();
0087 
0088     Q_EMIT contentItemChanged();
0089 }
0090 
0091 QQuickItem *HeaderFooterLayout::contentItem()
0092 {
0093     return m_contentItem;
0094 }
0095 
0096 void HeaderFooterLayout::setFooter(QQuickItem *item)
0097 {
0098     if (m_footer == item) {
0099         return;
0100     }
0101 
0102     if (m_footer) {
0103         disconnectItem(m_footer);
0104         m_footer->setParentItem(nullptr);
0105     }
0106 
0107     m_footer = item;
0108 
0109     if (m_footer) {
0110         m_footer->setParentItem(this);
0111         if (m_footer->z() == 0) {
0112             m_footer->setZ(1);
0113         }
0114 
0115         connect(m_footer, &QQuickItem::implicitWidthChanged, this, &HeaderFooterLayout::markAsDirty);
0116         connect(m_footer, &QQuickItem::implicitHeightChanged, this, &HeaderFooterLayout::markAsDirty);
0117         connect(m_footer, &QQuickItem::visibleChanged, this, &HeaderFooterLayout::markAsDirty);
0118 
0119         if (m_footer->inherits("QQuickTabBar") || m_footer->inherits("QQuickToolBar") || m_footer->inherits("QQuickDialogButtonBox")) {
0120             // Assume 1 is Footer for all 3 types
0121             m_footer->setProperty("position", 1);
0122         }
0123     }
0124 
0125     markAsDirty();
0126 
0127     Q_EMIT footerChanged();
0128 }
0129 
0130 QQuickItem *HeaderFooterLayout::footer()
0131 {
0132     return m_footer;
0133 }
0134 
0135 void HeaderFooterLayout::forceLayout()
0136 {
0137     updatePolish();
0138 }
0139 
0140 void HeaderFooterLayout::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
0141 {
0142     if (newGeometry != oldGeometry) {
0143         markAsDirty();
0144     }
0145 
0146     QQuickItem::geometryChange(newGeometry, oldGeometry);
0147 }
0148 
0149 void HeaderFooterLayout::componentComplete()
0150 {
0151     QQuickItem::componentComplete();
0152     if (m_isDirty) {
0153         performLayout();
0154     }
0155 }
0156 
0157 void HeaderFooterLayout::updatePolish()
0158 {
0159     if (m_isDirty) {
0160         performLayout();
0161     }
0162 }
0163 
0164 void HeaderFooterLayout::markAsDirty()
0165 {
0166     if (!m_isDirty) {
0167         m_isDirty = true;
0168         polish();
0169     }
0170 }
0171 
0172 void HeaderFooterLayout::performLayout()
0173 {
0174     if (!isComponentComplete() || m_performingLayout) {
0175         return;
0176     }
0177 
0178     m_isDirty = false;
0179     m_performingLayout = true;
0180 
0181     // Implicit size has to be updated first, as it may propagate to the
0182     // actual size which will be used below during layouting.
0183     updateImplicitSize();
0184 
0185     const QSizeF newSize = size();
0186     qreal headerHeight = 0;
0187     qreal footerHeight = 0;
0188 
0189     if (m_header) {
0190         m_header->setWidth(newSize.width());
0191         if (m_header->isVisible()) {
0192             headerHeight = m_header->height();
0193         }
0194     }
0195     if (m_footer) {
0196         m_footer->setY(newSize.height() - m_footer->height());
0197         m_footer->setWidth(newSize.width());
0198         if (m_footer->isVisible()) {
0199             footerHeight = m_footer->height();
0200         }
0201     }
0202     if (m_contentItem) {
0203         m_contentItem->setY(headerHeight);
0204         m_contentItem->setWidth(newSize.width());
0205         m_contentItem->setHeight(newSize.height() - headerHeight - footerHeight);
0206     }
0207 
0208     m_performingLayout = false;
0209 }
0210 
0211 void HeaderFooterLayout::updateImplicitSize()
0212 {
0213     qreal impWidth = 0;
0214     qreal impHeight = 0;
0215 
0216     if (m_header && m_header->isVisible()) {
0217         impWidth = std::max(impWidth, m_header->implicitWidth());
0218         impHeight += m_header->implicitHeight();
0219     }
0220     if (m_footer && m_footer->isVisible()) {
0221         impWidth = std::max(impWidth, m_footer->implicitWidth());
0222         impHeight += m_footer->implicitHeight();
0223     }
0224     if (m_contentItem && m_contentItem->isVisible()) {
0225         impWidth = std::max(impWidth, m_contentItem->implicitWidth());
0226         impHeight += m_contentItem->implicitHeight();
0227     }
0228     setImplicitSize(impWidth, impHeight);
0229 }
0230 
0231 void HeaderFooterLayout::disconnectItem(QQuickItem *item)
0232 {
0233     if (item) {
0234         disconnect(item, &QQuickItem::implicitWidthChanged, this, &HeaderFooterLayout::markAsDirty);
0235         disconnect(item, &QQuickItem::implicitHeightChanged, this, &HeaderFooterLayout::markAsDirty);
0236         disconnect(item, &QQuickItem::visibleChanged, this, &HeaderFooterLayout::markAsDirty);
0237     }
0238 }
0239 
0240 #include "moc_headerfooterlayout.cpp"