Paint reaction animations above everything.

This commit is contained in:
John Preston 2022-01-11 17:57:00 +03:00
parent 0ab26f0c82
commit 34c36d77c3
10 changed files with 107 additions and 45 deletions

View file

@ -554,6 +554,12 @@ 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);
}
}
}
}
@ -894,6 +900,9 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
} else {
_emptyPainter = nullptr;
}
auto reactionEffects = base::flat_map<
not_null<Element*>,
Ui::ReactionEffectPainter>();
if (!noHistoryDisplayed) {
auto readMentions = base::flat_set<not_null<HistoryItem*>>();
@ -923,12 +932,20 @@ 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.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());
}
const auto height = view->height();
const auto middle = top + height / 2;
@ -978,14 +995,21 @@ 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.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());
}
const auto item = view->data();
const auto middle = top + height / 2;
const auto bottom = top + height;
if (_visibleAreaBottom >= bottom) {
@ -1127,6 +1151,14 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
_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);
}
p.translate(0, _historyPaddingTop);
_emojiInteractions->paint(p);
}
@ -1616,6 +1648,7 @@ void HistoryInner::itemRemoved(not_null<const HistoryItem*> item) {
return;
}
_reactionEffects.remove(item->fullId());
_animatedStickersPlayed.remove(item);
_reactionsManager->remove(item->fullId());

View file

@ -427,6 +427,7 @@ 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;

View file

