File indexing completed on 2025-03-09 03:57:04

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2009-10-22
0007  * Description : a dynamic layout manager
0008  *
0009  * SPDX-FileCopyrightText: 2009-2012 by Andi Clemens <andi dot clemens at gmail dot com>
0010  *
0011  * SPDX-License-Identifier: GPL-2.0-or-later
0012  *
0013  * ============================================================ */
0014 
0015 #include "dynamiclayout.h"
0016 
0017 // Qt includes
0018 
0019 #include <QStyle>
0020 #include <QWidget>
0021 
0022 namespace Digikam
0023 {
0024 
0025 class Q_DECL_HIDDEN DynamicLayout::Private
0026 {
0027 public:
0028 
0029     explicit Private(int hSpacing, int vSpacing)
0030       : hSpace      (hSpacing),
0031         vSpace      (vSpacing),
0032         spaceX      (0),
0033         spaceY      (0),
0034         minItemWidth(0),
0035         minColumns  (2)
0036     {
0037     }
0038 
0039     int                 hSpace;
0040     int                 vSpace;
0041     int                 spaceX;
0042     int                 spaceY;
0043     int                 minItemWidth;
0044     const int           minColumns;
0045 
0046     QList<QLayoutItem*> itemList;
0047 };
0048 
0049 // --------------------------------------------------------
0050 
0051 DynamicLayout::DynamicLayout(QWidget* const parent, int margin, int hSpacing, int vSpacing)
0052     : QLayout(parent),
0053       d      (new Private(hSpacing, vSpacing))
0054 {
0055     setContentsMargins(margin, margin, margin, margin);
0056 }
0057 
0058 DynamicLayout::DynamicLayout(int margin, int hSpacing, int vSpacing)
0059     : d(new Private(hSpacing, vSpacing))
0060 {
0061     setContentsMargins(margin, margin, margin, margin);
0062 }
0063 
0064 DynamicLayout::~DynamicLayout()
0065 {
0066     QLayoutItem* item = nullptr;
0067 
0068     while ((item = this->takeAt(0)))
0069     {
0070         delete item;
0071     }
0072 
0073     delete d;
0074 }
0075 
0076 void DynamicLayout::addItem(QLayoutItem* layItem)
0077 {
0078     d->minItemWidth = 0;
0079     d->itemList.append(layItem);
0080 
0081     Q_FOREACH (QLayoutItem* const item, d->itemList)
0082     {
0083         QWidget* const wid = item->widget();
0084         d->spaceX          = qMax<int>(wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton,
0085                                                                    Qt::Horizontal), d->spaceX);
0086         d->spaceY          = qMax<int>(wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton,
0087                                                                    Qt::Vertical), d->spaceY);
0088         d->minItemWidth    = qMax<int>(wid->sizeHint().width(), d->minItemWidth);
0089     }
0090 
0091     Q_FOREACH (QLayoutItem* const item, d->itemList)
0092     {
0093         QWidget* const wid = item->widget();
0094         wid->setMinimumWidth(d->minItemWidth);
0095     }
0096 }
0097 
0098 int DynamicLayout::horizontalSpacing() const
0099 {
0100     return d->hSpace;
0101 }
0102 
0103 int DynamicLayout::verticalSpacing() const
0104 {
0105     return d->vSpace;
0106 }
0107 
0108 int DynamicLayout::count() const
0109 {
0110     return d->itemList.size();
0111 }
0112 
0113 QLayoutItem* DynamicLayout::itemAt(int index) const
0114 {
0115     return d->itemList.value(index);
0116 }
0117 
0118 QLayoutItem* DynamicLayout::takeAt(int index)
0119 {
0120     QLayoutItem* item = nullptr;
0121 
0122     if ((index >= 0) && (index < d->itemList.size()))
0123     {
0124         item = d->itemList.takeAt(index);
0125     }
0126 
0127     return item;
0128 }
0129 
0130 Qt::Orientations DynamicLayout::expandingDirections() const
0131 {
0132     return Qt::Orientations();
0133 }
0134 
0135 bool DynamicLayout::hasHeightForWidth() const
0136 {
0137     return true;
0138 }
0139 
0140 int DynamicLayout::heightForWidth(int width) const
0141 {
0142     int height = reLayout(QRect(0, 0, width, 0), true);
0143 
0144     return height;
0145 }
0146 
0147 void DynamicLayout::setGeometry(const QRect& rect)
0148 {
0149     QLayout::setGeometry(rect);
0150     reLayout(rect, false);
0151 }
0152 
0153 QSize DynamicLayout::sizeHint() const
0154 {
0155     return minimumSize();
0156 }
0157 
0158 QSize DynamicLayout::minimumSize() const
0159 {
0160     QSize size;
0161 
0162     Q_FOREACH (QLayoutItem* const item, d->itemList)
0163     {
0164         size = size.expandedTo(item->minimumSize());
0165     }
0166 
0167     size += QSize(2 * contentsMargins().left(), 2 * contentsMargins().top());
0168     int w = (size.width() * d->minColumns) + (d->minColumns * d->spaceX);
0169     size.setWidth(w);
0170 
0171     return size;
0172 }
0173 
0174 int DynamicLayout::reLayout(const QRect& rect, bool testOnly) const
0175 {
0176     int left            = 0;
0177     int top             = 0;
0178     int right           = 0;
0179     int bottom          = 0;
0180     getContentsMargins(&left, &top, &right, &bottom);
0181 
0182     QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
0183     int x               = effectiveRect.x();
0184     int y               = effectiveRect.y();
0185     int lineHeight      = 0;
0186 
0187     // --------------------------------------------------------
0188 
0189     int buttonWidth     = d->minItemWidth + d->spaceX;
0190 
0191     if (buttonWidth == 0)
0192     {
0193         buttonWidth = 1;
0194     }
0195 
0196     int maxButtonsInRow = (effectiveRect.width() - d->spaceX) / buttonWidth;
0197 
0198     if (maxButtonsInRow < d->minColumns)
0199     {
0200         maxButtonsInRow = d->minColumns;
0201     }
0202 
0203     int maxButtonWidth  = d->minItemWidth + ((effectiveRect.width() - (maxButtonsInRow * buttonWidth)) / maxButtonsInRow);
0204 
0205     int currentBtnWidth = (maxButtonsInRow >= d->itemList.count()) ? buttonWidth : maxButtonWidth;
0206 
0207     // --------------------------------------------------------
0208 
0209     Q_FOREACH (QLayoutItem* const item, d->itemList)
0210     {
0211         int nextX = x + currentBtnWidth + d->spaceX;
0212 
0213         // cppcheck-suppress knownConditionTrueFalse
0214         if (((nextX - d->spaceX) > effectiveRect.right()) && (lineHeight > 0))
0215         {
0216             x          = effectiveRect.x();
0217             y          = y + lineHeight + d->spaceY;
0218             nextX      = x + currentBtnWidth  + d->spaceX;
0219             lineHeight = 0;
0220         }
0221 
0222         if (!testOnly)
0223         {
0224             QSize s = item->sizeHint();
0225             s.setWidth(currentBtnWidth);
0226             item->setGeometry(QRect(QPoint(x, y), s));
0227         }
0228 
0229         x          = nextX;
0230         lineHeight = qMax(lineHeight, item->sizeHint().height());
0231     }
0232 
0233     return (y + lineHeight - rect.y() + bottom);
0234 }
0235 
0236 } // namespace Digikam
0237 
0238 #include "moc_dynamiclayout.cpp"