blob: 8ee4229013283e37168a393dfeb32af797db8c8b [file] [log] [blame]
/****************************************************************************
**
** 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 <qqmlinfo.h>
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<QQuickAnchors::Anchors>(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 <moc_qquickanchors_p.cpp>