From bff614144b94a88a9b7128134e0d0a5fc4a30d15 Mon Sep 17 00:00:00 2001 From: "S. Razi Alavizadeh" Date: Tue, 26 Nov 2013 14:14:36 +0100 Subject: [PATCH] Added TabStackedWidget + ComboTabBar Set of patches: Introduce TabStackedWidget+ComboTabbar [Windows] Fix enabling blur background in fullscreen [ComboTabBar] Fix some issues with pinned tabs [ComboTabBar] Don't use pointer to integer, Implement TabStackedWidget::keyPressEvent() and fix changing tab by wheel event Fix animation delay of dragged tab's buttons. [ComboTabBar] Add themable scrollButtons --- src/lib/app/mainapplication.cpp | 4 +- src/lib/app/qupzilla.cpp | 34 +- src/lib/app/qupzilla.h | 1 - src/lib/lib.pro | 8 +- src/lib/navigation/locationbar.cpp | 10 +- src/lib/navigation/navigationbar.cpp | 2 +- src/lib/tools/combotabbar.cpp | 1444 ++++++++++++++++++++++++++ src/lib/tools/combotabbar.h | 320 ++++++ src/lib/tools/tabstackedwidget.cpp | 303 ++++++ src/lib/tools/tabstackedwidget.h | 91 ++ src/lib/tools/toolbutton.cpp | 19 + src/lib/tools/toolbutton.h | 5 + src/lib/webview/tabbar.cpp | 277 +++-- src/lib/webview/tabbar.h | 38 +- src/lib/webview/tabwidget.cpp | 91 +- src/lib/webview/tabwidget.h | 23 +- src/lib/webview/webtab.cpp | 59 +- src/lib/webview/webtab.h | 3 - 18 files changed, 2417 insertions(+), 315 deletions(-) create mode 100644 src/lib/tools/combotabbar.cpp create mode 100644 src/lib/tools/combotabbar.h create mode 100644 src/lib/tools/tabstackedwidget.cpp create mode 100644 src/lib/tools/tabstackedwidget.h diff --git a/src/lib/app/mainapplication.cpp b/src/lib/app/mainapplication.cpp index 74be5074b..8ecdb0849 100644 --- a/src/lib/app/mainapplication.cpp +++ b/src/lib/app/mainapplication.cpp @@ -1173,7 +1173,9 @@ bool MainApplication::restoreStateSlot(QupZilla* window, RestoreData recoveryDat else { // QTabWidget::count() - count of tabs is not updated after closing // recovery tab ... - int tabCount = window->tabWidget()->count(); + // update: it seems with ComboTabBar QTabWidget::count() is updated, + // we add pinnedTabCounts to currentTab! + int tabCount = window->tabWidget()->pinnedTabsCount(); RestoreManager::WindowData data = recoveryData.first(); data.currentTab += tabCount; recoveryData.remove(0); diff --git a/src/lib/app/qupzilla.cpp b/src/lib/app/qupzilla.cpp index b7641bb2b..940523e5a 100644 --- a/src/lib/app/qupzilla.cpp +++ b/src/lib/app/qupzilla.cpp @@ -87,6 +87,7 @@ #include #include #include +#include #if QT_VERSION < 0x050000 #include "qwebkitversion.h" @@ -274,6 +275,8 @@ void QupZilla::postLaunch() setUpdatesEnabled(true); raise(); activateWindow(); + + QTimer::singleShot(0, tabWidget()->getTabBar(), SLOT(ensureVisible())); } void QupZilla::setupUi() @@ -344,7 +347,7 @@ void QupZilla::setupUi() m_navigationContainer->setLayout(l); m_mainSplitter->addWidget(m_tabWidget); - m_mainLayout->addWidget(m_navigationContainer); + triggerTabsOnTop(tabsOnTop()); m_mainLayout->addWidget(m_mainSplitter); m_mainSplitter->setCollapsible(0, false); @@ -838,7 +841,7 @@ void QupZilla::loadSettings() if (m_usingTransparentBackground && !makeTransparent) { QtWin::extendFrameIntoClientArea(this, 0, 0, 0, 0); QtWin::enableBlurBehindWindow(this, false); - QtWin::enableBlurBehindWindow(m_tabWidget->getTabBar(), false); + m_tabWidget->getTabBar()->enableBluredBackground(false); m_usingTransparentBackground = false; } #endif @@ -866,11 +869,14 @@ void QupZilla::loadSettings() m_usingTransparentBackground = true; - QtWin::enableBlurBehindWindow(m_tabWidget->getTabBar(), true); - QtWin::extendFrameIntoClientArea(this); + if (!isFullScreen()) { + m_tabWidget->getTabBar()->enableBluredBackground(true); + QtWin::extendFrameIntoClientArea(this); + } //install event filter menuBar()->installEventFilter(this); + m_tabWidget->getTabBar()->installEventFilter(this); m_navigationBar->installEventFilter(this); m_bookmarksToolbar->installEventFilter(this); statusBar()->installEventFilter(this); @@ -923,15 +929,6 @@ Qz::BrowserWindow QupZilla::windowType() const return m_windowType; } -QWidget* QupZilla::navigationContainer() const -{ - if (!tabsOnTop()) { - return 0; - } - - return m_navigationContainer; -} - void QupZilla::popupToolbarsMenu(const QPoint &pos) { aboutToShowViewMenu(); @@ -1692,10 +1689,12 @@ void QupZilla::showBookmarkImport() void QupZilla::triggerTabsOnTop(bool enable) { if (enable) { - m_tabWidget->showNavigationBar(m_navigationContainer); + m_mainLayout->insertWidget(0, m_tabWidget->getTabBar()); + m_mainLayout->insertWidget(1, m_navigationContainer); } else { m_mainLayout->insertWidget(0, m_navigationContainer); + m_mainLayout->insertWidget(1, m_tabWidget->getTabBar()); } m_tabsOnTopState = enable ? 1 : 0; @@ -1797,9 +1796,10 @@ void QupZilla::webSearch() void QupZilla::searchOnPage() { SearchToolBar* toolBar = searchToolBar(); - const int searchPos = tabsOnTop() ? 1 : 2; if (!toolBar) { + const int searchPos = 3; + toolBar = new SearchToolBar(weView(), this); m_mainLayout->insertWidget(searchPos, toolBar); } @@ -1892,6 +1892,7 @@ bool QupZilla::event(QEvent* event) emit setWebViewMouseTracking(true); #ifdef Q_OS_WIN if (m_usingTransparentBackground) { + m_tabWidget->getTabBar()->enableBluredBackground(false); QtWin::extendFrameIntoClientArea(this, 0, 0, 0 , 0); QtWin::enableBlurBehindWindow(this, false); } @@ -1915,6 +1916,7 @@ bool QupZilla::event(QEvent* event) emit setWebViewMouseTracking(false); #ifdef Q_OS_WIN if (m_usingTransparentBackground) { + m_tabWidget->getTabBar()->enableBluredBackground(true); applyBlurToMainWindow(true); } #endif @@ -2231,7 +2233,7 @@ void QupZilla::closeEvent(QCloseEvent* event) SearchToolBar* QupZilla::searchToolBar() { SearchToolBar* toolBar = 0; - const int searchPos = tabsOnTop() ? 1 : 2; + const int searchPos = 3; if (m_mainLayout->count() == searchPos + 1) { toolBar = qobject_cast(m_mainLayout->itemAt(searchPos)->widget()); diff --git a/src/lib/app/qupzilla.h b/src/lib/app/qupzilla.h index b318df454..5512b26f5 100644 --- a/src/lib/app/qupzilla.h +++ b/src/lib/app/qupzilla.h @@ -107,7 +107,6 @@ public: QMenu* menuHelp() { return m_menuHelp; } QMenu* superMenu() { return m_superMenu; } - QWidget* navigationContainer() const; void popupToolbarsMenu(const QPoint &pos); bool isClosing() { return m_isClosing; } diff --git a/src/lib/lib.pro b/src/lib/lib.pro index 9c1b32809..12089d677 100644 --- a/src/lib/lib.pro +++ b/src/lib/lib.pro @@ -248,7 +248,9 @@ SOURCES += \ tools/aesinterface.cpp \ autofill/passwordbackends/databaseencryptedpasswordbackend.cpp \ network/sslerrordialog.cpp \ - adblock/adblocksearchtree.cpp + adblock/adblocksearchtree.cpp \ + tools/tabstackedwidget.cpp \ + tools/combotabbar.cpp HEADERS += \ @@ -433,7 +435,9 @@ HEADERS += \ tools/aesinterface.h \ autofill/passwordbackends/databaseencryptedpasswordbackend.h \ network/sslerrordialog.h \ - adblock/adblocksearchtree.h + adblock/adblocksearchtree.h \ + tools/tabstackedwidget.h \ + tools/combotabbar.h FORMS += \ preferences/autofillmanager.ui \ diff --git a/src/lib/navigation/locationbar.cpp b/src/lib/navigation/locationbar.cpp index ea449960e..ee1b5337d 100644 --- a/src/lib/navigation/locationbar.cpp +++ b/src/lib/navigation/locationbar.cpp @@ -369,11 +369,13 @@ void LocationBar::contextMenuEvent(QContextMenuEvent* event) void LocationBar::focusInEvent(QFocusEvent* event) { - const QString &stringUrl = convertUrlToText(m_webView->url()); + if (m_webView) { + const QString &stringUrl = convertUrlToText(m_webView->url()); - // Text has been edited, let's show go button - if (stringUrl != text()) { - showGoButton(); + // Text has been edited, let's show go button + if (stringUrl != text()) { + showGoButton(); + } } LineEdit::focusInEvent(event); diff --git a/src/lib/navigation/navigationbar.cpp b/src/lib/navigation/navigationbar.cpp index c922bfb17..9df5a6bc5 100644 --- a/src/lib/navigation/navigationbar.cpp +++ b/src/lib/navigation/navigationbar.cpp @@ -350,7 +350,7 @@ void NavigationBar::goAtHistoryIndexInNewTab(int index) void NavigationBar::refreshHistory() { - if (mApp->isClosing() || p_QupZilla->isClosing()) { + if (mApp->isClosing() || p_QupZilla->isClosing() || !p_QupZilla->weView()) { return; } diff --git a/src/lib/tools/combotabbar.cpp b/src/lib/tools/combotabbar.cpp new file mode 100644 index 000000000..ffd86692b --- /dev/null +++ b/src/lib/tools/combotabbar.cpp @@ -0,0 +1,1444 @@ +/* ============================================================ +* QupZilla - WebKit based browser +* Copyright (C) 2013 S. Razi Alavizadeh +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* ============================================================ */ +#include "combotabbar.h" +#include "toolbutton.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// taken from qtabbar_p.h +#define ANIMATION_DURATION 250 + +ComboTabBar::ComboTabBar(QWidget* parent) + : QWidget(parent) + , m_mainTabBar(0) + , m_pinnedTabBar(0) + , m_maxVisiblePinnedTab(0) + , m_heightIsDirty(true) + , m_mainBarOverFlowed(false) + , m_dragOffset(0) + , m_usesScrollButtons(false) +{ + m_mainTabBar = new TabBarHelper(this); + m_pinnedTabBar = new TabBarHelper(this); + m_mainTabBarWidget = new TabBarScrollWidget(m_mainTabBar, this); + m_pinnedTabBarWidget = new TabBarScrollWidget(m_pinnedTabBar, this); + + m_mainTabBar->setScrollArea(m_mainTabBarWidget->scrollArea()); + m_pinnedTabBar->setScrollArea(m_pinnedTabBarWidget->scrollArea()); + + connect(m_mainTabBarWidget->scrollBar(), SIGNAL(rangeChanged(int,int)), this, SLOT(setMinimumWidthes())); + connect(m_mainTabBarWidget->scrollBar(), SIGNAL(valueChanged(int)), this, SIGNAL(scrollBarValueChanged(int))); + connect(m_pinnedTabBarWidget->scrollBar(), SIGNAL(rangeChanged(int,int)), this, SLOT(setMinimumWidthes())); + connect(m_pinnedTabBarWidget->scrollBar(), SIGNAL(valueChanged(int)), this, SIGNAL(scrollBarValueChanged(int))); + connect(this, SIGNAL(overFlowChanged(bool)), m_mainTabBarWidget, SLOT(overFlowChanged(bool))); + + m_mainTabBar->setActiveTabBar(true); + m_pinnedTabBar->setTabsClosable(false); + + m_mainLayout = new QHBoxLayout; + m_mainLayout->setSpacing(0); + m_mainLayout->setContentsMargins(0, 0, 0, 0); + m_mainLayout->addWidget(m_pinnedTabBarWidget, 4); + m_mainLayout->addWidget(m_mainTabBarWidget, 1); + setLayout(m_mainLayout); + + connect(m_mainTabBar, SIGNAL(currentChanged(int)), this, SLOT(slotCurrentChanged(int))); + connect(m_mainTabBar, SIGNAL(tabCloseRequested(int)), this, SLOT(slotTabCloseRequested(int))); + connect(m_mainTabBar, SIGNAL(tabMoved(int,int)), this, SLOT(slotTabMoved(int,int))); + + connect(m_pinnedTabBar, SIGNAL(currentChanged(int)), this, SLOT(slotCurrentChanged(int))); + connect(m_pinnedTabBar, SIGNAL(tabCloseRequested(int)), this, SLOT(slotTabCloseRequested(int))); + connect(m_pinnedTabBar, SIGNAL(tabMoved(int,int)), this, SLOT(slotTabMoved(int,int))); + + setAutoFillBackground(false); + m_mainTabBar->setAutoFillBackground(false); + m_pinnedTabBar->setAutoFillBackground(false); + + m_mainTabBar->installEventFilter(this); +} + +int ComboTabBar::addTab(const QString &text) +{ + return insertTab(-1, text); +} + +int ComboTabBar::addTab(const QIcon &icon, const QString &text) +{ + return insertTab(-1, icon, text); +} + +int ComboTabBar::insertTab(int index, const QString &text) +{ + return insertTab(index, QIcon(), text); +} + +int ComboTabBar::insertTab(int index, const QIcon &icon, const QString &text, bool pinned) +{ + if (pinned) { + index = m_pinnedTabBar->insertTab(index, icon, text); + } + else { + index -= pinnedTabsCount(); + index = m_mainTabBar->insertTab(index, icon, text); + + if (tabsClosable()) { + QWidget* closeButton = m_mainTabBar->tabButton(index, closeButtonPosition()); + if ((closeButton && closeButton->objectName() != "combotabbar_tabs_close_button") || + !closeButton) { + // insert our close button + insertCloseButton(index + pinnedTabsCount()); + if (closeButton) { + closeButton->deleteLater(); + } + } + } + + index += pinnedTabsCount(); + } + + updatePinnedTabBarVisibility(); + tabInserted(index); + setMinimumWidthes(); + + if (m_heightIsDirty) { + setUpLayout(); + + if (m_mainTabBarWidget->height() > 0) { + m_heightIsDirty = false; + } + } + + return index; +} + +void ComboTabBar::removeTab(int index) +{ + if (validIndex(index)) { + localTabBar(index)->removeTab(toLocalIndex(index)); + updatePinnedTabBarVisibility(); + + tabRemoved(index); + setMinimumWidthes(); + } +} + +void ComboTabBar::moveTab(int from, int to) +{ + if (from >= pinnedTabsCount() && to >= pinnedTabsCount()) { + m_mainTabBar->moveTab(from - pinnedTabsCount(), to - pinnedTabsCount()); + } + else if (from < pinnedTabsCount() && to < pinnedTabsCount()) { + m_pinnedTabBar->moveTab(from, to); + } +} + +bool ComboTabBar::isTabEnabled(int index) const +{ + return localTabBar(index)->isTabEnabled(toLocalIndex(index)); +} + +void ComboTabBar::setTabEnabled(int index, bool enabled) +{ + localTabBar(index)->setTabEnabled(toLocalIndex(index), enabled); +} + +QColor ComboTabBar::tabTextColor(int index) const +{ + return localTabBar(index)->tabTextColor(toLocalIndex(index)); +} + +void ComboTabBar::setTabTextColor(int index, const QColor &color) +{ + localTabBar(index)->setTabTextColor(toLocalIndex(index), color); +} + +QRect ComboTabBar::tabRect(int index) const +{ + QRect rect; + if (index != -1) { + bool mainTabBar = index >= pinnedTabsCount(); + rect = localTabBar(index)->tabRect(toLocalIndex(index)); + + if (mainTabBar) { + rect.moveLeft(rect.x() + mapFromGlobal(m_mainTabBar->mapToGlobal(QPoint(0, 0))).x()); + QRect widgetRect = m_mainTabBarWidget->scrollArea()->viewport()->rect(); + widgetRect.moveLeft(widgetRect.x() + mapFromGlobal(m_mainTabBarWidget->scrollArea()->viewport()->mapToGlobal(QPoint(0, 0))).x()); + rect = rect.intersect(widgetRect); + } + else { + rect.moveLeft(rect.x() + mapFromGlobal(m_pinnedTabBar->mapToGlobal(QPoint(0, 0))).x()); + QRect widgetRect = m_pinnedTabBarWidget->scrollArea()->viewport()->rect(); + widgetRect.moveLeft(widgetRect.x() + mapFromGlobal(m_pinnedTabBarWidget->scrollArea()->viewport()->mapToGlobal(QPoint(0, 0))).x()); + rect = rect.intersect(widgetRect); + } + } + + return rect; +} + +int ComboTabBar::tabAt(const QPoint &pos) const +{ + QWidget* that = const_cast(this); + + int index = -1; + if (m_mainTabBarWidget->rect().contains(m_mainTabBarWidget->mapFromGlobal(QCursor::pos()))) { + index = m_mainTabBar->tabAt(m_mainTabBar->mapFrom(that, pos)); + if (index != -1) { + index += pinnedTabsCount(); + } + } + else if (m_pinnedTabBarWidget->rect().contains(m_pinnedTabBarWidget->mapFromGlobal(QCursor::pos()))) { + index = m_pinnedTabBar->tabAt(m_pinnedTabBar->mapFrom(that, pos)); + } + + return index; +} + +int ComboTabBar::mainTabBarCurrentIndex() const +{ + return (m_mainTabBar->currentIndex() == -1 ? -1 : pinnedTabsCount() + m_mainTabBar->currentIndex()); +} + +int ComboTabBar::currentIndex() const +{ + if (m_pinnedTabBar->isActiveTabBar()) { + return m_pinnedTabBar->currentIndex(); + } + else { + return (m_mainTabBar->currentIndex() == -1 ? -1 : pinnedTabsCount() + m_mainTabBar->currentIndex()); + } +} + +void ComboTabBar::setCurrentIndex(int index) +{ + return localTabBar(index)->setCurrentIndex(toLocalIndex(index)); +} + +void ComboTabBar::slotCurrentChanged(int index) +{ + if (sender() == m_pinnedTabBar) { + if (index == -1 && m_mainTabBar->count() > 0) { + m_mainTabBar->setActiveTabBar(true); + m_pinnedTabBar->setActiveTabBar(false); + emit currentChanged(pinnedTabsCount()); + } + else { + m_pinnedTabBar->setActiveTabBar(true); + m_mainTabBar->setActiveTabBar(false); + emit currentChanged(index); + } + } + else { + if (index == -1 && pinnedTabsCount() > 0) { + m_pinnedTabBar->setActiveTabBar(true); + m_mainTabBar->setActiveTabBar(false); + emit currentChanged(pinnedTabsCount() - 1); + } + else { + m_mainTabBar->setActiveTabBar(true); + m_pinnedTabBar->setActiveTabBar(false); + emit currentChanged(index + pinnedTabsCount()); + } + } +} + +void ComboTabBar::slotTabCloseRequested(int index) +{ + if (sender() == m_pinnedTabBar) { + emit tabCloseRequested(index); + } + else { + emit tabCloseRequested(index + pinnedTabsCount()); + } +} + +void ComboTabBar::slotTabMoved(int from, int to) +{ + if (sender() == m_pinnedTabBar) { + emit tabMoved(from, to); + } + else { + emit tabMoved(from + pinnedTabsCount(), to + pinnedTabsCount()); + } +} + +void ComboTabBar::closeTabFromButton() +{ + QWidget* button = qobject_cast(sender()); + + int tabToClose = -1; + + for (int i = 0; i < m_mainTabBar->count(); ++i) { + if (m_mainTabBar->tabButton(i, closeButtonPosition()) == button) { + tabToClose = i; + break; + } + } + + if (tabToClose != -1) { + emit tabCloseRequested(tabToClose + pinnedTabsCount()); + } +} + +int ComboTabBar::count() const +{ + return pinnedTabsCount() + m_mainTabBar->count(); +} + +void ComboTabBar::setDrawBase(bool drawTheBase) +{ + m_mainTabBar->setDrawBase(drawTheBase); + m_pinnedTabBar->setDrawBase(drawTheBase); +} + +bool ComboTabBar::drawBase() const +{ + return m_mainTabBar->drawBase(); +} + +Qt::TextElideMode ComboTabBar::elideMode() const +{ + return m_mainTabBar->elideMode(); +} + +void ComboTabBar::setElideMode(Qt::TextElideMode elide) +{ + m_mainTabBar->setElideMode(elide); + m_pinnedTabBar->setElideMode(elide); +} + +QString ComboTabBar::tabText(int index) const +{ + return localTabBar(index)->tabText(toLocalIndex(index)); +} + +void ComboTabBar::setTabText(int index, const QString &text) +{ + localTabBar(index)->setTabText(toLocalIndex(index), text); +} + +void ComboTabBar::setTabToolTip(int index, const QString &tip) +{ + localTabBar(index)->setTabToolTip(toLocalIndex(index), tip); +} + +QString ComboTabBar::tabToolTip(int index) const +{ + return localTabBar(index)->tabToolTip(toLocalIndex(index)); +} + +bool ComboTabBar::tabsClosable() const +{ + return m_mainTabBar->tabsClosable(); +} + +void ComboTabBar::setTabsClosable(bool closable) +{ + if (closable == tabsClosable()) { + return; + } + + if (closable) { + // insert our close button + for (int i = 0; i < m_mainTabBar->count(); ++i) { + QWidget* closeButton = m_mainTabBar->tabButton(i, closeButtonPosition()); + if (closeButton) { + if (closeButton->objectName() == "combotabbar_tabs_close_button") { + continue; + } + } + + insertCloseButton(i + pinnedTabsCount()); + if (closeButton) { + closeButton->deleteLater(); + } + } + } + m_mainTabBar->setTabsClosable(closable); +} + +void ComboTabBar::setTabButton(int index, QTabBar::ButtonPosition position, QWidget* widget) +{ + localTabBar(index)->setTabButton(toLocalIndex(index), position, widget); +} + +QWidget* ComboTabBar::tabButton(int index, QTabBar::ButtonPosition position) const +{ + return localTabBar(index)->tabButton(toLocalIndex(index), position); +} + +QTabBar::SelectionBehavior ComboTabBar::selectionBehaviorOnRemove() const +{ + return m_mainTabBar->selectionBehaviorOnRemove(); +} + +void ComboTabBar::setSelectionBehaviorOnRemove(QTabBar::SelectionBehavior behavior) +{ + m_mainTabBar->setSelectionBehaviorOnRemove(behavior); + m_pinnedTabBar->setSelectionBehaviorOnRemove(behavior); +} + +bool ComboTabBar::expanding() const +{ + return m_mainTabBar->expanding(); +} + +void ComboTabBar::setExpanding(bool enabled) +{ + m_mainTabBar->setExpanding(enabled); + m_pinnedTabBar->setExpanding(enabled); +} + +bool ComboTabBar::isMovable() const +{ + return m_mainTabBar->isMovable(); +} + +void ComboTabBar::setMovable(bool movable) +{ + m_mainTabBar->setMovable(movable); + m_pinnedTabBar->setMovable(movable); +} + +bool ComboTabBar::documentMode() const +{ + return m_mainTabBar->documentMode(); +} + +void ComboTabBar::setDocumentMode(bool set) +{ + m_mainTabBar->setDocumentMode(set); + m_pinnedTabBar->setDocumentMode(set); +} + +int ComboTabBar::pinnedTabsCount() const +{ + return m_pinnedTabBar->count(); +} + +int ComboTabBar::normalTabsCount() const +{ + return m_mainTabBar->count(); +} + +bool ComboTabBar::isPinned(int index) const +{ + return index >= 0 && index < pinnedTabsCount(); +} + +void ComboTabBar::setMaxVisiblePinnedTab(int max) +{ + m_maxVisiblePinnedTab = max; + setMinimumWidthes(); +} + +void ComboTabBar::setObjectName(const QString &name) +{ + m_mainTabBar->setObjectName(name); + m_pinnedTabBar->setObjectName(name); + + m_pinnedTabBarWidget->setContainersName(name); + m_mainTabBarWidget->setContainersName(name); +} + +void ComboTabBar::setMouseTracking(bool enable) +{ + m_mainTabBarWidget->scrollArea()->setMouseTracking(enable); + m_mainTabBarWidget->setMouseTracking(enable); + m_mainTabBar->setMouseTracking(enable); + + m_pinnedTabBarWidget->scrollArea()->setMouseTracking(enable); + m_pinnedTabBarWidget->setMouseTracking(enable); + m_pinnedTabBar->setMouseTracking(enable); + + QWidget::setMouseTracking(enable); +} + +void ComboTabBar::setUpLayout() +{ + const int height = m_mainTabBar->height(); + + setFixedHeight(height); + m_pinnedTabBar->setFixedHeight(height); + m_mainTabBarWidget->setUpLayout(); + m_pinnedTabBarWidget->setUpLayout(); + + setMinimumWidthes(); +} + +void ComboTabBar::insertCloseButton(int index) +{ + index -= pinnedTabsCount(); + if (index < 0) { + return; + } + + QAbstractButton* closeButton = new CloseButton(this); + closeButton->setToolTip(m_closeButtonsToolTip); + connect(closeButton, SIGNAL(clicked()), this, SLOT(closeTabFromButton())); + m_mainTabBar->setTabButton(index, closeButtonPosition(), closeButton); +} + +void ComboTabBar::setCloseButtonsToolTip(const QString &tip) +{ + m_closeButtonsToolTip = tip; +} + +void ComboTabBar::enableBluredBackground(bool enable) +{ + m_mainTabBar->enableBluredBackground(enable); + m_pinnedTabBar->enableBluredBackground(enable); + m_mainTabBarWidget->enableBluredBackground(enable); + m_pinnedTabBarWidget->enableBluredBackground(enable); +} + +int ComboTabBar::mainTabBarWidth() const +{ + return m_mainTabBar->width(); +} + +int ComboTabBar::pinTabBarWidth() const +{ + return m_pinnedTabBarWidget->isHidden() ? 0 : m_pinnedTabBarWidget->width(); +} + +void ComboTabBar::wheelEvent(QWheelEvent* event) +{ + event->accept(); + if (m_mainTabBarWidget->underMouse()) { + if (m_mainTabBarWidget->scrollBar()->isOverFlowed()) { + m_mainTabBarWidget->scrollByWheel(event); + } + else if (m_pinnedTabBarWidget->scrollBar()->isOverFlowed()) { + m_pinnedTabBarWidget->scrollByWheel(event); + } + } + else if (m_pinnedTabBarWidget->underMouse()) { + if (m_pinnedTabBarWidget->scrollBar()->isOverFlowed()) { + m_pinnedTabBarWidget->scrollByWheel(event); + } + else if (m_mainTabBarWidget->scrollBar()->isOverFlowed()) { + m_mainTabBarWidget->scrollByWheel(event); + } + } + + if (!m_mainTabBarWidget->scrollBar()->isOverFlowed() && !m_pinnedTabBarWidget->scrollBar()->isOverFlowed()) { + setCurrentNextEnabledIndex(event->delta() > 0 ? -1 : 1); + } +} + +void ComboTabBar::resizeEvent(QResizeEvent* event) +{ + QWidget::resizeEvent(event); + + if (m_mainBarOverFlowed != m_mainTabBarWidget->scrollBar()->isOverFlowed()) { + setMinimumWidthes(); + } +} + +bool ComboTabBar::eventFilter(QObject* obj, QEvent* ev) +{ + if (obj == m_mainTabBar && ev->type() == QEvent::Resize) { + QResizeEvent* event = static_cast(ev); + if (event->oldSize().height() != event->size().height()) { + setUpLayout(); + } + } + + return QWidget::eventFilter(obj, ev); +} + +int ComboTabBar::comboTabBarPixelMetric(ComboTabBar::SizeType sizeType) const +{ + switch (sizeType) { + case ExtraReservedWidth: + return 0; + case NormalTabMaximumWidth: + return 150; + case NormalTabMinimumWidth: + case PinnedTabWidth: + return 30; + case ActiveTabMinimumWidth: + case OverflowedTabWidth: + return 100; + default: + break; + } + + return -1; +} + +QTabBar::ButtonPosition ComboTabBar::iconButtonPosition() +{ + return (closeButtonPosition() == QTabBar::RightSide ? QTabBar::LeftSide : QTabBar::RightSide); +} + +QTabBar::ButtonPosition ComboTabBar::closeButtonPosition() +{ + return (QTabBar::ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, 0, this); +} + +bool ComboTabBar::validIndex(int index) const +{ + return (index >= 0 && index < count()); +} + +void ComboTabBar::setCurrentNextEnabledIndex(int offset) +{ + for (int index = currentIndex() + offset; validIndex(index); index += offset) { + if (isTabEnabled(index)) { + setCurrentIndex(index); + break; + } + } +} + +bool ComboTabBar::usesScrollButtons() const +{ + return m_mainTabBarWidget->usesScrollButtons(); +} + +void ComboTabBar::setUsesScrollButtons(bool useButtons) +{ + m_mainTabBarWidget->setUsesScrollButtons(useButtons); +} + +void ComboTabBar::addMainBarWidget(QWidget* widget, Qt::Alignment align, int stretch, Qt::Alignment layoutAlignment) +{ + if (align == Qt::AlignRight) { + m_mainTabBarWidget->addRightWidget(widget, stretch, layoutAlignment); + } + else { + m_mainTabBarWidget->addLeftWidget(widget, stretch, layoutAlignment); + } +} + +void ComboTabBar::ensureVisible(int index, int xmargin) +{ + if (index == -1) { + index = currentIndex(); + } + + if (index < pinnedTabsCount()) { + if (xmargin == -1) { + xmargin = qMax(20, comboTabBarPixelMetric(PinnedTabWidth)); + } + m_pinnedTabBarWidget->ensureVisible(index, xmargin); + } + else { + if (xmargin == -1) { + xmargin = comboTabBarPixelMetric(OverflowedTabWidth); + } + index -= pinnedTabsCount(); + m_mainTabBarWidget->ensureVisible(index, xmargin); + } +} + +QSize ComboTabBar::tabSizeHint(int index, bool fast) const +{ + Q_UNUSED(fast) + + return localTabBar(index)->baseClassTabSizeHint(toLocalIndex(index)); +} + +void ComboTabBar::tabInserted(int index) +{ + Q_UNUSED(index) +} + +void ComboTabBar::tabRemoved(int index) +{ + Q_UNUSED(index) +} + +void ComboTabBar::tabLayoutChange() +{ +} + +TabBarHelper* ComboTabBar::localTabBar(int index) const +{ + if (index < 0 || index >= pinnedTabsCount()) { + return m_mainTabBar; + } + else { + return m_pinnedTabBar; + } +} + +int ComboTabBar::toLocalIndex(int globalIndex) const +{ + if (globalIndex < 0) { + return -1; + } + + if (globalIndex >= pinnedTabsCount()) { + return globalIndex - pinnedTabsCount(); + } + else { + return globalIndex; + } +} + +void ComboTabBar::updatePinnedTabBarVisibility() +{ + m_pinnedTabBarWidget->setVisible(pinnedTabsCount() > 0); + + if (pinnedTabsCount() > 0) { + m_pinnedTabBarWidget->setFixedHeight(m_mainTabBarWidget->height()); + m_pinnedTabBar->setFixedHeight(m_mainTabBar->height()); + } +} + +void ComboTabBar::setMinimumWidthes() +{ + if (!isVisible() || comboTabBarPixelMetric(PinnedTabWidth) < 0) { + return; + } + + int pinnedTabBarWidth = pinnedTabsCount() * comboTabBarPixelMetric(PinnedTabWidth); + m_pinnedTabBar->setMinimumWidth(pinnedTabBarWidth); + if (m_maxVisiblePinnedTab > 0) { + pinnedTabBarWidth = qMin(pinnedTabBarWidth, m_maxVisiblePinnedTab * comboTabBarPixelMetric(PinnedTabWidth)); + } + m_pinnedTabBarWidget->setMaximumWidth(pinnedTabBarWidth); + + int mainTabBarWidth = comboTabBarPixelMetric(PinnedTabWidth) * (m_mainTabBar->count() - 1) + + comboTabBarPixelMetric(ActiveTabMinimumWidth) + + comboTabBarPixelMetric(ExtraReservedWidth); + if (mainTabBarWidth <= m_mainTabBarWidget->width()) { + m_mainTabBar->useFastTabSizeHint(false); + emit overFlowChanged(false); + m_mainTabBar->setMinimumWidth(mainTabBarWidth); + m_mainBarOverFlowed = false; + } + else { + emit overFlowChanged(true); + // The following line is the cause of calling tabSizeHint() for all tabs that is + // time consuming, Because of this we notify application to using a lighter + // version of it. (this is safe because all normal tabs have the same size) + m_mainTabBar->useFastTabSizeHint(true); + if (m_mainTabBar->count() * comboTabBarPixelMetric(OverflowedTabWidth) != m_mainTabBar->minimumWidth()) { + m_mainTabBar->setMinimumWidth(m_mainTabBar->count() * comboTabBarPixelMetric(OverflowedTabWidth)); + } + m_mainBarOverFlowed = true; + } +} + +void ComboTabBar::showEvent(QShowEvent* event) +{ + QTimer::singleShot(0, this, SLOT(setUpLayout())); + + QWidget::showEvent(event); +} + + +TabBarHelper::TabBarHelper(ComboTabBar* comboTabBar) + : QTabBar(comboTabBar) + , m_comboTabBar(comboTabBar) + , m_scrollArea(0) + , m_pressedIndex(-1) + , m_pressedGlobalX(-1) + , m_dragInProgress(false) + , m_activeTabBar(false) + , m_useFastTabSizeHint(false) + , m_bluredBackground(false) +{ +} + +void TabBarHelper::setTabButton(int index, QTabBar::ButtonPosition position, QWidget* widget) +{ + QTabBar::setTabButton(index, position, widget); +} + +QSize TabBarHelper::tabSizeHint(int index) const +{ + if (this == m_comboTabBar->mainTabBar()) { + index += m_comboTabBar->pinnedTabsCount(); + } + return m_comboTabBar->tabSizeHint(index, m_useFastTabSizeHint); +} + +QSize TabBarHelper::baseClassTabSizeHint(int index) const +{ + return QTabBar::tabSizeHint(index); +} + +bool TabBarHelper::isActiveTabBar() +{ + return m_activeTabBar; +} + +void TabBarHelper::setActiveTabBar(bool activate) +{ + if (m_activeTabBar != activate) { + m_activeTabBar = activate; + update(); + } +} + +void TabBarHelper::setScrollArea(QScrollArea* scrollArea) +{ + m_scrollArea = scrollArea; +} + +void TabBarHelper::useFastTabSizeHint(bool enabled) +{ + m_useFastTabSizeHint = enabled; +} + +bool TabBarHelper::isDisplayedOnViewPort(int globalLeft, int globalRight) +{ + bool isVisible = true; + + if (m_scrollArea) { + if (globalRight < m_scrollArea->viewport()->mapToGlobal(QPoint(0, 0)).x() || + globalLeft > m_scrollArea->viewport()->mapToGlobal(m_scrollArea->viewport()->rect().topRight()).x()) { + isVisible = false; + } + } + + return isVisible; +} + +void TabBarHelper::enableBluredBackground(bool enable) +{ + if (enable != m_bluredBackground) { + m_bluredBackground = enable; + update(); + } +} + +void TabBarHelper::setCurrentIndex(int index) +{ + if (index == currentIndex() && !m_activeTabBar) { + emit currentChanged(currentIndex()); + } + + QTabBar::setCurrentIndex(index); +} + +bool TabBarHelper::event(QEvent* ev) +{ + switch (ev->type()) { + case QEvent::ToolTip: + case QEvent::Wheel: + ev->ignore(); + return false; + break; + default: + break; + } + + QTabBar::event(ev); + ev->ignore(); + return false; +} + +// taken from qtabbar.cpp +void TabBarHelper::initStyleBaseOption(QStyleOptionTabBarBaseV2* optTabBase, QTabBar* tabbar, QSize size) +{ + QStyleOptionTab tabOverlap; + tabOverlap.shape = tabbar->shape(); + int overlap = tabbar->style()->pixelMetric(QStyle::PM_TabBarBaseOverlap, &tabOverlap, tabbar); + QWidget* theParent = tabbar->parentWidget(); + optTabBase->init(tabbar); + optTabBase->shape = tabbar->shape(); + optTabBase->documentMode = tabbar->documentMode(); + if (theParent && overlap > 0) { + QRect rect; + switch (tabOverlap.shape) { + case QTabBar::RoundedNorth: + case QTabBar::TriangularNorth: + rect.setRect(0, size.height() - overlap, size.width(), overlap); + break; + case QTabBar::RoundedSouth: + case QTabBar::TriangularSouth: + rect.setRect(0, 0, size.width(), overlap); + break; + case QTabBar::RoundedEast: + case QTabBar::TriangularEast: + rect.setRect(0, 0, overlap, size.height()); + break; + case QTabBar::RoundedWest: + case QTabBar::TriangularWest: + rect.setRect(size.width() - overlap, 0, overlap, size.height()); + break; + } + optTabBase->rect = rect; + } +} + +// some codes were taken from qtabbar.cpp +void TabBarHelper::paintEvent(QPaintEvent* event) +{ + if (m_bluredBackground) { + QPainter p(this); + p.setCompositionMode(QPainter::CompositionMode_Clear); + p.fillRect(event->rect(), QColor(0, 0, 0, 0)); + } + + // note: this code doesn't support vertical tabs + if (!m_dragInProgress) { + QStyleOptionTabBarBaseV2 optTabBase; + initStyleBaseOption(&optTabBase, this, size()); + + QStylePainter p(this); + int selected = currentIndex(); + + for (int i = 0; i < count(); ++i) { + optTabBase.tabBarRect |= tabRect(i); + } + + optTabBase.selectedTabRect = QRect(); + + if (drawBase()) { + p.drawPrimitive(QStyle::PE_FrameTabBarBase, optTabBase); + } + + for (int i = 0; i < count(); ++i) { + QStyleOptionTabV3 tab; + initStyleOption(&tab, i); + + if (!(tab.state & QStyle::State_Enabled)) { + tab.palette.setCurrentColorGroup(QPalette::Disabled); + } + + // Don't bother drawing a tab if the entire tab is outside of the visible tab bar. + if (!isDisplayedOnViewPort(mapToGlobal(tab.rect.topLeft()).x(), mapToGlobal(tab.rect.topRight()).x())) { + continue; + } + + if (i == selected) { + continue; + } + + // update mouse over state when scrolling + tab.state = tab.state & ~QStyle::State_MouseOver; + int index = tabAt(mapFromGlobal(QCursor::pos())); + if (i == index) { + tab.state = tab.state | QStyle::State_MouseOver; + } + + p.drawControl(QStyle::CE_TabBarTab, tab); + } + + // Draw the selected tab last to get it "on top" + if (selected >= 0) { + QStyleOptionTabV3 tab; + initStyleOption(&tab, selected); + + if (!m_activeTabBar) { + tab.state = tab.state & ~QStyle::State_Selected; + } + + p.drawControl(QStyle::CE_TabBarTab, tab); + } + } + else { + QTabBar::paintEvent(event); + } + + if (m_scrollArea) { + const int tearWidth = 15; + const int maxAlpha = 200; + const int colorId = 150; + const bool ltr = isLeftToRight(); + QWidget* viewPort = m_scrollArea->viewport(); + QPoint globalTopLeft = ltr ? viewPort->mapToGlobal(QPoint(0, 0)) : + viewPort->mapToGlobal(QPoint(viewPort->width() - tearWidth, 0)); + if (m_scrollArea->horizontalScrollBar()->value() > m_scrollArea->horizontalScrollBar()->minimum()) { + QPainter p(this); + QPoint localTopLeft = mapFromGlobal(globalTopLeft); + QLinearGradient fade(localTopLeft, localTopLeft + QPoint(tearWidth, 0)); + fade.setColorAt(ltr ? 0 : 1, QColor(colorId, colorId, colorId, maxAlpha)); + fade.setColorAt(ltr ? 1 : 0, QColor(colorId, colorId, colorId, 0)); + p.fillRect(QRect(localTopLeft, QSize(tearWidth, height())), fade); + } + + QPoint globalTopRight = ltr ? viewPort->mapToGlobal(QPoint(viewPort->width() - tearWidth, 0)) : + viewPort->mapToGlobal(QPoint(0, 0)); + if (m_scrollArea->horizontalScrollBar()->value() < m_scrollArea->horizontalScrollBar()->maximum()) { + QPainter p(this); + globalTopRight = mapFromGlobal(globalTopRight); + QLinearGradient fade(globalTopRight, globalTopRight + QPoint(tearWidth, 0)); + fade.setColorAt(ltr ? 0 : 1, QColor(colorId, colorId, colorId, 0)); + fade.setColorAt(ltr ? 1 : 0, QColor(colorId, colorId, colorId, maxAlpha)); + p.fillRect(QRect(globalTopRight, QSize(tearWidth, height())), fade); + } + } +} + +void TabBarHelper::mousePressEvent(QMouseEvent* event) +{ + event->ignore(); + if (event->button() == Qt::LeftButton) { + m_pressedIndex = tabAt(event->pos()); + if (m_pressedIndex != -1) { + m_pressedGlobalX = event->globalX(); + m_dragInProgress = true; + // virtualize selecting tab by click + if (m_pressedIndex == currentIndex() && !m_activeTabBar) { + emit currentChanged(currentIndex()); + } + } + } + + QTabBar::mousePressEvent(event); +} + +void TabBarHelper::mouseReleaseEvent(QMouseEvent* event) +{ + event->ignore(); + QTabBar::mouseReleaseEvent(event); + + if (m_pressedIndex >= 0) { + int length = qAbs(m_pressedGlobalX - event->globalX()); + int duration = qMin((length * ANIMATION_DURATION) / tabRect(m_pressedIndex).width(), + ANIMATION_DURATION); + + m_pressedIndex = -1; + m_pressedGlobalX = -1; + QTimer::singleShot(duration, this, SLOT(resetDragState())); + } +} + +void TabBarHelper::resetDragState() +{ + if (m_pressedIndex == -1) { + m_dragInProgress = false; + update(); + } +} + +TabScrollBar::TabScrollBar(QWidget* parent) + : QScrollBar(Qt::Horizontal, parent) + , m_animation(0) +{ +} + +TabScrollBar::~TabScrollBar() +{ +} + +void TabScrollBar::animateToValue(int to, QEasingCurve::Type type) +{ + if (!m_animation) { + m_animation = new QPropertyAnimation(this, "value", this); + } + m_animation->setEasingCurve(type); + + int current = value(); + to = qBound(minimum(), to, maximum()); + int lenght = qAbs(to - current); + + int duration = qMin(1500, 200 + lenght / 2); + + m_animation->setDuration(duration); + + if (m_animation->state() != QAbstractAnimation::Running) { + m_animation->setStartValue(value()); + } + m_animation->setEndValue(to); + m_animation->start(); +} + +bool TabScrollBar::isOverFlowed() +{ + return maximum() != minimum(); +} + +void TabScrollBar::wheelEvent(QWheelEvent* event) +{ + int delta = isRightToLeft() ? -event->delta() : event->delta(); + QWheelEvent fakeEvent(event->pos(), delta, event->buttons(), + event->modifiers(), Qt::Vertical); + QScrollBar::wheelEvent(&fakeEvent); + event->accept(); +} + + +TabBarScrollWidget::TabBarScrollWidget(QTabBar* tabBar, QWidget* parent) + : QWidget(parent) + , m_tabBar(tabBar) + , m_usesScrollButtons(false) + , m_bluredBackground(false) + , m_scrollByButtonAnim(0) +{ + m_scrollArea = new QScrollArea(this); + m_scrollArea->setFrameStyle(QFrame::NoFrame); + m_scrollArea->setWidgetResizable(true); + m_scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + m_scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + m_scrollBar = new TabScrollBar(m_scrollArea); + m_scrollArea->setHorizontalScrollBar(m_scrollBar); + m_scrollArea->setWidget(m_tabBar); + + m_leftScrollButton = new ToolButton(this); + m_leftScrollButton->setObjectName("tabbar-button-left"); + connect(m_leftScrollButton, SIGNAL(pressed()), this, SLOT(scrollStart())); + connect(m_leftScrollButton, SIGNAL(released()), this, SLOT(scrollStop())); + + m_rightScrollButton = new ToolButton(this); + m_rightScrollButton->setObjectName("tabbar-button-right"); + connect(m_rightScrollButton, SIGNAL(pressed()), this, SLOT(scrollStart())); + connect(m_rightScrollButton, SIGNAL(released()), this, SLOT(scrollStop())); + + m_leftLayout = new QHBoxLayout; + m_leftLayout->setSpacing(0); + m_leftLayout->setContentsMargins(0, 0, 0, 0); + m_rightLayout = new QHBoxLayout; + m_rightLayout->setSpacing(0); + m_rightLayout->setContentsMargins(0, 0, 0, 0); + + QHBoxLayout* leftLayout = new QHBoxLayout; + leftLayout->setSpacing(0); + leftLayout->setContentsMargins(0, 0, 0, 0); + leftLayout->addLayout(m_leftLayout); + leftLayout->addWidget(m_leftScrollButton); + QHBoxLayout* rightLayout = new QHBoxLayout; + rightLayout->setSpacing(0); + rightLayout->setContentsMargins(0, 0, 0, 0); + rightLayout->addWidget(m_rightScrollButton); + rightLayout->addLayout(m_rightLayout); + + m_leftContainer = new QWidget(this); + m_leftContainer->setLayout(leftLayout); + m_rightContainer = new QWidget(this); + m_rightContainer->setLayout(rightLayout); + m_leftContainer->installEventFilter(this); + m_rightContainer->installEventFilter(this); + + QHBoxLayout* hLayout = new QHBoxLayout; + hLayout->setSpacing(0); + hLayout->setContentsMargins(0, 0, 0, 0); + hLayout->addWidget(m_leftContainer); + hLayout->addWidget(m_scrollArea); + hLayout->addWidget(m_rightContainer); + setLayout(hLayout); + + m_scrollArea->viewport()->setAutoFillBackground(false); + connect(m_scrollBar, SIGNAL(valueChanged(int)), this, SLOT(scrollBarValueChange())); + + scrollBarValueChange(); + overFlowChanged(false); +} + +void TabBarScrollWidget::addLeftWidget(QWidget* widget, int stretch, Qt::Alignment alignment) +{ + m_leftLayout->addWidget(widget, stretch, alignment); +} + +void TabBarScrollWidget::addRightWidget(QWidget* widget, int stretch, Qt::Alignment alignment) +{ + m_rightLayout->addWidget(widget, stretch, alignment); +} + +QTabBar* TabBarScrollWidget::tabBar() +{ + return m_tabBar; +} + +QScrollArea* TabBarScrollWidget::scrollArea() +{ + return m_scrollArea; +} + +TabScrollBar* TabBarScrollWidget::scrollBar() +{ + return m_scrollBar; +} + +void TabBarScrollWidget::ensureVisible(int index, int xmargin) +{ + if (index == -1) { + index = m_tabBar->currentIndex(); + } + + if (index < 0 || index >= m_tabBar->count()) { + return; + } + + xmargin = qMin(xmargin, width() / 2); + + // Qt Bug? the following lines were taken from QScrollArea::ensureVisible() and + // then were fixed. The original version caculates wrong values in RTL layouts. + const QRect &logicalTabRect = QStyle::visualRect(m_tabBar->layoutDirection(), m_tabBar->rect(), m_tabBar->tabRect(index)); + int logicalX = QStyle::visualPos(Qt::LeftToRight, m_scrollArea->viewport()->rect(), logicalTabRect.center()).x(); + + if (logicalX - xmargin < m_scrollBar->value()) { + m_scrollBar->animateToValue(qMax(0, logicalX - xmargin)); + } + else if (logicalX > m_scrollBar->value() + m_scrollArea->viewport()->width() - xmargin) { + m_scrollBar->animateToValue(qMin(logicalX - m_scrollArea->viewport()->width() + xmargin, + m_scrollBar->maximum())); + } +} + +void TabBarScrollWidget::scrollToLeft(int n) +{ + n = qMax(1, n); + m_scrollBar->animateToValue(m_scrollBar->value() - n * m_scrollBar->singleStep(), QEasingCurve::Linear); +} + +void TabBarScrollWidget::scrollToRight(int n) +{ + n = qMax(1, n); + m_scrollBar->animateToValue(m_scrollBar->value() + n * m_scrollBar->singleStep(), QEasingCurve::Linear); +} + +void TabBarScrollWidget::scrollToLeftEdge() +{ + m_scrollBar->animateToValue(m_scrollBar->minimum()); +} + +void TabBarScrollWidget::scrollToRightEdge() +{ + m_scrollBar->animateToValue(m_scrollBar->maximum()); +} + +void TabBarScrollWidget::setUpLayout() +{ + const int height = m_tabBar->height(); + + setFixedHeight(height); + m_leftContainer->setFixedHeight(height); + m_rightContainer->setFixedHeight(height); +} + +void TabBarScrollWidget::scrollBarValueChange() +{ + m_leftScrollButton->setEnabled(m_scrollBar->value() != m_scrollBar->minimum()); + m_rightScrollButton->setEnabled(m_scrollBar->value() != m_scrollBar->maximum()); +} + +void TabBarScrollWidget::overFlowChanged(bool overflowed) +{ + m_leftScrollButton->setVisible(overflowed && m_usesScrollButtons); + m_rightScrollButton->setVisible(overflowed && m_usesScrollButtons); + + // a workaround for UI issue of buttons on very fast resizing + if (m_rightContainer->isVisible()) { + m_rightContainer->hide(); + m_rightContainer->show(); + } + if (m_leftContainer->isVisible()) { + m_leftContainer->hide(); + m_leftContainer->show(); + } +} + +void TabBarScrollWidget::scrollStart() +{ + if (QApplication::keyboardModifiers() & Qt::CTRL) { + if (sender() == m_leftScrollButton) { + scrollToLeftEdge(); + } + else if (sender() == m_rightScrollButton) { + scrollToRightEdge(); + } + return; + } + + if (!m_scrollByButtonAnim) { + m_scrollByButtonAnim = new QPropertyAnimation(m_scrollBar, "value", this); + m_scrollByButtonAnim->setEasingCurve(QEasingCurve::Linear); + } + m_scrollByButtonAnim->stop(); + int len = m_scrollBar->value(); + m_scrollByButtonAnim->setStartValue(len); + if (sender() == m_leftScrollButton) { + len = len - m_scrollBar->minimum(); + m_scrollByButtonAnim->setEndValue(m_scrollBar->minimum()); + } + else if (sender() == m_rightScrollButton) { + len = m_scrollBar->maximum() - len; + m_scrollByButtonAnim->setEndValue(m_scrollBar->maximum()); + } + m_scrollByButtonAnim->setDuration(len * 3); + m_scrollByButtonAnim->start(); +} + +void TabBarScrollWidget::scrollStop() +{ + if (m_scrollByButtonAnim) { + m_scrollByButtonAnim->stop(); + } +} + +bool TabBarScrollWidget::eventFilter(QObject* obj, QEvent* ev) +{ + if (m_bluredBackground) { + if (ev->type() == QEvent::Paint && (obj == m_leftContainer || obj == m_rightContainer)) { + QPaintEvent* event = static_cast(ev); + QPainter p(qobject_cast(obj)); + p.setCompositionMode(QPainter::CompositionMode_Clear); + p.fillRect(event->rect(), QColor(0, 0, 0, 0)); + } + } + + return QWidget::eventFilter(obj, ev); +} + +void TabBarScrollWidget::scrollByWheel(QWheelEvent* event) +{ + event->accept(); + + // support for some finer mouse + static int totalDeltas = 0; + + if (totalDeltas * event->delta() < 0) { + // direction has changed from last time + totalDeltas = 0; + } + totalDeltas += event->delta(); + + int factor = qMax(m_scrollBar->pageStep() / 3, m_scrollBar->singleStep()); + if ((event->modifiers() & Qt::ControlModifier) || (event->modifiers() & Qt::ShiftModifier)) { + factor = m_scrollBar->pageStep(); + } + + int offset = (totalDeltas / 120) * factor; + if (offset != 0) { + if (isRightToLeft()) { + m_scrollBar->animateToValue(m_scrollBar->value() + offset); + } + else { + m_scrollBar->animateToValue(m_scrollBar->value() - offset); + } + + totalDeltas -= (offset / factor) * 120; + } +} + +bool TabBarScrollWidget::usesScrollButtons() const +{ + return m_usesScrollButtons; +} + +void TabBarScrollWidget::setUsesScrollButtons(bool useButtons) +{ + if (useButtons != m_usesScrollButtons) { + m_usesScrollButtons = useButtons; + scrollBarValueChange(); + m_tabBar->setElideMode(m_tabBar->elideMode()); + } +} + +void TabBarScrollWidget::setContainersName(const QString &name) +{ + m_leftContainer->setObjectName(name); + m_rightContainer->setObjectName(name); +} + +void TabBarScrollWidget::enableBluredBackground(bool enable) +{ + if (enable != m_bluredBackground) { + m_bluredBackground = enable; + update(); + } +} + +void TabBarScrollWidget::mouseMoveEvent(QMouseEvent* event) +{ + event->ignore(); +} + + +CloseButton::CloseButton(QWidget* parent) + : QAbstractButton(parent) +{ + setObjectName("combotabbar_tabs_close_button"); + setFocusPolicy(Qt::NoFocus); + setCursor(Qt::ArrowCursor); + + resize(sizeHint()); +} + +QSize CloseButton::sizeHint() const +{ + ensurePolished(); + static int width = style()->pixelMetric(QStyle::PM_TabCloseIndicatorWidth, 0, this); + static int height = style()->pixelMetric(QStyle::PM_TabCloseIndicatorHeight, 0, this); + return QSize(width, height); +} + +QSize CloseButton::minimumSizeHint() const +{ + return sizeHint(); +} + +void CloseButton::enterEvent(QEvent* event) +{ + if (isEnabled()) { + update(); + } + + QAbstractButton::enterEvent(event); +} + +void CloseButton::leaveEvent(QEvent* event) +{ + if (isEnabled()) { + update(); + } + + QAbstractButton::leaveEvent(event); +} + +void CloseButton::paintEvent(QPaintEvent*) +{ + QPainter p(this); + QStyleOption opt; + opt.init(this); + opt.state |= QStyle::State_AutoRaise; + + // update raised state on scrolling + bool isUnderMouse = rect().contains(mapFromGlobal(QCursor::pos())); + + if (isEnabled() && isUnderMouse && !isChecked() && !isDown()) { + opt.state |= QStyle::State_Raised; + } + if (isChecked()) { + opt.state |= QStyle::State_On; + } + if (isDown()) { + opt.state |= QStyle::State_Sunken; + } + + if (TabBarHelper* tb = qobject_cast(parent())) { + int index = tb->currentIndex(); + QTabBar::ButtonPosition closeSide = (QTabBar::ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, 0, tb); + if (tb->tabButton(index, closeSide) == this && tb->isActiveTabBar()) { + opt.state |= QStyle::State_Selected; + } + } + + style()->drawPrimitive(QStyle::PE_IndicatorTabClose, &opt, &p, this); +} diff --git a/src/lib/tools/combotabbar.h b/src/lib/tools/combotabbar.h new file mode 100644 index 000000000..2ce7cf004 --- /dev/null +++ b/src/lib/tools/combotabbar.h @@ -0,0 +1,320 @@ +/* ============================================================ +* QupZilla - WebKit based browser +* Copyright (C) 2013 S. Razi Alavizadeh +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* ============================================================ */ +#ifndef COMBOTABBAR_H +#define COMBOTABBAR_H + +#include "qz_namespace.h" + +#include +#include +#include +#include + +class QScrollArea; +class QPropertyAnimation; +class QStyleOptionTabBarBaseV2; +class QHBoxLayout; + +class TabBarScrollWidget; +class TabBarHelper; +class ToolButton; + +class QT_QUPZILLA_EXPORT ComboTabBar : public QWidget +{ + Q_OBJECT + + Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentChanged) + Q_PROPERTY(int count READ count) + + friend class TabBarHelper; + +public: + enum SizeType { + PinnedTabWidth, + ActiveTabMinimumWidth, + NormalTabMinimumWidth, + NormalTabMaximumWidth, + OverflowedTabWidth, + ExtraReservedWidth + }; + + explicit ComboTabBar(QWidget* parent = 0); + + int addTab(const QString &text); + int addTab(const QIcon &icon, const QString &text); + + int insertTab(int index, const QString &text); + int insertTab(int index, const QIcon &icon, const QString &text, bool pinned = false); + + void removeTab(int index); + void moveTab(int from, int to); + + bool isTabEnabled(int index) const; + void setTabEnabled(int index, bool enabled); + + QColor tabTextColor(int index) const; + void setTabTextColor(int index, const QColor &color); + + QRect tabRect(int index) const; + int tabAt(const QPoint &pos) const; + + int mainTabBarCurrentIndex() const; + int currentIndex() const; + int count() const; + + void setDrawBase(bool drawTheBase); + bool drawBase() const; + + Qt::TextElideMode elideMode() const; + void setElideMode(Qt::TextElideMode elide); + + QString tabText(int index) const; + void setTabText(int index, const QString &text); + + void setTabToolTip(int index, const QString &tip); + QString tabToolTip(int index) const; + + bool tabsClosable() const; + void setTabsClosable(bool closable); + + void setTabButton(int index, QTabBar::ButtonPosition position, QWidget* widget); + QWidget* tabButton(int index, QTabBar::ButtonPosition position) const; + + QTabBar::SelectionBehavior selectionBehaviorOnRemove() const; + void setSelectionBehaviorOnRemove(QTabBar::SelectionBehavior behavior); + + bool expanding() const; + void setExpanding(bool enabled); + + bool isMovable() const; + void setMovable(bool movable); + + bool documentMode() const; + void setDocumentMode(bool set); + + int pinnedTabsCount() const; + int normalTabsCount() const; + bool isPinned(int index) const; + void setMaxVisiblePinnedTab(int max); + + void setObjectName(const QString &name); + void setMouseTracking(bool enable); + + void insertCloseButton(int index); + void setCloseButtonsToolTip(const QString &tip); + void enableBluredBackground(bool enable); + + QTabBar::ButtonPosition iconButtonPosition(); + QTabBar::ButtonPosition closeButtonPosition(); + + bool validIndex(int index) const; + void setCurrentNextEnabledIndex(int offset); + + bool usesScrollButtons() const; + void setUsesScrollButtons(bool useButtons); + + void addMainBarWidget(QWidget* widget, Qt::Alignment align, int stretch = 0, Qt::Alignment layoutAlignment = 0); + +public slots: + void setUpLayout(); + void ensureVisible(int index = -1, int xmargin = -1); + void setCurrentIndex(int index); + +private slots: + void setMinimumWidthes(); + void slotCurrentChanged(int index); + void slotTabCloseRequested(int index); + void slotTabMoved(int from, int to); + void closeTabFromButton(); + +protected: + int mainTabBarWidth() const; + int pinTabBarWidth() const; + void wheelEvent(QWheelEvent* event); + void showEvent(QShowEvent* event); + void resizeEvent(QResizeEvent* event); + bool eventFilter(QObject* obj, QEvent* ev); + + virtual int comboTabBarPixelMetric(SizeType sizeType) const; + virtual QSize tabSizeHint(int index, bool fast = false) const; + virtual void tabInserted(int index); + virtual void tabRemoved(int index); + virtual void tabLayoutChange(); + +private: + TabBarHelper* localTabBar(int index = -1) const; + int toLocalIndex(int globalIndex) const; + + inline TabBarHelper* mainTabBar() { return m_mainTabBar; } + void updatePinnedTabBarVisibility(); + + QHBoxLayout* m_mainLayout; + + TabBarHelper* m_mainTabBar; + TabBarHelper* m_pinnedTabBar; + + TabBarScrollWidget* m_mainTabBarWidget; + TabBarScrollWidget* m_pinnedTabBarWidget; + + int m_maxVisiblePinnedTab; + QString m_closeButtonsToolTip; + bool m_heightIsDirty; + bool m_mainBarOverFlowed; + int m_dragOffset; + bool m_usesScrollButtons; + +signals: + void overFlowChanged(bool overFlow); + void currentChanged(int index); + void tabCloseRequested(int index); + void tabMoved(int from, int to); + void scrollBarValueChanged(int value); +}; + +class QT_QUPZILLA_EXPORT TabBarHelper : public QTabBar +{ + Q_OBJECT + +public: + explicit TabBarHelper(ComboTabBar* comboTabBar); + + void setTabButton(int index, QTabBar::ButtonPosition position, QWidget* widget); + + QSize tabSizeHint(int index) const; + QSize baseClassTabSizeHint(int index) const; + + bool isActiveTabBar(); + void setActiveTabBar(bool activate); + + void setScrollArea(QScrollArea* scrollArea); + void useFastTabSizeHint(bool enabled); + bool isDisplayedOnViewPort(int globalLeft, int globalRight); + void enableBluredBackground(bool enable); + +public slots: + void setCurrentIndex(int index); + +private slots: + void resetDragState(); + +private: + void initStyleBaseOption(QStyleOptionTabBarBaseV2* optTabBase, QTabBar* tabbar, QSize size); + bool event(QEvent* ev); + void paintEvent(QPaintEvent* event); + void mousePressEvent(QMouseEvent* event); + void mouseReleaseEvent(QMouseEvent* event); + + ComboTabBar* m_comboTabBar; + QScrollArea* m_scrollArea; + + int m_pressedIndex; + bool m_dragInProgress; + int m_pressedGlobalX; + bool m_activeTabBar; + bool m_useFastTabSizeHint; + bool m_bluredBackground; +}; + +class QT_QUPZILLA_EXPORT TabScrollBar : public QScrollBar +{ + Q_OBJECT +public: + explicit TabScrollBar(QWidget* parent = 0); + + ~TabScrollBar(); + + void animateToValue(int to, QEasingCurve::Type type = QEasingCurve::InOutExpo); + + bool isOverFlowed(); + void wheelEvent(QWheelEvent* event); + +private: + QPropertyAnimation* m_animation; +}; + + +class QT_QUPZILLA_EXPORT TabBarScrollWidget : public QWidget +{ + Q_OBJECT +public: + explicit TabBarScrollWidget(QTabBar* tabBar, QWidget* parent = 0); + + void addLeftWidget(QWidget* widget, int stretch = 0, Qt::Alignment alignment = 0); + void addRightWidget(QWidget* widget, int stretch = 0, Qt::Alignment alignment = 0); + + QTabBar* tabBar(); + QScrollArea* scrollArea(); + TabScrollBar* scrollBar(); + + void scrollByWheel(QWheelEvent* event); + + bool usesScrollButtons() const; + void setUsesScrollButtons(bool useButtons); + + void setContainersName(const QString &name); + void enableBluredBackground(bool enable); + +public slots: + void ensureVisible(int index = -1, int xmargin = 132); + void scrollToLeft(int n = 1); + void scrollToRight(int n = 1); + void scrollToLeftEdge(); + void scrollToRightEdge(); + void setUpLayout(); + +private slots: + void scrollBarValueChange(); + void overFlowChanged(bool overflowed); + void scrollStart(); + void scrollStop(); + +private: + bool eventFilter(QObject* obj, QEvent* ev); + void mouseMoveEvent(QMouseEvent* event); + + QTabBar* m_tabBar; + QScrollArea* m_scrollArea; + TabScrollBar* m_scrollBar; + QHBoxLayout* m_leftLayout; + QHBoxLayout* m_rightLayout; + ToolButton* m_rightScrollButton; + ToolButton* m_leftScrollButton; + QWidget* m_leftContainer; + QWidget* m_rightContainer; + bool m_usesScrollButtons; + bool m_bluredBackground; + QPropertyAnimation* m_scrollByButtonAnim; +}; + +// Class for close button on tabs +// * taken from qtabbar.cpp +class CloseButton : public QAbstractButton +{ + Q_OBJECT + +public: + CloseButton(QWidget* parent = 0); + + QSize sizeHint() const; + QSize minimumSizeHint() const; + + void enterEvent(QEvent* event); + void leaveEvent(QEvent* event); + void paintEvent(QPaintEvent* event); +}; +#endif // COMBOTABBAR_H diff --git a/src/lib/tools/tabstackedwidget.cpp b/src/lib/tools/tabstackedwidget.cpp new file mode 100644 index 000000000..41e989c6d --- /dev/null +++ b/src/lib/tools/tabstackedwidget.cpp @@ -0,0 +1,303 @@ +/* ============================================================ +* QupZilla - WebKit based browser +* Copyright (C) 2013 S. Razi Alavizadeh +* +* Some code was taken from qtabwidget.cpp +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* ============================================================ */ +#include "tabstackedwidget.h" +#include "combotabbar.h" + +#include +#include +#include +#include + +// Note: just some of QTabWidget's methods were implemented + +TabStackedWidget::TabStackedWidget(QWidget* parent) + : QWidget(parent) + , m_stack(0) + , m_tabBar(0) +{ + m_stack = new QStackedWidget(this); + m_mainLayout = new QVBoxLayout; + m_mainLayout->setSpacing(0); + m_mainLayout->setContentsMargins(0, 0, 0, 0); + + m_mainLayout->addWidget(m_stack); + setLayout(m_mainLayout); + + setTabBar(new ComboTabBar); + connect(m_stack, SIGNAL(widgetRemoved(int)), this, SLOT(tabWasRemoved(int))); +} + +TabStackedWidget::~TabStackedWidget() +{ +} + +ComboTabBar* TabStackedWidget::tabBar() +{ + return m_tabBar; +} + +void TabStackedWidget::setTabBar(ComboTabBar* tb) +{ + Q_ASSERT(tb); + + if (tb->parentWidget() != this) { + tb->setParent(this); + tb->show(); + } + + delete m_tabBar; + m_dirtyTabBar = true; + m_tabBar = tb; + setFocusProxy(m_tabBar); + + connect(m_tabBar, SIGNAL(currentChanged(int)), this, SLOT(showTab(int))); + connect(m_tabBar, SIGNAL(tabMoved(int,int)), this, SLOT(tabWasMoved(int,int))); + + if (m_tabBar->tabsClosable()) { + connect(m_tabBar, SIGNAL(tabCloseRequested(int)), this, SIGNAL(tabCloseRequested(int))); + } + + setDocumentMode(m_tabBar->documentMode()); + + m_tabBar->installEventFilter(this); + setUpLayout(); +} + +bool TabStackedWidget::isValid(int index) +{ + return (index < m_stack->count() && index >= 0); +} + +void TabStackedWidget::tabWasRemoved(int index) +{ + m_tabBar->removeTab(index); +} + +void TabStackedWidget::tabWasMoved(int from, int to) +{ + m_stack->blockSignals(true); + QWidget* w = m_stack->widget(from); + m_stack->removeWidget(w); + m_stack->insertWidget(to, w); + m_stack->setCurrentIndex(currentIndex()); + m_stack->blockSignals(false); +} + +void TabStackedWidget::setUpLayout() +{ + if (!m_tabBar->isVisible()) { + m_dirtyTabBar = true; + return; + } + + m_tabBar->setElideMode(m_tabBar->elideMode()); + m_dirtyTabBar = false; +} + +bool TabStackedWidget::eventFilter(QObject* obj, QEvent* event) +{ + if (m_dirtyTabBar && obj == m_tabBar && event->type() == QEvent::Show) { + setUpLayout(); + } + + return false; +} + +void TabStackedWidget::keyPressEvent(QKeyEvent* event) +{ + if (((event->key() == Qt::Key_Tab || event->key() == Qt::Key_Backtab) && + count() > 1 && event->modifiers() & Qt::ControlModifier) +#ifdef QT_KEYPAD_NAVIGATION + || QApplication::keypadNavigationEnabled() && (event->key() == Qt::Key_Left || event->key() == Qt::Key_Right) && count() > 1 +#endif + ) { + int pageCount = count(); + int page = currentIndex(); + int dx = (event->key() == Qt::Key_Backtab || event->modifiers() & Qt::ShiftModifier) ? -1 : 1; +#ifdef QT_KEYPAD_NAVIGATION + if (QApplication::keypadNavigationEnabled() && (event->key() == Qt::Key_Left || event->key() == Qt::Key_Right)) { + dx = event->key() == (isRightToLeft() ? Qt::Key_Right : Qt::Key_Left) ? -1 : 1; + } +#endif + for (int pass = 0; pass < pageCount; ++pass) { + page += dx; + if (page < 0 +#ifdef QT_KEYPAD_NAVIGATION + && !event->isAutoRepeat() +#endif + ) { + page = count() - 1; + } + else if (page >= pageCount +#ifdef QT_KEYPAD_NAVIGATION + && !event->isAutoRepeat() +#endif + ) { + page = 0; + } + if (m_tabBar->isTabEnabled(page)) { + setCurrentIndex(page); + break; + } + } + if (!QApplication::focusWidget()) { + m_tabBar->setFocus(); + } + } + else { + event->ignore(); + } +} + +void TabStackedWidget::showTab(int index) +{ + if (isValid(index)) { + m_stack->setCurrentIndex(index); + } + + emit currentChanged(index); +} + +bool TabStackedWidget::documentMode() const +{ + return m_tabBar->documentMode(); +} + +void TabStackedWidget::setDocumentMode(bool enabled) +{ + m_tabBar->setDocumentMode(enabled); + m_tabBar->setExpanding(!enabled); + m_tabBar->setDrawBase(enabled); +} + +int TabStackedWidget::addTab(QWidget* widget, const QString &label, bool pinned) +{ + return insertTab(-1, widget, label, pinned); +} + +int TabStackedWidget::insertTab(int index, QWidget* w, const QString &label, bool pinned) +{ + if (!w) { + return -1; + } + + if (pinned) { + index = index < 0 ? m_tabBar->pinnedTabsCount() : qMin(index, m_tabBar->pinnedTabsCount()); + index = m_stack->insertWidget(index, w); + m_tabBar->insertTab(index, QIcon(), label, true); + } + else { + index = index < 0 ? -1 : qMax(index, m_tabBar->pinnedTabsCount()); + index = m_stack->insertWidget(index, w); + m_tabBar->insertTab(index, QIcon(), label, false); + } + + return index; +} + +QString TabStackedWidget::tabText(int index) const +{ + return m_tabBar->tabText(index); +} + +void TabStackedWidget::setTabText(int index, const QString &label) +{ + m_tabBar->setTabText(index, label); +} + +QString TabStackedWidget::tabToolTip(int index) const +{ + return m_tabBar->tabToolTip(index); +} + +void TabStackedWidget::setTabToolTip(int index, const QString &tip) +{ + m_tabBar->setTabToolTip(index, tip); +} + +int TabStackedWidget::pinUnPinTab(int index, const QString &title) +{ + int newIndex = -1; + if (QWidget* w = m_stack->widget(index)) { + QWidget* button = m_tabBar->tabButton(index, m_tabBar->iconButtonPosition()); + m_tabBar->setTabButton(index, m_tabBar->iconButtonPosition(), 0); + if (index < m_tabBar->pinnedTabsCount()) { + // Unpin + // fix selecting and loading a tab after removing the tab that contains 'w' + // by blocking ComboTabBar::currentChanged() + m_tabBar->blockSignals(true); + m_stack->removeWidget(w); + m_tabBar->blockSignals(false); + newIndex = insertTab(m_tabBar->pinnedTabsCount(), w, title, false); + } + else { + // Pin // same as above + m_tabBar->blockSignals(true); + m_stack->removeWidget(w); + m_tabBar->blockSignals(false); + newIndex = insertTab(0, w, QString(), true); + } + m_tabBar->setTabButton(newIndex, m_tabBar->iconButtonPosition(), button); + } + + return newIndex; +} + +void TabStackedWidget::removeTab(int index) +{ + if (QWidget* w = m_stack->widget(index)) { + m_stack->removeWidget(w); + } +} + +int TabStackedWidget::currentIndex() const +{ + return m_tabBar->currentIndex(); +} + +void TabStackedWidget::setCurrentIndex(int index) +{ + m_tabBar->setCurrentIndex(index); +} + +QWidget* TabStackedWidget::currentWidget() const +{ + return m_stack->currentWidget(); +} + +void TabStackedWidget::setCurrentWidget(QWidget* widget) +{ + m_tabBar->setCurrentIndex(indexOf(widget)); +} + +QWidget* TabStackedWidget::widget(int index) const +{ + return m_stack->widget(index); +} + +int TabStackedWidget::indexOf(QWidget* widget) const +{ + return m_stack->indexOf(widget); +} + +int TabStackedWidget::count() const +{ + return m_tabBar->count(); +} diff --git a/src/lib/tools/tabstackedwidget.h b/src/lib/tools/tabstackedwidget.h new file mode 100644 index 000000000..6f599ea33 --- /dev/null +++ b/src/lib/tools/tabstackedwidget.h @@ -0,0 +1,91 @@ +/* ============================================================ +* QupZilla - WebKit based browser +* Copyright (C) 2013 S. Razi Alavizadeh +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* ============================================================ */ +#ifndef TABSTACKEDWIDGET_H +#define TABSTACKEDWIDGET_H + +#include "qz_namespace.h" + +#include + +class ComboTabBar; + +class QStackedWidget; +class QVBoxLayout; + + +class QT_QUPZILLA_EXPORT TabStackedWidget : public QWidget +{ + Q_OBJECT +public: + explicit TabStackedWidget(QWidget* parent = 0); + ~TabStackedWidget(); + + ComboTabBar* tabBar(); + void setTabBar(ComboTabBar* tb); + + bool isValid(int index); + + bool documentMode() const; + void setDocumentMode(bool enabled); + + int addTab(QWidget* widget, const QString &label, bool pinned = false); + int insertTab(int index, QWidget* widget, const QString &label, bool pinned = false); + + QString tabText(int index) const; + void setTabText(int index, const QString &label); + + QString tabToolTip(int index) const; + void setTabToolTip(int index, const QString &tip); + + int pinUnPinTab(int index, const QString &title = QString()); + + void removeTab(int index); + + void setUpLayout(); + + int currentIndex() const; + QWidget* currentWidget() const; + QWidget* widget(int index) const; + int indexOf(QWidget* widget) const; + int count() const; + +public slots: + void setCurrentIndex(int index); + void setCurrentWidget(QWidget* widget); + +private: + QStackedWidget* m_stack; + ComboTabBar* m_tabBar; + QVBoxLayout* m_mainLayout; + bool m_dirtyTabBar; + +private slots: + void tabWasRemoved(int index); + void showTab(int index); + void tabWasMoved(int from, int to); + +protected: + bool eventFilter(QObject* obj, QEvent* event); + void keyPressEvent(QKeyEvent* event); + +signals: + void currentChanged(int index); + void tabCloseRequested(int index); +}; + +#endif // TABSTACKEDWIDGET_H diff --git a/src/lib/tools/toolbutton.cpp b/src/lib/tools/toolbutton.cpp index ac7cc0cbc..4d6a12fb1 100644 --- a/src/lib/tools/toolbutton.cpp +++ b/src/lib/tools/toolbutton.cpp @@ -27,6 +27,7 @@ ToolButton::ToolButton(QWidget* parent) : QToolButton(parent) , m_usingMultiIcon(false) , m_showMenuInside(false) + , m_forceHidden(false) { setMinimumWidth(16); } @@ -66,6 +67,24 @@ bool ToolButton::showMenuInside() const return m_showMenuInside; } +void ToolButton::setVisible(bool visible) +{ + QToolButton::setVisible(!m_forceHidden && visible); +} + +bool ToolButton::isForceHidden() +{ + return m_forceHidden; +} + +void ToolButton::setForceHidden(bool enable) +{ + m_forceHidden = enable; + if (m_forceHidden) { + hide(); + } +} + void ToolButton::setData(const QVariant &data) { m_data = data; diff --git a/src/lib/tools/toolbutton.h b/src/lib/tools/toolbutton.h index 71262aa21..8ae181fb3 100644 --- a/src/lib/tools/toolbutton.h +++ b/src/lib/tools/toolbutton.h @@ -53,6 +53,10 @@ public: void setShowMenuInside(bool inside); bool showMenuInside() const; + virtual void setVisible(bool visible); + bool isForceHidden(); + void setForceHidden(bool enable); + signals: void middleMouseClicked(); void controlClicked(); @@ -77,6 +81,7 @@ private: QString m_themeIcon; QVariant m_data; + bool m_forceHidden; }; #endif // TOOLBUTTON_H diff --git a/src/lib/webview/tabbar.cpp b/src/lib/webview/tabbar.cpp index eb1d0c4b7..16347d788 100644 --- a/src/lib/webview/tabbar.cpp +++ b/src/lib/webview/tabbar.cpp @@ -36,18 +36,21 @@ #include #include #include +#include +#include +#include #define MAXIMUM_TAB_WIDTH 250 #define MINIMUM_TAB_WIDTH 125 +#define OVERFLOWED_TAB_WIDTH 100 TabBar::TabBar(QupZilla* mainClass, TabWidget* tabWidget) - : QTabBar() + : ComboTabBar() , p_QupZilla(mainClass) , m_tabWidget(tabWidget) - , m_tabPreview(new TabPreview(mainClass, tabWidget)) + , m_tabPreview(new TabPreview(mainClass, mainClass)) , m_showTabPreviews(false) , m_clickedTab(0) - , m_pinnedTabsCount(0) , m_normalTabWidth(0) , m_activeTabWidth(0) { @@ -64,13 +67,18 @@ TabBar::TabBar(QupZilla* mainClass, TabWidget* tabWidget) connect(this, SIGNAL(currentChanged(int)), this, SLOT(currentTabChanged(int))); connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenuRequested(QPoint))); - connect(m_tabWidget, SIGNAL(pinnedTabClosed()), this, SLOT(pinnedTabClosed())); - connect(m_tabWidget, SIGNAL(pinnedTabAdded()), this, SLOT(pinnedTabAdded())); m_tabPreviewTimer = new QTimer(this); m_tabPreviewTimer->setInterval(200); m_tabPreviewTimer->setSingleShot(true); connect(m_tabPreviewTimer, SIGNAL(timeout()), m_tabPreview, SLOT(hideAnimated())); + + // ComboTabBar features + setUsesScrollButtons(true); + setCloseButtonsToolTip(QupZilla::tr("Close Tab")); + setMaxVisiblePinnedTab(0); + connect(this, SIGNAL(overFlowChanged(bool)), this, SLOT(overFlowChange(bool))); + connect(this, SIGNAL(scrollBarValueChanged(int)), this, SLOT(hideTabPreview())); } void TabBar::loadSettings() @@ -84,6 +92,8 @@ void TabBar::loadSettings() settings.endGroup(); setSelectionBehaviorOnRemove(activateLastTab ? QTabBar::SelectPreviousTab : QTabBar::SelectRightTab); + + setUpLayout(); } void TabBar::updateVisibilityWithFullscreen(bool visible) @@ -96,7 +106,7 @@ void TabBar::updateVisibilityWithFullscreen(bool visible) visible = !(count() == 1 && m_hideTabBarWithOneTab); } - QTabBar::setVisible(visible); + ComboTabBar::setVisible(visible); if (visible) { setGeometry(m_originalGeometry); @@ -128,7 +138,7 @@ void TabBar::setVisible(bool visible) } hideTabPreview(false); - QTabBar::setVisible(visible); + ComboTabBar::setVisible(visible); } void TabBar::contextMenuRequested(const QPoint &position) @@ -198,7 +208,7 @@ void TabBar::closeAllButCurrent() } } -QSize TabBar::tabSizeHint(int index) const +QSize TabBar::tabSizeHint(int index, bool fast) const { if (!isVisible() || !mApp->proxyStyle()) { // Don't calculate it when tabbar is not visible @@ -211,15 +221,19 @@ QSize TabBar::tabSizeHint(int index) const static int PINNED_TAB_WIDTH = -1; static int MINIMUM_ACTIVE_TAB_WIDTH = -1; - if (PINNED_TAB_WIDTH == -1) { - PINNED_TAB_WIDTH = 16 + mApp->proxyStyle()->pixelMetric(QStyle::PM_TabBarTabHSpace, 0, this); - MINIMUM_ACTIVE_TAB_WIDTH = PINNED_TAB_WIDTH + mApp->proxyStyle()->pixelMetric(QStyle::PM_TabCloseIndicatorWidth, 0, this); - - // We want to be sure buttonAddTab and buttonListTabs can't cover the active tab - MINIMUM_ACTIVE_TAB_WIDTH = qMax(MINIMUM_ACTIVE_TAB_WIDTH, 6 + m_tabWidget->buttonListTabs()->width() + m_tabWidget->buttonAddTab()->width()); + if (comboTabBarPixelMetric(ComboTabBar::PinnedTabWidth) > 0) { + PINNED_TAB_WIDTH = comboTabBarPixelMetric(ComboTabBar::PinnedTabWidth); + MINIMUM_ACTIVE_TAB_WIDTH = comboTabBarPixelMetric(ComboTabBar::ActiveTabMinimumWidth); + } + + QSize size = ComboTabBar::tabSizeHint(index); + + // The overflowed tabs have similar size and we can use this fast method + if (fast) { + size.setWidth(index >= pinnedTabsCount() ? OVERFLOWED_TAB_WIDTH : PINNED_TAB_WIDTH); + return size; } - QSize size = QTabBar::tabSizeHint(index); WebTab* webTab = qobject_cast(m_tabWidget->widget(index)); TabBar* tabBar = const_cast (this); @@ -227,12 +241,16 @@ QSize TabBar::tabSizeHint(int index) const size.setWidth(PINNED_TAB_WIDTH); } else { - int availableWidth = width() - (PINNED_TAB_WIDTH * m_pinnedTabsCount) - m_tabWidget->buttonListTabs()->width() - m_tabWidget->buttonAddTab()->width(); + int availableWidth = mainTabBarWidth(); + if (!m_tabWidget->buttonListTabs()->isForceHidden()) { + availableWidth -= comboTabBarPixelMetric(ExtraReservedWidth); + } + if (availableWidth < 0) { return QSize(-1, -1); } - int normalTabsCount = count() - m_pinnedTabsCount; + const int normalTabsCount = ComboTabBar::normalTabsCount(); if (availableWidth >= MAXIMUM_TAB_WIDTH * normalTabsCount) { m_normalTabWidth = MAXIMUM_TAB_WIDTH; size.setWidth(m_normalTabWidth); @@ -257,7 +275,7 @@ QSize TabBar::tabSizeHint(int index) const if (maxWidthForTab < PINNED_TAB_WIDTH) { // FIXME: It overflows now m_normalTabWidth = PINNED_TAB_WIDTH; - if (index == currentIndex()) { + if (index == mainTabBarCurrentIndex()) { size.setWidth(realTabWidth); } else { @@ -294,7 +312,7 @@ QSize TabBar::tabSizeHint(int index) const m_normalTabWidth = maxWidthForTab; // Fill any empty space (we've got from rounding) with active tab - if (index == currentIndex()) { + if (index == mainTabBarCurrentIndex()) { if (adjustingActiveTab) { m_activeTabWidth = (availableWidth - MINIMUM_ACTIVE_TAB_WIDTH - maxWidthForTab * (normalTabsCount - 1)) + realTabWidth; @@ -312,10 +330,10 @@ QSize TabBar::tabSizeHint(int index) const } if (index == count() - 1) { - WebTab* currentTab = qobject_cast(m_tabWidget->widget(currentIndex())); - int xForAddTabButton = (PINNED_TAB_WIDTH * m_pinnedTabsCount) + (count() - m_pinnedTabsCount) * (m_normalTabWidth); + WebTab* lastMainActiveTab = qobject_cast(m_tabWidget->widget(mainTabBarCurrentIndex())); + int xForAddTabButton = pinTabBarWidth() + normalTabsCount() * m_normalTabWidth; - if (currentTab && !currentTab->isPinned() && m_activeTabWidth > m_normalTabWidth) { + if (lastMainActiveTab && m_activeTabWidth > m_normalTabWidth) { xForAddTabButton += m_activeTabWidth - m_normalTabWidth; } @@ -330,6 +348,47 @@ QSize TabBar::tabSizeHint(int index) const return size; } +int TabBar::comboTabBarPixelMetric(ComboTabBar::SizeType sizeType) const +{ + if (!mApp->proxyStyle() || !isVisible()) { + return -1; + } + + static int PINNED_TAB_WIDTH = -1; + static int MINIMUM_ACTIVE_TAB_WIDTH = -1; + + if (PINNED_TAB_WIDTH == -1) { + PINNED_TAB_WIDTH = 16 + mApp->proxyStyle()->pixelMetric(QStyle::PM_TabBarTabHSpace, 0, this); + MINIMUM_ACTIVE_TAB_WIDTH = PINNED_TAB_WIDTH + mApp->proxyStyle()->pixelMetric(QStyle::PM_TabCloseIndicatorWidth, 0, this); + } + + switch (sizeType) { + case ComboTabBar::PinnedTabWidth: + return PINNED_TAB_WIDTH; + break; + case ComboTabBar::ActiveTabMinimumWidth: + return MINIMUM_ACTIVE_TAB_WIDTH; + break; + case ComboTabBar::NormalTabMinimumWidth: + return MINIMUM_TAB_WIDTH; + break; + case ComboTabBar::NormalTabMaximumWidth: + return MAXIMUM_TAB_WIDTH; + break; + case ComboTabBar::OverflowedTabWidth: + return OVERFLOWED_TAB_WIDTH; + break; + case ComboTabBar::ExtraReservedWidth: + return m_tabWidget->buttonListTabs()->width() + + m_tabWidget->buttonAddTab()->width(); + break; + default: + break; + } + + return -1; +} + void TabBar::showCloseButton(int index) { if (!validIndex(index)) { @@ -343,9 +402,7 @@ void TabBar::showCloseButton(int index) return; } - QAbstractButton* closeButton = new CloseButton(this); - connect(closeButton, SIGNAL(clicked()), this, SLOT(closeTabFromButton())); - setTabButton(index, closeButtonPosition(), closeButton); + insertCloseButton(index); } void TabBar::hideCloseButton(int index) @@ -423,6 +480,8 @@ void TabBar::currentTabChanged(int index) showCloseButton(index); hideCloseButton(m_tabWidget->lastTabIndex()); + ensureVisible(index); + m_tabWidget->currentTabChanged(index); } @@ -447,13 +506,6 @@ void TabBar::pinTab() webTab->pinTab(m_clickedTab); - if (webTab->isPinned()) { - m_pinnedTabsCount++; - } - else { - m_pinnedTabsCount--; - } - // We need to recalculate size of all tabs and repaint tabbar // Unfortunately, Qt doesn't offer refresh() function as a public API @@ -461,36 +513,6 @@ void TabBar::pinTab() setElideMode(elideMode()); } -void TabBar::pinnedTabClosed() -{ - m_pinnedTabsCount--; -} - -void TabBar::pinnedTabAdded() -{ - m_pinnedTabsCount++; -} - -int TabBar::pinnedTabsCount() -{ - return m_pinnedTabsCount; -} - -int TabBar::normalTabsCount() -{ - return count() - m_pinnedTabsCount; -} - -QTabBar::ButtonPosition TabBar::iconButtonPosition() -{ - return (closeButtonPosition() == QTabBar::RightSide ? QTabBar::LeftSide : QTabBar::RightSide); -} - -QTabBar::ButtonPosition TabBar::closeButtonPosition() -{ - return (QTabBar::ButtonPosition)style()->styleHint(QStyle::SH_TabBar_CloseButtonPosition, 0, this); -} - void TabBar::overrideTabTextColor(int index, QColor color) { if (!m_originalTabTextColor.isValid()) { @@ -514,7 +536,11 @@ void TabBar::showTabPreview() m_tabPreviewTimer->stop(); m_tabPreview->setWebTab(webTab, m_tabPreview->previewIndex() == currentIndex()); - m_tabPreview->showOnRect(tabRect(m_tabPreview->previewIndex())); + QRect r(tabRect(m_tabPreview->previewIndex())); + r.setTopLeft(mapTo(p_QupZilla, r.topLeft())); + r.setBottomRight(mapTo(p_QupZilla, r.bottomRight())); + + m_tabPreview->showOnRect(r); } void TabBar::hideTabPreview(bool delayed) @@ -527,6 +553,28 @@ void TabBar::hideTabPreview(bool delayed) } } +void TabBar::overFlowChange(bool overFlowed) +{ + if (overFlowed) { + m_tabWidget->buttonAddTab()->setForceHidden(true); + m_tabWidget->buttonListTabs()->setForceHidden(true); + + // Restore close buttons according to preferences + if (OVERFLOWED_TAB_WIDTH >= MINIMUM_TAB_WIDTH && !tabsClosable()) { + setTabsClosable(true); + } + + m_tabWidget->setUpLayout(); + ensureVisible(currentIndex()); + } + else { + m_tabWidget->buttonAddTab()->setForceHidden(false); + m_tabWidget->buttonListTabs()->setForceHidden(false); + m_tabWidget->showButtons(); + m_tabWidget->setUpLayout(); + } +} + void TabBar::tabInserted(int index) { Q_UNUSED(index) @@ -538,7 +586,6 @@ void TabBar::tabRemoved(int index) { Q_UNUSED(index) - m_tabWidget->showNavigationBar(p_QupZilla->navigationContainer()); showCloseButton(currentIndex()); setVisible(!(count() == 1 && m_hideTabBarWithOneTab)); } @@ -554,7 +601,7 @@ void TabBar::mouseDoubleClickEvent(QMouseEvent* event) return; } - QTabBar::mouseDoubleClickEvent(event); + ComboTabBar::mouseDoubleClickEvent(event); } void TabBar::mousePressEvent(QMouseEvent* event) @@ -572,7 +619,7 @@ void TabBar::mousePressEvent(QMouseEvent* event) m_dragStartPosition = QPoint(); } - QTabBar::mousePressEvent(event); + ComboTabBar::mousePressEvent(event); } void TabBar::mouseMoveEvent(QMouseEvent* event) @@ -600,7 +647,7 @@ void TabBar::mouseMoveEvent(QMouseEvent* event) } } - QTabBar::mouseMoveEvent(event); + ComboTabBar::mouseMoveEvent(event); } void TabBar::mouseReleaseEvent(QMouseEvent* event) @@ -616,7 +663,7 @@ void TabBar::mouseReleaseEvent(QMouseEvent* event) } if (!rect().contains(event->pos())) { - QTabBar::mouseReleaseEvent(event); + ComboTabBar::mouseReleaseEvent(event); return; } @@ -630,7 +677,7 @@ void TabBar::mouseReleaseEvent(QMouseEvent* event) return; } - QTabBar::mouseReleaseEvent(event); + ComboTabBar::mouseReleaseEvent(event); } bool TabBar::event(QEvent* event) @@ -654,7 +701,23 @@ bool TabBar::event(QEvent* event) break; } - return QTabBar::event(event); + return ComboTabBar::event(event); +} + +void TabBar::resizeEvent(QResizeEvent* e) +{ + QPoint posit; + posit.setY(0); + + if (isRightToLeft()) { + posit.setX(0); + } + else { + posit.setX(width() - m_tabWidget->buttonListTabs()->width()); + } + m_tabWidget->buttonListTabs()->move(posit); + + ComboTabBar::resizeEvent(e); } void TabBar::wheelEvent(QWheelEvent* event) @@ -663,7 +726,7 @@ void TabBar::wheelEvent(QWheelEvent* event) return; } - QTabBar::wheelEvent(event); + ComboTabBar::wheelEvent(event); } void TabBar::dragEnterEvent(QDragEnterEvent* event) @@ -675,7 +738,7 @@ void TabBar::dragEnterEvent(QDragEnterEvent* event) return; } - QTabBar::dragEnterEvent(event); + ComboTabBar::dragEnterEvent(event); } void TabBar::dropEvent(QDropEvent* event) @@ -683,7 +746,7 @@ void TabBar::dropEvent(QDropEvent* event) const QMimeData* mime = event->mimeData(); if (!mime->hasUrls()) { - QTabBar::dropEvent(event); + ComboTabBar::dropEvent(event); return; } @@ -705,71 +768,3 @@ void TabBar::disconnectObjects() { disconnect(this); } - -CloseButton::CloseButton(QWidget* parent) - : QAbstractButton(parent) -{ - setFocusPolicy(Qt::NoFocus); - setCursor(Qt::ArrowCursor); - setToolTip(QupZilla::tr("Close Tab")); - - resize(sizeHint()); -} - -QSize CloseButton::sizeHint() const -{ - ensurePolished(); - int width = style()->pixelMetric(QStyle::PM_TabCloseIndicatorWidth, 0, this); - int height = style()->pixelMetric(QStyle::PM_TabCloseIndicatorHeight, 0, this); - return QSize(width, height); -} - -QSize CloseButton::minimumSizeHint() const -{ - return sizeHint(); -} - -void CloseButton::enterEvent(QEvent* event) -{ - if (isEnabled()) { - update(); - } - - QAbstractButton::enterEvent(event); -} - -void CloseButton::leaveEvent(QEvent* event) -{ - if (isEnabled()) { - update(); - } - - QAbstractButton::leaveEvent(event); -} - -void CloseButton::paintEvent(QPaintEvent*) -{ - QPainter p(this); - QStyleOption opt; - opt.init(this); - opt.state |= QStyle::State_AutoRaise; - - if (isEnabled() && underMouse() && !isChecked() && !isDown()) { - opt.state |= QStyle::State_Raised; - } - if (isChecked()) { - opt.state |= QStyle::State_On; - } - if (isDown()) { - opt.state |= QStyle::State_Sunken; - } - - if (TabBar* tb = qobject_cast(parent())) { - int index = tb->currentIndex(); - if (tb->tabButton(index, tb->closeButtonPosition()) == this) { - opt.state |= QStyle::State_Selected; - } - } - - style()->drawPrimitive(QStyle::PE_IndicatorTabClose, &opt, &p, this); -} diff --git a/src/lib/webview/tabbar.h b/src/lib/webview/tabbar.h index 88c9d5595..66b8d60fb 100644 --- a/src/lib/webview/tabbar.h +++ b/src/lib/webview/tabbar.h @@ -18,9 +18,9 @@ #ifndef TABBAR_H #define TABBAR_H -#include +#include "combotabbar.h" + #include -#include #include "qz_namespace.h" @@ -28,7 +28,7 @@ class QupZilla; class TabWidget; class TabPreview; -class QT_QUPZILLA_EXPORT TabBar : public QTabBar +class QT_QUPZILLA_EXPORT TabBar : public ComboTabBar { Q_OBJECT public: @@ -39,12 +39,6 @@ public: void setVisible(bool visible); void updateVisibilityWithFullscreen(bool visible); - int pinnedTabsCount(); - int normalTabsCount(); - - QTabBar::ButtonPosition iconButtonPosition(); - QTabBar::ButtonPosition closeButtonPosition(); - void overrideTabTextColor(int index, QColor color); void restoreTabTextColor(int index); @@ -69,8 +63,6 @@ signals: private slots: void currentTabChanged(int index); - void pinnedTabAdded(); - void pinnedTabClosed(); void contextMenuRequested(const QPoint &position); void reloadTab() { emit reloadTab(m_clickedTab); } @@ -88,6 +80,8 @@ private slots: void showTabPreview(); void hideTabPreview(bool delayed = true); + void overFlowChange(bool overFlowed); + private: inline bool validIndex(int index) const { return index >= 0 && index < count(); } @@ -102,11 +96,13 @@ private: void mouseMoveEvent(QMouseEvent* event); void mouseReleaseEvent(QMouseEvent* event); bool event(QEvent* event); + void resizeEvent(QResizeEvent* e); void dragEnterEvent(QDragEnterEvent* event); void dropEvent(QDropEvent* event); - QSize tabSizeHint(int index) const; + QSize tabSizeHint(int index, bool fast) const; + int comboTabBarPixelMetric(ComboTabBar::SizeType sizeType) const; QupZilla* p_QupZilla; TabWidget* m_tabWidget; @@ -117,7 +113,6 @@ private: bool m_hideTabBarWithOneTab; int m_clickedTab; - int m_pinnedTabsCount; mutable int m_normalTabWidth; mutable int m_activeTabWidth; @@ -127,21 +122,4 @@ private: QPoint m_dragStartPosition; }; -// Class for close button on tabs -// * taken from qtabbar.cpp -class CloseButton : public QAbstractButton -{ - Q_OBJECT - -public: - CloseButton(QWidget* parent = 0); - - QSize sizeHint() const; - QSize minimumSizeHint() const; - - void enterEvent(QEvent* event); - void leaveEvent(QEvent* event); - void paintEvent(QPaintEvent* event); -}; - #endif // TABBAR_H diff --git a/src/lib/webview/tabwidget.cpp b/src/lib/webview/tabwidget.cpp index 841923ba0..4ad92336c 100644 --- a/src/lib/webview/tabwidget.cpp +++ b/src/lib/webview/tabwidget.cpp @@ -41,9 +41,10 @@ #include #include #include +#include AddTabButton::AddTabButton(TabWidget* tabWidget, TabBar* tabBar) - : ToolButton(tabWidget) + : ToolButton(tabBar) , m_tabBar(tabBar) , m_tabWidget(tabWidget) { @@ -111,7 +112,7 @@ void MenuTabs::mouseReleaseEvent(QMouseEvent* event) } TabWidget::TabWidget(QupZilla* mainClass, QWidget* parent) - : QTabWidget(parent) + : TabStackedWidget(parent) , p_QupZilla(mainClass) , m_lastTabIndex(-1) , m_lastBackgroundTabIndex(-1) @@ -139,9 +140,9 @@ TabWidget::TabWidget(QupZilla* mainClass, QWidget* parent) connect(m_tabBar, SIGNAL(showButtons()), this, SLOT(showButtons())); connect(m_tabBar, SIGNAL(hideButtons()), this, SLOT(hideButtons())); - m_buttonListTabs = new ToolButton(this); + m_buttonListTabs = new ToolButton(m_tabBar); m_buttonListTabs->setObjectName("tabwidget-button-opentabs"); - m_menuTabs = new MenuTabs(this); + m_menuTabs = new MenuTabs(m_tabBar); m_buttonListTabs->setMenu(m_menuTabs); m_buttonListTabs->setPopupMode(QToolButton::InstantPopup); m_buttonListTabs->setToolTip(tr("List of tabs")); @@ -153,6 +154,22 @@ TabWidget::TabWidget(QupZilla* mainClass, QWidget* parent) connect(m_buttonAddTab, SIGNAL(clicked()), p_QupZilla, SLOT(addTab())); connect(m_menuTabs, SIGNAL(aboutToShow()), this, SLOT(aboutToShowClosedTabsMenu())); + // copy of buttons + m_buttonListTabs2 = new ToolButton(m_tabBar); + m_buttonListTabs2->setObjectName("tabwidget-button-opentabs"); + m_buttonListTabs2->setMenu(m_menuTabs); + m_buttonListTabs2->setPopupMode(QToolButton::InstantPopup); + m_buttonListTabs2->setToolTip(tr("List of tabs")); + m_buttonListTabs2->setAutoRaise(true); + m_buttonListTabs2->setFocusPolicy(Qt::NoFocus); + m_buttonAddTab2 = new AddTabButton(this, m_tabBar); + connect(m_buttonAddTab2, SIGNAL(clicked()), p_QupZilla, SLOT(addTab())); + m_tabBar->addMainBarWidget(m_buttonAddTab2, Qt::AlignRight); + m_tabBar->addMainBarWidget(m_buttonListTabs2, Qt::AlignRight); + m_buttonAddTab2->hide(); + m_buttonListTabs2->hide(); + connect(m_tabBar, SIGNAL(overFlowChanged(bool)), this, SLOT(tabBarOverFlowChanged(bool))); + loadSettings(); } @@ -180,22 +197,6 @@ void TabWidget::loadSettings() } } -void TabWidget::resizeEvent(QResizeEvent* e) -{ - QPoint posit; - posit.setY(0); - //RTL Support - if (QApplication::layoutDirection() == Qt::RightToLeft) { - posit.setX(0); - } - else { - posit.setX(width() - m_buttonListTabs->width()); - } - m_buttonListTabs->move(posit); - - QTabWidget::resizeEvent(e); -} - WebTab* TabWidget::weTab() { return weTab(currentIndex()); @@ -218,6 +219,12 @@ void TabWidget::hideButtons() m_buttonAddTab->hide(); } +void TabWidget::tabBarOverFlowChanged(bool overFlowed) +{ + m_buttonAddTab2->setVisible(overFlowed); + m_buttonListTabs2->setVisible(overFlowed); +} + void TabWidget::moveAddTabButton(int posX) { int posY = (m_tabBar->height() - m_buttonAddTab->height()) / 2; @@ -282,27 +289,30 @@ void TabWidget::actionChangeIndex() if (QAction* action = qobject_cast(sender())) { WebTab* tab = qobject_cast(qvariant_cast(action->data())); if (tab) { + // needed when clicking on action of the current tab + m_tabBar->ensureVisible(tab->tabIndex()); + setCurrentIndex(tab->tabIndex()); } } } -int TabWidget::addView(const QUrl &url, const Qz::NewTabPositionFlags &openFlags, bool selectLine) +int TabWidget::addView(const QUrl &url, const Qz::NewTabPositionFlags &openFlags, bool selectLine, bool pinned) { - return addView(QNetworkRequest(url), openFlags, selectLine); + return addView(QNetworkRequest(url), openFlags, selectLine, pinned); } -int TabWidget::addView(const QNetworkRequest &req, const Qz::NewTabPositionFlags &openFlags, bool selectLine) +int TabWidget::addView(const QNetworkRequest &req, const Qz::NewTabPositionFlags &openFlags, bool selectLine, bool pinned) { - return addView(req, tr("New tab"), openFlags, selectLine); + return addView(req, tr("New tab"), openFlags, selectLine, -1, pinned); } -int TabWidget::addView(const QUrl &url, const QString &title, const Qz::NewTabPositionFlags &openFlags, bool selectLine, int position) +int TabWidget::addView(const QUrl &url, const QString &title, const Qz::NewTabPositionFlags &openFlags, bool selectLine, int position, bool pinned) { - return addView(QNetworkRequest(url), title, openFlags, selectLine, position); + return addView(QNetworkRequest(url), title, openFlags, selectLine, position, pinned); } -int TabWidget::addView(QNetworkRequest req, const QString &title, const Qz::NewTabPositionFlags &openFlags, bool selectLine, int position) +int TabWidget::addView(QNetworkRequest req, const QString &title, const Qz::NewTabPositionFlags &openFlags, bool selectLine, int position, bool pinned) { #ifdef Q_OS_WIN if (p_QupZilla->isTransparentBackgroundAllowed()) { @@ -338,10 +348,10 @@ int TabWidget::addView(QNetworkRequest req, const QString &title, const Qz::NewT int index; if (position == -1) { - index = addTab(new WebTab(p_QupZilla, locBar), QString()); + index = addTab(new WebTab(p_QupZilla, locBar), QString(), pinned); } else { - index = insertTab(position, new WebTab(p_QupZilla, locBar), QString()); + index = insertTab(position, new WebTab(p_QupZilla, locBar), QString(), pinned); } TabbedWebView* webView = weTab(index)->view(); @@ -453,10 +463,6 @@ void TabWidget::closeTab(int index, bool force) } } - if (webTab->isPinned()) { - emit pinnedTabClosed(); - } - m_locationBars->removeWidget(webView->webTab()->locationBar()); disconnect(webView, SIGNAL(wantsCloseTab(int)), this, SLOT(closeTab(int))); disconnect(webView, SIGNAL(changed()), mApp, SLOT(setStateChanged())); @@ -502,7 +508,6 @@ void TabWidget::currentTabChanged(int index) webTab->setCurrentTab(); p_QupZilla->currentTabChanged(); - showNavigationBar(p_QupZilla->navigationContainer()); } void TabWidget::tabMoved(int before, int after) @@ -556,7 +561,7 @@ void TabWidget::setCurrentIndex(int index) { m_lastTabIndex = currentIndex(); - QTabWidget::setCurrentIndex(index); + TabStackedWidget::setCurrentIndex(index); } void TabWidget::setTabIcon(int index, const QIcon &icon) @@ -592,7 +597,7 @@ void TabWidget::setTabText(int index, const QString &text) } setTabToolTip(index, text); - QTabWidget::setTabText(index, newtext); + TabStackedWidget::setTabText(index, newtext); } void TabWidget::nextTab() @@ -631,15 +636,6 @@ int TabWidget::lastTabIndex() const return m_lastTabIndex; } -void TabWidget::showNavigationBar(QWidget* bar) -{ - WebTab* tab = weTab(); - - if (tab) { - tab->showNavigationBar(bar); - } -} - TabBar* TabWidget::getTabBar() const { return m_tabBar; @@ -904,19 +900,18 @@ void TabWidget::restorePinnedTabs() int addedIndex; if (!historyState.isEmpty()) { - addedIndex = addView(QUrl(), Qz::NT_CleanSelectedTab); + addedIndex = addView(QUrl(), Qz::NT_CleanSelectedTab, false, true); weTab(addedIndex)->p_restoreTab(url, historyState); } else { - addedIndex = addView(url); + addedIndex = addView(url, tr("New tab"), Qz::NT_SelectedTab, false, -1, true); } WebTab* webTab = weTab(addedIndex); if (webTab) { webTab->setPinned(true); - emit pinnedTabAdded(); } m_tabBar->updatePinnedTabCloseButton(addedIndex); diff --git a/src/lib/webview/tabwidget.h b/src/lib/webview/tabwidget.h index e5a615a9c..faff40360 100644 --- a/src/lib/webview/tabwidget.h +++ b/src/lib/webview/tabwidget.h @@ -23,6 +23,7 @@ #include #include +#include "tabstackedwidget.h" #include "toolbutton.h" #include "qz_namespace.h" #include "webtab.h" @@ -64,7 +65,7 @@ private: void mouseReleaseEvent(QMouseEvent* event); }; -class QT_QUPZILLA_EXPORT TabWidget : public QTabWidget +class QT_QUPZILLA_EXPORT TabWidget : public TabStackedWidget { Q_OBJECT public: @@ -95,8 +96,6 @@ public: int pinnedTabsCount() const; int lastTabIndex() const; - void showNavigationBar(QWidget* bar); - TabBar* getTabBar() const; ClosedTabsManager* closedTabsManager() const; QList allTabs(bool withPinned = true); @@ -108,16 +107,12 @@ public: void disconnectObjects(); -signals: - void pinnedTabClosed(); - void pinnedTabAdded(); - public slots: - int addView(const QUrl &url, const Qz::NewTabPositionFlags &openFlags, bool selectLine = false); - int addView(const QNetworkRequest &req, const Qz::NewTabPositionFlags &openFlags, bool selectLine = false); + int addView(const QUrl &url, const Qz::NewTabPositionFlags &openFlags, bool selectLine = false, bool pinned = false); + int addView(const QNetworkRequest &req, const Qz::NewTabPositionFlags &openFlags, bool selectLine = false, bool pinned = false); - int addView(const QUrl &url, const QString &title = tr("New tab"), const Qz::NewTabPositionFlags &openFlags = Qz::NT_SelectedTab, bool selectLine = false, int position = -1); - int addView(QNetworkRequest req, const QString &title = tr("New tab"), const Qz::NewTabPositionFlags &openFlags = Qz::NT_SelectedTab, bool selectLine = false, int position = -1); + int addView(const QUrl &url, const QString &title = tr("New tab"), const Qz::NewTabPositionFlags &openFlags = Qz::NT_SelectedTab, bool selectLine = false, int position = -1, bool pinned = false); + int addView(QNetworkRequest req, const QString &title = tr("New tab"), const Qz::NewTabPositionFlags &openFlags = Qz::NT_SelectedTab, bool selectLine = false, int position = -1, bool pinned = false); int addView(WebTab* tab); void addTabFromClipboard(); @@ -138,6 +133,8 @@ public slots: void showButtons(); void hideButtons(); + void tabBarOverFlowChanged(bool overFlowed); + private slots: void aboutToShowTabsMenu(); void actionChangeIndex(); @@ -149,8 +146,6 @@ private: inline bool validIndex(int index) const { return index >= 0 && index < count(); } - void resizeEvent(QResizeEvent* e); - bool m_dontQuitWithOneTab; bool m_closedInsteadOpened; bool m_newTabAfterActive; @@ -168,6 +163,8 @@ private: MenuTabs* m_menuTabs; ToolButton* m_buttonListTabs; AddTabButton* m_buttonAddTab; + ToolButton* m_buttonListTabs2; + AddTabButton* m_buttonAddTab2; ClosedTabsManager* m_closedTabsManager; QStackedWidget* m_locationBars; diff --git a/src/lib/webview/webtab.cpp b/src/lib/webview/webtab.cpp index 126de9a72..bd84d7ce4 100644 --- a/src/lib/webview/webtab.cpp +++ b/src/lib/webview/webtab.cpp @@ -180,9 +180,6 @@ void WebTab::moveToWindow(QupZilla* window) { p_QupZilla = window; - hideNavigationBar(); - showNavigationBar(p_QupZilla->navigationContainer()); - m_view->moveToWindow(p_QupZilla); } @@ -343,7 +340,7 @@ QPixmap WebTab::renderTabPreview() void WebTab::showNotification(QWidget* notif) { - const int notifPos = p_QupZilla->tabsOnTop() ? 1 : 0; + const int notifPos = 0; if (m_layout->count() > notifPos + 1) { delete m_layout->itemAt(notifPos)->widget(); @@ -366,24 +363,6 @@ int WebTab::tabIndex() const return m_view->tabIndex(); } -void WebTab::showNavigationBar(QWidget* bar) -{ - if (bar) { - m_navigationContainer = bar; - m_layout->insertWidget(0, m_navigationContainer); - - // Needed to prevent flickering when closing tabs - m_navigationContainer->setUpdatesEnabled(true); - - if (p_QupZilla->isFullScreen()) { - m_navigationContainer->setVisible(p_QupZilla->tabWidget()->getTabBar()->isVisible()); - } - else { - m_navigationContainer->show(); - } - } -} - void WebTab::pinTab(int index) { TabWidget* tabWidget = p_QupZilla->tabWidget(); @@ -391,19 +370,9 @@ void WebTab::pinTab(int index) return; } - if (m_pinned) { //Unpin tab - m_pinned = false; - tabWidget->setTabText(index, m_view->title()); - tabWidget->getTabBar()->updatePinnedTabCloseButton(index); - } - else { // Pin tab - m_pinned = true; - tabWidget->setCurrentIndex(0); // <<-- those 2 lines fixes - tabWidget->getTabBar()->moveTab(index, 0); // | weird behavior with bad - tabWidget->setTabText(0, QString()); // | tabwidget update if we - tabWidget->setCurrentIndex(0); // <<-- are moving current tab - tabWidget->getTabBar()->updatePinnedTabCloseButton(0); - } + m_pinned = !m_pinned; + index = tabWidget->pinUnPinTab(index, m_view->title()); + tabWidget->setCurrentIndex(index); } void WebTab::disconnectObjects() @@ -413,27 +382,7 @@ void WebTab::disconnectObjects() disconnect(m_view); } - -void WebTab::hideNavigationBar() -{ - if (m_navigationContainer && p_QupZilla->tabsOnTop()) { - m_layout->removeWidget(m_navigationContainer); - - // Needed to prevent flickering when closing tabs - m_navigationContainer->setUpdatesEnabled(false); - m_navigationContainer->hide(); - - // Needed to prevent deleting m_navigationContainer in ~QWidget - m_navigationContainer->setParent(p_QupZilla); - } -} - WebTab::~WebTab() { - // #838 !p_QupZilla->isClosing() fixes crash on app close with Oxygen theme - if (!p_QupZilla->isClosing()) { - hideNavigationBar(); - } - delete m_locationBar.data(); } diff --git a/src/lib/webview/webtab.h b/src/lib/webview/webtab.h index 1d5415159..35e6b3985 100644 --- a/src/lib/webview/webtab.h +++ b/src/lib/webview/webtab.h @@ -78,7 +78,6 @@ public: void setPinned(bool state); int tabIndex() const; - void showNavigationBar(QWidget* bar); void setLocationBar(LocationBar* bar); LocationBar* locationBar() const; @@ -103,8 +102,6 @@ private slots: void slotRestore(); private: - void hideNavigationBar(); - QupZilla* p_QupZilla; TabbedWebView* m_view; QVBoxLayout* m_layout;