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

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