Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquicktabbar.cpp
Go to the documentation of this file.
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// Qt-Security score:significant reason:default
4
8
9#include <QtQuick/private/qquickitemview_p.h>
10
12
13/*!
14 \qmltype TabBar
15 \inherits Container
16//! \nativetype QQuickTabBar
17 \inqmlmodule QtQuick.Controls
18 \since 5.7
19 \ingroup qtquickcontrols-navigation
20 \ingroup qtquickcontrols-containers
21 \ingroup qtquickcontrols-focusscopes
22 \brief Allows the user to switch between different views or subtasks.
23
24 TabBar provides a tab-based navigation model.
25
26 \image qtquickcontrols-tabbar-wireframe.webp
27 {Tab bar wireframe showing tab navigation}
28
29 TabBar is populated with TabButton controls, and can be used together with
30 any layout or container control that provides \c currentIndex -property,
31 such as \l StackLayout or \l SwipeView
32
33 \snippet qtquickcontrols-tabbar.qml 1
34
35 As shown above, TabBar is typically populated with a static set of tab buttons
36 that are defined inline as children of the tab bar. It is also possible to
37 \l {Container::addItem()}{add}, \l {Container::insertItem()}{insert},
38 \l {Container::moveItem()}{move}, and \l {Container::removeItem()}{remove}
39 items dynamically at run time. The items can be accessed using
40 \l {Container::}{itemAt()} or \l {Container::}{contentChildren}.
41
42 \include container-currentindex.qdocinc {file} {TabBar} {SwipeView}
43
44 \section2 Resizing Tabs
45
46 By default, TabBar resizes its buttons to fit the width of the control.
47 The available space is distributed equally to each button. The default
48 resizing behavior can be overridden by setting an explicit width for the
49 buttons.
50
51 The following example illustrates how to keep each tab button at their
52 implicit size instead of being resized to fit the tabbar:
53
54 \image qtquickcontrols-tabbar-explicit.png
55 {Tab bar with First, Second, Third tabs at implicit widths}
56
57 \snippet qtquickcontrols-tabbar-explicit.qml 1
58
59 \section2 Flickable Tabs
60
61 If the total width of the buttons exceeds the available width of the tab bar,
62 it automatically becomes flickable.
63
64 \image qtquickcontrols-tabbar-flickable.png
65 {Tab bar in flickable for scrolling many tabs}
66
67 \snippet qtquickcontrols-tabbar-flickable.qml 1
68
69 \section2 Vertical Tabs
70
71 TabBar can be laid out vertically by setting \l orientation to
72 \c Qt.Vertical. In that case, the tab buttons are stacked in a vertical
73 list, and \l {Resizing Tabs}{resizing} is done based on the available
74 height instead of width.
75
76 \sa TabButton, {Customizing TabBar}, {Navigation Controls}, {Container Controls},
77 {Focus Management in Qt Quick Controls}
78*/
79
81{
82public:
83 Q_DECLARE_PUBLIC(QQuickTabBar)
84
88
89 qreal getContentWidth() const override;
90 qreal getContentHeight() const override;
91
92 void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &diff) override;
93 void itemImplicitWidthChanged(QQuickItem *item) override;
94 void itemImplicitHeightChanged(QQuickItem *item) override;
95
96 QPalette defaultPalette() const override { return QQuickTheme::palette(QQuickTheme::TabBar); }
97
98 bool updatingLayout = false;
101#if QT_CONFIG(wheelevent)
103#endif
104};
105
107{
108 Q_DECLARE_PUBLIC(QQuickTabBarAttached)
109
110public:
115
117
118 int index = -1;
120};
121
122void QQuickTabBarPrivate::updateCurrentItem()
123{
124 QQuickTabButton *button = qobject_cast<QQuickTabButton *>(contentModel->get(currentIndex));
125 if (button)
126 button->setChecked(true);
127}
128
130{
131 Q_Q(QQuickTabBar);
132 QQuickTabButton *button = qobject_cast<QQuickTabButton *>(q->sender());
133 if (button && button->isChecked())
134 q->setCurrentIndex(contentModel->indexOf(button, nullptr));
135}
136
138{
139 Q_Q(QQuickTabBar);
140 const int count = contentModel->count();
141 if (count <= 0 || !contentItem)
142 return;
143
144 const bool horizontal = orientation == Qt::Horizontal;
145 qreal reservedSize = 0;
146 int resizableCount = 0;
147
148 QList<QQuickItem *> allItems;
149 allItems.reserve(count);
150
151 for (int i = 0; i < count; ++i) {
152 QQuickItem *item = q->itemAt(i);
153 if (item) {
154 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
155 const bool sizeValid = horizontal ? p->widthValid() : p->heightValid();
156 if (!sizeValid)
157 ++resizableCount;
158 else
159 reservedSize += horizontal ? item->width() : item->height();
160 allItems += item;
161 }
162 }
163
164 const qreal totalSpacing = qMax(0, count - 1) * spacing;
165 const qreal availableSize = horizontal ? contentItem->width() : contentItem->height();
166 const qreal itemSize = (availableSize - reservedSize - totalSpacing) / qMax(1, resizableCount);
167
168 updatingLayout = true;
169 for (QQuickItem *item : std::as_const(allItems)) {
170 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
171 if (horizontal) {
172 if (!p->widthValid()) {
173 item->setWidth(itemSize);
174 p->widthValidFlag = false;
175 }
176 if (!p->heightValid()) {
177 item->setHeight(contentHeight);
178 p->heightValidFlag = false;
179 } else {
180 item->setY((contentHeight - item->height()) / 2);
181 }
182 } else {
183 if (!p->heightValid()) {
184 item->setHeight(itemSize);
185 p->heightValidFlag = false;
186 }
187 if (!p->widthValid()) {
188 item->setWidth(contentWidth);
189 p->widthValidFlag = false;
190 } else {
191 item->setX((contentWidth - item->width()) / 2);
192 }
193 }
194 }
195 updatingLayout = false;
196}
197
199{
200 if (hasContentWidth)
201 return contentWidth;
202
203 Q_Q(const QQuickTabBar);
204 const int count = contentModel->count();
205 if (orientation == Qt::Vertical) {
206 qreal maxWidth = 0;
207 for (int i = 0; i < count; ++i) {
208 QQuickItem *item = q->itemAt(i);
209 if (item)
210 maxWidth = qMax(maxWidth, item->implicitWidth());
211 }
212 return maxWidth;
213 }
214
215 qreal totalWidth = qMax(0, count - 1) * spacing;
216 for (int i = 0; i < count; ++i) {
217 QQuickItem *item = q->itemAt(i);
218 if (item) {
219 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
220 if (!p->widthValid())
221 totalWidth += item->implicitWidth();
222 else
223 totalWidth += item->width();
224 }
225 }
226 return totalWidth;
227}
228
230{
231 if (hasContentHeight)
232 return contentHeight;
233
234 Q_Q(const QQuickTabBar);
235 const int count = contentModel->count();
236 if (orientation == Qt::Vertical) {
237 qreal totalHeight = qMax(0, count - 1) * spacing;
238 for (int i = 0; i < count; ++i) {
239 QQuickItem *item = q->itemAt(i);
240 if (item) {
241 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
242 if (!p->heightValid())
243 totalHeight += item->implicitHeight();
244 else
245 totalHeight += item->height();
246 }
247 }
248 return totalHeight;
249 }
250
251 qreal maxHeight = 0;
252 for (int i = 0; i < count; ++i) {
253 QQuickItem *item = q->itemAt(i);
254 if (item)
255 maxHeight = qMax(maxHeight, item->implicitHeight());
256 }
257 return maxHeight;
258}
259
260void QQuickTabBarPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &diff)
261{
262 QQuickContainerPrivate::itemGeometryChanged(item, change, diff);
263 if (!updatingLayout) {
264 if (change.sizeChange())
265 updateImplicitContentSize();
267 }
268}
269
271{
272 QQuickContainerPrivate::itemImplicitWidthChanged(item);
273 if (item != contentItem)
274 updateImplicitContentWidth();
275}
276
278{
279 QQuickContainerPrivate::itemImplicitHeightChanged(item);
280 if (item != contentItem)
281 updateImplicitContentHeight();
282}
283
284QQuickTabBar::QQuickTabBar(QQuickItem *parent)
285 : QQuickContainer(*(new QQuickTabBarPrivate), parent)
286{
287 Q_D(QQuickTabBar);
288 d->changeTypes |= QQuickItemPrivate::Geometry | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight;
289 setFlag(ItemIsFocusScope);
290 QObjectPrivate::connect(this, &QQuickTabBar::currentIndexChanged, d, &QQuickTabBarPrivate::updateCurrentItem);
291}
292
293/*!
294 \qmlproperty enumeration QtQuick.Controls::TabBar::position
295
296 This property holds the position of the tab bar.
297
298 \note If the tab bar is assigned as a header or footer of \l ApplicationWindow
299 or \l Page, the appropriate position is set automatically.
300
301 Possible values:
302 \value TabBar.Header The tab bar is at the top, as a window or page header.
303 \value TabBar.Footer The tab bar is at the bottom, as a window or page footer.
304
305 The default value is style-specific.
306
307 \sa ApplicationWindow::header, ApplicationWindow::footer, Page::header, Page::footer
308*/
309QQuickTabBar::Position QQuickTabBar::position() const
310{
311 Q_D(const QQuickTabBar);
312 return d->position;
313}
314
315void QQuickTabBar::setPosition(Position position)
316{
317 Q_D(QQuickTabBar);
318 if (d->position == position)
319 return;
320
321 d->position = position;
322 emit positionChanged();
323}
324
325/*!
326 \since QtQuick.Controls 6.13
327 \qmlproperty enumeration QtQuick.Controls::TabBar::orientation
328
329 This property holds the orientation of the tab bar.
330
331 Possible values:
332 \value Qt.Horizontal Horizontal (default)
333 \value Qt.Vertical Vertical
334
335 \sa horizontal, vertical
336*/
337Qt::Orientation QQuickTabBar::orientation() const
338{
339 Q_D(const QQuickTabBar);
340 return d->orientation;
341}
342
343void QQuickTabBar::setOrientation(Qt::Orientation orientation)
344{
345 Q_D(QQuickTabBar);
346 if (d->orientation == orientation)
347 return;
348
349 d->orientation = orientation;
350 d->updateImplicitContentSize();
351 if (isComponentComplete())
352 polish();
353 emit orientationChanged();
354}
355
356/*!
357 \since QtQuick.Controls 6.13
358 \qmlproperty bool QtQuick.Controls::TabBar::horizontal
359 \readonly
360
361 This property holds whether the tab bar is horizontal.
362
363 \sa orientation
364*/
365bool QQuickTabBar::isHorizontal() const
366{
367 Q_D(const QQuickTabBar);
368 return d->orientation == Qt::Horizontal;
369}
370
371/*!
372 \since QtQuick.Controls 6.13
373 \qmlproperty bool QtQuick.Controls::TabBar::vertical
374 \readonly
375
376 This property holds whether the tab bar is vertical.
377
378 \sa orientation
379*/
380bool QQuickTabBar::isVertical() const
381{
382 Q_D(const QQuickTabBar);
383 return d->orientation == Qt::Vertical;
384}
385
386/*!
387 \since QtQuick.Controls 2.2 (Qt 5.9)
388 \qmlproperty real QtQuick.Controls::TabBar::contentWidth
389
390 This property holds the content width. It is used for calculating the total
391 implicit width of the tab bar.
392
393 \note This property is available in TabBar since \l{QtQuick.Controls} 2.2 (Qt 5.9),
394 but it was promoted to the Container base type in \l{QtQuick.Controls} 2.5 (Qt 5.12).
395
396 \sa Container::contentWidth
397*/
398
399/*!
400 \since QtQuick.Controls 2.2 (Qt 5.9)
401 \qmlproperty real QtQuick.Controls::TabBar::contentHeight
402
403 This property holds the content height. It is used for calculating the total
404 implicit height of the tab bar.
405
406 \note This property is available in TabBar since \l{QtQuick.Controls} 2.2 (Qt 5.9),
407 but it was promoted to the Container base type in \l{QtQuick.Controls} 2.5 (Qt 5.12).
408
409 \sa Container::contentHeight
410*/
411
412QQuickTabBarAttached *QQuickTabBar::qmlAttachedProperties(QObject *object)
413{
414 return new QQuickTabBarAttached(object);
415}
416
417void QQuickTabBar::updatePolish()
418{
419 Q_D(QQuickTabBar);
420 QQuickContainer::updatePolish();
421 d->updateLayout();
422}
423
424void QQuickTabBar::componentComplete()
425{
426 Q_D(QQuickTabBar);
427 QQuickContainer::componentComplete();
428 d->updateCurrentItem();
429 d->updateLayout();
430}
431
432void QQuickTabBar::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
433{
434 Q_D(QQuickTabBar);
435 QQuickContainer::geometryChange(newGeometry, oldGeometry);
436 d->updateLayout();
437}
438
439bool QQuickTabBar::isContent(QQuickItem *item) const
440{
441 return qobject_cast<QQuickTabButton *>(item);
442}
443
444void QQuickTabBar::itemAdded(int index, QQuickItem *item)
445{
446 Q_D(QQuickTabBar);
447 Q_UNUSED(index);
448 // Cull the tab so that it stays hidden until the view shows it (QTBUG-55129). Only do this
449 // for views, as nothing else would uncull it. A Repeater, for example, does uncull an item when
450 // it creates it, but for a tab added after the Repeater was created, that happens before this
451 // function is called. If we culled the tab here, it would stay culled, and hence invisible.
452 if (qobject_cast<QQuickItemView *>(d->contentItem))
453 QQuickItemPrivate::get(item)->setCulled(true);
454 if (QQuickTabButton *button = qobject_cast<QQuickTabButton *>(item))
455 QObjectPrivate::connect(button, &QQuickTabButton::checkedChanged, d, &QQuickTabBarPrivate::updateCurrentIndex);
456 QQuickTabBarAttached *attached = qobject_cast<QQuickTabBarAttached *>(qmlAttachedPropertiesObject<QQuickTabBar>(item));
457 if (attached)
458 QQuickTabBarAttachedPrivate::get(attached)->update(this, index);
459 d->updateImplicitContentSize();
460 if (isComponentComplete())
461 polish();
462}
463
464void QQuickTabBar::itemMoved(int index, QQuickItem *item)
465{
466 QQuickTabBarAttached *attached = qobject_cast<QQuickTabBarAttached *>(qmlAttachedPropertiesObject<QQuickTabBar>(item));
467 if (attached)
468 QQuickTabBarAttachedPrivate::get(attached)->update(this, index);
469}
470
471void QQuickTabBar::itemRemoved(int index, QQuickItem *item)
472{
473 Q_D(QQuickTabBar);
474 Q_UNUSED(index);
475 if (QQuickTabButton *button = qobject_cast<QQuickTabButton *>(item))
476 QObjectPrivate::disconnect(button, &QQuickTabButton::checkedChanged, d, &QQuickTabBarPrivate::updateCurrentIndex);
477 QQuickTabBarAttached *attached = qobject_cast<QQuickTabBarAttached *>(qmlAttachedPropertiesObject<QQuickTabBar>(item));
478 if (attached)
479 QQuickTabBarAttachedPrivate::get(attached)->update(nullptr, -1);
480 d->updateImplicitContentSize();
481 if (isComponentComplete())
482 polish();
483}
484
485#if QT_CONFIG(wheelevent)
486void QQuickTabBar::wheelEvent(QWheelEvent *event)
487{
488 Q_D(QQuickTabBar);
489 QQuickContainer::wheelEvent(event);
490 if (d->wheelEnabled) {
491 d->accumulatedAngleDelta += event->angleDelta();
492 int xSteps = d->accumulatedAngleDelta.x() / QWheelEvent::DefaultDeltasPerStep;
493 int ySteps = d->accumulatedAngleDelta.y() / QWheelEvent::DefaultDeltasPerStep;
494 if (xSteps > 0 || ySteps > 0) {
495 decrementCurrentIndex();
496 d->accumulatedAngleDelta = QPoint();
497 } else if (xSteps < 0 || ySteps < 0) {
498 incrementCurrentIndex();
499 d->accumulatedAngleDelta = QPoint();
500 }
501 }
502}
503#endif
504
505QFont QQuickTabBar::defaultFont() const
506{
507 return QQuickTheme::font(QQuickTheme::TabBar);
508}
509
510#if QT_CONFIG(accessibility)
511QAccessible::Role QQuickTabBar::accessibleRole() const
512{
513 return QAccessible::PageTabList;
514}
515#endif
516
517/*!
518 \qmlattachedproperty int QtQuick.Controls::TabBar::index
519 \since QtQuick.Controls 2.3 (Qt 5.10)
520 \readonly
521
522 This attached property holds the index of each tab button in the TabBar.
523
524 It is attached to each tab button of the TabBar.
525*/
526
527/*!
528 \qmlattachedproperty TabBar QtQuick.Controls::TabBar::tabBar
529 \since QtQuick.Controls 2.3 (Qt 5.10)
530 \readonly
531
532 This attached property holds the tab bar that manages this tab button.
533
534 It is attached to each tab button of the TabBar.
535*/
536
537/*!
538 \qmlattachedproperty enumeration QtQuick.Controls::TabBar::position
539 \since QtQuick.Controls 2.3 (Qt 5.10)
540 \readonly
541
542 This attached property holds the position of the tab bar.
543
544 It is attached to each tab button of the TabBar.
545
546 Possible values:
547 \value TabBar.Header The tab bar is at the top, as a window or page header.
548 \value TabBar.Footer The tab bar is at the bottom, as a window or page footer.
549*/
550
551void QQuickTabBarAttachedPrivate::update(QQuickTabBar *newTabBar, int newIndex)
552{
553 Q_Q(QQuickTabBarAttached);
554 const int oldIndex = index;
555 const QQuickTabBar *oldTabBar = tabBar;
556 const QQuickTabBar::Position oldPos = q->position();
557
558 index = newIndex;
559 tabBar = newTabBar;
560
561 if (oldTabBar != newTabBar) {
562 if (oldTabBar)
563 QObject::disconnect(oldTabBar, &QQuickTabBar::positionChanged, q, &QQuickTabBarAttached::positionChanged);
564 if (newTabBar)
565 QObject::connect(newTabBar, &QQuickTabBar::positionChanged, q, &QQuickTabBarAttached::positionChanged);
566 emit q->tabBarChanged();
567 }
568
569 if (oldIndex != newIndex)
570 emit q->indexChanged();
571 if (oldPos != q->position())
572 emit q->positionChanged();
573}
574
575QQuickTabBarAttached::QQuickTabBarAttached(QObject *parent)
576 : QObject(*(new QQuickTabBarAttachedPrivate), parent)
577{
578}
579
580int QQuickTabBarAttached::index() const
581{
582 Q_D(const QQuickTabBarAttached);
583 return d->index;
584}
585
586QQuickTabBar *QQuickTabBarAttached::tabBar() const
587{
588 Q_D(const QQuickTabBarAttached);
589 return d->tabBar;
590}
591
592QQuickTabBar::Position QQuickTabBarAttached::position() const
593{
594 Q_D(const QQuickTabBarAttached);
595 if (!d->tabBar)
596 return QQuickTabBar::Header;
597 return d->tabBar->position();
598}
599
600QT_END_NAMESPACE
601
602#include "moc_qquicktabbar_p.cpp"
Allows the user to switch between different views or subtasks.
void itemImplicitHeightChanged(QQuickItem *item) override
QPalette defaultPalette() const override
qreal getContentHeight() const override
Qt::Orientation orientation
void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &diff) override
void itemImplicitWidthChanged(QQuickItem *item) override
qreal getContentWidth() const override
Combined button and popup list for selecting options.