1 /*
2 * packedlayout.cpp - layout to pack items into rows
3 * Program: kalarm
4 * SPDX-FileCopyrightText: 2007-2021 David Jarvie <djarvie@kde.org>
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9 #include "packedlayout.h"
10
11 #include "kalarm_debug.h"
12
13 #include <QWidget>
14
PackedLayout(QWidget * parent,Qt::Alignment alignment)15 PackedLayout::PackedLayout(QWidget* parent, Qt::Alignment alignment)
16 : QLayout(parent)
17 , mAlignment(alignment)
18 {
19 }
20
PackedLayout(Qt::Alignment alignment)21 PackedLayout::PackedLayout(Qt::Alignment alignment)
22 : QLayout()
23 , mAlignment(alignment)
24 {
25 }
26
~PackedLayout()27 PackedLayout::~PackedLayout()
28 {
29 while (!mItems.isEmpty())
30 delete mItems.takeFirst();
31 }
32
setHorizontalSpacing(int spacing)33 void PackedLayout::setHorizontalSpacing(int spacing)
34 {
35 mHorizontalSpacing = spacing;
36 }
37
setVerticalSpacing(int spacing)38 void PackedLayout::setVerticalSpacing(int spacing)
39 {
40 mVerticalSpacing = spacing;
41 }
42
horizontalSpacing() const43 int PackedLayout::horizontalSpacing() const
44 {
45 return (mHorizontalSpacing >= 0) ? mHorizontalSpacing : spacing();
46 }
47
verticalSpacing() const48 int PackedLayout::verticalSpacing() const
49 {
50 return (mVerticalSpacing >= 0) ? mVerticalSpacing : spacing();
51 }
52
53 /******************************************************************************
54 * Inserts a button into the group.
55 */
addItem(QLayoutItem * item)56 void PackedLayout::addItem(QLayoutItem* item)
57 {
58 mWidthCached = 0;
59 mItems.append(item);
60 }
61
itemAt(int index) const62 QLayoutItem* PackedLayout::itemAt(int index) const
63 {
64 return (index >= 0 && index < mItems.count()) ? mItems[index] : nullptr;
65 }
66
takeAt(int index)67 QLayoutItem* PackedLayout::takeAt(int index)
68 {
69 if (index < 0 || index >= mItems.count())
70 return nullptr;
71 return mItems.takeAt(index);
72 }
73
heightForWidth(int w) const74 int PackedLayout::heightForWidth(int w) const
75 {
76 if (w != mWidthCached)
77 {
78 mHeightCached = arrange(QRect(0, 0, w, 0), false);
79 mWidthCached = w;
80 }
81 return mHeightCached;
82 }
83
setGeometry(const QRect & rect)84 void PackedLayout::setGeometry(const QRect& rect)
85 {
86 QLayout::setGeometry(rect);
87 arrange(rect, true);
88 }
89
90 // Return the maximum size of any widget.
minimumSize() const91 QSize PackedLayout::minimumSize() const
92 {
93 QSize size;
94 for (int i = 0, end = mItems.count(); i < end; ++i)
95 size = size.expandedTo(mItems[i]->minimumSize());
96 int left, top, right, bottom;
97 getContentsMargins(&left, &top, &right, &bottom);
98 return {size.width() + left + right, size.height() + top + bottom};
99 }
100
101 // Arranges widgets and returns height required.
arrange(const QRect & rect,bool set) const102 int PackedLayout::arrange(const QRect& rect, bool set) const
103 {
104 int x = rect.x();
105 int y = rect.y();
106 int yrow = 0;
107 QVector<QRect> posn;
108 QVector<QLayoutItem*> items;
109 for (QLayoutItem* item : std::as_const(mItems))
110 {
111 if (item->isEmpty())
112 continue;
113 const QSize size = item->sizeHint();
114 int right = x + size.width();
115 if (right > rect.right() && x > rect.x())
116 {
117 x = rect.x();
118 y = y + yrow + verticalSpacing();
119 right = x + size.width();
120 yrow = size.height();
121 }
122 else
123 yrow = qMax(yrow, size.height());
124 items.append(item);
125 posn.append(QRect(QPoint(x, y), size));
126 x = right + horizontalSpacing();
127 }
128 if (set)
129 {
130 const int count = items.count();
131 if (mAlignment == Qt::AlignLeft)
132 {
133 // Left aligned: no position adjustment needed
134 // Set the positions of all the layout items
135 for (int i = 0; i < count; ++i)
136 items[i]->setGeometry(alignRect(rect, posn[i]));
137 }
138 else
139 {
140 // Set the positions of all the layout items
141 for (int i = 0; i < count; )
142 {
143 // Adjust item positions a row at a time
144 y = posn[i].y();
145 int last; // after last item in this row
146 for (last = i + 1; last < count && posn[last].y() == y; ++last) ;
147 const int n = last - i; // number of items in this row
148 int free = rect.right() - posn[last - 1].right();
149 switch (mAlignment)
150 {
151 case Qt::AlignJustify:
152 if (n == 1)
153 {
154 items[i]->setGeometry(alignRect(rect, posn[i]));
155 ++i;
156 }
157 else if (n > 1)
158 {
159 for (int j = 0; i < last; ++j, ++i)
160 items[i]->setGeometry(alignRect(rect, QRect(QPoint(posn[i].x() + (free * j)/(n - 1), y), posn[i].size())));
161 }
162 break;
163 case Qt::AlignHCenter:
164 free /= 2;
165 // fall through to AlignRight
166 Q_FALLTHROUGH();
167 case Qt::AlignRight:
168 for ( ; i < last; ++i)
169 items[i]->setGeometry(alignRect(rect, QRect(QPoint(posn[i].x() + free, y), posn[i].size())));
170 break;
171 default:
172 break;
173 }
174 }
175 }
176 }
177 return y + yrow - rect.y();
178 }
179
180 /******************************************************************************
181 * Adjust an item's geometry if using right-to-left alignment.
182 */
alignRect(const QRect & rect,const QRect & itemRect) const183 QRect PackedLayout::alignRect(const QRect& rect, const QRect& itemRect) const
184 {
185 QRect result(itemRect);
186 if (parentWidget()->layoutDirection() == Qt::RightToLeft)
187 result.moveRight(rect.right() - (itemRect.left() - rect.left()));
188 return result;
189 }
190
191 // vim: et sw=4:
192