mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +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/media/history_view_web_page.h
|
||||||
history/view/reactions/history_view_reactions.cpp
|
history/view/reactions/history_view_reactions.cpp
|
||||||
history/view/reactions/history_view_reactions.h
|
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.cpp
|
||||||
history/view/reactions/history_view_reactions_button.h
|
history/view/reactions/history_view_reactions_button.h
|
||||||
history/view/reactions/history_view_reactions_list.cpp
|
history/view/reactions/history_view_reactions_list.cpp
|
||||||
|
@ -1233,9 +1231,13 @@ PRIVATE
|
||||||
ui/chat/choose_send_as.h
|
ui/chat/choose_send_as.h
|
||||||
ui/chat/choose_theme_controller.cpp
|
ui/chat/choose_theme_controller.cpp
|
||||||
ui/chat/choose_theme_controller.h
|
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_common.h
|
||||||
ui/effects/message_sending_animation_controller.cpp
|
ui/effects/message_sending_animation_controller.cpp
|
||||||
ui/effects/message_sending_animation_controller.h
|
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.cpp
|
||||||
ui/effects/send_action_animations.h
|
ui/effects/send_action_animations.h
|
||||||
ui/image/image.cpp
|
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 "boxes/peers/edit_forum_topic_box.h"
|
||||||
|
|
||||||
#include "ui/widgets/input_fields.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/abstract_button.h"
|
||||||
#include "ui/color_int_conversion.h"
|
#include "ui/color_int_conversion.h"
|
||||||
#include "data/data_channel.h"
|
#include "data/data_channel.h"
|
||||||
|
#include "data/data_document.h"
|
||||||
#include "data/data_forum.h"
|
#include "data/data_forum.h"
|
||||||
#include "data/data_forum_topic.h"
|
#include "data/data_forum_topic.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/stickers/data_custom_emoji.h"
|
#include "data/stickers/data_custom_emoji.h"
|
||||||
#include "base/random.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 "main/main_session.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "history/view/history_view_replies_section.h"
|
#include "history/view/history_view_replies_section.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "info/profile/info_profile_emoji_status_panel.h"
|
#include "info/profile/info_profile_emoji_status_panel.h"
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
|
#include "window/window_controller.h"
|
||||||
#include "settings/settings_common.h"
|
#include "settings/settings_common.h"
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
|
#include "mainwindow.h"
|
||||||
|
#include "styles/style_layers.h"
|
||||||
#include "styles/style_dialogs.h"
|
#include "styles/style_dialogs.h"
|
||||||
|
#include "styles/style_chat_helpers.h"
|
||||||
|
|
||||||
namespace {
|
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() {
|
[[nodiscard]] int EditIconSize() {
|
||||||
const auto tag = Data::CustomEmojiManager::SizeTag::Large;
|
const auto tag = Data::CustomEmojiManager::SizeTag::Large;
|
||||||
return Data::FrameSizeFromTag(tag) / style::DevicePixelRatio();
|
return Data::FrameSizeFromTag(tag) / style::DevicePixelRatio();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] rpl::producer<DocumentId> EditIconButton(
|
[[nodiscard]] int32 ChooseNextColorId(
|
||||||
not_null<Window::SessionController*> controller,
|
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,
|
not_null<QWidget*> parent,
|
||||||
int32 colorId,
|
not_null<Window::SessionController*> controller,
|
||||||
DocumentId iconId) {
|
rpl::producer<DefaultIcon> defaultIcon,
|
||||||
|
rpl::producer<DocumentId> iconId,
|
||||||
|
Fn<bool(not_null<Ui::RpWidget*>)> paintIconFrame) {
|
||||||
using namespace Info::Profile;
|
using namespace Info::Profile;
|
||||||
struct State {
|
struct State {
|
||||||
rpl::variable<DocumentId> iconId;
|
std::unique_ptr<Ui::Text::CustomEmoji> icon;
|
||||||
EmojiStatusPanel panel;
|
QImage defaultIcon;
|
||||||
std::unique_ptr<Ui::Text::CustomEmoji> chosen;
|
|
||||||
QImage empty;
|
|
||||||
};
|
};
|
||||||
const auto tag = Data::CustomEmojiManager::SizeTag::Large;
|
const auto tag = Data::CustomEmojiManager::SizeTag::Large;
|
||||||
const auto size = EditIconSize();
|
const auto size = EditIconSize();
|
||||||
const auto result = Ui::CreateChild<Ui::AbstractButton>(parent.get());
|
const auto result = Ui::CreateChild<Ui::AbstractButton>(parent.get());
|
||||||
|
result->show();
|
||||||
const auto state = result->lifetime().make_state<State>();
|
const auto state = result->lifetime().make_state<State>();
|
||||||
state->empty = Data::ForumTopicIconBackground(
|
|
||||||
colorId,
|
std::move(
|
||||||
st::largeForumTopicIcon.size);
|
iconId
|
||||||
state->iconId.value(
|
) | rpl::start_with_next([=](DocumentId id) {
|
||||||
) | rpl::start_with_next([=](DocumentId iconId) {
|
|
||||||
const auto owner = &controller->session().data();
|
const auto owner = &controller->session().data();
|
||||||
state->chosen = iconId
|
state->icon = id
|
||||||
? owner->customEmojiManager().create(
|
? owner->customEmojiManager().create(
|
||||||
iconId,
|
id,
|
||||||
[=] { result->update(); },
|
[=] { result->update(); },
|
||||||
tag)
|
tag)
|
||||||
: nullptr;
|
: nullptr;
|
||||||
result->update();
|
result->update();
|
||||||
}, result->lifetime());
|
}, result->lifetime());
|
||||||
state->iconId = iconId;
|
|
||||||
state->panel.setChooseFilter([=](DocumentId) {
|
std::move(
|
||||||
return true;
|
defaultIcon
|
||||||
});
|
) | rpl::start_with_next([=](DefaultIcon icon) {
|
||||||
state->panel.setChooseCallback([=](DocumentId id) {
|
state->defaultIcon = Data::ForumTopicIconFrame(
|
||||||
state->iconId = id;
|
icon.colorId,
|
||||||
});
|
icon.title,
|
||||||
|
st::largeForumTopicIcon);
|
||||||
|
result->update();
|
||||||
|
}, result->lifetime());
|
||||||
|
|
||||||
result->resize(size, size);
|
result->resize(size, size);
|
||||||
result->paintRequest(
|
result->paintRequest(
|
||||||
) | rpl::filter([=] {
|
) | rpl::filter([=] {
|
||||||
return !state->panel.paintBadgeFrame(result);
|
return !paintIconFrame(result);
|
||||||
}) | rpl::start_with_next([=](QRect clip) {
|
}) | rpl::start_with_next([=](QRect clip) {
|
||||||
auto args = Ui::Text::CustomEmoji::Context{
|
auto args = Ui::Text::CustomEmoji::Context{
|
||||||
.preview = st::windowBgOver->c,
|
.preview = st::windowBgOver->c,
|
||||||
|
@ -82,17 +181,142 @@ namespace {
|
||||||
Window::GifPauseReason::Layer),
|
Window::GifPauseReason::Layer),
|
||||||
};
|
};
|
||||||
auto p = QPainter(result);
|
auto p = QPainter(result);
|
||||||
if (state->chosen) {
|
if (state->icon) {
|
||||||
state->chosen->paint(p, args);
|
state->icon->paint(p, args);
|
||||||
} else {
|
} else {
|
||||||
const auto skip = (size - st::largeForumTopicIcon.size) / 2;
|
const auto skip = (size - st::largeForumTopicIcon.size) / 2;
|
||||||
p.drawImage(skip, skip, state->empty);
|
p.drawImage(skip, skip, state->defaultIcon);
|
||||||
}
|
}
|
||||||
}, result->lifetime());
|
}, 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
|
} // namespace
|
||||||
|
@ -116,42 +340,101 @@ void EditForumTopicBox(
|
||||||
// #TODO forum lang
|
// #TODO forum lang
|
||||||
box->setTitle(rpl::single(creating ? u"New topic"_q : u"Edit topic"_q));
|
box->setTitle(rpl::single(creating ? u"New topic"_q : u"Edit topic"_q));
|
||||||
|
|
||||||
|
box->setMaxHeight(st::editTopicMaxHeight);
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
int32 colorId = 0;
|
rpl::variable<DefaultIcon> defaultIcon;
|
||||||
DocumentId iconId = 0;
|
rpl::variable<DocumentId> iconId = 0;
|
||||||
|
std::vector<int32> otherColorIds;
|
||||||
mtpRequestId requestId = 0;
|
mtpRequestId requestId = 0;
|
||||||
|
Fn<bool(not_null<Ui::RpWidget*>)> paintIconFrame;
|
||||||
};
|
};
|
||||||
const auto state = box->lifetime().make_state<State>();
|
const auto state = box->lifetime().make_state<State>();
|
||||||
const auto &colors = Data::ForumTopicColorIds();
|
const auto &colors = Data::ForumTopicColorIds();
|
||||||
state->iconId = topic ? topic->iconId() : 0;
|
state->iconId = topic ? topic->iconId() : 0;
|
||||||
state->colorId = topic
|
state->otherColorIds = colors;
|
||||||
? topic->colorId()
|
state->defaultIcon = DefaultIcon{
|
||||||
: colors[base::RandomIndex(colors.size())];
|
topic ? topic->title() : QString(),
|
||||||
// #TODO forum lang and design
|
topic ? topic->colorId() : ChooseNextColorId(0, state->otherColorIds)
|
||||||
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());
|
|
||||||
|
|
||||||
const auto title = box->addRow(
|
const auto top = box->setPinnedToTopContent(
|
||||||
|
object_ptr<Ui::VerticalLayout>(box));
|
||||||
|
|
||||||
|
const auto title = top->add(
|
||||||
object_ptr<Ui::InputField>(
|
object_ptr<Ui::InputField>(
|
||||||
box,
|
box,
|
||||||
st::defaultInputField,
|
st::defaultInputField,
|
||||||
rpl::single(u"Topic Title"_q),
|
rpl::single(u"Topic Title"_q), // #TODO forum lang
|
||||||
topic ? topic->title() : QString())); // #TODO forum lang
|
topic ? topic->title() : QString()),
|
||||||
|
st::editTopicTitleMargin);
|
||||||
box->setFocusCallback([=] {
|
box->setFocusCallback([=] {
|
||||||
title->setFocusFast();
|
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 requestId = std::make_shared<mtpRequestId>();
|
||||||
const auto create = [=] {
|
const auto create = [=] {
|
||||||
const auto channel = forum->peer->asChannel();
|
const auto channel = forum->peer->asChannel();
|
||||||
|
@ -167,8 +450,8 @@ void EditForumTopicBox(
|
||||||
forum,
|
forum,
|
||||||
channel->forum()->reserveCreatingId(
|
channel->forum()->reserveCreatingId(
|
||||||
title->getLastText().trimmed(),
|
title->getLastText().trimmed(),
|
||||||
state->colorId,
|
state->defaultIcon.current().colorId,
|
||||||
state->iconId)),
|
state->iconId.current())),
|
||||||
Window::SectionShow::Way::ClearStack);
|
Window::SectionShow::Way::ClearStack);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -187,8 +470,8 @@ void EditForumTopicBox(
|
||||||
return;
|
return;
|
||||||
} else if (parent->creating(rootId)) {
|
} else if (parent->creating(rootId)) {
|
||||||
topic->applyTitle(title->getLastText().trimmed());
|
topic->applyTitle(title->getLastText().trimmed());
|
||||||
topic->applyColorId(state->colorId);
|
topic->applyColorId(state->defaultIcon.current().colorId);
|
||||||
topic->applyIconId(state->iconId);
|
topic->applyIconId(state->iconId.current());
|
||||||
} else {
|
} else {
|
||||||
using Flag = MTPchannels_EditForumTopic::Flag;
|
using Flag = MTPchannels_EditForumTopic::Flag;
|
||||||
const auto api = &forum->session().api();
|
const auto api = &forum->session().api();
|
||||||
|
@ -197,7 +480,7 @@ void EditForumTopicBox(
|
||||||
topic->channel()->inputChannel,
|
topic->channel()->inputChannel,
|
||||||
MTP_int(rootId),
|
MTP_int(rootId),
|
||||||
MTP_string(title->getLastText().trimmed()),
|
MTP_string(title->getLastText().trimmed()),
|
||||||
MTP_long(state->iconId)
|
MTP_long(state->iconId.current())
|
||||||
)).done([=](const MTPUpdates &result) {
|
)).done([=](const MTPUpdates &result) {
|
||||||
api->applyUpdates(result);
|
api->applyUpdates(result);
|
||||||
box->closeBox();
|
box->closeBox();
|
||||||
|
|
|
@ -1093,11 +1093,8 @@ DocumentData *EmojiListWidget::lookupCustomEmoji(
|
||||||
if (section == int(Section::Recent) && index < _recent.size()) {
|
if (section == int(Section::Recent) && index < _recent.size()) {
|
||||||
const auto document = std::get_if<RecentEmojiDocument>(
|
const auto document = std::get_if<RecentEmojiDocument>(
|
||||||
&_recent[index].id.data);
|
&_recent[index].id.data);
|
||||||
const auto custom = document
|
if (document) {
|
||||||
? session().data().document(document->id).get()
|
return session().data().document(document->id);
|
||||||
: nullptr;
|
|
||||||
if (custom && custom->sticker()) {
|
|
||||||
return custom;
|
|
||||||
}
|
}
|
||||||
} else if (section >= _staticCount
|
} else if (section >= _staticCount
|
||||||
&& index < _custom[section - _staticCount].list.size()) {
|
&& index < _custom[section - _staticCount].list.size()) {
|
||||||
|
|
|
@ -113,6 +113,7 @@ QImage ForumTopicIconFrame(
|
||||||
if (const auto one = ExtractNonEmojiLetter(title); !one.isEmpty()) {
|
if (const auto one = ExtractNonEmojiLetter(title); !one.isEmpty()) {
|
||||||
auto p = QPainter(&background);
|
auto p = QPainter(&background);
|
||||||
p.setPen(Qt::white);
|
p.setPen(Qt::white);
|
||||||
|
p.setFont(st.font);
|
||||||
p.drawText(
|
p.drawText(
|
||||||
QRect(0, st.textTop, st.size, st.font->height * 2),
|
QRect(0, st.textTop, st.size, st.font->height * 2),
|
||||||
one,
|
one,
|
||||||
|
@ -331,7 +332,9 @@ void ForumTopic::paintUserpic(
|
||||||
const auto size = st::defaultForumTopicIcon.size;
|
const auto size = st::defaultForumTopicIcon.size;
|
||||||
const auto esize = st::emojiSize;
|
const auto esize = st::emojiSize;
|
||||||
const auto shift = (esize - size) / 2;
|
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 {
|
defaultForumTopicIcon: ForumTopicIcon {
|
||||||
size: 21px;
|
size: 21px;
|
||||||
font: font(bold 12px);
|
font: font(bold 11px);
|
||||||
textTop: 3px;
|
textTop: 2px;
|
||||||
}
|
}
|
||||||
largeForumTopicIcon: ForumTopicIcon {
|
largeForumTopicIcon: ForumTopicIcon {
|
||||||
size: 26px;
|
size: 26px;
|
||||||
font: font(bold 13px);
|
font: font(bold 13px);
|
||||||
textTop: 4px;
|
textTop: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
dialogsUnreadFont: font(12px bold);
|
dialogsUnreadFont: font(12px bold);
|
||||||
|
@ -412,3 +412,7 @@ forumTopicRow: DialogRow(defaultDialogRow) {
|
||||||
textLeft: 68px;
|
textLeft: 68px;
|
||||||
textTop: 29px;
|
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_media.h"
|
||||||
#include "history/view/media/history_view_sticker.h"
|
#include "history/view/media/history_view_sticker.h"
|
||||||
#include "history/view/media/history_view_web_page.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_button.h"
|
||||||
#include "history/view/reactions/history_view_reactions_selector.h"
|
#include "history/view/reactions/history_view_reactions_selector.h"
|
||||||
#include "history/view/history_view_message.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/toasts/common_toasts.h"
|
||||||
#include "ui/effects/path_shift_gradient.h"
|
#include "ui/effects/path_shift_gradient.h"
|
||||||
#include "ui/effects/message_sending_animation_controller.h"
|
#include "ui/effects/message_sending_animation_controller.h"
|
||||||
|
#include "ui/effects/reaction_fly_animation.h"
|
||||||
#include "ui/text/text_options.h"
|
#include "ui/text/text_options.h"
|
||||||
#include "ui/boxes/report_box.h"
|
#include "ui/boxes/report_box.h"
|
||||||
#include "ui/layers/generic_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/message_bubble.h"
|
||||||
#include "ui/chat/chat_style.h"
|
#include "ui/chat/chat_style.h"
|
||||||
|
#include "ui/effects/reaction_fly_animation.h"
|
||||||
#include "ui/text/text_options.h"
|
#include "ui/text/text_options.h"
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
#include "ui/painter.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_item_components.h"
|
||||||
#include "history/history_message.h"
|
#include "history/history_message.h"
|
||||||
#include "history/history.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_message.h"
|
||||||
#include "history/view/history_view_cursor_state.h"
|
#include "history/view/history_view_cursor_state.h"
|
||||||
#include "core/click_handler_types.h"
|
#include "core/click_handler_types.h"
|
||||||
|
@ -31,7 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
|
|
||||||
struct BottomInfo::Reaction {
|
struct BottomInfo::Reaction {
|
||||||
mutable std::unique_ptr<Reactions::Animation> animation;
|
mutable std::unique_ptr<Ui::ReactionFlyAnimation> animation;
|
||||||
mutable QImage image;
|
mutable QImage image;
|
||||||
ReactionId id;
|
ReactionId id;
|
||||||
QString countText;
|
QString countText;
|
||||||
|
@ -332,7 +332,7 @@ void BottomInfo::paintReactions(
|
||||||
int availableWidth,
|
int availableWidth,
|
||||||
const PaintContext &context) const {
|
const PaintContext &context) const {
|
||||||
struct SingleAnimation {
|
struct SingleAnimation {
|
||||||
not_null<Reactions::Animation*> animation;
|
not_null<Ui::ReactionFlyAnimation*> animation;
|
||||||
QRect target;
|
QRect target;
|
||||||
};
|
};
|
||||||
std::vector<SingleAnimation> animations;
|
std::vector<SingleAnimation> animations;
|
||||||
|
@ -561,13 +561,13 @@ void BottomInfo::setReactionCount(Reaction &reaction, int count) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void BottomInfo::animateReaction(
|
void BottomInfo::animateReaction(
|
||||||
Reactions::AnimationArgs &&args,
|
Ui::ReactionFlyAnimationArgs &&args,
|
||||||
Fn<void()> repaint) {
|
Fn<void()> repaint) {
|
||||||
const auto i = ranges::find(_reactions, args.id, &Reaction::id);
|
const auto i = ranges::find(_reactions, args.id, &Reaction::id);
|
||||||
if (i == end(_reactions)) {
|
if (i == end(_reactions)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
i->animation = std::make_unique<Reactions::Animation>(
|
i->animation = std::make_unique<Ui::ReactionFlyAnimation>(
|
||||||
_reactionsOwner,
|
_reactionsOwner,
|
||||||
args.translated(QPoint(width(), height())),
|
args.translated(QPoint(width(), height())),
|
||||||
std::move(repaint),
|
std::move(repaint),
|
||||||
|
@ -575,10 +575,10 @@ void BottomInfo::animateReaction(
|
||||||
}
|
}
|
||||||
|
|
||||||
auto BottomInfo::takeReactionAnimations()
|
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<
|
auto result = base::flat_map<
|
||||||
ReactionId,
|
ReactionId,
|
||||||
std::unique_ptr<Reactions::Animation>>();
|
std::unique_ptr<Ui::ReactionFlyAnimation>>();
|
||||||
for (auto &reaction : _reactions) {
|
for (auto &reaction : _reactions) {
|
||||||
if (reaction.animation) {
|
if (reaction.animation) {
|
||||||
result.emplace(reaction.id, std::move(reaction.animation));
|
result.emplace(reaction.id, std::move(reaction.animation));
|
||||||
|
@ -589,7 +589,7 @@ auto BottomInfo::takeReactionAnimations()
|
||||||
|
|
||||||
void BottomInfo::continueReactionAnimations(base::flat_map<
|
void BottomInfo::continueReactionAnimations(base::flat_map<
|
||||||
ReactionId,
|
ReactionId,
|
||||||
std::unique_ptr<Reactions::Animation>> animations) {
|
std::unique_ptr<Ui::ReactionFlyAnimation>> animations) {
|
||||||
for (auto &[id, animation] : animations) {
|
for (auto &[id, animation] : animations) {
|
||||||
const auto i = ranges::find(_reactions, id, &Reaction::id);
|
const auto i = ranges::find(_reactions, id, &Reaction::id);
|
||||||
if (i != end(_reactions)) {
|
if (i != end(_reactions)) {
|
||||||
|
|
|
@ -14,6 +14,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
struct ChatPaintContext;
|
struct ChatPaintContext;
|
||||||
class AnimatedIcon;
|
class AnimatedIcon;
|
||||||
|
struct ReactionFlyAnimationArgs;
|
||||||
|
class ReactionFlyAnimation;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
|
@ -23,10 +25,6 @@ struct MessageReaction;
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
namespace Reactions {
|
|
||||||
class Animation;
|
|
||||||
struct AnimationArgs;
|
|
||||||
} // namespace Reactions
|
|
||||||
|
|
||||||
using PaintContext = Ui::ChatPaintContext;
|
using PaintContext = Ui::ChatPaintContext;
|
||||||
|
|
||||||
|
@ -80,13 +78,15 @@ public:
|
||||||
const PaintContext &context) const;
|
const PaintContext &context) const;
|
||||||
|
|
||||||
void animateReaction(
|
void animateReaction(
|
||||||
Reactions::AnimationArgs &&args,
|
Ui::ReactionFlyAnimationArgs &&args,
|
||||||
Fn<void()> repaint);
|
Fn<void()> repaint);
|
||||||
[[nodiscard]] auto takeReactionAnimations()
|
[[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<
|
void continueReactionAnimations(base::flat_map<
|
||||||
ReactionId,
|
ReactionId,
|
||||||
std::unique_ptr<Reactions::Animation>> animations);
|
std::unique_ptr<Ui::ReactionFlyAnimation>> animations);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Reaction;
|
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_sticker.h"
|
||||||
#include "history/view/media/history_view_large_emoji.h"
|
#include "history/view/media/history_view_large_emoji.h"
|
||||||
#include "history/view/media/history_view_custom_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_button.h"
|
||||||
#include "history/view/reactions/history_view_reactions.h"
|
#include "history/view/reactions/history_view_reactions.h"
|
||||||
#include "history/view/history_view_cursor_state.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 "chat_helpers/stickers_emoji_pack.h"
|
||||||
#include "window/window_session_controller.h"
|
#include "window/window_session_controller.h"
|
||||||
#include "ui/effects/path_shift_gradient.h"
|
#include "ui/effects/path_shift_gradient.h"
|
||||||
|
#include "ui/effects/reaction_fly_animation.h"
|
||||||
#include "ui/chat/chat_style.h"
|
#include "ui/chat/chat_style.h"
|
||||||
#include "ui/toast/toast.h"
|
#include "ui/toast/toast.h"
|
||||||
#include "ui/toasts/common_toasts.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() {
|
void Element::animateUnreadReactions() {
|
||||||
|
@ -1283,7 +1283,9 @@ void Element::animateUnreadReactions() {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Element::takeReactionAnimations()
|
auto Element::takeReactionAnimations()
|
||||||
-> base::flat_map<Data::ReactionId, std::unique_ptr<Reactions::Animation>> {
|
-> base::flat_map<
|
||||||
|
Data::ReactionId,
|
||||||
|
std::unique_ptr<Ui::ReactionFlyAnimation>> {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,12 +33,12 @@ class PathShiftGradient;
|
||||||
struct BubblePattern;
|
struct BubblePattern;
|
||||||
struct ChatPaintContext;
|
struct ChatPaintContext;
|
||||||
class ChatStyle;
|
class ChatStyle;
|
||||||
|
struct ReactionFlyAnimationArgs;
|
||||||
|
class ReactionFlyAnimation;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
namespace HistoryView::Reactions {
|
namespace HistoryView::Reactions {
|
||||||
struct ButtonParameters;
|
struct ButtonParameters;
|
||||||
struct AnimationArgs;
|
|
||||||
class Animation;
|
|
||||||
class InlineList;
|
class InlineList;
|
||||||
} // namespace HistoryView::Reactions
|
} // namespace HistoryView::Reactions
|
||||||
|
|
||||||
|
@ -454,12 +454,12 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] bool markSponsoredViewed(int shownFromTop) const;
|
[[nodiscard]] bool markSponsoredViewed(int shownFromTop) const;
|
||||||
|
|
||||||
virtual void animateReaction(Reactions::AnimationArgs &&args);
|
virtual void animateReaction(Ui::ReactionFlyAnimationArgs &&args);
|
||||||
void animateUnreadReactions();
|
void animateUnreadReactions();
|
||||||
[[nodiscard]] virtual auto takeReactionAnimations()
|
[[nodiscard]] virtual auto takeReactionAnimations()
|
||||||
-> base::flat_map<
|
-> base::flat_map<
|
||||||
Data::ReactionId,
|
Data::ReactionId,
|
||||||
std::unique_ptr<Reactions::Animation>>;
|
std::unique_ptr<Ui::ReactionFlyAnimation>>;
|
||||||
|
|
||||||
virtual ~Element();
|
virtual ~Element();
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/history_item_text.h"
|
#include "history/history_item_text.h"
|
||||||
#include "history/view/media/history_view_media.h"
|
#include "history/view/media/history_view_media.h"
|
||||||
#include "history/view/media/history_view_sticker.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_button.h"
|
||||||
#include "history/view/reactions/history_view_reactions_selector.h"
|
#include "history/view/reactions/history_view_reactions_selector.h"
|
||||||
#include "history/view/history_view_context_menu.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/inactive_press.h"
|
||||||
#include "ui/effects/message_sending_animation_controller.h"
|
#include "ui/effects/message_sending_animation_controller.h"
|
||||||
#include "ui/effects/path_shift_gradient.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_theme.h"
|
||||||
#include "ui/chat/chat_style.h"
|
#include "ui/chat/chat_style.h"
|
||||||
#include "ui/painter.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_media.h"
|
||||||
#include "history/view/media/history_view_web_page.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.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_button.h"
|
||||||
#include "history/view/history_view_group_call_bar.h" // UserpicInRow.
|
#include "history/view/history_view_group_call_bar.h" // UserpicInRow.
|
||||||
#include "history/view/history_view_view_button.h" // ViewButton.
|
#include "history/view/history_view_view_button.h" // ViewButton.
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "boxes/share_box.h"
|
#include "boxes/share_box.h"
|
||||||
#include "ui/effects/ripple_animation.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/message_bubble.h"
|
||||||
#include "ui/chat/chat_style.h"
|
#include "ui/chat/chat_style.h"
|
||||||
#include "ui/toast/toast.h"
|
#include "ui/toast/toast.h"
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
#include "ui/text/text_entity.h"
|
#include "ui/text/text_entity.h"
|
||||||
#include "ui/cached_round_corners.h"
|
#include "ui/cached_round_corners.h"
|
||||||
|
#include "base/unixtime.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
#include "data/data_channel.h"
|
#include "data/data_channel.h"
|
||||||
|
@ -301,7 +301,7 @@ Message::Message(
|
||||||
? replacing->takeReactionAnimations()
|
? replacing->takeReactionAnimations()
|
||||||
: base::flat_map<
|
: base::flat_map<
|
||||||
Data::ReactionId,
|
Data::ReactionId,
|
||||||
std::unique_ptr<Reactions::Animation>>();
|
std::unique_ptr<Ui::ReactionFlyAnimation>>();
|
||||||
if (!animations.empty()) {
|
if (!animations.empty()) {
|
||||||
const auto repainter = [=] { repaint(); };
|
const auto repainter = [=] { repaint(); };
|
||||||
for (const auto &[id, animation] : animations) {
|
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 item = message();
|
||||||
const auto media = this->media();
|
const auto media = this->media();
|
||||||
|
|
||||||
|
@ -475,7 +475,9 @@ void Message::animateReaction(Reactions::AnimationArgs &&args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Message::takeReactionAnimations()
|
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
|
return _reactions
|
||||||
? _reactions->takeAnimations()
|
? _reactions->takeAnimations()
|
||||||
: _bottomInfo.takeReactionAnimations();
|
: _bottomInfo.takeReactionAnimations();
|
||||||
|
|
|
@ -147,11 +147,11 @@ public:
|
||||||
void applyGroupAdminChanges(
|
void applyGroupAdminChanges(
|
||||||
const base::flat_set<UserId> &changes) override;
|
const base::flat_set<UserId> &changes) override;
|
||||||
|
|
||||||
void animateReaction(Reactions::AnimationArgs &&args) override;
|
void animateReaction(Ui::ReactionFlyAnimationArgs &&args) override;
|
||||||
auto takeReactionAnimations()
|
auto takeReactionAnimations()
|
||||||
-> base::flat_map<
|
-> base::flat_map<
|
||||||
Data::ReactionId,
|
Data::ReactionId,
|
||||||
std::unique_ptr<Reactions::Animation>> override;
|
std::unique_ptr<Ui::ReactionFlyAnimation>> override;
|
||||||
|
|
||||||
QRect innerGeometry() const override;
|
QRect innerGeometry() const override;
|
||||||
[[nodiscard]] BottomRippleMask bottomRippleMask(int buttonHeight) const;
|
[[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_message.h"
|
||||||
#include "history/history.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_message.h"
|
||||||
#include "history/view/history_view_cursor_state.h"
|
#include "history/view/history_view_cursor_state.h"
|
||||||
#include "history/view/history_view_group_call_bar.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 "lang/lang_tag.h"
|
||||||
#include "ui/text/text_custom_emoji.h"
|
#include "ui/text/text_custom_emoji.h"
|
||||||
#include "ui/chat/chat_style.h"
|
#include "ui/chat/chat_style.h"
|
||||||
|
#include "ui/effects/reaction_fly_animation.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
#include "styles/style_chat.h"
|
#include "styles/style_chat.h"
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ constexpr auto kMaxNicePerRow = 5;
|
||||||
|
|
||||||
struct InlineList::Button {
|
struct InlineList::Button {
|
||||||
QRect geometry;
|
QRect geometry;
|
||||||
mutable std::unique_ptr<Animation> animation;
|
mutable std::unique_ptr<Ui::ReactionFlyAnimation> animation;
|
||||||
mutable QImage image;
|
mutable QImage image;
|
||||||
mutable ClickHandlerPtr link;
|
mutable ClickHandlerPtr link;
|
||||||
mutable std::unique_ptr<Ui::Text::CustomEmoji> custom;
|
mutable std::unique_ptr<Ui::Text::CustomEmoji> custom;
|
||||||
|
@ -322,12 +322,12 @@ void InlineList::paint(
|
||||||
int outerWidth,
|
int outerWidth,
|
||||||
const QRect &clip) const {
|
const QRect &clip) const {
|
||||||
struct SingleAnimation {
|
struct SingleAnimation {
|
||||||
not_null<Reactions::Animation*> animation;
|
not_null<Ui::ReactionFlyAnimation*> animation;
|
||||||
QRect target;
|
QRect target;
|
||||||
};
|
};
|
||||||
std::vector<SingleAnimation> animations;
|
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 st = context.st;
|
||||||
const auto stm = context.messageStyle();
|
const auto stm = context.messageStyle();
|
||||||
const auto padding = st::reactionInlinePadding;
|
const auto padding = st::reactionInlinePadding;
|
||||||
|
@ -500,13 +500,13 @@ bool InlineList::getState(
|
||||||
}
|
}
|
||||||
|
|
||||||
void InlineList::animate(
|
void InlineList::animate(
|
||||||
AnimationArgs &&args,
|
Ui::ReactionFlyAnimationArgs &&args,
|
||||||
Fn<void()> repaint) {
|
Fn<void()> repaint) {
|
||||||
const auto i = ranges::find(_buttons, args.id, &Button::id);
|
const auto i = ranges::find(_buttons, args.id, &Button::id);
|
||||||
if (i == end(_buttons)) {
|
if (i == end(_buttons)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
i->animation = std::make_unique<Reactions::Animation>(
|
i->animation = std::make_unique<Ui::ReactionFlyAnimation>(
|
||||||
_owner,
|
_owner,
|
||||||
std::move(args),
|
std::move(args),
|
||||||
std::move(repaint),
|
std::move(repaint),
|
||||||
|
@ -579,10 +579,10 @@ void InlineList::paintCustomFrame(
|
||||||
}
|
}
|
||||||
|
|
||||||
auto InlineList::takeAnimations()
|
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<
|
auto result = base::flat_map<
|
||||||
ReactionId,
|
ReactionId,
|
||||||
std::unique_ptr<Reactions::Animation>>();
|
std::unique_ptr<Ui::ReactionFlyAnimation>>();
|
||||||
for (auto &button : _buttons) {
|
for (auto &button : _buttons) {
|
||||||
if (button.animation) {
|
if (button.animation) {
|
||||||
result.emplace(button.id, std::move(button.animation));
|
result.emplace(button.id, std::move(button.animation));
|
||||||
|
@ -593,7 +593,7 @@ auto InlineList::takeAnimations()
|
||||||
|
|
||||||
void InlineList::continueAnimations(base::flat_map<
|
void InlineList::continueAnimations(base::flat_map<
|
||||||
ReactionId,
|
ReactionId,
|
||||||
std::unique_ptr<Reactions::Animation>> animations) {
|
std::unique_ptr<Ui::ReactionFlyAnimation>> animations) {
|
||||||
for (auto &[id, animation] : animations) {
|
for (auto &[id, animation] : animations) {
|
||||||
const auto i = ranges::find(_buttons, id, &Button::id);
|
const auto i = ranges::find(_buttons, id, &Button::id);
|
||||||
if (i != end(_buttons)) {
|
if (i != end(_buttons)) {
|
||||||
|
|
|
@ -17,6 +17,8 @@ class Reactions;
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
struct ChatPaintContext;
|
struct ChatPaintContext;
|
||||||
|
struct ReactionFlyAnimationArgs;
|
||||||
|
class ReactionFlyAnimation;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
|
@ -30,8 +32,6 @@ namespace HistoryView::Reactions {
|
||||||
|
|
||||||
using ::Data::ReactionId;
|
using ::Data::ReactionId;
|
||||||
using ::Data::MessageReaction;
|
using ::Data::MessageReaction;
|
||||||
struct AnimationArgs;
|
|
||||||
class Animation;
|
|
||||||
|
|
||||||
struct InlineListData {
|
struct InlineListData {
|
||||||
enum class Flag : uchar {
|
enum class Flag : uchar {
|
||||||
|
@ -79,13 +79,15 @@ public:
|
||||||
not_null<TextState*> outResult) const;
|
not_null<TextState*> outResult) const;
|
||||||
|
|
||||||
void animate(
|
void animate(
|
||||||
AnimationArgs &&args,
|
Ui::ReactionFlyAnimationArgs &&args,
|
||||||
Fn<void()> repaint);
|
Fn<void()> repaint);
|
||||||
[[nodiscard]] auto takeAnimations()
|
[[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<
|
void continueAnimations(base::flat_map<
|
||||||
ReactionId,
|
ReactionId,
|
||||||
std::unique_ptr<Reactions::Animation>> animations);
|
std::unique_ptr<Ui::ReactionFlyAnimation>> animations);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Userpics {
|
struct Userpics {
|
||||||
|
|
|
@ -11,12 +11,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_document.h"
|
#include "data/data_document.h"
|
||||||
#include "data/data_emoji_statuses.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 "lang/lang_keys.h"
|
||||||
#include "menu/menu_send.h" // SendMenu::Type.
|
#include "menu/menu_send.h" // SendMenu::Type.
|
||||||
#include "ui/boxes/confirm_box.h"
|
#include "ui/boxes/confirm_box.h"
|
||||||
#include "ui/boxes/time_picker_box.h"
|
#include "ui/boxes/time_picker_box.h"
|
||||||
|
#include "ui/effects/emoji_fly_animation.h"
|
||||||
#include "ui/text/format_values.h"
|
#include "ui/text/format_values.h"
|
||||||
#include "base/unixtime.h"
|
#include "base/unixtime.h"
|
||||||
#include "boxes/premium_preview_box.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_panel.h"
|
||||||
#include "chat_helpers/tabbed_selector.h"
|
#include "chat_helpers/tabbed_selector.h"
|
||||||
#include "styles/style_chat_helpers.h"
|
#include "styles/style_chat_helpers.h"
|
||||||
#include "styles/style_info.h"
|
|
||||||
#include "styles/style_chat.h"
|
|
||||||
|
|
||||||
namespace Info::Profile {
|
namespace Info::Profile {
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -57,116 +54,6 @@ void PickUntilBox(not_null<Ui::GenericBox*> box, Fn<void(TimeId)> callback) {
|
||||||
|
|
||||||
} // namespace
|
} // 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;
|
||||||
|
|
||||||
EmojiStatusPanel::~EmojiStatusPanel() = default;
|
EmojiStatusPanel::~EmojiStatusPanel() = default;
|
||||||
|
@ -336,12 +223,12 @@ void EmojiStatusPanel::startAnimation(
|
||||||
if (!_panelButton || !statusId) {
|
if (!_panelButton || !statusId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto args = HistoryView::Reactions::AnimationArgs{
|
auto args = Ui::ReactionFlyAnimationArgs{
|
||||||
.id = { { statusId } },
|
.id = { { statusId } },
|
||||||
.flyIcon = from.frame,
|
.flyIcon = from.frame,
|
||||||
.flyFrom = body->mapFromGlobal(from.globalStartGeometry),
|
.flyFrom = body->mapFromGlobal(from.globalStartGeometry),
|
||||||
};
|
};
|
||||||
_animation = std::make_unique<Animation>(
|
_animation = std::make_unique<Ui::EmojiFlyAnimation>(
|
||||||
body,
|
body,
|
||||||
&owner->reactions(),
|
&owner->reactions(),
|
||||||
std::move(args),
|
std::move(args),
|
||||||
|
|
|
@ -20,6 +20,7 @@ class SessionController;
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
struct MessageSendingAnimationFrom;
|
struct MessageSendingAnimationFrom;
|
||||||
|
class EmojiFlyAnimation;
|
||||||
class RpWidget;
|
class RpWidget;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
|
@ -50,8 +51,6 @@ public:
|
||||||
bool paintBadgeFrame(not_null<Ui::RpWidget*> widget);
|
bool paintBadgeFrame(not_null<Ui::RpWidget*> widget);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class Animation;
|
|
||||||
|
|
||||||
void create(not_null<Window::SessionController*> controller);
|
void create(not_null<Window::SessionController*> controller);
|
||||||
[[nodiscard]] bool filter(
|
[[nodiscard]] bool filter(
|
||||||
not_null<Window::SessionController*> controller,
|
not_null<Window::SessionController*> controller,
|
||||||
|
@ -67,7 +66,7 @@ private:
|
||||||
Fn<bool(DocumentId)> _chooseFilter;
|
Fn<bool(DocumentId)> _chooseFilter;
|
||||||
Fn<void(DocumentId)> _chooseCallback;
|
Fn<void(DocumentId)> _chooseCallback;
|
||||||
QPointer<QWidget> _panelButton;
|
QPointer<QWidget> _panelButton;
|
||||||
std::unique_ptr<Animation> _animation;
|
std::unique_ptr<Ui::EmojiFlyAnimation> _animation;
|
||||||
Data::CustomEmojiSizeTag _animationSizeTag = {};
|
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:
|
For license and copyright information please follow this link:
|
||||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
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/text/text_custom_emoji.h"
|
||||||
#include "ui/animated_icon.h"
|
#include "ui/animated_icon.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
|
@ -19,7 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "base/random.h"
|
#include "base/random.h"
|
||||||
#include "styles/style_chat.h"
|
#include "styles/style_chat.h"
|
||||||
|
|
||||||
namespace HistoryView::Reactions {
|
namespace Ui {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr auto kFlyDuration = crl::time(300);
|
constexpr auto kFlyDuration = crl::time(300);
|
||||||
|
@ -33,7 +31,7 @@ constexpr auto kMiniCopiesMaxScaleMax = 0.9;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
AnimationArgs AnimationArgs::translated(QPoint point) const {
|
ReactionFlyAnimationArgs ReactionFlyAnimationArgs::translated(QPoint point) const {
|
||||||
return {
|
return {
|
||||||
.id = id,
|
.id = id,
|
||||||
.flyIcon = flyIcon,
|
.flyIcon = flyIcon,
|
||||||
|
@ -41,7 +39,7 @@ AnimationArgs AnimationArgs::translated(QPoint point) const {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Animation::flyCallback() {
|
auto ReactionFlyAnimation::flyCallback() {
|
||||||
return [=] {
|
return [=] {
|
||||||
if (!_fly.animating()) {
|
if (!_fly.animating()) {
|
||||||
_flyIcon = QImage();
|
_flyIcon = QImage();
|
||||||
|
@ -53,7 +51,7 @@ auto Animation::flyCallback() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Animation::callback() {
|
auto ReactionFlyAnimation::callback() {
|
||||||
return [=] {
|
return [=] {
|
||||||
if (_repaint) {
|
if (_repaint) {
|
||||||
_repaint();
|
_repaint();
|
||||||
|
@ -61,9 +59,9 @@ auto Animation::callback() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Animation::Animation(
|
ReactionFlyAnimation::ReactionFlyAnimation(
|
||||||
not_null<::Data::Reactions*> owner,
|
not_null<::Data::Reactions*> owner,
|
||||||
AnimationArgs &&args,
|
ReactionFlyAnimationArgs &&args,
|
||||||
Fn<void()> repaint,
|
Fn<void()> repaint,
|
||||||
int size,
|
int size,
|
||||||
Data::CustomEmojiSizeTag customSizeTag)
|
Data::CustomEmojiSizeTag customSizeTag)
|
||||||
|
@ -82,7 +80,7 @@ Animation::Animation(
|
||||||
document,
|
document,
|
||||||
callback(),
|
callback(),
|
||||||
customSizeTag);
|
customSizeTag);
|
||||||
_colored = std::make_unique<Ui::Text::CustomEmojiColored>();
|
_colored = std::make_unique<Text::CustomEmojiColored>();
|
||||||
_customSize = esize;
|
_customSize = esize;
|
||||||
_centerSizeMultiplier = _customSize / float64(size);
|
_centerSizeMultiplier = _customSize / float64(size);
|
||||||
aroundAnimation = owner->chooseGenericAnimation(document);
|
aroundAnimation = owner->chooseGenericAnimation(document);
|
||||||
|
@ -96,7 +94,7 @@ Animation::Animation(
|
||||||
_centerSizeMultiplier = 1.;
|
_centerSizeMultiplier = 1.;
|
||||||
}
|
}
|
||||||
const auto resolve = [&](
|
const auto resolve = [&](
|
||||||
std::unique_ptr<Ui::AnimatedIcon> &icon,
|
std::unique_ptr<AnimatedIcon> &icon,
|
||||||
DocumentData *document,
|
DocumentData *document,
|
||||||
int size) {
|
int size) {
|
||||||
if (!document) {
|
if (!document) {
|
||||||
|
@ -106,7 +104,7 @@ Animation::Animation(
|
||||||
if (!media || !media->loaded()) {
|
if (!media || !media->loaded()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
icon = Ui::MakeAnimatedIcon({
|
icon = MakeAnimatedIcon({
|
||||||
.generator = DocumentIconFrameGenerator(media),
|
.generator = DocumentIconFrameGenerator(media),
|
||||||
.sizeOverride = QSize(size, size),
|
.sizeOverride = QSize(size, size),
|
||||||
});
|
});
|
||||||
|
@ -128,9 +126,9 @@ Animation::Animation(
|
||||||
_valid = true;
|
_valid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Animation::~Animation() = default;
|
ReactionFlyAnimation::~ReactionFlyAnimation() = default;
|
||||||
|
|
||||||
QRect Animation::paintGetArea(
|
QRect ReactionFlyAnimation::paintGetArea(
|
||||||
QPainter &p,
|
QPainter &p,
|
||||||
QPoint origin,
|
QPoint origin,
|
||||||
QRect target,
|
QRect target,
|
||||||
|
@ -186,7 +184,7 @@ QRect Animation::paintGetArea(
|
||||||
return wide;
|
return wide;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Animation::paintCenterFrame(
|
void ReactionFlyAnimation::paintCenterFrame(
|
||||||
QPainter &p,
|
QPainter &p,
|
||||||
QRect target,
|
QRect target,
|
||||||
const QColor &colored,
|
const QColor &colored,
|
||||||
|
@ -220,7 +218,7 @@ void Animation::paintCenterFrame(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Animation::paintMiniCopies(
|
void ReactionFlyAnimation::paintMiniCopies(
|
||||||
QPainter &p,
|
QPainter &p,
|
||||||
QPoint center,
|
QPoint center,
|
||||||
const QColor &colored,
|
const QColor &colored,
|
||||||
|
@ -240,7 +238,7 @@ void Animation::paintMiniCopies(
|
||||||
const auto scaleOut = kMiniCopiesScaleOutDuration
|
const auto scaleOut = kMiniCopiesScaleOutDuration
|
||||||
/ float64(kMiniCopiesDurationMax);
|
/ float64(kMiniCopiesDurationMax);
|
||||||
_colored->color = colored;
|
_colored->color = colored;
|
||||||
auto context = Ui::Text::CustomEmoji::Context{
|
auto context = Text::CustomEmoji::Context{
|
||||||
.preview = preview,
|
.preview = preview,
|
||||||
.colored = _colored.get(),
|
.colored = _colored.get(),
|
||||||
.size = size,
|
.size = size,
|
||||||
|
@ -269,7 +267,7 @@ void Animation::paintMiniCopies(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Animation::generateMiniCopies(int size) {
|
void ReactionFlyAnimation::generateMiniCopies(int size) {
|
||||||
if (!_custom) {
|
if (!_custom) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -301,7 +299,7 @@ void Animation::generateMiniCopies(int size) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int Animation::computeParabolicTop(
|
int ReactionFlyAnimation::computeParabolicTop(
|
||||||
Parabolic &cache,
|
Parabolic &cache,
|
||||||
int from,
|
int from,
|
||||||
int to,
|
int to,
|
||||||
|
@ -339,7 +337,7 @@ int Animation::computeParabolicTop(
|
||||||
return int(base::SafeRound(cache.a * t * t + cache.b * t + from));
|
return int(base::SafeRound(cache.a * t * t + cache.b * t + from));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Animation::startAnimations() {
|
void ReactionFlyAnimation::startAnimations() {
|
||||||
if (const auto center = _center.get()) {
|
if (const auto center = _center.get()) {
|
||||||
_center->animate(callback());
|
_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);
|
_repaint = std::move(repaint);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Animation::flying() const {
|
bool ReactionFlyAnimation::flying() const {
|
||||||
return !_flyIcon.isNull();
|
return !_flyIcon.isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
float64 Animation::flyingProgress() const {
|
float64 ReactionFlyAnimation::flyingProgress() const {
|
||||||
return _fly.value(1.);
|
return _fly.value(1.);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Animation::finished() const {
|
bool ReactionFlyAnimation::finished() const {
|
||||||
return !_valid
|
return !_valid
|
||||||
|| (_flyIcon.isNull()
|
|| (_flyIcon.isNull()
|
||||||
&& (!_center || !_center->animating())
|
&& (!_center || !_center->animating())
|
|
@ -10,12 +10,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/effects/animations.h"
|
#include "ui/effects/animations.h"
|
||||||
#include "data/data_message_reaction_id.h"
|
#include "data/data_message_reaction_id.h"
|
||||||
|
|
||||||
namespace Ui {
|
|
||||||
class AnimatedIcon;
|
|
||||||
} // namespace Lottie
|
|
||||||
|
|
||||||
namespace Ui::Text {
|
namespace Ui::Text {
|
||||||
class CustomEmoji;
|
class CustomEmoji;
|
||||||
|
struct CustomEmojiColored;
|
||||||
} // namespace Ui::Text
|
} // namespace Ui::Text
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
|
@ -23,25 +20,27 @@ class Reactions;
|
||||||
enum class CustomEmojiSizeTag : uchar;
|
enum class CustomEmojiSizeTag : uchar;
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
||||||
namespace HistoryView::Reactions {
|
namespace Ui {
|
||||||
|
|
||||||
struct AnimationArgs {
|
class AnimatedIcon;
|
||||||
|
|
||||||
|
struct ReactionFlyAnimationArgs {
|
||||||
::Data::ReactionId id;
|
::Data::ReactionId id;
|
||||||
QImage flyIcon;
|
QImage flyIcon;
|
||||||
QRect flyFrom;
|
QRect flyFrom;
|
||||||
|
|
||||||
[[nodiscard]] AnimationArgs translated(QPoint point) const;
|
[[nodiscard]] ReactionFlyAnimationArgs translated(QPoint point) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Animation final {
|
class ReactionFlyAnimation final {
|
||||||
public:
|
public:
|
||||||
Animation(
|
ReactionFlyAnimation(
|
||||||
not_null<::Data::Reactions*> owner,
|
not_null<::Data::Reactions*> owner,
|
||||||
AnimationArgs &&args,
|
ReactionFlyAnimationArgs &&args,
|
||||||
Fn<void()> repaint,
|
Fn<void()> repaint,
|
||||||
int size,
|
int size,
|
||||||
Data::CustomEmojiSizeTag customSizeTag = {});
|
Data::CustomEmojiSizeTag customSizeTag = {});
|
||||||
~Animation();
|
~ReactionFlyAnimation();
|
||||||
|
|
||||||
void setRepaintCallback(Fn<void()> repaint);
|
void setRepaintCallback(Fn<void()> repaint);
|
||||||
QRect paintGetArea(
|
QRect paintGetArea(
|
||||||
|
@ -95,13 +94,13 @@ private:
|
||||||
const not_null<::Data::Reactions*> _owner;
|
const not_null<::Data::Reactions*> _owner;
|
||||||
Fn<void()> _repaint;
|
Fn<void()> _repaint;
|
||||||
QImage _flyIcon;
|
QImage _flyIcon;
|
||||||
std::unique_ptr<Ui::Text::CustomEmoji> _custom;
|
std::unique_ptr<Text::CustomEmoji> _custom;
|
||||||
std::unique_ptr<Ui::Text::CustomEmojiColored> _colored;
|
std::unique_ptr<Text::CustomEmojiColored> _colored;
|
||||||
std::unique_ptr<Ui::AnimatedIcon> _center;
|
std::unique_ptr<AnimatedIcon> _center;
|
||||||
std::unique_ptr<Ui::AnimatedIcon> _effect;
|
std::unique_ptr<AnimatedIcon> _effect;
|
||||||
std::vector<MiniCopy> _miniCopies;
|
std::vector<MiniCopy> _miniCopies;
|
||||||
Ui::Animations::Simple _fly;
|
Animations::Simple _fly;
|
||||||
Ui::Animations::Simple _minis;
|
Animations::Simple _minis;
|
||||||
QRect _flyFrom;
|
QRect _flyFrom;
|
||||||
float64 _centerSizeMultiplier = 0.;
|
float64 _centerSizeMultiplier = 0.;
|
||||||
int _customSize = 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