From 790fc2ff2895d15631b3806e30801439d268c434 Mon Sep 17 00:00:00 2001 From: Vikram Ambrose <34391406+vikramambrose@users.noreply.github.com> Date: Wed, 10 Jan 2018 11:22:05 +0200 Subject: [PATCH] Replace Nokia based FancyTabWidget with standard QTabWidget (#5956) * Replace Nokia sidebar widget with QTabWidget The old sidebar widget was written by Nokia 10 years ago and was a hand made hardcoded mess of widgets trying to emulate a QTabWidget. This commit completely replaces it with a widget of the same name (FancyTabWidget) but is a much simpler subclass of the standard QTabWidget allowing for a 50% code reduction. There is still some manual draw code copied over to get the exact same look of the previous widget but this is not strictly necessary and can be later refactored with simpler drawControl code based on styling preferences. Benefits: - 50% code reduction - Easier to understand standard QTabWidget mechanics - Built-in support for re-arranging and closing tabs * Save and restore sidebar tab order Allow the user to rearrange the tab order by dragging tabs to a new location. Tab order is saved on exit and restored on start * Fix some code formatting issues --- src/ui/mainwindow.cpp | 37 +- src/widgets/fancytabwidget.cpp | 1006 ++++++++++++-------------------- src/widgets/fancytabwidget.h | 262 ++------- 3 files changed, 457 insertions(+), 848 deletions(-) diff --git a/src/ui/mainwindow.cpp b/src/ui/mainwindow.cpp index ef0b80f25..5832942d7 100644 --- a/src/ui/mainwindow.cpp +++ b/src/ui/mainwindow.cpp @@ -255,37 +255,40 @@ MainWindow::MainWindow(Application* app, SystemTrayIcon* tray_icon, OSD* osd, SLOT(AddToPlaylist(QMimeData*))); // Add tabs to the fancy tab widget - ui_->tabs->AddTab(global_search_view_, + ui_->tabs->addTab(global_search_view_, IconLoader::Load("search", IconLoader::Base), tr("Search", "Global search settings dialog title.")); - ui_->tabs->AddTab(library_view_, + ui_->tabs->addTab(library_view_, IconLoader::Load("folder-sound", IconLoader::Base), tr("Library")); - ui_->tabs->AddTab(file_view_, + ui_->tabs->addTab(file_view_, IconLoader::Load("document-open", IconLoader::Base), tr("Files")); - ui_->tabs->AddTab(playlist_list_, + ui_->tabs->addTab(playlist_list_, IconLoader::Load("view-media-playlist", IconLoader::Base), tr("Playlists")); - ui_->tabs->AddTab(internet_view_, + ui_->tabs->addTab(internet_view_, IconLoader::Load("applications-internet", IconLoader::Base), tr("Internet")); - ui_->tabs->AddTab( + ui_->tabs->addTab( device_view_container_, IconLoader::Load("multimedia-player-ipod-mini-blue", IconLoader::Base), tr("Devices")); - ui_->tabs->AddSpacer(); - ui_->tabs->AddTab(song_info_view_, + ui_->tabs->addSpacer(); + ui_->tabs->addTab(song_info_view_, IconLoader::Load("view-media-lyrics", IconLoader::Base), tr("Song info")); - ui_->tabs->AddTab(artist_info_view_, + ui_->tabs->addTab(artist_info_view_, IconLoader::Load("x-clementine-artist", IconLoader::Base), tr("Artist info")); // Add the now playing widget to the fancy tab widget - ui_->tabs->AddBottomWidget(ui_->now_playing); + ui_->tabs->addBottomWidget(ui_->now_playing); - ui_->tabs->SetBackgroundPixmap(QPixmap(":/sidebar_background.png")); + ui_->tabs->setBackgroundPixmap(QPixmap(":/sidebar_background.png")); + + // Do this only after all default tabs have been added + ui_->tabs->loadSettings(kSettingsGroup); track_position_timer_->setInterval(kTrackPositionUpdateTimeMs); connect(track_position_timer_, SIGNAL(timeout()), @@ -979,7 +982,7 @@ MainWindow::MainWindow(Application* app, SystemTrayIcon* tray_icon, OSD* osd, settings_.value("splitter_state").toByteArray())) { ui_->splitter->setSizes(QList() << 300 << width() - 300); } - ui_->tabs->SetCurrentIndex( + ui_->tabs->setCurrentIndex( settings_.value("current_tab", 1 /* Library tab */).toInt()); FancyTabWidget::Mode default_mode = FancyTabWidget::Mode_LargeSidebar; ui_->tabs->SetMode( @@ -1265,8 +1268,10 @@ void MainWindow::SaveGeometry() { settings_.setValue("geometry", saveGeometry()); } settings_.setValue("splitter_state", ui_->splitter->saveState()); - settings_.setValue("current_tab", ui_->tabs->current_index()); + settings_.setValue("current_tab", ui_->tabs->currentIndex()); settings_.setValue("tab_mode", ui_->tabs->mode()); + + ui_->tabs->saveSettings(kSettingsGroup); } void MainWindow::SavePlaybackStatus() { @@ -2911,7 +2916,7 @@ void MainWindow::HandleNotificationPreview(OSD::Behaviour type, QString line1, void MainWindow::ScrollToInternetIndex(const QModelIndex& index) { internet_view_->ScrollToIndex(index); - ui_->tabs->SetCurrentWidget(internet_view_); + ui_->tabs->setCurrentWidget(internet_view_); } void MainWindow::AddPodcast() { @@ -2919,11 +2924,11 @@ void MainWindow::AddPodcast() { } void MainWindow::FocusLibraryTab() { - ui_->tabs->SetCurrentWidget(library_view_); + ui_->tabs->setCurrentWidget(library_view_); } void MainWindow::FocusGlobalSearchField() { - ui_->tabs->SetCurrentWidget(global_search_view_); + ui_->tabs->setCurrentWidget(global_search_view_); global_search_view_->FocusSearchField(); } diff --git a/src/widgets/fancytabwidget.cpp b/src/widgets/fancytabwidget.cpp index ccc93b119..05db6012c 100644 --- a/src/widgets/fancytabwidget.cpp +++ b/src/widgets/fancytabwidget.cpp @@ -1,31 +1,20 @@ -/************************************************************************** -** -** This file is part of Qt Creator -** -** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** Commercial Usage -** -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Nokia. -** -** GNU Lesser General Public License Usage -** -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. -** -**************************************************************************/ +/* This file is part of Clementine. + Copyright 2018, Vikram Ambrose + + Clementine 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. + + Clementine 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 Clementine. If not, see . +*/ + #include "fancytabwidget.h" #include "stylehelper.h" @@ -33,626 +22,402 @@ #include -#include -#include -#include #include #include #include -#include #include -#include -#include -#include -#include -#include +#include +#include +#include #include -#include +#include -using namespace Core; -using namespace Internal; +const QSize FancyTabWidget::IconSize_LargeSidebar = QSize(24,24); +const QSize FancyTabWidget::IconSize_SmallSidebar = QSize(22,22); -const int FancyTabBar::m_rounding = 22; -const int FancyTabBar::m_textPadding = 4; +const QSize FancyTabWidget::TabSize_LargeSidebar = QSize(70,47); +class FancyTabBar: public QTabBar { -namespace { -static void selectTab(QPainter* p, const QRect& rect) { - // background - p->save(); - QLinearGradient grad(rect.topLeft(), rect.topRight()); - grad.setColorAt(0, QColor(255, 255, 255, 140)); - grad.setColorAt(1, QColor(255, 255, 255, 210)); - p->fillRect(rect.adjusted(0, 0, 0, -1), grad); - p->restore(); +private: + int mouseHoverTabIndex = -1; + QMap labelCache; - // shadows - p->setPen(QColor(0, 0, 0, 110)); - p->drawLine(rect.topLeft() + QPoint(1, -1), rect.topRight() - QPoint(0, 1)); - p->drawLine(rect.bottomLeft(), rect.bottomRight()); - p->setPen(QColor(0, 0, 0, 40)); - p->drawLine(rect.topLeft(), rect.bottomLeft()); - - // highlights - p->setPen(QColor(255, 255, 255, 50)); - p->drawLine(rect.topLeft() + QPoint(0, -2), rect.topRight() - QPoint(0, 2)); - p->drawLine(rect.bottomLeft() + QPoint(0, 1), - rect.bottomRight() + QPoint(0, 1)); - p->setPen(QColor(255, 255, 255, 40)); - p->drawLine(rect.topLeft() + QPoint(0, 0), rect.topRight()); - p->drawLine(rect.topRight() + QPoint(0, 1), - rect.bottomRight() - QPoint(0, 1)); - p->drawLine(rect.bottomLeft() + QPoint(0, -1), - rect.bottomRight() - QPoint(0, 1)); -} -} - -void FancyTabProxyStyle::drawControl(ControlElement element, - const QStyleOption* option, QPainter* p, - const QWidget* widget) const { - - const QStyleOptionTabV3* v_opt = - qstyleoption_cast(option); - - if (element != CE_TabBarTab || !v_opt) { - QProxyStyle::drawControl(element, option, p, widget); - return; - } - - const QRect rect = v_opt->rect; - const bool selected = v_opt->state & State_Selected; - const bool vertical_tabs = v_opt->shape == QTabBar::RoundedWest; - const QString text = v_opt->text; - - if (selected) { - selectTab(p, rect); - } - - QTransform m; - if (vertical_tabs) { - m = QTransform::fromTranslate(rect.left(), rect.bottom()); - m.rotate(-90); - } else { - m = QTransform::fromTranslate(rect.left(), rect.top()); - } - - const QRect draw_rect(QPoint(0, 0), m.mapRect(rect).size()); - - p->save(); - p->setTransform(m); - - QRect icon_rect(QPoint(8, 0), v_opt->iconSize); - QRect text_rect(icon_rect.topRight() + QPoint(4, 0), draw_rect.size()); - text_rect.setRight(draw_rect.width()); - icon_rect.translate(0, (draw_rect.height() - icon_rect.height()) / 2); - - QFont boldFont(p->font()); - boldFont.setPointSizeF(Utils::StyleHelper::sidebarFontSize()); - boldFont.setBold(true); - p->setFont(boldFont); - p->setPen(selected ? QColor(255, 255, 255, 160) : QColor(0, 0, 0, 110)); - int textFlags = Qt::AlignHCenter | Qt::AlignVCenter; - p->drawText(text_rect, textFlags, text); - p->setPen(selected ? QColor(60, 60, 60) - : Utils::StyleHelper::panelTextColor()); -#ifndef Q_WS_MAC - if (widget) { - const QString fader_key = "tab_" + text + "_fader"; - const QString animation_key = "tab_" + text + "_animation"; - - const QString tab_hover = widget->property("tab_hover").toString(); - int fader = widget->property(fader_key.toUtf8().constData()).toInt(); - QPropertyAnimation* animation = - widget->property(animation_key.toUtf8().constData()) - .value(); - - if (!animation) { - QWidget* mut_widget = const_cast(widget); - fader = 0; - mut_widget->setProperty(fader_key.toUtf8().constData(), fader); - animation = - new QPropertyAnimation(mut_widget, fader_key.toUtf8(), mut_widget); - connect(animation, SIGNAL(valueChanged(QVariant)), mut_widget, - SLOT(update())); - mut_widget->setProperty(animation_key.toUtf8().constData(), - QVariant::fromValue(animation)); +public: + explicit FancyTabBar(QWidget* parent=0) : QTabBar(parent) { + setMouseTracking(true); } - if (text == tab_hover) { - if (animation->state() != QAbstractAnimation::Running && fader != 40) { - animation->stop(); - animation->setDuration(80); - animation->setEndValue(40); - animation->start(); - } - } else { - if (animation->state() != QAbstractAnimation::Running && fader != 0) { - animation->stop(); - animation->setDuration(160); - animation->setEndValue(0); - animation->start(); - } + QSize sizeHint() const { + QSize size(QTabBar::sizeHint()); + + FancyTabWidget *tabWidget = (FancyTabWidget*) parentWidget(); + if(tabWidget->mode() == FancyTabWidget::Mode_Tabs || + tabWidget->mode() == FancyTabWidget::Mode_IconOnlyTabs) + return size; + + QSize tabSize(tabSizeHint(0)); + size.setWidth(tabSize.width()); + int guessHeight = tabSize.height()*count(); + if(guessHeight > size.height()) + size.setHeight(guessHeight); + return size; } - if (!selected) { - p->save(); - QLinearGradient grad(draw_rect.topLeft(), vertical_tabs - ? draw_rect.bottomLeft() - : draw_rect.topRight()); - grad.setColorAt(0, Qt::transparent); - grad.setColorAt(0.5, QColor(255, 255, 255, fader)); - grad.setColorAt(1, Qt::transparent); - p->fillRect(draw_rect, grad); - p->setPen(QPen(grad, 1.0)); - p->drawLine(draw_rect.topLeft(), vertical_tabs ? draw_rect.bottomLeft() - : draw_rect.topRight()); - p->drawLine(draw_rect.bottomRight(), vertical_tabs - ? draw_rect.topRight() - : draw_rect.bottomLeft()); - p->restore(); + int width() { + return tabSizeHint(0).width(); } - } -#endif - Utils::StyleHelper::drawIconWithShadow(v_opt->icon, icon_rect, p, - QIcon::Normal); +protected: + QSize tabSizeHint(int index) const { + FancyTabWidget *tabWidget = (FancyTabWidget*) parentWidget(); + QSize size = FancyTabWidget::TabSize_LargeSidebar; - p->drawText(text_rect.translated(0, -1), textFlags, text); - - p->restore(); -} - -void FancyTabProxyStyle::polish(QWidget* widget) { - if (QString(widget->metaObject()->className()) == "QTabBar") { - widget->setMouseTracking(true); - widget->installEventFilter(this); - } - QProxyStyle::polish(widget); -} - -void FancyTabProxyStyle::polish(QApplication* app) { QProxyStyle::polish(app); } - -void FancyTabProxyStyle::polish(QPalette& palette) { - QProxyStyle::polish(palette); -} - -bool FancyTabProxyStyle::eventFilter(QObject* o, QEvent* e) { - QTabBar* bar = qobject_cast(o); - if (bar && (e->type() == QEvent::MouseMove || e->type() == QEvent::Leave)) { - QMouseEvent* event = static_cast(e); - const QString old_hovered_tab = bar->property("tab_hover").toString(); - const QString hovered_tab = e->type() == QEvent::Leave - ? QString() - : bar->tabText(bar->tabAt(event->pos())); - bar->setProperty("tab_hover", hovered_tab); - - if (old_hovered_tab != hovered_tab) bar->update(); - } - - return false; -} - -FancyTab::FancyTab(QWidget* tabbar) - : QWidget(tabbar), tabbar(tabbar), m_fader(0) { - animator.setPropertyName("fader"); - animator.setTargetObject(this); - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); -} - -void FancyTab::fadeIn() { - animator.stop(); - animator.setDuration(80); - animator.setEndValue(40); - animator.start(); -} - -void FancyTab::fadeOut() { - animator.stop(); - animator.setDuration(160); - animator.setEndValue(0); - animator.start(); -} - -void FancyTab::setFader(float value) { - m_fader = value; - tabbar->update(); -} - -FancyTabBar::FancyTabBar(QWidget* parent) : QWidget(parent) { - setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); - setStyle(new QWindowsStyle); - setMinimumWidth(qMax(2 * m_rounding, 40)); - setAttribute(Qt::WA_Hover, true); - setFocusPolicy(Qt::NoFocus); - setMouseTracking(true); // Needed for hover events - m_triggerTimer.setSingleShot(true); - - QVBoxLayout* layout = new QVBoxLayout; - layout->addSpacerItem( - new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding)); - layout->setSpacing(0); - layout->setContentsMargins(0, 0, 0, 0); - setLayout(layout); - - // We use a zerotimer to keep the sidebar responsive - connect(&m_triggerTimer, SIGNAL(timeout()), this, SLOT(emitCurrentIndex())); -} - -FancyTabBar::~FancyTabBar() { delete style(); } - -QSize FancyTab::sizeHint() const { - QFont boldFont(font()); - boldFont.setPointSizeF(Utils::StyleHelper::sidebarFontSize()); - boldFont.setBold(true); - QFontMetrics fm(boldFont); - int spacing = 8; - int width = 60 + spacing + 2; - int iconHeight = 32; - QSize ret(width, iconHeight + spacing + fm.height()); - return ret; -} - -QSize FancyTabBar::tabSizeHint(bool minimum) const { - QFont boldFont(font()); - boldFont.setPointSizeF(Utils::StyleHelper::sidebarFontSize()); - boldFont.setBold(true); - QFontMetrics fm(boldFont); - int spacing = 8; - int width = 60 + spacing + 2; - int iconHeight = minimum ? 0 : 32; - return QSize(width, iconHeight + spacing + fm.height()); -} - -void FancyTabBar::paintEvent(QPaintEvent* event) { - Q_UNUSED(event) - QPainter p(this); - - for (int i = 0; i < count(); ++i) - if (i != currentIndex()) paintTab(&p, i); - - // paint active tab last, since it overlaps the neighbors - if (currentIndex() != -1) paintTab(&p, currentIndex()); -} - -bool FancyTab::event(QEvent* event) { - if (event->type() == QEvent::ToolTip) { - QFontMetrics metrics(font()); - int text_width = metrics.width(text); - - if (text_width > sizeHint().width()) { - // The text is elided: show the tooltip - QHelpEvent* he = static_cast(event); - QToolTip::showText(he->globalPos(), text); - } else { - QToolTip::hideText(); + if(tabWidget->mode() != FancyTabWidget::Mode_LargeSidebar) { + size = QTabBar::tabSizeHint(index); + } + + return size; } - return true; - } - return QWidget::event(event); -} - -void FancyTab::enterEvent(QEvent*) { fadeIn(); } - -void FancyTab::leaveEvent(QEvent*) { fadeOut(); } - -QSize FancyTabBar::sizeHint() const { - QSize sh = tabSizeHint(); - return QSize(sh.width(), sh.height() * m_tabs.count()); -} - -QSize FancyTabBar::minimumSizeHint() const { - QSize sh = tabSizeHint(true); - return QSize(sh.width(), sh.height() * m_tabs.count()); -} - -QRect FancyTabBar::tabRect(int index) const { - return m_tabs[index]->geometry(); -} - -QString FancyTabBar::tabToolTip(int index) const { - return m_tabs[index]->toolTip(); -} - -void FancyTabBar::setTabToolTip(int index, const QString& toolTip) { - m_tabs[index]->setToolTip(toolTip); -} - -// This keeps the sidebar responsive since -// we get a repaint before loading the -// mode itself -void FancyTabBar::emitCurrentIndex() { emit currentChanged(m_currentIndex); } - -void FancyTabBar::mousePressEvent(QMouseEvent* e) { - e->accept(); - for (int index = 0; index < m_tabs.count(); ++index) { - if (tabRect(index).contains(e->pos())) { - m_currentIndex = index; - update(); - m_triggerTimer.start(0); - break; + + void leaveEvent(QEvent * event) { + mouseHoverTabIndex = -1; + update(); } - } -} -void FancyTabBar::addTab(const QIcon& icon, const QString& label) { - FancyTab* tab = new FancyTab(this); - tab->icon = icon; - tab->text = label; - tab->setToolTip(label); - m_tabs.append(tab); - qobject_cast(layout()) - ->insertWidget(layout()->count() - 1, tab); -} + void mouseMoveEvent(QMouseEvent * event) { + QPoint pos = event->pos(); -void FancyTabBar::addSpacer(int size) { - qobject_cast(layout())->insertSpacerItem( - layout()->count() - 1, - new QSpacerItem(0, size, QSizePolicy::Fixed, QSizePolicy::Maximum)); -} + mouseHoverTabIndex = tabAt(pos); + if(mouseHoverTabIndex > -1) + update(); + QTabBar::mouseMoveEvent(event); + } -void FancyTabBar::paintTab(QPainter* painter, int tabIndex) const { - if (!validIndex(tabIndex)) { - qWarning("invalid index"); - return; - } - painter->save(); + void paintEvent(QPaintEvent *pe) { + FancyTabWidget *tabWidget = (FancyTabWidget*) parentWidget(); - QRect rect = tabRect(tabIndex); - bool selected = (tabIndex == m_currentIndex); + bool verticalTextTabs = false; - if (selected) { - selectTab(painter, rect); - } + if(tabWidget->mode() == FancyTabWidget::Mode_SmallSidebar) + verticalTextTabs = true; - QString tabText(painter->fontMetrics().elidedText(this->tabText(tabIndex), - Qt::ElideRight, width())); - QRect tabTextRect(tabRect(tabIndex)); - QRect tabIconRect(tabTextRect); - tabIconRect.adjust(+4, +4, -4, -4); - tabTextRect.translate(0, -2); - QFont boldFont(painter->font()); - boldFont.setPointSizeF(Utils::StyleHelper::sidebarFontSize()); - boldFont.setBold(true); - painter->setFont(boldFont); - painter->setPen(selected ? QColor(255, 255, 255, 160) : QColor(0, 0, 0, 110)); - int textFlags = Qt::AlignCenter | Qt::AlignBottom; - painter->drawText(tabTextRect, textFlags, tabText); - painter->setPen(selected ? QColor(60, 60, 60) - : Utils::StyleHelper::panelTextColor()); -#ifndef Q_WS_MAC - if (!selected) { - painter->save(); - int fader = int(m_tabs[tabIndex]->fader()); - QLinearGradient grad(rect.topLeft(), rect.topRight()); - grad.setColorAt(0, Qt::transparent); - grad.setColorAt(0.5, QColor(255, 255, 255, fader)); - grad.setColorAt(1, Qt::transparent); - painter->fillRect(rect, grad); - painter->setPen(QPen(grad, 1.0)); - painter->drawLine(rect.topLeft(), rect.topRight()); - painter->drawLine(rect.bottomLeft(), rect.bottomRight()); - painter->restore(); - } -#endif + // Restore any label text that was hidden/cached for the IconOnlyTabs mode + if(labelCache.count() > 0 && tabWidget->mode() != FancyTabWidget::Mode_IconOnlyTabs) { + for(int i =0; i < count(); i++) { + setTabText(i,labelCache[tabWidget->widget(i)]); + } + labelCache.clear(); + } + if(tabWidget->mode() != FancyTabWidget::Mode_LargeSidebar && + tabWidget->mode() != FancyTabWidget::Mode_SmallSidebar) { + // Cache and hide label text for IconOnlyTabs mode + if(tabWidget->mode() == FancyTabWidget::Mode_IconOnlyTabs && labelCache.count() == 0) { + for(int i =0; i < count(); i++) { + labelCache[tabWidget->widget(i)] = tabText(i); + setTabText(i,""); + } + } + QTabBar::paintEvent(pe); + return; + } - const int textHeight = painter->fontMetrics().height(); - tabIconRect.adjust(0, 4, 0, -textHeight); - Utils::StyleHelper::drawIconWithShadow(tabIcon(tabIndex), tabIconRect, - painter, QIcon::Normal); + QStylePainter p(this); - painter->translate(0, -1); - painter->drawText(tabTextRect, textFlags, tabText); - painter->restore(); -} + + for (int index = 0; index < count(); index++) { + const bool selected = tabWidget->currentIndex() == index;; -void FancyTabBar::setCurrentIndex(int index) { - m_currentIndex = index; - update(); - emit currentChanged(m_currentIndex); -} + QRect tabrect = tabRect(index); -////// -// FancyColorButton -////// + QRect selectionRect = tabrect; -class FancyColorButton : public QWidget { - public: - FancyColorButton(QWidget* parent) : m_parent(parent) { - setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); - } + if(selected) { + // Selection highlight + p.save(); + QLinearGradient grad(selectionRect.topLeft(), selectionRect.topRight()); + grad.setColorAt(0, QColor(255, 255, 255, 140)); + grad.setColorAt(1, QColor(255, 255, 255, 210)); + p.fillRect(selectionRect.adjusted(0,0,0,-1), grad); + p.restore(); - void mousePressEvent(QMouseEvent* ev) { - if (ev->modifiers() & Qt::ShiftModifier) - Utils::StyleHelper::setBaseColor(QColorDialog::getColor( - Utils::StyleHelper::requestedBaseColor(), m_parent)); - } + // shadow lines + p.setPen(QColor(0, 0, 0, 110)); + p.drawLine(selectionRect.topLeft() + QPoint(1, -1), selectionRect.topRight() - QPoint(0, 1)); + p.drawLine(selectionRect.bottomLeft(), selectionRect.bottomRight()); + p.setPen(QColor(0, 0, 0, 40)); + p.drawLine(selectionRect.topLeft(), selectionRect.bottomLeft()); - private: - QWidget* m_parent; + // highlights + p.setPen(QColor(255, 255, 255, 50)); + p.drawLine(selectionRect.topLeft() + QPoint(0, -2), selectionRect.topRight() - QPoint(0, 2)); + p.drawLine(selectionRect.bottomLeft() + QPoint(0, 1), selectionRect.bottomRight() + QPoint(0, 1)); + p.setPen(QColor(255, 255, 255, 40)); + p.drawLine(selectionRect.topLeft() + QPoint(0, 0), selectionRect.topRight()); + p.drawLine(selectionRect.topRight() + QPoint(0, 1), selectionRect.bottomRight() - QPoint(0, 1)); + p.drawLine(selectionRect.bottomLeft() + QPoint(0, -1), selectionRect.bottomRight() - QPoint(0, 1)); + + } + + // Mouse hover effect + if(!selected && index == mouseHoverTabIndex && isTabEnabled(index)) + { + p.save(); + QLinearGradient grad(selectionRect.topLeft(), selectionRect.topRight()); + grad.setColorAt(0, Qt::transparent); + grad.setColorAt(0.5, QColor(255, 255, 255, 40)); + grad.setColorAt(1, Qt::transparent); + p.fillRect(selectionRect, grad); + p.setPen(QPen(grad, 1.0)); + p.drawLine(selectionRect.topLeft(), selectionRect.topRight()); + p.drawLine(selectionRect.bottomRight(), selectionRect.bottomLeft()); + p.restore(); + } + + // Label (Icon and Text) + { + p.save(); + QTransform m; + int textFlags; + Qt::Alignment iconFlags; + + QRect tabrectText; + QRect tabrectLabel; + + if (verticalTextTabs) { + m = QTransform::fromTranslate(tabrect.left(), tabrect.bottom()); + m.rotate(-90); + textFlags = Qt::AlignLeft | Qt::AlignVCenter ; + iconFlags = Qt::AlignLeft | Qt::AlignVCenter; + + tabrectLabel = QRect(QPoint(0, 0), m.mapRect(tabrect).size()); + + tabrectText = tabrectLabel; + tabrectText.translate(30,0); + } else { + m = QTransform::fromTranslate(tabrect.left(), tabrect.top()); + textFlags = Qt::AlignHCenter | Qt::AlignBottom ; + iconFlags = Qt::AlignHCenter | Qt::AlignTop; + + tabrectLabel = QRect(QPoint(0, 0), m.mapRect(tabrect).size()); + + tabrectText = tabrectLabel; + tabrectText.translate(0,-5); + } + + p.setTransform(m); + + QFont boldFont(p.font()); + boldFont.setPointSizeF(Utils::StyleHelper::sidebarFontSize()); + boldFont.setBold(true); + p.setFont(boldFont); + + // Text drop shadow color + p.setPen(selected ? QColor(255,255,255,160) : QColor(0,0,0,110) ); + p.translate(0, 3); + p.drawText(tabrectText, textFlags, tabText(index)); + + // Text foreground color + p.translate(0, -1); + p.setPen(selected ? QColor(60, 60, 60) : Utils::StyleHelper::panelTextColor()); + p.drawText(tabrectText, textFlags, tabText(index)); + + + // Draw the icon + QRect tabrectIcon; + const int PADDING = 5; + if(verticalTextTabs) { + tabrectIcon = tabrectLabel; + tabrectIcon.setSize(FancyTabWidget::IconSize_SmallSidebar); + tabrectIcon.translate(PADDING,PADDING); + } else { + tabrectIcon = tabrectLabel; + tabrectIcon.setSize(FancyTabWidget::IconSize_LargeSidebar); + // Center the icon + const int moveRight = (FancyTabWidget::TabSize_LargeSidebar.width() - + FancyTabWidget::IconSize_LargeSidebar.width() -1)/2; + tabrectIcon.translate(moveRight,PADDING); + } + tabIcon(index).paint(&p, tabrectIcon, iconFlags); + p.restore(); + } + } + } }; + +// Spacers are just disabled pages +void FancyTabWidget::addSpacer() { + QWidget *spacer = new QWidget(); + const int index = addTab(spacer,QIcon(),QString()); + setTabEnabled(index,false); +} -////// -// FancyTabWidget -////// +void FancyTabWidget::setBackgroundPixmap(const QPixmap& pixmap) { + background_pixmap_ = pixmap; + update(); +} -FancyTabWidget::FancyTabWidget(QWidget* parent) - : QWidget(parent), - mode_(Mode_None), - tab_bar_(nullptr), - stack_(new QStackedLayout), - side_widget_(new QWidget), - side_layout_(new QVBoxLayout), - top_layout_(new QVBoxLayout), - use_background_(false), +void FancyTabWidget::setCurrentIndex(int index) { + QWidget* currentPage = widget(index); + + QLayout *layout = currentPage->layout(); + if(bottom_widget_ != nullptr) + layout->addWidget(bottom_widget_); + + QTabWidget::setCurrentIndex(index); +} + +// Slot +void FancyTabWidget::currentTabChanged(int index) { + QWidget* currentPage = currentWidget(); + + QLayout *layout = currentPage->layout(); + if(bottom_widget_ != nullptr) + layout->addWidget(bottom_widget_); +} + +FancyTabWidget::FancyTabWidget(QWidget* parent) : QTabWidget(parent), menu_(nullptr), - proxy_style_(new FancyTabProxyStyle) { - side_layout_->setSpacing(0); - side_layout_->setMargin(0); - side_layout_->addSpacerItem( - new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding)); + mode_(Mode_None), + bottom_widget_(nullptr) { + FancyTabBar *tabBar = new FancyTabBar(this); - side_widget_->setLayout(side_layout_); - side_widget_->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); + setTabBar(tabBar); + setTabPosition(QTabWidget::West); + setMovable(true); - top_layout_->setMargin(0); - top_layout_->setSpacing(0); - top_layout_->addLayout(stack_); - - QHBoxLayout* main_layout = new QHBoxLayout; - main_layout->setMargin(0); - main_layout->setSpacing(1); - main_layout->addWidget(side_widget_); - main_layout->addLayout(top_layout_); - setLayout(main_layout); + connect(tabBar, SIGNAL(currentChanged(int)), this, SLOT(currentTabChanged(int))); } -void FancyTabWidget::AddTab(QWidget* tab, const QIcon& icon, - const QString& label) { - stack_->addWidget(tab); - items_ << Item(icon, label); -} +void FancyTabWidget::loadSettings(const char *kSettingsGroup) { + QSettings settings; + settings.beginGroup(kSettingsGroup); -void FancyTabWidget::AddSpacer(int size) { items_ << Item(size); } + for(int i =0;itabData(i).toInt(); + std::string k = "tab_index_" + std::to_string(originalIndex); -void FancyTabWidget::SetBackgroundPixmap(const QPixmap& pixmap) { - background_pixmap_ = pixmap; - update(); -} + int newIndex = settings.value(QString::fromStdString(k), i).toInt(); -void FancyTabWidget::paintEvent(QPaintEvent*) { - if (!use_background_) return; - - QPainter painter(this); - - QRect rect = side_widget_->rect().adjusted(0, 0, 1, 0); - rect = style()->visualRect(layoutDirection(), geometry(), rect); - Utils::StyleHelper::verticalGradient(&painter, rect, rect); - - if (!background_pixmap_.isNull()) { - QRect pixmap_rect(background_pixmap_.rect()); - pixmap_rect.moveTo(rect.topLeft()); - - while (pixmap_rect.top() < rect.bottom()) { - QRect source_rect(pixmap_rect.intersected(rect)); - source_rect.moveTo(0, 0); - painter.drawPixmap(pixmap_rect.topLeft(), background_pixmap_, - source_rect); - pixmap_rect.moveTop(pixmap_rect.bottom() - 10); - } - } - - painter.setPen(Utils::StyleHelper::borderColor()); - painter.drawLine(rect.topRight(), rect.bottomRight()); - - QColor light = Utils::StyleHelper::sidebarHighlight(); - painter.setPen(light); - painter.drawLine(rect.bottomLeft(), rect.bottomRight()); -} - -int FancyTabWidget::current_index() const { return stack_->currentIndex(); } - -void FancyTabWidget::SetCurrentIndex(int index) { - if (FancyTabBar* bar = qobject_cast(tab_bar_)) { - bar->setCurrentIndex(index); - } else if (QTabBar* bar = qobject_cast(tab_bar_)) { - bar->setCurrentIndex(index); - } else { - stack_->setCurrentIndex(index); - } -} - -void FancyTabWidget::SetCurrentWidget(QWidget* widget) { - SetCurrentIndex(stack_->indexOf(widget)); -} - -void FancyTabWidget::ShowWidget(int index) { - stack_->setCurrentIndex(index); - emit CurrentChanged(index); -} - -void FancyTabWidget::AddBottomWidget(QWidget* widget) { - top_layout_->addWidget(widget); -} - -void FancyTabWidget::SetMode(Mode mode) { - // Remove previous tab bar - delete tab_bar_; - tab_bar_ = nullptr; - - use_background_ = false; - - // Create new tab bar - switch (mode) { - case Mode_None: - default: - qLog(Warning) << "Unknown fancy tab mode" << mode; - // fallthrough - - case Mode_LargeSidebar: { - FancyTabBar* bar = new FancyTabBar(this); - side_layout_->insertWidget(0, bar); - tab_bar_ = bar; - - for (const Item& item : items_) { - if (item.type_ == Item::Type_Spacer) - bar->addSpacer(item.spacer_size_); + if(newIndex >= 0) + tabBar()->moveTab(i,newIndex); else - bar->addTab(item.tab_icon_, item.tab_label_); - } + removeTab(i); // Does not delete page + } +} - bar->setCurrentIndex(stack_->currentIndex()); - connect(bar, SIGNAL(currentChanged(int)), SLOT(ShowWidget(int))); +void FancyTabWidget::saveSettings(const char *kSettingsGroup) { + QSettings settings; + settings.beginGroup(kSettingsGroup); - use_background_ = true; + for(int i =0;itabData(i).toInt(); + std::string k = "tab_index_" + std::to_string(originalIndex); - break; + settings.setValue(QString::fromStdString(k), i); + } +} + + +void FancyTabWidget::addBottomWidget(QWidget* widget) { + bottom_widget_ = widget; +} + +int FancyTabWidget::addTab(QWidget * page, const QIcon & icon, const QString & label) { + return insertTab(count(),page,icon,label); +} + +int FancyTabWidget::insertTab(int index, QWidget * page, const QIcon & icon, const QString & label) { + // In order to achieve the same effect as the "Bottom Widget" of the + // old Nokia based FancyTabWidget a VBoxLayout is used on each page + QVBoxLayout *layout = new QVBoxLayout(); + layout->setSpacing(0); + layout->setContentsMargins(0,0,0,0); + layout->addWidget(page); + + QWidget *newPage = new QWidget(); + newPage->setLayout(layout); + + const int actualIndex = QTabWidget::insertTab(index,newPage,icon,label); + + // Remember the original index. Needed to save order of tabs + tabBar()->setTabData(actualIndex,QVariant(actualIndex)); + return actualIndex; +} + +void FancyTabWidget::paintEvent(QPaintEvent *pe) { + if(mode() != FancyTabWidget::Mode_LargeSidebar && + mode() != FancyTabWidget::Mode_SmallSidebar) { + QTabWidget::paintEvent(pe); + return; + } + QStylePainter p(this); + + // The brown color (Ubuntu) you see on the background gradient + QColor baseColor = StyleHelper::baseColor(); + + QRect backgroundRect = rect(); + backgroundRect.setWidth(((FancyTabBar*)tabBar())->width()); + p.fillRect(backgroundRect,baseColor); + + // Horizontal gradient over the sidebar from transparent to dark + Utils::StyleHelper::verticalGradient(&p,backgroundRect,backgroundRect,false); + + // Draw the translucent png graphics over the gradient fill + { + if (!background_pixmap_.isNull()) { + QRect pixmap_rect(background_pixmap_.rect()); + pixmap_rect.moveTo(backgroundRect.topLeft()); + + while (pixmap_rect.top() < backgroundRect.bottom()) { + QRect source_rect(pixmap_rect.intersected(backgroundRect)); + source_rect.moveTo(0, 0); + p.drawPixmap(pixmap_rect.topLeft(), background_pixmap_,source_rect); + pixmap_rect.moveTop(pixmap_rect.bottom() - 10); + } + } } - case Mode_Tabs: - MakeTabBar(QTabBar::RoundedNorth, true, false, false); - break; + // Shadow effect of the background + { + QColor light(255, 255, 255, 80); + p.setPen(light); + p.drawLine(backgroundRect.topRight() - QPoint(1, 0), backgroundRect.bottomRight() - QPoint(1, 0)); + QColor dark(0, 0, 0, 90); + p.setPen(dark); + p.drawLine(backgroundRect.topLeft(), backgroundRect.bottomLeft()); - case Mode_IconOnlyTabs: - MakeTabBar(QTabBar::RoundedNorth, false, true, false); - break; + p.setPen(Utils::StyleHelper::borderColor()); + p.drawLine(backgroundRect.topRight(), backgroundRect.bottomRight()); + } - case Mode_SmallSidebar: - MakeTabBar(QTabBar::RoundedWest, true, true, true); - use_background_ = true; - break; - - case Mode_PlainSidebar: - MakeTabBar(QTabBar::RoundedWest, true, true, false); - break; - } - - tab_bar_->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); - - mode_ = mode; - emit ModeChanged(mode); - update(); } -void FancyTabWidget::contextMenuEvent(QContextMenuEvent* e) { - if (!menu_) { - menu_ = new QMenu(this); - - QSignalMapper* mapper = new QSignalMapper(this); - QActionGroup* group = new QActionGroup(this); - AddMenuItem(mapper, group, tr("Large sidebar"), Mode_LargeSidebar); - AddMenuItem(mapper, group, tr("Small sidebar"), Mode_SmallSidebar); - AddMenuItem(mapper, group, tr("Plain sidebar"), Mode_PlainSidebar); - AddMenuItem(mapper, group, tr("Tabs on top"), Mode_Tabs); - AddMenuItem(mapper, group, tr("Icons on top"), Mode_IconOnlyTabs); - menu_->addActions(group->actions()); - - connect(mapper, SIGNAL(mapped(int)), SLOT(SetMode(int))); - } - - menu_->popup(e->globalPos()); +void FancyTabWidget::tabBarUpdateGeometry() { + tabBar()->updateGeometry(); } -void FancyTabWidget::AddMenuItem(QSignalMapper* mapper, QActionGroup* group, +void FancyTabWidget::SetMode(FancyTabWidget::Mode mode) { + mode_ = mode; + + if(mode == FancyTabWidget::Mode_Tabs || + mode == FancyTabWidget::Mode_IconOnlyTabs) { + setTabPosition(QTabWidget::North); + } else { + setTabPosition(QTabWidget::West); + } + + tabBar()->updateGeometry(); + updateGeometry(); + + // There appears to be a bug in QTabBar which causes tabSizeHint + // to be ignored thus the need for this second shot repaint + QTimer::singleShot(1,this,SLOT(tabBarUpdateGeometry())); + + emit ModeChanged(mode); +} + +void FancyTabWidget::addMenuItem(QSignalMapper* mapper, QActionGroup* group, const QString& text, Mode mode) { QAction* action = group->addAction(text); action->setCheckable(true); @@ -662,47 +427,22 @@ void FancyTabWidget::AddMenuItem(QSignalMapper* mapper, QActionGroup* group, if (mode == mode_) action->setChecked(true); } -void FancyTabWidget::MakeTabBar(QTabBar::Shape shape, bool text, bool icons, - bool fancy) { - QTabBar* bar = new QTabBar(this); - bar->setShape(shape); - bar->setDocumentMode(true); - bar->setUsesScrollButtons(true); - bar->setElideMode(Qt::ElideRight); - if (shape == QTabBar::RoundedWest) { - bar->setIconSize(QSize(22, 22)); +void FancyTabWidget::contextMenuEvent(QContextMenuEvent* e) { + if (!menu_) { + menu_ = new QMenu(this); + + QSignalMapper* mapper = new QSignalMapper(this); + QActionGroup* group = new QActionGroup(this); + addMenuItem(mapper, group, tr("Large sidebar"), Mode_LargeSidebar); + addMenuItem(mapper, group, tr("Small sidebar"), Mode_SmallSidebar); + addMenuItem(mapper, group, tr("Plain sidebar"), Mode_PlainSidebar); + addMenuItem(mapper, group, tr("Tabs on top"), Mode_Tabs); + addMenuItem(mapper, group, tr("Icons on top"), Mode_IconOnlyTabs); + menu_->addActions(group->actions()); + + connect(mapper, SIGNAL(mapped(int)), SLOT(SetMode(int))); } - if (fancy) { - bar->setStyle(proxy_style_.get()); - } - - if (shape == QTabBar::RoundedNorth) - top_layout_->insertWidget(0, bar); - else - side_layout_->insertWidget(0, bar); - - for (const Item& item : items_) { - if (item.type_ != Item::Type_Tab) continue; - - QString label = item.tab_label_; - if (shape == QTabBar::RoundedWest) { - label = QFontMetrics(font()).elidedText(label, Qt::ElideMiddle, 100); - } - - int tab_id = -1; - if (icons && text) - tab_id = bar->addTab(item.tab_icon_, label); - else if (icons) - tab_id = bar->addTab(item.tab_icon_, QString()); - else if (text) - tab_id = bar->addTab(label); - - bar->setTabToolTip(tab_id, item.tab_label_); - } - - bar->setCurrentIndex(stack_->currentIndex()); - connect(bar, SIGNAL(currentChanged(int)), SLOT(ShowWidget(int))); - tab_bar_ = bar; + menu_->popup(e->globalPos()); } diff --git a/src/widgets/fancytabwidget.h b/src/widgets/fancytabwidget.h index a0a3dd80a..9f9397ae7 100644 --- a/src/widgets/fancytabwidget.h +++ b/src/widgets/fancytabwidget.h @@ -1,233 +1,97 @@ -/************************************************************************** -** -** This file is part of Qt Creator -** -** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). -** -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** Commercial Usage -** -** Licensees holding valid Qt Commercial licenses may use this file in -** accordance with the Qt Commercial License Agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Nokia. -** -** GNU Lesser General Public License Usage -** -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at http://qt.nokia.com/contact. -** -**************************************************************************/ +/* This file is part of Clementine. + Copyright 2018, Vikram Ambrose + + Clementine 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. + + Clementine 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 Clementine. If not, see . +*/ #ifndef FANCYTABWIDGET_H #define FANCYTABWIDGET_H #include -#include -#include -#include -#include -#include +#include #include class QActionGroup; class QMenu; -class QPainter; class QSignalMapper; -class QStackedLayout; -class QStatusBar; -class QVBoxLayout; namespace Core { namespace Internal { -class FancyTabProxyStyle : public QProxyStyle { + +class FancyTabWidget : public QTabWidget { Q_OBJECT - public: - void drawControl(ControlElement element, const QStyleOption* option, - QPainter* painter, const QWidget* widget) const; - void polish(QWidget* widget); - void polish(QApplication* app); - void polish(QPalette& palette); + public: + FancyTabWidget(QWidget* parent = 0); + int addTab(QWidget * page, const QIcon & icon, const QString & label); + int insertTab(int index, QWidget * page, const QIcon & icon, const QString & label); + void addBottomWidget(QWidget* widget); - protected: - bool eventFilter(QObject* o, QEvent* e); -}; + void setBackgroundPixmap(const QPixmap& pixmap); + void addSpacer(); -class FancyTab : public QWidget { - Q_OBJECT + void loadSettings(const char *); + void saveSettings(const char *); + // Values are persisted - only add to the end + enum Mode { + Mode_None = 0, + Mode_LargeSidebar, + Mode_SmallSidebar, + Mode_Tabs, + Mode_IconOnlyTabs, + Mode_PlainSidebar, + }; + + static const QSize TabSize_LargeSidebar; - Q_PROPERTY(float fader READ fader WRITE setFader) - public: - FancyTab(QWidget* tabbar); - float fader() { return m_fader; } - void setFader(float value); + static const QSize IconSize_LargeSidebar; + static const QSize IconSize_SmallSidebar; + + Mode mode() { return mode_; } - QSize sizeHint() const; + signals: + void ModeChanged(FancyTabWidget::Mode mode); - void fadeIn(); - void fadeOut(); + public slots: + void setCurrentIndex(int index); + void SetMode(Mode mode); + // Mapper mapped signal needs this convenience function + void SetMode(int mode) { SetMode(Mode(mode)); } - QIcon icon; - QString text; + private slots: + void tabBarUpdateGeometry(); + void currentTabChanged(int); - protected: - bool event(QEvent*); - void enterEvent(QEvent*); - void leaveEvent(QEvent*); + protected: + void paintEvent(QPaintEvent *); + void contextMenuEvent(QContextMenuEvent* e); + private: + void addMenuItem(QSignalMapper* mapper, QActionGroup* group, + const QString& text, Mode mode); - private: - QPropertyAnimation animator; - QWidget* tabbar; - float m_fader; -}; + QPixmap background_pixmap_; + QMenu* menu_; + Mode mode_; + QWidget *bottom_widget_; -class FancyTabBar : public QWidget { - Q_OBJECT - - public: - FancyTabBar(QWidget* parent = nullptr); - ~FancyTabBar(); - - void paintEvent(QPaintEvent* event); - void paintTab(QPainter* painter, int tabIndex) const; - void mousePressEvent(QMouseEvent*); - bool validIndex(int index) const { - return index >= 0 && index < m_tabs.count(); - } - - QSize sizeHint() const; - QSize minimumSizeHint() const; - - void addTab(const QIcon& icon, const QString& label); - void addSpacer(int size = 40); - void removeTab(int index) { - FancyTab* tab = m_tabs.takeAt(index); - delete tab; - } - void setCurrentIndex(int index); - int currentIndex() const { return m_currentIndex; } - - void setTabToolTip(int index, const QString& toolTip); - QString tabToolTip(int index) const; - - QIcon tabIcon(int index) const { return m_tabs.at(index)->icon; } - QString tabText(int index) const { return m_tabs.at(index)->text; } - int count() const { return m_tabs.count(); } - QRect tabRect(int index) const; - -signals: - void currentChanged(int); - - public slots: - void emitCurrentIndex(); - - private: - static const int m_rounding; - static const int m_textPadding; - int m_currentIndex; - QList m_tabs; - QTimer m_triggerTimer; - QSize tabSizeHint(bool minimum = false) const; -}; - -class FancyTabWidget : public QWidget { - Q_OBJECT - - public: - FancyTabWidget(QWidget* parent = nullptr); - - // Values are persisted - only add to the end - enum Mode { - Mode_None = 0, - Mode_LargeSidebar = 1, - Mode_SmallSidebar = 2, - Mode_Tabs = 3, - Mode_IconOnlyTabs = 4, - Mode_PlainSidebar = 5, - }; - - struct Item { - Item(const QIcon& icon, const QString& label) - : type_(Type_Tab), - tab_label_(label), - tab_icon_(icon), - spacer_size_(0) {} - Item(int size) : type_(Type_Spacer), spacer_size_(size) {} - - enum Type { Type_Tab, Type_Spacer, }; - - Type type_; - QString tab_label_; - QIcon tab_icon_; - int spacer_size_; - }; - - void AddTab(QWidget* tab, const QIcon& icon, const QString& label); - void AddSpacer(int size = 40); - void SetBackgroundPixmap(const QPixmap& pixmap); - - void AddBottomWidget(QWidget* widget); - - int current_index() const; - Mode mode() const { return mode_; } - - public slots: - void SetCurrentIndex(int index); - void SetCurrentWidget(QWidget* widget); - void SetMode(Mode mode); - void SetMode(int mode) { SetMode(Mode(mode)); } - -signals: - void CurrentChanged(int index); - void ModeChanged(FancyTabWidget::Mode mode); - - protected: - void paintEvent(QPaintEvent* event); - void contextMenuEvent(QContextMenuEvent* e); - - private slots: - void ShowWidget(int index); - - private: - void MakeTabBar(QTabBar::Shape shape, bool text, bool icons, bool fancy); - void AddMenuItem(QSignalMapper* mapper, QActionGroup* group, - const QString& text, Mode mode); - - Mode mode_; - QList items_; - - QWidget* tab_bar_; - QStackedLayout* stack_; - QPixmap background_pixmap_; - QWidget* side_widget_; - QVBoxLayout* side_layout_; - QVBoxLayout* top_layout_; - - bool use_background_; - - QMenu* menu_; - - std::unique_ptr proxy_style_; }; } // namespace Internal } // namespace Core -Q_DECLARE_METATYPE(QPropertyAnimation*); - -using Core::Internal::FancyTab; -using Core::Internal::FancyTabBar; using Core::Internal::FancyTabWidget; #endif // FANCYTABWIDGET_H