add custom shortcut settings

This commit is contained in:
Nova 2025-09-30 02:09:03 +03:30
parent 4942c25f18
commit 1c0eb43617
9 changed files with 292 additions and 74 deletions

View File

@ -31,6 +31,19 @@ namespace Configs {
static QStringList List();
};
class Shortcuts : public JsonStore
{
public:
QMap<QString, QKeySequence> 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> shortcuts;
// Socks & HTTP Inbound
QString inbound_address = "127.0.0.1";

View File

@ -6,6 +6,7 @@
#define Dialog_DialogEditProfile "DialogEditProfile"
#define Dialog_DialogManageGroups "DialogManageGroups"
#define Dialog_DialogManageRoutes "DialogManageRoutes"
#define Dialog_DialogManageHotkeys "DialogManageHotkeys"
// Utils

View File

@ -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<QShortcut*> hiddenMenuShortcuts;
std::map<std::string, std::string> ruleSetMap;
QStringList remoteRouteProfiles;
@ -237,7 +240,13 @@ private:
void HotkeyEvent(const QString &key);
void RegisterShortcuts();
void RegisterHiddenMenuShortcuts(bool unregister = false);
void setActionsData();
QList<QAction*> getActionsForShortcut();
void loadShortcuts();
// grpc

View File

@ -14,10 +14,18 @@ class DialogHotkey : public QDialog {
Q_OBJECT
public:
explicit DialogHotkey(QWidget *parent = nullptr);
explicit DialogHotkey(QWidget *parent = nullptr, const QList<QAction*>& actions = {});
~DialogHotkey() override;
public slots:
void accept();
void reject();
private:
void generateShortcutItems(const QList<QAction*>& actions);
QMap<QtExtKeySequenceEdit*, QString> seqEdit2ID;
Ui::DialogHotkey *ui;
};

View File

@ -13,64 +13,105 @@
<property name="windowTitle">
<string>Hotkey</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Show groups</string>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Global</string>
</attribute>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Trigger main window</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QtExtKeySequenceEdit" name="show_mainwindow"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Show groups</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QtExtKeySequenceEdit" name="show_groups"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Show routes</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QtExtKeySequenceEdit" name="show_routes"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Proxy mode</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QtExtKeySequenceEdit" name="system_proxy"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="toggle_proxy_l">
<property name="text">
<string>Toggle System Proxy</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QKeySequenceEdit" name="toggle_proxy"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Shortcuts</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QScrollArea" name="shortcut_area">
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarPolicy::ScrollBarAsNeeded</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>380</width>
<height>311</height>
</rect>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item row="5" column="1">
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
<enum>Qt::FocusPolicy::StrongFocus</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Trigger main window</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Show routes</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QtExtKeySequenceEdit" name="system_proxy"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Proxy mode</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QtExtKeySequenceEdit" name="show_groups"/>
</item>
<item row="2" column="1">
<widget class="QtExtKeySequenceEdit" name="show_routes"/>
</item>
<item row="0" column="1">
<widget class="QtExtKeySequenceEdit" name="show_mainwindow"/>
</item>
<item row="4" column="1">
<widget class="QKeySequenceEdit" name="toggle_proxy"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="toggle_proxy_l">
<property name="text">
<string>Toggle System Proxy</string>
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
</property>
</widget>
</item>
@ -84,10 +125,6 @@
</customwidget>
</customwidgets>
<tabstops>
<tabstop>show_mainwindow</tabstop>
<tabstop>show_groups</tabstop>
<tabstop>show_routes</tabstop>
<tabstop>system_proxy</tabstop>
<tabstop>buttonBox</tabstop>
</tabstops>
<resources/>

View File

@ -8,7 +8,10 @@
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QKeySequence>
#include <QNetworkAccessManager>
#include <QStandardPaths>
#include <utility>
#include <include/api/RPC.h>
#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;i<keyVal.size();i+=2)
{
shortcuts[keyVal[i]] = QKeySequence(keyVal[i+1]);
}
return ret;
}
QStringList Routing::List() {
return {"Default"};
}

View File

@ -186,7 +186,7 @@ int main(int argc, char* argv[]) {
// Datastore & Flags
if (Configs::dataStore->start_minimal) Configs::dataStore->flag_tray = true;
// load routing
// load routing and shortcuts
Configs::dataStore->routing = std::make_unique<Configs::Routing>();
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::Shortcuts>();
Configs::dataStore->shortcuts->fn = "shortcuts.json";
isLoaded = Configs::dataStore->shortcuts->Load();
if (!isLoaded) {
Configs::dataStore->shortcuts->Save();
}
// Translate
QString locale;
switch (Configs::dataStore->language) {

View File

@ -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<QAction*> MainWindow::getActionsForShortcut()
{
QList<QAction*> list;
QList<QAction *> actions = findChildren<QAction *>();
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<QAction *> actions = findChildren<QAction *>(); 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] {

View File

@ -1,26 +1,63 @@
#include "include/ui/setting/dialog_hotkey.h"
#include <include/global/GuiUtils.hpp>
#include "include/ui/mainwindow_interface.h"
DialogHotkey::DialogHotkey(QWidget *parent) : QDialog(parent), ui(new Ui::DialogHotkey) {
DialogHotkey::DialogHotkey(QWidget *parent, const QList<QAction*>& 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<QAction*>& 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;
}