Apply shortcuts changes to actions.

This commit is contained in:
John Preston 2025-02-07 12:25:29 +04:00
parent a93e01b896
commit 86096db02d
3 changed files with 143 additions and 53 deletions

View file

@ -179,19 +179,30 @@ public:
[[nodiscard]] base::flat_map<QKeySequence, Command> keysDefaults() const;
[[nodiscard]] base::flat_map<QKeySequence, Command> keysCurrents() const;
void change(
QKeySequence was,
QKeySequence now,
Command command,
std::optional<Command> restore);
private:
void fillDefaults();
void writeDefaultFile();
bool readCustomFile();
void set(const QString &keys, Command command, bool replace = false);
void set(const QKeySequence &result, Command command, bool replace);
void remove(const QString &keys);
void remove(const QKeySequence &keys);
void unregister(base::unique_qptr<QAction> shortcut);
void pruneListened();
QStringList _errors;
base::flat_map<QKeySequence, base::unique_qptr<QAction>> _shortcuts;
base::flat_multi_map<not_null<QObject*>, Command> _commandByObject;
std::vector<QPointer<QWidget>> _listened;
base::flat_map<QKeySequence, Command> _defaults;
@ -299,6 +310,23 @@ base::flat_map<QKeySequence, Command> Manager::keysCurrents() const {
return result;
}
void Manager::change(
QKeySequence was,
QKeySequence now,
Command command,
std::optional<Command> restore) {
if (!was.isEmpty()) {
remove(was);
}
if (!now.isEmpty()) {
set(now, command, true);
}
if (restore) {
Assert(!was.isEmpty());
set(was, *restore, true);
}
}
std::vector<Command> Manager::lookup(not_null<QObject*> object) const {
auto result = std::vector<Command>();
auto i = _commandByObject.findFirst(object);
@ -322,11 +350,23 @@ void Manager::toggleSupport(bool toggled) {
}
void Manager::listen(not_null<QWidget*> widget) {
pruneListened();
_listened.push_back(widget.get());
for (const auto &[keys, shortcut] : _shortcuts) {
widget->addAction(shortcut.get());
}
}
void Manager::pruneListened() {
for (auto i = begin(_listened); i != end(_listened);) {
if (i->data()) {
++i;
} else {
i = _listened.erase(i);
}
}
}
bool Manager::readCustomFile() {
// read custom shortcuts from file if it exists or write an empty custom shortcuts file
QFile file(CustomFilePath());
@ -527,8 +567,15 @@ void Manager::set(const QString &keys, Command command, bool replace) {
_errors.push_back(u"Could not derive key sequence '%1'!"_q.arg(keys));
return;
}
set(result, command, replace);
}
void Manager::set(
const QKeySequence &keys,
Command command,
bool replace) {
auto shortcut = base::make_unique_q<QAction>();
shortcut->setShortcut(result);
shortcut->setShortcut(keys);
shortcut->setShortcutContext(Qt::ApplicationShortcut);
if (!AutoRepeatCommands.contains(command)) {
shortcut->setAutoRepeat(false);
@ -539,20 +586,26 @@ void Manager::set(const QString &keys, Command command, bool replace) {
shortcut->setEnabled(false);
}
auto object = shortcut.get();
auto i = _shortcuts.find(result);
auto i = _shortcuts.find(keys);
if (i == end(_shortcuts)) {
i = _shortcuts.emplace(result, std::move(shortcut)).first;
i = _shortcuts.emplace(keys, std::move(shortcut)).first;
} else if (replace) {
unregister(std::exchange(i->second, std::move(shortcut)));
} else {
object = i->second.get();
}
_commandByObject.emplace(object, command);
if (!shortcut && isMediaShortcut) {
_mediaShortcuts.emplace(i->second.get());
}
if (!shortcut && isSupportShortcut) {
_supportShortcuts.emplace(i->second.get());
if (!shortcut) { // Added the new one.
if (isMediaShortcut) {
_mediaShortcuts.emplace(i->second.get());
}
if (isSupportShortcut) {
_supportShortcuts.emplace(i->second.get());
}
pruneListened();
for (const auto &widget : _listened) {
widget->addAction(i->second.get());
}
}
}
@ -566,7 +619,11 @@ void Manager::remove(const QString &keys) {
_errors.push_back(u"Could not derive key sequence '%1'!"_q.arg(keys));
return;
}
const auto i = _shortcuts.find(result);
remove(result);
}
void Manager::remove(const QKeySequence &keys) {
const auto i = _shortcuts.find(keys);
if (i != end(_shortcuts)) {
unregister(std::move(i->second));
_shortcuts.erase(i);
@ -671,6 +728,14 @@ base::flat_map<QKeySequence, Command> KeysCurrents() {
return Data.keysCurrents();
}
void Change(
QKeySequence was,
QKeySequence now,
Command command,
std::optional<Command> restore) {
Data.change(was, now, command, restore);
}
bool AllowWithoutModifiers(int key) {
const auto service = {
Qt::Key_Escape,

View file

@ -144,6 +144,13 @@ void Unpause();
[[nodiscard]] base::flat_map<QKeySequence, Command> KeysDefaults();
[[nodiscard]] base::flat_map<QKeySequence, Command> KeysCurrents();
void Change(
QKeySequence was,
QKeySequence now,
Command command,
std::optional<Command> restore = {});
[[nodiscard]] bool AllowWithoutModifiers(int key);
} // namespace Shortcuts

View file

@ -227,10 +227,10 @@ struct Labeled {
TextWithEntities{ key.toString() },
EntityType::StrikeOut)
: TextWithEntities{ key.toString() });
keys->setTextColorOverride(removed
? st::attentionButtonFg->c
: (recording == raw)
keys->setTextColorOverride((recording == raw)
? st::boxTextFgGood->c
: removed
? st::attentionButtonFg->c
: std::optional<QColor>());
keys->resizeToNaturalWidth(available);
keys->moveToRight(
@ -259,56 +259,71 @@ struct Labeled {
return;
}
state->recording = nullptr;
if (result) {
auto was = button->key.current();
const auto now = *result;
for (auto &entry : state->entries) {
const auto i = ranges::find(
entry.buttons,
button,
&std::unique_ptr<Button>::get);
if (i != end(entry.buttons)) {
const auto index = i - begin(entry.buttons);
if (now.isEmpty()) {
entry.now.erase(begin(entry.now) + index);
} else {
const auto i = ranges::find(entry.now, now);
if (i == end(entry.now)) {
entry.now[index] = now;
} else if (i != begin(entry.now) + index) {
std::swap(entry.now[index], *i);
entry.now.erase(i);
}
}
fill(entry);
checkModified();
} else if (now != was) {
const auto i = now.isEmpty()
? end(entry.now)
: ranges::find(entry.now, now);
if (i != end(entry.now)) {
entry.buttons[i - begin(entry.now)]->removed = true;
}
const auto j = was.isEmpty()
? end(entry.now)
: ranges::find(entry.now, was);
if (j != end(entry.now)) {
entry.buttons[j - begin(entry.now)]->removed = false;
was = QKeySequence();
}
}
}
}
InvokeQueued(content, [=] {
InvokeQueued(content, [=] {
// Let all the shortcut events propagate first.
S::Unpause();
});
});
auto was = button->key.current();
const auto now = result.value_or(was);
if (now == was) {
if (!result || !button->removed.current()) {
return;
}
was = QKeySequence();
}
auto changed = false;
const auto command = button->command;
for (auto &entry : state->entries) {
const auto i = ranges::find(
entry.buttons,
button,
&std::unique_ptr<Button>::get);
if (i != end(entry.buttons)) {
const auto index = i - begin(entry.buttons);
if (now.isEmpty()) {
entry.now.erase(begin(entry.now) + index);
} else {
const auto i = ranges::find(entry.now, now);
if (i == end(entry.now)) {
entry.now[index] = now;
} else if (i != begin(entry.now) + index) {
std::swap(entry.now[index], *i);
entry.now.erase(i);
}
}
fill(entry);
checkModified();
} else if (now != was) {
const auto i = now.isEmpty()
? end(entry.now)
: ranges::find(entry.now, now);
if (i != end(entry.now)) {
entry.buttons[i - begin(entry.now)]->removed = true;
}
const auto j = was.isEmpty()
? end(entry.now)
: ranges::find(entry.now, was);
if (j != end(entry.now)) {
entry.buttons[j - begin(entry.now)]->removed = false;
S::Change(was, now, command, entry.command);
was = QKeySequence();
changed = true;
}
}
}
if (!changed) {
S::Change(was, now, command);
}
};
base::install_event_filter(content, qApp, [=](not_null<QEvent*> e) {
const auto type = e->type();
if (type == QEvent::ShortcutOverride && state->recording.current()) {
if (!content->window()->isActiveWindow()) {
return base::EventFilterResult::Continue;
}
const auto key = static_cast<QKeyEvent*>(e.get());
const auto m = key->modifiers();
const auto k = key->key();
@ -325,7 +340,10 @@ struct Labeled {
}
stopRecording(clear ? QKeySequence() : QKeySequence(k | m));
return base::EventFilterResult::Cancel;
} else if (type == QEvent::KeyPress) {
} else if (type == QEvent::KeyPress && state->recording.current()) {
if (!content->window()->isActiveWindow()) {
return base::EventFilterResult::Continue;
}
if (static_cast<QKeyEvent*>(e.get())->key() == Qt::Key_Escape) {
stopRecording();
return base::EventFilterResult::Cancel;