| 
									
										
										
										
											2023-11-19 13:31:45 +01:00
										 |  |  | ///////////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							|  |  |  | // Copyright (C) 2022 Jon Beniston, M7RCE <jon@beniston.com>                         //
 | 
					
						
							|  |  |  | // Copyright (C) 2022 Edouard Griffiths, F4EXB <f4exb06@gmail.com>                   //
 | 
					
						
							|  |  |  | //                                                                                   //
 | 
					
						
							|  |  |  | // This program is free software; you can redistribute it and/or modify              //
 | 
					
						
							|  |  |  | // it under the terms of the GNU General Public License as published by              //
 | 
					
						
							|  |  |  | // the Free Software Foundation as version 3 of the License, or                      //
 | 
					
						
							|  |  |  | // (at your option) any later version.                                               //
 | 
					
						
							|  |  |  | //                                                                                   //
 | 
					
						
							|  |  |  | // This program is distributed in the hope that it will be useful,                   //
 | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of                    //
 | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the                      //
 | 
					
						
							|  |  |  | // GNU General Public License V3 for more details.                                   //
 | 
					
						
							|  |  |  | //                                                                                   //
 | 
					
						
							|  |  |  | // You should have received a copy of the GNU General Public License                 //
 | 
					
						
							|  |  |  | // along with this program. If not, see <http://www.gnu.org/licenses/>.              //
 | 
					
						
							|  |  |  | ///////////////////////////////////////////////////////////////////////////////////////
 | 
					
						
							| 
									
										
										
										
											2022-03-19 12:18:55 +01:00
										 |  |  | /****************************************************************************
 | 
					
						
							|  |  |  | ** | 
					
						
							|  |  |  | ** Copyright (C) 2016 The Qt Company Ltd. | 
					
						
							|  |  |  | ** Contact: https://www.qt.io/licensing/
 | 
					
						
							|  |  |  | ** | 
					
						
							|  |  |  | ** This file is part of the examples of the Qt Toolkit. | 
					
						
							|  |  |  | ** | 
					
						
							|  |  |  | ** $QT_BEGIN_LICENSE:BSD$ | 
					
						
							|  |  |  | ** Commercial License Usage | 
					
						
							|  |  |  | ** Licensees holding valid commercial Qt licenses may use this file in | 
					
						
							|  |  |  | ** accordance with the commercial license agreement provided with the | 
					
						
							|  |  |  | ** Software or, alternatively, in accordance with the terms contained in | 
					
						
							|  |  |  | ** a written agreement between you and The Qt Company. For licensing terms | 
					
						
							|  |  |  | ** and conditions see https://www.qt.io/terms-conditions. For further
 | 
					
						
							|  |  |  | ** information use the contact form at https://www.qt.io/contact-us.
 | 
					
						
							|  |  |  | ** | 
					
						
							|  |  |  | ** BSD License Usage | 
					
						
							|  |  |  | ** Alternatively, you may use this file under the terms of the BSD license | 
					
						
							|  |  |  | ** as follows: | 
					
						
							|  |  |  | ** | 
					
						
							|  |  |  | ** "Redistribution and use in source and binary forms, with or without | 
					
						
							|  |  |  | ** modification, are permitted provided that the following conditions are | 
					
						
							|  |  |  | ** met: | 
					
						
							|  |  |  | **   * Redistributions of source code must retain the above copyright | 
					
						
							|  |  |  | **     notice, this list of conditions and the following disclaimer. | 
					
						
							|  |  |  | **   * Redistributions in binary form must reproduce the above copyright | 
					
						
							|  |  |  | **     notice, this list of conditions and the following disclaimer in | 
					
						
							|  |  |  | **     the documentation and/or other materials provided with the | 
					
						
							|  |  |  | **     distribution. | 
					
						
							|  |  |  | **   * Neither the name of The Qt Company Ltd nor the names of its | 
					
						
							|  |  |  | **     contributors may be used to endorse or promote products derived | 
					
						
							|  |  |  | **     from this software without specific prior written permission. | 
					
						
							|  |  |  | ** | 
					
						
							|  |  |  | ** | 
					
						
							|  |  |  | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 
					
						
							|  |  |  | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 
					
						
							|  |  |  | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 
					
						
							|  |  |  | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 
					
						
							|  |  |  | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 
					
						
							|  |  |  | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 
					
						
							|  |  |  | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
					
						
							|  |  |  | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
					
						
							|  |  |  | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
					
						
							|  |  |  | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
					
						
							|  |  |  | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." | 
					
						
							|  |  |  | ** | 
					
						
							|  |  |  | ** $QT_END_LICENSE$ | 
					
						
							|  |  |  | ** | 
					
						
							|  |  |  | ****************************************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <QtWidgets>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "flowlayout.h"
 | 
					
						
							| 
									
										
										
										
											2022-09-16 08:50:21 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-19 12:18:55 +01:00
										 |  |  | FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing) | 
					
						
							|  |  |  |     : QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     setContentsMargins(margin, margin, margin, margin); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing) | 
					
						
							|  |  |  |     : m_hSpace(hSpacing), m_vSpace(vSpacing) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     setContentsMargins(margin, margin, margin, margin); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | FlowLayout::~FlowLayout() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     QLayoutItem *item; | 
					
						
							|  |  |  |     while ((item = takeAt(0))) | 
					
						
							|  |  |  |         delete item; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void FlowLayout::addItem(QLayoutItem *item) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     itemList.append(item); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int FlowLayout::horizontalSpacing() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (m_hSpace >= 0) { | 
					
						
							|  |  |  |         return m_hSpace; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         return smartSpacing(QStyle::PM_LayoutHorizontalSpacing); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int FlowLayout::verticalSpacing() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (m_vSpace >= 0) { | 
					
						
							|  |  |  |         return m_vSpace; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         return smartSpacing(QStyle::PM_LayoutVerticalSpacing); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int FlowLayout::count() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return itemList.size(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | QLayoutItem *FlowLayout::itemAt(int index) const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return itemList.value(index); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | QLayoutItem *FlowLayout::takeAt(int index) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (index >= 0 && index < itemList.size()) | 
					
						
							|  |  |  |         return itemList.takeAt(index); | 
					
						
							|  |  |  |     return nullptr; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Qt::Orientations FlowLayout::expandingDirections() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return { }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool FlowLayout::hasHeightForWidth() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int FlowLayout::heightForWidth(int width) const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int height = doLayout(QRect(0, 0, width, 0), true); | 
					
						
							|  |  |  |     return height; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void FlowLayout::setGeometry(const QRect &rect) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     QLayout::setGeometry(rect); | 
					
						
							|  |  |  |     doLayout(rect, false); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | QSize FlowLayout::sizeHint() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return minimumSize(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | QSize FlowLayout::minimumSize() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     QSize size; | 
					
						
							|  |  |  |     for (const QLayoutItem *item : qAsConst(itemList)) | 
					
						
							|  |  |  |         size = size.expandedTo(item->minimumSize()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const QMargins margins = contentsMargins(); | 
					
						
							|  |  |  |     size += QSize(margins.left() + margins.right(), margins.top() + margins.bottom()); | 
					
						
							|  |  |  |     return size; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int FlowLayout::doLayout(const QRect &rect, bool testOnly) const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     int left, top, right, bottom; | 
					
						
							|  |  |  |     getContentsMargins(&left, &top, &right, &bottom); | 
					
						
							|  |  |  |     QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom); | 
					
						
							|  |  |  |     int x = effectiveRect.x(); | 
					
						
							|  |  |  |     int y = effectiveRect.y(); | 
					
						
							|  |  |  |     int lineHeight = 0; | 
					
						
							| 
									
										
										
										
											2022-09-16 08:50:21 +01:00
										 |  |  |     QList<int> maxLineHeights; | 
					
						
							|  |  |  |     int maxLineHeight = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // In order to support vertical alignment and expanding widgets, we need to first calculate maxLineHeight
 | 
					
						
							|  |  |  |     // for each row of widgets
 | 
					
						
							|  |  |  |     if (!testOnly) { | 
					
						
							|  |  |  |         for (QLayoutItem *item : qAsConst(itemList)) { | 
					
						
							|  |  |  |             const QWidget *wid = item->widget(); | 
					
						
							|  |  |  |             int spaceX = horizontalSpacing(); | 
					
						
							|  |  |  |             if (spaceX == -1) | 
					
						
							|  |  |  |                 spaceX = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             int nextX = x + item->sizeHint().width() + spaceX; | 
					
						
							|  |  |  |             if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) { | 
					
						
							|  |  |  |                 x = effectiveRect.x(); | 
					
						
							|  |  |  |                 nextX = x + item->sizeHint().width() + spaceX; | 
					
						
							|  |  |  |                 maxLineHeights.append(lineHeight); | 
					
						
							|  |  |  |                 lineHeight = 0; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             x = nextX; | 
					
						
							|  |  |  |             lineHeight = qMax(lineHeight, item->sizeHint().height()); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         maxLineHeights.append(lineHeight); | 
					
						
							|  |  |  |         maxLineHeight = maxLineHeights.takeFirst(); | 
					
						
							|  |  |  |         x = effectiveRect.x(); | 
					
						
							|  |  |  |         y = effectiveRect.y(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-03-19 12:18:55 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-16 08:50:21 +01:00
										 |  |  |     // Now do layout
 | 
					
						
							| 
									
										
										
										
											2022-03-19 12:18:55 +01:00
										 |  |  |     for (QLayoutItem *item : qAsConst(itemList)) { | 
					
						
							|  |  |  |         const QWidget *wid = item->widget(); | 
					
						
							|  |  |  |         int spaceX = horizontalSpacing(); | 
					
						
							|  |  |  |         if (spaceX == -1) | 
					
						
							| 
									
										
										
										
											2022-09-16 08:50:21 +01:00
										 |  |  |             spaceX = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal); | 
					
						
							| 
									
										
										
										
											2022-03-19 12:18:55 +01:00
										 |  |  |         int spaceY = verticalSpacing(); | 
					
						
							|  |  |  |         if (spaceY == -1) | 
					
						
							| 
									
										
										
										
											2022-09-16 08:50:21 +01:00
										 |  |  |             spaceY = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-19 12:18:55 +01:00
										 |  |  |         int nextX = x + item->sizeHint().width() + spaceX; | 
					
						
							|  |  |  |         if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) { | 
					
						
							|  |  |  |             x = effectiveRect.x(); | 
					
						
							|  |  |  |             y = y + lineHeight + spaceY; | 
					
						
							|  |  |  |             nextX = x + item->sizeHint().width() + spaceX; | 
					
						
							| 
									
										
										
										
											2022-09-16 08:50:21 +01:00
										 |  |  |             if (maxLineHeights.size() > 0) { | 
					
						
							|  |  |  |                 maxLineHeight = maxLineHeights.takeFirst(); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2022-03-19 12:18:55 +01:00
										 |  |  |             lineHeight = 0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!testOnly) | 
					
						
							| 
									
										
										
										
											2022-09-16 08:50:21 +01:00
										 |  |  |         { | 
					
						
							|  |  |  |             QSize size = item->sizeHint(); | 
					
						
							|  |  |  |             if (wid && (wid->sizePolicy().verticalPolicy() != QSizePolicy::Fixed)) { | 
					
						
							|  |  |  |                 size.setHeight(maxLineHeight); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             QRect r(QPoint(x, y), size); | 
					
						
							|  |  |  |             item->setGeometry(r); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-03-19 12:18:55 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         x = nextX; | 
					
						
							|  |  |  |         lineHeight = qMax(lineHeight, item->sizeHint().height()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return y + lineHeight - rect.y() + bottom; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-09-16 08:50:21 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-19 12:18:55 +01:00
										 |  |  | int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     QObject *parent = this->parent(); | 
					
						
							|  |  |  |     if (!parent) { | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } else if (parent->isWidgetType()) { | 
					
						
							|  |  |  |         QWidget *pw = static_cast<QWidget *>(parent); | 
					
						
							|  |  |  |         return pw->style()->pixelMetric(pm, nullptr, pw); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         return static_cast<QLayout *>(parent)->spacing(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |