summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--.qmake.conf2
-rw-r--r--examples/quick/controls/basiclayouts/main.qml25
-rw-r--r--src/controls/Styles/iOS/SliderStyle.qml50
-rw-r--r--src/controls/TextArea.qml13
-rw-r--r--src/controls/plugin.cpp4
-rw-r--r--src/layouts/layouts.pro2
-rw-r--r--src/layouts/plugin.cpp2
-rw-r--r--src/layouts/qquickgridlayoutengine.cpp255
-rw-r--r--src/layouts/qquickgridlayoutengine_p.h14
-rw-r--r--src/layouts/qquicklayout.cpp340
-rw-r--r--src/layouts/qquicklayout_p.h21
-rw-r--r--src/layouts/qquicklinearlayout.cpp61
-rw-r--r--src/layouts/qquicklinearlayout_p.h12
-rw-r--r--src/layouts/qquickstacklayout.cpp333
-rw-r--r--src/layouts/qquickstacklayout_p.h105
-rw-r--r--src/widgets/qquickqfiledialog.cpp1
-rw-r--r--tests/auto/auto.pro2
-rw-r--r--tests/auto/controls/data/textarea/ta_editingfinished.qml59
-rw-r--r--tests/auto/controls/data/tst_gridlayout.qml6
-rw-r--r--tests/auto/controls/data/tst_rowlayout.qml88
-rw-r--r--tests/auto/controls/data/tst_stacklayout.qml448
-rw-r--r--tests/auto/controls/data/tst_textarea.qml30
-rw-r--r--tests/auto/paint/paint.pro6
-rw-r--r--tests/auto/paint/tst_paint.cpp107
25 files changed, 1525 insertions, 464 deletions
diff --git a/.gitignore b/.gitignore
index ad328129..a953698b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,8 +17,9 @@
/tests/auto/dialogs/tst_dialogs
/tests/auto/extras/tst_extras
/tests/auto/paint/tst_paint
-/tests/auto/qtdesktop/tst_qtdesktop
/tests/auto/applicationwindow/tst_applicationwindow
+/tests/auto/customcontrolsstyle/tst_customcontrolsstyle
+/tests/auto/qquicktreemodeladaptor/tst_qquicktreemodeladaptor
/tests/auto/testplugin/QtQuickControlsTests/testplugin.*
/tests/benchmarks/statusindicator/tst_bench_statusindicator
diff --git a/.qmake.conf b/.qmake.conf
index 0abcc473..235e5216 100644
--- a/.qmake.conf
+++ b/.qmake.conf
@@ -2,4 +2,4 @@ load(qt_build_config)
CONFIG += warning_clean
android|ios|qnx|winrt|isEmpty(QT.widgets.name): CONFIG += no_desktop
-MODULE_VERSION = 5.5.1
+MODULE_VERSION = 5.6.0
diff --git a/examples/quick/controls/basiclayouts/main.qml b/examples/quick/controls/basiclayouts/main.qml
index bc5ca214..b6694b45 100644
--- a/examples/quick/controls/basiclayouts/main.qml
+++ b/examples/quick/controls/basiclayouts/main.qml
@@ -40,7 +40,7 @@
import QtQuick 2.2
import QtQuick.Controls 1.2
-import QtQuick.Layouts 1.0
+import QtQuick.Layouts 1.3
ApplicationWindow {
visible: true
@@ -108,5 +108,28 @@ ApplicationWindow {
Layout.fillHeight: true
Layout.fillWidth: true
}
+ GroupBox {
+ id: stackBox
+ title: "Stack layout"
+ implicitWidth: 200
+ implicitHeight: 60
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ StackLayout {
+ id: stackLayout
+ anchors.fill: parent
+
+ function advance() { currentIndex = (currentIndex + 1) % count }
+
+ Repeater {
+ id: stackRepeater
+ model: 5
+ Rectangle {
+ color: Qt.hsla((0.5 + index)/stackRepeater.count, 0.3, 0.7, 1)
+ Button { anchors.centerIn: parent; text: "Page " + (index + 1); onClicked: { stackLayout.advance() } }
+ }
+ }
+ }
+ }
}
}
diff --git a/src/controls/Styles/iOS/SliderStyle.qml b/src/controls/Styles/iOS/SliderStyle.qml
index 3d39ce2b..8a87f809 100644
--- a/src/controls/Styles/iOS/SliderStyle.qml
+++ b/src/controls/Styles/iOS/SliderStyle.qml
@@ -34,6 +34,54 @@
**
****************************************************************************/
+import QtQuick 2.3
import QtQuick.Controls.Styles 1.3
-SliderStyle { }
+SliderStyle {
+ groove: Rectangle {
+ implicitWidth: 20
+ implicitHeight: 2
+
+ color: "#a8a8a8"
+ radius: 45.0
+
+ Rectangle {
+ width: styleData.handlePosition
+ height: parent.height
+ color: "#0a60ff"
+ radius: parent.radius
+ }
+ }
+
+ handle: Item {
+ width: 29
+ height: 32
+
+ Rectangle {
+ y: 3
+ width: 29
+ height: 29
+ radius: 90.0
+
+ color: "#d6d6d6"
+ opacity: 0.2
+ }
+
+ Rectangle {
+ width: 29
+ height: 29
+ radius: 90.0
+
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: "#e2e2e2" }
+ GradientStop { position: 1.0; color: "#d6d6d6" }
+ }
+
+ Rectangle {
+ anchors.fill: parent
+ anchors.margins: 1
+ radius: parent.radius
+ }
+ }
+ }
+}
diff --git a/src/controls/TextArea.qml b/src/controls/TextArea.qml
index 23360f06..87b13e0d 100644
--- a/src/controls/TextArea.qml
+++ b/src/controls/TextArea.qml
@@ -34,7 +34,7 @@
**
****************************************************************************/
-import QtQuick 2.2
+import QtQuick 2.6
import QtQuick.Window 2.2
import QtQuick.Controls 1.2
import QtQuick.Controls.Private 1.0
@@ -423,6 +423,16 @@ ScrollView {
signal linkHovered(string link)
/*!
+ \qmlsignal TextArea::editingFinished()
+ \since QtQuick.Controls 1.5
+
+ This signal is emitted when the text area loses focus.
+
+ The corresponding handler is \c onEditingFinished.
+ */
+ signal editingFinished()
+
+ /*!
\qmlproperty string TextArea::hoveredLink
\since QtQuick.Controls 1.1
@@ -819,6 +829,7 @@ ScrollView {
onLinkActivated: area.linkActivated(link)
onLinkHovered: area.linkHovered(link)
+ onEditingFinished: area.editingFinished()
function activate() {
if (activeFocusOnPress) {
diff --git a/src/controls/plugin.cpp b/src/controls/plugin.cpp
index e6868569..f20cada1 100644
--- a/src/controls/plugin.cpp
+++ b/src/controls/plugin.cpp
@@ -111,7 +111,9 @@ static const struct {
{ "TextArea", 1, 3 },
- { "TreeView", 1, 4 }
+ { "TreeView", 1, 4 },
+
+ { "TextArea", 1, 5 }
};
void QtQuickControlsPlugin::registerTypes(const char *uri)
diff --git a/src/layouts/layouts.pro b/src/layouts/layouts.pro
index 3ef18f85..f7a73b7e 100644
--- a/src/layouts/layouts.pro
+++ b/src/layouts/layouts.pro
@@ -10,12 +10,14 @@ QMAKE_DOCS = $$PWD/doc/qtquicklayouts.qdocconf
SOURCES += plugin.cpp \
qquicklayout.cpp \
qquicklinearlayout.cpp \
+ qquickstacklayout.cpp \
qquickgridlayoutengine.cpp \
qquicklayoutstyleinfo.cpp
HEADERS += \
qquicklayout_p.h \
qquicklinearlayout_p.h \
+ qquickstacklayout_p.h \
qquickgridlayoutengine_p.h \
qquicklayoutstyleinfo_p.h
diff --git a/src/layouts/plugin.cpp b/src/layouts/plugin.cpp
index fa72fa8d..6a670539 100644
--- a/src/layouts/plugin.cpp
+++ b/src/layouts/plugin.cpp
@@ -37,6 +37,7 @@
#include <QtQml/qqmlextensionplugin.h>
#include "qquicklinearlayout_p.h"
+#include "qquickstacklayout_p.h"
QT_BEGIN_NAMESPACE
@@ -54,6 +55,7 @@ public:
qmlRegisterType<QQuickRowLayout>(uri, 1, 0, "RowLayout");
qmlRegisterType<QQuickColumnLayout>(uri, 1, 0, "ColumnLayout");
qmlRegisterType<QQuickGridLayout>(uri, 1, 0, "GridLayout");
+ qmlRegisterType<QQuickStackLayout>(uri, 1, 3, "StackLayout");
qmlRegisterUncreatableType<QQuickLayout>(uri, 1, 0, "Layout",
QStringLiteral("Do not create objects of type Layout"));
qmlRegisterUncreatableType<QQuickLayout>(uri, 1, 2, "Layout",
diff --git a/src/layouts/qquickgridlayoutengine.cpp b/src/layouts/qquickgridlayoutengine.cpp
index 553f45d0..2c08eec1 100644
--- a/src/layouts/qquickgridlayoutengine.cpp
+++ b/src/layouts/qquickgridlayoutengine.cpp
@@ -40,261 +40,6 @@
QT_BEGIN_NAMESPACE
-/*
- The layout engine assumes:
- 1. minimum <= preferred <= maximum
- 2. descent is within minimum and maximum bounds (### verify)
-
- This function helps to ensure that by the following rules (in the following order):
- 1. If minimum > maximum, set minimum = maximum
- 2. Make sure preferred is not outside the [minimum,maximum] range.
- 3. If descent > minimum, set descent = minimum (### verify if this is correct, it might
- need some refinements to multiline texts)
-
- If any values are "not set" (i.e. negative), they will be left untouched, so that we
- know which values needs to be fetched from the implicit hints (not user hints).
- */
-static void normalizeHints(qreal &minimum, qreal &preferred, qreal &maximum, qreal &descent)
-{
- if (minimum >= 0 && maximum >= 0 && minimum > maximum)
- minimum = maximum;
-
- if (preferred >= 0) {
- if (minimum >= 0 && preferred < minimum) {
- preferred = minimum;
- } else if (maximum >= 0 && preferred > maximum) {
- preferred = maximum;
- }
- }
-
- if (minimum >= 0 && descent > minimum)
- descent = minimum;
-}
-
-static void boundSize(QSizeF &result, const QSizeF &size)
-{
- if (size.width() >= 0 && size.width() < result.width())
- result.setWidth(size.width());
- if (size.height() >= 0 && size.height() < result.height())
- result.setHeight(size.height());
-}
-
-static void expandSize(QSizeF &result, const QSizeF &size)
-{
- if (size.width() >= 0 && size.width() > result.width())
- result.setWidth(size.width());
- if (size.height() >= 0 && size.height() > result.height())
- result.setHeight(size.height());
-}
-
-static inline void combineHints(qreal &current, qreal fallbackHint)
-{
- if (current < 0)
- current = fallbackHint;
-}
-
-static inline void combineSize(QSizeF &result, const QSizeF &fallbackSize)
-{
- combineHints(result.rwidth(), fallbackSize.width());
- combineHints(result.rheight(), fallbackSize.height());
-}
-
-static inline void combineImplicitHints(QQuickLayoutAttached *info, Qt::SizeHint which, QSizeF *size)
-{
- if (!info) return;
-
- Q_ASSERT(which == Qt::MinimumSize || which == Qt::MaximumSize);
-
- const QSizeF constraint(which == Qt::MinimumSize
- ? QSizeF(info->minimumWidth(), info->minimumHeight())
- : QSizeF(info->maximumWidth(), info->maximumHeight()));
-
- if (!info->isExtentExplicitlySet(Qt::Horizontal, which))
- combineHints(size->rwidth(), constraint.width());
- if (!info->isExtentExplicitlySet(Qt::Vertical, which))
- combineHints(size->rheight(), constraint.height());
-}
-
-/*!
- \internal
- Note: Can potentially return the attached QQuickLayoutAttached object through \a attachedInfo.
-
- It is like this is because it enables it to be reused.
-
- The goal of this function is to return the effective minimum, preferred and maximum size hints
- that the layout will use for this item.
- This function takes care of gathering all explicitly set size hints, normalizes them so
- that min < pref < max.
- Further, the hints _not_explicitly_ set will then be initialized with the implicit size hints,
- which is usually derived from the content of the layouts (or items).
-
- The following table illustrates the preference of the properties used for measuring layout
- items. If present, the USER properties will be preferred. If USER properties are not present,
- the HINT properties will be preferred. Finally, the FALLBACK properties will be used as an
- ultimate fallback.
-
- Note that one can query if the value of Layout.minimumWidth or Layout.maximumWidth has been
- explicitly or implicitly set with QQuickLayoutAttached::isExtentExplicitlySet(). This
- determines if it should be used as a USER or as a HINT value.
-
- Fractional size hints will be ceiled to the closest integer. This is in order to give some
- slack when the items are snapped to the pixel grid.
-
- | *Minimum* | *Preferred* | *Maximum* |
-+----------------+----------------------+-----------------------+--------------------------+
-|USER (explicit) | Layout.minimumWidth | Layout.preferredWidth | Layout.maximumWidth |
-|HINT (implicit) | Layout.minimumWidth | implicitWidth | Layout.maximumWidth |
-|FALLBACK | 0 | width | Number.POSITIVE_INFINITY |
-+----------------+----------------------+-----------------------+--------------------------+
- */
-void QQuickGridLayoutItem::effectiveSizeHints_helper(QQuickItem *item, QSizeF *cachedSizeHints, QQuickLayoutAttached **attachedInfo, bool useFallbackToWidthOrHeight)
-{
- for (int i = 0; i < Qt::NSizeHints; ++i)
- cachedSizeHints[i] = QSizeF();
- QQuickLayoutAttached *info = attachedLayoutObject(item, false);
- // First, retrieve the user-specified hints from the attached "Layout." properties
- if (info) {
- struct Getters {
- SizeGetter call[NSizes];
- };
-
- static Getters horGetters = {
- {&QQuickLayoutAttached::minimumWidth, &QQuickLayoutAttached::preferredWidth, &QQuickLayoutAttached::maximumWidth},
- };
-
- static Getters verGetters = {
- {&QQuickLayoutAttached::minimumHeight, &QQuickLayoutAttached::preferredHeight, &QQuickLayoutAttached::maximumHeight}
- };
- for (int i = 0; i < NSizes; ++i) {
- SizeGetter getter = horGetters.call[i];
- Q_ASSERT(getter);
-
- if (info->isExtentExplicitlySet(Qt::Horizontal, (Qt::SizeHint)i))
- cachedSizeHints[i].setWidth(qCeil((info->*getter)()));
-
- getter = verGetters.call[i];
- Q_ASSERT(getter);
- if (info->isExtentExplicitlySet(Qt::Vertical, (Qt::SizeHint)i))
- cachedSizeHints[i].setHeight(qCeil((info->*getter)()));
- }
- }
-
- QSizeF &minS = cachedSizeHints[Qt::MinimumSize];
- QSizeF &prefS = cachedSizeHints[Qt::PreferredSize];
- QSizeF &maxS = cachedSizeHints[Qt::MaximumSize];
- QSizeF &descentS = cachedSizeHints[Qt::MinimumDescent];
-
- // For instance, will normalize the following user-set hints
- // from: [10, 5, 60]
- // to: [10, 10, 60]
- normalizeHints(minS.rwidth(), prefS.rwidth(), maxS.rwidth(), descentS.rwidth());
- normalizeHints(minS.rheight(), prefS.rheight(), maxS.rheight(), descentS.rheight());
-
- // All explicit values gathered, now continue to gather the implicit sizes
-
- //--- GATHER MAXIMUM SIZE HINTS ---
- combineImplicitHints(info, Qt::MaximumSize, &maxS);
- combineSize(maxS, QSizeF(std::numeric_limits<qreal>::infinity(), std::numeric_limits<qreal>::infinity()));
- // implicit max or min sizes should not limit an explicitly set preferred size
- expandSize(maxS, prefS);
- expandSize(maxS, minS);
-
- //--- GATHER MINIMUM SIZE HINTS ---
- combineImplicitHints(info, Qt::MinimumSize, &minS);
- expandSize(minS, QSizeF(0,0));
- boundSize(minS, prefS);
- boundSize(minS, maxS);
-
- //--- GATHER PREFERRED SIZE HINTS ---
- // First, from implicitWidth/Height
- qreal &prefWidth = prefS.rwidth();
- qreal &prefHeight = prefS.rheight();
- if (prefWidth < 0 && item->implicitWidth() > 0)
- prefWidth = qCeil(item->implicitWidth());
- if (prefHeight < 0 && item->implicitHeight() > 0)
- prefHeight = qCeil(item->implicitHeight());
-
- // If that fails, make an ultimate fallback to width/height
-
- if (!info && (prefWidth < 0 || prefHeight < 0))
- info = attachedLayoutObject(item);
-
- if (useFallbackToWidthOrHeight && info) {
- /* This block is a bit hacky, but if we want to support using width/height
- as preferred size hints in layouts, (which we think most people expect),
- we only want to use the initial width.
- This is because the width will change due to layout rearrangement, and the preferred
- width should return the same value, regardless of the current width.
- We therefore store the width in the implicitWidth attached property.
- Since the layout listens to changes of implicitWidth, (it will
- basically cause an invalidation of the layout), we have to disable that
- notification while we set the implicit width (and height).
-
- Only use this fallback the first time the size hint is queried. Otherwise, we might
- end up picking a width that is different than what was specified in the QML.
- */
- if (prefWidth < 0 || prefHeight < 0) {
- item->blockSignals(true);
- if (prefWidth < 0) {
- prefWidth = item->width();
- item->setImplicitWidth(prefWidth);
- }
- if (prefHeight < 0) {
- prefHeight = item->height();
- item->setImplicitHeight(prefHeight);
- }
- item->blockSignals(false);
- }
- }
-
-
-
- // Normalize again after the implicit hints have been gathered
- expandSize(prefS, minS);
- boundSize(prefS, maxS);
-
- //--- GATHER DESCENT
- // Minimum descent is only applicable for the effective minimum height,
- // so we gather the descent last.
- const qreal minimumDescent = minS.height() - item->baselineOffset();
- descentS.setHeight(minimumDescent);
-
- if (info) {
- QMarginsF margins = info->qMargins();
- QSizeF extraMargins(margins.left() + margins.right(), margins.top() + margins.bottom());
- minS += extraMargins;
- prefS += extraMargins;
- maxS += extraMargins;
- descentS += extraMargins;
- }
- if (attachedInfo)
- *attachedInfo = info;
-}
-
-/*!
- \internal
-
- Assumes \a info is set (if the object has an attached property)
- */
-QLayoutPolicy::Policy QQuickGridLayoutItem::effectiveSizePolicy_helper(QQuickItem *item, Qt::Orientation orientation, QQuickLayoutAttached *info)
-{
- bool fillExtent = false;
- bool isSet = false;
- if (info) {
- if (orientation == Qt::Horizontal) {
- isSet = info->isFillWidthSet();
- if (isSet) fillExtent = info->fillWidth();
- } else {
- isSet = info->isFillHeightSet();
- if (isSet) fillExtent = info->fillHeight();
- }
- }
- if (!isSet && qobject_cast<QQuickLayout*>(item))
- fillExtent = true;
- return fillExtent ? QLayoutPolicy::Preferred : QLayoutPolicy::Fixed;
-
-}
-
void QQuickGridLayoutEngine::setAlignment(QQuickItem *quickItem, Qt::Alignment alignment)
{
if (QQuickGridLayoutItem *item = findLayoutItem(quickItem)) {
diff --git a/src/layouts/qquickgridlayoutengine_p.h b/src/layouts/qquickgridlayoutengine_p.h
index a94ef934..ce7285bf 100644
--- a/src/layouts/qquickgridlayoutengine_p.h
+++ b/src/layouts/qquickgridlayoutengine_p.h
@@ -64,23 +64,18 @@ public:
: QGridLayoutItem(row, column, rowSpan, columnSpan, alignment), m_item(item), sizeHintCacheDirty(true), useFallbackToWidthOrHeight(true) {}
- typedef qreal (QQuickLayoutAttached::*SizeGetter)() const;
-
QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
{
Q_UNUSED(constraint); // Quick Layouts does not support constraint atm
return effectiveSizeHints()[which];
}
- static void effectiveSizeHints_helper(QQuickItem *item, QSizeF *cachedSizeHints, QQuickLayoutAttached **info, bool useFallbackToWidthOrHeight);
- static QLayoutPolicy::Policy effectiveSizePolicy_helper(QQuickItem *item, Qt::Orientation orientation, QQuickLayoutAttached *info);
-
QSizeF *effectiveSizeHints() const
{
if (!sizeHintCacheDirty)
return cachedSizeHints;
- effectiveSizeHints_helper(m_item, cachedSizeHints, 0, useFallbackToWidthOrHeight);
+ QQuickLayout::effectiveSizeHints_helper(m_item, cachedSizeHints, 0, useFallbackToWidthOrHeight);
useFallbackToWidthOrHeight = false;
sizeHintCacheDirty = false;
@@ -103,7 +98,7 @@ public:
QLayoutPolicy::Policy sizePolicy(Qt::Orientation orientation) const
{
- return effectiveSizePolicy_helper(m_item, orientation, attachedLayoutObject(m_item, false));
+ return QQuickLayout::effectiveSizePolicy_helper(m_item, orientation, attachedLayoutObject(m_item, false));
}
void setGeometry(const QRectF &rect)
@@ -112,8 +107,7 @@ public:
const QRectF r = info ? rect.marginsRemoved(info->qMargins()) : rect;
const QSizeF oldSize(m_item->width(), m_item->height());
const QSizeF newSize = r.size();
- QPointF topLeft(qCeil(r.x()), qCeil(r.y()));
- m_item->setPosition(topLeft);
+ m_item->setPosition(r.topLeft());
if (newSize == oldSize) {
if (QQuickLayout *lay = qobject_cast<QQuickLayout *>(m_item)) {
if (lay->arrangementIsDirty())
@@ -135,7 +129,7 @@ private:
class QQuickGridLayoutEngine : public QGridLayoutEngine {
public:
- QQuickGridLayoutEngine() : QGridLayoutEngine(Qt::AlignVCenter) { }
+ QQuickGridLayoutEngine() : QGridLayoutEngine(Qt::AlignVCenter, true /*snapToPixelGrid*/) { }
int indexOf(QQuickItem *item) const {
for (int i = 0; i < q_items.size(); ++i) {
diff --git a/src/layouts/qquicklayout.cpp b/src/layouts/qquicklayout.cpp
index 759ad6f2..d812c112 100644
--- a/src/layouts/qquicklayout.cpp
+++ b/src/layouts/qquicklayout.cpp
@@ -38,6 +38,7 @@
#include <QEvent>
#include <QtCore/qcoreapplication.h>
#include <QtCore/qnumeric.h>
+#include <QtCore/qmath.h>
#include <limits>
/*!
@@ -684,9 +685,6 @@ QQuickItem *QQuickLayoutAttached::item() const
}
-
-
-
QQuickLayout::QQuickLayout(QQuickLayoutPrivate &dd, QQuickItem *parent)
: QQuickItem(dd, parent),
m_dirty(false)
@@ -695,7 +693,7 @@ QQuickLayout::QQuickLayout(QQuickLayoutPrivate &dd, QQuickItem *parent)
QQuickLayout::~QQuickLayout()
{
-
+ d_func()->m_isReady = false;
}
QQuickLayoutAttached *QQuickLayout::qmlAttachedProperties(QObject *object)
@@ -710,7 +708,11 @@ void QQuickLayout::updatePolish()
void QQuickLayout::componentComplete()
{
- QQuickItem::componentComplete();
+ Q_D(QQuickLayout);
+ d->m_disableRearrange = true;
+ QQuickItem::componentComplete(); // will call our geometryChanged(), (where isComponentComplete() == true)
+ d->m_disableRearrange = false;
+ d->m_isReady = true;
}
void QQuickLayout::invalidate(QQuickItem * /*childItem*/)
@@ -726,9 +728,337 @@ void QQuickLayout::invalidate(QQuickItem * /*childItem*/)
}
}
+bool QQuickLayout::shouldIgnoreItem(QQuickItem *child, QQuickLayoutAttached *&info, QSizeF *sizeHints) const
+{
+ Q_D(const QQuickLayout);
+ bool ignoreItem = true;
+ QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(child);
+ if (childPrivate->explicitVisible) {
+ effectiveSizeHints_helper(child, sizeHints, &info, true);
+ QSizeF effectiveMaxSize = sizeHints[Qt::MaximumSize];
+ if (!effectiveMaxSize.isNull()) {
+ QSizeF &prefS = sizeHints[Qt::PreferredSize];
+ if (effectiveSizePolicy_helper(child, Qt::Horizontal, info) == QLayoutPolicy::Fixed)
+ effectiveMaxSize.setWidth(prefS.width());
+ if (effectiveSizePolicy_helper(child, Qt::Vertical, info) == QLayoutPolicy::Fixed)
+ effectiveMaxSize.setHeight(prefS.height());
+ }
+ ignoreItem = effectiveMaxSize.isNull();
+ }
+
+ if (ignoreItem)
+ d->m_ignoredItems << child;
+ return ignoreItem;
+}
+
+void QQuickLayout::itemChange(ItemChange change, const ItemChangeData &value)
+{
+ if (change == ItemChildAddedChange) {
+ QQuickItem *item = value.item;
+ QObject::connect(item, SIGNAL(implicitWidthChanged()), this, SLOT(invalidateSenderItem()));
+ QObject::connect(item, SIGNAL(implicitHeightChanged()), this, SLOT(invalidateSenderItem()));
+ QObject::connect(item, SIGNAL(baselineOffsetChanged(qreal)), this, SLOT(invalidateSenderItem()));
+ if (isReady())
+ updateLayoutItems();
+ } else if (change == ItemChildRemovedChange) {
+ QQuickItem *item = value.item;
+ QObject::disconnect(item, SIGNAL(implicitWidthChanged()), this, SLOT(invalidateSenderItem()));
+ QObject::disconnect(item, SIGNAL(implicitHeightChanged()), this, SLOT(invalidateSenderItem()));
+ QObject::disconnect(item, SIGNAL(baselineOffsetChanged(qreal)), this, SLOT(invalidateSenderItem()));
+ if (isReady())
+ updateLayoutItems();
+ }
+ QQuickItem::itemChange(change, value);
+}
+
+void QQuickLayout::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ Q_D(QQuickLayout);
+ QQuickItem::geometryChanged(newGeometry, oldGeometry);
+ if (d->m_disableRearrange || !isReady() || !newGeometry.isValid())
+ return;
+
+ quickLayoutDebug() << "QQuickStackLayout::geometryChanged" << newGeometry << oldGeometry;
+ rearrange(newGeometry.size());
+}
+
+void QQuickLayout::invalidateSenderItem()
+{
+ if (!isReady())
+ return;
+ QQuickItem *item = static_cast<QQuickItem *>(sender());
+ Q_ASSERT(item);
+ invalidate(item);
+}
+
+bool QQuickLayout::isReady() const
+{
+ return d_func()->m_isReady;
+}
+
void QQuickLayout::rearrange(const QSizeF &/*size*/)
{
m_dirty = false;
}
+
+/*
+ The layout engine assumes:
+ 1. minimum <= preferred <= maximum
+ 2. descent is within minimum and maximum bounds (### verify)
+
+ This function helps to ensure that by the following rules (in the following order):
+ 1. If minimum > maximum, set minimum = maximum
+ 2. Clamp preferred to be between the [minimum,maximum] range.
+ 3. If descent > minimum, set descent = minimum (### verify if this is correct, it might
+ need some refinements to multiline texts)
+
+ If any values are "not set" (i.e. negative), they will be left untouched, so that we
+ know which values needs to be fetched from the implicit hints (not user hints).
+ */
+static void normalizeHints(qreal &minimum, qreal &preferred, qreal &maximum, qreal &descent)
+{
+ if (minimum >= 0 && maximum >= 0 && minimum > maximum)
+ minimum = maximum;
+
+ if (preferred >= 0) {
+ if (minimum >= 0 && preferred < minimum) {
+ preferred = minimum;
+ } else if (maximum >= 0 && preferred > maximum) {
+ preferred = maximum;
+ }
+ }
+
+ if (minimum >= 0 && descent > minimum)
+ descent = minimum;
+}
+
+static void boundSize(QSizeF &result, const QSizeF &size)
+{
+ if (size.width() >= 0 && size.width() < result.width())
+ result.setWidth(size.width());
+ if (size.height() >= 0 && size.height() < result.height())
+ result.setHeight(size.height());
+}
+
+static void expandSize(QSizeF &result, const QSizeF &size)
+{
+ if (size.width() >= 0 && size.width() > result.width())
+ result.setWidth(size.width());
+ if (size.height() >= 0 && size.height() > result.height())
+ result.setHeight(size.height());
+}
+
+static inline void combineHints(qreal &current, qreal fallbackHint)
+{
+ if (current < 0)
+ current = fallbackHint;
+}
+
+static inline void combineSize(QSizeF &result, const QSizeF &fallbackSize)
+{
+ combineHints(result.rwidth(), fallbackSize.width());
+ combineHints(result.rheight(), fallbackSize.height());
+}
+
+static inline void combineImplicitHints(QQuickLayoutAttached *info, Qt::SizeHint which, QSizeF *size)
+{
+ if (!info) return;
+
+ Q_ASSERT(which == Qt::MinimumSize || which == Qt::MaximumSize);
+
+ const QSizeF constraint(which == Qt::MinimumSize
+ ? QSizeF(info->minimumWidth(), info->minimumHeight())
+ : QSizeF(info->maximumWidth(), info->maximumHeight()));
+
+ if (!info->isExtentExplicitlySet(Qt::Horizontal, which))
+ combineHints(size->rwidth(), constraint.width());
+ if (!info->isExtentExplicitlySet(Qt::Vertical, which))
+ combineHints(size->rheight(), constraint.height());
+}
+
+typedef qreal (QQuickLayoutAttached::*SizeGetter)() const;
+
+/*!
+ \internal
+ Note: Can potentially return the attached QQuickLayoutAttached object through \a attachedInfo.
+
+ It is like this is because it enables it to be reused.
+
+ The goal of this function is to return the effective minimum, preferred and maximum size hints
+ that the layout will use for this item.
+ This function takes care of gathering all explicitly set size hints, normalizes them so
+ that min < pref < max.
+ Further, the hints _not_explicitly_ set will then be initialized with the implicit size hints,
+ which is usually derived from the content of the layouts (or items).
+
+ The following table illustrates the preference of the properties used for measuring layout
+ items. If present, the USER properties will be preferred. If USER properties are not present,
+ the HINT properties will be preferred. Finally, the FALLBACK properties will be used as an
+ ultimate fallback.
+
+ Note that one can query if the value of Layout.minimumWidth or Layout.maximumWidth has been
+ explicitly or implicitly set with QQuickLayoutAttached::isExtentExplicitlySet(). This
+ determines if it should be used as a USER or as a HINT value.
+
+ Fractional size hints will be ceiled to the closest integer. This is in order to give some
+ slack when the items are snapped to the pixel grid.
+
+ | *Minimum* | *Preferred* | *Maximum* |
++----------------+----------------------+-----------------------+--------------------------+
+|USER (explicit) | Layout.minimumWidth | Layout.preferredWidth | Layout.maximumWidth |
+|HINT (implicit) | Layout.minimumWidth | implicitWidth | Layout.maximumWidth |
+|FALLBACK | 0 | width | Number.POSITIVE_INFINITY |
++----------------+----------------------+-----------------------+--------------------------+
+ */
+void QQuickLayout::effectiveSizeHints_helper(QQuickItem *item, QSizeF *cachedSizeHints, QQuickLayoutAttached **attachedInfo, bool useFallbackToWidthOrHeight)
+{
+ for (int i = 0; i < Qt::NSizeHints; ++i)
+ cachedSizeHints[i] = QSizeF();
+ QQuickLayoutAttached *info = attachedLayoutObject(item, false);
+ // First, retrieve the user-specified hints from the attached "Layout." properties
+ if (info) {
+ struct Getters {
+ SizeGetter call[NSizes];
+ };
+
+ static Getters horGetters = {
+ {&QQuickLayoutAttached::minimumWidth, &QQuickLayoutAttached::preferredWidth, &QQuickLayoutAttached::maximumWidth},
+ };
+
+ static Getters verGetters = {
+ {&QQuickLayoutAttached::minimumHeight, &QQuickLayoutAttached::preferredHeight, &QQuickLayoutAttached::maximumHeight}
+ };
+ for (int i = 0; i < NSizes; ++i) {
+ SizeGetter getter = horGetters.call[i];
+ Q_ASSERT(getter);
+
+ if (info->isExtentExplicitlySet(Qt::Horizontal, (Qt::SizeHint)i))
+ cachedSizeHints[i].setWidth((info->*getter)());
+
+ getter = verGetters.call[i];
+ Q_ASSERT(getter);
+ if (info->isExtentExplicitlySet(Qt::Vertical, (Qt::SizeHint)i))
+ cachedSizeHints[i].setHeight((info->*getter)());
+ }
+ }
+
+ QSizeF &minS = cachedSizeHints[Qt::MinimumSize];
+ QSizeF &prefS = cachedSizeHints[Qt::PreferredSize];
+ QSizeF &maxS = cachedSizeHints[Qt::MaximumSize];
+ QSizeF &descentS = cachedSizeHints[Qt::MinimumDescent];
+
+ // For instance, will normalize the following user-set hints
+ // from: [10, 5, 60]
+ // to: [10, 10, 60]
+ normalizeHints(minS.rwidth(), prefS.rwidth(), maxS.rwidth(), descentS.rwidth());
+ normalizeHints(minS.rheight(), prefS.rheight(), maxS.rheight(), descentS.rheight());
+
+ // All explicit values gathered, now continue to gather the implicit sizes
+
+ //--- GATHER MAXIMUM SIZE HINTS ---
+ combineImplicitHints(info, Qt::MaximumSize, &maxS);
+ combineSize(maxS, QSizeF(std::numeric_limits<qreal>::infinity(), std::numeric_limits<qreal>::infinity()));
+ // implicit max or min sizes should not limit an explicitly set preferred size
+ expandSize(maxS, prefS);
+ expandSize(maxS, minS);
+
+ //--- GATHER MINIMUM SIZE HINTS ---
+ combineImplicitHints(info, Qt::MinimumSize, &minS);
+ expandSize(minS, QSizeF(0,0));
+ boundSize(minS, prefS);
+ boundSize(minS, maxS);
+
+ //--- GATHER PREFERRED SIZE HINTS ---
+ // First, from implicitWidth/Height
+ qreal &prefWidth = prefS.rwidth();
+ qreal &prefHeight = prefS.rheight();
+ if (prefWidth < 0 && item->implicitWidth() > 0)
+ prefWidth = qCeil(item->implicitWidth());
+ if (prefHeight < 0 && item->implicitHeight() > 0)
+ prefHeight = qCeil(item->implicitHeight());
+
+ // If that fails, make an ultimate fallback to width/height
+
+ if (!info && (prefWidth < 0 || prefHeight < 0))
+ info = attachedLayoutObject(item);
+
+ if (useFallbackToWidthOrHeight && info) {
+ /* This block is a bit hacky, but if we want to support using width/height
+ as preferred size hints in layouts, (which we think most people expect),
+ we only want to use the initial width.
+ This is because the width will change due to layout rearrangement, and the preferred
+ width should return the same value, regardless of the current width.
+ We therefore store the width in the implicitWidth attached property.
+ Since the layout listens to changes of implicitWidth, (it will
+ basically cause an invalidation of the layout), we have to disable that
+ notification while we set the implicit width (and height).
+
+ Only use this fallback the first time the size hint is queried. Otherwise, we might
+ end up picking a width that is different than what was specified in the QML.
+ */
+ if (prefWidth < 0 || prefHeight < 0) {
+ item->blockSignals(true);
+ if (prefWidth < 0) {
+ prefWidth = item->width();
+ item->setImplicitWidth(prefWidth);
+ }
+ if (prefHeight < 0) {
+ prefHeight = item->height();
+ item->setImplicitHeight(prefHeight);
+ }
+ item->blockSignals(false);
+ }
+ }
+
+
+
+ // Normalize again after the implicit hints have been gathered
+ expandSize(prefS, minS);
+ boundSize(prefS, maxS);
+
+ //--- GATHER DESCENT
+ // Minimum descent is only applicable for the effective minimum height,
+ // so we gather the descent last.
+ const qreal minimumDescent = minS.height() - item->baselineOffset();
+ descentS.setHeight(minimumDescent);
+
+ if (info) {
+ QMarginsF margins = info->qMargins();
+ QSizeF extraMargins(margins.left() + margins.right(), margins.top() + margins.bottom());
+ minS += extraMargins;
+ prefS += extraMargins;
+ maxS += extraMargins;
+ descentS += extraMargins;
+ }
+ if (attachedInfo)
+ *attachedInfo = info;
+}
+
+/*!
+ \internal
+
+ Assumes \a info is set (if the object has an attached property)
+ */
+QLayoutPolicy::Policy QQuickLayout::effectiveSizePolicy_helper(QQuickItem *item, Qt::Orientation orientation, QQuickLayoutAttached *info)
+{
+ bool fillExtent = false;
+ bool isSet = false;
+ if (info) {
+ if (orientation == Qt::Horizontal) {
+ isSet = info->isFillWidthSet();
+ if (isSet) fillExtent = info->fillWidth();
+ } else {
+ isSet = info->isFillHeightSet();
+ if (isSet) fillExtent = info->fillHeight();
+ }
+ }
+ if (!isSet && qobject_cast<QQuickLayout*>(item))
+ fillExtent = true;
+ return fillExtent ? QLayoutPolicy::Preferred : QLayoutPolicy::Fixed;
+
+}
+
+
+
QT_END_NAMESPACE
diff --git a/src/layouts/qquicklayout_p.h b/src/layouts/qquicklayout_p.h
index 37f6ca00..d613f5bd 100644
--- a/src/layouts/qquicklayout_p.h
+++ b/src/layouts/qquicklayout_p.h
@@ -40,6 +40,7 @@
#include <QPointer>
#include <QQuickItem>
#include <private/qquickitem_p.h>
+#include <QtGui/private/qlayoutpolicy_p.h>
QT_BEGIN_NAMESPACE
@@ -81,6 +82,16 @@ public:
virtual void rearrange(const QSizeF &);
bool arrangementIsDirty() const { return m_dirty; }
+
+ static void effectiveSizeHints_helper(QQuickItem *item, QSizeF *cachedSizeHints, QQuickLayoutAttached **info, bool useFallbackToWidthOrHeight);
+ static QLayoutPolicy::Policy effectiveSizePolicy_helper(QQuickItem *item, Qt::Orientation orientation, QQuickLayoutAttached *info);
+ bool shouldIgnoreItem(QQuickItem *child, QQuickLayoutAttached *&info, QSizeF *sizeHints) const;
+
+ void itemChange(ItemChange change, const ItemChangeData &value) Q_DECL_OVERRIDE;
+ void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE;
+ bool isReady() const;
+
+
protected:
void updatePolish() Q_DECL_OVERRIDE;
@@ -90,6 +101,9 @@ protected:
NOrientations
};
+protected slots:
+ void invalidateSenderItem();
+
private:
bool m_dirty;
@@ -102,6 +116,13 @@ private:
class QQuickLayoutPrivate : public QQuickItemPrivate
{
Q_DECLARE_PUBLIC(QQuickLayout)
+public:
+ QQuickLayoutPrivate() : m_isReady(false), m_disableRearrange(true) {}
+
+protected:
+ unsigned m_isReady : 1;
+ unsigned m_disableRearrange : 1;
+ mutable QSet<QQuickItem *> m_ignoredItems;
};
diff --git a/src/layouts/qquicklinearlayout.cpp b/src/layouts/qquicklinearlayout.cpp
index 0b9c6f54..cbd32976 100644
--- a/src/layouts/qquicklinearlayout.cpp
+++ b/src/layouts/qquicklinearlayout.cpp
@@ -323,7 +323,6 @@ void QQuickGridLayoutBase::setAlignment(QQuickItem *item, Qt::Alignment alignmen
QQuickGridLayoutBase::~QQuickGridLayoutBase()
{
Q_D(QQuickGridLayoutBase);
- d->m_isReady = false;
/* Avoid messy deconstruction, should give:
* Faster deconstruction
@@ -341,12 +340,8 @@ QQuickGridLayoutBase::~QQuickGridLayoutBase()
void QQuickGridLayoutBase::componentComplete()
{
- Q_D(QQuickGridLayoutBase);
quickLayoutDebug() << objectName() << "QQuickGridLayoutBase::componentComplete()" << parent();
- d->m_disableRearrange = true;
- QQuickLayout::componentComplete(); // will call our geometryChange(), (where isComponentComplete() == true)
- d->m_isReady = true;
- d->m_disableRearrange = false;
+ QQuickLayout::componentComplete();
updateLayoutItems();
QQuickItem *par = parentItem();
@@ -469,10 +464,6 @@ void QQuickGridLayoutBase::itemChange(ItemChange change, const ItemChangeData &v
QQuickItem *item = value.item;
QObject::connect(item, SIGNAL(destroyed()), this, SLOT(onItemDestroyed()));
QObject::connect(item, SIGNAL(visibleChanged()), this, SLOT(onItemVisibleChanged()));
- QObject::connect(item, SIGNAL(implicitWidthChanged()), this, SLOT(invalidateSenderItem()));
- QObject::connect(item, SIGNAL(implicitHeightChanged()), this, SLOT(invalidateSenderItem()));
- QObject::connect(item, SIGNAL(baselineOffsetChanged(qreal)), this, SLOT(invalidateSenderItem()));
-
if (isReady())
updateLayoutItems();
} else if (change == ItemChildRemovedChange) {
@@ -480,9 +471,6 @@ void QQuickGridLayoutBase::itemChange(ItemChange change, const ItemChangeData &v
QQuickItem *item = value.item;
QObject::disconnect(item, SIGNAL(destroyed()), this, SLOT(onItemDestroyed()));
QObject::disconnect(item, SIGNAL(visibleChanged()), this, SLOT(onItemVisibleChanged()));
- QObject::disconnect(item, SIGNAL(implicitWidthChanged()), this, SLOT(invalidateSenderItem()));
- QObject::disconnect(item, SIGNAL(implicitHeightChanged()), this, SLOT(invalidateSenderItem()));
- QObject::disconnect(item, SIGNAL(baselineOffsetChanged(qreal)), this, SLOT(invalidateSenderItem()));
if (isReady())
updateLayoutItems();
}
@@ -490,16 +478,6 @@ void QQuickGridLayoutBase::itemChange(ItemChange change, const ItemChangeData &v
QQuickLayout::itemChange(change, value);
}
-void QQuickGridLayoutBase::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
-{
- Q_D(QQuickGridLayoutBase);
- QQuickLayout::geometryChanged(newGeometry, oldGeometry);
- if (d->m_disableRearrange || !isReady() || !newGeometry.isValid())
- return;
- quickLayoutDebug() << "QQuickGridLayoutBase::geometryChanged" << newGeometry << oldGeometry;
- rearrange(newGeometry.size());
-}
-
void QQuickGridLayoutBase::removeGridItem(QGridLayoutItem *gridItem)
{
Q_D(QQuickGridLayoutBase);
@@ -508,11 +486,6 @@ void QQuickGridLayoutBase::removeGridItem(QGridLayoutItem *gridItem)
d->engine.removeRows(index, 1, d->orientation);
}
-bool QQuickGridLayoutBase::isReady() const
-{
- return d_func()->m_isReady;
-}
-
void QQuickGridLayoutBase::onItemVisibleChanged()
{
if (!isReady())
@@ -535,15 +508,6 @@ void QQuickGridLayoutBase::onItemDestroyed()
}
}
-void QQuickGridLayoutBase::invalidateSenderItem()
-{
- if (!isReady())
- return;
- QQuickItem *item = static_cast<QQuickItem *>(sender());
- Q_ASSERT(item);
- invalidate(item);
-}
-
void QQuickGridLayoutBase::rearrange(const QSizeF &size)
{
Q_D(QQuickGridLayoutBase);
@@ -578,29 +542,6 @@ void QQuickGridLayoutBase::rearrange(const QSizeF &size)
}
}
-bool QQuickGridLayoutBase::shouldIgnoreItem(QQuickItem *child, QQuickLayoutAttached *&info, QSizeF *sizeHints)
-{
- Q_D(QQuickGridLayoutBase);
- bool ignoreItem = true;
- QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(child);
- if (childPrivate->explicitVisible) {
- QQuickGridLayoutItem::effectiveSizeHints_helper(child, sizeHints, &info, true);
- QSizeF effectiveMaxSize = sizeHints[Qt::MaximumSize];
- if (!effectiveMaxSize.isNull()) {
- QSizeF &prefS = sizeHints[Qt::PreferredSize];
- if (QQuickGridLayoutItem::effectiveSizePolicy_helper(child, Qt::Horizontal, info) == QLayoutPolicy::Fixed)
- effectiveMaxSize.setWidth(prefS.width());
- if (QQuickGridLayoutItem::effectiveSizePolicy_helper(child, Qt::Vertical, info) == QLayoutPolicy::Fixed)
- effectiveMaxSize.setHeight(prefS.height());
- }
- ignoreItem = effectiveMaxSize.isNull();
- }
-
- if (ignoreItem)
- d->m_ignoredItems << child;
- return ignoreItem;
-}
-
/**********************************
**
** QQuickGridLayout
diff --git a/src/layouts/qquicklinearlayout_p.h b/src/layouts/qquicklinearlayout_p.h
index b6483c4e..e3522bce 100644
--- a/src/layouts/qquicklinearlayout_p.h
+++ b/src/layouts/qquicklinearlayout_p.h
@@ -39,7 +39,6 @@
#include "qquicklayout_p.h"
#include "qquickgridlayoutengine_p.h"
-#include <QtCore/qset.h>
QT_BEGIN_NAMESPACE
@@ -83,8 +82,6 @@ protected:
void rearrange(const QSizeF &size) Q_DECL_OVERRIDE;
virtual void insertLayoutItems() {}
void itemChange(ItemChange change, const ItemChangeData &data) Q_DECL_OVERRIDE;
- void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE;
- bool shouldIgnoreItem(QQuickItem *child, QQuickLayoutAttached *&info, QSizeF *sizeHints);
signals:
Q_REVISION(1) void layoutDirectionChanged();
@@ -92,11 +89,9 @@ signals:
protected slots:
void onItemVisibleChanged();
void onItemDestroyed();
- void invalidateSenderItem();
private:
void removeGridItem(QGridLayoutItem *gridItem);
- bool isReady() const;
Q_DECLARE_PRIVATE(QQuickGridLayoutBase)
};
@@ -107,9 +102,7 @@ class QQuickGridLayoutBasePrivate : public QQuickLayoutPrivate
Q_DECLARE_PUBLIC(QQuickGridLayoutBase)
public:
- QQuickGridLayoutBasePrivate() : m_disableRearrange(true)
- , m_isReady(false)
- , m_rearranging(false)
+ QQuickGridLayoutBasePrivate() : m_rearranging(false)
, m_updateAfterRearrange(false)
, m_layoutDirection(Qt::LeftToRight)
{}
@@ -122,14 +115,11 @@ public:
QQuickGridLayoutEngine engine;
Qt::Orientation orientation;
- unsigned m_disableRearrange : 1;
- unsigned m_isReady : 1;
unsigned m_rearranging : 1;
unsigned m_updateAfterRearrange : 1;
QVector<QQuickItem *> m_invalidateAfterRearrange;
Qt::LayoutDirection m_layoutDirection : 2;
- QSet<QQuickItem *> m_ignoredItems;
QQuickLayoutStyleInfo *styleInfo;
};
diff --git a/src/layouts/qquickstacklayout.cpp b/src/layouts/qquickstacklayout.cpp
new file mode 100644
index 00000000..3ada3b1e
--- /dev/null
+++ b/src/layouts/qquickstacklayout.cpp
@@ -0,0 +1,333 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Layouts module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickstacklayout_p.h"
+#include <limits>
+
+/*!
+ \qmltype StackLayout
+ \instantiates QQuickStackLayout
+ \inherits Item
+ \inqmlmodule QtQuick.Layouts
+ \ingroup layouts
+ \brief The StackLayout class provides a stack of items where
+ only one item is visible at a time.
+
+ The current visible item can be modified by setting the \l currentIndex property.
+ The index corresponds to the order of the StackLayout's children.
+
+ In contrast to most other layouts, child Items' \l{Layout::fillWidth}{Layout.fillWidth} and \l{Layout::fillHeight}{Layout.fillHeight} properties
+ defaults to \c true. As a consequence, child items are by default filled to match the size of the StackLayout as long as their
+ \l{Layout::maximumWidth}{Layout.maximumWidth} or \l{Layout::maximumHeight}{Layout.maximumHeight} does not prevent it.
+
+ Items are added to the layout by reparenting the item to the layout. Similarly, removal is done by reparenting the item from the layout.
+ Both of these operations will affect the layouts \l count property.
+
+ The following code will create a StackLayout where only the 'plum' rectangle is visible.
+ \code
+ StackLayout {
+ id: layout
+ anchors.fill: parent
+ currentIndex: 1
+ Rectangle {
+ color: 'teal'
+ implicitWidth: 200
+ implicitHeight: 200
+ }
+ Rectangle {
+ color: 'plum'
+ implicitWidth: 300
+ implicitHeight: 200
+ }
+ }
+ \endcode
+
+ Items in a StackLayout support these attached properties:
+ \list
+ \li \l{Layout::minimumWidth}{Layout.minimumWidth}
+ \li \l{Layout::minimumHeight}{Layout.minimumHeight}
+ \li \l{Layout::preferredWidth}{Layout.preferredWidth}
+ \li \l{Layout::preferredHeight}{Layout.preferredHeight}
+ \li \l{Layout::maximumWidth}{Layout.maximumWidth}
+ \li \l{Layout::maximumHeight}{Layout.maximumHeight}
+ \li \l{Layout::fillWidth}{Layout.fillWidth}
+ \li \l{Layout::fillHeight}{Layout.fillHeight}
+ \endlist
+
+ Read more about attached properties \l{QML Object Attributes}{here}.
+ \sa ColumnLayout
+ \sa GridLayout
+ \sa RowLayout
+ \sa StackView
+*/
+
+QQuickStackLayout::QQuickStackLayout(QQuickItem *parent) :
+ QQuickLayout(*new QQuickStackLayoutPrivate, parent)
+{
+}
+
+/*!
+ \qmlproperty int StackLayout::count
+
+ This property holds the number of items that belongs to the layout.
+
+ Only items that are children of the StackLayout will be candidates for layouting.
+*/
+int QQuickStackLayout::count() const
+{
+ Q_D(const QQuickStackLayout);
+ return d->count;
+}
+
+/*!
+ \qmlproperty int StackLayout::currentIndex
+
+ This property holds the current index of which of the StackLayout's items that should be visible.
+ By default it will be -1 for an empty layout, otherwise the default is 0 (referring to the first item).
+*/
+int QQuickStackLayout::currentIndex() const
+{
+ Q_D(const QQuickStackLayout);
+ return d->currentIndex;
+}
+
+void QQuickStackLayout::setCurrentIndex(int index)
+{
+ Q_D(QQuickStackLayout);
+ if (index != d->currentIndex) {
+ QQuickItem *prev = itemAt(d->currentIndex);
+ QQuickItem *next = itemAt(index);
+ d->currentIndex = index;
+ d->explicitCurrentIndex = true;
+ if (prev)
+ prev->setVisible(false);
+ if (next)
+ next->setVisible(true);
+
+ if (isComponentComplete()) {
+ rearrange(QSizeF(width(), height()));
+ emit currentIndexChanged();
+ }
+ }
+}
+
+void QQuickStackLayout::componentComplete()
+{
+ QQuickLayout::componentComplete(); // will call our geometryChange(), (where isComponentComplete() == true)
+
+ updateLayoutItems();
+
+ QQuickItem *par = parentItem();
+ if (qobject_cast<QQuickLayout*>(par))
+ return;
+
+ rearrange(QSizeF(width(), height()));
+}
+
+QSizeF QQuickStackLayout::sizeHint(Qt::SizeHint whichSizeHint) const
+{
+ QSizeF &askingFor = m_cachedSizeHints[whichSizeHint];
+ if (!askingFor.isValid()) {
+ QSizeF &minS = m_cachedSizeHints[Qt::MinimumSize];
+ QSizeF &prefS = m_cachedSizeHints[Qt::PreferredSize];
+ QSizeF &maxS = m_cachedSizeHints[Qt::MaximumSize];
+
+ minS = QSizeF(0,0);
+ prefS = QSizeF(0,0);
+ maxS = QSizeF(std::numeric_limits<qreal>::infinity(), std::numeric_limits<qreal>::infinity());
+
+ const int count = itemCount();
+ m_cachedItemSizeHints.resize(count);
+ for (int i = 0; i < count; ++i) {
+ SizeHints &hints = m_cachedItemSizeHints[i];
+ QQuickStackLayout::collectItemSizeHints(itemAt(i), hints.array);
+ minS = minS.expandedTo(hints.min());
+ prefS = prefS.expandedTo(hints.pref());
+ //maxS = maxS.boundedTo(hints.max()); // Can be resized to be larger than any of its items.
+ // This is the same as QStackLayout does it.
+ // Not sure how descent makes sense here...
+ }
+ }
+ return askingFor;
+}
+
+int QQuickStackLayout::indexOf(QQuickItem *childItem) const
+{
+ if (childItem) {
+ int indexOfItem = 0;
+ foreach (QQuickItem *item, childItems()) {
+ if (shouldIgnoreItem(item))
+ continue;
+ if (childItem == item)
+ return indexOfItem;
+ ++indexOfItem;
+ }
+ }
+ return -1;
+}
+
+QQuickItem *QQuickStackLayout::itemAt(int index) const
+{
+ foreach (QQuickItem *item, childItems()) {
+ if (shouldIgnoreItem(item))
+ continue;
+ if (index == 0)
+ return item;
+ --index;
+ }
+ return 0;
+}
+
+int QQuickStackLayout::itemCount() const
+{
+ int count = 0;
+ foreach (QQuickItem *item, childItems()) {
+ if (shouldIgnoreItem(item))
+ continue;
+ ++count;
+ }
+ return count;
+}
+
+void QQuickStackLayout::setAlignment(QQuickItem * /*item*/, Qt::Alignment /*align*/)
+{
+ // ### Do we have to respect alignment?
+}
+
+void QQuickStackLayout::invalidate(QQuickItem *childItem)
+{
+ Q_D(QQuickStackLayout);
+ if (d->m_ignoredItems.contains(childItem)) {
+ // If an invalid item gets a valid size, it should be included, as it was added to the layout
+ updateLayoutItems();
+ return;
+ }
+
+ const int indexOfChild = indexOf(childItem);
+ if (indexOfChild >= 0 && indexOfChild < m_cachedItemSizeHints.count()) {
+ m_cachedItemSizeHints[indexOfChild].min() = QSizeF();
+ m_cachedItemSizeHints[indexOfChild].pref() = QSizeF();
+ m_cachedItemSizeHints[indexOfChild].max() = QSizeF();
+ }
+
+ for (int i = 0; i < Qt::NSizeHints; ++i)
+ m_cachedSizeHints[i] = QSizeF();
+ QQuickLayout::invalidate(this);
+
+ QQuickLayoutAttached *info = attachedLayoutObject(this);
+
+ const QSizeF min = sizeHint(Qt::MinimumSize);
+ const QSizeF pref = sizeHint(Qt::PreferredSize);
+ const QSizeF max = sizeHint(Qt::MaximumSize);
+
+ const bool old = info->setChangesNotificationEnabled(false);
+ info->setMinimumImplicitSize(min);
+ info->setMaximumImplicitSize(max);
+ info->setChangesNotificationEnabled(old);
+ if (pref.width() == implicitWidth() && pref.height() == implicitHeight()) {
+ // In case setImplicitSize does not emit implicit{Width|Height}Changed
+ if (QQuickLayout *parentLayout = qobject_cast<QQuickLayout *>(parentItem()))
+ parentLayout->invalidate(this);
+ } else {
+ setImplicitSize(pref.width(), pref.height());
+ }
+}
+
+void QQuickStackLayout::updateLayoutItems()
+{
+ Q_D(QQuickStackLayout);
+ d->m_ignoredItems.clear();
+ const int count = itemCount();
+ int oldIndex = d->currentIndex;
+ if (!d->explicitCurrentIndex)
+ d->currentIndex = (count > 0 ? 0 : -1);
+
+ if (d->currentIndex != oldIndex)
+ emit currentIndexChanged();
+
+ if (count != d->count) {
+ d->count = count;
+ emit countChanged();
+ }
+ for (int i = 0; i < count; ++i)
+ itemAt(i)->setVisible(d->currentIndex == i);
+
+ invalidate();
+}
+
+void QQuickStackLayout::rearrange(const QSizeF &newSize)
+{
+ Q_D(QQuickStackLayout);
+ if (newSize.isNull() || !newSize.isValid())
+ return;
+ (void)sizeHint(Qt::PreferredSize); // Make sure m_cachedItemSizeHints are valid
+
+ if (d->currentIndex == -1 || d->currentIndex >= m_cachedItemSizeHints.count())
+ return;
+ QQuickStackLayout::SizeHints &hints = m_cachedItemSizeHints[d->currentIndex];
+ QQuickItem *item = itemAt(d->currentIndex);
+ Q_ASSERT(item);
+ item->setPosition(QPointF(0,0)); // ### respect alignment?
+ item->setSize(newSize.expandedTo(hints.min()).boundedTo(hints.max()));
+ QQuickLayout::rearrange(newSize);
+}
+
+void QQuickStackLayout::collectItemSizeHints(QQuickItem *item, QSizeF *sizeHints)
+{
+ QQuickLayoutAttached *info = 0;
+ QQuickLayout::effectiveSizeHints_helper(item, sizeHints, &info, true);
+ if (!info)
+ return;
+ if (info->isFillWidthSet() && !info->fillWidth()) {
+ const qreal pref = sizeHints[Qt::PreferredSize].width();
+ sizeHints[Qt::MinimumSize].setWidth(pref);
+ sizeHints[Qt::MaximumSize].setWidth(pref);
+ }
+
+ if (info->isFillHeightSet() && !info->fillHeight()) {
+ const qreal pref = sizeHints[Qt::PreferredSize].height();
+ sizeHints[Qt::MinimumSize].setHeight(pref);
+ sizeHints[Qt::MaximumSize].setHeight(pref);
+ }
+}
+
+bool QQuickStackLayout::shouldIgnoreItem(QQuickItem *item) const
+{
+ const bool ignored = QQuickItemPrivate::get(item)->isTransparentForPositioner();
+ if (ignored)
+ d_func()->m_ignoredItems << item;
+ return ignored;
+}
diff --git a/src/layouts/qquickstacklayout_p.h b/src/layouts/qquickstacklayout_p.h
new file mode 100644
index 00000000..92121400
--- /dev/null
+++ b/src/layouts/qquickstacklayout_p.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the Qt Quick Layouts module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKSTACKLAYOUT_H
+#define QQUICKSTACKLAYOUT_H
+
+#include <qquicklayout_p.h>
+
+class QQuickStackLayoutPrivate;
+
+class QQuickStackLayout : public QQuickLayout
+{
+ Q_OBJECT
+ Q_PROPERTY(int count READ count NOTIFY countChanged)
+ Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
+
+public:
+ explicit QQuickStackLayout(QQuickItem *parent = 0);
+ int count() const;
+ int currentIndex() const;
+ void setCurrentIndex(int index);
+
+ void componentComplete() Q_DECL_OVERRIDE;
+ QSizeF sizeHint(Qt::SizeHint whichSizeHint) const Q_DECL_OVERRIDE;
+ void setAlignment(QQuickItem *item, Qt::Alignment align) Q_DECL_OVERRIDE;
+ void invalidate(QQuickItem *childItem = 0) Q_DECL_OVERRIDE;
+ void updateLayoutItems() Q_DECL_OVERRIDE;
+ void rearrange(const QSizeF &) Q_DECL_OVERRIDE;
+
+ // iterator
+ Q_INVOKABLE QQuickItem *itemAt(int index) const Q_DECL_OVERRIDE;
+ int itemCount() const Q_DECL_OVERRIDE;
+ int indexOf(QQuickItem *item) const;
+
+
+
+signals:
+ void currentIndexChanged();
+ void countChanged();
+
+public slots:
+
+private:
+ static void collectItemSizeHints(QQuickItem *item, QSizeF *sizeHints);
+ bool shouldIgnoreItem(QQuickItem *item) const;
+ Q_DECLARE_PRIVATE(QQuickStackLayout)
+
+ QList<QQuickItem*> m_items;
+
+ typedef struct {
+ inline QSizeF &min() { return array[Qt::MinimumSize]; }
+ inline QSizeF &pref() { return array[Qt::PreferredSize]; }
+ inline QSizeF &max() { return array[Qt::MaximumSize]; }
+ QSizeF array[Qt::NSizeHints];
+ } SizeHints;
+
+ mutable QVector<SizeHints> m_cachedItemSizeHints;
+ mutable QSizeF m_cachedSizeHints[Qt::NSizeHints];
+};
+
+class QQuickStackLayoutPrivate : public QQuickLayoutPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickStackLayout)
+public:
+ QQuickStackLayoutPrivate() : count(0), currentIndex(-1), explicitCurrentIndex(false) {}
+private:
+ int count;
+ int currentIndex;
+ bool explicitCurrentIndex;
+};
+
+#endif // QQUICKSTACKLAYOUT_H
diff --git a/src/widgets/qquickqfiledialog.cpp b/src/widgets/qquickqfiledialog.cpp
index 90d1ef9e..7ebebf1f 100644
--- a/src/widgets/qquickqfiledialog.cpp
+++ b/src/widgets/qquickqfiledialog.cpp
@@ -209,6 +209,7 @@ void QFileDialogHelper::fileSelected(const QString& path)
void QFileDialogHelper::filesSelected(const QStringList& paths)
{
QList<QUrl> pathUrls;
+ pathUrls.reserve(paths.count());
foreach (const QString &path, paths)
pathUrls << QUrl::fromLocalFile(path);
emit QPlatformFileDialogHelper::filesSelected(pathUrls);
diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro
index 14ac7714..6b55a179 100644
--- a/tests/auto/auto.pro
+++ b/tests/auto/auto.pro
@@ -1,5 +1,5 @@
TEMPLATE = subdirs
SUBDIRS += testplugin controls activeFocusOnTab applicationwindow dialogs \
- extras paint qquicktreemodeladaptor customcontrolsstyle
+ extras qquicktreemodeladaptor customcontrolsstyle
!osx: SUBDIRS += menubar
controls.depends = testplugin
diff --git a/tests/auto/controls/data/textarea/ta_editingfinished.qml b/tests/auto/controls/data/textarea/ta_editingfinished.qml
new file mode 100644
index 00000000..5d4cba2d
--- /dev/null
+++ b/tests/auto/controls/data/textarea/ta_editingfinished.qml
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.6
+import QtQuick.Controls 1.5
+
+Row {
+ width: 100
+ height: 50
+ spacing: 10
+
+ property alias control1: _control1
+ property alias control2: _control2
+ TextArea {
+ id: _control1
+ text: 'A'
+ property bool myeditingfinished: false
+ onEditingFinished: myeditingfinished = true
+ }
+ TextArea {
+ id: _control2
+ text: 'B'
+ property bool myeditingfinished: false
+ onEditingFinished: myeditingfinished = true
+ }
+}
diff --git a/tests/auto/controls/data/tst_gridlayout.qml b/tests/auto/controls/data/tst_gridlayout.qml
index 2c931a7b..b6bbd7d9 100644
--- a/tests/auto/controls/data/tst_gridlayout.qml
+++ b/tests/auto/controls/data/tst_gridlayout.qml
@@ -908,9 +908,9 @@ Item {
for (var i = 0; i < 2; ++i)
compare(visualGeom[i] % 1, 0)
- // verify that x,y is is inside idealGeom
- verify(visualGeom[0] >= idealGeom[0])
- verify(visualGeom[1] >= idealGeom[1])
+ // verify that x,y is no more than one pixel from idealGeom
+ fuzzyCompare(visualGeom[0], idealGeom[0], 1)
+ fuzzyCompare(visualGeom[1], idealGeom[1], 1)
// verify that the visual size is no more than 1 pixel taller/wider than the ideal size.
verify(visualGeom[2] <= idealGeom[2] + 1)
diff --git a/tests/auto/controls/data/tst_rowlayout.qml b/tests/auto/controls/data/tst_rowlayout.qml
index 286200d1..a0a6b739 100644
--- a/tests/auto/controls/data/tst_rowlayout.qml
+++ b/tests/auto/controls/data/tst_rowlayout.qml
@@ -675,6 +675,94 @@ Item {
layout.destroy();
}
+ Component {
+ id: test_distributeToPixelGrid_Component
+ RowLayout {
+ spacing: 0
+ Rectangle {
+ color: 'red'
+ Layout.minimumWidth: 10
+ Layout.preferredWidth: 50
+ Layout.maximumWidth: 90
+ Layout.fillWidth: true
+ implicitHeight: 10
+ }
+ Rectangle {
+ color: 'red'
+ Layout.minimumWidth: 10
+ Layout.preferredWidth: 20
+ Layout.maximumWidth: 90
+ Layout.fillWidth: true
+ implicitHeight: 10
+ }
+ Rectangle {
+ color: 'red'
+ Layout.minimumWidth: 10
+ Layout.preferredWidth: 70
+ Layout.maximumWidth: 90
+ Layout.fillWidth: true
+ implicitHeight: 10
+ }
+ }
+ }
+
+ function test_distributeToPixelGrid_data() {
+ return [
+ { tag: "narrow", spacing: 0, width: 60 },
+ { tag: "belowPreferred", spacing: 0, width: 130 },
+ { tag: "belowPreferredWithSpacing", spacing: 10, width: 130 },
+ { tag: "abovePreferred", spacing: 0, width: 150 },
+ { tag: "stretchSomethingToMaximum", spacing: 0, width: 240,
+ expected: [90, 60, 90] },
+ { tag: "minSizeHasFractions", spacing: 2, width: 31 + 4, hints: [{min: 10+1/3}, {min: 10+1/3}, {min: 10+1/3}],
+ /*expected: [11, 11, 11]*/ }, /* verify that nothing gets allocated a size smaller than its minimum */
+ { tag: "maxSizeHasFractions", spacing: 2, width: 271 + 4, hints: [{max: 90+1/3}, {max: 90+1/3}, {max: 90+1/3}],
+ /*expected: [90, 90, 90]*/ }, /* verify that nothing gets allocated a size larger than its maximum */
+ { tag: "fixedSizeHasFractions", spacing: 2, width: 31 + 4, hints: [{min: 10+1/3, max: 10+1/3}, {min: 10+1/3, max: 10+1/3}, {min: 10+1/3, max: 10+1/3}],
+ /*expected: [11, 11, 11]*/ }, /* verify that nothing gets allocated a size smaller than its minimum */
+ ];
+ }
+
+ function test_distributeToPixelGrid(data)
+ {
+ // CONFIGURATION
+ var layout = test_distributeToPixelGrid_Component.createObject(container)
+ layout.spacing = data.spacing
+ layout.width = data.width
+ layout.height = 10
+ var kids = layout.children
+
+ if (data.hasOwnProperty('hints')) {
+ var hints = data.hints
+ for (var i = 0; i < hints.length; ++i) {
+ var h = hints[i]
+ if (h.hasOwnProperty('min'))
+ kids[i].Layout.minimumWidth = h.min
+ if (h.hasOwnProperty('pref'))
+ kids[i].Layout.preferredWidth = h.pref
+ if (h.hasOwnProperty('max'))
+ kids[i].Layout.maximumWidth = h.max
+ }
+ }
+ waitForRendering(layout)
+
+ var sum = 2 * layout.spacing
+ // TEST
+ for (var i = 0; i < kids.length; ++i) {
+ compare(kids[i].x % 1, 0) // checks if position is a whole integer
+ // verify if the items are within the size constraints as specified
+ verify(kids[i].width >= kids[i].Layout.minimumWidth)
+ verify(kids[i].width <= kids[i].Layout.maximumWidth)
+ if (data.hasOwnProperty('expected'))
+ compare(kids[i].width, data.expected[i])
+ sum += kids[i].width
+ }
+ fuzzyCompare(sum, layout.width, 1)
+
+ layout.destroy();
+ }
+
+
Component {
id: layout_deleteLayout
diff --git a/tests/auto/controls/data/tst_stacklayout.qml b/tests/auto/controls/data/tst_stacklayout.qml
new file mode 100644
index 00000000..ba468a24
--- /dev/null
+++ b/tests/auto/controls/data/tst_stacklayout.qml
@@ -0,0 +1,448 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.2
+import QtTest 1.0
+import QtQuick.Layouts 1.3
+
+Item {
+ id: container
+ width: 200
+ height: 200
+ TestCase {
+ id: testCase
+ name: "Tests_StackLayout"
+ when: windowShown
+ width: 200
+ height: 200
+
+ function geometry(item) {
+ return [item.x, item.y, item.width, item.height]
+ }
+
+ Component {
+ id: countGeometryChanges_Component
+ StackLayout {
+ id: stack
+ property alias col: _col
+ property alias row: _row
+ width: 100
+ ColumnLayout {
+ id: _col
+ property alias r1: _r1
+ property alias r2: _r2
+ property alias r3: _r3
+ spacing: 0
+ property int counter : 0
+ onWidthChanged: { ++counter; }
+ Rectangle {
+ id: _r1
+ implicitWidth: 20
+ implicitHeight: 20
+ Layout.fillWidth: true
+ property int counter : 0
+ onWidthChanged: { ++counter; }
+ }
+ Rectangle {
+ id: _r2
+ implicitWidth: 50
+ implicitHeight: 50
+ Layout.fillWidth: true
+ property int counter : 0
+ onWidthChanged: { ++counter; }
+ }
+ Rectangle {
+ id: _r3
+ implicitWidth: 40
+ implicitHeight: 40
+ Layout.fillWidth: true
+ property int counter : 0
+ onWidthChanged: { ++counter; }
+ }
+ }
+ RowLayout {
+ id: _row
+ property alias r5: _r5
+ spacing: 0
+ property int counter : 0
+ onWidthChanged: { ++counter; }
+ Rectangle {
+ id: _r5
+ implicitWidth: 100
+ implicitHeight: 100
+ Layout.fillWidth: true
+ property int counter : 0
+ onWidthChanged: { ++counter; }
+ }
+ }
+ }
+ }
+
+ function test_countGeometryChanges() {
+
+ var stack = countGeometryChanges_Component.createObject(container)
+ compare(stack.currentIndex, 0)
+ compare(stack.col.width, 100)
+ compare(stack.col.height, 110)
+ compare(stack.row.width, 100)
+ compare(stack.row.height, 100)
+ verify(stack.col.r1.counter <= 2)
+ compare(stack.col.r2.counter, 1)
+ verify(stack.col.r3.counter <= 2)
+ verify(stack.col.counter <= 2)
+ compare(stack.row.counter, 1) // not visible, will only receive the initial geometry change
+ compare(stack.row.r5.counter, 0)
+ stack.destroy()
+ }
+
+ Component {
+ id: layoutItem_Component
+ Rectangle {
+ implicitWidth: 20
+ implicitHeight: 20
+ }
+ }
+
+ Component {
+ id: emtpy_StackLayout_Component
+ StackLayout {
+ property int num_onCountChanged: 0
+ property int num_onCurrentIndexChanged: 0
+ onCountChanged: { ++num_onCountChanged; }
+ onCurrentIndexChanged: { ++num_onCurrentIndexChanged; }
+ }
+ }
+
+ function test_addAndRemoveItems()
+ {
+ var stack = emtpy_StackLayout_Component.createObject(container)
+ stack.currentIndex = 2
+ compare(stack.implicitWidth, 0)
+ compare(stack.implicitHeight, 0)
+
+ var rect0 = layoutItem_Component.createObject(stack)
+ compare(stack.implicitWidth, 20)
+ compare(stack.implicitHeight, 20)
+ compare(rect0.visible, false)
+
+ var rect1 = layoutItem_Component.createObject(stack)
+ rect1.Layout.preferredWidth = 30
+ rect1.Layout.preferredHeight = 10
+ compare(stack.implicitWidth, 30)
+ compare(stack.implicitHeight, 20)
+ compare(rect0.visible, false)
+ compare(rect1.visible, false)
+
+ var rect2 = layoutItem_Component.createObject(stack)
+ rect2.x = 42 // ### items in a stacklayout will have their x and y positions discarded.
+ rect2.y = 42
+ rect2.Layout.preferredWidth = 80
+ rect2.Layout.preferredHeight = 30
+ rect2.Layout.fillWidth = true
+ compare(stack.implicitWidth, 80)
+ compare(stack.implicitHeight, 30)
+ compare(rect0.visible, false)
+ compare(rect1.visible, false)
+ compare(rect2.visible, true)
+ compare(geometry(rect2), geometry(stack))
+
+ rect2.destroy()
+ wait(0) // this will hopefully effectuate the destruction of the object
+ compare(stack.implicitWidth, 30)
+ compare(stack.implicitHeight, 20)
+
+ rect0.destroy()
+ wait(0)
+ compare(stack.implicitWidth, 30)
+ compare(stack.implicitHeight, 10)
+
+ rect1.destroy()
+ wait(0)
+ compare(stack.implicitWidth, 0)
+ compare(stack.implicitHeight, 0)
+
+ stack.destroy()
+ }
+
+ function test_sizeHint_data() {
+ return [
+ { tag: "propagateNone", layoutHints: [10, 20, 30], childHints: [11, 21, 31], expected:[10, 20, Number.POSITIVE_INFINITY]},
+ { tag: "propagateMinimumWidth", layoutHints: [-1, 20, 30], childHints: [10, 21, 31], expected:[10, 20, Number.POSITIVE_INFINITY]},
+ { tag: "propagatePreferredWidth", layoutHints: [10, -1, 30], childHints: [11, 20, 31], expected:[10, 20, Number.POSITIVE_INFINITY]},
+ { tag: "propagateMaximumWidth", layoutHints: [10, 20, -1], childHints: [11, 21, 30], expected:[10, 20, Number.POSITIVE_INFINITY]},
+ { tag: "propagateAll", layoutHints: [-1, -1, -1], childHints: [10, 20, 30], expected:[10, 20, Number.POSITIVE_INFINITY]},
+ { tag: "propagateCrazy", layoutHints: [-1, -1, -1], childHints: [40, 21, 30], expected:[30, 30, Number.POSITIVE_INFINITY]},
+ { tag: "expandMinToExplicitPref", layoutHints: [-1, 1, -1], childHints: [11, 21, 31], expected:[ 1, 1, Number.POSITIVE_INFINITY]},
+ { tag: "expandMaxToExplicitPref", layoutHints: [-1, 99, -1], childHints: [11, 21, 31], expected:[11, 99, Number.POSITIVE_INFINITY]},
+ { tag: "expandAllToExplicitMin", layoutHints: [99, -1, -1], childHints: [11, 21, 31], expected:[99, 99, Number.POSITIVE_INFINITY]},
+ { tag: "expandPrefToExplicitMin", layoutHints: [24, -1, -1], childHints: [11, 21, 31], expected:[24, 24, Number.POSITIVE_INFINITY]},
+ { tag: "boundPrefToExplicitMax", layoutHints: [-1, -1, 19], childHints: [11, 21, 31], expected:[11, 19, Number.POSITIVE_INFINITY]},
+ { tag: "boundAllToExplicitMax", layoutHints: [-1, -1, 9], childHints: [11, 21, 31], expected:[ 9, 9, Number.POSITIVE_INFINITY]},
+ ];
+ }
+
+ function itemSizeHints(item) {
+ return [item.Layout.minimumWidth, item.implicitWidth, item.Layout.maximumWidth]
+ }
+ Component {
+ id: stacklayout_sizeHint_Component
+ StackLayout {
+ property int implicitWidthChangedCount : 0
+ onImplicitWidthChanged: { ++implicitWidthChangedCount }
+ ColumnLayout {
+ Rectangle {
+ id: r1
+ color: "red"
+ Layout.minimumWidth: 1
+ Layout.preferredWidth: 2
+ Layout.maximumWidth: 3
+
+ Layout.minimumHeight: 20
+ Layout.preferredHeight: 20
+ Layout.maximumHeight: 20
+ Layout.fillWidth: true
+ }
+ }
+ }
+ }
+
+ function test_sizeHint(data) {
+ var layout = stacklayout_sizeHint_Component.createObject(container)
+
+ var col = layout.children[0]
+ col.Layout.minimumWidth = data.layoutHints[0]
+ col.Layout.preferredWidth = data.layoutHints[1]
+ col.Layout.maximumWidth = data.layoutHints[2]
+
+ var child = col.children[0]
+ if (data.implicitWidth !== undefined) {
+ child.implicitWidth = data.implicitWidth
+ }
+ child.Layout.minimumWidth = data.childHints[0]
+ child.Layout.preferredWidth = data.childHints[1]
+ child.Layout.maximumWidth = data.childHints[2]
+
+ var effectiveSizeHintResult = [layout.Layout.minimumWidth, layout.implicitWidth, layout.Layout.maximumWidth]
+ compare(effectiveSizeHintResult, data.expected)
+ layout.destroy()
+ }
+
+ Component {
+ id: stacklayout_addIgnoredItem_Component
+ StackLayout {
+ Repeater {
+ id: rep
+ model: 1
+ Rectangle {
+ id: r
+ }
+ }
+ }
+ }
+
+ // Items with no size information is ignored.
+ function test_addIgnoredItem()
+ {
+ var stack = stacklayout_addIgnoredItem_Component.createObject(container)
+ compare(stack.count, 1)
+ compare(stack.implicitWidth, 0)
+ compare(stack.implicitHeight, 0)
+ var r = stack.children[0]
+ r.Layout.preferredWidth = 20
+ r.Layout.preferredHeight = 30
+ compare(stack.count, 1)
+ compare(stack.implicitWidth, 20)
+ compare(stack.implicitHeight, 30)
+ stack.destroy();
+ }
+
+ function test_dontCrashWhenAnchoredToAWindow() {
+ var test_layoutStr =
+ 'import QtQuick 2.2; \
+ import QtQuick.Window 2.1; \
+ import QtQuick.Layouts 1.3; \
+ Window { \
+ visible: true; \
+ width: stack.implicitWidth; \
+ height: stack.implicitHeight; \
+ StackLayout { \
+ id: stack; \
+ currentIndex: 0; \
+ anchors.fill: parent; \
+ Rectangle { \
+ color: "red"; \
+ implicitWidth: 300; \
+ implicitHeight: 200; \
+ } \
+ } \
+ } '
+
+ var lay = Qt.createQmlObject(test_layoutStr, container, '');
+ tryCompare(lay, 'width', 300);
+ lay.destroy()
+ }
+
+ Component {
+ id: test_dontCrashWhenChildIsResizedToNull_Component
+ StackLayout {
+ property alias rect : _rect
+ Rectangle {
+ id: _rect;
+ color: "red"
+ implicitWidth: 200
+ implicitHeight: 200
+ }
+ }
+ }
+
+ function test_dontCrashWhenChildIsResizedToNull() {
+ var layout = test_dontCrashWhenChildIsResizedToNull_Component.createObject(container)
+ layout.rect.width = 0
+ layout.width = 222 // trigger a rearrange with a valid size
+ layout.height = 222
+ }
+
+ Component {
+ id: test_currentIndex_Component
+ StackLayout {
+ currentIndex: 1
+ Text {
+ text: "0"
+ }
+ Text {
+ text: "1"
+ }
+ }
+ }
+
+ function test_currentIndex() {
+ var layout = test_currentIndex_Component.createObject(container)
+ var c0 = layout.children[0]
+ var c1 = layout.children[1]
+ compare(layout.currentIndex, 1)
+ tryCompare(layout, 'visible', true)
+ compare(c0.visible, false)
+ compare(c1.visible, true)
+ layout.currentIndex = 0
+ compare(c0.visible, true)
+ compare(c1.visible, false)
+ var c2 = layoutItem_Component.createObject(layout)
+ compare(c2.visible, false)
+
+ /*
+ * destroy the current item and check if visibility advances to next
+ */
+ c0.destroy()
+ tryCompare(c1, 'visible', true)
+ compare(c2.visible, false)
+ c1.destroy()
+ tryCompare(c2, 'visible', true)
+ c2.destroy()
+ tryCompare(layout, 'currentIndex', 0)
+
+ layout.destroy()
+
+ /*
+ * Test the default/implicit value of currentIndex, either -1 (if empty) or 0:
+ */
+ layout = emtpy_StackLayout_Component.createObject(container)
+ tryCompare(layout, 'visible', true)
+ compare(layout.currentIndex, -1)
+ compare(layout.num_onCurrentIndexChanged, 0)
+ // make it non-empty
+ c0 = layoutItem_Component.createObject(layout)
+ compare(layout.currentIndex, 0)
+ compare(layout.num_onCurrentIndexChanged, 1)
+ compare(c0.visible, true)
+ // make it empty again
+ c0.destroy()
+ wait(0)
+ compare(layout.currentIndex, -1)
+ //tryCompare(layout, 'currentIndex', -1)
+ compare(layout.num_onCurrentIndexChanged, 2)
+
+ /*
+ * Check that explicit value doesn't change,
+ * and that no items are visible if the index is invalid/out of range
+ */
+ layout.currentIndex = 2
+ compare(layout.currentIndex, 2)
+ compare(layout.num_onCurrentIndexChanged, 3)
+ c0 = layoutItem_Component.createObject(layout)
+ compare(layout.currentIndex, 2)
+ compare(c0.visible, false)
+
+ c1 = layoutItem_Component.createObject(layout)
+ compare(layout.currentIndex, 2)
+ compare(c0.visible, false)
+ compare(c1.visible, false)
+
+ c2 = layoutItem_Component.createObject(layout)
+ compare(layout.currentIndex, 2)
+ compare(c0.visible, false)
+ compare(c1.visible, false)
+ compare(c2.visible, true)
+
+ c2.destroy()
+ wait(0)
+ compare(layout.currentIndex, 2)
+ compare(c0.visible, false)
+ compare(c1.visible, false)
+ c1.destroy()
+ wait(0)
+ compare(layout.currentIndex, 2)
+ compare(c0.visible, false)
+ c0.destroy()
+ wait(0)
+ compare(layout.currentIndex, 2)
+ compare(layout.num_onCurrentIndexChanged, 3)
+ }
+
+ function test_count() {
+ var layout = emtpy_StackLayout_Component.createObject(container)
+ tryCompare(layout, 'visible', true)
+ compare(layout.count, 0)
+ compare(layout.currentIndex, -1)
+ compare(layout.num_onCountChanged, 0)
+ compare(layout.num_onCurrentIndexChanged, 0)
+ var c0 = layoutItem_Component.createObject(layout)
+ compare(layout.count, 1)
+ compare(layout.currentIndex, 0)
+ compare(layout.num_onCurrentIndexChanged, 1)
+ compare(layout.num_onCountChanged, 1)
+ }
+ }
+}
diff --git a/tests/auto/controls/data/tst_textarea.qml b/tests/auto/controls/data/tst_textarea.qml
index b7043845..e8f76c85 100644
--- a/tests/auto/controls/data/tst_textarea.qml
+++ b/tests/auto/controls/data/tst_textarea.qml
@@ -156,6 +156,36 @@ TestCase {
control.destroy()
}
+ function test_editingFinished() {
+ var component = Qt.createComponent("textarea/ta_editingfinished.qml")
+ compare(component.status, Component.Ready)
+ var test = component.createObject(container);
+ verify(test !== null, "test control created is null")
+ var control1 = test.control1
+ verify(control1 !== null)
+ var control2 = test.control2
+ verify(control2 !== null)
+
+ control1.forceActiveFocus()
+ verify(control1.activeFocus)
+ verify(!control2.activeFocus)
+
+ verify(control1.myeditingfinished === false)
+ verify(control2.myeditingfinished === false)
+
+ keyPress(Qt.Key_Backtab)
+ verify(!control1.activeFocus)
+ verify(control2.activeFocus)
+ verify(control1.myeditingfinished === true)
+
+ keyPress(Qt.Key_Backtab)
+ verify(control1.activeFocus)
+ verify(!control2.activeFocus)
+ verify(control2.myeditingfinished === true)
+
+ test.destroy()
+ }
+
function test_keys() {
var component = Qt.createComponent("textarea/ta_keys.qml")
compare(component.status, Component.Ready)
diff --git a/tests/auto/paint/paint.pro b/tests/auto/paint/paint.pro
deleted file mode 100644
index 48ec8c02..00000000
--- a/tests/auto/paint/paint.pro
+++ /dev/null
@@ -1,6 +0,0 @@
-TEMPLATE = app
-TARGET = tst_paint
-QT += qml quick testlib
-CONFIG += testcase insignificant_test
-
-SOURCES += $$PWD/tst_paint.cpp
diff --git a/tests/auto/paint/tst_paint.cpp b/tests/auto/paint/tst_paint.cpp
deleted file mode 100644
index e7a99b09..00000000
--- a/tests/auto/paint/tst_paint.cpp
+++ /dev/null
@@ -1,107 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL3$
-** 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 http://www.qt.io/terms-conditions. For further
-** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free
-** Software Foundation and appearing in the file LICENSE.GPL included in
-** the packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 2.0 requirements will be
-** met: http://www.gnu.org/licenses/gpl-2.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtTest>
-#include <QtQml>
-#include <QtQuick>
-
-class tst_Paint : public QObject
-{
- Q_OBJECT
-
-private slots:
- void bounds_data();
- void bounds();
-};
-
-void tst_Paint::bounds_data()
-{
- QTest::addColumn<QString>("name");
-
- QTest::newRow("CircularGauge") << "CircularGauge";
- QTest::newRow("Dial") << "Dial";
- QTest::newRow("Gauge") << "Gauge";
- QTest::newRow("PieMenu") << "PieMenu";
- QTest::newRow("DelayButton") << "DelayButton";
- QTest::newRow("ToggleButton") << "ToggleButton";
- QTest::newRow("Tumbler") << "Tumbler";
-}
-
-void tst_Paint::bounds()
-{
- QFETCH(QString, name);
-
- QQmlEngine engine;
- QQmlComponent component(&engine);
- component.setData(QStringLiteral("import QtQuick.Extras 1.2; %1 { }").arg(name).toUtf8(), QUrl());
- QQuickItem *control = qobject_cast<QQuickItem*>(component.create());
- QVERIFY(control);
-
- const int w = control->width();
- const int h = control->height();
- QVERIFY(w > 0);
- QVERIFY(h > 0);
-
- static const int margin = 10;
- static const QColor bg = Qt::yellow;
-
- QQuickWindow window;
- window.setColor(bg);
- window.resize(w + 2 * margin, h + 2 * margin);
- control->setParentItem(window.contentItem());
- control->setPosition(QPoint(margin, margin));
- window.create();
- window.show();
-
- QTest::qWaitForWindowExposed(&window);
-
- const QRect bounds(margin, margin, w, h);
- const QImage image = window.grabWindow();
-
- for (int x = 0; x < image.width(); ++x) {
- for (int y = 0; y < image.height(); ++y) {
- if (!bounds.contains(x, y)) {
- const QByteArray msg = QString("painted outside bounds (%1,%2 %3x%4) at (%5,%6)").arg(bounds.x()).arg(bounds.y()).arg(bounds.width()).arg(bounds.height()).arg(x).arg(y).toUtf8();
- const QColor px = image.pixel(x, y);
- QVERIFY2(px == bg, msg);
- }
- }
- }
-}
-
-QTEST_MAIN(tst_Paint)
-
-#include "tst_paint.moc"