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 | |
12 | QT_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.png |
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 | |
71 | static const QQuickItemPrivate::ChangeTypes LayoutChanges = QQuickItemPrivate::Geometry | QQuickItemPrivate::Visibility | QQuickItemPrivate::Destroyed |
72 | | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight; |
73 | |
74 | namespace { |
75 | enum Position { |
76 | Header, |
77 | Footer |
78 | }; |
79 | |
80 | Q_STATIC_ASSERT(int(Header) == int(QQuickToolBar::Header)); |
81 | Q_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 | |
104 | void 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 | |
130 | void QQuickPagePrivate::resizeContent() |
131 | { |
132 | relayout(); |
133 | } |
134 | |
135 | void QQuickPagePrivate::itemVisibilityChanged(QQuickItem *item) |
136 | { |
137 | Q_Q(QQuickPage); |
138 | QQuickPanePrivate::itemVisibilityChanged(item); |
139 | if (item == header) { |
140 | QBoolBlocker signalGuard(emittingImplicitSizeChangedSignals); |
141 | emit q->implicitHeaderWidthChanged(); |
142 | emit q->implicitHeaderHeightChanged(); |
143 | relayout(); |
144 | } else if (item == footer) { |
145 | QBoolBlocker signalGuard(emittingImplicitSizeChangedSignals); |
146 | emit q->implicitFooterWidthChanged(); |
147 | emit q->implicitFooterHeightChanged(); |
148 | relayout(); |
149 | } |
150 | } |
151 | |
152 | void QQuickPagePrivate::itemImplicitWidthChanged(QQuickItem *item) |
153 | { |
154 | Q_Q(QQuickPage); |
155 | QQuickPanePrivate::itemImplicitWidthChanged(item); |
156 | |
157 | // Avoid binding loops by skipping signal emission if we're already doing it. |
158 | if (emittingImplicitSizeChangedSignals) |
159 | return; |
160 | |
161 | if (item == header) |
162 | emit q->implicitHeaderWidthChanged(); |
163 | else if (item == footer) |
164 | emit q->implicitFooterWidthChanged(); |
165 | } |
166 | |
167 | void QQuickPagePrivate::itemImplicitHeightChanged(QQuickItem *item) |
168 | { |
169 | Q_Q(QQuickPage); |
170 | QQuickPanePrivate::itemImplicitHeightChanged(item); |
171 | |
172 | // Avoid binding loops by skipping signal emission if we're already doing it. |
173 | if (emittingImplicitSizeChangedSignals) |
174 | return; |
175 | |
176 | if (item == header) |
177 | emit q->implicitHeaderHeightChanged(); |
178 | else if (item == footer) |
179 | emit q->implicitFooterHeightChanged(); |
180 | } |
181 | |
182 | void QQuickPagePrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF & diff) |
183 | { |
184 | QQuickPanePrivate::itemGeometryChanged(item, change, diff); |
185 | if (item == header || item == footer) |
186 | relayout(); |
187 | } |
188 | |
189 | void QQuickPagePrivate::itemDestroyed(QQuickItem *item) |
190 | { |
191 | Q_Q(QQuickPage); |
192 | QQuickPanePrivate::itemDestroyed(item); |
193 | if (item == header) { |
194 | header = nullptr; |
195 | relayout(); |
196 | emit q->implicitHeaderWidthChanged(); |
197 | emit q->implicitHeaderHeightChanged(); |
198 | emit q->headerChanged(); |
199 | } else if (item == footer) { |
200 | footer = nullptr; |
201 | relayout(); |
202 | emit q->implicitFooterWidthChanged(); |
203 | emit q->implicitFooterHeightChanged(); |
204 | emit q->footerChanged(); |
205 | } |
206 | } |
207 | |
208 | QQuickPage::QQuickPage(QQuickItem *parent) |
209 | : QQuickPane(*(new QQuickPagePrivate), parent) |
210 | { |
211 | } |
212 | |
213 | QQuickPage::QQuickPage(QQuickPagePrivate &dd, QQuickItem *parent) |
214 | : QQuickPane(dd, parent) |
215 | { |
216 | } |
217 | |
218 | QQuickPage::~QQuickPage() |
219 | { |
220 | Q_D(QQuickPage); |
221 | if (d->header) |
222 | QQuickItemPrivate::get(item: d->header)->removeItemChangeListener(d, types: LayoutChanges); |
223 | if (d->footer) |
224 | QQuickItemPrivate::get(item: d->footer)->removeItemChangeListener(d, types: LayoutChanges); |
225 | } |
226 | |
227 | /*! |
228 | \qmlproperty string QtQuick.Controls::Page::title |
229 | |
230 | This property holds the page title. |
231 | |
232 | The title is often displayed at the top of a page to give |
233 | the user context about the page they are viewing. |
234 | |
235 | Page does not render the title itself, but instead relies |
236 | on the application to do so. For example: |
237 | |
238 | \code |
239 | ApplicationWindow { |
240 | visible: true |
241 | width: 400 |
242 | height: 400 |
243 | |
244 | header: Label { |
245 | text: view.currentItem.title |
246 | horizontalAlignment: Text.AlignHCenter |
247 | } |
248 | |
249 | SwipeView { |
250 | id: view |
251 | anchors.fill: parent |
252 | |
253 | Page { |
254 | title: qsTr("Home") |
255 | } |
256 | Page { |
257 | title: qsTr("Discover") |
258 | } |
259 | Page { |
260 | title: qsTr("Activity") |
261 | } |
262 | } |
263 | } |
264 | \endcode |
265 | */ |
266 | |
267 | QString QQuickPage::title() const |
268 | { |
269 | return d_func()->title; |
270 | } |
271 | |
272 | void QQuickPage::setTitle(const QString &title) |
273 | { |
274 | Q_D(QQuickPage); |
275 | if (d->title == title) |
276 | return; |
277 | |
278 | d->title = title; |
279 | maybeSetAccessibleName(name: title); |
280 | emit titleChanged(); |
281 | } |
282 | |
283 | void QQuickPage::resetTitle() |
284 | { |
285 | setTitle(QString()); |
286 | } |
287 | |
288 | /*! |
289 | \qmlproperty Item QtQuick.Controls::Page::header |
290 | |
291 | This property holds the page header item. The header item is positioned to |
292 | the top, and resized to the width of the page. The default value is \c null. |
293 | |
294 | \note Assigning a ToolBar, TabBar, or DialogButtonBox as a page header |
295 | automatically sets the respective \l ToolBar::position, \l TabBar::position, |
296 | or \l DialogButtonBox::position property to \c Header. |
297 | |
298 | \sa footer, ApplicationWindow::header |
299 | */ |
300 | QQuickItem *QQuickPage::header() const |
301 | { |
302 | Q_D(const QQuickPage); |
303 | return d->header; |
304 | } |
305 | |
306 | void QQuickPage::setHeader(QQuickItem *header) |
307 | { |
308 | Q_D(QQuickPage); |
309 | if (d->header == header) |
310 | return; |
311 | |
312 | if (d->header) { |
313 | QQuickItemPrivate::get(item: d->header)->removeItemChangeListener(d, types: LayoutChanges); |
314 | d->header->setParentItem(nullptr); |
315 | } |
316 | d->header = header; |
317 | if (header) { |
318 | header->setParentItem(this); |
319 | QQuickItemPrivate::get(item: header)->addItemChangeListener(listener: d, types: LayoutChanges); |
320 | if (qFuzzyIsNull(d: header->z())) |
321 | header->setZ(1); |
322 | setPos(item: header, position: Header); |
323 | } |
324 | if (isComponentComplete()) |
325 | d->relayout(); |
326 | emit headerChanged(); |
327 | } |
328 | |
329 | /*! |
330 | \qmlproperty Item QtQuick.Controls::Page::footer |
331 | |
332 | This property holds the page footer item. The footer item is positioned to |
333 | the bottom, and resized to the width of the page. The default value is \c null. |
334 | |
335 | \note Assigning a ToolBar, TabBar, or DialogButtonBox as a page footer |
336 | automatically sets the respective \l ToolBar::position, \l TabBar::position, |
337 | or \l DialogButtonBox::position property to \c Footer. |
338 | |
339 | \sa header, ApplicationWindow::footer |
340 | */ |
341 | QQuickItem *QQuickPage::footer() const |
342 | { |
343 | Q_D(const QQuickPage); |
344 | return d->footer; |
345 | } |
346 | |
347 | void QQuickPage::setFooter(QQuickItem *footer) |
348 | { |
349 | Q_D(QQuickPage); |
350 | if (d->footer == footer) |
351 | return; |
352 | |
353 | if (d->footer) { |
354 | QQuickItemPrivate::get(item: d->footer)->removeItemChangeListener(d, types: LayoutChanges); |
355 | d->footer->setParentItem(nullptr); |
356 | } |
357 | d->footer = footer; |
358 | if (footer) { |
359 | footer->setParentItem(this); |
360 | QQuickItemPrivate::get(item: footer)->addItemChangeListener(listener: d, types: LayoutChanges); |
361 | if (qFuzzyIsNull(d: footer->z())) |
362 | footer->setZ(1); |
363 | setPos(item: footer, position: Footer); |
364 | } |
365 | if (isComponentComplete()) |
366 | d->relayout(); |
367 | emit footerChanged(); |
368 | } |
369 | |
370 | /*! |
371 | \since QtQuick.Controls 2.5 (Qt 5.12) |
372 | \qmlproperty real QtQuick.Controls::Page::implicitHeaderWidth |
373 | \readonly |
374 | |
375 | This property holds the implicit header width. |
376 | |
377 | The value is equal to \c {header && header.visible ? header.implicitWidth : 0}. |
378 | |
379 | \sa implicitHeaderHeight, implicitFooterWidth |
380 | */ |
381 | qreal QQuickPage::implicitHeaderWidth() const |
382 | { |
383 | Q_D(const QQuickPage); |
384 | if (!d->header || !d->header->isVisible()) |
385 | return 0; |
386 | return d->header->implicitWidth(); |
387 | } |
388 | |
389 | /*! |
390 | \since QtQuick.Controls 2.5 (Qt 5.12) |
391 | \qmlproperty real QtQuick.Controls::Page::implicitHeaderHeight |
392 | \readonly |
393 | |
394 | This property holds the implicit header height. |
395 | |
396 | The value is equal to \c {header && header.visible ? header.implicitHeight : 0}. |
397 | |
398 | \sa implicitHeaderWidth, implicitFooterHeight |
399 | */ |
400 | qreal QQuickPage::implicitHeaderHeight() const |
401 | { |
402 | Q_D(const QQuickPage); |
403 | if (!d->header || !d->header->isVisible()) |
404 | return 0; |
405 | return d->header->implicitHeight(); |
406 | } |
407 | |
408 | /*! |
409 | \since QtQuick.Controls 2.5 (Qt 5.12) |
410 | \qmlproperty real QtQuick.Controls::Page::implicitFooterWidth |
411 | \readonly |
412 | |
413 | This property holds the implicit footer width. |
414 | |
415 | The value is equal to \c {footer && footer.visible ? footer.implicitWidth : 0}. |
416 | |
417 | \sa implicitFooterHeight, implicitHeaderWidth |
418 | */ |
419 | qreal QQuickPage::implicitFooterWidth() const |
420 | { |
421 | Q_D(const QQuickPage); |
422 | if (!d->footer || !d->footer->isVisible()) |
423 | return 0; |
424 | return d->footer->implicitWidth(); |
425 | } |
426 | |
427 | /*! |
428 | \since QtQuick.Controls 2.5 (Qt 5.12) |
429 | \qmlproperty real QtQuick.Controls::Page::implicitFooterHeight |
430 | \readonly |
431 | |
432 | This property holds the implicit footer height. |
433 | |
434 | The value is equal to \c {footer && footer.visible ? footer.implicitHeight : 0}. |
435 | |
436 | \sa implicitFooterWidth, implicitHeaderHeight |
437 | */ |
438 | qreal QQuickPage::implicitFooterHeight() const |
439 | { |
440 | Q_D(const QQuickPage); |
441 | if (!d->footer || !d->footer->isVisible()) |
442 | return 0; |
443 | return d->footer->implicitHeight(); |
444 | } |
445 | |
446 | void QQuickPage::componentComplete() |
447 | { |
448 | Q_D(QQuickPage); |
449 | QQuickPane::componentComplete(); |
450 | d->relayout(); |
451 | } |
452 | |
453 | void QQuickPage::spacingChange(qreal newSpacing, qreal oldSpacing) |
454 | { |
455 | Q_D(QQuickPage); |
456 | QQuickPane::spacingChange(newSpacing, oldSpacing); |
457 | d->relayout(); |
458 | } |
459 | |
460 | #if QT_CONFIG(accessibility) |
461 | QAccessible::Role QQuickPage::accessibleRole() const |
462 | { |
463 | return QAccessible::PageTab; |
464 | } |
465 | |
466 | void QQuickPage::accessibilityActiveChanged(bool active) |
467 | { |
468 | Q_D(QQuickPage); |
469 | QQuickPane::accessibilityActiveChanged(active); |
470 | |
471 | if (active) |
472 | maybeSetAccessibleName(name: d->title); |
473 | } |
474 | #endif |
475 | |
476 | QT_END_NAMESPACE |
477 | |
478 | #include "moc_qquickpage_p.cpp" |
479 |
Definitions
- LayoutChanges
- Position
- setPos
- relayout
- resizeContent
- itemVisibilityChanged
- itemImplicitWidthChanged
- itemImplicitHeightChanged
- itemGeometryChanged
- itemDestroyed
- QQuickPage
- QQuickPage
- ~QQuickPage
- title
- setTitle
- resetTitle
- header
- setHeader
- footer
- setFooter
- implicitHeaderWidth
- implicitHeaderHeight
- implicitFooterWidth
- implicitFooterHeight
- componentComplete
- spacingChange
- accessibleRole
Learn Advanced QML with KDAB
Find out more