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 }