mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-19 15:47:11 +02:00
Paint reaction animations above everything.
This commit is contained in:
parent
0ab26f0c82
commit
34c36d77c3
10 changed files with 107 additions and 45 deletions
|
@ -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());
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue