1// Copyright (C) 2016 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
4#ifndef QTABBAR_P_H
5#define QTABBAR_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <QtWidgets/private/qtwidgetsglobal_p.h>
19#include "qtabbar.h"
20#include "private/qwidget_p.h"
21
22#include <qicon.h>
23#include <qtoolbutton.h>
24#include <qdebug.h>
25#if QT_CONFIG(animation)
26#include <qvariantanimation.h>
27#endif
28
29#define ANIMATION_DURATION 250
30
31#include <qstyleoption.h>
32#include <utility>
33
34QT_REQUIRE_CONFIG(tabbar);
35
36QT_BEGIN_NAMESPACE
37
38class QMovableTabWidget : public QWidget
39{
40public:
41 explicit QMovableTabWidget(QWidget *parent = nullptr);
42 void setPixmap(const QPixmap &pixmap);
43
44protected:
45 void paintEvent(QPaintEvent *e) override;
46
47private:
48 QPixmap m_pixmap;
49};
50
51class Q_WIDGETS_EXPORT QTabBarPrivate : public QWidgetPrivate
52{
53 Q_DECLARE_PUBLIC(QTabBar)
54public:
55 QTabBarPrivate()
56 : layoutDirty(false), drawBase(true), elideModeSetByUser(false), useScrollButtons(false),
57 useScrollButtonsSetByUser(false), expanding(true), closeButtonOnTabs(false),
58 paintWithOffsets(true), movable(false), dragInProgress(false), documentMode(false),
59 autoHide(false), changeCurrentOnDrag(false)
60 {}
61 ~QTabBarPrivate()
62 {
63 qDeleteAll(c: tabList);
64 }
65
66 QRect hoverRect;
67 QPoint dragStartPosition;
68 QPoint mousePosition = {-1, -1};
69#if QT_CONFIG(wheelevent)
70 QPoint accumulatedAngleDelta;
71#endif
72 QSize iconSize;
73 QToolButton* rightB = nullptr; // right or bottom
74 QToolButton* leftB = nullptr; // left or top
75 QMovableTabWidget *movingTab = nullptr;
76 int hoverIndex = -1;
77 int switchTabCurrentIndex = -1;
78 int switchTabTimerId = 0;
79 Qt::TextElideMode elideMode = Qt::ElideNone;
80 QTabBar::SelectionBehavior selectionBehaviorOnRemove = QTabBar::SelectRightTab;
81 QTabBar::Shape shape = QTabBar::RoundedNorth;
82 Qt::MouseButtons mouseButtons = Qt::NoButton;
83
84 int currentIndex = -1;
85 int pressedIndex = -1;
86 int firstVisible = 0;
87 int lastVisible = -1;
88 int scrollOffset = 0;
89
90 bool layoutDirty : 1;
91 bool drawBase : 1;
92 bool elideModeSetByUser : 1;
93 bool useScrollButtons : 1;
94 bool useScrollButtonsSetByUser : 1;
95 bool expanding : 1;
96 bool closeButtonOnTabs : 1;
97 bool paintWithOffsets : 1;
98 bool movable : 1;
99 bool dragInProgress : 1;
100 bool documentMode : 1;
101 bool autoHide : 1;
102 bool changeCurrentOnDrag : 1;
103
104 struct Tab {
105 inline Tab(const QIcon &ico, const QString &txt)
106 : text(txt), icon(ico), enabled(true), visible(true)
107 {
108 }
109 /*
110 Tabs are managed by instance; they are not the same even
111 if all properties are the same.
112 */
113 Q_DISABLE_COPY_MOVE(Tab);
114
115 QString text;
116#if QT_CONFIG(tooltip)
117 QString toolTip;
118#endif
119#if QT_CONFIG(whatsthis)
120 QString whatsThis;
121#endif
122#if QT_CONFIG(accessibility)
123 QString accessibleName;
124#endif
125 QIcon icon;
126 QRect rect;
127 QRect minRect;
128 QRect maxRect;
129
130 QColor textColor;
131 QVariant data;
132 QWidget *leftWidget = nullptr;
133 QWidget *rightWidget = nullptr;
134 int shortcutId = 0;
135 int lastTab = -1;
136 int dragOffset = 0;
137 uint enabled : 1;
138 uint visible : 1;
139
140#if QT_CONFIG(animation)
141 struct TabBarAnimation : public QVariantAnimation {
142 TabBarAnimation(Tab *t, QTabBarPrivate *_priv) : tab(t), priv(_priv)
143 { setEasingCurve(QEasingCurve::InOutQuad); }
144
145 void updateCurrentValue(const QVariant &current) override;
146
147 void updateState(State newState, State) override;
148 private:
149 //these are needed for the callbacks
150 Tab *tab;
151 QTabBarPrivate *priv;
152 };
153 std::unique_ptr<TabBarAnimation> animation;
154
155 void startAnimation(QTabBarPrivate *priv, int duration) {
156 if (!priv->isAnimated()) {
157 priv->moveTabFinished(index: priv->tabList.indexOf(t: this));
158 return;
159 }
160 if (!animation)
161 animation = std::make_unique<TabBarAnimation>(args: this, args&: priv);
162 animation->setStartValue(dragOffset);
163 animation->setEndValue(0);
164 animation->setDuration(duration);
165 animation->start();
166 }
167#else
168 void startAnimation(QTabBarPrivate *priv, int duration)
169 { Q_UNUSED(duration); priv->moveTabFinished(priv->tabList.indexOf(this)); }
170#endif // animation
171 };
172 QList<Tab*> tabList;
173 mutable QHash<QString, QSize> textSizes;
174
175 void calculateFirstLastVisible(int index, bool visible, bool remove);
176 int selectNewCurrentIndexFrom(int currentIndex);
177 int calculateNewPosition(int from, int to, int index) const;
178 void slide(int from, int to);
179 void init();
180
181 inline Tab *at(int index) { return tabList.value(i: index, defaultValue: nullptr); }
182 inline const Tab *at(int index) const { return tabList.value(i: index, defaultValue: nullptr); }
183
184 int indexAtPos(const QPoint &p) const;
185
186 inline bool isAnimated() const { Q_Q(const QTabBar); return q->style()->styleHint(stylehint: QStyle::SH_Widget_Animation_Duration, opt: nullptr, widget: q) > 0; }
187 inline bool validIndex(int index) const { return index >= 0 && index < tabList.size(); }
188 void setCurrentNextEnabledIndex(int offset);
189
190 void _q_scrollTabs();
191 void _q_closeTab();
192 void moveTab(int index, int offset);
193 void moveTabFinished(int index);
194
195 void refresh();
196 void layoutTabs();
197 void layoutWidgets(int start = 0);
198 void layoutTab(int index);
199 void updateMacBorderMetrics();
200 bool isTabInMacUnifiedToolbarArea() const;
201 void setupMovableTab();
202 void autoHideTabs();
203 QRect normalizedScrollRect(int index = -1);
204 int hoveredTabIndex() const;
205
206 void initBasicStyleOption(QStyleOptionTab *option, int tabIndex) const;
207
208 void makeVisible(int index);
209
210 // shared by tabwidget and qtabbar
211 static void initStyleBaseOption(QStyleOptionTabBarBase *optTabBase, QTabBar *tabbar, QSize size)
212 {
213 QStyleOptionTab tabOverlap;
214 tabOverlap.shape = tabbar->shape();
215 int overlap = tabbar->style()->pixelMetric(metric: QStyle::PM_TabBarBaseOverlap, option: &tabOverlap, widget: tabbar);
216 QWidget *theParent = tabbar->parentWidget();
217 optTabBase->initFrom(w: tabbar);
218 optTabBase->shape = tabbar->shape();
219 optTabBase->documentMode = tabbar->documentMode();
220 if (theParent && overlap > 0) {
221 QRect rect;
222 switch (tabOverlap.shape) {
223 case QTabBar::RoundedNorth:
224 case QTabBar::TriangularNorth:
225 rect.setRect(ax: 0, ay: size.height()-overlap, aw: size.width(), ah: overlap);
226 break;
227 case QTabBar::RoundedSouth:
228 case QTabBar::TriangularSouth:
229 rect.setRect(ax: 0, ay: 0, aw: size.width(), ah: overlap);
230 break;
231 case QTabBar::RoundedEast:
232 case QTabBar::TriangularEast:
233 rect.setRect(ax: 0, ay: 0, aw: overlap, ah: size.height());
234 break;
235 case QTabBar::RoundedWest:
236 case QTabBar::TriangularWest:
237 rect.setRect(ax: size.width() - overlap, ay: 0, aw: overlap, ah: size.height());
238 break;
239 }
240 optTabBase->rect = rect;
241 }
242 }
243
244 void killSwitchTabTimer();
245
246};
247
248constexpr inline bool verticalTabs(QTabBar::Shape shape) noexcept
249{
250 return shape == QTabBar::RoundedWest
251 || shape == QTabBar::RoundedEast
252 || shape == QTabBar::TriangularWest
253 || shape == QTabBar::TriangularEast;
254}
255
256QT_END_NAMESPACE
257
258#endif
259

source code of qtbase/src/widgets/widgets/qtabbar_p.h