Qt Forum

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Unsolved

    Unsolved FlowLayout doesn't take care of actual content size

    General and Desktop
    1
    1
    162
    Loading More Posts
    • 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
      Mark81 last edited by

      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 Reply Quote 0
      • First post
        Last post