aboutsummaryrefslogtreecommitdiffstats
path: root/src/quicktemplates/qquicktabbar.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/quicktemplates/qquicktabbar.cpp')
-rw-r--r--src/quicktemplates/qquicktabbar.cpp475
1 files changed, 475 insertions, 0 deletions
diff --git a/src/quicktemplates/qquicktabbar.cpp b/src/quicktemplates/qquicktabbar.cpp
new file mode 100644
index 0000000000..2ea62e4e89
--- /dev/null
+++ b/src/quicktemplates/qquicktabbar.cpp
@@ -0,0 +1,475 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qquicktabbar_p.h"
+#include "qquicktabbutton_p.h"
+#include "qquickcontainer_p_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype TabBar
+ \inherits Container
+//! \instantiates QQuickTabBar
+ \inqmlmodule QtQuick.Controls
+ \since 5.7
+ \ingroup qtquickcontrols-navigation
+ \ingroup qtquickcontrols-containers
+ \ingroup qtquickcontrols-focusscopes
+ \brief Allows the user to switch between different views or subtasks.
+
+ TabBar provides a tab-based navigation model.
+
+ \image qtquickcontrols-tabbar-wireframe.png
+
+ TabBar is populated with TabButton controls, and can be used together with
+ any layout or container control that provides \c currentIndex -property,
+ such as \l StackLayout or \l SwipeView
+
+ \snippet qtquickcontrols-tabbar.qml 1
+
+ As shown above, TabBar is typically populated with a static set of tab buttons
+ that are defined inline as children of the tab bar. It is also possible to
+ \l {Container::addItem()}{add}, \l {Container::insertItem()}{insert},
+ \l {Container::moveItem()}{move}, and \l {Container::removeItem()}{remove}
+ items dynamically at run time. The items can be accessed using
+ \l {Container::}{itemAt()} or \l {Container::}{contentChildren}.
+
+ \include container-currentindex.qdocinc {file} {TabBar} {SwipeView}
+
+ \section2 Resizing Tabs
+
+ By default, TabBar resizes its buttons to fit the width of the control.
+ The available space is distributed equally to each button. The default
+ resizing behavior can be overridden by setting an explicit width for the
+ buttons.
+
+ The following example illustrates how to keep each tab button at their
+ implicit size instead of being resized to fit the tabbar:
+
+ \borderedimage qtquickcontrols-tabbar-explicit.png
+
+ \snippet qtquickcontrols-tabbar-explicit.qml 1
+
+ \section2 Flickable Tabs
+
+ If the total width of the buttons exceeds the available width of the tab bar,
+ it automatically becomes flickable.
+
+ \image qtquickcontrols-tabbar-flickable.png
+
+ \snippet qtquickcontrols-tabbar-flickable.qml 1
+
+ \sa TabButton, {Customizing TabBar}, {Navigation Controls}, {Container Controls},
+ {Focus Management in Qt Quick Controls}
+*/
+
+class QQuickTabBarPrivate : public QQuickContainerPrivate
+{
+public:
+ Q_DECLARE_PUBLIC(QQuickTabBar)
+
+ void updateCurrentItem();
+ void updateCurrentIndex();
+ void updateLayout();
+
+ qreal getContentWidth() const override;
+ qreal getContentHeight() const override;
+
+ void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &diff) override;
+ void itemImplicitWidthChanged(QQuickItem *item) override;
+ void itemImplicitHeightChanged(QQuickItem *item) override;
+
+ QPalette defaultPalette() const override { return QQuickTheme::palette(QQuickTheme::TabBar); }
+
+ bool updatingLayout = false;
+ QQuickTabBar::Position position = QQuickTabBar::Header;
+#if QT_CONFIG(wheelevent)
+ QPoint accumulatedAngleDelta;
+#endif
+};
+
+class QQuickTabBarAttachedPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickTabBarAttached)
+
+public:
+ static QQuickTabBarAttachedPrivate *get(QQuickTabBarAttached *attached)
+ {
+ return attached->d_func();
+ }
+
+ void update(QQuickTabBar *tabBar, int index);
+
+ int index = -1;
+ QQuickTabBar *tabBar = nullptr;
+};
+
+void QQuickTabBarPrivate::updateCurrentItem()
+{
+ QQuickTabButton *button = qobject_cast<QQuickTabButton *>(contentModel->get(currentIndex));
+ if (button)
+ button->setChecked(true);
+}
+
+void QQuickTabBarPrivate::updateCurrentIndex()
+{
+ Q_Q(QQuickTabBar);
+ QQuickTabButton *button = qobject_cast<QQuickTabButton *>(q->sender());
+ if (button && button->isChecked())
+ q->setCurrentIndex(contentModel->indexOf(button, nullptr));
+}
+
+void QQuickTabBarPrivate::updateLayout()
+{
+ Q_Q(QQuickTabBar);
+ const int count = contentModel->count();
+ if (count <= 0 || !contentItem)
+ return;
+
+ qreal reservedWidth = 0;
+ int resizableCount = 0;
+
+ QList<QQuickItem *> allItems;
+ allItems.reserve(count);
+
+ for (int i = 0; i < count; ++i) {
+ QQuickItem *item = q->itemAt(i);
+ if (item) {
+ QQuickItemPrivate *p = QQuickItemPrivate::get(item);
+ if (!p->widthValid())
+ ++resizableCount;
+ else
+ reservedWidth += item->width();
+ allItems += item;
+ }
+ }
+
+ const qreal totalSpacing = qMax(0, count - 1) * spacing;
+ const qreal itemWidth = (contentItem->width() - reservedWidth - totalSpacing) / qMax(1, resizableCount);
+
+ updatingLayout = true;
+ for (QQuickItem *item : std::as_const(allItems)) {
+ QQuickItemPrivate *p = QQuickItemPrivate::get(item);
+ if (!p->widthValid()) {
+ item->setWidth(itemWidth);
+ p->widthValidFlag = false;
+ }
+ if (!p->heightValid()) {
+ item->setHeight(contentHeight);
+ p->heightValidFlag = false;
+ } else {
+ item->setY((contentHeight - item->height()) / 2);
+ }
+ }
+ updatingLayout = false;
+}
+
+qreal QQuickTabBarPrivate::getContentWidth() const
+{
+ Q_Q(const QQuickTabBar);
+ const int count = contentModel->count();
+ qreal totalWidth = qMax(0, count - 1) * spacing;
+ for (int i = 0; i < count; ++i) {
+ QQuickItem *item = q->itemAt(i);
+ if (item) {
+ QQuickItemPrivate *p = QQuickItemPrivate::get(item);
+ if (!p->widthValid())
+ totalWidth += item->implicitWidth();
+ else
+ totalWidth += item->width();
+ }
+ }
+ return totalWidth;
+}
+
+qreal QQuickTabBarPrivate::getContentHeight() const
+{
+ Q_Q(const QQuickTabBar);
+ const int count = contentModel->count();
+ qreal maxHeight = 0;
+ for (int i = 0; i < count; ++i) {
+ QQuickItem *item = q->itemAt(i);
+ if (item)
+ maxHeight = qMax(maxHeight, item->implicitHeight());
+ }
+ return maxHeight;
+}
+
+void QQuickTabBarPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &diff)
+{
+ QQuickContainerPrivate::itemGeometryChanged(item, change, diff);
+ if (!updatingLayout) {
+ if (change.sizeChange())
+ updateImplicitContentSize();
+ updateLayout();
+ }
+}
+
+void QQuickTabBarPrivate::itemImplicitWidthChanged(QQuickItem *item)
+{
+ QQuickContainerPrivate::itemImplicitWidthChanged(item);
+ if (item != contentItem)
+ updateImplicitContentWidth();
+}
+
+void QQuickTabBarPrivate::itemImplicitHeightChanged(QQuickItem *item)
+{
+ QQuickContainerPrivate::itemImplicitHeightChanged(item);
+ if (item != contentItem)
+ updateImplicitContentHeight();
+}
+
+QQuickTabBar::QQuickTabBar(QQuickItem *parent)
+ : QQuickContainer(*(new QQuickTabBarPrivate), parent)
+{
+ Q_D(QQuickTabBar);
+ d->changeTypes |= QQuickItemPrivate::Geometry | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight;
+ setFlag(ItemIsFocusScope);
+ QObjectPrivate::connect(this, &QQuickTabBar::currentIndexChanged, d, &QQuickTabBarPrivate::updateCurrentItem);
+}
+
+/*!
+ \qmlproperty enumeration QtQuick.Controls::TabBar::position
+
+ This property holds the position of the tab bar.
+
+ \note If the tab bar is assigned as a header or footer of \l ApplicationWindow
+ or \l Page, the appropriate position is set automatically.
+
+ Possible values:
+ \value TabBar.Header The tab bar is at the top, as a window or page header.
+ \value TabBar.Footer The tab bar is at the bottom, as a window or page footer.
+
+ The default value is style-specific.
+
+ \sa ApplicationWindow::header, ApplicationWindow::footer, Page::header, Page::footer
+*/
+QQuickTabBar::Position QQuickTabBar::position() const
+{
+ Q_D(const QQuickTabBar);
+ return d->position;
+}
+
+void QQuickTabBar::setPosition(Position position)
+{
+ Q_D(QQuickTabBar);
+ if (d->position == position)
+ return;
+
+ d->position = position;
+ emit positionChanged();
+}
+
+/*!
+ \since QtQuick.Controls 2.2 (Qt 5.9)
+ \qmlproperty real QtQuick.Controls::TabBar::contentWidth
+
+ This property holds the content width. It is used for calculating the total
+ implicit width of the tab bar.
+
+ \note This property is available in TabBar since QtQuick.Controls 2.2 (Qt 5.9),
+ but it was promoted to the Container base type in QtQuick.Controls 2.5 (Qt 5.12).
+
+ \sa Container::contentWidth
+*/
+
+/*!
+ \since QtQuick.Controls 2.2 (Qt 5.9)
+ \qmlproperty real QtQuick.Controls::TabBar::contentHeight
+
+ This property holds the content height. It is used for calculating the total
+ implicit height of the tab bar.
+
+ \note This property is available in TabBar since QtQuick.Controls 2.2 (Qt 5.9),
+ but it was promoted to the Container base type in QtQuick.Controls 2.5 (Qt 5.12).
+
+ \sa Container::contentHeight
+*/
+
+QQuickTabBarAttached *QQuickTabBar::qmlAttachedProperties(QObject *object)
+{
+ return new QQuickTabBarAttached(object);
+}
+
+void QQuickTabBar::updatePolish()
+{
+ Q_D(QQuickTabBar);
+ QQuickContainer::updatePolish();
+ d->updateLayout();
+}
+
+void QQuickTabBar::componentComplete()
+{
+ Q_D(QQuickTabBar);
+ QQuickContainer::componentComplete();
+ d->updateCurrentItem();
+ d->updateLayout();
+}
+
+void QQuickTabBar::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ Q_D(QQuickTabBar);
+ QQuickContainer::geometryChange(newGeometry, oldGeometry);
+ d->updateLayout();
+}
+
+bool QQuickTabBar::isContent(QQuickItem *item) const
+{
+ return qobject_cast<QQuickTabButton *>(item);
+}
+
+void QQuickTabBar::itemAdded(int index, QQuickItem *item)
+{
+ Q_D(QQuickTabBar);
+ Q_UNUSED(index);
+ QQuickItemPrivate::get(item)->setCulled(true); // QTBUG-55129
+ if (QQuickTabButton *button = qobject_cast<QQuickTabButton *>(item))
+ QObjectPrivate::connect(button, &QQuickTabButton::checkedChanged, d, &QQuickTabBarPrivate::updateCurrentIndex);
+ QQuickTabBarAttached *attached = qobject_cast<QQuickTabBarAttached *>(qmlAttachedPropertiesObject<QQuickTabBar>(item));
+ if (attached)
+ QQuickTabBarAttachedPrivate::get(attached)->update(this, index);
+ d->updateImplicitContentSize();
+ if (isComponentComplete())
+ polish();
+}
+
+void QQuickTabBar::itemMoved(int index, QQuickItem *item)
+{
+ QQuickTabBarAttached *attached = qobject_cast<QQuickTabBarAttached *>(qmlAttachedPropertiesObject<QQuickTabBar>(item));
+ if (attached)
+ QQuickTabBarAttachedPrivate::get(attached)->update(this, index);
+}
+
+void QQuickTabBar::itemRemoved(int index, QQuickItem *item)
+{
+ Q_D(QQuickTabBar);
+ Q_UNUSED(index);
+ if (QQuickTabButton *button = qobject_cast<QQuickTabButton *>(item))
+ QObjectPrivate::disconnect(button, &QQuickTabButton::checkedChanged, d, &QQuickTabBarPrivate::updateCurrentIndex);
+ QQuickTabBarAttached *attached = qobject_cast<QQuickTabBarAttached *>(qmlAttachedPropertiesObject<QQuickTabBar>(item));
+ if (attached)
+ QQuickTabBarAttachedPrivate::get(attached)->update(nullptr, -1);
+ d->updateImplicitContentSize();
+ if (isComponentComplete())
+ polish();
+}
+
+#if QT_CONFIG(wheelevent)
+void QQuickTabBar::wheelEvent(QWheelEvent *event)
+{
+ Q_D(QQuickTabBar);
+ QQuickContainer::wheelEvent(event);
+ if (d->wheelEnabled) {
+ d->accumulatedAngleDelta += event->angleDelta();
+ int xSteps = d->accumulatedAngleDelta.x() / QWheelEvent::DefaultDeltasPerStep;
+ int ySteps = d->accumulatedAngleDelta.y() / QWheelEvent::DefaultDeltasPerStep;
+ if (xSteps > 0 || ySteps > 0) {
+ decrementCurrentIndex();
+ d->accumulatedAngleDelta = QPoint();
+ } else if (xSteps < 0 || ySteps < 0) {
+ incrementCurrentIndex();
+ d->accumulatedAngleDelta = QPoint();
+ }
+ }
+}
+#endif
+
+QFont QQuickTabBar::defaultFont() const
+{
+ return QQuickTheme::font(QQuickTheme::TabBar);
+}
+
+#if QT_CONFIG(accessibility)
+QAccessible::Role QQuickTabBar::accessibleRole() const
+{
+ return QAccessible::PageTabList;
+}
+#endif
+
+/*!
+ \qmlattachedproperty int QtQuick.Controls::TabBar::index
+ \since QtQuick.Controls 2.3 (Qt 5.10)
+ \readonly
+
+ This attached property holds the index of each tab button in the TabBar.
+
+ It is attached to each tab button of the TabBar.
+*/
+
+/*!
+ \qmlattachedproperty TabBar QtQuick.Controls::TabBar::tabBar
+ \since QtQuick.Controls 2.3 (Qt 5.10)
+ \readonly
+
+ This attached property holds the tab bar that manages this tab button.
+
+ It is attached to each tab button of the TabBar.
+*/
+
+/*!
+ \qmlattachedproperty enumeration QtQuick.Controls::TabBar::position
+ \since QtQuick.Controls 2.3 (Qt 5.10)
+ \readonly
+
+ This attached property holds the position of the tab bar.
+
+ It is attached to each tab button of the TabBar.
+
+ Possible values:
+ \value TabBar.Header The tab bar is at the top, as a window or page header.
+ \value TabBar.Footer The tab bar is at the bottom, as a window or page footer.
+*/
+
+void QQuickTabBarAttachedPrivate::update(QQuickTabBar *newTabBar, int newIndex)
+{
+ Q_Q(QQuickTabBarAttached);
+ const int oldIndex = index;
+ const QQuickTabBar *oldTabBar = tabBar;
+ const QQuickTabBar::Position oldPos = q->position();
+
+ index = newIndex;
+ tabBar = newTabBar;
+
+ if (oldTabBar != newTabBar) {
+ if (oldTabBar)
+ QObject::disconnect(oldTabBar, &QQuickTabBar::positionChanged, q, &QQuickTabBarAttached::positionChanged);
+ if (newTabBar)
+ QObject::connect(newTabBar, &QQuickTabBar::positionChanged, q, &QQuickTabBarAttached::positionChanged);
+ emit q->tabBarChanged();
+ }
+
+ if (oldIndex != newIndex)
+ emit q->indexChanged();
+ if (oldPos != q->position())
+ emit q->positionChanged();
+}
+
+QQuickTabBarAttached::QQuickTabBarAttached(QObject *parent)
+ : QObject(*(new QQuickTabBarAttachedPrivate), parent)
+{
+}
+
+int QQuickTabBarAttached::index() const
+{
+ Q_D(const QQuickTabBarAttached);
+ return d->index;
+}
+
+QQuickTabBar *QQuickTabBarAttached::tabBar() const
+{
+ Q_D(const QQuickTabBarAttached);
+ return d->tabBar;
+}
+
+QQuickTabBar::Position QQuickTabBarAttached::position() const
+{
+ Q_D(const QQuickTabBarAttached);
+ if (!d->tabBar)
+ return QQuickTabBar::Header;
+ return d->tabBar->position();
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qquicktabbar_p.cpp"