mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-15 21:57:10 +02:00
Implement nice topic icon create / edit box.
This commit is contained in:
parent
3aa7f4dd62
commit
2c50f7b18c
22 changed files with 596 additions and 274 deletions
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {};
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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 = {};
|
||||
|
||||
};
|
||||
|
|
104
Telegram/SourceFiles/ui/effects/emoji_fly_animation.cpp
Normal file
104
Telegram/SourceFiles/ui/effects/emoji_fly_animation.cpp
Normal 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
|
40
Telegram/SourceFiles/ui/effects/emoji_fly_animation.h
Normal file
40
Telegram/SourceFiles/ui/effects/emoji_fly_animation.h
Normal 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
|
|
@ -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())
|
|
@ -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
|
Loading…
Add table
Reference in a new issue