| /**************************************************************************** |
| ** |
| ** Copyright (C) 2017 The Qt Company Ltd. |
| ** Contact: http://www.qt.io/licensing/ |
| ** |
| ** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL3$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see http://www.qt.io/terms-conditions. For further |
| ** information use the contact form at http://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPLv3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or later as published by the Free |
| ** Software Foundation and appearing in the file LICENSE.GPL included in |
| ** the packaging of this file. Please review the following information to |
| ** ensure the GNU General Public License version 2.0 requirements will be |
| ** met: http://www.gnu.org/licenses/gpl-2.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #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 qtquickcontrols2-navigation |
| \ingroup qtquickcontrols2-containers |
| \ingroup qtquickcontrols2-focusscopes |
| \brief Allows the user to switch between different views or subtasks. |
| |
| TabBar provides a tab-based navigation model. |
| |
| \image qtquickcontrols2-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 qtquickcontrols2-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}. |
| |
| \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 qtquickcontrols2-tabbar-explicit.png |
| |
| \snippet qtquickcontrols2-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 qtquickcontrols2-tabbar-flickable.png |
| |
| \snippet qtquickcontrols2-tabbar-flickable.qml 1 |
| |
| \sa TabButton, {Customizing TabBar}, {Navigation Controls}, {Container Controls}, |
| {Focus Management in Qt Quick Controls} |
| */ |
| |
| class QQuickTabBarPrivate : public QQuickContainerPrivate |
| { |
| Q_DECLARE_PUBLIC(QQuickTabBar) |
| |
| public: |
| 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; |
| |
| bool updatingLayout = false; |
| QQuickTabBar::Position position = QQuickTabBar::Header; |
| }; |
| |
| 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; |
| |
| QVector<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 : qAsConst(allItems)) { |
| QQuickItemPrivate *p = QQuickItemPrivate::get(item); |
| if (!p->widthValid) { |
| item->setWidth(itemWidth); |
| p->widthValid = false; |
| } |
| if (!p->heightValid) { |
| item->setHeight(contentHeight); |
| p->heightValid = 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::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) |
| { |
| Q_D(QQuickTabBar); |
| QQuickContainer::geometryChanged(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(); |
| } |
| |
| QFont QQuickTabBar::defaultFont() const |
| { |
| return QQuickTheme::font(QQuickTheme::TabBar); |
| } |
| |
| QPalette QQuickTabBar::defaultPalette() const |
| { |
| return QQuickTheme::palette(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 |