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 "qquicklinearlayout_p.h" |
41 | #include "qquickgridlayoutengine_p.h" |
42 | #include "qquicklayoutstyleinfo_p.h" |
43 | #include <QtCore/private/qnumeric_p.h> |
44 | #include <QtQml/qqmlinfo.h> |
45 | #include "qdebug.h" |
46 | #include <limits> |
47 | |
48 | /*! |
49 | \qmltype RowLayout |
50 | \instantiates QQuickRowLayout |
51 | \inherits Item |
52 | \inqmlmodule QtQuick.Layouts |
53 | \ingroup layouts |
54 | \brief Identical to \l GridLayout, but having only one row. |
55 | |
56 | It is available as a convenience for developers, as it offers a cleaner API. |
57 | |
58 | Items in a RowLayout support these attached properties: |
59 | \list |
60 | \input layout.qdocinc attached-properties |
61 | \endlist |
62 | |
63 | \image rowlayout.png |
64 | |
65 | \code |
66 | RowLayout { |
67 | id: layout |
68 | anchors.fill: parent |
69 | spacing: 6 |
70 | Rectangle { |
71 | color: 'teal' |
72 | Layout.fillWidth: true |
73 | Layout.minimumWidth: 50 |
74 | Layout.preferredWidth: 100 |
75 | Layout.maximumWidth: 300 |
76 | Layout.minimumHeight: 150 |
77 | Text { |
78 | anchors.centerIn: parent |
79 | text: parent.width + 'x' + parent.height |
80 | } |
81 | } |
82 | Rectangle { |
83 | color: 'plum' |
84 | Layout.fillWidth: true |
85 | Layout.minimumWidth: 100 |
86 | Layout.preferredWidth: 200 |
87 | Layout.preferredHeight: 100 |
88 | Text { |
89 | anchors.centerIn: parent |
90 | text: parent.width + 'x' + parent.height |
91 | } |
92 | } |
93 | } |
94 | \endcode |
95 | |
96 | Read more about attached properties \l{QML Object Attributes}{here}. |
97 | \sa ColumnLayout |
98 | \sa GridLayout |
99 | \sa Row |
100 | */ |
101 | |
102 | /*! |
103 | \qmltype ColumnLayout |
104 | \instantiates QQuickColumnLayout |
105 | \inherits Item |
106 | \inqmlmodule QtQuick.Layouts |
107 | \ingroup layouts |
108 | \brief Identical to \l GridLayout, but having only one column. |
109 | |
110 | It is available as a convenience for developers, as it offers a cleaner API. |
111 | |
112 | Items in a ColumnLayout support these attached properties: |
113 | \list |
114 | \input layout.qdocinc attached-properties |
115 | \endlist |
116 | |
117 | \image columnlayout.png |
118 | |
119 | \code |
120 | ColumnLayout{ |
121 | spacing: 2 |
122 | |
123 | Rectangle { |
124 | Layout.alignment: Qt.AlignCenter |
125 | color: "red" |
126 | Layout.preferredWidth: 40 |
127 | Layout.preferredHeight: 40 |
128 | } |
129 | |
130 | Rectangle { |
131 | Layout.alignment: Qt.AlignRight |
132 | color: "green" |
133 | Layout.preferredWidth: 40 |
134 | Layout.preferredHeight: 70 |
135 | } |
136 | |
137 | Rectangle { |
138 | Layout.alignment: Qt.AlignBottom |
139 | Layout.fillHeight: true |
140 | color: "blue" |
141 | Layout.preferredWidth: 70 |
142 | Layout.preferredHeight: 40 |
143 | } |
144 | } |
145 | \endcode |
146 | |
147 | Read more about attached properties \l{QML Object Attributes}{here}. |
148 | |
149 | \sa RowLayout |
150 | \sa GridLayout |
151 | \sa Column |
152 | */ |
153 | |
154 | |
155 | /*! |
156 | \qmltype GridLayout |
157 | \instantiates QQuickGridLayout |
158 | \inherits Item |
159 | \inqmlmodule QtQuick.Layouts |
160 | \ingroup layouts |
161 | \brief Provides a way of dynamically arranging items in a grid. |
162 | |
163 | |
164 | |
165 | If the GridLayout is resized, all items in the layout will be rearranged. It is similar |
166 | to the widget-based QGridLayout. All visible children of the GridLayout element will belong to |
167 | the layout. If you want a layout with just one row or one column, you can use the |
168 | \l RowLayout or \l ColumnLayout. These offer a bit more convenient API, and improve |
169 | readability. |
170 | |
171 | By default items will be arranged according to the \l flow property. The default value of |
172 | the \l flow property is \c GridLayout.LeftToRight. |
173 | |
174 | If the \l columns property is specified, it will be treated as a maximum limit of how many |
175 | columns the layout can have, before the auto-positioning wraps back to the beginning of the |
176 | next row. The \l columns property is only used when \l flow is \c GridLayout.LeftToRight. |
177 | |
178 | \image gridlayout.png |
179 | |
180 | \code |
181 | GridLayout { |
182 | id: grid |
183 | columns: 3 |
184 | |
185 | Text { text: "Three"; font.bold: true; } |
186 | Text { text: "words"; color: "red" } |
187 | Text { text: "in"; font.underline: true } |
188 | Text { text: "a"; font.pixelSize: 20 } |
189 | Text { text: "row"; font.strikeout: true } |
190 | } |
191 | \endcode |
192 | |
193 | The \l rows property works in a similar way, but items are auto-positioned vertically. The \l |
194 | rows property is only used when \l flow is \c GridLayout.TopToBottom. |
195 | |
196 | You can specify which cell you want an item to occupy by setting the |
197 | \l{Layout::row}{Layout.row} and \l{Layout::column}{Layout.column} properties. You can also |
198 | specify the row span or column span by setting the \l{Layout::rowSpan}{Layout.rowSpan} or |
199 | \l{Layout::columnSpan}{Layout.columnSpan} properties. |
200 | |
201 | |
202 | Items in a GridLayout support these attached properties: |
203 | \list |
204 | \li \l{Layout::row}{Layout.row} |
205 | \li \l{Layout::column}{Layout.column} |
206 | \li \l{Layout::rowSpan}{Layout.rowSpan} |
207 | \li \l{Layout::columnSpan}{Layout.columnSpan} |
208 | \input layout.qdocinc attached-properties |
209 | \endlist |
210 | |
211 | Read more about attached properties \l{QML Object Attributes}{here}. |
212 | |
213 | \sa RowLayout |
214 | \sa ColumnLayout |
215 | \sa Grid |
216 | */ |
217 | |
218 | |
219 | |
220 | QT_BEGIN_NAMESPACE |
221 | |
222 | QQuickGridLayoutBase::QQuickGridLayoutBase() |
223 | : QQuickLayout(*new QQuickGridLayoutBasePrivate) |
224 | { |
225 | |
226 | } |
227 | |
228 | QQuickGridLayoutBase::QQuickGridLayoutBase(QQuickGridLayoutBasePrivate &dd, |
229 | Qt::Orientation orientation, |
230 | QQuickItem *parent /*= 0*/) |
231 | : QQuickLayout(dd, parent) |
232 | { |
233 | Q_D(QQuickGridLayoutBase); |
234 | d->orientation = orientation; |
235 | d->styleInfo = new QQuickLayoutStyleInfo; |
236 | } |
237 | |
238 | Qt::Orientation QQuickGridLayoutBase::orientation() const |
239 | { |
240 | Q_D(const QQuickGridLayoutBase); |
241 | return d->orientation; |
242 | } |
243 | |
244 | void QQuickGridLayoutBase::setOrientation(Qt::Orientation orientation) |
245 | { |
246 | Q_D(QQuickGridLayoutBase); |
247 | if (d->orientation == orientation) |
248 | return; |
249 | |
250 | d->orientation = orientation; |
251 | invalidate(); |
252 | } |
253 | |
254 | QSizeF QQuickGridLayoutBase::sizeHint(Qt::SizeHint whichSizeHint) const |
255 | { |
256 | Q_D(const QQuickGridLayoutBase); |
257 | ensureLayoutItemsUpdated(); |
258 | return d->engine.sizeHint(which: whichSizeHint, constraint: QSizeF(), styleInfo: d->styleInfo); |
259 | } |
260 | |
261 | /*! |
262 | \qmlproperty enumeration GridLayout::layoutDirection |
263 | \since QtQuick.Layouts 1.1 |
264 | |
265 | This property holds the layout direction of the grid layout - it controls whether items are |
266 | laid out from left to right or right to left. If \c Qt.RightToLeft is specified, |
267 | left-aligned items will be right-aligned and right-aligned items will be left-aligned. |
268 | |
269 | Possible values: |
270 | |
271 | \list |
272 | \li Qt.LeftToRight (default) - Items are laid out from left to right. |
273 | \li Qt.RightToLeft - Items are laid out from right to left. |
274 | \endlist |
275 | |
276 | \sa RowLayout::layoutDirection, ColumnLayout::layoutDirection |
277 | */ |
278 | Qt::LayoutDirection QQuickGridLayoutBase::layoutDirection() const |
279 | { |
280 | Q_D(const QQuickGridLayoutBase); |
281 | return d->m_layoutDirection; |
282 | } |
283 | |
284 | void QQuickGridLayoutBase::setLayoutDirection(Qt::LayoutDirection dir) |
285 | { |
286 | Q_D(QQuickGridLayoutBase); |
287 | if (d->m_layoutDirection == dir) |
288 | return; |
289 | d->m_layoutDirection = dir; |
290 | invalidate(); |
291 | emit layoutDirectionChanged(); |
292 | } |
293 | |
294 | Qt::LayoutDirection QQuickGridLayoutBase::effectiveLayoutDirection() const |
295 | { |
296 | Q_D(const QQuickGridLayoutBase); |
297 | return !d->effectiveLayoutMirror == (layoutDirection() == Qt::LeftToRight) |
298 | ? Qt::LeftToRight : Qt::RightToLeft; |
299 | } |
300 | |
301 | void QQuickGridLayoutBase::setAlignment(QQuickItem *item, Qt::Alignment alignment) |
302 | { |
303 | Q_D(QQuickGridLayoutBase); |
304 | d->engine.setAlignment(quickItem: item, alignment); |
305 | } |
306 | |
307 | QQuickGridLayoutBase::~QQuickGridLayoutBase() |
308 | { |
309 | Q_D(QQuickGridLayoutBase); |
310 | |
311 | // Remove item listeners so we do not act on signalling unnecessarily |
312 | // (there is no point, as the layout will be torn down anyway). |
313 | deactivateRecur(); |
314 | delete d->styleInfo; |
315 | } |
316 | |
317 | void QQuickGridLayoutBase::componentComplete() |
318 | { |
319 | qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::componentComplete()" << this << parent(); |
320 | QQuickLayout::componentComplete(); |
321 | |
322 | /* The layout is invalid when it is constructed, but during construction of the layout and |
323 | its children (in the "static/from QML" case which this is trying to cover) things |
324 | change and as a consequence invalidate() and ensureLayoutItemsUpdated() might be called. |
325 | As soon as ensureLayoutItemsUpdated() is called it will set d->dirty = false. |
326 | However, a subsequent invalidate() will return early if the component is not completed |
327 | because it knows that componentComplete() will take care of doing the proper layouting |
328 | (so it won't set d->dirty = true). When we then call ensureLayoutItemsUpdated() again here |
329 | it sees that its not dirty and assumes everything up-to-date. For those cases we therefore |
330 | need to call invalidate() in advance |
331 | */ |
332 | invalidate(); |
333 | ensureLayoutItemsUpdated(); |
334 | |
335 | QQuickItem *par = parentItem(); |
336 | if (qobject_cast<QQuickLayout*>(object: par)) |
337 | return; |
338 | rearrange(size: QSizeF(width(), height())); |
339 | qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::componentComplete(). COMPLETED" << this << parent(); |
340 | } |
341 | |
342 | /* |
343 | Invalidation happens like this as a reaction to that a size hint changes on an item "a": |
344 | |
345 | Suppose we have the following Qml document: |
346 | RowLayout { |
347 | id: l1 |
348 | RowLayout { |
349 | id: l2 |
350 | Item { |
351 | id: a |
352 | } |
353 | Item { |
354 | id: b |
355 | } |
356 | } |
357 | } |
358 | |
359 | 1. l2->invalidate(a) is called on l2, where item refers to "a". |
360 | (this will dirty the cached size hints of item "a") |
361 | 2. The layout engine will invalidate: |
362 | i) invalidate the layout engine |
363 | ii) dirty the cached size hints of item "l2" (by calling parentLayout()->invalidate(l2) |
364 | The recursion continues to the topmost layout |
365 | */ |
366 | /*! |
367 | \internal |
368 | |
369 | Invalidates \a childItem and this layout. |
370 | After a call to invalidate, the next call to retrieve e.g. sizeHint will be up-to date. |
371 | This function will also call QQuickLayout::invalidate(0), to ensure that the parent layout |
372 | is invalidated. |
373 | */ |
374 | void QQuickGridLayoutBase::invalidate(QQuickItem *childItem) |
375 | { |
376 | Q_D(QQuickGridLayoutBase); |
377 | if (!isReady()) |
378 | return; |
379 | qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::invalidate()" << this << ", invalidated:" << invalidated(); |
380 | if (invalidated()) { |
381 | return; |
382 | } |
383 | qCDebug(lcQuickLayouts) << "d->m_rearranging:" << d->m_rearranging; |
384 | if (d->m_rearranging) { |
385 | d->m_invalidateAfterRearrange << childItem; |
386 | return; |
387 | } |
388 | |
389 | if (childItem) { |
390 | if (QQuickGridLayoutItem *layoutItem = d->engine.findLayoutItem(layoutItem: childItem)) |
391 | layoutItem->invalidate(); |
392 | } |
393 | // invalidate engine |
394 | d->engine.invalidate(); |
395 | |
396 | qCDebug(lcQuickLayouts) << "calling QQuickLayout::invalidate();" ; |
397 | QQuickLayout::invalidate(); |
398 | |
399 | if (QQuickLayout *parentLayout = qobject_cast<QQuickLayout *>(object: parentItem())) |
400 | parentLayout->invalidate(childItem: this); |
401 | qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::invalidate() LEAVING" << this; |
402 | } |
403 | |
404 | void QQuickGridLayoutBase::updateLayoutItems() |
405 | { |
406 | Q_D(QQuickGridLayoutBase); |
407 | if (!isReady()) |
408 | return; |
409 | if (d->m_rearranging) { |
410 | d->m_updateAfterRearrange = true; |
411 | return; |
412 | } |
413 | |
414 | qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::updateLayoutItems ENTERING" << this; |
415 | d->engine.deleteItems(); |
416 | insertLayoutItems(); |
417 | qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::updateLayoutItems() LEAVING" << this; |
418 | } |
419 | |
420 | QQuickItem *QQuickGridLayoutBase::itemAt(int index) const |
421 | { |
422 | Q_D(const QQuickGridLayoutBase); |
423 | qCDebug(lcQuickLayouts).nospace() << "QQuickGridLayoutBase::itemAt(" << index << ")" ; |
424 | ensureLayoutItemsUpdated(); |
425 | qCDebug(lcQuickLayouts).nospace() << "QQuickGridLayoutBase::itemAt(" << index << ") LEAVING" ; |
426 | return static_cast<QQuickGridLayoutItem*>(d->engine.itemAt(index))->layoutItem(); |
427 | } |
428 | |
429 | int QQuickGridLayoutBase::itemCount() const |
430 | { |
431 | Q_D(const QQuickGridLayoutBase); |
432 | ensureLayoutItemsUpdated(); |
433 | return d->engine.itemCount(); |
434 | } |
435 | |
436 | void QQuickGridLayoutBase::removeGridItem(QGridLayoutItem *gridItem) |
437 | { |
438 | Q_D(QQuickGridLayoutBase); |
439 | const int index = gridItem->firstRow(orientation: d->orientation); |
440 | d->engine.removeItem(item: gridItem); |
441 | d->engine.removeRows(row: index, count: 1, orientation: d->orientation); |
442 | } |
443 | |
444 | void QQuickGridLayoutBase::itemDestroyed(QQuickItem *item) |
445 | { |
446 | if (!isReady()) |
447 | return; |
448 | Q_D(QQuickGridLayoutBase); |
449 | qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::itemDestroyed" ; |
450 | if (QQuickGridLayoutItem *gridItem = d->engine.findLayoutItem(layoutItem: item)) { |
451 | removeGridItem(gridItem); |
452 | delete gridItem; |
453 | invalidate(); |
454 | } |
455 | } |
456 | |
457 | void QQuickGridLayoutBase::itemVisibilityChanged(QQuickItem *item) |
458 | { |
459 | Q_UNUSED(item); |
460 | |
461 | if (!isReady()) |
462 | return; |
463 | qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::itemVisibilityChanged()" ; |
464 | invalidate(childItem: item); |
465 | } |
466 | |
467 | void QQuickGridLayoutBase::rearrange(const QSizeF &size) |
468 | { |
469 | Q_D(QQuickGridLayoutBase); |
470 | if (!isReady()) |
471 | return; |
472 | |
473 | qCDebug(lcQuickLayouts) << "QQuickGridLayoutBase::rearrange" << d->m_recurRearrangeCounter << this; |
474 | const auto refCounter = qScopeGuard(f: [&d] { |
475 | --(d->m_recurRearrangeCounter); |
476 | }); |
477 | if (d->m_recurRearrangeCounter++ == 2) { |
478 | // allow a recursive depth of two in order to respond to height-for-width |
479 | // (e.g QQuickText changes implicitHeight when its width gets changed) |
480 | qWarning() << "Qt Quick Layouts: Detected recursive rearrange. Aborting after two iterations." ; |
481 | return; |
482 | } |
483 | |
484 | ensureLayoutItemsUpdated(); |
485 | |
486 | d->m_rearranging = true; |
487 | qCDebug(lcQuickLayouts) << objectName() << "QQuickGridLayoutBase::rearrange()" << size; |
488 | Qt::LayoutDirection visualDir = effectiveLayoutDirection(); |
489 | d->engine.setVisualDirection(visualDir); |
490 | |
491 | /* |
492 | qreal left, top, right, bottom; |
493 | left = top = right = bottom = 0; // ### support for margins? |
494 | if (visualDir == Qt::RightToLeft) |
495 | qSwap(left, right); |
496 | */ |
497 | |
498 | // Set m_dirty to false in case size hint changes during arrangement. |
499 | // This could happen if there is a binding like implicitWidth: height |
500 | QQuickLayout::rearrange(size); |
501 | d->engine.setGeometries(contentsGeometry: QRectF(QPointF(0,0), size), styleInfo: d->styleInfo); |
502 | d->m_rearranging = false; |
503 | |
504 | for (QQuickItem *invalid : qAsConst(t&: d->m_invalidateAfterRearrange)) |
505 | invalidate(childItem: invalid); |
506 | d->m_invalidateAfterRearrange.clear(); |
507 | |
508 | if (d->m_updateAfterRearrange) { |
509 | ensureLayoutItemsUpdated(); |
510 | d->m_updateAfterRearrange = false; |
511 | } |
512 | } |
513 | |
514 | /********************************** |
515 | ** |
516 | ** QQuickGridLayout |
517 | ** |
518 | **/ |
519 | QQuickGridLayout::QQuickGridLayout(QQuickItem *parent /* = 0*/) |
520 | : QQuickGridLayoutBase(*new QQuickGridLayoutPrivate, Qt::Horizontal, parent) |
521 | { |
522 | } |
523 | |
524 | /*! |
525 | \qmlproperty real GridLayout::columnSpacing |
526 | |
527 | This property holds the spacing between each column. |
528 | The default value is \c 5. |
529 | */ |
530 | qreal QQuickGridLayout::columnSpacing() const |
531 | { |
532 | Q_D(const QQuickGridLayout); |
533 | return d->engine.spacing(orientation: Qt::Horizontal, styleInfo: d->styleInfo); |
534 | } |
535 | |
536 | void QQuickGridLayout::setColumnSpacing(qreal spacing) |
537 | { |
538 | Q_D(QQuickGridLayout); |
539 | if (qt_is_nan(d: spacing) || columnSpacing() == spacing) |
540 | return; |
541 | |
542 | d->engine.setSpacing(spacing, orientations: Qt::Horizontal); |
543 | invalidate(); |
544 | emit columnSpacingChanged(); |
545 | } |
546 | |
547 | /*! |
548 | \qmlproperty real GridLayout::rowSpacing |
549 | |
550 | This property holds the spacing between each row. |
551 | The default value is \c 5. |
552 | */ |
553 | qreal QQuickGridLayout::rowSpacing() const |
554 | { |
555 | Q_D(const QQuickGridLayout); |
556 | return d->engine.spacing(orientation: Qt::Vertical, styleInfo: d->styleInfo); |
557 | } |
558 | |
559 | void QQuickGridLayout::setRowSpacing(qreal spacing) |
560 | { |
561 | Q_D(QQuickGridLayout); |
562 | if (qt_is_nan(d: spacing) || rowSpacing() == spacing) |
563 | return; |
564 | |
565 | d->engine.setSpacing(spacing, orientations: Qt::Vertical); |
566 | invalidate(); |
567 | emit rowSpacingChanged(); |
568 | } |
569 | |
570 | /*! |
571 | \qmlproperty int GridLayout::columns |
572 | |
573 | This property holds the column limit for items positioned if \l flow is |
574 | \c GridLayout.LeftToRight. |
575 | The default value is that there is no limit. |
576 | */ |
577 | int QQuickGridLayout::columns() const |
578 | { |
579 | Q_D(const QQuickGridLayout); |
580 | return d->columns; |
581 | } |
582 | |
583 | void QQuickGridLayout::setColumns(int columns) |
584 | { |
585 | Q_D(QQuickGridLayout); |
586 | if (d->columns == columns) |
587 | return; |
588 | d->columns = columns; |
589 | invalidate(); |
590 | emit columnsChanged(); |
591 | } |
592 | |
593 | |
594 | /*! |
595 | \qmlproperty int GridLayout::rows |
596 | |
597 | This property holds the row limit for items positioned if \l flow is \c GridLayout.TopToBottom. |
598 | The default value is that there is no limit. |
599 | */ |
600 | int QQuickGridLayout::rows() const |
601 | { |
602 | Q_D(const QQuickGridLayout); |
603 | return d->rows; |
604 | } |
605 | |
606 | void QQuickGridLayout::setRows(int rows) |
607 | { |
608 | Q_D(QQuickGridLayout); |
609 | if (d->rows == rows) |
610 | return; |
611 | d->rows = rows; |
612 | invalidate(); |
613 | emit rowsChanged(); |
614 | } |
615 | |
616 | |
617 | /*! |
618 | \qmlproperty enumeration GridLayout::flow |
619 | |
620 | This property holds the flow direction of items that does not have an explicit cell |
621 | position set. |
622 | It is used together with the \l columns or \l rows property, where |
623 | they specify when flow is reset to the next row or column respectively. |
624 | |
625 | Possible values are: |
626 | |
627 | \list |
628 | \li GridLayout.LeftToRight (default) - Items are positioned next to |
629 | each other, then wrapped to the next line. |
630 | \li GridLayout.TopToBottom - Items are positioned next to each |
631 | other from top to bottom, then wrapped to the next column. |
632 | \endlist |
633 | |
634 | \sa rows |
635 | \sa columns |
636 | */ |
637 | QQuickGridLayout::Flow QQuickGridLayout::flow() const |
638 | { |
639 | Q_D(const QQuickGridLayout); |
640 | return d->flow; |
641 | } |
642 | |
643 | void QQuickGridLayout::setFlow(QQuickGridLayout::Flow flow) |
644 | { |
645 | Q_D(QQuickGridLayout); |
646 | if (d->flow == flow) |
647 | return; |
648 | d->flow = flow; |
649 | // If flow is changed, the layout needs to be repopulated |
650 | invalidate(); |
651 | emit flowChanged(); |
652 | } |
653 | |
654 | void QQuickGridLayout::insertLayoutItems() |
655 | { |
656 | Q_D(QQuickGridLayout); |
657 | |
658 | int nextCellPos[2] = {0,0}; |
659 | int &nextColumn = nextCellPos[0]; |
660 | int &nextRow = nextCellPos[1]; |
661 | |
662 | const QSize gridSize(columns(), rows()); |
663 | const int flowOrientation = flow(); |
664 | int &flowColumn = nextCellPos[flowOrientation]; |
665 | int &flowRow = nextCellPos[1 - flowOrientation]; |
666 | int flowBound = (flowOrientation == QQuickGridLayout::LeftToRight) ? columns() : rows(); |
667 | |
668 | if (flowBound < 0) |
669 | flowBound = std::numeric_limits<int>::max(); |
670 | |
671 | QSizeF sizeHints[Qt::NSizeHints]; |
672 | const auto items = childItems(); |
673 | for (QQuickItem *child : items) { |
674 | checkAnchors(item: child); |
675 | QQuickLayoutAttached *info = nullptr; |
676 | |
677 | // Will skip all items with effective maximum width/height == 0 |
678 | if (shouldIgnoreItem(child, info, sizeHints)) |
679 | continue; |
680 | |
681 | Qt::Alignment alignment; |
682 | int row = -1; |
683 | int column = -1; |
684 | int span[2] = {1,1}; |
685 | int &columnSpan = span[0]; |
686 | int &rowSpan = span[1]; |
687 | |
688 | if (info) { |
689 | if (info->isRowSet() || info->isColumnSet()) { |
690 | // If row is specified and column is not specified (or vice versa), |
691 | // the unspecified component of the cell position should default to 0 |
692 | // The getters do this for us, as they will return 0 for an |
693 | // unset (or negative) value. |
694 | // In addition, if \a rows is less than Layout.row, treat Layout.row as if it was not set |
695 | // This will basically make it find the next available cell (potentially wrapping to |
696 | // the next line). Likewise for an invalid Layout.column |
697 | |
698 | if (gridSize.height() >= 0 && row >= gridSize.height()) { |
699 | qmlWarning(me: child) << QString::fromLatin1(str: "Layout: row (%1) should be less than the number of rows (%2)" ).arg(a: info->row()).arg(a: rows()); |
700 | } else { |
701 | row = info->row(); |
702 | } |
703 | |
704 | if (gridSize.width() >= 0 && info->column() >= gridSize.width()) { |
705 | qmlWarning(me: child) << QString::fromLatin1(str: "Layout: column (%1) should be less than the number of columns (%2)" ).arg(a: info->column()).arg(a: columns()); |
706 | } else { |
707 | column = info->column(); |
708 | } |
709 | } |
710 | rowSpan = info->rowSpan(); |
711 | columnSpan = info->columnSpan(); |
712 | if (columnSpan < 1) { |
713 | qmlWarning(me: child) << "Layout: invalid column span: " << columnSpan; |
714 | return; |
715 | |
716 | } else if (rowSpan < 1) { |
717 | qmlWarning(me: child) << "Layout: invalid row span: " << rowSpan; |
718 | return; |
719 | } |
720 | alignment = info->alignment(); |
721 | } |
722 | |
723 | Q_ASSERT(columnSpan >= 1); |
724 | Q_ASSERT(rowSpan >= 1); |
725 | const int sp = span[flowOrientation]; |
726 | if (sp > flowBound) |
727 | return; |
728 | |
729 | if (row >= 0) |
730 | nextRow = row; |
731 | if (column >= 0) |
732 | nextColumn = column; |
733 | |
734 | if (row < 0 || column < 0) { |
735 | /* if row or column is not specified, find next position by |
736 | advancing in the flow direction until there is a cell that |
737 | can accept the item. |
738 | |
739 | The acceptance rules are pretty simple, but complexity arises |
740 | when an item requires several cells (due to spans): |
741 | 1. Check if the cells that the item will require |
742 | does not extend beyond columns (for LeftToRight) or |
743 | rows (for TopToBottom). |
744 | 2. Check if the cells that the item will require is not already |
745 | taken by another item. |
746 | */ |
747 | bool cellAcceptsItem; |
748 | while (true) { |
749 | // Check if the item does not span beyond the layout bound |
750 | cellAcceptsItem = (flowColumn + sp) <= flowBound; |
751 | |
752 | // Check if all the required cells are not taken |
753 | for (int rs = 0; cellAcceptsItem && rs < rowSpan; ++rs) { |
754 | for (int cs = 0; cellAcceptsItem && cs < columnSpan; ++cs) { |
755 | if (d->engine.itemAt(row: nextRow + rs, column: nextColumn + cs)) { |
756 | cellAcceptsItem = false; |
757 | } |
758 | } |
759 | } |
760 | if (cellAcceptsItem) |
761 | break; |
762 | ++flowColumn; |
763 | if (flowColumn == flowBound) { |
764 | flowColumn = 0; |
765 | ++flowRow; |
766 | } |
767 | } |
768 | } |
769 | column = nextColumn; |
770 | row = nextRow; |
771 | QQuickGridLayoutItem *layoutItem = new QQuickGridLayoutItem(child, row, column, rowSpan, columnSpan, alignment); |
772 | layoutItem->setCachedSizeHints(sizeHints); |
773 | |
774 | d->engine.insertItem(item: layoutItem, index: -1); |
775 | } |
776 | } |
777 | |
778 | /********************************** |
779 | ** |
780 | ** QQuickLinearLayout |
781 | ** |
782 | **/ |
783 | QQuickLinearLayout::QQuickLinearLayout(Qt::Orientation orientation, |
784 | QQuickItem *parent /*= 0*/) |
785 | : QQuickGridLayoutBase(*new QQuickLinearLayoutPrivate, orientation, parent) |
786 | { |
787 | } |
788 | |
789 | /*! |
790 | \qmlproperty enumeration RowLayout::layoutDirection |
791 | \since QtQuick.Layouts 1.1 |
792 | |
793 | This property holds the layout direction of the row layout - it controls whether items are laid |
794 | out from left ro right or right to left. If \c Qt.RightToLeft is specified, |
795 | left-aligned items will be right-aligned and right-aligned items will be left-aligned. |
796 | |
797 | Possible values: |
798 | |
799 | \list |
800 | \li Qt.LeftToRight (default) - Items are laid out from left to right. |
801 | \li Qt.RightToLeft - Items are laid out from right to left |
802 | \endlist |
803 | |
804 | \sa GridLayout::layoutDirection, ColumnLayout::layoutDirection |
805 | */ |
806 | /*! |
807 | \qmlproperty enumeration ColumnLayout::layoutDirection |
808 | \since QtQuick.Layouts 1.1 |
809 | |
810 | This property holds the layout direction of the column layout - it controls whether items are laid |
811 | out from left ro right or right to left. If \c Qt.RightToLeft is specified, |
812 | left-aligned items will be right-aligned and right-aligned items will be left-aligned. |
813 | |
814 | Possible values: |
815 | |
816 | \list |
817 | \li Qt.LeftToRight (default) - Items are laid out from left to right. |
818 | \li Qt.RightToLeft - Items are laid out from right to left |
819 | \endlist |
820 | |
821 | \sa GridLayout::layoutDirection, RowLayout::layoutDirection |
822 | */ |
823 | |
824 | |
825 | /*! |
826 | \qmlproperty real RowLayout::spacing |
827 | |
828 | This property holds the spacing between each cell. |
829 | The default value is \c 5. |
830 | */ |
831 | /*! |
832 | \qmlproperty real ColumnLayout::spacing |
833 | |
834 | This property holds the spacing between each cell. |
835 | The default value is \c 5. |
836 | */ |
837 | |
838 | qreal QQuickLinearLayout::spacing() const |
839 | { |
840 | Q_D(const QQuickLinearLayout); |
841 | return d->engine.spacing(orientation: d->orientation, styleInfo: d->styleInfo); |
842 | } |
843 | |
844 | void QQuickLinearLayout::setSpacing(qreal space) |
845 | { |
846 | Q_D(QQuickLinearLayout); |
847 | if (qt_is_nan(d: space) || spacing() == space) |
848 | return; |
849 | |
850 | d->engine.setSpacing(spacing: space, orientations: Qt::Horizontal | Qt::Vertical); |
851 | invalidate(); |
852 | emit spacingChanged(); |
853 | } |
854 | |
855 | void QQuickLinearLayout::insertLayoutItems() |
856 | { |
857 | Q_D(QQuickLinearLayout); |
858 | QSizeF sizeHints[Qt::NSizeHints]; |
859 | const auto items = childItems(); |
860 | for (QQuickItem *child : items) { |
861 | Q_ASSERT(child); |
862 | checkAnchors(item: child); |
863 | QQuickLayoutAttached *info = nullptr; |
864 | |
865 | // Will skip all items with effective maximum width/height == 0 |
866 | if (shouldIgnoreItem(child, info, sizeHints)) |
867 | continue; |
868 | |
869 | Qt::Alignment alignment; |
870 | if (info) |
871 | alignment = info->alignment(); |
872 | |
873 | const int index = d->engine.rowCount(orientation: d->orientation); |
874 | d->engine.insertRow(row: index, orientation: d->orientation); |
875 | |
876 | int gridRow = 0; |
877 | int gridColumn = index; |
878 | if (d->orientation == Qt::Vertical) |
879 | qSwap(value1&: gridRow, value2&: gridColumn); |
880 | QQuickGridLayoutItem *layoutItem = new QQuickGridLayoutItem(child, gridRow, gridColumn, 1, 1, alignment); |
881 | layoutItem->setCachedSizeHints(sizeHints); |
882 | d->engine.insertItem(item: layoutItem, index); |
883 | } |
884 | } |
885 | |
886 | QT_END_NAMESPACE |
887 | |
888 | #include "moc_qquicklinearlayout_p.cpp" |
889 | |