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*))); SLOT(AddToPlaylist(QMimeData*)));
// Add tabs to the fancy tab widget // Add tabs to the fancy tab widget
ui_->tabs->AddTab(global_search_view_, ui_->tabs->addTab(global_search_view_,
IconLoader::Load("search", IconLoader::Base), IconLoader::Load("search", IconLoader::Base),
tr("Search", "Global search settings dialog title.")); tr("Search", "Global search settings dialog title."));
ui_->tabs->AddTab(library_view_, ui_->tabs->addTab(library_view_,
IconLoader::Load("folder-sound", IconLoader::Base), IconLoader::Load("folder-sound", IconLoader::Base),
tr("Library")); tr("Library"));
ui_->tabs->AddTab(file_view_, ui_->tabs->addTab(file_view_,
IconLoader::Load("document-open", IconLoader::Base), IconLoader::Load("document-open", IconLoader::Base),
tr("Files")); tr("Files"));
ui_->tabs->AddTab(playlist_list_, ui_->tabs->addTab(playlist_list_,
IconLoader::Load("view-media-playlist", IconLoader::Base), IconLoader::Load("view-media-playlist", IconLoader::Base),
tr("Playlists")); tr("Playlists"));
ui_->tabs->AddTab(internet_view_, ui_->tabs->addTab(internet_view_,
IconLoader::Load("applications-internet", IconLoader::Base), IconLoader::Load("applications-internet", IconLoader::Base),
tr("Internet")); tr("Internet"));
ui_->tabs->AddTab( ui_->tabs->addTab(
device_view_container_, device_view_container_,
IconLoader::Load("multimedia-player-ipod-mini-blue", IconLoader::Base), IconLoader::Load("multimedia-player-ipod-mini-blue", IconLoader::Base),
tr("Devices")); tr("Devices"));
ui_->tabs->AddSpacer(); ui_->tabs->addSpacer();
ui_->tabs->AddTab(song_info_view_, ui_->tabs->addTab(song_info_view_,
IconLoader::Load("view-media-lyrics", IconLoader::Base), IconLoader::Load("view-media-lyrics", IconLoader::Base),
tr("Song info")); tr("Song info"));
ui_->tabs->AddTab(artist_info_view_, ui_->tabs->addTab(artist_info_view_,
IconLoader::Load("x-clementine-artist", IconLoader::Base), IconLoader::Load("x-clementine-artist", IconLoader::Base),
tr("Artist info")); tr("Artist info"));
// Add the now playing widget to the fancy tab widget // 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); track_position_timer_->setInterval(kTrackPositionUpdateTimeMs);
connect(track_position_timer_, SIGNAL(timeout()), connect(track_position_timer_, SIGNAL(timeout()),
@ -979,7 +982,7 @@ MainWindow::MainWindow(Application* app, SystemTrayIcon* tray_icon, OSD* osd,
settings_.value("splitter_state").toByteArray())) { settings_.value("splitter_state").toByteArray())) {
ui_->splitter->setSizes(QList<int>() << 300 << width() - 300); ui_->splitter->setSizes(QList<int>() << 300 << width() - 300);
} }
ui_->tabs->SetCurrentIndex( ui_->tabs->setCurrentIndex(
settings_.value("current_tab", 1 /* Library tab */).toInt()); settings_.value("current_tab", 1 /* Library tab */).toInt());
FancyTabWidget::Mode default_mode = FancyTabWidget::Mode_LargeSidebar; FancyTabWidget::Mode default_mode = FancyTabWidget::Mode_LargeSidebar;
ui_->tabs->SetMode( ui_->tabs->SetMode(
@ -1265,8 +1268,10 @@ void MainWindow::SaveGeometry() {
settings_.setValue("geometry", saveGeometry()); settings_.setValue("geometry", saveGeometry());
} }
settings_.setValue("splitter_state", ui_->splitter->saveState()); 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()); settings_.setValue("tab_mode", ui_->tabs->mode());
ui_->tabs->saveSettings(kSettingsGroup);
} }
void MainWindow::SavePlaybackStatus() { void MainWindow::SavePlaybackStatus() {
@ -2911,7 +2916,7 @@ void MainWindow::HandleNotificationPreview(OSD::Behaviour type, QString line1,
void MainWindow::ScrollToInternetIndex(const QModelIndex& index) { void MainWindow::ScrollToInternetIndex(const QModelIndex& index) {
internet_view_->ScrollToIndex(index); internet_view_->ScrollToIndex(index);
ui_->tabs->SetCurrentWidget(internet_view_); ui_->tabs->setCurrentWidget(internet_view_);
} }
void MainWindow::AddPodcast() { void MainWindow::AddPodcast() {
@ -2919,11 +2924,11 @@ void MainWindow::AddPodcast() {
} }
void MainWindow::FocusLibraryTab() { void MainWindow::FocusLibraryTab() {
ui_->tabs->SetCurrentWidget(library_view_); ui_->tabs->setCurrentWidget(library_view_);
} }
void MainWindow::FocusGlobalSearchField() { void MainWindow::FocusGlobalSearchField() {
ui_->tabs->SetCurrentWidget(global_search_view_); ui_->tabs->setCurrentWidget(global_search_view_);
global_search_view_->FocusSearchField(); 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 Clementine.
** Copyright 2018, Vikram Ambrose <ambroseworks@gmail.com>
** This file is part of Qt Creator
** Clementine is free software: you can redistribute it and/or modify
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** Contact: Nokia Corporation (qt-info@nokia.com) (at your option) any later version.
**
** Commercial Usage Clementine is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** Licensees holding valid Qt Commercial licenses may use this file in MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** accordance with the Qt Commercial License Agreement provided with the GNU General Public License for more details.
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia. You should have received a copy of the GNU General Public License
** along with Clementine. If not, see <http://www.gnu.org/licenses/>.
** 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.
**
**************************************************************************/
#ifndef FANCYTABWIDGET_H #ifndef FANCYTABWIDGET_H
#define FANCYTABWIDGET_H #define FANCYTABWIDGET_H
#include <memory> #include <memory>
#include <QIcon> #include <QTabWidget>
#include <QPropertyAnimation>
#include <QProxyStyle>
#include <QTabBar>
#include <QTimer>
#include <QWidget> #include <QWidget>
class QActionGroup; class QActionGroup;
class QMenu; class QMenu;
class QPainter;
class QSignalMapper; class QSignalMapper;
class QStackedLayout;
class QStatusBar;
class QVBoxLayout;
namespace Core { namespace Core {
namespace Internal { namespace Internal {
class FancyTabProxyStyle : public QProxyStyle {
class FancyTabWidget : public QTabWidget {
Q_OBJECT Q_OBJECT
public: public:
void drawControl(ControlElement element, const QStyleOption* option, FancyTabWidget(QWidget* parent = 0);
QPainter* painter, const QWidget* widget) const; int addTab(QWidget * page, const QIcon & icon, const QString & label);
void polish(QWidget* widget); int insertTab(int index, QWidget * page, const QIcon & icon, const QString & label);
void polish(QApplication* app); void addBottomWidget(QWidget* widget);
void polish(QPalette& palette);
protected: void setBackgroundPixmap(const QPixmap& pixmap);
bool eventFilter(QObject* o, QEvent* e); void addSpacer();
};
class FancyTab : public QWidget { void loadSettings(const char *);
Q_OBJECT 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) static const QSize IconSize_LargeSidebar;
public: static const QSize IconSize_SmallSidebar;
FancyTab(QWidget* tabbar);
float fader() { return m_fader; } Mode mode() { return mode_; }
void setFader(float value);
QSize sizeHint() const; signals:
void ModeChanged(FancyTabWidget::Mode mode);
void fadeIn(); public slots:
void fadeOut(); void setCurrentIndex(int index);
void SetMode(Mode mode);
// Mapper mapped signal needs this convenience function
void SetMode(int mode) { SetMode(Mode(mode)); }
QIcon icon; private slots:
QString text; void tabBarUpdateGeometry();
void currentTabChanged(int);
protected: protected:
bool event(QEvent*); void paintEvent(QPaintEvent *);
void enterEvent(QEvent*); void contextMenuEvent(QContextMenuEvent* e);
void leaveEvent(QEvent*); private:
void addMenuItem(QSignalMapper* mapper, QActionGroup* group,
const QString& text, Mode mode);
private: QPixmap background_pixmap_;
QPropertyAnimation animator; QMenu* menu_;
QWidget* tabbar; Mode mode_;
float m_fader; 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 Internal
} // namespace Core } // namespace Core
Q_DECLARE_METATYPE(QPropertyAnimation*);
using Core::Internal::FancyTab;
using Core::Internal::FancyTabBar;
using Core::Internal::FancyTabWidget; using Core::Internal::FancyTabWidget;
#endif // FANCYTABWIDGET_H #endif // FANCYTABWIDGET_H