File indexing completed on 2024-05-12 04:58:24

0001 /* ============================================================
0002 * Falkon - Qt web browser
0003 * Copyright (C) 2013-2014  S. Razi Alavizadeh <s.r.alavizadeh@gmail.com>
0004 * Copyright (C) 2018       David Rosca <nowrep@gmail.com>
0005 *
0006 * Some code was taken from qtabwidget.cpp
0007 *
0008 * This program is free software: you can redistribute it and/or modify
0009 * it under the terms of the GNU General Public License as published by
0010 * the Free Software Foundation, either version 3 of the License, or
0011 * (at your option) any later version.
0012 *
0013 * This program is distributed in the hope that it will be useful,
0014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
0015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0016 * GNU General Public License for more details.
0017 *
0018 * You should have received a copy of the GNU General Public License
0019 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0020 * ============================================================ */
0021 #include "tabstackedwidget.h"
0022 #include "combotabbar.h"
0023 
0024 #include <QVBoxLayout>
0025 #include <QStackedWidget>
0026 #include <QKeyEvent>
0027 #include <QApplication>
0028 #include <QTimer>
0029 
0030 // Note: just some of QTabWidget's methods were implemented
0031 
0032 TabStackedWidget::TabStackedWidget(QWidget* parent)
0033     : QWidget(parent)
0034     , m_stack(nullptr)
0035     , m_tabBar(nullptr)
0036     , m_currentIndex(-1)
0037     , m_previousIndex(-1)
0038 {
0039     m_stack = new QStackedWidget(this);
0040     m_mainLayout = new QVBoxLayout;
0041     m_mainLayout->setSpacing(0);
0042     m_mainLayout->setContentsMargins(0, 0, 0, 0);
0043 
0044     m_mainLayout->addWidget(m_stack);
0045     setLayout(m_mainLayout);
0046 
0047     connect(m_stack, &QStackedWidget::widgetRemoved, this, &TabStackedWidget::tabWasRemoved);
0048 }
0049 
0050 TabStackedWidget::~TabStackedWidget()
0051 = default;
0052 
0053 ComboTabBar* TabStackedWidget::tabBar()
0054 {
0055     return m_tabBar;
0056 }
0057 
0058 void TabStackedWidget::setTabBar(ComboTabBar* tb)
0059 {
0060     Q_ASSERT(tb);
0061 
0062     if (tb->parentWidget() != this) {
0063         tb->setParent(this);
0064         tb->show();
0065     }
0066 
0067     delete m_tabBar;
0068     m_dirtyTabBar = true;
0069     m_tabBar = tb;
0070     setFocusProxy(m_tabBar);
0071 
0072     connect(m_tabBar, &ComboTabBar::currentChanged, this, &TabStackedWidget::showTab);
0073     connect(m_tabBar, &ComboTabBar::tabMoved, this, &TabStackedWidget::tabWasMoved);
0074     connect(m_tabBar, &ComboTabBar::overFlowChanged, this, &TabStackedWidget::setUpLayout);
0075 
0076     if (m_tabBar->tabsClosable()) {
0077         connect(m_tabBar, &ComboTabBar::tabCloseRequested, this, &TabStackedWidget::tabCloseRequested);
0078     }
0079 
0080     setDocumentMode(m_tabBar->documentMode());
0081 
0082     m_tabBar->installEventFilter(this);
0083     setUpLayout();
0084 }
0085 
0086 void TabStackedWidget::tabWasMoved(int from, int to)
0087 {
0088     m_stack->blockSignals(true);
0089     QWidget* w = m_stack->widget(from);
0090     m_stack->removeWidget(w);
0091     m_stack->insertWidget(to, w);
0092     m_stack->blockSignals(false);
0093 }
0094 
0095 void TabStackedWidget::tabWasRemoved(int index)
0096 {
0097     if (m_previousIndex == index)
0098         m_previousIndex = -1;
0099     else if (m_previousIndex > index)
0100         --m_previousIndex;
0101 
0102     if (m_currentIndex == index)
0103         m_currentIndex = -1;
0104     else if (m_currentIndex > index)
0105         --m_currentIndex;
0106 
0107     m_tabBar->removeTab(index);
0108 }
0109 
0110 void TabStackedWidget::setUpLayout()
0111 {
0112     if (!m_tabBar->isVisible()) {
0113         m_dirtyTabBar = true;
0114         return;
0115     }
0116 
0117     m_tabBar->setElideMode(m_tabBar->elideMode());
0118     m_dirtyTabBar = false;
0119 }
0120 
0121 bool TabStackedWidget::eventFilter(QObject* obj, QEvent* event)
0122 {
0123     if (m_dirtyTabBar && obj == m_tabBar && event->type() == QEvent::Show) {
0124         setUpLayout();
0125     }
0126 
0127     return false;
0128 }
0129 
0130 void TabStackedWidget::keyPressEvent(QKeyEvent* event)
0131 {
0132     if (((event->key() == Qt::Key_Tab || event->key() == Qt::Key_Backtab) &&
0133          count() > 1 && event->modifiers() & Qt::ControlModifier)
0134 #ifdef QT_KEYPAD_NAVIGATION
0135         || QApplication::keypadNavigationEnabled() && (event->key() == Qt::Key_Left || event->key() == Qt::Key_Right) && count() > 1
0136 #endif
0137        ) {
0138         int pageCount = count();
0139         int page = currentIndex();
0140         int dx = (event->key() == Qt::Key_Backtab || event->modifiers() & Qt::ShiftModifier) ? -1 : 1;
0141 #ifdef QT_KEYPAD_NAVIGATION
0142         if (QApplication::keypadNavigationEnabled() && (event->key() == Qt::Key_Left || event->key() == Qt::Key_Right)) {
0143             dx = event->key() == (isRightToLeft() ? Qt::Key_Right : Qt::Key_Left) ? -1 : 1;
0144         }
0145 #endif
0146         for (int pass = 0; pass < pageCount; ++pass) {
0147             page += dx;
0148             if (page < 0
0149 #ifdef QT_KEYPAD_NAVIGATION
0150                 && !event->isAutoRepeat()
0151 #endif
0152                ) {
0153                 page = count() - 1;
0154             }
0155             else if (page >= pageCount
0156 #ifdef QT_KEYPAD_NAVIGATION
0157                      && !event->isAutoRepeat()
0158 #endif
0159                     ) {
0160                 page = 0;
0161             }
0162             if (m_tabBar->isTabEnabled(page)) {
0163                 setCurrentIndex(page);
0164                 break;
0165             }
0166         }
0167         if (!QApplication::focusWidget()) {
0168             m_tabBar->setFocus();
0169         }
0170     }
0171     else {
0172         event->ignore();
0173     }
0174 }
0175 
0176 void TabStackedWidget::showTab(int index)
0177 {
0178     if (validIndex(index)) {
0179         m_stack->setCurrentIndex(index);
0180     }
0181 
0182     m_previousIndex = m_currentIndex;
0183     m_currentIndex = index;
0184 
0185     // This is slot connected to ComboTabBar::currentChanged
0186     // We must send the signal even with invalid index (-1)
0187     Q_EMIT currentChanged(index);
0188 }
0189 
0190 bool TabStackedWidget::documentMode() const
0191 {
0192     return m_tabBar->documentMode();
0193 }
0194 
0195 void TabStackedWidget::setDocumentMode(bool enabled)
0196 {
0197     m_tabBar->setDocumentMode(enabled);
0198     m_tabBar->setExpanding(!enabled);
0199     m_tabBar->setDrawBase(enabled);
0200 }
0201 
0202 int TabStackedWidget::addTab(QWidget* widget, const QString &label, bool pinned)
0203 {
0204     return insertTab(-1, widget, label, pinned);
0205 }
0206 
0207 int TabStackedWidget::insertTab(int index, QWidget* w, const QString &label, bool pinned)
0208 {
0209     if (!w) {
0210         return -1;
0211     }
0212 
0213     if (pinned) {
0214         index = index < 0 ? m_tabBar->pinnedTabsCount() : qMin(index, m_tabBar->pinnedTabsCount());
0215         index = m_stack->insertWidget(index, w);
0216         m_tabBar->insertTab(index, QIcon(), label, true);
0217     }
0218     else {
0219         index = index < 0 ? -1 : qMax(index, m_tabBar->pinnedTabsCount());
0220         index = m_stack->insertWidget(index, w);
0221         m_tabBar->insertTab(index, QIcon(), label, false);
0222     }
0223 
0224     if (m_previousIndex >= index)
0225         ++m_previousIndex;
0226     if (m_currentIndex >= index)
0227         ++m_currentIndex;
0228 
0229     QTimer::singleShot(0, this, &TabStackedWidget::setUpLayout);
0230 
0231     return index;
0232 }
0233 
0234 QString TabStackedWidget::tabText(int index) const
0235 {
0236     return m_tabBar->tabText(index);
0237 }
0238 
0239 void TabStackedWidget::setTabText(int index, const QString &label)
0240 {
0241     m_tabBar->setTabText(index, label);
0242 }
0243 
0244 QString TabStackedWidget::tabToolTip(int index) const
0245 {
0246     return m_tabBar->tabToolTip(index);
0247 }
0248 
0249 void TabStackedWidget::setTabToolTip(int index, const QString &tip)
0250 {
0251     m_tabBar->setTabToolTip(index, tip);
0252 }
0253 
0254 int TabStackedWidget::pinUnPinTab(int index, const QString &title)
0255 {
0256     QWidget* widget = m_stack->widget(index);
0257     QWidget* currentWidget = m_stack->currentWidget();
0258 
0259     if (!widget || !currentWidget)
0260         return -1;
0261 
0262     bool makePinned = index >= m_tabBar->pinnedTabsCount();
0263     QWidget* button = m_tabBar->tabButton(index, m_tabBar->iconButtonPosition());
0264     // To show tooltip of tab which is pinned in the current session
0265     QString toolTip =  tabToolTip(index);
0266 
0267     m_tabBar->m_blockCurrentChangedSignal = true;
0268     m_tabBar->setTabButton(index, m_tabBar->iconButtonPosition(), nullptr);
0269 
0270     m_stack->removeWidget(widget);
0271     int newIndex = insertTab(makePinned ? 0 : m_tabBar->pinnedTabsCount(), widget, title, makePinned);
0272 
0273     m_tabBar->setTabButton(newIndex, m_tabBar->iconButtonPosition(), button);
0274     m_tabBar->m_blockCurrentChangedSignal = false;
0275     setTabToolTip(newIndex, toolTip);
0276 
0277     // Restore current widget
0278     setCurrentWidget(currentWidget);
0279 
0280     Q_EMIT pinStateChanged(newIndex, makePinned);
0281 
0282     return newIndex;
0283 }
0284 
0285 void TabStackedWidget::removeTab(int index)
0286 {
0287     if (QWidget* w = m_stack->widget(index)) {
0288         // Select another current tab before remove, so it won't be handled by QTabBar
0289         if (index == currentIndex() && count() > 1)
0290             selectTabOnRemove();
0291         m_stack->removeWidget(w);
0292     }
0293 }
0294 
0295 void TabStackedWidget::moveTab(int from, int to)
0296 {
0297     m_tabBar->moveTab(from, to);
0298 }
0299 
0300 int TabStackedWidget::currentIndex() const
0301 {
0302     return m_tabBar->currentIndex();
0303 }
0304 
0305 void TabStackedWidget::setCurrentIndex(int index)
0306 {
0307     m_tabBar->setCurrentIndex(index);
0308 }
0309 
0310 QWidget* TabStackedWidget::currentWidget() const
0311 {
0312     return m_stack->currentWidget();
0313 }
0314 
0315 void TabStackedWidget::setCurrentWidget(QWidget* widget)
0316 {
0317     m_tabBar->setCurrentIndex(indexOf(widget));
0318 }
0319 
0320 QWidget* TabStackedWidget::widget(int index) const
0321 {
0322     return m_stack->widget(index);
0323 }
0324 
0325 int TabStackedWidget::indexOf(QWidget* widget) const
0326 {
0327     return m_stack->indexOf(widget);
0328 }
0329 
0330 int TabStackedWidget::count() const
0331 {
0332     return m_tabBar->count();
0333 }
0334 
0335 bool TabStackedWidget::validIndex(int index) const
0336 {
0337     return (index < m_stack->count() && index >= 0);
0338 }
0339 
0340 void TabStackedWidget::selectTabOnRemove()
0341 {
0342     Q_ASSERT(count() > 1);
0343 
0344     int index = -1;
0345 
0346     switch (m_tabBar->selectionBehaviorOnRemove()) {
0347     case QTabBar::SelectPreviousTab:
0348         if (validIndex(m_previousIndex)) {
0349             index = m_previousIndex;
0350             break;
0351         }
0352         // fallthrough
0353 
0354     case QTabBar::SelectLeftTab:
0355         index = currentIndex() - 1;
0356         if (!validIndex(index))
0357             index = 1;
0358         break;
0359 
0360     case QTabBar::SelectRightTab:
0361         index = currentIndex() + 1;
0362         if (!validIndex(index))
0363             index = currentIndex() - 1;
0364         break;
0365 
0366     default:
0367         break;
0368     }
0369 
0370     Q_ASSERT(validIndex(index));
0371     setCurrentIndex(index);
0372 }