File indexing completed on 2024-04-21 04:39:56

0001 /*
0002     SPDX-FileCopyrightText: 2006-2007 Sebastian Trueg <trueg@kde.org>
0003 
0004     KBlockLayout is based on the FlowLayout example from QT4.
0005     SPDX-FileCopyrightText: 2004-2006 Trolltech ASA
0006     SPDX-FileCopyrightText: 2010 Nokia Corporation and /or its subsidiary(-ies) <qt-info@nokia.com>
0007 
0008     SPDX-License-Identifier: LGPL-2.0-or-later
0009 */
0010 
0011 #include "kblocklayout.h"
0012 
0013 #include <QList>
0014 #include <QStyle>
0015 #include <QWidget>
0016 
0017 class KBlockLayout::Private
0018 {
0019 public:
0020     Private()
0021         : alignment(Qt::AlignLeft | Qt::AlignTop)
0022     {
0023     }
0024 
0025     int smartSpacing(QStyle::PixelMetric pm) const
0026     {
0027         QObject *parent = q->parent();
0028         if (!parent) {
0029             return -1;
0030         } else if (parent->isWidgetType()) {
0031             auto pw = static_cast<QWidget *>(parent);
0032             return pw->style()->pixelMetric(pm, nullptr, pw);
0033         } else {
0034             return static_cast<QLayout *>(parent)->spacing();
0035         }
0036     }
0037 
0038     QList<QLayoutItem *> itemList;
0039 
0040     int m_hSpace;
0041     int m_vSpace;
0042 
0043     Qt::Alignment alignment;
0044 
0045     KBlockLayout *q = nullptr;
0046 };
0047 
0048 KBlockLayout::KBlockLayout(QWidget *parent, int margin, int hSpacing, int vSpacing)
0049     : QLayout(parent)
0050     , d(new Private())
0051 {
0052     d->q = this;
0053     setContentsMargins(margin, margin, margin, margin);
0054     setSpacing(hSpacing, vSpacing);
0055 }
0056 
0057 KBlockLayout::KBlockLayout(int margin, int hSpacing, int vSpacing)
0058     : d(new Private())
0059 {
0060     d->q = this;
0061     setContentsMargins(margin, margin, margin, margin);
0062     setSpacing(hSpacing, vSpacing);
0063 }
0064 
0065 KBlockLayout::~KBlockLayout()
0066 {
0067     QLayoutItem *item;
0068     while ((item = takeAt(0)))
0069         delete item;
0070     delete d;
0071 }
0072 
0073 void KBlockLayout::setAlignment(Qt::Alignment a)
0074 {
0075     d->alignment = a;
0076     QLayout::update();
0077 }
0078 
0079 Qt::Alignment KBlockLayout::alignment() const
0080 {
0081     return d->alignment;
0082 }
0083 
0084 int KBlockLayout::horizontalSpacing() const
0085 {
0086     if (d->m_hSpace >= 0) {
0087         return d->m_hSpace;
0088     } else {
0089         return d->smartSpacing(QStyle::PM_LayoutHorizontalSpacing);
0090     }
0091 }
0092 
0093 int KBlockLayout::verticalSpacing() const
0094 {
0095     if (d->m_vSpace >= 0) {
0096         return d->m_vSpace;
0097     } else {
0098         return d->smartSpacing(QStyle::PM_LayoutVerticalSpacing);
0099     }
0100 }
0101 
0102 void KBlockLayout::setSpacing(int h, int v)
0103 {
0104     d->m_hSpace = h;
0105     d->m_vSpace = v;
0106     QLayout::setSpacing(h);
0107 }
0108 
0109 void KBlockLayout::addItem(QLayoutItem *item)
0110 {
0111     d->itemList.append(item);
0112 }
0113 
0114 int KBlockLayout::count() const
0115 {
0116     return d->itemList.size();
0117 }
0118 
0119 QLayoutItem *KBlockLayout::itemAt(int index) const
0120 {
0121     return d->itemList.value(index);
0122 }
0123 
0124 QLayoutItem *KBlockLayout::takeAt(int index)
0125 {
0126     if (index >= 0 && index < d->itemList.size())
0127         return d->itemList.takeAt(index);
0128     else
0129         return nullptr;
0130 }
0131 
0132 Qt::Orientations KBlockLayout::expandingDirections() const
0133 {
0134     return {};
0135 }
0136 
0137 bool KBlockLayout::hasHeightForWidth() const
0138 {
0139     return true;
0140 }
0141 
0142 int KBlockLayout::heightForWidth(int width) const
0143 {
0144     int height = doLayout(QRect(0, 0, width, 0), true);
0145     return height;
0146 }
0147 
0148 void KBlockLayout::setGeometry(const QRect &rect)
0149 {
0150     QLayout::setGeometry(rect);
0151     doLayout(rect, false);
0152 }
0153 
0154 int KBlockLayout::getMargin() const
0155 {
0156     int left, top, right, bottom;
0157     getContentsMargins(&left, &top, &right, &bottom);
0158     if (left == top && top == right && right == bottom) {
0159         return left;
0160     } else {
0161         return -1;
0162     }
0163 }
0164 
0165 QSize KBlockLayout::sizeHint() const
0166 {
0167     // TODO: try to get the items into a square
0168     QSize size;
0169     for (QLayoutItem *item : std::as_const(d->itemList)) {
0170         const QSize itemSize = item->minimumSize();
0171         size.rwidth() += itemSize.width();
0172         if (itemSize.height() > size.height()) {
0173             size.setHeight(itemSize.height());
0174         }
0175     }
0176 
0177     size.rwidth() += horizontalSpacing() * d->itemList.count();
0178     size += QSize(2 * getMargin(), 2 * getMargin());
0179     return size;
0180 }
0181 
0182 QSize KBlockLayout::minimumSize() const
0183 {
0184     QSize size;
0185     for (QLayoutItem *item : std::as_const(d->itemList)) {
0186         size = size.expandedTo(item->minimumSize());
0187     }
0188 
0189     size += QSize(2 * getMargin(), 2 * getMargin());
0190     return size;
0191 }
0192 
0193 struct Row {
0194     Row(const QList<QLayoutItem *> &i, int h, int w)
0195         : items(i)
0196         , height(h)
0197         , width(w)
0198     {
0199     }
0200 
0201     QList<QLayoutItem *> items;
0202     int height;
0203     int width;
0204 };
0205 
0206 int KBlockLayout::doLayout(const QRect &rect, bool testOnly) const
0207 {
0208     int x = rect.x();
0209     int y = rect.y();
0210     int lineHeight = 0;
0211 
0212     // 1. calculate lines
0213     QList<Row> rows;
0214     QList<QLayoutItem *> rowItems;
0215     for (int i = 0; i < d->itemList.count(); ++i) {
0216         QLayoutItem *item = d->itemList[i];
0217         int nextX = x + item->sizeHint().width() + horizontalSpacing();
0218         if (nextX - horizontalSpacing() > rect.right() && lineHeight > 0) {
0219             rows.append(Row(rowItems, lineHeight, x - horizontalSpacing()));
0220             rowItems.clear();
0221 
0222             x = rect.x();
0223             y = y + lineHeight + verticalSpacing();
0224             nextX = x + item->sizeHint().width() + horizontalSpacing();
0225             lineHeight = 0;
0226         }
0227 
0228         rowItems.append(item);
0229 
0230         x = nextX;
0231         lineHeight = qMax(lineHeight, item->sizeHint().height());
0232     }
0233     // append the last row
0234     rows.append(Row(rowItems, lineHeight, x - horizontalSpacing()));
0235 
0236     int finalHeight = y + lineHeight - rect.y();
0237     if (testOnly)
0238         return finalHeight;
0239 
0240     // 2. place the items
0241     y = rect.y();
0242     for (const Row &row : std::as_const(rows)) {
0243         x = rect.x();
0244         if (alignment() & Qt::AlignRight)
0245             x += (rect.width() - row.width);
0246         else if (alignment() & Qt::AlignHCenter)
0247             x += (rect.width() - row.width) / 2;
0248 
0249         for (QLayoutItem *item : std::as_const(row.items)) {
0250             int yy = y;
0251             if (alignment() & Qt::AlignBottom)
0252                 yy += (row.height - item->sizeHint().height());
0253             else if (alignment() & Qt::AlignVCenter)
0254                 yy += (row.height - item->sizeHint().height()) / 2;
0255             item->setGeometry(QRect(QPoint(x, yy), item->sizeHint()));
0256 
0257             x += item->sizeHint().width() + horizontalSpacing();
0258 
0259             if (alignment() & Qt::AlignJustify)
0260                 x += (rect.width() - row.width) / qMax(row.items.count() - 1, 1);
0261         }
0262 
0263         y = y + row.height + verticalSpacing();
0264     }
0265 
0266     return finalHeight;
0267 }