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 | |
19 | QT_BEGIN_NAMESPACE |
20 | |
21 | inline 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 | |
27 | inline static QSize fromLayoutItemSize(QWidgetPrivate *priv, const QSize &size) |
28 | { |
29 | return fromLayoutItemRect(priv, rect: QRect(QPoint(0, 0), size)).size(); |
30 | } |
31 | |
32 | inline 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 | |
38 | inline 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 | */ |
156 | void 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 | */ |
225 | QSpacerItem::~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 | */ |
241 | void 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 | */ |
258 | QWidgetItem::~QWidgetItem() = default; |
259 | |
260 | /*! |
261 | Destroys the QLayoutItem. |
262 | */ |
263 | QLayoutItem::~QLayoutItem() = default; |
264 | |
265 | /*! |
266 | Invalidates any cached information in this layout item. |
267 | */ |
268 | void 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 | */ |
278 | QLayout *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 | */ |
289 | QSpacerItem *QLayoutItem::spacerItem() |
290 | { |
291 | return nullptr; |
292 | } |
293 | |
294 | /*! |
295 | \reimp |
296 | */ |
297 | QLayout * QLayout::layout() |
298 | { |
299 | return this; |
300 | } |
301 | |
302 | /*! |
303 | Returns a pointer to this object. |
304 | */ |
305 | QSpacerItem * 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 | */ |
327 | QWidget *QLayoutItem::widget() const |
328 | { |
329 | return nullptr; |
330 | } |
331 | |
332 | /*! |
333 | Returns the widget managed by this item. |
334 | */ |
335 | QWidget *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 | */ |
350 | bool 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 | */ |
360 | int 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 | */ |
384 | int 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 | */ |
397 | QSizePolicy::ControlTypes QLayoutItem::controlTypes() const |
398 | { |
399 | return QSizePolicy::DefaultType; |
400 | } |
401 | |
402 | /*! |
403 | \reimp |
404 | */ |
405 | void QSpacerItem::setGeometry(const QRect &r) |
406 | { |
407 | rect = r; |
408 | } |
409 | |
410 | /*! |
411 | \reimp |
412 | */ |
413 | void 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 | */ |
484 | QRect QSpacerItem::geometry() const |
485 | { |
486 | return rect; |
487 | } |
488 | |
489 | /*! |
490 | \reimp |
491 | */ |
492 | QRect 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 | */ |
503 | bool QWidgetItem::hasHeightForWidth() const |
504 | { |
505 | if (isEmpty()) |
506 | return false; |
507 | return wid->hasHeightForWidth(); |
508 | } |
509 | |
510 | /*! |
511 | \reimp |
512 | */ |
513 | int 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 | |
542 | int 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 | */ |
575 | Qt::Orientations QSpacerItem::expandingDirections() const |
576 | { |
577 | return sizeP.expandingDirections(); |
578 | } |
579 | |
580 | /*! |
581 | \reimp |
582 | */ |
583 | Qt::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 | */ |
612 | QSize 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 | */ |
621 | QSize 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 | */ |
633 | QSize 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 | */ |
642 | QSize 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 | */ |
656 | QSize QSpacerItem::sizeHint() const |
657 | { |
658 | return QSize(width, height); |
659 | } |
660 | |
661 | /*! |
662 | \reimp |
663 | */ |
664 | QSize 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 | */ |
686 | bool 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 | */ |
696 | bool 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 | */ |
707 | QSizePolicy::ControlTypes QWidgetItem::controlTypes() const |
708 | { |
709 | return wid->sizePolicy().controlType(); |
710 | } |
711 | |
712 | /*! |
713 | \class QWidgetItemV2 |
714 | \internal |
715 | */ |
716 | |
717 | inline bool QWidgetItemV2::useSizeCache() const |
718 | { |
719 | return wid->d_func()->widgetItem == this; |
720 | } |
721 | |
722 | void 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 | |
760 | QWidgetItemV2::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 | |
774 | QWidgetItemV2::~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 | |
783 | QSize 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 | |
796 | QSize 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 | |
809 | QSize 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 | |
837 | int 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 | |
861 | QT_END_NAMESPACE |
862 | |