mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +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) {
|
if (top >= 0) {
|
||||||
const auto range = view->verticalRepaintRange();
|
const auto range = view->verticalRepaintRange();
|
||||||
update(0, top + range.top, width(), range.height);
|
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 {
|
} else {
|
||||||
_emptyPainter = nullptr;
|
_emptyPainter = nullptr;
|
||||||
}
|
}
|
||||||
|
auto reactionEffects = base::flat_map<
|
||||||
|
not_null<Element*>,
|
||||||
|
Ui::ReactionEffectPainter>();
|
||||||
if (!noHistoryDisplayed) {
|
if (!noHistoryDisplayed) {
|
||||||
auto readMentions = base::flat_set<not_null<HistoryItem*>>();
|
auto readMentions = base::flat_set<not_null<HistoryItem*>>();
|
||||||
|
|
||||||
|
@ -923,12 +932,20 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||||
context.translate(0, -top);
|
context.translate(0, -top);
|
||||||
p.translate(0, top);
|
p.translate(0, top);
|
||||||
if (context.clip.y() < view->height()) while (top < drawToY) {
|
if (context.clip.y() < view->height()) while (top < drawToY) {
|
||||||
|
auto effect = Ui::ReactionEffectPainter();
|
||||||
|
context.reactionEffects = &effect;
|
||||||
context.outbg = view->hasOutLayout();
|
context.outbg = view->hasOutLayout();
|
||||||
context.selection = itemRenderSelection(
|
context.selection = itemRenderSelection(
|
||||||
view,
|
view,
|
||||||
selfromy - mtop,
|
selfromy - mtop,
|
||||||
seltoy - mtop);
|
seltoy - mtop);
|
||||||
view->draw(p, context);
|
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 height = view->height();
|
||||||
const auto middle = top + height / 2;
|
const auto middle = top + height / 2;
|
||||||
|
@ -978,14 +995,21 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||||
while (top < drawToY) {
|
while (top < drawToY) {
|
||||||
const auto height = view->height();
|
const auto height = view->height();
|
||||||
if (context.clip.y() < height && hdrawtop < top + height) {
|
if (context.clip.y() < height && hdrawtop < top + height) {
|
||||||
|
auto effect = Ui::ReactionEffectPainter();
|
||||||
|
context.reactionEffects = &effect;
|
||||||
context.outbg = view->hasOutLayout();
|
context.outbg = view->hasOutLayout();
|
||||||
context.selection = itemRenderSelection(
|
context.selection = itemRenderSelection(
|
||||||
view,
|
view,
|
||||||
selfromy - htop,
|
selfromy - htop,
|
||||||
seltoy - htop);
|
seltoy - htop);
|
||||||
view->draw(p, context);
|
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 middle = top + height / 2;
|
||||||
const auto bottom = top + height;
|
const auto bottom = top + height;
|
||||||
if (_visibleAreaBottom >= bottom) {
|
if (_visibleAreaBottom >= bottom) {
|
||||||
|
@ -1127,6 +1151,14 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
||||||
|
|
||||||
_reactionsManager->paintButtons(p, context);
|
_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);
|
p.translate(0, _historyPaddingTop);
|
||||||
_emojiInteractions->paint(p);
|
_emojiInteractions->paint(p);
|
||||||
}
|
}
|
||||||
|
@ -1616,6 +1648,7 @@ void HistoryInner::itemRemoved(not_null<const HistoryItem*> item) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_reactionEffects.remove(item->fullId());
|
||||||
_animatedStickersPlayed.remove(item);
|
_animatedStickersPlayed.remove(item);
|
||||||
_reactionsManager->remove(item->fullId());
|
_reactionsManager->remove(item->fullId());
|
||||||
|
|
||||||
|
|
|
@ -427,6 +427,7 @@ private:
|
||||||
std::shared_ptr<Data::CloudImageView>> _userpics, _userpicsCache;
|
std::shared_ptr<Data::CloudImageView>> _userpics, _userpicsCache;
|
||||||
|
|
||||||
std::unique_ptr<HistoryView::Reactions::Manager> _reactionsManager;
|
std::unique_ptr<HistoryView::Reactions::Manager> _reactionsManager;
|
||||||
|
base::flat_map<FullMsgId, QRect> _reactionEffects;
|
||||||
|
|
||||||
MouseAction _mouseAction = MouseAction::None;
|
MouseAction _mouseAction = MouseAction::None;
|
||||||
TextSelectType _mouseSelectType = TextSelectType::Letters;
|
TextSelectType _mouseSelectType = TextSelectType::Letters;
|
||||||
|
|
|
@ -225,7 +225,7 @@ void BottomInfo::paint(
|
||||||
left += width() - available;
|
left += width() - available;
|
||||||
top += st::msgDateFont->height;
|
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,
|
QPoint origin,
|
||||||
int left,
|
int left,
|
||||||
int top,
|
int top,
|
||||||
int availableWidth) const {
|
int availableWidth,
|
||||||
|
const PaintContext &context) const {
|
||||||
auto x = left;
|
auto x = left;
|
||||||
auto y = top;
|
auto y = top;
|
||||||
auto widthLeft = availableWidth;
|
auto widthLeft = availableWidth;
|
||||||
const auto animated = _reactionAnimation
|
const auto animated = (_reactionAnimation && context.reactionEffects)
|
||||||
? _reactionAnimation->playingAroundEmoji()
|
? _reactionAnimation->playingAroundEmoji()
|
||||||
: QString();
|
: QString();
|
||||||
if (_reactionAnimation && animated.isEmpty()) {
|
if (_reactionAnimation && animated.isEmpty()) {
|
||||||
|
@ -274,7 +275,9 @@ void BottomInfo::paintReactions(
|
||||||
p.drawImage(image.topLeft(), reaction.image);
|
p.drawImage(image.topLeft(), reaction.image);
|
||||||
}
|
}
|
||||||
if (animating) {
|
if (animating) {
|
||||||
_reactionAnimation->paint(p, origin, image);
|
context.reactionEffects->paint = [=](QPainter &p) {
|
||||||
|
return _reactionAnimation->paintGetArea(p, origin, image);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
if (reaction.countTextWidth > 0) {
|
if (reaction.countTextWidth > 0) {
|
||||||
p.drawText(
|
p.drawText(
|
||||||
|
|
|
@ -102,7 +102,8 @@ private:
|
||||||
QPoint origin,
|
QPoint origin,
|
||||||
int left,
|
int left,
|
||||||
int top,
|
int top,
|
||||||
int availableWidth) const;
|
int availableWidth,
|
||||||
|
const PaintContext &context) const;
|
||||||
|
|
||||||
QSize countOptimalSize() override;
|
QSize countOptimalSize() override;
|
||||||
QSize countCurrentSize(int newWidth) 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);
|
const auto reactionsPosition = QPoint(reactionsLeft + g.left(), g.top() + g.height() + st::mediaInBubbleSkip);
|
||||||
p.translate(reactionsPosition);
|
p.translate(reactionsPosition);
|
||||||
_reactions->paint(p, context, g.width(), context.clip.translated(-reactionsPosition));
|
_reactions->paint(p, context, g.width(), context.clip.translated(-reactionsPosition));
|
||||||
|
if (context.reactionEffects && context.reactionEffects->paint) {
|
||||||
|
context.reactionEffects->offset += reactionsPosition;
|
||||||
|
}
|
||||||
p.translate(-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);
|
const auto reactionsPosition = QPoint(trect.left(), trect.top() + trect.height() + reactionsTop);
|
||||||
p.translate(reactionsPosition);
|
p.translate(reactionsPosition);
|
||||||
_reactions->paint(p, context, g.width(), context.clip.translated(-reactionsPosition));
|
_reactions->paint(p, context, g.width(), context.clip.translated(-reactionsPosition));
|
||||||
|
if (context.reactionEffects && context.reactionEffects->paint) {
|
||||||
|
context.reactionEffects->offset += reactionsPosition;
|
||||||
|
}
|
||||||
p.translate(-reactionsPosition);
|
p.translate(-reactionsPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -828,15 +834,20 @@ void Message::draw(Painter &p, const PaintContext &context) const {
|
||||||
paintText(p, trect, context);
|
paintText(p, trect, context);
|
||||||
if (mediaDisplayed) {
|
if (mediaDisplayed) {
|
||||||
auto mediaHeight = media->height();
|
auto mediaHeight = media->height();
|
||||||
auto mediaLeft = inner.left();
|
auto mediaPosition = QPoint(
|
||||||
auto mediaTop = (trect.y() + trect.height() - mediaHeight);
|
inner.left(),
|
||||||
|
trect.y() + trect.height() - mediaHeight);
|
||||||
|
|
||||||
p.translate(mediaLeft, mediaTop);
|
p.translate(mediaPosition);
|
||||||
media->draw(p, context.translated(
|
media->draw(p, context.translated(
|
||||||
-mediaLeft,
|
-mediaPosition
|
||||||
-mediaTop
|
|
||||||
).withSelection(skipTextSelection(context.selection)));
|
).withSelection(skipTextSelection(context.selection)));
|
||||||
p.translate(-mediaLeft, -mediaTop);
|
if (context.reactionEffects
|
||||||
|
&& context.reactionEffects->paint
|
||||||
|
&& !_reactions) {
|
||||||
|
context.reactionEffects->offset += mediaPosition;
|
||||||
|
}
|
||||||
|
p.translate(-mediaPosition);
|
||||||
}
|
}
|
||||||
if (entry) {
|
if (entry) {
|
||||||
auto entryLeft = inner.left();
|
auto entryLeft = inner.left();
|
||||||
|
@ -890,6 +901,9 @@ void Message::draw(Painter &p, const PaintContext &context) const {
|
||||||
media->draw(p, context.translated(
|
media->draw(p, context.translated(
|
||||||
-g.topLeft()
|
-g.topLeft()
|
||||||
).withSelection(skipTextSelection(context.selection)));
|
).withSelection(skipTextSelection(context.selection)));
|
||||||
|
if (context.reactionEffects && context.reactionEffects->paint && !_reactions) {
|
||||||
|
context.reactionEffects->offset += g.topLeft();
|
||||||
|
}
|
||||||
p.translate(-g.topLeft());
|
p.translate(-g.topLeft());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,36 +67,38 @@ SendAnimation::SendAnimation(
|
||||||
|
|
||||||
SendAnimation::~SendAnimation() = default;
|
SendAnimation::~SendAnimation() = default;
|
||||||
|
|
||||||
void SendAnimation::paint(QPainter &p, QPoint origin, QRect target) const {
|
QRect SendAnimation::paintGetArea(QPainter &p, QPoint origin, QRect target) const {
|
||||||
if (_flyIcon) {
|
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 {
|
|
||||||
p.drawImage(target, _center->frame());
|
p.drawImage(target, _center->frame());
|
||||||
p.drawImage(QRect(
|
const auto wide = QRect(
|
||||||
target.topLeft() - QPoint(target.width(), target.height()) / 2,
|
target.topLeft() - QPoint(target.width(), target.height()) / 2,
|
||||||
target.size() * 2
|
target.size() * 2);
|
||||||
), _effect->frame());
|
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() {
|
void SendAnimation::startAnimations() {
|
||||||
|
|
|
@ -33,7 +33,7 @@ public:
|
||||||
~SendAnimation();
|
~SendAnimation();
|
||||||
|
|
||||||
void setRepaintCallback(Fn<void()> repaint);
|
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]] QString playingAroundEmoji() const;
|
||||||
[[nodiscard]] bool flying() const;
|
[[nodiscard]] bool flying() const;
|
||||||
|
|
|
@ -810,7 +810,7 @@ void Manager::setSelectedIcon(int index) const {
|
||||||
if (select && !icon->selectAnimated) {
|
if (select && !icon->selectAnimated) {
|
||||||
icon->selectAnimated = true;
|
icon->selectAnimated = true;
|
||||||
select->animate(
|
select->animate(
|
||||||
[=] { updateCurrentButton(); },
|
crl::guard(this, [=] { updateCurrentButton(); }),
|
||||||
0,
|
0,
|
||||||
select->framesCount() - 1);
|
select->framesCount() - 1);
|
||||||
}
|
}
|
||||||
|
@ -1301,9 +1301,9 @@ void Manager::paintAllEmoji(
|
||||||
const auto shift = QPoint(0, oneHeight * (expandUp ? -1 : 1));
|
const auto shift = QPoint(0, oneHeight * (expandUp ? -1 : 1));
|
||||||
auto emojiPosition = mainEmojiPosition
|
auto emojiPosition = mainEmojiPosition
|
||||||
+ QPoint(0, scroll * (expandUp ? 1 : -1));
|
+ QPoint(0, scroll * (expandUp ? 1 : -1));
|
||||||
const auto update = [=] {
|
const auto update = crl::guard(this, [=] {
|
||||||
updateCurrentButton();
|
updateCurrentButton();
|
||||||
};
|
});
|
||||||
for (const auto &icon : _icons) {
|
for (const auto &icon : _icons) {
|
||||||
const auto target = countTarget(*icon).translated(emojiPosition);
|
const auto target = countTarget(*icon).translated(emojiPosition);
|
||||||
emojiPosition += shift;
|
emojiPosition += shift;
|
||||||
|
|
|
@ -190,7 +190,7 @@ void InlineList::paint(
|
||||||
const auto size = st::reactionBottomSize;
|
const auto size = st::reactionBottomSize;
|
||||||
const auto skip = (size - st::reactionBottomImage) / 2;
|
const auto skip = (size - st::reactionBottomImage) / 2;
|
||||||
const auto inbubble = (_data.flags & InlineListData::Flag::InBubble);
|
const auto inbubble = (_data.flags & InlineListData::Flag::InBubble);
|
||||||
const auto animated = _animation
|
const auto animated = (_animation && context.reactionEffects)
|
||||||
? _animation->playingAroundEmoji()
|
? _animation->playingAroundEmoji()
|
||||||
: QString();
|
: QString();
|
||||||
if (_animation && animated.isEmpty()) {
|
if (_animation && animated.isEmpty()) {
|
||||||
|
@ -236,7 +236,9 @@ void InlineList::paint(
|
||||||
p.drawImage(image.topLeft(), button.image);
|
p.drawImage(image.topLeft(), button.image);
|
||||||
}
|
}
|
||||||
if (animating) {
|
if (animating) {
|
||||||
_animation->paint(p, QPoint(), image);
|
context.reactionEffects->paint = [=](QPainter &p) {
|
||||||
|
return _animation->paintGetArea(p, QPoint(), image);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
p.setPen(!inbubble
|
p.setPen(!inbubble
|
||||||
? (chosen
|
? (chosen
|
||||||
|
|
|
@ -90,9 +90,15 @@ struct MessageImageStyle {
|
||||||
style::icon historyVideoMessageMute = { Qt::Uninitialized };
|
style::icon historyVideoMessageMute = { Qt::Uninitialized };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ReactionEffectPainter {
|
||||||
|
QPoint offset;
|
||||||
|
Fn<QRect(QPainter&)> paint;
|
||||||
|
};
|
||||||
|
|
||||||
struct ChatPaintContext {
|
struct ChatPaintContext {
|
||||||
not_null<const ChatStyle*> st;
|
not_null<const ChatStyle*> st;
|
||||||
const BubblePattern *bubblesPattern = nullptr;
|
const BubblePattern *bubblesPattern = nullptr;
|
||||||
|
ReactionEffectPainter *reactionEffects = nullptr;
|
||||||
QRect viewport;
|
QRect viewport;
|
||||||
QRect clip;
|
QRect clip;
|
||||||
TextSelection selection;
|
TextSelection selection;
|
||||||
|
|
Loading…
Add table
Reference in a new issue