Added ability to configure quick action on double click.

This commit is contained in:
23rd 2022-04-11 11:34:56 +03:00 committed by John Preston
parent 572eb1f5f8
commit 4304071d18
16 changed files with 212 additions and 17 deletions

View File

@ -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

View File

@ -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";

View File

@ -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<uint64> accountsOrder;
qint32 hardwareAcceleratedVideo = _hardwareAcceleratedVideo ? 1 : 0;
qint32 chatQuickAction = static_cast<qint32>(_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<Quick>(
chatQuickAction);
switch (uncheckedChatQuickAction) {
case Quick::None:
case Quick::Reply:
case Quick::React: _chatQuickAction = uncheckedChatQuickAction; break;
}
}
}
QString Settings::getSoundPath(const QString &key) const {

View File

@ -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<uint64> _accountsOrder;
bool _hardwareAcceleratedVideo = true;
HistoryView::DoubleClickQuickAction _chatQuickAction =
HistoryView::DoubleClickQuickAction();
bool _tabbedReplacedWithInfo = false; // per-window
rpl::event_stream<bool> _tabbedReplacedWithInfoValue; // per-window

View File

@ -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);

View File

@ -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<DocumentMedia> media;
std::unique_ptr<Lottie::Icon> icon;
bool fromAppearAnimation = false;

View File

@ -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<Element*> 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);
}

View File

@ -374,6 +374,7 @@ private:
QPoint position,
const HistoryView::TextState &reactionState) const
-> HistoryView::Reactions::ButtonParameters;
void toggleFavoriteReaction(not_null<Element*> view) const;
void setupSharingDisallowed();
[[nodiscard]] bool hasCopyRestriction(HistoryItem *item = nullptr) const;

View File

@ -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<Element*> 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 });
}
}

View File

@ -248,6 +248,7 @@ public:
not_null<const Element*> view,
QPoint position,
const TextState &reactionState) const;
void toggleFavoriteReaction(not_null<Element*> view) const;
// ElementDelegate interface.
Context elementContext() override;

View File

@ -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

View File

@ -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

View File

@ -591,8 +591,7 @@ void Manager::applyList(
setSelectedIcon(selected < _icons.size() ? selected : -1);
}
void Manager::updateAllowedSublist(
std::optional<base::flat_set<QString>> 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<HistoryItem*> item) {
if (item->fullId() != _buttonContext) {
return;
@ -1598,7 +1601,7 @@ rpl::producer<QString> Manager::faveRequests() const {
void SetupManagerList(
not_null<Manager*> manager,
not_null<Main::Session*> session,
rpl::producer<std::optional<base::flat_set<QString>>> filter) {
rpl::producer<Manager::AllowedSublist> 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<base::flat_set<QString>> &&list) {
) | rpl::start_with_next([=](Manager::AllowedSublist &&list) {
manager->updateAllowedSublist(std::move(list));
}, manager->lifetime());

View File

@ -143,10 +143,13 @@ public:
IconFactory iconFactory);
~Manager();
using AllowedSublist = std::optional<base::flat_set<QString>>;
void applyList(
const std::vector<Data::Reaction> &list,
const QString &favorite);
void updateAllowedSublist(std::optional<base::flat_set<QString>> filter);
void updateAllowedSublist(AllowedSublist filter);
[[nodiscard]] const AllowedSublist &allowedSublist() const;
void updateUniqueLimit(not_null<HistoryItem*> item);
void updateButton(ButtonParameters parameters);
@ -307,7 +310,7 @@ private:
rpl::event_stream<Chosen> _chosen;
std::vector<ReactionIcons> _list;
QString _favorite;
std::optional<base::flat_set<QString>> _filter;
AllowedSublist _filter;
QSize _outer;
QRect _inner;
QSize _overlayFull;
@ -382,7 +385,7 @@ private:
void SetupManagerList(
not_null<Manager*> manager,
not_null<Main::Session*> session,
rpl::producer<std::optional<base::flat_set<QString>>> filter);
rpl::producer<Manager::AllowedSublist> filter);
[[nodiscard]] std::shared_ptr<Lottie::Icon> DefaultIconFactory(
not_null<Data::DocumentMedia*> media,

View File

@ -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<Ui::VerticalLayout>(container);
@ -790,32 +793,93 @@ void SetupMessages(
std::move(wrap),
QMargins(0, skip, 0, skip)));
const auto group = std::make_shared<Ui::RadioenumGroup<SendByType>>(
const auto groupSend = std::make_shared<Ui::RadioenumGroup<SendByType>>(
Core::App().settings().sendSubmitWay());
const auto add = [&](SendByType value, const QString &text) {
const auto addSend = [&](SendByType value, const QString &text) {
inner->add(
object_ptr<Ui::Radioenum<SendByType>>(
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<Ui::RadioenumGroup<Quick>>(
Core::App().settings().chatQuickAction());
const auto addQuick = [&](Quick value, const QString &text) {
return inner->add(
object_ptr<Ui::Radioenum<Quick>>(
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<Ui::RpWidget>(inner);
struct State {
QString lastFavorite;
QImage cache;
};
const auto state = reactRight->lifetime().make_state<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(

View File

@ -1006,6 +1006,8 @@ reactionInfoSkip: 3px;
reactionInfoDigitSkip: 6px;
reactionInfoBetween: 3px;
reactionSettingsImage: 40px;
reactionCornerSize: size(36px, 32px);
reactionCornerCenter: point(7px, -9px);
reactionCornerImage: 22px;