mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Section for shortcuts editing.
This commit is contained in:
parent
c82fbefcfc
commit
0585e72c35
7 changed files with 572 additions and 4 deletions
|
@ -1474,6 +1474,8 @@ PRIVATE
|
||||||
settings/settings_privacy_security.h
|
settings/settings_privacy_security.h
|
||||||
settings/settings_scale_preview.cpp
|
settings/settings_scale_preview.cpp
|
||||||
settings/settings_scale_preview.h
|
settings/settings_scale_preview.h
|
||||||
|
settings/settings_shortcuts.cpp
|
||||||
|
settings/settings_shortcuts.h
|
||||||
settings/settings_type.h
|
settings/settings_type.h
|
||||||
settings/settings_websites.cpp
|
settings/settings_websites.cpp
|
||||||
settings/settings_websites.h
|
settings/settings_websites.h
|
||||||
|
|
|
@ -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_quick_action_react" = "Send reaction with double click";
|
||||||
"lng_settings_chat_corner_reaction" = "Reaction button on messages";
|
"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_title" = "Quick Reaction";
|
||||||
"lng_settings_chat_reactions_subtitle" = "Choose your favorite reaction";
|
"lng_settings_chat_reactions_subtitle" = "Choose your favorite reaction";
|
||||||
"lng_settings_chat_message_reply_from" = "Bob Harris";
|
"lng_settings_chat_message_reply_from" = "Bob Harris";
|
||||||
|
|
|
@ -28,6 +28,7 @@ namespace {
|
||||||
constexpr auto kCountLimit = 256; // How many shortcuts can be in json file.
|
constexpr auto kCountLimit = 256; // How many shortcuts can be in json file.
|
||||||
|
|
||||||
rpl::event_stream<not_null<Request*>> RequestsStream;
|
rpl::event_stream<not_null<Request*>> RequestsStream;
|
||||||
|
bool Paused/* = false*/;
|
||||||
|
|
||||||
const auto AutoRepeatCommands = base::flat_set<Command>{
|
const auto AutoRepeatCommands = base::flat_set<Command>{
|
||||||
Command::MediaPrevious,
|
Command::MediaPrevious,
|
||||||
|
@ -175,6 +176,9 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] const QStringList &errors() const;
|
[[nodiscard]] const QStringList &errors() const;
|
||||||
|
|
||||||
|
[[nodiscard]] base::flat_map<QKeySequence, Command> keysDefaults() const;
|
||||||
|
[[nodiscard]] base::flat_map<QKeySequence, Command> keysCurrents() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void fillDefaults();
|
void fillDefaults();
|
||||||
void writeDefaultFile();
|
void writeDefaultFile();
|
||||||
|
@ -189,6 +193,8 @@ private:
|
||||||
base::flat_map<QKeySequence, base::unique_qptr<QAction>> _shortcuts;
|
base::flat_map<QKeySequence, base::unique_qptr<QAction>> _shortcuts;
|
||||||
base::flat_multi_map<not_null<QObject*>, Command> _commandByObject;
|
base::flat_multi_map<not_null<QObject*>, Command> _commandByObject;
|
||||||
|
|
||||||
|
base::flat_map<QKeySequence, Command> _defaults;
|
||||||
|
|
||||||
base::flat_set<QAction*> _mediaShortcuts;
|
base::flat_set<QAction*> _mediaShortcuts;
|
||||||
base::flat_set<QAction*> _supportShortcuts;
|
base::flat_set<QAction*> _supportShortcuts;
|
||||||
|
|
||||||
|
@ -278,6 +284,21 @@ const QStringList &Manager::errors() const {
|
||||||
return _errors;
|
return _errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
base::flat_map<QKeySequence, Command> Manager::keysDefaults() const {
|
||||||
|
return _defaults;
|
||||||
|
}
|
||||||
|
|
||||||
|
base::flat_map<QKeySequence, Command> Manager::keysCurrents() const {
|
||||||
|
auto result = base::flat_map<QKeySequence, Command>();
|
||||||
|
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<Command> Manager::lookup(not_null<QObject*> object) const {
|
std::vector<Command> Manager::lookup(not_null<QObject*> object) const {
|
||||||
auto result = std::vector<Command>();
|
auto result = std::vector<Command>();
|
||||||
auto i = _commandByObject.findFirst(object);
|
auto i = _commandByObject.findFirst(object);
|
||||||
|
@ -441,6 +462,8 @@ void Manager::fillDefaults() {
|
||||||
set(u"ctrl+r"_q, Command::ReadChat);
|
set(u"ctrl+r"_q, Command::ReadChat);
|
||||||
|
|
||||||
set(u"ctrl+\\"_q, Command::ShowChatMenu);
|
set(u"ctrl+\\"_q, Command::ShowChatMenu);
|
||||||
|
|
||||||
|
_defaults = keysCurrents();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Manager::writeDefaultFile() {
|
void Manager::writeDefaultFile() {
|
||||||
|
@ -598,7 +621,9 @@ bool Launch(Command command) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Launch(std::vector<Command> commands) {
|
bool Launch(std::vector<Command> commands) {
|
||||||
if (auto handler = RequestHandler(std::move(commands))) {
|
if (Paused) {
|
||||||
|
return false;
|
||||||
|
} else if (auto handler = RequestHandler(std::move(commands))) {
|
||||||
return handler();
|
return handler();
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -630,6 +655,55 @@ void ToggleSupportShortcuts(bool toggled) {
|
||||||
Data.toggleSupport(toggled);
|
Data.toggleSupport(toggled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Pause() {
|
||||||
|
Paused = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Unpause() {
|
||||||
|
Paused = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
base::flat_map<QKeySequence, Command> KeysDefaults() {
|
||||||
|
return Data.keysDefaults();
|
||||||
|
}
|
||||||
|
|
||||||
|
base::flat_map<QKeySequence, Command> 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() {
|
void Finish() {
|
||||||
Data.clear();
|
Data.clear();
|
||||||
}
|
}
|
||||||
|
|
|
@ -139,4 +139,11 @@ void ToggleMediaShortcuts(bool toggled);
|
||||||
// have some conflicts with default input shortcuts, like Ctrl+Delete.
|
// have some conflicts with default input shortcuts, like Ctrl+Delete.
|
||||||
void ToggleSupportShortcuts(bool toggled);
|
void ToggleSupportShortcuts(bool toggled);
|
||||||
|
|
||||||
|
void Pause();
|
||||||
|
void Unpause();
|
||||||
|
|
||||||
|
[[nodiscard]] base::flat_map<QKeySequence, Command> KeysDefaults();
|
||||||
|
[[nodiscard]] base::flat_map<QKeySequence, Command> KeysCurrents();
|
||||||
|
[[nodiscard]] bool AllowWithoutModifiers(int key);
|
||||||
|
|
||||||
} // namespace Shortcuts
|
} // namespace Shortcuts
|
||||||
|
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "settings/settings_advanced.h"
|
#include "settings/settings_advanced.h"
|
||||||
#include "settings/settings_privacy_security.h"
|
#include "settings/settings_privacy_security.h"
|
||||||
#include "settings/settings_experimental.h"
|
#include "settings/settings_experimental.h"
|
||||||
|
#include "settings/settings_shortcuts.h"
|
||||||
#include "boxes/abstract_box.h"
|
#include "boxes/abstract_box.h"
|
||||||
#include "boxes/peers/edit_peer_color_box.h"
|
#include "boxes/peers/edit_peer_color_box.h"
|
||||||
#include "boxes/connection_box.h"
|
#include "boxes/connection_box.h"
|
||||||
|
@ -833,7 +834,8 @@ void SetupStickersEmoji(
|
||||||
|
|
||||||
void SetupMessages(
|
void SetupMessages(
|
||||||
not_null<Window::SessionController*> controller,
|
not_null<Window::SessionController*> controller,
|
||||||
not_null<Ui::VerticalLayout*> container) {
|
not_null<Ui::VerticalLayout*> container,
|
||||||
|
Fn<void(Type)> showOther) {
|
||||||
Ui::AddDivider(container);
|
Ui::AddDivider(container);
|
||||||
Ui::AddSkip(container);
|
Ui::AddSkip(container);
|
||||||
|
|
||||||
|
@ -1003,7 +1005,16 @@ void SetupMessages(
|
||||||
Core::App().saveSettingsDelayed();
|
Core::App().saveSettingsDelayed();
|
||||||
}, inner->lifetime());
|
}, 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(
|
void SetupArchive(
|
||||||
|
@ -1793,7 +1804,7 @@ void Chat::setupContent(not_null<Window::SessionController*> controller) {
|
||||||
SetupCloudThemes(controller, content);
|
SetupCloudThemes(controller, content);
|
||||||
SetupChatBackground(controller, content);
|
SetupChatBackground(controller, content);
|
||||||
SetupStickersEmoji(controller, content);
|
SetupStickersEmoji(controller, content);
|
||||||
SetupMessages(controller, content);
|
SetupMessages(controller, content, showOtherMethod());
|
||||||
Ui::AddDivider(content);
|
Ui::AddDivider(content);
|
||||||
SetupSensitiveContent(controller, content, std::move(updateOnTick));
|
SetupSensitiveContent(controller, content, std::move(updateOnTick));
|
||||||
SetupArchive(controller, content);
|
SetupArchive(controller, content);
|
||||||
|
|
406
Telegram/SourceFiles/settings/settings_shortcuts.cpp
Normal file
406
Telegram/SourceFiles/settings/settings_shortcuts.cpp
Normal file
|
@ -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<QString> label;
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] std::vector<Labeled> 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<void()> SetupShortcutsContent(
|
||||||
|
not_null<Window::SessionController*> controller,
|
||||||
|
not_null<Ui::VerticalLayout*> content) {
|
||||||
|
const auto &defaults = S::KeysDefaults();
|
||||||
|
const auto ¤ts = S::KeysCurrents();
|
||||||
|
|
||||||
|
struct Button {
|
||||||
|
S::Command command;
|
||||||
|
std::unique_ptr<Ui::SettingsButton> widget;
|
||||||
|
rpl::variable<QKeySequence> key;
|
||||||
|
rpl::variable<bool> removed;
|
||||||
|
};
|
||||||
|
struct Entry {
|
||||||
|
S::Command command;
|
||||||
|
rpl::producer<QString> label;
|
||||||
|
std::vector<QKeySequence> original;
|
||||||
|
std::vector<QKeySequence> now;
|
||||||
|
Ui::VerticalLayout *wrap = nullptr;
|
||||||
|
std::vector<std::unique_ptr<Button>> buttons;
|
||||||
|
};
|
||||||
|
struct State {
|
||||||
|
std::vector<Entry> entries;
|
||||||
|
rpl::variable<bool> modified;
|
||||||
|
rpl::variable<Button*> recording;
|
||||||
|
rpl::variable<QKeySequence> lastKey;
|
||||||
|
};
|
||||||
|
const auto state = content->lifetime().make_state<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<Button>(Button{
|
||||||
|
.command = entry.command,
|
||||||
|
.key = now,
|
||||||
|
});
|
||||||
|
const auto raw = button.get();
|
||||||
|
const auto widget = entry.wrap->add(
|
||||||
|
object_ptr<Ui::SettingsButton>(
|
||||||
|
entry.wrap,
|
||||||
|
rpl::duplicate(entry.label),
|
||||||
|
st::settingsButtonNoIcon));
|
||||||
|
const auto keys = Ui::CreateChild<Ui::FlatLabel>(
|
||||||
|
widget,
|
||||||
|
st::settingsButtonNoIcon.rightLabel);
|
||||||
|
keys->show();
|
||||||
|
rpl::combine(
|
||||||
|
widget->widthValue(),
|
||||||
|
rpl::duplicate(entry.label),
|
||||||
|
button->key.value(),
|
||||||
|
state->recording.value(),
|
||||||
|
button->removed.value()
|
||||||
|
) | rpl::start_with_next([=](
|
||||||
|
int width,
|
||||||
|
const QString &button,
|
||||||
|
const QKeySequence &key,
|
||||||
|
Button *recording,
|
||||||
|
bool removed) {
|
||||||
|
const auto &st = st::settingsButtonNoIcon;
|
||||||
|
const auto available = width
|
||||||
|
- st.padding.left()
|
||||||
|
- st.padding.right()
|
||||||
|
- st.style.font->width(button)
|
||||||
|
- st::settingsButtonRightSkip;
|
||||||
|
keys->setMarkedText((recording == raw)
|
||||||
|
? Ui::Text::Italic(
|
||||||
|
tr::lng_shortcuts_recording(tr::now))
|
||||||
|
: key.isEmpty()
|
||||||
|
? TextWithEntities()
|
||||||
|
: removed
|
||||||
|
? Ui::Text::Wrapped(
|
||||||
|
TextWithEntities{ key.toString() },
|
||||||
|
EntityType::StrikeOut)
|
||||||
|
: TextWithEntities{ key.toString() });
|
||||||
|
keys->setTextColorOverride(removed
|
||||||
|
? st::attentionButtonFg->c
|
||||||
|
: (recording == raw)
|
||||||
|
? st::boxTextFgGood->c
|
||||||
|
: std::optional<QColor>());
|
||||||
|
keys->resizeToNaturalWidth(available);
|
||||||
|
keys->moveToRight(
|
||||||
|
st::settingsButtonRightSkip,
|
||||||
|
st.padding.top());
|
||||||
|
}, keys->lifetime());
|
||||||
|
keys->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
|
|
||||||
|
widget->setClickedCallback([=] {
|
||||||
|
S::Pause();
|
||||||
|
state->recording = raw;
|
||||||
|
});
|
||||||
|
button->widget.reset(widget);
|
||||||
|
entry.buttons.push_back(std::move(button));
|
||||||
|
}
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
while (entry.wrap->count() > index) {
|
||||||
|
entry.buttons.pop_back();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto stopRecording = [=](std::optional<QKeySequence> result = {}) {
|
||||||
|
const auto button = state->recording.current();
|
||||||
|
if (!button) {
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
base::install_event_filter(content, qApp, [=](not_null<QEvent*> e) {
|
||||||
|
const auto type = e->type();
|
||||||
|
if (type == QEvent::ShortcutOverride && state->recording.current()) {
|
||||||
|
const auto key = static_cast<QKeyEvent*>(e.get());
|
||||||
|
const auto m = key->modifiers();
|
||||||
|
const auto k = key->key();
|
||||||
|
const auto clear = !m
|
||||||
|
&& (k == Qt::Key_Backspace || k == Qt::Key_Delete);
|
||||||
|
if (k == Qt::Key_Control
|
||||||
|
|| k == Qt::Key_Shift
|
||||||
|
|| k == Qt::Key_Alt
|
||||||
|
|| k == Qt::Key_Meta) {
|
||||||
|
return base::EventFilterResult::Cancel;
|
||||||
|
} else if (!m && !clear && !S::AllowWithoutModifiers(k)) {
|
||||||
|
stopRecording();
|
||||||
|
return base::EventFilterResult::Cancel;
|
||||||
|
}
|
||||||
|
stopRecording(clear ? QKeySequence() : QKeySequence(k | m));
|
||||||
|
return base::EventFilterResult::Cancel;
|
||||||
|
} else if (type == QEvent::KeyPress) {
|
||||||
|
if (static_cast<QKeyEvent*>(e.get())->key() == Qt::Key_Escape) {
|
||||||
|
stopRecording();
|
||||||
|
return base::EventFilterResult::Cancel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return base::EventFilterResult::Continue;
|
||||||
|
});
|
||||||
|
|
||||||
|
const auto modifiedWrap = content->add(
|
||||||
|
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||||
|
content,
|
||||||
|
object_ptr<Ui::VerticalLayout>(content)));
|
||||||
|
const auto modifiedInner = modifiedWrap->entity();
|
||||||
|
AddDivider(modifiedInner);
|
||||||
|
AddSkip(modifiedInner);
|
||||||
|
const auto reset = modifiedInner->add(object_ptr<Ui::SettingsButton>(
|
||||||
|
modifiedInner,
|
||||||
|
tr::lng_shortcuts_reset(),
|
||||||
|
st::settingsButtonNoIcon));
|
||||||
|
reset->setClickedCallback([=] {
|
||||||
|
stopRecording();
|
||||||
|
for (auto &entry : state->entries) {
|
||||||
|
if (entry.now != entry.original) {
|
||||||
|
entry.now = entry.original;
|
||||||
|
fill(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checkModified();
|
||||||
|
});
|
||||||
|
AddSkip(modifiedInner);
|
||||||
|
AddDivider(modifiedInner);
|
||||||
|
modifiedWrap->toggleOn(state->modified.value());
|
||||||
|
|
||||||
|
AddSkip(content);
|
||||||
|
for (auto &entry : entries) {
|
||||||
|
if (!entry.label) {
|
||||||
|
AddSkip(content);
|
||||||
|
AddDivider(content);
|
||||||
|
AddSkip(content);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
entry.wrap = content->add(object_ptr<Ui::VerticalLayout>(content));
|
||||||
|
fill(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [=] {
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
Shortcuts::Shortcuts(
|
||||||
|
QWidget *parent,
|
||||||
|
not_null<Window::SessionController*> controller)
|
||||||
|
: Section(parent) {
|
||||||
|
setupContent(controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
Shortcuts::~Shortcuts() {
|
||||||
|
if (!Core::Quitting()) {
|
||||||
|
_save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<QString> Shortcuts::title() {
|
||||||
|
return tr::lng_settings_shortcuts();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shortcuts::setupContent(
|
||||||
|
not_null<Window::SessionController*> controller) {
|
||||||
|
const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
|
||||||
|
|
||||||
|
_save = SetupShortcutsContent(controller, content);
|
||||||
|
|
||||||
|
Ui::ResizeFitChild(this, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Settings
|
31
Telegram/SourceFiles/settings/settings_shortcuts.h
Normal file
31
Telegram/SourceFiles/settings/settings_shortcuts.h
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "settings/settings_common_session.h"
|
||||||
|
|
||||||
|
namespace Settings {
|
||||||
|
|
||||||
|
class Shortcuts : public Section<Shortcuts> {
|
||||||
|
public:
|
||||||
|
Shortcuts(
|
||||||
|
QWidget *parent,
|
||||||
|
not_null<Window::SessionController*> controller);
|
||||||
|
~Shortcuts();
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::producer<QString> title() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setupContent(not_null<Window::SessionController*> controller);
|
||||||
|
|
||||||
|
Fn<void()> _save;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Settings
|
||||||
|
|
Loading…
Add table
Reference in a new issue