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