diff options
Diffstat (limited to 'src/quicktemplates/qquickpage.cpp')
-rw-r--r-- | src/quicktemplates/qquickpage.cpp | 473 |
1 files changed, 473 insertions, 0 deletions
diff --git a/src/quicktemplates/qquickpage.cpp b/src/quicktemplates/qquickpage.cpp new file mode 100644 index 0000000000..55c6d3a178 --- /dev/null +++ b/src/quicktemplates/qquickpage.cpp @@ -0,0 +1,473 @@ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qquickpage_p.h" +#include "qquickpage_p_p.h" +#include "qquicktoolbar_p.h" +#if QT_CONFIG(quicktemplates2_container) +#include "qquicktabbar_p.h" +#include "qquickdialogbuttonbox_p.h" +#endif + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Page + \inherits Pane +//! \instantiates QQuickPage + \inqmlmodule QtQuick.Controls + \since 5.7 + \ingroup qtquickcontrols-containers + \ingroup qtquickcontrols-focusscopes + \brief Styled page control with support for a header and footer. + + Page is a container control which makes it convenient to add + a \l header and \l footer item to a page. + + \image qtquickcontrols-page-wireframe.png + + Items declared as children of a Page are: + \list + \li automatically parented to the Page's contentItem. Items created + dynamically need to be explicitly parented to the contentItem. + \li not automatically positioned or resized. + \endlist + + The following example snippet illustrates how to use a page-specific + toolbar header and an application-wide tabbar footer. + + \qml + import QtQuick.Controls + import QtQuick.Layouts + + ApplicationWindow { + visible: true + + StackView { + anchors.fill: parent + + initialItem: Page { + header: ToolBar { + // ... + } + + ColumnLayout { + anchors.fill: parent + // ... + } + } + } + + footer: TabBar { + // ... + } + } + \endqml + + \sa ApplicationWindow, {Container Controls}, + {Focus Management in Qt Quick Controls} +*/ + +static const QQuickItemPrivate::ChangeTypes LayoutChanges = QQuickItemPrivate::Geometry | QQuickItemPrivate::Visibility | QQuickItemPrivate::Destroyed + | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight; + +namespace { + enum Position { + Header, + Footer + }; + +Q_STATIC_ASSERT(int(Header) == int(QQuickToolBar::Header)); +Q_STATIC_ASSERT(int(Footer) == int(QQuickToolBar::Footer)); + +#if QT_CONFIG(quicktemplates2_container) + Q_STATIC_ASSERT(int(Header) == int(QQuickTabBar::Header)); + Q_STATIC_ASSERT(int(Footer) == int(QQuickTabBar::Footer)); + + Q_STATIC_ASSERT(int(Header) == int(QQuickDialogButtonBox::Header)); + Q_STATIC_ASSERT(int(Footer) == int(QQuickDialogButtonBox::Footer)); +#endif + + static void setPos(QQuickItem *item, Position position) + { + if (QQuickToolBar *toolBar = qobject_cast<QQuickToolBar *>(item)) + toolBar->setPosition(static_cast<QQuickToolBar::Position>(position)); +#if QT_CONFIG(quicktemplates2_container) + else if (QQuickTabBar *tabBar = qobject_cast<QQuickTabBar *>(item)) + tabBar->setPosition(static_cast<QQuickTabBar::Position>(position)); + else if (QQuickDialogButtonBox *buttonBox = qobject_cast<QQuickDialogButtonBox *>(item)) + buttonBox->setPosition(static_cast<QQuickDialogButtonBox::Position>(position)); +#endif + } +} + +void QQuickPagePrivate::relayout() +{ + Q_Q(QQuickPage); + const qreal hh = header && header->isVisible() ? header->height() : 0; + const qreal fh = footer && footer->isVisible() ? footer->height() : 0; + const qreal hsp = hh > 0 ? spacing : 0; + const qreal fsp = fh > 0 ? spacing : 0; + + if (contentItem) { + contentItem->setY(q->topPadding() + hh + hsp); + contentItem->setX(q->leftPadding()); + contentItem->setWidth(q->availableWidth()); + contentItem->setHeight(q->availableHeight() - hh - fh - hsp - fsp); + } + + if (header) { + header->setY(0); + header->setWidth(q->width()); + } + + if (footer) { + footer->setY(q->height() - footer->height()); + footer->setWidth(q->width()); + } +} + +void QQuickPagePrivate::resizeContent() +{ + relayout(); +} + +void QQuickPagePrivate::itemVisibilityChanged(QQuickItem *item) +{ + Q_Q(QQuickPage); + QQuickPanePrivate::itemVisibilityChanged(item); + if (item == header) { + QBoolBlocker signalGuard(emittingImplicitSizeChangedSignals); + emit q->implicitHeaderWidthChanged(); + emit q->implicitHeaderHeightChanged(); + relayout(); + } else if (item == footer) { + QBoolBlocker signalGuard(emittingImplicitSizeChangedSignals); + emit q->implicitFooterWidthChanged(); + emit q->implicitFooterHeightChanged(); + relayout(); + } +} + +void QQuickPagePrivate::itemImplicitWidthChanged(QQuickItem *item) +{ + Q_Q(QQuickPage); + QQuickPanePrivate::itemImplicitWidthChanged(item); + + // Avoid binding loops by skipping signal emission if we're already doing it. + if (emittingImplicitSizeChangedSignals) + return; + + if (item == header) + emit q->implicitHeaderWidthChanged(); + else if (item == footer) + emit q->implicitFooterWidthChanged(); +} + +void QQuickPagePrivate::itemImplicitHeightChanged(QQuickItem *item) +{ + Q_Q(QQuickPage); + QQuickPanePrivate::itemImplicitHeightChanged(item); + + // Avoid binding loops by skipping signal emission if we're already doing it. + if (emittingImplicitSizeChangedSignals) + return; + + if (item == header) + emit q->implicitHeaderHeightChanged(); + else if (item == footer) + emit q->implicitFooterHeightChanged(); +} + +void QQuickPagePrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF & diff) +{ + QQuickPanePrivate::itemGeometryChanged(item, change, diff); + if (item == header || item == footer) + relayout(); +} + +void QQuickPagePrivate::itemDestroyed(QQuickItem *item) +{ + Q_Q(QQuickPage); + QQuickPanePrivate::itemDestroyed(item); + if (item == header) { + header = nullptr; + relayout(); + emit q->implicitHeaderWidthChanged(); + emit q->implicitHeaderHeightChanged(); + emit q->headerChanged(); + } else if (item == footer) { + footer = nullptr; + relayout(); + emit q->implicitFooterWidthChanged(); + emit q->implicitFooterHeightChanged(); + emit q->footerChanged(); + } +} + +QQuickPage::QQuickPage(QQuickItem *parent) + : QQuickPane(*(new QQuickPagePrivate), parent) +{ +} + +QQuickPage::QQuickPage(QQuickPagePrivate &dd, QQuickItem *parent) + : QQuickPane(dd, parent) +{ +} + +QQuickPage::~QQuickPage() +{ + Q_D(QQuickPage); + if (d->header) + QQuickItemPrivate::get(d->header)->removeItemChangeListener(d, LayoutChanges); + if (d->footer) + QQuickItemPrivate::get(d->footer)->removeItemChangeListener(d, LayoutChanges); +} + +/*! + \qmlproperty string QtQuick.Controls::Page::title + + This property holds the page title. + + The title is often displayed at the top of a page to give + the user context about the page they are viewing. + + Page does not render the title itself, but instead relies + on the application to do so. For example: + + \code + ApplicationWindow { + visible: true + width: 400 + height: 400 + + header: Label { + text: view.currentItem.title + horizontalAlignment: Text.AlignHCenter + } + + SwipeView { + id: view + anchors.fill: parent + + Page { + title: qsTr("Home") + } + Page { + title: qsTr("Discover") + } + Page { + title: qsTr("Activity") + } + } + } + \endcode +*/ + +QString QQuickPage::title() const +{ + return d_func()->title; +} + +void QQuickPage::setTitle(const QString &title) +{ + Q_D(QQuickPage); + if (d->title == title) + return; + + d->title = title; + maybeSetAccessibleName(title); + emit titleChanged(); +} + +/*! + \qmlproperty Item QtQuick.Controls::Page::header + + This property holds the page header item. The header item is positioned to + the top, and resized to the width of the page. The default value is \c null. + + \note Assigning a ToolBar, TabBar, or DialogButtonBox as a page header + automatically sets the respective \l ToolBar::position, \l TabBar::position, + or \l DialogButtonBox::position property to \c Header. + + \sa footer, ApplicationWindow::header +*/ +QQuickItem *QQuickPage::header() const +{ + Q_D(const QQuickPage); + return d->header; +} + +void QQuickPage::setHeader(QQuickItem *header) +{ + Q_D(QQuickPage); + if (d->header == header) + return; + + if (d->header) { + QQuickItemPrivate::get(d->header)->removeItemChangeListener(d, LayoutChanges); + d->header->setParentItem(nullptr); + } + d->header = header; + if (header) { + header->setParentItem(this); + QQuickItemPrivate::get(header)->addItemChangeListener(d, LayoutChanges); + if (qFuzzyIsNull(header->z())) + header->setZ(1); + setPos(header, Header); + } + if (isComponentComplete()) + d->relayout(); + emit headerChanged(); +} + +/*! + \qmlproperty Item QtQuick.Controls::Page::footer + + This property holds the page footer item. The footer item is positioned to + the bottom, and resized to the width of the page. The default value is \c null. + + \note Assigning a ToolBar, TabBar, or DialogButtonBox as a page footer + automatically sets the respective \l ToolBar::position, \l TabBar::position, + or \l DialogButtonBox::position property to \c Footer. + + \sa header, ApplicationWindow::footer +*/ +QQuickItem *QQuickPage::footer() const +{ + Q_D(const QQuickPage); + return d->footer; +} + +void QQuickPage::setFooter(QQuickItem *footer) +{ + Q_D(QQuickPage); + if (d->footer == footer) + return; + + if (d->footer) { + QQuickItemPrivate::get(d->footer)->removeItemChangeListener(d, LayoutChanges); + d->footer->setParentItem(nullptr); + } + d->footer = footer; + if (footer) { + footer->setParentItem(this); + QQuickItemPrivate::get(footer)->addItemChangeListener(d, LayoutChanges); + if (qFuzzyIsNull(footer->z())) + footer->setZ(1); + setPos(footer, Footer); + } + if (isComponentComplete()) + d->relayout(); + emit footerChanged(); +} + +/*! + \since QtQuick.Controls 2.5 (Qt 5.12) + \qmlproperty real QtQuick.Controls::Page::implicitHeaderWidth + \readonly + + This property holds the implicit header width. + + The value is equal to \c {header && header.visible ? header.implicitWidth : 0}. + + \sa implicitHeaderHeight, implicitFooterWidth +*/ +qreal QQuickPage::implicitHeaderWidth() const +{ + Q_D(const QQuickPage); + if (!d->header || !d->header->isVisible()) + return 0; + return d->header->implicitWidth(); +} + +/*! + \since QtQuick.Controls 2.5 (Qt 5.12) + \qmlproperty real QtQuick.Controls::Page::implicitHeaderHeight + \readonly + + This property holds the implicit header height. + + The value is equal to \c {header && header.visible ? header.implicitHeight : 0}. + + \sa implicitHeaderWidth, implicitFooterHeight +*/ +qreal QQuickPage::implicitHeaderHeight() const +{ + Q_D(const QQuickPage); + if (!d->header || !d->header->isVisible()) + return 0; + return d->header->implicitHeight(); +} + +/*! + \since QtQuick.Controls 2.5 (Qt 5.12) + \qmlproperty real QtQuick.Controls::Page::implicitFooterWidth + \readonly + + This property holds the implicit footer width. + + The value is equal to \c {footer && footer.visible ? footer.implicitWidth : 0}. + + \sa implicitFooterHeight, implicitHeaderWidth +*/ +qreal QQuickPage::implicitFooterWidth() const +{ + Q_D(const QQuickPage); + if (!d->footer || !d->footer->isVisible()) + return 0; + return d->footer->implicitWidth(); +} + +/*! + \since QtQuick.Controls 2.5 (Qt 5.12) + \qmlproperty real QtQuick.Controls::Page::implicitFooterHeight + \readonly + + This property holds the implicit footer height. + + The value is equal to \c {footer && footer.visible ? footer.implicitHeight : 0}. + + \sa implicitFooterWidth, implicitHeaderHeight +*/ +qreal QQuickPage::implicitFooterHeight() const +{ + Q_D(const QQuickPage); + if (!d->footer || !d->footer->isVisible()) + return 0; + return d->footer->implicitHeight(); +} + +void QQuickPage::componentComplete() +{ + Q_D(QQuickPage); + QQuickPane::componentComplete(); + d->relayout(); +} + +void QQuickPage::spacingChange(qreal newSpacing, qreal oldSpacing) +{ + Q_D(QQuickPage); + QQuickPane::spacingChange(newSpacing, oldSpacing); + d->relayout(); +} + +#if QT_CONFIG(accessibility) +QAccessible::Role QQuickPage::accessibleRole() const +{ + return QAccessible::PageTab; +} + +void QQuickPage::accessibilityActiveChanged(bool active) +{ + Q_D(QQuickPage); + QQuickPane::accessibilityActiveChanged(active); + + if (active) + maybeSetAccessibleName(d->title); +} +#endif + +QT_END_NAMESPACE + +#include "moc_qquickpage_p.cpp" |