Implement config checking (#1859)

* Add error handling functions to ConfigHandler

Refurbished functions setValue and value which were previously unused.
These functions now set/get a setting with error handling.
Currently recognizes only errors recognizable by QSettings.

* Make use of value and setValue in ConfigHandler

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Add checker for unrecognized general options

Extraneous config options in [General] will be reported as errors.
Added some placeholder functions to be implemented in future commits.

* Introduce keysFromGroup function

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Check shortcut names for duplicates

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix notification spam

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Implement shortcut conflict checking

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix reading of fallbacks on error

If there is a config error, some values would not be loaded correctly.
Using the newly implemented function ConfigHandler::contains instead of
QSettings::contains solves this issue.

These changes reveal u bug that causes a crash on startup.

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix crashes introduced in previous commit

Because ConfigHandler is a dependency of most other classes,
calling functions from those classes inside ConfigHandler caused
infinite recursions in some cases.

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Add config file watcher

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Add missing config options

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix bug in shortcut conflict detection

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Add error resolved notification

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Add GUI error message overlay

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Add indicator in config window

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Use ConfigHandler::fileChanged in ConfigWindow

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix watcher sometimes not firing

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Improve config file watching performance

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Add new way to handle config

This is only a fundamental implementation. Future commits will replace
everything with this new paradigm.

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix getButtons and related functions

Also refactored related code to use QList instead of QVector because
QSettings does not work well with QVector.

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Make good use of the new way

* Implement proper checking for basic types

Everything is covered, apart from KeySequence.

* Move fallback path to ExistingDir value handler

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Use consistent naming scheme in ConfigHandler

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Implement config getters/setters via macro

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Surround text with tr and clang-format

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix colors being saved obfuscated

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Add ValueHandler::represenation

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Move ValueHandler to separate files

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* confighandler.cpp: rename macro CUSTOM to OPTION

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix bug with shortcut conflict checker

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Update docs and fix setAllTheButtons

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Handle filenamePattern properly

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix failing build due to wrong function name

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix QSet error due to Qt version mismatch

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Replace QSharedPointer::get with data for older Qt versions

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix failing build on MacOS and ubuntu 18.04

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Add column headers to recognizedGeneralOptions map

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix ubuntu 18.04 error

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix false positive when shortcuts empty

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix wrong shortcut group prefix

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Implement proper shortcut checking

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Add shortcut map in ConfigHandler

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Move ConfigShortcuts functions to ShortcutsWidget

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix minor bugs

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Add fallback scheme: Pictures, HOME, TMP

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Add config --check CLI option

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Add config error log to GUI

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Rename ValueHandler::description to expected

* Convert Qt's #AARRGGBB to #RRGGBBAA and vice versa

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Remove obsolete `saveAfterCopyPath`

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix errors in example config

Also added an additional ; in front of actual comments to differentiate
them from commented options.

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Allow special value 'picker' in userColors

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Allow only name, #RRGGBB, and #RRGGBBAA color formats

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>
This commit is contained in:
Haris Gušić
2021-09-15 18:56:01 +02:00
committed by GitHub
parent a7e88e60de
commit d1428889b9
35 changed files with 1665 additions and 859 deletions

View File

@@ -11,10 +11,17 @@
#include "src/utils/confighandler.h"
#include "src/utils/globalvalues.h"
#include "src/utils/pathinfo.h"
#include <QApplication>
#include <QDialog>
#include <QDialogButtonBox>
#include <QFileSystemWatcher>
#include <QIcon>
#include <QKeyEvent>
#include <QLabel>
#include <QSizePolicy>
#include <QTabBar>
#include <QTextEdit>
#include <QTextStream>
#include <QVBoxLayout>
// ConfigWindow contains the menus where you can configure the application
@@ -24,25 +31,18 @@ ConfigWindow::ConfigWindow(QWidget* parent)
{
// We wrap QTabWidget in a QWidget because of a Qt bug
auto layout = new QVBoxLayout(this);
m_tabs = new QTabWidget(this);
m_tabs->tabBar()->setUsesScrollButtons(false);
layout->addWidget(m_tabs);
m_tabWidget = new QTabWidget(this);
m_tabWidget->tabBar()->setUsesScrollButtons(false);
layout->addWidget(m_tabWidget);
setAttribute(Qt::WA_DeleteOnClose);
setWindowIcon(QIcon(":img/app/flameshot.svg"));
setWindowTitle(tr("Configuration"));
auto changedSlot = [this](QString s) {
QStringList files = m_configWatcher->files();
if (!files.contains(s)) {
this->m_configWatcher->addPath(s);
}
emit updateChildren();
};
m_configWatcher = new QFileSystemWatcher(this);
m_configWatcher->addPath(ConfigHandler().configFilePath());
connect(
m_configWatcher, &QFileSystemWatcher::fileChanged, this, changedSlot);
connect(ConfigHandler::getInstance(),
&ConfigHandler::fileChanged,
this,
&ConfigWindow::updateChildren);
QColor background = this->palette().window().color();
bool isDark = ColorUtils::colorIsDark(background);
@@ -51,24 +51,40 @@ ConfigWindow::ConfigWindow(QWidget* parent)
// visuals
m_visuals = new VisualsEditor();
m_tabs->addTab(
m_visuals, QIcon(modifier + "graphics.svg"), tr("Interface"));
m_visualsTab = new QWidget();
QVBoxLayout* visualsLayout = new QVBoxLayout(m_visualsTab);
m_visualsTab->setLayout(visualsLayout);
visualsLayout->addWidget(m_visuals);
m_tabWidget->addTab(
m_visualsTab, QIcon(modifier + "graphics.svg"), tr("Interface"));
// filename
m_filenameEditor = new FileNameEditor();
m_tabs->addTab(m_filenameEditor,
QIcon(modifier + "name_edition.svg"),
tr("Filename Editor"));
m_filenameEditorTab = new QWidget();
QVBoxLayout* filenameEditorLayout = new QVBoxLayout(m_filenameEditorTab);
m_filenameEditorTab->setLayout(filenameEditorLayout);
filenameEditorLayout->addWidget(m_filenameEditor);
m_tabWidget->addTab(m_filenameEditorTab,
QIcon(modifier + "name_edition.svg"),
tr("Filename Editor"));
// general
m_generalConfig = new GeneralConf();
m_tabs->addTab(
m_generalConfig, QIcon(modifier + "config.svg"), tr("General"));
m_generalConfigTab = new QWidget();
QVBoxLayout* generalConfigLayout = new QVBoxLayout(m_generalConfigTab);
m_generalConfigTab->setLayout(generalConfigLayout);
generalConfigLayout->addWidget(m_generalConfig);
m_tabWidget->addTab(
m_generalConfigTab, QIcon(modifier + "config.svg"), tr("General"));
// shortcuts
m_shortcuts = new ShortcutsWidget();
m_tabs->addTab(
m_shortcuts, QIcon(modifier + "shortcut.svg"), tr("Shortcuts"));
m_shortcutsTab = new QWidget();
QVBoxLayout* shortcutsLayout = new QVBoxLayout(m_shortcutsTab);
m_shortcutsTab->setLayout(shortcutsLayout);
shortcutsLayout->addWidget(m_shortcuts);
m_tabWidget->addTab(
m_shortcutsTab, QIcon(modifier + "shortcut.svg"), tr("Shortcuts"));
// connect update sigslots
connect(this,
@@ -83,6 +99,12 @@ ConfigWindow::ConfigWindow(QWidget* parent)
&ConfigWindow::updateChildren,
m_generalConfig,
&GeneralConf::updateComponents);
// Error indicator (this must come last)
initErrorIndicator(m_visualsTab, m_visuals);
initErrorIndicator(m_filenameEditorTab, m_filenameEditor);
initErrorIndicator(m_generalConfigTab, m_generalConfig);
initErrorIndicator(m_shortcutsTab, m_shortcuts);
}
void ConfigWindow::keyPressEvent(QKeyEvent* e)
@@ -91,3 +113,84 @@ void ConfigWindow::keyPressEvent(QKeyEvent* e)
close();
}
}
void ConfigWindow::initErrorIndicator(QWidget* tab, QWidget* widget)
{
QLabel* label = new QLabel(tab);
QPushButton* btnShowErrors = new QPushButton("Show errors", tab);
QHBoxLayout* btnLayout = new QHBoxLayout(tab);
// Set up label
label->setText(tr(
"<b>Configuration file has errors. Resolve them before continuing.</b>"));
label->setStyleSheet(QStringLiteral(":disabled { color: %1; }")
.arg(qApp->palette().color(QPalette::Text).name()));
label->setVisible(ConfigHandler().hasError());
// Set up "Show errors" button
btnShowErrors->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
btnLayout->addWidget(btnShowErrors);
btnShowErrors->setVisible(ConfigHandler().hasError());
widget->setEnabled(!ConfigHandler().hasError());
// Add label and button to the parent widget's layout
QBoxLayout* layout = static_cast<QBoxLayout*>(tab->layout());
if (layout != nullptr) {
layout->insertWidget(0, label);
layout->insertLayout(1, btnLayout);
} else {
widget->layout()->addWidget(label);
widget->layout()->addWidget(btnShowErrors);
}
// Sigslots
connect(ConfigHandler::getInstance(), &ConfigHandler::error, widget, [=]() {
widget->setEnabled(false);
label->show();
btnShowErrors->show();
});
connect(ConfigHandler::getInstance(),
&ConfigHandler::errorResolved,
widget,
[=]() {
widget->setEnabled(true);
label->hide();
btnShowErrors->hide();
});
connect(btnShowErrors, &QPushButton::clicked, this, [this]() {
// Generate error log message
QString str;
QTextStream stream(&str);
ConfigHandler().checkForErrors(&stream);
// Set up dialog
QDialog dialog;
dialog.setWindowTitle(QStringLiteral("Configuration errors"));
dialog.setLayout(new QVBoxLayout(&dialog));
// Add text display
QTextEdit* textDisplay = new QTextEdit(&dialog);
textDisplay->setPlainText(str);
textDisplay->setReadOnly(true);
dialog.layout()->addWidget(textDisplay);
// Add Ok button
using BBox = QDialogButtonBox;
BBox* buttons = new BBox(BBox::Ok);
dialog.layout()->addWidget(buttons);
connect(buttons, &QDialogButtonBox::clicked, this, [&dialog]() {
dialog.close();
});
dialog.show();
qApp->processEvents();
QPoint center = dialog.geometry().center();
QRect dialogRect(0, 0, 400, 400);
dialogRect.moveCenter(center);
dialog.setGeometry(dialogRect);
dialog.exec();
});
}