Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. FlowLayout doesn't take care of actual content size

FlowLayout doesn't take care of actual content size

Scheduled Pinned Locked Moved Unsolved General and Desktop
1 Posts 1 Posters 238 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • M Offline
    M Offline
    Mark81
    wrote on last edited by
    #1

    Here my "fork" of FlowLayout, which can place the items also in vertical order:

    flowlayout.h

    #ifndef FLOWLAYOUT_H
    #define FLOWLAYOUT_H
    
    #include <QLayout>
    #include <QRect>
    #include <QStyle>
    
    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() override;
    
        void addItem(QLayoutItem *item) override;
        int horizontalSpacing() const;
        int verticalSpacing() const;
        Qt::Orientations expandingDirections() const override;
        void setOrientation(Qt::Orientations orientation) { _orientation = orientation; }
        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<QLayoutItem *> _itemList;
        Qt::Orientations _orientation;
        int _hSpace;
        int _vSpace;
    };
    
    #endif // FLOWLAYOUT_H
    

    flowlayout.cpp

    #include <QtWidgets>
    #include "flowlayout.h"
    
    FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing) : QLayout(parent), _hSpace(hSpacing), _vSpace(vSpacing)
    {
        setContentsMargins(margin, margin, margin, margin);
        _orientation = Qt::Horizontal;
    }
    
    FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing) : _hSpace(hSpacing), _vSpace(vSpacing)
    {
        setContentsMargins(margin, margin, margin, margin);
        _orientation = Qt::Horizontal;
    }
    
    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);
        else return nullptr;
    }
    
    Qt::Orientations FlowLayout::expandingDirections() const
    {
        return nullptr;
    }
    
    bool FlowLayout::hasHeightForWidth() const
    {
        return false;
    }
    
    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;
        QLayoutItem *item;
        foreach (item, _itemList) size = size.expandedTo(item->minimumSize());
        size += QSize(2 * margin(), 2 * margin());
        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;
        int colWidth = 0;
    
        QLayoutItem *item;
        foreach (item, _itemList)
        {
            QWidget *widget = item->widget();
            int spaceX = horizontalSpacing();
            if (spaceX == -1) spaceX = widget->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal);
            int spaceY = verticalSpacing();
            if (spaceY == -1) spaceY = widget->style()->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);
    
            switch (_orientation) {
            case Qt::Horizontal: {
                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());
                break; }
    
            case Qt::Vertical: {
                int nextY = y + item->sizeHint().height() + spaceY;
                if (nextY - spaceY > effectiveRect.bottom() && colWidth > 0)
                {
                    x = x + colWidth + spaceX;
                    y = effectiveRect.y();
                    nextY = y + item->sizeHint().height() + spaceY;
                    colWidth = 0;
                }
                if (!testOnly) item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));
                y = nextY;
                colWidth = qMax(colWidth, item->sizeHint().width());
                break; }
            }
    
        }
    
        if (_orientation == Qt::Horizontal) return y + lineHeight - rect.y() + bottom;
        else return x + colWidth - rect.x() + right;
    }
    
    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();
        }
    }
    

    I'm testing it mainly with vertical orientation. The problem is the horizontal scroll bar appears only when the width is less than a specific value (about 480 px) regardless the actual contents.

    I think it's related to the sizeHint() function. I don't understand how it can calculate the needed space in both direction without doing a layout(). Well, in fact it doesn't work!

    What should I change to make the horizontal scrollbar behavior reliable like the vertical one?

    1 Reply Last reply
    0

    • Login

    • Login or register to search.
    • First post
      Last post
    0
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Get Qt Extensions
    • Unsolved