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

source code of qtdeclarative/src/quicktemplates/qquickpage.cpp