@ -225,7 +225,7 @@ void BottomInfo::paint(
left += width() - available;
top += st::msgDateFont->height;
}
paintReactions(p, position, left, top, available);
paintReactions(p, position, left, top, available, context);
}
}
@ -234,11 +234,12 @@ void BottomInfo::paintReactions(
QPoint origin,
int left,
int top,
int availableWidth) const {
int availableWidth,
const PaintContext &context) const {
auto x = left;
auto y = top;
auto widthLeft = availableWidth;
const auto animated = _reactionAnimation
const auto animated = (_reactionAnimation && context.reactionEffects)
? _reactionAnimation->playingAroundEmoji()
: QString();
if (_reactionAnimation && animated.isEmpty()) {
@ -274,7 +275,9 @@ void BottomInfo::paintReactions(
p.drawImage(image.topLeft(), reaction.image);
}
if (animating) {
_reactionAnimation->paint(p, origin, image);
context.reactionEffects->paint = [=](QPainter &p) {
return _reactionAnimation->paintGetArea(p, origin, image);
};
}
if (reaction.countTextWidth > 0) {
p.drawText(

View file

@ -102,7 +102,8 @@ private:
QPoint origin,
int left,
int top,
int availableWidth) const;
int availableWidth,
const PaintContext &context) const;
QSize countOptimalSize() override;
QSize countCurrentSize(int newWidth) override;

View file

@ -729,6 +729,9 @@ void Message::draw(Painter &p, const PaintContext &context) const {
const auto reactionsPosition = QPoint(reactionsLeft + g.left(), g.top() + g.height() + st::mediaInBubbleSkip);
p.translate(reactionsPosition);
_reactions->paint(p, context, g.width(), context.clip.translated(-reactionsPosition));
if (context.reactionEffects && context.reactionEffects->paint) {
context.reactionEffects->offset += reactionsPosition;
}
p.translate(-reactionsPosition);
}
@ -781,6 +784,9 @@ void Message::draw(Painter &p, const PaintContext &context) const {
const auto reactionsPosition = QPoint(trect.left(), trect.top() + trect.height() + reactionsTop);
p.translate(reactionsPosition);
_reactions->paint(p, context, g.width(), context.clip.translated(-reactionsPosition));
if (context.reactionEffects && context.reactionEffects->paint) {
context.reactionEffects->offset += reactionsPosition;
}
p.translate(-reactionsPosition);
}
@ -828,15 +834,20 @@ void Message::draw(Painter &p, const PaintContext &context) const {
paintText(p, trect, context);
if (mediaDisplayed) {
auto mediaHeight = media->height();
auto mediaLeft = inner.left();
auto mediaTop = (trect.y() + trect.height() - mediaHeight);
auto mediaPosition = QPoint(
inner.left(),
trect.y() + trect.height() - mediaHeight);
p.translate(mediaLeft, mediaTop);
p.translate(mediaPosition);
media->draw(p, context.translated(
-mediaLeft,
-mediaTop
-mediaPosition
).withSelection(skipTextSelection(context.selection)));
p.translate(-mediaLeft, -mediaTop);
if (context.reactionEffects
&& context.reactionEffects->paint
&& !_reactions) {
context.reactionEffects->offset += mediaPosition;
}
p.translate(-mediaPosition);
}
if (entry) {
auto entryLeft = inner.left();
@ -890,6 +901,9 @@ void Message::draw(Painter &p, const PaintContext &context) const {
media->draw(p, context.translated(
-g.topLeft()
).withSelection(skipTextSelection(context.selection)));
if (context.reactionEffects && context.reactionEffects->paint && !_reactions) {
context.reactionEffects->offset += g.topLeft();
}
p.translate(-g.topLeft());
}

View file

@ -67,36 +67,38 @@ SendAnimation::SendAnimation(
SendAnimation::~SendAnimation() = default;
void SendAnimation::paint(QPainter &p, QPoint origin, QRect target) const {
if (_flyIcon) {
const auto from = _flyFrom.translated(origin);
const auto lshift = target.width() / 4;
const auto rshift = target.width() / 2 - lshift;
const auto margins = QMargins{ lshift, lshift, rshift, rshift };
target = target.marginsRemoved(margins);
const auto progress = _fly.value(1.);
const auto rect = QRect(
anim::interpolate(from.x(), target.x(), progress),
anim::interpolate(from.y(), target.y(), progress),
anim::interpolate(from.width(), target.width(), progress),
anim::interpolate(from.height(), target.height(), progress));
auto hq = PainterHighQualityEnabler(p);
if (progress < 1.) {
p.setOpacity(1. - progress);
p.drawImage(rect, _flyIcon->frame());
}
if (progress > 0.) {
p.setOpacity(progress);
p.drawImage(rect.marginsAdded(margins), _center->frame());
}
p.setOpacity(1.);
} else {
QRect SendAnimation::paintGetArea(QPainter &p, QPoint origin, QRect target) const {
if (!_flyIcon) {
p.drawImage(target, _center->frame());
p.drawImage(QRect(
const auto wide = QRect(
target.topLeft() - QPoint(target.width(), target.height()) / 2,
target.size() * 2
), _effect->frame());
target.size() * 2);
p.drawImage(wide, _effect->frame());
return wide;
}
const auto from = _flyFrom.translated(origin);
const auto lshift = target.width() / 4;
const auto rshift = target.width() / 2 - lshift;
const auto margins = QMargins{ lshift, lshift, rshift, rshift };
target = target.marginsRemoved(margins);
const auto progress = _fly.value(1.);
const auto rect = QRect(
anim::interpolate(from.x(), target.x(), progress),
anim::interpolate(from.y(), target.y(), progress),
anim::interpolate(from.width(), target.width(), progress),
anim::interpolate(from.height(), target.height(), progress));
const auto wide = rect.marginsAdded(margins);
auto hq = PainterHighQualityEnabler(p);
if (progress < 1.) {
p.setOpacity(1. - progress);
p.drawImage(rect, _flyIcon->frame());
}
if (progress > 0.) {
p.setOpacity(progress);
p.drawImage(wide, _center->frame());
}
p.setOpacity(1.);
return wide;
}
void SendAnimation::startAnimations() {

View file

@ -33,7 +33,7 @@ public:
~SendAnimation();
void setRepaintCallback(Fn<void()> repaint);
void paint(QPainter &p, QPoint origin, QRect target) const;
QRect paintGetArea(QPainter &p, QPoint origin, QRect target) const;
[[nodiscard]] QString playingAroundEmoji() const;
[[nodiscard]] bool flying() const;

View file

@ -810,7 +810,7 @@ void Manager::setSelectedIcon(int index) const {
if (select && !icon->selectAnimated) {
icon->selectAnimated = true;
select->animate(
[=] { updateCurrentButton(); },
crl::guard(this, [=] { updateCurrentButton(); }),
0,
select->framesCount() - 1);
}
@ -1301,9 +1301,9 @@ void Manager::paintAllEmoji(
const auto shift = QPoint(0, oneHeight * (expandUp ? -1 : 1));
auto emojiPosition = mainEmojiPosition
+ QPoint(0, scroll * (expandUp ? 1 : -1));
const auto update = [=] {
const auto update = crl::guard(this, [=] {
updateCurrentButton();
};
});
for (const auto &icon : _icons) {
const auto target = countTarget(*icon).translated(emojiPosition);
emojiPosition += shift;

View file

@ -190,7 +190,7 @@ void InlineList::paint(
const auto size = st::reactionBottomSize;
const auto skip = (size - st::reactionBottomImage) / 2;
const auto inbubble = (_data.flags & InlineListData::Flag::InBubble);
const auto animated = _animation
const auto animated = (_animation && context.reactionEffects)
? _animation->playingAroundEmoji()
: QString();
if (_animation && animated.isEmpty()) {
@ -236,7 +236,9 @@ void InlineList::paint(
p.drawImage(image.topLeft(), button.image);
}
if (animating) {
_animation->paint(p, QPoint(), image);
context.reactionEffects->paint = [=](QPainter &p) {
return _animation->paintGetArea(p, QPoint(), image);
};
}
p.setPen(!inbubble
? (chosen

View file

@ -90,9 +90,15 @@ struct MessageImageStyle {
style::icon historyVideoMessageMute = { Qt::Uninitialized };
};
struct ReactionEffectPainter {
QPoint offset;
Fn<QRect(QPainter&)> paint;
};
struct ChatPaintContext {
not_null<const ChatStyle*> st;
const BubblePattern *bubblesPattern = nullptr;
ReactionEffectPainter *reactionEffects = nullptr;
QRect viewport;
QRect clip;
TextSelection selection;