Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqstylekitlayout.cpp
Go to the documentation of this file.
1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
7
8static qreal layoutWidth(QQStyleKitLayoutItem *li, qreal availableWidth = .0)
9{
10 Q_ASSERT(li);
11 Q_ASSERT(li->item());
12 QQuickItem *item = li->item();
13 qreal w = item->implicitWidth();
14 if (li->fillWidth())
15 w = qMax(.0, availableWidth - li->margins().left() - li->margins().right());
16 return qMax(.0, w);
17}
18
19static qreal layoutHeight(QQStyleKitLayoutItem *li, qreal availableHeight = .0)
20{
21 Q_ASSERT(li);
22 Q_ASSERT(li->item());
23 QQuickItem *item = li->item();
24 qreal h = item->implicitHeight();
25 if (li->fillHeight())
26 h = qMax(.0, availableHeight - li->margins().top() - li->margins().bottom());
27 return qMax(.0, h);
28}
29
30static qreal totalWidth(const QList<QQStyleKitLayoutItem *> &items, qreal spacing)
31{
32 qreal total = .0;
33 for (QQStyleKitLayoutItem *li : items) {
34 if (li->item() && li->item()->isVisible())
35 total += layoutWidth(li) + li->margins().left() + li->margins().right() + spacing;
36 }
37 return total;
38}
39
40static qreal totalHeight(const QList<QQStyleKitLayoutItem *> &items)
41{
42 qreal maxHeight = .0;
43 for (QQStyleKitLayoutItem *li : items) {
44 if (li->item() && li->item()->isVisible()) {
45 const qreal h = layoutHeight(li) + li->margins().top() + li->margins().bottom();
46 if (h > maxHeight)
47 maxHeight = h;
48 }
49 }
50 return maxHeight;
51}
52
53static qreal vAlignY(QQStyleKitLayoutItem *li, qreal containerY, qreal containerHeight)
54{
55 Q_ASSERT(li);
56 Q_ASSERT(li->item());
57
58 const auto itemHeight = layoutHeight(li, containerHeight);
59 const auto vAlign = li->alignment() & Qt::AlignVertical_Mask;
60 const auto margins = li->margins();
61 if (vAlign & Qt::AlignTop)
62 return containerY + margins.top();
63 if (vAlign & Qt::AlignBottom)
64 return containerY + containerHeight - itemHeight - margins.bottom();
65 return containerY + margins.top()
66 + (containerHeight - margins.top() - margins.bottom() - itemHeight) / 2.0;
67}
68
69QQStyleKitLayoutItem::QQStyleKitLayoutItem(QObject *parent)
70 : QObject(parent)
71{
72}
73
74QQuickItem *QQStyleKitLayoutItem::item() const
75{
76 return m_item;
77}
78
79void QQStyleKitLayoutItem::setItem(QQuickItem *item)
80{
81 if (m_item == item)
82 return;
83
84 if (m_item)
85 disconnect(m_item, nullptr, this, nullptr);
86
87 m_item = item;
88 if (m_item) {
89 connect(m_item, &QQuickItem::implicitWidthChanged, this, [this]() { emit itemChanged(); });
90 connect(m_item, &QQuickItem::implicitHeightChanged, this, [this]() { emit itemChanged(); });
91 connect(m_item, &QQuickItem::visibleChanged, this, [this]() { emit itemChanged(); });
92 }
93 // TODO: parentchanged
94 emit itemChanged();
95}
96
97qreal QQStyleKitLayoutItem::x() const
98{
99 return m_x;
100}
101
102void QQStyleKitLayoutItem::setX(qreal x)
103{
104 if (qFuzzyCompare(m_x, x))
105 return;
106
107 m_x = x;
108 emit xChanged();
109}
110
111qreal QQStyleKitLayoutItem::y() const
112{
113 return m_y;
114}
115
116void QQStyleKitLayoutItem::setY(qreal y)
117{
118 if (qFuzzyCompare(m_y, y))
119 return;
120
121 m_y = y;
122 emit yChanged();
123}
124
125qreal QQStyleKitLayoutItem::width() const
126{
127 return m_width;
128}
129
130void QQStyleKitLayoutItem::setWidth(qreal width)
131{
132 if (qFuzzyCompare(m_width, width))
133 return;
134
135 m_width = width;
136 emit widthChanged();
137}
138
139qreal QQStyleKitLayoutItem::height() const
140{
141 return m_height;
142}
143
144void QQStyleKitLayoutItem::setHeight(qreal height)
145{
146 if (qFuzzyCompare(m_height, height))
147 return;
148
149 m_height = height;
150 emit heightChanged();
151}
152
153Qt::Alignment QQStyleKitLayoutItem::alignment() const
154{
155 return m_alignment;
156}
157
158void QQStyleKitLayoutItem::setAlignment(Qt::Alignment alignment)
159{
160 if (m_alignment == alignment)
161 return;
162
163 m_alignment = alignment;
164 emit alignmentChanged();
165}
166
167QMarginsF QQStyleKitLayoutItem::margins() const
168{
169 return m_margins;
170}
171
172void QQStyleKitLayoutItem::setMargins(const QMarginsF &margins)
173{
174 if (m_margins == margins)
175 return;
176
177 m_margins = margins;
178 emit marginsChanged();
179}
180
181bool QQStyleKitLayoutItem::fillWidth() const
182{
183 return m_fillWidth;
184}
185
186void QQStyleKitLayoutItem::setFillWidth(bool fill)
187{
188 if (m_fillWidth == fill)
189 return;
190
191 m_fillWidth = fill;
192 emit fillWidthChanged();
193}
194
195bool QQStyleKitLayoutItem::fillHeight() const
196{
197 return m_fillHeight;
198}
199
200void QQStyleKitLayoutItem::setFillHeight(bool fill)
201{
202 if (m_fillHeight == fill)
203 return;
204
205 m_fillHeight = fill;
206 emit fillHeightChanged();
207}
208
209QQStyleKitLayout::QQStyleKitLayout(QQuickItem *parent)
210 : QQuickItem(parent)
211 , m_mirrored(false)
212 , m_enabled(true)
213{
214}
215
217{
218 return m_container;
219}
220
221void QQStyleKitLayout::setContainer(QQuickItem *container)
222{
223 if (m_container == container)
224 return;
225
226 m_container = container;
227 emit containerChanged();
228 connect(m_container, &QQuickItem::widthChanged, this, &QQStyleKitLayout::polish);
229 connect(m_container, &QQuickItem::heightChanged, this, &QQStyleKitLayout::polish);
230
231 polish();
232}
233
235{
236 return QQmlListProperty<QQStyleKitLayoutItem>(const_cast<QQStyleKitLayout *>(this),
237 nullptr,
238 &QQStyleKitLayout::layoutItem_append,
239 &QQStyleKitLayout::layoutItem_count,
240 &QQStyleKitLayout::layoutItem_at,
241 &QQStyleKitLayout::layoutItem_clear);
242}
243
244void QQStyleKitLayout::layoutItem_append(QQmlListProperty<QQStyleKitLayoutItem> *list, QQStyleKitLayoutItem *item)
245{
246 QQStyleKitLayout *layout = qobject_cast<QQStyleKitLayout *>(list->object);
247 if (layout && item) {
248 layout->m_layoutItems.append(item);
249 connect(item, &QQStyleKitLayoutItem::itemChanged, layout, &QQStyleKitLayout::polish);
250 connect(item, &QQStyleKitLayoutItem::alignmentChanged, layout, &QQStyleKitLayout::polish);
251 connect(item, &QQStyleKitLayoutItem::marginsChanged, layout, &QQStyleKitLayout::polish);
252 connect(item, &QQStyleKitLayoutItem::fillWidthChanged, layout, &QQStyleKitLayout::polish);
253 connect(item, &QQStyleKitLayoutItem::fillHeightChanged, layout, &QQStyleKitLayout::polish);
254 emit layout->layoutItemsChanged();
255 layout->polish();
256 }
257}
258
259qsizetype QQStyleKitLayout::layoutItem_count(QQmlListProperty<QQStyleKitLayoutItem> *list)
260{
261 QQStyleKitLayout *layout = qobject_cast<QQStyleKitLayout *>(list->object);
262 if (layout)
263 return layout->m_layoutItems.size();
264 return 0;
265}
266
267QQStyleKitLayoutItem *QQStyleKitLayout::layoutItem_at(QQmlListProperty<QQStyleKitLayoutItem> *list, qsizetype index)
268{
269 QQStyleKitLayout *layout = qobject_cast<QQStyleKitLayout *>(list->object);
270 if (layout)
271 return layout->m_layoutItems.value(index);
272 return nullptr;
273}
274
275void QQStyleKitLayout::layoutItem_clear(QQmlListProperty<QQStyleKitLayoutItem> *list)
276{
277 QQStyleKitLayout *layout = qobject_cast<QQStyleKitLayout *>(list->object);
278 if (layout) {
279 layout->m_layoutItems.clear();
280 emit layout->layoutItemsChanged();
281 layout->polish();
282 }
283}
284
286{
287 return m_padding;
288}
289
291{
292 return m_contentMargins;
293}
294
295void QQStyleKitLayout::setContentMargins(const QMarginsF &margins)
296{
297 if (m_contentMargins == margins)
298 return;
299
300 m_contentMargins = margins;
301 emit contentMarginsChanged();
302 polish();
303}
304
306{
307 return m_spacing;
308}
309
310void QQStyleKitLayout::setSpacing(qreal spacing)
311{
312 if (qFuzzyCompare(m_spacing, spacing))
313 return;
314
315 m_spacing = spacing;
316 emit spacingChanged();
317 polish();
318}
319
321{
322 return m_mirrored;
323}
324
325void QQStyleKitLayout::setMirrored(bool mirrored)
326{
327 if (m_mirrored == mirrored)
328 return;
329
330 m_mirrored = mirrored;
331 emit mirroredChanged();
332 polish();
333}
334
336{
337 return m_implicitWidth;
338}
339
341{
342 return m_implicitHeight;
343}
344
346{
347 if (qFuzzyCompare(m_implicitWidth, width))
348 return;
349
350 m_implicitWidth = width;
351 emit implicitWidthChanged();
352 polish();
353}
354
356{
357 if (qFuzzyCompare(m_implicitHeight, height))
358 return;
359
360 m_implicitHeight = height;
361 emit implicitHeightChanged();
362 polish();
363}
364
366{
367 return m_enabled;
368}
369
370void QQStyleKitLayout::setEnabled(bool enabled)
371{
372 if (m_enabled == enabled)
373 return;
374
375 m_enabled = enabled;
376 emit enabledChanged();
377
378 if (m_enabled)
379 polish();
380}
381
383{
384 if (!m_enabled)
385 return;
386
387 if (!m_container || m_container->width() <= 0 || m_container->height() <= 0)
388 return;
389
390 QList<QQStyleKitLayoutItem *> left;
391 QList<QQStyleKitLayoutItem *> right;
392 QList<QQStyleKitLayoutItem *> center;
393
394 for (QQStyleKitLayoutItem *li : m_layoutItems) {
395 if (!li->item() || !li->item()->isVisible())
396 continue;
397 const auto hAlign = li->alignment() & Qt::AlignHorizontal_Mask;
398 const bool isMirrored = m_mirrored && !(hAlign & Qt::AlignAbsolute);
399 switch (hAlign) {
400 case Qt::AlignLeft:
401 if (isMirrored)
402 right.append(li);
403 else
404 left.append(li);
405 break;
406 case Qt::AlignRight:
407 if (isMirrored)
408 left.append(li);
409 else
410 right.append(li);
411 break;
412 default:
413 center.append(li);
414 break;
415 }
416 }
417
418 const qreal containerWidth = m_container->width() ? m_container->width() : m_container->implicitWidth();
419 const qreal containerHeight = m_container->height() ? m_container->height() : m_container->implicitHeight();
420 const qreal paddedX = m_contentMargins.left();
421 const qreal paddedY = m_contentMargins.top();
422 const qreal paddedWidth = qMax(containerWidth - m_contentMargins.left() - m_contentMargins.right(), .0);
423 const qreal paddedHeight = qMax(containerHeight - m_contentMargins.top() - m_contentMargins.bottom(), .0);
424
425 qreal maxTopMargin = .0;
426 qreal maxBottomMargin = .0;
427
428 // Position left-aligned items
429 {
430 qreal x = paddedX;
431 for (QQStyleKitLayoutItem *li : left) {
432 QQuickItem *item = li->item();
433 if (!item || !item->isVisible())
434 continue;
435
436 const QMarginsF margins = li->margins();
437 const qreal itemWidth = layoutWidth(li, paddedWidth);
438 const qreal itemHeight = layoutHeight(li, paddedHeight);
439 auto y = vAlignY(li, paddedY, paddedHeight);
440 li->setX(x + margins.left());
441 li->setY(y);
442 li->setWidth(itemWidth);
443 li->setHeight(itemHeight);
444 x += itemWidth + margins.left() + margins.right() + m_spacing;
445 maxTopMargin = qMax(maxTopMargin, margins.top());
446 maxBottomMargin = qMax(maxBottomMargin, margins.bottom());
447 }
448 }
449
450 // Position right-aligned items
451 {
452 qreal x = paddedX + paddedWidth;
453 for (QQStyleKitLayoutItem *li : right) {
454 QQuickItem *item = li->item();
455 if (!item || !item->isVisible())
456 continue;
457
458 const QMarginsF margins = li->margins();
459 const qreal itemWidth = layoutWidth(li, paddedWidth);
460 const qreal itemHeight = layoutHeight(li, paddedHeight);
461 x -= itemWidth + margins.right() + margins.left();
462 auto y = vAlignY(li, paddedY, paddedHeight);
463 li->setX(x + margins.left());
464 li->setY(y);
465 li->setWidth(itemWidth);
466 li->setHeight(itemHeight);
467 x -= m_spacing;
468 maxTopMargin = qMax(maxTopMargin, margins.top());
469 maxBottomMargin = qMax(maxBottomMargin, margins.bottom());
470 }
471 }
472
473 // Position center-aligned items
474 {
475 qreal x = paddedX + (paddedWidth - totalWidth(center, m_spacing)) / 2;
476 for (QQStyleKitLayoutItem *li : center) {
477 QQuickItem *item = li->item();
478 if (!item || !item->isVisible())
479 continue;
480
481 const QMarginsF margins = li->margins();
482 const qreal itemWidth = layoutWidth(li, paddedWidth);
483 const qreal itemHeight = layoutHeight(li, paddedHeight);
484 auto y = vAlignY(li, paddedY, paddedHeight);
485 li->setX(x + margins.left());
486 li->setY(y);
487 li->setWidth(itemWidth);
488 li->setHeight(itemHeight);
489 x += itemWidth + margins.left() + margins.right() + m_spacing;
490 maxTopMargin = qMax(maxTopMargin, margins.top());
491 maxBottomMargin = qMax(maxBottomMargin, margins.bottom());
492 }
493 }
494
495 const auto leftWidth = totalWidth(left, m_spacing);
496 const auto leftHeight = totalHeight(left);
497 const auto rightWidth = totalWidth(right, m_spacing);
498 const auto rightHeight = totalHeight(right);
499 const auto centerWidth = totalWidth(center, m_spacing);
500 const auto centerHeight = totalHeight(center);
501
502 const auto implicitWidth = leftWidth + rightWidth + centerWidth
503 - m_spacing * (left.isEmpty() ? 0 : 1)
504 - m_spacing * (right.isEmpty() ? 0 : 1)
505 - m_spacing * (center.isEmpty() ? 0 : 1)
506 + m_contentMargins.left() + m_contentMargins.right();
507 setImplicitWidth(implicitWidth);
508 const auto implicitHeight = qMax(qMax(leftHeight, rightHeight), centerHeight)
509 + m_contentMargins.top() + m_contentMargins.bottom();
510 setImplicitHeight(implicitHeight);
511
512 // HACK for control's contentItem
513 // QQuickControl determines the contentItem geometry based on the control size and padding
514 // So we include the layout's left/right widths into the padding calculation
515 auto leftPadding = m_contentMargins.left() + leftWidth;
516 auto topPadding = m_contentMargins.top() + maxTopMargin; // TODO: support vertical layout items
517 auto rightPadding = m_contentMargins.right() + rightWidth;
518 auto bottomPadding = m_contentMargins.bottom() + maxBottomMargin; // TODO: support vertical layout items
519 if (isMirrored())
520 std::swap(leftPadding, rightPadding);
521 QMarginsF newPadding = QMarginsF(leftPadding, topPadding, rightPadding, bottomPadding);
522 if (m_padding != newPadding) {
523 m_padding = newPadding;
524 emit paddingChanged();
525 }
526}
527
528QT_END_NAMESPACE
529
530#include "moc_qqstylekitlayout_p.cpp"
void setImplicitHeight(qreal height)
QMarginsF padding() const
void setSpacing(qreal spacing)
qreal implicitHeight() const
qreal implicitWidth() const
QMarginsF contentMargins() const
void setContentMargins(const QMarginsF &margins)
void setEnabled(bool enabled)
void setImplicitWidth(qreal width)
void setContainer(QQuickItem *item)
void updatePolish() override
This function should perform any layout as required for this item.
QQuickItem * container() const
QQmlListProperty< QQStyleKitLayoutItem > layoutItems()
void setMirrored(bool mirrored)
Combined button and popup list for selecting options.
static QT_BEGIN_NAMESPACE qreal layoutWidth(QQStyleKitLayoutItem *li, qreal availableWidth=.0)
static qreal vAlignY(QQStyleKitLayoutItem *li, qreal containerY, qreal containerHeight)
static qreal totalWidth(const QList< QQStyleKitLayoutItem * > &items, qreal spacing)
static qreal totalHeight(const QList< QQStyleKitLayoutItem * > &items)
static qreal layoutHeight(QQStyleKitLayoutItem *li, qreal availableHeight=.0)