diff --git a/include/ui/flowlayout.h b/include/ui/flowlayout.h new file mode 100644 index 00000000..6364dfd0 --- /dev/null +++ b/include/ui/flowlayout.h @@ -0,0 +1,37 @@ +#ifndef FLOWLAYOUT_H +#define FLOWLAYOUT_H + +#include +#include + + +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 horzSpace; + int vertSpace; +}; + +#endif // FLOWLAYOUT_H diff --git a/porymap.pro b/porymap.pro index 04fb94e2..da274dba 100644 --- a/porymap.pro +++ b/porymap.pro @@ -68,6 +68,7 @@ SOURCES += src/core/block.cpp \ src/ui/regionmapeditor.cpp \ src/ui/newmappopup.cpp \ src/ui/mapimageexporter.cpp \ + src/ui/flowlayout.cpp \ src/config.cpp \ src/editor.cpp \ src/main.cpp \ @@ -134,6 +135,7 @@ HEADERS += include/core/block.h \ include/ui/regionmapeditor.h \ include/ui/newmappopup.h \ include/ui/mapimageexporter.h \ + include/ui/flowlayout.h \ include/config.h \ include/editor.h \ include/mainwindow.h \ diff --git a/src/ui/flowlayout.cpp b/src/ui/flowlayout.cpp new file mode 100644 index 00000000..2d5a5faa --- /dev/null +++ b/src/ui/flowlayout.cpp @@ -0,0 +1,136 @@ +#include + +#include + + +FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing) : + QLayout(parent), + horzSpace(hSpacing), + vertSpace(vSpacing) +{ + setContentsMargins(margin, margin, margin, margin); +} + +FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing) : + horzSpace(hSpacing), + vertSpace(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 (horzSpace >= 0) + return horzSpace; + else + return smartSpacing(QStyle::PM_LayoutHorizontalSpacing); +} + +int FlowLayout::verticalSpacing() const { + if (vertSpace >= 0) + return vertSpace; + 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(); + } +}