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 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(QObject *parent)
210 : QObject(parent)
211 , m_mirrored(false)
212 , m_enabled(true)
213 , m_updatingLayout(false)
214{
215 m_updateTimer.setSingleShot(true);
216 connect(&m_updateTimer, &QTimer::timeout, this, &QQStyleKitLayout::updateLayout);
217}
218
220{
221 return m_container;
222}
223
224void QQStyleKitLayout::setContainer(QQuickItem *container)
225{
226 if (m_container == container)
227 return;
228
229 m_container = container;
230 emit containerChanged();
231 connect(m_container, &QQuickItem::widthChanged, this, &QQStyleKitLayout::scheduleUpdate);
232 connect(m_container, &QQuickItem::heightChanged, this, &QQStyleKitLayout::scheduleUpdate);
233
234 scheduleUpdate();
235}
236
238{
239 return QQmlListProperty<QQStyleKitLayoutItem>(const_cast<QQStyleKitLayout *>(this),
240 nullptr,
241 &QQStyleKitLayout::layoutItem_append,
242 &QQStyleKitLayout::layoutItem_count,
243 &QQStyleKitLayout::layoutItem_at,
244 &QQStyleKitLayout::layoutItem_clear);
245}
246
247void QQStyleKitLayout::layoutItem_append(QQmlListProperty<QQStyleKitLayoutItem> *list, QQStyleKitLayoutItem *item)
248{
249 QQStyleKitLayout *layout = qobject_cast<QQStyleKitLayout *>(list->object);
250 if (layout && item) {
251 layout->m_layoutItems.append(item);
252 connect(item, &QQStyleKitLayoutItem::itemChanged, layout, &QQStyleKitLayout::scheduleUpdate);
253 connect(item, &QQStyleKitLayoutItem::alignmentChanged, layout, &QQStyleKitLayout::scheduleUpdate);
254 connect(item, &QQStyleKitLayoutItem::marginsChanged, layout, &QQStyleKitLayout::scheduleUpdate);
255 connect(item, &QQStyleKitLayoutItem::fillWidthChanged, layout, &QQStyleKitLayout::scheduleUpdate);
256 connect(item, &QQStyleKitLayoutItem::fillHeightChanged, layout, &QQStyleKitLayout::scheduleUpdate);
257 emit layout->layoutItemsChanged();
258 layout->scheduleUpdate();
259 }
260}
261
262qsizetype QQStyleKitLayout::layoutItem_count(QQmlListProperty<QQStyleKitLayoutItem> *list)
263{
264 QQStyleKitLayout *layout = qobject_cast<QQStyleKitLayout *>(list->object);
265 if (layout)
266 return layout->m_layoutItems.size();
267 return 0;
268}
269
270QQStyleKitLayoutItem *QQStyleKitLayout::layoutItem_at(QQmlListProperty<QQStyleKitLayoutItem> *list, qsizetype index)
271{
272 QQStyleKitLayout *layout = qobject_cast<QQStyleKitLayout *>(list->object);
273 if (layout)
274 return layout->m_layoutItems.value(index);
275 return nullptr;
276}
277
278void QQStyleKitLayout::layoutItem_clear(QQmlListProperty<QQStyleKitLayoutItem> *list)
279{
280 QQStyleKitLayout *layout = qobject_cast<QQStyleKitLayout *>(list->object);
281 if (layout) {
282 layout->m_layoutItems.clear();
283 emit layout->layoutItemsChanged();
284 layout->scheduleUpdate();
285 }
286}
287
289{
290 return m_padding;
291}
292
294{
295 return m_contentMargins;
296}
297
298void QQStyleKitLayout::setContentMargins(const QMarginsF &margins)
299{
300 if (m_contentMargins == margins)
301 return;
302
303 m_contentMargins = margins;
304 emit contentMarginsChanged();
305 scheduleUpdate();
306}
307
309{
310 return m_spacing;
311}
312
313void QQStyleKitLayout::setSpacing(qreal spacing)
314{
315 if (qFuzzyCompare(m_spacing, spacing))
316 return;
317
318 m_spacing = spacing;
319 emit spacingChanged();
320 scheduleUpdate();
321}
322
324{
325 return m_mirrored;
326}
327
328void QQStyleKitLayout::setMirrored(bool mirrored)
329{
330 if (m_mirrored == mirrored)
331 return;
332
333 m_mirrored = mirrored;
334 emit mirroredChanged();
335 scheduleUpdate();
336}
337
339{
340 return m_implicitWidth;
341}
342
344{
345 return m_implicitHeight;
346}
347
349{
350 if (qFuzzyCompare(m_implicitWidth, width))
351 return;
352
353 m_implicitWidth = width;
354 emit implicitWidthChanged();
355 scheduleUpdate();
356}
357
359{
360 if (qFuzzyCompare(m_implicitHeight, height))
361 return;
362
363 m_implicitHeight = height;
364 emit implicitHeightChanged();
365 scheduleUpdate();
366}
367
369{
370 return m_enabled;
371}
372
373void QQStyleKitLayout::setEnabled(bool enabled)
374{
375 if (m_enabled == enabled)
376 return;
377
378 m_enabled = enabled;
379 emit enabledChanged();
380
381 if (m_enabled)
382 scheduleUpdate();
383}
384
385void QQStyleKitLayout::updateLayout()
386{
387 if (!m_enabled)
388 return;
389
390 if (!m_container || m_container->width() <= 0 || m_container->height() <= 0)
391 return;
392
393 if (m_updatingLayout)
394 return;
395 m_updatingLayout = true;
396
397 QList<QQStyleKitLayoutItem *> left;
398 QList<QQStyleKitLayoutItem *> right;
399 QList<QQStyleKitLayoutItem *> center;
400
401 for (QQStyleKitLayoutItem *li : m_layoutItems) {
402 if (!li->item() || !li->item()->isVisible())
403 continue;
404 const auto hAlign = li->alignment() & Qt::AlignHorizontal_Mask;
405 const bool isMirrored = m_mirrored && !(hAlign & Qt::AlignAbsolute);
406 switch (hAlign) {
407 case Qt::AlignLeft:
408 if (isMirrored)
409 right.append(li);
410 else
411 left.append(li);
412 break;
413 case Qt::AlignRight:
414 if (isMirrored)
415 left.append(li);
416 else
417 right.append(li);
418 break;
419 default:
420 center.append(li);
421 break;
422 }
423 }
424
425 const qreal containerWidth = m_container->width() ? m_container->width() : m_container->implicitWidth();
426 const qreal containerHeight = m_container->height() ? m_container->height() : m_container->implicitHeight();
427 const qreal paddedX = m_contentMargins.left();
428 const qreal paddedY = m_contentMargins.top();
429 const qreal paddedWidth = qMax(containerWidth - m_contentMargins.left() - m_contentMargins.right(), .0);
430 const qreal paddedHeight = qMax(containerHeight - m_contentMargins.top() - m_contentMargins.bottom(), .0);
431
432 qreal maxTopMargin = .0;
433 qreal maxBottomMargin = .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 maxTopMargin = qMax(maxTopMargin, margins.top());
453 maxBottomMargin = qMax(maxBottomMargin, margins.bottom());
454 }
455 }
456
457 // Position right-aligned items
458 {
459 qreal x = paddedX + paddedWidth;
460 for (QQStyleKitLayoutItem *li : right) {
461 QQuickItem *item = li->item();
462 if (!item || !item->isVisible())
463 continue;
464
465 const QMarginsF margins = li->margins();
466 const qreal itemWidth = width(li, paddedWidth);
467 const qreal itemHeight = height(li, paddedHeight);
468 x -= itemWidth + margins.right() + margins.left();
469 auto y = vAlignY(li, paddedY, paddedHeight);
470 li->setX(x + margins.left());
471 li->setY(y);
472 li->setWidth(itemWidth);
473 li->setHeight(itemHeight);
474 x -= m_spacing;
475 maxTopMargin = qMax(maxTopMargin, margins.top());
476 maxBottomMargin = qMax(maxBottomMargin, margins.bottom());
477 }
478 }
479
480 // Position center-aligned items
481 {
482 qreal x = paddedX + (paddedWidth - totalWidth(center, m_spacing)) / 2;
483 for (QQStyleKitLayoutItem *li : center) {
484 QQuickItem *item = li->item();
485 if (!item || !item->isVisible())
486 continue;
487
488 const QMarginsF margins = li->margins();
489 const qreal itemWidth = width(li, paddedWidth);
490 const qreal itemHeight = height(li, paddedHeight);
491 auto y = vAlignY(li, paddedY, paddedHeight);
492 li->setX(x + margins.left());
493 li->setY(y);
494 li->setWidth(itemWidth);
495 li->setHeight(itemHeight);
496 x += itemWidth + margins.left() + margins.right() + m_spacing;
497 maxTopMargin = qMax(maxTopMargin, margins.top());
498 maxBottomMargin = qMax(maxBottomMargin, margins.bottom());
499 }
500 }
501
502 const auto leftWidth = totalWidth(left, m_spacing);
503 const auto leftHeight = totalHeight(left);
504 const auto rightWidth = totalWidth(right, m_spacing);
505 const auto rightHeight = totalHeight(right);
506 const auto centerWidth = totalWidth(center, m_spacing);
507 const auto centerHeight = totalHeight(center);
508
509 const auto implicitWidth = leftWidth + rightWidth + centerWidth
510 - m_spacing * (left.isEmpty() ? 0 : 1)
511 - m_spacing * (right.isEmpty() ? 0 : 1)
512 - m_spacing * (center.isEmpty() ? 0 : 1)
513 + m_contentMargins.left() + m_contentMargins.right();
514 setImplicitWidth(implicitWidth);
515 const auto implicitHeight = qMax(qMax(leftHeight, rightHeight), centerHeight)
516 + m_contentMargins.top() + m_contentMargins.bottom();
517 setImplicitHeight(implicitHeight);
518
519 // HACK for control's contentItem
520 // QQuickControl determines the contentItem geometry based on the control size and padding
521 // So we include the layout's left/right widths into the padding calculation
522 auto leftPadding = m_contentMargins.left() + leftWidth;
523 auto topPadding = m_contentMargins.top() + maxTopMargin; // TODO: support vertical layout items
524 auto rightPadding = m_contentMargins.right() + rightWidth;
525 auto bottomPadding = m_contentMargins.bottom() + maxBottomMargin; // TODO: support vertical layout items
526 if (isMirrored())
527 std::swap(leftPadding, rightPadding);
528 QMarginsF newPadding = QMarginsF(leftPadding, topPadding, rightPadding, bottomPadding);
529 if (m_padding != newPadding) {
530 m_padding = newPadding;
531 emit paddingChanged();
532 }
533 m_updatingLayout = false;
534}
535
536void QQStyleKitLayout::scheduleUpdate()
537{
538 if (!m_updateTimer.isActive())
539 m_updateTimer.start(0);
540}
541
542QT_END_NAMESPACE
543
544#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)