/* ============================================================ * The rekonq project * ============================================================ * SPDX-License-Identifier: GPL-2.0-or-later * Copyright (C) 2008-2012 by Andrea Diamantini * Copyright (C) 2009-2011 by Lionel Chauvin * SPDX-License-Identifier: GPL-3.0-only * Copyright (C) 2022 aqua * ============================================================ */ #include "tabbar.h" // ------------------------------------------------------------------------------------ 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::currentChanged, this, [this](int index) { if (index == -1) { emit currentChanged(nullptr); return; } auto view = m_views.at(index); Q_ASSERT(!view.isNull()); emit currentChanged(view); }); 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(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(sender()); if (a) { int index = a->data().toInt(); emit cloneTab(index); } } void TabBar::closeTab() { KAction *a = qobject_cast(sender()); if (a) { int index = a->data().toInt(); emit closeTab(index); } } void TabBar::closeOtherTabs() { KAction *a = qobject_cast(sender()); if (a) { int index = a->data().toInt(); emit closeOtherTabs(index); } } void TabBar::reloadTab() { KAction *a = qobject_cast(sender()); if (a) { int index = a->data().toInt(); emit reloadTab(index); } } void TabBar::detachTab() { KAction *a = qobject_cast(sender()); if (a) { int index = a->data().toInt(); emit detachTab(index); } } void TabBar::contextMenu(int tabIndex, const QPoint &pos) { TabWidget *w = qobject_cast(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(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(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(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(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(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(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(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(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(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(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(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(); } */