From 2c50f7b18c339cfbfacde14d5dd2d7e0e17b73dd Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 6 Oct 2022 17:06:18 +0400 Subject: [PATCH] Implement nice topic icon create / edit box. --- Telegram/CMakeLists.txt | 6 +- .../boxes/peers/edit_forum_topic_box.cpp | 395 +++++++++++++++--- .../chat_helpers/emoji_list_widget.cpp | 7 +- .../SourceFiles/data/data_forum_topic.cpp | 5 +- Telegram/SourceFiles/dialogs/dialogs.style | 10 +- .../history/history_inner_widget.cpp | 2 +- .../history/view/history_view_bottom_info.cpp | 16 +- .../history/view/history_view_bottom_info.h | 14 +- .../history/view/history_view_element.cpp | 8 +- .../history/view/history_view_element.h | 8 +- .../history/view/history_view_list_widget.cpp | 2 +- .../history/view/history_view_message.cpp | 12 +- .../history/view/history_view_message.h | 4 +- .../view/reactions/history_view_reactions.cpp | 18 +- .../view/reactions/history_view_reactions.h | 12 +- .../info_profile_emoji_status_panel.cpp | 119 +----- .../profile/info_profile_emoji_status_panel.h | 5 +- .../ui/effects/emoji_fly_animation.cpp | 104 +++++ .../ui/effects/emoji_fly_animation.h | 40 ++ .../effects/reaction_fly_animation.cpp} | 46 +- .../effects/reaction_fly_animation.h} | 35 +- Telegram/lib_ui | 2 +- 22 files changed, 596 insertions(+), 274 deletions(-) create mode 100644 Telegram/SourceFiles/ui/effects/emoji_fly_animation.cpp create mode 100644 Telegram/SourceFiles/ui/effects/emoji_fly_animation.h rename Telegram/SourceFiles/{history/view/reactions/history_view_reactions_animation.cpp => ui/effects/reaction_fly_animation.cpp} (89%) rename Telegram/SourceFiles/{history/view/reactions/history_view_reactions_animation.h => ui/effects/reaction_fly_animation.h} (79%) diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 97f93033c..a26e68bc8 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -674,8 +674,6 @@ PRIVATE history/view/media/history_view_web_page.h history/view/reactions/history_view_reactions.cpp history/view/reactions/history_view_reactions.h - history/view/reactions/history_view_reactions_animation.cpp - history/view/reactions/history_view_reactions_animation.h history/view/reactions/history_view_reactions_button.cpp history/view/reactions/history_view_reactions_button.h history/view/reactions/history_view_reactions_list.cpp @@ -1233,9 +1231,13 @@ PRIVATE ui/chat/choose_send_as.h ui/chat/choose_theme_controller.cpp ui/chat/choose_theme_controller.h + ui/effects/emoji_fly_animation.cpp + ui/effects/emoji_fly_animation.h ui/effects/message_sending_animation_common.h ui/effects/message_sending_animation_controller.cpp ui/effects/message_sending_animation_controller.h + ui/effects/reaction_fly_animation.cpp + ui/effects/reaction_fly_animation.h ui/effects/send_action_animations.cpp ui/effects/send_action_animations.h ui/image/image.cpp diff --git a/Telegram/SourceFiles/boxes/peers/edit_forum_topic_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_forum_topic_box.cpp index 1e533cd60..baa1d7245 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_forum_topic_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_forum_topic_box.cpp @@ -8,72 +8,171 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/peers/edit_forum_topic_box.h" #include "ui/widgets/input_fields.h" +#include "ui/widgets/shadow.h" +#include "ui/effects/emoji_fly_animation.h" #include "ui/abstract_button.h" #include "ui/color_int_conversion.h" #include "data/data_channel.h" +#include "data/data_document.h" #include "data/data_forum.h" #include "data/data_forum_topic.h" #include "data/data_session.h" #include "data/stickers/data_custom_emoji.h" #include "base/random.h" +#include "base/qt_signal_producer.h" +#include "chat_helpers/emoji_list_widget.h" +#include "chat_helpers/stickers_list_footer.h" +#include "boxes/premium_preview_box.h" #include "main/main_session.h" #include "history/history.h" #include "history/view/history_view_replies_section.h" #include "lang/lang_keys.h" #include "info/profile/info_profile_emoji_status_panel.h" #include "window/window_session_controller.h" +#include "window/window_controller.h" #include "settings/settings_common.h" #include "apiwrap.h" +#include "mainwindow.h" +#include "styles/style_layers.h" #include "styles/style_dialogs.h" +#include "styles/style_chat_helpers.h" namespace { +namespace { + +constexpr auto kDefaultIconId = DocumentId(0x7FFF'FFFF'FFFF'FFFFULL); + +struct DefaultIcon { + QString title; + int32 colorId = 0; +}; + +class DefaultIconEmoji final : public Ui::Text::CustomEmoji { +public: + DefaultIconEmoji( + rpl::producer value, + Fn repaint); + + QString entityData() override; + + void paint(QPainter &p, const Context &context) override; + void unload() override; + bool ready() override; + bool readyInDefaultState() override; + +private: + DefaultIcon _icon = {}; + QImage _image; + + rpl::lifetime _lifetime; + +}; + +DefaultIconEmoji::DefaultIconEmoji( + rpl::producer value, + Fn repaint) { + std::move(value) | rpl::start_with_next([=](DefaultIcon value) { + _icon = value; + _image = QImage(); + repaint(); + }, _lifetime); +} + +QString DefaultIconEmoji::entityData() { + return u"topic_icon:%1"_q.arg(_icon.colorId); +} + +void DefaultIconEmoji::paint(QPainter &p, const Context &context) { + if (_image.isNull()) { + _image = Data::ForumTopicIconFrame( + _icon.colorId, + _icon.title, + st::defaultForumTopicIcon); + } + const auto esize = Ui::Emoji::GetSizeLarge() / style::DevicePixelRatio(); + const auto customSize = Ui::Text::AdjustCustomEmojiSize(esize); + const auto skip = (customSize - st::defaultForumTopicIcon.size) / 2; + p.drawImage(context.position + QPoint(skip, skip), _image); +} + +void DefaultIconEmoji::unload() { + _image = QImage(); +} + +bool DefaultIconEmoji::ready() { + return true; +} + +bool DefaultIconEmoji::readyInDefaultState() { + return true; +} + +} // namespace [[nodiscard]] int EditIconSize() { const auto tag = Data::CustomEmojiManager::SizeTag::Large; return Data::FrameSizeFromTag(tag) / style::DevicePixelRatio(); } -[[nodiscard]] rpl::producer EditIconButton( - not_null controller, +[[nodiscard]] int32 ChooseNextColorId( + int32 currentId, + std::vector &otherIds) { + if (otherIds.size() == 1 && otherIds.front() == currentId) { + otherIds = Data::ForumTopicColorIds(); + } + const auto i = ranges::find(otherIds, currentId); + if (i != end(otherIds)) { + otherIds.erase(i); + } + return otherIds.empty() + ? currentId + : otherIds[base::RandomIndex(otherIds.size())]; +} + +[[nodiscard]] not_null EditIconButton( not_null parent, - int32 colorId, - DocumentId iconId) { + not_null controller, + rpl::producer defaultIcon, + rpl::producer iconId, + Fn)> paintIconFrame) { using namespace Info::Profile; struct State { - rpl::variable iconId; - EmojiStatusPanel panel; - std::unique_ptr chosen; - QImage empty; + std::unique_ptr icon; + QImage defaultIcon; }; const auto tag = Data::CustomEmojiManager::SizeTag::Large; const auto size = EditIconSize(); const auto result = Ui::CreateChild(parent.get()); + result->show(); const auto state = result->lifetime().make_state(); - state->empty = Data::ForumTopicIconBackground( - colorId, - st::largeForumTopicIcon.size); - state->iconId.value( - ) | rpl::start_with_next([=](DocumentId iconId) { + + std::move( + iconId + ) | rpl::start_with_next([=](DocumentId id) { const auto owner = &controller->session().data(); - state->chosen = iconId + state->icon = id ? owner->customEmojiManager().create( - iconId, + id, [=] { result->update(); }, tag) : nullptr; result->update(); }, result->lifetime()); - state->iconId = iconId; - state->panel.setChooseFilter([=](DocumentId) { - return true; - }); - state->panel.setChooseCallback([=](DocumentId id) { - state->iconId = id; - }); + + std::move( + defaultIcon + ) | rpl::start_with_next([=](DefaultIcon icon) { + state->defaultIcon = Data::ForumTopicIconFrame( + icon.colorId, + icon.title, + st::largeForumTopicIcon); + result->update(); + }, result->lifetime()); + result->resize(size, size); result->paintRequest( ) | rpl::filter([=] { - return !state->panel.paintBadgeFrame(result); + return !paintIconFrame(result); }) | rpl::start_with_next([=](QRect clip) { auto args = Ui::Text::CustomEmoji::Context{ .preview = st::windowBgOver->c, @@ -82,17 +181,142 @@ namespace { Window::GifPauseReason::Layer), }; auto p = QPainter(result); - if (state->chosen) { - state->chosen->paint(p, args); + if (state->icon) { + state->icon->paint(p, args); } else { const auto skip = (size - st::largeForumTopicIcon.size) / 2; - p.drawImage(skip, skip, state->empty); + p.drawImage(skip, skip, state->defaultIcon); } }, result->lifetime()); - result->setClickedCallback([=] { - state->panel.show(controller, result, tag); + + return result; +} + +struct IconSelector { + Fn)> paintIconFrame; + rpl::producer iconIdValue; +}; + +[[nodiscard]] IconSelector AddIconSelector( + not_null box, + not_null button, + not_null controller, + rpl::producer defaultIcon, + rpl::producer coverHeight, + DocumentId iconId, + Fn)> placeFooter) { + using namespace ChatHelpers; + + struct State { + std::unique_ptr animation; + rpl::variable iconId; + QPointer button; + }; + const auto state = box->lifetime().make_state(State{ + .iconId = iconId, + .button = button.get(), }); - return state->iconId.value(); + + const auto manager = &controller->session().data().customEmojiManager(); + + auto factory = [=](DocumentId id, Fn repaint) + -> std::unique_ptr { + const auto tag = Data::CustomEmojiManager::SizeTag::Large; + if (const auto colorId = kDefaultIconId) { + return std::make_unique( + rpl::duplicate(defaultIcon), + repaint); + } + return manager->create(id, std::move(repaint), tag); + }; + + const auto body = box->verticalLayout(); + const auto selector = body->add( + object_ptr(body, EmojiListDescriptor{ + .session = &controller->session(), + .mode = EmojiListWidget::Mode::EmojiStatus, + .controller = controller, + .paused = Window::PausedIn( + controller, + Window::GifPauseReason::Layer), + .customRecentList = { kDefaultIconId }, + .customRecentFactory = std::move(factory), + .st = &st::reactPanelEmojiPan, + }), + st::reactPanelEmojiPan.padding); + + auto ownedFooter = selector->createFooter(); + const auto footer = ownedFooter.data(); + placeFooter(std::move(ownedFooter)); + + const auto shadow = Ui::CreateChild(box.get()); + shadow->show(); + + rpl::combine( + rpl::duplicate(coverHeight), + selector->widthValue() + ) | rpl::start_with_next([=](int top, int width) { + shadow->setGeometry(0, top, width, st::lineWidth); + }, shadow->lifetime()); + + selector->refreshEmoji(); + + selector->scrollToRequests( + ) | rpl::start_with_next([=](int y) { + box->scrollToY(y); + shadow->update(); + }, selector->lifetime()); + + rpl::combine( + box->heightValue(), + std::move(coverHeight), + rpl::mappers::_1 - rpl::mappers::_2 + ) | rpl::start_with_next([=](int height) { + selector->setMinimalHeight(selector->width(), height); + }, body->lifetime()); + + selector->customChosen( + ) | rpl::start_with_next([=](ChatHelpers::FileChosen data) { + const auto owner = &controller->session().data(); + const auto custom = (data.document->id != kDefaultIconId); + if (custom && !controller->session().premium()) { + // #TODO forum premium promo + ShowPremiumPreviewBox(controller, PremiumPreview::EmojiStatus); + return; + } + const auto body = controller->window().widget()->bodyWidget(); + if (state->button && custom) { + const auto &from = data.messageSendingFrom; + auto args = Ui::ReactionFlyAnimationArgs{ + .id = { { data.document->id } }, + .flyIcon = from.frame, + .flyFrom = body->mapFromGlobal(from.globalStartGeometry), + }; + state->animation = std::make_unique( + body, + &owner->reactions(), + std::move(args), + [=] { state->animation->repaint(); }, + Data::CustomEmojiSizeTag::Large); + } + state->iconId = data.document->id; + }, selector->lifetime()); + + auto paintIconFrame = [=](not_null button) { + if (!state->animation) { + return false; + } else if (state->animation->paintBadgeFrame(button)) { + return true; + } + InvokeQueued(state->animation->layer(), [=] { + state->animation = nullptr; + }); + return false; + }; + return { + .paintIconFrame = std::move(paintIconFrame), + .iconIdValue = state->iconId.value(), + }; } } // namespace @@ -116,42 +340,101 @@ void EditForumTopicBox( // #TODO forum lang box->setTitle(rpl::single(creating ? u"New topic"_q : u"Edit topic"_q)); + box->setMaxHeight(st::editTopicMaxHeight); + struct State { - int32 colorId = 0; - DocumentId iconId = 0; + rpl::variable defaultIcon; + rpl::variable iconId = 0; + std::vector otherColorIds; mtpRequestId requestId = 0; + Fn)> paintIconFrame; }; const auto state = box->lifetime().make_state(); const auto &colors = Data::ForumTopicColorIds(); state->iconId = topic ? topic->iconId() : 0; - state->colorId = topic - ? topic->colorId() - : colors[base::RandomIndex(colors.size())]; - // #TODO forum lang and design - Settings::AddSubsectionTitle( - box->verticalLayout(), - rpl::single(u"Topic Icon"_q)); - const auto badgeWrap = box->addRow( - object_ptr(box, EditIconSize())); - EditIconButton( - controller, - badgeWrap, - state->colorId, - state->iconId - ) | rpl::start_with_next([=](DocumentId id) { - state->iconId = id; - }, box->lifetime()); + state->otherColorIds = colors; + state->defaultIcon = DefaultIcon{ + topic ? topic->title() : QString(), + topic ? topic->colorId() : ChooseNextColorId(0, state->otherColorIds) + }; - const auto title = box->addRow( + const auto top = box->setPinnedToTopContent( + object_ptr(box)); + + const auto title = top->add( object_ptr( box, st::defaultInputField, - rpl::single(u"Topic Title"_q), - topic ? topic->title() : QString())); // #TODO forum lang + rpl::single(u"Topic Title"_q), // #TODO forum lang + topic ? topic->title() : QString()), + st::editTopicTitleMargin); box->setFocusCallback([=] { title->setFocusFast(); }); + const auto paintIconFrame = [=](not_null widget) { + return state->paintIconFrame(widget); + }; + const auto icon = EditIconButton( + title->parentWidget(), + controller, + state->defaultIcon.value(), + state->iconId.value(), + paintIconFrame); + + title->geometryValue( + ) | rpl::start_with_next([=](QRect geometry) { + icon->move( + st::editTopicIconPosition.x(), + st::editTopicIconPosition.y()); + }, icon->lifetime()); + + state->iconId.value( + ) | rpl::start_with_next([=](DocumentId iconId) { + icon->setAttribute( + Qt::WA_TransparentForMouseEvents, + !creating || (iconId != 0)); + }, box->lifetime()); + + icon->setClickedCallback([=] { + const auto current = state->defaultIcon.current(); + state->defaultIcon = DefaultIcon{ + current.title, + ChooseNextColorId(current.colorId, state->otherColorIds), + }; + }); + base::qt_signal_producer( + title, + &Ui::InputField::changed + ) | rpl::start_with_next([=] { + state->defaultIcon = DefaultIcon{ + title->getLastText().trimmed(), + state->defaultIcon.current().colorId, + }; + }, box->lifetime()); + + Settings::AddDividerText( + top, + rpl::single(u"Choose title and icon for your topic"_q)); + + box->setScrollStyle(st::reactPanelScroll); + + auto selector = AddIconSelector( + box, + icon, + controller, + state->defaultIcon.value(), + top->heightValue(), + state->iconId.current(), + [&](object_ptr footer) { + top->add(std::move(footer)); }); + state->paintIconFrame = std::move(selector.paintIconFrame); + std::move( + selector.iconIdValue + ) | rpl::start_with_next([=](DocumentId iconId) { + state->iconId = (iconId != kDefaultIconId) ? iconId : 0; + }, box->lifetime()); + const auto requestId = std::make_shared(); const auto create = [=] { const auto channel = forum->peer->asChannel(); @@ -167,8 +450,8 @@ void EditForumTopicBox( forum, channel->forum()->reserveCreatingId( title->getLastText().trimmed(), - state->colorId, - state->iconId)), + state->defaultIcon.current().colorId, + state->iconId.current())), Window::SectionShow::Way::ClearStack); }; @@ -187,8 +470,8 @@ void EditForumTopicBox( return; } else if (parent->creating(rootId)) { topic->applyTitle(title->getLastText().trimmed()); - topic->applyColorId(state->colorId); - topic->applyIconId(state->iconId); + topic->applyColorId(state->defaultIcon.current().colorId); + topic->applyIconId(state->iconId.current()); } else { using Flag = MTPchannels_EditForumTopic::Flag; const auto api = &forum->session().api(); @@ -197,7 +480,7 @@ void EditForumTopicBox( topic->channel()->inputChannel, MTP_int(rootId), MTP_string(title->getLastText().trimmed()), - MTP_long(state->iconId) + MTP_long(state->iconId.current()) )).done([=](const MTPUpdates &result) { api->applyUpdates(result); box->closeBox(); diff --git a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp index 9e75a58d6..9ebf2854d 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp @@ -1093,11 +1093,8 @@ DocumentData *EmojiListWidget::lookupCustomEmoji( if (section == int(Section::Recent) && index < _recent.size()) { const auto document = std::get_if( &_recent[index].id.data); - const auto custom = document - ? session().data().document(document->id).get() - : nullptr; - if (custom && custom->sticker()) { - return custom; + if (document) { + return session().data().document(document->id); } } else if (section >= _staticCount && index < _custom[section - _staticCount].list.size()) { diff --git a/Telegram/SourceFiles/data/data_forum_topic.cpp b/Telegram/SourceFiles/data/data_forum_topic.cpp index 3a4086cf4..3d956f9f0 100644 --- a/Telegram/SourceFiles/data/data_forum_topic.cpp +++ b/Telegram/SourceFiles/data/data_forum_topic.cpp @@ -113,6 +113,7 @@ QImage ForumTopicIconFrame( if (const auto one = ExtractNonEmojiLetter(title); !one.isEmpty()) { auto p = QPainter(&background); p.setPen(Qt::white); + p.setFont(st.font); p.drawText( QRect(0, st.textTop, st.size, st.font->height * 2), one, @@ -331,7 +332,9 @@ void ForumTopic::paintUserpic( const auto size = st::defaultForumTopicIcon.size; const auto esize = st::emojiSize; const auto shift = (esize - size) / 2; - p.drawImage(position + QPoint(shift, shift), _defaultIcon); + p.drawImage( + position + st::forumTopicIconPosition + QPoint(shift, 0), + _defaultIcon); } } diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index 2376099dd..61244914b 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -27,13 +27,13 @@ ForumTopicIcon { defaultForumTopicIcon: ForumTopicIcon { size: 21px; - font: font(bold 12px); - textTop: 3px; + font: font(bold 11px); + textTop: 2px; } largeForumTopicIcon: ForumTopicIcon { size: 26px; font: font(bold 13px); - textTop: 4px; + textTop: 3px; } dialogsUnreadFont: font(12px bold); @@ -412,3 +412,7 @@ forumTopicRow: DialogRow(defaultDialogRow) { textLeft: 68px; textTop: 29px; } +forumTopicIconPosition: point(2px, 0px); +editTopicTitleMargin: margins(70px, 2px, 22px, 18px); +editTopicIconPosition: point(24px, 19px); +editTopicMaxHeight: 408px; diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index a40ad560d..9def8115c 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -15,7 +15,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/media/history_view_media.h" #include "history/view/media/history_view_sticker.h" #include "history/view/media/history_view_web_page.h" -#include "history/view/reactions/history_view_reactions_animation.h" #include "history/view/reactions/history_view_reactions_button.h" #include "history/view/reactions/history_view_reactions_selector.h" #include "history/view/history_view_message.h" @@ -32,6 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/toasts/common_toasts.h" #include "ui/effects/path_shift_gradient.h" #include "ui/effects/message_sending_animation_controller.h" +#include "ui/effects/reaction_fly_animation.h" #include "ui/text/text_options.h" #include "ui/boxes/report_box.h" #include "ui/layers/generic_box.h" diff --git a/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp b/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp index f18672f9c..104663634 100644 --- a/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp +++ b/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/chat/message_bubble.h" #include "ui/chat/chat_style.h" +#include "ui/effects/reaction_fly_animation.h" #include "ui/text/text_options.h" #include "ui/text/text_utilities.h" #include "ui/painter.h" @@ -16,7 +17,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_item_components.h" #include "history/history_message.h" #include "history/history.h" -#include "history/view/reactions/history_view_reactions_animation.h" #include "history/view/history_view_message.h" #include "history/view/history_view_cursor_state.h" #include "core/click_handler_types.h" @@ -31,7 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace HistoryView { struct BottomInfo::Reaction { - mutable std::unique_ptr animation; + mutable std::unique_ptr animation; mutable QImage image; ReactionId id; QString countText; @@ -332,7 +332,7 @@ void BottomInfo::paintReactions( int availableWidth, const PaintContext &context) const { struct SingleAnimation { - not_null animation; + not_null animation; QRect target; }; std::vector animations; @@ -561,13 +561,13 @@ void BottomInfo::setReactionCount(Reaction &reaction, int count) { } void BottomInfo::animateReaction( - Reactions::AnimationArgs &&args, + Ui::ReactionFlyAnimationArgs &&args, Fn repaint) { const auto i = ranges::find(_reactions, args.id, &Reaction::id); if (i == end(_reactions)) { return; } - i->animation = std::make_unique( + i->animation = std::make_unique( _reactionsOwner, args.translated(QPoint(width(), height())), std::move(repaint), @@ -575,10 +575,10 @@ void BottomInfo::animateReaction( } auto BottomInfo::takeReactionAnimations() --> base::flat_map> { +-> base::flat_map> { auto result = base::flat_map< ReactionId, - std::unique_ptr>(); + std::unique_ptr>(); for (auto &reaction : _reactions) { if (reaction.animation) { result.emplace(reaction.id, std::move(reaction.animation)); @@ -589,7 +589,7 @@ auto BottomInfo::takeReactionAnimations() void BottomInfo::continueReactionAnimations(base::flat_map< ReactionId, - std::unique_ptr> animations) { + std::unique_ptr> animations) { for (auto &[id, animation] : animations) { const auto i = ranges::find(_reactions, id, &Reaction::id); if (i != end(_reactions)) { diff --git a/Telegram/SourceFiles/history/view/history_view_bottom_info.h b/Telegram/SourceFiles/history/view/history_view_bottom_info.h index def9ad338..6e8d2117f 100644 --- a/Telegram/SourceFiles/history/view/history_view_bottom_info.h +++ b/Telegram/SourceFiles/history/view/history_view_bottom_info.h @@ -14,6 +14,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Ui { struct ChatPaintContext; class AnimatedIcon; +struct ReactionFlyAnimationArgs; +class ReactionFlyAnimation; } // namespace Ui namespace Data { @@ -23,10 +25,6 @@ struct MessageReaction; } // namespace Data namespace HistoryView { -namespace Reactions { -class Animation; -struct AnimationArgs; -} // namespace Reactions using PaintContext = Ui::ChatPaintContext; @@ -80,13 +78,15 @@ public: const PaintContext &context) const; void animateReaction( - Reactions::AnimationArgs &&args, + Ui::ReactionFlyAnimationArgs &&args, Fn repaint); [[nodiscard]] auto takeReactionAnimations() - -> base::flat_map>; + -> base::flat_map< + ReactionId, + std::unique_ptr>; void continueReactionAnimations(base::flat_map< ReactionId, - std::unique_ptr> animations); + std::unique_ptr> animations); private: struct Reaction; diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp index 2b4aac4eb..7ec29fb01 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.cpp +++ b/Telegram/SourceFiles/history/view/history_view_element.cpp @@ -17,7 +17,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/media/history_view_sticker.h" #include "history/view/media/history_view_large_emoji.h" #include "history/view/media/history_view_custom_emoji.h" -#include "history/view/reactions/history_view_reactions_animation.h" #include "history/view/reactions/history_view_reactions_button.h" #include "history/view/reactions/history_view_reactions.h" #include "history/view/history_view_cursor_state.h" @@ -33,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "chat_helpers/stickers_emoji_pack.h" #include "window/window_session_controller.h" #include "ui/effects/path_shift_gradient.h" +#include "ui/effects/reaction_fly_animation.h" #include "ui/chat/chat_style.h" #include "ui/toast/toast.h" #include "ui/toasts/common_toasts.h" @@ -1270,7 +1270,7 @@ void Element::clickHandlerPressedChanged( } } -void Element::animateReaction(Reactions::AnimationArgs &&args) { +void Element::animateReaction(Ui::ReactionFlyAnimationArgs &&args) { } void Element::animateUnreadReactions() { @@ -1283,7 +1283,9 @@ void Element::animateUnreadReactions() { } auto Element::takeReactionAnimations() --> base::flat_map> { +-> base::flat_map< + Data::ReactionId, + std::unique_ptr> { return {}; } diff --git a/Telegram/SourceFiles/history/view/history_view_element.h b/Telegram/SourceFiles/history/view/history_view_element.h index cdf1bf595..d98f6e171 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.h +++ b/Telegram/SourceFiles/history/view/history_view_element.h @@ -33,12 +33,12 @@ class PathShiftGradient; struct BubblePattern; struct ChatPaintContext; class ChatStyle; +struct ReactionFlyAnimationArgs; +class ReactionFlyAnimation; } // namespace Ui namespace HistoryView::Reactions { struct ButtonParameters; -struct AnimationArgs; -class Animation; class InlineList; } // namespace HistoryView::Reactions @@ -454,12 +454,12 @@ public: [[nodiscard]] bool markSponsoredViewed(int shownFromTop) const; - virtual void animateReaction(Reactions::AnimationArgs &&args); + virtual void animateReaction(Ui::ReactionFlyAnimationArgs &&args); void animateUnreadReactions(); [[nodiscard]] virtual auto takeReactionAnimations() -> base::flat_map< Data::ReactionId, - std::unique_ptr>; + std::unique_ptr>; virtual ~Element(); diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index 50d3ccf0f..f72d2f26f 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -15,7 +15,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_item_text.h" #include "history/view/media/history_view_media.h" #include "history/view/media/history_view_sticker.h" -#include "history/view/reactions/history_view_reactions_animation.h" #include "history/view/reactions/history_view_reactions_button.h" #include "history/view/reactions/history_view_reactions_selector.h" #include "history/view/history_view_context_menu.h" @@ -46,6 +45,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/inactive_press.h" #include "ui/effects/message_sending_animation_controller.h" #include "ui/effects/path_shift_gradient.h" +#include "ui/effects/reaction_fly_animation.h" #include "ui/chat/chat_theme.h" #include "ui/chat/chat_style.h" #include "ui/painter.h" diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index 8857049ee..094aa7867 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -14,20 +14,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/media/history_view_media.h" #include "history/view/media/history_view_web_page.h" #include "history/view/reactions/history_view_reactions.h" -#include "history/view/reactions/history_view_reactions_animation.h" #include "history/view/reactions/history_view_reactions_button.h" #include "history/view/history_view_group_call_bar.h" // UserpicInRow. #include "history/view/history_view_view_button.h" // ViewButton. #include "history/history.h" #include "boxes/share_box.h" #include "ui/effects/ripple_animation.h" -#include "base/unixtime.h" +#include "ui/effects/reaction_fly_animation.h" #include "ui/chat/message_bubble.h" #include "ui/chat/chat_style.h" #include "ui/toast/toast.h" #include "ui/text/text_utilities.h" #include "ui/text/text_entity.h" #include "ui/cached_round_corners.h" +#include "base/unixtime.h" #include "data/data_session.h" #include "data/data_user.h" #include "data/data_channel.h" @@ -301,7 +301,7 @@ Message::Message( ? replacing->takeReactionAnimations() : base::flat_map< Data::ReactionId, - std::unique_ptr>(); + std::unique_ptr>(); if (!animations.empty()) { const auto repainter = [=] { repaint(); }; for (const auto &[id, animation] : animations) { @@ -376,7 +376,7 @@ void Message::applyGroupAdminChanges( } } -void Message::animateReaction(Reactions::AnimationArgs &&args) { +void Message::animateReaction(Ui::ReactionFlyAnimationArgs &&args) { const auto item = message(); const auto media = this->media(); @@ -475,7 +475,9 @@ void Message::animateReaction(Reactions::AnimationArgs &&args) { } auto Message::takeReactionAnimations() --> base::flat_map> { +-> base::flat_map< + Data::ReactionId, + std::unique_ptr> { return _reactions ? _reactions->takeAnimations() : _bottomInfo.takeReactionAnimations(); diff --git a/Telegram/SourceFiles/history/view/history_view_message.h b/Telegram/SourceFiles/history/view/history_view_message.h index 8c5cfaae9..72b066e07 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.h +++ b/Telegram/SourceFiles/history/view/history_view_message.h @@ -147,11 +147,11 @@ public: void applyGroupAdminChanges( const base::flat_set &changes) override; - void animateReaction(Reactions::AnimationArgs &&args) override; + void animateReaction(Ui::ReactionFlyAnimationArgs &&args) override; auto takeReactionAnimations() -> base::flat_map< Data::ReactionId, - std::unique_ptr> override; + std::unique_ptr> override; QRect innerGeometry() const override; [[nodiscard]] BottomRippleMask bottomRippleMask(int buttonHeight) const; diff --git a/Telegram/SourceFiles/history/view/reactions/history_view_reactions.cpp b/Telegram/SourceFiles/history/view/reactions/history_view_reactions.cpp index 6bc95d87d..fb9816bf4 100644 --- a/Telegram/SourceFiles/history/view/reactions/history_view_reactions.cpp +++ b/Telegram/SourceFiles/history/view/reactions/history_view_reactions.cpp @@ -9,7 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_message.h" #include "history/history.h" -#include "history/view/reactions/history_view_reactions_animation.h" #include "history/view/history_view_message.h" #include "history/view/history_view_cursor_state.h" #include "history/view/history_view_group_call_bar.h" @@ -23,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_tag.h" #include "ui/text/text_custom_emoji.h" #include "ui/chat/chat_style.h" +#include "ui/effects/reaction_fly_animation.h" #include "ui/painter.h" #include "styles/style_chat.h" @@ -43,7 +43,7 @@ constexpr auto kMaxNicePerRow = 5; struct InlineList::Button { QRect geometry; - mutable std::unique_ptr animation; + mutable std::unique_ptr animation; mutable QImage image; mutable ClickHandlerPtr link; mutable std::unique_ptr custom; @@ -322,12 +322,12 @@ void InlineList::paint( int outerWidth, const QRect &clip) const { struct SingleAnimation { - not_null animation; + not_null animation; QRect target; }; std::vector animations; - auto finished = std::vector>(); + auto finished = std::vector>(); const auto st = context.st; const auto stm = context.messageStyle(); const auto padding = st::reactionInlinePadding; @@ -500,13 +500,13 @@ bool InlineList::getState( } void InlineList::animate( - AnimationArgs &&args, + Ui::ReactionFlyAnimationArgs &&args, Fn repaint) { const auto i = ranges::find(_buttons, args.id, &Button::id); if (i == end(_buttons)) { return; } - i->animation = std::make_unique( + i->animation = std::make_unique( _owner, std::move(args), std::move(repaint), @@ -579,10 +579,10 @@ void InlineList::paintCustomFrame( } auto InlineList::takeAnimations() --> base::flat_map> { +-> base::flat_map> { auto result = base::flat_map< ReactionId, - std::unique_ptr>(); + std::unique_ptr>(); for (auto &button : _buttons) { if (button.animation) { result.emplace(button.id, std::move(button.animation)); @@ -593,7 +593,7 @@ auto InlineList::takeAnimations() void InlineList::continueAnimations(base::flat_map< ReactionId, - std::unique_ptr> animations) { + std::unique_ptr> animations) { for (auto &[id, animation] : animations) { const auto i = ranges::find(_buttons, id, &Button::id); if (i != end(_buttons)) { diff --git a/Telegram/SourceFiles/history/view/reactions/history_view_reactions.h b/Telegram/SourceFiles/history/view/reactions/history_view_reactions.h index e97e0198f..88ac5d0ce 100644 --- a/Telegram/SourceFiles/history/view/reactions/history_view_reactions.h +++ b/Telegram/SourceFiles/history/view/reactions/history_view_reactions.h @@ -17,6 +17,8 @@ class Reactions; namespace Ui { struct ChatPaintContext; +struct ReactionFlyAnimationArgs; +class ReactionFlyAnimation; } // namespace Ui namespace HistoryView { @@ -30,8 +32,6 @@ namespace HistoryView::Reactions { using ::Data::ReactionId; using ::Data::MessageReaction; -struct AnimationArgs; -class Animation; struct InlineListData { enum class Flag : uchar { @@ -79,13 +79,15 @@ public: not_null outResult) const; void animate( - AnimationArgs &&args, + Ui::ReactionFlyAnimationArgs &&args, Fn repaint); [[nodiscard]] auto takeAnimations() - -> base::flat_map>; + -> base::flat_map< + ReactionId, + std::unique_ptr>; void continueAnimations(base::flat_map< ReactionId, - std::unique_ptr> animations); + std::unique_ptr> animations); private: struct Userpics { diff --git a/Telegram/SourceFiles/info/profile/info_profile_emoji_status_panel.cpp b/Telegram/SourceFiles/info/profile/info_profile_emoji_status_panel.cpp index 56fb000f1..5b48b9415 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_emoji_status_panel.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_emoji_status_panel.cpp @@ -11,12 +11,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "data/data_document.h" #include "data/data_emoji_statuses.h" -#include "data/stickers/data_custom_emoji.h" -#include "history/view/reactions/history_view_reactions_animation.h" #include "lang/lang_keys.h" #include "menu/menu_send.h" // SendMenu::Type. #include "ui/boxes/confirm_box.h" #include "ui/boxes/time_picker_box.h" +#include "ui/effects/emoji_fly_animation.h" #include "ui/text/format_values.h" #include "base/unixtime.h" #include "boxes/premium_preview_box.h" @@ -27,8 +26,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "chat_helpers/tabbed_panel.h" #include "chat_helpers/tabbed_selector.h" #include "styles/style_chat_helpers.h" -#include "styles/style_info.h" -#include "styles/style_chat.h" namespace Info::Profile { namespace { @@ -57,116 +54,6 @@ void PickUntilBox(not_null box, Fn callback) { } // namespace -class EmojiStatusPanel::Animation { -public: - Animation( - not_null body, - not_null owner, - HistoryView::Reactions::AnimationArgs &&args, - Fn repaint, - Data::CustomEmojiSizeTag tag); - - [[nodiscard]] not_null layer(); - [[nodiscard]] bool finished() const; - - void repaint(); - bool paintBadgeFrame(not_null widget); - -private: - const int _flySize = 0; - HistoryView::Reactions::Animation _fly; - Ui::RpWidget _layer; - QRect _area; - bool _areaUpdated = false; - QPointer _target; - -}; - -[[nodiscard]] int ComputeFlySize(Data::CustomEmojiSizeTag tag) { - using Tag = Data::CustomEmojiSizeTag; - if (tag == Tag::Normal) { - return st::reactionInlineImage; - } - return int(base::SafeRound( - (st::reactionInlineImage * Data::FrameSizeFromTag(tag) - / float64(Data::FrameSizeFromTag(Tag::Normal))))); -} - -EmojiStatusPanel::Animation::Animation( - not_null body, - not_null owner, - HistoryView::Reactions::AnimationArgs &&args, - Fn repaint, - Data::CustomEmojiSizeTag tag) -: _flySize(ComputeFlySize(tag)) -, _fly( - owner, - std::move(args), - std::move(repaint), - _flySize, - tag) -, _layer(body) { - body->sizeValue() | rpl::start_with_next([=](QSize size) { - _layer.setGeometry(QRect(QPoint(), size)); - }, _layer.lifetime()); - - _layer.paintRequest( - ) | rpl::start_with_next([=](QRect clip) { - const auto target = _target.data(); - if (!target || !target->isVisible()) { - return; - } - auto p = QPainter(&_layer); - - const auto rect = Ui::MapFrom(&_layer, target, target->rect()); - const auto skipx = (rect.width() - _flySize) / 2; - const auto skipy = (rect.height() - _flySize) / 2; - const auto area = _fly.paintGetArea( - p, - QPoint(), - QRect( - rect.topLeft() + QPoint(skipx, skipy), - QSize(_flySize, _flySize)), - st::infoPeerBadge.premiumFg->c, - clip, - crl::now()); - if (_areaUpdated || _area.isEmpty()) { - _area = area; - } else { - _area = _area.united(area); - } - }, _layer.lifetime()); - - _layer.setAttribute(Qt::WA_TransparentForMouseEvents); - _layer.show(); -} - -not_null EmojiStatusPanel::Animation::layer() { - return &_layer; -} - -bool EmojiStatusPanel::Animation::finished() const { - if (const auto target = _target.data()) { - return _fly.finished() || !target->isVisible(); - } - return true; -} - -void EmojiStatusPanel::Animation::repaint() { - if (_area.isEmpty()) { - _layer.update(); - } else { - _layer.update(_area); - _areaUpdated = true; - } -} - -bool EmojiStatusPanel::Animation::paintBadgeFrame( - not_null widget) { - _target = widget; - return !_fly.finished(); -} - EmojiStatusPanel::EmojiStatusPanel() = default; EmojiStatusPanel::~EmojiStatusPanel() = default; @@ -336,12 +223,12 @@ void EmojiStatusPanel::startAnimation( if (!_panelButton || !statusId) { return; } - auto args = HistoryView::Reactions::AnimationArgs{ + auto args = Ui::ReactionFlyAnimationArgs{ .id = { { statusId } }, .flyIcon = from.frame, .flyFrom = body->mapFromGlobal(from.globalStartGeometry), }; - _animation = std::make_unique( + _animation = std::make_unique( body, &owner->reactions(), std::move(args), diff --git a/Telegram/SourceFiles/info/profile/info_profile_emoji_status_panel.h b/Telegram/SourceFiles/info/profile/info_profile_emoji_status_panel.h index cd7649443..a1acf5556 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_emoji_status_panel.h +++ b/Telegram/SourceFiles/info/profile/info_profile_emoji_status_panel.h @@ -20,6 +20,7 @@ class SessionController; namespace Ui { struct MessageSendingAnimationFrom; +class EmojiFlyAnimation; class RpWidget; } // namespace Ui @@ -50,8 +51,6 @@ public: bool paintBadgeFrame(not_null widget); private: - class Animation; - void create(not_null controller); [[nodiscard]] bool filter( not_null controller, @@ -67,7 +66,7 @@ private: Fn _chooseFilter; Fn _chooseCallback; QPointer _panelButton; - std::unique_ptr _animation; + std::unique_ptr _animation; Data::CustomEmojiSizeTag _animationSizeTag = {}; }; diff --git a/Telegram/SourceFiles/ui/effects/emoji_fly_animation.cpp b/Telegram/SourceFiles/ui/effects/emoji_fly_animation.cpp new file mode 100644 index 000000000..8e55994cc --- /dev/null +++ b/Telegram/SourceFiles/ui/effects/emoji_fly_animation.cpp @@ -0,0 +1,104 @@ +/* +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 "ui/effects/emoji_fly_animation.h" + +#include "data/stickers/data_custom_emoji.h" +#include "styles/style_info.h" +#include "styles/style_chat.h" + +namespace Ui { +namespace { + +[[nodiscard]] int ComputeFlySize(Data::CustomEmojiSizeTag tag) { + using Tag = Data::CustomEmojiSizeTag; + if (tag == Tag::Normal) { + return st::reactionInlineImage; + } + return int(base::SafeRound( + (st::reactionInlineImage * Data::FrameSizeFromTag(tag) + / float64(Data::FrameSizeFromTag(Tag::Normal))))); +} + +} // namespace + +EmojiFlyAnimation::EmojiFlyAnimation( + not_null body, + not_null owner, + ReactionFlyAnimationArgs &&args, + Fn repaint, + Data::CustomEmojiSizeTag tag) +: _flySize(ComputeFlySize(tag)) +, _fly( + owner, + std::move(args), + std::move(repaint), + _flySize, + tag) +, _layer(body) { + body->sizeValue() | rpl::start_with_next([=](QSize size) { + _layer.setGeometry(QRect(QPoint(), size)); + }, _layer.lifetime()); + + _layer.paintRequest( + ) | rpl::start_with_next([=](QRect clip) { + const auto target = _target.data(); + if (!target || !target->isVisible()) { + return; + } + auto p = QPainter(&_layer); + + const auto rect = Ui::MapFrom(&_layer, target, target->rect()); + const auto skipx = (rect.width() - _flySize) / 2; + const auto skipy = (rect.height() - _flySize) / 2; + const auto area = _fly.paintGetArea( + p, + QPoint(), + QRect( + rect.topLeft() + QPoint(skipx, skipy), + QSize(_flySize, _flySize)), + st::infoPeerBadge.premiumFg->c, + clip, + crl::now()); + if (_areaUpdated || _area.isEmpty()) { + _area = area; + } else { + _area = _area.united(area); + } + }, _layer.lifetime()); + + _layer.setAttribute(Qt::WA_TransparentForMouseEvents); + _layer.show(); +} + +not_null EmojiFlyAnimation::layer() { + return &_layer; +} + +bool EmojiFlyAnimation::finished() const { + if (const auto target = _target.data()) { + return _fly.finished() || !target->isVisible(); + } + return true; +} + +void EmojiFlyAnimation::repaint() { + if (_area.isEmpty()) { + _layer.update(); + } else { + _layer.update(_area); + _areaUpdated = true; + } +} + +bool EmojiFlyAnimation::paintBadgeFrame( + not_null widget) { + _target = widget; + return !_fly.finished(); +} + +} // namespace Ui diff --git a/Telegram/SourceFiles/ui/effects/emoji_fly_animation.h b/Telegram/SourceFiles/ui/effects/emoji_fly_animation.h new file mode 100644 index 000000000..7bfd08ce2 --- /dev/null +++ b/Telegram/SourceFiles/ui/effects/emoji_fly_animation.h @@ -0,0 +1,40 @@ +/* +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 "ui/effects/reaction_fly_animation.h" +#include "ui/rp_widget.h" + +namespace Ui { + +class EmojiFlyAnimation { +public: + EmojiFlyAnimation( + not_null body, + not_null owner, + Ui::ReactionFlyAnimationArgs &&args, + Fn repaint, + Data::CustomEmojiSizeTag tag); + + [[nodiscard]] not_null layer(); + [[nodiscard]] bool finished() const; + + void repaint(); + bool paintBadgeFrame(not_null widget); + +private: + const int _flySize = 0; + Ui::ReactionFlyAnimation _fly; + Ui::RpWidget _layer; + QRect _area; + bool _areaUpdated = false; + QPointer _target; + +}; + +} // namespace Ui diff --git a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_animation.cpp b/Telegram/SourceFiles/ui/effects/reaction_fly_animation.cpp similarity index 89% rename from Telegram/SourceFiles/history/view/reactions/history_view_reactions_animation.cpp rename to Telegram/SourceFiles/ui/effects/reaction_fly_animation.cpp index a1b1f17d7..21938af5f 100644 --- a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_animation.cpp +++ b/Telegram/SourceFiles/ui/effects/reaction_fly_animation.cpp @@ -5,10 +5,8 @@ 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/reactions/history_view_reactions_animation.h" +#include "ui/effects/reaction_fly_animation.h" -#include "history/view/history_view_element.h" -#include "history/view/history_view_bottom_info.h" #include "ui/text/text_custom_emoji.h" #include "ui/animated_icon.h" #include "ui/painter.h" @@ -19,7 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/random.h" #include "styles/style_chat.h" -namespace HistoryView::Reactions { +namespace Ui { namespace { constexpr auto kFlyDuration = crl::time(300); @@ -33,7 +31,7 @@ constexpr auto kMiniCopiesMaxScaleMax = 0.9; } // namespace -AnimationArgs AnimationArgs::translated(QPoint point) const { +ReactionFlyAnimationArgs ReactionFlyAnimationArgs::translated(QPoint point) const { return { .id = id, .flyIcon = flyIcon, @@ -41,7 +39,7 @@ AnimationArgs AnimationArgs::translated(QPoint point) const { }; } -auto Animation::flyCallback() { +auto ReactionFlyAnimation::flyCallback() { return [=] { if (!_fly.animating()) { _flyIcon = QImage(); @@ -53,7 +51,7 @@ auto Animation::flyCallback() { }; } -auto Animation::callback() { +auto ReactionFlyAnimation::callback() { return [=] { if (_repaint) { _repaint(); @@ -61,9 +59,9 @@ auto Animation::callback() { }; } -Animation::Animation( +ReactionFlyAnimation::ReactionFlyAnimation( not_null<::Data::Reactions*> owner, - AnimationArgs &&args, + ReactionFlyAnimationArgs &&args, Fn repaint, int size, Data::CustomEmojiSizeTag customSizeTag) @@ -82,7 +80,7 @@ Animation::Animation( document, callback(), customSizeTag); - _colored = std::make_unique(); + _colored = std::make_unique(); _customSize = esize; _centerSizeMultiplier = _customSize / float64(size); aroundAnimation = owner->chooseGenericAnimation(document); @@ -96,7 +94,7 @@ Animation::Animation( _centerSizeMultiplier = 1.; } const auto resolve = [&]( - std::unique_ptr &icon, + std::unique_ptr &icon, DocumentData *document, int size) { if (!document) { @@ -106,7 +104,7 @@ Animation::Animation( if (!media || !media->loaded()) { return false; } - icon = Ui::MakeAnimatedIcon({ + icon = MakeAnimatedIcon({ .generator = DocumentIconFrameGenerator(media), .sizeOverride = QSize(size, size), }); @@ -128,9 +126,9 @@ Animation::Animation( _valid = true; } -Animation::~Animation() = default; +ReactionFlyAnimation::~ReactionFlyAnimation() = default; -QRect Animation::paintGetArea( +QRect ReactionFlyAnimation::paintGetArea( QPainter &p, QPoint origin, QRect target, @@ -186,7 +184,7 @@ QRect Animation::paintGetArea( return wide; } -void Animation::paintCenterFrame( +void ReactionFlyAnimation::paintCenterFrame( QPainter &p, QRect target, const QColor &colored, @@ -220,7 +218,7 @@ void Animation::paintCenterFrame( } } -void Animation::paintMiniCopies( +void ReactionFlyAnimation::paintMiniCopies( QPainter &p, QPoint center, const QColor &colored, @@ -240,7 +238,7 @@ void Animation::paintMiniCopies( const auto scaleOut = kMiniCopiesScaleOutDuration / float64(kMiniCopiesDurationMax); _colored->color = colored; - auto context = Ui::Text::CustomEmoji::Context{ + auto context = Text::CustomEmoji::Context{ .preview = preview, .colored = _colored.get(), .size = size, @@ -269,7 +267,7 @@ void Animation::paintMiniCopies( } } -void Animation::generateMiniCopies(int size) { +void ReactionFlyAnimation::generateMiniCopies(int size) { if (!_custom) { return; } @@ -301,7 +299,7 @@ void Animation::generateMiniCopies(int size) { } } -int Animation::computeParabolicTop( +int ReactionFlyAnimation::computeParabolicTop( Parabolic &cache, int from, int to, @@ -339,7 +337,7 @@ int Animation::computeParabolicTop( return int(base::SafeRound(cache.a * t * t + cache.b * t + from)); } -void Animation::startAnimations() { +void ReactionFlyAnimation::startAnimations() { if (const auto center = _center.get()) { _center->animate(callback()); } @@ -351,19 +349,19 @@ void Animation::startAnimations() { } } -void Animation::setRepaintCallback(Fn repaint) { +void ReactionFlyAnimation::setRepaintCallback(Fn repaint) { _repaint = std::move(repaint); } -bool Animation::flying() const { +bool ReactionFlyAnimation::flying() const { return !_flyIcon.isNull(); } -float64 Animation::flyingProgress() const { +float64 ReactionFlyAnimation::flyingProgress() const { return _fly.value(1.); } -bool Animation::finished() const { +bool ReactionFlyAnimation::finished() const { return !_valid || (_flyIcon.isNull() && (!_center || !_center->animating()) diff --git a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_animation.h b/Telegram/SourceFiles/ui/effects/reaction_fly_animation.h similarity index 79% rename from Telegram/SourceFiles/history/view/reactions/history_view_reactions_animation.h rename to Telegram/SourceFiles/ui/effects/reaction_fly_animation.h index 17ac9c3b1..53069db3f 100644 --- a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_animation.h +++ b/Telegram/SourceFiles/ui/effects/reaction_fly_animation.h @@ -10,12 +10,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/effects/animations.h" #include "data/data_message_reaction_id.h" -namespace Ui { -class AnimatedIcon; -} // namespace Lottie - namespace Ui::Text { class CustomEmoji; +struct CustomEmojiColored; } // namespace Ui::Text namespace Data { @@ -23,25 +20,27 @@ class Reactions; enum class CustomEmojiSizeTag : uchar; } // namespace Data -namespace HistoryView::Reactions { +namespace Ui { -struct AnimationArgs { +class AnimatedIcon; + +struct ReactionFlyAnimationArgs { ::Data::ReactionId id; QImage flyIcon; QRect flyFrom; - [[nodiscard]] AnimationArgs translated(QPoint point) const; + [[nodiscard]] ReactionFlyAnimationArgs translated(QPoint point) const; }; -class Animation final { +class ReactionFlyAnimation final { public: - Animation( + ReactionFlyAnimation( not_null<::Data::Reactions*> owner, - AnimationArgs &&args, + ReactionFlyAnimationArgs &&args, Fn repaint, int size, Data::CustomEmojiSizeTag customSizeTag = {}); - ~Animation(); + ~ReactionFlyAnimation(); void setRepaintCallback(Fn repaint); QRect paintGetArea( @@ -95,13 +94,13 @@ private: const not_null<::Data::Reactions*> _owner; Fn _repaint; QImage _flyIcon; - std::unique_ptr _custom; - std::unique_ptr _colored; - std::unique_ptr _center; - std::unique_ptr _effect; + std::unique_ptr _custom; + std::unique_ptr _colored; + std::unique_ptr _center; + std::unique_ptr _effect; std::vector _miniCopies; - Ui::Animations::Simple _fly; - Ui::Animations::Simple _minis; + Animations::Simple _fly; + Animations::Simple _minis; QRect _flyFrom; float64 _centerSizeMultiplier = 0.; int _customSize = 0; @@ -111,4 +110,4 @@ private: }; -} // namespace HistoryView::Reactions +} // namespace Ui diff --git a/Telegram/lib_ui b/Telegram/lib_ui index a755fa391..f450dcf2c 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit a755fa391edad68951142a0fcc034df695a007ca +Subproject commit f450dcf2c5fa1dfa0ad1fda483421c6548ff4fbb