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 | |