Pencil2D Animation
Download Community News Docs Contribute
  • Overview
  • Articles
  • Code
  •  
  • Class List
  • Class Index
  • Class Hierarchy
  • Class Members
  • File List
Loading...
Searching...
No Matches
  • core_lib
  • src
  • interface
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
52Pencil2D - Traditional Animation Software
53Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon
54Copyright (C) 2012-2020 Matthew Chiawen Chang
55
56This program is free software; you can redistribute it and/or
57modify it under the terms of the GNU General Public License
58as published by the Free Software Foundation; version 2 of the License.
59
60This program is distributed in the hope that it will be useful,
61but WITHOUT ANY WARRANTY; without even the implied warranty of
62MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
63GNU General Public License for more details.
64
65*/
66
67#include <QWidget>
68#include <QLayout>
69#include <QtMath>
70#include <QDebug>
71#include <QDockWidget>
72
73#include "flowlayout.h"
74
75FlowLayout::FlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing)
76 : QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing)
77{
78 setContentsMargins(margin, margin, margin, margin);
79}
80
81FlowLayout::FlowLayout(int margin, int hSpacing, int vSpacing)
82 : FlowLayout(nullptr, margin, hSpacing, vSpacing)
83{}
84
85FlowLayout::~FlowLayout()
86{
87 QLayoutItem *item;
88 while ((item = takeAt(0)))
89 delete item;
90}
91
92void FlowLayout::addItem(QLayoutItem *item)
93{
94 itemList.append(item);
95}
96
97int FlowLayout::horizontalSpacing() const
98{
99 if (m_hSpace >= 0) {
100 return m_hSpace;
101 } else {
102 return smartSpacing(QStyle::PM_LayoutHorizontalSpacing);
103 }
104}
105
106int FlowLayout::verticalSpacing() const
107{
108 if (m_vSpace >= 0) {
109 return m_vSpace;
110 } else {
111 return smartSpacing(QStyle::PM_LayoutVerticalSpacing);
112 }
113}
114
115int FlowLayout::count() const
116{
117 return itemList.size();
118}
119
120QLayoutItem *FlowLayout::itemAt(int index) const
121{
122 return itemList.value(index);
123}
124
125QLayoutItem *FlowLayout::takeAt(int index)
126{
127 if (index >= 0 && index < itemList.size())
128 return itemList.takeAt(index);
129 else
130 return nullptr;
131}
132
133bool FlowLayout::hasHeightForWidth() const
134{
135 return true;
136}
137
138Qt::Orientations FlowLayout::expandingDirections() const
139{
140 return {};
141}
142
143int FlowLayout::heightForWidth(int width) const
144{
145 return calculateHeightForWidth(width);
146}
147
148void FlowLayout::setGeometry(const QRect &rect)
149{
150 QLayout::setGeometry(rect);
151 mNumberOfRows = applyLayout(rect);
152}
153
154QSize FlowLayout::sizeHint() const
155{
156 return minimumSize();
157}
158
159QSize FlowLayout::minimumSize() const
160{
161 QSize size;
162 QLayoutItem *item;
163 foreach (item, itemList)
164 size = size.expandedTo(item->minimumSize());
165 int left, top, right, bottom;
166 getContentsMargins(&left, &top, &right, &bottom);
167 size += QSize(left + right, top + bottom);
168 return size;
169}
170
171RowLayoutInfo FlowLayout::alignJustifiedRow(int startIndex, int count, const QRect& effectiveRect, int spaceX) const
172{
173
174 int spacing = 0;
175 if (count > 0) {
176 int gapCount = count + 1;
177 int rowWidth = calculateRowWidth(startIndex, count, spaceX);
178 int availableSpace = effectiveRect.width() - rowWidth;
179
180 spacing = (gapCount > 0 && availableSpace > 0)
181 ? availableSpace / gapCount
182 : 0;
183 }
184
185 int itemX = effectiveRect.left() + spacing;
186
187 RowLayoutInfo row;
188
189 row.startX = itemX;
190 row.startIndex = startIndex;
191 row.spacing = spaceX + spacing;
192
193 for (int j = startIndex; j < startIndex + count; j += 1) {
194 QLayoutItem *rowItem = itemList.at(j);
195 const QSize& itemSize = rowItem->sizeHint();
196 rowItem->setGeometry(QRect(QPoint(itemX, rowItem->geometry().y()), itemSize));
197 itemX += row.spacing + itemSize.width();
198 }
199
200 return row;
201}
202
203RowLayoutInfo FlowLayout::alignHCenterRow(int startIndex, int count, const QRect &effectiveRect, int spaceX) const
204{
205 int rowWidth = calculateRowWidth(startIndex, count, spaceX);
206 int offset = (effectiveRect.width() - rowWidth) / 2;
207 int rowOffsetX = effectiveRect.left() + offset;
208
209 RowLayoutInfo row;
210
211 row.startX = rowOffsetX;
212 row.startIndex = startIndex;
213 row.spacing = spaceX;
214
215 for (int i = startIndex; i < startIndex + count; i += 1) {
216 QLayoutItem *rowItem = itemList.at(i);
217
218 const QSize& itemSize = rowItem->sizeHint();
219 rowItem->setGeometry(QRect(QPoint(rowOffsetX, rowItem->geometry().y()), itemSize));
220 rowOffsetX += row.spacing + itemSize.width();
221 }
222
223 return row;
224}
225
226int FlowLayout::calculateHeightForWidth(int width) const
227{
228 int left, top, right, bottom;
229 getContentsMargins(&left, &top, &right, &bottom);
230 int lineHeight = 0;
231 int rowCount = 0;
232 int totalRows = 0;
233
234 int spaceX = horizontalSpacing();
235 int spaceY = verticalSpacing();
236
237 int y = 0;
238
239 for (int i = 0; i < itemList.length(); i++) {
240 QLayoutItem* item = itemList.at(i);
241 QWidget *wid = item->widget();
242 int rowWidth = calculateRowWidth(0, rowCount, spaceX);
243
244 if (spaceX == -1)
245 spaceX = wid->style()->layoutSpacing(
246 QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal);
247 if (spaceY == -1)
248 spaceY = wid->style()->layoutSpacing(
249 QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);
250
251 if (rowWidth + item->sizeHint().width() + spaceX >= width && lineHeight > 0) {
252 totalRows++;
253
254 y += lineHeight + spaceY;
255 lineHeight = 0;
256 rowCount = 0;
257 }
258
259 lineHeight = qMax(lineHeight, item->sizeHint().height());
260 rowCount++;
261 }
262
263
264 return lineHeight + y + top + bottom;
265}
266
267int FlowLayout::applyLayout(const QRect &rect) const
268{
269 int left, top, right, bottom;
270 getContentsMargins(&left, &top, &right, &bottom);
271
272 int spaceX = horizontalSpacing();
273 int spaceY = verticalSpacing();
274
275 QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom);
276 int x = effectiveRect.x();
277 int y = effectiveRect.y();
278 int lineHeight = 0;
279
280 QLayoutItem *item;
281
282 QVector<RowLayoutInfo> rowAlignments;
283
284 int currentRowCount = 0;
285 int maxRowCount = 0;
286
287 for (int i = 0; i < itemList.length(); i += 1) {
288 item = itemList.at(i);
289 QWidget *wid = item->widget();
290
291 if (spaceX == -1)
292 spaceX = wid->style()->layoutSpacing(
293 QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal);
294 if (spaceY == -1)
295 spaceY = wid->style()->layoutSpacing(
296 QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical);
297
298 int startRowIndex = i - currentRowCount;
299 int rowWidth = calculateRowWidth(startRowIndex, currentRowCount, spaceX);
300
301 if (currentRowCount > 0) {
302 maxRowCount = qMax(currentRowCount, maxRowCount);
303
304 if (rowWidth + item->sizeHint().width() + spaceX >= effectiveRect.width()) {
305 if (alignment() & Qt::AlignHCenter) {
306 rowAlignments.append(alignHCenterRow(startRowIndex, currentRowCount, effectiveRect, spaceX));
307 } else if (alignment() & Qt::AlignJustify) {
308 rowAlignments.append(alignJustifiedRow(startRowIndex, currentRowCount, effectiveRect, spaceX));
309 }
310
311 y = y + lineHeight + spaceY;
312 lineHeight = 0;
313 currentRowCount = 0;
314 }
315 }
316
317 item->setGeometry(QRect(QPoint(x, y), item->sizeHint()));
318
319 lineHeight = qMax(lineHeight, item->sizeHint().height());
320 currentRowCount += 1;
321 }
322
323 if (maxRowCount == itemList.length() - 1) {
324 alignHCenterRow(itemList.length() - currentRowCount, currentRowCount, effectiveRect, spaceX);
325 } else if (currentRowCount > 0) {
326 lastLineAlignment(itemList.length() - currentRowCount, currentRowCount, rowAlignments.last(), effectiveRect);
327 }
328
329 return maxRowCount;
330}
331
332void FlowLayout::lastLineAlignment(int startIndex, int count, RowLayoutInfo rowInfo, const QRect& effectiveRect) const
333{
334 if (alignment() & Qt::AlignHCenter) {
335 alignHCenterRow(startIndex, count, effectiveRect, rowInfo.spacing);
336 } else if (alignment() & Qt::AlignJustify) {
337 alignJustifiedRow(startIndex, count, effectiveRect, rowInfo.spacing);
338 }
339}
340
341int FlowLayout::calculateRowWidth(int start, int end, int spacing) const
342{
343 if (itemList.isEmpty()) { return 0; }
344
345 int totalWidth = 0;
346 // Calculate the total width of all item in row including spacing
347 for (int i = start; i < start + end; i += 1) {
348 totalWidth += itemList.at(i)->sizeHint().width();
349 }
350
351 return totalWidth + (spacing * (end - 1));
352}
353
354int FlowLayout::smartSpacing(QStyle::PixelMetric pm) const
355{
356 QObject* parent = this->parent();
357 if (!parent) {
358 return -1;
359 } else if (parent->isWidgetType()) {
360 QWidget *pw = static_cast<QWidget *>(parent);
361 return pw->style()->pixelMetric(pm, nullptr, pw);
362 } else {
363 return static_cast<QLayout *>(parent)->spacing();
364 }
365}
FlowLayout
Definition: flowlayout.h:81
QLayout
QLayout::getContentsMargins
void getContentsMargins(int *left, int *top, int *right, int *bottom) const const
QLayout::setGeometry
virtual void setGeometry(const QRect &r) override
QLayout::spacing
spacing
QLayoutItem
QLayoutItem::alignment
Qt::Alignment alignment() const const
QLayoutItem::geometry
virtual QRect geometry() const const=0
QLayoutItem::minimumSize
virtual QSize minimumSize() const const=0
QLayoutItem::setGeometry
virtual void setGeometry(const QRect &r)=0
QLayoutItem::sizeHint
virtual QSize sizeHint() const const=0
QLayoutItem::widget
virtual QWidget * widget()
QList::append
void append(const T &value)
QList::at
const T & at(int i) const const
QList::isEmpty
bool isEmpty() const const
QList::length
int length() const const
QList::size
int size() const const
QList::takeAt
T takeAt(int i)
QList::value
T value(int i) const const
QObject
QObject::isWidgetType
bool isWidgetType() const const
QObject::parent
QObject * parent() const const
QPoint
QRect
QRect::adjusted
QRect adjusted(int dx1, int dy1, int dx2, int dy2) const const
QRect::left
int left() const const
QRect::width
int width() const const
QRect::x
int x() const const
QRect::y
int y() const const
QSize
QSize::expandedTo
QSize expandedTo(const QSize &otherSize) const const
QSize::height
int height() const const
QSize::width
int width() const const
QSizePolicy::PushButton
PushButton
QStyle::PM_LayoutHorizontalSpacing
PM_LayoutHorizontalSpacing
QStyle::layoutSpacing
virtual int layoutSpacing(QSizePolicy::ControlType control1, QSizePolicy::ControlType control2, Qt::Orientation orientation, const QStyleOption *option, const QWidget *widget) const const=0
QStyle::pixelMetric
virtual int pixelMetric(QStyle::PixelMetric metric, const QStyleOption *option, const QWidget *widget) const const=0
Qt::AlignHCenter
AlignHCenter
Qt::Orientations
typedef Orientations
Qt::left
QTextStream & left(QTextStream &stream)
Qt::right
QTextStream & right(QTextStream &stream)
QVector
QVector::append
void append(const T &value)
QVector::last
T & last()
QWidget
QWidget::style
QStyle * style() const const
RowLayoutInfo
Definition: flowlayout.h:74
Generated on Thu Jun 5 2025 14:06:43 for Pencil2D by doxygen 1.9.6 based on revision 4c63407997b2c03e5048716586dec6fbbb755173