| 1 | /**************************************************************************** |
| 2 | ** |
| 3 | ** Copyright (C) 2016 The Qt Company Ltd. |
| 4 | ** Contact: https://www.qt.io/licensing/ |
| 5 | ** |
| 6 | ** This file is part of the QtWidgets module of the Qt Toolkit. |
| 7 | ** |
| 8 | ** $QT_BEGIN_LICENSE:LGPL$ |
| 9 | ** Commercial License Usage |
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in |
| 11 | ** accordance with the commercial license agreement provided with the |
| 12 | ** Software or, alternatively, in accordance with the terms contained in |
| 13 | ** a written agreement between you and The Qt Company. For licensing terms |
| 14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
| 15 | ** information use the contact form at https://www.qt.io/contact-us. |
| 16 | ** |
| 17 | ** GNU Lesser General Public License Usage |
| 18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
| 19 | ** General Public License version 3 as published by the Free Software |
| 20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| 21 | ** packaging of this file. Please review the following information to |
| 22 | ** ensure the GNU Lesser General Public License version 3 requirements |
| 23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| 24 | ** |
| 25 | ** GNU General Public License Usage |
| 26 | ** Alternatively, this file may be used under the terms of the GNU |
| 27 | ** General Public License version 2.0 or (at your option) the GNU General |
| 28 | ** Public license version 3 or any later version approved by the KDE Free |
| 29 | ** Qt Foundation. The licenses are as published by the Free Software |
| 30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| 31 | ** included in the packaging of this file. Please review the following |
| 32 | ** information to ensure the GNU General Public License requirements will |
| 33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| 34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
| 35 | ** |
| 36 | ** $QT_END_LICENSE$ |
| 37 | ** |
| 38 | ****************************************************************************/ |
| 39 | |
| 40 | #ifndef QTABBAR_P_H |
| 41 | #define QTABBAR_P_H |
| 42 | |
| 43 | // |
| 44 | // W A R N I N G |
| 45 | // ------------- |
| 46 | // |
| 47 | // This file is not part of the Qt API. It exists purely as an |
| 48 | // implementation detail. This header file may change from version to |
| 49 | // version without notice, or even be removed. |
| 50 | // |
| 51 | // We mean it. |
| 52 | // |
| 53 | |
| 54 | #include <QtWidgets/private/qtwidgetsglobal_p.h> |
| 55 | #include "qtabbar.h" |
| 56 | #include "private/qwidget_p.h" |
| 57 | |
| 58 | #include <qicon.h> |
| 59 | #include <qtoolbutton.h> |
| 60 | #include <qdebug.h> |
| 61 | #if QT_CONFIG(animation) |
| 62 | #include <qvariantanimation.h> |
| 63 | #endif |
| 64 | |
| 65 | #define ANIMATION_DURATION 250 |
| 66 | |
| 67 | #include <qstyleoption.h> |
| 68 | |
| 69 | QT_REQUIRE_CONFIG(tabbar); |
| 70 | |
| 71 | QT_BEGIN_NAMESPACE |
| 72 | |
| 73 | class QMovableTabWidget : public QWidget |
| 74 | { |
| 75 | public: |
| 76 | explicit QMovableTabWidget(QWidget *parent = nullptr); |
| 77 | void setPixmap(const QPixmap &pixmap); |
| 78 | |
| 79 | protected: |
| 80 | void paintEvent(QPaintEvent *e) override; |
| 81 | |
| 82 | private: |
| 83 | QPixmap m_pixmap; |
| 84 | }; |
| 85 | |
| 86 | class Q_WIDGETS_EXPORT QTabBarPrivate : public QWidgetPrivate |
| 87 | { |
| 88 | Q_DECLARE_PUBLIC(QTabBar) |
| 89 | public: |
| 90 | QTabBarPrivate() |
| 91 | :currentIndex(-1), pressedIndex(-1), firstVisible(0), lastVisible(-1), shape(QTabBar::RoundedNorth), layoutDirty(false), |
| 92 | drawBase(true), scrollOffset(0), hoverIndex(-1), elideModeSetByUser(false), useScrollButtonsSetByUser(false), expanding(true), closeButtonOnTabs(false), |
| 93 | selectionBehaviorOnRemove(QTabBar::SelectRightTab), paintWithOffsets(true), movable(false), |
| 94 | dragInProgress(false), documentMode(false), autoHide(false), changeCurrentOnDrag(false), |
| 95 | switchTabCurrentIndex(-1), switchTabTimerId(0), movingTab(nullptr) |
| 96 | {} |
| 97 | |
| 98 | int currentIndex; |
| 99 | int pressedIndex; |
| 100 | int firstVisible; |
| 101 | int lastVisible; |
| 102 | QTabBar::Shape shape; |
| 103 | bool layoutDirty; |
| 104 | bool drawBase; |
| 105 | int scrollOffset; |
| 106 | |
| 107 | struct Tab { |
| 108 | inline Tab(const QIcon &ico, const QString &txt) |
| 109 | : enabled(true) , visible(true), shortcutId(0), text(txt), icon(ico), |
| 110 | leftWidget(nullptr), rightWidget(nullptr), lastTab(-1), dragOffset(0) |
| 111 | #if QT_CONFIG(animation) |
| 112 | , animation(nullptr) |
| 113 | #endif // animation |
| 114 | {} |
| 115 | bool operator==(const Tab &other) const { return &other == this; } |
| 116 | bool enabled; |
| 117 | bool visible; |
| 118 | int shortcutId; |
| 119 | QString text; |
| 120 | #ifndef QT_NO_TOOLTIP |
| 121 | QString toolTip; |
| 122 | #endif |
| 123 | #if QT_CONFIG(whatsthis) |
| 124 | QString whatsThis; |
| 125 | #endif |
| 126 | QIcon icon; |
| 127 | QRect rect; |
| 128 | QRect minRect; |
| 129 | QRect maxRect; |
| 130 | |
| 131 | QColor textColor; |
| 132 | QVariant data; |
| 133 | QWidget *leftWidget; |
| 134 | QWidget *rightWidget; |
| 135 | int lastTab; |
| 136 | int dragOffset; |
| 137 | #ifndef QT_NO_ACCESSIBILITY |
| 138 | QString accessibleName; |
| 139 | #endif |
| 140 | |
| 141 | #if QT_CONFIG(animation) |
| 142 | ~Tab() { delete animation; } |
| 143 | struct TabBarAnimation : public QVariantAnimation { |
| 144 | TabBarAnimation(Tab *t, QTabBarPrivate *_priv) : tab(t), priv(_priv) |
| 145 | { setEasingCurve(QEasingCurve::InOutQuad); } |
| 146 | |
| 147 | void updateCurrentValue(const QVariant ¤t) override; |
| 148 | |
| 149 | void updateState(State newState, State) override; |
| 150 | private: |
| 151 | //these are needed for the callbacks |
| 152 | Tab *tab; |
| 153 | QTabBarPrivate *priv; |
| 154 | } *animation; |
| 155 | |
| 156 | void startAnimation(QTabBarPrivate *priv, int duration) { |
| 157 | if (!priv->isAnimated()) { |
| 158 | priv->moveTabFinished(index: priv->tabList.indexOf(t: *this)); |
| 159 | return; |
| 160 | } |
| 161 | if (!animation) |
| 162 | animation = new TabBarAnimation(this, priv); |
| 163 | animation->setStartValue(dragOffset); |
| 164 | animation->setEndValue(0); |
| 165 | animation->setDuration(duration); |
| 166 | animation->start(); |
| 167 | } |
| 168 | #else |
| 169 | void startAnimation(QTabBarPrivate *priv, int duration) |
| 170 | { Q_UNUSED(duration); priv->moveTabFinished(priv->tabList.indexOf(*this)); } |
| 171 | #endif // animation |
| 172 | }; |
| 173 | QList<Tab> tabList; |
| 174 | mutable QHash<QString, QSize> textSizes; |
| 175 | |
| 176 | void calculateFirstLastVisible(int index, bool visible, bool remove); |
| 177 | int selectNewCurrentIndexFrom(int currentIndex); |
| 178 | int calculateNewPosition(int from, int to, int index) const; |
| 179 | void slide(int from, int to); |
| 180 | void init(); |
| 181 | |
| 182 | Tab *at(int index); |
| 183 | const Tab *at(int index) const; |
| 184 | |
| 185 | int indexAtPos(const QPoint &p) const; |
| 186 | |
| 187 | inline bool isAnimated() const { Q_Q(const QTabBar); return q->style()->styleHint(stylehint: QStyle::SH_Widget_Animation_Duration, opt: nullptr, widget: q) > 0; } |
| 188 | inline bool validIndex(int index) const { return index >= 0 && index < tabList.count(); } |
| 189 | void setCurrentNextEnabledIndex(int offset); |
| 190 | |
| 191 | QToolButton* rightB; // right or bottom |
| 192 | QToolButton* leftB; // left or top |
| 193 | |
| 194 | void _q_scrollTabs(); |
| 195 | void _q_closeTab(); |
| 196 | void moveTab(int index, int offset); |
| 197 | void moveTabFinished(int index); |
| 198 | QRect hoverRect; |
| 199 | int hoverIndex; |
| 200 | |
| 201 | void refresh(); |
| 202 | void layoutTabs(); |
| 203 | void layoutWidgets(int start = 0); |
| 204 | void layoutTab(int index); |
| 205 | void updateMacBorderMetrics(); |
| 206 | bool isTabInMacUnifiedToolbarArea() const; |
| 207 | void setupMovableTab(); |
| 208 | void autoHideTabs(); |
| 209 | QRect normalizedScrollRect(int index = -1); |
| 210 | int hoveredTabIndex() const; |
| 211 | |
| 212 | void initBasicStyleOption(QStyleOptionTab *option, int tabIndex) const; |
| 213 | |
| 214 | void makeVisible(int index); |
| 215 | QSize iconSize; |
| 216 | Qt::TextElideMode elideMode; |
| 217 | bool elideModeSetByUser; |
| 218 | bool useScrollButtons; |
| 219 | bool useScrollButtonsSetByUser; |
| 220 | |
| 221 | bool expanding; |
| 222 | bool closeButtonOnTabs; |
| 223 | QTabBar::SelectionBehavior selectionBehaviorOnRemove; |
| 224 | |
| 225 | QPoint dragStartPosition; |
| 226 | bool paintWithOffsets; |
| 227 | bool movable; |
| 228 | bool dragInProgress; |
| 229 | bool documentMode; |
| 230 | bool autoHide; |
| 231 | bool changeCurrentOnDrag; |
| 232 | |
| 233 | int switchTabCurrentIndex; |
| 234 | int switchTabTimerId; |
| 235 | |
| 236 | QMovableTabWidget *movingTab; |
| 237 | // shared by tabwidget and qtabbar |
| 238 | static void initStyleBaseOption(QStyleOptionTabBarBase *optTabBase, QTabBar *tabbar, QSize size) |
| 239 | { |
| 240 | QStyleOptionTab tabOverlap; |
| 241 | tabOverlap.shape = tabbar->shape(); |
| 242 | int overlap = tabbar->style()->pixelMetric(metric: QStyle::PM_TabBarBaseOverlap, option: &tabOverlap, widget: tabbar); |
| 243 | QWidget *theParent = tabbar->parentWidget(); |
| 244 | optTabBase->init(w: tabbar); |
| 245 | optTabBase->shape = tabbar->shape(); |
| 246 | optTabBase->documentMode = tabbar->documentMode(); |
| 247 | if (theParent && overlap > 0) { |
| 248 | QRect rect; |
| 249 | switch (tabOverlap.shape) { |
| 250 | case QTabBar::RoundedNorth: |
| 251 | case QTabBar::TriangularNorth: |
| 252 | rect.setRect(ax: 0, ay: size.height()-overlap, aw: size.width(), ah: overlap); |
| 253 | break; |
| 254 | case QTabBar::RoundedSouth: |
| 255 | case QTabBar::TriangularSouth: |
| 256 | rect.setRect(ax: 0, ay: 0, aw: size.width(), ah: overlap); |
| 257 | break; |
| 258 | case QTabBar::RoundedEast: |
| 259 | case QTabBar::TriangularEast: |
| 260 | rect.setRect(ax: 0, ay: 0, aw: overlap, ah: size.height()); |
| 261 | break; |
| 262 | case QTabBar::RoundedWest: |
| 263 | case QTabBar::TriangularWest: |
| 264 | rect.setRect(ax: size.width() - overlap, ay: 0, aw: overlap, ah: size.height()); |
| 265 | break; |
| 266 | } |
| 267 | optTabBase->rect = rect; |
| 268 | } |
| 269 | } |
| 270 | |
| 271 | void killSwitchTabTimer(); |
| 272 | |
| 273 | }; |
| 274 | |
| 275 | QT_END_NAMESPACE |
| 276 | |
| 277 | #endif |
| 278 | |