File indexing completed on 2024-12-01 04:36:37

0001 /*
0002    SPDX-FileCopyrightText: 2020-2024 Laurent Montel <montel@kde.org>
0003 
0004    SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "flowlayout.h"
0008 
0009 #include <QStyle>
0010 #include <QWidget>
0011 
0012 static int smartSpacing(QObject *parent, QStyle::PixelMetric pm)
0013 {
0014     if (!parent) {
0015         return -1;
0016     } else if (parent->isWidgetType()) {
0017         auto pw = static_cast<QWidget *>(parent);
0018         return pw->style()->pixelMetric(pm, nullptr, pw);
0019     } else {
0020         return static_cast<QLayout *>(parent)->spacing();
0021     }
0022 }
0023 
0024 FlowLayout::FlowLayout(QWidget *parent)
0025     : QLayout(parent)
0026 {
0027 }
0028 
0029 FlowLayout::~FlowLayout()
0030 {
0031     clear();
0032 }
0033 
0034 int FlowLayout::horizontalSpacing() const
0035 {
0036     if (mHorizontalSpacing >= 0) {
0037         return mHorizontalSpacing;
0038     } else {
0039         return smartSpacing(parent(), QStyle::PM_LayoutHorizontalSpacing);
0040     }
0041 }
0042 
0043 void FlowLayout::setHorizontalSpacing(int horizontalSpacing)
0044 {
0045     if (mHorizontalSpacing != horizontalSpacing) {
0046         mHorizontalSpacing = horizontalSpacing;
0047         invalidate();
0048     }
0049 }
0050 
0051 int FlowLayout::verticalSpacing() const
0052 {
0053     if (mVerticalSpacing >= 0) {
0054         return mVerticalSpacing;
0055     } else {
0056         return smartSpacing(parent(), QStyle::PM_LayoutVerticalSpacing);
0057     }
0058 }
0059 
0060 void FlowLayout::setVerticalSpacing(int verticalSpacing)
0061 {
0062     if (mVerticalSpacing != verticalSpacing) {
0063         mVerticalSpacing = verticalSpacing;
0064         invalidate();
0065     }
0066 }
0067 
0068 QSize FlowLayout::sizeHint() const
0069 {
0070     return minimumSize();
0071 }
0072 
0073 QSize FlowLayout::minimumSize() const
0074 {
0075     const QMargins margins = contentsMargins();
0076     QSize size;
0077 
0078     for (const QLayoutItem *item : mItems) {
0079         size = size.expandedTo(item->minimumSize());
0080     }
0081 
0082     size += QSize(margins.left() + margins.right(), margins.top() + margins.bottom());
0083     return size;
0084 }
0085 
0086 void FlowLayout::addItem(QLayoutItem *item)
0087 {
0088     Q_ASSERT(!mItems.contains(item));
0089     mItems.append(item);
0090     invalidate();
0091 }
0092 
0093 QLayoutItem *FlowLayout::itemAt(int index) const
0094 {
0095     if (index >= 0 && index < mItems.count()) {
0096         return mItems[index];
0097     }
0098 
0099     return nullptr;
0100 }
0101 
0102 QLayoutItem *FlowLayout::takeAt(int index)
0103 {
0104     if (index >= 0 && index < mItems.count()) {
0105         auto *it = mItems.takeAt(index);
0106         invalidate();
0107         return it;
0108     }
0109 
0110     return nullptr;
0111 }
0112 
0113 int FlowLayout::count() const
0114 {
0115     return mItems.count();
0116 }
0117 
0118 Qt::Orientations FlowLayout::expandingDirections() const
0119 {
0120     return {};
0121 }
0122 
0123 bool FlowLayout::hasHeightForWidth() const
0124 {
0125     return true;
0126 }
0127 
0128 int FlowLayout::heightForWidth(int width) const
0129 {
0130     return doFlow(QRect(0, 0, width, 0), false);
0131 }
0132 
0133 void FlowLayout::setGeometry(const QRect &rect)
0134 {
0135     QLayout::setGeometry(rect);
0136     doFlow(rect, true);
0137 }
0138 
0139 void FlowLayout::clear()
0140 {
0141     while (QLayoutItem *item = takeAt(0)) {
0142         delete item;
0143     }
0144 }
0145 
0146 void FlowLayout::clearAndDeleteWidgets()
0147 {
0148     while (QLayoutItem *item = takeAt(0)) {
0149         item->widget()->deleteLater();
0150         delete item;
0151     }
0152 }
0153 
0154 int FlowLayout::doFlow(QRect rect, bool effective) const
0155 {
0156     const QMargins margins = contentsMargins();
0157     const QRect effectiveRect = rect.adjusted(margins.left(), margins.top(), -margins.right(), -margins.bottom());
0158     int x = effectiveRect.x();
0159     int y = effectiveRect.y();
0160     int highest = 0;
0161 
0162     for (QLayoutItem *item : mItems) {
0163         const QWidget *widget = item->widget();
0164 
0165         if (!widget->isVisibleTo(parentWidget())) {
0166             continue;
0167         }
0168 
0169         int hSpacing = horizontalSpacing();
0170         int vSpacing = verticalSpacing();
0171 
0172         if (hSpacing == -1) {
0173             hSpacing = widget->style()->layoutSpacing(QSizePolicy::Frame, QSizePolicy::Frame, Qt::Horizontal);
0174         }
0175 
0176         if (vSpacing == -1) {
0177             vSpacing = widget->style()->layoutSpacing(QSizePolicy::Frame, QSizePolicy::Frame, Qt::Vertical);
0178         }
0179 
0180         int widgetXPos = x + item->sizeHint().width() + hSpacing;
0181 
0182         if (widgetXPos - hSpacing > effectiveRect.right() && highest > 0) {
0183             x = effectiveRect.x();
0184             y += highest + vSpacing;
0185             widgetXPos = x + item->sizeHint().width() + hSpacing;
0186             highest = 0;
0187         }
0188 
0189         if (effective) {
0190             item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));
0191         }
0192 
0193         x = widgetXPos;
0194         highest = qMax(highest, item->sizeHint().height());
0195     }
0196 
0197     return y + highest - rect.y() + margins.bottom();
0198 }
0199 
0200 #include "moc_flowlayout.cpp"