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