/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQuick module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qquickanchors_p_p.h" #include "qquickitem_p.h" #include QT_BEGIN_NAMESPACE static Q_ALWAYS_INLINE QQuickItem *readParentItem(const QQuickItem *item) { return QQuickItemPrivate::get(item)->parentItem; } static Q_ALWAYS_INLINE qreal readX(const QQuickItem *item) { return QQuickItemPrivate::get(item)->x; } static Q_ALWAYS_INLINE qreal readY(const QQuickItem *item) { return QQuickItemPrivate::get(item)->y; } static Q_ALWAYS_INLINE qreal readWidth(const QQuickItem *item) { return QQuickItemPrivate::get(item)->width; } static Q_ALWAYS_INLINE qreal readHeight(const QQuickItem *item) { return QQuickItemPrivate::get(item)->height; } static Q_ALWAYS_INLINE qreal readBaselineOffset(const QQuickItem *item) { return QQuickItemPrivate::get(item)->baselineOffset; } //TODO: should we cache relationships, so we don't have to check each time (parent-child or sibling)? //TODO: support non-parent, non-sibling (need to find lowest common ancestor) static inline qreal hcenter(const QQuickItem *item) { qreal width = readWidth(item); if (QQuickAnchors *anchors = QQuickItemPrivate::get(item)->_anchors) { if (!QQuickAnchorsPrivate::get(anchors)->centerAligned) return width / 2; } int iw = width; if (iw % 2) return (width + 1) / 2; else return width / 2; } static inline qreal vcenter(const QQuickItem *item) { qreal height = readHeight(item); if (QQuickAnchors *anchors = QQuickItemPrivate::get(item)->_anchors) { if (!QQuickAnchorsPrivate::get(anchors)->centerAligned) return height / 2; } int ih = height; if (ih % 2) return (height + 1) / 2; else return height / 2; } //local position static inline qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine) { qreal ret = 0.0; switch (anchorLine) { case QQuickAnchors::LeftAnchor: ret = readX(item); break; case QQuickAnchors::RightAnchor: ret = readX(item) + readWidth(item); break; case QQuickAnchors::TopAnchor: ret = readY(item); break; case QQuickAnchors::BottomAnchor: ret = readY(item) + readHeight(item); break; case QQuickAnchors::HCenterAnchor: ret = readX(item) + hcenter(item); break; case QQuickAnchors::VCenterAnchor: ret = readY(item) + vcenter(item); break; case QQuickAnchors::BaselineAnchor: ret = readY(item) + readBaselineOffset(item); break; default: break; } return ret; } //position when origin is 0,0 static inline qreal adjustedPosition(QQuickItem *item, QQuickAnchors::Anchor anchorLine) { qreal ret = 0.0; switch (anchorLine) { case QQuickAnchors::LeftAnchor: ret = 0.0; break; case QQuickAnchors::RightAnchor: ret = readWidth(item); break; case QQuickAnchors::TopAnchor: ret = 0.0; break; case QQuickAnchors::BottomAnchor: ret = readHeight(item); break; case QQuickAnchors::HCenterAnchor: ret = hcenter(item); break; case QQuickAnchors::VCenterAnchor: ret = vcenter(item); break; case QQuickAnchors::BaselineAnchor: ret = readBaselineOffset(item); break; default: break; } return ret; } QQuickAnchors::QQuickAnchors(QQuickItem *item, QObject *parent) : QObject(*new QQuickAnchorsPrivate(item), parent) { } QQuickAnchors::~QQuickAnchors() { Q_D(QQuickAnchors); d->inDestructor = true; d->remDepend(d->fill); d->remDepend(d->centerIn); d->remDepend(d->leftAnchorItem); d->remDepend(d->rightAnchorItem); d->remDepend(d->topAnchorItem); d->remDepend(d->bottomAnchorItem); d->remDepend(d->vCenterAnchorItem); d->remDepend(d->hCenterAnchorItem); d->remDepend(d->baselineAnchorItem); } void QQuickAnchorsPrivate::fillChanged() { Q_Q(QQuickAnchors); if (!fill || !isItemComplete()) return; if (updatingFill < 2) { ++updatingFill; qreal horizontalMargin = q->mirrored() ? rightMargin : leftMargin; if (fill == readParentItem(item)) { //child-parent setItemPos(QPointF(horizontalMargin, topMargin)); } else if (readParentItem(fill) == readParentItem(item)) { //siblings setItemPos(QPointF(readX(fill)+horizontalMargin, readY(fill) + topMargin)); } setItemSize(QSizeF(readWidth(fill) - leftMargin - rightMargin, readHeight(fill) - topMargin - bottomMargin)); --updatingFill; } else { // ### Make this certain :) qmlWarning(item) << QQuickAnchors::tr("Possible anchor loop detected on fill."); } } void QQuickAnchorsPrivate::centerInChanged() { Q_Q(QQuickAnchors); if (!centerIn || fill || !isItemComplete()) return; if (updatingCenterIn < 2) { ++updatingCenterIn; qreal effectiveHCenterOffset = q->mirrored() ? -hCenterOffset : hCenterOffset; if (centerIn == readParentItem(item)) { QPointF p(hcenter(readParentItem(item)) - hcenter(item) + effectiveHCenterOffset, vcenter(readParentItem(item)) - vcenter(item) + vCenterOffset); setItemPos(p); } else if (readParentItem(centerIn) == readParentItem(item)) { QPointF p(centerIn->x() + hcenter(centerIn) - hcenter(item) + effectiveHCenterOffset, centerIn->y() + vcenter(centerIn) - vcenter(item) + vCenterOffset); setItemPos(p); } --updatingCenterIn; } else { // ### Make this certain :) qmlWarning(item) << QQuickAnchors::tr("Possible anchor loop detected on centerIn."); } } void QQuickAnchorsPrivate::clearItem(QQuickItem *item) { if (!item) return; if (fill == item) fill = nullptr; if (centerIn == item) centerIn = nullptr; if (leftAnchorItem == item) { leftAnchorItem = nullptr; usedAnchors &= ~QQuickAnchors::LeftAnchor; } if (rightAnchorItem == item) { rightAnchorItem = nullptr; usedAnchors &= ~QQuickAnchors::RightAnchor; } if (topAnchorItem == item) { topAnchorItem = nullptr; usedAnchors &= ~QQuickAnchors::TopAnchor; } if (bottomAnchorItem == item) { bottomAnchorItem = nullptr; usedAnchors &= ~QQuickAnchors::BottomAnchor; } if (vCenterAnchorItem == item) { vCenterAnchorItem = nullptr; usedAnchors &= ~QQuickAnchors::VCenterAnchor; } if (hCenterAnchorItem == item) { hCenterAnchorItem = nullptr; usedAnchors &= ~QQuickAnchors::HCenterAnchor; } if (baselineAnchorItem == item) { baselineAnchorItem = nullptr; usedAnchors &= ~QQuickAnchors::BaselineAnchor; } } QQuickGeometryChange QQuickAnchorsPrivate::calculateDependency(QQuickItem *controlItem) const { QQuickGeometryChange dependency; if (!controlItem || inDestructor) return dependency; if (fill == controlItem) { if (controlItem == readParentItem(item)) dependency.setSizeChange(true); else //sibling dependency.setAllChanged(true); return dependency; //exit early } if (centerIn == controlItem) { if (controlItem == readParentItem(item)) dependency.setSizeChange(true); else //sibling dependency.setAllChanged(true); return dependency; //exit early } if ((usedAnchors & QQuickAnchors::LeftAnchor && leftAnchorItem == controlItem) || (usedAnchors & QQuickAnchors::RightAnchor && rightAnchorItem == controlItem) || (usedAnchors & QQuickAnchors::HCenterAnchor && hCenterAnchorItem == controlItem)) { if (controlItem == readParentItem(item)) dependency.setWidthChange(true); else //sibling dependency.setHorizontalChange(true); } if ((usedAnchors & QQuickAnchors::TopAnchor && topAnchorItem == controlItem) || (usedAnchors & QQuickAnchors::BottomAnchor && bottomAnchorItem == controlItem) || (usedAnchors & QQuickAnchors::VCenterAnchor && vCenterAnchorItem == controlItem) || (usedAnchors & QQuickAnchors::BaselineAnchor && baselineAnchorItem == controlItem)) { if (controlItem == readParentItem(item)) dependency.setHeightChange(true); else //sibling dependency.setVerticalChange(true); } return dependency; } void QQuickAnchorsPrivate::addDepend(QQuickItem *item) { if (!item || !componentComplete) return; QQuickItemPrivate *p = QQuickItemPrivate::get(item); p->updateOrAddGeometryChangeListener(this, calculateDependency(item)); } void QQuickAnchorsPrivate::remDepend(QQuickItem *item) { if (!item || !componentComplete) return; QQuickItemPrivate *p = QQuickItemPrivate::get(item); p->updateOrRemoveGeometryChangeListener(this, calculateDependency(item)); } bool QQuickAnchors::mirrored() { Q_D(QQuickAnchors); return QQuickItemPrivate::get(d->item)->effectiveLayoutMirror; } bool QQuickAnchors::alignWhenCentered() const { Q_D(const QQuickAnchors); return d->centerAligned; } void QQuickAnchors::setAlignWhenCentered(bool aligned) { Q_D(QQuickAnchors); if (aligned == d->centerAligned) return; d->centerAligned = aligned; emit centerAlignedChanged(); if (d->centerIn) { d->centerInChanged(); } else { if (d->usedAnchors & QQuickAnchors::VCenterAnchor) d->updateVerticalAnchors(); else if (d->usedAnchors & QQuickAnchors::HCenterAnchor) d->updateHorizontalAnchors(); } } bool QQuickAnchorsPrivate::isItemComplete() const { return componentComplete; } void QQuickAnchors::classBegin() { Q_D(QQuickAnchors); d->componentComplete = false; } void QQuickAnchors::componentComplete() { Q_D(QQuickAnchors); d->componentComplete = true; } void QQuickAnchorsPrivate::setItemHeight(qreal v) { updatingMe = true; item->setHeight(v); updatingMe = false; } void QQuickAnchorsPrivate::setItemWidth(qreal v) { updatingMe = true; item->setWidth(v); updatingMe = false; } void QQuickAnchorsPrivate::setItemX(qreal v) { updatingMe = true; item->setX(v); updatingMe = false; } void QQuickAnchorsPrivate::setItemY(qreal v) { updatingMe = true; item->setY(v); updatingMe = false; } void QQuickAnchorsPrivate::setItemPos(const QPointF &v) { updatingMe = true; item->setPosition(v); updatingMe = false; } void QQuickAnchorsPrivate::setItemSize(const QSizeF &v) { updatingMe = true; item->setSize(v); updatingMe = false; } void QQuickAnchorsPrivate::updateMe() { if (updatingMe) { updatingMe = false; return; } update(); } void QQuickAnchorsPrivate::updateOnComplete() { //optimization to only set initial dependencies once, at completion time QQuickItem *dependencies[9]; dependencies[0] = fill; dependencies[1] = centerIn; dependencies[2] = leftAnchorItem; dependencies[3] = rightAnchorItem; dependencies[4] = hCenterAnchorItem; dependencies[5] = topAnchorItem; dependencies[6] = bottomAnchorItem; dependencies[7] = vCenterAnchorItem; dependencies[8] = baselineAnchorItem; std::sort(dependencies, dependencies + 9); QQuickItem *lastDependency = nullptr; for (int i = 0; i < 9; ++i) { QQuickItem *dependency = dependencies[i]; if (lastDependency != dependency) { addDepend(dependency); lastDependency = dependency; } } update(); } void QQuickAnchorsPrivate::update() { if (!isItemComplete()) return; if (fill) { fillChanged(); } else if (centerIn) { centerInChanged(); } else { if (usedAnchors & QQuickAnchors::Horizontal_Mask) updateHorizontalAnchors(); if (usedAnchors & QQuickAnchors::Vertical_Mask) updateVerticalAnchors(); } } void QQuickAnchorsPrivate::itemGeometryChanged(QQuickItem *, QQuickGeometryChange change, const QRectF &) { if (!isItemComplete()) return; if (fill) { fillChanged(); } else if (centerIn) { centerInChanged(); } else { if ((usedAnchors & QQuickAnchors::Horizontal_Mask) && change.horizontalChange()) updateHorizontalAnchors(); if ((usedAnchors & QQuickAnchors::Vertical_Mask) && change.verticalChange()) updateVerticalAnchors(); } } QQuickItem *QQuickAnchors::fill() const { Q_D(const QQuickAnchors); return d->fill; } void QQuickAnchors::setFill(QQuickItem *f) { Q_D(QQuickAnchors); if (d->fill == f) return; if (!f) { QQuickItem *oldFill = d->fill; d->fill = f; d->remDepend(oldFill); emit fillChanged(); return; } if (f != readParentItem(d->item) && readParentItem(f) != readParentItem(d->item)){ qmlWarning(d->item) << tr("Cannot anchor to an item that isn't a parent or sibling."); return; } QQuickItem *oldFill = d->fill; d->fill = f; d->remDepend(oldFill); d->addDepend(d->fill); emit fillChanged(); d->fillChanged(); } void QQuickAnchors::resetFill() { setFill(nullptr); } QQuickItem *QQuickAnchors::centerIn() const { Q_D(const QQuickAnchors); return d->centerIn; } void QQuickAnchors::setCenterIn(QQuickItem* c) { Q_D(QQuickAnchors); if (d->centerIn == c) return; if (!c) { QQuickItem *oldCI = d->centerIn; d->centerIn = c; d->remDepend(oldCI); emit centerInChanged(); return; } if (c != readParentItem(d->item) && readParentItem(c) != readParentItem(d->item)){ qmlWarning(d->item) << tr("Cannot anchor to an item that isn't a parent or sibling."); return; } QQuickItem *oldCI = d->centerIn; d->centerIn = c; d->remDepend(oldCI); d->addDepend(d->centerIn); emit centerInChanged(); d->centerInChanged(); } void QQuickAnchors::resetCenterIn() { setCenterIn(nullptr); } bool QQuickAnchorsPrivate::calcStretch(QQuickItem *edge1Item, QQuickAnchors::Anchor edge1Line, QQuickItem *edge2Item, QQuickAnchors::Anchor edge2Line, qreal offset1, qreal offset2, QQuickAnchors::Anchor line, qreal &stretch) const { bool edge1IsParent = (edge1Item == readParentItem(item)); bool edge2IsParent = (edge2Item == readParentItem(item)); bool edge1IsSibling = (readParentItem(edge1Item) == readParentItem(item)); bool edge2IsSibling = (readParentItem(edge2Item) == readParentItem(item)); bool invalid = false; if ((edge2IsParent && edge1IsParent) || (edge2IsSibling && edge1IsSibling)) { stretch = (position(edge2Item, edge2Line) + offset2) - (position(edge1Item, edge1Line) + offset1); } else if (edge2IsParent && edge1IsSibling) { stretch = (position(edge2Item, edge2Line) + offset2) - (position(readParentItem(item), line) + position(edge1Item, edge1Line) + offset1); } else if (edge2IsSibling && edge1IsParent) { stretch = (position(readParentItem(item), line) + position(edge2Item, edge2Line) + offset2) - (position(edge1Item, edge1Line) + offset1); } else invalid = true; return invalid; } void QQuickAnchorsPrivate::updateVerticalAnchors() { if (fill || centerIn || !isItemComplete()) return; if (Q_UNLIKELY(updatingVerticalAnchor > 1)) { // ### Make this certain :) qmlWarning(item) << QQuickAnchors::tr("Possible anchor loop detected on vertical anchor."); return; } ++updatingVerticalAnchor; if (usedAnchors & QQuickAnchors::TopAnchor) { //Handle stretching bool invalid = true; qreal height = 0.0; if (usedAnchors & QQuickAnchors::BottomAnchor) { invalid = calcStretch(topAnchorItem, topAnchorLine, bottomAnchorItem, bottomAnchorLine, topMargin, -bottomMargin, QQuickAnchors::TopAnchor, height); } else if (usedAnchors & QQuickAnchors::VCenterAnchor) { invalid = calcStretch(topAnchorItem, topAnchorLine, vCenterAnchorItem, vCenterAnchorLine, topMargin, vCenterOffset, QQuickAnchors::TopAnchor, height); height *= 2; } if (!invalid) setItemHeight(height); //Handle top if (topAnchorItem == readParentItem(item)) { setItemY(adjustedPosition(topAnchorItem, topAnchorLine) + topMargin); } else if (readParentItem(topAnchorItem) == readParentItem(item)) { setItemY(position(topAnchorItem, topAnchorLine) + topMargin); } } else if (usedAnchors & QQuickAnchors::BottomAnchor) { //Handle stretching (top + bottom case is handled above) if (usedAnchors & QQuickAnchors::VCenterAnchor) { qreal height = 0.0; bool invalid = calcStretch(vCenterAnchorItem, vCenterAnchorLine, bottomAnchorItem, bottomAnchorLine, vCenterOffset, -bottomMargin, QQuickAnchors::TopAnchor, height); if (!invalid) setItemHeight(height*2); } //Handle bottom if (bottomAnchorItem == readParentItem(item)) { setItemY(adjustedPosition(bottomAnchorItem, bottomAnchorLine) - readHeight(item) - bottomMargin); } else if (readParentItem(bottomAnchorItem) == readParentItem(item)) { setItemY(position(bottomAnchorItem, bottomAnchorLine) - readHeight(item) - bottomMargin); } } else if (usedAnchors & QQuickAnchors::VCenterAnchor) { //(stetching handled above) //Handle vCenter if (vCenterAnchorItem == readParentItem(item)) { setItemY(adjustedPosition(vCenterAnchorItem, vCenterAnchorLine) - vcenter(item) + vCenterOffset); } else if (readParentItem(vCenterAnchorItem) == readParentItem(item)) { setItemY(position(vCenterAnchorItem, vCenterAnchorLine) - vcenter(item) + vCenterOffset); } } else if (usedAnchors & QQuickAnchors::BaselineAnchor) { //Handle baseline if (baselineAnchorItem == readParentItem(item)) { setItemY(adjustedPosition(baselineAnchorItem, baselineAnchorLine) - readBaselineOffset(item) + baselineOffset); } else if (readParentItem(baselineAnchorItem) == readParentItem(item)) { setItemY(position(baselineAnchorItem, baselineAnchorLine) - readBaselineOffset(item) + baselineOffset); } } --updatingVerticalAnchor; } static inline QQuickAnchors::Anchor reverseAnchorLine(QQuickAnchors::Anchor anchorLine) { if (anchorLine == QQuickAnchors::LeftAnchor) { return QQuickAnchors::RightAnchor; } else if (anchorLine == QQuickAnchors::RightAnchor) { return QQuickAnchors::LeftAnchor; } else { return anchorLine; } } void QQuickAnchorsPrivate::updateHorizontalAnchors() { Q_Q(QQuickAnchors); if (fill || centerIn || !isItemComplete()) return; if (updatingHorizontalAnchor < 3) { ++updatingHorizontalAnchor; qreal effectiveRightMargin, effectiveLeftMargin, effectiveHorizontalCenterOffset; QQuickItem *effectiveLeftItem, *effectiveRightItem, *effectiveHorizontalCenterItem; QQuickAnchors::Anchor effectiveLeftLine, effectiveRightLine, effectiveHorizontalCenterLine; QQuickAnchors::Anchor effectiveLeftAnchor, effectiveRightAnchor; if (q->mirrored()) { effectiveLeftAnchor = QQuickAnchors::RightAnchor; effectiveRightAnchor = QQuickAnchors::LeftAnchor; effectiveLeftItem = rightAnchorItem; effectiveLeftLine = reverseAnchorLine(rightAnchorLine); effectiveRightItem = leftAnchorItem; effectiveRightLine = reverseAnchorLine(leftAnchorLine); effectiveHorizontalCenterItem = hCenterAnchorItem; effectiveHorizontalCenterLine = reverseAnchorLine(hCenterAnchorLine); effectiveLeftMargin = rightMargin; effectiveRightMargin = leftMargin; effectiveHorizontalCenterOffset = -hCenterOffset; } else { effectiveLeftAnchor = QQuickAnchors::LeftAnchor; effectiveRightAnchor = QQuickAnchors::RightAnchor; effectiveLeftItem = leftAnchorItem; effectiveLeftLine = leftAnchorLine; effectiveRightItem = rightAnchorItem; effectiveRightLine = rightAnchorLine; effectiveHorizontalCenterItem = hCenterAnchorItem; effectiveHorizontalCenterLine = hCenterAnchorLine; effectiveLeftMargin = leftMargin; effectiveRightMargin = rightMargin; effectiveHorizontalCenterOffset = hCenterOffset; } if (usedAnchors & effectiveLeftAnchor) { //Handle stretching bool invalid = true; qreal width = 0.0; if (usedAnchors & effectiveRightAnchor) { invalid = calcStretch(effectiveLeftItem, effectiveLeftLine, effectiveRightItem, effectiveRightLine, effectiveLeftMargin, -effectiveRightMargin, QQuickAnchors::LeftAnchor, width); } else if (usedAnchors & QQuickAnchors::HCenterAnchor) { invalid = calcStretch(effectiveLeftItem, effectiveLeftLine, effectiveHorizontalCenterItem, effectiveHorizontalCenterLine, effectiveLeftMargin, effectiveHorizontalCenterOffset, QQuickAnchors::LeftAnchor, width); width *= 2; } if (!invalid) setItemWidth(width); //Handle left if (effectiveLeftItem == readParentItem(item)) { setItemX(adjustedPosition(effectiveLeftItem, effectiveLeftLine) + effectiveLeftMargin); } else if (readParentItem(effectiveLeftItem) == readParentItem(item)) { setItemX(position(effectiveLeftItem, effectiveLeftLine) + effectiveLeftMargin); } } else if (usedAnchors & effectiveRightAnchor) { //Handle stretching (left + right case is handled in updateLeftAnchor) if (usedAnchors & QQuickAnchors::HCenterAnchor) { qreal width = 0.0; bool invalid = calcStretch(effectiveHorizontalCenterItem, effectiveHorizontalCenterLine, effectiveRightItem, effectiveRightLine, effectiveHorizontalCenterOffset, -effectiveRightMargin, QQuickAnchors::LeftAnchor, width); if (!invalid) setItemWidth(width*2); } //Handle right if (effectiveRightItem == readParentItem(item)) { setItemX(adjustedPosition(effectiveRightItem, effectiveRightLine) - readWidth(item) - effectiveRightMargin); } else if (readParentItem(effectiveRightItem) == readParentItem(item)) { setItemX(position(effectiveRightItem, effectiveRightLine) - readWidth(item) - effectiveRightMargin); } } else if (usedAnchors & QQuickAnchors::HCenterAnchor) { //Handle hCenter if (effectiveHorizontalCenterItem == readParentItem(item)) { setItemX(adjustedPosition(effectiveHorizontalCenterItem, effectiveHorizontalCenterLine) - hcenter(item) + effectiveHorizontalCenterOffset); } else if (readParentItem(effectiveHorizontalCenterItem) == readParentItem(item)) { setItemX(position(effectiveHorizontalCenterItem, effectiveHorizontalCenterLine) - hcenter(item) + effectiveHorizontalCenterOffset); } } --updatingHorizontalAnchor; } else { // ### Make this certain :) qmlWarning(item) << QQuickAnchors::tr("Possible anchor loop detected on horizontal anchor."); } } QQuickAnchorLine QQuickAnchors::top() const { Q_D(const QQuickAnchors); return QQuickAnchorLine(d->topAnchorItem, d->topAnchorLine); } void QQuickAnchors::setTop(const QQuickAnchorLine &edge) { Q_D(QQuickAnchors); if (!d->checkVAnchorValid(edge) || (d->topAnchorItem == edge.item && d->topAnchorLine == edge.anchorLine)) return; d->usedAnchors |= TopAnchor; if (!d->checkVValid()) { d->usedAnchors &= ~TopAnchor; return; } QQuickItem *oldTop = d->topAnchorItem; d->topAnchorItem = edge.item; d->topAnchorLine = edge.anchorLine; d->remDepend(oldTop); d->addDepend(d->topAnchorItem); emit topChanged(); d->updateVerticalAnchors(); } void QQuickAnchors::resetTop() { Q_D(QQuickAnchors); d->usedAnchors &= ~TopAnchor; d->remDepend(d->topAnchorItem); d->topAnchorItem = nullptr; d->topAnchorLine = QQuickAnchors::InvalidAnchor; emit topChanged(); d->updateVerticalAnchors(); } QQuickAnchorLine QQuickAnchors::bottom() const { Q_D(const QQuickAnchors); return QQuickAnchorLine(d->bottomAnchorItem, d->bottomAnchorLine); } void QQuickAnchors::setBottom(const QQuickAnchorLine &edge) { Q_D(QQuickAnchors); if (!d->checkVAnchorValid(edge) || (d->bottomAnchorItem == edge.item && d->bottomAnchorLine == edge.anchorLine)) return; d->usedAnchors |= BottomAnchor; if (!d->checkVValid()) { d->usedAnchors &= ~BottomAnchor; return; } QQuickItem *oldBottom = d->bottomAnchorItem; d->bottomAnchorItem = edge.item; d->bottomAnchorLine = edge.anchorLine; d->remDepend(oldBottom); d->addDepend(d->bottomAnchorItem); emit bottomChanged(); d->updateVerticalAnchors(); } void QQuickAnchors::resetBottom() { Q_D(QQuickAnchors); d->usedAnchors &= ~BottomAnchor; d->remDepend(d->bottomAnchorItem); d->bottomAnchorItem = nullptr; d->bottomAnchorLine = QQuickAnchors::InvalidAnchor; emit bottomChanged(); d->updateVerticalAnchors(); } QQuickAnchorLine QQuickAnchors::verticalCenter() const { Q_D(const QQuickAnchors); return QQuickAnchorLine(d->vCenterAnchorItem, d->vCenterAnchorLine); } void QQuickAnchors::setVerticalCenter(const QQuickAnchorLine &edge) { Q_D(QQuickAnchors); if (!d->checkVAnchorValid(edge) || (d->vCenterAnchorItem == edge.item && d->vCenterAnchorLine == edge.anchorLine)) return; d->usedAnchors |= VCenterAnchor; if (!d->checkVValid()) { d->usedAnchors &= ~VCenterAnchor; return; } QQuickItem *oldVCenter = d->vCenterAnchorItem; d->vCenterAnchorItem = edge.item; d->vCenterAnchorLine = edge.anchorLine; d->remDepend(oldVCenter); d->addDepend(d->vCenterAnchorItem); emit verticalCenterChanged(); d->updateVerticalAnchors(); } void QQuickAnchors::resetVerticalCenter() { Q_D(QQuickAnchors); d->usedAnchors &= ~VCenterAnchor; d->remDepend(d->vCenterAnchorItem); d->vCenterAnchorItem = nullptr; d->vCenterAnchorLine = QQuickAnchors::InvalidAnchor; emit verticalCenterChanged(); d->updateVerticalAnchors(); } QQuickAnchorLine QQuickAnchors::baseline() const { Q_D(const QQuickAnchors); return QQuickAnchorLine(d->baselineAnchorItem, d->baselineAnchorLine); } void QQuickAnchors::setBaseline(const QQuickAnchorLine &edge) { Q_D(QQuickAnchors); if (!d->checkVAnchorValid(edge) || (d->baselineAnchorItem == edge.item && d->baselineAnchorLine == edge.anchorLine)) return; d->usedAnchors |= BaselineAnchor; if (!d->checkVValid()) { d->usedAnchors &= ~BaselineAnchor; return; } QQuickItem *oldBaseline = d->baselineAnchorItem; d->baselineAnchorItem = edge.item; d->baselineAnchorLine = edge.anchorLine; d->remDepend(oldBaseline); d->addDepend(d->baselineAnchorItem); emit baselineChanged(); d->updateVerticalAnchors(); } void QQuickAnchors::resetBaseline() { Q_D(QQuickAnchors); d->usedAnchors &= ~BaselineAnchor; d->remDepend(d->baselineAnchorItem); d->baselineAnchorItem = nullptr; d->baselineAnchorLine = QQuickAnchors::InvalidAnchor; emit baselineChanged(); d->updateVerticalAnchors(); } QQuickAnchorLine QQuickAnchors::left() const { Q_D(const QQuickAnchors); return QQuickAnchorLine(d->leftAnchorItem, d->leftAnchorLine); } void QQuickAnchors::setLeft(const QQuickAnchorLine &edge) { Q_D(QQuickAnchors); if (!d->checkHAnchorValid(edge) || (d->leftAnchorItem == edge.item && d->leftAnchorLine == edge.anchorLine)) return; d->usedAnchors |= LeftAnchor; if (!d->checkHValid()) { d->usedAnchors &= ~LeftAnchor; return; } QQuickItem *oldLeft = d->leftAnchorItem; d->leftAnchorItem = edge.item; d->leftAnchorLine = edge.anchorLine; d->remDepend(oldLeft); d->addDepend(d->leftAnchorItem); emit leftChanged(); d->updateHorizontalAnchors(); } void QQuickAnchors::resetLeft() { Q_D(QQuickAnchors); d->usedAnchors &= ~LeftAnchor; d->remDepend(d->leftAnchorItem); d->leftAnchorItem = nullptr; d->leftAnchorLine = QQuickAnchors::InvalidAnchor; emit leftChanged(); d->updateHorizontalAnchors(); } QQuickAnchorLine QQuickAnchors::right() const { Q_D(const QQuickAnchors); return QQuickAnchorLine(d->rightAnchorItem, d->rightAnchorLine); } void QQuickAnchors::setRight(const QQuickAnchorLine &edge) { Q_D(QQuickAnchors); if (!d->checkHAnchorValid(edge) || (d->rightAnchorItem == edge.item && d->rightAnchorLine == edge.anchorLine)) return; d->usedAnchors |= RightAnchor; if (!d->checkHValid()) { d->usedAnchors &= ~RightAnchor; return; } QQuickItem *oldRight = d->rightAnchorItem; d->rightAnchorItem = edge.item; d->rightAnchorLine = edge.anchorLine; d->remDepend(oldRight); d->addDepend(d->rightAnchorItem); emit rightChanged(); d->updateHorizontalAnchors(); } void QQuickAnchors::resetRight() { Q_D(QQuickAnchors); d->usedAnchors &= ~RightAnchor; d->remDepend(d->rightAnchorItem); d->rightAnchorItem = nullptr; d->rightAnchorLine = QQuickAnchors::InvalidAnchor; emit rightChanged(); d->updateHorizontalAnchors(); } QQuickAnchorLine QQuickAnchors::horizontalCenter() const { Q_D(const QQuickAnchors); return QQuickAnchorLine(d->hCenterAnchorItem, d->hCenterAnchorLine); } void QQuickAnchors::setHorizontalCenter(const QQuickAnchorLine &edge) { Q_D(QQuickAnchors); if (!d->checkHAnchorValid(edge) || (d->hCenterAnchorItem == edge.item && d->hCenterAnchorLine == edge.anchorLine)) return; d->usedAnchors |= HCenterAnchor; if (!d->checkHValid()) { d->usedAnchors &= ~HCenterAnchor; return; } QQuickItem *oldHCenter = d->hCenterAnchorItem; d->hCenterAnchorItem = edge.item; d->hCenterAnchorLine = edge.anchorLine; d->remDepend(oldHCenter); d->addDepend(d->hCenterAnchorItem); emit horizontalCenterChanged(); d->updateHorizontalAnchors(); } void QQuickAnchors::resetHorizontalCenter() { Q_D(QQuickAnchors); d->usedAnchors &= ~HCenterAnchor; d->remDepend(d->hCenterAnchorItem); d->hCenterAnchorItem = nullptr; d->hCenterAnchorLine = QQuickAnchors::InvalidAnchor; emit horizontalCenterChanged(); d->updateHorizontalAnchors(); } qreal QQuickAnchors::leftMargin() const { Q_D(const QQuickAnchors); return d->leftMargin; } void QQuickAnchors::setLeftMargin(qreal offset) { Q_D(QQuickAnchors); d->leftMarginExplicit = true; if (d->leftMargin == offset) return; d->leftMargin = offset; if (d->fill) d->fillChanged(); else d->updateHorizontalAnchors(); emit leftMarginChanged(); } void QQuickAnchors::resetLeftMargin() { Q_D(QQuickAnchors); d->leftMarginExplicit = false; if (d->leftMargin == d->margins) return; d->leftMargin = d->margins; if (d->fill) d->fillChanged(); else d->updateHorizontalAnchors(); emit leftMarginChanged(); } qreal QQuickAnchors::rightMargin() const { Q_D(const QQuickAnchors); return d->rightMargin; } void QQuickAnchors::setRightMargin(qreal offset) { Q_D(QQuickAnchors); d->rightMarginExplicit = true; if (d->rightMargin == offset) return; d->rightMargin = offset; if (d->fill) d->fillChanged(); else d->updateHorizontalAnchors(); emit rightMarginChanged(); } void QQuickAnchors::resetRightMargin() { Q_D(QQuickAnchors); d->rightMarginExplicit = false; if (d->rightMargin == d->margins) return; d->rightMargin = d->margins; if (d->fill) d->fillChanged(); else d->updateHorizontalAnchors(); emit rightMarginChanged(); } qreal QQuickAnchors::margins() const { Q_D(const QQuickAnchors); return d->margins; } void QQuickAnchors::setMargins(qreal offset) { Q_D(QQuickAnchors); if (d->margins == offset) return; d->margins = offset; bool updateHorizontal = false; bool updateVertical = false; if (!d->rightMarginExplicit && d->rightMargin != offset) { d->rightMargin = offset; updateHorizontal = true; emit rightMarginChanged(); } if (!d->leftMarginExplicit && d->leftMargin != offset) { d->leftMargin = offset; updateHorizontal = true; emit leftMarginChanged(); } if (!d->topMarginExplicit && d->topMargin != offset) { d->topMargin = offset; updateVertical = true; emit topMarginChanged(); } if (!d->bottomMarginExplicit && d->bottomMargin != offset) { d->bottomMargin = offset; updateVertical = true; emit bottomMarginChanged(); } if (d->fill) { if (updateHorizontal || updateVertical) d->fillChanged(); } else { if (updateHorizontal) d->updateHorizontalAnchors(); if (updateVertical) d->updateVerticalAnchors(); } emit marginsChanged(); } qreal QQuickAnchors::horizontalCenterOffset() const { Q_D(const QQuickAnchors); return d->hCenterOffset; } void QQuickAnchors::setHorizontalCenterOffset(qreal offset) { Q_D(QQuickAnchors); if (d->hCenterOffset == offset) return; d->hCenterOffset = offset; if (d->centerIn) d->centerInChanged(); else d->updateHorizontalAnchors(); emit horizontalCenterOffsetChanged(); } qreal QQuickAnchors::topMargin() const { Q_D(const QQuickAnchors); return d->topMargin; } void QQuickAnchors::setTopMargin(qreal offset) { Q_D(QQuickAnchors); d->topMarginExplicit = true; if (d->topMargin == offset) return; d->topMargin = offset; if (d->fill) d->fillChanged(); else d->updateVerticalAnchors(); emit topMarginChanged(); } void QQuickAnchors::resetTopMargin() { Q_D(QQuickAnchors); d->topMarginExplicit = false; if (d->topMargin == d->margins) return; d->topMargin = d->margins; if (d->fill) d->fillChanged(); else d->updateVerticalAnchors(); emit topMarginChanged(); } qreal QQuickAnchors::bottomMargin() const { Q_D(const QQuickAnchors); return d->bottomMargin; } void QQuickAnchors::setBottomMargin(qreal offset) { Q_D(QQuickAnchors); d->bottomMarginExplicit = true; if (d->bottomMargin == offset) return; d->bottomMargin = offset; if (d->fill) d->fillChanged(); else d->updateVerticalAnchors(); emit bottomMarginChanged(); } void QQuickAnchors::resetBottomMargin() { Q_D(QQuickAnchors); d->bottomMarginExplicit = false; if (d->bottomMargin == d->margins) return; d->bottomMargin = d->margins; if (d->fill) d->fillChanged(); else d->updateVerticalAnchors(); emit bottomMarginChanged(); } qreal QQuickAnchors::verticalCenterOffset() const { Q_D(const QQuickAnchors); return d->vCenterOffset; } void QQuickAnchors::setVerticalCenterOffset(qreal offset) { Q_D(QQuickAnchors); if (d->vCenterOffset == offset) return; d->vCenterOffset = offset; if (d->centerIn) d->centerInChanged(); else d->updateVerticalAnchors(); emit verticalCenterOffsetChanged(); } qreal QQuickAnchors::baselineOffset() const { Q_D(const QQuickAnchors); return d->baselineOffset; } void QQuickAnchors::setBaselineOffset(qreal offset) { Q_D(QQuickAnchors); if (d->baselineOffset == offset) return; d->baselineOffset = offset; d->updateVerticalAnchors(); emit baselineOffsetChanged(); } QQuickAnchors::Anchors QQuickAnchors::usedAnchors() const { Q_D(const QQuickAnchors); return static_cast(d->usedAnchors); } Qt::Orientations QQuickAnchors::activeDirections() const { Q_D(const QQuickAnchors); if (d->fill || d->centerIn) return Qt::Horizontal | Qt::Vertical; Qt::Orientations o; if (d->usedAnchors & QQuickAnchors::Horizontal_Mask) o |= Qt::Horizontal; if (d->usedAnchors & QQuickAnchors::Vertical_Mask) o |= Qt::Vertical; return o; } bool QQuickAnchorsPrivate::checkHValid() const { if (usedAnchors & QQuickAnchors::LeftAnchor && usedAnchors & QQuickAnchors::RightAnchor && usedAnchors & QQuickAnchors::HCenterAnchor) { qmlWarning(item) << QQuickAnchors::tr("Cannot specify left, right, and horizontalCenter anchors at the same time."); return false; } return true; } bool QQuickAnchorsPrivate::checkHAnchorValid(QQuickAnchorLine anchor) const { if (!anchor.item) { qmlWarning(item) << QQuickAnchors::tr("Cannot anchor to a null item."); return false; } else if (anchor.anchorLine & QQuickAnchors::Vertical_Mask) { qmlWarning(item) << QQuickAnchors::tr("Cannot anchor a horizontal edge to a vertical edge."); return false; } else if (anchor.item != readParentItem(item) && readParentItem(anchor.item) != readParentItem(item)) { qmlWarning(item) << QQuickAnchors::tr("Cannot anchor to an item that isn't a parent or sibling."); return false; } else if (anchor.item == item) { qmlWarning(item) << QQuickAnchors::tr("Cannot anchor item to self."); return false; } return true; } bool QQuickAnchorsPrivate::checkVValid() const { if (usedAnchors & QQuickAnchors::TopAnchor && usedAnchors & QQuickAnchors::BottomAnchor && usedAnchors & QQuickAnchors::VCenterAnchor) { qmlWarning(item) << QQuickAnchors::tr("Cannot specify top, bottom, and verticalCenter anchors at the same time."); return false; } else if (usedAnchors & QQuickAnchors::BaselineAnchor && (usedAnchors & QQuickAnchors::TopAnchor || usedAnchors & QQuickAnchors::BottomAnchor || usedAnchors & QQuickAnchors::VCenterAnchor)) { qmlWarning(item) << QQuickAnchors::tr("Baseline anchor cannot be used in conjunction with top, bottom, or verticalCenter anchors."); return false; } return true; } bool QQuickAnchorsPrivate::checkVAnchorValid(QQuickAnchorLine anchor) const { if (!anchor.item) { qmlWarning(item) << QQuickAnchors::tr("Cannot anchor to a null item."); return false; } else if (anchor.anchorLine & QQuickAnchors::Horizontal_Mask) { qmlWarning(item) << QQuickAnchors::tr("Cannot anchor a vertical edge to a horizontal edge."); return false; } else if (anchor.item != readParentItem(item) && readParentItem(anchor.item) != readParentItem(item)) { qmlWarning(item) << QQuickAnchors::tr("Cannot anchor to an item that isn't a parent or sibling."); return false; } else if (anchor.item == item){ qmlWarning(item) << QQuickAnchors::tr("Cannot anchor item to self."); return false; } return true; } QT_END_NAMESPACE #include