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#include "private/qlayoutengine_p.h"
41#if QT_CONFIG(itemviews)
42#include "qabstractitemdelegate.h"
43#endif
44#include "qapplication.h"
45#include "qbitmap.h"
46#include "qcursor.h"
47#include "qevent.h"
48#include "qpainter.h"
49#include "qstyle.h"
50#include "qstyleoption.h"
51#include "qstylepainter.h"
52#if QT_CONFIG(tabwidget)
53#include "qtabwidget.h"
54#endif
55#include "qtooltip.h"
56#if QT_CONFIG(whatsthis)
57#include "qwhatsthis.h"
58#endif
59#include "private/qtextengine_p.h"
60#ifndef QT_NO_ACCESSIBILITY
61#include "qaccessible.h"
62#endif
63#ifdef Q_OS_MACOS
64#include <qpa/qplatformnativeinterface.h>
65#endif
66
67#include "qdebug.h"
68#include "private/qapplication_p.h"
69#include "private/qtabbar_p.h"
70
71QT_BEGIN_NAMESPACE
72
73namespace {
74class CloseButton : public QAbstractButton
75{
76 Q_OBJECT
77
78public:
79 explicit CloseButton(QWidget *parent = nullptr);
80
81 QSize sizeHint() const override;
82 QSize minimumSizeHint() const override
83 { return sizeHint(); }
84 void enterEvent(QEvent *event) override;
85 void leaveEvent(QEvent *event) override;
86 void paintEvent(QPaintEvent *event) override;
87};
88}
89
90QMovableTabWidget::QMovableTabWidget(QWidget *parent)
91 : QWidget(parent)
92{
93}
94
95void QMovableTabWidget::setPixmap(const QPixmap &pixmap)
96{
97 m_pixmap = pixmap;
98 update();
99}
100
101void QMovableTabWidget::paintEvent(QPaintEvent *e)
102{
103 Q_UNUSED(e);
104 QPainter p(this);
105 p.drawPixmap(x: 0, y: 0, pm: m_pixmap);
106}
107
108inline static bool verticalTabs(QTabBar::Shape shape)
109{
110 return shape == QTabBar::RoundedWest
111 || shape == QTabBar::RoundedEast
112 || shape == QTabBar::TriangularWest
113 || shape == QTabBar::TriangularEast;
114}
115
116void QTabBarPrivate::updateMacBorderMetrics()
117{
118#if defined(Q_OS_MACOS)
119 Q_Q(QTabBar);
120 // Extend the unified title and toolbar area to cover the tab bar iff
121 // 1) the tab bar is in document mode
122 // 2) the tab bar is directly below an "unified" area.
123 // The extending itself is done in the Cocoa platform plugin and Mac style,
124 // this function registers geometry and visibility state for the tab bar.
125
126 // Calculate geometry
127 int upper, lower;
128 if (documentMode) {
129 QPoint windowPos = q->mapTo(q->window(), QPoint(0,0));
130 upper = windowPos.y();
131 int tabStripHeight = q->tabSizeHint(0).height();
132 int pixelTweak = -3;
133 lower = upper + tabStripHeight + pixelTweak;
134 } else {
135 upper = 0;
136 lower = 0;
137 }
138
139 QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface();
140 if (!nativeInterface)
141 return;
142 quintptr identifier = reinterpret_cast<quintptr>(q);
143
144 // Set geometry
145 QPlatformNativeInterface::NativeResourceForIntegrationFunction function =
146 nativeInterface->nativeResourceFunctionForIntegration("registerContentBorderArea");
147 if (!function)
148 return; // Not Cocoa platform plugin.
149 typedef void (*RegisterContentBorderAreaFunction)(QWindow *window, quintptr identifier, int upper, int lower);
150 (reinterpret_cast<RegisterContentBorderAreaFunction>(function))(q->window()->windowHandle(), identifier, upper, lower);
151
152 // Set visibility state
153 function = nativeInterface->nativeResourceFunctionForIntegration("setContentBorderAreaEnabled");
154 if (!function)
155 return;
156 typedef void (*SetContentBorderAreaEnabledFunction)(QWindow *window, quintptr identifier, bool enable);
157 (reinterpret_cast<SetContentBorderAreaEnabledFunction>(function))(q->window()->windowHandle(), identifier, q->isVisible());
158#endif
159}
160
161/*!
162 \internal
163 This is basically QTabBar::initStyleOption() but
164 without the expensive QFontMetrics::elidedText() call.
165*/
166
167void QTabBarPrivate::initBasicStyleOption(QStyleOptionTab *option, int tabIndex) const
168{
169 Q_Q(const QTabBar);
170 const int totalTabs = tabList.size();
171
172 if (!option || (tabIndex < 0 || tabIndex >= totalTabs))
173 return;
174
175 const QTabBarPrivate::Tab &tab = tabList.at(i: tabIndex);
176 option->initFrom(w: q);
177 option->state &= ~(QStyle::State_HasFocus | QStyle::State_MouseOver);
178 option->rect = q->tabRect(index: tabIndex);
179 const bool isCurrent = tabIndex == currentIndex;
180 option->row = 0;
181 if (tabIndex == pressedIndex)
182 option->state |= QStyle::State_Sunken;
183 if (isCurrent)
184 option->state |= QStyle::State_Selected;
185 if (isCurrent && q->hasFocus())
186 option->state |= QStyle::State_HasFocus;
187 if (!tab.enabled)
188 option->state &= ~QStyle::State_Enabled;
189 if (q->isActiveWindow())
190 option->state |= QStyle::State_Active;
191 if (!dragInProgress && option->rect == hoverRect)
192 option->state |= QStyle::State_MouseOver;
193 option->shape = shape;
194 option->text = tab.text;
195
196 if (tab.textColor.isValid())
197 option->palette.setColor(acr: q->foregroundRole(), acolor: tab.textColor);
198 option->icon = tab.icon;
199 option->iconSize = q->iconSize(); // Will get the default value then.
200
201 option->leftButtonSize = tab.leftWidget ? tab.leftWidget->size() : QSize();
202 option->rightButtonSize = tab.rightWidget ? tab.rightWidget->size() : QSize();
203 option->documentMode = documentMode;
204
205 if (tabIndex > 0 && tabIndex - 1 == currentIndex)
206 option->selectedPosition = QStyleOptionTab::PreviousIsSelected;
207 else if (tabIndex + 1 < totalTabs && tabIndex + 1 == currentIndex)
208 option->selectedPosition = QStyleOptionTab::NextIsSelected;
209 else
210 option->selectedPosition = QStyleOptionTab::NotAdjacent;
211
212 const bool paintBeginning = (tabIndex == firstVisible) || (dragInProgress && tabIndex == pressedIndex + 1);
213 const bool paintEnd = (tabIndex == lastVisible) || (dragInProgress && tabIndex == pressedIndex - 1);
214 if (paintBeginning) {
215 if (paintEnd)
216 option->position = QStyleOptionTab::OnlyOneTab;
217 else
218 option->position = QStyleOptionTab::Beginning;
219 } else if (paintEnd) {
220 option->position = QStyleOptionTab::End;
221 } else {
222 option->position = QStyleOptionTab::Middle;
223 }
224
225#if QT_CONFIG(tabwidget)
226 if (const QTabWidget *tw = qobject_cast<const QTabWidget *>(object: q->parentWidget())) {
227 option->features |= QStyleOptionTab::HasFrame;
228 if (tw->cornerWidget(corner: Qt::TopLeftCorner) || tw->cornerWidget(corner: Qt::BottomLeftCorner))
229 option->cornerWidgets |= QStyleOptionTab::LeftCornerWidget;
230 if (tw->cornerWidget(corner: Qt::TopRightCorner) || tw->cornerWidget(corner: Qt::BottomRightCorner))
231 option->cornerWidgets |= QStyleOptionTab::RightCornerWidget;
232 }
233#endif
234 if (QStyleOptionTabV4 *optv4 = qstyleoption_cast<QStyleOptionTabV4 *>(opt: option))
235 optv4->tabIndex = tabIndex;
236}
237
238/*!
239 Initialize \a option with the values from the tab at \a tabIndex. This method
240 is useful for subclasses when they need a QStyleOptionTab,
241 but don't want to fill in all the information themselves.
242
243 \sa QStyleOption::initFrom(), QTabWidget::initStyleOption()
244*/
245void QTabBar::initStyleOption(QStyleOptionTab *option, int tabIndex) const
246{
247 Q_D(const QTabBar);
248 d->initBasicStyleOption(option, tabIndex);
249
250 QRect textRect = style()->subElementRect(subElement: QStyle::SE_TabBarTabText, option, widget: this);
251 option->text = fontMetrics().elidedText(text: option->text, mode: d->elideMode, width: textRect.width(),
252 flags: Qt::TextShowMnemonic);
253}
254
255/*!
256 \class QTabBar
257 \brief The QTabBar class provides a tab bar, e.g. for use in tabbed dialogs.
258
259 \ingroup basicwidgets
260 \inmodule QtWidgets
261
262 QTabBar is straightforward to use; it draws the tabs using one of
263 the predefined \l{QTabBar::Shape}{shapes}, and emits a
264 signal when a tab is selected. It can be subclassed to tailor the
265 look and feel. Qt also provides a ready-made \l{QTabWidget}.
266
267 Each tab has a tabText(), an optional tabIcon(), an optional
268 tabToolTip(), optional tabWhatsThis() and optional tabData().
269 The tabs's attributes can be changed with setTabText(), setTabIcon(),
270 setTabToolTip(), setTabWhatsThis and setTabData(). Each tabs can be
271 enabled or disabled individually with setTabEnabled().
272
273 Each tab can display text in a distinct color. The current text color
274 for a tab can be found with the tabTextColor() function. Set the text
275 color for a particular tab with setTabTextColor().
276
277 Tabs are added using addTab(), or inserted at particular positions
278 using insertTab(). The total number of tabs is given by
279 count(). Tabs can be removed from the tab bar with
280 removeTab(). Combining removeTab() and insertTab() allows you to
281 move tabs to different positions.
282
283 The \l shape property defines the tabs' appearance. The choice of
284 shape is a matter of taste, although tab dialogs (for preferences
285 and similar) invariably use \l RoundedNorth.
286 Tab controls in windows other than dialogs almost
287 always use either \l RoundedSouth or \l TriangularSouth. Many
288 spreadsheets and other tab controls in which all the pages are
289 essentially similar use \l TriangularSouth, whereas \l
290 RoundedSouth is used mostly when the pages are different (e.g. a
291 multi-page tool palette). The default in QTabBar is \l
292 RoundedNorth.
293
294 The most important part of QTabBar's API is the currentChanged()
295 signal. This is emitted whenever the current tab changes (even at
296 startup, when the current tab changes from 'none'). There is also
297 a slot, setCurrentIndex(), which can be used to select a tab
298 programmatically. The function currentIndex() returns the index of
299 the current tab, \l count holds the number of tabs.
300
301 QTabBar creates automatic mnemonic keys in the manner of QAbstractButton;
302 e.g. if a tab's label is "\&Graphics", Alt+G becomes a shortcut
303 key for switching to that tab.
304
305 The following virtual functions may need to be reimplemented in
306 order to tailor the look and feel or store extra data with each
307 tab:
308
309 \list
310 \li tabSizeHint() calcuates the size of a tab.
311 \li tabInserted() notifies that a new tab was added.
312 \li tabRemoved() notifies that a tab was removed.
313 \li tabLayoutChange() notifies that the tabs have been re-laid out.
314 \li paintEvent() paints all tabs.
315 \endlist
316
317 For subclasses, you might also need the tabRect() functions which
318 returns the visual geometry of a single tab.
319
320 \table 100%
321 \row \li \inlineimage fusion-tabbar.png Screenshot of a Fusion style tab bar
322 \li A tab bar shown in the \l{Qt Widget Gallery}{Fusion widget style}.
323 \row \li \inlineimage fusion-tabbar-truncated.png Screenshot of a truncated Fusion tab bar
324 \li A truncated tab bar shown in the Fusion widget style.
325 \endtable
326
327 \sa QTabWidget
328*/
329
330/*!
331 \enum QTabBar::Shape
332
333 This enum type lists the built-in shapes supported by QTabBar. Treat these
334 as hints as some styles may not render some of the shapes. However,
335 position should be honored.
336
337 \value RoundedNorth The normal rounded look above the pages
338
339 \value RoundedSouth The normal rounded look below the pages
340
341 \value RoundedWest The normal rounded look on the left side of the pages
342
343 \value RoundedEast The normal rounded look on the right side the pages
344
345 \value TriangularNorth Triangular tabs above the pages.
346
347 \value TriangularSouth Triangular tabs similar to those used in
348 the Excel spreadsheet, for example
349
350 \value TriangularWest Triangular tabs on the left of the pages.
351
352 \value TriangularEast Triangular tabs on the right of the pages.
353*/
354
355/*!
356 \fn void QTabBar::currentChanged(int index)
357
358 This signal is emitted when the tab bar's current tab changes. The
359 new current has the given \a index, or -1 if there isn't a new one
360 (for example, if there are no tab in the QTabBar)
361*/
362
363/*!
364 \fn void QTabBar::tabCloseRequested(int index)
365 \since 4.5
366
367 This signal is emitted when the close button on a tab is clicked.
368 The \a index is the index that should be removed.
369
370 \sa setTabsClosable()
371*/
372
373/*!
374 \fn void QTabBar::tabMoved(int from, int to)
375 \since 4.5
376
377 This signal is emitted when the tab has moved the tab
378 at index position \a from to index position \a to.
379
380 note: QTabWidget will automatically move the page when
381 this signal is emitted from its tab bar.
382
383 \sa moveTab()
384*/
385
386/*!
387 \fn void QTabBar::tabBarClicked(int index)
388
389 This signal is emitted when user clicks on a tab at an \a index.
390
391 \a index is the index of a clicked tab, or -1 if no tab is under the cursor.
392
393 \since 5.2
394*/
395
396/*!
397 \fn void QTabBar::tabBarDoubleClicked(int index)
398
399 This signal is emitted when the user double clicks on a tab at \a index.
400
401 \a index refers to the tab clicked, or -1 if no tab is under the cursor.
402
403 \since 5.2
404*/
405
406void QTabBarPrivate::init()
407{
408 Q_Q(QTabBar);
409 leftB = new QToolButton(q);
410 leftB->setObjectName(QStringLiteral("ScrollLeftButton"));
411 leftB->setAutoRepeat(true);
412 QObject::connect(sender: leftB, SIGNAL(clicked()), receiver: q, SLOT(_q_scrollTabs()));
413 leftB->hide();
414 rightB = new QToolButton(q);
415 rightB->setObjectName(QStringLiteral("ScrollRightButton"));
416 rightB->setAutoRepeat(true);
417 QObject::connect(sender: rightB, SIGNAL(clicked()), receiver: q, SLOT(_q_scrollTabs()));
418 rightB->hide();
419#ifdef QT_KEYPAD_NAVIGATION
420 if (QApplicationPrivate::keypadNavigationEnabled()) {
421 leftB->setFocusPolicy(Qt::NoFocus);
422 rightB->setFocusPolicy(Qt::NoFocus);
423 q->setFocusPolicy(Qt::NoFocus);
424 } else
425#endif
426 q->setFocusPolicy(Qt::TabFocus);
427
428#ifndef QT_NO_ACCESSIBILITY
429 leftB->setAccessibleName(QTabBar::tr(s: "Scroll Left"));
430 rightB->setAccessibleName(QTabBar::tr(s: "Scroll Right"));
431#endif
432 q->setSizePolicy(hor: QSizePolicy::Preferred, ver: QSizePolicy::Fixed);
433 elideMode = Qt::TextElideMode(q->style()->styleHint(stylehint: QStyle::SH_TabBar_ElideMode, opt: nullptr, widget: q));
434 useScrollButtons = !q->style()->styleHint(stylehint: QStyle::SH_TabBar_PreferNoArrows, opt: nullptr, widget: q);
435}
436
437QTabBarPrivate::Tab *QTabBarPrivate::at(int index)
438{
439 return validIndex(index)?&tabList[index]:nullptr;
440}
441
442const QTabBarPrivate::Tab *QTabBarPrivate::at(int index) const
443{
444 return validIndex(index)?&tabList[index]:nullptr;
445}
446
447int QTabBarPrivate::indexAtPos(const QPoint &p) const
448{
449 Q_Q(const QTabBar);
450 if (q->tabRect(index: currentIndex).contains(p))
451 return currentIndex;
452 for (int i = 0; i < tabList.count(); ++i)
453 if (tabList.at(i).enabled && q->tabRect(index: i).contains(p))
454 return i;
455 return -1;
456}
457
458void QTabBarPrivate::layoutTabs()
459{
460 Q_Q(QTabBar);
461 layoutDirty = false;
462 QSize size = q->size();
463 int last, available;
464 int maxExtent;
465 int i;
466 bool vertTabs = verticalTabs(shape);
467 int tabChainIndex = 0;
468 int hiddenTabs = 0;
469
470 Qt::Alignment tabAlignment = Qt::Alignment(q->style()->styleHint(stylehint: QStyle::SH_TabBar_Alignment, opt: nullptr, widget: q));
471 QVector<QLayoutStruct> tabChain(tabList.count() + 2);
472
473 // We put an empty item at the front and back and set its expansive attribute
474 // depending on tabAlignment and expanding.
475 tabChain[tabChainIndex].init();
476 tabChain[tabChainIndex].expansive = (!expanding)
477 && (tabAlignment != Qt::AlignLeft)
478 && (tabAlignment != Qt::AlignJustify);
479 tabChain[tabChainIndex].empty = true;
480 ++tabChainIndex;
481
482 // We now go through our list of tabs and set the minimum size and the size hint
483 // This will allow us to elide text if necessary. Since we don't set
484 // a maximum size, tabs will EXPAND to fill up the empty space.
485 // Since tab widget is rather *ahem* strict about keeping the geometry of the
486 // tab bar to its absolute minimum, this won't bleed through, but will show up
487 // if you use tab bar on its own (a.k.a. not a bug, but a feature).
488 // Update: if expanding is false, we DO set a maximum size to prevent the tabs
489 // being wider than necessary.
490 if (!vertTabs) {
491 int minx = 0;
492 int x = 0;
493 int maxHeight = 0;
494 for (i = 0; i < tabList.count(); ++i) {
495 if (!tabList.at(i).visible) {
496 ++hiddenTabs;
497 continue;
498 }
499 QSize sz = q->tabSizeHint(index: i);
500 tabList[i].maxRect = QRect(x, 0, sz.width(), sz.height());
501 x += sz.width();
502 maxHeight = qMax(a: maxHeight, b: sz.height());
503 sz = q->minimumTabSizeHint(index: i);
504 tabList[i].minRect = QRect(minx, 0, sz.width(), sz.height());
505 minx += sz.width();
506 tabChain[tabChainIndex].init();
507 tabChain[tabChainIndex].sizeHint = tabList.at(i).maxRect.width();
508 tabChain[tabChainIndex].minimumSize = sz.width();
509 tabChain[tabChainIndex].empty = false;
510 tabChain[tabChainIndex].expansive = true;
511
512 if (!expanding)
513 tabChain[tabChainIndex].maximumSize = tabChain[tabChainIndex].sizeHint;
514 ++tabChainIndex;
515 }
516
517 last = minx;
518 available = size.width();
519 maxExtent = maxHeight;
520 } else {
521 int miny = 0;
522 int y = 0;
523 int maxWidth = 0;
524 for (i = 0; i < tabList.count(); ++i) {
525 if (!tabList.at(i).visible) {
526 ++hiddenTabs;
527 continue;
528 }
529 QSize sz = q->tabSizeHint(index: i);
530 tabList[i].maxRect = QRect(0, y, sz.width(), sz.height());
531 y += sz.height();
532 maxWidth = qMax(a: maxWidth, b: sz.width());
533 sz = q->minimumTabSizeHint(index: i);
534 tabList[i].minRect = QRect(0, miny, sz.width(), sz.height());
535 miny += sz.height();
536 tabChain[tabChainIndex].init();
537 tabChain[tabChainIndex].sizeHint = tabList.at(i).maxRect.height();
538 tabChain[tabChainIndex].minimumSize = sz.height();
539 tabChain[tabChainIndex].empty = false;
540 tabChain[tabChainIndex].expansive = true;
541
542 if (!expanding)
543 tabChain[tabChainIndex].maximumSize = tabChain[tabChainIndex].sizeHint;
544 ++tabChainIndex;
545 }
546
547 last = miny;
548 available = size.height();
549 maxExtent = maxWidth;
550 }
551
552 // Mirror our front item.
553 tabChain[tabChainIndex].init();
554 tabChain[tabChainIndex].expansive = (!expanding)
555 && (tabAlignment != Qt::AlignRight)
556 && (tabAlignment != Qt::AlignJustify);
557 tabChain[tabChainIndex].empty = true;
558 Q_ASSERT(tabChainIndex == tabChain.count() - 1 - hiddenTabs); // add an assert just to make sure.
559
560 // Do the calculation
561 qGeomCalc(chain&: tabChain, start: 0, count: tabChain.count(), pos: 0, space: qMax(a: available, b: last), spacer: 0);
562
563 // Use the results
564 hiddenTabs = 0;
565 for (i = 0; i < tabList.count(); ++i) {
566 if (!tabList.at(i).visible) {
567 tabList[i].rect = QRect();
568 ++hiddenTabs;
569 continue;
570 }
571 const QLayoutStruct &lstruct = tabChain.at(i: i + 1 - hiddenTabs);
572 if (!vertTabs)
573 tabList[i].rect.setRect(ax: lstruct.pos, ay: 0, aw: lstruct.size, ah: maxExtent);
574 else
575 tabList[i].rect.setRect(ax: 0, ay: lstruct.pos, aw: maxExtent, ah: lstruct.size);
576 }
577
578 if (useScrollButtons && tabList.count() && last > available) {
579 const QRect scrollRect = normalizedScrollRect(index: 0);
580 scrollOffset = -scrollRect.left();
581
582 Q_Q(QTabBar);
583 QStyleOption opt;
584 opt.init(w: q);
585 QRect scrollButtonLeftRect = q->style()->subElementRect(subElement: QStyle::SE_TabBarScrollLeftButton, option: &opt, widget: q);
586 QRect scrollButtonRightRect = q->style()->subElementRect(subElement: QStyle::SE_TabBarScrollRightButton, option: &opt, widget: q);
587 int scrollButtonWidth = q->style()->pixelMetric(metric: QStyle::PM_TabBarScrollButtonWidth, option: &opt, widget: q);
588
589 // Normally SE_TabBarScrollLeftButton should have the same width as PM_TabBarScrollButtonWidth.
590 // But if that is not the case, we set the actual button width to PM_TabBarScrollButtonWidth, and
591 // use the extra space from SE_TabBarScrollLeftButton as margins towards the tabs.
592 if (vertTabs) {
593 scrollButtonLeftRect.setHeight(scrollButtonWidth);
594 scrollButtonRightRect.setY(scrollButtonRightRect.bottom() + 1 - scrollButtonWidth);
595 scrollButtonRightRect.setHeight(scrollButtonWidth);
596 leftB->setArrowType(Qt::UpArrow);
597 rightB->setArrowType(Qt::DownArrow);
598 } else if (q->layoutDirection() == Qt::RightToLeft) {
599 scrollButtonRightRect.setWidth(scrollButtonWidth);
600 scrollButtonLeftRect.setX(scrollButtonLeftRect.right() + 1 - scrollButtonWidth);
601 scrollButtonLeftRect.setWidth(scrollButtonWidth);
602 leftB->setArrowType(Qt::RightArrow);
603 rightB->setArrowType(Qt::LeftArrow);
604 } else {
605 scrollButtonLeftRect.setWidth(scrollButtonWidth);
606 scrollButtonRightRect.setX(scrollButtonRightRect.right() + 1 - scrollButtonWidth);
607 scrollButtonRightRect.setWidth(scrollButtonWidth);
608 leftB->setArrowType(Qt::LeftArrow);
609 rightB->setArrowType(Qt::RightArrow);
610 }
611
612 leftB->setGeometry(scrollButtonLeftRect);
613 leftB->setEnabled(false);
614 leftB->show();
615
616 rightB->setGeometry(scrollButtonRightRect);
617 rightB->setEnabled(last - scrollOffset > scrollRect.x() + scrollRect.width());
618 rightB->show();
619 } else {
620 scrollOffset = 0;
621 rightB->hide();
622 leftB->hide();
623 }
624
625 layoutWidgets();
626 q->tabLayoutChange();
627}
628
629QRect QTabBarPrivate::normalizedScrollRect(int index)
630{
631 // "Normalized scroll rect" means return the free space on the tab bar
632 // that doesn't overlap with scroll buttons or tear indicators, and
633 // always return the rect as horizontal Qt::LeftToRight, even if the
634 // tab bar itself is in a different orientation.
635
636 Q_Q(QTabBar);
637 QStyleOptionTabV4 opt;
638 q->initStyleOption(option: &opt, tabIndex: currentIndex);
639 opt.rect = q->rect();
640
641 QRect scrollButtonLeftRect = q->style()->subElementRect(subElement: QStyle::SE_TabBarScrollLeftButton, option: &opt, widget: q);
642 QRect scrollButtonRightRect = q->style()->subElementRect(subElement: QStyle::SE_TabBarScrollRightButton, option: &opt, widget: q);
643 QRect tearLeftRect = q->style()->subElementRect(subElement: QStyle::SE_TabBarTearIndicatorLeft, option: &opt, widget: q);
644 QRect tearRightRect = q->style()->subElementRect(subElement: QStyle::SE_TabBarTearIndicatorRight, option: &opt, widget: q);
645
646 if (verticalTabs(shape)) {
647 int topEdge, bottomEdge;
648 bool leftButtonIsOnTop = scrollButtonLeftRect.y() < q->height() / 2;
649 bool rightButtonIsOnTop = scrollButtonRightRect.y() < q->height() / 2;
650
651 if (leftButtonIsOnTop && rightButtonIsOnTop) {
652 topEdge = scrollButtonRightRect.bottom() + 1;
653 bottomEdge = q->height();
654 } else if (!leftButtonIsOnTop && !rightButtonIsOnTop) {
655 topEdge = 0;
656 bottomEdge = scrollButtonLeftRect.top();
657 } else {
658 topEdge = scrollButtonLeftRect.bottom() + 1;
659 bottomEdge = scrollButtonRightRect.top();
660 }
661
662 bool tearTopVisible = index != 0 && topEdge != -scrollOffset;
663 bool tearBottomVisible = index != tabList.size() - 1 && bottomEdge != tabList.constLast().rect.bottom() + 1 - scrollOffset;
664 if (tearTopVisible && !tearLeftRect.isNull())
665 topEdge = tearLeftRect.bottom() + 1;
666 if (tearBottomVisible && !tearRightRect.isNull())
667 bottomEdge = tearRightRect.top();
668
669 return QRect(topEdge, 0, bottomEdge - topEdge, q->height());
670 } else {
671 if (q->layoutDirection() == Qt::RightToLeft) {
672 scrollButtonLeftRect = QStyle::visualRect(direction: Qt::RightToLeft, boundingRect: q->rect(), logicalRect: scrollButtonLeftRect);
673 scrollButtonRightRect = QStyle::visualRect(direction: Qt::RightToLeft, boundingRect: q->rect(), logicalRect: scrollButtonRightRect);
674 tearLeftRect = QStyle::visualRect(direction: Qt::RightToLeft, boundingRect: q->rect(), logicalRect: tearLeftRect);
675 tearRightRect = QStyle::visualRect(direction: Qt::RightToLeft, boundingRect: q->rect(), logicalRect: tearRightRect);
676 }
677
678 int leftEdge, rightEdge;
679 bool leftButtonIsOnLeftSide = scrollButtonLeftRect.x() < q->width() / 2;
680 bool rightButtonIsOnLeftSide = scrollButtonRightRect.x() < q->width() / 2;
681
682 if (leftButtonIsOnLeftSide && rightButtonIsOnLeftSide) {
683 leftEdge = scrollButtonRightRect.right() + 1;
684 rightEdge = q->width();
685 } else if (!leftButtonIsOnLeftSide && !rightButtonIsOnLeftSide) {
686 leftEdge = 0;
687 rightEdge = scrollButtonLeftRect.left();
688 } else {
689 leftEdge = scrollButtonLeftRect.right() + 1;
690 rightEdge = scrollButtonRightRect.left();
691 }
692
693 bool tearLeftVisible = index != 0 && leftEdge != -scrollOffset;
694 bool tearRightVisible = index != tabList.size() - 1 && rightEdge != tabList.constLast().rect.right() + 1 - scrollOffset;
695 if (tearLeftVisible && !tearLeftRect.isNull())
696 leftEdge = tearLeftRect.right() + 1;
697 if (tearRightVisible && !tearRightRect.isNull())
698 rightEdge = tearRightRect.left();
699
700 return QRect(leftEdge, 0, rightEdge - leftEdge, q->height());
701 }
702}
703
704int QTabBarPrivate::hoveredTabIndex() const
705{
706 if (dragInProgress)
707 return currentIndex;
708 if (hoverIndex >= 0)
709 return hoverIndex;
710 return -1;
711}
712
713void QTabBarPrivate::makeVisible(int index)
714{
715 Q_Q(QTabBar);
716 if (!validIndex(index) || leftB->isHidden())
717 return;
718
719 const QRect tabRect = tabList.at(i: index).rect;
720 const int oldScrollOffset = scrollOffset;
721 const bool horiz = !verticalTabs(shape);
722 const int tabStart = horiz ? tabRect.left() : tabRect.top();
723 const int tabEnd = horiz ? tabRect.right() : tabRect.bottom();
724 const int lastTabEnd = horiz ? tabList.constLast().rect.right() : tabList.constLast().rect.bottom();
725 const QRect scrollRect = normalizedScrollRect(index);
726 const int scrolledTabBarStart = qMax(a: 1, b: scrollRect.left() + scrollOffset);
727 const int scrolledTabBarEnd = qMin(a: lastTabEnd - 1, b: scrollRect.right() + scrollOffset);
728
729 if (tabStart < scrolledTabBarStart) {
730 // Tab is outside on the left, so scroll left.
731 scrollOffset = tabStart - scrollRect.left();
732 } else if (tabEnd > scrolledTabBarEnd) {
733 // Tab is outside on the right, so scroll right.
734 scrollOffset = tabEnd - scrollRect.right();
735 }
736
737 leftB->setEnabled(scrollOffset > -scrollRect.left());
738 rightB->setEnabled(scrollOffset < lastTabEnd - scrollRect.right());
739
740 if (oldScrollOffset != scrollOffset) {
741 q->update();
742 layoutWidgets();
743 }
744}
745
746void QTabBarPrivate::killSwitchTabTimer()
747{
748 Q_Q(QTabBar);
749 if (switchTabTimerId) {
750 q->killTimer(id: switchTabTimerId);
751 switchTabTimerId = 0;
752 }
753 switchTabCurrentIndex = -1;
754}
755
756void QTabBarPrivate::layoutTab(int index)
757{
758 Q_Q(QTabBar);
759 Q_ASSERT(index >= 0);
760
761 Tab &tab = tabList[index];
762 bool vertical = verticalTabs(shape);
763 if (!(tab.leftWidget || tab.rightWidget))
764 return;
765
766 QStyleOptionTabV4 opt;
767 q->initStyleOption(option: &opt, tabIndex: index);
768 if (tab.leftWidget) {
769 QRect rect = q->style()->subElementRect(subElement: QStyle::SE_TabBarTabLeftButton, option: &opt, widget: q);
770 QPoint p = rect.topLeft();
771 if ((index == pressedIndex) || paintWithOffsets) {
772 if (vertical)
773 p.setY(p.y() + tabList[index].dragOffset);
774 else
775 p.setX(p.x() + tabList[index].dragOffset);
776 }
777 tab.leftWidget->move(p);
778 }
779 if (tab.rightWidget) {
780 QRect rect = q->style()->subElementRect(subElement: QStyle::SE_TabBarTabRightButton, option: &opt, widget: q);
781 QPoint p = rect.topLeft();
782 if ((index == pressedIndex) || paintWithOffsets) {
783 if (vertical)
784 p.setY(p.y() + tab.dragOffset);
785 else
786 p.setX(p.x() + tab.dragOffset);
787 }
788 tab.rightWidget->move(p);
789 }
790}
791
792void QTabBarPrivate::layoutWidgets(int start)
793{
794 Q_Q(QTabBar);
795 for (int i = start; i < q->count(); ++i) {
796 layoutTab(index: i);
797 }
798}
799
800void QTabBarPrivate::autoHideTabs()
801{
802 Q_Q(QTabBar);
803
804 if (autoHide)
805 q->setVisible(q->count() > 1);
806}
807
808void QTabBarPrivate::_q_closeTab()
809{
810 Q_Q(QTabBar);
811 QObject *object = q->sender();
812 int tabToClose = -1;
813 QTabBar::ButtonPosition closeSide = (QTabBar::ButtonPosition)q->style()->styleHint(stylehint: QStyle::SH_TabBar_CloseButtonPosition, opt: nullptr, widget: q);
814 for (int i = 0; i < tabList.count(); ++i) {
815 if (closeSide == QTabBar::LeftSide) {
816 if (tabList.at(i).leftWidget == object) {
817 tabToClose = i;
818 break;
819 }
820 } else {
821 if (tabList.at(i).rightWidget == object) {
822 tabToClose = i;
823 break;
824 }
825 }
826 }
827 if (tabToClose != -1)
828 emit q->tabCloseRequested(index: tabToClose);
829}
830
831void QTabBarPrivate::_q_scrollTabs()
832{
833 Q_Q(QTabBar);
834 const QObject *sender = q->sender();
835 const bool horizontal = !verticalTabs(shape);
836 const QRect scrollRect = normalizedScrollRect().translated(dx: scrollOffset, dy: 0);
837
838 int i = -1;
839
840 if (sender == leftB) {
841 for (i = tabList.count() - 1; i >= 0; --i) {
842 int start = horizontal ? tabList.at(i).rect.left() : tabList.at(i).rect.top();
843 if (start < scrollRect.left()) {
844 makeVisible(index: i);
845 return;
846 }
847 }
848 } else if (sender == rightB) {
849 for (i = 0; i < tabList.count(); ++i) {
850 const auto tabRect = tabList.at(i).rect;
851 int start = horizontal ? tabRect.left() : tabRect.top();
852 int end = horizontal ? tabRect.right() : tabRect.bottom();
853 if (end > scrollRect.right() && start > scrollOffset) {
854 makeVisible(index: i);
855 return;
856 }
857 }
858 }
859}
860
861void QTabBarPrivate::refresh()
862{
863 Q_Q(QTabBar);
864
865 // be safe in case a subclass is also handling move with the tabs
866 if (pressedIndex != -1
867 && movable
868 && QGuiApplication::mouseButtons() == Qt::NoButton) {
869 moveTabFinished(index: pressedIndex);
870 if (!validIndex(index: pressedIndex))
871 pressedIndex = -1;
872 }
873
874 if (!q->isVisible()) {
875 layoutDirty = true;
876 } else {
877 layoutTabs();
878 makeVisible(index: currentIndex);
879 q->update();
880 q->updateGeometry();
881 }
882}
883
884/*!
885 Creates a new tab bar with the given \a parent.
886*/
887QTabBar::QTabBar(QWidget* parent)
888 :QWidget(*new QTabBarPrivate, parent, { })
889{
890 Q_D(QTabBar);
891 d->init();
892}
893
894
895/*!
896 Destroys the tab bar.
897*/
898QTabBar::~QTabBar()
899{
900}
901
902/*!
903 \property QTabBar::shape
904 \brief the shape of the tabs in the tab bar
905
906 Possible values for this property are described by the Shape enum.
907*/
908
909
910QTabBar::Shape QTabBar::shape() const
911{
912 Q_D(const QTabBar);
913 return d->shape;
914}
915
916void QTabBar::setShape(Shape shape)
917{
918 Q_D(QTabBar);
919 if (d->shape == shape)
920 return;
921 d->shape = shape;
922 d->refresh();
923}
924
925/*!
926 \property QTabBar::drawBase
927 \brief defines whether or not tab bar should draw its base.
928
929 If true then QTabBar draws a base in relation to the styles overlab.
930 Otherwise only the tabs are drawn.
931
932 \sa QStyle::pixelMetric(), QStyle::PM_TabBarBaseOverlap, QStyleOptionTabBarBase
933*/
934
935void QTabBar::setDrawBase(bool drawBase)
936{
937 Q_D(QTabBar);
938 if (d->drawBase == drawBase)
939 return;
940 d->drawBase = drawBase;
941 update();
942}
943
944bool QTabBar::drawBase() const
945{
946 Q_D(const QTabBar);
947 return d->drawBase;
948}
949
950/*!
951 Adds a new tab with text \a text. Returns the new
952 tab's index.
953*/
954int QTabBar::addTab(const QString &text)
955{
956 return insertTab(index: -1, text);
957}
958
959/*!
960 \overload
961
962 Adds a new tab with icon \a icon and text \a
963 text. Returns the new tab's index.
964*/
965int QTabBar::addTab(const QIcon& icon, const QString &text)
966{
967 return insertTab(index: -1, icon, text);
968}
969
970/*!
971 Inserts a new tab with text \a text at position \a index. If \a
972 index is out of range, the new tab is appened. Returns the new
973 tab's index.
974*/
975int QTabBar::insertTab(int index, const QString &text)
976{
977 return insertTab(index, icon: QIcon(), text);
978}
979
980/*!\overload
981
982 Inserts a new tab with icon \a icon and text \a text at position
983 \a index. If \a index is out of range, the new tab is
984 appended. Returns the new tab's index.
985
986 If the QTabBar was empty before this function is called, the inserted tab
987 becomes the current tab.
988
989 Inserting a new tab at an index less than or equal to the current index
990 will increment the current index, but keep the current tab.
991*/
992int QTabBar::insertTab(int index, const QIcon& icon, const QString &text)
993{
994 Q_D(QTabBar);
995 if (!d->validIndex(index)) {
996 index = d->tabList.count();
997 d->tabList.append(t: QTabBarPrivate::Tab(icon, text));
998 } else {
999 d->tabList.insert(i: index, t: QTabBarPrivate::Tab(icon, text));
1000 }
1001#ifndef QT_NO_SHORTCUT
1002 d->tabList[index].shortcutId = grabShortcut(key: QKeySequence::mnemonic(text));
1003#endif
1004 d->firstVisible = qMax(a: qMin(a: index, b: d->firstVisible), b: 0);
1005 d->refresh();
1006 if (d->tabList.count() == 1)
1007 setCurrentIndex(index);
1008 else if (index <= d->currentIndex)
1009 ++d->currentIndex;
1010
1011 if (index <= d->lastVisible)
1012 ++d->lastVisible;
1013 else
1014 d->lastVisible = index;
1015
1016 if (d->closeButtonOnTabs) {
1017 QStyleOptionTabV4 opt;
1018 initStyleOption(option: &opt, tabIndex: index);
1019 ButtonPosition closeSide = (ButtonPosition)style()->styleHint(stylehint: QStyle::SH_TabBar_CloseButtonPosition, opt: nullptr, widget: this);
1020 QAbstractButton *closeButton = new CloseButton(this);
1021 connect(sender: closeButton, SIGNAL(clicked()), receiver: this, SLOT(_q_closeTab()));
1022 setTabButton(index, position: closeSide, widget: closeButton);
1023 }
1024
1025 for (int i = 0; i < d->tabList.count(); ++i) {
1026 if (d->tabList[i].lastTab >= index)
1027 ++d->tabList[i].lastTab;
1028 }
1029
1030 tabInserted(index);
1031 d->autoHideTabs();
1032 return index;
1033}
1034
1035
1036/*!
1037 Removes the tab at position \a index.
1038
1039 \sa SelectionBehavior
1040 */
1041void QTabBar::removeTab(int index)
1042{
1043 Q_D(QTabBar);
1044 if (d->validIndex(index)) {
1045 if (d->dragInProgress)
1046 d->moveTabFinished(index: d->pressedIndex);
1047
1048#ifndef QT_NO_SHORTCUT
1049 releaseShortcut(id: d->tabList.at(i: index).shortcutId);
1050#endif
1051 if (d->tabList[index].leftWidget) {
1052 d->tabList[index].leftWidget->hide();
1053 d->tabList[index].leftWidget->deleteLater();
1054 d->tabList[index].leftWidget = nullptr;
1055 }
1056 if (d->tabList[index].rightWidget) {
1057 d->tabList[index].rightWidget->hide();
1058 d->tabList[index].rightWidget->deleteLater();
1059 d->tabList[index].rightWidget = nullptr;
1060 }
1061
1062 int newIndex = d->tabList[index].lastTab;
1063 d->tabList.removeAt(i: index);
1064 for (int i = 0; i < d->tabList.count(); ++i) {
1065 if (d->tabList[i].lastTab == index)
1066 d->tabList[i].lastTab = -1;
1067 if (d->tabList[i].lastTab > index)
1068 --d->tabList[i].lastTab;
1069 }
1070
1071 d->calculateFirstLastVisible(index, visible: false, remove: true);
1072
1073 if (index == d->currentIndex) {
1074 // The current tab is going away, in order to make sure
1075 // we emit that "current has changed", we need to reset this
1076 // around.
1077 d->currentIndex = -1;
1078 if (d->tabList.size() > 0) {
1079 switch(d->selectionBehaviorOnRemove) {
1080 case SelectPreviousTab:
1081 if (newIndex > index)
1082 newIndex--;
1083 if (d->validIndex(index: newIndex) && d->tabList.at(i: newIndex).visible)
1084 break;
1085 Q_FALLTHROUGH();
1086 case SelectRightTab:
1087 newIndex = qBound(min: d->firstVisible, val: index, max: d->lastVisible);
1088 break;
1089 case SelectLeftTab:
1090 newIndex = qBound(min: d->firstVisible, val: index-1, max: d->lastVisible);
1091 if (newIndex < 0)
1092 newIndex = 0;
1093 break;
1094 default:
1095 break;
1096 }
1097
1098 if (d->validIndex(index: newIndex)) {
1099 // don't loose newIndex's old through setCurrentIndex
1100 int bump = d->tabList[newIndex].lastTab;
1101 setCurrentIndex(newIndex);
1102 d->tabList[newIndex].lastTab = bump;
1103 }
1104 } else {
1105 emit currentChanged(index: -1);
1106 }
1107 } else if (index < d->currentIndex) {
1108 setCurrentIndex(d->currentIndex - 1);
1109 }
1110 d->refresh();
1111 d->autoHideTabs();
1112 if (!d->hoverRect.isEmpty()) {
1113 for (int i = 0; i < d->tabList.count(); ++i) {
1114 const QRect area = tabRect(index: i);
1115 if (area.contains(p: mapFromGlobal(QCursor::pos()))) {
1116 d->hoverIndex = i;
1117 d->hoverRect = area;
1118 break;
1119 }
1120 }
1121 update(d->hoverRect);
1122 }
1123 tabRemoved(index);
1124 }
1125}
1126
1127
1128/*!
1129 Returns \c true if the tab at position \a index is enabled; otherwise
1130 returns \c false.
1131*/
1132bool QTabBar::isTabEnabled(int index) const
1133{
1134 Q_D(const QTabBar);
1135 if (const QTabBarPrivate::Tab *tab = d->at(index))
1136 return tab->enabled;
1137 return false;
1138}
1139
1140/*!
1141 If \a enabled is true then the tab at position \a index is
1142 enabled; otherwise the item at position \a index is disabled.
1143*/
1144void QTabBar::setTabEnabled(int index, bool enabled)
1145{
1146 Q_D(QTabBar);
1147 if (QTabBarPrivate::Tab *tab = d->at(index)) {
1148 tab->enabled = enabled;
1149#ifndef QT_NO_SHORTCUT
1150 setShortcutEnabled(id: tab->shortcutId, enable: enabled);
1151#endif
1152 update();
1153 if (!enabled && index == d->currentIndex)
1154 setCurrentIndex(d->selectNewCurrentIndexFrom(currentIndex: index+1));
1155 else if (enabled && !isTabVisible(index: d->currentIndex))
1156 setCurrentIndex(d->selectNewCurrentIndexFrom(currentIndex: index));
1157 }
1158}
1159
1160
1161/*!
1162 Returns true if the tab at position \a index is visible; otherwise
1163 returns false.
1164 \since 5.15
1165*/
1166bool QTabBar::isTabVisible(int index) const
1167{
1168 Q_D(const QTabBar);
1169 if (d->validIndex(index))
1170 return d->tabList.at(i: index).visible;
1171 return false;
1172}
1173
1174/*!
1175 If \a visible is true, make the tab at position \a index visible,
1176 otherwise make it hidden.
1177 \since 5.15
1178*/
1179void QTabBar::setTabVisible(int index, bool visible)
1180{
1181 Q_D(QTabBar);
1182 if (QTabBarPrivate::Tab *tab = d->at(index)) {
1183 d->layoutDirty = (visible != tab->visible);
1184 if (!d->layoutDirty)
1185 return;
1186 tab->visible = visible;
1187 if (tab->leftWidget)
1188 tab->leftWidget->setVisible(visible);
1189 if (tab->rightWidget)
1190 tab->rightWidget->setVisible(visible);
1191#ifndef QT_NO_SHORTCUT
1192 setShortcutEnabled(id: tab->shortcutId, enable: visible);
1193#endif
1194 d->calculateFirstLastVisible(index, visible, remove: false);
1195 if (!visible && index == d->currentIndex) {
1196 const int newindex = d->selectNewCurrentIndexFrom(currentIndex: index+1);
1197 setCurrentIndex(newindex);
1198 }
1199 update();
1200 }
1201}
1202
1203
1204/*!
1205 Returns the text of the tab at position \a index, or an empty
1206 string if \a index is out of range.
1207*/
1208QString QTabBar::tabText(int index) const
1209{
1210 Q_D(const QTabBar);
1211 if (const QTabBarPrivate::Tab *tab = d->at(index))
1212 return tab->text;
1213 return QString();
1214}
1215
1216/*!
1217 Sets the text of the tab at position \a index to \a text.
1218*/
1219void QTabBar::setTabText(int index, const QString &text)
1220{
1221 Q_D(QTabBar);
1222 if (QTabBarPrivate::Tab *tab = d->at(index)) {
1223 d->textSizes.remove(akey: tab->text);
1224 tab->text = text;
1225#ifndef QT_NO_SHORTCUT
1226 releaseShortcut(id: tab->shortcutId);
1227 tab->shortcutId = grabShortcut(key: QKeySequence::mnemonic(text));
1228 setShortcutEnabled(id: tab->shortcutId, enable: tab->enabled);
1229#endif
1230 d->refresh();
1231 }
1232}
1233
1234/*!
1235 Returns the text color of the tab with the given \a index, or a invalid
1236 color if \a index is out of range.
1237
1238 \sa setTabTextColor()
1239*/
1240QColor QTabBar::tabTextColor(int index) const
1241{
1242 Q_D(const QTabBar);
1243 if (const QTabBarPrivate::Tab *tab = d->at(index))
1244 return tab->textColor;
1245 return QColor();
1246}
1247
1248/*!
1249 Sets the color of the text in the tab with the given \a index to the specified \a color.
1250
1251 If an invalid color is specified, the tab will use the QTabBar foreground role instead.
1252
1253 \sa tabTextColor()
1254*/
1255void QTabBar::setTabTextColor(int index, const QColor &color)
1256{
1257 Q_D(QTabBar);
1258 if (QTabBarPrivate::Tab *tab = d->at(index)) {
1259 tab->textColor = color;
1260 update(tabRect(index));
1261 }
1262}
1263
1264/*!
1265 Returns the icon of the tab at position \a index, or a null icon
1266 if \a index is out of range.
1267*/
1268QIcon QTabBar::tabIcon(int index) const
1269{
1270 Q_D(const QTabBar);
1271 if (const QTabBarPrivate::Tab *tab = d->at(index))
1272 return tab->icon;
1273 return QIcon();
1274}
1275
1276/*!
1277 Sets the icon of the tab at position \a index to \a icon.
1278*/
1279void QTabBar::setTabIcon(int index, const QIcon & icon)
1280{
1281 Q_D(QTabBar);
1282 if (QTabBarPrivate::Tab *tab = d->at(index)) {
1283 bool simpleIconChange = (!icon.isNull() && !tab->icon.isNull());
1284 tab->icon = icon;
1285 if (simpleIconChange)
1286 update(tabRect(index));
1287 else
1288 d->refresh();
1289 }
1290}
1291
1292#ifndef QT_NO_TOOLTIP
1293/*!
1294 Sets the tool tip of the tab at position \a index to \a tip.
1295*/
1296void QTabBar::setTabToolTip(int index, const QString & tip)
1297{
1298 Q_D(QTabBar);
1299 if (QTabBarPrivate::Tab *tab = d->at(index))
1300 tab->toolTip = tip;
1301}
1302
1303/*!
1304 Returns the tool tip of the tab at position \a index, or an empty
1305 string if \a index is out of range.
1306*/
1307QString QTabBar::tabToolTip(int index) const
1308{
1309 Q_D(const QTabBar);
1310 if (const QTabBarPrivate::Tab *tab = d->at(index))
1311 return tab->toolTip;
1312 return QString();
1313}
1314#endif // QT_NO_TOOLTIP
1315
1316#if QT_CONFIG(whatsthis)
1317/*!
1318 \since 4.1
1319
1320 Sets the What's This help text of the tab at position \a index
1321 to \a text.
1322*/
1323void QTabBar::setTabWhatsThis(int index, const QString &text)
1324{
1325 Q_D(QTabBar);
1326 if (QTabBarPrivate::Tab *tab = d->at(index))
1327 tab->whatsThis = text;
1328}
1329
1330/*!
1331 \since 4.1
1332
1333 Returns the What's This help text of the tab at position \a index,
1334 or an empty string if \a index is out of range.
1335*/
1336QString QTabBar::tabWhatsThis(int index) const
1337{
1338 Q_D(const QTabBar);
1339 if (const QTabBarPrivate::Tab *tab = d->at(index))
1340 return tab->whatsThis;
1341 return QString();
1342}
1343
1344#endif // QT_CONFIG(whatsthis)
1345
1346/*!
1347 Sets the data of the tab at position \a index to \a data.
1348*/
1349void QTabBar::setTabData(int index, const QVariant & data)
1350{
1351 Q_D(QTabBar);
1352 if (QTabBarPrivate::Tab *tab = d->at(index))
1353 tab->data = data;
1354}
1355
1356/*!
1357 Returns the data of the tab at position \a index, or a null
1358 variant if \a index is out of range.
1359*/
1360QVariant QTabBar::tabData(int index) const
1361{
1362 Q_D(const QTabBar);
1363 if (const QTabBarPrivate::Tab *tab = d->at(index))
1364 return tab->data;
1365 return QVariant();
1366}
1367
1368/*!
1369 Returns the visual rectangle of the tab at position \a
1370 index, or a null rectangle if \a index is hidden, or out of range.
1371*/
1372QRect QTabBar::tabRect(int index) const
1373{
1374 Q_D(const QTabBar);
1375 if (const QTabBarPrivate::Tab *tab = d->at(index)) {
1376 if (d->layoutDirty)
1377 const_cast<QTabBarPrivate*>(d)->layoutTabs();
1378 if (!tab->visible)
1379 return QRect();
1380 QRect r = tab->rect;
1381 if (verticalTabs(shape: d->shape))
1382 r.translate(dx: 0, dy: -d->scrollOffset);
1383 else
1384 r.translate(dx: -d->scrollOffset, dy: 0);
1385 if (!verticalTabs(shape: d->shape))
1386 r = QStyle::visualRect(direction: layoutDirection(), boundingRect: rect(), logicalRect: r);
1387 return r;
1388 }
1389 return QRect();
1390}
1391
1392/*!
1393 \since 4.3
1394 Returns the index of the tab that covers \a position or -1 if no
1395 tab covers \a position;
1396*/
1397
1398int QTabBar::tabAt(const QPoint &position) const
1399{
1400 Q_D(const QTabBar);
1401 if (d->validIndex(index: d->currentIndex)
1402 && tabRect(index: d->currentIndex).contains(p: position)) {
1403 return d->currentIndex;
1404 }
1405 const int max = d->tabList.size();
1406 for (int i = 0; i < max; ++i) {
1407 if (tabRect(index: i).contains(p: position)) {
1408 return i;
1409 }
1410 }
1411 return -1;
1412}
1413
1414/*!
1415 \property QTabBar::currentIndex
1416 \brief the index of the tab bar's visible tab
1417
1418 The current index is -1 if there is no current tab.
1419*/
1420
1421int QTabBar::currentIndex() const
1422{
1423 Q_D(const QTabBar);
1424 if (d->validIndex(index: d->currentIndex))
1425 return d->currentIndex;
1426 return -1;
1427}
1428
1429
1430void QTabBar::setCurrentIndex(int index)
1431{
1432 Q_D(QTabBar);
1433 if (d->dragInProgress && d->pressedIndex != -1)
1434 return;
1435
1436 int oldIndex = d->currentIndex;
1437 if (d->validIndex(index) && d->currentIndex != index) {
1438 d->currentIndex = index;
1439 update();
1440 d->makeVisible(index);
1441 d->tabList[index].lastTab = oldIndex;
1442 if (oldIndex >= 0 && oldIndex < count())
1443 d->layoutTab(index: oldIndex);
1444 d->layoutTab(index);
1445#ifndef QT_NO_ACCESSIBILITY
1446 if (QAccessible::isActive()) {
1447 if (hasFocus()) {
1448 QAccessibleEvent focusEvent(this, QAccessible::Focus);
1449 focusEvent.setChild(index);
1450 QAccessible::updateAccessibility(event: &focusEvent);
1451 }
1452 QAccessibleEvent selectionEvent(this, QAccessible::Selection);
1453 selectionEvent.setChild(index);
1454 QAccessible::updateAccessibility(event: &selectionEvent);
1455 }
1456#endif
1457 emit currentChanged(index);
1458 }
1459}
1460
1461/*!
1462 \property QTabBar::iconSize
1463 \brief The size for icons in the tab bar
1464 \since 4.1
1465
1466 The default value is style-dependent. \c iconSize is a maximum
1467 size; icons that are smaller are not scaled up.
1468
1469 \sa QTabWidget::iconSize
1470*/
1471QSize QTabBar::iconSize() const
1472{
1473 Q_D(const QTabBar);
1474 if (d->iconSize.isValid())
1475 return d->iconSize;
1476 int iconExtent = style()->pixelMetric(metric: QStyle::PM_TabBarIconSize, option: nullptr, widget: this);
1477 return QSize(iconExtent, iconExtent);
1478
1479}
1480
1481void QTabBar::setIconSize(const QSize &size)
1482{
1483 Q_D(QTabBar);
1484 d->iconSize = size;
1485 d->layoutDirty = true;
1486 update();
1487 updateGeometry();
1488}
1489
1490/*!
1491 \property QTabBar::count
1492 \brief the number of tabs in the tab bar
1493*/
1494
1495int QTabBar::count() const
1496{
1497 Q_D(const QTabBar);
1498 return d->tabList.count();
1499}
1500
1501
1502/*!\reimp
1503 */
1504QSize QTabBar::sizeHint() const
1505{
1506 Q_D(const QTabBar);
1507 if (d->layoutDirty)
1508 const_cast<QTabBarPrivate*>(d)->layoutTabs();
1509 QRect r;
1510 for (int i = 0; i < d->tabList.count(); ++i) {
1511 if (d->tabList.at(i).visible)
1512 r = r.united(r: d->tabList.at(i).maxRect);
1513 }
1514 QSize sz = QApplication::globalStrut();
1515 return r.size().expandedTo(otherSize: sz);
1516}
1517
1518/*!\reimp
1519 */
1520QSize QTabBar::minimumSizeHint() const
1521{
1522 Q_D(const QTabBar);
1523 if (d->layoutDirty)
1524 const_cast<QTabBarPrivate*>(d)->layoutTabs();
1525 if (!d->useScrollButtons) {
1526 QRect r;
1527 for (int i = 0; i < d->tabList.count(); ++i) {
1528 if (d->tabList.at(i).visible)
1529 r = r.united(r: d->tabList.at(i).minRect);
1530 }
1531 return r.size().expandedTo(otherSize: QApplication::globalStrut());
1532 }
1533 if (verticalTabs(shape: d->shape))
1534 return QSize(sizeHint().width(), d->rightB->sizeHint().height() * 2 + 75);
1535 else
1536 return QSize(d->rightB->sizeHint().width() * 2 + 75, sizeHint().height());
1537}
1538
1539// Compute the most-elided possible text, for minimumSizeHint
1540static QString computeElidedText(Qt::TextElideMode mode, const QString &text)
1541{
1542 if (text.length() <= 3)
1543 return text;
1544
1545 static const QLatin1String Ellipses("...");
1546 QString ret;
1547 switch (mode) {
1548 case Qt::ElideRight:
1549 ret = text.leftRef(n: 2) + Ellipses;
1550 break;
1551 case Qt::ElideMiddle:
1552 ret = text.leftRef(n: 1) + Ellipses + text.rightRef(n: 1);
1553 break;
1554 case Qt::ElideLeft:
1555 ret = Ellipses + text.rightRef(n: 2);
1556 break;
1557 case Qt::ElideNone:
1558 ret = text;
1559 break;
1560 }
1561 return ret;
1562}
1563
1564/*!
1565 Returns the minimum tab size hint for the tab at position \a index.
1566 \since 5.0
1567*/
1568
1569QSize QTabBar::minimumTabSizeHint(int index) const
1570{
1571 Q_D(const QTabBar);
1572 QTabBarPrivate::Tab &tab = const_cast<QTabBarPrivate::Tab&>(d->tabList[index]);
1573 QString oldText = tab.text;
1574 tab.text = computeElidedText(mode: d->elideMode, text: oldText);
1575 QSize size = tabSizeHint(index);
1576 tab.text = oldText;
1577 return size;
1578}
1579
1580/*!
1581 Returns the size hint for the tab at position \a index.
1582*/
1583QSize QTabBar::tabSizeHint(int index) const
1584{
1585 //Note: this must match with the computations in QCommonStylePrivate::tabLayout
1586 Q_D(const QTabBar);
1587 if (const QTabBarPrivate::Tab *tab = d->at(index)) {
1588 QStyleOptionTabV4 opt;
1589 d->initBasicStyleOption(option: &opt, tabIndex: index);
1590 opt.text = d->tabList.at(i: index).text;
1591 QSize iconSize = tab->icon.isNull() ? QSize(0, 0) : opt.iconSize;
1592 int hframe = style()->pixelMetric(metric: QStyle::PM_TabBarTabHSpace, option: &opt, widget: this);
1593 int vframe = style()->pixelMetric(metric: QStyle::PM_TabBarTabVSpace, option: &opt, widget: this);
1594 const QFontMetrics fm = fontMetrics();
1595
1596 int maxWidgetHeight = qMax(a: opt.leftButtonSize.height(), b: opt.rightButtonSize.height());
1597 int maxWidgetWidth = qMax(a: opt.leftButtonSize.width(), b: opt.rightButtonSize.width());
1598
1599 int widgetWidth = 0;
1600 int widgetHeight = 0;
1601 int padding = 0;
1602 if (!opt.leftButtonSize.isEmpty()) {
1603 padding += 4;
1604 widgetWidth += opt.leftButtonSize.width();
1605 widgetHeight += opt.leftButtonSize.height();
1606 }
1607 if (!opt.rightButtonSize.isEmpty()) {
1608 padding += 4;
1609 widgetWidth += opt.rightButtonSize.width();
1610 widgetHeight += opt.rightButtonSize.height();
1611 }
1612 if (!opt.icon.isNull())
1613 padding += 4;
1614
1615 QHash<QString, QSize>::iterator it = d->textSizes.find(akey: tab->text);
1616 if (it == d->textSizes.end())
1617 it = d->textSizes.insert(akey: tab->text, avalue: fm.size(flags: Qt::TextShowMnemonic, str: tab->text));
1618 const int textWidth = it.value().width();
1619 QSize csz;
1620 if (verticalTabs(shape: d->shape)) {
1621 csz = QSize( qMax(a: maxWidgetWidth, b: qMax(a: fm.height(), b: iconSize.height())) + vframe,
1622 textWidth + iconSize.width() + hframe + widgetHeight + padding);
1623 } else {
1624 csz = QSize(textWidth + iconSize.width() + hframe + widgetWidth + padding,
1625 qMax(a: maxWidgetHeight, b: qMax(a: fm.height(), b: iconSize.height())) + vframe);
1626 }
1627
1628 QSize retSize = style()->sizeFromContents(ct: QStyle::CT_TabBarTab, opt: &opt, contentsSize: csz, w: this);
1629 return retSize;
1630 }
1631 return QSize();
1632}
1633
1634/*!
1635 This virtual handler is called after a new tab was added or
1636 inserted at position \a index.
1637
1638 \sa tabRemoved()
1639 */
1640void QTabBar::tabInserted(int index)
1641{
1642 Q_UNUSED(index)
1643}
1644
1645/*!
1646 This virtual handler is called after a tab was removed from
1647 position \a index.
1648
1649 \sa tabInserted()
1650 */
1651void QTabBar::tabRemoved(int index)
1652{
1653 Q_UNUSED(index)
1654}
1655
1656/*!
1657 This virtual handler is called whenever the tab layout changes.
1658
1659 \sa tabRect()
1660 */
1661void QTabBar::tabLayoutChange()
1662{
1663}
1664
1665
1666/*!\reimp
1667 */
1668void QTabBar::showEvent(QShowEvent *)
1669{
1670 Q_D(QTabBar);
1671 if (d->layoutDirty)
1672 d->refresh();
1673 if (!d->validIndex(index: d->currentIndex))
1674 setCurrentIndex(0);
1675 d->updateMacBorderMetrics();
1676}
1677
1678/*!\reimp
1679 */
1680void QTabBar::hideEvent(QHideEvent *)
1681{
1682 Q_D(QTabBar);
1683 d->updateMacBorderMetrics();
1684}
1685
1686/*!\reimp
1687 */
1688bool QTabBar::event(QEvent *event)
1689{
1690 Q_D(QTabBar);
1691 if (event->type() == QEvent::HoverMove
1692 || event->type() == QEvent::HoverEnter) {
1693 QHoverEvent *he = static_cast<QHoverEvent *>(event);
1694 if (!d->hoverRect.contains(p: he->pos())) {
1695 QRect oldHoverRect = d->hoverRect;
1696 bool cursorOverTabs = false;
1697 for (int i = 0; i < d->tabList.count(); ++i) {
1698 QRect area = tabRect(index: i);
1699 if (area.contains(p: he->pos())) {
1700 d->hoverIndex = i;
1701 d->hoverRect = area;
1702 cursorOverTabs = true;
1703 break;
1704 }
1705 }
1706 if (!cursorOverTabs) {
1707 d->hoverIndex = -1;
1708 d->hoverRect = QRect();
1709 }
1710 if (he->oldPos() != QPoint(-1, -1))
1711 update(oldHoverRect);
1712 update(d->hoverRect);
1713 }
1714 return true;
1715 } else if (event->type() == QEvent::HoverLeave) {
1716 QRect oldHoverRect = d->hoverRect;
1717 d->hoverIndex = -1;
1718 d->hoverRect = QRect();
1719 update(oldHoverRect);
1720 return true;
1721#ifndef QT_NO_TOOLTIP
1722 } else if (event->type() == QEvent::ToolTip) {
1723 if (const QTabBarPrivate::Tab *tab = d->at(index: tabAt(position: static_cast<QHelpEvent*>(event)->pos()))) {
1724 if (!tab->toolTip.isEmpty()) {
1725 QToolTip::showText(pos: static_cast<QHelpEvent*>(event)->globalPos(), text: tab->toolTip, w: this);
1726 return true;
1727 }
1728 }
1729#endif // QT_NO_TOOLTIP
1730#if QT_CONFIG(whatsthis)
1731 } else if (event->type() == QEvent::QueryWhatsThis) {
1732 const QTabBarPrivate::Tab *tab = d->at(index: d->indexAtPos(p: static_cast<QHelpEvent*>(event)->pos()));
1733 if (!tab || tab->whatsThis.isEmpty())
1734 event->ignore();
1735 return true;
1736 } else if (event->type() == QEvent::WhatsThis) {
1737 if (const QTabBarPrivate::Tab *tab = d->at(index: d->indexAtPos(p: static_cast<QHelpEvent*>(event)->pos()))) {
1738 if (!tab->whatsThis.isEmpty()) {
1739 QWhatsThis::showText(pos: static_cast<QHelpEvent*>(event)->globalPos(),
1740 text: tab->whatsThis, w: this);
1741 return true;
1742 }
1743 }
1744#endif // QT_CONFIG(whatsthis)
1745#ifndef QT_NO_SHORTCUT
1746 } else if (event->type() == QEvent::Shortcut) {
1747 QShortcutEvent *se = static_cast<QShortcutEvent *>(event);
1748 for (int i = 0; i < d->tabList.count(); ++i) {
1749 const QTabBarPrivate::Tab *tab = &d->tabList.at(i);
1750 if (tab->shortcutId == se->shortcutId()) {
1751 setCurrentIndex(i);
1752 return true;
1753 }
1754 }
1755#endif
1756 } else if (event->type() == QEvent::MouseButtonDblClick) { // ### fixme Qt 6: move to mouseDoubleClickEvent(), here for BC reasons.
1757 const QPoint pos = static_cast<const QMouseEvent *>(event)->pos();
1758 const bool isEventInCornerButtons = (!d->leftB->isHidden() && d->leftB->geometry().contains(p: pos))
1759 || (!d->rightB->isHidden() && d->rightB->geometry().contains(p: pos));
1760 if (!isEventInCornerButtons)
1761 emit tabBarDoubleClicked(index: tabAt(position: pos));
1762 } else if (event->type() == QEvent::Move) {
1763 d->updateMacBorderMetrics();
1764 return QWidget::event(event);
1765
1766#if QT_CONFIG(draganddrop)
1767 } else if (event->type() == QEvent::DragEnter) {
1768 if (d->changeCurrentOnDrag)
1769 event->accept();
1770 } else if (event->type() == QEvent::DragMove) {
1771 if (d->changeCurrentOnDrag) {
1772 const int tabIndex = tabAt(position: static_cast<QDragMoveEvent *>(event)->pos());
1773 if (isTabEnabled(index: tabIndex) && d->switchTabCurrentIndex != tabIndex) {
1774 d->switchTabCurrentIndex = tabIndex;
1775 if (d->switchTabTimerId)
1776 killTimer(id: d->switchTabTimerId);
1777 d->switchTabTimerId = startTimer(interval: style()->styleHint(stylehint: QStyle::SH_TabBar_ChangeCurrentDelay));
1778 }
1779 event->ignore();
1780 }
1781 } else if (event->type() == QEvent::DragLeave || event->type() == QEvent::Drop) {
1782 d->killSwitchTabTimer();
1783 event->ignore();
1784#endif
1785 }
1786 return QWidget::event(event);
1787}
1788
1789/*!\reimp
1790 */
1791void QTabBar::resizeEvent(QResizeEvent *)
1792{
1793 Q_D(QTabBar);
1794 if (d->layoutDirty)
1795 updateGeometry();
1796 d->layoutTabs();
1797
1798 d->makeVisible(index: d->currentIndex);
1799}
1800
1801/*!\reimp
1802 */
1803void QTabBar::paintEvent(QPaintEvent *)
1804{
1805 Q_D(QTabBar);
1806
1807 QStyleOptionTabBarBase optTabBase;
1808 QTabBarPrivate::initStyleBaseOption(optTabBase: &optTabBase, tabbar: this, size: size());
1809
1810 QStylePainter p(this);
1811 int selected = -1;
1812 int cutLeft = -1;
1813 int cutRight = -1;
1814 bool vertical = verticalTabs(shape: d->shape);
1815 QStyleOptionTab cutTabLeft;
1816 QStyleOptionTab cutTabRight;
1817 selected = d->currentIndex;
1818 if (d->dragInProgress)
1819 selected = d->pressedIndex;
1820 const QRect scrollRect = d->normalizedScrollRect();
1821
1822 for (int i = 0; i < d->tabList.count(); ++i)
1823 optTabBase.tabBarRect |= tabRect(index: i);
1824
1825 optTabBase.selectedTabRect = tabRect(index: selected);
1826
1827 if (d->drawBase)
1828 p.drawPrimitive(pe: QStyle::PE_FrameTabBarBase, opt: optTabBase);
1829
1830 for (int i = 0; i < d->tabList.count(); ++i) {
1831 if (!d->at(index: i)->visible)
1832 continue;
1833 QStyleOptionTabV4 tab;
1834 initStyleOption(option: &tab, tabIndex: i);
1835 if (d->paintWithOffsets && d->tabList[i].dragOffset != 0) {
1836 if (vertical) {
1837 tab.rect.moveTop(pos: tab.rect.y() + d->tabList[i].dragOffset);
1838 } else {
1839 tab.rect.moveLeft(pos: tab.rect.x() + d->tabList[i].dragOffset);
1840 }
1841 }
1842 if (!(tab.state & QStyle::State_Enabled)) {
1843 tab.palette.setCurrentColorGroup(QPalette::Disabled);
1844 }
1845
1846 // If this tab is partially obscured, make a note of it so that we can pass the information
1847 // along when we draw the tear.
1848 QRect tabRect = d->tabList[i].rect;
1849 int tabStart = vertical ? tabRect.top() : tabRect.left();
1850 int tabEnd = vertical ? tabRect.bottom() : tabRect.right();
1851 if (tabStart < scrollRect.left() + d->scrollOffset) {
1852 cutLeft = i;
1853 cutTabLeft = tab;
1854 } else if (tabEnd > scrollRect.right() + d->scrollOffset) {
1855 cutRight = i;
1856 cutTabRight = tab;
1857 }
1858
1859 // Don't bother drawing a tab if the entire tab is outside of the visible tab bar.
1860 if ((!vertical && (tab.rect.right() < 0 || tab.rect.left() > width()))
1861 || (vertical && (tab.rect.bottom() < 0 || tab.rect.top() > height())))
1862 continue;
1863
1864 optTabBase.tabBarRect |= tab.rect;
1865 if (i == selected)
1866 continue;
1867
1868 p.drawControl(ce: QStyle::CE_TabBarTab, opt: tab);
1869 }
1870
1871 // Draw the selected tab last to get it "on top"
1872 if (selected >= 0) {
1873 QStyleOptionTabV4 tab;
1874 initStyleOption(option: &tab, tabIndex: selected);
1875 if (d->paintWithOffsets && d->tabList[selected].dragOffset != 0) {
1876 if (vertical)
1877 tab.rect.moveTop(pos: tab.rect.y() + d->tabList[selected].dragOffset);
1878 else
1879 tab.rect.moveLeft(pos: tab.rect.x() + d->tabList[selected].dragOffset);
1880 }
1881 if (!d->dragInProgress)
1882 p.drawControl(ce: QStyle::CE_TabBarTab, opt: tab);
1883 else {
1884 int taboverlap = style()->pixelMetric(metric: QStyle::PM_TabBarTabOverlap, option: nullptr, widget: this);
1885 if (verticalTabs(shape: d->shape))
1886 d->movingTab->setGeometry(tab.rect.adjusted(xp1: 0, yp1: -taboverlap, xp2: 0, yp2: taboverlap));
1887 else
1888 d->movingTab->setGeometry(tab.rect.adjusted(xp1: -taboverlap, yp1: 0, xp2: taboverlap, yp2: 0));
1889 }
1890 }
1891
1892 // Only draw the tear indicator if necessary. Most of the time we don't need too.
1893 if (d->leftB->isVisible() && cutLeft >= 0) {
1894 cutTabLeft.rect = rect();
1895 cutTabLeft.rect = style()->subElementRect(subElement: QStyle::SE_TabBarTearIndicatorLeft, option: &cutTabLeft, widget: this);
1896 p.drawPrimitive(pe: QStyle::PE_IndicatorTabTearLeft, opt: cutTabLeft);
1897 }
1898
1899 if (d->rightB->isVisible() && cutRight >= 0) {
1900 cutTabRight.rect = rect();
1901 cutTabRight.rect = style()->subElementRect(subElement: QStyle::SE_TabBarTearIndicatorRight, option: &cutTabRight, widget: this);
1902 p.drawPrimitive(pe: QStyle::PE_IndicatorTabTearRight, opt: cutTabRight);
1903 }
1904}
1905
1906/*
1907 When index changes visibility, we have to find first & last visible indexes.
1908 If remove is set, we force both
1909 */
1910void QTabBarPrivate::calculateFirstLastVisible(int index, bool visible, bool remove)
1911{
1912 if (visible) {
1913 firstVisible = qMin(a: index, b: firstVisible);
1914 lastVisible = qMax(a: index, b: lastVisible);
1915 } else {
1916 if (remove || (index == firstVisible)) {
1917 firstVisible = -1;
1918 for (int i = 0; i < tabList.count(); ++i) {
1919 if (tabList.at(i).visible) {
1920 firstVisible = i;
1921 break;
1922 }
1923 }
1924 if (firstVisible < 0)
1925 firstVisible = 0;
1926 }
1927 if (remove || (index == lastVisible)) {
1928 lastVisible = -1;
1929 for (int i = tabList.count() - 1; i >= 0; --i) {
1930 if (tabList.at(i).visible) {
1931 lastVisible = i;
1932 break;
1933 }
1934 }
1935 }
1936 }
1937}
1938
1939/*
1940 Selects the new current index starting at "fromIndex". If "fromIndex" is visible we're done.
1941 Else it tries any index AFTER fromIndex, then any BEFORE fromIndex and, if everything fails,
1942 it returns -1 indicating that no index is available
1943 */
1944int QTabBarPrivate::selectNewCurrentIndexFrom(int fromIndex)
1945{
1946 int newindex = -1;
1947 for (int i = fromIndex; i < tabList.count(); ++i) {
1948 if (at(index: i)->visible && at(index: i)->enabled) {
1949 newindex = i;
1950 break;
1951 }
1952 }
1953 if (newindex < 0) {
1954 for (int i = fromIndex-1; i > -1; --i) {
1955 if (at(index: i)->visible && at(index: i)->enabled) {
1956 newindex = i;
1957 break;
1958 }
1959 }
1960 }
1961
1962 return newindex;
1963}
1964
1965/*
1966 Given that index at position from moved to position to where return where index goes.
1967 */
1968int QTabBarPrivate::calculateNewPosition(int from, int to, int index) const
1969{
1970 if (index == from)
1971 return to;
1972
1973 int start = qMin(a: from, b: to);
1974 int end = qMax(a: from, b: to);
1975 if (index >= start && index <= end)
1976 index += (from < to) ? -1 : 1;
1977 return index;
1978}
1979
1980/*!
1981 Moves the item at index position \a from to index position \a to.
1982 \since 4.5
1983
1984 \sa tabMoved(), tabLayoutChange()
1985 */
1986void QTabBar::moveTab(int from, int to)
1987{
1988 Q_D(QTabBar);
1989 if (from == to
1990 || !d->validIndex(index: from)
1991 || !d->validIndex(index: to))
1992 return;
1993
1994 bool vertical = verticalTabs(shape: d->shape);
1995 int oldPressedPosition = 0;
1996 if (d->pressedIndex != -1) {
1997 // Record the position of the pressed tab before reordering the tabs.
1998 oldPressedPosition = vertical ? d->tabList[d->pressedIndex].rect.y()
1999 : d->tabList[d->pressedIndex].rect.x();
2000 }
2001
2002 // Update the locations of the tabs first
2003 int start = qMin(a: from, b: to);
2004 int end = qMax(a: from, b: to);
2005 int width = vertical ? d->tabList[from].rect.height() : d->tabList[from].rect.width();
2006 if (from < to)
2007 width *= -1;
2008 bool rtl = isRightToLeft();
2009 for (int i = start; i <= end; ++i) {
2010 if (i == from)
2011 continue;
2012 if (vertical)
2013 d->tabList[i].rect.moveTop(pos: d->tabList[i].rect.y() + width);
2014 else
2015 d->tabList[i].rect.moveLeft(pos: d->tabList[i].rect.x() + width);
2016 int direction = -1;
2017 if (rtl && !vertical)
2018 direction *= -1;
2019 if (d->tabList[i].dragOffset != 0)
2020 d->tabList[i].dragOffset += (direction * width);
2021 }
2022
2023 if (vertical) {
2024 if (from < to)
2025 d->tabList[from].rect.moveTop(pos: d->tabList[to].rect.bottom() + 1);
2026 else
2027 d->tabList[from].rect.moveTop(pos: d->tabList[to].rect.top() - width);
2028 } else {
2029 if (from < to)
2030 d->tabList[from].rect.moveLeft(pos: d->tabList[to].rect.right() + 1);
2031 else
2032 d->tabList[from].rect.moveLeft(pos: d->tabList[to].rect.left() - width);
2033 }
2034
2035 // Move the actual data structures
2036 d->tabList.move(from, to);
2037
2038 // update lastTab locations
2039 for (int i = 0; i < d->tabList.count(); ++i)
2040 d->tabList[i].lastTab = d->calculateNewPosition(from, to, index: d->tabList[i].lastTab);
2041
2042 // update external variables
2043 int previousIndex = d->currentIndex;
2044 d->currentIndex = d->calculateNewPosition(from, to, index: d->currentIndex);
2045
2046 // If we are in the middle of a drag update the dragStartPosition
2047 if (d->pressedIndex != -1) {
2048 d->pressedIndex = d->calculateNewPosition(from, to, index: d->pressedIndex);
2049 int newPressedPosition = vertical ? d->tabList[d->pressedIndex].rect.top() : d->tabList[d->pressedIndex].rect.left();
2050 int diff = oldPressedPosition - newPressedPosition;
2051 if (isRightToLeft() && !vertical)
2052 diff *= -1;
2053 if (vertical)
2054 d->dragStartPosition.setY(d->dragStartPosition.y() - diff);
2055 else
2056 d->dragStartPosition.setX(d->dragStartPosition.x() - diff);
2057 }
2058
2059 d->layoutWidgets(start);
2060 update();
2061 emit tabMoved(from, to);
2062 if (previousIndex != d->currentIndex)
2063 emit currentChanged(index: d->currentIndex);
2064 emit tabLayoutChange();
2065}
2066
2067void QTabBarPrivate::slide(int from, int to)
2068{
2069 Q_Q(QTabBar);
2070 if (from == to
2071 || !validIndex(index: from)
2072 || !validIndex(index: to))
2073 return;
2074 bool vertical = verticalTabs(shape);
2075 int preLocation = vertical ? q->tabRect(index: from).y() : q->tabRect(index: from).x();
2076 q->setUpdatesEnabled(false);
2077 q->moveTab(from, to);
2078 q->setUpdatesEnabled(true);
2079 int postLocation = vertical ? q->tabRect(index: to).y() : q->tabRect(index: to).x();
2080 int length = postLocation - preLocation;
2081 tabList[to].dragOffset -= length;
2082 tabList[to].startAnimation(priv: this, ANIMATION_DURATION);
2083}
2084
2085void QTabBarPrivate::moveTab(int index, int offset)
2086{
2087 if (!validIndex(index))
2088 return;
2089 tabList[index].dragOffset = offset;
2090 layoutTab(index); // Make buttons follow tab
2091 q_func()->update();
2092}
2093
2094/*!\reimp
2095*/
2096void QTabBar::mousePressEvent(QMouseEvent *event)
2097{
2098 Q_D(QTabBar);
2099
2100 const QPoint pos = event->pos();
2101 const bool isEventInCornerButtons = (!d->leftB->isHidden() && d->leftB->geometry().contains(p: pos))
2102 || (!d->rightB->isHidden() && d->rightB->geometry().contains(p: pos));
2103 if (!isEventInCornerButtons) {
2104 const int index = d->indexAtPos(p: pos);
2105 emit tabBarClicked(index);
2106 }
2107
2108 if (event->button() != Qt::LeftButton) {
2109 event->ignore();
2110 return;
2111 }
2112 // Be safe!
2113 if (d->pressedIndex != -1 && d->movable)
2114 d->moveTabFinished(index: d->pressedIndex);
2115
2116 d->pressedIndex = d->indexAtPos(p: event->pos());
2117
2118 if (d->validIndex(index: d->pressedIndex)) {
2119 QStyleOptionTabBarBase optTabBase;
2120 optTabBase.init(w: this);
2121 optTabBase.documentMode = d->documentMode;
2122 if (event->type() == style()->styleHint(stylehint: QStyle::SH_TabBar_SelectMouseType, opt: &optTabBase, widget: this))
2123 setCurrentIndex(d->pressedIndex);
2124 else
2125 repaint(tabRect(index: d->pressedIndex));
2126 if (d->movable) {
2127 d->dragStartPosition = event->pos();
2128 }
2129 }
2130}
2131
2132/*!\reimp
2133 */
2134void QTabBar::mouseMoveEvent(QMouseEvent *event)
2135{
2136 Q_D(QTabBar);
2137 if (d->movable) {
2138 // Be safe!
2139 if (d->pressedIndex != -1
2140 && event->buttons() == Qt::NoButton)
2141 d->moveTabFinished(index: d->pressedIndex);
2142
2143 // Start drag
2144 if (!d->dragInProgress && d->pressedIndex != -1) {
2145 if ((event->pos() - d->dragStartPosition).manhattanLength() > QApplication::startDragDistance()) {
2146 d->dragInProgress = true;
2147 d->setupMovableTab();
2148 }
2149 }
2150
2151 if (event->buttons() == Qt::LeftButton
2152 && d->dragInProgress
2153 && d->validIndex(index: d->pressedIndex)) {
2154 bool vertical = verticalTabs(shape: d->shape);
2155 int dragDistance;
2156 if (vertical) {
2157 dragDistance = (event->pos().y() - d->dragStartPosition.y());
2158 } else {
2159 dragDistance = (event->pos().x() - d->dragStartPosition.x());
2160 }
2161 d->tabList[d->pressedIndex].dragOffset = dragDistance;
2162
2163 QRect startingRect = tabRect(index: d->pressedIndex);
2164 if (vertical)
2165 startingRect.moveTop(pos: startingRect.y() + dragDistance);
2166 else
2167 startingRect.moveLeft(pos: startingRect.x() + dragDistance);
2168
2169 int overIndex;
2170 if (dragDistance < 0)
2171 overIndex = tabAt(position: startingRect.topLeft());
2172 else
2173 overIndex = tabAt(position: startingRect.topRight());
2174
2175 if (overIndex != d->pressedIndex && overIndex != -1) {
2176 int offset = 1;
2177 if (isRightToLeft() && !vertical)
2178 offset *= -1;
2179 if (dragDistance < 0) {
2180 dragDistance *= -1;
2181 offset *= -1;
2182 }
2183 for (int i = d->pressedIndex;
2184 offset > 0 ? i < overIndex : i > overIndex;
2185 i += offset) {
2186 QRect overIndexRect = tabRect(index: overIndex);
2187 int needsToBeOver = (vertical ? overIndexRect.height() : overIndexRect.width()) / 2;
2188 if (dragDistance > needsToBeOver)
2189 d->slide(from: i + offset, to: d->pressedIndex);
2190 }
2191 }
2192 // Buttons needs to follow the dragged tab
2193 d->layoutTab(index: d->pressedIndex);
2194
2195 update();
2196 }
2197 }
2198
2199 if (event->buttons() != Qt::LeftButton) {
2200 event->ignore();
2201 return;
2202 }
2203}
2204
2205void QTabBarPrivate::setupMovableTab()
2206{
2207 Q_Q(QTabBar);
2208 if (!movingTab)
2209 movingTab = new QMovableTabWidget(q);
2210
2211 int taboverlap = q->style()->pixelMetric(metric: QStyle::PM_TabBarTabOverlap, option: nullptr ,widget: q);
2212 QRect grabRect = q->tabRect(index: pressedIndex);
2213 if (verticalTabs(shape))
2214 grabRect.adjust(dx1: 0, dy1: -taboverlap, dx2: 0, dy2: taboverlap);
2215 else
2216 grabRect.adjust(dx1: -taboverlap, dy1: 0, dx2: taboverlap, dy2: 0);
2217
2218 QPixmap grabImage(grabRect.size() * q->devicePixelRatioF());
2219 grabImage.setDevicePixelRatio(q->devicePixelRatioF());
2220 grabImage.fill(fillColor: Qt::transparent);
2221 QStylePainter p(&grabImage, q);
2222
2223 QStyleOptionTabV4 tab;
2224 q->initStyleOption(option: &tab, tabIndex: pressedIndex);
2225 tab.position = QStyleOptionTab::OnlyOneTab;
2226 if (verticalTabs(shape))
2227 tab.rect.moveTopLeft(p: QPoint(0, taboverlap));
2228 else
2229 tab.rect.moveTopLeft(p: QPoint(taboverlap, 0));
2230 p.drawControl(ce: QStyle::CE_TabBarTab, opt: tab);
2231 p.end();
2232
2233 movingTab->setPixmap(grabImage);
2234 movingTab->setGeometry(grabRect);
2235 movingTab->raise();
2236
2237 // Re-arrange widget order to avoid overlaps
2238 if (tabList[pressedIndex].leftWidget)
2239 tabList[pressedIndex].leftWidget->raise();
2240 if (tabList[pressedIndex].rightWidget)
2241 tabList[pressedIndex].rightWidget->raise();
2242 if (leftB)
2243 leftB->raise();
2244 if (rightB)
2245 rightB->raise();
2246 movingTab->setVisible(true);
2247}
2248
2249void QTabBarPrivate::moveTabFinished(int index)
2250{
2251 Q_Q(QTabBar);
2252 bool cleanup = (pressedIndex == index) || (pressedIndex == -1) || !validIndex(index);
2253 bool allAnimationsFinished = true;
2254#if QT_CONFIG(animation)
2255 for(int i = 0; allAnimationsFinished && i < tabList.count(); ++i) {
2256 const Tab &t = tabList.at(i);
2257 if (t.animation && t.animation->state() == QAbstractAnimation::Running)
2258 allAnimationsFinished = false;
2259 }
2260#endif // animation
2261 if (allAnimationsFinished && cleanup) {
2262 if(movingTab)
2263 movingTab->setVisible(false); // We might not get a mouse release
2264 for (int i = 0; i < tabList.count(); ++i) {
2265 tabList[i].dragOffset = 0;
2266 }
2267 if (pressedIndex != -1 && movable) {
2268 pressedIndex = -1;
2269 dragInProgress = false;
2270 dragStartPosition = QPoint();
2271 }
2272 layoutWidgets();
2273 } else {
2274 if (!validIndex(index))
2275 return;
2276 tabList[index].dragOffset = 0;
2277 }
2278 q->update();
2279}
2280
2281/*!\reimp
2282*/
2283void QTabBar::mouseReleaseEvent(QMouseEvent *event)
2284{
2285 Q_D(QTabBar);
2286 if (event->button() != Qt::LeftButton) {
2287 event->ignore();
2288 return;
2289 }
2290
2291 if (d->movable && d->dragInProgress && d->validIndex(index: d->pressedIndex)) {
2292 int length = d->tabList[d->pressedIndex].dragOffset;
2293 int width = verticalTabs(shape: d->shape)
2294 ? tabRect(index: d->pressedIndex).height()
2295 : tabRect(index: d->pressedIndex).width();
2296 int duration = qMin(ANIMATION_DURATION,
2297 b: (qAbs(t: length) * ANIMATION_DURATION) / width);
2298 d->tabList[d->pressedIndex].startAnimation(priv: d, duration);
2299 d->dragInProgress = false;
2300 d->movingTab->setVisible(false);
2301 d->dragStartPosition = QPoint();
2302 }
2303
2304 // mouse release event might happen outside the tab, so keep the pressed index
2305 int oldPressedIndex = d->pressedIndex;
2306 int i = d->indexAtPos(p: event->pos()) == d->pressedIndex ? d->pressedIndex : -1;
2307 d->pressedIndex = -1;
2308 QStyleOptionTabBarBase optTabBase;
2309 optTabBase.initFrom(w: this);
2310 optTabBase.documentMode = d->documentMode;
2311 const bool selectOnRelease =
2312 (style()->styleHint(stylehint: QStyle::SH_TabBar_SelectMouseType, opt: &optTabBase, widget: this) == QEvent::MouseButtonRelease);
2313 if (selectOnRelease)
2314 setCurrentIndex(i);
2315 if (d->validIndex(index: oldPressedIndex))
2316 update(tabRect(index: oldPressedIndex));
2317}
2318
2319/*!\reimp
2320 */
2321void QTabBar::keyPressEvent(QKeyEvent *event)
2322{
2323 Q_D(QTabBar);
2324 if (event->key() != Qt::Key_Left && event->key() != Qt::Key_Right) {
2325 event->ignore();
2326 return;
2327 }
2328 int offset = event->key() == (isRightToLeft() ? Qt::Key_Right : Qt::Key_Left) ? -1 : 1;
2329 d->setCurrentNextEnabledIndex(offset);
2330}
2331
2332/*!\reimp
2333 */
2334#if QT_CONFIG(wheelevent)
2335void QTabBar::wheelEvent(QWheelEvent *event)
2336{
2337#ifndef Q_OS_MAC
2338 Q_D(QTabBar);
2339 int delta = (qAbs(t: event->angleDelta().x()) > qAbs(t: event->angleDelta().y()) ?
2340 event->angleDelta().x() : event->angleDelta().y());
2341 int offset = delta > 0 ? -1 : 1;
2342 d->setCurrentNextEnabledIndex(offset);
2343 QWidget::wheelEvent(event);
2344#else
2345 Q_UNUSED(event)
2346#endif
2347}
2348#endif // QT_CONFIG(wheelevent)
2349
2350void QTabBarPrivate::setCurrentNextEnabledIndex(int offset)
2351{
2352 Q_Q(QTabBar);
2353 for (int index = currentIndex + offset; validIndex(index); index += offset) {
2354 if (tabList.at(i: index).enabled) {
2355 q->setCurrentIndex(index);
2356 break;
2357 }
2358 }
2359}
2360
2361/*!\reimp
2362 */
2363void QTabBar::changeEvent(QEvent *event)
2364{
2365 Q_D(QTabBar);
2366 switch (event->type()) {
2367 case QEvent::StyleChange:
2368 if (!d->elideModeSetByUser)
2369 d->elideMode = Qt::TextElideMode(style()->styleHint(stylehint: QStyle::SH_TabBar_ElideMode, opt: nullptr, widget: this));
2370 if (!d->useScrollButtonsSetByUser)
2371 d->useScrollButtons = !style()->styleHint(stylehint: QStyle::SH_TabBar_PreferNoArrows, opt: nullptr, widget: this);
2372 Q_FALLTHROUGH();
2373 case QEvent::FontChange:
2374 d->textSizes.clear();
2375 d->refresh();
2376 break;
2377 default:
2378 break;
2379 }
2380
2381 QWidget::changeEvent(event);
2382}
2383
2384/*!
2385 \reimp
2386*/
2387void QTabBar::timerEvent(QTimerEvent *event)
2388{
2389 Q_D(QTabBar);
2390 if (event->timerId() == d->switchTabTimerId) {
2391 killTimer(id: d->switchTabTimerId);
2392 d->switchTabTimerId = 0;
2393 setCurrentIndex(d->switchTabCurrentIndex);
2394 d->switchTabCurrentIndex = -1;
2395 }
2396 QWidget::timerEvent(event);
2397}
2398
2399/*!
2400 \property QTabBar::elideMode
2401 \brief how to elide text in the tab bar
2402 \since 4.2
2403
2404 This property controls how items are elided when there is not
2405 enough space to show them for a given tab bar size.
2406
2407 By default the value is style dependent.
2408
2409 \sa QTabWidget::elideMode, usesScrollButtons, QStyle::SH_TabBar_ElideMode
2410*/
2411
2412Qt::TextElideMode QTabBar::elideMode() const
2413{
2414 Q_D(const QTabBar);
2415 return d->elideMode;
2416}
2417
2418void QTabBar::setElideMode(Qt::TextElideMode mode)
2419{
2420 Q_D(QTabBar);
2421 d->elideMode = mode;
2422 d->elideModeSetByUser = true;
2423 d->textSizes.clear();
2424 d->refresh();
2425}
2426
2427/*!
2428 \property QTabBar::usesScrollButtons
2429 \brief Whether or not a tab bar should use buttons to scroll tabs when it
2430 has many tabs.
2431 \since 4.2
2432
2433 When there are too many tabs in a tab bar for its size, the tab bar can either choose
2434 to expand its size or to add buttons that allow you to scroll through the tabs.
2435
2436 By default the value is style dependant.
2437
2438 \sa elideMode, QTabWidget::usesScrollButtons, QStyle::SH_TabBar_PreferNoArrows
2439*/
2440bool QTabBar::usesScrollButtons() const
2441{
2442 return d_func()->useScrollButtons;
2443}
2444
2445void QTabBar::setUsesScrollButtons(bool useButtons)
2446{
2447 Q_D(QTabBar);
2448 d->useScrollButtonsSetByUser = true;
2449 if (d->useScrollButtons == useButtons)
2450 return;
2451 d->useScrollButtons = useButtons;
2452 d->refresh();
2453}
2454
2455/*!
2456 \property QTabBar::tabsClosable
2457 \brief Whether or not a tab bar should place close buttons on each tab
2458 \since 4.5
2459
2460 When tabsClosable is set to true a close button will appear on the tab on
2461 either the left or right hand side depending upon the style. When the button
2462 is clicked the tab the signal tabCloseRequested will be emitted.
2463
2464 By default the value is false.
2465
2466 \sa setTabButton(), tabRemoved()
2467*/
2468
2469bool QTabBar::tabsClosable() const
2470{
2471 Q_D(const QTabBar);
2472 return d->closeButtonOnTabs;
2473}
2474
2475void QTabBar::setTabsClosable(bool closable)
2476{
2477 Q_D(QTabBar);
2478 if (d->closeButtonOnTabs == closable)
2479 return;
2480 d->closeButtonOnTabs = closable;
2481 ButtonPosition closeSide = (ButtonPosition)style()->styleHint(stylehint: QStyle::SH_TabBar_CloseButtonPosition, opt: nullptr, widget: this);
2482 if (!closable) {
2483 for (int i = 0; i < d->tabList.count(); ++i) {
2484 if (closeSide == LeftSide && d->tabList[i].leftWidget) {
2485 d->tabList[i].leftWidget->deleteLater();
2486 d->tabList[i].leftWidget = nullptr;
2487 }
2488 if (closeSide == RightSide && d->tabList[i].rightWidget) {
2489 d->tabList[i].rightWidget->deleteLater();
2490 d->tabList[i].rightWidget = nullptr;
2491 }
2492 }
2493 } else {
2494 bool newButtons = false;
2495 for (int i = 0; i < d->tabList.count(); ++i) {
2496 if (tabButton(index: i, position: closeSide))
2497 continue;
2498 newButtons = true;
2499 QAbstractButton *closeButton = new CloseButton(this);
2500 connect(sender: closeButton, SIGNAL(clicked()), receiver: this, SLOT(_q_closeTab()));
2501 setTabButton(index: i, position: closeSide, widget: closeButton);
2502 }
2503 if (newButtons)
2504 d->layoutTabs();
2505 }
2506 update();
2507}
2508
2509/*!
2510 \enum QTabBar::ButtonPosition
2511 \since 4.5
2512
2513 This enum type lists the location of the widget on a tab.
2514
2515 \value LeftSide Left side of the tab.
2516
2517 \value RightSide Right side of the tab.
2518
2519*/
2520
2521/*!
2522 \enum QTabBar::SelectionBehavior
2523 \since 4.5
2524
2525 This enum type lists the behavior of QTabBar when a tab is removed
2526 and the tab being removed is also the current tab.
2527
2528 \value SelectLeftTab Select the tab to the left of the one being removed.
2529
2530 \value SelectRightTab Select the tab to the right of the one being removed.
2531
2532 \value SelectPreviousTab Select the previously selected tab.
2533
2534*/
2535
2536/*!
2537 \property QTabBar::selectionBehaviorOnRemove
2538 \brief What tab should be set as current when removeTab is called if
2539 the removed tab is also the current tab.
2540 \since 4.5
2541
2542 By default the value is SelectRightTab.
2543
2544 \sa removeTab()
2545*/
2546
2547
2548QTabBar::SelectionBehavior QTabBar::selectionBehaviorOnRemove() const
2549{
2550 Q_D(const QTabBar);
2551 return d->selectionBehaviorOnRemove;
2552}
2553
2554void QTabBar::setSelectionBehaviorOnRemove(QTabBar::SelectionBehavior behavior)
2555{
2556 Q_D(QTabBar);
2557 d->selectionBehaviorOnRemove = behavior;
2558}
2559
2560/*!
2561 \property QTabBar::expanding
2562 \brief When expanding is true QTabBar will expand the tabs to use the empty space.
2563 \since 4.5
2564
2565 By default the value is true.
2566
2567 \sa QTabWidget::documentMode
2568*/
2569
2570bool QTabBar::expanding() const
2571{
2572 Q_D(const QTabBar);
2573 return d->expanding;
2574}
2575
2576void QTabBar::setExpanding(bool enabled)
2577{
2578 Q_D(QTabBar);
2579 if (d->expanding == enabled)
2580 return;
2581 d->expanding = enabled;
2582 d->layoutTabs();
2583}
2584
2585/*!
2586 \property QTabBar::movable
2587 \brief This property holds whether the user can move the tabs
2588 within the tabbar area.
2589
2590 \since 4.5
2591
2592 By default, this property is \c false;
2593*/
2594
2595bool QTabBar::isMovable() const
2596{
2597 Q_D(const QTabBar);
2598 return d->movable;
2599}
2600
2601void QTabBar::setMovable(bool movable)
2602{
2603 Q_D(QTabBar);
2604 d->movable = movable;
2605}
2606
2607
2608/*!
2609 \property QTabBar::documentMode
2610 \brief Whether or not the tab bar is rendered in a mode suitable for the main window.
2611 \since 4.5
2612
2613 This property is used as a hint for styles to draw the tabs in a different
2614 way then they would normally look in a tab widget. On \macos this will
2615 look similar to the tabs in Safari or Sierra's Terminal.app.
2616
2617 \sa QTabWidget::documentMode
2618*/
2619bool QTabBar::documentMode() const
2620{
2621 return d_func()->documentMode;
2622}
2623
2624void QTabBar::setDocumentMode(bool enabled)
2625{
2626 Q_D(QTabBar);
2627
2628 d->documentMode = enabled;
2629 d->updateMacBorderMetrics();
2630}
2631
2632/*!
2633 \property QTabBar::autoHide
2634 \brief If true, the tab bar is automatically hidden when it contains less
2635 than 2 tabs.
2636 \since 5.4
2637
2638 By default, this property is false.
2639
2640 \sa QWidget::visible
2641*/
2642
2643bool QTabBar::autoHide() const
2644{
2645 Q_D(const QTabBar);
2646 return d->autoHide;
2647}
2648
2649void QTabBar::setAutoHide(bool hide)
2650{
2651 Q_D(QTabBar);
2652 if (d->autoHide == hide)
2653 return;
2654
2655 d->autoHide = hide;
2656 if (hide)
2657 d->autoHideTabs();
2658 else
2659 setVisible(true);
2660}
2661
2662/*!
2663 \property QTabBar::changeCurrentOnDrag
2664 \brief If true, then the current tab is automatically changed when dragging
2665 over the tabbar.
2666 \since 5.4
2667
2668 \note You should also set acceptDrops property to true to make this feature
2669 work.
2670
2671 By default, this property is false.
2672*/
2673
2674bool QTabBar::changeCurrentOnDrag() const
2675{
2676 Q_D(const QTabBar);
2677 return d->changeCurrentOnDrag;
2678}
2679
2680void QTabBar::setChangeCurrentOnDrag(bool change)
2681{
2682 Q_D(QTabBar);
2683 d->changeCurrentOnDrag = change;
2684 if (!change)
2685 d->killSwitchTabTimer();
2686}
2687
2688/*!
2689 Sets \a widget on the tab \a index. The widget is placed
2690 on the left or right hand side depending upon the \a position.
2691 \since 4.5
2692
2693 Any previously set widget in \a position is hidden.
2694
2695 The tab bar will take ownership of the widget and so all widgets set here
2696 will be deleted by the tab bar when it is destroyed unless you separately
2697 reparent the widget after setting some other widget (or \nullptr).
2698
2699 \sa tabsClosable()
2700 */
2701void QTabBar::setTabButton(int index, ButtonPosition position, QWidget *widget)
2702{
2703 Q_D(QTabBar);
2704 if (index < 0 || index >= d->tabList.count())
2705 return;
2706 if (widget) {
2707 widget->setParent(this);
2708 // make sure our left and right widgets stay on top
2709 widget->lower();
2710 widget->show();
2711 }
2712 if (position == LeftSide) {
2713 if (d->tabList[index].leftWidget)
2714 d->tabList[index].leftWidget->hide();
2715 d->tabList[index].leftWidget = widget;
2716 } else {
2717 if (d->tabList[index].rightWidget)
2718 d->tabList[index].rightWidget->hide();
2719 d->tabList[index].rightWidget = widget;
2720 }
2721 d->layoutTabs();
2722 d->refresh();
2723 update();
2724}
2725
2726/*!
2727 Returns the widget set a tab \a index and \a position or \nullptr
2728 if one is not set.
2729 */
2730QWidget *QTabBar::tabButton(int index, ButtonPosition position) const
2731{
2732 Q_D(const QTabBar);
2733 if (index < 0 || index >= d->tabList.count())
2734 return nullptr;
2735 if (position == LeftSide)
2736 return d->tabList.at(i: index).leftWidget;
2737 else
2738 return d->tabList.at(i: index).rightWidget;
2739}
2740
2741#ifndef QT_NO_ACCESSIBILITY
2742/*!
2743 Sets the accessibleName of the tab at position \a index to \a name.
2744*/
2745void QTabBar::setAccessibleTabName(int index, const QString &name)
2746{
2747 Q_D(QTabBar);
2748 if (QTabBarPrivate::Tab *tab = d->at(index)) {
2749 tab->accessibleName = name;
2750 QAccessibleEvent event(this, QAccessible::NameChanged);
2751 event.setChild(index);
2752 QAccessible::updateAccessibility(event: &event);
2753 }
2754}
2755
2756/*!
2757 Returns the accessibleName of the tab at position \a index, or an empty
2758 string if \a index is out of range.
2759*/
2760QString QTabBar::accessibleTabName(int index) const
2761{
2762 Q_D(const QTabBar);
2763 if (const QTabBarPrivate::Tab *tab = d->at(index))
2764 return tab->accessibleName;
2765 return QString();
2766}
2767#endif // QT_NO_ACCESSIBILITY
2768
2769CloseButton::CloseButton(QWidget *parent)
2770 : QAbstractButton(parent)
2771{
2772 setFocusPolicy(Qt::NoFocus);
2773#ifndef QT_NO_CURSOR
2774 setCursor(Qt::ArrowCursor);
2775#endif
2776#ifndef QT_NO_TOOLTIP
2777 setToolTip(tr(s: "Close Tab"));
2778#endif
2779 resize(sizeHint());
2780}
2781
2782QSize CloseButton::sizeHint() const
2783{
2784 ensurePolished();
2785 int width = style()->pixelMetric(metric: QStyle::PM_TabCloseIndicatorWidth, option: nullptr, widget: this);
2786 int height = style()->pixelMetric(metric: QStyle::PM_TabCloseIndicatorHeight, option: nullptr, widget: this);
2787 return QSize(width, height);
2788}
2789
2790void CloseButton::enterEvent(QEvent *event)
2791{
2792 if (isEnabled())
2793 update();
2794 QAbstractButton::enterEvent(event);
2795}
2796
2797void CloseButton::leaveEvent(QEvent *event)
2798{
2799 if (isEnabled())
2800 update();
2801 QAbstractButton::leaveEvent(event);
2802}
2803
2804void CloseButton::paintEvent(QPaintEvent *)
2805{
2806 QPainter p(this);
2807 QStyleOption opt;
2808 opt.init(w: this);
2809 opt.state |= QStyle::State_AutoRaise;
2810 if (isEnabled() && underMouse() && !isChecked() && !isDown())
2811 opt.state |= QStyle::State_Raised;
2812 if (isChecked())
2813 opt.state |= QStyle::State_On;
2814 if (isDown())
2815 opt.state |= QStyle::State_Sunken;
2816
2817 if (const QTabBar *tb = qobject_cast<const QTabBar *>(object: parent())) {
2818 int index = tb->currentIndex();
2819 QTabBar::ButtonPosition position = (QTabBar::ButtonPosition)style()->styleHint(stylehint: QStyle::SH_TabBar_CloseButtonPosition, opt: nullptr, widget: tb);
2820 if (tb->tabButton(index, position) == this)
2821 opt.state |= QStyle::State_Selected;
2822 }
2823
2824 style()->drawPrimitive(pe: QStyle::PE_IndicatorTabClose, opt: &opt, p: &p, w: this);
2825}
2826
2827#if QT_CONFIG(animation)
2828void QTabBarPrivate::Tab::TabBarAnimation::updateCurrentValue(const QVariant &current)
2829{
2830 priv->moveTab(index: priv->tabList.indexOf(t: *tab), offset: current.toInt());
2831}
2832
2833void QTabBarPrivate::Tab::TabBarAnimation::updateState(QAbstractAnimation::State newState, QAbstractAnimation::State)
2834{
2835 if (newState == Stopped) priv->moveTabFinished(index: priv->tabList.indexOf(t: *tab));
2836}
2837#endif
2838
2839QT_END_NAMESPACE
2840
2841#include "moc_qtabbar.cpp"
2842#include "qtabbar.moc"
2843

source code of qtbase/src/widgets/widgets/qtabbar.cpp