diff --git a/scwx-qt/scwx-qt.cmake b/scwx-qt/scwx-qt.cmake index 9fad529c..1e3735de 100644 --- a/scwx-qt/scwx-qt.cmake +++ b/scwx-qt/scwx-qt.cmake @@ -76,6 +76,8 @@ set(SRC_MAP source/scwx/qt/map/map_widget.cpp source/scwx/qt/map/triangle_layer.cpp) set(HDR_SETTINGS source/scwx/qt/settings/general_settings.hpp) set(SRC_SETTINGS source/scwx/qt/settings/general_settings.cpp) +set(HDR_UI source/scwx/qt/ui/flow_layout.hpp) +set(SRC_UI source/scwx/qt/ui/flow_layout.cpp) set(HDR_UTIL source/scwx/qt/util/font.hpp source/scwx/qt/util/font_buffer.hpp source/scwx/qt/util/json.hpp) @@ -111,6 +113,8 @@ set(PROJECT_SOURCES ${HDR_MAIN} ${SRC_MAP} ${HDR_SETTINGS} ${SRC_SETTINGS} + ${HDR_UI} + ${SRC_UI} ${HDR_UTIL} ${SRC_UTIL} ${HDR_VIEW} @@ -131,6 +135,8 @@ source_group("Header Files\\map" FILES ${HDR_MAP}) source_group("Source Files\\map" FILES ${SRC_MAP}) source_group("Header Files\\settings" FILES ${HDR_SETTINGS}) source_group("Source Files\\settings" FILES ${SRC_SETTINGS}) +source_group("Header Files\\ui" FILES ${HDR_UI}) +source_group("Source Files\\ui" FILES ${SRC_UI}) source_group("Header Files\\util" FILES ${HDR_UTIL}) source_group("Source Files\\util" FILES ${SRC_UTIL}) source_group("Header Files\\view" FILES ${HDR_VIEW}) diff --git a/scwx-qt/source/scwx/qt/ui/flow_layout.cpp b/scwx-qt/source/scwx/qt/ui/flow_layout.cpp new file mode 100644 index 00000000..ba4e32a2 --- /dev/null +++ b/scwx-qt/source/scwx/qt/ui/flow_layout.cpp @@ -0,0 +1,240 @@ +/**************************************************************************** +** +** 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 + +#include + +namespace scwx +{ +namespace qt +{ +namespace ui +{ + +FlowLayout::FlowLayout(QWidget* parent, + int margin, + int hSpacing, + int vSpacing) : + QLayout(parent), hSpace_(hSpacing), vSpace_(vSpacing) +{ + setContentsMargins(margin, margin, margin, margin); +} + +FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing) : + hSpace_(hSpacing), 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 (hSpace_ >= 0) + { + return hSpace_; + } + else + { + return smartSpacing(QStyle::PM_LayoutHorizontalSpacing); + } +} + +int FlowLayout::verticalSpacing() const +{ + if (vSpace_ >= 0) + { + return 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; + + 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; + lineHeight = 0; + } + + if (!testOnly) + { + item->setGeometry(QRect(QPoint(x, y), item->sizeHint())); + } + + 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(parent); + return pw->style()->pixelMetric(pm, nullptr, pw); + } + else + { + return static_cast(parent)->spacing(); + } +} + +} // namespace ui +} // namespace qt +} // namespace scwx diff --git a/scwx-qt/source/scwx/qt/ui/flow_layout.hpp b/scwx-qt/source/scwx/qt/ui/flow_layout.hpp new file mode 100644 index 00000000..78b236ae --- /dev/null +++ b/scwx-qt/source/scwx/qt/ui/flow_layout.hpp @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#pragma once + +#include +#include + +namespace scwx +{ +namespace qt +{ +namespace ui +{ + +/* Adapted from: + * https://doc.qt.io/qt-6/qtwidgets-layouts-flowlayout-example.html + */ +class FlowLayout : public QLayout +{ +public: + explicit FlowLayout(QWidget* parent, + int margin = -1, + int hSpacing = -1, + int vSpacing = -1); + explicit FlowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1); + ~FlowLayout(); + + void addItem(QLayoutItem* item) override; + int horizontalSpacing() const; + int verticalSpacing() const; + Qt::Orientations expandingDirections() const override; + bool hasHeightForWidth() const override; + int heightForWidth(int) const override; + int count() const override; + QLayoutItem* itemAt(int index) const override; + QSize minimumSize() const override; + void setGeometry(const QRect& rect) override; + QSize sizeHint() const override; + QLayoutItem* takeAt(int index) override; + +private: + int doLayout(const QRect& rect, bool testOnly) const; + int smartSpacing(QStyle::PixelMetric pm) const; + + QList itemList_; + int hSpace_; + int vSpace_; +}; + +} // namespace ui +} // namespace qt +} // namespace scwx