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 | #if QT_CONFIG(sizegrip) |
15 | #include "qsizegrip.h" |
16 | #endif |
17 | #include "qevent.h" |
18 | #include "qstyle.h" |
19 | #include "qvariant.h" |
20 | #include "qwidget_p.h" |
21 | #include "qlayout_p.h" |
22 | |
23 | QT_BEGIN_NAMESPACE |
24 | |
25 | static int menuBarHeightForWidth(QWidget *menubar, int w) |
26 | { |
27 | if (menubar && !menubar->isHidden() && !menubar->isWindow()) { |
28 | int result = menubar->heightForWidth(qMax(a: w, b: menubar->minimumWidth())); |
29 | if (result == -1) |
30 | result = menubar->sizeHint().height(); |
31 | const int min = qSmartMinSize(w: menubar).height(); |
32 | result = qBound(min, val: result, max: menubar->maximumSize().height()); |
33 | if (result != -1) |
34 | return result; |
35 | } |
36 | return 0; |
37 | } |
38 | |
39 | /*! |
40 | \class QLayout |
41 | \brief The QLayout class is the base class of geometry managers. |
42 | |
43 | \ingroup geomanagement |
44 | \inmodule QtWidgets |
45 | |
46 | This is an abstract base class inherited by the concrete classes |
47 | QBoxLayout, QGridLayout, QFormLayout, and QStackedLayout. |
48 | |
49 | For users of QLayout subclasses or of QMainWindow there is seldom |
50 | any need to use the basic functions provided by QLayout, such as |
51 | setSizeConstraint() or setMenuBar(). See \l{Layout Management} |
52 | for more information. |
53 | |
54 | To make your own layout manager, implement the functions |
55 | addItem(), sizeHint(), setGeometry(), itemAt() and takeAt(). You |
56 | should also implement minimumSize() to ensure your layout isn't |
57 | resized to zero size if there is too little space. To support |
58 | children whose heights depend on their widths, implement |
59 | hasHeightForWidth() and heightForWidth(). See the |
60 | \l{layouts/flowlayout}{Flow Layout} example for |
61 | more information about implementing custom layout managers. |
62 | |
63 | Geometry management stops when the layout manager is deleted. |
64 | |
65 | \sa QLayoutItem, {Layout Management}, {Basic Layouts Example}, |
66 | {Flow Layout Example} |
67 | */ |
68 | |
69 | |
70 | /*! |
71 | Constructs a new top-level QLayout, with parent \a parent. |
72 | |
73 | The layout is set directly as the top-level layout for |
74 | \a parent. There can be only one top-level layout for a |
75 | widget. It is returned by QWidget::layout(). |
76 | |
77 | If \a parent is \nullptr, then you must insert this layout |
78 | into another layout, or set it as a widget's layout using |
79 | QWidget::setLayout(). |
80 | |
81 | \sa QWidget::setLayout() |
82 | */ |
83 | QLayout::QLayout(QWidget *parent) |
84 | : QObject(*new QLayoutPrivate, parent) |
85 | { |
86 | if (!parent) |
87 | return; |
88 | parent->setLayout(this); |
89 | } |
90 | |
91 | /*! \internal |
92 | */ |
93 | QLayout::QLayout(QLayoutPrivate &dd, QLayout *lay, QWidget *w) |
94 | : QObject(dd, lay ? static_cast<QObject*>(lay) : static_cast<QObject*>(w)) |
95 | { |
96 | Q_D(QLayout); |
97 | if (lay) { |
98 | lay->addItem(this); |
99 | } else if (w) { |
100 | if (Q_UNLIKELY(w->layout())) { |
101 | qWarning(msg: "QLayout: Attempting to add QLayout \"%ls\" to %s \"%ls\", which" |
102 | " already has a layout", |
103 | qUtf16Printable(QObject::objectName()), w->metaObject()->className(), |
104 | qUtf16Printable(w->objectName())); |
105 | setParent(nullptr); |
106 | } else { |
107 | d->topLevel = true; |
108 | w->d_func()->layout = this; |
109 | QT_TRY { |
110 | invalidate(); |
111 | } QT_CATCH(...) { |
112 | w->d_func()->layout = nullptr; |
113 | QT_RETHROW; |
114 | } |
115 | } |
116 | } |
117 | } |
118 | |
119 | QLayoutPrivate::QLayoutPrivate() |
120 | : QObjectPrivate(), insideSpacing(-1), userLeftMargin(-1), userTopMargin(-1), userRightMargin(-1), |
121 | userBottomMargin(-1), topLevel(false), enabled(true), activated(true), autoNewChild(false), |
122 | constraint(QLayout::SetDefaultConstraint), menubar(nullptr) |
123 | { |
124 | } |
125 | |
126 | void QLayoutPrivate::getMargin(int *result, int userMargin, QStyle::PixelMetric pm) const |
127 | { |
128 | if (!result) |
129 | return; |
130 | |
131 | Q_Q(const QLayout); |
132 | if (userMargin >= 0) { |
133 | *result = userMargin; |
134 | } else if (!topLevel) { |
135 | *result = 0; |
136 | } else if (QWidget *pw = q->parentWidget()) { |
137 | *result = pw->style()->pixelMetric(metric: pm, option: nullptr, widget: pw); |
138 | } else { |
139 | *result = 0; |
140 | } |
141 | } |
142 | |
143 | // Static item factory functions that allow for hooking things in Designer |
144 | |
145 | QLayoutPrivate::QWidgetItemFactoryMethod QLayoutPrivate::widgetItemFactoryMethod = nullptr; |
146 | QLayoutPrivate::QSpacerItemFactoryMethod QLayoutPrivate::spacerItemFactoryMethod = nullptr; |
147 | |
148 | QWidgetItem *QLayoutPrivate::createWidgetItem(const QLayout *layout, QWidget *widget) |
149 | { |
150 | if (widgetItemFactoryMethod) |
151 | if (QWidgetItem *wi = (*widgetItemFactoryMethod)(layout, widget)) |
152 | return wi; |
153 | return new QWidgetItemV2(widget); |
154 | } |
155 | |
156 | QSpacerItem *QLayoutPrivate::createSpacerItem(const QLayout *layout, int w, int h, QSizePolicy::Policy hPolicy, QSizePolicy::Policy vPolicy) |
157 | { |
158 | if (spacerItemFactoryMethod) |
159 | if (QSpacerItem *si = (*spacerItemFactoryMethod)(layout, w, h, hPolicy, vPolicy)) |
160 | return si; |
161 | return new QSpacerItem(w, h, hPolicy, vPolicy); |
162 | } |
163 | |
164 | |
165 | |
166 | /*! |
167 | \fn void QLayout::addItem(QLayoutItem *item) |
168 | |
169 | Implemented in subclasses to add an \a item. How it is added is |
170 | specific to each subclass. |
171 | |
172 | This function is not usually called in application code. To add a widget |
173 | to a layout, use the addWidget() function; to add a child layout, use the |
174 | addLayout() function provided by the relevant QLayout subclass. |
175 | |
176 | \b{Note:} The ownership of \a item is transferred to the layout, and it's |
177 | the layout's responsibility to delete it. |
178 | |
179 | \sa addWidget(), QBoxLayout::addLayout(), QGridLayout::addLayout() |
180 | */ |
181 | |
182 | /*! |
183 | Adds widget \a w to this layout in a manner specific to the |
184 | layout. This function uses addItem(). |
185 | */ |
186 | void QLayout::addWidget(QWidget *w) |
187 | { |
188 | addChildWidget(w); |
189 | addItem(QLayoutPrivate::createWidgetItem(layout: this, widget: w)); |
190 | } |
191 | |
192 | |
193 | |
194 | /*! |
195 | Sets the alignment for widget \a w to \a alignment and returns |
196 | true if \a w is found in this layout (not including child |
197 | layouts); otherwise returns \c false. |
198 | */ |
199 | bool QLayout::setAlignment(QWidget *w, Qt::Alignment alignment) |
200 | { |
201 | int i = 0; |
202 | QLayoutItem *item = itemAt(index: i); |
203 | while (item) { |
204 | if (item->widget() == w) { |
205 | item->setAlignment(alignment); |
206 | invalidate(); |
207 | return true; |
208 | } |
209 | ++i; |
210 | item = itemAt(index: i); |
211 | } |
212 | return false; |
213 | } |
214 | |
215 | /*! |
216 | \overload |
217 | |
218 | Sets the alignment for the layout \a l to \a alignment and |
219 | returns \c true if \a l is found in this layout (not including child |
220 | layouts); otherwise returns \c false. |
221 | */ |
222 | bool QLayout::setAlignment(QLayout *l, Qt::Alignment alignment) |
223 | { |
224 | int i = 0; |
225 | QLayoutItem *item = itemAt(index: i); |
226 | while (item) { |
227 | if (item->layout() == l) { |
228 | item->setAlignment(alignment); |
229 | invalidate(); |
230 | return true; |
231 | } |
232 | ++i; |
233 | item = itemAt(index: i); |
234 | } |
235 | return false; |
236 | } |
237 | |
238 | /*! |
239 | \property QLayout::spacing |
240 | \brief the spacing between widgets inside the layout |
241 | |
242 | If no value is explicitly set, the layout's spacing is inherited |
243 | from the parent layout, or from the style settings for the parent |
244 | widget. |
245 | |
246 | For QGridLayout and QFormLayout, it is possible to set different horizontal and |
247 | vertical spacings using \l{QGridLayout::}{setHorizontalSpacing()} |
248 | and \l{QGridLayout::}{setVerticalSpacing()}. In that case, |
249 | spacing() returns -1. |
250 | |
251 | \sa contentsRect(), getContentsMargins(), QStyle::layoutSpacing(), |
252 | QStyle::pixelMetric() |
253 | */ |
254 | |
255 | int QLayout::spacing() const |
256 | { |
257 | Q_D(const QLayout); |
258 | if (d->insideSpacing >=0) { |
259 | return d->insideSpacing; |
260 | } else { |
261 | // arbitrarily prefer horizontal spacing to vertical spacing |
262 | return qSmartSpacing(layout: this, pm: QStyle::PM_LayoutHorizontalSpacing); |
263 | } |
264 | } |
265 | |
266 | void QLayout::setSpacing(int spacing) |
267 | { |
268 | Q_D(QLayout); |
269 | d->insideSpacing = spacing; |
270 | invalidate(); |
271 | } |
272 | |
273 | /*! |
274 | \since 4.3 |
275 | |
276 | Sets the \a left, \a top, \a right, and \a bottom margins to use |
277 | around the layout. |
278 | |
279 | By default, QLayout uses the values provided by the style. On |
280 | most platforms, the margin is 11 pixels in all directions. |
281 | |
282 | \sa getContentsMargins(), QStyle::pixelMetric(), |
283 | {QStyle::}{PM_LayoutLeftMargin}, |
284 | {QStyle::}{PM_LayoutTopMargin}, |
285 | {QStyle::}{PM_LayoutRightMargin}, |
286 | {QStyle::}{PM_LayoutBottomMargin} |
287 | */ |
288 | void QLayout::setContentsMargins(int left, int top, int right, int bottom) |
289 | { |
290 | Q_D(QLayout); |
291 | |
292 | if (d->userLeftMargin == left && d->userTopMargin == top && |
293 | d->userRightMargin == right && d->userBottomMargin == bottom) |
294 | return; |
295 | |
296 | d->userLeftMargin = left; |
297 | d->userTopMargin = top; |
298 | d->userRightMargin = right; |
299 | d->userBottomMargin = bottom; |
300 | invalidate(); |
301 | } |
302 | |
303 | /*! |
304 | \since 4.6 |
305 | |
306 | Sets the \a margins to use around the layout. |
307 | |
308 | By default, QLayout uses the values provided by the style. On |
309 | most platforms, the margin is 11 pixels in all directions. |
310 | |
311 | \sa contentsMargins() |
312 | */ |
313 | void QLayout::setContentsMargins(const QMargins &margins) |
314 | { |
315 | setContentsMargins(left: margins.left(), top: margins.top(), right: margins.right(), bottom: margins.bottom()); |
316 | } |
317 | |
318 | /*! |
319 | \since 6.1 |
320 | |
321 | Unsets any user-defined margins around the layout. The layout will |
322 | use the default values provided by the style. |
323 | |
324 | \sa setContentsMargins() |
325 | */ |
326 | void QLayout::unsetContentsMargins() |
327 | { |
328 | setContentsMargins(left: -1, top: -1, right: -1, bottom: -1); |
329 | } |
330 | |
331 | /*! |
332 | \since 4.3 |
333 | |
334 | For each of \a left, \a top, \a right and \a bottom that is not |
335 | \nullptr, stores the size of the margin named in the location the |
336 | pointer refers to. |
337 | |
338 | By default, QLayout uses the values provided by the style. On |
339 | most platforms, the margin is 11 pixels in all directions. |
340 | |
341 | \sa setContentsMargins(), QStyle::pixelMetric(), |
342 | {QStyle::}{PM_LayoutLeftMargin}, |
343 | {QStyle::}{PM_LayoutTopMargin}, |
344 | {QStyle::}{PM_LayoutRightMargin}, |
345 | {QStyle::}{PM_LayoutBottomMargin} |
346 | */ |
347 | void QLayout::getContentsMargins(int *left, int *top, int *right, int *bottom) const |
348 | { |
349 | Q_D(const QLayout); |
350 | d->getMargin(result: left, userMargin: d->userLeftMargin, pm: QStyle::PM_LayoutLeftMargin); |
351 | d->getMargin(result: top, userMargin: d->userTopMargin, pm: QStyle::PM_LayoutTopMargin); |
352 | d->getMargin(result: right, userMargin: d->userRightMargin, pm: QStyle::PM_LayoutRightMargin); |
353 | d->getMargin(result: bottom, userMargin: d->userBottomMargin, pm: QStyle::PM_LayoutBottomMargin); |
354 | } |
355 | |
356 | /*! |
357 | \since 4.6 |
358 | |
359 | Returns the margins used around the layout. |
360 | |
361 | By default, QLayout uses the values provided by the style. On |
362 | most platforms, the margin is 11 pixels in all directions. |
363 | |
364 | \sa setContentsMargins() |
365 | */ |
366 | QMargins QLayout::contentsMargins() const |
367 | { |
368 | int left, top, right, bottom; |
369 | getContentsMargins(left: &left, top: &top, right: &right, bottom: &bottom); |
370 | return QMargins(left, top, right, bottom); |
371 | } |
372 | |
373 | /*! |
374 | \since 4.3 |
375 | |
376 | Returns the layout's geometry() rectangle, but taking into account the |
377 | contents margins. |
378 | |
379 | \sa setContentsMargins(), getContentsMargins() |
380 | */ |
381 | QRect QLayout::contentsRect() const |
382 | { |
383 | Q_D(const QLayout); |
384 | int left, top, right, bottom; |
385 | getContentsMargins(left: &left, top: &top, right: &right, bottom: &bottom); |
386 | return d->rect.adjusted(xp1: +left, yp1: +top, xp2: -right, yp2: -bottom); |
387 | } |
388 | |
389 | |
390 | /*! |
391 | Returns the parent widget of this layout, or \nullptr if this |
392 | layout is not installed on any widget. |
393 | |
394 | If the layout is a sub-layout, this function returns the parent |
395 | widget of the parent layout. |
396 | |
397 | \sa parent() |
398 | */ |
399 | QWidget *QLayout::parentWidget() const |
400 | { |
401 | Q_D(const QLayout); |
402 | if (!d->topLevel) { |
403 | if (parent()) { |
404 | QLayout *parentLayout = qobject_cast<QLayout*>(object: parent()); |
405 | if (Q_UNLIKELY(!parentLayout)) { |
406 | qWarning(msg: "QLayout::parentWidget: A layout can only have another layout as a parent."); |
407 | return nullptr; |
408 | } |
409 | return parentLayout->parentWidget(); |
410 | } else { |
411 | return nullptr; |
412 | } |
413 | } else { |
414 | Q_ASSERT(parent() && parent()->isWidgetType()); |
415 | return static_cast<QWidget *>(parent()); |
416 | } |
417 | } |
418 | |
419 | /*! |
420 | \reimp |
421 | */ |
422 | bool QLayout::isEmpty() const |
423 | { |
424 | int i = 0; |
425 | QLayoutItem *item = itemAt(index: i); |
426 | while (item) { |
427 | if (!item->isEmpty()) |
428 | return false; |
429 | ++i; |
430 | item = itemAt(index: i); |
431 | } |
432 | return true; |
433 | } |
434 | |
435 | /*! |
436 | \reimp |
437 | */ |
438 | QSizePolicy::ControlTypes QLayout::controlTypes() const |
439 | { |
440 | if (count() == 0) |
441 | return QSizePolicy::DefaultType; |
442 | QSizePolicy::ControlTypes types; |
443 | for (int i = count() - 1; i >= 0; --i) |
444 | types |= itemAt(index: i)->controlTypes(); |
445 | return types; |
446 | } |
447 | |
448 | /*! |
449 | \reimp |
450 | */ |
451 | void QLayout::setGeometry(const QRect &r) |
452 | { |
453 | Q_D(QLayout); |
454 | d->rect = r; |
455 | } |
456 | |
457 | /*! |
458 | \reimp |
459 | */ |
460 | QRect QLayout::geometry() const |
461 | { |
462 | Q_D(const QLayout); |
463 | return d->rect; |
464 | } |
465 | |
466 | /*! |
467 | \reimp |
468 | */ |
469 | void QLayout::invalidate() |
470 | { |
471 | Q_D(QLayout); |
472 | d->rect = QRect(); |
473 | update(); |
474 | } |
475 | |
476 | static bool removeWidgetRecursively(QLayoutItem *li, QObject *w) |
477 | { |
478 | QLayout *lay = li->layout(); |
479 | if (!lay) |
480 | return false; |
481 | int i = 0; |
482 | QLayoutItem *child; |
483 | while ((child = lay->itemAt(index: i))) { |
484 | if (child->widget() == w) { |
485 | delete lay->takeAt(index: i); |
486 | lay->invalidate(); |
487 | return true; |
488 | } else if (removeWidgetRecursively(li: child, w)) { |
489 | return true; |
490 | } else { |
491 | ++i; |
492 | } |
493 | } |
494 | return false; |
495 | } |
496 | |
497 | |
498 | void QLayoutPrivate::doResize() |
499 | { |
500 | Q_Q(QLayout); |
501 | QWidget *mw = q->parentWidget(); |
502 | QRect rect = mw->testAttribute(attribute: Qt::WA_LayoutOnEntireRect) ? mw->rect() : mw->contentsRect(); |
503 | const int mbh = menuBarHeightForWidth(menubar, w: rect.width()); |
504 | const int mbTop = rect.top(); |
505 | rect.setTop(mbTop + mbh); |
506 | q->setGeometry(rect); |
507 | #if QT_CONFIG(menubar) |
508 | if (menubar) |
509 | menubar->setGeometry(ax: rect.left(), ay: mbTop, aw: rect.width(), ah: mbh); |
510 | #endif |
511 | } |
512 | |
513 | |
514 | /*! |
515 | \internal |
516 | Performs child widget layout when the parent widget is |
517 | resized. Also handles removal of widgets. \a e is the |
518 | event |
519 | */ |
520 | void QLayout::widgetEvent(QEvent *e) |
521 | { |
522 | Q_D(QLayout); |
523 | const QEvent::Type type = e->type(); |
524 | if (!d->enabled && type != QEvent::ChildRemoved) |
525 | return; |
526 | |
527 | switch (type) { |
528 | case QEvent::Resize: |
529 | if (d->activated) |
530 | d->doResize(); |
531 | else |
532 | activate(); |
533 | break; |
534 | case QEvent::ChildRemoved: |
535 | { |
536 | QChildEvent *c = (QChildEvent *)e; |
537 | QObject *child = c->child(); |
538 | QObjectPrivate *op = QObjectPrivate::get(o: child); |
539 | if (op->wasWidget) { |
540 | #if QT_CONFIG(menubar) |
541 | if (child == d->menubar) |
542 | d->menubar = nullptr; |
543 | #endif |
544 | removeWidgetRecursively(li: this, w: child); |
545 | } |
546 | } |
547 | break; |
548 | case QEvent::LayoutRequest: |
549 | if (static_cast<QWidget *>(parent())->isVisible()) |
550 | activate(); |
551 | break; |
552 | default: |
553 | break; |
554 | } |
555 | } |
556 | |
557 | /*! |
558 | \reimp |
559 | */ |
560 | void QLayout::childEvent(QChildEvent *e) |
561 | { |
562 | Q_D(QLayout); |
563 | if (!d->enabled) |
564 | return; |
565 | |
566 | if (e->type() != QEvent::ChildRemoved) |
567 | return; |
568 | |
569 | if (QLayout *childLayout = qobject_cast<QLayout *>(object: e->child())) |
570 | removeItem(childLayout); |
571 | } |
572 | |
573 | /*! |
574 | \internal |
575 | Also takes contentsMargins and menu bar into account. |
576 | */ |
577 | int QLayout::totalMinimumHeightForWidth(int w) const |
578 | { |
579 | Q_D(const QLayout); |
580 | int side=0, top=0; |
581 | if (d->topLevel) { |
582 | QWidget *parent = parentWidget(); |
583 | parent->ensurePolished(); |
584 | QWidgetPrivate *wd = parent->d_func(); |
585 | side += wd->leftmargin + wd->rightmargin; |
586 | top += wd->topmargin + wd->bottommargin; |
587 | } |
588 | int h = minimumHeightForWidth(w - side) + top; |
589 | #if QT_CONFIG(menubar) |
590 | h += menuBarHeightForWidth(menubar: d->menubar, w); |
591 | #endif |
592 | return h; |
593 | } |
594 | |
595 | /*! |
596 | \internal |
597 | Also takes contentsMargins and menu bar into account. |
598 | */ |
599 | int QLayout::totalHeightForWidth(int w) const |
600 | { |
601 | Q_D(const QLayout); |
602 | int side=0, top=0; |
603 | if (d->topLevel) { |
604 | QWidget *parent = parentWidget(); |
605 | parent->ensurePolished(); |
606 | QWidgetPrivate *wd = parent->d_func(); |
607 | side += wd->leftmargin + wd->rightmargin; |
608 | top += wd->topmargin + wd->bottommargin; |
609 | } |
610 | int h = heightForWidth(w - side) + top; |
611 | #if QT_CONFIG(menubar) |
612 | h += menuBarHeightForWidth(menubar: d->menubar, w); |
613 | #endif |
614 | return h; |
615 | } |
616 | |
617 | /*! |
618 | \internal |
619 | Also takes contentsMargins and menu bar into account. |
620 | */ |
621 | QSize QLayout::totalMinimumSize() const |
622 | { |
623 | Q_D(const QLayout); |
624 | int side=0, top=0; |
625 | if (d->topLevel) { |
626 | QWidget *pw = parentWidget(); |
627 | pw->ensurePolished(); |
628 | QWidgetPrivate *wd = pw->d_func(); |
629 | side += wd->leftmargin + wd->rightmargin; |
630 | top += wd->topmargin + wd->bottommargin; |
631 | } |
632 | |
633 | QSize s = minimumSize(); |
634 | #if QT_CONFIG(menubar) |
635 | top += menuBarHeightForWidth(menubar: d->menubar, w: s.width() + side); |
636 | #endif |
637 | return s + QSize(side, top); |
638 | } |
639 | |
640 | /*! |
641 | \internal |
642 | Also takes contentsMargins and menu bar into account. |
643 | */ |
644 | QSize QLayout::totalSizeHint() const |
645 | { |
646 | Q_D(const QLayout); |
647 | int side=0, top=0; |
648 | if (d->topLevel) { |
649 | QWidget *pw = parentWidget(); |
650 | pw->ensurePolished(); |
651 | QWidgetPrivate *wd = pw->d_func(); |
652 | side += wd->leftmargin + wd->rightmargin; |
653 | top += wd->topmargin + wd->bottommargin; |
654 | } |
655 | |
656 | QSize s = sizeHint(); |
657 | if (hasHeightForWidth()) |
658 | s.setHeight(heightForWidth(s.width() + side)); |
659 | #if QT_CONFIG(menubar) |
660 | top += menuBarHeightForWidth(menubar: d->menubar, w: s.width()); |
661 | #endif |
662 | return s + QSize(side, top); |
663 | } |
664 | |
665 | /*! |
666 | \internal |
667 | Also takes contentsMargins and menu bar into account. |
668 | */ |
669 | QSize QLayout::totalMaximumSize() const |
670 | { |
671 | Q_D(const QLayout); |
672 | int side=0, top=0; |
673 | if (d->topLevel) { |
674 | QWidget *pw = parentWidget(); |
675 | pw->ensurePolished(); |
676 | QWidgetPrivate *wd = pw->d_func(); |
677 | side += wd->leftmargin + wd->rightmargin; |
678 | top += wd->topmargin + wd->bottommargin; |
679 | } |
680 | |
681 | QSize s = maximumSize(); |
682 | #if QT_CONFIG(menubar) |
683 | top += menuBarHeightForWidth(menubar: d->menubar, w: s.width()); |
684 | #endif |
685 | |
686 | if (d->topLevel) |
687 | s = QSize(qMin(a: s.width() + side, b: QLAYOUTSIZE_MAX), |
688 | qMin(a: s.height() + top, b: QLAYOUTSIZE_MAX)); |
689 | return s; |
690 | } |
691 | |
692 | /*! |
693 | \internal |
694 | Destroys the layout, deleting all child layouts. |
695 | Geometry management stops when a top-level layout is deleted. |
696 | |
697 | The layout classes will probably be fatally confused if you delete |
698 | a sublayout. |
699 | */ |
700 | QLayout::~QLayout() |
701 | { |
702 | Q_D(QLayout); |
703 | if (d->topLevel && parent() && parent()->isWidgetType() && parentWidget()->layout() == this) |
704 | parentWidget()->d_func()->layout = nullptr; |
705 | else if (QLayout *parentLayout = qobject_cast<QLayout *>(object: parent())) |
706 | parentLayout->removeItem(this); |
707 | } |
708 | |
709 | |
710 | /*! |
711 | This function is called from \c addLayout() or \c insertLayout() functions in |
712 | subclasses to add layout \a childLayout as a sub-layout. |
713 | |
714 | The only scenario in which you need to call it directly is if you |
715 | implement a custom layout that supports nested layouts. |
716 | |
717 | \sa QBoxLayout::addLayout(), QBoxLayout::insertLayout(), QGridLayout::addLayout() |
718 | */ |
719 | void QLayout::addChildLayout(QLayout *childLayout) |
720 | { |
721 | if (Q_UNLIKELY(childLayout->parent())) { |
722 | qWarning(msg: "QLayout::addChildLayout: layout %s \"%ls\" already has a parent", |
723 | childLayout->metaObject()->className(), qUtf16Printable(childLayout->objectName())); |
724 | return; |
725 | } |
726 | childLayout->setParent(this); |
727 | |
728 | if (QWidget *mw = parentWidget()) { |
729 | childLayout->d_func()->reparentChildWidgets(mw); |
730 | } |
731 | |
732 | } |
733 | |
734 | /*! |
735 | \internal |
736 | */ |
737 | bool QLayout::adoptLayout(QLayout *layout) |
738 | { |
739 | const bool ok = !layout->parent(); |
740 | addChildLayout(childLayout: layout); |
741 | return ok; |
742 | } |
743 | |
744 | #ifdef QT_DEBUG |
745 | static bool layoutDebug() |
746 | { |
747 | static int checked_env = -1; |
748 | if (checked_env == -1) |
749 | checked_env = !!qEnvironmentVariableIntValue(varName: "QT_LAYOUT_DEBUG"); |
750 | |
751 | return checked_env; |
752 | } |
753 | #endif |
754 | |
755 | void QLayoutPrivate::reparentChildWidgets(QWidget *mw) |
756 | { |
757 | Q_Q(QLayout); |
758 | int n = q->count(); |
759 | |
760 | #if QT_CONFIG(menubar) |
761 | if (menubar && menubar->parentWidget() != mw) { |
762 | menubar->setParent(mw); |
763 | } |
764 | #endif |
765 | bool mwVisible = mw && mw->isVisible(); |
766 | for (int i = 0; i < n; ++i) { |
767 | QLayoutItem *item = q->itemAt(index: i); |
768 | if (QWidget *w = item->widget()) { |
769 | QWidget *pw = w->parentWidget(); |
770 | #ifdef QT_DEBUG |
771 | if (Q_UNLIKELY(pw && pw != mw && layoutDebug())) { |
772 | qWarning(msg: "QLayout::addChildLayout: widget %s \"%ls\" in wrong parent; moved to correct parent", |
773 | w->metaObject()->className(), qUtf16Printable(w->objectName())); |
774 | } |
775 | #endif |
776 | bool needShow = mwVisible && !QWidgetPrivate::get(w)->isExplicitlyHidden(); |
777 | if (pw != mw) |
778 | w->setParent(mw); |
779 | if (needShow) |
780 | QMetaObject::invokeMethod(obj: w, member: "_q_showIfNotHidden", c: Qt::QueuedConnection); //show later |
781 | } else if (QLayout *l = item->layout()) { |
782 | l->d_func()->reparentChildWidgets(mw); |
783 | } |
784 | } |
785 | } |
786 | |
787 | /*! |
788 | Returns \c true if the \a widget can be added to the \a layout; |
789 | otherwise returns \c false. |
790 | */ |
791 | bool QLayoutPrivate::checkWidget(QWidget *widget) const |
792 | { |
793 | Q_Q(const QLayout); |
794 | if (Q_UNLIKELY(!widget)) { |
795 | qWarning(msg: "QLayout: Cannot add a null widget to %s/%ls", q->metaObject()->className(), |
796 | qUtf16Printable(q->objectName())); |
797 | return false; |
798 | } |
799 | if (Q_UNLIKELY(widget == q->parentWidget())) { |
800 | qWarning(msg: "QLayout: Cannot add parent widget %s/%ls to its child layout %s/%ls", |
801 | widget->metaObject()->className(), qUtf16Printable(widget->objectName()), |
802 | q->metaObject()->className(), qUtf16Printable(q->objectName())); |
803 | return false; |
804 | } |
805 | return true; |
806 | } |
807 | |
808 | /*! |
809 | Returns \c true if the \a otherLayout can be added to the \a layout; |
810 | otherwise returns \c false. |
811 | */ |
812 | bool QLayoutPrivate::checkLayout(QLayout *otherLayout) const |
813 | { |
814 | Q_Q(const QLayout); |
815 | if (Q_UNLIKELY(!otherLayout)) { |
816 | qWarning(msg: "QLayout: Cannot add a null layout to %s/%ls", |
817 | q->metaObject()->className(), qUtf16Printable(q->objectName())); |
818 | return false; |
819 | } |
820 | if (Q_UNLIKELY(otherLayout == q)) { |
821 | qWarning(msg: "QLayout: Cannot add layout %s/%ls to itself", |
822 | q->metaObject()->className(), qUtf16Printable(q->objectName())); |
823 | return false; |
824 | } |
825 | return true; |
826 | } |
827 | |
828 | /*! |
829 | This function is called from \c addWidget() functions in |
830 | subclasses to add \a w as a managed widget of a layout. |
831 | |
832 | If \a w is already managed by a layout, this function will produce |
833 | a warning, and remove \a w from that layout. This function must |
834 | therefore be called before adding \a w to the layout's data |
835 | structure. |
836 | */ |
837 | void QLayout::addChildWidget(QWidget *w) |
838 | { |
839 | QWidget *mw = parentWidget(); |
840 | QWidget *pw = w->parentWidget(); |
841 | |
842 | //Qt::WA_LaidOut is never reset. It only means that the widget at some point has |
843 | //been in a layout. |
844 | if (pw && w->testAttribute(attribute: Qt::WA_LaidOut)) { |
845 | QLayout *l = pw->layout(); |
846 | if (l && removeWidgetRecursively(li: l, w)) { |
847 | #ifdef QT_DEBUG |
848 | if (Q_UNLIKELY(layoutDebug())) |
849 | qWarning(msg: "QLayout::addChildWidget: %s \"%ls\" is already in a layout; moved to new layout", |
850 | w->metaObject()->className(), qUtf16Printable(w->objectName())); |
851 | #endif |
852 | } |
853 | } |
854 | if (pw && mw && pw != mw) { |
855 | #ifdef QT_DEBUG |
856 | if (Q_UNLIKELY(layoutDebug())) |
857 | qWarning(msg: "QLayout::addChildWidget: %s \"%ls\" in wrong parent; moved to correct parent", |
858 | w->metaObject()->className(), qUtf16Printable(w->objectName())); |
859 | #endif |
860 | pw = nullptr; |
861 | } |
862 | bool needShow = mw && mw->isVisible() && !QWidgetPrivate::get(w)->isExplicitlyHidden(); |
863 | if (!pw && mw) |
864 | w->setParent(mw); |
865 | w->setAttribute(Qt::WA_LaidOut); |
866 | if (needShow) |
867 | QMetaObject::invokeMethod(obj: w, member: "_q_showIfNotHidden", c: Qt::QueuedConnection); //show later |
868 | } |
869 | |
870 | |
871 | |
872 | |
873 | |
874 | |
875 | |
876 | |
877 | /*! |
878 | Tells the geometry manager to place the menu bar \a widget at the |
879 | top of parentWidget(), outside QWidget::contentsMargins(). All |
880 | child widgets are placed below the bottom edge of the menu bar. |
881 | */ |
882 | void QLayout::setMenuBar(QWidget *widget) |
883 | { |
884 | Q_D(QLayout); |
885 | if (widget) |
886 | addChildWidget(w: widget); |
887 | d->menubar = widget; |
888 | } |
889 | |
890 | /*! |
891 | Returns the menu bar set for this layout, or \nullptr if no |
892 | menu bar is set. |
893 | */ |
894 | |
895 | QWidget *QLayout::menuBar() const |
896 | { |
897 | Q_D(const QLayout); |
898 | return d->menubar; |
899 | } |
900 | |
901 | |
902 | /*! |
903 | Returns the minimum size of this layout. This is the smallest |
904 | size that the layout can have while still respecting the |
905 | specifications. |
906 | |
907 | The returned value doesn't include the space required by |
908 | QWidget::setContentsMargins() or menuBar(). |
909 | |
910 | The default implementation allows unlimited resizing. |
911 | */ |
912 | QSize QLayout::minimumSize() const |
913 | { |
914 | return QSize(0, 0); |
915 | } |
916 | |
917 | /*! |
918 | Returns the maximum size of this layout. This is the largest size |
919 | that the layout can have while still respecting the |
920 | specifications. |
921 | |
922 | The returned value doesn't include the space required by |
923 | QWidget::setContentsMargins() or menuBar(). |
924 | |
925 | The default implementation allows unlimited resizing. |
926 | */ |
927 | QSize QLayout::maximumSize() const |
928 | { |
929 | return QSize(QLAYOUTSIZE_MAX, QLAYOUTSIZE_MAX); |
930 | } |
931 | |
932 | /*! |
933 | Returns whether this layout can make use of more space than |
934 | sizeHint(). A value of Qt::Vertical or Qt::Horizontal means that |
935 | it wants to grow in only one dimension, whereas Qt::Vertical | |
936 | Qt::Horizontal means that it wants to grow in both dimensions. |
937 | |
938 | The default implementation returns Qt::Horizontal | Qt::Vertical. |
939 | Subclasses reimplement it to return a meaningful value based on |
940 | their child widgets's \l{QSizePolicy}{size policies}. |
941 | |
942 | \sa sizeHint() |
943 | */ |
944 | Qt::Orientations QLayout::expandingDirections() const |
945 | { |
946 | return Qt::Horizontal | Qt::Vertical; |
947 | } |
948 | |
949 | void QLayout::activateRecursiveHelper(QLayoutItem *item) |
950 | { |
951 | item->invalidate(); |
952 | QLayout *layout = item->layout(); |
953 | if (layout) { |
954 | QLayoutItem *child; |
955 | int i=0; |
956 | while ((child = layout->itemAt(index: i++))) |
957 | activateRecursiveHelper(item: child); |
958 | layout->d_func()->activated = true; |
959 | } |
960 | } |
961 | |
962 | /*! |
963 | Updates the layout for parentWidget(). |
964 | |
965 | You should generally not need to call this because it is |
966 | automatically called at the most appropriate times. |
967 | |
968 | \sa activate(), invalidate() |
969 | */ |
970 | |
971 | void QLayout::update() |
972 | { |
973 | QLayout *layout = this; |
974 | while (layout && layout->d_func()->activated) { |
975 | layout->d_func()->activated = false; |
976 | if (layout->d_func()->topLevel) { |
977 | Q_ASSERT(layout->parent()->isWidgetType()); |
978 | QWidget *mw = static_cast<QWidget*>(layout->parent()); |
979 | QCoreApplication::postEvent(receiver: mw, event: new QEvent(QEvent::LayoutRequest)); |
980 | break; |
981 | } |
982 | layout = static_cast<QLayout*>(layout->parent()); |
983 | } |
984 | } |
985 | |
986 | /*! |
987 | Redoes the layout for parentWidget() if necessary. |
988 | |
989 | You should generally not need to call this because it is |
990 | automatically called at the most appropriate times. It returns |
991 | true if the layout was redone. |
992 | |
993 | \sa update(), QWidget::updateGeometry() |
994 | */ |
995 | bool QLayout::activate() |
996 | { |
997 | Q_D(QLayout); |
998 | if (!d->enabled || !parent()) |
999 | return false; |
1000 | if (!d->topLevel) |
1001 | return static_cast<QLayout*>(parent())->activate(); |
1002 | if (d->activated) |
1003 | return false; |
1004 | QWidget *mw = static_cast<QWidget*>(parent()); |
1005 | if (Q_UNLIKELY(!mw)) { |
1006 | qWarning(msg: "QLayout::activate: %s \"%ls\" does not have a main widget", |
1007 | metaObject()->className(), qUtf16Printable(objectName())); |
1008 | return false; |
1009 | } |
1010 | activateRecursiveHelper(item: this); |
1011 | |
1012 | QWidgetPrivate *md = mw->d_func(); |
1013 | uint explMin = md->extra ? md->extra->explicitMinSize : 0; |
1014 | uint explMax = md->extra ? md->extra->explicitMaxSize : 0; |
1015 | |
1016 | switch (d->constraint) { |
1017 | case SetFixedSize: |
1018 | // will trigger resize |
1019 | mw->setFixedSize(totalSizeHint()); |
1020 | break; |
1021 | case SetMinimumSize: |
1022 | mw->setMinimumSize(totalMinimumSize()); |
1023 | break; |
1024 | case SetMaximumSize: |
1025 | mw->setMaximumSize(totalMaximumSize()); |
1026 | break; |
1027 | case SetMinAndMaxSize: |
1028 | mw->setMinimumSize(totalMinimumSize()); |
1029 | mw->setMaximumSize(totalMaximumSize()); |
1030 | break; |
1031 | case SetDefaultConstraint: { |
1032 | bool widthSet = explMin & Qt::Horizontal; |
1033 | bool heightSet = explMin & Qt::Vertical; |
1034 | if (mw->isWindow()) { |
1035 | QSize ms = totalMinimumSize(); |
1036 | if (widthSet) |
1037 | ms.setWidth(mw->minimumSize().width()); |
1038 | if (heightSet) |
1039 | ms.setHeight(mw->minimumSize().height()); |
1040 | mw->setMinimumSize(ms); |
1041 | } else if (!widthSet || !heightSet) { |
1042 | QSize ms = mw->minimumSize(); |
1043 | if (!widthSet) |
1044 | ms.setWidth(0); |
1045 | if (!heightSet) |
1046 | ms.setHeight(0); |
1047 | mw->setMinimumSize(ms); |
1048 | } |
1049 | break; |
1050 | } |
1051 | case SetNoConstraint: |
1052 | break; |
1053 | } |
1054 | |
1055 | d->doResize(); |
1056 | |
1057 | if (md->extra) { |
1058 | md->extra->explicitMinSize = explMin; |
1059 | md->extra->explicitMaxSize = explMax; |
1060 | } |
1061 | // ideally only if sizeHint() or sizePolicy() has changed |
1062 | mw->updateGeometry(); |
1063 | return true; |
1064 | } |
1065 | |
1066 | /*! |
1067 | \since 5.2 |
1068 | |
1069 | Searches for widget \a from and replaces it with widget \a to if found. |
1070 | Returns the layout item that contains the widget \a from on success. |
1071 | Otherwise \nullptr is returned. |
1072 | If \a options contains \c Qt::FindChildrenRecursively (the default), |
1073 | sub-layouts are searched for doing the replacement. |
1074 | Any other flag in \a options is ignored. |
1075 | |
1076 | Notice that the returned item therefore might not belong to this layout, |
1077 | but to a sub-layout. |
1078 | |
1079 | The returned layout item is no longer owned by the layout and should be |
1080 | either deleted or inserted to another layout. The widget \a from is no |
1081 | longer managed by the layout and may need to be deleted or hidden. The |
1082 | parent of widget \a from is left unchanged. |
1083 | |
1084 | This function works for the built-in Qt layouts, but might not work for |
1085 | custom layouts. |
1086 | |
1087 | \sa indexOf() |
1088 | */ |
1089 | |
1090 | QLayoutItem *QLayout::replaceWidget(QWidget *from, QWidget *to, Qt::FindChildOptions options) |
1091 | { |
1092 | Q_D(QLayout); |
1093 | if (!from || !to) |
1094 | return nullptr; |
1095 | if (from == to) // Do not return a QLayoutItem for \a from, since ownership still |
1096 | return nullptr; // belongs to the layout (since nothing was changed) |
1097 | |
1098 | int index = -1; |
1099 | QLayoutItem *item = nullptr; |
1100 | for (int u = 0; u < count(); ++u) { |
1101 | item = itemAt(index: u); |
1102 | if (!item) |
1103 | continue; |
1104 | |
1105 | if (item->widget() == from) { |
1106 | index = u; |
1107 | break; |
1108 | } |
1109 | |
1110 | if (item->layout() && (options & Qt::FindChildrenRecursively)) { |
1111 | QLayoutItem *r = item->layout()->replaceWidget(from, to, options); |
1112 | if (r) |
1113 | return r; |
1114 | } |
1115 | } |
1116 | if (index == -1) |
1117 | return nullptr; |
1118 | |
1119 | addChildWidget(w: to); |
1120 | QLayoutItem *newitem = new QWidgetItem(to); |
1121 | newitem->setAlignment(item->alignment()); |
1122 | QLayoutItem *r = d->replaceAt(index, newitem); |
1123 | if (!r) |
1124 | delete newitem; |
1125 | return r; |
1126 | } |
1127 | |
1128 | /*! |
1129 | \fn QLayoutItem *QLayout::itemAt(int index) const |
1130 | |
1131 | Must be implemented in subclasses to return the layout item at \a |
1132 | index. If there is no such item, the function must return \nullptr. |
1133 | Items are numbered consecutively from 0. If an item is deleted, other items will be renumbered. |
1134 | |
1135 | This function can be used to iterate over a layout. The following |
1136 | code will draw a rectangle for each layout item in the layout structure of the widget. |
1137 | |
1138 | \snippet code/src_gui_kernel_qlayout.cpp 0 |
1139 | |
1140 | \sa count(), takeAt() |
1141 | */ |
1142 | |
1143 | /*! |
1144 | \fn QLayoutItem *QLayout::takeAt(int index) |
1145 | |
1146 | Must be implemented in subclasses to remove the layout item at \a |
1147 | index from the layout, and return the item. If there is no such |
1148 | item, the function must do nothing and return 0. Items are numbered |
1149 | consecutively from 0. If an item is removed, other items will be |
1150 | renumbered. |
1151 | |
1152 | The following code fragment shows a safe way to remove all items |
1153 | from a layout: |
1154 | |
1155 | \snippet code/src_gui_kernel_qlayout.cpp 1 |
1156 | |
1157 | \sa itemAt(), count() |
1158 | */ |
1159 | |
1160 | /*! |
1161 | \fn int *QLayout::count() const |
1162 | |
1163 | Must be implemented in subclasses to return the number of items |
1164 | in the layout. |
1165 | |
1166 | \sa itemAt() |
1167 | */ |
1168 | |
1169 | /*! |
1170 | Searches for widget \a widget in this layout (not including child |
1171 | layouts). |
1172 | |
1173 | Returns the index of \a widget, or -1 if \a widget is not found. |
1174 | |
1175 | The default implementation iterates over all items using itemAt(). |
1176 | */ |
1177 | int QLayout::indexOf(const QWidget *widget) const |
1178 | { |
1179 | const int c = count(); |
1180 | |
1181 | for (int i = 0; i < c; ++i) { |
1182 | if (itemAt(index: i)->widget() == widget) |
1183 | return i; |
1184 | } |
1185 | |
1186 | return -1; |
1187 | } |
1188 | |
1189 | /*! |
1190 | \since 5.12 |
1191 | Searches for layout item \a layoutItem in this layout (not including child |
1192 | layouts). |
1193 | |
1194 | Returns the index of \a layoutItem, or -1 if \a layoutItem is not found. |
1195 | */ |
1196 | int QLayout::indexOf(const QLayoutItem *layoutItem) const |
1197 | { |
1198 | const int c = count(); |
1199 | |
1200 | for (int i = 0; i < c; ++i) { |
1201 | if (itemAt(index: i) == layoutItem) |
1202 | return i; |
1203 | } |
1204 | |
1205 | return -1; |
1206 | } |
1207 | |
1208 | /*! |
1209 | \enum QLayout::SizeConstraint |
1210 | |
1211 | The possible values are: |
1212 | |
1213 | \value SetDefaultConstraint The main widget's minimum size is set |
1214 | to minimumSize(), unless the widget already has |
1215 | a minimum size. |
1216 | |
1217 | \value SetFixedSize The main widget's size is set to sizeHint(); it |
1218 | cannot be resized at all. |
1219 | \value SetMinimumSize The main widget's minimum size is set to |
1220 | minimumSize(); it cannot be smaller. |
1221 | |
1222 | \value SetMaximumSize The main widget's maximum size is set to |
1223 | maximumSize(); it cannot be larger. |
1224 | |
1225 | \value SetMinAndMaxSize The main widget's minimum size is set to |
1226 | minimumSize() and its maximum size is set to |
1227 | maximumSize(). |
1228 | |
1229 | \value SetNoConstraint The widget is not constrained. |
1230 | |
1231 | \sa setSizeConstraint() |
1232 | */ |
1233 | |
1234 | /*! |
1235 | \property QLayout::sizeConstraint |
1236 | \brief the resize mode of the layout |
1237 | |
1238 | The default mode is \l {QLayout::SetDefaultConstraint} |
1239 | {SetDefaultConstraint}. |
1240 | */ |
1241 | void QLayout::setSizeConstraint(SizeConstraint constraint) |
1242 | { |
1243 | Q_D(QLayout); |
1244 | if (constraint == d->constraint) |
1245 | return; |
1246 | |
1247 | d->constraint = constraint; |
1248 | invalidate(); |
1249 | } |
1250 | |
1251 | QLayout::SizeConstraint QLayout::sizeConstraint() const |
1252 | { |
1253 | Q_D(const QLayout); |
1254 | return d->constraint; |
1255 | } |
1256 | |
1257 | /*! |
1258 | Returns the rectangle that should be covered when the geometry of |
1259 | this layout is set to \a r, provided that this layout supports |
1260 | setAlignment(). |
1261 | |
1262 | The result is derived from sizeHint() and expandingDirections(). It is never |
1263 | larger than \a r. |
1264 | */ |
1265 | QRect QLayout::alignmentRect(const QRect &r) const |
1266 | { |
1267 | QSize s = sizeHint(); |
1268 | Qt::Alignment a = alignment(); |
1269 | |
1270 | /* |
1271 | This is a hack to obtain the real maximum size, not |
1272 | QSize(QLAYOUTSIZE_MAX, QLAYOUTSIZE_MAX), the value consistently |
1273 | returned by QLayoutItems that have an alignment. |
1274 | */ |
1275 | QLayout *that = const_cast<QLayout *>(this); |
1276 | that->setAlignment({ }); |
1277 | QSize ms = that->maximumSize(); |
1278 | that->setAlignment(a); |
1279 | |
1280 | if ((expandingDirections() & Qt::Horizontal) || |
1281 | !(a & Qt::AlignHorizontal_Mask)) { |
1282 | s.setWidth(qMin(a: r.width(), b: ms.width())); |
1283 | } |
1284 | if ((expandingDirections() & Qt::Vertical) || |
1285 | !(a & Qt::AlignVertical_Mask)) { |
1286 | s.setHeight(qMin(a: r.height(), b: ms.height())); |
1287 | } else if (hasHeightForWidth()) { |
1288 | int hfw = heightForWidth(s.width()); |
1289 | if (hfw < s.height()) |
1290 | s.setHeight(qMin(a: hfw, b: ms.height())); |
1291 | } |
1292 | |
1293 | s = s.boundedTo(otherSize: r.size()); |
1294 | int x = r.x(); |
1295 | int y = r.y(); |
1296 | |
1297 | if (a & Qt::AlignBottom) |
1298 | y += (r.height() - s.height()); |
1299 | else if (!(a & Qt::AlignTop)) |
1300 | y += (r.height() - s.height()) / 2; |
1301 | |
1302 | QWidget *parent = parentWidget(); |
1303 | a = QStyle::visualAlignment(direction: parent ? parent->layoutDirection() : QGuiApplication::layoutDirection(), alignment: a); |
1304 | if (a & Qt::AlignRight) |
1305 | x += (r.width() - s.width()); |
1306 | else if (!(a & Qt::AlignLeft)) |
1307 | x += (r.width() - s.width()) / 2; |
1308 | |
1309 | return QRect(x, y, s.width(), s.height()); |
1310 | } |
1311 | |
1312 | /*! |
1313 | Removes the widget \a widget from the layout. After this call, it |
1314 | is the caller's responsibility to give the widget a reasonable |
1315 | geometry or to put the widget back into a layout or to explicitly |
1316 | hide it if necessary. |
1317 | |
1318 | \b{Note:} The ownership of \a widget remains the same as |
1319 | when it was added. |
1320 | |
1321 | \sa removeItem(), QWidget::setGeometry(), addWidget() |
1322 | */ |
1323 | void QLayout::removeWidget(QWidget *widget) |
1324 | { |
1325 | if (Q_UNLIKELY(!widget)) { |
1326 | qWarning(msg: "QLayout::removeWidget: Cannot remove a null widget."); |
1327 | return; |
1328 | } |
1329 | |
1330 | int i = 0; |
1331 | QLayoutItem *child; |
1332 | while ((child = itemAt(index: i))) { |
1333 | if (child->widget() == widget) { |
1334 | delete takeAt(index: i); |
1335 | invalidate(); |
1336 | } else { |
1337 | ++i; |
1338 | } |
1339 | } |
1340 | } |
1341 | |
1342 | /*! |
1343 | Removes the layout item \a item from the layout. It is the |
1344 | caller's responsibility to delete the item. |
1345 | |
1346 | Notice that \a item can be a layout (since QLayout inherits |
1347 | QLayoutItem). |
1348 | |
1349 | \sa removeWidget(), addItem() |
1350 | */ |
1351 | void QLayout::removeItem(QLayoutItem *item) |
1352 | { |
1353 | int i = 0; |
1354 | QLayoutItem *child; |
1355 | while ((child = itemAt(index: i))) { |
1356 | if (child == item) { |
1357 | takeAt(index: i); |
1358 | invalidate(); |
1359 | } else { |
1360 | ++i; |
1361 | } |
1362 | } |
1363 | } |
1364 | |
1365 | /*! |
1366 | Enables this layout if \a enable is true, otherwise disables it. |
1367 | |
1368 | An enabled layout adjusts dynamically to changes; a disabled |
1369 | layout acts as if it did not exist. |
1370 | |
1371 | By default all layouts are enabled. |
1372 | |
1373 | \sa isEnabled() |
1374 | */ |
1375 | void QLayout::setEnabled(bool enable) |
1376 | { |
1377 | Q_D(QLayout); |
1378 | d->enabled = enable; |
1379 | } |
1380 | |
1381 | /*! |
1382 | Returns \c true if the layout is enabled; otherwise returns \c false. |
1383 | |
1384 | \sa setEnabled() |
1385 | */ |
1386 | bool QLayout::isEnabled() const |
1387 | { |
1388 | Q_D(const QLayout); |
1389 | return d->enabled; |
1390 | } |
1391 | |
1392 | /*! |
1393 | Returns a size that satisfies all size constraints on \a widget, |
1394 | including heightForWidth() and that is as close as possible to \a |
1395 | size. |
1396 | */ |
1397 | |
1398 | QSize QLayout::closestAcceptableSize(const QWidget *widget, const QSize &size) |
1399 | { |
1400 | QSize result = size.boundedTo(otherSize: qSmartMaxSize(w: widget)); |
1401 | result = result.expandedTo(otherSize: qSmartMinSize(w: widget)); |
1402 | QLayout *l = widget->layout(); |
1403 | if (l && l->hasHeightForWidth() && result.height() < l->minimumHeightForWidth(result.width()) ) { |
1404 | QSize current = widget->size(); |
1405 | int currentHfw = l->minimumHeightForWidth(current.width()); |
1406 | int newHfw = l->minimumHeightForWidth(result.width()); |
1407 | if (current.height() < currentHfw || currentHfw == newHfw) { |
1408 | //handle the constant hfw case and the vertical-only case, as well as the |
1409 | // current-size-is-not-correct case |
1410 | result.setHeight(newHfw); |
1411 | } else { |
1412 | // binary search; assume hfw is decreasing ### |
1413 | |
1414 | int maxw = qMax(a: widget->width(),b: result.width()); |
1415 | int maxh = qMax(a: widget->height(), b: result.height()); |
1416 | int minw = qMin(a: widget->width(),b: result.width()); |
1417 | int minh = qMin(a: widget->height(), b: result.height()); |
1418 | |
1419 | int minhfw = l->minimumHeightForWidth(minw); |
1420 | int maxhfw = l->minimumHeightForWidth(maxw); |
1421 | while (minw < maxw) { |
1422 | if (minhfw > maxh) { //assume decreasing |
1423 | minw = maxw - (maxw-minw)/2; |
1424 | minhfw = l->minimumHeightForWidth(minw); |
1425 | } else if (maxhfw < minh ) { //assume decreasing |
1426 | maxw = minw + (maxw-minw)/2; |
1427 | maxhfw = l->minimumHeightForWidth(maxw); |
1428 | } else { |
1429 | break; |
1430 | } |
1431 | } |
1432 | result = result.expandedTo(otherSize: QSize(minw, minhfw)); |
1433 | } |
1434 | } |
1435 | return result; |
1436 | } |
1437 | |
1438 | QT_END_NAMESPACE |
1439 | |
1440 | #include "moc_qlayout.cpp" |
1441 |
Definitions
- menuBarHeightForWidth
- QLayout
- QLayout
- QLayoutPrivate
- getMargin
- widgetItemFactoryMethod
- spacerItemFactoryMethod
- createWidgetItem
- createSpacerItem
- addWidget
- setAlignment
- setAlignment
- spacing
- setSpacing
- setContentsMargins
- setContentsMargins
- unsetContentsMargins
- getContentsMargins
- contentsMargins
- contentsRect
- parentWidget
- isEmpty
- controlTypes
- setGeometry
- geometry
- invalidate
- removeWidgetRecursively
- doResize
- widgetEvent
- childEvent
- totalMinimumHeightForWidth
- totalHeightForWidth
- totalMinimumSize
- totalSizeHint
- totalMaximumSize
- ~QLayout
- addChildLayout
- adoptLayout
- layoutDebug
- reparentChildWidgets
- checkWidget
- checkLayout
- addChildWidget
- setMenuBar
- menuBar
- minimumSize
- maximumSize
- expandingDirections
- activateRecursiveHelper
- update
- activate
- replaceWidget
- indexOf
- indexOf
- setSizeConstraint
- sizeConstraint
- alignmentRect
- removeWidget
- removeItem
- setEnabled
- isEnabled
Learn Advanced QML with KDAB
Find out more