diff --git a/examples/ApplicationTemplate/qml/main.qml b/examples/ApplicationTemplate/qml/main.qml index d5304784f72454ab7cdb18c0da0484d0112bfb79..510f6bfa67a97d1f0a412ca6e6843df3b29a0286 100644 --- a/examples/ApplicationTemplate/qml/main.qml +++ b/examples/ApplicationTemplate/qml/main.qml @@ -43,6 +43,7 @@ import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 ApplicationWindow { width: 640 diff --git a/examples/gallery/main.qml b/examples/gallery/main.qml index 1fc06981390b5a2321fe8f35868af31e556bce98..3b5e1a7c13d499415a5e0c9defeaca91a6f5b68b 100644 --- a/examples/gallery/main.qml +++ b/examples/gallery/main.qml @@ -44,6 +44,7 @@ import QtQuick 2.0 import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 import QtQuick.Controls.Private 1.0 import "content" diff --git a/examples/splitters/main.qml b/examples/splitters/main.qml index dfacadaa3c8274ba4c1a487978d7061a99361f37..0d35ae71f70a435d6446ace651c1d0de2081bae8 100644 --- a/examples/splitters/main.qml +++ b/examples/splitters/main.qml @@ -44,6 +44,7 @@ import QtQuick 2.0 import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 ApplicationWindow { width: 600 diff --git a/src/controls/ApplicationWindow.qml b/src/controls/ApplicationWindow.qml index d483a0750356bbbfa79412d19e68f167a57cb1ec..9fa5c37415e6635a7fc26ae1ca649fad915e3571 100644 --- a/src/controls/ApplicationWindow.qml +++ b/src/controls/ApplicationWindow.qml @@ -41,6 +41,8 @@ import QtQuick 2.0 import QtQuick.Window 2.0 import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 +import QtQuick.Controls.Private 1.0 /*! \qmltype ApplicationWindow diff --git a/src/controls/SplitView.qml b/src/controls/SplitView.qml index 3b226ac2f9ec1bcf454f576ca72dae610eed693b..ac858258e2d66e83f69053c081c367963431aef7 100644 --- a/src/controls/SplitView.qml +++ b/src/controls/SplitView.qml @@ -40,6 +40,7 @@ import QtQuick 2.0 import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 import QtQuick.Controls.Private 1.0 as Private /*! diff --git a/src/controls/plugin.cpp b/src/controls/plugin.cpp index b81347c08e3b400eb10305c35eb6915c97336b96..20d2f394f8ee6e6648c3551fbc0f623ce3adbd2d 100644 --- a/src/controls/plugin.cpp +++ b/src/controls/plugin.cpp @@ -44,7 +44,6 @@ #include "qtexclusivegroup_p.h" #include "qtmenu_p.h" #include "qtmenubar_p.h" -#include "qquicklinearlayout_p.h" #include "qpagestatus.h" #include <qimage.h> @@ -87,11 +86,6 @@ void StylePlugin::registerTypes(const char *uri) qmlRegisterUncreatableType<QtMenuBase>(uri, 1, 0, "MenuBase", QLatin1String("Do not create objects of type MenuBase")); - qmlRegisterType<QQuickComponentsRowLayout>(uri, 1, 0, "RowLayout"); - qmlRegisterType<QQuickComponentsColumnLayout>(uri, 1, 0, "ColumnLayout"); - qmlRegisterUncreatableType<QQuickComponentsLayout>(uri, 1, 0, "Layout", - QLatin1String("Do not create objects of type Layout")); - qmlRegisterUncreatableType<QPageStatus>(uri, 1, 0, "PageStatus", QLatin1String("Do not create objects of type PageStatus")); } diff --git a/src/controls/plugin.pri b/src/controls/plugin.pri index 5637e75ccc736d26084e89774ebd64164475cac2..338645e49a4fccbe86cfe7aec52157ed2297ab47 100644 --- a/src/controls/plugin.pri +++ b/src/controls/plugin.pri @@ -1,7 +1,4 @@ HEADERS += \ - $$PWD/qquicklayout_p.h \ - $$PWD/qquicklayoutengine_p.h \ - $$PWD/qquicklinearlayout_p.h \ $$PWD/plugin_p.h \ $$PWD/qtaction_p.h \ $$PWD/qtexclusivegroup_p.h \ @@ -12,9 +9,6 @@ HEADERS += \ $$PWD/qpagestatus.h SOURCES += \ - $$PWD/qquicklayout.cpp \ - $$PWD/qquicklayoutengine.cpp \ - $$PWD/qquicklinearlayout.cpp \ $$PWD/plugin.cpp \ $$PWD/qtaction.cpp \ $$PWD/qtexclusivegroup.cpp \ diff --git a/src/controls/plugins.qmltypes b/src/controls/plugins.qmltypes index 1e8d179df1aafe26ff2a3e4ddd63745bed1bd77d..1cfb3477da77bd71d3f04e793aea3869d9f0149d 100644 --- a/src/controls/plugins.qmltypes +++ b/src/controls/plugins.qmltypes @@ -20,49 +20,6 @@ Module { } } } - Component { - name: "QQuickComponentsColumnLayout" - defaultProperty: "data" - prototype: "QQuickComponentsLinearLayout" - exports: ["ColumnLayout 1.0"] - } - Component { - name: "QQuickComponentsLayout" - defaultProperty: "data" - prototype: "QQuickItem" - exports: ["Layout 1.0"] - attachedType: "QQuickComponentsLayoutAttached" - Enum { - name: "SizePolicy" - values: { - "Fixed": 0, - "Expanding": 1 - } - } - } - Component { - name: "QQuickComponentsLayoutAttached" - prototype: "QObject" - Property { name: "minimumWidth"; type: "double" } - Property { name: "minimumHeight"; type: "double" } - Property { name: "maximumWidth"; type: "double" } - Property { name: "maximumHeight"; type: "double" } - Property { name: "verticalSizePolicy"; type: "QQuickComponentsLayout::SizePolicy" } - Property { name: "horizontalSizePolicy"; type: "QQuickComponentsLayout::SizePolicy" } - } - Component { - name: "QQuickComponentsLinearLayout" - defaultProperty: "data" - prototype: "QQuickComponentsLayout" - Property { name: "spacing"; type: "double" } - Signal { name: "orientationChanged" } - } - Component { - name: "QQuickComponentsRowLayout" - defaultProperty: "data" - prototype: "QQuickComponentsLinearLayout" - exports: ["RowLayout 1.0"] - } Component { name: "QtAction" prototype: "QObject" diff --git a/src/controls/qquicklayout_p.h b/src/controls/qquicklayout_p.h deleted file mode 100644 index f8f88657c9bc1e1ce3c98eb5182be74c41f4b8b4..0000000000000000000000000000000000000000 --- a/src/controls/qquicklayout_p.h +++ /dev/null @@ -1,142 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Quick Controls 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 Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 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 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKLAYOUT_P_H -#define QQUICKLAYOUT_P_H - -#include <QPointer> -#include <QQuickItem> - -QT_BEGIN_NAMESPACE - -class QQuickComponentsLayoutAttached; - - -class QQuickComponentsLayout : public QQuickItem -{ - Q_OBJECT - Q_ENUMS(SizePolicy) - -public: - enum SizePolicy { - Fixed, - Expanding - }; - - explicit QQuickComponentsLayout(QQuickItem *parent = 0); - ~QQuickComponentsLayout(); - - static QQuickComponentsLayoutAttached *qmlAttachedProperties(QObject *object); - -protected: - void invalidate(); - bool event(QEvent *e); - void reconfigureTopDown(); - virtual void reconfigureLayout(); - void setupItemLayout(QQuickItem *item); - -private: - bool m_dirty; - - friend class QQuickComponentsLayoutAttached; -}; - - -class QQuickComponentsLayoutAttached : public QObject -{ - Q_OBJECT - Q_PROPERTY(qreal minimumWidth READ minimumWidth WRITE setMinimumWidth NOTIFY minimumWidthChanged) - Q_PROPERTY(qreal minimumHeight READ minimumHeight WRITE setMinimumHeight NOTIFY minimumHeightChanged) - Q_PROPERTY(qreal maximumWidth READ maximumWidth WRITE setMaximumWidth NOTIFY maximumWidthChanged) - Q_PROPERTY(qreal maximumHeight READ maximumHeight WRITE setMaximumHeight NOTIFY maximumHeightChanged) - Q_PROPERTY(QQuickComponentsLayout::SizePolicy verticalSizePolicy READ verticalSizePolicy WRITE setVerticalSizePolicy NOTIFY verticalSizePolicyChanged) - Q_PROPERTY(QQuickComponentsLayout::SizePolicy horizontalSizePolicy READ horizontalSizePolicy WRITE setHorizontalSizePolicy NOTIFY horizontalSizePolicyChanged) - -public: - QQuickComponentsLayoutAttached(QObject *object); - - qreal minimumWidth() const { return m_minimumWidth; } - void setMinimumWidth(qreal width); - - qreal minimumHeight() const { return m_minimumHeight; } - void setMinimumHeight(qreal height); - - qreal maximumWidth() const { return m_maximumWidth; } - void setMaximumWidth(qreal width); - - qreal maximumHeight() const { return m_maximumHeight; } - void setMaximumHeight(qreal height); - - QQuickComponentsLayout::SizePolicy verticalSizePolicy() const { return m_verticalSizePolicy; } - void setVerticalSizePolicy(QQuickComponentsLayout::SizePolicy policy); - - QQuickComponentsLayout::SizePolicy horizontalSizePolicy() const { return m_horizontalSizePolicy; } - void setHorizontalSizePolicy(QQuickComponentsLayout::SizePolicy policy); - -signals: - void minimumWidthChanged(); - void minimumHeightChanged(); - void maximumWidthChanged(); - void maximumHeightChanged(); - void verticalSizePolicyChanged(); - void horizontalSizePolicyChanged(); - -protected: - void updateLayout(); - -private: - qreal m_minimumWidth; - qreal m_minimumHeight; - qreal m_maximumWidth; - qreal m_maximumHeight; - QQuickComponentsLayout::SizePolicy m_verticalSizePolicy; - QQuickComponentsLayout::SizePolicy m_horizontalSizePolicy; - QPointer<QQuickComponentsLayout> m_layout; - - friend class QQuickComponentsLayout; -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQuickComponentsLayout) -QML_DECLARE_TYPEINFO(QQuickComponentsLayout, QML_HAS_ATTACHED_PROPERTIES) - -#endif // QQUICKLAYOUT_P_H diff --git a/src/controls/qquicklayoutengine.cpp b/src/controls/qquicklayoutengine.cpp deleted file mode 100644 index 1811b45359e534f8feee27d8b206b6169eecb4d9..0000000000000000000000000000000000000000 --- a/src/controls/qquicklayoutengine.cpp +++ /dev/null @@ -1,295 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Quick Controls 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 Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 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 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qquicklayoutengine_p.h" - -QT_BEGIN_NAMESPACE - -/* - This function is a modification of qGeomCalc() included in "QtCore/kernel/qlayoutengine_p.h". - It is used as a helper function to handle linear layout recalculations for QQuickItems. - - chain contains input and output parameters describing the geometry. - count is the count of items in the chain; pos and space give the - interval (relative to parentWidget topLeft). -*/ -void qDeclarativeLayoutCalculate(QVector<QQuickComponentsLayoutInfo> &chain, int start, - int count, qreal pos, qreal space, qreal spacer) -{ - if (chain.count() == 0) - return; - - bool wannaGrow = false; - qreal totalStretch = 0; - qreal totalSpacing = 0; - qreal totalSizeHint = 0; - qreal totalMinimumSize = 0; - - const int end = start + count; - const int spacerCount = chain.count() - 1; - - for (int i = start; i < end; i++) { - QQuickComponentsLayoutInfo *data = &chain[i]; - - data->done = false; - - totalStretch += data->stretch; - totalSizeHint += data->smartSizeHint(); - totalMinimumSize += data->minimumSize; - - // don't count last spacing - if (i < end - 1) - totalSpacing += data->effectiveSpacer(spacer); - - wannaGrow = (wannaGrow || data->expansive || data->stretch > 0); - } - - qreal extraSpace = 0; - - if (space < totalMinimumSize + totalSpacing) { - // Less space than minimumSize; take from the biggest first - qreal minSize = totalMinimumSize + totalSpacing; - - // shrink the spacers proportionally - if (spacer >= 0) { - spacer = minSize > 0 ? spacer * space / minSize : 0; - totalSpacing = spacer * spacerCount; - } - - QList<qreal> list; - - for (int i = start; i < end; i++) - list << chain.at(i).minimumSize; - - qSort(list); - - qreal spaceLeft = space - totalSpacing; - - qreal sum = 0; - int idx = 0; - qreal spaceUsed = 0; - qreal current = 0; - - while (idx < count && spaceUsed < spaceLeft) { - current = list.at(idx); - spaceUsed = sum + current * (count - idx); - sum += current; - ++idx; - } - - --idx; - - int items = count - idx; - qreal deficit = spaceUsed - spaceLeft; - qreal deficitPerItem = deficit / items; - qreal maxval = current - deficitPerItem; - - for (int i = start; i < end; i++) { - QQuickComponentsLayoutInfo *data = &chain[i]; - data->done = true; - - if (data->minimumSize > 0) - data->size = data->minimumSize; - else - data->size = qMin<qreal>(data->minimumSize, maxval); - } - } else if (space < totalSizeHint + totalSpacing) { - /* - Less space than smartSizeHint(), but more than minimumSize. - Currently take space equally from each, as in Qt 2.x. - Commented-out lines will give more space to stretchier - items. - */ - int n = count; - qreal spaceLeft = space - totalSpacing; - qreal overdraft = totalSizeHint - spaceLeft; - - // first give to the fixed ones - for (int i = start; i < end; i++) { - QQuickComponentsLayoutInfo *data = &chain[i]; - - if (!data->done && data->minimumSize >= data->smartSizeHint()) { - data->done = true; - data->size = data->smartSizeHint(); - spaceLeft -= data->smartSizeHint(); - n--; - } - } - - bool finished = (n == 0); - - while (!finished) { - finished = true; - - for (int i = start; i < end; i++) { - QQuickComponentsLayoutInfo *data = &chain[i]; - - if (data->done) - continue; - - qreal w = overdraft / n; - data->size = data->smartSizeHint() - w; - - if (data->size < data->minimumSize) { - data->done = true; - data->size = data->minimumSize; - finished = false; - overdraft -= data->smartSizeHint() - data->minimumSize; - n--; - break; - } - } - } - } else { // extra space - int n = count; - qreal spaceLeft = space - totalSpacing; - - // first give to the fixed ones, and handle non-expansiveness - for (int i = start; i < end; i++) { - QQuickComponentsLayoutInfo *data = &chain[i]; - - if (data->done) - continue; - - if (data->maximumSize <= data->smartSizeHint() - || (wannaGrow && !data->expansive && data->stretch == 0) - || (!data->expansive && data->stretch == 0)) { - data->done = true; - data->size = data->smartSizeHint(); - spaceLeft -= data->size; - totalStretch -= data->stretch; - n--; - } - } - - extraSpace = spaceLeft; - - /* - Do a trial distribution and calculate how much it is off. - If there are more deficit pixels than surplus pixels, give - the minimum size items what they need, and repeat. - Otherwise give to the maximum size items, and repeat. - - Paul Olav Tvete has a wonderful mathematical proof of the - correctness of this principle, but unfortunately this - comment is too small to contain it. - */ - qreal surplus, deficit; - - do { - surplus = deficit = 0; - - for (int i = start; i < end; i++) { - QQuickComponentsLayoutInfo *data = &chain[i]; - - if (data->done) - continue; - - extraSpace = 0; - - qreal w; - - if (totalStretch <= 0) - w = (spaceLeft / n); - else - w = (spaceLeft * data->stretch) / totalStretch; - - data->size = w; - - if (w < data->smartSizeHint()) - deficit += data->smartSizeHint() - w; - else if (w > data->maximumSize) - surplus += w - data->maximumSize; - } - - if (deficit > 0 && surplus <= deficit) { - // give to the ones that have too little - for (int i = start; i < end; i++) { - QQuickComponentsLayoutInfo *data = &chain[i]; - - if (!data->done && data->size < data->smartSizeHint()) { - data->done = true; - data->size = data->smartSizeHint(); - spaceLeft -= data->smartSizeHint(); - totalStretch -= data->stretch; - n--; - } - } - } - - if (surplus > 0 && surplus >= deficit) { - // take from the ones that have too much - for (int i = start; i < end; i++) { - QQuickComponentsLayoutInfo *data = &chain[i]; - - if (!data->done && data->size > data->maximumSize) { - data->done = true; - data->size = data->maximumSize; - spaceLeft -= data->maximumSize; - totalStretch -= data->stretch; - n--; - } - } - } - } while (n > 0 && surplus != deficit); - - if (n == 0) - extraSpace = spaceLeft; - } - - /* - As a last resort, we distribute the unwanted space equally - among the spacers (counting the start and end of the chain). We - could, but don't, attempt a sub-pixel allocation of the extra - space. - */ - qreal extra = extraSpace / (spacerCount + 2); - qreal p = pos + extra; - - for (int i = start; i < end; i++) { - QQuickComponentsLayoutInfo *data = &chain[i]; - data->pos = p; - p += data->size; - p += data->effectiveSpacer(spacer) + extra; - } -} - -QT_END_NAMESPACE diff --git a/src/controls/qquicklinearlayout.cpp b/src/controls/qquicklinearlayout.cpp deleted file mode 100644 index baf1d19d197b34724881bb3feba5530a6bf9587a..0000000000000000000000000000000000000000 --- a/src/controls/qquicklinearlayout.cpp +++ /dev/null @@ -1,270 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Quick Controls 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 Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 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 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qquicklinearlayout_p.h" -#include "qquicklayoutengine_p.h" -#include <QtCore/qnumeric.h> - -/*! - \qmltype RowLayout - \instantiates QQuickComponentsRowLayout - \inqmlmodule QtQuick.Controls 1.0 - \brief RowLayout is doing bla...bla... -*/ - -/*! - \qmltype ColumnLayout - \instantiates QQuickComponentsColumnLayout - \inqmlmodule QtQuick.Controls 1.0 - \brief ColumnLayout is doing bla...bla... -*/ - -QT_BEGIN_NAMESPACE - -static const qreal q_declarativeLayoutDefaultSpacing = 4.0; - - -QQuickComponentsLinearLayout::QQuickComponentsLinearLayout(Orientation orientation, - QQuickItem *parent) - : QQuickComponentsLayout(parent), - m_spacing(q_declarativeLayoutDefaultSpacing), - m_orientation(orientation) -{ - -} - -qreal QQuickComponentsLinearLayout::spacing() const -{ - return m_spacing; -} - -void QQuickComponentsLinearLayout::setSpacing(qreal spacing) -{ - if (qIsNaN(spacing) || m_spacing == spacing) - return; - - m_spacing = spacing; - invalidate(); -} - -QQuickComponentsLinearLayout::Orientation QQuickComponentsLinearLayout::orientation() const -{ - return m_orientation; -} - -void QQuickComponentsLinearLayout::setOrientation(Orientation orientation) -{ - if (m_orientation == orientation) - return; - - m_orientation = orientation; - invalidate(); - - emit orientationChanged(); -} - -void QQuickComponentsLinearLayout::componentComplete() -{ - QQuickComponentsLayout::componentComplete(); - updateLayoutItems(); - invalidate(); -} - -void QQuickComponentsLinearLayout::updateLayoutItems() -{ - const QList<QQuickItem *> &children = childItems(); - qreal implicitWidth = 0; - qreal implicitHeight = 0; - foreach (QQuickItem *child, children) { - if (m_orientation == Horizontal) { - implicitWidth += child->implicitWidth(); - implicitHeight = qMax(implicitHeight, child->implicitHeight()); - } else { - implicitHeight += child->implicitHeight(); - implicitWidth = qMax(implicitWidth, child->implicitWidth()); - } - insertLayoutItem(child); - } - setImplicitWidth(implicitWidth); - setImplicitHeight(implicitHeight); -} - -void QQuickComponentsLinearLayout::itemChange(ItemChange change, const ItemChangeData &value) -{ - if (isComponentComplete()) { - if (change == ItemChildAddedChange) - insertLayoutItem(value.item); - else if (change == ItemChildRemovedChange) - removeLayoutItem(value.item); - } - - QQuickComponentsLayout::itemChange(change, value); -} - -void QQuickComponentsLinearLayout::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) -{ - QQuickComponentsLayout::geometryChanged(newGeometry, oldGeometry); - invalidate(); -} - -void QQuickComponentsLinearLayout::insertLayoutItem(QQuickItem *item) -{ - m_items.append(item); - setupItemLayout(item); - - invalidate(); - QObject::connect(item, SIGNAL(destroyed()), this, SLOT(onItemDestroyed())); - QObject::connect(item, SIGNAL(visibleChanged()), this, SLOT(onItemVisibleChanged())); -} - -void QQuickComponentsLinearLayout::removeLayoutItem(QQuickItem *item) -{ - if (!m_items.removeOne(item)) - return; - - invalidate(); - QObject::disconnect(item, SIGNAL(destroyed()), this, SLOT(onItemDestroyed())); - QObject::disconnect(item, SIGNAL(visibleChanged()), this, SLOT(onItemVisibleChanged())); -} - -void QQuickComponentsLinearLayout::onItemVisibleChanged() -{ - invalidate(); -} - -void QQuickComponentsLinearLayout::onItemDestroyed() -{ - if (!m_items.removeOne(static_cast<QQuickItem *>(sender()))) - return; - - invalidate(); -} - -void QQuickComponentsLinearLayout::reconfigureLayout() -{ - if (!isComponentComplete()) - return; - - const int count = m_items.count(); - - if (count == 0) - return; - - qreal totalSpacing = 0; - qreal totalSizeHint = 0; - qreal totalMinimumSize = 0; - qreal totalMaximumSize = 0; - - QVector<QQuickComponentsLayoutInfo> itemData; - - for (int i = 0; i < count; i++) { - QQuickItem *item = m_items.at(i); - QObject *attached = qmlAttachedPropertiesObject<QQuickComponentsLayout>(item); - QQuickComponentsLayoutAttached *info = static_cast<QQuickComponentsLayoutAttached *>(attached); - - QQuickComponentsLayoutInfo data; - - if (item->isVisible()) { - if (m_orientation == Horizontal) { - data.sizeHint = item->implicitWidth(); - data.minimumSize = info->minimumWidth(); - data.maximumSize = info->maximumWidth(); - data.expansive = (info->horizontalSizePolicy() == QQuickComponentsLayout::Expanding); - data.stretch = info->horizontalSizePolicy() == Expanding ? 1.0 : 0; - } else { - data.sizeHint = item->implicitHeight(); - data.minimumSize = info->minimumHeight(); - data.maximumSize = info->maximumHeight(); - data.expansive = (info->verticalSizePolicy() == QQuickComponentsLayout::Expanding); - data.stretch = info->verticalSizePolicy() == Expanding ? 1.0 : 0; - } - - itemData.append(data); - // sum - totalSizeHint += data.sizeHint; - totalMinimumSize += data.minimumSize; - totalMaximumSize += data.maximumSize; - - // don't count last spacing - if (i < count - 1) - totalSpacing += data.spacing + m_spacing; - } - } - - qreal extent = m_orientation == Horizontal ? width() : height(); - qDeclarativeLayoutCalculate(itemData, 0, itemData.count(), 0, extent, m_spacing); - - int i = 0; - int id = 0; - while (i < count) { - QQuickItem *item = m_items.at(i++); - if (!item->isVisible()) - continue; - const QQuickComponentsLayoutInfo &data = itemData.at(id); - - if (m_orientation == Horizontal) { - item->setX(data.pos); - item->setY(height()/2 - item->height()/2); - item->setWidth(data.size); - } else { - item->setY(data.pos); - item->setX(width()/2 - item->width()/2); - item->setHeight(data.size); - } - ++id; - } - - // propagate hints to upper levels - QObject *attached = qmlAttachedPropertiesObject<QQuickComponentsLayout>(this); - QQuickComponentsLayoutAttached *info = static_cast<QQuickComponentsLayoutAttached *>(attached); - - if (m_orientation == Horizontal) { - setImplicitWidth(totalSizeHint); - info->setMinimumWidth(totalMinimumSize + totalSpacing); - info->setMaximumWidth(totalMaximumSize + totalSpacing); - } else { - setImplicitHeight(totalSizeHint); - info->setMinimumHeight(totalMinimumSize + totalSpacing); - info->setMaximumHeight(totalMaximumSize + totalSpacing); - } -} - -QT_END_NAMESPACE diff --git a/src/controls/qquicklinearlayout_p.h b/src/controls/qquicklinearlayout_p.h deleted file mode 100644 index 6db7562dac8efe5b66e48833e87c2523380adc5f..0000000000000000000000000000000000000000 --- a/src/controls/qquicklinearlayout_p.h +++ /dev/null @@ -1,116 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Quick Controls 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 Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 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 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKLINEARLAYOUT_P_H -#define QQUICKLINEARLAYOUT_P_H - -#include "qquicklayout_p.h" - -QT_BEGIN_NAMESPACE - -class QQuickComponentsLinearLayout : public QQuickComponentsLayout -{ - Q_OBJECT - Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing NOTIFY spacingChanged) - -public: - enum Orientation { - Vertical, - Horizontal - }; - - explicit QQuickComponentsLinearLayout(Orientation orientation, - QQuickItem *parent = 0); - ~QQuickComponentsLinearLayout() {} - - qreal spacing() const; - void setSpacing(qreal spacing); - - Orientation orientation() const; - void setOrientation(Orientation orientation); - - void componentComplete(); - -signals: - void spacingChanged(); - void orientationChanged(); - -protected: - void updateLayoutItems(); - void reconfigureLayout(); - void insertLayoutItem(QQuickItem *item); - void removeLayoutItem(QQuickItem *item); - void itemChange(ItemChange change, const ItemChangeData &data); - void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); - -protected slots: - void onItemVisibleChanged(); - void onItemDestroyed(); - -private: - qreal m_spacing; - Orientation m_orientation; - QList<QQuickItem *> m_items; -}; - - -class QQuickComponentsRowLayout : public QQuickComponentsLinearLayout -{ - Q_OBJECT - -public: - explicit QQuickComponentsRowLayout(QQuickItem *parent = 0) - : QQuickComponentsLinearLayout(Horizontal, parent) {} -}; - - -class QQuickComponentsColumnLayout : public QQuickComponentsLinearLayout -{ - Q_OBJECT - -public: - explicit QQuickComponentsColumnLayout(QQuickItem *parent = 0) - : QQuickComponentsLinearLayout(Vertical, parent) {} -}; - -QT_END_NAMESPACE - -#endif // QQUICKLINEARLAYOUT_P_H diff --git a/src/layouts/.gitattributes b/src/layouts/.gitattributes new file mode 100644 index 0000000000000000000000000000000000000000..4f72c998baf89180e7e8a71e363a42eccc57c044 --- /dev/null +++ b/src/layouts/.gitattributes @@ -0,0 +1 @@ +*.qmltypes diff=text \ No newline at end of file diff --git a/src/layouts/layouts.pro b/src/layouts/layouts.pro new file mode 100644 index 0000000000000000000000000000000000000000..20bcf20c19740e3f96d9744a476a73130533925e --- /dev/null +++ b/src/layouts/layouts.pro @@ -0,0 +1,26 @@ +CXX_MODULE = qml +TARGET = qquicklayoutsplugin +TARGETPATH = QtQuick/Layouts +IMPORT_VERSION = 2.1 + +QT *= qml-private quick-private v8-private gui-private core-private + +#QMAKE_DOCS = $$PWD/doc/qtquicklayouts.qdocconf + +SOURCES += plugin.cpp \ + qlayoutpolicy.cpp \ + qgridlayoutengine.cpp \ + qquicklayout.cpp \ + qquicklinearlayout.cpp + +HEADERS += \ + qlayoutpolicy_p.h \ + qgridlayoutengine_p.h \ + qquickgridlayoutengine_p.h \ + qquicklayout_p.h \ + qquicklinearlayout_p.h + +OTHER_FILES += \ +$$PWD/plugin.json + +load(qml_plugin) diff --git a/src/controls/qquicklayoutengine_p.h b/src/layouts/plugin.cpp similarity index 66% rename from src/controls/qquicklayoutengine_p.h rename to src/layouts/plugin.cpp index f83da4b1f462b2cfa2a049a79d7754c28289a121..8348d8a0be94eeeebb64a91adb05d57fde3675c3 100644 --- a/src/controls/qquicklayoutengine_p.h +++ b/src/layouts/plugin.cpp @@ -3,7 +3,7 @@ ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** -** This file is part of the Qt Quick Controls module of the Qt Toolkit. +** This file is part of the Qt Quick Layouts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage @@ -39,51 +39,32 @@ ** ****************************************************************************/ -#ifndef QQUICKLAYOUTENGINE_P_H -#define QQUICKLAYOUTENGINE_P_H +#include <QtQml/qqmlextensionplugin.h> -#include <QVector> +#include "qquicklinearlayout_p.h" QT_BEGIN_NAMESPACE -struct QQuickComponentsLayoutInfo +//![class decl] +class QtQuickLayoutsPlugin : public QQmlExtensionPlugin { - QQuickComponentsLayoutInfo() - : stretch(1), - sizeHint(0), - spacing(0), - minimumSize(0), - maximumSize(0), - expansive(true), - done(false), - pos(0), - size(0) - { } + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface/1.0") +public: + virtual void registerTypes(const char *uri) + { + Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQuick.Layouts")); + Q_UNUSED(uri); - inline qreal smartSizeHint() { - return (stretch > 0) ? minimumSize : sizeHint; + qmlRegisterType<QQuickRowLayout>(uri, 1, 0, "RowLayout"); + qmlRegisterType<QQuickColumnLayout>(uri, 1, 0, "ColumnLayout"); + qmlRegisterType<QQuickGridLayout>(uri, 1, 0, "GridLayout"); + qmlRegisterUncreatableType<QQuickLayout>(uri, 1, 0, "Layout", + QLatin1String("Do not create objects of type Layout")); } - - inline qreal effectiveSpacer(qreal value) const { - return value + spacing; - } - - qreal stretch; - qreal sizeHint; - qreal spacing; - qreal minimumSize; - qreal maximumSize; - bool expansive; - - // result - bool done; - qreal pos; - qreal size; }; - -void qDeclarativeLayoutCalculate(QVector<QQuickComponentsLayoutInfo> &chain, int start, - int count, qreal pos, qreal space, qreal spacer); +//![class decl] QT_END_NAMESPACE -#endif // QQUICKLAYOUTENGINE_P_H +#include "plugin.moc" diff --git a/src/layouts/plugin.json b/src/layouts/plugin.json new file mode 100644 index 0000000000000000000000000000000000000000..0967ef424bce6791893e9a57bb952f80fd536e93 --- /dev/null +++ b/src/layouts/plugin.json @@ -0,0 +1 @@ +{} diff --git a/src/layouts/plugins.qmltypes b/src/layouts/plugins.qmltypes new file mode 100644 index 0000000000000000000000000000000000000000..dd3dc69f02223dc029d0aa66f836fe0a0c9bb300 Binary files /dev/null and b/src/layouts/plugins.qmltypes differ diff --git a/src/layouts/qgridlayoutengine.cpp b/src/layouts/qgridlayoutengine.cpp new file mode 100644 index 0000000000000000000000000000000000000000..11188d56480a509d6d68da5d150e9211b4506e7a --- /dev/null +++ b/src/layouts/qgridlayoutengine.cpp @@ -0,0 +1,1678 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Quick Layouts 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qglobal.h" + +#ifndef QT_NO_GRAPHICSVIEW + +#include <math.h> + +//#include "qgraphicslayoutitem.h" +#include "qgridlayoutengine_p.h" +//#include "qstyleoption.h" +#include "qvarlengtharray.h" + +#include <QtDebug> +#include <QtCore/qmath.h> + +QT_BEGIN_NAMESPACE + +template <typename T> +static void insertOrRemoveItems(QVector<T> &items, int index, int delta) +{ + int count = items.count(); + if (index < count) { + if (delta > 0) { + items.insert(index, delta, T()); + } else if (delta < 0) { + items.remove(index, qMin(-delta, count - index)); + } + } +} + +static qreal growthFactorBelowPreferredSize(qreal desired, qreal sumAvailable, qreal sumDesired) +{ + Q_ASSERT(sumDesired != 0.0); + return desired * qPow(sumAvailable / sumDesired, desired / sumDesired); +} + +static qreal fixedDescent(qreal descent, qreal ascent, qreal targetSize) +{ + if (descent < 0.0) + return -1.0; + + Q_ASSERT(descent >= 0.0); + Q_ASSERT(ascent >= 0.0); + Q_ASSERT(targetSize >= ascent + descent); + + qreal extra = targetSize - (ascent + descent); + return descent + (extra / 2.0); +} + +static qreal compare(const QGridLayoutBox &box1, const QGridLayoutBox &box2, int which) +{ + qreal size1 = box1.q_sizes(which); + qreal size2 = box2.q_sizes(which); + + if (which == MaximumSize) { + return size2 - size1; + } else { + return size1 - size2; + } +} + +void QGridLayoutBox::add(const QGridLayoutBox &other, int stretch, qreal spacing) +{ + Q_ASSERT(q_minimumDescent < 0.0); + + q_minimumSize += other.q_minimumSize + spacing; + q_preferredSize += other.q_preferredSize + spacing; + q_maximumSize += ((stretch == 0) ? other.q_preferredSize : other.q_maximumSize) + spacing; +} + +void QGridLayoutBox::combine(const QGridLayoutBox &other) +{ + q_minimumDescent = qMax(q_minimumDescent, other.q_minimumDescent); + q_minimumAscent = qMax(q_minimumAscent, other.q_minimumAscent); + + q_minimumSize = qMax(q_minimumAscent + q_minimumDescent, + qMax(q_minimumSize, other.q_minimumSize)); + qreal maxMax; + if (q_maximumSize == FLT_MAX && other.q_maximumSize != FLT_MAX) + maxMax = other.q_maximumSize; + else if (other.q_maximumSize == FLT_MAX && q_maximumSize != FLT_MAX) + maxMax = q_maximumSize; + else + maxMax = qMax(q_maximumSize, other.q_maximumSize); + + q_maximumSize = qMax(q_minimumSize, maxMax); + q_preferredSize = qBound(q_minimumSize, qMax(q_preferredSize, other.q_preferredSize), + q_maximumSize); +} + +void QGridLayoutBox::normalize() +{ + q_maximumSize = qMax(qreal(0.0), q_maximumSize); + q_minimumSize = qBound(qreal(0.0), q_minimumSize, q_maximumSize); + q_preferredSize = qBound(q_minimumSize, q_preferredSize, q_maximumSize); + q_minimumDescent = qMin(q_minimumDescent, q_minimumSize); + + Q_ASSERT((q_minimumDescent < 0.0) == (q_minimumAscent < 0.0)); +} + +#ifdef QT_DEBUG +void QGridLayoutBox::dump(int indent) const +{ + qDebug("%*sBox (%g <= %g <= %g [%g/%g])", indent, "", q_minimumSize, q_preferredSize, + q_maximumSize, q_minimumAscent, q_minimumDescent); +} +#endif + +bool operator==(const QGridLayoutBox &box1, const QGridLayoutBox &box2) +{ + for (int i = 0; i < NSizes; ++i) { + if (box1.q_sizes(i) != box2.q_sizes(i)) + return false; + } + return box1.q_minimumDescent == box2.q_minimumDescent + && box1.q_minimumAscent == box2.q_minimumAscent; +} + +void QGridLayoutRowData::reset(int count) +{ + ignore.fill(false, count); + boxes.fill(QGridLayoutBox(), count); + multiCellMap.clear(); + stretches.fill(0, count); + spacings.fill(0.0, count); + hasIgnoreFlag = false; +} + +void QGridLayoutRowData::distributeMultiCells(const QGridLayoutRowInfo &rowInfo) +{ + MultiCellMap::const_iterator i = multiCellMap.constBegin(); + for (; i != multiCellMap.constEnd(); ++i) { + int start = i.key().first; + int span = i.key().second; + int end = start + span; + const QGridLayoutBox &box = i.value().q_box; + int stretch = i.value().q_stretch; + + QGridLayoutBox totalBox = this->totalBox(start, end); + QVarLengthArray<QGridLayoutBox> extras(span); + QVarLengthArray<qreal> dummy(span); + QVarLengthArray<qreal> newSizes(span); + + for (int j = 0; j < NSizes; ++j) { + qreal extra = compare(box, totalBox, j); + if (extra > 0.0) { + calculateGeometries(start, end, box.q_sizes(j), dummy.data(), newSizes.data(), + 0, totalBox, rowInfo); + + for (int k = 0; k < span; ++k) + extras[k].q_sizes(j) = newSizes[k]; + } + } + + for (int k = 0; k < span; ++k) { + boxes[start + k].combine(extras[k]); + if (stretch != 0) + stretches[start + k] = qMax(stretches[start + k], stretch); + } + } + multiCellMap.clear(); +} + +void QGridLayoutRowData::calculateGeometries(int start, int end, qreal targetSize, qreal *positions, + qreal *sizes, qreal *descents, + const QGridLayoutBox &totalBox, + const QGridLayoutRowInfo &rowInfo) +{ + Q_ASSERT(end > start); + + targetSize = qMax(totalBox.q_minimumSize, targetSize); + + int n = end - start; + QVarLengthArray<qreal> newSizes(n); + QVarLengthArray<qreal> factors(n); + qreal sumFactors = 0.0; + int sumStretches = 0; + qreal sumAvailable; + + for (int i = 0; i < n; ++i) { + if (stretches[start + i] > 0) + sumStretches += stretches[start + i]; + } + + if (targetSize < totalBox.q_preferredSize) { + stealBox(start, end, MinimumSize, positions, sizes); + + sumAvailable = targetSize - totalBox.q_minimumSize; + if (sumAvailable > 0.0) { + qreal sumDesired = totalBox.q_preferredSize - totalBox.q_minimumSize; + + for (int i = 0; i < n; ++i) { + if (ignore.testBit(start + i)) { + factors[i] = 0.0; + continue; + } + + const QGridLayoutBox &box = boxes.at(start + i); + qreal desired = box.q_preferredSize - box.q_minimumSize; + factors[i] = growthFactorBelowPreferredSize(desired, sumAvailable, sumDesired); + sumFactors += factors[i]; + } + + for (int i = 0; i < n; ++i) { + Q_ASSERT(sumFactors > 0.0); + qreal delta = sumAvailable * factors[i] / sumFactors; + newSizes[i] = sizes[i] + delta; + } + } + } else { + bool isLargerThanMaximum = (targetSize > totalBox.q_maximumSize); + if (isLargerThanMaximum) { + stealBox(start, end, MaximumSize, positions, sizes); + sumAvailable = targetSize - totalBox.q_maximumSize; + } else { + stealBox(start, end, PreferredSize, positions, sizes); + sumAvailable = targetSize - totalBox.q_preferredSize; + } + + if (sumAvailable > 0.0) { + qreal sumCurrentAvailable = sumAvailable; + bool somethingHasAMaximumSize = false; + + qreal sumSizes = 0.0; + for (int i = 0; i < n; ++i) + sumSizes += sizes[i]; + + for (int i = 0; i < n; ++i) { + if (ignore.testBit(start + i)) { + newSizes[i] = 0.0; + factors[i] = 0.0; + continue; + } + + const QGridLayoutBox &box = boxes.at(start + i); + qreal boxSize; + + qreal desired; + if (isLargerThanMaximum) { + boxSize = box.q_maximumSize; + desired = rowInfo.boxes.value(start + i).q_maximumSize - boxSize; + } else { + boxSize = box.q_preferredSize; + desired = box.q_maximumSize - boxSize; + } + if (desired == 0.0) { + newSizes[i] = sizes[i]; + factors[i] = 0.0; + } else { + Q_ASSERT(desired > 0.0); + + int stretch = stretches[start + i]; + if (sumStretches == 0) { + if (hasIgnoreFlag) { + factors[i] = (stretch < 0) ? 1.0 : 0.0; + } else { + factors[i] = (stretch < 0) ? sizes[i] : 0.0; + } + } else if (stretch == sumStretches) { + factors[i] = 1.0; + } else if (stretch <= 0) { + factors[i] = 0.0; + } else { + qreal ultimateSize; + qreal ultimateSumSizes; + qreal x = ((stretch * sumSizes) + - (sumStretches * boxSize)) + / (sumStretches - stretch); + if (x >= 0.0) { + ultimateSize = boxSize + x; + ultimateSumSizes = sumSizes + x; + } else { + ultimateSize = boxSize; + ultimateSumSizes = (sumStretches * boxSize) + / stretch; + } + + /* + We multiply these by 1.5 to give some space for a smooth transition + (at the expense of the stretch factors, which are not fully respected + during the transition). + */ + ultimateSize = ultimateSize * 3 / 2; + ultimateSumSizes = ultimateSumSizes * 3 / 2; + + qreal beta = ultimateSumSizes - sumSizes; + if (!beta) { + factors[i] = 1; + } else { + qreal alpha = qMin(sumCurrentAvailable, beta); + qreal ultimateFactor = (stretch * ultimateSumSizes / sumStretches) + - (boxSize); + qreal transitionalFactor = sumCurrentAvailable * (ultimateSize - boxSize) / beta; + + factors[i] = ((alpha * ultimateFactor) + + ((beta - alpha) * transitionalFactor)) / beta; + } + + } + sumFactors += factors[i]; + if (desired < sumCurrentAvailable) + somethingHasAMaximumSize = true; + + newSizes[i] = -1.0; + } + } + + bool keepGoing = somethingHasAMaximumSize; + while (keepGoing) { + keepGoing = false; + + for (int i = 0; i < n; ++i) { + if (newSizes[i] >= 0.0) + continue; + + qreal maxBoxSize; + if (isLargerThanMaximum) + maxBoxSize = rowInfo.boxes.value(start + i).q_maximumSize; + else + maxBoxSize = boxes.at(start + i).q_maximumSize; + + qreal avail = sumCurrentAvailable * factors[i] / sumFactors; + if (sizes[i] + avail >= maxBoxSize) { + newSizes[i] = maxBoxSize; + sumCurrentAvailable -= maxBoxSize - sizes[i]; + sumFactors -= factors[i]; + keepGoing = (sumCurrentAvailable > 0.0); + if (!keepGoing) + break; + } + } + } + + for (int i = 0; i < n; ++i) { + if (newSizes[i] < 0.0) { + qreal delta = (sumFactors == 0.0) ? 0.0 + : sumCurrentAvailable * factors[i] / sumFactors; + newSizes[i] = sizes[i] + delta; + } + } + } + } + + if (sumAvailable > 0) { + qreal offset = 0; + for (int i = 0; i < n; ++i) { + qreal delta = newSizes[i] - sizes[i]; + positions[i] += offset; + sizes[i] += delta; + offset += delta; + } + +#if 0 // some "pixel allocation" + int surplus = targetSize - (positions[n - 1] + sizes[n - 1]); + Q_ASSERT(surplus >= 0 && surplus <= n); + + int prevSurplus = -1; + while (surplus > 0 && surplus != prevSurplus) { + prevSurplus = surplus; + + int offset = 0; + for (int i = 0; i < n; ++i) { + const QGridLayoutBox &box = boxes.at(start + i); + int delta = (!ignore.testBit(start + i) && surplus > 0 + && factors[i] > 0 && sizes[i] < box.q_maximumSize) + ? 1 : 0; + + positions[i] += offset; + sizes[i] += delta; + offset += delta; + surplus -= delta; + } + } + Q_ASSERT(surplus == 0); +#endif + } + + if (descents) { + for (int i = 0; i < n; ++i) { + if (ignore.testBit(start + i)) + continue; + const QGridLayoutBox &box = boxes.at(start + i); + descents[i] = fixedDescent(box.q_minimumDescent, box.q_minimumAscent, sizes[i]); + } + } +} + +QGridLayoutBox QGridLayoutRowData::totalBox(int start, int end) const +{ + QGridLayoutBox result; + if (start < end) { + result.q_maximumSize = 0.0; + qreal nextSpacing = 0.0; + for (int i = start; i < end; ++i) { + result.add(boxes.at(i), stretches.at(i), nextSpacing); + nextSpacing = spacings.at(i); + } + } + return result; +} + +void QGridLayoutRowData::stealBox(int start, int end, int which, qreal *positions, qreal *sizes) +{ + qreal offset = 0.0; + qreal nextSpacing = 0.0; + + for (int i = start; i < end; ++i) { + qreal avail = 0.0; + + if (!ignore.testBit(i)) { + const QGridLayoutBox &box = boxes.at(i); + avail = box.q_sizes(which); + offset += nextSpacing; + nextSpacing = spacings.at(i); + } + + *positions++ = offset; + *sizes++ = avail; + offset += avail; + } +} + +#ifdef QT_DEBUG +void QGridLayoutRowData::dump(int indent) const +{ + qDebug("%*sData", indent, ""); + + for (int i = 0; i < ignore.count(); ++i) { + qDebug("%*s Row %d (stretch %d, spacing %g)", indent, "", i, stretches.at(i), + spacings.at(i)); + if (ignore.testBit(i)) + qDebug("%*s Ignored", indent, ""); + boxes.at(i).dump(indent + 2); + } + + MultiCellMap::const_iterator it = multiCellMap.constBegin(); + while (it != multiCellMap.constEnd()) { + qDebug("%*s Multi-cell entry <%d, %d> (stretch %d)", indent, "", it.key().first, + it.key().second, it.value().q_stretch); + it.value().q_box.dump(indent + 2); + } +} +#endif + +QGridLayoutItem::QGridLayoutItem(/*QGridLayoutEngine *engine, */ + int row, int column, int rowSpan, int columnSpan, + Qt::Alignment alignment) + : /*###q_engine(engine), q_layoutItem(layoutItem), */q_alignment(alignment) +{ + q_firstRows[Hor] = column; + q_firstRows[Ver] = row; + q_rowSpans[Hor] = columnSpan; + q_rowSpans[Ver] = rowSpan; + q_stretches[Hor] = -1; + q_stretches[Ver] = -1; + + //q_engine->insertItem(this, itemAtIndex);### +} + +int QGridLayoutItem::firstRow(Qt::Orientation orientation) const +{ + return q_firstRows[orientation == Qt::Vertical]; +} + +int QGridLayoutItem::firstColumn(Qt::Orientation orientation) const +{ + return q_firstRows[orientation == Qt::Horizontal]; +} + +int QGridLayoutItem::lastRow(Qt::Orientation orientation) const +{ + return firstRow(orientation) + rowSpan(orientation) - 1; +} + +int QGridLayoutItem::lastColumn(Qt::Orientation orientation) const +{ + return firstColumn(orientation) + columnSpan(orientation) - 1; +} + +int QGridLayoutItem::rowSpan(Qt::Orientation orientation) const +{ + return q_rowSpans[orientation == Qt::Vertical]; +} + +int QGridLayoutItem::columnSpan(Qt::Orientation orientation) const +{ + return q_rowSpans[orientation == Qt::Horizontal]; +} + +void QGridLayoutItem::setFirstRow(int row, Qt::Orientation orientation) +{ + q_firstRows[orientation == Qt::Vertical] = row; +} + +void QGridLayoutItem::setRowSpan(int rowSpan, Qt::Orientation orientation) +{ + q_rowSpans[orientation == Qt::Vertical] = rowSpan; +} + +int QGridLayoutItem::stretchFactor(Qt::Orientation orientation) const +{ + int stretch = q_stretches[orientation == Qt::Vertical]; + if (stretch >= 0) + return stretch; + + QLayoutPolicy::Policy policy = sizePolicy(orientation); + + if (policy & QLayoutPolicy::ExpandFlag) { + return 1; + } else if (policy & QLayoutPolicy::GrowFlag) { + return -1; // because we max it up + } else { + return 0; + } +} + +void QGridLayoutItem::setStretchFactor(int stretch, Qt::Orientation orientation) +{ + Q_ASSERT(stretch >= 0); // ### deal with too big stretches + q_stretches[orientation == Qt::Vertical] = stretch; +} + +QLayoutPolicy::ControlTypes QGridLayoutItem::controlTypes(LayoutSide /*side*/) const +{ + return QLayoutPolicy::DefaultType; //### +} + +QGridLayoutBox QGridLayoutItem::box(Qt::Orientation orientation, qreal constraint) const +{ + QGridLayoutBox result; + QLayoutPolicy::Policy policy = sizePolicy(orientation); + + if (orientation == Qt::Horizontal) { + QSizeF constraintSize(-1.0, constraint); + + result.q_preferredSize = sizeHint(Qt::PreferredSize, constraintSize).width(); + + if (policy & QLayoutPolicy::ShrinkFlag) { + result.q_minimumSize = sizeHint(Qt::MinimumSize, constraintSize).width(); + } else { + result.q_minimumSize = result.q_preferredSize; + } + + if (policy & (QLayoutPolicy::GrowFlag | QLayoutPolicy::ExpandFlag)) { + result.q_maximumSize = sizeHint(Qt::MaximumSize, constraintSize).width(); + } else { + result.q_maximumSize = result.q_preferredSize; + } + } else { + QSizeF constraintSize(constraint, -1.0); + + result.q_preferredSize = sizeHint(Qt::PreferredSize, constraintSize).height(); + + if (policy & QLayoutPolicy::ShrinkFlag) { + result.q_minimumSize = sizeHint(Qt::MinimumSize, constraintSize).height(); + } else { + result.q_minimumSize = result.q_preferredSize; + } + + if (policy & (QLayoutPolicy::GrowFlag | QLayoutPolicy::ExpandFlag)) { + result.q_maximumSize = sizeHint(Qt::MaximumSize, constraintSize).height(); + } else { + result.q_maximumSize = result.q_preferredSize; + } + + result.q_minimumDescent = sizeHint(Qt::MinimumDescent, constraintSize).height(); + if (result.q_minimumDescent >= 0.0) + result.q_minimumAscent = result.q_minimumSize - result.q_minimumDescent; + } + if (policy & QLayoutPolicy::IgnoreFlag) + result.q_preferredSize = result.q_minimumSize; + + return result; +} + +QRectF QGridLayoutItem::geometryWithin(qreal x, qreal y, qreal width, qreal height, + qreal rowDescent, Qt::Alignment align) const +{ + rowDescent = -1.0; // ### This disables the descent + + QGridLayoutBox vBox = box(Qt::Vertical); + if (vBox.q_minimumDescent < 0.0 || rowDescent < 0.0) { + qreal cellWidth = width; + qreal cellHeight = height; + + + QSizeF size = effectiveMaxSize(QSizeF(-1,-1)); + if (hasDynamicConstraint()) { + if (dynamicConstraintOrientation() == Qt::Vertical) { + if (size.width() > cellWidth) + size = effectiveMaxSize(QSizeF(cellWidth, -1)); + } else if (size.height() > cellHeight) { + size = effectiveMaxSize(QSizeF(-1, cellHeight)); + } + } + size = size.boundedTo(QSizeF(cellWidth, cellHeight)); + width = size.width(); + height = size.height(); + + switch (align & Qt::AlignHorizontal_Mask) { + case Qt::AlignHCenter: + x += (cellWidth - width)/2; + break; + case Qt::AlignRight: + x += cellWidth - width; + break; + default: + break; + } + switch (align & Qt::AlignVertical_Mask) { + case Qt::AlignVCenter: + y += (cellHeight - height)/2; + break; + case Qt::AlignBottom: + y += cellHeight - height; + break; + default: + break; + } + return QRectF(x, y, width, height); + } else { + qreal descent = vBox.q_minimumDescent; + qreal ascent = vBox.q_minimumSize - descent; + return QRectF(x, y + height - rowDescent - ascent, width, ascent + descent); + } +} + +void QGridLayoutItem::transpose() +{ + qSwap(q_firstRows[Hor], q_firstRows[Ver]); + qSwap(q_rowSpans[Hor], q_rowSpans[Ver]); + qSwap(q_stretches[Hor], q_stretches[Ver]); +} + +void QGridLayoutItem::insertOrRemoveRows(int row, int delta, Qt::Orientation orientation) +{ + int oldFirstRow = firstRow(orientation); + if (oldFirstRow >= row) { + setFirstRow(oldFirstRow + delta, orientation); + } else if (lastRow(orientation) >= row) { + setRowSpan(rowSpan(orientation) + delta, orientation); + } +} +/*! + \internal + returns the effective maximumSize, will take the sizepolicy into + consideration. (i.e. if sizepolicy does not have QLayoutPolicy::Grow, then + maxSizeHint will be the preferredSize) + Note that effectiveSizeHint does not take sizePolicy into consideration, + (since it only evaluates the hints, as the name implies) +*/ +QSizeF QGridLayoutItem::effectiveMaxSize(const QSizeF &constraint) const +{ + QSizeF size = constraint; + bool vGrow = (sizePolicy(Qt::Vertical) & QLayoutPolicy::GrowFlag) == QLayoutPolicy::GrowFlag; + bool hGrow = (sizePolicy(Qt::Horizontal) & QLayoutPolicy::GrowFlag) == QLayoutPolicy::GrowFlag; + if (!vGrow || !hGrow) { + QSizeF pref = sizeHint(Qt::PreferredSize, constraint); + if (!vGrow) + size.setHeight(pref.height()); + if (!hGrow) + size.setWidth(pref.width()); + } + + if (!size.isValid()) { + QSizeF maxSize = sizeHint(Qt::MaximumSize, size); + if (size.width() == -1) + size.setWidth(maxSize.width()); + if (size.height() == -1) + size.setHeight(maxSize.height()); + } + return size; +} + +#ifdef QT_DEBUG +void QGridLayoutItem::dump(int indent) const +{ + qDebug("%*s (%d, %d) %d x %d", indent, "", firstRow(), firstColumn(), //### + rowSpan(), columnSpan()); + + if (q_stretches[Hor] >= 0) + qDebug("%*s Horizontal stretch: %d", indent, "", q_stretches[Hor]); + if (q_stretches[Ver] >= 0) + qDebug("%*s Vertical stretch: %d", indent, "", q_stretches[Ver]); + if (q_alignment != 0) + qDebug("%*s Alignment: %x", indent, "", uint(q_alignment)); + qDebug("%*s Horizontal size policy: %x Vertical size policy: %x", + indent, "", sizePolicy(Qt::Horizontal), sizePolicy(Qt::Vertical)); +} +#endif + +void QGridLayoutRowInfo::insertOrRemoveRows(int row, int delta) +{ + count += delta; + + insertOrRemoveItems(stretches, row, delta); + insertOrRemoveItems(spacings, row, delta); + insertOrRemoveItems(alignments, row, delta); + insertOrRemoveItems(boxes, row, delta); +} + +#ifdef QT_DEBUG +void QGridLayoutRowInfo::dump(int indent) const +{ + qDebug("%*sInfo (count: %d)", indent, "", count); + for (int i = 0; i < count; ++i) { + QString message; + + if (stretches.value(i).value() >= 0) + message += QString::fromLatin1(" stretch %1").arg(stretches.value(i).value()); + if (spacings.value(i).value() >= 0.0) + message += QString::fromLatin1(" spacing %1").arg(spacings.value(i).value()); + if (alignments.value(i) != 0) + message += QString::fromLatin1(" alignment %1").arg(int(alignments.value(i)), 16); + + if (!message.isEmpty() || boxes.value(i) != QGridLayoutBox()) { + qDebug("%*s Row %d:%s", indent, "", i, qPrintable(message)); + if (boxes.value(i) != QGridLayoutBox()) + boxes.value(i).dump(indent + 1); + } + } +} +#endif + +QGridLayoutEngine::QGridLayoutEngine() +{ + m_visualDirection = Qt::LeftToRight; + invalidate(); +} + +int QGridLayoutEngine::rowCount(Qt::Orientation orientation) const +{ + return q_infos[orientation == Qt::Vertical].count; +} + +int QGridLayoutEngine::columnCount(Qt::Orientation orientation) const +{ + return q_infos[orientation == Qt::Horizontal].count; +} + +int QGridLayoutEngine::itemCount() const +{ + return q_items.count(); +} + +QGridLayoutItem *QGridLayoutEngine::itemAt(int index) const +{ + Q_ASSERT(index >= 0 && index < itemCount()); + return q_items.at(index); +} + +int QGridLayoutEngine::effectiveFirstRow(Qt::Orientation orientation) const +{ + ensureEffectiveFirstAndLastRows(); + return q_cachedEffectiveFirstRows[orientation == Qt::Vertical]; +} + +int QGridLayoutEngine::effectiveLastRow(Qt::Orientation orientation) const +{ + ensureEffectiveFirstAndLastRows(); + return q_cachedEffectiveLastRows[orientation == Qt::Vertical]; +} + +void QGridLayoutEngine::setSpacing(qreal spacing, Qt::Orientations orientations) +{ + Q_ASSERT(spacing >= 0.0); + if (orientations & Qt::Horizontal) + q_defaultSpacings[Hor].setUserValue(spacing); + if (orientations & Qt::Vertical) + q_defaultSpacings[Ver].setUserValue(spacing); + + invalidate(); +} + +qreal QGridLayoutEngine::spacing(Qt::Orientation orientation, const QLayoutStyleInfo &/*styleInfo*/) const +{ + if (q_defaultSpacings[orientation == Qt::Vertical].isDefault()) { + /* ### + if (QStyle *style = styleInfo.style()) { + QStyleOption option; + option.initFrom(styleInfo.widget()); + qreal defaultSpacing = (qreal)style->pixelMetric(orientation == Qt::Vertical ? QStyle::PM_LayoutVerticalSpacing + : QStyle::PM_LayoutHorizontalSpacing, &option, styleInfo.widget()); + q_defaultSpacings[orientation == Qt::Vertical].setCachedValue(defaultSpacing); + } + */ + } + return q_defaultSpacings[orientation == Qt::Vertical].value(); +} + +void QGridLayoutEngine::setRowSpacing(int row, qreal spacing, Qt::Orientation orientation) +{ + Q_ASSERT(row >= 0); + + QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical]; + if (row >= rowInfo.spacings.count()) + rowInfo.spacings.resize(row + 1); + if (spacing >= 0) + rowInfo.spacings[row].setUserValue(spacing); + else + rowInfo.spacings[row] = QLayoutParameter<qreal>(); + invalidate(); +} + +qreal QGridLayoutEngine::rowSpacing(int row, Qt::Orientation orientation) const +{ + QLayoutParameter<qreal> spacing = q_infos[orientation == Qt::Vertical].spacings.value(row); + if (!spacing.isDefault()) + return spacing.value(); + return q_defaultSpacings[orientation == Qt::Vertical].value(); +} + +void QGridLayoutEngine::setRowStretchFactor(int row, int stretch, Qt::Orientation orientation) +{ + Q_ASSERT(row >= 0); + Q_ASSERT(stretch >= 0); + + maybeExpandGrid(row, -1, orientation); + + QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical]; + if (row >= rowInfo.stretches.count()) + rowInfo.stretches.resize(row + 1); + rowInfo.stretches[row].setUserValue(stretch); +} + +int QGridLayoutEngine::rowStretchFactor(int row, Qt::Orientation orientation) const +{ + QStretchParameter stretch = q_infos[orientation == Qt::Vertical].stretches.value(row); + if (!stretch.isDefault()) + return stretch.value(); + return 0; +} +/* +void QGridLayoutEngine::setStretchFactor(QGraphicsLayoutItem *layoutItem, int stretch, + Qt::Orientation orientation) +{ + Q_ASSERT(stretch >= 0); + + if (QGridLayoutItem *item = findLayoutItem(layoutItem)) + item->setStretchFactor(stretch, orientation); +} + +int QGridLayoutEngine::stretchFactor(QGraphicsLayoutItem *layoutItem, Qt::Orientation orientation) const +{ + if (QGridLayoutItem *item = findLayoutItem(layoutItem)) + return item->stretchFactor(orientation); + return 0; +} +*/ +void QGridLayoutEngine::setRowSizeHint(Qt::SizeHint which, int row, qreal size, + Qt::Orientation orientation) +{ + Q_ASSERT(row >= 0); + Q_ASSERT(size >= 0.0); + + maybeExpandGrid(row, -1, orientation); + + QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical]; + if (row >= rowInfo.boxes.count()) + rowInfo.boxes.resize(row + 1); + rowInfo.boxes[row].q_sizes(which) = size; +} + +qreal QGridLayoutEngine::rowSizeHint(Qt::SizeHint which, int row, Qt::Orientation orientation) const +{ + return q_infos[orientation == Qt::Vertical].boxes.value(row).q_sizes(which); +} + +void QGridLayoutEngine::setRowAlignment(int row, Qt::Alignment alignment, + Qt::Orientation orientation) +{ + Q_ASSERT(row >= 0); + + maybeExpandGrid(row, -1, orientation); + + QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical]; + if (row >= rowInfo.alignments.count()) + rowInfo.alignments.resize(row + 1); + rowInfo.alignments[row] = alignment; +} + +Qt::Alignment QGridLayoutEngine::rowAlignment(int row, Qt::Orientation orientation) const +{ + Q_ASSERT(row >= 0); + return q_infos[orientation == Qt::Vertical].alignments.value(row); +} + +Qt::Alignment QGridLayoutEngine::effectiveAlignment(const QGridLayoutItem *layoutItem) const +{ + Qt::Alignment align = layoutItem->alignment(); + if (!(align & Qt::AlignVertical_Mask)) { + // no vertical alignment, respect the row alignment + int y = layoutItem->firstRow(); + align |= (rowAlignment(y, Qt::Vertical) & Qt::AlignVertical_Mask); + if (!align) + align = Qt::AlignVCenter; + } + if (!(align & Qt::AlignHorizontal_Mask)) { + // no horizontal alignment, respect the column alignment + int x = layoutItem->firstColumn(); + align |= (rowAlignment(x, Qt::Horizontal) & Qt::AlignHorizontal_Mask); + if (!align) + align = Qt::AlignHCenter; + } + return align; +} + +/*! + \internal + The \a index is only used by QGraphicsLinearLayout to ensure that itemAt() reflects the order + of visual arrangement. Strictly speaking it does not have to, but most people expect it to. + (And if it didn't we would have to add itemArrangedAt(int index) or something..) + */ +void QGridLayoutEngine::insertItem(QGridLayoutItem *item, int index) +{ + maybeExpandGrid(item->lastRow(), item->lastColumn()); + + if (index == -1) + q_items.append(item); + else + q_items.insert(index, item); + + for (int i = item->firstRow(); i <= item->lastRow(); ++i) { + for (int j = item->firstColumn(); j <= item->lastColumn(); ++j) { + if (itemAt(i, j)) + qWarning("QGridLayoutEngine::addItem: Cell (%d, %d) already taken", i, j); + setItemAt(i, j, item); + } + } +} + +void QGridLayoutEngine::addItem(QGridLayoutItem *item) +{ + insertItem(item, -1); +} + +void QGridLayoutEngine::removeItem(QGridLayoutItem *item) +{ + Q_ASSERT(q_items.contains(item)); + + invalidate(); + + for (int i = item->firstRow(); i <= item->lastRow(); ++i) { + for (int j = item->firstColumn(); j <= item->lastColumn(); ++j) { + if (itemAt(i, j) == item) + setItemAt(i, j, 0); + } + } + + q_items.removeAll(item); +} + + +QGridLayoutItem *QGridLayoutEngine::itemAt(int row, int column, Qt::Orientation orientation) const +{ + if (orientation == Qt::Horizontal) + qSwap(row, column); + if (uint(row) >= uint(rowCount()) || uint(column) >= uint(columnCount())) + return 0; + return q_grid.at((row * internalGridColumnCount()) + column); +} + +void QGridLayoutEngine::invalidate() +{ + q_cachedEffectiveFirstRows[Hor] = -1; + q_cachedEffectiveFirstRows[Ver] = -1; + q_cachedEffectiveLastRows[Hor] = -1; + q_cachedEffectiveLastRows[Ver] = -1; + q_cachedDataForStyleInfo.invalidate(); + q_cachedSize = QSizeF(); + q_cachedConstraintOrientation = UnknownConstraint; +} + +static void visualRect(QRectF *geom, Qt::LayoutDirection dir, const QRectF &contentsRect) +{ + if (dir == Qt::RightToLeft) + geom->moveRight(contentsRect.right() - (geom->left() - contentsRect.left())); +} + +void QGridLayoutEngine::setGeometries(const QRectF &contentsGeometry, const QLayoutStyleInfo &styleInfo) +{ + if (rowCount() < 1 || columnCount() < 1) + return; + + ensureGeometries(contentsGeometry.size(), styleInfo); + + for (int i = q_items.count() - 1; i >= 0; --i) { + QGridLayoutItem *item = q_items.at(i); + + qreal x = q_xx[item->firstColumn()]; + qreal y = q_yy[item->firstRow()]; + qreal width = q_widths[item->lastColumn()]; + qreal height = q_heights[item->lastRow()]; + + if (item->columnSpan() != 1) + width += q_xx[item->lastColumn()] - x; + if (item->rowSpan() != 1) + height += q_yy[item->lastRow()] - y; + + QRectF geom = item->geometryWithin(contentsGeometry.x() + x, contentsGeometry.y() + y, + width, height, q_descents[item->lastRow()], effectiveAlignment(item)); + visualRect(&geom, visualDirection(), contentsGeometry); + item->setGeometry(geom); + } +} + +// ### candidate for deletion +QRectF QGridLayoutEngine::cellRect(const QRectF &contentsGeometry, int row, int column, int rowSpan, + int columnSpan, const QLayoutStyleInfo &styleInfo) const +{ + if (uint(row) >= uint(rowCount()) || uint(column) >= uint(columnCount()) + || rowSpan < 1 || columnSpan < 1) + return QRectF(); + + ensureGeometries(contentsGeometry.size(), styleInfo); + + int lastColumn = qMax(column + columnSpan, columnCount()) - 1; + int lastRow = qMax(row + rowSpan, rowCount()) - 1; + + qreal x = q_xx[column]; + qreal y = q_yy[row]; + qreal width = q_widths[lastColumn]; + qreal height = q_heights[lastRow]; + + if (columnSpan != 1) + width += q_xx[lastColumn] - x; + if (rowSpan != 1) + height += q_yy[lastRow] - y; + + return QRectF(contentsGeometry.x() + x, contentsGeometry.y() + y, width, height); +} + +QSizeF QGridLayoutEngine::sizeHint(Qt::SizeHint which, const QSizeF &constraint, + const QLayoutStyleInfo &styleInfo) const +{ + QGridLayoutBox sizehint_totalBoxes[NOrientations]; + + bool sizeHintCalculated = false; + + if (hasDynamicConstraint() && rowCount() > 0 && columnCount() > 0) { + if (constraintOrientation() == Qt::Vertical) { + //We have items whose height depends on their width + if (constraint.width() >= 0) { + if (q_cachedDataForStyleInfo != styleInfo) + ensureColumnAndRowData(&q_columnData, &sizehint_totalBoxes[Hor], NULL, NULL, Qt::Horizontal, styleInfo); + else + sizehint_totalBoxes[Hor] = q_totalBoxes[Hor]; + QVector<qreal> sizehint_xx; + QVector<qreal> sizehint_widths; + + sizehint_xx.resize(columnCount()); + sizehint_widths.resize(columnCount()); + qreal width = constraint.width(); + //Calculate column widths and positions, and put results in q_xx.data() and q_widths.data() so that we can use this information as + //constraints to find the row heights + q_columnData.calculateGeometries(0, columnCount(), width, sizehint_xx.data(), sizehint_widths.data(), + 0, sizehint_totalBoxes[Hor], q_infos[Hor]); + ensureColumnAndRowData(&q_rowData, &sizehint_totalBoxes[Ver], sizehint_xx.data(), sizehint_widths.data(), Qt::Vertical, styleInfo); + sizeHintCalculated = true; + } + } else { + if (constraint.height() >= 0) { + //We have items whose width depends on their height + ensureColumnAndRowData(&q_rowData, &sizehint_totalBoxes[Ver], NULL, NULL, Qt::Vertical, styleInfo); + QVector<qreal> sizehint_yy; + QVector<qreal> sizehint_heights; + + sizehint_yy.resize(rowCount()); + sizehint_heights.resize(rowCount()); + qreal height = constraint.height(); + //Calculate row heights and positions, and put results in q_yy.data() and q_heights.data() so that we can use this information as + //constraints to find the column widths + q_rowData.calculateGeometries(0, rowCount(), height, sizehint_yy.data(), sizehint_heights.data(), + 0, sizehint_totalBoxes[Ver], q_infos[Ver]); + ensureColumnAndRowData(&q_columnData, &sizehint_totalBoxes[Hor], sizehint_yy.data(), sizehint_heights.data(), Qt::Horizontal, styleInfo); + sizeHintCalculated = true; + } + } + } + + if (!sizeHintCalculated) { + //No items with height for width, so it doesn't matter which order we do these in + if (q_cachedDataForStyleInfo != styleInfo) { + ensureColumnAndRowData(&q_columnData, &sizehint_totalBoxes[Hor], NULL, NULL, Qt::Horizontal, styleInfo); + ensureColumnAndRowData(&q_rowData, &sizehint_totalBoxes[Ver], NULL, NULL, Qt::Vertical, styleInfo); + } else { + sizehint_totalBoxes[Hor] = q_totalBoxes[Hor]; + sizehint_totalBoxes[Ver] = q_totalBoxes[Ver]; + } + } + + switch (which) { + case Qt::MinimumSize: + return QSizeF(sizehint_totalBoxes[Hor].q_minimumSize, sizehint_totalBoxes[Ver].q_minimumSize); + case Qt::PreferredSize: + return QSizeF(sizehint_totalBoxes[Hor].q_preferredSize, sizehint_totalBoxes[Ver].q_preferredSize); + case Qt::MaximumSize: + return QSizeF(sizehint_totalBoxes[Hor].q_maximumSize, sizehint_totalBoxes[Ver].q_maximumSize); + case Qt::MinimumDescent: + return QSizeF(-1.0, sizehint_totalBoxes[Hor].q_minimumDescent); // ### doesn't work + default: + break; + } + return QSizeF(); +} + +QLayoutPolicy::ControlTypes QGridLayoutEngine::controlTypes(LayoutSide side) const +{ + Qt::Orientation orientation = (side == Top || side == Bottom) ? Qt::Vertical : Qt::Horizontal; + int row = (side == Top || side == Left) ? effectiveFirstRow(orientation) + : effectiveLastRow(orientation); + QLayoutPolicy::ControlTypes result = 0; + + for (int column = columnCount(orientation) - 1; column >= 0; --column) { + if (QGridLayoutItem *item = itemAt(row, column, orientation)) + result |= item->controlTypes(side); + } + return result; +} + +void QGridLayoutEngine::transpose() +{ + invalidate(); + + for (int i = q_items.count() - 1; i >= 0; --i) + q_items.at(i)->transpose(); + + qSwap(q_defaultSpacings[Hor], q_defaultSpacings[Ver]); + qSwap(q_infos[Hor], q_infos[Ver]); + + regenerateGrid(); +} + +void QGridLayoutEngine::setVisualDirection(Qt::LayoutDirection direction) +{ + m_visualDirection = direction; +} + +Qt::LayoutDirection QGridLayoutEngine::visualDirection() const +{ + return m_visualDirection; +} + +#ifdef QT_DEBUG +void QGridLayoutEngine::dump(int indent) const +{ + qDebug("%*sEngine", indent, ""); + + qDebug("%*s Items (%d)", indent, "", q_items.count()); + int i; + for (i = 0; i < q_items.count(); ++i) + q_items.at(i)->dump(indent + 2); + + qDebug("%*s Grid (%d x %d)", indent, "", internalGridRowCount(), + internalGridColumnCount()); + for (int row = 0; row < internalGridRowCount(); ++row) { + QString message = QLatin1String("[ "); + for (int column = 0; column < internalGridColumnCount(); ++column) { + message += QString::number(q_items.indexOf(itemAt(row, column))).rightJustified(3); + message += QLatin1Char(' '); + } + message += QLatin1Char(']'); + qDebug("%*s %s", indent, "", qPrintable(message)); + } + + if (q_defaultSpacings[Hor].value() >= 0.0 || q_defaultSpacings[Ver].value() >= 0.0) + qDebug("%*s Default spacings: %g %g", indent, "", q_defaultSpacings[Hor].value(), + q_defaultSpacings[Ver].value()); + + qDebug("%*s Column and row info", indent, ""); + q_infos[Hor].dump(indent + 2); + q_infos[Ver].dump(indent + 2); + + qDebug("%*s Column and row data", indent, ""); + q_columnData.dump(indent + 2); + q_rowData.dump(indent + 2); + + qDebug("%*s Geometries output", indent, ""); + QVector<qreal> *cellPos = &q_yy; + for (int pass = 0; pass < 2; ++pass) { + QString message; + for (i = 0; i < cellPos->count(); ++i) { + message += QLatin1String((message.isEmpty() ? "[" : ", ")); + message += QString::number(cellPos->at(i)); + } + message += QLatin1Char(']'); + qDebug("%*s %s %s", indent, "", (pass == 0 ? "rows:" : "columns:"), qPrintable(message)); + cellPos = &q_xx; + } +} +#endif + +void QGridLayoutEngine::maybeExpandGrid(int row, int column, Qt::Orientation orientation) +{ + invalidate(); // ### move out of here? + + if (orientation == Qt::Horizontal) + qSwap(row, column); + + if (row < rowCount() && column < columnCount()) + return; + + int oldGridRowCount = internalGridRowCount(); + int oldGridColumnCount = internalGridColumnCount(); + + q_infos[Ver].count = qMax(row + 1, rowCount()); + q_infos[Hor].count = qMax(column + 1, columnCount()); + + int newGridRowCount = internalGridRowCount(); + int newGridColumnCount = internalGridColumnCount(); + + int newGridSize = newGridRowCount * newGridColumnCount; + if (newGridSize != q_grid.count()) { + q_grid.resize(newGridSize); + + if (newGridColumnCount != oldGridColumnCount) { + for (int i = oldGridRowCount - 1; i >= 1; --i) { + for (int j = oldGridColumnCount - 1; j >= 0; --j) { + int oldIndex = (i * oldGridColumnCount) + j; + int newIndex = (i * newGridColumnCount) + j; + + Q_ASSERT(newIndex > oldIndex); + q_grid[newIndex] = q_grid[oldIndex]; + q_grid[oldIndex] = 0; + } + } + } + } +} + +void QGridLayoutEngine::regenerateGrid() +{ + q_grid.fill(0); + + for (int i = q_items.count() - 1; i >= 0; --i) { + QGridLayoutItem *item = q_items.at(i); + + for (int j = item->firstRow(); j <= item->lastRow(); ++j) { + for (int k = item->firstColumn(); k <= item->lastColumn(); ++k) { + setItemAt(j, k, item); + } + } + } +} + +void QGridLayoutEngine::setItemAt(int row, int column, QGridLayoutItem *item) +{ + Q_ASSERT(row >= 0 && row < rowCount()); + Q_ASSERT(column >= 0 && column < columnCount()); + q_grid[(row * internalGridColumnCount()) + column] = item; +} + +void QGridLayoutEngine::insertOrRemoveRows(int row, int delta, Qt::Orientation orientation) +{ + int oldRowCount = rowCount(orientation); + Q_ASSERT(uint(row) <= uint(oldRowCount)); + + invalidate(); + + // appending rows (or columns) is easy + if (row == oldRowCount && delta > 0) { + maybeExpandGrid(oldRowCount + delta - 1, -1, orientation); + return; + } + + q_infos[orientation == Qt::Vertical].insertOrRemoveRows(row, delta); + + for (int i = q_items.count() - 1; i >= 0; --i) + q_items.at(i)->insertOrRemoveRows(row, delta, orientation); + + q_grid.resize(internalGridRowCount() * internalGridColumnCount()); + regenerateGrid(); +} + +void QGridLayoutEngine::fillRowData(QGridLayoutRowData *rowData, + qreal *colPositions, qreal *colSizes, + Qt::Orientation orientation, + const QLayoutStyleInfo &styleInfo) const +{ + const int ButtonMask = QLayoutPolicy::ButtonBox | QLayoutPolicy::PushButton; + const QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical]; + const QGridLayoutRowInfo &columnInfo = q_infos[orientation == Qt::Horizontal]; + LayoutSide top = (orientation == Qt::Vertical) ? Top : Left; + LayoutSide bottom = (orientation == Qt::Vertical) ? Bottom : Right; + + //QStyle *style = styleInfo.style(); + /*### + QStyleOption option; + option.initFrom(styleInfo.widget()); + */ + const QLayoutParameter<qreal> &defaultSpacing = q_defaultSpacings[orientation == Qt::Vertical]; + qreal innerSpacing = 0.0; + /* ### + if (style) + innerSpacing = (qreal)style->pixelMetric(orientation == Qt::Vertical ? QStyle::PM_LayoutVerticalSpacing + : QStyle::PM_LayoutHorizontalSpacing, + &option, styleInfo.widget()); + */ + if (innerSpacing >= 0.0) + defaultSpacing.setCachedValue(innerSpacing); + + for (int row = 0; row < rowInfo.count; ++row) { + bool rowIsEmpty = true; + bool rowIsIdenticalToPrevious = (row > 0); + + for (int column = 0; column < columnInfo.count; ++column) { + QGridLayoutItem *item = itemAt(row, column, orientation); + + if (rowIsIdenticalToPrevious && item != itemAt(row - 1, column, orientation)) + rowIsIdenticalToPrevious = false; + + if (item) + rowIsEmpty = false; + } + + if ((rowIsEmpty || rowIsIdenticalToPrevious) + && rowInfo.spacings.value(row).isDefault() + && rowInfo.stretches.value(row).isDefault() + && rowInfo.boxes.value(row) == QGridLayoutBox()) + rowData->ignore.setBit(row, true); + + if (rowInfo.spacings.value(row).isUser()) { + rowData->spacings[row] = rowInfo.spacings.at(row).value(); + } else if (!defaultSpacing.isDefault()) { + rowData->spacings[row] = defaultSpacing.value(); + } + + rowData->stretches[row] = rowInfo.stretches.value(row).value(); + } + + struct RowAdHocData { + int q_row; + unsigned int q_hasButtons : 8; + unsigned int q_hasNonButtons : 8; + + inline RowAdHocData() : q_row(-1), q_hasButtons(false), q_hasNonButtons(false) {} + inline void init(int row) { + this->q_row = row; + q_hasButtons = false; + q_hasNonButtons = false; + } + inline bool hasOnlyButtons() const { return q_hasButtons && !q_hasNonButtons; } + inline bool hasOnlyNonButtons() const { return q_hasNonButtons && !q_hasButtons; } + }; + RowAdHocData lastRowAdHocData; + RowAdHocData nextToLastRowAdHocData; + RowAdHocData nextToNextToLastRowAdHocData; + + rowData->hasIgnoreFlag = false; + for (int row = 0; row < rowInfo.count; ++row) { + if (rowData->ignore.testBit(row)) + continue; + + QGridLayoutBox &rowBox = rowData->boxes[row]; + if (styleInfo.isWindow()) { + nextToNextToLastRowAdHocData = nextToLastRowAdHocData; + nextToLastRowAdHocData = lastRowAdHocData; + lastRowAdHocData.init(row); + } + + bool userRowStretch = rowInfo.stretches.value(row).isUser(); + int &rowStretch = rowData->stretches[row]; + + bool hasIgnoreFlag = true; + for (int column = 0; column < columnInfo.count; ++column) { + QGridLayoutItem *item = itemAt(row, column, orientation); + if (item) { + int itemRow = item->firstRow(orientation); + int itemColumn = item->firstColumn(orientation); + + if (itemRow == row && itemColumn == column) { + int itemStretch = item->stretchFactor(orientation); + if (!(item->sizePolicy(orientation) & QLayoutPolicy::IgnoreFlag)) + hasIgnoreFlag = false; + int itemRowSpan = item->rowSpan(orientation); + + int effectiveRowSpan = 1; + for (int i = 1; i < itemRowSpan; ++i) { + if (!rowData->ignore.testBit(i)) + ++effectiveRowSpan; + } + + QGridLayoutBox *box; + if (effectiveRowSpan == 1) { + box = &rowBox; + if (!userRowStretch && itemStretch != 0) + rowStretch = qMax(rowStretch, itemStretch); + } else { + QGridLayoutMultiCellData &multiCell = + rowData->multiCellMap[qMakePair(row, effectiveRowSpan)]; + box = &multiCell.q_box; + multiCell.q_stretch = itemStretch; + } + // Items with constraints need to be passed the constraint + if (colSizes && colPositions && item->hasDynamicConstraint() && orientation == item->dynamicConstraintOrientation()) { + /* Get the width of the item by summing up the widths of the columns that it spans. + * We need to have already calculated the widths of the columns by calling + * q_columns->calculateGeometries() before hand and passing the value in the colSizes + * and colPositions parameters. + * The variable name is still colSizes even when it actually has the row sizes + */ + qreal length = colSizes[item->lastColumn(orientation)]; + if (item->columnSpan(orientation) != 1) + length += colPositions[item->lastColumn(orientation)] - colPositions[item->firstColumn(orientation)]; + box->combine(item->box(orientation, length)); + } else { + box->combine(item->box(orientation)); + } + + if (effectiveRowSpan == 1) { + QLayoutPolicy::ControlTypes controls = item->controlTypes(top); + if (controls & ButtonMask) + lastRowAdHocData.q_hasButtons = true; + if (controls & ~ButtonMask) + lastRowAdHocData.q_hasNonButtons = true; + } + } + } + } + if (row < rowInfo.boxes.count()) { + QGridLayoutBox rowBoxInfo = rowInfo.boxes.at(row); + rowBoxInfo.normalize(); + rowBox.q_minimumSize = qMax(rowBox.q_minimumSize, rowBoxInfo.q_minimumSize); + rowBox.q_maximumSize = qMax(rowBox.q_minimumSize, + (rowBoxInfo.q_maximumSize != FLT_MAX ? + rowBoxInfo.q_maximumSize : rowBox.q_maximumSize)); + rowBox.q_preferredSize = qBound(rowBox.q_minimumSize, + qMax(rowBox.q_preferredSize, rowBoxInfo.q_preferredSize), + rowBox.q_maximumSize); + } + if (hasIgnoreFlag) + rowData->hasIgnoreFlag = true; + } + + /* + Heuristic: Detect button boxes that don't use QLayoutPolicy::ButtonBox. + This is somewhat ad hoc but it usually does the trick. + */ + bool lastRowIsButtonBox = (lastRowAdHocData.hasOnlyButtons() + && nextToLastRowAdHocData.hasOnlyNonButtons()); + bool lastTwoRowsIsButtonBox = (lastRowAdHocData.hasOnlyButtons() + && nextToLastRowAdHocData.hasOnlyButtons() + && nextToNextToLastRowAdHocData.hasOnlyNonButtons() + && orientation == Qt::Vertical); + + if (defaultSpacing.isDefault()) { + int prevRow = -1; + for (int row = 0; row < rowInfo.count; ++row) { + if (rowData->ignore.testBit(row)) + continue; + + if (prevRow != -1 && !rowInfo.spacings.value(prevRow).isUser()) { + qreal &rowSpacing = rowData->spacings[prevRow]; + for (int column = 0; column < columnInfo.count; ++column) { + QGridLayoutItem *item1 = itemAt(prevRow, column, orientation); + QGridLayoutItem *item2 = itemAt(row, column, orientation); + + if (item1 && item2 && item1 != item2) { + QLayoutPolicy::ControlTypes controls1 = item1->controlTypes(bottom); + QLayoutPolicy::ControlTypes controls2 = item2->controlTypes(top); + + if (controls2 & QLayoutPolicy::PushButton) { + if ((row == nextToLastRowAdHocData.q_row && lastTwoRowsIsButtonBox) + || (row == lastRowAdHocData.q_row && lastRowIsButtonBox)) { + controls2 &= ~QLayoutPolicy::PushButton; + controls2 |= QLayoutPolicy::ButtonBox; + } + } + + qreal spacing = styleInfo.combinedLayoutSpacing(controls1, controls2, + orientation); + if (orientation == Qt::Horizontal) { + qreal width1 = rowData->boxes.at(prevRow).q_minimumSize; + qreal width2 = rowData->boxes.at(row).q_minimumSize; + QRectF rect1 = item1->geometryWithin(0.0, 0.0, width1, FLT_MAX, -1.0, effectiveAlignment(item1)); + QRectF rect2 = item2->geometryWithin(0.0, 0.0, width2, FLT_MAX, -1.0, effectiveAlignment(item2)); + spacing -= (width1 - (rect1.x() + rect1.width())) + rect2.x(); + } else { + const QGridLayoutBox &box1 = rowData->boxes.at(prevRow); + const QGridLayoutBox &box2 = rowData->boxes.at(row); + qreal height1 = box1.q_minimumSize; + qreal height2 = box2.q_minimumSize; + qreal rowDescent1 = fixedDescent(box1.q_minimumDescent, + box1.q_minimumAscent, height1); + qreal rowDescent2 = fixedDescent(box2.q_minimumDescent, + box2.q_minimumAscent, height2); + QRectF rect1 = item1->geometryWithin(0.0, 0.0, FLT_MAX, height1, + rowDescent1, effectiveAlignment(item1)); + QRectF rect2 = item2->geometryWithin(0.0, 0.0, FLT_MAX, height2, + rowDescent2, effectiveAlignment(item2)); + spacing -= (height1 - (rect1.y() + rect1.height())) + rect2.y(); + } + rowSpacing = qMax(spacing, rowSpacing); + } + } + } + prevRow = row; + } + } else if (lastRowIsButtonBox || lastTwoRowsIsButtonBox) { + /* + Even for styles that define a uniform spacing, we cheat a + bit and use the window margin as the spacing. This + significantly improves the look of dialogs. + */ + int prevRow = lastRowIsButtonBox ? nextToLastRowAdHocData.q_row + : nextToNextToLastRowAdHocData.q_row; + if (!defaultSpacing.isUser() && !rowInfo.spacings.value(prevRow).isUser()) { + qreal windowMargin = styleInfo.windowMargin(orientation); + qreal &rowSpacing = rowData->spacings[prevRow]; + rowSpacing = qMax(windowMargin, rowSpacing); + } + } +} + +void QGridLayoutEngine::ensureEffectiveFirstAndLastRows() const +{ + if (q_cachedEffectiveFirstRows[Hor] == -1 && !q_items.isEmpty()) { + int rowCount = this->rowCount(); + int columnCount = this->columnCount(); + + q_cachedEffectiveFirstRows[Ver] = rowCount; + q_cachedEffectiveFirstRows[Hor] = columnCount; + q_cachedEffectiveLastRows[Ver] = -1; + q_cachedEffectiveLastRows[Hor] = -1; + + for (int i = q_items.count() - 1; i >= 0; --i) { + const QGridLayoutItem *item = q_items.at(i); + + for (int j = 0; j < NOrientations; ++j) { + Qt::Orientation orientation = (j == Hor) ? Qt::Horizontal : Qt::Vertical; + if (item->firstRow(orientation) < q_cachedEffectiveFirstRows[j]) + q_cachedEffectiveFirstRows[j] = item->firstRow(orientation); + if (item->lastRow(orientation) > q_cachedEffectiveLastRows[j]) + q_cachedEffectiveLastRows[j] = item->lastRow(orientation); + } + } + } +} + +void QGridLayoutEngine::ensureColumnAndRowData(QGridLayoutRowData *rowData, QGridLayoutBox *totalBox, + qreal *colPositions, qreal *colSizes, + Qt::Orientation orientation, + const QLayoutStyleInfo &styleInfo) const +{ + rowData->reset(rowCount(orientation)); + fillRowData(rowData, colPositions, colSizes, orientation, styleInfo); + const QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical]; + rowData->distributeMultiCells(rowInfo); + *totalBox = rowData->totalBox(0, rowCount(orientation)); + //We have items whose width depends on their height +} + +/** + returns false if the layout has contradicting constraints (i.e. some items with a horizontal + constraint and other items with a vertical constraint) + */ +bool QGridLayoutEngine::ensureDynamicConstraint() const +{ + if (q_cachedConstraintOrientation == UnknownConstraint) { + for (int i = q_items.count() - 1; i >= 0; --i) { + QGridLayoutItem *item = q_items.at(i); + if (item->hasDynamicConstraint()) { + Qt::Orientation itemConstraintOrientation = item->dynamicConstraintOrientation(); + if (q_cachedConstraintOrientation == UnknownConstraint) { + q_cachedConstraintOrientation = itemConstraintOrientation; + } else if (q_cachedConstraintOrientation != itemConstraintOrientation) { + q_cachedConstraintOrientation = UnfeasibleConstraint; + qWarning("QGridLayoutEngine: Unfeasible, cannot mix horizontal and" + " vertical constraint in the same layout"); + return false; + } + } + } + if (q_cachedConstraintOrientation == UnknownConstraint) + q_cachedConstraintOrientation = NoConstraint; + } + return true; +} + +bool QGridLayoutEngine::hasDynamicConstraint() const +{ + if (!ensureDynamicConstraint()) + return false; + return q_cachedConstraintOrientation != NoConstraint; +} + +/* + * return value is only valid if hasConstraint() returns true + */ +Qt::Orientation QGridLayoutEngine::constraintOrientation() const +{ + (void)ensureDynamicConstraint(); + return (Qt::Orientation)q_cachedConstraintOrientation; +} + +void QGridLayoutEngine::ensureGeometries(const QSizeF &size, + const QLayoutStyleInfo &styleInfo) const +{ + if (q_cachedDataForStyleInfo == styleInfo && q_cachedSize == size) + return; + + q_cachedDataForStyleInfo = styleInfo; + q_cachedSize = size; + + q_xx.resize(columnCount()); + q_widths.resize(columnCount()); + q_yy.resize(rowCount()); + q_heights.resize(rowCount()); + q_descents.resize(rowCount()); + + if (constraintOrientation() != Qt::Horizontal) { + //We might have items whose width depends on their height + ensureColumnAndRowData(&q_columnData, &q_totalBoxes[Hor], NULL, NULL, Qt::Horizontal, styleInfo); + //Calculate column widths and positions, and put results in q_xx.data() and q_widths.data() so that we can use this information as + //constraints to find the row heights + q_columnData.calculateGeometries(0, columnCount(), size.width(), q_xx.data(), q_widths.data(), + 0, q_totalBoxes[Hor], q_infos[Hor] ); + ensureColumnAndRowData(&q_rowData, &q_totalBoxes[Ver], q_xx.data(), q_widths.data(), Qt::Vertical, styleInfo); + //Calculate row heights and positions, and put results in q_yy.data() and q_heights.data() + q_rowData.calculateGeometries(0, rowCount(), size.height(), q_yy.data(), q_heights.data(), + q_descents.data(), q_totalBoxes[Ver], q_infos[Ver]); + } else { + //We have items whose height depends on their width + ensureColumnAndRowData(&q_rowData, &q_totalBoxes[Ver], NULL, NULL, Qt::Vertical, styleInfo); + //Calculate row heights and positions, and put results in q_yy.data() and q_heights.data() so that we can use this information as + //constraints to find the column widths + q_rowData.calculateGeometries(0, rowCount(), size.height(), q_yy.data(), q_heights.data(), + q_descents.data(), q_totalBoxes[Ver], q_infos[Ver]); + ensureColumnAndRowData(&q_columnData, &q_totalBoxes[Hor], q_yy.data(), q_heights.data(), Qt::Horizontal, styleInfo); + //Calculate row heights and positions, and put results in q_yy.data() and q_heights.data() + q_columnData.calculateGeometries(0, columnCount(), size.width(), q_xx.data(), q_widths.data(), + 0, q_totalBoxes[Hor], q_infos[Hor]); + } +} + +QT_END_NAMESPACE + +#endif //QT_NO_GRAPHICSVIEW diff --git a/src/layouts/qgridlayoutengine_p.h b/src/layouts/qgridlayoutengine_p.h new file mode 100644 index 0000000000000000000000000000000000000000..86dec95520737b124312613daefda6796f2299a7 --- /dev/null +++ b/src/layouts/qgridlayoutengine_p.h @@ -0,0 +1,559 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Quick Layouts 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGRIDLAYOUTENGINE_P_H +#define QGRIDLAYOUTENGINE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the graphics view layout classes. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qalgorithms.h" +#include "qbitarray.h" +#include "qlist.h" +#include "qmap.h" +#include "qpair.h" +#include "qvector.h" +#include <QtCore/qsize.h> +#include <QtCore/qrect.h> +#include "qlayoutpolicy_p.h" +#include <float.h> +#include "qdebug.h" +QT_BEGIN_NAMESPACE + +//class QGraphicsLayoutItem; +class QStyle; +class QWidget; + +class QStyleOption; +// ### FIX THIS GUY +class QLayoutStyleInfo +{ +public: + inline QLayoutStyleInfo() + : m_valid(true), m_option(0) + { + m_defaultSpacing[0] = m_defaultSpacing[1] = 6; + } + + + QStyleOption *styleOption() { + if (!m_option) { + /* ### + m_option = new QStyleOption; + option.initFrom(styleInfo.widget()); + */ + } + return m_option; + } + + void invalidate() { m_valid = false; } + inline QStyle *style() const { return 0; } + inline QWidget *widget() const { return 0; } + inline bool operator==(const QLayoutStyleInfo &other) const + { return m_defaultSpacing[0] == other.m_defaultSpacing[0] && m_defaultSpacing[1] == other.m_defaultSpacing[1]; } + inline bool operator!=(const QLayoutStyleInfo &other) const + { return !(*this == other); } + + inline void setDefaultSpacing(Qt::Orientation o, qreal spacing){ + if (spacing >= 0) + m_defaultSpacing[o - 1] = spacing; + } + + inline qreal defaultSpacing(Qt::Orientation o) const { + return m_defaultSpacing[o - 1]; + } + + inline qreal perItemSpacing(QLayoutPolicy::ControlType /*control1*/, + QLayoutPolicy::ControlType /*control2*/, + Qt::Orientation /*orientation*/) const + { + return 6; + } + + inline qreal windowMargin(Qt::Orientation orientation) const + { + qreal margin = -1; + Q_UNUSED(orientation); + /* + if (style()) { + margin = style()->pixelMetric(orientation == Qt::Vertical + ? QStyle::PM_LayoutBottomMargin + : QStyle::PM_LayoutRightMargin, + styleOption(), widget()); + } + */ + return margin; + } + + inline qreal combinedLayoutSpacing(QLayoutPolicy::ControlTypes controls1, + QLayoutPolicy::ControlTypes controls2, Qt::Orientation orientation) const + { + qreal spacing = -1; + Q_UNUSED(controls1); + Q_UNUSED(controls2); + Q_UNUSED(orientation); + /* ### + if (style()) { + spacing = style()->combinedLayoutSpacing(controls1, controls2, + orientation, styleOption(), + widget()); + }*/ + return spacing; + } + + bool isWindow() const { + // return option.state & QStyle::State_Window + return false; + } +private: + bool m_valid; + qreal m_defaultSpacing[2]; + QStyleOption *m_option; +}; + + +// ### deal with Descent in a similar way +enum { + MinimumSize = Qt::MinimumSize, + PreferredSize = Qt::PreferredSize, + MaximumSize = Qt::MaximumSize, + NSizes +}; + +// do not reorder +enum { + Hor, + Ver, + NOrientations +}; + +// do not reorder +enum LayoutSide { + Left, + Top, + Right, + Bottom +}; + +enum { + NoConstraint, + HorizontalConstraint, // Width depends on the height + VerticalConstraint, // Height depends on the width + UnknownConstraint, // need to update cache + UnfeasibleConstraint // not feasible, it be has some items with Vertical and others with Horizontal constraints +}; + +template <typename T> +class QLayoutParameter +{ +public: + enum State { Default, User, Cached }; + + inline QLayoutParameter() : q_value(T()), q_state(Default) {} + inline QLayoutParameter(T value, State state = Default) : q_value(value), q_state(state) {} + + inline void setUserValue(T value) { + q_value = value; + q_state = User; + } + inline void setCachedValue(T value) const { + if (q_state != User) { + q_value = value; + q_state = Cached; + } + } + inline T value() const { return q_value; } + inline T value(T defaultValue) const { return isUser() ? q_value : defaultValue; } + inline bool isDefault() const { return q_state == Default; } + inline bool isUser() const { return q_state == User; } + inline bool isCached() const { return q_state == Cached; } + +private: + mutable T q_value; + mutable State q_state; +}; + +class QStretchParameter : public QLayoutParameter<int> +{ +public: + QStretchParameter() : QLayoutParameter<int>(-1) {} + +}; + +class QGridLayoutBox +{ +public: + inline QGridLayoutBox() + : q_minimumSize(0), q_preferredSize(0), q_maximumSize(FLT_MAX), + q_minimumDescent(-1), q_minimumAscent(-1) {} + + void add(const QGridLayoutBox &other, int stretch, qreal spacing); + void combine(const QGridLayoutBox &other); + void normalize(); + +#ifdef QT_DEBUG + void dump(int indent = 0) const; +#endif + // This code could use the union-struct-array trick, but a compiler + // bug prevents this from working. + qreal q_minimumSize; + qreal q_preferredSize; + qreal q_maximumSize; + qreal q_minimumDescent; + qreal q_minimumAscent; + inline qreal &q_sizes(int which) + { + qreal *t; + switch (which) { + case Qt::MinimumSize: + t = &q_minimumSize; + break; + case Qt::PreferredSize: + t = &q_preferredSize; + break; + case Qt::MaximumSize: + t = &q_maximumSize; + break; + case Qt::MinimumDescent: + t = &q_minimumDescent; + break; + case (Qt::MinimumDescent + 1): + t = &q_minimumAscent; + break; + default: + t = 0; + break; + } + return *t; + } + inline const qreal &q_sizes(int which) const + { + const qreal *t; + switch (which) { + case Qt::MinimumSize: + t = &q_minimumSize; + break; + case Qt::PreferredSize: + t = &q_preferredSize; + break; + case Qt::MaximumSize: + t = &q_maximumSize; + break; + case Qt::MinimumDescent: + t = &q_minimumDescent; + break; + case (Qt::MinimumDescent + 1): + t = &q_minimumAscent; + break; + default: + t = 0; + break; + } + return *t; + } +}; + +bool operator==(const QGridLayoutBox &box1, const QGridLayoutBox &box2); +inline bool operator!=(const QGridLayoutBox &box1, const QGridLayoutBox &box2) + { return !operator==(box1, box2); } + +class QGridLayoutMultiCellData +{ +public: + inline QGridLayoutMultiCellData() : q_stretch(-1) {} + + QGridLayoutBox q_box; + int q_stretch; +}; + +typedef QMap<QPair<int, int>, QGridLayoutMultiCellData> MultiCellMap; + +class QGridLayoutRowInfo; + +class QGridLayoutRowData +{ +public: + void reset(int count); + void distributeMultiCells(const QGridLayoutRowInfo &rowInfo); + void calculateGeometries(int start, int end, qreal targetSize, qreal *positions, qreal *sizes, + qreal *descents, const QGridLayoutBox &totalBox, + const QGridLayoutRowInfo &rowInfo); + QGridLayoutBox totalBox(int start, int end) const; + void stealBox(int start, int end, int which, qreal *positions, qreal *sizes); + +#ifdef QT_DEBUG + void dump(int indent = 0) const; +#endif + + QBitArray ignore; // ### rename q_ + QVector<QGridLayoutBox> boxes; + MultiCellMap multiCellMap; + QVector<int> stretches; + QVector<qreal> spacings; + bool hasIgnoreFlag; +}; + +class QGridLayoutItem +{ +public: + QGridLayoutItem(int row, int column, int rowSpan = 1, int columnSpan = 1, + Qt::Alignment alignment = 0); + + inline int firstRow() const { return q_firstRows[Ver]; } + inline int firstColumn() const { return q_firstRows[Hor]; } + inline int rowSpan() const { return q_rowSpans[Ver]; } + inline int columnSpan() const { return q_rowSpans[Hor]; } + inline int lastRow() const { return firstRow() + rowSpan() - 1; } + inline int lastColumn() const { return firstColumn() + columnSpan() - 1; } + + int firstRow(Qt::Orientation orientation) const; + int firstColumn(Qt::Orientation orientation) const; + int lastRow(Qt::Orientation orientation) const; + int lastColumn(Qt::Orientation orientation) const; + int rowSpan(Qt::Orientation orientation) const; + int columnSpan(Qt::Orientation orientation) const; + void setFirstRow(int row, Qt::Orientation orientation = Qt::Vertical); + void setRowSpan(int rowSpan, Qt::Orientation orientation = Qt::Vertical); + + int stretchFactor(Qt::Orientation orientation) const; + void setStretchFactor(int stretch, Qt::Orientation orientation); + + inline Qt::Alignment alignment() const { return q_alignment; } + inline void setAlignment(Qt::Alignment alignment) { q_alignment = alignment; } + + virtual QLayoutPolicy::Policy sizePolicy(Qt::Orientation orientation) const = 0; + virtual QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint) const = 0; + + virtual void setGeometry(const QRectF &rect) = 0; + /* + returns true if the size policy returns true for either hasHeightForWidth() + or hasWidthForHeight() + */ + virtual bool hasDynamicConstraint() const { return false; } + virtual Qt::Orientation dynamicConstraintOrientation() const { return Qt::Horizontal; } + + + QLayoutPolicy::ControlTypes controlTypes(LayoutSide side) const; + + QGridLayoutBox box(Qt::Orientation orientation, qreal constraint = -1.0) const; + QRectF geometryWithin(qreal x, qreal y, qreal width, qreal height, qreal rowDescent, Qt::Alignment align) const; + + + void transpose(); + void insertOrRemoveRows(int row, int delta, Qt::Orientation orientation = Qt::Vertical); + QSizeF effectiveMaxSize(const QSizeF &constraint) const; + +#ifdef QT_DEBUG + void dump(int indent = 0) const; +#endif + +//private: + //###QGridLayoutEngine *q_engine; // ### needed? + int q_firstRows[NOrientations]; + int q_rowSpans[NOrientations]; + int q_stretches[NOrientations]; + Qt::Alignment q_alignment; +}; + +class QGridLayoutRowInfo +{ +public: + inline QGridLayoutRowInfo() : count(0) {} + + void insertOrRemoveRows(int row, int delta); + +#ifdef QT_DEBUG + void dump(int indent = 0) const; +#endif + + int count; + QVector<QStretchParameter> stretches; + QVector<QLayoutParameter<qreal> > spacings; + QVector<Qt::Alignment> alignments; + QVector<QGridLayoutBox> boxes; +}; + +class QGridLayoutEngine +{ +public: + QGridLayoutEngine(); + inline ~QGridLayoutEngine() { qDeleteAll(q_items); } + + int rowCount(Qt::Orientation orientation) const; + int columnCount(Qt::Orientation orientation) const; + inline int rowCount() const { return q_infos[Ver].count; } + inline int columnCount() const { return q_infos[Hor].count; } + // returns the number of items inserted, which may be less than (rowCount * columnCount) + int itemCount() const; + QGridLayoutItem *itemAt(int index) const; + + int effectiveFirstRow(Qt::Orientation orientation = Qt::Vertical) const; + int effectiveLastRow(Qt::Orientation orientation = Qt::Vertical) const; + + void setSpacing(qreal spacing, Qt::Orientations orientations); + qreal spacing(Qt::Orientation orientation, const QLayoutStyleInfo &styleInfo = QLayoutStyleInfo()) const; + // ### setSpacingAfterRow(), spacingAfterRow() + void setRowSpacing(int row, qreal spacing, Qt::Orientation orientation = Qt::Vertical); + qreal rowSpacing(int row, Qt::Orientation orientation = Qt::Vertical) const; + + void setRowStretchFactor(int row, int stretch, Qt::Orientation orientation = Qt::Vertical); + int rowStretchFactor(int row, Qt::Orientation orientation = Qt::Vertical) const; + + //void setStretchFactor(QGraphicsLayoutItem *layoutItem, int stretch, + // Qt::Orientation orientation); + //int stretchFactor(QGraphicsLayoutItem *layoutItem, Qt::Orientation orientation) const; + + void setRowSizeHint(Qt::SizeHint which, int row, qreal size, + Qt::Orientation orientation = Qt::Vertical); + qreal rowSizeHint(Qt::SizeHint which, int row, + Qt::Orientation orientation = Qt::Vertical) const; + + void setRowAlignment(int row, Qt::Alignment alignment, Qt::Orientation orientation); + Qt::Alignment rowAlignment(int row, Qt::Orientation orientation) const; + + //void setAlignment(QGraphicsLayoutItem *layoutItem, Qt::Alignment alignment); + //Qt::Alignment alignment(QGraphicsLayoutItem *layoutItem) const; + Qt::Alignment effectiveAlignment(const QGridLayoutItem *layoutItem) const; + + + void insertItem(QGridLayoutItem *item, int index); + void addItem(QGridLayoutItem *item); + void removeItem(QGridLayoutItem *item); + void deleteItems() + { + const QList<QGridLayoutItem *> oldItems = q_items; + q_items.clear(); // q_items are used as input when the grid is regenerated in removeRows + // The following calls to removeRows are suboptimal + int rows = rowCount(Qt::Vertical); + removeRows(0, rows, Qt::Vertical); + rows = rowCount(Qt::Horizontal); + removeRows(0, rows, Qt::Horizontal); + qDeleteAll(oldItems); + } + + QGridLayoutItem *itemAt(int row, int column, Qt::Orientation orientation = Qt::Vertical) const; + inline void insertRow(int row, Qt::Orientation orientation = Qt::Vertical) + { insertOrRemoveRows(row, +1, orientation); } + inline void removeRows(int row, int count, Qt::Orientation orientation) + { insertOrRemoveRows(row, -count, orientation); } + + void invalidate(); + void setGeometries(const QRectF &contentsGeometry, const QLayoutStyleInfo &styleInfo = QLayoutStyleInfo()); + QRectF cellRect(const QRectF &contentsGeometry, int row, int column, int rowSpan, int columnSpan, + const QLayoutStyleInfo &styleInfo = QLayoutStyleInfo()) const; + QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint, + const QLayoutStyleInfo &styleInfo = QLayoutStyleInfo()) const; + + // heightForWidth / widthForHeight support + QSizeF dynamicallyConstrainedSizeHint(Qt::SizeHint which, const QSizeF &constraint) const; + bool ensureDynamicConstraint() const; + bool hasDynamicConstraint() const; + Qt::Orientation constraintOrientation() const; + + + QLayoutPolicy::ControlTypes controlTypes(LayoutSide side) const; + void transpose(); + void setVisualDirection(Qt::LayoutDirection direction); + Qt::LayoutDirection visualDirection() const; +#ifdef QT_DEBUG + void dump(int indent = 0) const; +#endif + +//private: + static int grossRoundUp(int n) { return ((n + 2) | 0x3) - 2; } + + void maybeExpandGrid(int row, int column, Qt::Orientation orientation = Qt::Vertical); + void regenerateGrid(); + inline int internalGridRowCount() const { return grossRoundUp(rowCount()); } + inline int internalGridColumnCount() const { return grossRoundUp(columnCount()); } + void setItemAt(int row, int column, QGridLayoutItem *item); + void insertOrRemoveRows(int row, int delta, Qt::Orientation orientation = Qt::Vertical); + void fillRowData(QGridLayoutRowData *rowData, + qreal *colPositions, qreal *colSizes, + Qt::Orientation orientation = Qt::Vertical, const QLayoutStyleInfo &styleInfo = QLayoutStyleInfo()) const; + void ensureEffectiveFirstAndLastRows() const; + void ensureColumnAndRowData(QGridLayoutRowData *rowData, QGridLayoutBox *totalBox, + qreal *colPositions, qreal *colSizes, + Qt::Orientation orientation, + const QLayoutStyleInfo &styleInfo = QLayoutStyleInfo()) const; + + void ensureGeometries(const QSizeF &size, const QLayoutStyleInfo &styleInfo = QLayoutStyleInfo()) const; + + // User input + QVector<QGridLayoutItem *> q_grid; + QList<QGridLayoutItem *> q_items; + QLayoutParameter<qreal> q_defaultSpacings[NOrientations]; + QGridLayoutRowInfo q_infos[NOrientations]; + Qt::LayoutDirection m_visualDirection; + + // Lazily computed from the above user input + mutable int q_cachedEffectiveFirstRows[NOrientations]; + mutable int q_cachedEffectiveLastRows[NOrientations]; + mutable quint8 q_cachedConstraintOrientation : 3; + + // Layout item input + mutable QLayoutStyleInfo q_cachedDataForStyleInfo; + mutable QGridLayoutRowData q_columnData; + mutable QGridLayoutRowData q_rowData; + mutable QGridLayoutBox q_totalBoxes[NOrientations]; + + // Output + mutable QSizeF q_cachedSize; + mutable QVector<qreal> q_xx; + mutable QVector<qreal> q_yy; + mutable QVector<qreal> q_widths; + mutable QVector<qreal> q_heights; + mutable QVector<qreal> q_descents; + + friend class QGridLayoutItem; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/layouts/qlayoutpolicy.cpp b/src/layouts/qlayoutpolicy.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9a154768ebd3ef1c33959f4b0ee86ec956ca84d6 --- /dev/null +++ b/src/layouts/qlayoutpolicy.cpp @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Quick Layouts 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qlayoutpolicy_p.h" +#include <QtCore/qdebug.h> +#include <QtCore/qdatastream.h> + +QT_BEGIN_NAMESPACE + +void QLayoutPolicy::setControlType(ControlType type) +{ + /* + The control type is a flag type, with values 0x1, 0x2, 0x4, 0x8, 0x10, + etc. In memory, we pack it onto the available bits (CTSize) in + setControlType(), and unpack it here. + + Example: + + 0x00000001 maps to 0 + 0x00000002 maps to 1 + 0x00000004 maps to 2 + 0x00000008 maps to 3 + etc. + */ + + int i = 0; + while (true) { + if (type & (0x1 << i)) { + bits.ctype = i; + return; + } + ++i; + } +} + +QLayoutPolicy::ControlType QLayoutPolicy::controlType() const +{ + return QLayoutPolicy::ControlType(1 << bits.ctype); +} + +#ifndef QT_NO_DATASTREAM + +/*! + \relates QLayoutPolicy + + Writes the size \a policy to the data stream \a stream. + + \sa{Serializing Qt Data Types}{Format of the QDataStream operators} +*/ +QDataStream &operator<<(QDataStream &stream, const QLayoutPolicy &policy) +{ + // The order here is for historical reasons. (compatibility with Qt4) + quint32 data = (policy.bits.horPolicy | // [0, 3] + policy.bits.verPolicy << 4 | // [4, 7] + policy.bits.hfw << 8 | // [8] + policy.bits.ctype << 9 | // [9, 13] + policy.bits.wfh << 14 | // [14] + //policy.bits.padding << 15 | // [15] + policy.bits.verStretch << 16 | // [16, 23] + policy.bits.horStretch << 24); // [24, 31] + return stream << data; +} + +#define VALUE_OF_BITS(data, bitstart, bitcount) ((data >> bitstart) & ((1 << bitcount) -1)) + +/*! + \relates QLayoutPolicy + + Reads the size \a policy from the data stream \a stream. + + \sa{Serializing Qt Data Types}{Format of the QDataStream operators} +*/ +QDataStream &operator>>(QDataStream &stream, QLayoutPolicy &policy) +{ + quint32 data; + stream >> data; + policy.bits.horPolicy = VALUE_OF_BITS(data, 0, 4); + policy.bits.verPolicy = VALUE_OF_BITS(data, 4, 4); + policy.bits.hfw = VALUE_OF_BITS(data, 8, 1); + policy.bits.ctype = VALUE_OF_BITS(data, 9, 5); + policy.bits.wfh = VALUE_OF_BITS(data, 14, 1); + policy.bits.padding = 0; + policy.bits.verStretch = VALUE_OF_BITS(data, 16, 8); + policy.bits.horStretch = VALUE_OF_BITS(data, 24, 8); + return stream; +} +#endif // QT_NO_DATASTREAM + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QLayoutPolicy &p) +{ + dbg.nospace() << "QLayoutPolicy(horizontalPolicy = " << p.horizontalPolicy() + << ", verticalPolicy = " << p.verticalPolicy() << ')'; + return dbg.space(); +} +#endif + +QT_END_NAMESPACE diff --git a/src/layouts/qlayoutpolicy_p.h b/src/layouts/qlayoutpolicy_p.h new file mode 100644 index 0000000000000000000000000000000000000000..0f3e6a081449228206d487160777369c4eddb5d1 --- /dev/null +++ b/src/layouts/qlayoutpolicy_p.h @@ -0,0 +1,180 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Quick Layouts 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QLAYOUTPOLICY_H +#define QLAYOUTPOLICY_H + +#include <QtCore/qobject.h> + +QT_BEGIN_NAMESPACE + + +class QVariant; + +class QLayoutPolicy +{ + Q_ENUMS(Policy) + +public: + enum PolicyFlag { + GrowFlag = 1, + ExpandFlag = 2, + ShrinkFlag = 4, + IgnoreFlag = 8 + }; + + enum Policy { + Fixed = 0, + Minimum = GrowFlag, + Maximum = ShrinkFlag, + Preferred = GrowFlag | ShrinkFlag, + MinimumExpanding = GrowFlag | ExpandFlag, + Expanding = GrowFlag | ShrinkFlag | ExpandFlag, + Ignored = ShrinkFlag | GrowFlag | IgnoreFlag + }; + + enum ControlType { + DefaultType = 0x00000001, + ButtonBox = 0x00000002, + CheckBox = 0x00000004, + ComboBox = 0x00000008, + Frame = 0x00000010, + GroupBox = 0x00000020, + Label = 0x00000040, + Line = 0x00000080, + LineEdit = 0x00000100, + PushButton = 0x00000200, + RadioButton = 0x00000400, + Slider = 0x00000800, + SpinBox = 0x00001000, + TabWidget = 0x00002000, + ToolButton = 0x00004000 + }; + Q_DECLARE_FLAGS(ControlTypes, ControlType) + + QLayoutPolicy() : data(0) { } + + QLayoutPolicy(Policy horizontal, Policy vertical, ControlType type = DefaultType) + : data(0) { + bits.horPolicy = horizontal; + bits.verPolicy = vertical; + setControlType(type); + } + Policy horizontalPolicy() const { return static_cast<Policy>(bits.horPolicy); } + Policy verticalPolicy() const { return static_cast<Policy>(bits.verPolicy); } + ControlType controlType() const; + + void setHorizontalPolicy(Policy d) { bits.horPolicy = d; } + void setVerticalPolicy(Policy d) { bits.verPolicy = d; } + void setControlType(ControlType type); + + Qt::Orientations expandingDirections() const { + Qt::Orientations result; + if (verticalPolicy() & ExpandFlag) + result |= Qt::Vertical; + if (horizontalPolicy() & ExpandFlag) + result |= Qt::Horizontal; + return result; + } + + void setHeightForWidth(bool b) { bits.hfw = b; } + bool hasHeightForWidth() const { return bits.hfw; } + void setWidthForHeight(bool b) { bits.wfh = b; } + bool hasWidthForHeight() const { return bits.wfh; } + + bool operator==(const QLayoutPolicy& s) const { return data == s.data; } + bool operator!=(const QLayoutPolicy& s) const { return data != s.data; } + + int horizontalStretch() const { return static_cast<int>(bits.horStretch); } + int verticalStretch() const { return static_cast<int>(bits.verStretch); } + void setHorizontalStretch(int stretchFactor) { bits.horStretch = static_cast<quint32>(qBound(0, stretchFactor, 255)); } + void setVerticalStretch(int stretchFactor) { bits.verStretch = static_cast<quint32>(qBound(0, stretchFactor, 255)); } + + void transpose(); + + +private: +#ifndef QT_NO_DATASTREAM + friend QDataStream &operator<<(QDataStream &, const QLayoutPolicy &); + friend QDataStream &operator>>(QDataStream &, QLayoutPolicy &); +#endif + QLayoutPolicy(int i) : data(i) { } + + union { + struct { + quint32 horStretch : 8; + quint32 verStretch : 8; + quint32 horPolicy : 4; + quint32 verPolicy : 4; + quint32 ctype : 5; + quint32 hfw : 1; + quint32 wfh : 1; + quint32 padding : 1; // feel free to use + } bits; + quint32 data; + }; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QLayoutPolicy::ControlTypes) + +#ifndef QT_NO_DATASTREAM +QDataStream &operator<<(QDataStream &, const QLayoutPolicy &); +QDataStream &operator>>(QDataStream &, QLayoutPolicy &); +#endif + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QLayoutPolicy &); +#endif + +inline void QLayoutPolicy::transpose() { + Policy hData = horizontalPolicy(); + Policy vData = verticalPolicy(); + int hStretch = horizontalStretch(); + int vStretch = verticalStretch(); + setHorizontalPolicy(vData); + setVerticalPolicy(hData); + setHorizontalStretch(vStretch); + setVerticalStretch(hStretch); +} + +QT_END_NAMESPACE + +#endif // QLAYOUTPOLICY_H diff --git a/src/layouts/qmldir b/src/layouts/qmldir new file mode 100644 index 0000000000000000000000000000000000000000..ec1d66a2406b7e2125e9b0f784b74b8f6324a906 --- /dev/null +++ b/src/layouts/qmldir @@ -0,0 +1,3 @@ +module QtQuick.Layouts +plugin qquicklayoutsplugin +typeinfo plugins.qmltypes diff --git a/src/layouts/qquickgridlayoutengine_p.h b/src/layouts/qquickgridlayoutengine_p.h new file mode 100644 index 0000000000000000000000000000000000000000..7a81f3c401ab7362c8677114b49239d96f41496f --- /dev/null +++ b/src/layouts/qquickgridlayoutengine_p.h @@ -0,0 +1,326 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Quick Layouts 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKGRIDLAYOUTENGINE_P_H +#define QQUICKGRIDLAYOUTENGINE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the graphics view layout classes. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qgridlayoutengine_p.h" +#include "qquickitem.h" +#include "qquicklayout_p.h" +#include <QtWidgets/qsizepolicy.h> +#include "qdebug.h" +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. 0 or less), 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 ¤t, qreal fallbackHint) +{ + if (current <= 0) + current = fallbackHint; +} + +class QQuickGridLayoutItem : public QGridLayoutItem { +public: + QQuickGridLayoutItem(QQuickItem *item, int row, int column, + int rowSpan = 1, int columnSpan = 1, Qt::Alignment alignment = 0) + : QGridLayoutItem(row, column, rowSpan, columnSpan, alignment), m_item(item), sizeHintCacheDirty(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]; + } + + QSizeF *effectiveSizeHints() const + { + if (!sizeHintCacheDirty) + return cachedSizeHints; + + QQuickLayoutAttached *info = 0; + // First, retrieve the user-specified hints from the attached "Layout." properties + if (QObject *attached = qmlAttachedPropertiesObject<QQuickLayout>(m_item, false)) { + info = static_cast<QQuickLayoutAttached *>(attached); + + 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); + cachedSizeHints[i].setWidth((info->*getter)()); + getter = verGetters.call[i]; + Q_ASSERT(getter); + cachedSizeHints[i].setHeight((info->*getter)()); + } + } else { + for (int i = 0; i < NSizes; ++i) + cachedSizeHints[i] = QSize(); + } + cachedSizeHints[Qt::MinimumDescent] = QSize(); //### FIXME when baseline support is added + + 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()); +/* + 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 1 properties will be preferred. Finally, the HINT 2 properties will be used as an + ultimate fallback. + + | USER | HINT 1 | HINT 2 + -----+--------------------------------+-------------------+------- + MIN | Layout.minimumWidth | | 0 + PREF | Layout.preferredWidth | implicitWidth | width + MAX | Layout.maximumWidth | | 100000 + -----+--------------------------------+-------------------+-------- +SizePolicy | Layout.horizontalSizePolicy | Expanding if layout, Fixed if item | + +*/ + //--- GATHER MINIMUM SIZE HINTS --- + // They are always 0 + + //--- GATHER PREFERRED SIZE HINTS --- + // First, from implicitWidth/Height + qreal &prefWidth = cachedSizeHints[Qt::PreferredSize].rwidth(); + qreal &prefHeight = cachedSizeHints[Qt::PreferredSize].rheight(); + combineHints(prefWidth, m_item->implicitWidth()); + combineHints(prefHeight, m_item->implicitHeight()); + + // If that fails, make an ultimate fallback to width/height + + if (!info && prefWidth <= 0 || prefHeight <= 0) + info = static_cast<QQuickLayoutAttached *>(qmlAttachedPropertiesObject<QQuickLayout>(m_item)); + + const bool was = info->setChangesNotificationEnabled(false); + if (prefWidth <= 0) { + prefWidth = m_item->width(); + info->setPreferredWidth(prefWidth); + } + if (prefHeight <= 0) { + prefHeight = m_item->height(); + info->setPreferredHeight(prefHeight); + } + info->setChangesNotificationEnabled(was); + + //--- GATHER MAXIMUM SIZE HINTS --- + // They are always q_declarativeLayoutMaxSize + combineHints(cachedSizeHints[Qt::MaximumSize].rwidth(), q_declarativeLayoutMaxSize); + combineHints(cachedSizeHints[Qt::MaximumSize].rheight(), q_declarativeLayoutMaxSize); + + //--- GATHER DESCENT + // ### Not implemented + + + // Normalize again after the implicit hints have been gathered + // (using different rules than normalizeHints actually??)) + // This is consistent with QGraphicsLayoutItemPrivate::effectiveSizeHints() +/* +The following shows how the different [min,pref,max] combinations are normalized after performing +each normalization stage (maxS, minS and prefS): + +input [1, 2, 3] [1, 3, 2] [2, 1, 3] [2, 3, 1] [3, 2, 1] [3, 1, 2] +------------------------------------------------------------------ +maxS [1, 2, 3] [1, 3, 3] [2, 1, 3] [2, 3, 3] [3, 2, 3] [3, 1, 3] +minS [1, 2, 3] [1, 3, 3] [1, 1, 3] [2, 3, 3] [2, 2, 3] [1, 1, 3] +prefS [1, 2, 3] [1, 3, 3] [1, 1, 3] [2, 3, 3] [2, 2, 3] [1, 1, 3] ###No change here. +*/ + boundSize(minS, maxS); + expandSize(prefS, minS); + boundSize(prefS, maxS); + //expandSize(maxS, prefS); // [3,2,1] > [3,2,2] + //expandSize(maxS, minS); + //boundSize(maxS, QSizeF(q_declarativeLayoutMaxSize, q_declarativeLayoutMaxSize)); + + + //boundSize(minS, prefS); // [3,2,1] > [2,2,1] + //boundSize(minS, maxS); + + // Both of these are invariants and inconsistent with the above combinations of the + // "symmetric" combination (i.e. boundSize(minS, prefS) and expandSize(maxS, prefS) + //expandSize(prefS, minS); //[3,2,1] > [3,3,1] + //boundSize(prefS, maxS); //[3,2,1] > [3,1,1] + + sizeHintCacheDirty = false; + return cachedSizeHints; + } + + void invalidate() + { + quickLayoutDebug() << "engine::invalidate()"; + sizeHintCacheDirty = true; + } + + + static QLayoutPolicy::Policy fromSizePolicy(QQuickLayout::SizePolicy policy) { + return (policy == QQuickLayout::Fixed ? QLayoutPolicy::Fixed : QLayoutPolicy::Preferred); + } + + QLayoutPolicy::Policy sizePolicy(Qt::Orientation orientation) const + { + if (QObject *attached = qmlAttachedPropertiesObject<QQuickLayout>(m_item, false)) { + QQuickLayoutAttached *info = static_cast<QQuickLayoutAttached *>(attached); + if (info) { + QLayoutPolicy sp(fromSizePolicy(info->horizontalSizePolicy()), fromSizePolicy(info->verticalSizePolicy())); + return (orientation == Qt::Horizontal) ? sp.horizontalPolicy() + : sp.verticalPolicy(); + } + } + // ### Correct way is to, go through all child items and combine the policies. + return qobject_cast<QQuickLayout*>(m_item) ? QLayoutPolicy::Preferred : QLayoutPolicy::Fixed; + } + + void setGeometry(const QRectF &rect) + { + m_item->setPosition(rect.topLeft()); + m_item->setSize(rect.size()); + } + + QQuickItem *layoutItem() const { return m_item; } + + QQuickItem *m_item; +private: + mutable QSizeF cachedSizeHints[Qt::NSizeHints]; + mutable bool sizeHintCacheDirty; +}; + +class QQuickGridLayoutEngine : public QGridLayoutEngine { +public: + QQuickGridLayoutEngine() : QGridLayoutEngine() {} //### not needed + + int indexOf(QQuickItem *item) const { + for (int i = 0; i < q_items.size(); ++i) { + if (item == static_cast<QQuickGridLayoutItem*>(q_items.at(i))->layoutItem()) + return i; + } + return -1; + } + + QQuickGridLayoutItem *findLayoutItem(QQuickItem *layoutItem) const + { + for (int i = q_items.count() - 1; i >= 0; --i) { + QQuickGridLayoutItem *item = static_cast<QQuickGridLayoutItem*>(q_items.at(i)); + if (item->layoutItem() == layoutItem) + return item; + } + return 0; + } +}; + + + +QT_END_NAMESPACE + +#endif // QQUICKGRIDLAYOUTENGINE_P_H diff --git a/src/controls/qquicklayout.cpp b/src/layouts/qquicklayout.cpp similarity index 50% rename from src/controls/qquicklayout.cpp rename to src/layouts/qquicklayout.cpp index 2013a4874a61527cb745dc5444bba40e39177896..e295e6cdf7c635bd51899f95875d0a1164d0a18d 100644 --- a/src/controls/qquicklayout.cpp +++ b/src/layouts/qquicklayout.cpp @@ -3,7 +3,7 @@ ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** -** This file is part of the Qt Quick Controls module of the Qt Toolkit. +** This file is part of the Qt Quick Layouts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage @@ -41,164 +41,204 @@ #include "qquicklayout_p.h" #include <QEvent> -#include <QApplication> +#include <QtCore/qcoreapplication.h> #include <QtCore/qnumeric.h> QT_BEGIN_NAMESPACE -static const qreal q_declarativeLayoutMaxSize = 10e8; - - -QQuickComponentsLayoutAttached::QQuickComponentsLayoutAttached(QObject *parent) +QQuickLayoutAttached::QQuickLayoutAttached(QObject *parent) : QObject(parent), m_minimumWidth(0), m_minimumHeight(0), + m_preferredWidth(0), + m_preferredHeight(0), m_maximumWidth(q_declarativeLayoutMaxSize), m_maximumHeight(q_declarativeLayoutMaxSize), - m_verticalSizePolicy(QQuickComponentsLayout::Fixed), - m_horizontalSizePolicy(QQuickComponentsLayout::Fixed) + m_verticalSizePolicy(QQuickLayout::Fixed), + m_horizontalSizePolicy(QQuickLayout::Fixed), + m_row(0), + m_column(0), + m_rowSpan(1), + m_columnSpan(1), + m_changesNotificationEnabled(true) { } -void QQuickComponentsLayoutAttached::setMinimumWidth(qreal width) +void QQuickLayoutAttached::setMinimumWidth(qreal width) { if (qIsNaN(width) || m_minimumWidth == width) return; m_minimumWidth = width; - updateLayout(); + invalidateItem(); emit minimumWidthChanged(); } -void QQuickComponentsLayoutAttached::setMinimumHeight(qreal height) +void QQuickLayoutAttached::setMinimumHeight(qreal height) { if (qIsNaN(height) || m_minimumHeight == height) return; m_minimumHeight = height; - updateLayout(); + invalidateItem(); emit minimumHeightChanged(); } -void QQuickComponentsLayoutAttached::setMaximumWidth(qreal width) +void QQuickLayoutAttached::setPreferredWidth(qreal width) +{ + if (qIsNaN(width) || m_preferredWidth == width) + return; + + m_preferredWidth = width; + if (m_changesNotificationEnabled) + invalidateItem(); + emit preferredWidthChanged(); +} + +void QQuickLayoutAttached::setPreferredHeight(qreal height) +{ + if (qIsNaN(height) || m_preferredHeight == height) + return; + + m_preferredHeight = height; + if (m_changesNotificationEnabled) + invalidateItem(); + emit preferredHeightChanged(); +} + +void QQuickLayoutAttached::setMaximumWidth(qreal width) { if (qIsNaN(width) || m_maximumWidth == width) return; m_maximumWidth = width; - updateLayout(); + invalidateItem(); emit maximumWidthChanged(); } -void QQuickComponentsLayoutAttached::setMaximumHeight(qreal height) +void QQuickLayoutAttached::setMaximumHeight(qreal height) { if (qIsNaN(height) || m_maximumHeight == height) return; m_maximumHeight = height; - updateLayout(); + invalidateItem(); emit maximumHeightChanged(); } -void QQuickComponentsLayoutAttached::setVerticalSizePolicy(QQuickComponentsLayout::SizePolicy policy) +void QQuickLayoutAttached::setVerticalSizePolicy(QQuickLayout::SizePolicy policy) { if (m_verticalSizePolicy != policy) { m_verticalSizePolicy = policy; - updateLayout(); + invalidateItem(); emit verticalSizePolicyChanged(); } } -void QQuickComponentsLayoutAttached::setHorizontalSizePolicy(QQuickComponentsLayout::SizePolicy policy) +void QQuickLayoutAttached::setHorizontalSizePolicy(QQuickLayout::SizePolicy policy) { if (m_horizontalSizePolicy != policy) { m_horizontalSizePolicy = policy; - updateLayout(); + invalidateItem(); emit horizontalSizePolicyChanged(); } } -void QQuickComponentsLayoutAttached::updateLayout() +void QQuickLayoutAttached::invalidateItem() { - if (m_layout) - m_layout->invalidate(); + quickLayoutDebug() << "QQuickLayoutAttached::invalidateItem"; + if (QQuickLayout *layout = parentLayout()) { + layout->invalidate(item()); + } } +QQuickLayout *QQuickLayoutAttached::parentLayout() const +{ + QQuickItem *parentItem = item()->parentItem(); + if (qobject_cast<QQuickLayout *>(parentItem)) + return static_cast<QQuickLayout *>(parentItem); + return 0; +} + +QQuickItem *QQuickLayoutAttached::item() const +{ + Q_ASSERT(qobject_cast<QQuickItem*>(parent())); + return static_cast<QQuickItem*>(parent()); +} + + + -QQuickComponentsLayout::QQuickComponentsLayout(QQuickItem *parent) - : QQuickItem(parent), +QQuickLayout::QQuickLayout(QQuickLayoutPrivate &dd, QQuickItem *parent) + : QQuickItem(dd, parent), m_dirty(false) { - } -QQuickComponentsLayout::~QQuickComponentsLayout() +QQuickLayout::~QQuickLayout() { } -void QQuickComponentsLayout::setupItemLayout(QQuickItem *item) +void QQuickLayout::setupItemLayout(QQuickItem *item) { - QObject *attached = qmlAttachedPropertiesObject<QQuickComponentsLayout>(item); - QQuickComponentsLayoutAttached *info = static_cast<QQuickComponentsLayoutAttached *>(attached); - info->m_layout = this; + //### not needed anymore, since these are deducted from hierarcy? + qmlAttachedPropertiesObject<QQuickLayout>(item); } -QQuickComponentsLayoutAttached *QQuickComponentsLayout::qmlAttachedProperties(QObject *object) +QQuickLayoutAttached *QQuickLayout::qmlAttachedProperties(QObject *object) { - return new QQuickComponentsLayoutAttached(object); + return new QQuickLayoutAttached(object); } -bool QQuickComponentsLayout::event(QEvent *e) +bool QQuickLayout::event(QEvent *e) { if (e->type() == QEvent::LayoutRequest) - reconfigureTopDown(); + rearrangeTopDown(); return QQuickItem::event(e); } -void QQuickComponentsLayout::invalidate() +void QQuickLayout::componentComplete() +{ + QQuickItem::componentComplete(); +} + +void QQuickLayout::invalidate(QQuickItem * /*childItem*/) { if (m_dirty) return; - QQuickComponentsLayout *layout = this; - QQuickComponentsLayout *parentLayout = 0; - - while (!layout->m_dirty) { - layout->m_dirty = true; - parentLayout = qobject_cast<QQuickComponentsLayout *>(layout->parentItem()); + m_dirty = true; - if (!parentLayout) - break; - else - layout = parentLayout; + if (QQuickLayout *parentLayout = qobject_cast<QQuickLayout *>(parentItem())) { + parentLayout->invalidate(this); + } else { + quickLayoutDebug() << "QQuickLayout::invalidate(), postEvent"; + QCoreApplication::postEvent(this, new QEvent(QEvent::LayoutRequest)); } - - // just post events for top level layouts - if (!parentLayout) - QApplication::postEvent(layout, new QEvent(QEvent::LayoutRequest)); } -void QQuickComponentsLayout::reconfigureTopDown() +void QQuickLayout::rearrangeTopDown() { + quickLayoutDebug() << "QQuickLayout::rearrangeTopDown()"; const QList<QQuickItem *> &children = childItems(); - reconfigureLayout(); + rearrange(); foreach (QQuickItem *child, children) { - QQuickComponentsLayout *layout = qobject_cast<QQuickComponentsLayout *>(child); + QQuickLayout *layout = qobject_cast<QQuickLayout *>(child); if (layout && layout->m_dirty) - layout->reconfigureTopDown(); + layout->rearrangeTopDown(); } m_dirty = false; } -void QQuickComponentsLayout::reconfigureLayout() +void QQuickLayout::rearrange() { } diff --git a/src/layouts/qquicklayout_p.h b/src/layouts/qquicklayout_p.h new file mode 100644 index 0000000000000000000000000000000000000000..9c7c04e7ca70feba7df4478f3a3e697b0b069f25 --- /dev/null +++ b/src/layouts/qquicklayout_p.h @@ -0,0 +1,220 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Quick Layouts 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKLAYOUT_P_H +#define QQUICKLAYOUT_P_H + +#include <QPointer> +#include <QQuickItem> +#include <private/qquickitem_p.h> + +QT_BEGIN_NAMESPACE + +class QQuickLayoutAttached; + +static const qreal q_declarativeLayoutMaxSize = 10e8; + +#if 0 && !defined(QT_NO_DEBUG) && !defined(QT_NO_DEBUG_OUTPUT) +# define quickLayoutDebug QMessageLogger(__FILE__, __LINE__, Q_FUNC_INFO).debug +#else +# define quickLayoutDebug QT_NO_QWARNING_MACRO +#endif + +class QQuickLayoutPrivate; +class QQuickLayout : public QQuickItem +{ + Q_OBJECT + Q_ENUMS(SizePolicy) +public: + enum SizePolicy { + Fixed, + Expanding + }; + + enum SizeHint { + MinimumSize = 0, + PreferredSize, + MaximumSize, + NSizes + }; + + explicit QQuickLayout(QQuickLayoutPrivate &dd, QQuickItem *parent = 0); + ~QQuickLayout(); + + static QQuickLayoutAttached *qmlAttachedProperties(QObject *object); + + + void componentComplete(); + virtual void invalidate(QQuickItem * childItem = 0); +protected: + bool event(QEvent *e); + void rearrangeTopDown(); + virtual void rearrange(); + void setupItemLayout(QQuickItem *item); + + + enum Orientation { + Vertical = 0, + Horizontal, + NOrientations + }; + +private: + bool m_dirty; + + Q_DECLARE_PRIVATE(QQuickLayout) + + friend class QQuickLayoutAttached; +}; + + +class QQuickLayoutPrivate : public QQuickItemPrivate +{ + Q_DECLARE_PUBLIC(QQuickLayout) +}; + + +class QQuickLayoutAttached : public QObject +{ + Q_OBJECT + Q_PROPERTY(qreal minimumWidth READ minimumWidth WRITE setMinimumWidth NOTIFY minimumWidthChanged) + Q_PROPERTY(qreal minimumHeight READ minimumHeight WRITE setMinimumHeight NOTIFY minimumHeightChanged) + Q_PROPERTY(qreal preferredWidth READ preferredWidth WRITE setPreferredWidth NOTIFY preferredWidthChanged) + Q_PROPERTY(qreal preferredHeight READ preferredHeight WRITE setPreferredHeight NOTIFY preferredHeightChanged) + Q_PROPERTY(qreal maximumWidth READ maximumWidth WRITE setMaximumWidth NOTIFY maximumWidthChanged) + Q_PROPERTY(qreal maximumHeight READ maximumHeight WRITE setMaximumHeight NOTIFY maximumHeightChanged) + Q_PROPERTY(QQuickLayout::SizePolicy verticalSizePolicy READ verticalSizePolicy WRITE setVerticalSizePolicy) + Q_PROPERTY(QQuickLayout::SizePolicy horizontalSizePolicy READ horizontalSizePolicy WRITE setHorizontalSizePolicy) + Q_PROPERTY(int row READ row WRITE setRow) + Q_PROPERTY(int column READ column WRITE setColumn) + Q_PROPERTY(int rowSpan READ rowSpan WRITE setRowSpan) + Q_PROPERTY(int columnSpan READ columnSpan WRITE setColumnSpan) + +public: + QQuickLayoutAttached(QObject *object); + + qreal minimumWidth() const { return m_minimumWidth; } + void setMinimumWidth(qreal width); + + qreal minimumHeight() const { return m_minimumHeight; } + void setMinimumHeight(qreal height); + + qreal preferredWidth() const { return m_preferredWidth; } + void setPreferredWidth(qreal width); + + qreal preferredHeight() const { return m_preferredHeight; } + void setPreferredHeight(qreal width); + + qreal maximumWidth() const { return m_maximumWidth; } + void setMaximumWidth(qreal width); + + qreal maximumHeight() const { return m_maximumHeight; } + void setMaximumHeight(qreal height); + + QQuickLayout::SizePolicy verticalSizePolicy() const { return m_verticalSizePolicy; } + void setVerticalSizePolicy(QQuickLayout::SizePolicy policy); + + QQuickLayout::SizePolicy horizontalSizePolicy() const { return m_horizontalSizePolicy; } + void setHorizontalSizePolicy(QQuickLayout::SizePolicy policy); + + int row() const { return m_row; } + void setRow(int row) { m_row = row; } + int column() const { return m_column; } + void setColumn(int column) { m_column = column; } + + int rowSpan() const { return m_rowSpan; } + void setRowSpan(int span) { m_rowSpan = span; } + int columnSpan() const { return m_columnSpan; } + void setColumnSpan(int span) { m_columnSpan = span; } + + bool setChangesNotificationEnabled(bool enabled) + { + const bool old = m_changesNotificationEnabled; + m_changesNotificationEnabled = enabled; + return old; + } + +signals: + void minimumWidthChanged(); + void minimumHeightChanged(); + void preferredWidthChanged(); + void preferredHeightChanged(); + void maximumWidthChanged(); + void maximumHeightChanged(); + void verticalSizePolicyChanged(); + void horizontalSizePolicyChanged(); + +private: + void invalidateItem(); + QQuickLayout *parentLayout() const; + QQuickItem *item() const; +private: + qreal m_minimumWidth; + qreal m_minimumHeight; + qreal m_preferredWidth; + qreal m_preferredHeight; + qreal m_maximumWidth; + qreal m_maximumHeight; + QQuickLayout::SizePolicy m_verticalSizePolicy; + QQuickLayout::SizePolicy m_horizontalSizePolicy; + + // GridLayout specific properties + int m_row; + int m_column; + int m_rowSpan; + int m_columnSpan; + + bool m_changesNotificationEnabled; + + friend class QQuickLayout; +}; + +inline QQuickLayoutAttached *attachedLayoutObject(QQuickItem *item, bool create = true) +{ + return static_cast<QQuickLayoutAttached *>(qmlAttachedPropertiesObject<QQuickLayout>(item, create)); +} + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickLayout) +QML_DECLARE_TYPEINFO(QQuickLayout, QML_HAS_ATTACHED_PROPERTIES) + +#endif // QQUICKLAYOUT_P_H diff --git a/src/layouts/qquicklinearlayout.cpp b/src/layouts/qquicklinearlayout.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2d6b830a80a9adde89b4c201ec97498dde4f1849 --- /dev/null +++ b/src/layouts/qquicklinearlayout.cpp @@ -0,0 +1,426 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Quick Layouts 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquicklinearlayout_p.h" +#include <QtCore/qnumeric.h> +#include "qdebug.h" +/*! + \qmltype RowLayout + \instantiates QQuickRowLayout + \inqmlmodule QtDesktop 1.0 + \brief RowLayout is doing bla...bla... +*/ + +/*! + \qmltype ColumnLayout + \instantiates QQuickColumnLayout + \inqmlmodule QtDesktop 1.0 + \brief ColumnLayout is doing bla...bla... +*/ + +QT_BEGIN_NAMESPACE + +static const qreal q_declarativeLayoutDefaultSpacing = 4.0; + + +QQuickGridLayoutBase::QQuickGridLayoutBase(QQuickGridLayoutBasePrivate &dd, + Qt::Orientation orientation, + QQuickItem *parent /*= 0*/) + : QQuickLayout(dd, parent) +{ + Q_D(QQuickGridLayoutBase); + d->orientation = orientation; +} + +Qt::Orientation QQuickGridLayoutBase::orientation() const +{ + Q_D(const QQuickGridLayoutBase); + return d->orientation; +} + +void QQuickGridLayoutBase::setOrientation(Qt::Orientation orientation) +{ + Q_D(QQuickGridLayoutBase); + if (d->orientation == orientation) + return; + + d->orientation = orientation; + invalidate(); +} + +void QQuickGridLayoutBase::componentComplete() +{ + QQuickLayout::componentComplete(); + quickLayoutDebug() << "QQuickGridLayoutBase::componentComplete()"; + updateLayoutItems(); + quickLayoutDebug() << "QQuickGridLayoutBase::componentComplete() DONE"; + +} + +/* + Invalidation happens like this as a reaction to that a size hint changes on an item "a": + + Suppose we have the following Qml document: + RowLayout { + id: l1 + RowLayout { + id: l2 + Item { + id: a + } + Item { + id: b + } + } + } + + 1. l2->invalidateChildItem(a) is called on l2, where item refers to "a". + (this will dirty the cached size hints of item "a") + 2. l2->invalidate() is called + this will : + i) invalidate the layout engine + ii) dirty the cached size hints of item "l2" (by calling parentLayout()->invalidateChildItem + + */ +/*! + \internal + + Invalidates \a childItem and this layout. + After a call to invalidate, the next call to retrieve e.g. sizeHint will be up-to date. + This function will also call QQuickLayout::invalidate(0), to ensure that the parent layout + is invalidated. + */ +void QQuickGridLayoutBase::invalidate(QQuickItem *childItem) +{ + Q_D(QQuickGridLayoutBase); + if (!isComponentComplete()) + return; + quickLayoutDebug() << "QQuickGridLayoutBase::invalidate()"; + + if (childItem) { + if (QQuickGridLayoutItem *layoutItem = d->engine.findLayoutItem(childItem)) + layoutItem->invalidate(); + } + // invalidate engine + d->engine.invalidate(); + + QQuickLayout::invalidate(this); +} + +void QQuickGridLayoutBase::updateLayoutItems() +{ + Q_D(QQuickGridLayoutBase); + if (!isComponentComplete()) + return; + quickLayoutDebug() << "QQuickGridLayoutBase::updateLayoutItems"; + d->engine.deleteItems(); + foreach (QQuickItem *child, childItems()) { + if (child->isVisible()) + insertLayoutItem(child); + } + + invalidate(); + quickLayoutDebug() << "QQuickGridLayoutBase::updateLayoutItems LEAVING"; + //propagateLayoutSizeHints(); +} + +void QQuickGridLayoutBase::propagateLayoutSizeHints() +{ + Q_D(QQuickGridLayoutBase); + quickLayoutDebug() << "propagateLayoutSizeHints()"; + QObject *attached = qmlAttachedPropertiesObject<QQuickLayout>(this); + QQuickLayoutAttached *info = static_cast<QQuickLayoutAttached *>(attached); + + const QSizeF min = d->engine.sizeHint(Qt::MinimumSize, QSizeF()); + const QSizeF pref = d->engine.sizeHint(Qt::PreferredSize, QSizeF()); + const QSizeF max = d->engine.sizeHint(Qt::MaximumSize, QSizeF()); + + info->setMinimumWidth(min.width()); + info->setMinimumHeight(min.height()); + setImplicitWidth(pref.width()); + setImplicitHeight(pref.height()); + info->setMaximumWidth(max.width()); + info->setMaximumHeight(max.height()); +} + +void QQuickGridLayoutBase::itemChange(ItemChange change, const ItemChangeData &value) +{ + if (change == ItemChildAddedChange) { + quickLayoutDebug() << "ItemChildAddedChange"; + 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(onItemImplicitSizeChanged())); + + if (isComponentComplete() && isVisible()) + updateLayoutItems(); + } else if (change == ItemChildRemovedChange) { + quickLayoutDebug() << "ItemChildRemovedChange"; + 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(onItemImplicitSizeChanged())); + if (isComponentComplete() && isVisible()) + updateLayoutItems(); + } + + QQuickLayout::itemChange(change, value); +} + +void QQuickGridLayoutBase::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + QQuickLayout::geometryChanged(newGeometry, oldGeometry); + if (!isComponentComplete() || !newGeometry.isValid()) + return; + quickLayoutDebug() << "QQuickGridLayoutBase::geometryChanged" << newGeometry << oldGeometry; + rearrange(); +} + +void QQuickGridLayoutBase::insertLayoutItem(QQuickItem *item) +{ + Q_D(QQuickGridLayoutBase); + if (!item) { + qWarning("QGraphicsGridLayout::addItem: cannot add null item"); + return; + } + QQuickLayoutAttached *info = attachedLayoutObject(item, false); + int row = 0; + int column = 0; + int rowSpan = 1; + int columnSpan = 1; + Qt::Alignment alignment = 0; + if (info) { + row = info->row(); + column = info->column(); + rowSpan = info->rowSpan(); + columnSpan = info->columnSpan(); + } + if (row < 0 || column < 0) { + qWarning("QQuickGridLayoutBase::insertLayoutItemAt: invalid row/column: %d", + row < 0 ? row : column); + return; + } + if (columnSpan < 1 || rowSpan < 1) { + qWarning("QQuickGridLayoutBase::addItem: invalid row span/column span: %d", + rowSpan < 1 ? rowSpan : columnSpan); + return; + } + QQuickGridLayoutItem *layoutItem = new QQuickGridLayoutItem(item, row, column, rowSpan, columnSpan, alignment); + d->engine.insertItem(layoutItem, -1); + + setupItemLayout(item); +} + +void QQuickGridLayoutBase::removeGridItem(QGridLayoutItem *gridItem) +{ + Q_D(QQuickGridLayoutBase); + const int index = gridItem->firstRow(d->orientation); + d->engine.removeItem(gridItem); + d->engine.removeRows(index, 1, d->orientation); +} + +void QQuickGridLayoutBase::removeLayoutItem(QQuickItem *item) +{ + Q_D(QQuickGridLayoutBase); + quickLayoutDebug() << "QQuickGridLayoutBase::removeLayoutItem"; + if (QQuickGridLayoutItem *gridItem = d->engine.findLayoutItem(item)) { + removeGridItem(gridItem); + delete gridItem; + invalidate(); + } +} + +void QQuickGridLayoutBase::onItemVisibleChanged() +{ + if (!isComponentComplete()) + return; + quickLayoutDebug() << "QQuickGridLayoutBase::onItemVisibleChanged"; + updateLayoutItems(); +} + +void QQuickGridLayoutBase::onItemDestroyed() +{ + Q_D(QQuickGridLayoutBase); + quickLayoutDebug() << "QQuickGridLayoutBase::onItemDestroyed"; + QQuickItem *inDestruction = static_cast<QQuickItem *>(sender()); + if (QQuickGridLayoutItem *gridItem = d->engine.findLayoutItem(inDestruction)) { + removeGridItem(gridItem); + delete gridItem; + invalidate(); + } +} + +void QQuickGridLayoutBase::onItemImplicitSizeChanged() +{ + Q_D(QQuickGridLayoutBase); + QQuickItem *item = static_cast<QQuickItem *>(sender()); + Q_ASSERT(item); + //invalidate(item); +} + +void QQuickGridLayoutBase::rearrange() +{ + Q_D(QQuickGridLayoutBase); + if (!isComponentComplete()) + return; + + quickLayoutDebug() << "QQuickGridLayoutBase::rearrange()"; + Qt::LayoutDirection visualDir = Qt::LeftToRight; // ### Fix if RTL support is needed + d->engine.setVisualDirection(visualDir); + + /* + qreal left, top, right, bottom; + left = top = right = bottom = 0; // ### support for margins? + if (visualDir == Qt::RightToLeft) + qSwap(left, right); + */ + + QRectF effectiveRect(0, 0, width(), height()); + d->engine.setGeometries(effectiveRect); + + // propagate hints to upper levels + propagateLayoutSizeHints(); +} + + +/********************************** + ** + ** QQuickGridLayout + ** + **/ +QQuickGridLayout::QQuickGridLayout(QQuickItem *parent /* = 0*/) + : QQuickGridLayoutBase(*new QQuickGridLayoutPrivate, Qt::Horizontal, parent) +{ + Q_D(QQuickGridLayout); + d->horizontalSpacing = q_declarativeLayoutDefaultSpacing; + d->verticalSpacing = q_declarativeLayoutDefaultSpacing; + d->engine.setSpacing(d->horizontalSpacing, Qt::Horizontal); + d->engine.setSpacing(d->verticalSpacing, Qt::Vertical); +} + +qreal QQuickGridLayout::horizontalSpacing() const +{ + Q_D(const QQuickGridLayout); + return d->horizontalSpacing; +} + +void QQuickGridLayout::setHorizontalSpacing(qreal spacing) +{ + Q_D(QQuickGridLayout); + if (qIsNaN(spacing) || d->horizontalSpacing == spacing) + return; + + d->horizontalSpacing = spacing; + d->engine.setSpacing(spacing, Qt::Horizontal | Qt::Vertical); + invalidate(); +} + +qreal QQuickGridLayout::verticalSpacing() const +{ + Q_D(const QQuickGridLayout); + return d->verticalSpacing; +} + +void QQuickGridLayout::setVerticalSpacing(qreal spacing) +{ + Q_D(QQuickGridLayout); + if (qIsNaN(spacing) || d->verticalSpacing == spacing) + return; + + d->verticalSpacing = spacing; + d->engine.setSpacing(spacing, Qt::Horizontal | Qt::Vertical); + invalidate(); +} + + +/********************************** + ** + ** QQuickLinearLayout + ** + **/ +QQuickLinearLayout::QQuickLinearLayout(Qt::Orientation orientation, + QQuickItem *parent /*= 0*/) + : QQuickGridLayoutBase(*new QQuickLinearLayoutPrivate, orientation, parent) +{ + Q_D(QQuickLinearLayout); + d->spacing = q_declarativeLayoutDefaultSpacing; + d->engine.setSpacing(d->spacing, Qt::Horizontal | Qt::Vertical); +} + +qreal QQuickLinearLayout::spacing() const +{ + Q_D(const QQuickLinearLayout); + return d->spacing; +} + +void QQuickLinearLayout::setSpacing(qreal spacing) +{ + Q_D(QQuickLinearLayout); + if (qIsNaN(spacing) || d->spacing == spacing) + return; + + d->spacing = spacing; + d->engine.setSpacing(spacing, Qt::Horizontal | Qt::Vertical); + invalidate(); +} + + +void QQuickLinearLayout::insertLayoutItem(QQuickItem *item) +{ + Q_D(QQuickLinearLayout); + const int index = d->engine.rowCount(d->orientation); + d->engine.insertRow(index, d->orientation); + + int gridRow = 0; + int gridColumn = index; + if (d->orientation == Qt::Vertical) + qSwap(gridRow, gridColumn); + QQuickGridLayoutItem *layoutItem = new QQuickGridLayoutItem(item, gridRow, gridColumn, 1, 1, 0); + d->engine.insertItem(layoutItem, index); + + setupItemLayout(item); + + invalidate(); +} + + + +QT_END_NAMESPACE diff --git a/src/layouts/qquicklinearlayout_p.h b/src/layouts/qquicklinearlayout_p.h new file mode 100644 index 0000000000000000000000000000000000000000..0a8db1cdc202988cef53712c86e29d5d4004ee15 --- /dev/null +++ b/src/layouts/qquicklinearlayout_p.h @@ -0,0 +1,199 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Quick Layouts 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 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 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKLINEARLAYOUT_P_H +#define QQUICKLINEARLAYOUT_P_H + +#include "qquicklayout_p.h" +#include "qquickgridlayoutengine_p.h" + +QT_BEGIN_NAMESPACE + +/********************************** + ** + ** QQuickGridLayoutBase + ** + **/ +class QQuickGridLayoutBasePrivate; + +class QQuickGridLayoutBase : public QQuickLayout +{ + Q_OBJECT +public: + explicit QQuickGridLayoutBase(QQuickGridLayoutBasePrivate &dd, + Qt::Orientation orientation, + QQuickItem *parent = 0); + ~QQuickGridLayoutBase() {} + + void componentComplete(); + void invalidate(QQuickItem *childItem = 0); + Qt::Orientation orientation() const; + void setOrientation(Qt::Orientation orientation); + +protected: + void updateLayoutItems(); + void rearrange(); + virtual void insertLayoutItem(QQuickItem *item); + void removeLayoutItem(QQuickItem *item); + void itemChange(ItemChange change, const ItemChangeData &data); + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); + +protected slots: + void onItemVisibleChanged(); + void onItemDestroyed(); + void onItemImplicitSizeChanged(); + +private: + void removeGridItem(QGridLayoutItem *gridItem); + void propagateLayoutSizeHints(); + Q_DECLARE_PRIVATE(QQuickGridLayoutBase) +}; + + +class QQuickGridLayoutBasePrivate : public QQuickLayoutPrivate +{ + Q_DECLARE_PUBLIC(QQuickGridLayoutBase) + +public: + QQuickGridLayoutBasePrivate() { } + QQuickGridLayoutEngine engine; + Qt::Orientation orientation; +}; + +/********************************** + ** + ** QQuickGridLayout + ** + **/ +class QQuickGridLayoutPrivate; +class QQuickGridLayout : public QQuickGridLayoutBase +{ + Q_OBJECT + Q_PROPERTY(qreal horizontalSpacing READ horizontalSpacing WRITE setHorizontalSpacing NOTIFY horizontalSpacingChanged) + Q_PROPERTY(qreal verticalSpacing READ verticalSpacing WRITE setVerticalSpacing NOTIFY verticalSpacingChanged) +public: + explicit QQuickGridLayout(QQuickItem *parent = 0); + qreal horizontalSpacing() const; + void setHorizontalSpacing(qreal spacing); + qreal verticalSpacing() const; + void setVerticalSpacing(qreal spacing); +signals: + void horizontalSpacingChanged(); + void verticalSpacingChanged(); + +private: + Q_DECLARE_PRIVATE(QQuickGridLayout) +}; + +class QQuickGridLayoutPrivate : public QQuickGridLayoutBasePrivate +{ + Q_DECLARE_PUBLIC(QQuickGridLayout) +public: + QQuickGridLayoutPrivate() {} + qreal horizontalSpacing; + qreal verticalSpacing; +}; + + +/********************************** + ** + ** QQuickLinearLayout + ** + **/ +class QQuickLinearLayoutPrivate; +class QQuickLinearLayout : public QQuickGridLayoutBase +{ + Q_OBJECT + Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing NOTIFY spacingChanged) +public: + explicit QQuickLinearLayout(Qt::Orientation orientation, + QQuickItem *parent = 0); + void insertLayoutItem(QQuickItem *item); + qreal spacing() const; + void setSpacing(qreal spacing); + +signals: + void spacingChanged(); +private: + Q_DECLARE_PRIVATE(QQuickLinearLayout) +}; + +class QQuickLinearLayoutPrivate : public QQuickGridLayoutBasePrivate +{ + Q_DECLARE_PUBLIC(QQuickLinearLayout) +public: + QQuickLinearLayoutPrivate() {} + qreal spacing; +}; + + +/********************************** + ** + ** QQuickRowLayout + ** + **/ +class QQuickRowLayout : public QQuickLinearLayout +{ + Q_OBJECT + +public: + explicit QQuickRowLayout(QQuickItem *parent = 0) + : QQuickLinearLayout(Qt::Horizontal, parent) {} +}; + + +/********************************** + ** + ** QQuickColumnLayout + ** + **/ +class QQuickColumnLayout : public QQuickLinearLayout +{ + Q_OBJECT + +public: + explicit QQuickColumnLayout(QQuickItem *parent = 0) + : QQuickLinearLayout(Qt::Vertical, parent) {} +}; + +QT_END_NAMESPACE + +#endif // QQUICKLINEARLAYOUT_P_H diff --git a/src/src.pro b/src/src.pro index 77753b0a696dce2702cd8da21b59d02641612fdf..7bd5b33e4025ef53621ae9a8cdd54404d0aeee33 100644 --- a/src/src.pro +++ b/src/src.pro @@ -1,3 +1,5 @@ TEMPLATE = subdirs SUBDIRS += controls styles experimental private + +SUBDIRS += layouts diff --git a/tests/manual/Layout.qml b/tests/manual/Layout.qml index 9646d885b2967b50aa21817e541a31191a20a7b5..d23aaa21ba17f7bc4856de0c81e7779e7a0ce346 100644 --- a/tests/manual/Layout.qml +++ b/tests/manual/Layout.qml @@ -40,8 +40,10 @@ import QtQuick 2.0 import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 Item { + id: window width: 600 height: 400 @@ -49,16 +51,34 @@ Item { property real defaultHeight: 30 Column { - anchors.fill: parent - CheckBox { - id: ckHideGreen - text: "Hide green rectangles" - checked: false + anchors { + top: parent.top + left: parent.left + right: parent.right + } + Row { width: parent.width + height: 20 + CheckBox { + id: ckHideGreen + text: "Hide green rectangles" + checked: false + height: parent.height + } + Slider { + id: spacingSlider + value: 4 + minimumValue: 0 + maximumValue: 100 + width: 200 + height: parent.height + } + } TabView { id:frame width: parent.width + height: window.height - ckHideGreen.height Tab { title: "Horizontal" @@ -78,18 +98,22 @@ Item { height: defaultHeight anchors.left: parent.left anchors.right: parent.right + spacing: spacingSlider.value Rectangle { color: "red" + width: 50 height: parent.height } Rectangle { color: "green" + width: 100 height: parent.height visible: !ckHideGreen.checked } Rectangle { color: "blue" + width: 200 height: parent.height } } @@ -100,14 +124,16 @@ Item { anchors.left: parent.left anchors.right: parent.right - spacing: 5 + spacing: spacingSlider.value Rectangle { color: "red" + width: 40 height: parent.height } Rectangle { color: "green" + width: 40 height: parent.height visible: !ckHideGreen.checked } @@ -116,6 +142,7 @@ Item { } Rectangle { color: "blue" + width: 40 height: parent.height } } @@ -125,6 +152,7 @@ Item { height: defaultHeight anchors.left: parent.left anchors.right: parent.right + spacing: spacingSlider.value Rectangle { color: "red" @@ -152,7 +180,8 @@ Item { // [4] RowLayout { - spacing: 100 + spacing: 100 + spacingSlider.value + height: defaultHeight anchors.left: parent.left anchors.right: parent.right @@ -180,6 +209,7 @@ Item { // [5] RowLayout { + spacing: spacingSlider.value height: defaultHeight anchors.left: parent.left anchors.right: parent.right @@ -195,7 +225,7 @@ Item { // [6] RowLayout { - spacing: 40 + spacing: 40 + spacingSlider.value height: defaultHeight anchors.left: parent.left anchors.right: parent.right @@ -225,6 +255,7 @@ Item { Rectangle { color: "green" height: parent.height + width: 40 visible: !ckHideGreen.checked Layout.maximumWidth: 300 Layout.horizontalSizePolicy: Layout.Expanding @@ -250,6 +281,7 @@ Item { anchors { top: parent.top + left: parent.left bottom: parent.bottom margins: 10 @@ -260,19 +292,23 @@ Item { width: defaultWidth anchors.top: parent.top anchors.bottom: parent.bottom + spacing: spacingSlider.value Rectangle { color: "red" width: parent.width + height: 40 } Rectangle { color: "green" width: parent.width + height: 80 visible: !ckHideGreen.checked } Rectangle { color: "blue" width: parent.width + height: 120 } } @@ -282,23 +318,26 @@ Item { anchors.top: parent.top anchors.bottom: parent.bottom - spacing: 5 + spacing: spacingSlider.value Rectangle { color: "red" width: parent.width + height: 40 } Rectangle { color: "green" width: parent.width visible: !ckHideGreen.checked + height: 80 } Item { - implicitWidth: 10 + implicitHeight: 10 } Rectangle { color: "blue" width: parent.width + height: 40 } } @@ -307,10 +346,12 @@ Item { width: defaultWidth anchors.top: parent.top anchors.bottom: parent.bottom + spacing: spacingSlider.value Rectangle { color: "red" width: parent.width + height: 60 Layout.minimumHeight: 50 Layout.maximumHeight: 100 Layout.verticalSizePolicy: Layout.Expanding @@ -318,23 +359,24 @@ Item { Rectangle { color: "green" width: parent.width + height: 90 visible: !ckHideGreen.checked - Layout.minimumHeight: 100 - Layout.maximumHeight: 200 + Layout.minimumHeight: 75 + Layout.maximumHeight: 125 Layout.verticalSizePolicy: Layout.Expanding } Rectangle { color: "blue" width: parent.width - Layout.minimumHeight: 200 - Layout.maximumHeight: 400 + height: 120 + Layout.minimumHeight: 100 Layout.verticalSizePolicy: Layout.Expanding } } // [4] ColumnLayout { - spacing: 100 + spacing: 100 + spacingSlider.value width: defaultWidth anchors.top: parent.top anchors.bottom: parent.bottom @@ -362,22 +404,7 @@ Item { // [5] ColumnLayout { - width: defaultWidth - anchors.top: parent.top - anchors.bottom: parent.bottom - - Rectangle { - color: "red" - width: parent.width - Layout.minimumHeight: 200 - Layout.maximumHeight: 500 - Layout.verticalSizePolicy: Layout.Expanding - } - } - - // [6] - ColumnLayout { - spacing: 40 + spacing: 40 + spacingSlider.value width: defaultWidth anchors.top: parent.top anchors.bottom: parent.bottom @@ -385,28 +412,33 @@ Item { ColumnLayout { spacing: 10 width: parent.width + Layout.verticalSizePolicy: Layout.Expanding Rectangle { color: "red" width: parent.width Layout.minimumHeight: 100 + Layout.maximumHeight: 300 Layout.verticalSizePolicy: Layout.Expanding } Rectangle { color: "blue" width: parent.width - Layout.minimumHeight: 200 + Layout.minimumHeight: 100 Layout.verticalSizePolicy: Layout.Expanding + Layout.maximumHeight: 200 } } ColumnLayout { spacing: 10 width: parent.width - + Layout.maximumHeight: 200 + Layout.verticalSizePolicy: Layout.Expanding Rectangle { color: "green" width: parent.width + height: 50 visible: !ckHideGreen.checked Layout.maximumHeight: 300 Layout.verticalSizePolicy: Layout.Expanding @@ -414,8 +446,8 @@ Item { Rectangle { color: "red" width: parent.width + height: 50 Layout.minimumHeight: 40 - Layout.maximumHeight: 100 Layout.verticalSizePolicy: Layout.Expanding } } @@ -423,7 +455,6 @@ Item { } } - Tab { title: "Horizontal and Vertical" @@ -511,6 +542,185 @@ Item { } } } + + Tab { + title: "Grid" + Column { + spacing: 4 + + anchors { + top: parent.top + left: parent.left + right: parent.right + bottom: parent.bottom + margins: 10 + } + + // [1] + GridLayout { + id: flag + verticalSpacing: spacingSlider.value + horizontalSpacing: spacingSlider.value + // row 0 + anchors.left: parent.left + anchors.right: parent.right + Rectangle { + color: "red" + width: 52 + height: 52 + Layout.row: 0 + Layout.column: 0 + Layout.horizontalSizePolicy: Layout.Expanding + Layout.verticalSizePolicy: Layout.Expanding + } + Rectangle { + color: "white" + width: 8 + height: 52 + Layout.row: 0 + Layout.column: 1 + Layout.horizontalSizePolicy: Layout.Expanding + Layout.verticalSizePolicy: Layout.Expanding + } + Rectangle { + color: "blue" + width: 18 + height: 60 + Layout.row: 0 + Layout.column: 2 + Layout.rowSpan: 2 + Layout.horizontalSizePolicy: Layout.Expanding + Layout.verticalSizePolicy: Layout.Expanding + } + Rectangle { + color: "white" + width: 8 + height: 52 + Layout.row: 0 + Layout.column: 3 + Layout.horizontalSizePolicy: Layout.Expanding + Layout.verticalSizePolicy: Layout.Expanding + } + Rectangle { + color: "red" + width: 140 + height: 52 + Layout.row: 0 + Layout.column: 4 + Layout.horizontalSizePolicy: Layout.Expanding + Layout.verticalSizePolicy: Layout.Expanding + } + + // row 1 + Rectangle { + color: "white" + width: 60 + height: 8 + Layout.row: 1 + Layout.column: 0 + Layout.columnSpan: 2 + Layout.horizontalSizePolicy: Layout.Expanding + Layout.verticalSizePolicy: Layout.Expanding + } + Rectangle { + color: "white" + width: 60 + height: 8 + Layout.row: 1 + Layout.column: 3 + Layout.columnSpan: 2 + Layout.horizontalSizePolicy: Layout.Expanding + Layout.verticalSizePolicy: Layout.Expanding + } + + // row 2 + Rectangle { + color: "blue" + width: 226 + height: 18 + Layout.row: 2 + Layout.column: 0 + Layout.columnSpan: 5 + Layout.horizontalSizePolicy: Layout.Expanding + Layout.verticalSizePolicy: Layout.Expanding + } + + // row 3 + Rectangle { + color: "white" + width: 60 + height: 8 + Layout.row: 3 + Layout.column: 0 + Layout.columnSpan: 2 + Layout.horizontalSizePolicy: Layout.Expanding + Layout.verticalSizePolicy: Layout.Expanding + } + Rectangle { + color: "blue" + width: 18 + height: 60 + Layout.row: 3 + Layout.column: 2 + Layout.rowSpan: 2 + Layout.horizontalSizePolicy: Layout.Expanding + Layout.verticalSizePolicy: Layout.Expanding + } + Rectangle { + color: "white" + width: 140 + height: 8 + Layout.row: 3 + Layout.column: 3 + Layout.columnSpan: 2 + Layout.horizontalSizePolicy: Layout.Expanding + Layout.verticalSizePolicy: Layout.Expanding + } + + // row 4 + Rectangle { + color: "red" + width: 52 + height: 52 + Layout.row: 4 + Layout.column: 0 + Layout.horizontalSizePolicy: Layout.Expanding + Layout.verticalSizePolicy: Layout.Expanding + } + Rectangle { + color: "white" + width: 8 + height: 52 + Layout.row: 4 + Layout.column: 1 + Layout.horizontalSizePolicy: Layout.Expanding + Layout.verticalSizePolicy: Layout.Expanding + } + Rectangle { + color: "white" + width: 8 + height: 52 + Layout.row: 4 + Layout.column: 3 + Layout.horizontalSizePolicy: Layout.Expanding + Layout.verticalSizePolicy: Layout.Expanding + } + Rectangle { + color: "red" + width: 140 + height: 52 + Layout.row: 4 + Layout.column: 4 + Layout.horizontalSizePolicy: Layout.Expanding + Layout.verticalSizePolicy: Layout.Expanding + } + } + + Text { + text: "Norwegian flag" + } + } + } } } } diff --git a/tests/manual/scrollbars.qml b/tests/manual/scrollbars.qml index 231ead7fc47f1148d389ce88392c0f722161d278..bcfa897516002f2e0032c93daa9f28e7325fcf7a 100644 --- a/tests/manual/scrollbars.qml +++ b/tests/manual/scrollbars.qml @@ -40,6 +40,7 @@ import QtQuick 2.0 import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 ApplicationWindow { title: "Component Gallery" diff --git a/tests/manual/testbench/content/PropertyLayouts.qml b/tests/manual/testbench/content/PropertyLayouts.qml index 2f0d85f0808e4274711cd2407e6b59eee5ec2c82..88d64acf4a8a103b6dc5dd5efb15776f2e168866 100644 --- a/tests/manual/testbench/content/PropertyLayouts.qml +++ b/tests/manual/testbench/content/PropertyLayouts.qml @@ -40,6 +40,7 @@ import QtQuick 2.0 import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 QtObject { property Component boolLayout: CheckBox { diff --git a/tests/manual/testbench/main.qml b/tests/manual/testbench/main.qml index 019f5c40385879071c774038d823d13803bf7a3a..a5f5f6937216b4640dd68edaea24dcee9c3ad1e5 100644 --- a/tests/manual/testbench/main.qml +++ b/tests/manual/testbench/main.qml @@ -40,6 +40,7 @@ import QtQuick 2.0 import QtQuick.Controls 1.0 +import QtQuick.Layouts 1.0 import "content" ApplicationWindow {