1 /****************************************************************************
2 **
3 ** Copyright (C) 2017 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL3$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or later as published by the Free
28 ** Software Foundation and appearing in the file LICENSE.GPL included in
29 ** the packaging of this file. Please review the following information to
30 ** ensure the GNU General Public License version 2.0 requirements will be
31 ** met: http://www.gnu.org/licenses/gpl-2.0.html.
32 **
33 ** $QT_END_LICENSE$
34 **
35 ****************************************************************************/
36 
37 #include "qquickpage_p.h"
38 #include "qquickpage_p_p.h"
39 #include "qquicktabbar_p.h"
40 #include "qquicktoolbar_p.h"
41 #include "qquickdialogbuttonbox_p.h"
42 
43 QT_BEGIN_NAMESPACE
44 
45 /*!
46     \qmltype Page
47     \inherits Pane
48 //!     \instantiates QQuickPage
49     \inqmlmodule QtQuick.Controls
50     \since 5.7
51     \ingroup qtquickcontrols2-containers
52     \ingroup qtquickcontrols2-focusscopes
53     \brief Styled page control with support for a header and footer.
54 
55     Page is a container control which makes it convenient to add
56     a \l header and \l footer item to a page.
57 
58     \image qtquickcontrols2-page-wireframe.png
59 
60     The following example snippet illustrates how to use a page-specific
61     toolbar header and an application-wide tabbar footer.
62 
63     \qml
64     import QtQuick.Controls 2.12
65 
66     ApplicationWindow {
67         visible: true
68 
69         StackView {
70             anchors.fill: parent
71 
72             initialItem: Page {
73                 header: ToolBar {
74                     // ...
75                 }
76             }
77         }
78 
79         footer: TabBar {
80             // ...
81         }
82     }
83     \endqml
84 
85     \sa ApplicationWindow, {Container Controls},
86         {Focus Management in Qt Quick Controls}
87 */
88 
89 static const QQuickItemPrivate::ChangeTypes LayoutChanges = QQuickItemPrivate::Geometry | QQuickItemPrivate::Visibility | QQuickItemPrivate::Destroyed
90                                                           | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight;
91 
92 namespace {
93     enum Position {
94         Header,
95         Footer
96     };
97 
98     Q_STATIC_ASSERT(int(Header) == int(QQuickTabBar::Header));
99     Q_STATIC_ASSERT(int(Footer) == int(QQuickTabBar::Footer));
100 
101     Q_STATIC_ASSERT(int(Header) == int(QQuickToolBar::Header));
102     Q_STATIC_ASSERT(int(Footer) == int(QQuickToolBar::Footer));
103 
104     Q_STATIC_ASSERT(int(Header) == int(QQuickDialogButtonBox::Header));
105     Q_STATIC_ASSERT(int(Footer) == int(QQuickDialogButtonBox::Footer));
106 
setPos(QQuickItem * item,Position position)107     static void setPos(QQuickItem *item, Position position)
108     {
109         if (QQuickToolBar *toolBar = qobject_cast<QQuickToolBar *>(item))
110             toolBar->setPosition(static_cast<QQuickToolBar::Position>(position));
111         else if (QQuickTabBar *tabBar = qobject_cast<QQuickTabBar *>(item))
112             tabBar->setPosition(static_cast<QQuickTabBar::Position>(position));
113         else if (QQuickDialogButtonBox *buttonBox = qobject_cast<QQuickDialogButtonBox *>(item))
114             buttonBox->setPosition(static_cast<QQuickDialogButtonBox::Position>(position));
115     }
116 }
117 
relayout()118 void QQuickPagePrivate::relayout()
119 {
120     Q_Q(QQuickPage);
121     const qreal hh = header && header->isVisible() ? header->height() : 0;
122     const qreal fh = footer && footer->isVisible() ? footer->height() : 0;
123     const qreal hsp = hh > 0 ? spacing : 0;
124     const qreal fsp = fh > 0 ? spacing : 0;
125 
126     if (contentItem) {
127         contentItem->setY(q->topPadding() + hh + hsp);
128         contentItem->setX(q->leftPadding());
129         contentItem->setWidth(q->availableWidth());
130         contentItem->setHeight(q->availableHeight() - hh - fh - hsp - fsp);
131     }
132 
133     if (header)
134         header->setWidth(q->width());
135 
136     if (footer) {
137         footer->setY(q->height() - footer->height());
138         footer->setWidth(q->width());
139     }
140 }
141 
resizeContent()142 void QQuickPagePrivate::resizeContent()
143 {
144     relayout();
145 }
146 
itemVisibilityChanged(QQuickItem * item)147 void QQuickPagePrivate::itemVisibilityChanged(QQuickItem *item)
148 {
149     Q_Q(QQuickPage);
150     QQuickPanePrivate::itemVisibilityChanged(item);
151     if (item == header) {
152         QBoolBlocker signalGuard(emittingImplicitSizeChangedSignals);
153         emit q->implicitHeaderWidthChanged();
154         emit q->implicitHeaderHeightChanged();
155         relayout();
156     } else if (item == footer) {
157         QBoolBlocker signalGuard(emittingImplicitSizeChangedSignals);
158         emit q->implicitFooterWidthChanged();
159         emit q->implicitFooterHeightChanged();
160         relayout();
161     }
162 }
163 
itemImplicitWidthChanged(QQuickItem * item)164 void QQuickPagePrivate::itemImplicitWidthChanged(QQuickItem *item)
165 {
166     Q_Q(QQuickPage);
167     QQuickPanePrivate::itemImplicitWidthChanged(item);
168 
169     // Avoid binding loops by skipping signal emission if we're already doing it.
170     if (emittingImplicitSizeChangedSignals)
171         return;
172 
173     if (item == header)
174         emit q->implicitHeaderWidthChanged();
175     else if (item == footer)
176         emit q->implicitFooterWidthChanged();
177 }
178 
itemImplicitHeightChanged(QQuickItem * item)179 void QQuickPagePrivate::itemImplicitHeightChanged(QQuickItem *item)
180 {
181     Q_Q(QQuickPage);
182     QQuickPanePrivate::itemImplicitHeightChanged(item);
183 
184     // Avoid binding loops by skipping signal emission if we're already doing it.
185     if (emittingImplicitSizeChangedSignals)
186         return;
187 
188     if (item == header)
189         emit q->implicitHeaderHeightChanged();
190     else if (item == footer)
191         emit q->implicitFooterHeightChanged();
192 }
193 
itemGeometryChanged(QQuickItem * item,QQuickGeometryChange change,const QRectF & diff)194 void QQuickPagePrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF & diff)
195 {
196     QQuickPanePrivate::itemGeometryChanged(item, change, diff);
197     if (item == header || item == footer)
198         relayout();
199 }
200 
itemDestroyed(QQuickItem * item)201 void QQuickPagePrivate::itemDestroyed(QQuickItem *item)
202 {
203     Q_Q(QQuickPage);
204     QQuickPanePrivate::itemDestroyed(item);
205     if (item == header) {
206         header = nullptr;
207         relayout();
208         emit q->implicitHeaderWidthChanged();
209         emit q->implicitHeaderHeightChanged();
210         emit q->headerChanged();
211     } else if (item == footer) {
212         footer = nullptr;
213         relayout();
214         emit q->implicitFooterWidthChanged();
215         emit q->implicitFooterHeightChanged();
216         emit q->footerChanged();
217     }
218 }
219 
QQuickPage(QQuickItem * parent)220 QQuickPage::QQuickPage(QQuickItem *parent)
221     : QQuickPane(*(new QQuickPagePrivate), parent)
222 {
223 }
224 
QQuickPage(QQuickPagePrivate & dd,QQuickItem * parent)225 QQuickPage::QQuickPage(QQuickPagePrivate &dd, QQuickItem *parent)
226     : QQuickPane(dd, parent)
227 {
228 }
229 
~QQuickPage()230 QQuickPage::~QQuickPage()
231 {
232     Q_D(QQuickPage);
233     if (d->header)
234         QQuickItemPrivate::get(d->header)->removeItemChangeListener(d, LayoutChanges);
235     if (d->footer)
236         QQuickItemPrivate::get(d->footer)->removeItemChangeListener(d, LayoutChanges);
237 }
238 
239 /*!
240     \qmlproperty string QtQuick.Controls::Page::title
241 
242     This property holds the page title.
243 
244     The title is often displayed at the top of a page to give
245     the user context about the page they are viewing.
246 
247     \code
248     ApplicationWindow {
249         visible: true
250         width: 400
251         height: 400
252 
253         header: Label {
254             text: view.currentItem.title
255             horizontalAlignment: Text.AlignHCenter
256         }
257 
258         SwipeView {
259             id: view
260             anchors.fill: parent
261 
262             Page {
263                 title: qsTr("Home")
264             }
265             Page {
266                 title: qsTr("Discover")
267             }
268             Page {
269                 title: qsTr("Activity")
270             }
271         }
272     }
273     \endcode
274 */
275 
title() const276 QString QQuickPage::title() const
277 {
278     return d_func()->title;
279 }
280 
setTitle(const QString & title)281 void QQuickPage::setTitle(const QString &title)
282 {
283     Q_D(QQuickPage);
284     if (d->title == title)
285         return;
286 
287     d->title = title;
288     maybeSetAccessibleName(title);
289     emit titleChanged();
290 }
291 
292 /*!
293     \qmlproperty Item QtQuick.Controls::Page::header
294 
295     This property holds the page header item. The header item is positioned to
296     the top, and resized to the width of the page. The default value is \c null.
297 
298     \note Assigning a ToolBar, TabBar, or DialogButtonBox as a page header
299     automatically sets the respective \l ToolBar::position, \l TabBar::position,
300     or \l DialogButtonBox::position property to \c Header.
301 
302     \sa footer, ApplicationWindow::header
303 */
header() const304 QQuickItem *QQuickPage::header() const
305 {
306     Q_D(const QQuickPage);
307     return d->header;
308 }
309 
setHeader(QQuickItem * header)310 void QQuickPage::setHeader(QQuickItem *header)
311 {
312     Q_D(QQuickPage);
313     if (d->header == header)
314         return;
315 
316     if (d->header) {
317         QQuickItemPrivate::get(d->header)->removeItemChangeListener(d, LayoutChanges);
318         d->header->setParentItem(nullptr);
319     }
320     d->header = header;
321     if (header) {
322         header->setParentItem(this);
323         QQuickItemPrivate::get(header)->addItemChangeListener(d, LayoutChanges);
324         if (qFuzzyIsNull(header->z()))
325             header->setZ(1);
326         setPos(header, Header);
327     }
328     if (isComponentComplete())
329         d->relayout();
330     emit headerChanged();
331 }
332 
333 /*!
334     \qmlproperty Item QtQuick.Controls::Page::footer
335 
336     This property holds the page footer item. The footer item is positioned to
337     the bottom, and resized to the width of the page. The default value is \c null.
338 
339     \note Assigning a ToolBar, TabBar, or DialogButtonBox as a page footer
340     automatically sets the respective \l ToolBar::position, \l TabBar::position,
341     or \l DialogButtonBox::position property to \c Footer.
342 
343     \sa header, ApplicationWindow::footer
344 */
footer() const345 QQuickItem *QQuickPage::footer() const
346 {
347     Q_D(const QQuickPage);
348     return d->footer;
349 }
350 
setFooter(QQuickItem * footer)351 void QQuickPage::setFooter(QQuickItem *footer)
352 {
353     Q_D(QQuickPage);
354     if (d->footer == footer)
355         return;
356 
357     if (d->footer) {
358         QQuickItemPrivate::get(d->footer)->removeItemChangeListener(d, LayoutChanges);
359         d->footer->setParentItem(nullptr);
360     }
361     d->footer = footer;
362     if (footer) {
363         footer->setParentItem(this);
364         QQuickItemPrivate::get(footer)->addItemChangeListener(d, LayoutChanges);
365         if (qFuzzyIsNull(footer->z()))
366             footer->setZ(1);
367         setPos(footer, Footer);
368     }
369     if (isComponentComplete())
370         d->relayout();
371     emit footerChanged();
372 }
373 
374 /*!
375     \since QtQuick.Controls 2.5 (Qt 5.12)
376     \qmlproperty real QtQuick.Controls::Page::implicitHeaderWidth
377     \readonly
378 
379     This property holds the implicit header width.
380 
381     The value is equal to \c {header && header.visible ? header.implicitWidth : 0}.
382 
383     \sa implicitHeaderHeight, implicitFooterWidth
384 */
implicitHeaderWidth() const385 qreal QQuickPage::implicitHeaderWidth() const
386 {
387     Q_D(const QQuickPage);
388     if (!d->header || !d->header->isVisible())
389         return 0;
390     return d->header->implicitWidth();
391 }
392 
393 /*!
394     \since QtQuick.Controls 2.5 (Qt 5.12)
395     \qmlproperty real QtQuick.Controls::Page::implicitHeaderHeight
396     \readonly
397 
398     This property holds the implicit header height.
399 
400     The value is equal to \c {header && header.visible ? header.implicitHeight : 0}.
401 
402     \sa implicitHeaderWidth, implicitFooterHeight
403 */
implicitHeaderHeight() const404 qreal QQuickPage::implicitHeaderHeight() const
405 {
406     Q_D(const QQuickPage);
407     if (!d->header || !d->header->isVisible())
408         return 0;
409     return d->header->implicitHeight();
410 }
411 
412 /*!
413     \since QtQuick.Controls 2.5 (Qt 5.12)
414     \qmlproperty real QtQuick.Controls::Page::implicitFooterWidth
415     \readonly
416 
417     This property holds the implicit footer width.
418 
419     The value is equal to \c {footer && footer.visible ? footer.implicitWidth : 0}.
420 
421     \sa implicitFooterHeight, implicitHeaderWidth
422 */
implicitFooterWidth() const423 qreal QQuickPage::implicitFooterWidth() const
424 {
425     Q_D(const QQuickPage);
426     if (!d->footer || !d->footer->isVisible())
427         return 0;
428     return d->footer->implicitWidth();
429 }
430 
431 /*!
432     \since QtQuick.Controls 2.5 (Qt 5.12)
433     \qmlproperty real QtQuick.Controls::Page::implicitFooterHeight
434     \readonly
435 
436     This property holds the implicit footer height.
437 
438     The value is equal to \c {footer && footer.visible ? footer.implicitHeight : 0}.
439 
440     \sa implicitFooterWidth, implicitHeaderHeight
441 */
implicitFooterHeight() const442 qreal QQuickPage::implicitFooterHeight() const
443 {
444     Q_D(const QQuickPage);
445     if (!d->footer || !d->footer->isVisible())
446         return 0;
447     return d->footer->implicitHeight();
448 }
449 
componentComplete()450 void QQuickPage::componentComplete()
451 {
452     Q_D(QQuickPage);
453     QQuickPane::componentComplete();
454     d->relayout();
455 }
456 
spacingChange(qreal newSpacing,qreal oldSpacing)457 void QQuickPage::spacingChange(qreal newSpacing, qreal oldSpacing)
458 {
459     Q_D(QQuickPage);
460     QQuickPane::spacingChange(newSpacing, oldSpacing);
461     d->relayout();
462 }
463 
464 #if QT_CONFIG(accessibility)
accessibleRole() const465 QAccessible::Role QQuickPage::accessibleRole() const
466 {
467     return QAccessible::PageTab;
468 }
469 
accessibilityActiveChanged(bool active)470 void QQuickPage::accessibilityActiveChanged(bool active)
471 {
472     Q_D(QQuickPage);
473     QQuickPane::accessibilityActiveChanged(active);
474 
475     if (active)
476         maybeSetAccessibleName(d->title);
477 }
478 #endif
479 
480 QT_END_NAMESPACE
481