From ade98b4cf6630635e2fdbf84ac29fe83a79cc371 Mon Sep 17 00:00:00 2001 From: ConfuSomu Date: Tue, 15 Aug 2023 19:42:18 -0400 Subject: Implement settings dialog --- src/main.cpp | 4 + src/mainwindow.cpp | 16 ++++ src/mainwindow.h | 4 + src/mainwindow.ui | 12 +++ src/settings_interface.cpp | 120 ++++++++++++++++++++++++ src/settings_interface.h | 46 ++++++++++ src/settingsdialog.cpp | 106 ++++++++++++++++++++++ src/settingsdialog.h | 43 +++++++++ src/settingsdialog.ui | 221 +++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 572 insertions(+) create mode 100644 src/settings_interface.cpp create mode 100644 src/settings_interface.h create mode 100644 src/settingsdialog.cpp create mode 100644 src/settingsdialog.h create mode 100644 src/settingsdialog.ui (limited to 'src') diff --git a/src/main.cpp b/src/main.cpp index 81f3024..02d36c1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,6 +7,10 @@ int main(int argc, char *argv[]) { QApplication app(argc, argv); + QCoreApplication::setOrganizationName("ConfuSomu"); + QCoreApplication::setOrganizationDomain("twilightsparkle.space"); + QCoreApplication::setApplicationName("ActorViewer"); + QCommandLineParser parser; CommandLineParsedOptions result = parse_command_line(parser, app); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 4fc3970..4943775 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -4,6 +4,7 @@ #include "src/finddialog.h" #include "src/list_item.h" #include "src/command_line.h" +#include "src/settingsdialog.h" #include #include @@ -56,6 +57,21 @@ void MainWindow::on_actionOpen_triggered(bool checked) { } } +void MainWindow::on_actionSettings_triggered(bool checked) { + if (!settings_dialog) { + settings_dialog = new SettingsDialog(this); + connect(settings_dialog, &QDialog::finished, this, &MainWindow::settingsDialog_done); + } + settings_dialog->open(); +} + +void MainWindow::settingsDialog_done(int result) { + if (result == QDialog::Accepted) { + // TODO: reload settings + } + delete settings_dialog; settings_dialog = nullptr; +} + void MainWindow::on_actionQuit_triggered(bool checked) { QCoreApplication::quit(); } diff --git a/src/mainwindow.h b/src/mainwindow.h index fced119..4c7e034 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -7,6 +7,7 @@ #include #include "archive/base_archive.h" +#include "src/settingsdialog.h" #include "types.h" #include "command_line.h" #include "finddialog.h" @@ -34,6 +35,7 @@ signals: private slots: void on_actionOpen_triggered(bool checked); + void on_actionSettings_triggered(bool checked); void on_actionQuit_triggered(bool checked); void on_actionAbout_triggered(bool checked); @@ -62,8 +64,10 @@ private: void finish_listWidget_itemActivated(const QString& status_info); void finish_open_file(const Archive::InitError& parse_error); + void settingsDialog_done(int result); FindDialog* find_dialog = nullptr; + SettingsDialog* settings_dialog = nullptr; QString open_file_filename; diff --git a/src/mainwindow.ui b/src/mainwindow.ui index cd351d4..292930d 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -96,6 +96,7 @@ p, li { white-space: pre-wrap; } File + @@ -259,6 +260,17 @@ p, li { white-space: pre-wrap; } Ctrl+F + + + Preferences… + + + Ctrl+Shift+, + + + QAction::PreferencesRole + + listWidget diff --git a/src/settings_interface.cpp b/src/settings_interface.cpp new file mode 100644 index 0000000..e14da92 --- /dev/null +++ b/src/settings_interface.cpp @@ -0,0 +1,120 @@ +#include "settings_interface.h" +#include +#include + +const QVariant default_setting(const QString &key); + +SettingsInterface::~SettingsInterface() { + delete modified; modified = nullptr; +} + +// Read a setting key and return the value associated with it. +// See SettingsInterface::write_setting() for updating a key's value. +const QVariant SettingsInterface::read_setting(const QString &key) { + qDebug() << "Read value of" << key; + if (must_clear.contains(key)) + return default_setting(key); + if (modified and modified->contains(key)) + return modified->value(key); + return qt_settings.value(key, default_setting(key)); +} + +// Write a new value for key. Isn't written to disk until you use SettingsInterface::commit(). +void SettingsInterface::write_setting(const QString &key, const QVariant &value) { + if (not modified) modified = new QHash; + modified->insert(key, value); + qDebug() << "New modified key value:" << key << value; + + // As the key has a new value it doesn't have to be cleared and thus if all keys were to be cleared it isn't true anymore + must_clear.removeOne(key); + must_clear_all = false; +} + +// Clear a setting and use the default value instead +void SettingsInterface::clear_setting(const QString &key) { + must_clear.append(key); +} + +// Clear all settings and only use the default values +void SettingsInterface::clear_all() { + // Optimization to be able to clear all settings faster + must_clear_all = true; + + // Construct a list of all settings in case one gets (written) a new value and thus must not be removed afterwards + must_clear.append(qt_settings.allKeys()); + if (modified) + must_clear.append(modified->keys()); + must_clear.removeDuplicates(); +} + +// Write the changed settings into the internal QSetting memeber. They are then written to disk. +// A return value of false indicates that an error occured while writing the config to disk. +bool SettingsInterface::commit() { + /* If everything has to be cleared, it is cleared and synced. + * If nothing has been modified or has to removed, then quits. + * If a few key(s) have to be removed they are removed, after that + * if key(s) have been modified/added they are added, then synced. + */ + + if (must_clear_all) { + qt_settings.clear(); + must_clear_all = false; + goto sync; + } else if ((not modified or (modified and modified->isEmpty())) + and must_clear.isEmpty()) + return true; + + if (not must_clear.isEmpty()) { + for (int i = 0; i < must_clear.size(); ++i) + qt_settings.remove(must_clear.at(i)); + must_clear.clear(); + } + if (not modified) goto sync; + + { + QHash::const_iterator i = modified->constBegin(); + while (i != modified->constEnd()) { + qt_settings.setValue(i.key(), i.value()); + qDebug() << "Wrote to disk" << i.key() << i.value(); + ++i; + } + modified->clear(); + } + +sync: + qt_settings.sync(); + qDebug() << "Sync done"; + return qt_settings.status() == QSettings::NoError; +} + +// whether the key been changed from its default value. +bool SettingsInterface::is_default(const QString &key) { + return ((modified and modified->contains(key)) + or qt_settings.contains(key)); +} + +// Whether the key been modified and not yet been commited to disk. +bool SettingsInterface::is_uncommited(const QString &key) { + if (not modified) return false; + return modified->contains(key); +} + +// Return the default setting value for key. +const QVariant default_setting(const QString &key) { + // This is not ideal as a compile time created key-value store would be better. Though won't it end up with making the same code? + if (key == "ui/timezone") + return AppSettingsTypes::Timezone::LOCALTIME; + else if (key == "net/access_internet") + return true; + else if (key == "net/download_emoji") + return true; + else if (key == "net/download_attachments") + return true; + else if (key == "net/instance/type") + return AppSettingsTypes::InstanceType::MASTODON; + else if (key == "net/instance/address" + or key == "net/instance/token") + return ""; + else + return -1; +} diff --git a/src/settings_interface.h b/src/settings_interface.h new file mode 100644 index 0000000..e3935d0 --- /dev/null +++ b/src/settings_interface.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include + +/* struct AppSettings { + enum {LOCALTIME, UTC} ui_timezone = LOCALTIME; + bool can_access_internet = true; + bool can_download_emoji = true; + bool can_download_attachments = true; + struct { + enum {MASTODON} type = MASTODON; + QString address; + QString token; + } instance; +}; */ + +namespace AppSettingsTypes { + enum Timezone {LOCALTIME = 0, UTC}; + enum InstanceType {MASTODON = 0}; +} +// Declare metatypes for use with QVariant +Q_DECLARE_METATYPE(AppSettingsTypes::Timezone); +Q_DECLARE_METATYPE(AppSettingsTypes::InstanceType); + +class SettingsInterface { +public: + ~SettingsInterface(); + const QVariant read_setting(const QString &key); + void write_setting(const QString &key, const QVariant &value); + void clear_setting(const QString &key); + void clear_all(); + bool commit(); + bool is_default(const QString &key); + bool is_uncommited(const QString &key); + +private: + QSettings qt_settings; + QHash* modified = nullptr; + + // Optimization to commit a clear_all() faster + bool must_clear_all = false; + // List of keys to remove from the QSettings + QStringList must_clear; +}; diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp new file mode 100644 index 0000000..0c64641 --- /dev/null +++ b/src/settingsdialog.cpp @@ -0,0 +1,106 @@ +#include "settingsdialog.h" +#include "settings_interface.h" +#include +#include + +SettingsDialog::SettingsDialog(QWidget* parent) + : QDialog(parent, Qt::Dialog), ui(new Ui::SettingsDialog) +{ + ui->setupUi(this); + + // Create connections for each dialog button box button, as this is the best way to do it + connect(ui->buttonBox->button(QDialogButtonBox::RestoreDefaults), &QPushButton::clicked, this, &SettingsDialog::defaults_button_clicked); + connect(ui->buttonBox->button(QDialogButtonBox::Apply), &QPushButton::clicked, this, &SettingsDialog::apply_button_clicked); + connect(ui->buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &SettingsDialog::cancel_button_clicked); + + update_ui(); +} + +SettingsDialog::~SettingsDialog() { + delete ui; +} + +/* ----- General tab ----- */ + +void SettingsDialog::on_timezoneCombo_activated(int index) { + if (update_ui_in_progress) return; + settings_interface.write_setting("ui/timezone", index); +} + +/* ----- Network tab ----- */ + +void SettingsDialog::on_connectToTheInternetCheckBox_clicked(bool checked) { + if (update_ui_in_progress) return; + settings_interface.write_setting("net/access_internet", checked); +} + +void SettingsDialog::on_DownloadCustomEmojiCheckBox_clicked(bool checked) { + if (update_ui_in_progress) return; + settings_interface.write_setting("net/download_emoji", checked); +} + +void SettingsDialog::on_downloadAttachmentsCheckBox_clicked(bool checked) { + if (update_ui_in_progress) return; + settings_interface.write_setting("net/download_attachments", checked); +} + +void SettingsDialog::on_instanceTypeComboBox_activated(int index) { + if (update_ui_in_progress) return; + settings_interface.write_setting("net/instance/type", index); +} + +void SettingsDialog::on_instanceAddressLineEdit_editingFinished() { + if (update_ui_in_progress) return; + settings_interface.write_setting("net/instance/address", ui->instanceAddressLineEdit->text()); +} + +void SettingsDialog::on_tokenLineEdit_editingFinished() { + if (update_ui_in_progress) return; + settings_interface.write_setting("net/instance/token", ui->tokenLineEdit->text()); +} + +void SettingsDialog::on_instanceActionsLabel_linkActivated(const QString& link) { + if (link == "action:fill-address") { + update_ui_in_progress = true; + // TODO + update_ui_in_progress = false; + } else if (link == "action:request-token") { + // TODO + } +} + +/* ----- Dialog & setting saving/restoring ----- */ + +// OK button pressed, save and apply new settings +void SettingsDialog::apply_button_clicked() { + settings_interface.commit(); + emit QDialog::accept(); // Closes the dialog with return code +} + +// Cancel button pressed, discard new settings +void SettingsDialog::cancel_button_clicked() { + emit QDialog::reject(); // Closes the dialog with return code +} + +// Restore defaults button pressed, reset settings +void SettingsDialog::defaults_button_clicked() { + settings_interface.clear_all(); + update_ui(); +} + +// Update the UI's setting values with the values in settings_interface. +void SettingsDialog::update_ui() { + update_ui_in_progress = true; + // General tab + ui->timezoneCombo->setCurrentIndex(settings_interface.read_setting("ui/timezone").value()); + + // Network tab + ui->connectToTheInternetCheckBox->setChecked(settings_interface.read_setting("net/access_internet").value()); + ui->DownloadCustomEmojiCheckBox->setChecked(settings_interface.read_setting("net/download_emoji").value()); + ui->downloadAttachmentsCheckBox->setChecked(settings_interface.read_setting("net/download_attachments").value()); + // Instance frame + ui->instanceTypeComboBox->setCurrentIndex(settings_interface.read_setting("net/instance/type").value()); + ui->instanceAddressLineEdit->setText(settings_interface.read_setting("net/instance/address").value()); + ui->tokenLineEdit->setText(settings_interface.read_setting("net/instance/token").value()); + update_ui_in_progress = false; +} diff --git a/src/settingsdialog.h b/src/settingsdialog.h new file mode 100644 index 0000000..e320a08 --- /dev/null +++ b/src/settingsdialog.h @@ -0,0 +1,43 @@ +#pragma once +#include +#include "./ui_settingsdialog.h" +#include "settings_interface.h" + +QT_BEGIN_NAMESPACE +namespace Ui { class SettingsDialog; } +QT_END_NAMESPACE + +class SettingsDialog : public QDialog { + Q_OBJECT + +public: + SettingsDialog(QWidget *parent = nullptr); + ~SettingsDialog(); + + static void initialize_default_settings(); + +public slots: + void defaults_button_clicked(); + void apply_button_clicked(); + void cancel_button_clicked(); + +private slots: + // General tab: + void on_timezoneCombo_activated(int index); + // Network tab: + void on_connectToTheInternetCheckBox_clicked(bool checked); + void on_DownloadCustomEmojiCheckBox_clicked(bool checked); + void on_downloadAttachmentsCheckBox_clicked(bool checked); + void on_instanceTypeComboBox_activated(int index); + void on_instanceAddressLineEdit_editingFinished(); + void on_tokenLineEdit_editingFinished(); + void on_instanceActionsLabel_linkActivated(const QString& link); + +private: + Ui::SettingsDialog* ui; + + SettingsInterface settings_interface; + bool update_ui_in_progress = false; + + void update_ui(); +}; diff --git a/src/settingsdialog.ui b/src/settingsdialog.ui new file mode 100644 index 0000000..b2c1c69 --- /dev/null +++ b/src/settingsdialog.ui @@ -0,0 +1,221 @@ + + + SettingsDialog + + + + 0 + 0 + 400 + 364 + + + + Preferences + + + true + + + + + + + + 0 + + + + General + + + + + + Time zone to use when formatting dates + + + + + + + + Local Time + + + + + UTC + + + + + + + + + Network + + + + + + + + Download custom emoji + + + + + + + true + + + + + + + Download remote attachments + + + + + + + true + + + + + + + Connect to the internet + + + + + + + true + + + + + + + + + Instance with whom to fetch posts with + + + + + + + Mastodon + + + + + + + + Instance type + + + + + + + Instance Address + + + + + + + + + + Token + + + + + + + + + + <html><head/><body><p>Actions: <a href="action:fill-address"><span style=" text-decoration: underline; color:#d2737f;">auto-fill address</span></a> and <a href="action:request-token"><span style=" text-decoration: underline; color:#d2737f;">request token</span></a>.</p></body></html> + + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::RestoreDefaults + + + + + + + + + tabWidget + timezoneCombo + connectToTheInternetCheckBox + DownloadCustomEmojiCheckBox + downloadAttachmentsCheckBox + instanceTypeComboBox + instanceAddressLineEdit + tokenLineEdit + + + + + buttonBox + accepted() + SettingsDialog + accept() + + + 255 + 304 + + + 157 + 274 + + + + + buttonBox + rejected() + SettingsDialog + reject() + + + 323 + 304 + + + 286 + 274 + + + + + -- cgit v1.2.3-54-g00ecf