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
43QT_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
89static const QQuickItemPrivate::ChangeTypes LayoutChanges = QQuickItemPrivate::Geometry | QQuickItemPrivate::Visibility | QQuickItemPrivate::Destroyed
90 | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight;
91
92namespace {
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
107 static void setPos(QQuickItem *item, Position position)
108 {
109 if (QQuickToolBar *toolBar = qobject_cast<QQuickToolBar *>(object: item))
110 toolBar->setPosition(static_cast<QQuickToolBar::Position>(position));
111 else if (QQuickTabBar *tabBar = qobject_cast<QQuickTabBar *>(object: item))
112 tabBar->setPosition(static_cast<QQuickTabBar::Position>(position));
113 else if (QQuickDialogButtonBox *buttonBox = qobject_cast<QQuickDialogButtonBox *>(object: item))
114 buttonBox->setPosition(static_cast<QQuickDialogButtonBox::Position>(position));
115 }
116}
117
118void 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
142void QQuickPagePrivate::resizeContent()
143{
144 relayout();
145}
146
147void 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
164void 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
179void 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
194void 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
201void 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
220QQuickPage::QQuickPage(QQuickItem *parent)
221 : QQuickPane(*(new QQuickPagePrivate), parent)
222{
223}
224
225QQuickPage::QQuickPage(QQuickPagePrivate &dd, QQuickItem *parent)
226 : QQuickPane(dd, parent)
227{
228}
229
230QQuickPage::~QQuickPage()
231{
232 Q_D(QQuickPage);
233 if (d->header)
234 QQuickItemPrivate::get(item: d->header)->removeItemChangeListener(d, types: LayoutChanges);
235 if (d->footer)
236 QQuickItemPrivate::get(item: d->footer)->removeItemChangeListener(d, types: 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 Page does not render the title itself, but instead relies
248 on the application to do so. For example:
249
250 \code
251 ApplicationWindow {
252 visible: true
253 width: 400
254 height: 400
255
256 header: Label {
257 text: view.currentItem.title
258 horizontalAlignment: Text.AlignHCenter
259 }
260
261 SwipeView {
262 id: view
263 anchors.fill: parent
264
265 Page {
266 title: qsTr("Home")
267 }
268 Page {
269 title: qsTr("Discover")
270 }
271 Page {
272 title: qsTr("Activity")
273 }
274 }
275 }
276 \endcode
277*/
278
279QString QQuickPage::title() const
280{
281 return d_func()->title;
282}
283
284void QQuickPage::setTitle(const QString &title)
285{
286 Q_D(QQuickPage);
287 if (d->title == title)
288 return;
289
290 d->title = title;
291 maybeSetAccessibleName(name: title);
292 emit titleChanged();
293}
294
295/*!
296 \qmlproperty Item QtQuick.Controls::Page::header
297
298 This property holds the page header item. The header item is positioned to
299 the top, and resized to the width of the page. The default value is \c null.
300
301 \note Assigning a ToolBar, TabBar, or DialogButtonBox as a page header
302 automatically sets the respective \l ToolBar::position, \l TabBar::position,
303 or \l DialogButtonBox::position property to \c Header.
304
305 \sa footer, ApplicationWindow::header
306*/
307QQuickItem *QQuickPage::header() const
308{
309 Q_D(const QQuickPage);
310 return d->header;
311}
312
313void QQuickPage::setHeader(QQuickItem *header)
314{
315 Q_D(QQuickPage);
316 if (d->header == header)
317 return;
318
319 if (d->header) {
320 QQuickItemPrivate::get(item: d->header)->removeItemChangeListener(d, types: LayoutChanges);
321 d->header->setParentItem(nullptr);
322 }
323 d->header = header;
324 if (header) {
325 header->setParentItem(this);
326 QQuickItemPrivate::get(item: header)->addItemChangeListener(listener: d, types: LayoutChanges);
327 if (qFuzzyIsNull(d: header->z()))
328 header->setZ(1);
329 setPos(item: header, position: Header);
330 }
331 if (isComponentComplete())
332 d->relayout();
333 emit headerChanged();
334}
335
336/*!
337 \qmlproperty Item QtQuick.Controls::Page::footer
338
339 This property holds the page footer item. The footer item is positioned to
340 the bottom, and resized to the width of the page. The default value is \c null.
341
342 \note Assigning a ToolBar, TabBar, or DialogButtonBox as a page footer
343 automatically sets the respective \l ToolBar::position, \l TabBar::position,
344 or \l DialogButtonBox::position property to \c Footer.
345
346 \sa header, ApplicationWindow::footer
347*/
348QQuickItem *QQuickPage::footer() const
349{
350 Q_D(const QQuickPage);
351 return d->footer;
352}
353
354void QQuickPage::setFooter(QQuickItem *footer)
355{
356 Q_D(QQuickPage);
357 if (d->footer == footer)
358 return;
359
360 if (d->footer) {
361 QQuickItemPrivate::get(item: d->footer)->removeItemChangeListener(d, types: LayoutChanges);
362 d->footer->setParentItem(nullptr);
363 }
364 d->footer = footer;
365 if (footer) {
366 footer->setParentItem(this);
367 QQuickItemPrivate::get(item: footer)->addItemChangeListener(listener: d, types: LayoutChanges);
368 if (qFuzzyIsNull(d: footer->z()))
369 footer->setZ(1);
370 setPos(item: footer, position: Footer);
371 }
372 if (isComponentComplete())
373 d->relayout();
374 emit footerChanged();
375}
376
377/*!
378 \since QtQuick.Controls 2.5 (Qt 5.12)
379 \qmlproperty real QtQuick.Controls::Page::implicitHeaderWidth
380 \readonly
381
382 This property holds the implicit header width.
383
384 The value is equal to \c {header && header.visible ? header.implicitWidth : 0}.
385
386 \sa implicitHeaderHeight, implicitFooterWidth
387*/
388qreal QQuickPage::implicitHeaderWidth() const
389{
390 Q_D(const QQuickPage);
391 if (!d->header || !d->header->isVisible())
392 return 0;
393 return d->header->implicitWidth();
394}
395
396/*!
397 \since QtQuick.Controls 2.5 (Qt 5.12)
398 \qmlproperty real QtQuick.Controls::Page::implicitHeaderHeight
399 \readonly
400
401 This property holds the implicit header height.
402
403 The value is equal to \c {header && header.visible ? header.implicitHeight : 0}.
404
405 \sa implicitHeaderWidth, implicitFooterHeight
406*/
407qreal QQuickPage::implicitHeaderHeight() const
408{
409 Q_D(const QQuickPage);
410 if (!d->header || !d->header->isVisible())
411 return 0;
412 return d->header->implicitHeight();
413}
414
415/*!
416 \since QtQuick.Controls 2.5 (Qt 5.12)
417 \qmlproperty real QtQuick.Controls::Page::implicitFooterWidth
418 \readonly
419
420 This property holds the implicit footer width.
421
422 The value is equal to \c {footer && footer.visible ? footer.implicitWidth : 0}.
423
424 \sa implicitFooterHeight, implicitHeaderWidth
425*/
426qreal QQuickPage::implicitFooterWidth() const
427{
428 Q_D(const QQuickPage);
429 if (!d->footer || !d->footer->isVisible())
430 return 0;
431 return d->footer->implicitWidth();
432}
433
434/*!
435 \since QtQuick.Controls 2.5 (Qt 5.12)
436 \qmlproperty real QtQuick.Controls::Page::implicitFooterHeight
437 \readonly
438
439 This property holds the implicit footer height.
440
441 The value is equal to \c {footer && footer.visible ? footer.implicitHeight : 0}.
442
443 \sa implicitFooterWidth, implicitHeaderHeight
444*/
445qreal QQuickPage::implicitFooterHeight() const
446{
447 Q_D(const QQuickPage);
448 if (!d->footer || !d->footer->isVisible())
449 return 0;
450 return d->footer->implicitHeight();
451}
452
453void QQuickPage::componentComplete()
454{
455 Q_D(QQuickPage);
456 QQuickPane::componentComplete();
457 d->relayout();
458}
459
460void QQuickPage::spacingChange(qreal newSpacing, qreal oldSpacing)
461{
462 Q_D(QQuickPage);
463 QQuickPane::spacingChange(newSpacing, oldSpacing);
464 d->relayout();
465}
466
467#if QT_CONFIG(accessibility)
468QAccessible::Role QQuickPage::accessibleRole() const
469{
470 return QAccessible::PageTab;
471}
472
473void QQuickPage::accessibilityActiveChanged(bool active)
474{
475 Q_D(QQuickPage);
476 QQuickPane::accessibilityActiveChanged(active);
477
478 if (active)
479 maybeSetAccessibleName(name: d->title);
480}
481#endif
482
483QT_END_NAMESPACE
484

source code of qtquickcontrols2/src/quicktemplates2/qquickpage.cpp