mirror of
				https://github.com/f4exb/sdrangel.git
				synced 2025-10-31 04:50:29 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			255 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			255 lines
		
	
	
		
			8.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| ///////////////////////////////////////////////////////////////////////////////////////
 | |
| // 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/>.              //
 | |
| ///////////////////////////////////////////////////////////////////////////////////////
 | |
| /****************************************************************************
 | |
| **
 | |
| ** 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"
 | |
| 
 | |
| 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;
 | |
|     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();
 | |
|     }
 | |
| 
 | |
|     // Now do layout
 | |
|     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 spaceY = verticalSpacing();
 | |
|         if (spaceY == -1)
 | |
|             spaceY = wid->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);
 | |
| 
 | |
|         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;
 | |
|             if (maxLineHeights.size() > 0) {
 | |
|                 maxLineHeight = maxLineHeights.takeFirst();
 | |
|             }
 | |
|             lineHeight = 0;
 | |
|         }
 | |
| 
 | |
|         if (!testOnly)
 | |
|         {
 | |
|             QSize size = item->sizeHint();
 | |
|             if (wid && (wid->sizePolicy().verticalPolicy() != QSizePolicy::Fixed)) {
 | |
|                 size.setHeight(maxLineHeight);
 | |
|             }
 | |
|             QRect r(QPoint(x, y), size);
 | |
|             item->setGeometry(r);
 | |
|         }
 | |
| 
 | |
|         x = nextX;
 | |
|         lineHeight = qMax(lineHeight, item->sizeHint().height());
 | |
|     }
 | |
|     return y + lineHeight - rect.y() + bottom;
 | |
| }
 | |
| 
 | |
| 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();
 | |
|     }
 | |
| }
 |