Fixup Saving/Profiling logic

This commit is contained in:
collecting
2026-02-04 19:26:27 -05:00
parent 23e7ea382a
commit be0e131113
5 changed files with 208 additions and 127 deletions

View File

@@ -6,6 +6,7 @@
#include <QInputDialog> #include <QInputDialog>
#include <QMenu> #include <QMenu>
#include <QMessageBox> #include <QMessageBox>
#include <QModelIndex>
#include <QShortcut> #include <QShortcut>
#include <QStandardItemModel> #include <QStandardItemModel>
#include <QTimer> #include <QTimer>
@@ -93,14 +94,20 @@ ConfigureHotkeys::ConfigureHotkeys(HotkeyRegistry& registry_, Core::HID::HIDCore
ConfigureHotkeys::~ConfigureHotkeys() = default; ConfigureHotkeys::~ConfigureHotkeys() = default;
void ConfigureHotkeys::Populate() { void ConfigureHotkeys::Populate(const std::string& profile_name) {
const auto& profiles = profile_manager.GetProfiles(); const auto& profiles = profile_manager.GetProfiles();
const auto& current_profile_name = profiles.current_profile; std::string target_profile = profile_name;
if (target_profile.empty()) {
target_profile = ui->combo_box_profile->currentText().toStdString();
}
if (target_profile.empty()) {
target_profile = profiles.current_profile;
}
// Use default if current profile missing (safety) // Use default if current profile missing (safety)
std::vector<Hotkey::BackendShortcut> current_shortcuts; std::vector<Hotkey::BackendShortcut> current_shortcuts;
if (profiles.profiles.count(current_profile_name)) { if (profiles.profiles.count(target_profile)) {
current_shortcuts = profiles.profiles.at(current_profile_name); current_shortcuts = profiles.profiles.at(target_profile);
} else if (profiles.profiles.count("Default")) { } else if (profiles.profiles.count("Default")) {
current_shortcuts = profiles.profiles.at("Default"); current_shortcuts = profiles.profiles.at("Default");
} }
@@ -119,18 +126,19 @@ void ConfigureHotkeys::Populate() {
auto* parent_item = new QStandardItem( auto* parent_item = new QStandardItem(
QCoreApplication::translate("Hotkeys", qPrintable(QString::fromStdString(group_name)))); QCoreApplication::translate("Hotkeys", qPrintable(QString::fromStdString(group_name))));
parent_item->setEditable(false); parent_item->setEditable(false);
parent_item->setData(QString::fromStdString(group_name)); parent_item->setData(QString::fromStdString(group_name), Qt::UserRole);
model->appendRow(parent_item); model->appendRow(parent_item);
for (const auto& [action_name, hotkey] : group_map) { for (const auto& [action_name, hotkey] : group_map) {
// Determine values (Registry Default vs Profile Override) // Determine values (Registry Default vs Profile Override)
QString keyseq_str = hotkey.keyseq.toString(QKeySequence::NativeText); QString keyseq_str = hotkey.keyseq.toString(QKeySequence::NativeText);
QString portable_keyseq = hotkey.keyseq.toString(QKeySequence::PortableText);
QString controller_keyseq_str = QString::fromStdString(hotkey.controller_keyseq); QString controller_keyseq_str = QString::fromStdString(hotkey.controller_keyseq);
if (overrides.count({group_name, action_name})) { if (overrides.count({group_name, action_name})) {
const auto& overridden = overrides.at({group_name, action_name}); const auto& overridden = overrides.at({group_name, action_name});
keyseq_str = QKeySequence(QString::fromStdString(overridden.shortcut.keyseq)) portable_keyseq = QString::fromStdString(overridden.shortcut.keyseq);
.toString(QKeySequence::NativeText); keyseq_str = QKeySequence(portable_keyseq).toString(QKeySequence::NativeText);
controller_keyseq_str = controller_keyseq_str =
QString::fromStdString(overridden.shortcut.controller_keyseq); QString::fromStdString(overridden.shortcut.controller_keyseq);
} }
@@ -141,7 +149,7 @@ void ConfigureHotkeys::Populate() {
action_item->setData(QString::fromStdString(action_name), Qt::UserRole); action_item->setData(QString::fromStdString(action_name), Qt::UserRole);
auto* keyseq_item = new QStandardItem(keyseq_str); auto* keyseq_item = new QStandardItem(keyseq_str);
keyseq_item->setData(keyseq_str, Qt::UserRole); keyseq_item->setData(portable_keyseq, Qt::UserRole);
keyseq_item->setEditable(false); keyseq_item->setEditable(false);
auto* controller_item = new QStandardItem(controller_keyseq_str); auto* controller_item = new QStandardItem(controller_keyseq_str);
@@ -238,32 +246,69 @@ void ConfigureHotkeys::OnRenameProfile() {
void ConfigureHotkeys::OnImportProfile() { void ConfigureHotkeys::OnImportProfile() {
QString fileName = QFileDialog::getOpenFileName(this, tr("Import Profile"), QString(), QString fileName = QFileDialog::getOpenFileName(this, tr("Import Profile"), QString(),
tr("JSON Files (*.json)")); tr("JSON Files (*.json)"));
if (!fileName.isEmpty()) { if (fileName.isEmpty())
if (profile_manager.ImportProfile(fileName.toStdString())) { return;
UpdateProfileList();
} else { QFile file(fileName);
QMessageBox::warning(this, tr("Error"), tr("Failed to import profile.")); if (!file.open(QIODevice::ReadOnly)) {
} QMessageBox::warning(this, tr("Error"), tr("Failed to open file for reading."));
return;
} }
const QByteArray jsonData = file.readAll();
const QJsonDocument doc = QJsonDocument::fromJson(jsonData);
if (!doc.isObject()) {
QMessageBox::warning(this, tr("Error"), tr("Invalid profile format."));
return;
}
const QJsonObject root = doc.object();
if (!root.contains(QStringLiteral("shortcuts"))) {
QMessageBox::warning(this, tr("Error"), tr("Invalid profile file (missing shortcuts)."));
return;
}
std::vector<Hotkey::BackendShortcut> shortcuts;
const QJsonArray arr = root[QStringLiteral("shortcuts")].toArray();
for (const auto& val : arr) {
shortcuts.push_back(Hotkey::ProfileManager::DeserializeShortcut(val.toObject()));
}
ApplyShortcutsToModel(shortcuts);
} }
void ConfigureHotkeys::OnExportProfile() { void ConfigureHotkeys::OnExportProfile() {
QString current = ui->combo_box_profile->currentText(); QString current = ui->combo_box_profile->currentText();
QString fileName = QFileDialog::getSaveFileName( QString fileName = QFileDialog::getSaveFileName(
this, tr("Export Profile"), current + QStringLiteral(".json"), tr("JSON Files (*.json)")); this, tr("Export Profile"), current + QStringLiteral(".json"), tr("JSON Files (*.json)"));
if (!fileName.isEmpty()) { if (fileName.isEmpty())
if (!profile_manager.ExportProfile(current.toStdString(), fileName.toStdString())) { return;
QMessageBox::warning(this, tr("Error"), tr("Failed to export profile."));
} const std::vector<Hotkey::BackendShortcut> shortcuts = GatherShortcutsFromUI();
QJsonObject root_obj;
root_obj[QStringLiteral("name")] = current;
QJsonArray shortcuts_arr;
for (const auto& s : shortcuts) {
shortcuts_arr.append(Hotkey::ProfileManager::SerializeShortcut(s));
} }
root_obj[QStringLiteral("shortcuts")] = shortcuts_arr;
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly)) {
QMessageBox::warning(this, tr("Error"), tr("Failed to open file for writing."));
return;
}
file.write(QJsonDocument(root_obj).toJson());
} }
void ConfigureHotkeys::OnProfileChanged(int index) { void ConfigureHotkeys::OnProfileChanged(int index) {
if (index == -1) if (index == -1)
return; return;
const std::string name = ui->combo_box_profile->currentText().toStdString(); const std::string name = ui->combo_box_profile->currentText().toStdString();
profile_manager.SetCurrentProfile(name); // Decoupled from permanent SetCurrentProfile to ensure "stagnant" behavior.
Populate(); Populate(name);
} }
void ConfigureHotkeys::changeEvent(QEvent* event) { void ConfigureHotkeys::changeEvent(QEvent* event) {
@@ -322,6 +367,7 @@ void ConfigureHotkeys::Configure(QModelIndex index) {
tr("The entered key sequence is already assigned to: %1").arg(used_action)); tr("The entered key sequence is already assigned to: %1").arg(used_action));
} else { } else {
model->setData(index, key_sequence.toString(QKeySequence::NativeText)); model->setData(index, key_sequence.toString(QKeySequence::NativeText));
model->setData(index, key_sequence.toString(QKeySequence::PortableText), Qt::UserRole);
} }
} }
void ConfigureHotkeys::ConfigureController(QModelIndex index) { void ConfigureHotkeys::ConfigureController(QModelIndex index) {
@@ -477,104 +523,60 @@ std::pair<bool, QString> ConfigureHotkeys::IsUsedControllerKey(const QString& ke
} }
void ConfigureHotkeys::ApplyConfiguration() { void ConfigureHotkeys::ApplyConfiguration() {
// 1. Update the runtime HotkeyRegistry and UISettings // 1. Sync the current profile selection permanently
const auto& root = model->invisibleRootItem(); const std::string current_profile_name = ui->combo_box_profile->currentText().toStdString();
std::vector<UISettings::Shortcut> new_ui_shortcuts; profile_manager.SetCurrentProfile(current_profile_name);
for (int group_row = 0; group_row < root->rowCount(); group_row++) { // 2. Update the runtime HotkeyRegistry and UISettings
const auto* group_item = root->child(group_row); const auto shortcuts = GatherShortcutsFromUI();
const std::string group_name = group_item->data().toString().toStdString();
for (int row = 0; row < group_item->rowCount(); row++) { for (const auto& s : shortcuts) {
const auto* action_item = group_item->child(row, name_column); // Update Registry
const auto* keyseq_item = group_item->child(row, hotkey_column); auto& hk = registry.hotkey_groups[s.group][s.name];
const auto* controller_item = group_item->child(row, controller_column); hk.keyseq = QKeySequence::fromString(QString::fromStdString(s.shortcut.keyseq));
hk.controller_keyseq = s.shortcut.controller_keyseq;
hk.context = static_cast<Qt::ShortcutContext>(s.shortcut.context);
hk.repeat = s.shortcut.repeat;
const std::string action_name = if (hk.shortcut) {
action_item->data(Qt::UserRole).toString().toStdString(); hk.shortcut->setKey(hk.keyseq);
const QString keyseq_str = keyseq_item->text(); }
const std::string controller_keyseq = controller_item->text().toStdString(); if (hk.controller_shortcut) {
const int context = action_item->data(Qt::UserRole + 1).toInt(); hk.controller_shortcut->SetKey(hk.controller_keyseq);
const bool repeat = action_item->data(Qt::UserRole + 2).toBool();
// Update Registry
auto& hk = registry.hotkey_groups[group_name][action_name];
hk.keyseq = QKeySequence::fromString(keyseq_str, QKeySequence::NativeText);
hk.controller_keyseq = controller_keyseq;
hk.context = static_cast<Qt::ShortcutContext>(context);
hk.repeat = repeat;
if (hk.shortcut) {
hk.shortcut->setKey(hk.keyseq);
}
if (hk.controller_shortcut) {
hk.controller_shortcut->SetKey(hk.controller_keyseq);
}
// Sync with UISettings::values.shortcuts (only if modified from default)
// Actually, registry.SaveHotkeys() handles the "is_modified" check,
// but we'll collect them here for completeness if needed or just call SaveHotkeys.
} }
} }
// This will correctly populate UISettings::values.shortcuts based on current registry state // This will correctly populate UISettings::values.shortcuts based on current registry state
registry.SaveHotkeys(); registry.SaveHotkeys();
// 2. Update the ProfileManager (Storage) // 3. Update the ProfileManager (Storage)
const std::string current_profile_name = profile_manager.GetProfiles().current_profile; profile_manager.SetProfileShortcuts(current_profile_name, shortcuts);
std::vector<Hotkey::BackendShortcut> new_shortcuts;
for (int group_row = 0; group_row < root->rowCount(); group_row++) {
const auto* group_item = root->child(group_row);
const std::string group_name = group_item->data().toString().toStdString();
for (int row = 0; row < group_item->rowCount(); row++) {
const auto* action_item = group_item->child(row, name_column);
const auto* keyseq_item = group_item->child(row, hotkey_column);
const auto* controller_item = group_item->child(row, controller_column);
Hotkey::BackendShortcut s;
s.group = group_name;
s.name = action_item->data(Qt::UserRole).toString().toStdString();
s.shortcut.keyseq =
QKeySequence::fromString(keyseq_item->text(), QKeySequence::NativeText)
.toString()
.toStdString();
s.shortcut.controller_keyseq = controller_item->text().toStdString();
s.shortcut.context = action_item->data(Qt::UserRole + 1).toInt();
s.shortcut.repeat = action_item->data(Qt::UserRole + 2).toBool();
new_shortcuts.push_back(s);
}
}
profile_manager.SetProfileShortcuts(current_profile_name, new_shortcuts);
profile_manager.Save(); profile_manager.Save();
} }
void ConfigureHotkeys::RestoreDefaults() { void ConfigureHotkeys::RestoreDefaults() {
size_t hotkey_index = 0;
const size_t total_default_hotkeys = UISettings::default_hotkeys.size();
for (int group_row = 0; group_row < model->rowCount(); ++group_row) { for (int group_row = 0; group_row < model->rowCount(); ++group_row) {
QStandardItem* parent = model->item(group_row, 0); QStandardItem* parent = model->item(group_row, 0);
const std::string group_name = parent->data(Qt::UserRole).toString().toStdString();
for (int child_row = 0; child_row < parent->rowCount(); ++child_row) { for (int child_row = 0; child_row < parent->rowCount(); ++child_row) {
// This bounds check prevents a crash. QStandardItem* action_item = parent->child(child_row, name_column);
if (hotkey_index >= total_default_hotkeys) { const std::string action_name =
QMessageBox::information(this, tr("Success!"), action_item->data(Qt::UserRole).toString().toStdString();
tr("Citron's Default hotkey entries have been restored!"));
return; // Find default
for (const auto& def : UISettings::default_hotkeys) {
if (def.group == group_name && def.name == action_name) {
QStandardItem* hotkey_item = parent->child(child_row, hotkey_column);
hotkey_item->setText(
QKeySequence::fromString(QString::fromStdString(def.shortcut.keyseq))
.toString(QKeySequence::NativeText));
hotkey_item->setData(QString::fromStdString(def.shortcut.keyseq), Qt::UserRole);
parent->child(child_row, controller_column)
->setText(QString::fromStdString(def.shortcut.controller_keyseq));
break;
}
} }
const auto& default_shortcut = UISettings::default_hotkeys[hotkey_index].shortcut;
parent->child(child_row, hotkey_column)
->setText(QString::fromStdString(default_shortcut.keyseq));
parent->child(child_row, controller_column)
->setText(QString::fromStdString(default_shortcut.controller_keyseq));
hotkey_index++;
} }
} }
@@ -586,7 +588,9 @@ void ConfigureHotkeys::ClearAll() {
const QStandardItem* parent = model->item(r, 0); const QStandardItem* parent = model->item(r, 0);
for (int r2 = 0; r2 < parent->rowCount(); ++r2) { for (int r2 = 0; r2 < parent->rowCount(); ++r2) {
model->item(r, 0)->child(r2, hotkey_column)->setText(QString{}); QStandardItem* hotkey_item = model->item(r, 0)->child(r2, hotkey_column);
hotkey_item->setText(QString{});
hotkey_item->setData(QString{}, Qt::UserRole);
model->item(r, 0)->child(r2, controller_column)->setText(QString{}); model->item(r, 0)->child(r2, controller_column)->setText(QString{});
} }
} }
@@ -623,8 +627,8 @@ void ConfigureHotkeys::PopupContextMenu(const QPoint& menu_location) {
void ConfigureHotkeys::RestoreControllerHotkey(QModelIndex index) { void ConfigureHotkeys::RestoreControllerHotkey(QModelIndex index) {
const auto* group_item = model->itemFromIndex(index.parent()); const auto* group_item = model->itemFromIndex(index.parent());
const auto* action_item = group_item->child(index.row(), name_column); const auto* action_item = group_item->child(index.row(), name_column);
const std::string group_name = group_item->data().toString().toStdString(); const std::string group_name = group_item->data(Qt::UserRole).toString().toStdString();
const std::string action_name = action_item->data().toString().toStdString(); const std::string action_name = action_item->data(Qt::UserRole).toString().toStdString();
QString default_key_sequence; QString default_key_sequence;
for (const auto& def : UISettings::default_hotkeys) { for (const auto& def : UISettings::default_hotkeys) {
@@ -648,8 +652,8 @@ void ConfigureHotkeys::RestoreControllerHotkey(QModelIndex index) {
void ConfigureHotkeys::RestoreHotkey(QModelIndex index) { void ConfigureHotkeys::RestoreHotkey(QModelIndex index) {
const auto* group_item = model->itemFromIndex(index.parent()); const auto* group_item = model->itemFromIndex(index.parent());
const auto* action_item = group_item->child(index.row(), name_column); const auto* action_item = group_item->child(index.row(), name_column);
const std::string group_name = group_item->data().toString().toStdString(); const std::string group_name = group_item->data(Qt::UserRole).toString().toStdString();
const std::string action_name = action_item->data().toString().toStdString(); const std::string action_name = action_item->data(Qt::UserRole).toString().toStdString();
QString default_key_str; QString default_key_str;
for (const auto& def : UISettings::default_hotkeys) { for (const auto& def : UISettings::default_hotkeys) {
@@ -667,7 +671,64 @@ void ConfigureHotkeys::RestoreHotkey(QModelIndex index) {
QMessageBox::warning( QMessageBox::warning(
this, tr("Conflicting Key Sequence"), this, tr("Conflicting Key Sequence"),
tr("The default key sequence is already assigned to: %1").arg(used_action)); tr("The default key sequence is already assigned to: %1").arg(used_action));
} else { }
model->setData(index, default_key_sequence.toString(QKeySequence::NativeText)); }
std::vector<Hotkey::BackendShortcut> ConfigureHotkeys::GatherShortcutsFromUI() const {
std::vector<Hotkey::BackendShortcut> shortcuts;
const auto& root = model->invisibleRootItem();
for (int group_row = 0; group_row < root->rowCount(); group_row++) {
const auto* group_item = root->child(group_row);
const std::string group_name = group_item->data(Qt::UserRole).toString().toStdString();
for (int row = 0; row < group_item->rowCount(); row++) {
const auto* action_item = group_item->child(row, name_column);
const auto* keyseq_item = group_item->child(row, hotkey_column);
const auto* controller_item = group_item->child(row, controller_column);
Hotkey::BackendShortcut s;
s.group = group_name;
s.name = action_item->data(Qt::UserRole).toString().toStdString();
s.shortcut.keyseq = keyseq_item->data(Qt::UserRole).toString().toStdString();
s.shortcut.controller_keyseq = controller_item->text().toStdString();
s.shortcut.context = action_item->data(Qt::UserRole + 1).toInt();
s.shortcut.repeat = action_item->data(Qt::UserRole + 2).toBool();
shortcuts.push_back(s);
}
}
return shortcuts;
}
void ConfigureHotkeys::ApplyShortcutsToModel(
const std::vector<Hotkey::BackendShortcut>& shortcuts) {
// Map for faster lookup
std::map<std::pair<std::string, std::string>, Hotkey::BackendShortcut> shortcut_map;
for (const auto& s : shortcuts) {
shortcut_map[{s.group, s.name}] = s;
}
const auto& root = model->invisibleRootItem();
for (int group_row = 0; group_row < root->rowCount(); group_row++) {
auto* group_item = root->child(group_row);
const std::string group_name = group_item->data(Qt::UserRole).toString().toStdString();
for (int row = 0; row < group_item->rowCount(); row++) {
auto* action_item = group_item->child(row, name_column);
const std::string action_name =
action_item->data(Qt::UserRole).toString().toStdString();
if (shortcut_map.count({group_name, action_name})) {
const auto& s = shortcut_map.at({group_name, action_name});
QStandardItem* hotkey_item = group_item->child(row, hotkey_column);
hotkey_item->setText(QKeySequence(QString::fromStdString(s.shortcut.keyseq))
.toString(QKeySequence::NativeText));
hotkey_item->setData(QString::fromStdString(s.shortcut.keyseq), Qt::UserRole);
model->setData(model->index(row, controller_column, group_item->index()),
QString::fromStdString(s.shortcut.controller_keyseq));
model->setData(model->index(row, name_column, group_item->index()),
s.shortcut.context, Qt::UserRole + 1);
model->setData(model->index(row, name_column, group_item->index()),
s.shortcut.repeat, Qt::UserRole + 2);
}
}
} }
} }

View File

@@ -35,7 +35,7 @@ public:
* Called every time the Configure dialog is opened. * Called every time the Configure dialog is opened.
* @param profiles The UserHotkeyProfiles used to populate the list. * @param profiles The UserHotkeyProfiles used to populate the list.
*/ */
void Populate(); void Populate(const std::string& profile_name = "");
private slots: private slots:
void OnCreateProfile(); void OnCreateProfile();
@@ -61,6 +61,9 @@ private:
void RestoreControllerHotkey(QModelIndex index); void RestoreControllerHotkey(QModelIndex index);
void RestoreHotkey(QModelIndex index); void RestoreHotkey(QModelIndex index);
std::vector<Hotkey::BackendShortcut> GatherShortcutsFromUI() const;
void ApplyShortcutsToModel(const std::vector<Hotkey::BackendShortcut>& shortcuts);
void SetPollingResult(bool cancel); void SetPollingResult(bool cancel);
QString GetButtonCombinationName(Core::HID::NpadButton button, bool home, bool capture) const; QString GetButtonCombinationName(Core::HID::NpadButton button, bool home, bool capture) const;

View File

@@ -2,6 +2,8 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm> #include <algorithm>
#include <string>
#include <vector>
#include <QDir> #include <QDir>
#include <QFile> #include <QFile>
#include <QJsonArray> #include <QJsonArray>
@@ -29,7 +31,7 @@ static std::string GetSaveFilePath() {
} }
// JSON Serialization Helpers // JSON Serialization Helpers
static QJsonObject SerializeShortcut(const BackendShortcut& shortcut) { QJsonObject ProfileManager::SerializeShortcut(const BackendShortcut& shortcut) {
QJsonObject obj; QJsonObject obj;
obj[QStringLiteral("name")] = QString::fromStdString(shortcut.name); obj[QStringLiteral("name")] = QString::fromStdString(shortcut.name);
obj[QStringLiteral("group")] = QString::fromStdString(shortcut.group); obj[QStringLiteral("group")] = QString::fromStdString(shortcut.group);
@@ -41,7 +43,7 @@ static QJsonObject SerializeShortcut(const BackendShortcut& shortcut) {
return obj; return obj;
} }
static BackendShortcut DeserializeShortcut(const QJsonObject& obj) { BackendShortcut ProfileManager::DeserializeShortcut(const QJsonObject& obj) {
BackendShortcut s; BackendShortcut s;
s.name = obj[QStringLiteral("name")].toString().toStdString(); s.name = obj[QStringLiteral("name")].toString().toStdString();
s.group = obj[QStringLiteral("group")].toString().toStdString(); s.group = obj[QStringLiteral("group")].toString().toStdString();
@@ -204,26 +206,35 @@ bool ProfileManager::ExportProfile(const std::string& profile_name, const std::s
} }
bool ProfileManager::ImportProfile(const std::string& file_path) { bool ProfileManager::ImportProfile(const std::string& file_path) {
return !ImportProfileAndGetFinalName(file_path).empty();
}
std::string ProfileManager::ImportProfileAndGetFinalName(const std::string& file_path) {
QFile file(QString::fromStdString(file_path)); QFile file(QString::fromStdString(file_path));
if (!file.open(QIODevice::ReadOnly)) if (!file.open(QIODevice::ReadOnly))
return false; return {};
const QByteArray data = file.readAll(); const QByteArray data = file.readAll();
const QJsonDocument doc = QJsonDocument::fromJson(data); const QJsonDocument doc = QJsonDocument::fromJson(data);
const QJsonObject root = doc.object(); const QJsonObject root = doc.object();
if (!root.contains(QStringLiteral("name")) || !root.contains(QStringLiteral("shortcuts"))) if (!root.contains(QStringLiteral("name")) || !root.contains(QStringLiteral("shortcuts")))
return false; return {};
std::string profile_name = root[QStringLiteral("name")].toString().toStdString(); std::string profile_name = root[QStringLiteral("name")].toString().toStdString();
if (profile_name.empty()) {
profile_name = "Imported Profile";
}
// Handle name collision // Handle name collision
if (profiles.profiles.count(profile_name)) { std::string base_name = profile_name;
profile_name += " (Imported)"; int suffix = 1;
while (profiles.profiles.count(profile_name)) {
profile_name = base_name + " (" + std::to_string(suffix++) + ")";
} }
if (profiles.profiles.size() >= MAX_PROFILES) if (profiles.profiles.size() >= MAX_PROFILES)
return false; return {};
std::vector<BackendShortcut> shortcuts; std::vector<BackendShortcut> shortcuts;
const QJsonArray arr = root[QStringLiteral("shortcuts")].toArray(); const QJsonArray arr = root[QStringLiteral("shortcuts")].toArray();
@@ -233,7 +244,7 @@ bool ProfileManager::ImportProfile(const std::string& file_path) {
profiles.profiles[profile_name] = shortcuts; profiles.profiles[profile_name] = shortcuts;
Save(); Save();
return true; return profile_name;
} }
} // namespace Hotkey } // namespace Hotkey

View File

@@ -53,6 +53,11 @@ public:
// Import/Export // Import/Export
bool ExportProfile(const std::string& profile_name, const std::string& file_path); bool ExportProfile(const std::string& profile_name, const std::string& file_path);
bool ImportProfile(const std::string& file_path); bool ImportProfile(const std::string& file_path);
std::string ImportProfileAndGetFinalName(const std::string& file_path);
// JSON Serialization Helpers
static QJsonObject SerializeShortcut(const BackendShortcut& shortcut);
static BackendShortcut DeserializeShortcut(const QJsonObject& obj);
// IO // IO
void Load(); void Load();

View File

@@ -7,9 +7,10 @@
#include <QTreeWidgetItem> #include <QTreeWidgetItem>
#include <QtGlobal> #include <QtGlobal>
#include "hid_core/frontend/emulated_controller.h"
#include "citron/hotkeys.h" #include "citron/hotkeys.h"
#include "citron/uisettings.h" #include "citron/uisettings.h"
#include "hid_core/frontend/emulated_controller.h"
HotkeyRegistry::HotkeyRegistry() = default; HotkeyRegistry::HotkeyRegistry() = default;
HotkeyRegistry::~HotkeyRegistry() = default; HotkeyRegistry::~HotkeyRegistry() = default;
@@ -46,10 +47,10 @@ void HotkeyRegistry::SaveHotkeys() {
if (is_modified) { if (is_modified) {
UISettings::values.shortcuts.push_back( UISettings::values.shortcuts.push_back(
{action_name, group_name, {action_name, group_name,
UISettings::ContextualShortcut( UISettings::ContextualShortcut({current_hotkey.keyseq.toString().toStdString(),
{current_hotkey.keyseq.toString().toStdString(), current_hotkey.controller_keyseq,
current_hotkey.controller_keyseq, current_hotkey.context, current_hotkey.context,
current_hotkey.repeat})}); current_hotkey.repeat})});
} }
} }
} }
@@ -70,8 +71,7 @@ void HotkeyRegistry::LoadHotkeys() {
for (const auto& shortcut : UISettings::values.shortcuts) { for (const auto& shortcut : UISettings::values.shortcuts) {
Hotkey& hk = hotkey_groups[shortcut.group][shortcut.name]; Hotkey& hk = hotkey_groups[shortcut.group][shortcut.name];
if (!shortcut.shortcut.keyseq.empty()) { if (!shortcut.shortcut.keyseq.empty()) {
hk.keyseq = QKeySequence::fromString(QString::fromStdString(shortcut.shortcut.keyseq), hk.keyseq = QKeySequence::fromString(QString::fromStdString(shortcut.shortcut.keyseq));
QKeySequence::NativeText);
} else { } else {
// This is the fix: explicitly clear the key sequence if it was saved as empty. // This is the fix: explicitly clear the key sequence if it was saved as empty.
hk.keyseq = QKeySequence(); hk.keyseq = QKeySequence();
@@ -101,9 +101,9 @@ QShortcut* HotkeyRegistry::GetHotkey(const std::string& group, const std::string
return hk.shortcut; return hk.shortcut;
} }
ControllerShortcut* HotkeyRegistry::GetControllerHotkey(const std::string& group, ControllerShortcut* HotkeyRegistry::GetControllerHotkey(
const std::string& action, const std::string& group, const std::string& action,
Core::HID::EmulatedController* controller) const { Core::HID::EmulatedController* controller) const {
Hotkey& hk = hotkey_groups[group][action]; Hotkey& hk = hotkey_groups[group][action];
if (!hk.controller_shortcut) { if (!hk.controller_shortcut) {
@@ -114,7 +114,8 @@ ControllerShortcut* HotkeyRegistry::GetControllerHotkey(const std::string& group
return hk.controller_shortcut; return hk.controller_shortcut;
} }
QKeySequence HotkeyRegistry::GetKeySequence(const std::string& group, const std::string& action) const { QKeySequence HotkeyRegistry::GetKeySequence(const std::string& group,
const std::string& action) const {
return hotkey_groups[group][action].keyseq; return hotkey_groups[group][action].keyseq;
} }