mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-14 05:07:10 +02:00
Save custom shortcuts to disk.
This commit is contained in:
parent
86096db02d
commit
5ebdf3ed39
10 changed files with 172 additions and 71 deletions
|
@ -1,6 +1,7 @@
|
|||
// This is a list of your own shortcuts for Telegram Desktop
|
||||
// You can see full list of commands in the 'shortcuts-default.json' file
|
||||
// Place a null value instead of a command string to switch the shortcut off
|
||||
// You can also edit them in Settings > Chat Settings > Keyboard Shortcuts.
|
||||
|
||||
[
|
||||
// {
|
||||
|
|
BIN
Telegram/Resources/icons/menu/shortcut.png
Normal file
BIN
Telegram/Resources/icons/menu/shortcut.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 447 B |
BIN
Telegram/Resources/icons/menu/shortcut@2x.png
Normal file
BIN
Telegram/Resources/icons/menu/shortcut@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 671 B |
BIN
Telegram/Resources/icons/menu/shortcut@3x.png
Normal file
BIN
Telegram/Resources/icons/menu/shortcut@3x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1,004 B |
|
@ -643,7 +643,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_settings_chat_corner_reaction" = "Reaction button on messages";
|
||||
|
||||
"lng_settings_shortcuts" = "Keyboard shortcuts";
|
||||
|
||||
"lng_shortcuts_reset" = "Reset to default";
|
||||
"lng_shortcuts_recording" = "Recording...";
|
||||
"lng_shortcuts_add_another" = "Add another";
|
||||
|
||||
"lng_shortcuts_close" = "Close the window";
|
||||
"lng_shortcuts_lock" = "Lock the application";
|
||||
"lng_shortcuts_minimize" = "Minimize the window";
|
||||
|
@ -677,7 +681,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_shortcuts_archive_chat" = "Archive chat";
|
||||
"lng_shortcuts_media_fullscreen" = "Toggle video fullscreen";
|
||||
"lng_shortcuts_show_chat_menu" = "Show chat menu";
|
||||
"lng_shortcuts_recording" = "Recording...";
|
||||
|
||||
"lng_settings_chat_reactions_title" = "Quick Reaction";
|
||||
"lng_settings_chat_reactions_subtitle" = "Choose your favorite reaction";
|
||||
|
|
|
@ -113,45 +113,15 @@ const auto CommandByName = base::flat_map<QString, Command>{
|
|||
//
|
||||
};
|
||||
|
||||
const auto CommandNames = base::flat_map<Command, QString>{
|
||||
{ Command::Close , u"close_telegram"_q },
|
||||
{ Command::Lock , u"lock_telegram"_q },
|
||||
{ Command::Minimize , u"minimize_telegram"_q },
|
||||
{ Command::Quit , u"quit_telegram"_q },
|
||||
|
||||
{ Command::MediaPlay , u"media_play"_q },
|
||||
{ Command::MediaPause , u"media_pause"_q },
|
||||
{ Command::MediaPlayPause , u"media_playpause"_q },
|
||||
{ Command::MediaStop , u"media_stop"_q },
|
||||
{ Command::MediaPrevious , u"media_previous"_q },
|
||||
{ Command::MediaNext , u"media_next"_q },
|
||||
|
||||
{ Command::Search , u"search"_q },
|
||||
|
||||
{ Command::ChatPrevious , u"previous_chat"_q },
|
||||
{ Command::ChatNext , u"next_chat"_q },
|
||||
{ Command::ChatFirst , u"first_chat"_q },
|
||||
{ Command::ChatLast , u"last_chat"_q },
|
||||
{ Command::ChatSelf , u"self_chat"_q },
|
||||
|
||||
{ Command::FolderPrevious , u"previous_folder"_q },
|
||||
{ Command::FolderNext , u"next_folder"_q },
|
||||
{ Command::ShowAllChats , u"all_chats"_q },
|
||||
|
||||
{ Command::ShowFolder1 , u"folder1"_q },
|
||||
{ Command::ShowFolder2 , u"folder2"_q },
|
||||
{ Command::ShowFolder3 , u"folder3"_q },
|
||||
{ Command::ShowFolder4 , u"folder4"_q },
|
||||
{ Command::ShowFolder5 , u"folder5"_q },
|
||||
{ Command::ShowFolder6 , u"folder6"_q },
|
||||
{ Command::ShowFolderLast , u"last_folder"_q },
|
||||
|
||||
{ Command::ShowArchive , u"show_archive"_q },
|
||||
{ Command::ShowContacts , u"show_contacts"_q },
|
||||
|
||||
{ Command::ReadChat , u"read_chat"_q },
|
||||
|
||||
{ Command::ShowChatMenu , u"show_chat_menu"_q },
|
||||
const base::flat_map<Command, QString> &CommandNames() {
|
||||
static const auto result = [&] {
|
||||
auto result = base::flat_map<Command, QString>();
|
||||
for (const auto &[name, command] : CommandByName) {
|
||||
result.emplace(command, name);
|
||||
}
|
||||
return result;
|
||||
}();
|
||||
return result;
|
||||
};
|
||||
|
||||
[[maybe_unused]] constexpr auto kNoValue = {
|
||||
|
@ -176,18 +146,22 @@ public:
|
|||
|
||||
[[nodiscard]] const QStringList &errors() const;
|
||||
|
||||
[[nodiscard]] base::flat_map<QKeySequence, Command> keysDefaults() const;
|
||||
[[nodiscard]] base::flat_map<QKeySequence, Command> keysCurrents() const;
|
||||
[[nodiscard]] auto keysDefaults() const
|
||||
-> base::flat_map<QKeySequence, base::flat_set<Command>>;
|
||||
[[nodiscard]] auto keysCurrents() const
|
||||
-> base::flat_map<QKeySequence, base::flat_set<Command>>;
|
||||
|
||||
void change(
|
||||
QKeySequence was,
|
||||
QKeySequence now,
|
||||
Command command,
|
||||
std::optional<Command> restore);
|
||||
void resetToDefaults();
|
||||
|
||||
private:
|
||||
void fillDefaults();
|
||||
void writeDefaultFile();
|
||||
void writeCustomFile();
|
||||
bool readCustomFile();
|
||||
|
||||
void set(const QString &keys, Command command, bool replace = false);
|
||||
|
@ -204,7 +178,7 @@ private:
|
|||
base::flat_multi_map<not_null<QObject*>, Command> _commandByObject;
|
||||
std::vector<QPointer<QWidget>> _listened;
|
||||
|
||||
base::flat_map<QKeySequence, Command> _defaults;
|
||||
base::flat_map<QKeySequence, base::flat_set<Command>> _defaults;
|
||||
|
||||
base::flat_set<QAction*> _mediaShortcuts;
|
||||
base::flat_set<QAction*> _supportShortcuts;
|
||||
|
@ -295,16 +269,19 @@ const QStringList &Manager::errors() const {
|
|||
return _errors;
|
||||
}
|
||||
|
||||
base::flat_map<QKeySequence, Command> Manager::keysDefaults() const {
|
||||
auto Manager::keysDefaults() const
|
||||
-> base::flat_map<QKeySequence, base::flat_set<Command>> {
|
||||
return _defaults;
|
||||
}
|
||||
|
||||
base::flat_map<QKeySequence, Command> Manager::keysCurrents() const {
|
||||
auto result = base::flat_map<QKeySequence, Command>();
|
||||
auto Manager::keysCurrents() const
|
||||
-> base::flat_map<QKeySequence, base::flat_set<Command>> {
|
||||
auto result = base::flat_map<QKeySequence, base::flat_set<Command>>();
|
||||
for (const auto &[keys, command] : _shortcuts) {
|
||||
const auto i = _commandByObject.findFirst(command);
|
||||
if (i != _commandByObject.end()) {
|
||||
result.emplace(keys, i->second);
|
||||
auto i = _commandByObject.findFirst(command);
|
||||
const auto end = _commandByObject.end();
|
||||
for (; i != end && (i->first == command); ++i) {
|
||||
result[keys].emplace(i->second);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
@ -325,6 +302,19 @@ void Manager::change(
|
|||
Assert(!was.isEmpty());
|
||||
set(was, *restore, true);
|
||||
}
|
||||
writeCustomFile();
|
||||
}
|
||||
|
||||
void Manager::resetToDefaults() {
|
||||
while (!_shortcuts.empty()) {
|
||||
remove(_shortcuts.begin()->first);
|
||||
}
|
||||
for (const auto &[sequence, commands] : _defaults) {
|
||||
for (const auto command : commands) {
|
||||
set(sequence, command, false);
|
||||
}
|
||||
}
|
||||
writeCustomFile();
|
||||
}
|
||||
|
||||
std::vector<Command> Manager::lookup(not_null<QObject*> object) const {
|
||||
|
@ -529,8 +519,8 @@ void Manager::writeDefaultFile() {
|
|||
auto i = _commandByObject.findFirst(object);
|
||||
const auto end = _commandByObject.end();
|
||||
for (; i != end && i->first == object; ++i) {
|
||||
const auto j = CommandNames.find(i->second);
|
||||
if (j != CommandNames.end()) {
|
||||
const auto j = CommandNames().find(i->second);
|
||||
if (j != CommandNames().end()) {
|
||||
QJsonObject entry;
|
||||
entry.insert(u"keys"_q, sequence.toString().toLower());
|
||||
entry.insert(u"command"_q, j->second);
|
||||
|
@ -551,6 +541,55 @@ void Manager::writeDefaultFile() {
|
|||
}
|
||||
}
|
||||
|
||||
auto document = QJsonDocument();
|
||||
document.setArray(shortcuts);
|
||||
file.write(document.toJson(QJsonDocument::Indented));
|
||||
}
|
||||
|
||||
void Manager::writeCustomFile() {
|
||||
auto shortcuts = QJsonArray();
|
||||
for (const auto &[sequence, shortcut] : _shortcuts) {
|
||||
const auto object = shortcut.get();
|
||||
auto i = _commandByObject.findFirst(object);
|
||||
const auto end = _commandByObject.end();
|
||||
for (; i != end && i->first == object; ++i) {
|
||||
const auto d = _defaults.find(sequence);
|
||||
if (d == _defaults.end() || !d->second.contains(i->second)) {
|
||||
const auto j = CommandNames().find(i->second);
|
||||
if (j != CommandNames().end()) {
|
||||
QJsonObject entry;
|
||||
entry.insert(u"keys"_q, sequence.toString().toLower());
|
||||
entry.insert(u"command"_q, j->second);
|
||||
shortcuts.append(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const auto &[sequence, command] : _defaults) {
|
||||
if (!_shortcuts.contains(sequence)) {
|
||||
QJsonObject entry;
|
||||
entry.insert(u"keys"_q, sequence.toString().toLower());
|
||||
entry.insert(u"command"_q, QJsonValue());
|
||||
shortcuts.append(entry);
|
||||
}
|
||||
}
|
||||
|
||||
if (shortcuts.isEmpty()) {
|
||||
WriteDefaultCustomFile();
|
||||
return;
|
||||
}
|
||||
|
||||
auto file = QFile(CustomFilePath());
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
LOG(("Shortcut Warning: could not write custom shortcuts file."));
|
||||
return;
|
||||
}
|
||||
const char *customHeader = R"HEADER(
|
||||
// This is a list of changed shortcuts for Telegram Desktop
|
||||
// You can edit them in Settings > Chat Settings > Keyboard Shortcuts.
|
||||
|
||||
)HEADER";
|
||||
file.write(customHeader);
|
||||
|
||||
auto document = QJsonDocument();
|
||||
document.setArray(shortcuts);
|
||||
|
@ -632,7 +671,7 @@ void Manager::remove(const QKeySequence &keys) {
|
|||
|
||||
void Manager::unregister(base::unique_qptr<QAction> shortcut) {
|
||||
if (shortcut) {
|
||||
_commandByObject.erase(shortcut.get());
|
||||
_commandByObject.removeAll(shortcut.get());
|
||||
_mediaShortcuts.erase(shortcut.get());
|
||||
_supportShortcuts.erase(shortcut.get());
|
||||
}
|
||||
|
@ -720,11 +759,13 @@ void Unpause() {
|
|||
Paused = false;
|
||||
}
|
||||
|
||||
base::flat_map<QKeySequence, Command> KeysDefaults() {
|
||||
auto KeysDefaults()
|
||||
-> base::flat_map<QKeySequence, base::flat_set<Command>> {
|
||||
return Data.keysDefaults();
|
||||
}
|
||||
|
||||
base::flat_map<QKeySequence, Command> KeysCurrents() {
|
||||
auto KeysCurrents()
|
||||
-> base::flat_map<QKeySequence, base::flat_set<Command>> {
|
||||
return Data.keysCurrents();
|
||||
}
|
||||
|
||||
|
@ -736,6 +777,10 @@ void Change(
|
|||
Data.change(was, now, command, restore);
|
||||
}
|
||||
|
||||
void ResetToDefaults() {
|
||||
Data.resetToDefaults();
|
||||
}
|
||||
|
||||
bool AllowWithoutModifiers(int key) {
|
||||
const auto service = {
|
||||
Qt::Key_Escape,
|
||||
|
|
|
@ -142,14 +142,17 @@ void ToggleSupportShortcuts(bool toggled);
|
|||
void Pause();
|
||||
void Unpause();
|
||||
|
||||
[[nodiscard]] base::flat_map<QKeySequence, Command> KeysDefaults();
|
||||
[[nodiscard]] base::flat_map<QKeySequence, Command> KeysCurrents();
|
||||
[[nodiscard]] auto KeysDefaults()
|
||||
-> base::flat_map<QKeySequence, base::flat_set<Command>>;
|
||||
[[nodiscard]] auto KeysCurrents()
|
||||
-> base::flat_map<QKeySequence, base::flat_set<Command>>;
|
||||
|
||||
void Change(
|
||||
QKeySequence was,
|
||||
QKeySequence now,
|
||||
Command command,
|
||||
std::optional<Command> restore = {});
|
||||
void ResetToDefaults();
|
||||
|
||||
[[nodiscard]] bool AllowWithoutModifiers(int key);
|
||||
|
||||
|
|
|
@ -1009,7 +1009,7 @@ void SetupMessages(
|
|||
inner,
|
||||
tr::lng_settings_shortcuts(),
|
||||
st::settingsButton,
|
||||
{ &st::menuIconBotCommands }
|
||||
{ &st::menuIconShortcut }
|
||||
)->addClickHandler([=] {
|
||||
showOther(Shortcuts::Id());
|
||||
});
|
||||
|
|
|
@ -14,9 +14,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "styles/style_menu_icons.h"
|
||||
#include "styles/style_settings.h"
|
||||
|
||||
namespace Settings {
|
||||
|
@ -135,6 +137,7 @@ struct Labeled {
|
|||
rpl::variable<bool> modified;
|
||||
rpl::variable<Button*> recording;
|
||||
rpl::variable<QKeySequence> lastKey;
|
||||
Fn<void(S::Command command)> showMenuFor;
|
||||
};
|
||||
const auto state = content->lifetime().make_state<State>();
|
||||
const auto labeled = Entries();
|
||||
|
@ -144,17 +147,21 @@ struct Labeled {
|
|||
return Entry{ labeled.command, std::move(labeled.label) };
|
||||
}) | ranges::to_vector;
|
||||
|
||||
for (const auto &[keys, command] : defaults) {
|
||||
const auto i = ranges::find(entries, command, &Entry::command);
|
||||
if (i != end(entries)) {
|
||||
i->original.push_back(keys);
|
||||
for (const auto &[keys, commands] : defaults) {
|
||||
for (const auto command : commands) {
|
||||
const auto i = ranges::find(entries, command, &Entry::command);
|
||||
if (i != end(entries)) {
|
||||
i->original.push_back(keys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &[keys, command] : currents) {
|
||||
const auto i = ranges::find(entries, command, &Entry::command);
|
||||
if (i != end(entries)) {
|
||||
i->now.push_back(keys);
|
||||
for (const auto &[keys, commands] : currents) {
|
||||
for (const auto command : commands) {
|
||||
const auto i = ranges::find(entries, command, &Entry::command);
|
||||
if (i != end(entries)) {
|
||||
i->now.push_back(keys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -173,6 +180,7 @@ struct Labeled {
|
|||
};
|
||||
checkModified();
|
||||
|
||||
const auto menu = std::make_shared<QPointer<Ui::PopupMenu>>();
|
||||
const auto fill = [=](Entry &entry) {
|
||||
auto index = 0;
|
||||
if (entry.original.empty()) {
|
||||
|
@ -184,6 +192,7 @@ struct Labeled {
|
|||
for (const auto &now : entry.now) {
|
||||
if (index < entry.buttons.size()) {
|
||||
entry.buttons[index]->key = now;
|
||||
entry.buttons[index]->removed = false;
|
||||
} else {
|
||||
auto button = std::make_unique<Button>(Button{
|
||||
.command = entry.command,
|
||||
|
@ -239,10 +248,21 @@ struct Labeled {
|
|||
}, keys->lifetime());
|
||||
keys->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
|
||||
widget->setClickedCallback([=] {
|
||||
S::Pause();
|
||||
state->recording = raw;
|
||||
});
|
||||
widget->setAcceptBoth(true);
|
||||
widget->clicks(
|
||||
) | rpl::start_with_next([=](Qt::MouseButton button) {
|
||||
if (const auto strong = *menu) {
|
||||
strong->hideMenu();
|
||||
return;
|
||||
}
|
||||
if (button == Qt::RightButton) {
|
||||
state->showMenuFor(raw->command);
|
||||
} else {
|
||||
S::Pause();
|
||||
state->recording = raw;
|
||||
}
|
||||
}, widget->lifetime());
|
||||
|
||||
button->widget.reset(widget);
|
||||
entry.buttons.push_back(std::move(button));
|
||||
}
|
||||
|
@ -252,6 +272,29 @@ struct Labeled {
|
|||
entry.buttons.pop_back();
|
||||
}
|
||||
};
|
||||
state->showMenuFor = [=](S::Command command) {
|
||||
*menu = Ui::CreateChild<Ui::PopupMenu>(
|
||||
content,
|
||||
st::popupMenuWithIcons);
|
||||
(*menu)->addAction(tr::lng_shortcuts_add_another(tr::now), [=] {
|
||||
const auto i = ranges::find(
|
||||
state->entries,
|
||||
command,
|
||||
&Entry::command);
|
||||
if (i != end(state->entries)) {
|
||||
S::Pause();
|
||||
const auto j = ranges::find(i->now, QKeySequence());
|
||||
if (j != end(i->now)) {
|
||||
state->recording = i->buttons[j - begin(i->now)].get();
|
||||
} else {
|
||||
i->now.push_back(QKeySequence());
|
||||
fill(*i);
|
||||
state->recording = i->buttons.back().get();
|
||||
}
|
||||
}
|
||||
}, &st::menuIconTopics);
|
||||
(*menu)->popup(QCursor::pos());
|
||||
};
|
||||
|
||||
const auto stopRecording = [=](std::optional<QKeySequence> result = {}) {
|
||||
const auto button = state->recording.current();
|
||||
|
@ -268,10 +311,11 @@ struct Labeled {
|
|||
auto was = button->key.current();
|
||||
const auto now = result.value_or(was);
|
||||
if (now == was) {
|
||||
if (!result || !button->removed.current()) {
|
||||
if (!now.isEmpty() && (!result || !button->removed.current())) {
|
||||
return;
|
||||
}
|
||||
was = QKeySequence();
|
||||
button->removed = false;
|
||||
}
|
||||
|
||||
auto changed = false;
|
||||
|
@ -335,7 +379,10 @@ struct Labeled {
|
|||
|| k == Qt::Key_Meta) {
|
||||
return base::EventFilterResult::Cancel;
|
||||
} else if (!m && !clear && !S::AllowWithoutModifiers(k)) {
|
||||
stopRecording();
|
||||
if (k != Qt::Key_Escape) {
|
||||
// Intercept this KeyPress event.
|
||||
stopRecording();
|
||||
}
|
||||
return base::EventFilterResult::Cancel;
|
||||
}
|
||||
stopRecording(clear ? QKeySequence() : QKeySequence(k | m));
|
||||
|
@ -372,6 +419,7 @@ struct Labeled {
|
|||
}
|
||||
}
|
||||
checkModified();
|
||||
S::ResetToDefaults();
|
||||
});
|
||||
AddSkip(modifiedInner);
|
||||
AddDivider(modifiedInner);
|
||||
|
|
|
@ -175,6 +175,7 @@ menuIconTradable: icon {{ "menu/tradable", menuIconColor }};
|
|||
menuIconUnique: icon {{ "menu/unique", menuIconColor }};
|
||||
menuIconNftWear: icon {{ "menu/nft_wear", menuIconColor }};
|
||||
menuIconNftTakeOff: icon {{ "menu/nft_takeoff", menuIconColor }};
|
||||
menuIconShortcut: icon {{ "menu/shortcut", menuIconColor }};
|
||||
|
||||
menuIconTTLAny: icon {{ "menu/auto_delete_plain", menuIconColor }};
|
||||
menuIconTTLAnyTextPosition: point(11px, 22px);
|
||||
|
|
Loading…
Add table
Reference in a new issue