diff options
Diffstat (limited to 'src/tabbar')
-rw-r--r-- | src/tabbar/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/tabbar/tabbar.cpp | 599 | ||||
-rw-r--r-- | src/tabbar/tabbar.h | 90 |
3 files changed, 691 insertions, 0 deletions
diff --git a/src/tabbar/CMakeLists.txt b/src/tabbar/CMakeLists.txt new file mode 100644 index 00000000..9333ef0a --- /dev/null +++ b/src/tabbar/CMakeLists.txt @@ -0,0 +1,2 @@ +add_library(tabbar STATIC tabbar.cpp tabbar.h) +target_link_libraries(tabbar PUBLIC Qt6::Widgets)
\ No newline at end of file diff --git a/src/tabbar/tabbar.cpp b/src/tabbar/tabbar.cpp new file mode 100644 index 00000000..c8624bda --- /dev/null +++ b/src/tabbar/tabbar.cpp @@ -0,0 +1,599 @@ +/* ============================================================ + * The rekonq project + * ============================================================ + * SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (C) 2008-2012 by Andrea Diamantini <adjam7 at gmail dot com> + * Copyright (C) 2009-2011 by Lionel Chauvin <megabigbug@yahoo.fr> + * SPDX-License-Identifier: GPL-3.0-only + * Copyright (C) 2022 aqua <aqua@iserlohn-fortress.net> + * ============================================================ */ + +#include "tabbar.h" + +static inline QByteArray highlightPropertyName(int index) +{ + return QByteArray("hAnim").append(QByteArray::number(index)); +} + + +// ------------------------------------------------------------------------------------ + +TabBar::TabBar(QWidget *parent) : QTabBar(parent) +//, m_tabHighlightEffect(new TabHighlightEffect(this)) +//, m_animationMapper(new QSignalMapper(this)) +{ + setElideMode(Qt::ElideRight); + + setTabsClosable(true); + setMovable(true); + setAcceptDrops(true); + + connect(this, &QTabBar::tabMoved, this, [this](int from, int to) { m_views.move(from, to); }); + + connect(this, &QTabBar::tabCloseRequested, this, [this](int index) { + removeTab(index); + emit removeView(m_views.at(index)); + m_views.removeAt(index); + }); + + /* + // context menu(s) + setContextMenuPolicy(Qt::CustomContextMenu); + + connect(this, SIGNAL(contextMenu(int,QPoint)), this, SLOT(contextMenu(int,QPoint))); + connect(this, SIGNAL(emptyAreaContextMenu(QPoint)), this, SLOT(emptyAreaContextMenu(QPoint))); + + // Highlight effect + connect(m_animationMapper, SIGNAL(mapped(int)), this, SLOT(removeAnimation(int))); + setGraphicsEffect(m_tabHighlightEffect); + m_tabHighlightEffect->setEnabled(true); + */ +} + +int TabBar::addTab(RekonqView *view) +{ + m_views.append(view); + const auto id = QTabBar::addTab(view->title()); + + connect(view, &RekonqView::titleChanged, this, [this, view](const QString &title) { + const int index = m_views.indexOf(view); + setTabText(index, title); + }); + + return id; +} + +// ------------------------------------------------------------------------------------ + +QSize TabBar::tabSizeHint(int index) const +{ + QWidget* p = qobject_cast<QWidget *>(parent()); + + int w; + // if tab is pinned... + if (tabData(index).toBool()) + { + w = 36; + } + else + { + // available width is tab widget width minus addTabButton width + int tabWidgetWidth = p->size().width() - 24; + w = c_baseTabWidth; + if (w * count() > tabWidgetWidth) + { + w = tabWidgetWidth / count(); + if (w < c_minTabWidth) + { + w = c_minTabWidth; + } + } + } + + int h = size().height(); + + // this because it may happen sometimes (eg: exiting fullscreen) + // that tabbar height is set to ZERO. And this is NOT good... + if (h == 0) h = 30; + + return {w, h}; +} + +// ------------------------------------------------------------------------------------ +// slots +/* +void TabBar::cloneTab() +{ + KAction *a = qobject_cast<KAction *>(sender()); + if (a) + { + int index = a->data().toInt(); + emit cloneTab(index); + } +} + + +void TabBar::closeTab() +{ + KAction *a = qobject_cast<KAction *>(sender()); + if (a) + { + int index = a->data().toInt(); + emit closeTab(index); + } +} + + +void TabBar::closeOtherTabs() +{ + KAction *a = qobject_cast<KAction *>(sender()); + if (a) + { + int index = a->data().toInt(); + emit closeOtherTabs(index); + } +} + + +void TabBar::reloadTab() +{ + KAction *a = qobject_cast<KAction *>(sender()); + if (a) + { + int index = a->data().toInt(); + emit reloadTab(index); + } +} + + +void TabBar::detachTab() +{ + KAction *a = qobject_cast<KAction *>(sender()); + if (a) + { + int index = a->data().toInt(); + emit detachTab(index); + } +} + + +void TabBar::contextMenu(int tabIndex, const QPoint &pos) +{ + TabWidget *w = qobject_cast<TabWidget *>(parent()); + + QAction *a; + + KMenu menu; + + a = w->actionByName(QL1S("new_tab")); + menu.addAction(a); + + menu.addSeparator(); // ---------------------------------------------------------------- + + a = new KAction(KIcon("tab-duplicate"), i18n("Clone"), this); + a->setData(tabIndex); + connect(a, SIGNAL(triggered(bool)), this, SLOT(cloneTab())); + menu.addAction(a); + + a = new KAction(KIcon("view-refresh"), i18n("Reload"), this); + connect(a, SIGNAL(triggered(bool)), this, SLOT(reloadTab())); + a->setData(tabIndex); + menu.addAction(a); + + a = new KAction(KIcon("view-refresh"), i18n("Reload All"), this); + connect(a, SIGNAL(triggered(bool)), w, SLOT(reloadAllTabs())); + menu.addAction(a); + + if (count() > 1) + { + a = new KAction(KIcon("tab-detach"), i18n("Detach"), this); + connect(a, SIGNAL(triggered(bool)), this, SLOT(detachTab())); + a->setData(tabIndex); + menu.addAction(a); + } + + if (tabData(tabIndex).toBool()) + { + a = new KAction(i18n("Unpin Tab"), this); + connect(a, SIGNAL(triggered(bool)), this, SLOT(unpinTab())); + a->setData(tabIndex); + menu.addAction(a); + } + else + { + a = new KAction(i18n("Pin Tab"), this); + connect(a, SIGNAL(triggered(bool)), this, SLOT(pinTab())); + a->setData(tabIndex); + menu.addAction(a); + } + menu.addSeparator(); // ---------------------------------------------------------------- + + a = new KAction(KIcon("tab-close"), i18n("&Close"), this); + a->setData(tabIndex); + connect(a, SIGNAL(triggered(bool)), this, SLOT(closeTab())); + menu.addAction(a); + + if (count() > 1) + { + a = new KAction(KIcon("tab-close-other"), i18n("Close &Other Tabs"), this); + connect(a, SIGNAL(triggered(bool)), this, SLOT(closeOtherTabs())); + a->setData(tabIndex); + menu.addAction(a); + } + + menu.addSeparator(); + + + a = w->actionByName(QL1S("open_last_closed_tab")); + menu.addAction(a); + + if (count() > 1) + { + a = w->actionByName(QL1S("bookmark_all_tabs")); + menu.addAction(a); + } + + menu.exec(pos); +} + + +void TabBar::emptyAreaContextMenu(const QPoint &pos) +{ + TabWidget *w = qobject_cast<TabWidget *>(parent()); + + QAction *a; + + KMenu menu; + + a = w->actionByName(QL1S("new_tab")); + menu.addAction(a); + + a = w->actionByName(QL1S("open_last_closed_tab")); + menu.addAction(a); + + if (count() > 1) + { + a = w->actionByName(QL1S("bookmark_all_tabs")); + menu.addAction(a); + } + + menu.exec(pos); +} + + +void TabBar::setTabHighlighted(int index, bool b) +{ + if (!b) + { + removeAnimation(index); + setTabTextColor(index, KColorScheme(QPalette::Active, +KColorScheme::Window).foreground(KColorScheme::NormalText).color()); return; + } + + const QByteArray propertyName = highlightPropertyName(index); + const QColor highlightColor = KColorScheme(QPalette::Active, +KColorScheme::Window).foreground(KColorScheme::PositiveText).color(); + + if (tabTextColor(index) != highlightColor) + { + m_tabHighlightEffect->setEnabled(true); + m_tabHighlightEffect->setProperty(propertyName, qreal(0.9)); + QPropertyAnimation *anim = new QPropertyAnimation(m_tabHighlightEffect, propertyName); + m_highlightAnimation.insert(propertyName, anim); + + //setup the animation + anim->setStartValue(0.9); + anim->setEndValue(0.0); + anim->setDuration(500); + anim->setLoopCount(2); + anim->start(QAbstractAnimation::DeleteWhenStopped); + + m_animationMapper->setMapping(anim, index); + connect(anim, SIGNAL(finished()), m_animationMapper, SLOT(map())); + + setTabTextColor(index, highlightColor); + } +} + + +QRect TabBar::tabTextRect(int index) +{ + QStyleOptionTabV3 option; + initStyleOption(&option, index); + return style()->subElementRect(QStyle::SE_TabBarTabText, &option, this); +} + + +void TabBar::removeAnimation(int index) +{ + const QByteArray propertyName = highlightPropertyName(index); + m_tabHighlightEffect->setProperty(propertyName, QVariant()); //destroy the property + + QPropertyAnimation *anim = m_highlightAnimation.take(propertyName); + m_animationMapper->removeMappings(anim); + delete anim; + + if (m_highlightAnimation.isEmpty()) + m_tabHighlightEffect->setEnabled(false); +} + + +void TabBar::tabInserted(int index) +{ + // Find the available index to move + int availableIndex = index; + for (int i = index; i < count(); i++) + { + if (tabData(i).toBool()) + { + availableIndex++; + break; + } + } + + if (index < availableIndex) + { + TabWidget *w = qobject_cast<TabWidget *>(parent()); + w->moveTab(index, availableIndex); + } + + KTabBar::tabInserted(index); +} + + +void TabBar::tabRemoved(int index) +{ + hideTabPreview(); + removeAnimation(index); + + KTabBar::tabRemoved(index); +} + + +void TabBar::mouseMoveEvent(QMouseEvent *event) +{ + KTabBar::mouseMoveEvent(event); + + if (count() == 1) + { + return; + } + + if (ReKonfig::hoveringTabOption() != 0) + return; + + // Find the tab under the mouse + const int tabIndex = tabAt(event->pos()); + + // if found and not the current tab then show tab preview + if (tabIndex != -1 + && tabIndex != currentIndex() + && m_currentTabPreviewIndex != tabIndex + && event->buttons() == Qt::NoButton + ) + { + m_currentTabPreviewIndex = tabIndex; + + // if first time over tab, apply a small delay. If not, show it now! + m_isFirstTimeOnTab + ? QTimer::singleShot(200, this, SLOT(showTabPreview())) + : showTabPreview(); + } + + // if current tab or not found then hide previous tab preview + if (tabIndex == currentIndex() || tabIndex == -1) + { + hideTabPreview(); + } +} + + +void TabBar::leaveEvent(QEvent *event) +{ + hideTabPreview(); + m_isFirstTimeOnTab = true; + + KTabBar::leaveEvent(event); +} + + +void TabBar::mousePressEvent(QMouseEvent *event) +{ + hideTabPreview(); + + // just close tab on middle mouse click + if (event->button() == Qt::MidButton) + return; + + KTabBar::mousePressEvent(event); +} + + +void TabBar::mouseReleaseEvent(QMouseEvent *event) +{ + // count pinned tabs + int pinnedTabs = 0; + for (int i = 0; i < count(); i++) + { + if (tabData(i).toBool()) + pinnedTabs++; + } + + // fix unpinned ones + for (int i = 0; i < pinnedTabs; i++) + { + if (!tabData(i).toBool()) + { + TabWidget *w = qobject_cast<TabWidget *>(parent()); + w->moveTab(i, pinnedTabs); + w->setCurrentIndex(pinnedTabs); + } + } + + // fix pinned ones + for (int i = pinnedTabs; i < count(); i++) + { + if (tabData(i).toBool()) + { + TabWidget *w = qobject_cast<TabWidget *>(parent()); + w->moveTab(i, pinnedTabs - 1); + w->setCurrentIndex(pinnedTabs - 1); + } + } + KTabBar::mouseReleaseEvent(event); +} + + +void TabBar::tabLayoutChange() +{ + KTabBar::tabLayoutChange(); + emit tabLayoutChanged(); +} + + +void TabBar::showTabPreview() +{ + if (m_isFirstTimeOnTab) + m_isFirstTimeOnTab = false; + + //delete previous tab preview + delete m_previewPopup.data(); + m_previewPopup.clear(); + + TabWidget *tabW = qobject_cast<TabWidget *>(parent()); + + WebWindow *indexedTab = tabW->webWindow(m_currentTabPreviewIndex); + WebWindow *currentTab = tabW->webWindow(currentIndex()); + + // check if view && currentView exist before using them :) + if (!currentTab || !indexedTab) + return; + + // no previews during load + if (indexedTab->isLoading()) + return; + + int w = c_baseTabWidth; + int h = w * tabW->size().height() / tabW->size().width(); + + m_previewPopup = new TabPreviewPopup(indexedTab->tabPreview(w, h), indexedTab->url().url() , this); + + int tabBarWidth = tabW->size().width(); + int leftIndex = tabRect(m_currentTabPreviewIndex).x() + (tabRect(m_currentTabPreviewIndex).width() - w) / 2; + + if (leftIndex < 0) + { + leftIndex = 0; + } + else if (leftIndex + w > tabBarWidth) + { + leftIndex = tabBarWidth - w; + } + + QPoint pos(leftIndex, tabRect(m_currentTabPreviewIndex).y() + tabRect(m_currentTabPreviewIndex).height()); + m_previewPopup.data()->show(mapToGlobal(pos)); +} + + +void TabBar::hideTabPreview() +{ + if (!m_previewPopup.isNull()) + { + m_previewPopup.data()->hide(); + } + m_currentTabPreviewIndex = -1; +} + + +void TabBar::pinTab() +{ + KAction *a = qobject_cast<KAction *>(sender()); + if (!a) + return; + + int index = a->data().toInt(); + + // Find the available index to move + int availableIndex = 0; + for (int i = 0; i < count(); i++) + { + if (!tabData(i).toBool()) + { + availableIndex = i; + break; + } + } + + TabWidget *w = qobject_cast<TabWidget *>(parent()); + w->moveTab(index, availableIndex); + index = availableIndex; + + // set this tab data true to know this has been pinned + setTabData(index, true); + + tabButton(index, QTabBar::RightSide)->hide(); + setTabText(index, QString()); + + // workaround: "fix" the icon (or at least, try to...) + QLabel *label = qobject_cast<QLabel* >(tabButton(index, QTabBar::LeftSide)); + if (!label) + label = new QLabel(this); + + label->resize(QSize(16,16)); + + setTabButton(index, QTabBar::LeftSide, 0); + setTabButton(index, QTabBar::LeftSide, label); + + KIcon ic = IconManager::self()->iconForUrl(w->webWindow(index)->url()); + label->setPixmap(ic.pixmap(16, 16)); + + SessionManager::self()->saveSession(); +} + + +void TabBar::unpinTab() +{ + KAction *a = qobject_cast<KAction *>(sender()); + if (!a) + return; + + int index = a->data().toInt(); + + // Find the available index to move + int availableIndex = 0; + for (int i = 1; i < count(); i++) + { + if (!tabData(i).toBool()) + { + availableIndex = i - 1; + break; + } + availableIndex++; + } + + TabWidget *w = qobject_cast<TabWidget *>(parent()); + w->moveTab(index, availableIndex); + index = availableIndex; + + tabButton(index, QTabBar::RightSide)->show(); + setTabText(index, w->webWindow(index)->title()); + + // set the tab data false to forget this pinned tab + setTabData(index, false); + + // workaround: "fix" the icon (or at least, try to...) + QLabel *label = qobject_cast<QLabel* >(tabButton(index, QTabBar::LeftSide)); + if (!label) + label = new QLabel(this); + + label->resize(QSize(16,16)); + + setTabButton(index, QTabBar::LeftSide, 0); + setTabButton(index, QTabBar::LeftSide, label); + + KIcon ic = IconManager::self()->iconForUrl(w->webWindow(index)->url()); + label->setPixmap(ic.pixmap(16, 16)); + + SessionManager::self()->saveSession(); +} +*/
\ No newline at end of file diff --git a/src/tabbar/tabbar.h b/src/tabbar/tabbar.h new file mode 100644 index 00000000..c305d1b5 --- /dev/null +++ b/src/tabbar/tabbar.h @@ -0,0 +1,90 @@ +/* ============================================================ + * The rekonq project + * ============================================================ + * SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (C) 2008-2012 by Andrea Diamantini <adjam7 at gmail dot com> + * Copyright (C) 2009-2011 by Lionel Chauvin <megabigbug@yahoo.fr> + * SPDX-License-Identifier: GPL-3.0-only + * Copyright (C) 2022 aqua <aqua@iserlohn-fortress.net> + * ============================================================ + * Description: Tab Bar + * ============================================================ */ + +#pragma once + +#include <QPointer> +#include <QTabBar> +#include <rview.hpp> + +class TabBar : public QTabBar { + Q_OBJECT + +public: + explicit TabBar(QWidget *parent = nullptr); + + int addTab(RekonqView *view); + [[nodiscard]] RekonqView *view(int index) { return m_views.at(index); } + /* + void setTabHighlighted(int index, bool b); + QRect tabTextRect(int index); + */ + +signals: + void removeView(RekonqView *); + +protected: + [[nodiscard]] QSize tabSizeHint(int index) const override; + /* + virtual void mouseMoveEvent(QMouseEvent *event); + virtual void leaveEvent(QEvent *event); + virtual void mousePressEvent(QMouseEvent *event); + virtual void mouseReleaseEvent(QMouseEvent *event); + + virtual void tabInserted(int index); + virtual void tabRemoved(int index); + + virtual void tabLayoutChange(); + + Q_SIGNALS: + void cloneTab(int); + void closeTab(int); + void closeOtherTabs(int); + void reloadTab(int); + void detachTab(int); + void tabLayoutChanged(); + + private Q_SLOTS: + void cloneTab(); + void closeTab(); + void closeOtherTabs(); + void reloadTab(); + void detachTab(); + + void pinTab(); + void unpinTab(); + + void contextMenu(int, const QPoint &); + void emptyAreaContextMenu(const QPoint &); + + void removeAnimation(int index); + + void showTabPreview(); + void hideTabPreview(); + + private: + // highlightAnimation + TabHighlightEffect *m_tabHighlightEffect; + QHash<QByteArray, QPropertyAnimation*> m_highlightAnimation; + QSignalMapper *m_animationMapper; + + // tab preview + QWeakPointer<TabPreviewPopup> m_previewPopup; + int m_currentTabPreviewIndex; + bool m_isFirstTimeOnTab; + */ + static constexpr int c_baseTabWidth = 250; + static constexpr int c_minTabWidth = 50; + +private: + QList<QPointer<RekonqView>> m_views; +}; |