mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-03-25 04:38:23 +00:00
Improve code for Shortcuts handling.
This commit is contained in:
parent
8a3615281c
commit
b3ffbeb63e
14
Telegram/Resources/default_shortcuts-custom.json
Normal file
14
Telegram/Resources/default_shortcuts-custom.json
Normal file
@ -0,0 +1,14 @@
|
||||
// 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
|
||||
|
||||
[
|
||||
// {
|
||||
// "command": "close_telegram",
|
||||
// "keys": "ctrl+f4"
|
||||
// },
|
||||
// {
|
||||
// "command": "quit_telegram",
|
||||
// "keys": "ctrl+q"
|
||||
// }
|
||||
]
|
@ -65,6 +65,9 @@
|
||||
<qresource prefix="/qt-project.org">
|
||||
<file>qmime/freedesktop.org.xml</file>
|
||||
</qresource>
|
||||
<qresource prefix="/misc">
|
||||
<file alias="default_shortcuts-custom.json">../default_shortcuts-custom.json</file>
|
||||
</qresource>
|
||||
<qresource prefix="/langs">
|
||||
<file alias="lang_it.strings">../langs/lang_it.strings</file>
|
||||
<file alias="lang_es.strings">../langs/lang_es.strings</file>
|
||||
|
@ -68,8 +68,6 @@ enum {
|
||||
MaxZoomLevel = 7, // x8
|
||||
ZoomToScreenLevel = 1024, // just constant
|
||||
|
||||
ShortcutsCountLimit = 256, // how many shortcuts can be in json file
|
||||
|
||||
PreloadHeightsCount = 3, // when 3 screens to scroll left make a preload request
|
||||
|
||||
SearchPeopleLimit = 5,
|
||||
|
451
Telegram/SourceFiles/core/shortcuts.cpp
Normal file
451
Telegram/SourceFiles/core/shortcuts.cpp
Normal file
@ -0,0 +1,451 @@
|
||||
/*
|
||||
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 "core/shortcuts.h"
|
||||
|
||||
#include "mainwindow.h"
|
||||
#include "mainwidget.h"
|
||||
#include "messenger.h"
|
||||
#include "media/player/media_player_instance.h"
|
||||
#include "platform/platform_specific.h"
|
||||
#include "base/parse_helper.h"
|
||||
|
||||
namespace Shortcuts {
|
||||
namespace {
|
||||
|
||||
constexpr auto kCountLimit = 256; // How many shortcuts can be in json file.
|
||||
|
||||
rpl::event_stream<not_null<Request*>> RequestsStream;
|
||||
|
||||
const auto AutoRepeatCommands = base::flat_set<Command>{
|
||||
Command::MediaPrevious,
|
||||
Command::MediaNext,
|
||||
Command::ChatPrevious,
|
||||
Command::ChatNext,
|
||||
};
|
||||
|
||||
const auto MediaCommands = base::flat_set<Command>{
|
||||
Command::MediaPlay,
|
||||
Command::MediaPause,
|
||||
Command::MediaPlayPause,
|
||||
Command::MediaStop,
|
||||
Command::MediaPrevious,
|
||||
Command::MediaNext,
|
||||
};
|
||||
|
||||
const auto CommandByName = base::flat_map<QString, Command>{
|
||||
{ qsl("close_telegram") , Command::Close },
|
||||
{ qsl("lock_telegram") , Command::Lock },
|
||||
{ qsl("minimize_telegram"), Command::Minimize },
|
||||
{ qsl("quit_telegram") , Command::Quit },
|
||||
|
||||
{ qsl("media_play") , Command::MediaPlay },
|
||||
{ qsl("media_pause") , Command::MediaPause },
|
||||
{ qsl("media_playpause") , Command::MediaPlayPause },
|
||||
{ qsl("media_stop") , Command::MediaStop },
|
||||
{ qsl("media_previous") , Command::MediaPrevious },
|
||||
{ qsl("media_next") , Command::MediaNext },
|
||||
|
||||
{ qsl("search") , Command::Search },
|
||||
|
||||
{ qsl("previous_chat") , Command::ChatPrevious },
|
||||
{ qsl("next_chat") , Command::ChatNext },
|
||||
};
|
||||
|
||||
const auto CommandNames = base::flat_map<Command, QString>{
|
||||
{ Command::Close , qsl("close_telegram") },
|
||||
{ Command::Lock , qsl("lock_telegram") },
|
||||
{ Command::Minimize , qsl("minimize_telegram") },
|
||||
{ Command::Quit , qsl("quit_telegram") },
|
||||
|
||||
{ Command::MediaPlay , qsl("media_play") },
|
||||
{ Command::MediaPause , qsl("media_pause") },
|
||||
{ Command::MediaPlayPause, qsl("media_playpause") },
|
||||
{ Command::MediaStop , qsl("media_stop") },
|
||||
{ Command::MediaPrevious , qsl("media_previous") },
|
||||
{ Command::MediaNext , qsl("media_next") },
|
||||
|
||||
{ Command::Search , qsl("search") },
|
||||
|
||||
{ Command::ChatPrevious , qsl("previous_chat") },
|
||||
{ Command::ChatNext , qsl("next_chat") },
|
||||
};
|
||||
|
||||
class Manager {
|
||||
public:
|
||||
void fill();
|
||||
void clear();
|
||||
|
||||
std::optional<Command> lookup(int shortcutId) const;
|
||||
void toggleMedia(bool toggled);
|
||||
|
||||
const QStringList &errors() const;
|
||||
|
||||
private:
|
||||
void fillDefaults();
|
||||
void writeDefaultFile();
|
||||
bool readCustomFile();
|
||||
|
||||
void set(const QString &keys, Command command);
|
||||
void remove(const QString &keys);
|
||||
void unregister(base::unique_qptr<QShortcut> shortcut);
|
||||
|
||||
QStringList _errors;
|
||||
|
||||
base::flat_map<QKeySequence, base::unique_qptr<QShortcut>> _shortcuts;
|
||||
base::flat_map<int, Command> _commandByShortcutId;
|
||||
|
||||
base::flat_set<QShortcut*> _mediaShortcuts;
|
||||
|
||||
};
|
||||
|
||||
QString DefaultFilePath() {
|
||||
return cWorkingDir() + qsl("tdata/shortcuts-default.json");
|
||||
}
|
||||
|
||||
QString CustomFilePath() {
|
||||
return cWorkingDir() + qsl("tdata/shortcuts-custom.json");
|
||||
}
|
||||
|
||||
bool DefaultFileIsValid() {
|
||||
QFile file(DefaultFilePath());
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
return false;
|
||||
}
|
||||
auto error = QJsonParseError{ 0, QJsonParseError::NoError };
|
||||
const auto document = QJsonDocument::fromJson(
|
||||
base::parse::stripComments(file.readAll()),
|
||||
&error);
|
||||
file.close();
|
||||
|
||||
if (error.error != QJsonParseError::NoError || !document.isArray()) {
|
||||
return false;
|
||||
}
|
||||
const auto shortcuts = document.array();
|
||||
if (shortcuts.isEmpty() || !(*shortcuts.constBegin()).isObject()) {
|
||||
return false;
|
||||
}
|
||||
const auto versionObject = (*shortcuts.constBegin()).toObject();
|
||||
const auto version = versionObject.constFind(qsl("version"));
|
||||
if (version == versionObject.constEnd()
|
||||
|| !(*version).isString()
|
||||
|| (*version).toString() != QString::number(AppVersion)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void WriteDefaultCustomFile() {
|
||||
const auto path = CustomFilePath();
|
||||
auto input = QFile(":/misc/default_shortcuts-custom.json");
|
||||
auto output = QFile(path);
|
||||
if (input.open(QIODevice::ReadOnly) && output.open(QIODevice::WriteOnly)) {
|
||||
output.write(input.readAll());
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::fill() {
|
||||
fillDefaults();
|
||||
|
||||
if (!DefaultFileIsValid()) {
|
||||
writeDefaultFile();
|
||||
}
|
||||
if (!readCustomFile()) {
|
||||
WriteDefaultCustomFile();
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::clear() {
|
||||
_errors.clear();
|
||||
_shortcuts.clear();
|
||||
_commandByShortcutId.clear();
|
||||
_mediaShortcuts.clear();
|
||||
}
|
||||
|
||||
const QStringList &Manager::errors() const {
|
||||
return _errors;
|
||||
}
|
||||
|
||||
std::optional<Command> Manager::lookup(int shortcutId) const {
|
||||
const auto i = _commandByShortcutId.find(shortcutId);
|
||||
return (i != end(_commandByShortcutId))
|
||||
? base::make_optional(i->second)
|
||||
: std::nullopt;
|
||||
}
|
||||
|
||||
void Manager::toggleMedia(bool toggled) {
|
||||
for (const auto shortcut : _mediaShortcuts) {
|
||||
shortcut->setEnabled(toggled);
|
||||
}
|
||||
}
|
||||
|
||||
bool Manager::readCustomFile() {
|
||||
// read custom shortcuts from file if it exists or write an empty custom shortcuts file
|
||||
QFile file(CustomFilePath());
|
||||
if (!file.exists()) {
|
||||
return false;
|
||||
}
|
||||
const auto guard = gsl::finally([&] {
|
||||
if (!_errors.isEmpty()) {
|
||||
_errors.push_front(qsl("While reading file '%1'..."
|
||||
).arg(file.fileName()));
|
||||
}
|
||||
});
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
_errors.push_back(qsl("Could not read the file!"));
|
||||
return true;
|
||||
}
|
||||
auto error = QJsonParseError{ 0, QJsonParseError::NoError };
|
||||
const auto document = QJsonDocument::fromJson(
|
||||
base::parse::stripComments(file.readAll()),
|
||||
&error);
|
||||
file.close();
|
||||
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
_errors.push_back(qsl("Failed to parse! Error: %2"
|
||||
).arg(error.errorString()));
|
||||
return true;
|
||||
} else if (!document.isArray()) {
|
||||
_errors.push_back(qsl("Failed to parse! Error: array expected"));
|
||||
return true;
|
||||
}
|
||||
const auto shortcuts = document.array();
|
||||
auto limit = kCountLimit;
|
||||
for (auto i = shortcuts.constBegin(), e = shortcuts.constEnd(); i != e; ++i) {
|
||||
if (!(*i).isObject()) {
|
||||
_errors.push_back(qsl("Bad entry! Error: object expected"));
|
||||
continue;
|
||||
}
|
||||
const auto entry = (*i).toObject();
|
||||
const auto keys = entry.constFind(qsl("keys"));
|
||||
const auto command = entry.constFind(qsl("command"));
|
||||
if (keys == entry.constEnd()
|
||||
|| command == entry.constEnd()
|
||||
|| !(*keys).isString()
|
||||
|| (!(*command).isString() && !(*command).isNull())) {
|
||||
_errors.push_back(qsl("Bad entry! "
|
||||
"{\"keys\": \"...\", \"command\": [ \"...\" | null ]} "
|
||||
"expected."));
|
||||
} else if ((*command).isNull()) {
|
||||
remove((*keys).toString());
|
||||
} else {
|
||||
const auto name = (*command).toString();
|
||||
const auto i = CommandByName.find(name);
|
||||
if (i != end(CommandByName)) {
|
||||
set((*keys).toString(), i->second);
|
||||
} else {
|
||||
LOG(("Shortcut Warning: "
|
||||
"could not find shortcut command handler '%1'"
|
||||
).arg(name));
|
||||
}
|
||||
}
|
||||
if (!--limit) {
|
||||
_errors.push_back(qsl("Too many entries! Limit is %1"
|
||||
).arg(kCountLimit));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Manager::fillDefaults() {
|
||||
set(qsl("ctrl+w"), Command::Close);
|
||||
set(qsl("ctrl+f4"), Command::Close);
|
||||
set(qsl("ctrl+l"), Command::Lock);
|
||||
set(qsl("ctrl+m"), Command::Minimize);
|
||||
set(qsl("ctrl+q"), Command::Quit);
|
||||
|
||||
set(qsl("media play"), Command::MediaPlay);
|
||||
set(qsl("media pause"), Command::MediaPause);
|
||||
set(qsl("toggle media play/pause"), Command::MediaPlayPause);
|
||||
set(qsl("media stop"), Command::MediaStop);
|
||||
set(qsl("media previous"), Command::MediaPrevious);
|
||||
set(qsl("media next"), Command::MediaNext);
|
||||
|
||||
set(qsl("ctrl+f"), Command::Search);
|
||||
set(qsl("search"), Command::Search);
|
||||
set(qsl("find"), Command::Search);
|
||||
|
||||
set(qsl("ctrl+pgdown"), Command::ChatNext);
|
||||
set(qsl("alt+down"), Command::ChatNext);
|
||||
set(qsl("ctrl+pgup"), Command::ChatPrevious);
|
||||
set(qsl("alt+up"), Command::ChatPrevious);
|
||||
if (cPlatform() == dbipMac || cPlatform() == dbipMacOld) {
|
||||
set(qsl("meta+tab"), Command::ChatNext);
|
||||
set(qsl("meta+shift+tab"), Command::ChatPrevious);
|
||||
set(qsl("meta+backtab"), Command::ChatPrevious);
|
||||
} else {
|
||||
set(qsl("ctrl+tab"), Command::ChatNext);
|
||||
set(qsl("ctrl+shift+tab"), Command::ChatPrevious);
|
||||
set(qsl("ctrl+backtab"), Command::ChatPrevious);
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::writeDefaultFile() {
|
||||
auto file = QFile(DefaultFilePath());
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
return;
|
||||
}
|
||||
const char *defaultHeader = R"HEADER(
|
||||
// This is a list of default shortcuts for Telegram Desktop
|
||||
// Please don't modify it, its content is not used in any way
|
||||
// You can place your own shortcuts in the 'shortcuts-custom.json' file
|
||||
|
||||
)HEADER";
|
||||
file.write(defaultHeader);
|
||||
|
||||
auto shortcuts = QJsonArray();
|
||||
auto version = QJsonObject();
|
||||
version.insert(qsl("version"), QString::number(AppVersion));
|
||||
shortcuts.push_back(version);
|
||||
|
||||
for (const auto &[sequence, shortcut] : _shortcuts) {
|
||||
const auto i = _commandByShortcutId.find(shortcut->id());
|
||||
if (i != end(_commandByShortcutId)) {
|
||||
const auto j = CommandNames.find(i->second);
|
||||
if (j != end(CommandNames)) {
|
||||
QJsonObject entry;
|
||||
entry.insert(qsl("keys"), sequence.toString().toLower());
|
||||
entry.insert(qsl("command"), j->second);
|
||||
shortcuts.append(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto document = QJsonDocument();
|
||||
document.setArray(shortcuts);
|
||||
file.write(document.toJson(QJsonDocument::Indented));
|
||||
}
|
||||
|
||||
void Manager::set(const QString &keys, Command command) {
|
||||
if (keys.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto result = QKeySequence(keys, QKeySequence::PortableText);
|
||||
if (result.isEmpty()) {
|
||||
_errors.push_back(qsl("Could not derive key sequence '%1'!"
|
||||
).arg(keys));
|
||||
return;
|
||||
}
|
||||
auto shortcut = base::make_unique_q<QShortcut>(
|
||||
result,
|
||||
Messenger::Instance().getActiveWindow(),
|
||||
nullptr,
|
||||
nullptr,
|
||||
Qt::ApplicationShortcut);
|
||||
if (AutoRepeatCommands.contains(command)) {
|
||||
shortcut->setAutoRepeat(false);
|
||||
}
|
||||
const auto isMediaShortcut = MediaCommands.contains(command);
|
||||
if (isMediaShortcut) {
|
||||
shortcut->setEnabled(false);
|
||||
}
|
||||
const auto id = shortcut->id();
|
||||
if (!id) {
|
||||
_errors.push_back(qsl("Could not create shortcut '%1'!").arg(keys));
|
||||
return;
|
||||
}
|
||||
auto i = _shortcuts.find(result);
|
||||
if (i == end(_shortcuts)) {
|
||||
i = _shortcuts.emplace(result, std::move(shortcut)).first;
|
||||
} else {
|
||||
unregister(std::exchange(i->second, std::move(shortcut)));
|
||||
}
|
||||
_commandByShortcutId.emplace(id, command);
|
||||
if (isMediaShortcut) {
|
||||
_mediaShortcuts.emplace(i->second.get());
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::remove(const QString &keys) {
|
||||
if (keys.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto result = QKeySequence(keys, QKeySequence::PortableText);
|
||||
if (result.isEmpty()) {
|
||||
_errors.push_back(qsl("Could not derive key sequence '%1'!"
|
||||
).arg(keys));
|
||||
return;
|
||||
}
|
||||
const auto i = _shortcuts.find(result);
|
||||
if (i != end(_shortcuts)) {
|
||||
unregister(std::move(i->second));
|
||||
_shortcuts.erase(i);
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::unregister(base::unique_qptr<QShortcut> shortcut) {
|
||||
if (shortcut) {
|
||||
_commandByShortcutId.erase(shortcut->id());
|
||||
_mediaShortcuts.erase(shortcut.get());
|
||||
}
|
||||
}
|
||||
|
||||
Manager Data;
|
||||
|
||||
} // namespace
|
||||
|
||||
Request::Request(Command command) : _command(command) {
|
||||
}
|
||||
|
||||
bool Request::check(Command command, int priority) {
|
||||
if (_command == command && priority > _handlerPriority) {
|
||||
_handlerPriority = priority;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Request::handle(FnMut<bool()> handler) {
|
||||
_handler = std::move(handler);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Launch(Command command) {
|
||||
auto request = Request(command);
|
||||
RequestsStream.fire(&request);
|
||||
return request._handler ? request._handler() : false;
|
||||
}
|
||||
|
||||
rpl::producer<not_null<Request*>> Requests() {
|
||||
return RequestsStream.events();
|
||||
}
|
||||
|
||||
void Start() {
|
||||
Assert(Global::started());
|
||||
|
||||
Data.fill();
|
||||
}
|
||||
|
||||
const QStringList &Errors() {
|
||||
return Data.errors();
|
||||
}
|
||||
|
||||
bool HandleEvent(not_null<QShortcutEvent*> event) {
|
||||
if (const auto command = Data.lookup(event->shortcutId())) {
|
||||
return Launch(*command);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void EnableMediaShortcuts() {
|
||||
Data.toggleMedia(true);
|
||||
Platform::SetWatchingMediaKeys(true);
|
||||
}
|
||||
|
||||
void DisableMediaShortcuts() {
|
||||
Data.toggleMedia(false);
|
||||
Platform::SetWatchingMediaKeys(false);
|
||||
}
|
||||
|
||||
void Finish() {
|
||||
Data.clear();
|
||||
}
|
||||
|
||||
} // namespace Shortcuts
|
64
Telegram/SourceFiles/core/shortcuts.h
Normal file
64
Telegram/SourceFiles/core/shortcuts.h
Normal file
@ -0,0 +1,64 @@
|
||||
/*
|
||||
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
|
||||
|
||||
namespace Shortcuts {
|
||||
|
||||
enum class Command {
|
||||
Close,
|
||||
Lock,
|
||||
Minimize,
|
||||
Quit,
|
||||
|
||||
MediaPlay,
|
||||
MediaPause,
|
||||
MediaPlayPause,
|
||||
MediaStop,
|
||||
MediaPrevious,
|
||||
MediaNext,
|
||||
|
||||
Search,
|
||||
|
||||
ChatPrevious,
|
||||
ChatNext,
|
||||
};
|
||||
|
||||
bool Launch(Command command);
|
||||
|
||||
class Request {
|
||||
public:
|
||||
bool check(Command command, int priority = 0);
|
||||
bool handle(FnMut<bool()> handler);
|
||||
|
||||
private:
|
||||
explicit Request(Command command);
|
||||
|
||||
Command _command;
|
||||
int _handlerPriority = -1;
|
||||
FnMut<bool()> _handler;
|
||||
|
||||
friend bool Launch(Command command);
|
||||
|
||||
};
|
||||
|
||||
rpl::producer<not_null<Request*>> Requests();
|
||||
|
||||
void Start();
|
||||
void Finish();
|
||||
|
||||
bool HandleEvent(not_null<QShortcutEvent*> event);
|
||||
|
||||
const QStringList &Errors();
|
||||
|
||||
// Media shortcuts are not enabled by default, because other
|
||||
// applications also use them. They are enabled only when
|
||||
// the in-app player is active and disabled back after.
|
||||
void EnableMediaShortcuts();
|
||||
void DisableMediaShortcuts();
|
||||
|
||||
} // namespace Shortcuts
|
@ -10,9 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/admin_log/history_admin_log_inner.h"
|
||||
#include "history/admin_log/history_admin_log_filter.h"
|
||||
#include "profile/profile_back_button.h"
|
||||
#include "styles/style_history.h"
|
||||
#include "styles/style_window.h"
|
||||
#include "styles/style_info.h"
|
||||
#include "core/shortcuts.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
#include "ui/widgets/shadow.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
@ -25,6 +23,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "base/timer.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "styles/style_history.h"
|
||||
#include "styles/style_window.h"
|
||||
#include "styles/style_info.h"
|
||||
|
||||
namespace AdminLog {
|
||||
|
||||
@ -262,6 +263,8 @@ Widget::Widget(QWidget *parent, not_null<Window::Controller*> controller, not_nu
|
||||
connect(_scroll, &Ui::ScrollArea::scrolled, this, [this] { onScroll(); });
|
||||
|
||||
_whatIsThis->setClickedCallback([=] { Ui::show(Box<InformBox>(lang(lng_admin_log_about_text))); });
|
||||
|
||||
setupShortcuts();
|
||||
}
|
||||
|
||||
void Widget::showFilter() {
|
||||
@ -317,12 +320,17 @@ void Widget::setInternalState(const QRect &geometry, not_null<SectionMemento*> m
|
||||
restoreState(memento);
|
||||
}
|
||||
|
||||
bool Widget::cmd_search() {
|
||||
if (!inFocusChain()) {
|
||||
return false;
|
||||
}
|
||||
_fixedBar->showSearch();
|
||||
return true;
|
||||
void Widget::setupShortcuts() {
|
||||
Shortcuts::Requests(
|
||||
) | rpl::start_with_next([=](not_null<Shortcuts::Request*> request) {
|
||||
using Command = Shortcuts::Command;
|
||||
if (isActiveWindow() && !Ui::isLayerShown() && inFocusChain()) {
|
||||
request->check(Command::Search, 1) && request->handle([=] {
|
||||
_fixedBar->showSearch();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
std::unique_ptr<Window::SectionMemento> Widget::createMemento() {
|
||||
|
@ -87,8 +87,6 @@ public:
|
||||
|
||||
void applyFilter(FilterValue &&value);
|
||||
|
||||
bool cmd_search() override;
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
@ -104,6 +102,7 @@ private:
|
||||
void updateAdaptiveLayout();
|
||||
void saveState(not_null<SectionMemento*> memento);
|
||||
void restoreState(not_null<SectionMemento*> memento);
|
||||
void setupShortcuts();
|
||||
|
||||
object_ptr<Ui::ScrollArea> _scroll;
|
||||
QPointer<InnerWidget> _inner;
|
||||
|
@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/history_service.h"
|
||||
#include "history/history_inner_widget.h"
|
||||
#include "core/event_filter.h"
|
||||
#include "core/shortcuts.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/shadow.h"
|
||||
@ -134,6 +135,7 @@ Widget::Widget(
|
||||
}, lifetime());
|
||||
|
||||
setupScrollDownButton();
|
||||
setupShortcuts();
|
||||
}
|
||||
|
||||
void Widget::setupScrollDownButton() {
|
||||
@ -295,13 +297,17 @@ void Widget::setInternalState(
|
||||
restoreState(memento);
|
||||
}
|
||||
|
||||
bool Widget::cmd_search() {
|
||||
if (!inFocusChain()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
App::main()->searchInChat(_feed);
|
||||
return true;
|
||||
void Widget::setupShortcuts() {
|
||||
Shortcuts::Requests(
|
||||
) | rpl::start_with_next([=](not_null<Shortcuts::Request*> request) {
|
||||
using Command = Shortcuts::Command;
|
||||
if (isActiveWindow() && !Ui::isLayerShown() && inFocusChain()) {
|
||||
request->check(Command::Search, 1) && request->handle([=] {
|
||||
App::main()->searchInChat(_feed);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
HistoryView::Context Widget::listContext() {
|
||||
|
@ -68,8 +68,6 @@ public:
|
||||
bool wheelEventFromFloatPlayer(QEvent *e) override;
|
||||
QRect rectForFloatPlayer() const override;
|
||||
|
||||
bool cmd_search() override;
|
||||
|
||||
// HistoryView::ListDelegate interface.
|
||||
HistoryView::Context listContext() override;
|
||||
void listScrollTo(int top) override;
|
||||
@ -122,6 +120,8 @@ private:
|
||||
void confirmDeleteSelected();
|
||||
void clearSelected();
|
||||
|
||||
void setupShortcuts();
|
||||
|
||||
not_null<Data::Feed*> _feed;
|
||||
object_ptr<Ui::ScrollArea> _scroll;
|
||||
QPointer<HistoryView::ListWidget> _inner;
|
||||
|
@ -72,6 +72,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "inline_bots/inline_results_widget.h"
|
||||
#include "chat_helpers/emoji_suggestions_widget.h"
|
||||
#include "core/crash_reports.h"
|
||||
#include "core/shortcuts.h"
|
||||
#include "support/support_common.h"
|
||||
#include "support/support_autocomplete.h"
|
||||
#include "dialogs/dialogs_key.h"
|
||||
@ -778,6 +779,7 @@ HistoryWidget::HistoryWidget(
|
||||
}, lifetime());
|
||||
|
||||
orderWidgets();
|
||||
setupShortcuts();
|
||||
}
|
||||
|
||||
void HistoryWidget::supportInitAutocomplete() {
|
||||
@ -1582,17 +1584,30 @@ void HistoryWidget::notify_migrateUpdated(PeerData *peer) {
|
||||
}
|
||||
}
|
||||
|
||||
bool HistoryWidget::cmd_search() {
|
||||
if (!inFocusChain() || !_history) return false;
|
||||
|
||||
App::main()->searchInChat(_history);
|
||||
return true;
|
||||
void HistoryWidget::setupShortcuts() {
|
||||
Shortcuts::Requests(
|
||||
) | rpl::start_with_next([=](not_null<Shortcuts::Request*> request) {
|
||||
using Command = Shortcuts::Command;
|
||||
if (isActiveWindow() && !Ui::isLayerShown() && _history) {
|
||||
if (inFocusChain()) {
|
||||
request->check(Command::Search) && request->handle([=] {
|
||||
App::main()->searchInChat(_history);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
request->check(Command::ChatPrevious) && request->handle([=] {
|
||||
return showPreviousChat();
|
||||
});
|
||||
request->check(Command::ChatNext) && request->handle([=] {
|
||||
return showNextChat();
|
||||
});
|
||||
}
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
bool HistoryWidget::cmd_next_chat() {
|
||||
if (!_history) {
|
||||
return false;
|
||||
}
|
||||
bool HistoryWidget::showNextChat() {
|
||||
Expects(_history != nullptr);
|
||||
|
||||
const auto next = App::main()->chatListEntryAfter(
|
||||
Dialogs::RowDescriptor(
|
||||
_history,
|
||||
@ -1611,10 +1626,9 @@ bool HistoryWidget::cmd_next_chat() {
|
||||
return jumpToDialogRow(to);
|
||||
}
|
||||
|
||||
bool HistoryWidget::cmd_previous_chat() {
|
||||
if (!_history) {
|
||||
return false;
|
||||
}
|
||||
bool HistoryWidget::showPreviousChat() {
|
||||
Expects(_history != nullptr);
|
||||
|
||||
const auto previous = App::main()->chatListEntryBefore(
|
||||
Dialogs::RowDescriptor(
|
||||
_history,
|
||||
|
@ -354,10 +354,6 @@ public:
|
||||
void notify_userIsBotChanged(UserData *user);
|
||||
void notify_migrateUpdated(PeerData *peer);
|
||||
|
||||
bool cmd_search();
|
||||
bool cmd_next_chat();
|
||||
bool cmd_previous_chat();
|
||||
|
||||
~HistoryWidget();
|
||||
|
||||
protected:
|
||||
@ -570,6 +566,10 @@ private:
|
||||
}
|
||||
bool jumpToDialogRow(const Dialogs::RowDescriptor &to);
|
||||
|
||||
void setupShortcuts();
|
||||
bool showNextChat();
|
||||
bool showPreviousChat();
|
||||
|
||||
MsgId _replyToId = 0;
|
||||
Text _replyToName;
|
||||
int _replyToNameVersion = 0;
|
||||
|
@ -17,9 +17,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "storage/storage_shared_media.h"
|
||||
#include "mainwidget.h"
|
||||
#include "mainwindow.h"
|
||||
#include "shortcuts.h"
|
||||
#include "auth_session.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "core/shortcuts.h"
|
||||
#include "ui/special_buttons.h"
|
||||
#include "ui/unread_badge.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
|
@ -61,7 +61,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/download_path_box.h"
|
||||
#include "boxes/connection_box.h"
|
||||
#include "storage/localstorage.h"
|
||||
#include "shortcuts.h"
|
||||
#include "media/media_audio.h"
|
||||
#include "media/player/media_player_panel.h"
|
||||
#include "media/player/media_player_widget.h"
|
||||
@ -79,6 +78,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "mtproto/dc_options.h"
|
||||
#include "core/file_utilities.h"
|
||||
#include "core/update_checker.h"
|
||||
#include "core/shortcuts.h"
|
||||
#include "calls/calls_instance.h"
|
||||
#include "calls/calls_top_bar.h"
|
||||
#include "export/export_settings.h"
|
||||
@ -621,24 +621,6 @@ void MainWidget::notify_historyMuteUpdated(History *history) {
|
||||
_dialogs->notify_historyMuteUpdated(history);
|
||||
}
|
||||
|
||||
bool MainWidget::cmd_search() {
|
||||
if (Ui::isLayerShown() || !isActiveWindow()) return false;
|
||||
if (_mainSection) {
|
||||
return _mainSection->cmd_search();
|
||||
}
|
||||
return _history->cmd_search();
|
||||
}
|
||||
|
||||
bool MainWidget::cmd_next_chat() {
|
||||
if (Ui::isLayerShown() || !isActiveWindow()) return false;
|
||||
return _history->cmd_next_chat();
|
||||
}
|
||||
|
||||
bool MainWidget::cmd_previous_chat() {
|
||||
if (Ui::isLayerShown() || !isActiveWindow()) return false;
|
||||
return _history->cmd_previous_chat();
|
||||
}
|
||||
|
||||
void MainWidget::noHider(HistoryHider *destroyed) {
|
||||
if (_hider == destroyed) {
|
||||
_hider = nullptr;
|
||||
@ -1208,7 +1190,7 @@ void MainWidget::closeBothPlayers() {
|
||||
Media::Player::instance()->stop(AudioMsgId::Type::Voice);
|
||||
Media::Player::instance()->stop(AudioMsgId::Type::Song);
|
||||
|
||||
Shortcuts::disableMediaShortcuts();
|
||||
Shortcuts::DisableMediaShortcuts();
|
||||
}
|
||||
|
||||
void MainWidget::createPlayer() {
|
||||
@ -1230,7 +1212,7 @@ void MainWidget::createPlayer() {
|
||||
if (_a_show.animating()) {
|
||||
_player->show(anim::type::instant);
|
||||
_player->setVisible(false);
|
||||
Shortcuts::enableMediaShortcuts();
|
||||
Shortcuts::EnableMediaShortcuts();
|
||||
} else {
|
||||
_player->hide(anim::type::instant);
|
||||
}
|
||||
@ -1240,7 +1222,7 @@ void MainWidget::createPlayer() {
|
||||
_player->show(anim::type::normal);
|
||||
_playerHeight = _contentScrollAddToY = _player->contentHeight();
|
||||
updateControlsGeometry();
|
||||
Shortcuts::enableMediaShortcuts();
|
||||
Shortcuts::EnableMediaShortcuts();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -314,10 +314,6 @@ public:
|
||||
void notify_migrateUpdated(PeerData *peer);
|
||||
void notify_historyMuteUpdated(History *history);
|
||||
|
||||
bool cmd_search();
|
||||
bool cmd_next_chat();
|
||||
bool cmd_previous_chat();
|
||||
|
||||
~MainWidget();
|
||||
|
||||
signals:
|
||||
|
@ -20,7 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "lang/lang_cloud_manager.h"
|
||||
#include "lang/lang_instance.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "shortcuts.h"
|
||||
#include "core/shortcuts.h"
|
||||
#include "messenger.h"
|
||||
#include "auth_session.h"
|
||||
#include "application.h"
|
||||
|
@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/history_item.h"
|
||||
#include "data/data_media_types.h"
|
||||
#include "window/window_controller.h"
|
||||
#include "core/shortcuts.h"
|
||||
#include "messenger.h"
|
||||
#include "mainwindow.h"
|
||||
#include "auth_session.h"
|
||||
@ -75,6 +76,8 @@ Instance::Instance()
|
||||
Messenger::Instance().authSessionChanged(),
|
||||
handleAuthSessionChange);
|
||||
handleAuthSessionChange();
|
||||
|
||||
setupShortcuts();
|
||||
}
|
||||
|
||||
AudioMsgId::Type Instance::getActiveType() const {
|
||||
@ -494,5 +497,36 @@ void Instance::handleLogout() {
|
||||
_usePanelPlayer.notify(false, true);
|
||||
}
|
||||
|
||||
void Instance::setupShortcuts() {
|
||||
Shortcuts::Requests(
|
||||
) | rpl::start_with_next([=](not_null<Shortcuts::Request*> request) {
|
||||
using Command = Shortcuts::Command;
|
||||
request->check(Command::MediaPlay) && request->handle([=] {
|
||||
play();
|
||||
return true;
|
||||
});
|
||||
request->check(Command::MediaPause) && request->handle([=] {
|
||||
pause();
|
||||
return true;
|
||||
});
|
||||
request->check(Command::MediaPlayPause) && request->handle([=] {
|
||||
playPause();
|
||||
return true;
|
||||
});
|
||||
request->check(Command::MediaStop) && request->handle([=] {
|
||||
stop();
|
||||
return true;
|
||||
});
|
||||
request->check(Command::MediaPrevious) && request->handle([=] {
|
||||
previous();
|
||||
return true;
|
||||
});
|
||||
request->check(Command::MediaNext) && request->handle([=] {
|
||||
next();
|
||||
return true;
|
||||
});
|
||||
}, _lifetime);
|
||||
}
|
||||
|
||||
} // namespace Player
|
||||
} // namespace Media
|
||||
|
@ -127,6 +127,8 @@ private:
|
||||
Instance();
|
||||
friend void start();
|
||||
|
||||
void setupShortcuts();
|
||||
|
||||
using SharedMediaType = Storage::SharedMediaType;
|
||||
using SliceKey = SparseIdsMergedSlice::Key;
|
||||
struct Data {
|
||||
@ -202,6 +204,8 @@ private:
|
||||
base::Observable<AudioMsgId::Type> _trackChangedNotifier;
|
||||
base::Observable<AudioMsgId::Type> _repeatChangedNotifier;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Clip
|
||||
|
@ -12,13 +12,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_session.h"
|
||||
#include "base/timer.h"
|
||||
#include "core/update_checker.h"
|
||||
#include "core/shortcuts.h"
|
||||
#include "storage/localstorage.h"
|
||||
#include "platform/platform_specific.h"
|
||||
#include "mainwindow.h"
|
||||
#include "dialogs/dialogs_entry.h"
|
||||
#include "history/history.h"
|
||||
#include "application.h"
|
||||
#include "shortcuts.h"
|
||||
#include "auth_session.h"
|
||||
#include "apiwrap.h"
|
||||
#include "calls/calls_instance.h"
|
||||
@ -143,8 +143,7 @@ Messenger::Messenger(not_null<Core::Launcher*> launcher)
|
||||
|
||||
DEBUG_LOG(("Application Info: window created..."));
|
||||
|
||||
Shortcuts::start();
|
||||
|
||||
startShortcuts();
|
||||
App::initMedia();
|
||||
|
||||
Local::ReadMapState state = Local::readMap(QByteArray());
|
||||
@ -172,11 +171,8 @@ Messenger::Messenger(not_null<Core::Launcher*> launcher)
|
||||
|
||||
_window->updateIsActive(Global::OnlineFocusTimeout());
|
||||
|
||||
if (!Shortcuts::errors().isEmpty()) {
|
||||
const QStringList &errors(Shortcuts::errors());
|
||||
for (QStringList::const_iterator i = errors.cbegin(), e = errors.cend(); i != e; ++i) {
|
||||
LOG(("Shortcuts Error: %1").arg(*i));
|
||||
}
|
||||
for (const auto &error : Shortcuts::Errors()) {
|
||||
LOG(("Shortcuts Error: %1").arg(error));
|
||||
}
|
||||
}
|
||||
|
||||
@ -252,8 +248,10 @@ bool Messenger::eventFilter(QObject *object, QEvent *e) {
|
||||
} break;
|
||||
|
||||
case QEvent::Shortcut: {
|
||||
DEBUG_LOG(("Shortcut event caught: %1").arg(static_cast<QShortcutEvent*>(e)->key().toString()));
|
||||
if (Shortcuts::launch(static_cast<QShortcutEvent*>(e)->shortcutId())) {
|
||||
const auto event = static_cast<QShortcutEvent*>(e);
|
||||
DEBUG_LOG(("Shortcut event caught: %1"
|
||||
).arg(event->key().toString()));
|
||||
if (Shortcuts::HandleEvent(event)) {
|
||||
return true;
|
||||
}
|
||||
} break;
|
||||
@ -1039,7 +1037,7 @@ Messenger::~Messenger() {
|
||||
_mtproto.reset();
|
||||
_mtprotoForKeysDestroy.reset();
|
||||
|
||||
Shortcuts::finish();
|
||||
Shortcuts::Finish();
|
||||
|
||||
Ui::Emoji::Clear();
|
||||
|
||||
@ -1217,3 +1215,29 @@ void Messenger::quitDelayed() {
|
||||
_private->quitTimer.callOnce(kQuitPreventTimeoutMs);
|
||||
}
|
||||
}
|
||||
|
||||
void Messenger::startShortcuts() {
|
||||
Shortcuts::Start();
|
||||
|
||||
Shortcuts::Requests(
|
||||
) | rpl::start_with_next([=](not_null<Shortcuts::Request*> request) {
|
||||
using Command = Shortcuts::Command;
|
||||
request->check(Command::Quit) && request->handle([] {
|
||||
App::quit();
|
||||
return true;
|
||||
});
|
||||
request->check(Command::Lock) && request->handle([=] {
|
||||
if (!passcodeLocked() && Global::LocalPasscode()) {
|
||||
lockByPasscode();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
request->check(Command::Minimize) && request->handle([=] {
|
||||
return minimizeActiveWindow();
|
||||
});
|
||||
request->check(Command::Close) && request->handle([=] {
|
||||
return closeActiveWindow();
|
||||
});
|
||||
}, _lifetime);
|
||||
}
|
||||
|
@ -220,6 +220,7 @@ public slots:
|
||||
private:
|
||||
void destroyMtpKeys(MTP::AuthKeysList &&keys);
|
||||
void startLocalStorage();
|
||||
void startShortcuts();
|
||||
|
||||
friend void App::quit();
|
||||
static void QuitAttempt();
|
||||
@ -277,4 +278,6 @@ private:
|
||||
};
|
||||
std::vector<LeaveSubscription> _leaveSubscriptions;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
@ -1,446 +0,0 @@
|
||||
/*
|
||||
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 "shortcuts.h"
|
||||
|
||||
#include "mainwindow.h"
|
||||
#include "mainwidget.h"
|
||||
#include "messenger.h"
|
||||
#include "media/player/media_player_instance.h"
|
||||
#include "platform/platform_specific.h"
|
||||
#include "base/parse_helper.h"
|
||||
|
||||
namespace ShortcutCommands {
|
||||
|
||||
using Handler = bool(*)();
|
||||
|
||||
bool lock_telegram() {
|
||||
if (!Messenger::Instance().passcodeLocked()
|
||||
&& Global::LocalPasscode()) {
|
||||
Messenger::Instance().lockByPasscode();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool minimize_telegram() {
|
||||
return Messenger::Instance().minimizeActiveWindow();
|
||||
}
|
||||
|
||||
bool close_telegram() {
|
||||
return Messenger::Instance().closeActiveWindow();
|
||||
}
|
||||
|
||||
bool quit_telegram() {
|
||||
App::quit();
|
||||
return true;
|
||||
}
|
||||
|
||||
//void start_stop_recording() {
|
||||
|
||||
//}
|
||||
|
||||
//void cancel_recording() {
|
||||
|
||||
//}
|
||||
|
||||
bool media_play() {
|
||||
Media::Player::instance()->play();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool media_pause() {
|
||||
Media::Player::instance()->pause(AudioMsgId::Type::Song);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool media_playpause() {
|
||||
Media::Player::instance()->playPause();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool media_stop() {
|
||||
Media::Player::instance()->stop();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool media_previous() {
|
||||
Media::Player::instance()->previous();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool media_next() {
|
||||
Media::Player::instance()->next();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool search() {
|
||||
if (auto m = App::main()) {
|
||||
return m->cmd_search();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool previous_chat() {
|
||||
if (auto m = App::main()) {
|
||||
return m->cmd_previous_chat();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool next_chat() {
|
||||
if (auto m = App::main()) {
|
||||
return m->cmd_next_chat();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// other commands here
|
||||
|
||||
} // namespace ShortcutCommands
|
||||
|
||||
inline bool qMapLessThanKey(const ShortcutCommands::Handler &a, const ShortcutCommands::Handler &b) {
|
||||
return a < b;
|
||||
}
|
||||
|
||||
namespace Shortcuts {
|
||||
|
||||
struct DataStruct;
|
||||
DataStruct *DataPtr = nullptr;
|
||||
|
||||
namespace {
|
||||
|
||||
void createCommand(const QString &command, ShortcutCommands::Handler handler);
|
||||
QKeySequence setShortcut(const QString &keys, const QString &command);
|
||||
void destroyShortcut(QShortcut *shortcut);
|
||||
|
||||
} // namespace
|
||||
|
||||
struct DataStruct {
|
||||
DataStruct() {
|
||||
Assert(DataPtr == nullptr);
|
||||
DataPtr = this;
|
||||
|
||||
if (autoRepeatCommands.isEmpty()) {
|
||||
autoRepeatCommands.insert(qsl("media_previous"));
|
||||
autoRepeatCommands.insert(qsl("media_next"));
|
||||
autoRepeatCommands.insert(qsl("next_chat"));
|
||||
autoRepeatCommands.insert(qsl("previous_chat"));
|
||||
}
|
||||
|
||||
if (mediaCommands.isEmpty()) {
|
||||
mediaCommands.insert(qsl("media_play"));
|
||||
mediaCommands.insert(qsl("media_playpause"));
|
||||
mediaCommands.insert(qsl("media_play"));
|
||||
mediaCommands.insert(qsl("media_stop"));
|
||||
mediaCommands.insert(qsl("media_previous"));
|
||||
mediaCommands.insert(qsl("media_next"));
|
||||
}
|
||||
|
||||
#define DeclareAlias(keys, command) setShortcut(qsl(keys), qsl(#command))
|
||||
#define DeclareCommand(keys, command) createCommand(qsl(#command), ShortcutCommands::command); DeclareAlias(keys, command)
|
||||
|
||||
DeclareCommand("ctrl+w", close_telegram);
|
||||
DeclareAlias("ctrl+f4", close_telegram);
|
||||
DeclareCommand("ctrl+l", lock_telegram);
|
||||
DeclareCommand("ctrl+m", minimize_telegram);
|
||||
DeclareCommand("ctrl+q", quit_telegram);
|
||||
|
||||
//DeclareCommand("ctrl+r", start_stop_recording);
|
||||
//DeclareCommand("ctrl+shift+r", cancel_recording);
|
||||
//DeclareCommand("media record", start_stop_recording);
|
||||
|
||||
DeclareCommand("media play", media_play);
|
||||
DeclareCommand("media pause", media_pause);
|
||||
DeclareCommand("toggle media play/pause", media_playpause);
|
||||
DeclareCommand("media stop", media_stop);
|
||||
DeclareCommand("media previous", media_previous);
|
||||
DeclareCommand("media next", media_next);
|
||||
|
||||
DeclareCommand("ctrl+f", search);
|
||||
DeclareAlias("search", search);
|
||||
DeclareAlias("find", search);
|
||||
|
||||
DeclareCommand("ctrl+pgdown", next_chat);
|
||||
DeclareAlias("alt+down", next_chat);
|
||||
DeclareCommand("ctrl+pgup", previous_chat);
|
||||
DeclareAlias("alt+up", previous_chat);
|
||||
if (cPlatform() == dbipMac || cPlatform() == dbipMacOld) {
|
||||
DeclareAlias("meta+tab", next_chat);
|
||||
DeclareAlias("meta+shift+tab", previous_chat);
|
||||
DeclareAlias("meta+backtab", previous_chat);
|
||||
} else {
|
||||
DeclareAlias("ctrl+tab", next_chat);
|
||||
DeclareAlias("ctrl+shift+tab", previous_chat);
|
||||
DeclareAlias("ctrl+backtab", previous_chat);
|
||||
}
|
||||
|
||||
// other commands here
|
||||
|
||||
#undef DeclareCommand
|
||||
#undef DeclareAlias
|
||||
}
|
||||
QStringList errors;
|
||||
|
||||
QMap<QString, ShortcutCommands::Handler> commands;
|
||||
QMap<ShortcutCommands::Handler, QString> commandnames;
|
||||
|
||||
QMap<QKeySequence, QShortcut*> sequences;
|
||||
QMap<int, ShortcutCommands::Handler> handlers;
|
||||
|
||||
QSet<QShortcut*> mediaShortcuts;
|
||||
QSet<QString> autoRepeatCommands;
|
||||
QSet<QString> mediaCommands;
|
||||
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
void createCommand(const QString &command, ShortcutCommands::Handler handler) {
|
||||
Assert(DataPtr != nullptr);
|
||||
Assert(!command.isEmpty());
|
||||
|
||||
DataPtr->commands.insert(command, handler);
|
||||
DataPtr->commandnames.insert(handler, command);
|
||||
}
|
||||
|
||||
QKeySequence setShortcut(const QString &keys, const QString &command) {
|
||||
Assert(DataPtr != nullptr);
|
||||
Assert(!command.isEmpty());
|
||||
if (keys.isEmpty()) return QKeySequence();
|
||||
|
||||
QKeySequence seq(keys, QKeySequence::PortableText);
|
||||
if (seq.isEmpty()) {
|
||||
DataPtr->errors.push_back(qsl("Could not derive key sequence '%1'!").arg(keys));
|
||||
} else {
|
||||
auto it = DataPtr->commands.constFind(command);
|
||||
if (it == DataPtr->commands.cend()) {
|
||||
LOG(("Warning: could not find shortcut command handler '%1'").arg(command));
|
||||
} else {
|
||||
auto shortcut = std::make_unique<QShortcut>(seq, Messenger::Instance().getActiveWindow(), nullptr, nullptr, Qt::ApplicationShortcut);
|
||||
if (!DataPtr->autoRepeatCommands.contains(command)) {
|
||||
shortcut->setAutoRepeat(false);
|
||||
}
|
||||
auto isMediaShortcut = DataPtr->mediaCommands.contains(command);
|
||||
if (isMediaShortcut) {
|
||||
shortcut->setEnabled(false);
|
||||
}
|
||||
int shortcutId = shortcut->id();
|
||||
if (!shortcutId) {
|
||||
DataPtr->errors.push_back(qsl("Could not create shortcut '%1'!").arg(keys));
|
||||
} else {
|
||||
auto seqIt = DataPtr->sequences.find(seq);
|
||||
if (seqIt == DataPtr->sequences.cend()) {
|
||||
seqIt = DataPtr->sequences.insert(seq, shortcut.release());
|
||||
} else {
|
||||
auto oldShortcut = seqIt.value();
|
||||
seqIt.value() = shortcut.release();
|
||||
destroyShortcut(oldShortcut);
|
||||
}
|
||||
DataPtr->handlers.insert(shortcutId, it.value());
|
||||
if (isMediaShortcut) {
|
||||
DataPtr->mediaShortcuts.insert(seqIt.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return seq;
|
||||
}
|
||||
|
||||
QKeySequence removeShortcut(const QString &keys) {
|
||||
Assert(DataPtr != nullptr);
|
||||
if (keys.isEmpty()) return QKeySequence();
|
||||
|
||||
QKeySequence seq(keys, QKeySequence::PortableText);
|
||||
if (seq.isEmpty()) {
|
||||
DataPtr->errors.push_back(qsl("Could not derive key sequence '%1'!").arg(keys));
|
||||
} else {
|
||||
auto seqIt = DataPtr->sequences.find(seq);
|
||||
if (seqIt != DataPtr->sequences.cend()) {
|
||||
auto shortcut = seqIt.value();
|
||||
DataPtr->sequences.erase(seqIt);
|
||||
destroyShortcut(shortcut);
|
||||
}
|
||||
}
|
||||
return seq;
|
||||
}
|
||||
|
||||
void destroyShortcut(QShortcut *shortcut) {
|
||||
Assert(DataPtr != nullptr);
|
||||
|
||||
DataPtr->handlers.remove(shortcut->id());
|
||||
DataPtr->mediaShortcuts.remove(shortcut);
|
||||
delete shortcut;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void start() {
|
||||
Assert(Global::started());
|
||||
|
||||
new DataStruct();
|
||||
|
||||
// write default shortcuts to a file if they are not there already
|
||||
bool defaultValid = false;
|
||||
QFile defaultFile(cWorkingDir() + qsl("tdata/shortcuts-default.json"));
|
||||
if (defaultFile.open(QIODevice::ReadOnly)) {
|
||||
QJsonParseError error = { 0, QJsonParseError::NoError };
|
||||
QJsonDocument doc = QJsonDocument::fromJson(base::parse::stripComments(defaultFile.readAll()), &error);
|
||||
defaultFile.close();
|
||||
|
||||
if (error.error == QJsonParseError::NoError && doc.isArray()) {
|
||||
QJsonArray shortcuts(doc.array());
|
||||
if (!shortcuts.isEmpty() && (*shortcuts.constBegin()).isObject()) {
|
||||
QJsonObject versionObject((*shortcuts.constBegin()).toObject());
|
||||
QJsonObject::const_iterator version = versionObject.constFind(qsl("version"));
|
||||
if (version != versionObject.constEnd() && (*version).isString() && (*version).toString() == QString::number(AppVersion)) {
|
||||
defaultValid = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!defaultValid && defaultFile.open(QIODevice::WriteOnly)) {
|
||||
const char *defaultHeader = "\
|
||||
// This is a list of default shortcuts for Telegram Desktop\n\
|
||||
// Please don't modify it, its content is not used in any way\n\
|
||||
// You can place your own shortcuts in the 'shortcuts-custom.json' file\n\n";
|
||||
defaultFile.write(defaultHeader);
|
||||
|
||||
QJsonArray shortcuts;
|
||||
|
||||
QJsonObject version;
|
||||
version.insert(qsl("version"), QString::number(AppVersion));
|
||||
shortcuts.push_back(version);
|
||||
|
||||
for (auto i = DataPtr->sequences.cbegin(), e = DataPtr->sequences.cend(); i != e; ++i) {
|
||||
auto h = DataPtr->handlers.constFind(i.value()->id());
|
||||
if (h != DataPtr->handlers.cend()) {
|
||||
auto n = DataPtr->commandnames.constFind(h.value());
|
||||
if (n != DataPtr->commandnames.cend()) {
|
||||
QJsonObject entry;
|
||||
entry.insert(qsl("keys"), i.key().toString().toLower());
|
||||
entry.insert(qsl("command"), n.value());
|
||||
shortcuts.append(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QJsonDocument doc;
|
||||
doc.setArray(shortcuts);
|
||||
defaultFile.write(doc.toJson(QJsonDocument::Indented));
|
||||
defaultFile.close();
|
||||
}
|
||||
|
||||
// read custom shortcuts from file if it exists or write an empty custom shortcuts file
|
||||
QFile customFile(cWorkingDir() + qsl("tdata/shortcuts-custom.json"));
|
||||
if (customFile.exists()) {
|
||||
if (customFile.open(QIODevice::ReadOnly)) {
|
||||
QJsonParseError error = { 0, QJsonParseError::NoError };
|
||||
QJsonDocument doc = QJsonDocument::fromJson(base::parse::stripComments(customFile.readAll()), &error);
|
||||
customFile.close();
|
||||
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
DataPtr->errors.push_back(qsl("Failed to parse! Error: %2").arg(error.errorString()));
|
||||
} else if (!doc.isArray()) {
|
||||
DataPtr->errors.push_back(qsl("Failed to parse! Error: array expected"));
|
||||
} else {
|
||||
QJsonArray shortcuts = doc.array();
|
||||
int limit = ShortcutsCountLimit;
|
||||
for (QJsonArray::const_iterator i = shortcuts.constBegin(), e = shortcuts.constEnd(); i != e; ++i) {
|
||||
if (!(*i).isObject()) {
|
||||
DataPtr->errors.push_back(qsl("Bad entry! Error: object expected"));
|
||||
} else {
|
||||
QKeySequence seq;
|
||||
QJsonObject entry((*i).toObject());
|
||||
QJsonObject::const_iterator keys = entry.constFind(qsl("keys")), command = entry.constFind(qsl("command"));
|
||||
if (keys == entry.constEnd() || command == entry.constEnd() || !(*keys).isString() || (!(*command).isString() && !(*command).isNull())) {
|
||||
DataPtr->errors.push_back(qsl("Bad entry! {\"keys\": \"...\", \"command\": [ \"...\" | null ]} expected"));
|
||||
} else if ((*command).isNull()) {
|
||||
seq = removeShortcut((*keys).toString());
|
||||
} else {
|
||||
seq = setShortcut((*keys).toString(), (*command).toString());
|
||||
}
|
||||
if (!--limit) {
|
||||
DataPtr->errors.push_back(qsl("Too many entries! Limit is %1").arg(ShortcutsCountLimit));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DataPtr->errors.push_back(qsl("Could not read the file!"));
|
||||
}
|
||||
if (!DataPtr->errors.isEmpty()) {
|
||||
DataPtr->errors.push_front(qsl("While reading file '%1'...").arg(customFile.fileName()));
|
||||
}
|
||||
} else if (customFile.open(QIODevice::WriteOnly)) {
|
||||
const char *customContent = "\
|
||||
// This is a list of your own shortcuts for Telegram Desktop\n\
|
||||
// You can see full list of commands in the 'shortcuts-default.json' file\n\
|
||||
// Place a null value instead of a command string to switch the shortcut off\n\n\
|
||||
[\n\
|
||||
// {\n\
|
||||
// \"command\": \"close_telegram\",\n\
|
||||
// \"keys\": \"ctrl+f4\"\n\
|
||||
// },\n\
|
||||
// {\n\
|
||||
// \"command\": \"quit_telegram\",\n\
|
||||
// \"keys\": \"ctrl+q\"\n\
|
||||
// }\n\
|
||||
]\n";
|
||||
customFile.write(customContent);
|
||||
customFile.close();
|
||||
}
|
||||
}
|
||||
|
||||
const QStringList &errors() {
|
||||
Assert(DataPtr != nullptr);
|
||||
return DataPtr->errors;
|
||||
}
|
||||
|
||||
bool launch(int shortcutId) {
|
||||
Assert(DataPtr != nullptr);
|
||||
|
||||
auto it = DataPtr->handlers.constFind(shortcutId);
|
||||
if (it == DataPtr->handlers.cend()) {
|
||||
return false;
|
||||
}
|
||||
return (*it.value())();
|
||||
}
|
||||
|
||||
bool launch(const QString &command) {
|
||||
Assert(DataPtr != nullptr);
|
||||
|
||||
auto it = DataPtr->commands.constFind(command);
|
||||
if (it == DataPtr->commands.cend()) {
|
||||
return false;
|
||||
}
|
||||
return (*it.value())();
|
||||
}
|
||||
|
||||
void enableMediaShortcuts() {
|
||||
if (!DataPtr) return;
|
||||
for_const (auto shortcut, DataPtr->mediaShortcuts) {
|
||||
shortcut->setEnabled(true);
|
||||
}
|
||||
Platform::SetWatchingMediaKeys(true);
|
||||
}
|
||||
|
||||
void disableMediaShortcuts() {
|
||||
if (!DataPtr) return;
|
||||
for_const (auto shortcut, DataPtr->mediaShortcuts) {
|
||||
shortcut->setEnabled(false);
|
||||
}
|
||||
Platform::SetWatchingMediaKeys(false);
|
||||
}
|
||||
|
||||
void finish() {
|
||||
delete DataPtr;
|
||||
DataPtr = nullptr;
|
||||
}
|
||||
|
||||
} // namespace Shortcuts
|
@ -1,26 +0,0 @@
|
||||
/*
|
||||
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
|
||||
|
||||
namespace Shortcuts {
|
||||
|
||||
void start();
|
||||
const QStringList &errors();
|
||||
|
||||
bool launch(int shortcutId);
|
||||
bool launch(const QString &command);
|
||||
|
||||
// Media shortcuts are not enabled by default, because other
|
||||
// applications also use them. They are enabled only when
|
||||
// the in-app player is active and disabled back after.
|
||||
void enableMediaShortcuts();
|
||||
void disableMediaShortcuts();
|
||||
|
||||
void finish();
|
||||
|
||||
} // namespace Shortcuts
|
@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "support/support_common.h"
|
||||
|
||||
#include "shortcuts.h"
|
||||
#include "core/shortcuts.h"
|
||||
|
||||
namespace Support {
|
||||
|
||||
@ -36,10 +36,10 @@ Qt::KeyboardModifiers SkipSwitchModifiers() {
|
||||
void PerformSwitch(SwitchSettings value) {
|
||||
switch (value) {
|
||||
case SwitchSettings::Next:
|
||||
Shortcuts::launch("next_chat");
|
||||
Shortcuts::Launch(Shortcuts::Command::ChatNext);
|
||||
break;
|
||||
case SwitchSettings::Previous:
|
||||
Shortcuts::launch("previous_chat");
|
||||
Shortcuts::Launch(Shortcuts::Command::ChatPrevious);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -122,11 +122,6 @@ public:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Global shortcut handler. For now that ugly :(
|
||||
virtual bool cmd_search() {
|
||||
return false;
|
||||
}
|
||||
|
||||
static void PaintBackground(QWidget *widget, QPaintEvent *event);
|
||||
|
||||
protected:
|
||||
|
@ -122,6 +122,8 @@
|
||||
<(src_loc)/core/media_active_cache.h
|
||||
<(src_loc)/core/mime_type.cpp
|
||||
<(src_loc)/core/mime_type.h
|
||||
<(src_loc)/core/shortcuts.cpp
|
||||
<(src_loc)/core/shortcuts.h
|
||||
<(src_loc)/core/single_timer.cpp
|
||||
<(src_loc)/core/single_timer.h
|
||||
<(src_loc)/core/tl_help.h
|
||||
@ -766,8 +768,6 @@
|
||||
<(src_loc)/qt_static_plugins.cpp
|
||||
<(src_loc)/settings.cpp
|
||||
<(src_loc)/settings.h
|
||||
<(src_loc)/shortcuts.cpp
|
||||
<(src_loc)/shortcuts.h
|
||||
<(emoji_suggestions_loc)/emoji_suggestions.cpp
|
||||
<(emoji_suggestions_loc)/emoji_suggestions.h
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user