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
This commit is contained in:
Vikram Ambrose 2018-01-10 11:22:05 +02:00 committed by John Maguire
parent 2e4a67b8da
commit 790fc2ff28
3 changed files with 457 additions and 848 deletions

View File

@ -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<int>() << 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();
}

File diff suppressed because it is too large Load Diff

View File

@ -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 <ambroseworks@gmail.com>
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 <http://www.gnu.org/licenses/>.
*/
#ifndef FANCYTABWIDGET_H
#define FANCYTABWIDGET_H
#include <memory>
#include <QIcon>
#include <QPropertyAnimation>
#include <QProxyStyle>
#include <QTabBar>
#include <QTimer>
#include <QTabWidget>
#include <QWidget>
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<FancyTab*> 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<Item> 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<FancyTabProxyStyle> 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