Implement nice topic icon create / edit box.

This commit is contained in:
John Preston 2022-10-06 17:06:18 +04:00
parent 3aa7f4dd62
commit 2c50f7b18c
22 changed files with 596 additions and 274 deletions

View File

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

View File

@ -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<DefaultIcon> value,
Fn<void()> 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<DefaultIcon> value,
Fn<void()> 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<DocumentId> EditIconButton(
not_null<Window::SessionController*> controller,
[[nodiscard]] int32 ChooseNextColorId(
int32 currentId,
std::vector<int32> &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<Ui::AbstractButton*> EditIconButton(
not_null<QWidget*> parent,
int32 colorId,
DocumentId iconId) {
not_null<Window::SessionController*> controller,
rpl::producer<DefaultIcon> defaultIcon,
rpl::producer<DocumentId> iconId,
Fn<bool(not_null<Ui::RpWidget*>)> paintIconFrame) {
using namespace Info::Profile;
struct State {
rpl::variable<DocumentId> iconId;
EmojiStatusPanel panel;
std::unique_ptr<Ui::Text::CustomEmoji> chosen;
QImage empty;
std::unique_ptr<Ui::Text::CustomEmoji> icon;
QImage defaultIcon;
};
const auto tag = Data::CustomEmojiManager::SizeTag::Large;
const auto size = EditIconSize();
const auto result = Ui::CreateChild<Ui::AbstractButton>(parent.get());
result->show();
const auto state = result->lifetime().make_state<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<bool(not_null<Ui::RpWidget*>)> paintIconFrame;
rpl::producer<DocumentId> iconIdValue;
};
[[nodiscard]] IconSelector AddIconSelector(
not_null<Ui::GenericBox*> box,
not_null<Ui::RpWidget*> button,
not_null<Window::SessionController*> controller,
rpl::producer<DefaultIcon> defaultIcon,
rpl::producer<int> coverHeight,
DocumentId iconId,
Fn<void(object_ptr<Ui::RpWidget>)> placeFooter) {
using namespace ChatHelpers;
struct State {
std::unique_ptr<Ui::EmojiFlyAnimation> animation;
rpl::variable<DocumentId> iconId;
QPointer<QWidget> button;
};
const auto state = box->lifetime().make_state<State>(State{
.iconId = iconId,
.button = button.get(),
});
return state->iconId.value();
const auto manager = &controller->session().data().customEmojiManager();
auto factory = [=](DocumentId id, Fn<void()> repaint)
-> std::unique_ptr<Ui::Text::CustomEmoji> {
const auto tag = Data::CustomEmojiManager::SizeTag::Large;
if (const auto colorId = kDefaultIconId) {
return std::make_unique<DefaultIconEmoji>(
rpl::duplicate(defaultIcon),
repaint);
}
return manager->create(id, std::move(repaint), tag);
};
const auto body = box->verticalLayout();
const auto selector = body->add(
object_ptr<EmojiListWidget>(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<Ui::PlainShadow>(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<Ui::EmojiFlyAnimation>(
body,
&owner->reactions(),
std::move(args),
[=] { state->animation->repaint(); },
Data::CustomEmojiSizeTag::Large);
}
state->iconId = data.document->id;
}, selector->lifetime());
auto paintIconFrame = [=](not_null<Ui::RpWidget*> 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> defaultIcon;
rpl::variable<DocumentId> iconId = 0;
std::vector<int32> otherColorIds;
mtpRequestId requestId = 0;
Fn<bool(not_null<Ui::RpWidget*>)> paintIconFrame;
};
const auto state = box->lifetime().make_state<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<Ui::FixedHeightWidget>(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<Ui::VerticalLayout>(box));
const auto title = top->add(
object_ptr<Ui::InputField>(
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<Ui::RpWidget*> 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<Ui::RpWidget> 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<mtpRequestId>();
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();

View File

@ -1093,11 +1093,8 @@ DocumentData *EmojiListWidget::lookupCustomEmoji(
if (section == int(Section::Recent) && index < _recent.size()) {
const auto document = std::get_if<RecentEmojiDocument>(
&_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()) {

View File

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

View File

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

View File

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

View File

@ -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<Reactions::Animation> animation;
mutable std::unique_ptr<Ui::ReactionFlyAnimation> 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<Reactions::Animation*> animation;
not_null<Ui::ReactionFlyAnimation*> animation;
QRect target;
};
std::vector<SingleAnimation> animations;
@ -561,13 +561,13 @@ void BottomInfo::setReactionCount(Reaction &reaction, int count) {
}
void BottomInfo::animateReaction(
Reactions::AnimationArgs &&args,
Ui::ReactionFlyAnimationArgs &&args,
Fn<void()> repaint) {
const auto i = ranges::find(_reactions, args.id, &Reaction::id);
if (i == end(_reactions)) {
return;
}
i->animation = std::make_unique<Reactions::Animation>(
i->animation = std::make_unique<Ui::ReactionFlyAnimation>(
_reactionsOwner,
args.translated(QPoint(width(), height())),
std::move(repaint),
@ -575,10 +575,10 @@ void BottomInfo::animateReaction(
}
auto BottomInfo::takeReactionAnimations()
-> base::flat_map<ReactionId, std::unique_ptr<Reactions::Animation>> {
-> base::flat_map<ReactionId, std::unique_ptr<Ui::ReactionFlyAnimation>> {
auto result = base::flat_map<
ReactionId,
std::unique_ptr<Reactions::Animation>>();
std::unique_ptr<Ui::ReactionFlyAnimation>>();
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<Reactions::Animation>> animations) {
std::unique_ptr<Ui::ReactionFlyAnimation>> animations) {
for (auto &[id, animation] : animations) {
const auto i = ranges::find(_reactions, id, &Reaction::id);
if (i != end(_reactions)) {

View File

@ -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<void()> repaint);
[[nodiscard]] auto takeReactionAnimations()
-> base::flat_map<ReactionId, std::unique_ptr<Reactions::Animation>>;
-> base::flat_map<
ReactionId,
std::unique_ptr<Ui::ReactionFlyAnimation>>;
void continueReactionAnimations(base::flat_map<
ReactionId,
std::unique_ptr<Reactions::Animation>> animations);
std::unique_ptr<Ui::ReactionFlyAnimation>> animations);
private:
struct Reaction;

View File

@ -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<Data::ReactionId, std::unique_ptr<Reactions::Animation>> {
-> base::flat_map<
Data::ReactionId,
std::unique_ptr<Ui::ReactionFlyAnimation>> {
return {};
}

View File

@ -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<Reactions::Animation>>;
std::unique_ptr<Ui::ReactionFlyAnimation>>;
virtual ~Element();

View File

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

View File

@ -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<Reactions::Animation>>();
std::unique_ptr<Ui::ReactionFlyAnimation>>();
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<Data::ReactionId, std::unique_ptr<Reactions::Animation>> {
-> base::flat_map<
Data::ReactionId,
std::unique_ptr<Ui::ReactionFlyAnimation>> {
return _reactions
? _reactions->takeAnimations()
: _bottomInfo.takeReactionAnimations();

View File

@ -147,11 +147,11 @@ public:
void applyGroupAdminChanges(
const base::flat_set<UserId> &changes) override;
void animateReaction(Reactions::AnimationArgs &&args) override;
void animateReaction(Ui::ReactionFlyAnimationArgs &&args) override;
auto takeReactionAnimations()
-> base::flat_map<
Data::ReactionId,
std::unique_ptr<Reactions::Animation>> override;
std::unique_ptr<Ui::ReactionFlyAnimation>> override;
QRect innerGeometry() const override;
[[nodiscard]] BottomRippleMask bottomRippleMask(int buttonHeight) const;

View File

@ -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> animation;
mutable std::unique_ptr<Ui::ReactionFlyAnimation> animation;
mutable QImage image;
mutable ClickHandlerPtr link;
mutable std::unique_ptr<Ui::Text::CustomEmoji> custom;
@ -322,12 +322,12 @@ void InlineList::paint(
int outerWidth,
const QRect &clip) const {
struct SingleAnimation {
not_null<Reactions::Animation*> animation;
not_null<Ui::ReactionFlyAnimation*> animation;
QRect target;
};
std::vector<SingleAnimation> animations;
auto finished = std::vector<std::unique_ptr<Animation>>();
auto finished = std::vector<std::unique_ptr<Ui::ReactionFlyAnimation>>();
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<void()> repaint) {
const auto i = ranges::find(_buttons, args.id, &Button::id);
if (i == end(_buttons)) {
return;
}
i->animation = std::make_unique<Reactions::Animation>(
i->animation = std::make_unique<Ui::ReactionFlyAnimation>(
_owner,
std::move(args),
std::move(repaint),
@ -579,10 +579,10 @@ void InlineList::paintCustomFrame(
}
auto InlineList::takeAnimations()
-> base::flat_map<ReactionId, std::unique_ptr<Reactions::Animation>> {
-> base::flat_map<ReactionId, std::unique_ptr<Ui::ReactionFlyAnimation>> {
auto result = base::flat_map<
ReactionId,
std::unique_ptr<Reactions::Animation>>();
std::unique_ptr<Ui::ReactionFlyAnimation>>();
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<Reactions::Animation>> animations) {
std::unique_ptr<Ui::ReactionFlyAnimation>> animations) {
for (auto &[id, animation] : animations) {
const auto i = ranges::find(_buttons, id, &Button::id);
if (i != end(_buttons)) {

View File

@ -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<TextState*> outResult) const;
void animate(
AnimationArgs &&args,
Ui::ReactionFlyAnimationArgs &&args,
Fn<void()> repaint);
[[nodiscard]] auto takeAnimations()
-> base::flat_map<ReactionId, std::unique_ptr<Reactions::Animation>>;
-> base::flat_map<
ReactionId,
std::unique_ptr<Ui::ReactionFlyAnimation>>;
void continueAnimations(base::flat_map<
ReactionId,
std::unique_ptr<Reactions::Animation>> animations);
std::unique_ptr<Ui::ReactionFlyAnimation>> animations);
private:
struct Userpics {

View File

@ -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<Ui::GenericBox*> box, Fn<void(TimeId)> callback) {
} // namespace
class EmojiStatusPanel::Animation {
public:
Animation(
not_null<Ui::RpWidget*> body,
not_null<Data::Reactions*> owner,
HistoryView::Reactions::AnimationArgs &&args,
Fn<void()> repaint,
Data::CustomEmojiSizeTag tag);
[[nodiscard]] not_null<Ui::RpWidget*> layer();
[[nodiscard]] bool finished() const;
void repaint();
bool paintBadgeFrame(not_null<Ui::RpWidget*> widget);
private:
const int _flySize = 0;
HistoryView::Reactions::Animation _fly;
Ui::RpWidget _layer;
QRect _area;
bool _areaUpdated = false;
QPointer<Ui::RpWidget> _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<Ui::RpWidget*> body,
not_null<Data::Reactions*> owner,
HistoryView::Reactions::AnimationArgs &&args,
Fn<void()> 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<Ui::RpWidget*> 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<Ui::RpWidget*> 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>(
_animation = std::make_unique<Ui::EmojiFlyAnimation>(
body,
&owner->reactions(),
std::move(args),

View File

@ -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<Ui::RpWidget*> widget);
private:
class Animation;
void create(not_null<Window::SessionController*> controller);
[[nodiscard]] bool filter(
not_null<Window::SessionController*> controller,
@ -67,7 +66,7 @@ private:
Fn<bool(DocumentId)> _chooseFilter;
Fn<void(DocumentId)> _chooseCallback;
QPointer<QWidget> _panelButton;
std::unique_ptr<Animation> _animation;
std::unique_ptr<Ui::EmojiFlyAnimation> _animation;
Data::CustomEmojiSizeTag _animationSizeTag = {};
};

View File

@ -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<Ui::RpWidget*> body,
not_null<Data::Reactions*> owner,
ReactionFlyAnimationArgs &&args,
Fn<void()> 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<Ui::RpWidget*> 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<Ui::RpWidget*> widget) {
_target = widget;
return !_fly.finished();
}
} // namespace Ui

View File

@ -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<RpWidget*> body,
not_null<Data::Reactions*> owner,
Ui::ReactionFlyAnimationArgs &&args,
Fn<void()> repaint,
Data::CustomEmojiSizeTag tag);
[[nodiscard]] not_null<Ui::RpWidget*> layer();
[[nodiscard]] bool finished() const;
void repaint();
bool paintBadgeFrame(not_null<Ui::RpWidget*> widget);
private:
const int _flySize = 0;
Ui::ReactionFlyAnimation _fly;
Ui::RpWidget _layer;
QRect _area;
bool _areaUpdated = false;
QPointer<Ui::RpWidget> _target;
};
} // namespace Ui

View File

@ -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<void()> repaint,
int size,
Data::CustomEmojiSizeTag customSizeTag)
@ -82,7 +80,7 @@ Animation::Animation(
document,
callback(),
customSizeTag);
_colored = std::make_unique<Ui::Text::CustomEmojiColored>();
_colored = std::make_unique<Text::CustomEmojiColored>();
_customSize = esize;
_centerSizeMultiplier = _customSize / float64(size);
aroundAnimation = owner->chooseGenericAnimation(document);
@ -96,7 +94,7 @@ Animation::Animation(
_centerSizeMultiplier = 1.;
}
const auto resolve = [&](
std::unique_ptr<Ui::AnimatedIcon> &icon,
std::unique_ptr<AnimatedIcon> &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<void()> repaint) {
void ReactionFlyAnimation::setRepaintCallback(Fn<void()> 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())

View File

@ -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<void()> repaint,
int size,
Data::CustomEmojiSizeTag customSizeTag = {});
~Animation();
~ReactionFlyAnimation();
void setRepaintCallback(Fn<void()> repaint);
QRect paintGetArea(
@ -95,13 +94,13 @@ private:
const not_null<::Data::Reactions*> _owner;
Fn<void()> _repaint;
QImage _flyIcon;
std::unique_ptr<Ui::Text::CustomEmoji> _custom;
std::unique_ptr<Ui::Text::CustomEmojiColored> _colored;
std::unique_ptr<Ui::AnimatedIcon> _center;
std::unique_ptr<Ui::AnimatedIcon> _effect;
std::unique_ptr<Text::CustomEmoji> _custom;
std::unique_ptr<Text::CustomEmojiColored> _colored;
std::unique_ptr<AnimatedIcon> _center;
std::unique_ptr<AnimatedIcon> _effect;
std::vector<MiniCopy> _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

@ -1 +1 @@
Subproject commit a755fa391edad68951142a0fcc034df695a007ca
Subproject commit f450dcf2c5fa1dfa0ad1fda483421c6548ff4fbb