0549b42ec0
Co-authored-by: Chad Gorman <nevicar@Chads-Mini.localdomain> Reviewed-by: Mm2PL <mm2pl+gh@kotmisia.pl>
679 lines
26 KiB
C++
679 lines
26 KiB
C++
// SPDX-FileCopyrightText: 2021 Contributors to Chatterino <https://chatterino.com>
|
|
//
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
#include "controllers/hotkeys/HotkeyController.hpp"
|
|
|
|
#include "common/QLogging.hpp"
|
|
#include "controllers/hotkeys/Hotkey.hpp"
|
|
#include "controllers/hotkeys/HotkeyCategory.hpp"
|
|
#include "controllers/hotkeys/HotkeyModel.hpp"
|
|
#include "util/RapidJsonSerializeQString.hpp" // IWYU pragma: keep
|
|
|
|
#include <pajlada/settings.hpp>
|
|
#include <QKeyCombination>
|
|
#include <QMessageBox>
|
|
#include <QShortcut>
|
|
#include <QWidget>
|
|
|
|
namespace {
|
|
|
|
using namespace chatterino;
|
|
|
|
const std::map<HotkeyCategory, HotkeyCategoryData> HOTKEY_CATEGORIES = {
|
|
{HotkeyCategory::PopupWindow, {"popupWindow", "Popup Windows"}},
|
|
{HotkeyCategory::Split, {"split", "Split"}},
|
|
{HotkeyCategory::SplitInput, {"splitInput", "Split input box"}},
|
|
{HotkeyCategory::Window, {"window", "Window"}},
|
|
};
|
|
|
|
} // namespace
|
|
|
|
namespace chatterino {
|
|
|
|
const std::map<HotkeyCategory, HotkeyCategoryData> &hotkeyCategories()
|
|
{
|
|
return HOTKEY_CATEGORIES;
|
|
}
|
|
|
|
QString hotkeyCategoryName(HotkeyCategory category)
|
|
{
|
|
if (!HOTKEY_CATEGORIES.contains(category))
|
|
{
|
|
qCWarning(chatterinoHotkeys) << "Invalid HotkeyCategory passed to "
|
|
"categoryDisplayName function";
|
|
return {};
|
|
}
|
|
|
|
const auto &categoryData = HOTKEY_CATEGORIES.at(category);
|
|
|
|
return categoryData.name;
|
|
}
|
|
|
|
QString hotkeyCategoryDisplayName(HotkeyCategory category)
|
|
{
|
|
if (!HOTKEY_CATEGORIES.contains(category))
|
|
{
|
|
qCWarning(chatterinoHotkeys) << "Invalid HotkeyCategory passed to "
|
|
"categoryDisplayName function";
|
|
return {};
|
|
}
|
|
|
|
const auto &categoryData = HOTKEY_CATEGORIES.at(category);
|
|
|
|
return categoryData.displayName;
|
|
}
|
|
|
|
static bool hotkeySortCompare_(const std::shared_ptr<Hotkey> &a,
|
|
const std::shared_ptr<Hotkey> &b)
|
|
{
|
|
if (a->category() == b->category())
|
|
{
|
|
return a->name() < b->name();
|
|
}
|
|
|
|
return a->category() < b->category();
|
|
}
|
|
|
|
HotkeyController::HotkeyController()
|
|
: hotkeys_(hotkeySortCompare_)
|
|
{
|
|
this->loadHotkeys();
|
|
|
|
this->clearRemovedDefaults();
|
|
|
|
this->signalHolder_.managedConnect(
|
|
this->hotkeys_.delayedItemsChanged, [this]() {
|
|
qCDebug(chatterinoHotkeys) << "Reloading hotkeys!";
|
|
this->onItemsUpdated.invoke();
|
|
});
|
|
}
|
|
|
|
HotkeyModel *HotkeyController::createModel(QObject *parent)
|
|
{
|
|
HotkeyModel *model = new HotkeyModel(parent);
|
|
model->initialize(&this->hotkeys_);
|
|
return model;
|
|
}
|
|
|
|
std::vector<QShortcut *> HotkeyController::shortcutsForCategory(
|
|
HotkeyCategory category,
|
|
std::map<QString, std::function<QString(std::vector<QString>)>> actionMap,
|
|
QWidget *parent)
|
|
{
|
|
std::vector<QShortcut *> output;
|
|
for (const auto &hotkey : this->hotkeys_)
|
|
{
|
|
if (hotkey->category() != category)
|
|
{
|
|
continue;
|
|
}
|
|
auto target = actionMap.find(hotkey->action());
|
|
if (target == actionMap.end())
|
|
{
|
|
qCDebug(chatterinoHotkeys)
|
|
<< qPrintable(parent->objectName())
|
|
<< "Unimplemented hotkey action:" << hotkey->action() << "in"
|
|
<< hotkey->getCategory();
|
|
continue;
|
|
}
|
|
if (!target->second)
|
|
{
|
|
// Widget has chosen to explicitly not handle this action
|
|
continue;
|
|
}
|
|
auto createShortcutFromKeySeq = [&](QKeySequence qs) {
|
|
auto *s = new QShortcut(qs, parent);
|
|
s->setContext(hotkey->getContext());
|
|
auto functionPointer = target->second;
|
|
QObject::connect(s, &QShortcut::activated, parent,
|
|
[functionPointer, hotkey, this]() {
|
|
QString output =
|
|
functionPointer(hotkey->arguments());
|
|
if (!output.isEmpty())
|
|
{
|
|
this->showHotkeyError(hotkey, output);
|
|
}
|
|
});
|
|
output.push_back(s);
|
|
};
|
|
auto qs = QKeySequence(hotkey->keySequence());
|
|
|
|
// Create shortcut for the original key sequence
|
|
createShortcutFromKeySeq(qs);
|
|
|
|
// Qt treats Key_Return (main keyboard) and Key_Enter (numpad) as different keys, but they are expect to behave identically.
|
|
// Create a duplicate shortcut with the alternate key.
|
|
for (int i = 0; i < qs.count(); i++)
|
|
{
|
|
auto combo = qs[i];
|
|
if (combo.key() == Qt::Key_Return)
|
|
{
|
|
QKeyCombination enterCombo(combo.keyboardModifiers(),
|
|
Qt::Key_Enter);
|
|
createShortcutFromKeySeq(QKeySequence(enterCombo));
|
|
}
|
|
else if (combo.key() == Qt::Key_Enter)
|
|
{
|
|
QKeyCombination returnCombo(combo.keyboardModifiers(),
|
|
Qt::Key_Return);
|
|
createShortcutFromKeySeq(QKeySequence(returnCombo));
|
|
}
|
|
}
|
|
}
|
|
return output;
|
|
}
|
|
|
|
void HotkeyController::save()
|
|
{
|
|
this->saveHotkeys();
|
|
}
|
|
|
|
std::shared_ptr<Hotkey> HotkeyController::getHotkeyByName(QString name)
|
|
{
|
|
for (const auto &hotkey : this->hotkeys_)
|
|
{
|
|
if (hotkey->name() == name)
|
|
{
|
|
return hotkey;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
int HotkeyController::replaceHotkey(QString oldName,
|
|
std::shared_ptr<Hotkey> newHotkey)
|
|
{
|
|
int i = 0;
|
|
for (const auto &hotkey : this->hotkeys_)
|
|
{
|
|
if (hotkey->name() == oldName)
|
|
{
|
|
this->hotkeys_.removeAt(i);
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
return this->hotkeys_.append(newHotkey);
|
|
}
|
|
|
|
std::optional<HotkeyCategory> HotkeyController::hotkeyCategoryFromName(
|
|
QString categoryName)
|
|
{
|
|
for (const auto &[category, data] : HOTKEY_CATEGORIES)
|
|
{
|
|
if (data.name == categoryName)
|
|
{
|
|
return category;
|
|
}
|
|
}
|
|
qCDebug(chatterinoHotkeys) << "Unknown category: " << categoryName;
|
|
return {};
|
|
}
|
|
|
|
bool HotkeyController::isDuplicate(std::shared_ptr<Hotkey> hotkey,
|
|
QString ignoreNamed)
|
|
{
|
|
for (const auto &shared : this->hotkeys_)
|
|
{
|
|
if (shared->name() == ignoreNamed || shared->name() == hotkey->name())
|
|
{
|
|
// Given hotkey is the same as shared, just before it was being edited.
|
|
continue;
|
|
}
|
|
|
|
if (shared->category() == hotkey->category() &&
|
|
shared->keySequence() == hotkey->keySequence())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const std::set<QString> &HotkeyController::removedOrDeprecatedHotkeys() const
|
|
{
|
|
return this->removedOrDeprecatedHotkeys_;
|
|
}
|
|
|
|
void HotkeyController::loadHotkeys()
|
|
{
|
|
auto defaultHotkeysAdded =
|
|
pajlada::Settings::Setting<std::vector<QString>>::get(
|
|
"/hotkeys/addedDefaults");
|
|
auto set = std::set<QString>(defaultHotkeysAdded.begin(),
|
|
defaultHotkeysAdded.end());
|
|
|
|
// set is currently "defaults added in settings"
|
|
auto numDefaultsFromSettings = set.size();
|
|
|
|
auto keys = pajlada::Settings::SettingManager::getObjectKeys("/hotkeys");
|
|
this->addDefaults(set);
|
|
|
|
// set is currently "all defaults (defaults defined from application + defaults defined in settings)"
|
|
auto numCombinedDefaults = set.size();
|
|
|
|
pajlada::Settings::Setting<std::vector<QString>>::set(
|
|
"/hotkeys/addedDefaults", std::vector<QString>(set.begin(), set.end()),
|
|
pajlada::Settings::SettingOption::CompareBeforeSet);
|
|
|
|
qCDebug(chatterinoHotkeys) << "Loading hotkeys...";
|
|
for (const auto &key : keys)
|
|
{
|
|
if (key == "addedDefaults")
|
|
{
|
|
continue;
|
|
}
|
|
|
|
auto section = "/hotkeys/" + key;
|
|
auto categoryName =
|
|
pajlada::Settings::Setting<QString>::get(section + "/category");
|
|
auto keySequence =
|
|
pajlada::Settings::Setting<QString>::get(section + "/keySequence");
|
|
auto action =
|
|
pajlada::Settings::Setting<QString>::get(section + "/action");
|
|
auto arguments = pajlada::Settings::Setting<std::vector<QString>>::get(
|
|
section + "/arguments");
|
|
qCDebug(chatterinoHotkeys)
|
|
<< "Hotkey " << categoryName << keySequence << action << arguments;
|
|
|
|
if (categoryName.isEmpty() || keySequence.isEmpty() || action.isEmpty())
|
|
{
|
|
continue;
|
|
}
|
|
auto category = this->hotkeyCategoryFromName(categoryName);
|
|
if (!category)
|
|
{
|
|
continue;
|
|
}
|
|
this->hotkeys_.append(std::make_shared<Hotkey>(
|
|
*category, QKeySequence(keySequence), action, arguments,
|
|
QString::fromStdString(key)));
|
|
}
|
|
|
|
if (numDefaultsFromSettings != numCombinedDefaults)
|
|
{
|
|
// some default that the user was not aware of has been added to the application, force a save to ensure shared state between hotkey controller and settings
|
|
this->saveHotkeys();
|
|
}
|
|
}
|
|
|
|
void HotkeyController::saveHotkeys()
|
|
{
|
|
auto defaultHotkeysAdded =
|
|
pajlada::Settings::Setting<std::vector<QString>>::get(
|
|
"/hotkeys/addedDefaults");
|
|
|
|
// make sure that hotkeys are deleted
|
|
pajlada::Settings::SettingManager::getInstance()->set(
|
|
"/hotkeys", rapidjson::Value(rapidjson::kObjectType));
|
|
|
|
// re-add /hotkeys/addedDefaults as previous set call deleted that key
|
|
pajlada::Settings::Setting<std::vector<QString>>::set(
|
|
"/hotkeys/addedDefaults",
|
|
std::vector<QString>(defaultHotkeysAdded.begin(),
|
|
defaultHotkeysAdded.end()));
|
|
|
|
for (const auto &hotkey : this->hotkeys_)
|
|
{
|
|
auto section = "/hotkeys/" + hotkey->name().toStdString();
|
|
pajlada::Settings::Setting<QString>::set(section + "/action",
|
|
hotkey->action());
|
|
pajlada::Settings::Setting<QString>::set(
|
|
section + "/keySequence", hotkey->keySequence().toString());
|
|
|
|
auto categoryName = hotkeyCategoryName(hotkey->category());
|
|
pajlada::Settings::Setting<QString>::set(section + "/category",
|
|
categoryName);
|
|
pajlada::Settings::Setting<std::vector<QString>>::set(
|
|
section + "/arguments", hotkey->arguments());
|
|
}
|
|
}
|
|
|
|
void HotkeyController::addDefaults(std::set<QString> &addedHotkeys)
|
|
{
|
|
// popup window
|
|
{
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::PopupWindow,
|
|
QKeySequence("Escape"), "delete",
|
|
std::vector<QString>(), "close popup window");
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::PopupWindow,
|
|
QKeySequence(QString("Ctrl+%1").arg(i + 1)),
|
|
"openTab", {QString::number(i)},
|
|
QString("popup select tab #%1").arg(i + 1));
|
|
}
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::PopupWindow,
|
|
QKeySequence("Ctrl+9"), "openTab", {"last"},
|
|
"popup select last tab");
|
|
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::PopupWindow,
|
|
QKeySequence("Ctrl+Tab"), "openTab", {"next"},
|
|
"popup select next tab");
|
|
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::PopupWindow,
|
|
QKeySequence("Ctrl+Shift+Tab"), "openTab",
|
|
{"previous"}, "popup select previous tab");
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::PopupWindow,
|
|
QKeySequence("PgUp"), "scrollPage", {"up"},
|
|
"popup scroll up");
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::PopupWindow,
|
|
QKeySequence("PgDown"), "scrollPage", {"down"},
|
|
"popup scroll down");
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::PopupWindow,
|
|
QKeySequence("Return"), "accept",
|
|
std::vector<QString>(), "popup accept");
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::PopupWindow,
|
|
QKeySequence("Escape"), "reject",
|
|
std::vector<QString>(), "popup reject");
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::PopupWindow,
|
|
QKeySequence("Ctrl+F"), "search",
|
|
std::vector<QString>(), "popup focus search box");
|
|
}
|
|
|
|
// split
|
|
{
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::Split,
|
|
QKeySequence("Ctrl+W"), "delete",
|
|
std::vector<QString>(), "delete");
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::Split,
|
|
QKeySequence("Ctrl+R"), "changeChannel",
|
|
std::vector<QString>(), "change channel");
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::Split,
|
|
QKeySequence("Ctrl+F"), "showSearch",
|
|
std::vector<QString>(), "show search");
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::Split,
|
|
QKeySequence("Ctrl+Shift+F"), "showGlobalSearch",
|
|
std::vector<QString>(), "show global search");
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::Split,
|
|
QKeySequence("Ctrl+F5"), "reconnect",
|
|
std::vector<QString>(), "reconnect");
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::Split,
|
|
QKeySequence("F5"), "reloadEmotes",
|
|
std::vector<QString>(), "reload emotes");
|
|
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::Split,
|
|
QKeySequence("Alt+x"), "createClip",
|
|
std::vector<QString>(), "create clip");
|
|
|
|
#ifndef Q_OS_MACOS
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::Split,
|
|
QKeySequence("Alt+left"), "focus", {"left"},
|
|
"focus left");
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::Split,
|
|
QKeySequence("Alt+down"), "focus", {"down"},
|
|
"focus down");
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::Split,
|
|
QKeySequence("Alt+up"), "focus", {"up"},
|
|
"focus up");
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::Split,
|
|
QKeySequence("Alt+right"), "focus", {"right"},
|
|
"focus right");
|
|
#endif
|
|
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::Split,
|
|
QKeySequence("PgUp"), "scrollPage", {"up"},
|
|
"scroll page up");
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::Split,
|
|
QKeySequence("PgDown"), "scrollPage", {"down"},
|
|
"scroll page down");
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::Split,
|
|
QKeySequence("Ctrl+End"), "scrollToBottom",
|
|
std::vector<QString>(), "scroll to bottom");
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::Split,
|
|
QKeySequence("Ctrl+Home"), "scrollToTop",
|
|
std::vector<QString>(), "scroll to top");
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::Split,
|
|
QKeySequence("F10"), "debug",
|
|
std::vector<QString>(), "open debug popup");
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::Split,
|
|
QKeySequence("Ctrl+Alt+N"), "popupOverlay", {},
|
|
"open overlay");
|
|
this->tryAddDefault(
|
|
addedHotkeys, HotkeyCategory::Split, QKeySequence("Ctrl+Shift+U"),
|
|
"toggleOverlayInertia", {"all"}, "toggle overlay click-through");
|
|
}
|
|
|
|
// split input
|
|
{
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::SplitInput,
|
|
QKeySequence("Ctrl+E"), "openEmotesPopup",
|
|
std::vector<QString>(), "emote picker");
|
|
|
|
// all variations of send message :)
|
|
{
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::SplitInput,
|
|
QKeySequence("Return"), "sendMessage",
|
|
std::vector<QString>(), "send message");
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::SplitInput,
|
|
QKeySequence("Ctrl+Return"), "sendMessage",
|
|
{"keepInput"}, "send message and keep text");
|
|
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::SplitInput,
|
|
QKeySequence("Shift+Return"), "sendMessage",
|
|
std::vector<QString>(), "send message");
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::SplitInput,
|
|
QKeySequence("Ctrl+Shift+Return"),
|
|
"sendMessage", {"keepInput"},
|
|
"send message and keep text");
|
|
}
|
|
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::SplitInput,
|
|
QKeySequence("Home"), "cursorToStart",
|
|
{"withoutSelection"}, "go to start of input");
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::SplitInput,
|
|
QKeySequence("End"), "cursorToEnd",
|
|
{"withoutSelection"}, "go to end of input");
|
|
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::SplitInput,
|
|
QKeySequence("Shift+Home"), "cursorToStart",
|
|
{"withSelection"},
|
|
"go to start of input with selection");
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::SplitInput,
|
|
QKeySequence("Shift+End"), "cursorToEnd",
|
|
{"withSelection"},
|
|
"go to end of input with selection");
|
|
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::SplitInput,
|
|
QKeySequence("Up"), "previousMessage",
|
|
std::vector<QString>(), "previous message");
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::SplitInput,
|
|
QKeySequence("Down"), "nextMessage",
|
|
std::vector<QString>(), "next message");
|
|
}
|
|
|
|
// window
|
|
{
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::Window,
|
|
QKeySequence("Ctrl+P"), "openSettings",
|
|
std::vector<QString>(), "open settings");
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::Window,
|
|
QKeySequence("Ctrl+T"), "newSplit",
|
|
std::vector<QString>(), "new split");
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::Window,
|
|
QKeySequence(QString("Ctrl+%1").arg(i + 1)),
|
|
"openTab", {QString::number(i)},
|
|
QString("select tab #%1").arg(i + 1));
|
|
}
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::Window,
|
|
QKeySequence("Ctrl+9"), "openTab", {"last"},
|
|
"select last tab");
|
|
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::Window,
|
|
QKeySequence("Ctrl+Tab"), "openTab", {"next"},
|
|
"select next tab");
|
|
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::Window,
|
|
QKeySequence("Ctrl+Shift+Tab"), "openTab",
|
|
{"previous"}, "select previous tab");
|
|
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::Window,
|
|
QKeySequence("Ctrl+N"), "popup", {"split"},
|
|
"new popup window");
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::Window,
|
|
QKeySequence("Ctrl+Shift+N"), "popup", {"window"},
|
|
"new popup window from tab");
|
|
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::Window,
|
|
QKeySequence::ZoomIn, "zoom", {"in"}, "zoom in");
|
|
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::Window,
|
|
QKeySequence::ZoomOut, "zoom", {"out"}, "zoom out");
|
|
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::Window,
|
|
QKeySequence("CTRL+0"), "zoom", {"reset"},
|
|
"zoom reset");
|
|
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::Window,
|
|
QKeySequence("Ctrl+Shift+T"), "newTab",
|
|
std::vector<QString>(), "new tab");
|
|
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::Window,
|
|
QKeySequence("Ctrl+Shift+W"), "removeTab",
|
|
std::vector<QString>(), "remove tab");
|
|
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::Window,
|
|
QKeySequence("Ctrl+G"), "reopenSplit",
|
|
std::vector<QString>(), "reopen split");
|
|
|
|
#ifndef Q_OS_MACOS
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::Window,
|
|
QKeySequence("Ctrl+H"), "toggleLocalR9K",
|
|
std::vector<QString>(), "toggle local r9k");
|
|
#endif
|
|
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::Window,
|
|
QKeySequence("Ctrl+K"), "openQuickSwitcher",
|
|
std::vector<QString>(), "open quick switcher");
|
|
|
|
this->tryAddDefault(addedHotkeys, HotkeyCategory::Window,
|
|
QKeySequence("Ctrl+U"), "setTabVisibility",
|
|
{"toggle"}, "toggle tab visibility");
|
|
}
|
|
}
|
|
|
|
void HotkeyController::resetToDefaults()
|
|
{
|
|
std::set<QString> addedSet;
|
|
pajlada::Settings::Setting<std::vector<QString>>::set(
|
|
"/hotkeys/addedDefaults",
|
|
std::vector<QString>(addedSet.begin(), addedSet.end()));
|
|
auto size = this->hotkeys_.raw().size();
|
|
for (unsigned long i = 0; i < size; i++)
|
|
{
|
|
this->hotkeys_.removeAt(0);
|
|
}
|
|
|
|
// add defaults back
|
|
this->saveHotkeys();
|
|
this->loadHotkeys();
|
|
}
|
|
|
|
void HotkeyController::clearRemovedDefaults()
|
|
{
|
|
// The "toggleLiveOnly" argument was removed 2024-08-04
|
|
this->tryRemoveDefault(HotkeyCategory::Window, QKeySequence("Ctrl+Shift+L"),
|
|
"setTabVisibility", {"toggleLiveOnly"},
|
|
"toggle live tabs only");
|
|
|
|
this->warnForRemovedHotkeyActions(HotkeyCategory::Window,
|
|
"setTabVisibility", {"toggleLiveOnly"});
|
|
}
|
|
|
|
void HotkeyController::tryAddDefault(std::set<QString> &addedHotkeys,
|
|
HotkeyCategory category,
|
|
QKeySequence keySequence, QString action,
|
|
std::vector<QString> args, QString name)
|
|
{
|
|
qCDebug(chatterinoHotkeys) << "Try add default" << name;
|
|
if (addedHotkeys.count(name) != 0)
|
|
{
|
|
qCDebug(chatterinoHotkeys) << "Already exists";
|
|
return; // hotkey was added before
|
|
}
|
|
qCDebug(chatterinoHotkeys) << "Inserted";
|
|
this->hotkeys_.append(
|
|
std::make_shared<Hotkey>(category, keySequence, action, args, name));
|
|
addedHotkeys.insert(name);
|
|
}
|
|
|
|
bool HotkeyController::tryRemoveDefault(HotkeyCategory category,
|
|
QKeySequence keySequence,
|
|
QString action,
|
|
std::vector<QString> args, QString name)
|
|
{
|
|
return this->hotkeys_.removeFirstMatching([&](const auto &hotkey) {
|
|
return hotkey->category() == category &&
|
|
hotkey->keySequence() == keySequence &&
|
|
hotkey->action() == action && hotkey->arguments() == args &&
|
|
hotkey->name() == name;
|
|
});
|
|
}
|
|
|
|
void HotkeyController::warnForRemovedHotkeyActions(HotkeyCategory category,
|
|
QString action,
|
|
std::vector<QString> args)
|
|
{
|
|
for (const auto &hotkey : this->hotkeys_)
|
|
{
|
|
if (hotkey->category() == category && hotkey->action() == action &&
|
|
hotkey->arguments() == args)
|
|
{
|
|
this->removedOrDeprecatedHotkeys_.insert(hotkey->name());
|
|
}
|
|
}
|
|
}
|
|
|
|
void HotkeyController::showHotkeyError(const std::shared_ptr<Hotkey> &hotkey,
|
|
QString warning)
|
|
{
|
|
auto *msgBox = new QMessageBox(
|
|
QMessageBox::Icon::Warning, "Hotkey error",
|
|
QString(
|
|
"There was an error while executing your hotkey named \"%1\": \n%2")
|
|
.arg(hotkey->name(), warning),
|
|
QMessageBox::Ok);
|
|
msgBox->exec();
|
|
}
|
|
|
|
QKeySequence HotkeyController::getDisplaySequence(
|
|
HotkeyCategory category, const QString &action,
|
|
const std::optional<std::vector<QString>> &arguments) const
|
|
{
|
|
const auto &found = this->findLike(category, action, arguments);
|
|
if (found != nullptr)
|
|
{
|
|
return found->keySequence();
|
|
}
|
|
return {};
|
|
}
|
|
|
|
std::shared_ptr<Hotkey> HotkeyController::findLike(
|
|
HotkeyCategory category, const QString &action,
|
|
const std::optional<std::vector<QString>> &arguments) const
|
|
{
|
|
for (auto other : this->hotkeys_)
|
|
{
|
|
if (other->category() == category && other->action() == action)
|
|
{
|
|
if (arguments)
|
|
{
|
|
if (other->arguments() == *arguments)
|
|
{
|
|
return other;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return other;
|
|
}
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
} // namespace chatterino
|