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