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