All Classes Namespaces Functions Variables Enumerations Properties Pages
flowlayout.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the examples of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:BSD$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** BSD License Usage
18 ** Alternatively, you may use this file under the terms of the BSD license
19 ** as follows:
20 **
21 ** "Redistribution and use in source and binary forms, with or without
22 ** modification, are permitted provided that the following conditions are
23 ** met:
24 ** * Redistributions of source code must retain the above copyright
25 ** notice, this list of conditions and the following disclaimer.
26 ** * Redistributions in binary form must reproduce the above copyright
27 ** notice, this list of conditions and the following disclaimer in
28 ** the documentation and/or other materials provided with the
29 ** distribution.
30 ** * Neither the name of The Qt Company Ltd nor the names of its
31 ** contributors may be used to endorse or promote products derived
32 ** from this software without specific prior written permission.
33 **
34 **
35 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46 **
47 ** $QT_END_LICENSE$
48 **
49 ****************************************************************************/
50 
51 #include <QWidget>
52 #include <QLayout>
53 #include <QtMath>
54 
55 #include "flowlayout.h"
56 
57 FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing)
58  : QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing)
59 {
60  setContentsMargins(margin, margin, margin, margin);
61 }
62 
63 FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing)
64  : m_hSpace(hSpacing), m_vSpace(vSpacing)
65 {
66  setContentsMargins(margin, margin, margin, margin);
67 }
68 
69 FlowLayout::~FlowLayout()
70 {
71  QLayoutItem *item;
72  while ((item = takeAt(0)))
73  delete item;
74 }
75 
76 void FlowLayout::addItem(QLayoutItem *item)
77 {
78  itemList.append(item);
79 }
80 
81 int FlowLayout::horizontalSpacing() const
82 {
83  if (m_hSpace >= 0) {
84  return m_hSpace;
85  } else {
86  return smartSpacing(QStyle::PM_LayoutHorizontalSpacing);
87  }
88 }
89 
90 int FlowLayout::verticalSpacing() const
91 {
92  if (m_vSpace >= 0) {
93  return m_vSpace;
94  } else {
95  return smartSpacing(QStyle::PM_LayoutVerticalSpacing);
96  }
97 }
98 
99 int FlowLayout::count() const
100 {
101  return itemList.size();
102 }
103 
104 QLayoutItem *FlowLayout::itemAt(int index) const
105 {
106  return itemList.value(index);
107 }
108 
109 QLayoutItem *FlowLayout::takeAt(int index)
110 {
111  if (index >= 0 && index < itemList.size())
112  return itemList.takeAt(index);
113  else
114  return nullptr;
115 }
116 
117 Qt::Orientations FlowLayout::expandingDirections() const
118 {
119  return 0;
120 }
121 
122 bool FlowLayout::hasHeightForWidth() const
123 {
124  return true;
125 }
126 
127 int FlowLayout::heightForWidth(int width) const
128 {
129  int height = doLayout(QRect(0, 0, width, 0), true);
130  return height;
131 }
132 
133 void FlowLayout::setGeometry(const QRect &rect)
134 {
135  QLayout::setGeometry(rect);
136  doLayout(rect, false);
137 }
138 
139 QSize FlowLayout::sizeHint() const
140 {
141  return minimumSize();
142 }
143 
144 QSize FlowLayout::minimumSize() const
145 {
146  QSize size;
147  QLayoutItem *item;
148  foreach (item, itemList)
149  size = size.expandedTo(item->minimumSize());
150 
151  size += QSize(2*margin(), 2*margin());
152  return size;
153 }
154 
155 int FlowLayout::doLayout(const QRect &rect, bool testOnly) const
156 {
157  int left, top, right, bottom;
158  getContentsMargins(&left, &top, &right, &bottom);
159  QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
160  int x = effectiveRect.x();
161  int y = effectiveRect.y();
162  int lineHeight = 0;
163  int rowCount = 0;
164 
165  QLayoutItem *item;
166  int spaceX = 0;
167  for (int i = 0; i < itemList.length(); i++) {
168  item = itemList.at(i);
169  QWidget *wid = item->widget();
170  spaceX = horizontalSpacing();
171  if (spaceX == -1)
172  spaceX = wid->style()->layoutSpacing(
174  int spaceY = verticalSpacing();
175  if (spaceY == -1)
176  spaceY = wid->style()->layoutSpacing(
178 
179  int nextX = x + item->sizeHint().width() + spaceX;
180  if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) {
181  if(!testOnly && alignment() & Qt::AlignHCenter) {
182  int offset = qFloor((effectiveRect.right() + spaceX - x) / 2);
183  for(int j = i-1; j > i-1-rowCount; j--) {
184  auto rowItem = itemList.at(j);
185  rowItem->setGeometry(rowItem->geometry().adjusted(offset, 0, offset, 0));
186  }
187  }
188 
189  x = effectiveRect.x();
190  y = y + lineHeight + spaceY;
191  nextX = x + item->sizeHint().width() + spaceX;
192  lineHeight = 0;
193  rowCount = 0;
194  }
195  rowCount++;
196 
197  if (!testOnly) {
198  item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));
199  }
200 
201  x = nextX;
202  lineHeight = qMax(lineHeight, item->sizeHint().height());
203  }
204 
205  if (!testOnly && alignment() & Qt::AlignHCenter) {
206  int offset = qFloor((effectiveRect.right() + spaceX - x) / 2);
207  for (int j = itemList.length()-1; j > itemList.length()-1-rowCount; j--) {
208  auto rowItem = itemList.at(j);
209  rowItem->setGeometry(rowItem->geometry().adjusted(offset, 0, offset, 0));
210  }
211  }
212 
213  return y + lineHeight - rect.y() + bottom;
214 }
215 
216 int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const
217 {
218  QObject* parent = this->parent();
219  if (!parent) {
220  return -1;
221  } else if (parent->isWidgetType()) {
222  QWidget *pw = static_cast<QWidget *>(parent);
223  return pw->style()->pixelMetric(pm, nullptr, pw);
224  } else {
225  return static_cast<QLayout *>(parent)->spacing();
226  }
227 }
int width() const const
int right() const const
int length() const const
virtual void setGeometry(const QRect &r)=0
QStyle * style() const const
virtual int pixelMetric(QStyle::PixelMetric metric, const QStyleOption *option, const QWidget *widget) const const =0
virtual QWidget * widget()
const T & at(int i) const const
QTextStream & right(QTextStream &stream)
T takeAt(int i)
int x() const const
int y() const const
AlignHCenter
int size() const const
T value(int i) const const
QTextStream & left(QTextStream &stream)
void getContentsMargins(int *left, int *top, int *right, int *bottom) const const
void append(const T &value)
virtual QSize minimumSize() const const =0
QRect adjusted(int dx1, int dy1, int dx2, int dy2) const const
PM_LayoutHorizontalSpacing
QSize expandedTo(const QSize &otherSize) const const
virtual int layoutSpacing(QSizePolicy::ControlType control1, QSizePolicy::ControlType control2, Qt::Orientation orientation, const QStyleOption *option, const QWidget *widget) const const =0
int height() const const
Qt::Alignment alignment() const const
int spacing() const const
typedef Orientations
bool isWidgetType() const const
QObject * parent() const const
virtual void setGeometry(const QRect &r) override
int margin() const const
virtual QSize sizeHint() const const =0