From edf3db8bf44c80933fc1f3aa9c7dfb95d683213c Mon Sep 17 00:00:00 2001 From: Roland Pallai Date: Sun, 23 Jun 2019 22:42:16 +0200 Subject: [PATCH] Turn settings menu into a dialog window Closes #553. --- CMakeLists.txt | 9 +- client/mainwindow.cpp | 139 +++++----------------- client/mainwindow.h | 1 + client/settingsdialog.cpp | 120 +++++++++++++++++++ client/settingsdialog.h | 78 ++++++++++++ client/settingsgeneralpage.ui | 216 ++++++++++++++++++++++++++++++++++ 6 files changed, 452 insertions(+), 111 deletions(-) create mode 100644 client/settingsdialog.cpp create mode 100644 client/settingsdialog.h create mode 100644 client/settingsgeneralpage.ui diff --git a/CMakeLists.txt b/CMakeLists.txt index 9317ca233..3659b46d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -142,6 +142,7 @@ set(quaternion_SRCS client/activitydetector.cpp client/dialog.cpp client/logindialog.cpp + client/settingsdialog.cpp client/networkconfigdialog.cpp client/roomdialogs.cpp client/mainwindow.cpp @@ -163,6 +164,10 @@ set(quaternion_QRC client/resources.qrc ) +set(quaternion_UI + client/settingsgeneralpage.ui + ) + # quaternion_en.ts is updated explicitly by building trbase target, # while all other translation files are created and updated externally at # Lokalise.co @@ -178,6 +183,8 @@ set(quaternion_TS ) QT5_ADD_TRANSLATION(quaternion_QM ${quaternion_TS}) +qt5_wrap_ui(quaternion_UI_OUT ${quaternion_UI}) + QT5_ADD_RESOURCES(quaternion_QRC_SRC ${quaternion_QRC}) set_property(SOURCE qrc_resources.cpp PROPERTY SKIP_AUTOMOC ON) @@ -206,7 +213,7 @@ endif(APPLE) # Windows, this is a GUI executable; OSX, make a bundle add_executable(${PROJECT_NAME} WIN32 MACOSX_BUNDLE - ${quaternion_SRCS} ${quaternion_QRC_SRC} ${quaternion_QM} + ${quaternion_SRCS} ${quaternion_UI_OUT} ${quaternion_QRC_SRC} ${quaternion_QM} ${quaternion_WINRC} ${${PROJECT_NAME}_MAC_ICON}) target_link_libraries(${PROJECT_NAME} diff --git a/client/mainwindow.cpp b/client/mainwindow.cpp index 6c89bbfea..a1ab8730d 100644 --- a/client/mainwindow.cpp +++ b/client/mainwindow.cpp @@ -23,6 +23,7 @@ #include "userlistdock.h" #include "chatroomwidget.h" #include "logindialog.h" +#include "settingsdialog.h" #include "networkconfigdialog.h" #include "roomdialogs.h" #include "systemtrayicon.h" @@ -168,6 +169,24 @@ void MainWindow::createMenu() { using QMatrixClient::Settings; + // Application menu + auto applicationMenu = menuBar()->addMenu(tr("A&pplication")); + applicationMenu->addAction(QIcon::fromTheme("preferences"), tr("&Preferences..."), + this, [=]{ showSettingsWindow(); } ); + + applicationMenu->addAction(QIcon::fromTheme("preferences-system-network"), + tr("Configure &network proxy..."), [this] + { + static QPointer dlg; + summon(dlg, this); + }); + + // Augment poor Windows users with a handy Ctrl-Q shortcut. + static const auto quitShortcut = QSysInfo::productType() == "windows" + ? QKeySequence(Qt::CTRL + Qt::Key_Q) : QKeySequence::Quit; + applicationMenu->addAction(QIcon::fromTheme("application-exit"), + tr("&Quit"), qApp, &QApplication::quit, quitShortcut); + // Connection menu connectionMenu = menuBar()->addMenu(tr("&Accounts")); @@ -178,12 +197,6 @@ void MainWindow::createMenu() // Account submenus will be added in this place - see addConnection() accountListGrowthPoint = connectionMenu->addSeparator(); - // Augment poor Windows users with a handy Ctrl-Q shortcut. - static const auto quitShortcut = QSysInfo::productType() == "windows" - ? QKeySequence(Qt::CTRL + Qt::Key_Q) : QKeySequence::Quit; - connectionMenu->addAction(QIcon::fromTheme("application-exit"), - tr("&Quit"), qApp, &QApplication::quit, quitShortcut); - // View menu auto viewMenu = menuBar()->addMenu(tr("&View")); @@ -318,114 +331,10 @@ void MainWindow::createMenu() tr("&Close current room"), [this] { selectRoom(nullptr); }, QKeySequence::Close); - // Settings menu - auto settingsMenu = menuBar()->addMenu(tr("&Settings")); - // Help menu auto helpMenu = menuBar()->addMenu(tr("&Help")); helpMenu->addAction(QIcon::fromTheme("help-about"), tr("&About"), [=]{ showAboutWindow(); }); - - { - auto notifGroup = new QActionGroup(this); - connect(notifGroup, &QActionGroup::triggered, this, - [] (QAction* notifAction) - { - notifAction->setChecked(true); - Settings().setValue("UI/notifications", - notifAction->data().toString()); - }); - - auto noNotif = notifGroup->addAction(tr("&Highlight only")); - noNotif->setData(QStringLiteral("none")); - noNotif->setStatusTip(tr("Notifications are entirely suppressed")); - auto gentleNotif = notifGroup->addAction(tr("&Non-intrusive")); - gentleNotif->setData(QStringLiteral("non-intrusive")); - gentleNotif->setStatusTip( - tr("Show notifications but do not activate the window")); - auto fullNotif = notifGroup->addAction(tr("&Full")); - fullNotif->setData(QStringLiteral("intrusive")); - fullNotif->setStatusTip( - tr("Show notifications and activate the window")); - - auto notifMenu = settingsMenu->addMenu( - QIcon::fromTheme("preferences-desktop-notification"), - tr("Notifications")); - for (auto a: {noNotif, gentleNotif, fullNotif}) - { - a->setCheckable(true); - notifMenu->addAction(a); - } - - const auto curSetting = Settings().value("UI/notifications", - fullNotif->data().toString()); - if (curSetting == noNotif->data().toString()) - noNotif->setChecked(true); - else if (curSetting == gentleNotif->data().toString()) - gentleNotif->setChecked(true); - else - fullNotif->setChecked(true); - } - { - auto layoutGroup = new QActionGroup(this); - connect(layoutGroup, &QActionGroup::triggered, this, - [this] (QAction* action) - { - action->setChecked(true); - Settings().setValue("UI/timeline_style", action->data().toString()); - chatRoomWidget->setRoom(nullptr); - chatRoomWidget->setRoom(currentRoom); - }); - - auto defaultLayout = layoutGroup->addAction(tr("Default")); - defaultLayout->setStatusTip( - tr("The layout with author labels above blocks of messages")); - auto xchatLayout = layoutGroup->addAction("XChat"); - xchatLayout->setData(QStringLiteral("xchat")); - xchatLayout->setStatusTip( - tr("The layout with author labels to the left from each message")); - - auto layoutMenu = settingsMenu->addMenu(QIcon::fromTheme("table"), - tr("Timeline layout")); - for (auto a: {defaultLayout, xchatLayout}) - { - a->setCheckable(true); - layoutMenu->addAction(a); - } - - const auto curSetting = Settings().value("UI/timeline_style", - defaultLayout->data().toString()); - if (curSetting == xchatLayout->data().toString()) - xchatLayout->setChecked(true); - else - defaultLayout->setChecked(true); - } - addTimelineOptionCheckbox( - settingsMenu, - tr("Use shuttle scrollbar (requires restart)"), - tr("Control scroll velocity instead of position with the timeline scrollbar"), - QStringLiteral("use_shuttle_dial"), true - ); - addTimelineOptionCheckbox( - settingsMenu, - tr("Load full-size images at once"), - tr("Automatically download a full-size image instead of a thumbnail"), - QStringLiteral("autoload_images"), true - ); - addTimelineOptionCheckbox( - settingsMenu, - tr("Close to tray"), - tr("Make close button [X] minimize to tray instead of closing main window"), - QStringLiteral("close_to_tray"), false - ); - - settingsMenu->addSeparator(); - settingsMenu->addAction(QIcon::fromTheme("preferences-system-network"), - tr("Configure &network proxy..."), [this] - { - static QPointer dlg; - summon(dlg, this); - }); } void MainWindow::loadSettings() @@ -888,6 +797,16 @@ void MainWindow::processLogin(LoginDialog& dialog) addConnection(connection, deviceName); } +void MainWindow::showSettingsWindow() +{ + SettingsDialog settingsDialog(this); + connect(&settingsDialog, &SettingsDialog::timelineChanged, this, [=]() { + chatRoomWidget->setRoom(nullptr); + chatRoomWidget->setRoom(currentRoom); + }); + settingsDialog.exec(); +} + void MainWindow::showAboutWindow() { Dialog aboutDialog(tr("About Quaternion"), QDialogButtonBox::Close, diff --git a/client/mainwindow.h b/client/mainwindow.h index cb2e0776a..ac12e2cdb 100644 --- a/client/mainwindow.h +++ b/client/mainwindow.h @@ -91,6 +91,7 @@ class MainWindow: public QMainWindow void showLoginWindow(const QString& statusMessage = {}); void showLoginWindow(const QString& statusMessage, QMatrixClient::AccountSettings& reloginAccount); + void showSettingsWindow(); void showAboutWindow(); void logout(Connection* c); diff --git a/client/settingsdialog.cpp b/client/settingsdialog.cpp new file mode 100644 index 000000000..cb06abfba --- /dev/null +++ b/client/settingsdialog.cpp @@ -0,0 +1,120 @@ +/************************************************************************** + * * + * Copyright (C) 2019 Roland Pallai * + * * + * This program 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. * + * * + * This program 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 this program. If not, see . * + * * + **************************************************************************/ + +#include "settingsdialog.h" +#include "mainwindow.h" +#include + +#include +#include +#include + +using QMatrixClient::SettingsGroup; + +SettingsDialog::SettingsDialog(MainWindow *parent) : QDialog(parent) { + setWindowTitle(tr("Settings")); + + QVBoxLayout *layout = new QVBoxLayout(this); + stack = new QTabWidget(this); + stack->setTabBarAutoHide(true); + layout->addWidget(stack); + stack->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + + auto generalPage = new GeneralPage(this); + connect(generalPage, &GeneralPage::timelineChanged, this, [this] { + emit timelineChanged(); + }); + addPage(generalPage, tr("&General")); +} + +SettingsDialog::~SettingsDialog() = default; + +void SettingsDialog::addPage(ConfigurationWidgetInterface *page, const QString &title) +{ + stack->addTab(page->asWidget(), title); + pages << page; +} + +// +// GeneralPage +// +GeneralPage::GeneralPage(SettingsDialog *parent): + QWidget(parent), Ui_GeneralPage(), m_parent(parent) +{ + Ui_GeneralPage::setupUi(this); + + // closeToTray + closeToTray->setChecked(SettingsGroup("UI").value("close_to_tray", false).toBool()); + connect(closeToTray, &QAbstractButton::toggled, this, [=](bool checked) { + SettingsGroup("UI").setValue("close_to_tray", checked); + }); + + // timeline_layout + const auto curTimelineStyle = SettingsGroup("UI").value("timeline_style", "default"); + timeline_layout->addItem(tr("Default"), QVariant(QStringLiteral("default"))); + timeline_layout->addItem(tr("XChat"), QVariant(QStringLiteral("xchat"))); + if (curTimelineStyle == "xchat") + timeline_layout->setCurrentText(tr("XChat")); + + connect(timeline_layout, QOverload::of(&QComboBox::currentIndexChanged), this, [this](int index) { + auto new_layout = timeline_layout->itemData(index).toString(); + SettingsGroup("UI").setValue("timeline_style", new_layout); + emit timelineChanged(); + }); + + // useShuttleScrollbar + useShuttleScrollbar->setChecked(SettingsGroup("UI").value("use_shuttle_dial", true).toBool()); + connect(useShuttleScrollbar, &QAbstractButton::toggled, this, [=](bool checked) { + SettingsGroup("UI").setValue("use_shuttle_dial", checked); + // needs restart instead + //emit timelineChanged(); + }); + + // loadFullSizeImages + loadFullSizeImages->setChecked(SettingsGroup("UI").value("autoload_images", true).toBool()); + connect(loadFullSizeImages, &QAbstractButton::toggled, this, [=](bool checked) { + SettingsGroup("UI").setValue("autoload_images", checked); + emit timelineChanged(); + }); + + // *Notif + const auto curNotifications = SettingsGroup("UI").value("notifications", "intrusive"); + if (curNotifications == "intrusive") { + fullNotif->setChecked(true); + } else if (curNotifications == "non-intrusive") { + gentleNotif->setChecked(true); + } else if (curNotifications == "none") { + noNotif->setChecked(true); + } + + connect(noNotif, &QAbstractButton::clicked, this, [=]() { + SettingsGroup("UI").setValue("notifications", "none"); + }); + connect(gentleNotif, &QAbstractButton::clicked, this, [=]() { + SettingsGroup("UI").setValue("notifications", "non-intrusive"); + }); + connect(fullNotif, &QAbstractButton::clicked, this, [=]() { + SettingsGroup("UI").setValue("notifications", "intrusive"); + }); +} + +QWidget *GeneralPage::asWidget() +{ + return this; +} diff --git a/client/settingsdialog.h b/client/settingsdialog.h new file mode 100644 index 000000000..ab10e8421 --- /dev/null +++ b/client/settingsdialog.h @@ -0,0 +1,78 @@ +/************************************************************************** + * * + * Copyright (C) 2019 Roland Pallai * + * * + * This program 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. * + * * + * This program 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 this program. If not, see . * + * * + **************************************************************************/ + +#ifndef SETTINGSDIALOG_H +#define SETTINGSDIALOG_H + +#include "ui_settingsgeneralpage.h" + +#include + +class SettingsDialog; +class MainWindow; +class QTabWidget; + +/** Common interface for any page of the settings dialogue + * + * Following the modern UX standards changes are instantly applied. However, it is + * highly likely that there will be need for transactional input in the future. Use + * own 'Apply' button for each input form in this case and leave the rest of the + * controls as instant apply. + */ +class ConfigurationWidgetInterface { +public: + virtual QWidget *asWidget() = 0; +}; + +class SettingsDialog : public QDialog +{ + Q_OBJECT +public: + SettingsDialog(MainWindow *parent); + ~SettingsDialog() override; + +private: + QTabWidget *stack; + QVector pages; + + void addPage(ConfigurationWidgetInterface *page, const QString &title); + +signals: + // need a model for settings in the future to get rid of this + void timelineChanged(); +}; + +// +// GeneralPage +// +class GeneralPage : public QWidget, Ui_GeneralPage, public ConfigurationWidgetInterface +{ + Q_OBJECT +public: + GeneralPage(SettingsDialog *parent); + virtual QWidget *asWidget(); + +private: + SettingsDialog *m_parent; + +signals: + void timelineChanged(); +}; + +#endif /* SETTINGSDIALOG_H */ diff --git a/client/settingsgeneralpage.ui b/client/settingsgeneralpage.ui new file mode 100644 index 000000000..5291039a8 --- /dev/null +++ b/client/settingsgeneralpage.ui @@ -0,0 +1,216 @@ + + + GeneralPage + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + + + + + 12 + false + + + + General + + + + + + + Qt::Horizontal + + + + + + + Make close button [X] minimize to tray instead of closing main window. + + + Close to tray + + + + + + + + + + QFrame::NoFrame + + + + + + + 12 + false + + + + Timeline + + + + + + + Qt::Horizontal + + + + + + + QFormLayout::FieldsStayAtSizeHint + + + + + Layout + + + + + + + + + + + + Control scroll velocity instead of position with the timeline scrollbar. Requires restart. + + + Use shuttle scrollbar + + + + + + + Automatically download a full-size image instead of a thumbnail. + + + Load full-size images + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + + + + + 12 + + + + Notifications + + + + + + + Qt::Horizontal + + + + + + + + + Show notifications and activate the window. + + + Full + + + + + + + Show notifications but do not activate the window. + + + Non-intrusive + + + + + + + Notifications are entirely suppressed. + + + Highlight only + + + + + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + +