From 64a4fc1fb07863f35289b2dc050d30ecc96aaac6 Mon Sep 17 00:00:00 2001 From: Chris Xiong Date: Sun, 2 Oct 2022 02:04:05 -0400 Subject: Add shortcut settings for item actions. Implement check for ambiguous shortcuts. Make preference dialog wider by default. --- qdeduper/mingui.cpp | 4 +- qdeduper/preferencedialog.cpp | 178 +++++++++++++++++++++++++++++++++++++++++- qdeduper/preferencedialog.hpp | 19 +++++ 3 files changed, 196 insertions(+), 5 deletions(-) (limited to 'qdeduper') diff --git a/qdeduper/mingui.cpp b/qdeduper/mingui.cpp index caf0249..f599ba0 100644 --- a/qdeduper/mingui.cpp +++ b/qdeduper/mingui.cpp @@ -829,9 +829,9 @@ void DeduperMainWindow::apply_prefs() size_t actt = i % ItemActionType::ACTION_MAX; std::string iamt = "hotkey/item_action_mod_" + std::to_string(actt); std::string iakt = "hotkey/item_" + std::to_string(actn) + "_action_key"; - int ik = sr->get_option_int(iakt); int im = sr->get_option_int(iamt); - QKeySequence ks = QKeySequence(static_cast(ik | im)); + int ik = sr->get_option_int(iakt); + QKeySequence ks = ~im ? QKeySequence(static_cast(ik | im)) : QKeySequence(); act->setShortcut(ks); } } diff --git a/qdeduper/preferencedialog.cpp b/qdeduper/preferencedialog.cpp index 28ccbea..40d2aea 100644 --- a/qdeduper/preferencedialog.cpp +++ b/qdeduper/preferencedialog.cpp @@ -1,6 +1,8 @@ +#include #include #include +#include #include #include #include @@ -13,6 +15,8 @@ #include #include #include +#include +#include #include "preferencedialog.hpp" #include "settings.hpp" @@ -23,6 +27,7 @@ PreferenceDialog::PreferenceDialog(SettingsRegistry *sr, QWidget *parent) : QDia tw = new QTabWidget(this); bb = new QDialogButtonBox(QDialogButtonBox::StandardButton::Ok | QDialogButtonBox::StandardButton::Cancel, this); this->setWindowTitle("Preferences"); + this->setMinimumWidth(480); QVBoxLayout *l = new QVBoxLayout(); this->setLayout(l); l->addWidget(tw); @@ -40,6 +45,22 @@ void PreferenceDialog::open() void PreferenceDialog::accept() { + QKeySequence bks; + switch (verify_shortcuts(&bks)) + { + case 0: + break; + case 1: + QMessageBox::critical(this, "Ambiguous shortcut found", + QString("Shortcut %1 is assigned to multiple actions.").arg(bks.toString())); + return; + case 2: + QMessageBox::critical(this, "Bad shortcut assignment", + QString("Shortcut %1 has more than one key and cannot be assigned to item actions.").arg(bks.toString())); + return; + default: + break; + } save_widget_status(); QDialog::accept(); } @@ -56,6 +77,7 @@ void PreferenceDialog::setup_widgets() for (auto &k : sr->klist) { auto &p = sr->smap[k]; + if (!p.desc.length()) continue; QWidget *w = nullptr; QGridLayout *l = p.tab < tabs.size() ? tabs[p.tab] : nullptr; if (!l) continue; @@ -117,6 +139,23 @@ void PreferenceDialog::set_hkactions(int tab, std::maphktv->setSortingEnabled(false); this->hktv->setSelectionMode(QAbstractItemView::SelectionMode::NoSelection); this->hktv->setItemDelegateForColumn(1, new ShortcutEditorDelegate); + + QGroupBox *gb = new QGroupBox("Action Modifiers"); + QGridLayout *l = new QGridLayout(); + gb->setLayout(l); + this->tabs[tab]->addWidget(gb, 1, 0); + + const QStringList llist = {"Mark/Unmark", "Mark All Except", "Maximize", "Open with System Viewer", "Open Containing Folder"}; + + for (int i = 0; i < llist.size(); ++i) + { + auto < = llist[i]; + ModifierEdit *me = new ModifierEdit(); + QLabel *lb = new QLabel(lt); + l->addWidget(lb, i, 0); + l->addWidget(me, i, 1); + mes.push_back(me); + } } void PreferenceDialog::load_widget_status() @@ -162,7 +201,7 @@ void PreferenceDialog::load_widget_status() continue; QAction *act = this->actmap[actn]; QStandardItem *itma = new QStandardItem(act->text()); - QStandardItem *itmk = new QStandardItem(act->shortcut().toString()); + QStandardItem *itmk = new QStandardItem(ks.toString()); itma->setIcon(act->icon()); itma->setEditable(false); itma->setData(QString::fromStdString(actn), Qt::ItemDataRole::UserRole); @@ -170,6 +209,24 @@ void PreferenceDialog::load_widget_status() this->hkim->appendRow({itma, itmk}); } this->hktv->resizeColumnsToContents(); + for (size_t i = 0; i < 16; ++i) + { + std::string iakt = "item_" + std::to_string(i) + "_action_key"; + int ik = sr->get_option_int("hotkey/" + iakt); + QKeySequence ks(ik); + QStandardItem *itma = new QStandardItem(QString("Item %1 Action Key").arg(i + 1)); + QStandardItem *itmk = new QStandardItem(ks.toString()); + itma->setEditable(false); + itma->setData(QString::fromStdString(iakt), Qt::ItemDataRole::UserRole); + itmk->setData(QVariant::fromValue(ks), Qt::ItemDataRole::UserRole); + this->hkim->appendRow({itma, itmk}); + } + for (size_t i = 0; i < 5; ++i) + { + std::string iamt = "hotkey/item_action_mod_" + std::to_string(i); + int im = sr->get_option_int(iamt); + mes[i]->set_modifier(static_cast(im)); + } } void PreferenceDialog::save_widget_status() @@ -207,11 +264,49 @@ void PreferenceDialog::save_widget_status() } for (int i = 0; i < hkim->rowCount(); ++i) { - std::string actn = hkim->item(i, 0)->data(Qt::ItemDataRole::UserRole).toString().toStdString(); + QString actn = hkim->item(i, 0)->data(Qt::ItemDataRole::UserRole).toString(); QKeySequence ks = hkim->item(i, 1)->data(Qt::ItemDataRole::UserRole).value(); - sr->set_option_keyseq("hotkey/" + actn, ks); + if (actn.endsWith("_action_key")) + sr->set_option_int("hotkey/" + actn.toStdString(), ks[0]); + else sr->set_option_keyseq("hotkey/" + actn.toStdString(), ks); + } + for (size_t i = 0; i < 5; ++i) + { + std::string iamt = "hotkey/item_action_mod_" + std::to_string(i); + sr->set_option_int(iamt, mes[i]->get_modifier()); } } + +int PreferenceDialog::verify_shortcuts(QKeySequence *bks) +{ + QList ksl; + for (int i = 0; i < hkim->rowCount(); ++i) + { + QString actn = hkim->item(i, 0)->data(Qt::ItemDataRole::UserRole).toString(); + QKeySequence ks = hkim->item(i, 1)->data(Qt::ItemDataRole::UserRole).value(); + if (actn.endsWith("_action_key")) + { + if (ks.count() > 1) + { + *bks = ks; + return 2; + } + for (int j = 0;j < 5; ++j) + if (mes[j]->get_modifier() != INT_MAX) + ksl.push_back(QKeySequence(mes[j]->get_modifier() | ks[0])); + } + else ksl.push_back(hkim->item(i, 1)->data(Qt::ItemDataRole::UserRole).value()); + } + for (int i = 0; i < ksl.size(); ++i) + for (int j = i + 1; j < ksl.size(); ++j) + if (!ksl[i].isEmpty() && ksl[i] == ksl[j]) + { + *bks = ksl[i]; + return 1; + } + return 0; +} + QWidget* ShortcutEditorDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { Q_UNUSED(option); @@ -236,3 +331,80 @@ void ShortcutEditorDelegate::setModelData(QWidget *editor, QAbstractItemModel *m model->setData(index, QVariant::fromValue(kse->keySequence()), Qt::ItemDataRole::UserRole); model->setData(index, kse->keySequence().toString(), Qt::ItemDataRole::DisplayRole); } + +ModifierEdit::ModifierEdit(QWidget *par) : QPushButton(par) +{ + this->setCheckable(true); + this->setFocusPolicy(Qt::FocusPolicy::StrongFocus); + this->mod = static_cast(0); + QObject::connect(this, &QPushButton::toggled, [this](bool c) { + if (c) + { + if (int(this->mod) == INT_MAX) + this->set_modifier(static_cast(0)); + this->setText("Input modifier..."); + } + else + this->set_modifier(this->mod); + }); +} + +Qt::Modifier ModifierEdit::get_modifier() +{ + return this->mod; +} + +void ModifierEdit::set_modifier(Qt::Modifier mod) +{ + this->mod = mod; + int imod = mod; + switch (imod) + { + case 0: + if (this->isChecked()) + this->setText("Input modifier..."); + else + this->setText("No modifier"); + break; + case INT_MAX: + this->setText("Disabled"); + break; + default: + QString kss = QKeySequence(mod).toString(); + while (kss.length() > 0 && kss.back() == '+') kss.chop(1); + this->setText(kss); + } +} + +void ModifierEdit::keyPressEvent(QKeyEvent *e) +{ + if (!this->isChecked()) return QPushButton::keyPressEvent(e); + switch (e->key()) + { + case Qt::Key::Key_Enter: + case Qt::Key::Key_Return: + case Qt::Key::Key_Space: + this->set_modifier(static_cast(Qt::KeyboardModifiers::Int(e->modifiers()))); + this->setChecked(false); + break; + case Qt::Key::Key_Escape: + this->set_modifier(static_cast(0)); + this->setChecked(false); + break; + case Qt::Key::Key_Backspace: + case Qt::Key::Key_Delete: + this->set_modifier(static_cast(INT_MAX)); + this->setChecked(false); + break; + } +} + +bool ModifierEdit::event(QEvent *e) +{ + if (e->type() == QEvent::Type::ShortcutOverride) + { + e->accept(); + return true; + } + return QPushButton::event(e); +} diff --git a/qdeduper/preferencedialog.hpp b/qdeduper/preferencedialog.hpp index 8efefde..78e78c5 100644 --- a/qdeduper/preferencedialog.hpp +++ b/qdeduper/preferencedialog.hpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -17,6 +18,20 @@ class QDialogButtonBox; class QTableView; class QStandardItemModel; +class ModifierEdit : public QPushButton +{ + Q_OBJECT +public: + ModifierEdit(QWidget *par = nullptr); + Qt::Modifier get_modifier(); + void set_modifier(Qt::Modifier mod); + bool event(QEvent *e) override; +protected: + void keyPressEvent(QKeyEvent *e) override; +private: + Qt::Modifier mod; +}; + class PreferenceDialog : public QDialog { Q_OBJECT @@ -27,9 +42,12 @@ public: void load_widget_status(); void save_widget_status(); +public Q_SLOTS: void open() override; void accept() override; private: + int verify_shortcuts(QKeySequence *bks); + SettingsRegistry *sr; QTabWidget *tw; std::vector tabs; @@ -38,6 +56,7 @@ private: QStandardItemModel *hkim = nullptr; std::map defmap; std::map actmap; + std::vector mes; }; class ShortcutEditorDelegate : public QStyledItemDelegate -- cgit v1.2.3