| 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 Qt Quick Layouts 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 "qquicklayout_p.h" | 
| 41 | #include <QEvent> | 
| 42 | #include <QtCore/qcoreapplication.h> | 
| 43 | #include <QtCore/private/qnumeric_p.h> | 
| 44 | #include <QtCore/qstack.h> | 
| 45 | #include <QtCore/qmath.h> | 
| 46 | #include <QtQml/qqmlinfo.h> | 
| 47 | #include <limits> | 
| 48 |  | 
| 49 | /*! | 
| 50 |     \qmltype Layout | 
| 51 |     \instantiates QQuickLayoutAttached | 
| 52 |     \inqmlmodule QtQuick.Layouts | 
| 53 |     \ingroup layouts | 
| 54 |     \brief Provides attached properties for items pushed onto a \l GridLayout, | 
| 55 |     \l RowLayout or \l ColumnLayout. | 
| 56 |  | 
| 57 |     An object of type Layout is attached to children of the layout to provide layout specific | 
| 58 |     information about the item. | 
| 59 |     The properties of the attached object influence how the layout will arrange the items. | 
| 60 |  | 
| 61 |     For instance, you can specify \l minimumWidth, \l preferredWidth, and | 
| 62 |     \l maximumWidth if the default values are not satisfactory. | 
| 63 |  | 
| 64 |     When a layout is resized, items may grow or shrink. Due to this, items have a | 
| 65 |     \l{Layout::minimumWidth}{minimum size}, \l{Layout::preferredWidth}{preferred size} and a | 
| 66 |     \l{Layout::maximumWidth}{maximum size}. | 
| 67 |  | 
| 68 |     If minimum size has not been explicitly specified on an item, the size is set to \c 0. | 
| 69 |     If maximum size has not been explicitly specified on an item, the size is set to | 
| 70 |     \c Number.POSITIVE_INFINITY. | 
| 71 |  | 
| 72 |     For layouts, the implicit minimum and maximum sizes depend on the content of the layouts. | 
| 73 |  | 
| 74 |     The \l fillWidth and \l fillHeight properties can either be \c true or \c false. If they are \c | 
| 75 |     false, the item's size will be fixed to its preferred size. Otherwise, it will grow or shrink | 
| 76 |     between its minimum and maximum size as the layout is resized. | 
| 77 |  | 
| 78 |     \note Do not bind to the x, y, width, or height properties of items in a layout, | 
| 79 |     as this would conflict with the goals of Layout, and can also cause binding loops. | 
| 80 |     The width and height properties are used by the layout engine to store the current | 
| 81 |     size of items as calculated from the minimum/preferred/maximum attached properties, | 
| 82 |     and can be ovewritten each time the items are laid out. Use | 
| 83 |     \l {Layout::preferredWidth}{Layout.preferredWidth} and | 
| 84 |     \l {Layout::preferredHeight}{Layout.preferredHeight}, or \l {Item::}{implicitWidth} | 
| 85 |     and \l {Item::}{implicitHeight} to specify the preferred size of items. | 
| 86 |  | 
| 87 |     \sa GridLayout | 
| 88 |     \sa RowLayout | 
| 89 |     \sa ColumnLayout | 
| 90 | */ | 
| 91 |  | 
| 92 | QT_BEGIN_NAMESPACE | 
| 93 |  | 
| 94 | Q_LOGGING_CATEGORY(lcQuickLayouts, "qt.quick.layouts" ) | 
| 95 |  | 
| 96 | QQuickLayoutAttached::QQuickLayoutAttached(QObject *parent) | 
| 97 |     : QObject(parent), | 
| 98 |       m_minimumWidth(0), | 
| 99 |       m_minimumHeight(0), | 
| 100 |       m_preferredWidth(-1), | 
| 101 |       m_preferredHeight(-1), | 
| 102 |       m_maximumWidth(std::numeric_limits<qreal>::infinity()), | 
| 103 |       m_maximumHeight(std::numeric_limits<qreal>::infinity()), | 
| 104 |       m_defaultMargins(0), | 
| 105 |       m_fallbackWidth(-1), | 
| 106 |       m_fallbackHeight(-1), | 
| 107 |       m_row(-1), | 
| 108 |       m_column(-1), | 
| 109 |       m_rowSpan(1), | 
| 110 |       m_columnSpan(1), | 
| 111 |       m_fillWidth(false), | 
| 112 |       m_fillHeight(false), | 
| 113 |       m_isFillWidthSet(false), | 
| 114 |       m_isFillHeightSet(false), | 
| 115 |       m_isMinimumWidthSet(false), | 
| 116 |       m_isMinimumHeightSet(false), | 
| 117 |       m_isMaximumWidthSet(false), | 
| 118 |       m_isMaximumHeightSet(false), | 
| 119 |       m_changesNotificationEnabled(true), | 
| 120 |       m_isLeftMarginSet(false), | 
| 121 |       m_isTopMarginSet(false), | 
| 122 |       m_isRightMarginSet(false), | 
| 123 |       m_isBottomMarginSet(false) | 
| 124 | { | 
| 125 |  | 
| 126 | } | 
| 127 |  | 
| 128 | /*! | 
| 129 |     \qmlattachedproperty real Layout::minimumWidth | 
| 130 |  | 
| 131 |     This property holds the minimum width of an item in a layout. | 
| 132 |     The default value is the item's implicit minimum width. | 
| 133 |  | 
| 134 |     If the item is a layout, the implicit minimum width will be the minimum width the layout can | 
| 135 |     have without any of its items shrinking below their minimum width. | 
| 136 |     The implicit minimum width for any other item is \c 0. | 
| 137 |  | 
| 138 |     Setting this value to -1 will reset the width back to its implicit minimum width. | 
| 139 |  | 
| 140 |  | 
| 141 |     \sa preferredWidth | 
| 142 |     \sa maximumWidth | 
| 143 | */ | 
| 144 | void QQuickLayoutAttached::setMinimumWidth(qreal width) | 
| 145 | { | 
| 146 |     if (qt_is_nan(d: width)) | 
| 147 |         return; | 
| 148 |     m_isMinimumWidthSet = width >= 0; | 
| 149 |     if (m_minimumWidth == width) | 
| 150 |         return; | 
| 151 |  | 
| 152 |     m_minimumWidth = width; | 
| 153 |     invalidateItem(); | 
| 154 |     emit minimumWidthChanged(); | 
| 155 | } | 
| 156 |  | 
| 157 | /*! | 
| 158 |     \qmlattachedproperty real Layout::minimumHeight | 
| 159 |  | 
| 160 |     This property holds the minimum height of an item in a layout. | 
| 161 |     The default value is the item's implicit minimum height. | 
| 162 |  | 
| 163 |     If the item is a layout, the implicit minimum height will be the minimum height the layout can | 
| 164 |     have without any of its items shrinking below their minimum height. | 
| 165 |     The implicit minimum height for any other item is \c 0. | 
| 166 |  | 
| 167 |     Setting this value to -1 will reset the height back to its implicit minimum height. | 
| 168 |  | 
| 169 |     \sa preferredHeight | 
| 170 |     \sa maximumHeight | 
| 171 | */ | 
| 172 | void QQuickLayoutAttached::setMinimumHeight(qreal height) | 
| 173 | { | 
| 174 |     if (qt_is_nan(d: height)) | 
| 175 |         return; | 
| 176 |     m_isMinimumHeightSet = height >= 0; | 
| 177 |     if (m_minimumHeight == height) | 
| 178 |         return; | 
| 179 |  | 
| 180 |     m_minimumHeight = height; | 
| 181 |     invalidateItem(); | 
| 182 |     emit minimumHeightChanged(); | 
| 183 | } | 
| 184 |  | 
| 185 | /*! | 
| 186 |     \qmlattachedproperty real Layout::preferredWidth | 
| 187 |  | 
| 188 |     This property holds the preferred width of an item in a layout. | 
| 189 |     If the preferred width is \c -1 it will be ignored, and the layout | 
| 190 |     will use \l{Item::implicitWidth}{implicitWidth} instead. | 
| 191 |     The default is \c -1. | 
| 192 |  | 
| 193 |     \sa minimumWidth | 
| 194 |     \sa maximumWidth | 
| 195 | */ | 
| 196 | void QQuickLayoutAttached::setPreferredWidth(qreal width) | 
| 197 | { | 
| 198 |     if (qt_is_nan(d: width) || m_preferredWidth == width) | 
| 199 |         return; | 
| 200 |  | 
| 201 |     m_preferredWidth = width; | 
| 202 |     invalidateItem(); | 
| 203 |     emit preferredWidthChanged(); | 
| 204 | } | 
| 205 |  | 
| 206 | /*! | 
| 207 |     \qmlattachedproperty real Layout::preferredHeight | 
| 208 |  | 
| 209 |     This property holds the preferred height of an item in a layout. | 
| 210 |     If the preferred height is \c -1 it will be ignored, and the layout | 
| 211 |     will use \l{Item::implicitHeight}{implicitHeight} instead. | 
| 212 |     The default is \c -1. | 
| 213 |  | 
| 214 |     \sa minimumHeight | 
| 215 |     \sa maximumHeight | 
| 216 | */ | 
| 217 | void QQuickLayoutAttached::setPreferredHeight(qreal height) | 
| 218 | { | 
| 219 |     if (qt_is_nan(d: height) || m_preferredHeight == height) | 
| 220 |         return; | 
| 221 |  | 
| 222 |     m_preferredHeight = height; | 
| 223 |     invalidateItem(); | 
| 224 |     emit preferredHeightChanged(); | 
| 225 | } | 
| 226 |  | 
| 227 | /*! | 
| 228 |     \qmlattachedproperty real Layout::maximumWidth | 
| 229 |  | 
| 230 |     This property holds the maximum width of an item in a layout. | 
| 231 |     The default value is the item's implicit maximum width. | 
| 232 |  | 
| 233 |     If the item is a layout, the implicit maximum width will be the maximum width the layout can | 
| 234 |     have without any of its items growing beyond their maximum width. | 
| 235 |     The implicit maximum width for any other item is \c Number.POSITIVE_INFINITY. | 
| 236 |  | 
| 237 |     Setting this value to \c -1 will reset the width back to its implicit maximum width. | 
| 238 |  | 
| 239 |     \sa minimumWidth | 
| 240 |     \sa preferredWidth | 
| 241 | */ | 
| 242 | void QQuickLayoutAttached::setMaximumWidth(qreal width) | 
| 243 | { | 
| 244 |     if (qt_is_nan(d: width)) | 
| 245 |         return; | 
| 246 |     m_isMaximumWidthSet = width >= 0; | 
| 247 |     if (m_maximumWidth == width) | 
| 248 |         return; | 
| 249 |  | 
| 250 |     m_maximumWidth = width; | 
| 251 |     invalidateItem(); | 
| 252 |     emit maximumWidthChanged(); | 
| 253 | } | 
| 254 |  | 
| 255 | /*! | 
| 256 |     \qmlattachedproperty real Layout::maximumHeight | 
| 257 |  | 
| 258 |     The default value is the item's implicit maximum height. | 
| 259 |  | 
| 260 |     If the item is a layout, the implicit maximum height will be the maximum height the layout can | 
| 261 |     have without any of its items growing beyond their maximum height. | 
| 262 |     The implicit maximum height for any other item is \c Number.POSITIVE_INFINITY. | 
| 263 |  | 
| 264 |     Setting this value to \c -1 will reset the height back to its implicit maximum height. | 
| 265 |  | 
| 266 |     \sa minimumHeight | 
| 267 |     \sa preferredHeight | 
| 268 | */ | 
| 269 | void QQuickLayoutAttached::setMaximumHeight(qreal height) | 
| 270 | { | 
| 271 |     if (qt_is_nan(d: height)) | 
| 272 |         return; | 
| 273 |     m_isMaximumHeightSet = height >= 0; | 
| 274 |     if (m_maximumHeight == height) | 
| 275 |         return; | 
| 276 |  | 
| 277 |     m_maximumHeight = height; | 
| 278 |     invalidateItem(); | 
| 279 |     emit maximumHeightChanged(); | 
| 280 | } | 
| 281 |  | 
| 282 | void QQuickLayoutAttached::setMinimumImplicitSize(const QSizeF &sz) | 
| 283 | { | 
| 284 |     bool emitWidthChanged = false; | 
| 285 |     bool emitHeightChanged = false; | 
| 286 |     if (!m_isMinimumWidthSet && m_minimumWidth != sz.width()) { | 
| 287 |         m_minimumWidth = sz.width(); | 
| 288 |         emitWidthChanged = true; | 
| 289 |     } | 
| 290 |     if (!m_isMinimumHeightSet && m_minimumHeight != sz.height()) { | 
| 291 |         m_minimumHeight = sz.height(); | 
| 292 |         emitHeightChanged = true; | 
| 293 |     } | 
| 294 |     // Only invalidate the item once, and make sure we emit signal changed after the call to | 
| 295 |     // invalidateItem() | 
| 296 |     if (emitWidthChanged || emitHeightChanged) { | 
| 297 |         invalidateItem(); | 
| 298 |         if (emitWidthChanged) | 
| 299 |             emit minimumWidthChanged(); | 
| 300 |         if (emitHeightChanged) | 
| 301 |             emit minimumHeightChanged(); | 
| 302 |     } | 
| 303 | } | 
| 304 |  | 
| 305 | void QQuickLayoutAttached::setMaximumImplicitSize(const QSizeF &sz) | 
| 306 | { | 
| 307 |     bool emitWidthChanged = false; | 
| 308 |     bool emitHeightChanged = false; | 
| 309 |     if (!m_isMaximumWidthSet && m_maximumWidth != sz.width()) { | 
| 310 |         m_maximumWidth = sz.width(); | 
| 311 |         emitWidthChanged = true; | 
| 312 |     } | 
| 313 |     if (!m_isMaximumHeightSet && m_maximumHeight != sz.height()) { | 
| 314 |         m_maximumHeight = sz.height(); | 
| 315 |         emitHeightChanged = true; | 
| 316 |     } | 
| 317 |     // Only invalidate the item once, and make sure we emit changed signal after the call to | 
| 318 |     // invalidateItem() | 
| 319 |     if (emitWidthChanged || emitHeightChanged) { | 
| 320 |         invalidateItem(); | 
| 321 |         if (emitWidthChanged) | 
| 322 |             emit maximumWidthChanged(); | 
| 323 |         if (emitHeightChanged) | 
| 324 |             emit maximumHeightChanged(); | 
| 325 |     } | 
| 326 | } | 
| 327 |  | 
| 328 | /*! | 
| 329 |     \qmlattachedproperty bool Layout::fillWidth | 
| 330 |  | 
| 331 |     If this property is \c true, the item will be as wide as possible while respecting | 
| 332 |     the given constraints. If the property is \c false, the item will have a fixed width | 
| 333 |     set to the preferred width. | 
| 334 |     The default is \c false, except for layouts themselves, which default to \c true. | 
| 335 |  | 
| 336 |     \sa fillHeight | 
| 337 | */ | 
| 338 | void QQuickLayoutAttached::setFillWidth(bool fill) | 
| 339 | { | 
| 340 |     m_isFillWidthSet = true; | 
| 341 |     if (m_fillWidth != fill) { | 
| 342 |         m_fillWidth = fill; | 
| 343 |         invalidateItem(); | 
| 344 |         emit fillWidthChanged(); | 
| 345 |     } | 
| 346 | } | 
| 347 |  | 
| 348 | /*! | 
| 349 |     \qmlattachedproperty bool Layout::fillHeight | 
| 350 |  | 
| 351 |     If this property is \c true, the item will be as tall as possible while respecting | 
| 352 |     the given constraints. If the property is \c false, the item will have a fixed height | 
| 353 |     set to the preferred height. | 
| 354 |     The default is \c false, except for layouts themselves, which default to \c true. | 
| 355 |  | 
| 356 |     \sa fillWidth | 
| 357 | */ | 
| 358 | void QQuickLayoutAttached::setFillHeight(bool fill) | 
| 359 | { | 
| 360 |     m_isFillHeightSet = true; | 
| 361 |     if (m_fillHeight != fill) { | 
| 362 |         m_fillHeight = fill; | 
| 363 |         invalidateItem(); | 
| 364 |         emit fillHeightChanged(); | 
| 365 |     } | 
| 366 | } | 
| 367 |  | 
| 368 | /*! | 
| 369 |     \qmlattachedproperty int Layout::row | 
| 370 |  | 
| 371 |     This property allows you to specify the row position of an item in a \l GridLayout. | 
| 372 |  | 
| 373 |     If both \l column and this property are not set, it is up to the layout to assign a cell to the item. | 
| 374 |  | 
| 375 |     The default value is \c 0. | 
| 376 |  | 
| 377 |     \sa column | 
| 378 |     \sa rowSpan | 
| 379 | */ | 
| 380 | void QQuickLayoutAttached::setRow(int row) | 
| 381 | { | 
| 382 |     if (row >= 0 && row != m_row) { | 
| 383 |         m_row = row; | 
| 384 |         invalidateItem(); | 
| 385 |         emit rowChanged(); | 
| 386 |     } | 
| 387 | } | 
| 388 |  | 
| 389 | /*! | 
| 390 |     \qmlattachedproperty int Layout::column | 
| 391 |  | 
| 392 |     This property allows you to specify the column position of an item in a \l GridLayout. | 
| 393 |  | 
| 394 |     If both \l row and this property are not set, it is up to the layout to assign a cell to the item. | 
| 395 |  | 
| 396 |     The default value is \c 0. | 
| 397 |  | 
| 398 |     \sa row | 
| 399 |     \sa columnSpan | 
| 400 | */ | 
| 401 | void QQuickLayoutAttached::setColumn(int column) | 
| 402 | { | 
| 403 |     if (column >= 0 && column != m_column) { | 
| 404 |         m_column = column; | 
| 405 |         invalidateItem(); | 
| 406 |         emit columnChanged(); | 
| 407 |     } | 
| 408 | } | 
| 409 |  | 
| 410 |  | 
| 411 | /*! | 
| 412 |     \qmlattachedproperty Qt.Alignment Layout::alignment | 
| 413 |  | 
| 414 |     This property allows you to specify the alignment of an item within the cell(s) it occupies. | 
| 415 |  | 
| 416 |     The default value is \c 0, which means it will be \c{Qt.AlignVCenter | Qt.AlignLeft}. | 
| 417 |     These defaults also apply if only a horizontal or vertical flag is specified: | 
| 418 |     if only a horizontal flag is specified, the default vertical flag will be | 
| 419 |     \c Qt.AlignVCenter, and if only a vertical flag is specified, the default | 
| 420 |     horizontal flag will be \c Qt.AlignLeft. | 
| 421 |  | 
| 422 |     A valid alignment is a combination of the following flags: | 
| 423 |     \list | 
| 424 |     \li Qt::AlignLeft | 
| 425 |     \li Qt::AlignHCenter | 
| 426 |     \li Qt::AlignRight | 
| 427 |     \li Qt::AlignTop | 
| 428 |     \li Qt::AlignVCenter | 
| 429 |     \li Qt::AlignBottom | 
| 430 |     \li Qt::AlignBaseline | 
| 431 |     \endlist | 
| 432 |  | 
| 433 | */ | 
| 434 | void QQuickLayoutAttached::setAlignment(Qt::Alignment align) | 
| 435 | { | 
| 436 |     if (align != m_alignment) { | 
| 437 |         m_alignment = align; | 
| 438 |         if (QQuickLayout *layout = parentLayout()) { | 
| 439 |             layout->setAlignment(item: item(), align); | 
| 440 |             invalidateItem(); | 
| 441 |         } | 
| 442 |         emit alignmentChanged(); | 
| 443 |     } | 
| 444 | } | 
| 445 |  | 
| 446 | /*! | 
| 447 |     \qmlattachedproperty real Layout::margins | 
| 448 |  | 
| 449 |     Sets the margins outside of an item to all have the same value. The item | 
| 450 |     itself does not evaluate its own margins. It is the parent's responsibility | 
| 451 |     to decide if it wants to evaluate the margins. | 
| 452 |  | 
| 453 |     Specifically, margins are only evaluated by ColumnLayout, RowLayout, | 
| 454 |     GridLayout, and other layout-like containers, such as SplitView, where the | 
| 455 |     effective cell size of an item will be increased as the margins are | 
| 456 |     increased. | 
| 457 |  | 
| 458 |     Therefore, if an item with margins is a child of another \c Item, its | 
| 459 |     position, size and implicit size will remain unchanged. | 
| 460 |  | 
| 461 |     Combining margins with alignment will align the item \e including its | 
| 462 |     margins. For instance, a vertically-centered Item with a top margin of \c 1 | 
| 463 |     and a bottom margin of \c 9 will cause the Items effective alignment within | 
| 464 |     the cell to be 4 pixels above the center. | 
| 465 |  | 
| 466 |     The default value is \c 0. | 
| 467 |  | 
| 468 |     \sa leftMargin | 
| 469 |     \sa topMargin | 
| 470 |     \sa rightMargin | 
| 471 |     \sa bottomMargin | 
| 472 |  | 
| 473 |     \since QtQuick.Layouts 1.2 | 
| 474 | */ | 
| 475 | void QQuickLayoutAttached::setMargins(qreal m) | 
| 476 | { | 
| 477 |     if (m == m_defaultMargins) | 
| 478 |         return; | 
| 479 |  | 
| 480 |     m_defaultMargins = m; | 
| 481 |     invalidateItem(); | 
| 482 |     if (!m_isLeftMarginSet && m_margins.left() != m) | 
| 483 |         emit leftMarginChanged(); | 
| 484 |     if (!m_isTopMarginSet && m_margins.top() != m) | 
| 485 |         emit topMarginChanged(); | 
| 486 |     if (!m_isRightMarginSet && m_margins.right() != m) | 
| 487 |         emit rightMarginChanged(); | 
| 488 |     if (!m_isBottomMarginSet && m_margins.bottom() != m) | 
| 489 |         emit bottomMarginChanged(); | 
| 490 |     emit marginsChanged(); | 
| 491 | } | 
| 492 |  | 
| 493 | /*! | 
| 494 |     \qmlattachedproperty real Layout::leftMargin | 
| 495 |  | 
| 496 |     Specifies the left margin outside of an item. | 
| 497 |     If the value is not set, it will use the value from \l margins. | 
| 498 |  | 
| 499 |     \sa margins | 
| 500 |  | 
| 501 |     \since QtQuick.Layouts 1.2 | 
| 502 | */ | 
| 503 | void QQuickLayoutAttached::setLeftMargin(qreal m) | 
| 504 | { | 
| 505 |     const bool changed = leftMargin() != m; | 
| 506 |     m_margins.setLeft(m); | 
| 507 |     m_isLeftMarginSet = true; | 
| 508 |     if (changed) { | 
| 509 |         invalidateItem(); | 
| 510 |         emit leftMarginChanged(); | 
| 511 |     } | 
| 512 | } | 
| 513 |  | 
| 514 | void QQuickLayoutAttached::resetLeftMargin() | 
| 515 | { | 
| 516 |     const bool changed = m_isLeftMarginSet && (m_defaultMargins != m_margins.left()); | 
| 517 |     m_isLeftMarginSet = false; | 
| 518 |     if (changed) { | 
| 519 |         invalidateItem(); | 
| 520 |         emit leftMarginChanged(); | 
| 521 |     } | 
| 522 | } | 
| 523 |  | 
| 524 | /*! | 
| 525 |     \qmlattachedproperty real Layout::topMargin | 
| 526 |  | 
| 527 |     Specifies the top margin outside of an item. | 
| 528 |     If the value is not set, it will use the value from \l margins. | 
| 529 |  | 
| 530 |     \sa margins | 
| 531 |  | 
| 532 |     \since QtQuick.Layouts 1.2 | 
| 533 | */ | 
| 534 | void QQuickLayoutAttached::setTopMargin(qreal m) | 
| 535 | { | 
| 536 |     const bool changed = topMargin() != m; | 
| 537 |     m_margins.setTop(m); | 
| 538 |     m_isTopMarginSet = true; | 
| 539 |     if (changed) { | 
| 540 |         invalidateItem(); | 
| 541 |         emit topMarginChanged(); | 
| 542 |     } | 
| 543 | } | 
| 544 |  | 
| 545 | void QQuickLayoutAttached::resetTopMargin() | 
| 546 | { | 
| 547 |     const bool changed = m_isTopMarginSet && (m_defaultMargins != m_margins.top()); | 
| 548 |     m_isTopMarginSet = false; | 
| 549 |     if (changed) { | 
| 550 |         invalidateItem(); | 
| 551 |         emit topMarginChanged(); | 
| 552 |     } | 
| 553 | } | 
| 554 |  | 
| 555 | /*! | 
| 556 |     \qmlattachedproperty real Layout::rightMargin | 
| 557 |  | 
| 558 |     Specifies the right margin outside of an item. | 
| 559 |     If the value is not set, it will use the value from \l margins. | 
| 560 |  | 
| 561 |     \sa margins | 
| 562 |  | 
| 563 |     \since QtQuick.Layouts 1.2 | 
| 564 | */ | 
| 565 | void QQuickLayoutAttached::setRightMargin(qreal m) | 
| 566 | { | 
| 567 |     const bool changed = rightMargin() != m; | 
| 568 |     m_margins.setRight(m); | 
| 569 |     m_isRightMarginSet = true; | 
| 570 |     if (changed) { | 
| 571 |         invalidateItem(); | 
| 572 |         emit rightMarginChanged(); | 
| 573 |     } | 
| 574 | } | 
| 575 |  | 
| 576 | void QQuickLayoutAttached::resetRightMargin() | 
| 577 | { | 
| 578 |     const bool changed = m_isRightMarginSet && (m_defaultMargins != m_margins.right()); | 
| 579 |     m_isRightMarginSet = false; | 
| 580 |     if (changed) { | 
| 581 |         invalidateItem(); | 
| 582 |         emit rightMarginChanged(); | 
| 583 |     } | 
| 584 | } | 
| 585 |  | 
| 586 | /*! | 
| 587 |     \qmlattachedproperty real Layout::bottomMargin | 
| 588 |  | 
| 589 |     Specifies the bottom margin outside of an item. | 
| 590 |     If the value is not set, it will use the value from \l margins. | 
| 591 |  | 
| 592 |     \sa margins | 
| 593 |  | 
| 594 |     \since QtQuick.Layouts 1.2 | 
| 595 | */ | 
| 596 | void QQuickLayoutAttached::setBottomMargin(qreal m) | 
| 597 | { | 
| 598 |     const bool changed = bottomMargin() != m; | 
| 599 |     m_margins.setBottom(m); | 
| 600 |     m_isBottomMarginSet = true; | 
| 601 |     if (changed) { | 
| 602 |         invalidateItem(); | 
| 603 |         emit bottomMarginChanged(); | 
| 604 |     } | 
| 605 | } | 
| 606 |  | 
| 607 | void QQuickLayoutAttached::resetBottomMargin() | 
| 608 | { | 
| 609 |     const bool changed = m_isBottomMarginSet && (m_defaultMargins != m_margins.bottom()); | 
| 610 |     m_isBottomMarginSet = false; | 
| 611 |     if (changed) { | 
| 612 |         invalidateItem(); | 
| 613 |         emit bottomMarginChanged(); | 
| 614 |     } | 
| 615 | } | 
| 616 |  | 
| 617 |  | 
| 618 | /*! | 
| 619 |     \qmlattachedproperty int Layout::rowSpan | 
| 620 |  | 
| 621 |     This property allows you to specify the row span of an item in a \l GridLayout. | 
| 622 |  | 
| 623 |     The default value is \c 1. | 
| 624 |  | 
| 625 |     \sa columnSpan | 
| 626 |     \sa row | 
| 627 | */ | 
| 628 | void QQuickLayoutAttached::setRowSpan(int span) | 
| 629 | { | 
| 630 |     if (span != m_rowSpan) { | 
| 631 |         m_rowSpan = span; | 
| 632 |         invalidateItem(); | 
| 633 |         emit rowSpanChanged(); | 
| 634 |     } | 
| 635 | } | 
| 636 |  | 
| 637 |  | 
| 638 | /*! | 
| 639 |     \qmlattachedproperty int Layout::columnSpan | 
| 640 |  | 
| 641 |     This property allows you to specify the column span of an item in a \l GridLayout. | 
| 642 |  | 
| 643 |     The default value is \c 1. | 
| 644 |  | 
| 645 |     \sa rowSpan | 
| 646 |     \sa column | 
| 647 | */ | 
| 648 | void QQuickLayoutAttached::setColumnSpan(int span) | 
| 649 | { | 
| 650 |     if (span != m_columnSpan) { | 
| 651 |         m_columnSpan = span; | 
| 652 |         invalidateItem(); | 
| 653 |         emit columnSpanChanged(); | 
| 654 |     } | 
| 655 | } | 
| 656 |  | 
| 657 |  | 
| 658 | qreal QQuickLayoutAttached::sizeHint(Qt::SizeHint which, Qt::Orientation orientation) const | 
| 659 | { | 
| 660 |     qreal result = 0; | 
| 661 |     if (QQuickLayout *layout = qobject_cast<QQuickLayout *>(object: item())) { | 
| 662 |         const QSizeF sz = layout->sizeHint(whichSizeHint: which); | 
| 663 |         result = (orientation == Qt::Horizontal ? sz.width() : sz.height()); | 
| 664 |     } else { | 
| 665 |         if (which == Qt::MaximumSize) | 
| 666 |             result = std::numeric_limits<qreal>::infinity(); | 
| 667 |     } | 
| 668 |     return result; | 
| 669 | } | 
| 670 |  | 
| 671 | void QQuickLayoutAttached::invalidateItem() | 
| 672 | { | 
| 673 |     qCDebug(lcQuickLayouts) << "QQuickLayoutAttached::invalidateItem" ; | 
| 674 |     if (QQuickLayout *layout = parentLayout()) { | 
| 675 |         layout->invalidate(childItem: item()); | 
| 676 |     } | 
| 677 | } | 
| 678 |  | 
| 679 | QQuickLayout *QQuickLayoutAttached::parentLayout() const | 
| 680 | { | 
| 681 |     QQuickItem *parentItem = item(); | 
| 682 |     if (parentItem) { | 
| 683 |         parentItem = parentItem->parentItem(); | 
| 684 |         return qobject_cast<QQuickLayout *>(object: parentItem); | 
| 685 |     } else { | 
| 686 |         qmlWarning(me: parent()) << "Layout must be attached to Item elements" ; | 
| 687 |     } | 
| 688 |     return nullptr; | 
| 689 | } | 
| 690 |  | 
| 691 | QQuickItem *QQuickLayoutAttached::item() const | 
| 692 | { | 
| 693 |     return qobject_cast<QQuickItem *>(object: parent()); | 
| 694 | } | 
| 695 |  | 
| 696 | qreal QQuickLayoutPrivate::getImplicitWidth() const | 
| 697 | { | 
| 698 |     Q_Q(const QQuickLayout); | 
| 699 |     if (q->invalidated()) { | 
| 700 |         QQuickLayoutPrivate *that = const_cast<QQuickLayoutPrivate*>(this); | 
| 701 |         that->implicitWidth = q->sizeHint(whichSizeHint: Qt::PreferredSize).width(); | 
| 702 |     } | 
| 703 |     return implicitWidth; | 
| 704 | } | 
| 705 |  | 
| 706 | qreal QQuickLayoutPrivate::getImplicitHeight() const | 
| 707 | { | 
| 708 |     Q_Q(const QQuickLayout); | 
| 709 |     if (q->invalidated()) { | 
| 710 |         QQuickLayoutPrivate *that = const_cast<QQuickLayoutPrivate*>(this); | 
| 711 |         that->implicitHeight = q->sizeHint(whichSizeHint: Qt::PreferredSize).height(); | 
| 712 |     } | 
| 713 |     return implicitHeight; | 
| 714 | } | 
| 715 |  | 
| 716 | void QQuickLayoutPrivate::applySizeHints() const { | 
| 717 |     Q_Q(const QQuickLayout); | 
| 718 |     QQuickLayout *that = const_cast<QQuickLayout*>(q); | 
| 719 |     QQuickLayoutAttached *info = attachedLayoutObject(item: that, create: true); | 
| 720 |  | 
| 721 |     const QSizeF min = q->sizeHint(whichSizeHint: Qt::MinimumSize); | 
| 722 |     const QSizeF max = q->sizeHint(whichSizeHint: Qt::MaximumSize); | 
| 723 |     const QSizeF pref = q->sizeHint(whichSizeHint: Qt::PreferredSize); | 
| 724 |     info->setMinimumImplicitSize(min); | 
| 725 |     info->setMaximumImplicitSize(max); | 
| 726 |     that->setImplicitSize(pref.width(), pref.height()); | 
| 727 | } | 
| 728 |  | 
| 729 | QQuickLayout::QQuickLayout(QQuickLayoutPrivate &dd, QQuickItem *parent) | 
| 730 |     : QQuickItem(dd, parent) | 
| 731 |     , m_inUpdatePolish(false) | 
| 732 |     , m_polishInsideUpdatePolish(0) | 
| 733 | { | 
| 734 | } | 
| 735 |  | 
| 736 | static QQuickItemPrivate::ChangeTypes changeTypes = | 
| 737 |     QQuickItemPrivate::SiblingOrder | 
| 738 |     | QQuickItemPrivate::ImplicitWidth | 
| 739 |     | QQuickItemPrivate::ImplicitHeight | 
| 740 |     | QQuickItemPrivate::Destroyed | 
| 741 |     | QQuickItemPrivate::Visibility; | 
| 742 |  | 
| 743 | QQuickLayout::~QQuickLayout() | 
| 744 | { | 
| 745 |     d_func()->m_isReady = false; | 
| 746 |  | 
| 747 |     const auto childItems = d_func()->childItems; | 
| 748 |     for (QQuickItem *child : childItems) | 
| 749 |         QQuickItemPrivate::get(item: child)->removeItemChangeListener(this, types: changeTypes); | 
| 750 | } | 
| 751 |  | 
| 752 | QQuickLayoutAttached *QQuickLayout::qmlAttachedProperties(QObject *object) | 
| 753 | { | 
| 754 |     return new QQuickLayoutAttached(object); | 
| 755 | } | 
| 756 |  | 
| 757 | void QQuickLayout::updatePolish() | 
| 758 | { | 
| 759 |     qCDebug(lcQuickLayouts) << "updatePolish() ENTERING"  << this; | 
| 760 |     m_inUpdatePolish = true; | 
| 761 |  | 
| 762 |     // Might have become "undirty" before we reach this updatePolish() | 
| 763 |     // (e.g. if somebody queried for implicitWidth it will immediately | 
| 764 |     // calculate size hints) | 
| 765 |     if (invalidated()) { | 
| 766 |         // Ensure that all invalidated layouts are synced and valid again. Since | 
| 767 |         // ensureLayoutItemsUpdated() will also call applySizeHints(), and sizeHint() will call its | 
| 768 |         // childrens sizeHint(), and sizeHint() will call ensureLayoutItemsUpdated(), this will be done | 
| 769 |         // recursive as we want. | 
| 770 |         // Note that we need to call ensureLayoutItemsUpdated() *before* we query width() and height(), | 
| 771 |         // because width()/height() might return their implicitWidth/implicitHeight (e.g. for a layout | 
| 772 |         // with no explicitly specified size, (nor anchors.fill: parent)) | 
| 773 |         ensureLayoutItemsUpdated(); | 
| 774 |     } | 
| 775 |     rearrange(QSizeF(width(), height())); | 
| 776 |     m_inUpdatePolish = false; | 
| 777 |     qCDebug(lcQuickLayouts) << "updatePolish() LEAVING"  << this; | 
| 778 | } | 
| 779 |  | 
| 780 | void QQuickLayout::componentComplete() | 
| 781 | { | 
| 782 |     Q_D(QQuickLayout); | 
| 783 |     d->m_disableRearrange = true; | 
| 784 |     QQuickItem::componentComplete();    // will call our geometryChanged(), (where isComponentComplete() == true) | 
| 785 |     d->m_disableRearrange = false; | 
| 786 |     d->m_isReady = true; | 
| 787 | } | 
| 788 |  | 
| 789 | void QQuickLayout::invalidate(QQuickItem * /*childItem*/) | 
| 790 | { | 
| 791 |     Q_D(QQuickLayout); | 
| 792 |     if (invalidated()) | 
| 793 |         return; | 
| 794 |  | 
| 795 |     qCDebug(lcQuickLayouts) << "QQuickLayout::invalidate()"  << this; | 
| 796 |     d->m_dirty = true; | 
| 797 |     d->m_dirtyArrangement = true; | 
| 798 |  | 
| 799 |     if (!qobject_cast<QQuickLayout *>(object: parentItem())) { | 
| 800 |  | 
| 801 |         if (m_inUpdatePolish) | 
| 802 |             ++m_polishInsideUpdatePolish; | 
| 803 |         else | 
| 804 |             m_polishInsideUpdatePolish = 0; | 
| 805 |  | 
| 806 |         if (m_polishInsideUpdatePolish <= 2) { | 
| 807 |             // allow at most two consecutive loops in order to respond to height-for-width | 
| 808 |             // (e.g QQuickText changes implicitHeight when its width gets changed) | 
| 809 |             qCDebug(lcQuickLayouts) << "QQuickLayout::invalidate(), polish()" ; | 
| 810 |             polish(); | 
| 811 |         } else { | 
| 812 |             qWarning() << "Qt Quick Layouts: Polish loop detected. Aborting after two iterations." ; | 
| 813 |         } | 
| 814 |     } | 
| 815 | } | 
| 816 |  | 
| 817 | bool QQuickLayout::shouldIgnoreItem(QQuickItem *child, QQuickLayoutAttached *&info, QSizeF *sizeHints) const | 
| 818 | { | 
| 819 |     bool ignoreItem = true; | 
| 820 |     QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(item: child); | 
| 821 |     if (childPrivate->explicitVisible) { | 
| 822 |         effectiveSizeHints_helper(item: child, cachedSizeHints: sizeHints, info: &info, useFallbackToWidthOrHeight: true); | 
| 823 |         QSizeF effectiveMaxSize = sizeHints[Qt::MaximumSize]; | 
| 824 |         if (!effectiveMaxSize.isNull()) { | 
| 825 |             QSizeF &prefS = sizeHints[Qt::PreferredSize]; | 
| 826 |             if (effectiveSizePolicy_helper(item: child, orientation: Qt::Horizontal, info) == QLayoutPolicy::Fixed) | 
| 827 |                 effectiveMaxSize.setWidth(prefS.width()); | 
| 828 |             if (effectiveSizePolicy_helper(item: child, orientation: Qt::Vertical, info) == QLayoutPolicy::Fixed) | 
| 829 |                 effectiveMaxSize.setHeight(prefS.height()); | 
| 830 |         } | 
| 831 |         ignoreItem = effectiveMaxSize.isNull(); | 
| 832 |     } | 
| 833 |  | 
| 834 |     if (!ignoreItem && childPrivate->isTransparentForPositioner()) | 
| 835 |         ignoreItem = true; | 
| 836 |  | 
| 837 |     return ignoreItem; | 
| 838 | } | 
| 839 |  | 
| 840 | void QQuickLayout::checkAnchors(QQuickItem *item) const | 
| 841 | { | 
| 842 |     QQuickAnchors *anchors = QQuickItemPrivate::get(item)->_anchors; | 
| 843 |     if (anchors && anchors->activeDirections()) | 
| 844 |         qmlWarning(me: item) << "Detected anchors on an item that is managed by a layout. This is undefined behavior; use Layout.alignment instead." ; | 
| 845 | } | 
| 846 |  | 
| 847 | void QQuickLayout::ensureLayoutItemsUpdated() const | 
| 848 | { | 
| 849 |     Q_D(const QQuickLayout); | 
| 850 |     if (!invalidated()) | 
| 851 |         return; | 
| 852 |     const_cast<QQuickLayout*>(this)->updateLayoutItems(); | 
| 853 |     d->m_dirty = false; | 
| 854 |     d->applySizeHints(); | 
| 855 | } | 
| 856 |  | 
| 857 |  | 
| 858 | void QQuickLayout::itemChange(ItemChange change, const ItemChangeData &value) | 
| 859 | { | 
| 860 |     if (change == ItemChildAddedChange) { | 
| 861 |         Q_D(QQuickLayout); | 
| 862 |         QQuickItem *item = value.item; | 
| 863 |         qmlobject_connect(item, QQuickItem, SIGNAL(baselineOffsetChanged(qreal)), this, QQuickLayout, SLOT(invalidateSenderItem())); | 
| 864 |         QQuickItemPrivate::get(item)->addItemChangeListener(listener: this, types: changeTypes); | 
| 865 |         d->m_hasItemChangeListeners = true; | 
| 866 |         qCDebug(lcQuickLayouts) << "ChildAdded"  << item; | 
| 867 |         if (isReady()) | 
| 868 |             invalidate(); | 
| 869 |     } else if (change == ItemChildRemovedChange) { | 
| 870 |         QQuickItem *item = value.item; | 
| 871 |         qmlobject_disconnect(item, QQuickItem, SIGNAL(baselineOffsetChanged(qreal)), this, QQuickLayout, SLOT(invalidateSenderItem())); | 
| 872 |         QQuickItemPrivate::get(item)->removeItemChangeListener(this, types: changeTypes); | 
| 873 |         qCDebug(lcQuickLayouts) << "ChildRemoved"  << item; | 
| 874 |         if (isReady()) | 
| 875 |             invalidate(); | 
| 876 |     } | 
| 877 |     QQuickItem::itemChange(change, value); | 
| 878 | } | 
| 879 |  | 
| 880 | void QQuickLayout::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) | 
| 881 | { | 
| 882 |     Q_D(QQuickLayout); | 
| 883 |     QQuickItem::geometryChanged(newGeometry, oldGeometry); | 
| 884 |     if (d->m_disableRearrange || !isReady() || !newGeometry.isValid()) | 
| 885 |         return; | 
| 886 |  | 
| 887 |     qCDebug(lcQuickLayouts) << "QQuickLayout::geometryChanged"  << newGeometry << oldGeometry; | 
| 888 |     rearrange(newGeometry.size()); | 
| 889 | } | 
| 890 |  | 
| 891 | void QQuickLayout::invalidateSenderItem() | 
| 892 | { | 
| 893 |     if (!isReady()) | 
| 894 |         return; | 
| 895 |     QQuickItem *item = static_cast<QQuickItem *>(sender()); | 
| 896 |     Q_ASSERT(item); | 
| 897 |     invalidate(item); | 
| 898 | } | 
| 899 |  | 
| 900 | bool QQuickLayout::isReady() const | 
| 901 | { | 
| 902 |     return d_func()->m_isReady; | 
| 903 | } | 
| 904 |  | 
| 905 | /*! | 
| 906 |  * \brief QQuickLayout::deactivateRecur | 
| 907 |  * \internal | 
| 908 |  * | 
| 909 |  * Call this from the dtor of the top-level layout. | 
| 910 |  * Otherwise, it will trigger lots of unneeded item change listeners (itemVisibleChanged()) for all its descendants | 
| 911 |  * that will have its impact thrown away. | 
| 912 |  */ | 
| 913 | void QQuickLayout::deactivateRecur() | 
| 914 | { | 
| 915 |     if (d_func()->m_hasItemChangeListeners) { | 
| 916 |         for (int i = 0; i < itemCount(); ++i) { | 
| 917 |             QQuickItem *item = itemAt(index: i); | 
| 918 |             // When deleting a layout with children, there is no reason for the children to inform the layout that their | 
| 919 |             // e.g. visibility got changed. The layout already knows that all its children will eventually become invisible, so | 
| 920 |             // we therefore remove its change listener. | 
| 921 |             QQuickItemPrivate::get(item)->removeItemChangeListener(this, types: changeTypes); | 
| 922 |             if (QQuickLayout *layout = qobject_cast<QQuickLayout*>(object: item)) | 
| 923 |                 layout->deactivateRecur(); | 
| 924 |         } | 
| 925 |         d_func()->m_hasItemChangeListeners = false; | 
| 926 |     } | 
| 927 | } | 
| 928 |  | 
| 929 | bool QQuickLayout::invalidated() const | 
| 930 | { | 
| 931 |     return d_func()->m_dirty; | 
| 932 | } | 
| 933 |  | 
| 934 | bool QQuickLayout::invalidatedArrangement() const | 
| 935 | { | 
| 936 |     return d_func()->m_dirtyArrangement; | 
| 937 | } | 
| 938 |  | 
| 939 | bool QQuickLayout::isMirrored() const | 
| 940 | { | 
| 941 |     return d_func()->isMirrored(); | 
| 942 | } | 
| 943 |  | 
| 944 | void QQuickLayout::itemSiblingOrderChanged(QQuickItem *item) | 
| 945 | { | 
| 946 |     Q_UNUSED(item); | 
| 947 |     invalidate(); | 
| 948 | } | 
| 949 |  | 
| 950 | void QQuickLayout::itemImplicitWidthChanged(QQuickItem *item) | 
| 951 | { | 
| 952 |     if (!isReady() || item->signalsBlocked()) | 
| 953 |         return; | 
| 954 |     invalidate(item); | 
| 955 | } | 
| 956 |  | 
| 957 | void QQuickLayout::itemImplicitHeightChanged(QQuickItem *item) | 
| 958 | { | 
| 959 |     if (!isReady() || item->signalsBlocked()) | 
| 960 |         return; | 
| 961 |     invalidate(item); | 
| 962 | } | 
| 963 |  | 
| 964 | void QQuickLayout::itemDestroyed(QQuickItem *item) | 
| 965 | { | 
| 966 |     Q_UNUSED(item); | 
| 967 | } | 
| 968 |  | 
| 969 | void QQuickLayout::itemVisibilityChanged(QQuickItem *item) | 
| 970 | { | 
| 971 |     Q_UNUSED(item); | 
| 972 | } | 
| 973 |  | 
| 974 | void QQuickLayout::rearrange(const QSizeF &/*size*/) | 
| 975 | { | 
| 976 |     d_func()->m_dirtyArrangement = false; | 
| 977 | } | 
| 978 |  | 
| 979 |  | 
| 980 | /* | 
| 981 |   The layout engine assumes: | 
| 982 |     1. minimum <= preferred <= maximum | 
| 983 |     2. descent is within minimum and maximum bounds     (### verify) | 
| 984 |  | 
| 985 |     This function helps to ensure that by the following rules (in the following order): | 
| 986 |     1. If minimum > maximum, set minimum = maximum | 
| 987 |     2. Clamp preferred to be between the [minimum,maximum] range. | 
| 988 |     3. If descent > minimum, set descent = minimum      (### verify if this is correct, it might | 
| 989 |                                                         need some refinements to multiline texts) | 
| 990 |  | 
| 991 |     If any values are "not set" (i.e. negative), they will be left untouched, so that we | 
| 992 |     know which values needs to be fetched from the implicit hints (not user hints). | 
| 993 |   */ | 
| 994 | static void normalizeHints(qreal &minimum, qreal &preferred, qreal &maximum, qreal &descent) | 
| 995 | { | 
| 996 |     if (minimum >= 0 && maximum >= 0 && minimum > maximum) | 
| 997 |         minimum = maximum; | 
| 998 |  | 
| 999 |     if (preferred >= 0) { | 
| 1000 |         if (minimum >= 0 && preferred < minimum) { | 
| 1001 |             preferred = minimum; | 
| 1002 |         } else if (maximum >= 0 && preferred > maximum) { | 
| 1003 |             preferred = maximum; | 
| 1004 |         } | 
| 1005 |     } | 
| 1006 |  | 
| 1007 |     if (minimum >= 0 && descent > minimum) | 
| 1008 |         descent = minimum; | 
| 1009 | } | 
| 1010 |  | 
| 1011 | static void boundSize(QSizeF &result, const QSizeF &size) | 
| 1012 | { | 
| 1013 |     if (size.width() >= 0 && size.width() < result.width()) | 
| 1014 |         result.setWidth(size.width()); | 
| 1015 |     if (size.height() >= 0 && size.height() < result.height()) | 
| 1016 |         result.setHeight(size.height()); | 
| 1017 | } | 
| 1018 |  | 
| 1019 | static void expandSize(QSizeF &result, const QSizeF &size) | 
| 1020 | { | 
| 1021 |     if (size.width() >= 0 && size.width() > result.width()) | 
| 1022 |         result.setWidth(size.width()); | 
| 1023 |     if (size.height() >= 0 && size.height() > result.height()) | 
| 1024 |         result.setHeight(size.height()); | 
| 1025 | } | 
| 1026 |  | 
| 1027 | static inline void combineHints(qreal ¤t, qreal fallbackHint) | 
| 1028 | { | 
| 1029 |     if (current < 0) | 
| 1030 |         current = fallbackHint; | 
| 1031 | } | 
| 1032 |  | 
| 1033 | static inline void combineSize(QSizeF &result, const QSizeF &fallbackSize) | 
| 1034 | { | 
| 1035 |     combineHints(current&: result.rwidth(), fallbackHint: fallbackSize.width()); | 
| 1036 |     combineHints(current&: result.rheight(), fallbackHint: fallbackSize.height()); | 
| 1037 | } | 
| 1038 |  | 
| 1039 | static inline void combineImplicitHints(QQuickLayoutAttached *info, Qt::SizeHint which, QSizeF *size) | 
| 1040 | { | 
| 1041 |     if (!info) return; | 
| 1042 |  | 
| 1043 |     Q_ASSERT(which == Qt::MinimumSize || which == Qt::MaximumSize); | 
| 1044 |  | 
| 1045 |     const QSizeF constraint(which == Qt::MinimumSize | 
| 1046 |                             ? QSizeF(info->minimumWidth(), info->minimumHeight()) | 
| 1047 |                             : QSizeF(info->maximumWidth(), info->maximumHeight())); | 
| 1048 |  | 
| 1049 |     if (!info->isExtentExplicitlySet(o: Qt::Horizontal, whichSize: which)) | 
| 1050 |         combineHints(current&: size->rwidth(),  fallbackHint: constraint.width()); | 
| 1051 |     if (!info->isExtentExplicitlySet(o: Qt::Vertical, whichSize: which)) | 
| 1052 |         combineHints(current&: size->rheight(), fallbackHint: constraint.height()); | 
| 1053 | } | 
| 1054 |  | 
| 1055 | typedef qreal (QQuickLayoutAttached::*SizeGetter)() const; | 
| 1056 |  | 
| 1057 | /*! | 
| 1058 |     \internal | 
| 1059 |     Note: Can potentially return the attached QQuickLayoutAttached object through \a attachedInfo. | 
| 1060 |  | 
| 1061 |     It is like this is because it enables it to be reused. | 
| 1062 |  | 
| 1063 |     The goal of this function is to return the effective minimum, preferred and maximum size hints | 
| 1064 |     that the layout will use for this item. | 
| 1065 |     This function takes care of gathering all explicitly set size hints, normalizes them so | 
| 1066 |     that min < pref < max. | 
| 1067 |     Further, the hints _not_explicitly_ set will then be initialized with the implicit size hints, | 
| 1068 |     which is usually derived from the content of the layouts (or items). | 
| 1069 |  | 
| 1070 |     The following table illustrates the preference of the properties used for measuring layout | 
| 1071 |     items. If present, the USER properties will be preferred. If USER properties are not present, | 
| 1072 |     the HINT properties will be preferred. Finally, the FALLBACK properties will be used as an | 
| 1073 |     ultimate fallback. | 
| 1074 |  | 
| 1075 |     Note that one can query if the value of Layout.minimumWidth or Layout.maximumWidth has been | 
| 1076 |     explicitly or implicitly set with QQuickLayoutAttached::isExtentExplicitlySet(). This | 
| 1077 |     determines if it should be used as a USER or as a HINT value. | 
| 1078 |  | 
| 1079 |     Fractional size hints will be ceiled to the closest integer. This is in order to give some | 
| 1080 |     slack when the items are snapped to the pixel grid. | 
| 1081 |  | 
| 1082 |                  | *Minimum*            | *Preferred*           | *Maximum*                | | 
| 1083 | +----------------+----------------------+-----------------------+--------------------------+ | 
| 1084 | |USER (explicit) | Layout.minimumWidth  | Layout.preferredWidth | Layout.maximumWidth      | | 
| 1085 | |HINT (implicit) | Layout.minimumWidth  | implicitWidth         | Layout.maximumWidth      | | 
| 1086 | |FALLBACK        | 0                    | width                 | Number.POSITIVE_INFINITY | | 
| 1087 | +----------------+----------------------+-----------------------+--------------------------+ | 
| 1088 |  */ | 
| 1089 | void QQuickLayout::effectiveSizeHints_helper(QQuickItem *item, QSizeF *cachedSizeHints, QQuickLayoutAttached **attachedInfo, bool useFallbackToWidthOrHeight) | 
| 1090 | { | 
| 1091 |     for (int i = 0; i < Qt::NSizeHints; ++i) | 
| 1092 |         cachedSizeHints[i] = QSizeF(); | 
| 1093 |     QQuickLayoutAttached *info = attachedLayoutObject(item, create: false); | 
| 1094 |     // First, retrieve the user-specified hints from the attached "Layout." properties | 
| 1095 |     if (info) { | 
| 1096 |         struct Getters { | 
| 1097 |             SizeGetter call[NSizes]; | 
| 1098 |         }; | 
| 1099 |  | 
| 1100 |         static Getters horGetters = { | 
| 1101 |             .call: {&QQuickLayoutAttached::minimumWidth, &QQuickLayoutAttached::preferredWidth, &QQuickLayoutAttached::maximumWidth}, | 
| 1102 |         }; | 
| 1103 |  | 
| 1104 |         static Getters verGetters = { | 
| 1105 |             .call: {&QQuickLayoutAttached::minimumHeight, &QQuickLayoutAttached::preferredHeight, &QQuickLayoutAttached::maximumHeight} | 
| 1106 |         }; | 
| 1107 |         for (int i = 0; i < NSizes; ++i) { | 
| 1108 |             SizeGetter getter = horGetters.call[i]; | 
| 1109 |             Q_ASSERT(getter); | 
| 1110 |  | 
| 1111 |             if (info->isExtentExplicitlySet(o: Qt::Horizontal, whichSize: (Qt::SizeHint)i)) | 
| 1112 |                 cachedSizeHints[i].setWidth((info->*getter)()); | 
| 1113 |  | 
| 1114 |             getter = verGetters.call[i]; | 
| 1115 |             Q_ASSERT(getter); | 
| 1116 |             if (info->isExtentExplicitlySet(o: Qt::Vertical, whichSize: (Qt::SizeHint)i)) | 
| 1117 |                 cachedSizeHints[i].setHeight((info->*getter)()); | 
| 1118 |         } | 
| 1119 |     } | 
| 1120 |  | 
| 1121 |     QSizeF &minS = cachedSizeHints[Qt::MinimumSize]; | 
| 1122 |     QSizeF &prefS = cachedSizeHints[Qt::PreferredSize]; | 
| 1123 |     QSizeF &maxS = cachedSizeHints[Qt::MaximumSize]; | 
| 1124 |     QSizeF &descentS = cachedSizeHints[Qt::MinimumDescent]; | 
| 1125 |  | 
| 1126 |     // For instance, will normalize the following user-set hints | 
| 1127 |     // from: [10, 5, 60] | 
| 1128 |     // to:   [10, 10, 60] | 
| 1129 |     normalizeHints(minimum&: minS.rwidth(), preferred&: prefS.rwidth(), maximum&: maxS.rwidth(), descent&: descentS.rwidth()); | 
| 1130 |     normalizeHints(minimum&: minS.rheight(), preferred&: prefS.rheight(), maximum&: maxS.rheight(), descent&: descentS.rheight()); | 
| 1131 |  | 
| 1132 |     // All explicit values gathered, now continue to gather the implicit sizes | 
| 1133 |  | 
| 1134 |     //--- GATHER MAXIMUM SIZE HINTS --- | 
| 1135 |     combineImplicitHints(info, which: Qt::MaximumSize, size: &maxS); | 
| 1136 |     combineSize(result&: maxS, fallbackSize: QSizeF(std::numeric_limits<qreal>::infinity(), std::numeric_limits<qreal>::infinity())); | 
| 1137 |     // implicit max or min sizes should not limit an explicitly set preferred size | 
| 1138 |     expandSize(result&: maxS, size: prefS); | 
| 1139 |     expandSize(result&: maxS, size: minS); | 
| 1140 |  | 
| 1141 |     //--- GATHER MINIMUM SIZE HINTS --- | 
| 1142 |     combineImplicitHints(info, which: Qt::MinimumSize, size: &minS); | 
| 1143 |     expandSize(result&: minS, size: QSizeF(0,0)); | 
| 1144 |     boundSize(result&: minS, size: prefS); | 
| 1145 |     boundSize(result&: minS, size: maxS); | 
| 1146 |  | 
| 1147 |     //--- GATHER PREFERRED SIZE HINTS --- | 
| 1148 |     // First, from implicitWidth/Height | 
| 1149 |     qreal &prefWidth = prefS.rwidth(); | 
| 1150 |     qreal &prefHeight = prefS.rheight(); | 
| 1151 |     if (prefWidth < 0 && item->implicitWidth() > 0) | 
| 1152 |         prefWidth = qCeil(v: item->implicitWidth()); | 
| 1153 |     if (prefHeight < 0 &&  item->implicitHeight() > 0) | 
| 1154 |         prefHeight = qCeil(v: item->implicitHeight()); | 
| 1155 |  | 
| 1156 |     // If that fails, make an ultimate fallback to width/height | 
| 1157 |     if (useFallbackToWidthOrHeight && !prefS.isValid()) { | 
| 1158 |         /* If we want to support using width/height as preferred size hints in | 
| 1159 |            layouts, (which we think most people expect), we only want to use the | 
| 1160 |            initial width. | 
| 1161 |            This is because the width will change due to layout rearrangement, | 
| 1162 |            and the preferred width should return the same value, regardless of | 
| 1163 |            the current width. | 
| 1164 |            We therefore store this initial width in the attached layout object | 
| 1165 |            and reuse it if needed rather than querying the width another time. | 
| 1166 |            That means we need to ensure that an Layout attached object is available | 
| 1167 |            by creating one if necessary. | 
| 1168 |         */ | 
| 1169 |         if (!info) | 
| 1170 |             info = attachedLayoutObject(item); | 
| 1171 |  | 
| 1172 |         auto updatePreferredSizes = [](qreal &cachedSize, qreal &attachedSize, qreal size) { | 
| 1173 |             if (cachedSize < 0) { | 
| 1174 |                 if (attachedSize < 0) | 
| 1175 |                     attachedSize = size; | 
| 1176 |  | 
| 1177 |                 cachedSize = attachedSize; | 
| 1178 |             } | 
| 1179 |         }; | 
| 1180 |         updatePreferredSizes(prefWidth, info->m_fallbackWidth, item->width()); | 
| 1181 |         updatePreferredSizes(prefHeight, info->m_fallbackHeight, item->height()); | 
| 1182 |     } | 
| 1183 |  | 
| 1184 |     // Normalize again after the implicit hints have been gathered | 
| 1185 |     expandSize(result&: prefS, size: minS); | 
| 1186 |     boundSize(result&: prefS, size: maxS); | 
| 1187 |  | 
| 1188 |     //--- GATHER DESCENT | 
| 1189 |     // Minimum descent is only applicable for the effective minimum height, | 
| 1190 |     // so we gather the descent last. | 
| 1191 |     const qreal minimumDescent = minS.height() - item->baselineOffset(); | 
| 1192 |     descentS.setHeight(minimumDescent); | 
| 1193 |  | 
| 1194 |     if (info) { | 
| 1195 |         QMarginsF margins = info->qMargins(); | 
| 1196 |         QSizeF (margins.left() + margins.right(), margins.top() + margins.bottom()); | 
| 1197 |         minS += extraMargins; | 
| 1198 |         prefS += extraMargins; | 
| 1199 |         maxS += extraMargins; | 
| 1200 |         descentS += extraMargins; | 
| 1201 |     } | 
| 1202 |     if (attachedInfo) | 
| 1203 |         *attachedInfo = info; | 
| 1204 | } | 
| 1205 |  | 
| 1206 | /*! | 
| 1207 |     \internal | 
| 1208 |  | 
| 1209 |     Assumes \a info is set (if the object has an attached property) | 
| 1210 |  */ | 
| 1211 | QLayoutPolicy::Policy QQuickLayout::effectiveSizePolicy_helper(QQuickItem *item, Qt::Orientation orientation, QQuickLayoutAttached *info) | 
| 1212 | { | 
| 1213 |     bool fillExtent = false; | 
| 1214 |     bool isSet = false; | 
| 1215 |     if (info) { | 
| 1216 |         if (orientation == Qt::Horizontal) { | 
| 1217 |             isSet = info->isFillWidthSet(); | 
| 1218 |             if (isSet) fillExtent = info->fillWidth(); | 
| 1219 |         } else { | 
| 1220 |             isSet = info->isFillHeightSet(); | 
| 1221 |             if (isSet) fillExtent = info->fillHeight(); | 
| 1222 |         } | 
| 1223 |     } | 
| 1224 |     if (!isSet && qobject_cast<QQuickLayout*>(object: item)) | 
| 1225 |         fillExtent = true; | 
| 1226 |     return fillExtent ? QLayoutPolicy::Preferred : QLayoutPolicy::Fixed; | 
| 1227 |  | 
| 1228 | } | 
| 1229 |  | 
| 1230 | void QQuickLayout::_q_dumpLayoutTree() const | 
| 1231 | { | 
| 1232 |     QString buf; | 
| 1233 |     dumpLayoutTreeRecursive(level: 0, buf); | 
| 1234 |     qDebug(msg: "\n%s" , qPrintable(buf)); | 
| 1235 | } | 
| 1236 |  | 
| 1237 | void QQuickLayout::dumpLayoutTreeRecursive(int level, QString &buf) const | 
| 1238 | { | 
| 1239 |     auto formatLine = [&level](const char *fmt) { | 
| 1240 |         QString ss(level *4, QLatin1Char(' ')); | 
| 1241 |         return QString::fromLatin1(str: "%1%2\n" ).arg(a: ss).arg(a: fmt); | 
| 1242 |     }; | 
| 1243 |  | 
| 1244 |     auto f2s = [](qreal f) { | 
| 1245 |         return QString::number(f); | 
| 1246 |     }; | 
| 1247 |     auto b2s = [](bool b) { | 
| 1248 |         static const char *strBool[] = {"false" , "true" }; | 
| 1249 |         return QLatin1String(strBool[int(b)]); | 
| 1250 |     }; | 
| 1251 |  | 
| 1252 |     buf += formatLine("%1 {" ).arg(a: QQmlMetaType::prettyTypeName(object: this)); | 
| 1253 |     ++level; | 
| 1254 |     buf += formatLine("// Effective calculated values:" ); | 
| 1255 |     buf += formatLine("sizeHintDirty: %2" ).arg(a: invalidated()); | 
| 1256 |     QSizeF min = sizeHint(whichSizeHint: Qt::MinimumSize); | 
| 1257 |     buf += formatLine("sizeHint.min : [%1, %2]" ).arg(a: f2s(min.width()), fieldWidth: 5).arg(a: min.height(), fieldWidth: 5); | 
| 1258 |     QSizeF pref = sizeHint(whichSizeHint: Qt::PreferredSize); | 
| 1259 |     buf += formatLine("sizeHint.pref: [%1, %2]" ).arg(a: pref.width(), fieldWidth: 5).arg(a: pref.height(), fieldWidth: 5); | 
| 1260 |     QSizeF max = sizeHint(whichSizeHint: Qt::MaximumSize); | 
| 1261 |     buf += formatLine("sizeHint.max : [%1, %2]" ).arg(a: f2s(max.width()), fieldWidth: 5).arg(a: f2s(max.height()), fieldWidth: 5); | 
| 1262 |  | 
| 1263 |     for (QQuickItem *item : childItems()) { | 
| 1264 |         buf += QLatin1Char('\n'); | 
| 1265 |         if (QQuickLayout *childLayout = qobject_cast<QQuickLayout*>(object: item)) { | 
| 1266 |             childLayout->dumpLayoutTreeRecursive(level, buf); | 
| 1267 |         } else { | 
| 1268 |             buf += formatLine("%1 {" ).arg(a: QQmlMetaType::prettyTypeName(object: item)); | 
| 1269 |             ++level; | 
| 1270 |             if (item->implicitWidth() > 0) | 
| 1271 |                 buf += formatLine("implicitWidth: %1" ).arg(a: f2s(item->implicitWidth())); | 
| 1272 |             if (item->implicitHeight() > 0) | 
| 1273 |                 buf += formatLine("implicitHeight: %1" ).arg(a: f2s(item->implicitHeight())); | 
| 1274 |             QSizeF min; | 
| 1275 |             QSizeF pref; | 
| 1276 |             QSizeF max; | 
| 1277 |             QQuickLayoutAttached *info = attachedLayoutObject(item, create: false); | 
| 1278 |             if (info) { | 
| 1279 |                 min = QSizeF(info->minimumWidth(), info->minimumHeight()); | 
| 1280 |                 pref = QSizeF(info->preferredWidth(), info->preferredHeight()); | 
| 1281 |                 max = QSizeF(info->maximumWidth(), info->maximumHeight()); | 
| 1282 |                 if (info->isExtentExplicitlySet(o: Qt::Horizontal, whichSize: Qt::MinimumSize)) | 
| 1283 |                     buf += formatLine("Layout.minimumWidth: %1" ).arg(a: f2s(min.width())); | 
| 1284 |                 if (info->isExtentExplicitlySet(o: Qt::Vertical, whichSize: Qt::MinimumSize)) | 
| 1285 |                     buf += formatLine("Layout.minimumHeight: %1" ).arg(a: f2s(min.height())); | 
| 1286 |                 if (pref.width() >= 0) | 
| 1287 |                     buf += formatLine("Layout.preferredWidth: %1" ).arg(a: f2s(pref.width())); | 
| 1288 |                 if (pref.height() >= 0) | 
| 1289 |                     buf += formatLine("Layout.preferredHeight: %1" ).arg(a: f2s(pref.height())); | 
| 1290 |                 if (info->isExtentExplicitlySet(o: Qt::Horizontal, whichSize: Qt::MaximumSize)) | 
| 1291 |                     buf += formatLine("Layout.maximumWidth: %1" ).arg(a: f2s(max.width())); | 
| 1292 |                 if (info->isExtentExplicitlySet(o: Qt::Vertical, whichSize: Qt::MaximumSize)) | 
| 1293 |                     buf += formatLine("Layout.maximumHeight: %1" ).arg(a: f2s(max.height())); | 
| 1294 |  | 
| 1295 |                 if (info->isFillWidthSet()) | 
| 1296 |                     buf += formatLine("Layout.fillWidth: %1" ).arg(a: b2s(info->fillWidth())); | 
| 1297 |                 if (info->isFillHeightSet()) | 
| 1298 |                     buf += formatLine("Layout.fillHeight: %1" ).arg(a: b2s(info->fillHeight())); | 
| 1299 |             } | 
| 1300 |             --level; | 
| 1301 |             buf += formatLine("}" ); | 
| 1302 |         } | 
| 1303 |     } | 
| 1304 |     --level; | 
| 1305 |     buf += formatLine("}" ); | 
| 1306 | } | 
| 1307 |  | 
| 1308 | QT_END_NAMESPACE | 
| 1309 |  |