diff --git a/include/global/DataStore.hpp b/include/global/DataStore.hpp index b61f4d6..74c4fce 100644 --- a/include/global/DataStore.hpp +++ b/include/global/DataStore.hpp @@ -31,6 +31,19 @@ namespace Configs { static QStringList List(); }; + class Shortcuts : public JsonStore + { + public: + QMap shortcuts; + + QStringList keyVal; + + explicit Shortcuts(); + bool Save() override; + + bool Load(); + }; + class DataStore : public JsonStore { public: // Running @@ -110,6 +123,7 @@ namespace Configs { int remember_id = -1919; bool remember_enable = false; bool windows_set_admin = false; + std::unique_ptr shortcuts; // Socks & HTTP Inbound QString inbound_address = "127.0.0.1"; diff --git a/include/global/GuiUtils.hpp b/include/global/GuiUtils.hpp index ce1aab0..3131091 100644 --- a/include/global/GuiUtils.hpp +++ b/include/global/GuiUtils.hpp @@ -6,6 +6,7 @@ #define Dialog_DialogEditProfile "DialogEditProfile" #define Dialog_DialogManageGroups "DialogManageGroups" #define Dialog_DialogManageRoutes "DialogManageRoutes" +#define Dialog_DialogManageHotkeys "DialogManageHotkeys" // Utils diff --git a/include/ui/mainwindow.h b/include/ui/mainwindow.h index 0cf2ef6..e1d932e 100644 --- a/include/ui/mainwindow.h +++ b/include/ui/mainwindow.h @@ -204,6 +204,9 @@ private: libcore::SpeedTestResult currentTestResult; DownloadProgressReport currentDownloadReport; // could use a list, but don't think can show more than one anyways + // shortcuts + QList hiddenMenuShortcuts; + std::map ruleSetMap; QStringList remoteRouteProfiles; @@ -237,7 +240,13 @@ private: void HotkeyEvent(const QString &key); - void RegisterShortcuts(); + void RegisterHiddenMenuShortcuts(bool unregister = false); + + void setActionsData(); + + QList getActionsForShortcut(); + + void loadShortcuts(); // grpc diff --git a/include/ui/setting/dialog_hotkey.h b/include/ui/setting/dialog_hotkey.h index ec5ed8a..0196c25 100644 --- a/include/ui/setting/dialog_hotkey.h +++ b/include/ui/setting/dialog_hotkey.h @@ -14,10 +14,18 @@ class DialogHotkey : public QDialog { Q_OBJECT public: - explicit DialogHotkey(QWidget *parent = nullptr); + explicit DialogHotkey(QWidget *parent = nullptr, const QList& actions = {}); ~DialogHotkey() override; + public slots: + + void accept(); + + void reject(); + private: + void generateShortcutItems(const QList& actions); + QMap seqEdit2ID; Ui::DialogHotkey *ui; }; diff --git a/include/ui/setting/dialog_hotkey.ui b/include/ui/setting/dialog_hotkey.ui index 72b2d97..5bf76bc 100644 --- a/include/ui/setting/dialog_hotkey.ui +++ b/include/ui/setting/dialog_hotkey.ui @@ -13,64 +13,105 @@ Hotkey - - - - - Show groups + + + + + 0 + + + Global + + + + + + Trigger main window + + + + + + + + + + Show groups + + + + + + + + + + Show routes + + + + + + + + + + Proxy mode + + + + + + + + + + Toggle System Proxy + + + + + + + + + + + Shortcuts + + + + + + Qt::ScrollBarPolicy::ScrollBarAsNeeded + + + true + + + + + 0 + 0 + 380 + 311 + + + + + + + - + - Qt::StrongFocus + Qt::FocusPolicy::StrongFocus - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - Trigger main window - - - - - - - Show routes - - - - - - - - - - Proxy mode - - - - - - - - - - - - - - - - - - - Toggle System Proxy + QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok @@ -84,10 +125,6 @@ - show_mainwindow - show_groups - show_routes - system_proxy buttonBox diff --git a/src/global/Configs.cpp b/src/global/Configs.cpp index ee35951..04c688c 100644 --- a/src/global/Configs.cpp +++ b/src/global/Configs.cpp @@ -8,7 +8,10 @@ #include #include #include +#include +#include #include +#include #include #ifdef Q_OS_WIN @@ -352,6 +355,34 @@ namespace Configs { _add(new configItem("dns_final_out", &this->dns_final_out, itemType::string)); } + Shortcuts::Shortcuts() : JsonStore() + { + _add(new configItem("keyval", &keyVal, itemType::stringList)); + } + + bool Shortcuts::Save() + { + keyVal.clear(); + for (auto [k, v] : shortcuts.asKeyValueRange()) + { + if (v.isEmpty()) continue; + keyVal << k << v.toString(); + } + + return JsonStore::Save(); + } + + bool Shortcuts::Load() { + auto ret = JsonStore::Load(); + if (!ret) return false; + if (keyVal.count()%2 != 0) return false; + for (int i=0;istart_minimal) Configs::dataStore->flag_tray = true; - // load routing + // load routing and shortcuts Configs::dataStore->routing = std::make_unique(); Configs::dataStore->routing->fn = ROUTES_PREFIX + "Default"; isLoaded = Configs::dataStore->routing->Load(); @@ -194,6 +194,13 @@ int main(int argc, char* argv[]) { Configs::dataStore->routing->Save(); } + Configs::dataStore->shortcuts = std::make_unique(); + Configs::dataStore->shortcuts->fn = "shortcuts.json"; + isLoaded = Configs::dataStore->shortcuts->Load(); + if (!isLoaded) { + Configs::dataStore->shortcuts->Save(); + } + // Translate QString locale; switch (Configs::dataStore->language) { diff --git a/src/ui/mainwindow.cpp b/src/ui/mainwindow.cpp index ac16c07..3d1cc12 100644 --- a/src/ui/mainwindow.cpp +++ b/src/ui/mainwindow.cpp @@ -86,6 +86,10 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi themeManager->ApplyTheme(Configs::dataStore->theme); ui->setupUi(this); + // init shortcuts + setActionsData(); + loadShortcuts(); + // setup log ui->splitter->restoreState(DecodeB64IfValid(Configs::dataStore->splitter_state)); new SyntaxHighlighter(isDarkMode() || Configs::dataStore->theme.toLower() == "qdarkstyle", qvLogDocument); @@ -201,7 +205,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi ui->tabWidget->installEventFilter(this); // RegisterHotkey(false); - RegisterShortcuts(); // auto last_size = Configs::dataStore->mw_size.split("x"); if (last_size.length() == 2) { @@ -749,6 +752,10 @@ void MainWindow::dialog_message_impl(const QString &sender, const QString &info) if (info == "NeedAdmin") { get_elevated_permissions(); } + if (info == "UpdateShortcuts") + { + loadShortcuts(); + } // sender if (sender == Dialog_DialogEditProfile) { auto msg = info.split(","); @@ -838,7 +845,15 @@ void MainWindow::on_menu_vpn_settings_triggered() { } void MainWindow::on_menu_hotkey_settings_triggered() { - USE_DIALOG(DialogHotkey) + if (dialog_is_using) return; + dialog_is_using = true; + auto dialog = new DialogHotkey(this, getActionsForShortcut()); + connect(dialog, &QDialog::finished, this, [=,this] + { + dialog->deleteLater(); + dialog_is_using = false; + }); + dialog->show(); } void MainWindow::on_commitDataRequest() { @@ -879,6 +894,7 @@ void MainWindow::prepare_exit() tray->hide(); Configs::dataStore->prepare_exit = true; // + RegisterHiddenMenuShortcuts(true); RegisterHotkey(true); if (Configs::dataStore->system_dns_set) set_system_dns(false, false); set_spmode_system_proxy(false, false); @@ -2282,7 +2298,7 @@ void MainWindow::RegisterHotkey(bool unregister) { auto hk = RegisteredHotkey.takeFirst(); hk->deleteLater(); } - if (unregister) return; + if (unregister || Configs::dataStore->prepare_exit) return; QStringList regstr{ Configs::dataStore->hotkey_mainwindow, @@ -2309,14 +2325,72 @@ void MainWindow::RegisterHotkey(bool unregister) { } } -void MainWindow::RegisterShortcuts() { +void MainWindow::RegisterHiddenMenuShortcuts(bool unregister) { + for (const auto s : hiddenMenuShortcuts) s->deleteLater(); + hiddenMenuShortcuts.clear(); + + if (unregister) return; + for (const auto &action: ui->menuHidden_menu->actions()) { - new QShortcut(action->shortcut(), this, [=,this](){ - action->trigger(); - }); + if (!action->shortcut().toString().isEmpty()) + { + hiddenMenuShortcuts.append(new QShortcut(action->shortcut(), this, [=,this](){ + action->trigger(); + })); + } } } +void MainWindow::setActionsData() +{ + // assign ids to menu actions so that we can save and restore them + ui->menu_add_from_input->setData(QString("m2")); + ui->menu_clear_test_result->setData(QString("m3")); + ui->menu_clone->setData(QString("m4")); + ui->menu_copy_links->setData(QString("m5")); + ui->menu_delete_repeat->setData(QString("m6")); + ui->menu_export_config->setData(QString("m7")); + ui->menu_qr->setData(QString("m8")); + ui->menu_remove_invalid->setData(QString("m9")); + ui->menu_remove_unavailable->setData(QString("m10")); + ui->menu_reset_traffic->setData(QString("m11")); + ui->menu_resolve_domain->setData(QString("m12")); + ui->menu_resolve_selected->setData(QString("m13")); + ui->menu_scan_qr->setData(QString("m14")); + ui->menu_stop_testing->setData(QString("m15")); + ui->menu_update_subscription->setData(QString("m16")); + ui->actionSpeedtest_Current->setData(QString("m18")); + ui->actionSpeedtest_Group->setData(QString("m19")); + ui->actionSpeedtest_Selected->setData(QString("m20")); + ui->actionUrl_Test_Group->setData(QString("m21")); + ui->actionUrl_Test_Selected->setData(QString("m22")); +} + +QList MainWindow::getActionsForShortcut() +{ + QList list; + QList actions = findChildren(); + + for (QAction *action : actions) { + if (action->data().isNull() || action->data().toString().isEmpty()) continue; + list.append(action); + } + return list; +} + +void MainWindow::loadShortcuts() +{ + auto mp = Configs::dataStore->shortcuts->shortcuts; + for (QList actions = findChildren(); QAction *action : actions) + { + if (mp.count(action->data().toString()) == 0) action->setShortcut(QKeySequence()); + else action->setShortcut(mp[action->data().toString()]); + } + + RegisterHiddenMenuShortcuts(); +} + + void MainWindow::HotkeyEvent(const QString &key) { if (key.isEmpty()) return; runOnUiThread([=,this] { diff --git a/src/ui/setting/dialog_hotkey.cpp b/src/ui/setting/dialog_hotkey.cpp index 10f287d..ff7e946 100644 --- a/src/ui/setting/dialog_hotkey.cpp +++ b/src/ui/setting/dialog_hotkey.cpp @@ -1,26 +1,63 @@ #include "include/ui/setting/dialog_hotkey.h" +#include + #include "include/ui/mainwindow_interface.h" -DialogHotkey::DialogHotkey(QWidget *parent) : QDialog(parent), ui(new Ui::DialogHotkey) { +DialogHotkey::DialogHotkey(QWidget *parent, const QList& actions) : QDialog(parent), ui(new Ui::DialogHotkey) { ui->setupUi(this); ui->show_mainwindow->setKeySequence(Configs::dataStore->hotkey_mainwindow); ui->show_groups->setKeySequence(Configs::dataStore->hotkey_group); ui->show_routes->setKeySequence(Configs::dataStore->hotkey_route); ui->system_proxy->setKeySequence(Configs::dataStore->hotkey_system_proxy_menu); ui->toggle_proxy->setKeySequence(Configs::dataStore->hotkey_toggle_system_proxy); + + generateShortcutItems(actions); + GetMainWindow()->RegisterHotkey(true); } -DialogHotkey::~DialogHotkey() { - if (result() == QDialog::Accepted) { - Configs::dataStore->hotkey_mainwindow = ui->show_mainwindow->keySequence().toString(); - Configs::dataStore->hotkey_group = ui->show_groups->keySequence().toString(); - Configs::dataStore->hotkey_route = ui->show_routes->keySequence().toString(); - Configs::dataStore->hotkey_system_proxy_menu = ui->system_proxy->keySequence().toString(); - Configs::dataStore->hotkey_toggle_system_proxy = ui->toggle_proxy->keySequence().toString(); - Configs::dataStore->Save(); +void DialogHotkey::generateShortcutItems(const QList& actions) +{ + auto layout = new QFormLayout(this); + auto widget = new QWidget(this); + widget->setLayout(layout); + ui->shortcut_area->setWidget(widget); + for (auto action : actions) + { + auto kseq = new QtExtKeySequenceEdit(this); + if (!action->shortcut().isEmpty()) kseq->setKeySequence(action->shortcut()); + seqEdit2ID[kseq] = action->data().toString(); + layout->addRow(action->text(), kseq); } +} + +void DialogHotkey::accept() +{ + Configs::dataStore->hotkey_mainwindow = ui->show_mainwindow->keySequence().toString(); + Configs::dataStore->hotkey_group = ui->show_groups->keySequence().toString(); + Configs::dataStore->hotkey_route = ui->show_routes->keySequence().toString(); + Configs::dataStore->hotkey_system_proxy_menu = ui->system_proxy->keySequence().toString(); + Configs::dataStore->hotkey_toggle_system_proxy = ui->toggle_proxy->keySequence().toString(); + + for (auto [kseq, actionID] : seqEdit2ID.asKeyValueRange()) + { + Configs::dataStore->shortcuts->shortcuts[actionID] = kseq->keySequence(); + } + Configs::dataStore->shortcuts->Save(); + + Configs::dataStore->Save(); + MW_dialog_message(Dialog_DialogManageHotkeys, "UpdateShortcuts"); GetMainWindow()->RegisterHotkey(false); + QDialog::accept(); +} + +void DialogHotkey::reject() +{ + GetMainWindow()->RegisterHotkey(false); + QDialog::reject(); +} + +DialogHotkey::~DialogHotkey() { delete ui; }