From 0585e72c35c7423e81ed72c0fdd7bd70cd3abef8 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 6 Feb 2025 18:22:58 +0400 Subject: [PATCH] Section for shortcuts editing. --- Telegram/CMakeLists.txt | 2 + Telegram/Resources/langs/lang.strings | 37 ++ Telegram/SourceFiles/core/shortcuts.cpp | 76 +++- Telegram/SourceFiles/core/shortcuts.h | 7 + .../SourceFiles/settings/settings_chat.cpp | 17 +- .../settings/settings_shortcuts.cpp | 406 ++++++++++++++++++ .../SourceFiles/settings/settings_shortcuts.h | 31 ++ 7 files changed, 572 insertions(+), 4 deletions(-) create mode 100644 Telegram/SourceFiles/settings/settings_shortcuts.cpp create mode 100644 Telegram/SourceFiles/settings/settings_shortcuts.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 47081bf7a..5253ebde5 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -1474,6 +1474,8 @@ PRIVATE settings/settings_privacy_security.h settings/settings_scale_preview.cpp settings/settings_scale_preview.h + settings/settings_shortcuts.cpp + settings/settings_shortcuts.h settings/settings_type.h settings/settings_websites.cpp settings/settings_websites.h diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 0a3820fea..0c6651782 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -642,6 +642,43 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_settings_chat_quick_action_react" = "Send reaction with double click"; "lng_settings_chat_corner_reaction" = "Reaction button on messages"; +"lng_settings_shortcuts" = "Keyboard shortcuts"; +"lng_shortcuts_reset" = "Reset to default"; +"lng_shortcuts_close" = "Close the window"; +"lng_shortcuts_lock" = "Lock the application"; +"lng_shortcuts_minimize" = "Minimize the window"; +"lng_shortcuts_quit" = "Quit the application"; +"lng_shortcuts_media_play" = "Play the media"; +"lng_shortcuts_media_pause" = "Pause the media"; +"lng_shortcuts_media_play_pause" = "Toggle media playback"; +"lng_shortcuts_media_stop" = "Stop media playback"; +"lng_shortcuts_media_previous" = "Previous track"; +"lng_shortcuts_media_next" = "Next track"; +"lng_shortcuts_search" = "Search messages"; +"lng_shortcuts_chat_previous" = "Previous chat"; +"lng_shortcuts_chat_next" = "Next chat"; +"lng_shortcuts_chat_first" = "First chat"; +"lng_shortcuts_chat_last" = "Last chat"; +"lng_shortcuts_chat_self" = "Saved Messages"; +"lng_shortcuts_chat_pinned_n" = "Pinned chat #{index}"; +"lng_shortcuts_show_account_n" = "Account #{index}"; +"lng_shortcuts_show_all_chats" = "All Chats folder"; +"lng_shortcuts_show_folder_n" = "Folder #{index}"; +"lng_shortcuts_show_folder_last" = "Last folder"; +"lng_shortcuts_folder_next" = "Next folder"; +"lng_shortcuts_folder_previous" = "Previous folder"; +"lng_shortcuts_scheduled" = "Scheduled messages"; +"lng_shortcuts_archive" = "Archived chats"; +"lng_shortcuts_contacts" = "Contacts list"; +"lng_shortcuts_just_send" = "Just send"; +"lng_shortcuts_silent_send" = "Silent send"; +"lng_shortcuts_schedule" = "Schedule"; +"lng_shortcuts_read_chat" = "Mark chat as read"; +"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"; "lng_settings_chat_message_reply_from" = "Bob Harris"; diff --git a/Telegram/SourceFiles/core/shortcuts.cpp b/Telegram/SourceFiles/core/shortcuts.cpp index 49f81d1d8..687db4d25 100644 --- a/Telegram/SourceFiles/core/shortcuts.cpp +++ b/Telegram/SourceFiles/core/shortcuts.cpp @@ -28,6 +28,7 @@ namespace { constexpr auto kCountLimit = 256; // How many shortcuts can be in json file. rpl::event_stream> RequestsStream; +bool Paused/* = false*/; const auto AutoRepeatCommands = base::flat_set{ Command::MediaPrevious, @@ -175,6 +176,9 @@ public: [[nodiscard]] const QStringList &errors() const; + [[nodiscard]] base::flat_map keysDefaults() const; + [[nodiscard]] base::flat_map keysCurrents() const; + private: void fillDefaults(); void writeDefaultFile(); @@ -189,6 +193,8 @@ private: base::flat_map> _shortcuts; base::flat_multi_map, Command> _commandByObject; + base::flat_map _defaults; + base::flat_set _mediaShortcuts; base::flat_set _supportShortcuts; @@ -278,6 +284,21 @@ const QStringList &Manager::errors() const { return _errors; } +base::flat_map Manager::keysDefaults() const { + return _defaults; +} + +base::flat_map Manager::keysCurrents() const { + auto result = base::flat_map(); + for (const auto &[keys, command] : _shortcuts) { + const auto i = _commandByObject.findFirst(command); + if (i != _commandByObject.end()) { + result.emplace(keys, i->second); + } + } + return result; +} + std::vector Manager::lookup(not_null object) const { auto result = std::vector(); auto i = _commandByObject.findFirst(object); @@ -441,6 +462,8 @@ void Manager::fillDefaults() { set(u"ctrl+r"_q, Command::ReadChat); set(u"ctrl+\\"_q, Command::ShowChatMenu); + + _defaults = keysCurrents(); } void Manager::writeDefaultFile() { @@ -598,7 +621,9 @@ bool Launch(Command command) { } bool Launch(std::vector commands) { - if (auto handler = RequestHandler(std::move(commands))) { + if (Paused) { + return false; + } else if (auto handler = RequestHandler(std::move(commands))) { return handler(); } return false; @@ -630,6 +655,55 @@ void ToggleSupportShortcuts(bool toggled) { Data.toggleSupport(toggled); } +void Pause() { + Paused = true; +} + +void Unpause() { + Paused = false; +} + +base::flat_map KeysDefaults() { + return Data.keysDefaults(); +} + +base::flat_map KeysCurrents() { + return Data.keysCurrents(); +} + +bool AllowWithoutModifiers(int key) { + const auto service = { + Qt::Key_Escape, + Qt::Key_Tab, + Qt::Key_Backtab, + Qt::Key_Backspace, + Qt::Key_Return, + Qt::Key_Enter, + Qt::Key_Insert, + Qt::Key_Delete, + Qt::Key_Pause, + Qt::Key_Print, + Qt::Key_SysReq, + Qt::Key_Clear, + Qt::Key_Home, + Qt::Key_End, + Qt::Key_Left, + Qt::Key_Up, + Qt::Key_Right, + Qt::Key_Down, + Qt::Key_PageUp, + Qt::Key_PageDown, + Qt::Key_Shift, + Qt::Key_Control, + Qt::Key_Meta, + Qt::Key_Alt, + Qt::Key_CapsLock, + Qt::Key_NumLock, + Qt::Key_ScrollLock, + }; + return (key >= 0x80) && !ranges::contains(service, key); +} + void Finish() { Data.clear(); } diff --git a/Telegram/SourceFiles/core/shortcuts.h b/Telegram/SourceFiles/core/shortcuts.h index 5355d1681..53da6e0b4 100644 --- a/Telegram/SourceFiles/core/shortcuts.h +++ b/Telegram/SourceFiles/core/shortcuts.h @@ -139,4 +139,11 @@ void ToggleMediaShortcuts(bool toggled); // have some conflicts with default input shortcuts, like Ctrl+Delete. void ToggleSupportShortcuts(bool toggled); +void Pause(); +void Unpause(); + +[[nodiscard]] base::flat_map KeysDefaults(); +[[nodiscard]] base::flat_map KeysCurrents(); +[[nodiscard]] bool AllowWithoutModifiers(int key); + } // namespace Shortcuts diff --git a/Telegram/SourceFiles/settings/settings_chat.cpp b/Telegram/SourceFiles/settings/settings_chat.cpp index 1dfda764c..32cd54682 100644 --- a/Telegram/SourceFiles/settings/settings_chat.cpp +++ b/Telegram/SourceFiles/settings/settings_chat.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "settings/settings_advanced.h" #include "settings/settings_privacy_security.h" #include "settings/settings_experimental.h" +#include "settings/settings_shortcuts.h" #include "boxes/abstract_box.h" #include "boxes/peers/edit_peer_color_box.h" #include "boxes/connection_box.h" @@ -833,7 +834,8 @@ void SetupStickersEmoji( void SetupMessages( not_null controller, - not_null container) { + not_null container, + Fn showOther) { Ui::AddDivider(container); Ui::AddSkip(container); @@ -1003,7 +1005,16 @@ void SetupMessages( Core::App().saveSettingsDelayed(); }, inner->lifetime()); - Ui::AddSkip(inner, st::settingsCheckboxesSkip); + AddButtonWithIcon( + inner, + tr::lng_settings_shortcuts(), + st::settingsButton, + { &st::menuIconBotCommands } + )->addClickHandler([=] { + showOther(Shortcuts::Id()); + }); + + Ui::AddSkip(inner); } void SetupArchive( @@ -1793,7 +1804,7 @@ void Chat::setupContent(not_null controller) { SetupCloudThemes(controller, content); SetupChatBackground(controller, content); SetupStickersEmoji(controller, content); - SetupMessages(controller, content); + SetupMessages(controller, content, showOtherMethod()); Ui::AddDivider(content); SetupSensitiveContent(controller, content, std::move(updateOnTick)); SetupArchive(controller, content); diff --git a/Telegram/SourceFiles/settings/settings_shortcuts.cpp b/Telegram/SourceFiles/settings/settings_shortcuts.cpp new file mode 100644 index 000000000..f895cfbb0 --- /dev/null +++ b/Telegram/SourceFiles/settings/settings_shortcuts.cpp @@ -0,0 +1,406 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "settings/settings_shortcuts.h" + +#include "base/event_filter.h" +#include "core/application.h" +#include "core/shortcuts.h" +#include "lang/lang_keys.h" +#include "ui/text/text_utilities.h" +#include "ui/widgets/buttons.h" +#include "ui/widgets/labels.h" +#include "ui/wrap/slide_wrap.h" +#include "ui/wrap/vertical_layout.h" +#include "ui/vertical_list.h" +#include "styles/style_settings.h" + +namespace Settings { +namespace { + +namespace S = ::Shortcuts; + +struct Labeled { + S::Command command = {}; + rpl::producer label; +}; + +[[nodiscard]] std::vector Entries() { + using C = S::Command; + const auto pinned = [](int index) { + return tr::lng_shortcuts_chat_pinned_n( + lt_index, + rpl::single(QString::number(index))); + }; + const auto account = [](int index) { + return tr::lng_shortcuts_show_account_n( + lt_index, + rpl::single(QString::number(index))); + }; + const auto folder = [](int index) { + return tr::lng_shortcuts_show_folder_n( + lt_index, + rpl::single(QString::number(index))); + }; + const auto separator = Labeled{ C(), nullptr }; + return { + { C::Close, tr::lng_shortcuts_close() }, + { C::Lock, tr::lng_shortcuts_lock() }, + { C::Minimize, tr::lng_shortcuts_minimize() }, + { C::Quit, tr::lng_shortcuts_quit() }, + separator, + { C::Search, tr::lng_shortcuts_search() }, + separator, + { C::ChatPrevious, tr::lng_shortcuts_chat_previous() }, + { C::ChatNext, tr::lng_shortcuts_chat_next() }, + { C::ChatFirst, tr::lng_shortcuts_chat_first() }, + { C::ChatLast, tr::lng_shortcuts_chat_last() }, + { C::ChatSelf, tr::lng_shortcuts_chat_self() }, + separator, + { C::ChatPinned1, pinned(1) }, + { C::ChatPinned2, pinned(2) }, + { C::ChatPinned3, pinned(3) }, + { C::ChatPinned4, pinned(4) }, + { C::ChatPinned5, pinned(5) }, + { C::ChatPinned6, pinned(6) }, + { C::ChatPinned7, pinned(7) }, + { C::ChatPinned8, pinned(8) }, + separator, + { C::ShowAccount1, account(1) }, + { C::ShowAccount2, account(2) }, + { C::ShowAccount3, account(3) }, + { C::ShowAccount4, account(4) }, + { C::ShowAccount5, account(5) }, + { C::ShowAccount6, account(6) }, + separator, + { C::ShowAllChats, tr::lng_shortcuts_show_all_chats() }, + { C::ShowFolder1, folder(1) }, + { C::ShowFolder2, folder(2) }, + { C::ShowFolder3, folder(3) }, + { C::ShowFolder4, folder(4) }, + { C::ShowFolder5, folder(5) }, + { C::ShowFolder6, folder(6) }, + { C::ShowFolderLast, tr::lng_shortcuts_show_folder_last() }, + { C::FolderNext, tr::lng_shortcuts_folder_next() }, + { C::FolderPrevious, tr::lng_shortcuts_folder_previous() }, + { C::ShowArchive, tr::lng_shortcuts_archive() }, + { C::ShowContacts, tr::lng_shortcuts_contacts() }, + separator, + { C::ReadChat, tr::lng_shortcuts_read_chat() }, + { C::ArchiveChat, tr::lng_shortcuts_archive_chat() }, + { C::ShowScheduled, tr::lng_shortcuts_scheduled() }, + { C::ShowChatMenu, tr::lng_shortcuts_show_chat_menu() }, + separator, + { C::JustSendMessage, tr::lng_shortcuts_just_send() }, + { C::SendSilentMessage, tr::lng_shortcuts_silent_send() }, + { C::ScheduleMessage, tr::lng_shortcuts_schedule() }, + separator, + { C::MediaViewerFullscreen, tr::lng_shortcuts_media_fullscreen() }, + separator, + { C::MediaPlay, tr::lng_shortcuts_media_play() }, + { C::MediaPause, tr::lng_shortcuts_media_pause() }, + { C::MediaPlayPause, tr::lng_shortcuts_media_play_pause() }, + { C::MediaStop, tr::lng_shortcuts_media_stop() }, + { C::MediaPrevious, tr::lng_shortcuts_media_previous() }, + { C::MediaNext, tr::lng_shortcuts_media_next() }, + }; +} + +[[nodiscard]] Fn SetupShortcutsContent( + not_null controller, + not_null content) { + const auto &defaults = S::KeysDefaults(); + const auto ¤ts = S::KeysCurrents(); + + struct Button { + S::Command command; + std::unique_ptr widget; + rpl::variable key; + rpl::variable removed; + }; + struct Entry { + S::Command command; + rpl::producer label; + std::vector original; + std::vector now; + Ui::VerticalLayout *wrap = nullptr; + std::vector> buttons; + }; + struct State { + std::vector entries; + rpl::variable modified; + rpl::variable recording; + rpl::variable lastKey; + }; + const auto state = content->lifetime().make_state(); + const auto labeled = Entries(); + auto &entries = state->entries = ranges::views::all( + labeled + ) | ranges::views::transform([](Labeled 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, command] : currents) { + const auto i = ranges::find(entries, command, &Entry::command); + if (i != end(entries)) { + i->now.push_back(keys); + } + } + + const auto checkModified = [=] { + for (const auto &entry : state->entries) { + auto original = entry.original; + auto now = entry.now; + ranges::sort(original); + ranges::sort(now); + if (original != now) { + state->modified = true; + return; + } + } + state->modified = false; + }; + checkModified(); + + const auto fill = [=](Entry &entry) { + auto index = 0; + if (entry.original.empty()) { + entry.original.push_back(QKeySequence()); + } + if (entry.now.empty()) { + entry.now.push_back(QKeySequence()); + } + for (const auto &now : entry.now) { + if (index < entry.buttons.size()) { + entry.buttons[index]->key = now; + } else { + auto button = std::make_unique