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 }