diff --git a/Source/Core/DolphinQt2/CMakeLists.txt b/Source/Core/DolphinQt2/CMakeLists.txt index 6ddffd597f..66ba436282 100644 --- a/Source/Core/DolphinQt2/CMakeLists.txt +++ b/Source/Core/DolphinQt2/CMakeLists.txt @@ -65,6 +65,7 @@ set(SRCS GameList/ListProxyModel.cpp QtUtils/DoubleClickEventFilter.cpp QtUtils/ElidedButton.cpp + QtUtils/ListTabWidget.cpp QtUtils/WindowActivationEventFilter.cpp Settings/AudioPane.cpp Settings/GeneralPane.cpp diff --git a/Source/Core/DolphinQt2/Config/SettingsWindow.cpp b/Source/Core/DolphinQt2/Config/SettingsWindow.cpp index 3d9cf7320f..4099af0cd4 100644 --- a/Source/Core/DolphinQt2/Config/SettingsWindow.cpp +++ b/Source/Core/DolphinQt2/Config/SettingsWindow.cpp @@ -3,16 +3,11 @@ // Refer to the license.txt file included. #include -#include -#include -#include -#include -#include -#include #include #include "DolphinQt2/Config/SettingsWindow.h" #include "DolphinQt2/MainWindow.h" +#include "DolphinQt2/QtUtils/ListTabWidget.h" #include "DolphinQt2/Resources.h" #include "DolphinQt2/Settings.h" #include "DolphinQt2/Settings/AudioPane.h" @@ -20,6 +15,16 @@ #include "DolphinQt2/Settings/InterfacePane.h" #include "DolphinQt2/Settings/PathPane.h" +static int AddTab(ListTabWidget* tab_widget, const QString& label, QWidget* widget, + const char* icon_name) +{ + int index = tab_widget->addTab(widget, label); + auto set_icon = [=] { tab_widget->setTabIcon(index, Resources::GetScaledThemeIcon(icon_name)); }; + QObject::connect(&Settings::Instance(), &Settings::ThemeChanged, set_icon); + set_icon(); + return index; +} + SettingsWindow::SettingsWindow(QWidget* parent) : QDialog(parent) { // Set Window Properties @@ -28,21 +33,21 @@ SettingsWindow::SettingsWindow(QWidget* parent) : QDialog(parent) // Main Layout QVBoxLayout* layout = new QVBoxLayout; - QHBoxLayout* content = new QHBoxLayout; - // Content's widgets - { - // Category list - MakeCategoryList(); - content->addWidget(m_categories); - - // Actual Settings UI - SetupSettingsWidget(); - - content->addWidget(m_settings_outer); - } // Add content to layout before dialog buttons. - layout->addLayout(content); + m_tabs = new ListTabWidget(); + layout->addWidget(m_tabs); + + AddTab(m_tabs, tr("General"), new GeneralPane(), "config"); + AddTab(m_tabs, tr("Interface"), new InterfacePane(), "browse"); + auto* audio_pane = new AudioPane; + m_audio_pane_index = AddTab(m_tabs, tr("Audio"), audio_pane, "play"); + AddTab(m_tabs, tr("Paths"), new PathPane(), "browse"); + + connect(this, &SettingsWindow::EmulationStarted, + [audio_pane] { audio_pane->OnEmulationStateChanged(true); }); + connect(this, &SettingsWindow::EmulationStopped, + [audio_pane] { audio_pane->OnEmulationStateChanged(false); }); // Dialog box buttons QDialogButtonBox* ok_box = new QDialogButtonBox(QDialogButtonBox::Ok); @@ -52,59 +57,7 @@ SettingsWindow::SettingsWindow(QWidget* parent) : QDialog(parent) setLayout(layout); } -void SettingsWindow::SetupSettingsWidget() +void SettingsWindow::SelectAudioPane() { - m_settings_outer = new QStackedWidget; - m_settings_outer->setCurrentIndex(0); - - // Panes initalised here - m_settings_outer->addWidget(new GeneralPane); - m_settings_outer->addWidget(new InterfacePane); - - auto* audio_pane = new AudioPane; - connect(this, &SettingsWindow::EmulationStarted, - [audio_pane] { audio_pane->OnEmulationStateChanged(true); }); - connect(this, &SettingsWindow::EmulationStopped, - [audio_pane] { audio_pane->OnEmulationStateChanged(false); }); - m_settings_outer->addWidget(audio_pane); - - m_settings_outer->addWidget(new PathPane); -} - -void SettingsWindow::AddCategoryToList(const QString& title, const std::string& icon_name) -{ - QListWidgetItem* button = new QListWidgetItem(); - button->setText(title); - button->setTextAlignment(Qt::AlignVCenter); - button->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - - auto set_icon = [=] { button->setIcon(Resources::GetScaledThemeIcon(icon_name)); }; - QObject::connect(&Settings::Instance(), &Settings::ThemeChanged, set_icon); - set_icon(); - m_categories->addItem(button); -} - -void SettingsWindow::MakeCategoryList() -{ - m_categories = new QListWidget; - m_categories->setIconSize(QSize(32, 32)); - m_categories->setMovement(QListView::Static); - m_categories->setSpacing(0); - - AddCategoryToList(tr("General"), "config"); - AddCategoryToList(tr("Interface"), "browse"); - AddCategoryToList(tr("Audio"), "play"); - AddCategoryToList(tr("Paths"), "browse"); - connect(m_categories, &QListWidget::currentItemChanged, this, &SettingsWindow::changePage); - - m_categories->setFixedWidth(m_categories->sizeHintForColumn(0) + - m_categories->verticalScrollBar()->sizeHint().width() + - 2 * m_categories->frameWidth()); -} - -void SettingsWindow::changePage(QListWidgetItem* current, QListWidgetItem* previous) -{ - if (!current) - current = previous; - m_settings_outer->setCurrentIndex(m_categories->row(current)); + m_tabs->setCurrentIndex(m_audio_pane_index); } diff --git a/Source/Core/DolphinQt2/Config/SettingsWindow.h b/Source/Core/DolphinQt2/Config/SettingsWindow.h index 1feeec8661..5733cb03da 100644 --- a/Source/Core/DolphinQt2/Config/SettingsWindow.h +++ b/Source/Core/DolphinQt2/Config/SettingsWindow.h @@ -4,32 +4,22 @@ #pragma once -#include - #include -class MainWindow; -class QGroupBox; -class QListWidget; -class QListWidgetItem; -class QStackedWidget; +class ListTabWidget; class SettingsWindow final : public QDialog { Q_OBJECT public: explicit SettingsWindow(QWidget* parent = nullptr); + void SelectAudioPane(); + signals: void EmulationStarted(); void EmulationStopped(); -public slots: - void changePage(QListWidgetItem* current, QListWidgetItem* previous); - private: - void MakeCategoryList(); - void AddCategoryToList(const QString& title, const std::string& icon_name); - void SetupSettingsWidget(); - QStackedWidget* m_settings_outer; - QListWidget* m_categories; + ListTabWidget* m_tabs; + int m_audio_pane_index = -1; }; diff --git a/Source/Core/DolphinQt2/DolphinQt2.vcxproj b/Source/Core/DolphinQt2/DolphinQt2.vcxproj index 1a51b5e633..e51b6c9006 100644 --- a/Source/Core/DolphinQt2/DolphinQt2.vcxproj +++ b/Source/Core/DolphinQt2/DolphinQt2.vcxproj @@ -185,6 +185,7 @@ + @@ -214,6 +215,7 @@ + diff --git a/Source/Core/DolphinQt2/MainWindow.cpp b/Source/Core/DolphinQt2/MainWindow.cpp index 4f1350ab22..0ed3447a48 100644 --- a/Source/Core/DolphinQt2/MainWindow.cpp +++ b/Source/Core/DolphinQt2/MainWindow.cpp @@ -184,6 +184,10 @@ void MainWindow::ConnectMenuBar() connect(m_menu_bar, &MenuBar::SetStateSlot, this, &MainWindow::SetStateSlot); // Options + connect(m_menu_bar, &MenuBar::Configure, this, &MainWindow::ShowSettingsWindow); + connect(m_menu_bar, &MenuBar::ConfigureGraphics, this, &MainWindow::ShowGraphicsWindow); + connect(m_menu_bar, &MenuBar::ConfigureAudio, this, &MainWindow::ShowAudioWindow); + connect(m_menu_bar, &MenuBar::ConfigureControllers, this, &MainWindow::ShowControllersWindow); connect(m_menu_bar, &MenuBar::ConfigureHotkeys, this, &MainWindow::ShowHotkeyDialog); // Tools @@ -505,6 +509,12 @@ void MainWindow::ShowSettingsWindow() m_settings_window->activateWindow(); } +void MainWindow::ShowAudioWindow() +{ + m_settings_window->SelectAudioPane(); + ShowSettingsWindow(); +} + void MainWindow::ShowAboutDialog() { AboutDialog* about = new AboutDialog(this); diff --git a/Source/Core/DolphinQt2/MainWindow.h b/Source/Core/DolphinQt2/MainWindow.h index d44d0a4db4..83ef56368a 100644 --- a/Source/Core/DolphinQt2/MainWindow.h +++ b/Source/Core/DolphinQt2/MainWindow.h @@ -83,6 +83,7 @@ private: void HideRenderWidget(); void ShowSettingsWindow(); + void ShowAudioWindow(); void ShowControllersWindow(); void ShowGraphicsWindow(); void ShowAboutDialog(); diff --git a/Source/Core/DolphinQt2/MenuBar.cpp b/Source/Core/DolphinQt2/MenuBar.cpp index 6dbbb89f4b..9d2b4107d9 100644 --- a/Source/Core/DolphinQt2/MenuBar.cpp +++ b/Source/Core/DolphinQt2/MenuBar.cpp @@ -197,7 +197,12 @@ void MenuBar::AddViewMenu() void MenuBar::AddOptionsMenu() { QMenu* options_menu = addMenu(tr("Options")); - options_menu->addAction(tr("Hotkey Settings"), this, &MenuBar::ConfigureHotkeys); + options_menu->addAction(tr("Co&nfiguration..."), this, &MenuBar::Configure); + options_menu->addSeparator(); + options_menu->addAction(tr("&Graphics Settings..."), this, &MenuBar::ConfigureGraphics); + options_menu->addAction(tr("&Audio Settings..."), this, &MenuBar::ConfigureAudio); + options_menu->addAction(tr("&Controller Settings..."), this, &MenuBar::ConfigureControllers); + options_menu->addAction(tr("&Hotkey Settings..."), this, &MenuBar::ConfigureHotkeys); } void MenuBar::AddHelpMenu() diff --git a/Source/Core/DolphinQt2/MenuBar.h b/Source/Core/DolphinQt2/MenuBar.h index 848fad1e67..b4cf5309e6 100644 --- a/Source/Core/DolphinQt2/MenuBar.h +++ b/Source/Core/DolphinQt2/MenuBar.h @@ -44,6 +44,10 @@ signals: void PerformOnlineUpdate(const std::string& region); // Options + void Configure(); + void ConfigureGraphics(); + void ConfigureAudio(); + void ConfigureControllers(); void ConfigureHotkeys(); // View diff --git a/Source/Core/DolphinQt2/QtUtils/ListTabWidget.cpp b/Source/Core/DolphinQt2/QtUtils/ListTabWidget.cpp new file mode 100644 index 0000000000..a7a94618c2 --- /dev/null +++ b/Source/Core/DolphinQt2/QtUtils/ListTabWidget.cpp @@ -0,0 +1,79 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "DolphinQt2/QtUtils/ListTabWidget.h" + +#include +#include +#include +#include + +class OverriddenListWidget : public QListWidget +{ +public: + // We want this widget to have a fixed width that fits all items, unlike a normal QListWidget + // which adds scrollbars and expands/contracts. + OverriddenListWidget() { setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); } + QSize sizeHint() const override + { + int width = sizeHintForColumn(0) + verticalScrollBar()->sizeHint().width() + 2 * frameWidth(); + int height = QListWidget::sizeHint().height(); + return {width, height}; + } + + // Since this is trying to emulate tabs, an item should always be selected. If the selection tries + // to change to empty, don't acknowledge it. + void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) override + { + if (selected.indexes().empty()) + return; + QListWidget::selectionChanged(selected, deselected); + } +}; + +ListTabWidget::ListTabWidget() +{ + QHBoxLayout* layout = new QHBoxLayout(); + layout->setContentsMargins(0, 0, 0, 0); + setLayout(layout); + + m_labels = new OverriddenListWidget(); + layout->addWidget(m_labels); + m_labels->setIconSize(QSize(32, 32)); + m_labels->setMovement(QListView::Static); + m_labels->setSpacing(0); + + m_display = new QStackedWidget(); + layout->addWidget(m_display); + + connect(m_labels, &QListWidget::currentItemChanged, this, + [=](QListWidgetItem* current, QListWidgetItem* previous) { + m_display->setCurrentIndex(m_labels->row(current)); + }); +} + +int ListTabWidget::addTab(QWidget* page, const QString& label) +{ + QListWidgetItem* button = new QListWidgetItem(); + button->setText(label); + button->setTextAlignment(Qt::AlignVCenter); + button->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); + + m_labels->addItem(button); + if (!m_labels->currentItem()) + m_labels->setCurrentItem(button); + + return m_display->addWidget(page); +} + +void ListTabWidget::setTabIcon(int index, const QIcon& icon) +{ + if (auto* label = m_labels->item(index)) + label->setIcon(icon); +} + +void ListTabWidget::setCurrentIndex(int index) +{ + m_labels->setCurrentRow(index); +} diff --git a/Source/Core/DolphinQt2/QtUtils/ListTabWidget.h b/Source/Core/DolphinQt2/QtUtils/ListTabWidget.h new file mode 100644 index 0000000000..57a23db36c --- /dev/null +++ b/Source/Core/DolphinQt2/QtUtils/ListTabWidget.h @@ -0,0 +1,23 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include + +class QListWidget; +class QStackedWidget; + +class ListTabWidget : public QWidget +{ +public: + ListTabWidget(); + int addTab(QWidget* page, const QString& label); + void setTabIcon(int index, const QIcon& icon); + void setCurrentIndex(int index); + +private: + QListWidget* m_labels; + QStackedWidget* m_display; +};