1// Copyright (C) 2016 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
4#include "qlayout.h"
5
6#include "qapplication.h"
7#include "qlayoutengine_p.h"
8#if QT_CONFIG(menubar)
9#include "qmenubar.h"
10#endif
11#if QT_CONFIG(toolbar)
12#include "qtoolbar.h"
13#endif
14#include "qevent.h"
15#include "qstyle.h"
16#include "qvariant.h"
17#include "qwidget_p.h"
18
19QT_BEGIN_NAMESPACE
20
21inline static QRect fromLayoutItemRect(QWidgetPrivate *priv, const QRect &rect)
22{
23 return rect.adjusted(xp1: priv->leftLayoutItemMargin, yp1: priv->topLayoutItemMargin,
24 xp2: -priv->rightLayoutItemMargin, yp2: -priv->bottomLayoutItemMargin);
25}
26
27inline static QSize fromLayoutItemSize(QWidgetPrivate *priv, const QSize &size)
28{
29 return fromLayoutItemRect(priv, rect: QRect(QPoint(0, 0), size)).size();
30}
31
32inline static QRect toLayoutItemRect(QWidgetPrivate *priv, const QRect &rect)
33{
34 return rect.adjusted(xp1: -priv->leftLayoutItemMargin, yp1: -priv->topLayoutItemMargin,
35 xp2: priv->rightLayoutItemMargin, yp2: priv->bottomLayoutItemMargin);
36}
37
38inline static QSize toLayoutItemSize(QWidgetPrivate *priv, const QSize &size)
39{
40 return toLayoutItemRect(priv, rect: QRect(QPoint(0, 0), size)).size();
41}
42
43/*!
44 \class QLayoutItem
45 \brief The QLayoutItem class provides an abstract item that a
46 QLayout manipulates.
47
48 \ingroup geomanagement
49 \inmodule QtWidgets
50
51 This is used by custom layouts.
52
53 Pure virtual functions are provided to return information about
54 the layout, including, sizeHint(), minimumSize(), maximumSize()
55 and expandingDirections().
56
57 The layout's geometry can be set and retrieved with setGeometry()
58 and geometry(), and its alignment with setAlignment() and
59 alignment().
60
61 isEmpty() returns whether the layout item is empty. If the
62 concrete item is a QWidget, it can be retrieved using widget().
63 Similarly for layout() and spacerItem().
64
65 Some layouts have width and height interdependencies. These can
66 be expressed using hasHeightForWidth(), heightForWidth(), and
67 minimumHeightForWidth(). For more explanation see the \e{Qt
68 Quarterly} article
69 \l{http://doc.qt.io/archives/qq/qq04-height-for-width.html}{Trading
70 Height for Width}.
71
72 \sa QLayout
73*/
74
75/*!
76 \class QSpacerItem
77 \ingroup geomanagement
78 \brief The QSpacerItem class provides blank space in a layout.
79
80 \inmodule QtWidgets
81
82 Normally, you don't need to use this class directly. Qt's
83 built-in layout managers provide the following functions for
84 manipulating empty space in layouts:
85
86 \table
87 \header \li Class
88 \li Functions
89 \row \li QHBoxLayout
90 \li \l{QBoxLayout::addSpacing()}{addSpacing()},
91 \l{QBoxLayout::addStretch()}{addStretch()},
92 \l{QBoxLayout::insertSpacing()}{insertSpacing()},
93 \l{QBoxLayout::insertStretch()}{insertStretch()}
94 \row \li QGridLayout
95 \li \l{QGridLayout::setRowMinimumHeight()}{setRowMinimumHeight()},
96 \l{QGridLayout::setRowStretch()}{setRowStretch()},
97 \l{QGridLayout::setColumnMinimumWidth()}{setColumnMinimumWidth()},
98 \l{QGridLayout::setColumnStretch()}{setColumnStretch()}
99 \endtable
100
101 \sa QLayout, QWidgetItem, QLayoutItem::spacerItem()
102*/
103
104/*!
105 \class QWidgetItem
106 \ingroup geomanagement
107 \brief The QWidgetItem class is a layout item that represents a widget.
108
109 \inmodule QtWidgets
110
111 Normally, you don't need to use this class directly. Qt's
112 built-in layout managers provide the following functions for
113 manipulating widgets in layouts:
114
115 \table
116 \header \li Class
117 \li Functions
118 \row \li QBoxLayout
119 \li \l{QBoxLayout::addWidget()}{addWidget()},
120 \l{QBoxLayout::insertWidget()}{insertWidget()},
121 \l{QBoxLayout::setStretchFactor()}{setStretchFactor()}
122 \row \li QGridLayout
123 \li \l{QGridLayout::addWidget()}{addWidget()}
124 \row \li QStackedLayout
125 \li \l{QStackedLayout::addWidget()}{addWidget()},
126 \l{QStackedLayout::insertWidget()}{insertWidget()},
127 \l{QStackedLayout::currentWidget()}{currentWidget()},
128 \l{QStackedLayout::setCurrentWidget()}{setCurrentWidget()},
129 \l{QStackedLayout::widget()}{widget()}
130 \endtable
131
132 \sa QLayout, QSpacerItem, QLayoutItem::widget()
133*/
134
135/*!
136 \fn QLayoutItem::QLayoutItem(Qt::Alignment alignment)
137
138 Constructs a layout item with an \a alignment.
139 Not all subclasses support alignment.
140*/
141
142/*!
143 \fn Qt::Alignment QLayoutItem::alignment() const
144
145 Returns the alignment of this item.
146*/
147
148/*!
149 Sets the alignment of this item to \a alignment.
150
151 \b{Note:} Item alignment is only supported by QLayoutItem subclasses
152 where it would have a visual effect. Except for QSpacerItem, which provides
153 blank space for layouts, all public Qt classes that inherit QLayoutItem
154 support item alignment.
155*/
156void QLayoutItem::setAlignment(Qt::Alignment alignment)
157{
158 align = alignment;
159}
160
161/*!
162 \fn QSize QLayoutItem::maximumSize() const
163
164 Implemented in subclasses to return the maximum size of this item.
165*/
166
167/*!
168 \fn QSize QLayoutItem::minimumSize() const
169
170 Implemented in subclasses to return the minimum size of this item.
171*/
172
173/*!
174 \fn QSize QLayoutItem::sizeHint() const
175
176 Implemented in subclasses to return the preferred size of this item.
177*/
178
179/*!
180 \fn Qt::Orientations QLayoutItem::expandingDirections() const
181
182 Returns whether this layout item can make use of more space than
183 sizeHint(). A value of Qt::Vertical or Qt::Horizontal means that
184 it wants to grow in only one dimension, whereas Qt::Vertical |
185 Qt::Horizontal means that it wants to grow in both dimensions.
186*/
187
188/*!
189 \fn void QLayoutItem::setGeometry(const QRect &r)
190
191 Implemented in subclasses to set this item's geometry to \a r.
192
193 \sa geometry()
194*/
195
196/*!
197 \fn QRect QLayoutItem::geometry() const
198
199 Returns the rectangle covered by this layout item.
200
201 \sa setGeometry()
202*/
203
204/*!
205 \fn virtual bool QLayoutItem::isEmpty() const
206
207 Implemented in subclasses to return whether this item is empty,
208 i.e. whether it contains any widgets.
209*/
210
211/*!
212 \fn QSpacerItem::QSpacerItem(int w, int h, QSizePolicy::Policy hPolicy, QSizePolicy::Policy vPolicy)
213
214 Constructs a spacer item with preferred width \a w, preferred
215 height \a h, horizontal size policy \a hPolicy and vertical size
216 policy \a vPolicy.
217
218 The default values provide a gap that is able to stretch if
219 nothing else wants the space.
220*/
221
222/*!
223 Destructor.
224*/
225QSpacerItem::~QSpacerItem() {}
226
227/*!
228 Changes this spacer item to have preferred width \a w, preferred
229 height \a h, horizontal size policy \a hPolicy and vertical size
230 policy \a vPolicy.
231
232 The default values provide a gap that is able to stretch if
233 nothing else wants the space.
234
235 Note that if changeSize() is called after the spacer item has been added
236 to a layout, it is necessary to invalidate the layout in order for the
237 spacer item's new size to take effect.
238
239 \sa QSpacerItem::invalidate()
240*/
241void QSpacerItem::changeSize(int w, int h, QSizePolicy::Policy hPolicy,
242 QSizePolicy::Policy vPolicy)
243{
244 width = w;
245 height = h;
246 sizeP = QSizePolicy(hPolicy, vPolicy);
247}
248
249/*!
250 \fn QWidgetItem::QWidgetItem(QWidget *widget)
251
252 Creates an item containing the given \a widget.
253*/
254
255/*!
256 Destructor.
257*/
258QWidgetItem::~QWidgetItem() = default;
259
260/*!
261 Destroys the QLayoutItem.
262*/
263QLayoutItem::~QLayoutItem() = default;
264
265/*!
266 Invalidates any cached information in this layout item.
267*/
268void QLayoutItem::invalidate()
269{
270}
271
272/*!
273 If this item is a QLayout, it is returned as a QLayout; otherwise
274 \nullptr is returned. This function provides type-safe casting.
275
276 \sa spacerItem(), widget()
277*/
278QLayout *QLayoutItem::layout()
279{
280 return nullptr;
281}
282
283/*!
284 If this item is a QSpacerItem, it is returned as a QSpacerItem;
285 otherwise \nullptr is returned. This function provides type-safe casting.
286
287 \sa layout(), widget()
288*/
289QSpacerItem *QLayoutItem::spacerItem()
290{
291 return nullptr;
292}
293
294/*!
295 \reimp
296*/
297QLayout * QLayout::layout()
298{
299 return this;
300}
301
302/*!
303 Returns a pointer to this object.
304*/
305QSpacerItem * QSpacerItem::spacerItem()
306{
307 return this;
308}
309
310/*!
311 \fn QSizePolicy QSpacerItem::sizePolicy() const
312 \since 5.5
313
314 Returns the size policy of this item.
315*/
316
317/*!
318 If this item manages a QWidget, returns that widget. Otherwise,
319 \nullptr is returned.
320
321 \note While the functions layout() and spacerItem() perform casts, this
322 function returns another object: QLayout and QSpacerItem inherit QLayoutItem,
323 while QWidget does not.
324
325 \sa layout(), spacerItem()
326*/
327QWidget *QLayoutItem::widget() const
328{
329 return nullptr;
330}
331
332/*!
333 Returns the widget managed by this item.
334*/
335QWidget *QWidgetItem::widget() const
336{
337 return wid;
338}
339
340/*!
341 Returns \c true if this layout's preferred height depends on its
342 width; otherwise returns \c false. The default implementation returns
343 false.
344
345 Reimplement this function in layout managers that support height
346 for width.
347
348 \sa heightForWidth(), QWidget::heightForWidth()
349*/
350bool QLayoutItem::hasHeightForWidth() const
351{
352 return false;
353}
354
355/*!
356 Returns the minimum height this widget needs for the given width,
357 \a w. The default implementation simply returns heightForWidth(\a
358 w).
359*/
360int QLayoutItem::minimumHeightForWidth(int w) const
361{
362 return heightForWidth(w);
363}
364
365
366/*!
367 Returns the preferred height for this layout item, given the
368 width, which is not used in this default implementation.
369
370 The default implementation returns -1, indicating that the
371 preferred height is independent of the width of the item. Using
372 the function hasHeightForWidth() will typically be much faster
373 than calling this function and testing for -1.
374
375 Reimplement this function in layout managers that support height
376 for width. A typical implementation will look like this:
377 \snippet code/src_gui_kernel_qlayoutitem.cpp 0
378
379 Caching is strongly recommended; without it layout will take
380 exponential time.
381
382 \sa hasHeightForWidth()
383*/
384int QLayoutItem::heightForWidth(int /* w */) const
385{
386 return -1;
387}
388
389/*!
390 Returns the control type(s) for the layout item. For a
391 QWidgetItem, the control type comes from the widget's size
392 policy; for a QLayoutItem, the control types is derived from the
393 layout's contents.
394
395 \sa QSizePolicy::controlType()
396*/
397QSizePolicy::ControlTypes QLayoutItem::controlTypes() const
398{
399 return QSizePolicy::DefaultType;
400}
401
402/*!
403 \reimp
404*/
405void QSpacerItem::setGeometry(const QRect &r)
406{
407 rect = r;
408}
409
410/*!
411 \reimp
412*/
413void QWidgetItem::setGeometry(const QRect &rect)
414{
415 if (isEmpty())
416 return;
417
418 QRect r = !wid->testAttribute(attribute: Qt::WA_LayoutUsesWidgetRect)
419 ? fromLayoutItemRect(priv: wid->d_func(), rect)
420 : rect;
421 const QSize widgetRectSurplus = r.size() - rect.size();
422
423 /*
424 For historical reasons, this code is done using widget rect
425 coordinates, not layout item rect coordinates. However,
426 QWidgetItem's sizeHint(), maximumSize(), and heightForWidth()
427 all work in terms of layout item rect coordinates, so we have to
428 add or subtract widgetRectSurplus here and there. The code could
429 be much simpler if we did everything using layout item rect
430 coordinates and did the conversion right before the call to
431 QWidget::setGeometry().
432 */
433
434 QSize s = r.size().boundedTo(otherSize: maximumSize() + widgetRectSurplus);
435 int x = r.x();
436 int y = r.y();
437 if (align & (Qt::AlignHorizontal_Mask | Qt::AlignVertical_Mask)) {
438 QSize pref(sizeHint());
439 QSizePolicy sp = wid->sizePolicy();
440 if (sp.horizontalPolicy() == QSizePolicy::Ignored)
441 pref.setWidth(wid->sizeHint().expandedTo(otherSize: wid->minimumSize()).width());
442 if (sp.verticalPolicy() == QSizePolicy::Ignored)
443 pref.setHeight(wid->sizeHint().expandedTo(otherSize: wid->minimumSize()).height());
444 pref += widgetRectSurplus;
445 if (align & Qt::AlignHorizontal_Mask)
446 s.setWidth(qMin(a: s.width(), b: pref.width()));
447 if (align & Qt::AlignVertical_Mask) {
448 if (hasHeightForWidth())
449 s.setHeight(qMin(a: s.height(),
450 b: heightForWidth(s.width() - widgetRectSurplus.width())
451 + widgetRectSurplus.height()));
452 else
453 s.setHeight(qMin(a: s.height(), b: pref.height()));
454 }
455 }
456 Qt::Alignment alignHoriz = QStyle::visualAlignment(direction: wid->layoutDirection(), alignment: align);
457 if (alignHoriz & Qt::AlignRight)
458 x = x + (r.width() - s.width());
459 else if (!(alignHoriz & Qt::AlignLeft))
460 x = x + (r.width() - s.width()) / 2;
461
462 if (align & Qt::AlignBottom)
463 y = y + (r.height() - s.height());
464 else if (!(align & Qt::AlignTop))
465 y = y + (r.height() - s.height()) / 2;
466
467 // Make sure we don't move outside of the parent, e.g when styles demand
468 // surplus space that exceeds the available margins (f.ex macOS with QGroupBox)
469 if (x < 0) {
470 s.rwidth() += x;
471 x = 0;
472 }
473 if (y < 0) {
474 s.rheight() += y;
475 y = 0;
476 }
477
478 wid->setGeometry(ax: x, ay: y, aw: s.width(), ah: s.height());
479}
480
481/*!
482 \reimp
483*/
484QRect QSpacerItem::geometry() const
485{
486 return rect;
487}
488
489/*!
490 \reimp
491*/
492QRect QWidgetItem::geometry() const
493{
494 return !wid->testAttribute(attribute: Qt::WA_LayoutUsesWidgetRect)
495 ? toLayoutItemRect(priv: wid->d_func(), rect: wid->geometry())
496 : wid->geometry();
497}
498
499
500/*!
501 \reimp
502*/
503bool QWidgetItem::hasHeightForWidth() const
504{
505 if (isEmpty())
506 return false;
507 return wid->hasHeightForWidth();
508}
509
510/*!
511 \reimp
512*/
513int QWidgetItem::heightForWidth(int w) const
514{
515 if (isEmpty())
516 return -1;
517
518 w = !wid->testAttribute(attribute: Qt::WA_LayoutUsesWidgetRect)
519 ? fromLayoutItemSize(priv: wid->d_func(), size: QSize(w, 0)).width()
520 : w;
521
522 int hfw;
523 if (wid->layout())
524 hfw = wid->layout()->totalHeightForWidth(w);
525 else
526 hfw = wid->heightForWidth(w);
527
528 if (hfw > wid->maximumHeight())
529 hfw = wid->maximumHeight();
530 if (hfw < wid->minimumHeight())
531 hfw = wid->minimumHeight();
532
533 hfw = !wid->testAttribute(attribute: Qt::WA_LayoutUsesWidgetRect)
534 ? toLayoutItemSize(priv: wid->d_func(), size: QSize(0, hfw)).height()
535 : hfw;
536
537 if (hfw < 0)
538 hfw = 0;
539 return hfw;
540}
541
542int QWidgetItem::minimumHeightForWidth(int w) const
543{
544 if (isEmpty())
545 return -1;
546
547 w = !wid->testAttribute(attribute: Qt::WA_LayoutUsesWidgetRect)
548 ? fromLayoutItemSize(priv: wid->d_func(), size: QSize(w, 0)).width()
549 : w;
550
551 int hfw;
552 if (wid->layout())
553 hfw = wid->layout()->totalMinimumHeightForWidth(w);
554 else
555 hfw = wid->heightForWidth(w); // QWidget doesn't have minimumHeightForWidth()
556
557 if (hfw > wid->maximumHeight())
558 hfw = wid->maximumHeight();
559 if (hfw < wid->minimumHeight())
560 hfw = wid->minimumHeight();
561
562 hfw = !wid->testAttribute(attribute: Qt::WA_LayoutUsesWidgetRect)
563 ? toLayoutItemSize(priv: wid->d_func(), size: QSize(0, hfw)).height()
564 : hfw;
565
566 if (hfw < 0)
567 hfw = 0;
568 return hfw;
569
570}
571
572/*!
573 \reimp
574*/
575Qt::Orientations QSpacerItem::expandingDirections() const
576{
577 return sizeP.expandingDirections();
578}
579
580/*!
581 \reimp
582*/
583Qt::Orientations QWidgetItem::expandingDirections() const
584{
585 if (isEmpty())
586 return {};
587
588 Qt::Orientations e = wid->sizePolicy().expandingDirections();
589 /*
590 If the layout is expanding, we make the widget expanding, even if
591 its own size policy isn't expanding.
592 */
593 if (wid->layout()) {
594 if (wid->sizePolicy().horizontalPolicy() & QSizePolicy::GrowFlag
595 && (wid->layout()->expandingDirections() & Qt::Horizontal))
596 e |= Qt::Horizontal;
597 if (wid->sizePolicy().verticalPolicy() & QSizePolicy::GrowFlag
598 && (wid->layout()->expandingDirections() & Qt::Vertical))
599 e |= Qt::Vertical;
600 }
601
602 if (align & Qt::AlignHorizontal_Mask)
603 e &= ~Qt::Horizontal;
604 if (align & Qt::AlignVertical_Mask)
605 e &= ~Qt::Vertical;
606 return e;
607}
608
609/*!
610 \reimp
611*/
612QSize QSpacerItem::minimumSize() const
613{
614 return QSize(sizeP.horizontalPolicy() & QSizePolicy::ShrinkFlag ? 0 : width,
615 sizeP.verticalPolicy() & QSizePolicy::ShrinkFlag ? 0 : height);
616}
617
618/*!
619 \reimp
620*/
621QSize QWidgetItem::minimumSize() const
622{
623 if (isEmpty())
624 return QSize(0, 0);
625 return !wid->testAttribute(attribute: Qt::WA_LayoutUsesWidgetRect)
626 ? toLayoutItemSize(priv: wid->d_func(), size: qSmartMinSize(i: this))
627 : qSmartMinSize(i: this);
628}
629
630/*!
631 \reimp
632*/
633QSize QSpacerItem::maximumSize() const
634{
635 return QSize(sizeP.horizontalPolicy() & QSizePolicy::GrowFlag ? QLAYOUTSIZE_MAX : width,
636 sizeP.verticalPolicy() & QSizePolicy::GrowFlag ? QLAYOUTSIZE_MAX : height);
637}
638
639/*!
640 \reimp
641*/
642QSize QWidgetItem::maximumSize() const
643{
644 if (isEmpty()) {
645 return QSize(0, 0);
646 } else {
647 return !wid->testAttribute(attribute: Qt::WA_LayoutUsesWidgetRect)
648 ? toLayoutItemSize(priv: wid->d_func(), size: qSmartMaxSize(i: this, align))
649 : qSmartMaxSize(i: this, align);
650 }
651}
652
653/*!
654 \reimp
655*/
656QSize QSpacerItem::sizeHint() const
657{
658 return QSize(width, height);
659}
660
661/*!
662 \reimp
663*/
664QSize QWidgetItem::sizeHint() const
665{
666 QSize s(0, 0);
667 if (!isEmpty()) {
668 s = wid->sizeHint().expandedTo(otherSize: wid->minimumSizeHint());
669 s = s.boundedTo(otherSize: wid->maximumSize())
670 .expandedTo(otherSize: wid->minimumSize());
671 s = !wid->testAttribute(attribute: Qt::WA_LayoutUsesWidgetRect)
672 ? toLayoutItemSize(priv: wid->d_func(), size: s)
673 : s;
674
675 if (wid->sizePolicy().horizontalPolicy() == QSizePolicy::Ignored)
676 s.setWidth(0);
677 if (wid->sizePolicy().verticalPolicy() == QSizePolicy::Ignored)
678 s.setHeight(0);
679 }
680 return s;
681}
682
683/*!
684 Returns \c true.
685*/
686bool QSpacerItem::isEmpty() const
687{
688 return true;
689}
690
691/*!
692 Returns \c true if the widget is hidden; otherwise returns \c false.
693
694 \sa QWidget::isHidden()
695*/
696bool QWidgetItem::isEmpty() const
697{
698 return (wid->isHidden() && !wid->sizePolicy().retainSizeWhenHidden()) || wid->isWindow();
699}
700
701/*!
702 Returns the control type associated with the widget for which
703 this size policy applies.
704
705 \sa QSizePolicy::controlType()
706 */
707QSizePolicy::ControlTypes QWidgetItem::controlTypes() const
708{
709 return wid->sizePolicy().controlType();
710}
711
712/*!
713 \class QWidgetItemV2
714 \internal
715*/
716
717inline bool QWidgetItemV2::useSizeCache() const
718{
719 return wid->d_func()->widgetItem == this;
720}
721
722void QWidgetItemV2::updateCacheIfNecessary() const
723{
724 if (q_cachedMinimumSize.width() != Dirty)
725 return;
726
727 const QSize sizeHint(wid->sizeHint());
728 const QSize minimumSizeHint(wid->minimumSizeHint());
729 const QSize minimumSize(wid->minimumSize());
730 const QSize maximumSize(wid->maximumSize());
731 const QSizePolicy sizePolicy(wid->sizePolicy());
732 const QSize expandedSizeHint(sizeHint.expandedTo(otherSize: minimumSizeHint));
733
734 const QSize smartMinSize(qSmartMinSize(sizeHint, minSizeHint: minimumSizeHint, minSize: minimumSize, maxSize: maximumSize, sizePolicy));
735 const QSize smartMaxSize(qSmartMaxSize(sizeHint: expandedSizeHint, minSize: minimumSize, maxSize: maximumSize, sizePolicy, align));
736
737 const bool useLayoutItemRect = !wid->testAttribute(attribute: Qt::WA_LayoutUsesWidgetRect);
738
739 q_cachedMinimumSize = useLayoutItemRect
740 ? toLayoutItemSize(priv: wid->d_func(), size: smartMinSize)
741 : smartMinSize;
742
743 q_cachedSizeHint = expandedSizeHint;
744 q_cachedSizeHint = q_cachedSizeHint.boundedTo(otherSize: maximumSize)
745 .expandedTo(otherSize: minimumSize);
746 q_cachedSizeHint = useLayoutItemRect
747 ? toLayoutItemSize(priv: wid->d_func(), size: q_cachedSizeHint)
748 : q_cachedSizeHint;
749
750 if (wid->sizePolicy().horizontalPolicy() == QSizePolicy::Ignored)
751 q_cachedSizeHint.setWidth(0);
752 if (wid->sizePolicy().verticalPolicy() == QSizePolicy::Ignored)
753 q_cachedSizeHint.setHeight(0);
754
755 q_cachedMaximumSize = useLayoutItemRect
756 ? toLayoutItemSize(priv: wid->d_func(), size: smartMaxSize)
757 : smartMaxSize;
758}
759
760QWidgetItemV2::QWidgetItemV2(QWidget *widget)
761 : QWidgetItem(widget),
762 q_cachedMinimumSize(Dirty, Dirty),
763 q_cachedSizeHint(Dirty, Dirty),
764 q_cachedMaximumSize(Dirty, Dirty),
765 q_firstCachedHfw(0),
766 q_hfwCacheSize(0),
767 d(nullptr)
768{
769 QWidgetPrivate *wd = wid->d_func();
770 if (!wd->widgetItem)
771 wd->widgetItem = this;
772}
773
774QWidgetItemV2::~QWidgetItemV2()
775{
776 if (wid) {
777 auto *wd = static_cast<QWidgetPrivate *>(QObjectPrivate::get(o: wid));
778 if (wd->widgetItem == this)
779 wd->widgetItem = nullptr;
780 }
781}
782
783QSize QWidgetItemV2::sizeHint() const
784{
785 if (isEmpty())
786 return QSize(0, 0);
787
788 if (useSizeCache()) {
789 updateCacheIfNecessary();
790 return q_cachedSizeHint;
791 } else {
792 return QWidgetItem::sizeHint();
793 }
794}
795
796QSize QWidgetItemV2::minimumSize() const
797{
798 if (isEmpty())
799 return QSize(0, 0);
800
801 if (useSizeCache()) {
802 updateCacheIfNecessary();
803 return q_cachedMinimumSize;
804 } else {
805 return QWidgetItem::minimumSize();
806 }
807}
808
809QSize QWidgetItemV2::maximumSize() const
810{
811 if (isEmpty())
812 return QSize(0, 0);
813
814 if (useSizeCache()) {
815 updateCacheIfNecessary();
816 return q_cachedMaximumSize;
817 } else {
818 return QWidgetItem::maximumSize();
819 }
820}
821
822/*
823 The height-for-width cache is organized as a circular buffer. The entries
824
825 q_hfwCachedHfws[q_firstCachedHfw],
826 ...,
827 q_hfwCachedHfws[(q_firstCachedHfw + q_hfwCacheSize - 1) % HfwCacheMaxSize]
828
829 contain the last cached values. When the cache is full, the first entry to
830 be erased is the entry before q_hfwCachedHfws[q_firstCachedHfw]. When
831 values are looked up, we try to move q_firstCachedHfw to point to that new
832 entry (unless the cache is not full, in which case it would leave the cache
833 in a broken state), so that the most recently used entry is also the last
834 to be erased.
835*/
836
837int QWidgetItemV2::heightForWidth(int width) const
838{
839 if (isEmpty())
840 return -1;
841
842 for (int i = 0; i < q_hfwCacheSize; ++i) {
843 int offset = q_firstCachedHfw + i;
844 const QSize &size = q_cachedHfws[offset % HfwCacheMaxSize];
845 if (size.width() == width) {
846 if (q_hfwCacheSize == HfwCacheMaxSize)
847 q_firstCachedHfw = offset % HfwCacheMaxSize;
848 return size.height();
849 }
850 }
851
852 if (q_hfwCacheSize < HfwCacheMaxSize)
853 ++q_hfwCacheSize;
854 q_firstCachedHfw = (q_firstCachedHfw + HfwCacheMaxSize - 1) % HfwCacheMaxSize;
855
856 int height = QWidgetItem::heightForWidth(w: width);
857 q_cachedHfws[q_firstCachedHfw] = QSize(width, height);
858 return height;
859}
860
861QT_END_NAMESPACE
862

source code of qtbase/src/widgets/kernel/qlayoutitem.cpp