/**************************************************************************** ** ** 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 "qquickstateoperations_p.h" #include "qquickitem_p.h" #include #include #include #include QT_BEGIN_NAMESPACE class QQuickParentChangePrivate : public QQuickStateOperationPrivate { Q_DECLARE_PUBLIC(QQuickParentChange) public: QQuickItem *target = nullptr; QPointer parent; struct StateSnapshot { QPointer parent; QPointer stackBefore; qreal x = 0, y = 0, width = 0, height = 0, scale = 0, rotation = 0; }; std::unique_ptr orig; std::unique_ptr rewind; QQmlNullableValue xString; QQmlNullableValue yString; QQmlNullableValue widthString; QQmlNullableValue heightString; QQmlNullableValue scaleString; QQmlNullableValue rotationString; void doChange(QQuickItem *targetParent); void reverseRewindHelper(const std::unique_ptr &snapshot); }; void QQuickParentChangePrivate::doChange(QQuickItem *targetParent) { if (targetParent && target && target->parentItem()) { Q_Q(QQuickParentChange); bool ok; const QTransform &transform = target->parentItem()->itemTransform(targetParent, &ok); if (transform.type() >= QTransform::TxShear || !ok) { qmlWarning(q) << QQuickParentChange::tr("Unable to preserve appearance under complex transform"); ok = false; } qreal scale = 1; qreal rotation = 0; bool isRotate = (transform.type() == QTransform::TxRotate) || (transform.m11() < 0); if (ok && !isRotate) { if (transform.m11() == transform.m22()) scale = transform.m11(); else { qmlWarning(q) << QQuickParentChange::tr("Unable to preserve appearance under non-uniform scale"); ok = false; } } else if (ok && isRotate) { if (transform.m11() == transform.m22()) scale = qSqrt(transform.m11()*transform.m11() + transform.m12()*transform.m12()); else { qmlWarning(q) << QQuickParentChange::tr("Unable to preserve appearance under non-uniform scale"); ok = false; } if (scale != 0) rotation = qRadiansToDegrees(qAtan2(transform.m12() / scale, transform.m11() / scale)); else { qmlWarning(q) << QQuickParentChange::tr("Unable to preserve appearance under scale of 0"); ok = false; } } const QPointF &point = transform.map(QPointF(target->x(),target->y())); qreal x = point.x(); qreal y = point.y(); // setParentItem will update the transformOriginPoint if needed target->setParentItem(targetParent); if (ok && target->transformOrigin() != QQuickItem::TopLeft) { qreal tempxt = target->transformOriginPoint().x(); qreal tempyt = target->transformOriginPoint().y(); QTransform t; t.translate(-tempxt, -tempyt); t.rotate(rotation); t.scale(scale, scale); t.translate(tempxt, tempyt); const QPointF &offset = t.map(QPointF(0,0)); x += offset.x(); y += offset.y(); } if (ok) { //qDebug() << x << y << rotation << scale; target->setPosition(QPointF(x, y)); target->setRotation(target->rotation() + rotation); target->setScale(target->scale() * scale); } } else if (target) { target->setParentItem(targetParent); } } /*! \qmltype ParentChange \instantiates QQuickParentChange \inqmlmodule QtQuick \ingroup qtquick-states \brief Specifies how to reparent an Item in a state change. ParentChange reparents an item while preserving its visual appearance (position, size, rotation, and scale) on screen. You can then specify a transition to move/resize/rotate/scale the item to its final intended appearance. ParentChange can only preserve visual appearance if no complex transforms are involved. More specifically, it will not work if the transform property has been set for any items involved in the reparenting (i.e. items in the common ancestor tree for the original and new parent). The example below displays a large red rectangle and a small blue rectangle, side by side. When the \c blueRect is clicked, it changes to the "reparented" state: its parent is changed to \c redRect and it is positioned at (10, 10) within the red rectangle, as specified in the ParentChange. \snippet qml/parentchange.qml 0 \image parentchange.png You can specify at which point in a transition you want a ParentChange to occur by using a ParentAnimation. Note that unlike PropertyChanges, ParentChange expects an Item-based target; it will not work with arbitrary objects (for example, you couldn't use it to reparent a Timer). */ QQuickParentChange::QQuickParentChange(QObject *parent) : QQuickStateOperation(*(new QQuickParentChangePrivate), parent) { } QQuickParentChange::~QQuickParentChange() { } /*! \qmlproperty real QtQuick::ParentChange::x \qmlproperty real QtQuick::ParentChange::y \qmlproperty real QtQuick::ParentChange::width \qmlproperty real QtQuick::ParentChange::height \qmlproperty real QtQuick::ParentChange::scale \qmlproperty real QtQuick::ParentChange::rotation These properties hold the new position, size, scale, and rotation for the item in this state. */ QQmlScriptString QQuickParentChange::x() const { Q_D(const QQuickParentChange); return d->xString.value; } void QQuickParentChange::setX(const QQmlScriptString &x) { Q_D(QQuickParentChange); d->xString = x; } bool QQuickParentChange::xIsSet() const { Q_D(const QQuickParentChange); return d->xString.isValid(); } QQmlScriptString QQuickParentChange::y() const { Q_D(const QQuickParentChange); return d->yString.value; } void QQuickParentChange::setY(const QQmlScriptString &y) { Q_D(QQuickParentChange); d->yString = y; } bool QQuickParentChange::yIsSet() const { Q_D(const QQuickParentChange); return d->yString.isValid(); } QQmlScriptString QQuickParentChange::width() const { Q_D(const QQuickParentChange); return d->widthString.value; } void QQuickParentChange::setWidth(const QQmlScriptString &width) { Q_D(QQuickParentChange); d->widthString = width; } bool QQuickParentChange::widthIsSet() const { Q_D(const QQuickParentChange); return d->widthString.isValid(); } QQmlScriptString QQuickParentChange::height() const { Q_D(const QQuickParentChange); return d->heightString.value; } void QQuickParentChange::setHeight(const QQmlScriptString &height) { Q_D(QQuickParentChange); d->heightString = height; } bool QQuickParentChange::heightIsSet() const { Q_D(const QQuickParentChange); return d->heightString.isValid(); } QQmlScriptString QQuickParentChange::scale() const { Q_D(const QQuickParentChange); return d->scaleString.value; } void QQuickParentChange::setScale(const QQmlScriptString &scale) { Q_D(QQuickParentChange); d->scaleString = scale; } bool QQuickParentChange::scaleIsSet() const { Q_D(const QQuickParentChange); return d->scaleString.isValid(); } QQmlScriptString QQuickParentChange::rotation() const { Q_D(const QQuickParentChange); return d->rotationString.value; } void QQuickParentChange::setRotation(const QQmlScriptString &rotation) { Q_D(QQuickParentChange); d->rotationString = rotation; } bool QQuickParentChange::rotationIsSet() const { Q_D(const QQuickParentChange); return d->rotationString.isValid(); } QQuickItem *QQuickParentChange::originalParent() const { Q_D(const QQuickParentChange); return d->orig ? d->orig->parent : nullptr; } /*! \qmlproperty Item QtQuick::ParentChange::target This property holds the item to be reparented */ QQuickItem *QQuickParentChange::object() const { Q_D(const QQuickParentChange); return d->target; } void QQuickParentChange::setObject(QQuickItem *target) { Q_D(QQuickParentChange); d->target = target; } /*! \qmlproperty Item QtQuick::ParentChange::parent This property holds the new parent for the item in this state. */ QQuickItem *QQuickParentChange::parent() const { Q_D(const QQuickParentChange); return d->parent; } void QQuickParentChange::setParent(QQuickItem *parent) { Q_D(QQuickParentChange); d->parent = parent; } QQuickStateOperation::ActionList QQuickParentChange::actions() { Q_D(QQuickParentChange); if (!d->target || !d->parent) return ActionList(); ActionList actions; QQuickStateAction a; a.event = this; actions << a; if (d->xString.isValid()) { bool ok = false; qreal x = d->xString.value.numberLiteral(&ok); if (ok) { QQuickStateAction xa(d->target, QLatin1String("x"), x); actions << xa; } else { QQmlProperty property(d->target, QLatin1String("x")); QQmlBinding *newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, d->xString.value, d->target, qmlContext(this)); newBinding->setTarget(property); QQuickStateAction xa; xa.property = property; xa.toBinding = newBinding; xa.fromValue = xa.property.read(); xa.deletableToBinding = true; actions << xa; } } if (d->yString.isValid()) { bool ok = false; qreal y = d->yString.value.numberLiteral(&ok); if (ok) { QQuickStateAction ya(d->target, QLatin1String("y"), y); actions << ya; } else { QQmlProperty property(d->target, QLatin1String("y")); QQmlBinding *newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, d->yString.value, d->target, qmlContext(this)); newBinding->setTarget(property); QQuickStateAction ya; ya.property = property; ya.toBinding = newBinding; ya.fromValue = ya.property.read(); ya.deletableToBinding = true; actions << ya; } } if (d->scaleString.isValid()) { bool ok = false; qreal scale = d->scaleString.value.numberLiteral(&ok); if (ok) { QQuickStateAction sa(d->target, QLatin1String("scale"), scale); actions << sa; } else { QQmlProperty property(d->target, QLatin1String("scale")); QQmlBinding *newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, d->scaleString.value, d->target, qmlContext(this)); newBinding->setTarget(property); QQuickStateAction sa; sa.property = property; sa.toBinding = newBinding; sa.fromValue = sa.property.read(); sa.deletableToBinding = true; actions << sa; } } if (d->rotationString.isValid()) { bool ok = false; qreal rotation = d->rotationString.value.numberLiteral(&ok); if (ok) { QQuickStateAction ra(d->target, QLatin1String("rotation"), rotation); actions << ra; } else { QQmlProperty property(d->target, QLatin1String("rotation")); QQmlBinding *newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, d->rotationString.value, d->target, qmlContext(this)); newBinding->setTarget(property); QQuickStateAction ra; ra.property = property; ra.toBinding = newBinding; ra.fromValue = ra.property.read(); ra.deletableToBinding = true; actions << ra; } } if (d->widthString.isValid()) { bool ok = false; qreal width = d->widthString.value.numberLiteral(&ok); if (ok) { QQuickStateAction wa(d->target, QLatin1String("width"), width); actions << wa; } else { QQmlProperty property(d->target, QLatin1String("width")); QQmlBinding *newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, d->widthString.value, d->target, qmlContext(this)); newBinding->setTarget(property); QQuickStateAction wa; wa.property = property; wa.toBinding = newBinding; wa.fromValue = wa.property.read(); wa.deletableToBinding = true; actions << wa; } } if (d->heightString.isValid()) { bool ok = false; qreal height = d->heightString.value.numberLiteral(&ok); if (ok) { QQuickStateAction ha(d->target, QLatin1String("height"), height); actions << ha; } else { QQmlProperty property(d->target, QLatin1String("height")); QQmlBinding *newBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(property)->core, d->heightString.value, d->target, qmlContext(this)); newBinding->setTarget(property); QQuickStateAction ha; ha.property = property; ha.toBinding = newBinding; ha.fromValue = ha.property.read(); ha.deletableToBinding = true; actions << ha; } } return actions; } void QQuickParentChange::saveOriginals() { Q_D(QQuickParentChange); saveCurrentValues(); if (!d->orig) d->orig.reset(new QQuickParentChangePrivate::StateSnapshot); *d->orig = *d->rewind; } void QQuickParentChange::execute() { Q_D(QQuickParentChange); d->doChange(d->parent); } bool QQuickParentChange::isReversable() { return true; } void QQuickParentChangePrivate::reverseRewindHelper(const std::unique_ptr &snapshot) { if (!target || !snapshot) return; target->setX(snapshot->x); target->setY(snapshot->y); target->setScale(snapshot->scale); target->setWidth(snapshot->width); target->setHeight(snapshot->height); target->setRotation(snapshot->rotation); target->setParentItem(snapshot->parent); if (snapshot->stackBefore) target->stackBefore(snapshot->stackBefore); } void QQuickParentChange::reverse() { Q_D(QQuickParentChange); d->reverseRewindHelper(d->orig); } QQuickStateActionEvent::EventType QQuickParentChange::type() const { return ParentChange; } bool QQuickParentChange::mayOverride(QQuickStateActionEvent*other) { Q_D(QQuickParentChange); if (other->type() != ParentChange) return false; if (QQuickParentChange *otherPC = static_cast(other)) return (d->target == otherPC->object()); return false; } void QQuickParentChange::saveCurrentValues() { Q_D(QQuickParentChange); if (!d->target) { d->rewind = nullptr; return; } d->rewind.reset(new QQuickParentChangePrivate::StateSnapshot); d->rewind->x = d->target->x(); d->rewind->y = d->target->y(); d->rewind->scale = d->target->scale(); d->rewind->width = d->target->width(); d->rewind->height = d->target->height(); d->rewind->rotation = d->target->rotation(); d->rewind->parent = d->target->parentItem(); d->rewind->stackBefore = nullptr; if (!d->rewind->parent) return; QList children = d->rewind->parent->childItems(); for (int ii = 0; ii < children.count() - 1; ++ii) { if (children.at(ii) == d->target) { d->rewind->stackBefore = children.at(ii + 1); break; } } } void QQuickParentChange::rewind() { Q_D(QQuickParentChange); d->reverseRewindHelper(d->rewind); d->rewind.reset(); } /*! \qmltype AnchorChanges \instantiates QQuickAnchorChanges \inqmlmodule QtQuick \ingroup qtquick-states \brief Specifies how to change the anchors of an item in a state. The AnchorChanges type is used to modify the anchors of an item in a \l State. AnchorChanges cannot be used to modify the margins on an item. For this, use PropertyChanges intead. In the following example we change the top and bottom anchors of an item using AnchorChanges, and the top and bottom anchor margins using PropertyChanges: \snippet qml/anchorchanges.qml 0 \image anchorchanges.png AnchorChanges can be animated using AnchorAnimation. \qml //animate our anchor changes Transition { AnchorAnimation {} } \endqml Changes to anchor margins can be animated using NumberAnimation. For more information on anchors see \l {anchor-layout}{Anchor Layouts}. */ class QQuickAnchorSetPrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QQuickAnchorSet) public: QQuickAnchors::Anchors usedAnchors; QQuickAnchors::Anchors resetAnchors; QQmlScriptString leftScript; QQmlScriptString rightScript; QQmlScriptString topScript; QQmlScriptString bottomScript; QQmlScriptString hCenterScript; QQmlScriptString vCenterScript; QQmlScriptString baselineScript; }; QQuickAnchorSet::QQuickAnchorSet(QObject *parent) : QObject(*new QQuickAnchorSetPrivate, parent) { } QQuickAnchorSet::~QQuickAnchorSet() { } QQmlScriptString QQuickAnchorSet::top() const { Q_D(const QQuickAnchorSet); return d->topScript; } void QQuickAnchorSet::setTop(const QQmlScriptString &edge) { Q_D(QQuickAnchorSet); d->usedAnchors |= QQuickAnchors::TopAnchor; d->topScript = edge; if (edge.isUndefinedLiteral()) resetTop(); } void QQuickAnchorSet::resetTop() { Q_D(QQuickAnchorSet); d->usedAnchors &= ~QQuickAnchors::TopAnchor; d->resetAnchors |= QQuickAnchors::TopAnchor; } QQmlScriptString QQuickAnchorSet::bottom() const { Q_D(const QQuickAnchorSet); return d->bottomScript; } void QQuickAnchorSet::setBottom(const QQmlScriptString &edge) { Q_D(QQuickAnchorSet); d->usedAnchors |= QQuickAnchors::BottomAnchor; d->bottomScript = edge; if (edge.isUndefinedLiteral()) resetBottom(); } void QQuickAnchorSet::resetBottom() { Q_D(QQuickAnchorSet); d->usedAnchors &= ~QQuickAnchors::BottomAnchor; d->resetAnchors |= QQuickAnchors::BottomAnchor; } QQmlScriptString QQuickAnchorSet::verticalCenter() const { Q_D(const QQuickAnchorSet); return d->vCenterScript; } void QQuickAnchorSet::setVerticalCenter(const QQmlScriptString &edge) { Q_D(QQuickAnchorSet); d->usedAnchors |= QQuickAnchors::VCenterAnchor; d->vCenterScript = edge; if (edge.isUndefinedLiteral()) resetVerticalCenter(); } void QQuickAnchorSet::resetVerticalCenter() { Q_D(QQuickAnchorSet); d->usedAnchors &= ~QQuickAnchors::VCenterAnchor; d->resetAnchors |= QQuickAnchors::VCenterAnchor; } QQmlScriptString QQuickAnchorSet::baseline() const { Q_D(const QQuickAnchorSet); return d->baselineScript; } void QQuickAnchorSet::setBaseline(const QQmlScriptString &edge) { Q_D(QQuickAnchorSet); d->usedAnchors |= QQuickAnchors::BaselineAnchor; d->baselineScript = edge; if (edge.isUndefinedLiteral()) resetBaseline(); } void QQuickAnchorSet::resetBaseline() { Q_D(QQuickAnchorSet); d->usedAnchors &= ~QQuickAnchors::BaselineAnchor; d->resetAnchors |= QQuickAnchors::BaselineAnchor; } QQmlScriptString QQuickAnchorSet::left() const { Q_D(const QQuickAnchorSet); return d->leftScript; } void QQuickAnchorSet::setLeft(const QQmlScriptString &edge) { Q_D(QQuickAnchorSet); d->usedAnchors |= QQuickAnchors::LeftAnchor; d->leftScript = edge; if (edge.isUndefinedLiteral()) resetLeft(); } void QQuickAnchorSet::resetLeft() { Q_D(QQuickAnchorSet); d->usedAnchors &= ~QQuickAnchors::LeftAnchor; d->resetAnchors |= QQuickAnchors::LeftAnchor; } QQmlScriptString QQuickAnchorSet::right() const { Q_D(const QQuickAnchorSet); return d->rightScript; } void QQuickAnchorSet::setRight(const QQmlScriptString &edge) { Q_D(QQuickAnchorSet); d->usedAnchors |= QQuickAnchors::RightAnchor; d->rightScript = edge; if (edge.isUndefinedLiteral()) resetRight(); } void QQuickAnchorSet::resetRight() { Q_D(QQuickAnchorSet); d->usedAnchors &= ~QQuickAnchors::RightAnchor; d->resetAnchors |= QQuickAnchors::RightAnchor; } QQmlScriptString QQuickAnchorSet::horizontalCenter() const { Q_D(const QQuickAnchorSet); return d->hCenterScript; } void QQuickAnchorSet::setHorizontalCenter(const QQmlScriptString &edge) { Q_D(QQuickAnchorSet); d->usedAnchors |= QQuickAnchors::HCenterAnchor; d->hCenterScript = edge; if (edge.isUndefinedLiteral()) resetHorizontalCenter(); } void QQuickAnchorSet::resetHorizontalCenter() { Q_D(QQuickAnchorSet); d->usedAnchors &= ~QQuickAnchors::HCenterAnchor; d->resetAnchors |= QQuickAnchors::HCenterAnchor; } class QQuickAnchorChangesPrivate : public QQuickStateOperationPrivate { public: QQuickAnchorChangesPrivate() : target(nullptr), anchorSet(new QQuickAnchorSet) { } ~QQuickAnchorChangesPrivate() { delete anchorSet; } QQuickItem *target; QQuickAnchorSet *anchorSet; QExplicitlySharedDataPointer leftBinding; QExplicitlySharedDataPointer rightBinding; QExplicitlySharedDataPointer hCenterBinding; QExplicitlySharedDataPointer topBinding; QExplicitlySharedDataPointer bottomBinding; QExplicitlySharedDataPointer vCenterBinding; QExplicitlySharedDataPointer baselineBinding; QQmlAbstractBinding::Ptr origLeftBinding; QQmlAbstractBinding::Ptr origRightBinding; QQmlAbstractBinding::Ptr origHCenterBinding; QQmlAbstractBinding::Ptr origTopBinding; QQmlAbstractBinding::Ptr origBottomBinding; QQmlAbstractBinding::Ptr origVCenterBinding; QQmlAbstractBinding::Ptr origBaselineBinding; QQuickAnchorLine rewindLeft; QQuickAnchorLine rewindRight; QQuickAnchorLine rewindHCenter; QQuickAnchorLine rewindTop; QQuickAnchorLine rewindBottom; QQuickAnchorLine rewindVCenter; QQuickAnchorLine rewindBaseline; qreal fromX; qreal fromY; qreal fromWidth; qreal fromHeight; qreal toX; qreal toY; qreal toWidth; qreal toHeight; qreal rewindX; qreal rewindY; qreal rewindWidth; qreal rewindHeight; bool applyOrigLeft; bool applyOrigRight; bool applyOrigHCenter; bool applyOrigTop; bool applyOrigBottom; bool applyOrigVCenter; bool applyOrigBaseline; QQmlNullableValue origWidth; QQmlNullableValue origHeight; qreal origX; qreal origY; QQmlProperty leftProp; QQmlProperty rightProp; QQmlProperty hCenterProp; QQmlProperty topProp; QQmlProperty bottomProp; QQmlProperty vCenterProp; QQmlProperty baselineProp; }; QQuickAnchorChanges::QQuickAnchorChanges(QObject *parent) : QQuickStateOperation(*(new QQuickAnchorChangesPrivate), parent) { } QQuickAnchorChanges::~QQuickAnchorChanges() { } QQuickAnchorChanges::ActionList QQuickAnchorChanges::actions() { Q_D(QQuickAnchorChanges); //### ASSERT these are all 0? d->leftBinding = d->rightBinding = d->hCenterBinding = d->topBinding = d->bottomBinding = d->vCenterBinding = d->baselineBinding = nullptr; d->leftProp = QQmlProperty(d->target, QLatin1String("anchors.left")); d->rightProp = QQmlProperty(d->target, QLatin1String("anchors.right")); d->hCenterProp = QQmlProperty(d->target, QLatin1String("anchors.horizontalCenter")); d->topProp = QQmlProperty(d->target, QLatin1String("anchors.top")); d->bottomProp = QQmlProperty(d->target, QLatin1String("anchors.bottom")); d->vCenterProp = QQmlProperty(d->target, QLatin1String("anchors.verticalCenter")); d->baselineProp = QQmlProperty(d->target, QLatin1String("anchors.baseline")); if (d->anchorSet->d_func()->usedAnchors & QQuickAnchors::LeftAnchor) { d->leftBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(d->leftProp)->core, d->anchorSet->d_func()->leftScript, d->target, qmlContext(this)); d->leftBinding->setTarget(d->leftProp); } if (d->anchorSet->d_func()->usedAnchors & QQuickAnchors::RightAnchor) { d->rightBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(d->rightProp)->core, d->anchorSet->d_func()->rightScript, d->target, qmlContext(this)); d->rightBinding->setTarget(d->rightProp); } if (d->anchorSet->d_func()->usedAnchors & QQuickAnchors::HCenterAnchor) { d->hCenterBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(d->hCenterProp)->core, d->anchorSet->d_func()->hCenterScript, d->target, qmlContext(this)); d->hCenterBinding->setTarget(d->hCenterProp); } if (d->anchorSet->d_func()->usedAnchors & QQuickAnchors::TopAnchor) { d->topBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(d->topProp)->core, d->anchorSet->d_func()->topScript, d->target, qmlContext(this)); d->topBinding->setTarget(d->topProp); } if (d->anchorSet->d_func()->usedAnchors & QQuickAnchors::BottomAnchor) { d->bottomBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(d->bottomProp)->core, d->anchorSet->d_func()->bottomScript, d->target, qmlContext(this)); d->bottomBinding->setTarget(d->bottomProp); } if (d->anchorSet->d_func()->usedAnchors & QQuickAnchors::VCenterAnchor) { d->vCenterBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(d->vCenterProp)->core, d->anchorSet->d_func()->vCenterScript, d->target, qmlContext(this)); d->vCenterBinding->setTarget(d->vCenterProp); } if (d->anchorSet->d_func()->usedAnchors & QQuickAnchors::BaselineAnchor) { d->baselineBinding = QQmlBinding::create(&QQmlPropertyPrivate::get(d->baselineProp)->core, d->anchorSet->d_func()->baselineScript, d->target, qmlContext(this)); d->baselineBinding->setTarget(d->baselineProp); } QQuickStateAction a; a.event = this; return ActionList() << a; } QQuickAnchorSet *QQuickAnchorChanges::anchors() const { Q_D(const QQuickAnchorChanges); return d->anchorSet; } /*! \qmlproperty Item QtQuick::AnchorChanges::target This property holds the \l Item for which the anchor changes will be applied. */ QQuickItem *QQuickAnchorChanges::object() const { Q_D(const QQuickAnchorChanges); return d->target; } void QQuickAnchorChanges::setObject(QQuickItem *target) { Q_D(QQuickAnchorChanges); d->target = target; } /*! \qmlpropertygroup QtQuick::AnchorChanges::anchors \qmlproperty AnchorLine QtQuick::AnchorChanges::anchors.left \qmlproperty AnchorLine QtQuick::AnchorChanges::anchors.right \qmlproperty AnchorLine QtQuick::AnchorChanges::anchors.horizontalCenter \qmlproperty AnchorLine QtQuick::AnchorChanges::anchors.top \qmlproperty AnchorLine QtQuick::AnchorChanges::anchors.bottom \qmlproperty AnchorLine QtQuick::AnchorChanges::anchors.verticalCenter \qmlproperty AnchorLine QtQuick::AnchorChanges::anchors.baseline These properties change the respective anchors of the item. To reset an anchor you can assign \c undefined: \qml AnchorChanges { target: myItem anchors.left: undefined //remove myItem's left anchor anchors.right: otherItem.right } \endqml */ void QQuickAnchorChanges::execute() { Q_D(QQuickAnchorChanges); if (!d->target) return; QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(d->target); //incorporate any needed "reverts" if (d->applyOrigLeft) { if (!d->origLeftBinding) targetPrivate->anchors()->resetLeft(); QQmlPropertyPrivate::setBinding(d->leftProp, d->origLeftBinding.data()); } if (d->applyOrigRight) { if (!d->origRightBinding) targetPrivate->anchors()->resetRight(); QQmlPropertyPrivate::setBinding(d->rightProp, d->origRightBinding.data()); } if (d->applyOrigHCenter) { if (!d->origHCenterBinding) targetPrivate->anchors()->resetHorizontalCenter(); QQmlPropertyPrivate::setBinding(d->hCenterProp, d->origHCenterBinding.data()); } if (d->applyOrigTop) { if (!d->origTopBinding) targetPrivate->anchors()->resetTop(); QQmlPropertyPrivate::setBinding(d->topProp, d->origTopBinding.data()); } if (d->applyOrigBottom) { if (!d->origBottomBinding) targetPrivate->anchors()->resetBottom(); QQmlPropertyPrivate::setBinding(d->bottomProp, d->origBottomBinding.data()); } if (d->applyOrigVCenter) { if (!d->origVCenterBinding) targetPrivate->anchors()->resetVerticalCenter(); QQmlPropertyPrivate::setBinding(d->vCenterProp, d->origVCenterBinding.data()); } if (d->applyOrigBaseline) { if (!d->origBaselineBinding) targetPrivate->anchors()->resetBaseline(); QQmlPropertyPrivate::setBinding(d->baselineProp, d->origBaselineBinding.data()); } //reset any anchors that have been specified as "undefined" if (d->anchorSet->d_func()->resetAnchors & QQuickAnchors::LeftAnchor) { targetPrivate->anchors()->resetLeft(); QQmlPropertyPrivate::removeBinding(d->leftProp); } if (d->anchorSet->d_func()->resetAnchors & QQuickAnchors::RightAnchor) { targetPrivate->anchors()->resetRight(); QQmlPropertyPrivate::removeBinding(d->rightProp); } if (d->anchorSet->d_func()->resetAnchors & QQuickAnchors::HCenterAnchor) { targetPrivate->anchors()->resetHorizontalCenter(); QQmlPropertyPrivate::removeBinding(d->hCenterProp); } if (d->anchorSet->d_func()->resetAnchors & QQuickAnchors::TopAnchor) { targetPrivate->anchors()->resetTop(); QQmlPropertyPrivate::removeBinding(d->topProp); } if (d->anchorSet->d_func()->resetAnchors & QQuickAnchors::BottomAnchor) { targetPrivate->anchors()->resetBottom(); QQmlPropertyPrivate::removeBinding(d->bottomProp); } if (d->anchorSet->d_func()->resetAnchors & QQuickAnchors::VCenterAnchor) { targetPrivate->anchors()->resetVerticalCenter(); QQmlPropertyPrivate::removeBinding(d->vCenterProp); } if (d->anchorSet->d_func()->resetAnchors & QQuickAnchors::BaselineAnchor) { targetPrivate->anchors()->resetBaseline(); QQmlPropertyPrivate::removeBinding(d->baselineProp); } //set any anchors that have been specified if (d->leftBinding) QQmlPropertyPrivate::setBinding(d->leftBinding.data()); if (d->rightBinding) QQmlPropertyPrivate::setBinding(d->rightBinding.data()); if (d->hCenterBinding) QQmlPropertyPrivate::setBinding(d->hCenterBinding.data()); if (d->topBinding) QQmlPropertyPrivate::setBinding(d->topBinding.data()); if (d->bottomBinding) QQmlPropertyPrivate::setBinding(d->bottomBinding.data()); if (d->vCenterBinding) QQmlPropertyPrivate::setBinding(d->vCenterBinding.data()); if (d->baselineBinding) QQmlPropertyPrivate::setBinding(d->baselineBinding.data()); } bool QQuickAnchorChanges::isReversable() { return true; } void QQuickAnchorChanges::reverse() { Q_D(QQuickAnchorChanges); if (!d->target) return; QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(d->target); //reset any anchors set by the state if (d->leftBinding) { targetPrivate->anchors()->resetLeft(); QQmlPropertyPrivate::removeBinding(d->leftBinding.data()); } if (d->rightBinding) { targetPrivate->anchors()->resetRight(); QQmlPropertyPrivate::removeBinding(d->rightBinding.data()); } if (d->hCenterBinding) { targetPrivate->anchors()->resetHorizontalCenter(); QQmlPropertyPrivate::removeBinding(d->hCenterBinding.data()); } if (d->topBinding) { targetPrivate->anchors()->resetTop(); QQmlPropertyPrivate::removeBinding(d->topBinding.data()); } if (d->bottomBinding) { targetPrivate->anchors()->resetBottom(); QQmlPropertyPrivate::removeBinding(d->bottomBinding.data()); } if (d->vCenterBinding) { targetPrivate->anchors()->resetVerticalCenter(); QQmlPropertyPrivate::removeBinding(d->vCenterBinding.data()); } if (d->baselineBinding) { targetPrivate->anchors()->resetBaseline(); QQmlPropertyPrivate::removeBinding(d->baselineBinding.data()); } //restore previous anchors if (d->origLeftBinding) QQmlPropertyPrivate::setBinding(d->leftProp, d->origLeftBinding.data()); if (d->origRightBinding) QQmlPropertyPrivate::setBinding(d->rightProp, d->origRightBinding.data()); if (d->origHCenterBinding) QQmlPropertyPrivate::setBinding(d->hCenterProp, d->origHCenterBinding.data()); if (d->origTopBinding) QQmlPropertyPrivate::setBinding(d->topProp, d->origTopBinding.data()); if (d->origBottomBinding) QQmlPropertyPrivate::setBinding(d->bottomProp, d->origBottomBinding.data()); if (d->origVCenterBinding) QQmlPropertyPrivate::setBinding(d->vCenterProp, d->origVCenterBinding.data()); if (d->origBaselineBinding) QQmlPropertyPrivate::setBinding(d->baselineProp, d->origBaselineBinding.data()); //restore any absolute geometry changed by the state's anchors QQuickAnchors::Anchors stateVAnchors = d->anchorSet->d_func()->usedAnchors & QQuickAnchors::Vertical_Mask; QQuickAnchors::Anchors origVAnchors = targetPrivate->anchors()->usedAnchors() & QQuickAnchors::Vertical_Mask; QQuickAnchors::Anchors stateHAnchors = d->anchorSet->d_func()->usedAnchors & QQuickAnchors::Horizontal_Mask; QQuickAnchors::Anchors origHAnchors = targetPrivate->anchors()->usedAnchors() & QQuickAnchors::Horizontal_Mask; bool stateSetWidth = (stateHAnchors && stateHAnchors != QQuickAnchors::LeftAnchor && stateHAnchors != QQuickAnchors::RightAnchor && stateHAnchors != QQuickAnchors::HCenterAnchor); bool origSetWidth = (origHAnchors && origHAnchors != QQuickAnchors::LeftAnchor && origHAnchors != QQuickAnchors::RightAnchor && origHAnchors != QQuickAnchors::HCenterAnchor); if (d->origWidth.isValid() && stateSetWidth && !origSetWidth) d->target->setWidth(d->origWidth.value); bool stateSetHeight = (stateVAnchors && stateVAnchors != QQuickAnchors::TopAnchor && stateVAnchors != QQuickAnchors::BottomAnchor && stateVAnchors != QQuickAnchors::VCenterAnchor && stateVAnchors != QQuickAnchors::BaselineAnchor); bool origSetHeight = (origVAnchors && origVAnchors != QQuickAnchors::TopAnchor && origVAnchors != QQuickAnchors::BottomAnchor && origVAnchors != QQuickAnchors::VCenterAnchor && origVAnchors != QQuickAnchors::BaselineAnchor); if (d->origHeight.isValid() && stateSetHeight && !origSetHeight) d->target->setHeight(d->origHeight.value); if (stateHAnchors && !origHAnchors) d->target->setX(d->origX); if (stateVAnchors && !origVAnchors) d->target->setY(d->origY); } QQuickStateActionEvent::EventType QQuickAnchorChanges::type() const { return AnchorChanges; } QList QQuickAnchorChanges::additionalActions() const { Q_D(const QQuickAnchorChanges); QList extra; QQuickAnchors::Anchors combined = d->anchorSet->d_func()->usedAnchors | d->anchorSet->d_func()->resetAnchors; bool hChange = combined & QQuickAnchors::Horizontal_Mask; bool vChange = combined & QQuickAnchors::Vertical_Mask; if (d->target) { QQuickStateAction a; if (hChange && d->fromX != d->toX) { a.property = QQmlProperty(d->target, QLatin1String("x")); a.toValue = d->toX; extra << a; } if (vChange && d->fromY != d->toY) { a.property = QQmlProperty(d->target, QLatin1String("y")); a.toValue = d->toY; extra << a; } if (hChange && d->fromWidth != d->toWidth) { a.property = QQmlProperty(d->target, QLatin1String("width")); a.toValue = d->toWidth; extra << a; } if (vChange && d->fromHeight != d->toHeight) { a.property = QQmlProperty(d->target, QLatin1String("height")); a.toValue = d->toHeight; extra << a; } } return extra; } bool QQuickAnchorChanges::changesBindings() { return true; } void QQuickAnchorChanges::saveOriginals() { Q_D(QQuickAnchorChanges); if (!d->target) return; d->origLeftBinding = QQmlPropertyPrivate::binding(d->leftProp); d->origRightBinding = QQmlPropertyPrivate::binding(d->rightProp); d->origHCenterBinding = QQmlPropertyPrivate::binding(d->hCenterProp); d->origTopBinding = QQmlPropertyPrivate::binding(d->topProp); d->origBottomBinding = QQmlPropertyPrivate::binding(d->bottomProp); d->origVCenterBinding = QQmlPropertyPrivate::binding(d->vCenterProp); d->origBaselineBinding = QQmlPropertyPrivate::binding(d->baselineProp); QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(d->target); if (targetPrivate->widthValid) d->origWidth = d->target->width(); if (targetPrivate->heightValid) d->origHeight = d->target->height(); d->origX = d->target->x(); d->origY = d->target->y(); d->applyOrigLeft = d->applyOrigRight = d->applyOrigHCenter = d->applyOrigTop = d->applyOrigBottom = d->applyOrigVCenter = d->applyOrigBaseline = false; saveCurrentValues(); } void QQuickAnchorChanges::copyOriginals(QQuickStateActionEvent *other) { Q_D(QQuickAnchorChanges); QQuickAnchorChanges *ac = static_cast(other); QQuickAnchorChangesPrivate *acp = ac->d_func(); QQuickAnchors::Anchors combined = acp->anchorSet->d_func()->usedAnchors | acp->anchorSet->d_func()->resetAnchors; //probably also need to revert some things d->applyOrigLeft = (combined & QQuickAnchors::LeftAnchor); d->applyOrigRight = (combined & QQuickAnchors::RightAnchor); d->applyOrigHCenter = (combined & QQuickAnchors::HCenterAnchor); d->applyOrigTop = (combined & QQuickAnchors::TopAnchor); d->applyOrigBottom = (combined & QQuickAnchors::BottomAnchor); d->applyOrigVCenter = (combined & QQuickAnchors::VCenterAnchor); d->applyOrigBaseline = (combined & QQuickAnchors::BaselineAnchor); d->origLeftBinding = acp->origLeftBinding; d->origRightBinding = acp->origRightBinding; d->origHCenterBinding = acp->origHCenterBinding; d->origTopBinding = acp->origTopBinding; d->origBottomBinding = acp->origBottomBinding; d->origVCenterBinding = acp->origVCenterBinding; d->origBaselineBinding = acp->origBaselineBinding; d->origWidth = acp->origWidth; d->origHeight = acp->origHeight; d->origX = acp->origX; d->origY = acp->origY; //clear old values from other //### could this be generalized for all QQuickStateActionEvents, and called after copyOriginals? acp->leftBinding = nullptr; acp->rightBinding = nullptr; acp->hCenterBinding = nullptr; acp->topBinding = nullptr; acp->bottomBinding = nullptr; acp->vCenterBinding = nullptr; acp->baselineBinding = nullptr; acp->origLeftBinding = nullptr; acp->origRightBinding = nullptr; acp->origHCenterBinding = nullptr; acp->origTopBinding = nullptr; acp->origBottomBinding = nullptr; acp->origVCenterBinding = nullptr; acp->origBaselineBinding = nullptr; saveCurrentValues(); } void QQuickAnchorChanges::clearBindings() { Q_D(QQuickAnchorChanges); if (!d->target) return; //### should this (saving "from" values) be moved to saveCurrentValues()? d->fromX = d->target->x(); d->fromY = d->target->y(); d->fromWidth = d->target->width(); d->fromHeight = d->target->height(); QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(d->target); //reset any anchors with corresponding reverts //reset any anchors that have been specified as "undefined" //reset any anchors that we'll be setting in the state QQuickAnchors::Anchors combined = d->anchorSet->d_func()->resetAnchors | d->anchorSet->d_func()->usedAnchors; if (d->applyOrigLeft || (combined & QQuickAnchors::LeftAnchor)) { targetPrivate->anchors()->resetLeft(); QQmlPropertyPrivate::removeBinding(d->leftProp); } if (d->applyOrigRight || (combined & QQuickAnchors::RightAnchor)) { targetPrivate->anchors()->resetRight(); QQmlPropertyPrivate::removeBinding(d->rightProp); } if (d->applyOrigHCenter || (combined & QQuickAnchors::HCenterAnchor)) { targetPrivate->anchors()->resetHorizontalCenter(); QQmlPropertyPrivate::removeBinding(d->hCenterProp); } if (d->applyOrigTop || (combined & QQuickAnchors::TopAnchor)) { targetPrivate->anchors()->resetTop(); QQmlPropertyPrivate::removeBinding(d->topProp); } if (d->applyOrigBottom || (combined & QQuickAnchors::BottomAnchor)) { targetPrivate->anchors()->resetBottom(); QQmlPropertyPrivate::removeBinding(d->bottomProp); } if (d->applyOrigVCenter || (combined & QQuickAnchors::VCenterAnchor)) { targetPrivate->anchors()->resetVerticalCenter(); QQmlPropertyPrivate::removeBinding(d->vCenterProp); } if (d->applyOrigBaseline || (combined & QQuickAnchors::BaselineAnchor)) { targetPrivate->anchors()->resetBaseline(); QQmlPropertyPrivate::removeBinding(d->baselineProp); } } bool QQuickAnchorChanges::mayOverride(QQuickStateActionEvent*other) { if (other->type() != AnchorChanges) return false; if (static_cast(this) == other) return true; if (static_cast(other)->object() == object()) return true; return false; } void QQuickAnchorChanges::rewind() { Q_D(QQuickAnchorChanges); if (!d->target) return; QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(d->target); //restore previous values (but not previous bindings, i.e. anchors) d->target->setX(d->rewindX); d->target->setY(d->rewindY); if (targetPrivate->widthValid) { d->target->setWidth(d->rewindWidth); } if (targetPrivate->heightValid) { d->target->setHeight(d->rewindHeight); } } void QQuickAnchorChanges::saveCurrentValues() { Q_D(QQuickAnchorChanges); if (!d->target) return; QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(d->target); d->rewindLeft = targetPrivate->anchors()->left(); d->rewindRight = targetPrivate->anchors()->right(); d->rewindHCenter = targetPrivate->anchors()->horizontalCenter(); d->rewindTop = targetPrivate->anchors()->top(); d->rewindBottom = targetPrivate->anchors()->bottom(); d->rewindVCenter = targetPrivate->anchors()->verticalCenter(); d->rewindBaseline = targetPrivate->anchors()->baseline(); d->rewindX = d->target->x(); d->rewindY = d->target->y(); d->rewindWidth = d->target->width(); d->rewindHeight = d->target->height(); } void QQuickAnchorChanges::saveTargetValues() { Q_D(QQuickAnchorChanges); if (!d->target) return; d->toX = d->target->x(); d->toY = d->target->y(); d->toWidth = d->target->width(); d->toHeight = d->target->height(); } #include QT_END_NAMESPACE