From 4304071d18940a6119165966462d9d17940c2c64 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Mon, 11 Apr 2022 11:34:56 +0300 Subject: [PATCH] Added ability to configure quick action on double click. --- Telegram/CMakeLists.txt | 2 + Telegram/Resources/langs/lang.strings | 2 + Telegram/SourceFiles/core/core_settings.cpp | 18 ++++- Telegram/SourceFiles/core/core_settings.h | 12 +++ .../data/data_message_reactions.cpp | 7 ++ .../SourceFiles/data/data_message_reactions.h | 2 + .../history/history_inner_widget.cpp | 23 +++++- .../history/history_inner_widget.h | 1 + .../history/view/history_view_list_widget.cpp | 23 +++++- .../history/view/history_view_list_widget.h | 1 + .../view/history_view_quick_action.cpp | 19 +++++ .../history/view/history_view_quick_action.h | 20 +++++ .../view/history_view_react_button.cpp | 12 +-- .../history/view/history_view_react_button.h | 9 ++- .../SourceFiles/settings/settings_chat.cpp | 76 +++++++++++++++++-- Telegram/SourceFiles/ui/chat/chat.style | 2 + 16 files changed, 212 insertions(+), 17 deletions(-) create mode 100644 Telegram/SourceFiles/history/view/history_view_quick_action.cpp create mode 100644 Telegram/SourceFiles/history/view/history_view_quick_action.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 2a594aa58..97b1a2df4 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -658,6 +658,8 @@ PRIVATE history/view/history_view_pinned_section.h history/view/history_view_pinned_tracker.cpp history/view/history_view_pinned_tracker.h + history/view/history_view_quick_action.cpp + history/view/history_view_quick_action.h history/view/history_view_react_animation.cpp history/view/history_view_react_animation.h history/view/history_view_react_button.cpp diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 860f96b02..c71691854 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -437,6 +437,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_settings_send_enter" = "Send by Enter"; "lng_settings_send_ctrlenter" = "Send by Ctrl+Enter"; "lng_settings_send_cmdenter" = "Send by Cmd+Enter"; +"lng_settings_chat_quick_action_reply" = "Reply with double click"; +"lng_settings_chat_quick_action_react" = "Send reaction with double click"; "lng_settings_section_filters" = "Folders"; diff --git a/Telegram/SourceFiles/core/core_settings.cpp b/Telegram/SourceFiles/core/core_settings.cpp index 0d1640288..c9fe5acf2 100644 --- a/Telegram/SourceFiles/core/core_settings.cpp +++ b/Telegram/SourceFiles/core/core_settings.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/core_settings.h" #include "boxes/send_files_box.h" +#include "history/view/history_view_quick_action.h" #include "ui/widgets/input_fields.h" #include "storage/serialize_common.h" #include "window/section_widget.h" @@ -238,7 +239,8 @@ QByteArray Settings::serialize() const { } stream - << qint32(_hardwareAcceleratedVideo ? 1 : 0); + << qint32(_hardwareAcceleratedVideo ? 1 : 0) + << qint32(_chatQuickAction); } return result; } @@ -329,6 +331,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) { qint32 accountsOrderCount = 0; std::vector accountsOrder; qint32 hardwareAcceleratedVideo = _hardwareAcceleratedVideo ? 1 : 0; + qint32 chatQuickAction = static_cast(_chatQuickAction); stream >> themesAccentColors; if (!stream.atEnd()) { @@ -513,6 +516,9 @@ void Settings::addFromSerialized(const QByteArray &serialized) { if (!stream.atEnd()) { stream >> hardwareAcceleratedVideo; } + if (!stream.atEnd()) { + stream >> chatQuickAction; + } if (stream.status() != QDataStream::Ok) { LOG(("App Error: " "Bad data for Core::Settings::constructFromSerialized()")); @@ -670,6 +676,16 @@ void Settings::addFromSerialized(const QByteArray &serialized) { } _macWarnBeforeQuit = (macWarnBeforeQuit == 1); _hardwareAcceleratedVideo = (hardwareAcceleratedVideo == 1); + { + using Quick = HistoryView::DoubleClickQuickAction; + const auto uncheckedChatQuickAction = static_cast( + chatQuickAction); + switch (uncheckedChatQuickAction) { + case Quick::None: + case Quick::Reply: + case Quick::React: _chatQuickAction = uncheckedChatQuickAction; break; + } + } } QString Settings::getSoundPath(const QString &key) const { diff --git a/Telegram/SourceFiles/core/core_settings.h b/Telegram/SourceFiles/core/core_settings.h index cc51ff3cc..b21704b14 100644 --- a/Telegram/SourceFiles/core/core_settings.h +++ b/Telegram/SourceFiles/core/core_settings.h @@ -20,6 +20,10 @@ namespace Ui { enum class InputSubmitSettings; } // namespace Ui +namespace HistoryView { +enum class DoubleClickQuickAction; +} // namespace HistoryView + namespace Window { enum class Column; } // namespace Window @@ -676,6 +680,12 @@ public: [[nodiscard]] bool macWarnBeforeQuit() const { return _macWarnBeforeQuit; } + void setChatQuickAction(HistoryView::DoubleClickQuickAction value) { + _chatQuickAction = value; + } + [[nodiscard]] HistoryView::DoubleClickQuickAction chatQuickAction() const { + return _chatQuickAction; + } [[nodiscard]] static bool ThirdColumnByDefault(); [[nodiscard]] static float64 DefaultDialogsWidthRatio(); @@ -783,6 +793,8 @@ private: bool _macWarnBeforeQuit = true; std::vector _accountsOrder; bool _hardwareAcceleratedVideo = true; + HistoryView::DoubleClickQuickAction _chatQuickAction = + HistoryView::DoubleClickQuickAction(); bool _tabbedReplacedWithInfo = false; // per-window rpl::event_stream _tabbedReplacedWithInfoValue; // per-window diff --git a/Telegram/SourceFiles/data/data_message_reactions.cpp b/Telegram/SourceFiles/data/data_message_reactions.cpp index 958b85bcf..4c5f1e41b 100644 --- a/Telegram/SourceFiles/data/data_message_reactions.cpp +++ b/Telegram/SourceFiles/data/data_message_reactions.cpp @@ -179,6 +179,13 @@ QImage Reactions::resolveImageFor( } image.setDevicePixelRatio(factor); }; + if (size == ImageSize::Settings) { + if (set.settings.isNull() && set.icon) { + resolve(set.settings, st::reactionSettingsImage); + crl::async([icon = std::move(set.icon)]{}); + } + return set.settings; + } if (set.bottomInfo.isNull() && set.icon) { resolve(set.bottomInfo, st::reactionInfoImage); resolve(set.inlineList, st::reactionInlineImage); diff --git a/Telegram/SourceFiles/data/data_message_reactions.h b/Telegram/SourceFiles/data/data_message_reactions.h index 0a587ea15..f428e1308 100644 --- a/Telegram/SourceFiles/data/data_message_reactions.h +++ b/Telegram/SourceFiles/data/data_message_reactions.h @@ -54,6 +54,7 @@ public: enum class ImageSize { BottomInfo, InlineList, + Settings, }; void preloadImageFor(const QString &emoji); void preloadAnimationsFor(const QString &emoji); @@ -77,6 +78,7 @@ private: struct ImageSet { QImage bottomInfo; QImage inlineList; + QImage settings; std::shared_ptr media; std::unique_ptr icon; bool fromAppearAnimation = false; diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 979652998..282cbbdce 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_service_message.h" #include "history/view/history_view_cursor_state.h" #include "history/view/history_view_context_menu.h" +#include "history/view/history_view_quick_action.h" #include "history/view/history_view_react_button.h" #include "history/view/history_view_emoji_interactions.h" #include "history/history_item_components.h" @@ -1854,11 +1855,31 @@ void HistoryInner::mouseDoubleClickEvent(QMouseEvent *e) { && !_emptyPainter) { if (const auto view = Element::Moused()) { mouseActionCancel(); - _widget->replyToMessage(view->data()); + switch (HistoryView::CurrentQuickAction()) { + case HistoryView::DoubleClickQuickAction::Reply: { + _widget->replyToMessage(view->data()); + } break; + case HistoryView::DoubleClickQuickAction::React: { + toggleFavoriteReaction(view); + } break; + default: break; + } } } } +void HistoryInner::toggleFavoriteReaction(not_null view) const { + const auto favorite = session().data().reactions().favorite(); + const auto allowed = _reactionsManager->allowedSublist(); + if (allowed && !allowed->contains(favorite)) { + return; + } + view->data()->toggleReaction(favorite); + if (const auto top = itemTop(view); top >= 0) { + view->animateReaction({ .emoji = favorite }); + } +} + void HistoryInner::contextMenuEvent(QContextMenuEvent *e) { showContextMenu(e); } diff --git a/Telegram/SourceFiles/history/history_inner_widget.h b/Telegram/SourceFiles/history/history_inner_widget.h index b511d1679..07b8fca9d 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.h +++ b/Telegram/SourceFiles/history/history_inner_widget.h @@ -374,6 +374,7 @@ private: QPoint position, const HistoryView::TextState &reactionState) const -> HistoryView::Reactions::ButtonParameters; + void toggleFavoriteReaction(not_null view) const; void setupSharingDisallowed(); [[nodiscard]] bool hasCopyRestriction(HistoryItem *item = nullptr) const; diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index 481c88e85..add09dd07 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_service_message.h" #include "history/view/history_view_cursor_state.h" #include "history/view/history_view_react_button.h" +#include "history/view/history_view_quick_action.h" #include "chat_helpers/message_field.h" #include "mainwindow.h" #include "mainwidget.h" @@ -2089,7 +2090,27 @@ void ListWidget::mouseDoubleClickEvent(QMouseEvent *e) { && _overElement && _overElement->data()->isRegular()) { mouseActionCancel(); - replyToMessageRequestNotify(_overElement->data()->fullId()); + switch (CurrentQuickAction()) { + case DoubleClickQuickAction::Reply: { + replyToMessageRequestNotify(_overElement->data()->fullId()); + } break; + case DoubleClickQuickAction::React: { + toggleFavoriteReaction(_overElement); + } break; + default: break; + } + } +} + +void ListWidget::toggleFavoriteReaction(not_null view) const { + const auto favorite = session().data().reactions().favorite(); + const auto allowed = _reactionsManager->allowedSublist(); + if (allowed && !allowed->contains(favorite)) { + return; + } + view->data()->toggleReaction(favorite); + if (const auto top = itemTop(view); top >= 0) { + view->animateReaction({ .emoji = favorite }); } } diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.h b/Telegram/SourceFiles/history/view/history_view_list_widget.h index 403c060f8..7e086435e 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.h @@ -248,6 +248,7 @@ public: not_null view, QPoint position, const TextState &reactionState) const; + void toggleFavoriteReaction(not_null view) const; // ElementDelegate interface. Context elementContext() override; diff --git a/Telegram/SourceFiles/history/view/history_view_quick_action.cpp b/Telegram/SourceFiles/history/view/history_view_quick_action.cpp new file mode 100644 index 000000000..40222da67 --- /dev/null +++ b/Telegram/SourceFiles/history/view/history_view_quick_action.cpp @@ -0,0 +1,19 @@ +/* +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 "history/view/history_view_quick_action.h" + +#include "core/application.h" +#include "core/core_settings.h" + +namespace HistoryView { + +DoubleClickQuickAction CurrentQuickAction() { + return Core::App().settings().chatQuickAction(); +} + +} // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/history_view_quick_action.h b/Telegram/SourceFiles/history/view/history_view_quick_action.h new file mode 100644 index 000000000..73dafa834 --- /dev/null +++ b/Telegram/SourceFiles/history/view/history_view_quick_action.h @@ -0,0 +1,20 @@ +/* +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 HistoryView { + +enum class DoubleClickQuickAction { + Reply, // Default. + React, + None, +}; + +[[nodiscard]] DoubleClickQuickAction CurrentQuickAction(); + +} // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/history_view_react_button.cpp b/Telegram/SourceFiles/history/view/history_view_react_button.cpp index a2a6a9e6f..2aa24cf12 100644 --- a/Telegram/SourceFiles/history/view/history_view_react_button.cpp +++ b/Telegram/SourceFiles/history/view/history_view_react_button.cpp @@ -591,8 +591,7 @@ void Manager::applyList( setSelectedIcon(selected < _icons.size() ? selected : -1); } -void Manager::updateAllowedSublist( - std::optional> filter) { +void Manager::updateAllowedSublist(AllowedSublist filter) { if (_filter == filter) { return; } @@ -600,6 +599,10 @@ void Manager::updateAllowedSublist( applyListFilters(); } +const Manager::AllowedSublist &Manager::allowedSublist() const { + return _filter; +} + void Manager::updateUniqueLimit(not_null item) { if (item->fullId() != _buttonContext) { return; @@ -1598,7 +1601,7 @@ rpl::producer Manager::faveRequests() const { void SetupManagerList( not_null manager, not_null session, - rpl::producer>> filter) { + rpl::producer filter) { const auto reactions = &session->data().reactions(); rpl::single(rpl::empty) | rpl::then( reactions->updates() @@ -1610,8 +1613,7 @@ void SetupManagerList( std::move( filter - ) | rpl::start_with_next([=]( - std::optional> &&list) { + ) | rpl::start_with_next([=](Manager::AllowedSublist &&list) { manager->updateAllowedSublist(std::move(list)); }, manager->lifetime()); diff --git a/Telegram/SourceFiles/history/view/history_view_react_button.h b/Telegram/SourceFiles/history/view/history_view_react_button.h index f1a7fc05f..10d3767a8 100644 --- a/Telegram/SourceFiles/history/view/history_view_react_button.h +++ b/Telegram/SourceFiles/history/view/history_view_react_button.h @@ -143,10 +143,13 @@ public: IconFactory iconFactory); ~Manager(); + using AllowedSublist = std::optional>; + void applyList( const std::vector &list, const QString &favorite); - void updateAllowedSublist(std::optional> filter); + void updateAllowedSublist(AllowedSublist filter); + [[nodiscard]] const AllowedSublist &allowedSublist() const; void updateUniqueLimit(not_null item); void updateButton(ButtonParameters parameters); @@ -307,7 +310,7 @@ private: rpl::event_stream _chosen; std::vector _list; QString _favorite; - std::optional> _filter; + AllowedSublist _filter; QSize _outer; QRect _inner; QSize _overlayFull; @@ -382,7 +385,7 @@ private: void SetupManagerList( not_null manager, not_null session, - rpl::producer>> filter); + rpl::producer filter); [[nodiscard]] std::shared_ptr DefaultIconFactory( not_null media, diff --git a/Telegram/SourceFiles/settings/settings_chat.cpp b/Telegram/SourceFiles/settings/settings_chat.cpp index 26102e082..1429457c4 100644 --- a/Telegram/SourceFiles/settings/settings_chat.cpp +++ b/Telegram/SourceFiles/settings/settings_chat.cpp @@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/toast/toast.h" #include "ui/image/image.h" #include "ui/ui_utility.h" +#include "history/view/history_view_quick_action.h" #include "lang/lang_keys.h" #include "export/export_manager.h" #include "window/themes/window_theme.h" @@ -49,6 +50,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "data/data_cloud_themes.h" #include "data/data_file_origin.h" +#include "data/data_message_reactions.h" #include "chat_helpers/emoji_sets_manager.h" #include "base/platform/base_platform_info.h" #include "platform/platform_specific.h" @@ -780,6 +782,7 @@ void SetupMessages( AddSkip(container, st::settingsSendTypeSkip); using SendByType = Ui::InputSubmitSettings; + using Quick = HistoryView::DoubleClickQuickAction; const auto skip = st::settingsSendTypeSkip; auto wrap = object_ptr(container); @@ -790,32 +793,93 @@ void SetupMessages( std::move(wrap), QMargins(0, skip, 0, skip))); - const auto group = std::make_shared>( + const auto groupSend = std::make_shared>( Core::App().settings().sendSubmitWay()); - const auto add = [&](SendByType value, const QString &text) { + const auto addSend = [&](SendByType value, const QString &text) { inner->add( object_ptr>( inner, - group, + groupSend, value, text, st::settingsSendType), st::settingsSendTypePadding); }; - add(SendByType::Enter, tr::lng_settings_send_enter(tr::now)); - add( + addSend(SendByType::Enter, tr::lng_settings_send_enter(tr::now)); + addSend( SendByType::CtrlEnter, (Platform::IsMac() ? tr::lng_settings_send_cmdenter(tr::now) : tr::lng_settings_send_ctrlenter(tr::now))); - group->setChangedCallback([=](SendByType value) { + groupSend->setChangedCallback([=](SendByType value) { Core::App().settings().setSendSubmitWay(value); Core::App().saveSettingsDelayed(); controller->content()->ctrlEnterSubmitUpdated(); }); AddSkip(inner, st::settingsCheckboxesSkip); + + const auto groupQuick = std::make_shared>( + Core::App().settings().chatQuickAction()); + const auto addQuick = [&](Quick value, const QString &text) { + return inner->add( + object_ptr>( + inner, + groupQuick, + value, + text, + st::settingsSendType), + st::settingsSendTypePadding); + }; + addQuick(Quick::Reply, tr::lng_settings_chat_quick_action_reply(tr::now)); + const auto react = addQuick( + Quick::React, + tr::lng_settings_chat_quick_action_react(tr::now)); + const auto reactRight = Ui::CreateChild(inner); + + struct State { + QString lastFavorite; + QImage cache; + }; + const auto state = reactRight->lifetime().make_state(); + const auto updateState = [=] { + auto &reactions = controller->session().data().reactions(); + if (state->lastFavorite == reactions.favorite()) { + return; + } + state->lastFavorite = reactions.favorite(); + state->cache = reactions.resolveImageFor( + reactions.favorite(), + Data::Reactions::ImageSize::Settings); + reactRight->resize(state->cache.size() / style::DevicePixelRatio()); + }; + updateState(); + controller->session().data().reactions().updates( + ) | rpl::start_with_next(updateState, reactRight->lifetime()); + + reactRight->setAttribute(Qt::WA_TransparentForMouseEvents); + reactRight->paintRequest( + ) | rpl::start_with_next([=] { + Painter p(reactRight); + + p.drawImage(0, 0, state->cache); + }, reactRight->lifetime()); + rpl::combine( + reactRight->sizeValue(), + react->geometryValue() + ) | rpl::start_with_next([=](const QSize &rightSize, const QRect &r) { + reactRight->moveToRight( + st::settingsButtonRightSkip, + r.y() + (r.height() - rightSize.height()) / 2); + }, reactRight->lifetime()); + + groupQuick->setChangedCallback([=](Quick value) { + Core::App().settings().setChatQuickAction(value); + Core::App().saveSettingsDelayed(); + }); + + AddSkip(inner, st::settingsCheckboxesSkip); } void SetupExport( diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index fa71b6c6b..57b996bc6 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -1006,6 +1006,8 @@ reactionInfoSkip: 3px; reactionInfoDigitSkip: 6px; reactionInfoBetween: 3px; +reactionSettingsImage: 40px; + reactionCornerSize: size(36px, 32px); reactionCornerCenter: point(7px, -9px); reactionCornerImage: 22px;