1 // Commit: 1f38d41854fa2daa51d938a4eb398752b1751e0b
2 /****************************************************************************
4 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
5 ** All rights reserved.
6 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 ** This file is part of the QtDeclarative module of the Qt Toolkit.
10 ** $QT_BEGIN_LICENSE:LGPL$
11 ** No Commercial Usage
12 ** This file contains pre-release code and may not be distributed.
13 ** You may use this file in accordance with the terms and conditions
14 ** contained in the Technology Preview License Agreement accompanying
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 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
41 ****************************************************************************/
43 #include "qsgpositioners_p.h"
44 #include "qsgpositioners_p_p.h"
46 #include <QtDeclarative/qdeclarative.h>
47 #include <QtDeclarative/qdeclarativeinfo.h>
48 #include <QtCore/qmath.h>
49 #include <QtCore/qcoreapplication.h>
51 #include <private/qdeclarativestate_p.h>
52 #include <private/qdeclarativestategroup_p.h>
53 #include <private/qdeclarativestateoperations_p.h>
57 static const QSGItemPrivate::ChangeTypes watchedChanges
58 = QSGItemPrivate::Geometry
59 | QSGItemPrivate::SiblingOrder
60 | QSGItemPrivate::Visibility
61 | QSGItemPrivate::Opacity
62 | QSGItemPrivate::Destroyed;
64 void QSGBasePositionerPrivate::watchChanges(QSGItem *other)
66 QSGItemPrivate *otherPrivate = QSGItemPrivate::get(other);
67 otherPrivate->addItemChangeListener(this, watchedChanges);
70 void QSGBasePositionerPrivate::unwatchChanges(QSGItem* other)
72 QSGItemPrivate *otherPrivate = QSGItemPrivate::get(other);
73 otherPrivate->removeItemChangeListener(this, watchedChanges);
76 QSGBasePositioner::QSGBasePositioner(PositionerType at, QSGItem *parent)
77 : QSGImplicitSizeItem(*(new QSGBasePositionerPrivate), parent)
79 Q_D(QSGBasePositioner);
83 QSGBasePositioner::QSGBasePositioner(QSGBasePositionerPrivate &dd, PositionerType at, QSGItem *parent)
84 : QSGImplicitSizeItem(dd, parent)
86 Q_D(QSGBasePositioner);
90 QSGBasePositioner::~QSGBasePositioner()
92 Q_D(QSGBasePositioner);
93 for (int i = 0; i < positionedItems.count(); ++i)
94 d->unwatchChanges(positionedItems.at(i).item);
95 positionedItems.clear();
98 int QSGBasePositioner::spacing() const
100 Q_D(const QSGBasePositioner);
104 void QSGBasePositioner::setSpacing(int s)
106 Q_D(QSGBasePositioner);
111 emit spacingChanged();
114 QDeclarativeTransition *QSGBasePositioner::move() const
116 Q_D(const QSGBasePositioner);
117 return d->moveTransition;
120 void QSGBasePositioner::setMove(QDeclarativeTransition *mt)
122 Q_D(QSGBasePositioner);
123 if (mt == d->moveTransition)
125 d->moveTransition = mt;
129 QDeclarativeTransition *QSGBasePositioner::add() const
131 Q_D(const QSGBasePositioner);
132 return d->addTransition;
135 void QSGBasePositioner::setAdd(QDeclarativeTransition *add)
137 Q_D(QSGBasePositioner);
138 if (add == d->addTransition)
141 d->addTransition = add;
145 void QSGBasePositioner::componentComplete()
147 QSGItem::componentComplete();
148 positionedItems.reserve(childItems().count());
150 reportConflictingAnchors();
153 void QSGBasePositioner::itemChange(ItemChange change, const ItemChangeData &value)
155 Q_D(QSGBasePositioner);
156 if (change == ItemChildAddedChange){
158 } else if (change == ItemChildRemovedChange) {
159 QSGItem *child = value.item;
160 QSGBasePositioner::PositionedItem posItem(child);
161 int idx = positionedItems.find(posItem);
163 d->unwatchChanges(child);
164 positionedItems.remove(idx);
169 QSGItem::itemChange(change, value);
172 void QSGBasePositioner::prePositioning()
174 Q_D(QSGBasePositioner);
175 if (!isComponentComplete())
178 if (d->doingPositioning)
181 d->queuedPositioning = false;
182 d->doingPositioning = true;
183 //Need to order children by creation order modified by stacking order
184 QList<QSGItem *> children = childItems();
186 QPODVector<PositionedItem,8> oldItems;
187 positionedItems.copyAndClear(oldItems);
188 for (int ii = 0; ii < children.count(); ++ii) {
189 QSGItem *child = children.at(ii);
190 QSGItemPrivate *childPrivate = QSGItemPrivate::get(child);
191 PositionedItem *item = 0;
192 PositionedItem posItem(child);
193 int wIdx = oldItems.find(posItem);
195 d->watchChanges(child);
196 positionedItems.append(posItem);
197 item = &positionedItems[positionedItems.count()-1];
199 if (child->opacity() <= 0.0 || !childPrivate->explicitVisible || !child->width() || !child->height())
200 item->isVisible = false;
202 item = &oldItems[wIdx];
203 // Items are only omitted from positioning if they are explicitly hidden
204 // i.e. their positioning is not affected if an ancestor is hidden.
205 if (child->opacity() <= 0.0 || !childPrivate->explicitVisible || !child->width() || !child->height()) {
206 item->isVisible = false;
207 } else if (!item->isVisible) {
208 item->isVisible = true;
213 positionedItems.append(*item);
217 doPositioning(&contentSize);
218 if(d->addTransition || d->moveTransition)
219 finishApplyTransitions();
220 d->doingPositioning = false;
221 //Set implicit size to the size of its children
222 setImplicitHeight(contentSize.height());
223 setImplicitWidth(contentSize.width());
226 void QSGBasePositioner::positionX(int x, const PositionedItem &target)
228 Q_D(QSGBasePositioner);
229 if(d->type == Horizontal || d->type == Both){
231 if (!d->addTransition)
232 target.item->setX(x);
234 d->addActions << QDeclarativeAction(target.item, QLatin1String("x"), QVariant(x));
235 } else if (x != target.item->x()) {
236 if (!d->moveTransition)
237 target.item->setX(x);
239 d->moveActions << QDeclarativeAction(target.item, QLatin1String("x"), QVariant(x));
244 void QSGBasePositioner::positionY(int y, const PositionedItem &target)
246 Q_D(QSGBasePositioner);
247 if(d->type == Vertical || d->type == Both){
249 if (!d->addTransition)
250 target.item->setY(y);
252 d->addActions << QDeclarativeAction(target.item, QLatin1String("y"), QVariant(y));
253 } else if (y != target.item->y()) {
254 if (!d->moveTransition)
255 target.item->setY(y);
257 d->moveActions << QDeclarativeAction(target.item, QLatin1String("y"), QVariant(y));
262 void QSGBasePositioner::finishApplyTransitions()
264 Q_D(QSGBasePositioner);
265 // Note that if a transition is not set the transition manager will
266 // apply the changes directly, in the case add/move aren't set
267 d->addTransitionManager.transition(d->addActions, d->addTransition);
268 d->moveTransitionManager.transition(d->moveActions, d->moveTransition);
269 d->addActions.clear();
270 d->moveActions.clear();
273 QSGColumn::QSGColumn(QSGItem *parent)
274 : QSGBasePositioner(Vertical, parent)
278 void QSGColumn::doPositioning(QSizeF *contentSize)
282 for (int ii = 0; ii < positionedItems.count(); ++ii) {
283 const PositionedItem &child = positionedItems.at(ii);
284 if (!child.item || !child.isVisible)
287 if(child.item->y() != voffset)
288 positionY(voffset, child);
290 contentSize->setWidth(qMax(contentSize->width(), child.item->width()));
292 voffset += child.item->height();
293 voffset += spacing();
296 contentSize->setHeight(voffset - spacing());
299 void QSGColumn::reportConflictingAnchors()
301 QSGBasePositionerPrivate *d = static_cast<QSGBasePositionerPrivate*>(QSGBasePositionerPrivate::get(this));
302 for (int ii = 0; ii < positionedItems.count(); ++ii) {
303 const PositionedItem &child = positionedItems.at(ii);
305 QSGAnchors *anchors = QSGItemPrivate::get(static_cast<QSGItem *>(child.item))->_anchors;
307 QSGAnchors::Anchors usedAnchors = anchors->usedAnchors();
308 if (usedAnchors & QSGAnchors::TopAnchor ||
309 usedAnchors & QSGAnchors::BottomAnchor ||
310 usedAnchors & QSGAnchors::VCenterAnchor ||
311 anchors->fill() || anchors->centerIn()) {
312 d->anchorConflict = true;
318 if (d->anchorConflict) {
319 qmlInfo(this) << "Cannot specify top, bottom, verticalCenter, fill or centerIn anchors for items inside Column";
323 QSGRow::QSGRow(QSGItem *parent)
324 : QSGBasePositioner(Horizontal, parent)
328 Qt::LayoutDirection QSGRow::layoutDirection() const
330 return QSGBasePositionerPrivate::getLayoutDirection(this);
333 void QSGRow::setLayoutDirection(Qt::LayoutDirection layoutDirection)
335 QSGBasePositionerPrivate *d = static_cast<QSGBasePositionerPrivate* >(QSGBasePositionerPrivate::get(this));
336 if (d->layoutDirection != layoutDirection) {
337 d->layoutDirection = layoutDirection;
338 // For RTL layout the positioning changes when the width changes.
339 if (d->layoutDirection == Qt::RightToLeft)
340 d->addItemChangeListener(d, QSGItemPrivate::Geometry);
342 d->removeItemChangeListener(d, QSGItemPrivate::Geometry);
344 emit layoutDirectionChanged();
345 emit effectiveLayoutDirectionChanged();
349 Qt::LayoutDirection QSGRow::effectiveLayoutDirection() const
351 return QSGBasePositionerPrivate::getEffectiveLayoutDirection(this);
354 void QSGRow::doPositioning(QSizeF *contentSize)
356 QSGBasePositionerPrivate *d = static_cast<QSGBasePositionerPrivate* >(QSGBasePositionerPrivate::get(this));
360 for (int ii = 0; ii < positionedItems.count(); ++ii) {
361 const PositionedItem &child = positionedItems.at(ii);
362 if (!child.item || !child.isVisible)
365 if (d->isLeftToRight()) {
366 if (child.item->x() != hoffset)
367 positionX(hoffset, child);
372 contentSize->setHeight(qMax(contentSize->height(), child.item->height()));
374 hoffset += child.item->width();
375 hoffset += spacing();
378 contentSize->setWidth(hoffset - spacing());
380 if (d->isLeftToRight())
383 //Right to Left layout
386 end = contentSize->width();
391 for (int ii = 0; ii < positionedItems.count(); ++ii) {
392 const PositionedItem &child = positionedItems.at(ii);
393 if (!child.item || !child.isVisible)
395 hoffset = end - hoffsets[acc++] - child.item->width();
396 if (child.item->x() != hoffset)
397 positionX(hoffset, child);
401 void QSGRow::reportConflictingAnchors()
403 QSGBasePositionerPrivate *d = static_cast<QSGBasePositionerPrivate*>(QSGBasePositionerPrivate::get(this));
404 for (int ii = 0; ii < positionedItems.count(); ++ii) {
405 const PositionedItem &child = positionedItems.at(ii);
407 QSGAnchors *anchors = QSGItemPrivate::get(static_cast<QSGItem *>(child.item))->_anchors;
409 QSGAnchors::Anchors usedAnchors = anchors->usedAnchors();
410 if (usedAnchors & QSGAnchors::LeftAnchor ||
411 usedAnchors & QSGAnchors::RightAnchor ||
412 usedAnchors & QSGAnchors::HCenterAnchor ||
413 anchors->fill() || anchors->centerIn()) {
414 d->anchorConflict = true;
420 if (d->anchorConflict)
421 qmlInfo(this) << "Cannot specify left, right, horizontalCenter, fill or centerIn anchors for items inside Row";
424 QSGGrid::QSGGrid(QSGItem *parent) :
425 QSGBasePositioner(Both, parent), m_rows(-1), m_columns(-1), m_flow(LeftToRight)
429 void QSGGrid::setColumns(const int columns)
431 if (columns == m_columns)
435 emit columnsChanged();
438 void QSGGrid::setRows(const int rows)
447 QSGGrid::Flow QSGGrid::flow() const
452 void QSGGrid::setFlow(Flow flow)
454 if (m_flow != flow) {
461 Qt::LayoutDirection QSGGrid::layoutDirection() const
463 return QSGBasePositionerPrivate::getLayoutDirection(this);
466 void QSGGrid::setLayoutDirection(Qt::LayoutDirection layoutDirection)
468 QSGBasePositionerPrivate *d = static_cast<QSGBasePositionerPrivate*>(QSGBasePositionerPrivate::get(this));
469 if (d->layoutDirection != layoutDirection) {
470 d->layoutDirection = layoutDirection;
471 // For RTL layout the positioning changes when the width changes.
472 if (d->layoutDirection == Qt::RightToLeft)
473 d->addItemChangeListener(d, QSGItemPrivate::Geometry);
475 d->removeItemChangeListener(d, QSGItemPrivate::Geometry);
477 emit layoutDirectionChanged();
478 emit effectiveLayoutDirectionChanged();
482 Qt::LayoutDirection QSGGrid::effectiveLayoutDirection() const
484 return QSGBasePositionerPrivate::getEffectiveLayoutDirection(this);
487 void QSGGrid::doPositioning(QSizeF *contentSize)
489 QSGBasePositionerPrivate *d = static_cast<QSGBasePositionerPrivate*>(QSGBasePositionerPrivate::get(this));
492 //Is allocating the extra QPODVector too much overhead?
493 QPODVector<PositionedItem, 8> visibleItems;//we aren't concerned with invisible items
494 visibleItems.reserve(positionedItems.count());
495 for(int i=0; i<positionedItems.count(); i++)
496 if(positionedItems[i].item && positionedItems[i].isVisible)
497 visibleItems.append(positionedItems[i]);
499 int numVisible = visibleItems.count();
500 if (m_columns <= 0 && m_rows <= 0){
502 r = (numVisible+3)/4;
503 } else if (m_rows <= 0){
504 r = (numVisible+(m_columns-1))/m_columns;
505 } else if (m_columns <= 0){
506 c = (numVisible+(m_rows-1))/m_rows;
510 return; //Nothing to do
512 QList<int> maxColWidth;
513 QList<int> maxRowHeight;
515 if (m_flow == LeftToRight) {
516 for (int i=0; i < r; i++){
517 for (int j=0; j < c; j++){
523 if (childIndex == visibleItems.count())
526 const PositionedItem &child = visibleItems.at(childIndex++);
527 if (child.item->width() > maxColWidth[j])
528 maxColWidth[j] = child.item->width();
529 if (child.item->height() > maxRowHeight[i])
530 maxRowHeight[i] = child.item->height();
534 for (int j=0; j < c; j++){
535 for (int i=0; i < r; i++){
541 if (childIndex == visibleItems.count())
544 const PositionedItem &child = visibleItems.at(childIndex++);
545 if (child.item->width() > maxColWidth[j])
546 maxColWidth[j] = child.item->width();
547 if (child.item->height() > maxRowHeight[i])
548 maxRowHeight[i] = child.item->height();
554 for (int j=0; j < maxColWidth.size(); j++){
556 widthSum += spacing();
557 widthSum += maxColWidth[j];
561 for (int i=0; i < maxRowHeight.size(); i++){
563 heightSum += spacing();
564 heightSum += maxRowHeight[i];
567 contentSize->setHeight(heightSum);
568 contentSize->setWidth(widthSum);
577 if (!d->isLeftToRight())
582 for (int i = 0; i < visibleItems.count(); ++i) {
583 const PositionedItem &child = visibleItems.at(i);
584 int childXOffset = xoffset;
585 if (!d->isLeftToRight())
586 childXOffset -= child.item->width();
587 if ((child.item->x() != childXOffset) || (child.item->y() != yoffset)){
588 positionX(childXOffset, child);
589 positionY(yoffset, child);
592 if (m_flow == LeftToRight) {
593 if (d->isLeftToRight())
594 xoffset += maxColWidth[curCol]+spacing();
596 xoffset -= maxColWidth[curCol]+spacing();
600 yoffset += maxRowHeight[curRow]+spacing();
601 if (d->isLeftToRight())
610 yoffset+=maxRowHeight[curRow]+spacing();
614 if (d->isLeftToRight())
615 xoffset += maxColWidth[curCol]+spacing();
617 xoffset -= maxColWidth[curCol]+spacing();
627 void QSGGrid::reportConflictingAnchors()
629 QSGBasePositionerPrivate *d = static_cast<QSGBasePositionerPrivate*>(QSGBasePositionerPrivate::get(this));
630 for (int ii = 0; ii < positionedItems.count(); ++ii) {
631 const PositionedItem &child = positionedItems.at(ii);
633 QSGAnchors *anchors = QSGItemPrivate::get(static_cast<QSGItem *>(child.item))->_anchors;
634 if (anchors && (anchors->usedAnchors() || anchors->fill() || anchors->centerIn())) {
635 d->anchorConflict = true;
640 if (d->anchorConflict)
641 qmlInfo(this) << "Cannot specify anchors for items inside Grid";
644 class QSGFlowPrivate : public QSGBasePositionerPrivate
646 Q_DECLARE_PUBLIC(QSGFlow)
650 : QSGBasePositionerPrivate(), flow(QSGFlow::LeftToRight)
656 QSGFlow::QSGFlow(QSGItem *parent)
657 : QSGBasePositioner(*(new QSGFlowPrivate), Both, parent)
660 // Flow layout requires relayout if its own size changes too.
661 d->addItemChangeListener(d, QSGItemPrivate::Geometry);
664 QSGFlow::Flow QSGFlow::flow() const
670 void QSGFlow::setFlow(Flow flow)
673 if (d->flow != flow) {
680 Qt::LayoutDirection QSGFlow::layoutDirection() const
683 return d->layoutDirection;
686 void QSGFlow::setLayoutDirection(Qt::LayoutDirection layoutDirection)
689 if (d->layoutDirection != layoutDirection) {
690 d->layoutDirection = layoutDirection;
692 emit layoutDirectionChanged();
693 emit effectiveLayoutDirectionChanged();
697 Qt::LayoutDirection QSGFlow::effectiveLayoutDirection() const
699 return QSGBasePositionerPrivate::getEffectiveLayoutDirection(this);
702 void QSGFlow::doPositioning(QSizeF *contentSize)
711 for (int i = 0; i < positionedItems.count(); ++i) {
712 const PositionedItem &child = positionedItems.at(i);
713 if (!child.item || !child.isVisible)
716 if (d->flow == LeftToRight) {
717 if (widthValid() && hoffset && hoffset + child.item->width() > width()) {
719 voffset += linemax + spacing();
723 if (heightValid() && voffset && voffset + child.item->height() > height()) {
725 hoffset += linemax + spacing();
730 if (d->isLeftToRight()) {
731 if (child.item->x() != hoffset)
732 positionX(hoffset, child);
736 if (child.item->y() != voffset)
737 positionY(voffset, child);
739 contentSize->setWidth(qMax(contentSize->width(), hoffset + child.item->width()));
740 contentSize->setHeight(qMax(contentSize->height(), voffset + child.item->height()));
742 if (d->flow == LeftToRight) {
743 hoffset += child.item->width();
744 hoffset += spacing();
745 linemax = qMax(linemax, qCeil(child.item->height()));
747 voffset += child.item->height();
748 voffset += spacing();
749 linemax = qMax(linemax, qCeil(child.item->width()));
752 if (d->isLeftToRight())
759 end = contentSize->width();
761 for (int i = 0; i < positionedItems.count(); ++i) {
762 const PositionedItem &child = positionedItems.at(i);
763 if (!child.item || !child.isVisible)
765 hoffset = end - hoffsets[acc++] - child.item->width();
766 if (child.item->x() != hoffset)
767 positionX(hoffset, child);
771 void QSGFlow::reportConflictingAnchors()
774 for (int ii = 0; ii < positionedItems.count(); ++ii) {
775 const PositionedItem &child = positionedItems.at(ii);
777 QSGAnchors *anchors = QSGItemPrivate::get(static_cast<QSGItem *>(child.item))->_anchors;
778 if (anchors && (anchors->usedAnchors() || anchors->fill() || anchors->centerIn())) {
779 d->anchorConflict = true;
784 if (d->anchorConflict)
785 qmlInfo(this) << "Cannot specify anchors for items inside Flow";