Add reaction animations to comments.
This commit is contained in:
parent
34c36d77c3
commit
490e688a91
8 changed files with 101 additions and 56 deletions
|
@ -86,6 +86,24 @@ void Reactions::preloadImageFor(const QString &emoji) {
|
|||
}
|
||||
}
|
||||
|
||||
void Reactions::preloadAnimationsFor(const QString &emoji) {
|
||||
const auto i = ranges::find(_available, emoji, &Reaction::emoji);
|
||||
if (i == end(_available)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto preload = [&](DocumentData *document) {
|
||||
const auto view = document
|
||||
? document->activeMediaView()
|
||||
: nullptr;
|
||||
if (view) {
|
||||
view->checkStickerLarge();
|
||||
}
|
||||
};
|
||||
preload(i->centerIcon);
|
||||
preload(i->aroundAnimation);
|
||||
}
|
||||
|
||||
QImage Reactions::resolveImageFor(
|
||||
const QString &emoji,
|
||||
ImageSize size) {
|
||||
|
|
|
@ -54,6 +54,7 @@ public:
|
|||
InlineList,
|
||||
};
|
||||
void preloadImageFor(const QString &emoji);
|
||||
void preloadAnimationsFor(const QString &emoji);
|
||||
[[nodiscard]] QImage resolveImageFor(
|
||||
const QString &emoji,
|
||||
ImageSize size);
|
||||
|
|
|
@ -554,11 +554,9 @@ void HistoryInner::repaintItem(const Element *view) {
|
|||
if (top >= 0) {
|
||||
const auto range = view->verticalRepaintRange();
|
||||
update(0, top + range.top, width(), range.height);
|
||||
if (!_reactionEffects.empty()) {
|
||||
const auto i = _reactionEffects.find(view->data()->fullId());
|
||||
if (i != end(_reactionEffects)) {
|
||||
update(i->second);
|
||||
}
|
||||
const auto id = view->data()->fullId();
|
||||
if (const auto area = _reactionsManager->lookupEffectArea(id)) {
|
||||
update(*area);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -900,9 +898,8 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
|||
} else {
|
||||
_emptyPainter = nullptr;
|
||||
}
|
||||
auto reactionEffects = base::flat_map<
|
||||
not_null<Element*>,
|
||||
Ui::ReactionEffectPainter>();
|
||||
|
||||
_reactionsManager->startEffectsCollection();
|
||||
if (!noHistoryDisplayed) {
|
||||
auto readMentions = base::flat_set<not_null<HistoryItem*>>();
|
||||
|
||||
|
@ -932,20 +929,17 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
|||
context.translate(0, -top);
|
||||
p.translate(0, top);
|
||||
if (context.clip.y() < view->height()) while (top < drawToY) {
|
||||
auto effect = Ui::ReactionEffectPainter();
|
||||
context.reactionEffects = &effect;
|
||||
context.reactionEffects
|
||||
= _reactionsManager->currentReactionEffect();
|
||||
context.outbg = view->hasOutLayout();
|
||||
context.selection = itemRenderSelection(
|
||||
view,
|
||||
selfromy - mtop,
|
||||
seltoy - mtop);
|
||||
view->draw(p, context);
|
||||
if (effect.paint) {
|
||||
effect.offset += QPoint(0, top);
|
||||
reactionEffects.emplace(view, effect);
|
||||
} else if (!_reactionEffects.empty()) {
|
||||
_reactionEffects.remove(item->fullId());
|
||||
}
|
||||
_reactionsManager->recordCurrentReactionEffect(
|
||||
item->fullId(),
|
||||
QPoint(0, top));
|
||||
|
||||
const auto height = view->height();
|
||||
const auto middle = top + height / 2;
|
||||
|
@ -995,20 +989,17 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
|||
while (top < drawToY) {
|
||||
const auto height = view->height();
|
||||
if (context.clip.y() < height && hdrawtop < top + height) {
|
||||
auto effect = Ui::ReactionEffectPainter();
|
||||
context.reactionEffects = &effect;
|
||||
context.reactionEffects
|
||||
= _reactionsManager->currentReactionEffect();
|
||||
context.outbg = view->hasOutLayout();
|
||||
context.selection = itemRenderSelection(
|
||||
view,
|
||||
selfromy - htop,
|
||||
seltoy - htop);
|
||||
view->draw(p, context);
|
||||
if (effect.paint) {
|
||||
effect.offset += QPoint(0, top);
|
||||
reactionEffects.emplace(view, effect);
|
||||
} else if (!_reactionEffects.empty()) {
|
||||
_reactionEffects.remove(item->fullId());
|
||||
}
|
||||
_reactionsManager->recordCurrentReactionEffect(
|
||||
item->fullId(),
|
||||
QPoint(0, top));
|
||||
|
||||
const auto middle = top + height / 2;
|
||||
const auto bottom = top + height;
|
||||
|
@ -1149,15 +1140,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
|||
});
|
||||
p.setOpacity(1.);
|
||||
|
||||
_reactionsManager->paintButtons(p, context);
|
||||
|
||||
for (const auto &[view, effect] : reactionEffects) {
|
||||
const auto offset = effect.offset;
|
||||
p.translate(offset);
|
||||
_reactionEffects[view->data()->fullId()]
|
||||
= effect.paint(p).translated(offset);
|
||||
p.translate(-offset);
|
||||
}
|
||||
_reactionsManager->paint(p, context);
|
||||
|
||||
p.translate(0, _historyPaddingTop);
|
||||
_emojiInteractions->paint(p);
|
||||
|
@ -1648,7 +1631,6 @@ void HistoryInner::itemRemoved(not_null<const HistoryItem*> item) {
|
|||
return;
|
||||
}
|
||||
|
||||
_reactionEffects.remove(item->fullId());
|
||||
_animatedStickersPlayed.remove(item);
|
||||
_reactionsManager->remove(item->fullId());
|
||||
|
||||
|
|
|
@ -427,7 +427,6 @@ private:
|
|||
std::shared_ptr<Data::CloudImageView>> _userpics, _userpicsCache;
|
||||
|
||||
std::unique_ptr<HistoryView::Reactions::Manager> _reactionsManager;
|
||||
base::flat_map<FullMsgId, QRect> _reactionEffects;
|
||||
|
||||
MouseAction _mouseAction = MouseAction::None;
|
||||
TextSelectType _mouseSelectType = TextSelectType::Letters;
|
||||
|
|
|
@ -1731,6 +1731,8 @@ void ListWidget::paintEvent(QPaintEvent *e) {
|
|||
});
|
||||
|
||||
if (from != end(_items)) {
|
||||
_reactionsManager->startEffectsCollection();
|
||||
|
||||
auto top = itemTop(from->get());
|
||||
auto context = controller()->preparePaintContext({
|
||||
.theme = _delegate->listChatTheme(),
|
||||
|
@ -1742,9 +1744,14 @@ void ListWidget::paintEvent(QPaintEvent *e) {
|
|||
p.translate(0, top);
|
||||
for (auto i = from; i != to; ++i) {
|
||||
const auto view = *i;
|
||||
context.reactionEffects
|
||||
= _reactionsManager->currentReactionEffect();
|
||||
context.outbg = view->hasOutLayout();
|
||||
context.selection = itemRenderSelection(view);
|
||||
view->draw(p, context);
|
||||
_reactionsManager->recordCurrentReactionEffect(
|
||||
view->data()->fullId(),
|
||||
QPoint(0, top));
|
||||
const auto height = view->height();
|
||||
top += height;
|
||||
context.translate(0, -height);
|
||||
|
@ -1829,7 +1836,7 @@ void ListWidget::paintEvent(QPaintEvent *e) {
|
|||
return true;
|
||||
});
|
||||
|
||||
_reactionsManager->paintButtons(p, context);
|
||||
_reactionsManager->paint(p, context);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2895,6 +2902,10 @@ void ListWidget::repaintItem(const Element *view) {
|
|||
const auto top = itemTop(view);
|
||||
const auto range = view->verticalRepaintRange();
|
||||
update(0, top + range.top, width(), range.height);
|
||||
const auto id = view->data()->fullId();
|
||||
if (const auto area = _reactionsManager->lookupEffectArea(id)) {
|
||||
update(*area);
|
||||
}
|
||||
}
|
||||
|
||||
void ListWidget::repaintItem(FullMsgId itemId) {
|
||||
|
|
|
@ -539,9 +539,7 @@ void Manager::applyList(const std::vector<Data::Reaction> &list) {
|
|||
return std::tie(
|
||||
obj.emoji,
|
||||
obj.appearAnimation,
|
||||
obj.selectAnimation,
|
||||
obj.centerIcon,
|
||||
obj.aroundAnimation);
|
||||
obj.selectAnimation);
|
||||
};
|
||||
if (ranges::equal(_list, list, ranges::equal_to(), proj, proj)) {
|
||||
return;
|
||||
|
@ -555,8 +553,6 @@ void Manager::applyList(const std::vector<Data::Reaction> &list) {
|
|||
.emoji = reaction.emoji,
|
||||
.appearAnimation = reaction.appearAnimation,
|
||||
.selectAnimation = reaction.selectAnimation,
|
||||
.centerIcon = reaction.centerIcon,
|
||||
.aroundAnimation = reaction.aroundAnimation,
|
||||
});
|
||||
}
|
||||
applyListFilters();
|
||||
|
@ -716,18 +712,10 @@ void Manager::loadIcons() {
|
|||
all = false;
|
||||
}
|
||||
}
|
||||
if (all) {
|
||||
const auto preload = [&](DocumentData *document) {
|
||||
const auto view = document
|
||||
? document->activeMediaView()
|
||||
: nullptr;
|
||||
if (view) {
|
||||
view->checkStickerLarge();
|
||||
}
|
||||
};
|
||||
if (all && !_icons.empty()) {
|
||||
auto &data = _icons.front()->appearAnimation->owner().reactions();
|
||||
for (const auto &icon : _icons) {
|
||||
preload(icon->centerIcon);
|
||||
preload(icon->aroundAnimation);
|
||||
data.preloadAnimationsFor(icon->emoji);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -751,7 +739,7 @@ void Manager::removeStaleButtons() {
|
|||
end(_buttonHiding));
|
||||
}
|
||||
|
||||
void Manager::paintButtons(Painter &p, const PaintContext &context) {
|
||||
void Manager::paint(Painter &p, const PaintContext &context) {
|
||||
removeStaleButtons();
|
||||
for (const auto &button : _buttonHiding) {
|
||||
paintButton(p, context, button.get());
|
||||
|
@ -759,6 +747,14 @@ void Manager::paintButtons(Painter &p, const PaintContext &context) {
|
|||
if (const auto current = _button.get()) {
|
||||
paintButton(p, context, current);
|
||||
}
|
||||
|
||||
for (const auto &[id, effect] : _collectedEffects) {
|
||||
const auto offset = effect.offset;
|
||||
p.translate(offset);
|
||||
_activeEffectAreas[id] = effect.paint(p).translated(offset);
|
||||
p.translate(-offset);
|
||||
}
|
||||
_collectedEffects.clear();
|
||||
}
|
||||
|
||||
ClickHandlerPtr Manager::computeButtonLink(QPoint position) const {
|
||||
|
@ -856,6 +852,7 @@ bool Manager::overCurrentButton(QPoint position) const {
|
|||
}
|
||||
|
||||
void Manager::remove(FullMsgId context) {
|
||||
_activeEffectAreas.remove(context);
|
||||
if (_buttonContext == context) {
|
||||
_buttonContext = {};
|
||||
_button = nullptr;
|
||||
|
@ -1507,6 +1504,31 @@ QRect Manager::validateFrame(
|
|||
return result;
|
||||
}
|
||||
|
||||
std::optional<QRect> Manager::lookupEffectArea(FullMsgId itemId) const {
|
||||
const auto i = _activeEffectAreas.find(itemId);
|
||||
return (i != end(_activeEffectAreas))
|
||||
? i->second
|
||||
: std::optional<QRect>();
|
||||
}
|
||||
|
||||
void Manager::startEffectsCollection() {
|
||||
_collectedEffects.clear();
|
||||
_currentEffect = {};
|
||||
}
|
||||
|
||||
not_null<Ui::ReactionEffectPainter*> Manager::currentReactionEffect() {
|
||||
return &_currentEffect;
|
||||
}
|
||||
|
||||
void Manager::recordCurrentReactionEffect(FullMsgId itemId, QPoint origin) {
|
||||
if (_currentEffect.paint) {
|
||||
_currentEffect.offset += origin;
|
||||
_collectedEffects[itemId] = base::take(_currentEffect);
|
||||
} else if (!_collectedEffects.empty()) {
|
||||
_collectedEffects.remove(itemId);
|
||||
}
|
||||
}
|
||||
|
||||
void SetupManagerList(
|
||||
not_null<Manager*> manager,
|
||||
not_null<Main::Session*> session,
|
||||
|
|
|
@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include "ui/effects/animations.h"
|
||||
#include "ui/widgets/scroll_area.h"
|
||||
#include "ui/chat/chat_style.h"
|
||||
|
||||
namespace Ui {
|
||||
struct ChatPaintContext;
|
||||
|
@ -145,7 +146,7 @@ public:
|
|||
void updateUniqueLimit(not_null<HistoryItem*> item);
|
||||
|
||||
void updateButton(ButtonParameters parameters);
|
||||
void paintButtons(Painter &p, const PaintContext &context);
|
||||
void paint(Painter &p, const PaintContext &context);
|
||||
[[nodiscard]] TextState buttonTextState(QPoint position) const;
|
||||
void remove(FullMsgId context);
|
||||
|
||||
|
@ -165,6 +166,13 @@ public:
|
|||
return _chosen.events();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<QRect> lookupEffectArea(
|
||||
FullMsgId itemId) const;
|
||||
void startEffectsCollection();
|
||||
[[nodiscard]] auto currentReactionEffect()
|
||||
-> not_null<Ui::ReactionEffectPainter*>;
|
||||
void recordCurrentReactionEffect(FullMsgId itemId, QPoint origin);
|
||||
|
||||
[[nodiscard]] rpl::lifetime &lifetime() {
|
||||
return _lifetime;
|
||||
}
|
||||
|
@ -178,8 +186,6 @@ private:
|
|||
QString emoji;
|
||||
not_null<DocumentData*> appearAnimation;
|
||||
not_null<DocumentData*> selectAnimation;
|
||||
DocumentData *centerIcon = nullptr;
|
||||
DocumentData *aroundAnimation = nullptr;
|
||||
std::shared_ptr<Lottie::Icon> appear;
|
||||
std::shared_ptr<Lottie::Icon> select;
|
||||
mutable ClickHandlerPtr link;
|
||||
|
@ -335,6 +341,11 @@ private:
|
|||
mutable base::flat_map<QString, ClickHandlerPtr> _reactionsLinks;
|
||||
Fn<Fn<void()>(QString)> _createChooseCallback;
|
||||
|
||||
base::flat_map<FullMsgId, QRect> _activeEffectAreas;
|
||||
|
||||
Ui::ReactionEffectPainter _currentEffect;
|
||||
base::flat_map<FullMsgId, Ui::ReactionEffectPainter> _collectedEffects;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
|
|
@ -275,6 +275,7 @@ bool InlineList::getState(
|
|||
if (button.geometry.contains(point)) {
|
||||
if (!button.link) {
|
||||
button.link = _handlerFactory(button.emoji);
|
||||
_owner->preloadAnimationsFor(button.emoji);
|
||||
}
|
||||
outResult->link = button.link;
|
||||
return true;
|
||||
|
|
Loading…
Reference in a new issue