Falkon Develop
Cross-platform Qt-based web browser
tabstackedwidget.cpp
Go to the documentation of this file.
1/* ============================================================
2* Falkon - Qt web browser
3* Copyright (C) 2013-2014 S. Razi Alavizadeh <s.r.alavizadeh@gmail.com>
4* Copyright (C) 2018 David Rosca <nowrep@gmail.com>
5*
6* Some code was taken from qtabwidget.cpp
7*
8* This program is free software: you can redistribute it and/or modify
9* it under the terms of the GNU General Public License as published by
10* the Free Software Foundation, either version 3 of the License, or
11* (at your option) any later version.
12*
13* This program is distributed in the hope that it will be useful,
14* but WITHOUT ANY WARRANTY; without even the implied warranty of
15* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16* GNU General Public License for more details.
17*
18* You should have received a copy of the GNU General Public License
19* along with this program. If not, see <http://www.gnu.org/licenses/>.
20* ============================================================ */
21#include "tabstackedwidget.h"
22#include "combotabbar.h"
23
24#include <QVBoxLayout>
25#include <QStackedWidget>
26#include <QKeyEvent>
27#include <QApplication>
28#include <QTimer>
29
30// Note: just some of QTabWidget's methods were implemented
31
33 : QWidget(parent)
34 , m_stack(nullptr)
35 , m_tabBar(nullptr)
36 , m_currentIndex(-1)
37 , m_previousIndex(-1)
38{
39 m_stack = new QStackedWidget(this);
40 m_mainLayout = new QVBoxLayout;
41 m_mainLayout->setSpacing(0);
42 m_mainLayout->setContentsMargins(0, 0, 0, 0);
43
44 m_mainLayout->addWidget(m_stack);
45 setLayout(m_mainLayout);
46
47 connect(m_stack, &QStackedWidget::widgetRemoved, this, &TabStackedWidget::tabWasRemoved);
48}
49
51= default;
52
54{
55 return m_tabBar;
56}
57
59{
60 Q_ASSERT(tb);
61
62 if (tb->parentWidget() != this) {
63 tb->setParent(this);
64 tb->show();
65 }
66
67 delete m_tabBar;
68 m_dirtyTabBar = true;
69 m_tabBar = tb;
70 setFocusProxy(m_tabBar);
71
72 connect(m_tabBar, &ComboTabBar::currentChanged, this, &TabStackedWidget::showTab);
73 connect(m_tabBar, &ComboTabBar::tabMoved, this, &TabStackedWidget::tabWasMoved);
75
76 if (m_tabBar->tabsClosable()) {
78 }
79
80 setDocumentMode(m_tabBar->documentMode());
81
82 m_tabBar->installEventFilter(this);
84}
85
86void TabStackedWidget::tabWasMoved(int from, int to)
87{
88 m_stack->blockSignals(true);
89 QWidget* w = m_stack->widget(from);
90 m_stack->removeWidget(w);
91 m_stack->insertWidget(to, w);
92 m_stack->blockSignals(false);
93}
94
95void TabStackedWidget::tabWasRemoved(int index)
96{
97 if (m_previousIndex == index)
98 m_previousIndex = -1;
99 else if (m_previousIndex > index)
100 --m_previousIndex;
101
102 if (m_currentIndex == index)
103 m_currentIndex = -1;
104 else if (m_currentIndex > index)
105 --m_currentIndex;
106
107 m_tabBar->removeTab(index);
108}
109
111{
112 if (!m_tabBar->isVisible()) {
113 m_dirtyTabBar = true;
114 return;
115 }
116
117 m_tabBar->setElideMode(m_tabBar->elideMode());
118 m_dirtyTabBar = false;
119}
120
121bool TabStackedWidget::eventFilter(QObject* obj, QEvent* event)
122{
123 if (m_dirtyTabBar && obj == m_tabBar && event->type() == QEvent::Show) {
124 setUpLayout();
125 }
126
127 return false;
128}
129
131{
132 if (((event->key() == Qt::Key_Tab || event->key() == Qt::Key_Backtab) &&
133 count() > 1 && event->modifiers() & Qt::ControlModifier)
134#ifdef QT_KEYPAD_NAVIGATION
135 || QApplication::keypadNavigationEnabled() && (event->key() == Qt::Key_Left || event->key() == Qt::Key_Right) && count() > 1
136#endif
137 ) {
138 int pageCount = count();
139 int page = currentIndex();
140 int dx = (event->key() == Qt::Key_Backtab || event->modifiers() & Qt::ShiftModifier) ? -1 : 1;
141#ifdef QT_KEYPAD_NAVIGATION
142 if (QApplication::keypadNavigationEnabled() && (event->key() == Qt::Key_Left || event->key() == Qt::Key_Right)) {
143 dx = event->key() == (isRightToLeft() ? Qt::Key_Right : Qt::Key_Left) ? -1 : 1;
144 }
145#endif
146 for (int pass = 0; pass < pageCount; ++pass) {
147 page += dx;
148 if (page < 0
149#ifdef QT_KEYPAD_NAVIGATION
150 && !event->isAutoRepeat()
151#endif
152 ) {
153 page = count() - 1;
154 }
155 else if (page >= pageCount
156#ifdef QT_KEYPAD_NAVIGATION
157 && !event->isAutoRepeat()
158#endif
159 ) {
160 page = 0;
161 }
162 if (m_tabBar->isTabEnabled(page)) {
163 setCurrentIndex(page);
164 break;
165 }
166 }
167 if (!QApplication::focusWidget()) {
168 m_tabBar->setFocus();
169 }
170 }
171 else {
172 event->ignore();
173 }
174}
175
176void TabStackedWidget::showTab(int index)
177{
178 if (validIndex(index)) {
179 m_stack->setCurrentIndex(index);
180 }
181
182 m_previousIndex = m_currentIndex;
183 m_currentIndex = index;
184
185 // This is slot connected to ComboTabBar::currentChanged
186 // We must send the signal even with invalid index (-1)
187 Q_EMIT currentChanged(index);
188}
189
191{
192 return m_tabBar->documentMode();
193}
194
196{
197 m_tabBar->setDocumentMode(enabled);
198 m_tabBar->setExpanding(!enabled);
199 m_tabBar->setDrawBase(enabled);
200}
201
202int TabStackedWidget::addTab(QWidget* widget, const QString &label, bool pinned)
203{
204 return insertTab(-1, widget, label, pinned);
205}
206
207int TabStackedWidget::insertTab(int index, QWidget* w, const QString &label, bool pinned)
208{
209 if (!w) {
210 return -1;
211 }
212
213 if (pinned) {
214 index = index < 0 ? m_tabBar->pinnedTabsCount() : qMin(index, m_tabBar->pinnedTabsCount());
215 index = m_stack->insertWidget(index, w);
216 m_tabBar->insertTab(index, QIcon(), label, true);
217 }
218 else {
219 index = index < 0 ? -1 : qMax(index, m_tabBar->pinnedTabsCount());
220 index = m_stack->insertWidget(index, w);
221 m_tabBar->insertTab(index, QIcon(), label, false);
222 }
223
224 if (m_previousIndex >= index)
225 ++m_previousIndex;
226 if (m_currentIndex >= index)
227 ++m_currentIndex;
228
229 QTimer::singleShot(0, this, &TabStackedWidget::setUpLayout);
230
231 return index;
232}
233
234QString TabStackedWidget::tabText(int index) const
235{
236 return m_tabBar->tabText(index);
237}
238
239void TabStackedWidget::setTabText(int index, const QString &label)
240{
241 m_tabBar->setTabText(index, label);
242}
243
244QString TabStackedWidget::tabToolTip(int index) const
245{
246 return m_tabBar->tabToolTip(index);
247}
248
249void TabStackedWidget::setTabToolTip(int index, const QString &tip)
250{
251 m_tabBar->setTabToolTip(index, tip);
252}
253
254int TabStackedWidget::pinUnPinTab(int index, const QString &title)
255{
256 QWidget* widget = m_stack->widget(index);
257 QWidget* currentWidget = m_stack->currentWidget();
258
259 if (!widget || !currentWidget)
260 return -1;
261
262 bool makePinned = index >= m_tabBar->pinnedTabsCount();
263 QWidget* button = m_tabBar->tabButton(index, m_tabBar->iconButtonPosition());
264 // To show tooltip of tab which is pinned in the current session
265 QString toolTip = tabToolTip(index);
266
267 m_tabBar->m_blockCurrentChangedSignal = true;
268 m_tabBar->setTabButton(index, m_tabBar->iconButtonPosition(), nullptr);
269
270 m_stack->removeWidget(widget);
271 int newIndex = insertTab(makePinned ? 0 : m_tabBar->pinnedTabsCount(), widget, title, makePinned);
272
273 m_tabBar->setTabButton(newIndex, m_tabBar->iconButtonPosition(), button);
274 m_tabBar->m_blockCurrentChangedSignal = false;
275 setTabToolTip(newIndex, toolTip);
276
277 // Restore current widget
279
280 Q_EMIT pinStateChanged(newIndex, makePinned);
281
282 return newIndex;
283}
284
286{
287 if (QWidget* w = m_stack->widget(index)) {
288 // Select another current tab before remove, so it won't be handled by QTabBar
289 if (index == currentIndex() && count() > 1)
290 selectTabOnRemove();
291 m_stack->removeWidget(w);
292 }
293}
294
295void TabStackedWidget::moveTab(int from, int to)
296{
297 m_tabBar->moveTab(from, to);
298}
299
301{
302 return m_tabBar->currentIndex();
303}
304
306{
307 m_tabBar->setCurrentIndex(index);
308}
309
311{
312 return m_stack->currentWidget();
313}
314
316{
317 m_tabBar->setCurrentIndex(indexOf(widget));
318}
319
320QWidget* TabStackedWidget::widget(int index) const
321{
322 return m_stack->widget(index);
323}
324
325int TabStackedWidget::indexOf(QWidget* widget) const
326{
327 return m_stack->indexOf(widget);
328}
329
331{
332 return m_tabBar->count();
333}
334
335bool TabStackedWidget::validIndex(int index) const
336{
337 return (index < m_stack->count() && index >= 0);
338}
339
340void TabStackedWidget::selectTabOnRemove()
341{
342 Q_ASSERT(count() > 1);
343
344 int index = -1;
345
346 switch (m_tabBar->selectionBehaviorOnRemove()) {
347 case QTabBar::SelectPreviousTab:
348 if (validIndex(m_previousIndex)) {
349 index = m_previousIndex;
350 break;
351 }
352 // fallthrough
353
354 case QTabBar::SelectLeftTab:
355 index = currentIndex() - 1;
356 if (!validIndex(index))
357 index = 1;
358 break;
359
360 case QTabBar::SelectRightTab:
361 index = currentIndex() + 1;
362 if (!validIndex(index))
363 index = currentIndex() - 1;
364 break;
365
366 default:
367 break;
368 }
369
370 Q_ASSERT(validIndex(index));
371 setCurrentIndex(index);
372}
QTabBar::ButtonPosition iconButtonPosition() const
void tabCloseRequested(int index)
bool documentMode() const
int insertTab(int index, const QString &text)
QString tabText(int index) const
void moveTab(int from, int to)
QTabBar::SelectionBehavior selectionBehaviorOnRemove() const
void removeTab(int index)
QString tabToolTip(int index) const
void setExpanding(bool enabled)
void setElideMode(Qt::TextElideMode elide)
QWidget * tabButton(int index, QTabBar::ButtonPosition position) const
int currentIndex
Definition: combotabbar.h:42
int pinnedTabsCount() const
void setTabButton(int index, QTabBar::ButtonPosition position, QWidget *widget)
void currentChanged(int index)
void setDrawBase(bool drawTheBase)
void overFlowChanged(bool overFlow)
void tabMoved(int from, int to)
void setTabToolTip(int index, const QString &tip)
void setTabText(int index, const QString &text)
bool isTabEnabled(int index) const
bool tabsClosable() const
void setDocumentMode(bool set)
Qt::TextElideMode elideMode() const
void setCurrentIndex(int index)
QWidget * currentWidget() const
int pinUnPinTab(int index, const QString &title=QString())
void keyPressEvent(QKeyEvent *event) override
~TabStackedWidget() override
void removeTab(int index)
int currentIndex() const
void setDocumentMode(bool enabled)
QString tabToolTip(int index) const
TabStackedWidget(QWidget *parent=nullptr)
void setTabBar(ComboTabBar *tb)
void pinStateChanged(int index, bool pinned)
void setTabToolTip(int index, const QString &tip)
int indexOf(QWidget *widget) const
void setCurrentIndex(int index)
int addTab(QWidget *widget, const QString &label, bool pinned=false)
bool eventFilter(QObject *obj, QEvent *event) override
void currentChanged(int index)
void setTabText(int index, const QString &label)
int insertTab(int index, QWidget *widget, const QString &label, bool pinned=false)
QString tabText(int index) const
void moveTab(int from, int to)
ComboTabBar * tabBar()
QWidget * widget(int index) const
void setCurrentWidget(QWidget *widget)
bool documentMode() const
void tabCloseRequested(int index)