#include "settings.hpp" namespace getsuyomi { using fud::FudStatus; Settings::Settings(QWidget* parent, Shortcuts&& shortcuts) : QDialog{parent}, m_shortcuts{std::move(shortcuts)} { auto* layout = new QVBoxLayout(); setWindowTitle("getsuyomi settings"); if (!m_shortcuts.valid()) { return; } auto containerLayout = new QHBoxLayout(); auto* columnLayout = new QVBoxLayout(); size_t counter{0}; constexpr size_t maxEntriesPerColumn{4}; for (const auto& action : m_shortcuts.actions()) { auto* collector = new ShortcutCollector(this, action, m_shortcuts); columnLayout->addWidget(collector); counter++; if (counter % (maxEntriesPerColumn + 1) == 0) { containerLayout->addLayout(columnLayout); columnLayout = new QVBoxLayout(); } } if (columnLayout->count() > 0) { containerLayout->addLayout(columnLayout); } layout->addLayout(containerLayout); auto* dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(dialogButtonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(dialogButtonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); layout->addWidget(dialogButtonBox); setLayout(layout); } bool Settings::valid() const { return m_shortcuts.valid(); } const Shortcuts& Settings::shortcuts() const { return m_shortcuts; } ShortcutCollector::ShortcutCollector(QWidget* parent, ActionType action, Shortcuts& shortcuts) : QWidget(parent), m_action{action}, m_shortcuts{shortcuts}, m_bindings{} { m_layout = new QVBoxLayout(); auto headerLayout = new QHBoxLayout(); auto* name = new QLabel(actionTypeToString(m_action)); headerLayout->addWidget(name); m_shortcutEditor = new QKeySequenceEdit(this); m_shortcutEditor->setMaximumSequenceLength(1); connect(m_shortcutEditor, &QKeySequenceEdit::editingFinished, this, &ShortcutCollector::checkBinding); headerLayout->addWidget(m_shortcutEditor); m_acceptButton = new QPushButton("Accept", this); m_acceptButton->setEnabled(false); connect(m_acceptButton, &QPushButton::clicked, this, &ShortcutCollector::addBinding); headerLayout->addWidget(m_acceptButton); headerLayout->addStretch(); m_layout->addLayout(headerLayout); auto shortcutOptions = m_shortcuts.shortcuts(m_action); if (shortcutOptions != std::nullopt) { for (const auto& binding : *shortcutOptions) { createBinding(binding); } } else { qWarning("No shortcuts found for %s", actionTypeToString(action)); } setLayout(m_layout); } void ShortcutCollector::createBinding(QKeySequence binding) { auto displayItem = new ShortcutDisplay(this, binding); m_bindings[binding] = displayItem; m_layout->addWidget(displayItem); connect(displayItem, &ShortcutDisplay::removeClicked, this, &ShortcutCollector::removeBinding); } void ShortcutCollector::checkBinding() { auto keySequence = m_shortcutEditor->keySequence(); if (keySequence == QKeySequence::UnknownKey) { m_acceptButton->setEnabled(false); return; } m_acceptButton->setEnabled(not m_shortcuts.contains(keySequence)); } void ShortcutCollector::addBinding() { auto keySequence = m_shortcutEditor->keySequence(); if (keySequence == QKeySequence::UnknownKey) { qWarning("Invalid state - can't accept unknown key"); return; } else if (m_shortcuts.contains(keySequence)) { qWarning("Shortcut %s already bound", qPrintable(keySequence.toString())); return; } auto result = m_shortcuts.bind(m_action, keySequence); if (result != FudStatus::Success) { qCritical("Error binding %s to action %s", qPrintable(keySequence.toString()), actionTypeToString(m_action)); return; } if (m_bindings.contains(keySequence)) { qWarning( "binding %s to action %s already exists", qPrintable(keySequence.toString()), actionTypeToString(m_action)); m_layout->removeWidget(m_bindings[keySequence]); delete m_bindings[keySequence]; } createBinding(keySequence); m_shortcutEditor->clear(); } void ShortcutCollector::removeBinding(QKeySequence binding) { auto result = m_shortcuts.remove(binding); if (result == FudStatus::NotFound) { qWarning("binding %s not found", qPrintable(binding.toString())); } else if (result != FudStatus::Success) { qWarning("error removing binding %s: %s", qPrintable(binding.toString()), FudStatusToString(result)); } auto bindingHandle = m_bindings.extract(binding); if (bindingHandle) { m_layout->removeWidget(bindingHandle.mapped()); bindingHandle.mapped()->deleteLater(); } else { qWarning("Could not remove widget!"); } checkBinding(); } ShortcutDisplay::ShortcutDisplay(QWidget* parent, QKeySequence shortcut) : QWidget{parent}, m_binding{shortcut} { auto layout = new QHBoxLayout(); layout->addWidget(new QLabel(shortcut.toString())); auto* deleteButton = new QPushButton("Delete", this); connect(deleteButton, &QPushButton::clicked, this, &ShortcutDisplay::removeOnClicked); layout->addWidget(deleteButton); setLayout(layout); } void ShortcutDisplay::removeOnClicked() { emit removeClicked(m_binding); } }