summaryrefslogtreecommitdiff
path: root/src/tabbar
diff options
context:
space:
mode:
Diffstat (limited to 'src/tabbar')
-rw-r--r--src/tabbar/CMakeLists.txt2
-rw-r--r--src/tabbar/tabbar.cpp599
-rw-r--r--src/tabbar/tabbar.h90
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;
+};