diff --git a/Telegram/SourceFiles/boxes/sticker_set_box.cpp b/Telegram/SourceFiles/boxes/sticker_set_box.cpp index 592ba282c..53454fcd2 100644 --- a/Telegram/SourceFiles/boxes/sticker_set_box.cpp +++ b/Telegram/SourceFiles/boxes/sticker_set_box.cpp @@ -1268,13 +1268,12 @@ void StickerSetBox::Inner::paintSticker( (_singleSize.height() - size.height()) / 2); auto lottieFrame = QImage(); if (element.emoji) { - element.emoji->paint( - p, - ppos.x(), - ppos.y(), - now, - st::windowBgOver->c, - paused); + element.emoji->paint(p, { + .preview = st::windowBgOver->c, + .now = now, + .position = ppos, + .paused = paused, + }); } else if (element.lottie && element.lottie->ready()) { lottieFrame = element.lottie->frame(); p.drawImage( diff --git a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp index ec82d7aa6..85fe949f8 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp @@ -40,6 +40,7 @@ namespace ChatHelpers { namespace { constexpr auto kCollapsedRows = 3; +constexpr auto kAppearDuration = 0.3; using Core::RecentEmojiId; using Core::RecentEmojiDocument; @@ -486,15 +487,19 @@ auto EmojiListWidget::premiumChosen() const void EmojiListWidget::paintExpanding( QPainter &p, QRect clip, + int finalBottom, + float64 progress, RectPart origin) { const auto shift = clip.topLeft(); const auto adjusted = clip.translated(-shift); + const auto finalHeight = (finalBottom - clip.y()); p.translate(shift); p.setClipRect(adjusted); - const auto context = ExpandingContext{ + paint(p, ExpandingContext{ + .progress = progress, + .finalHeight = finalHeight, .expanding = true, - }; - paint(p, context, adjusted); + }, adjusted); p.translate(-shift); } @@ -778,7 +783,7 @@ void EmojiListWidget::paintEvent(QPaintEvent *e) { void EmojiListWidget::paint( QPainter &p, - const ExpandingContext &context, + ExpandingContext context, QRect clip) { auto fromColumn = floorclamp( clip.x() - _rowsLeft, @@ -796,6 +801,7 @@ void EmojiListWidget::paint( toColumn = _columnCount - toColumn; } + const auto expandProgress = context.progress; const auto paused = this->paused(); const auto now = crl::now(); auto selectedButton = std::get_if(!v::is_null(_pressed) @@ -866,10 +872,26 @@ void EmojiListWidget::paint( const auto selected = (state == _selected) || (!_picker->isHidden() && state == _pickerSelected); - auto w = QPoint( + const auto position = QPoint( _rowsLeft + j * _singleSize.width(), info.rowsTop + i * _singleSize.height() - ) + _areaPosition; + ); + const auto w = position + _areaPosition; + if (context.expanding) { + const auto y = (position.y() - st().padding.top()); + const auto x = (position.x() - _rowsLeft); + const auto sum = y + + std::max(std::min(y, width()) - x, 0); + const auto maxSum = context.finalHeight + + std::min(context.finalHeight, width()); + const auto started = (sum / float64(maxSum)) + - kAppearDuration; + context.progress = (expandProgress <= started) + ? 0. + : (expandProgress >= started + kAppearDuration) + ? 1. + : ((expandProgress - started) / kAppearDuration); + } if (info.collapsed && index + 1 == _columnCount * kCollapsedRows) { drawCollapsedBadge(p, w - _areaPosition, info.count); @@ -883,12 +905,12 @@ void EmojiListWidget::paint( _overBg.paint(p, QRect(tl, st::emojiPanArea)); } if (info.section == int(Section::Recent)) { - drawRecent(p, w, now, paused, index); + drawRecent(p, context, w, now, paused, index); } else if (info.section < _staticCount) { - drawEmoji(p, w, _emoji[info.section][index]); + drawEmoji(p, context, w, _emoji[info.section][index]); } else { const auto set = info.section - _staticCount; - drawCustom(p, w, now, paused, set, index); + drawCustom(p, context, w, now, paused, set, index); } } } @@ -919,6 +941,7 @@ void EmojiListWidget::drawCollapsedBadge( void EmojiListWidget::drawRecent( QPainter &p, + const ExpandingContext &context, QPoint position, crl::time now, bool paused, @@ -932,23 +955,25 @@ void EmojiListWidget::drawRecent( ) - _areaPosition; p.drawImage(position, _premiumIcon->image()); } else { - drawEmoji(p, position, *emoji); + drawEmoji(p, context, position, *emoji); } } else { Assert(_recent[index].custom != nullptr); position += _innerPosition + _customPosition; - _recent[index].custom->paint( - p, - position.x(), - position.y(), - now, - st::windowBgRipple->c, - paused); + _recent[index].custom->paint(p, { + .preview = st::windowBgRipple->c, + .now = now, + .scale = context.progress, + .position = position, + .paused = paused, + .scaled = context.expanding, + }); } } void EmojiListWidget::drawEmoji( QPainter &p, + const ExpandingContext &context, QPoint position, EmojiPtr emoji) { position += _innerPosition; @@ -962,6 +987,7 @@ void EmojiListWidget::drawEmoji( void EmojiListWidget::drawCustom( QPainter &p, + const ExpandingContext &context, QPoint position, crl::time now, bool paused, @@ -969,13 +995,14 @@ void EmojiListWidget::drawCustom( int index) { position += _innerPosition + _customPosition; _custom[set].painted = true; - _custom[set].list[index].custom->paint( - p, - position.x(), - position.y(), - now, - st::windowBgRipple->c, - paused); + _custom[set].list[index].custom->paint(p, { + .preview = st::windowBgRipple->c, + .now = now, + .scale = context.progress, + .position = position, + .paused = paused, + .scaled = context.expanding, + }); } bool EmojiListWidget::checkPickerHide() { diff --git a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h index b8a35e9cc..1411571b5 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h +++ b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h @@ -112,7 +112,12 @@ public: [[nodiscard]] auto premiumChosen() const -> rpl::producer>; - void paintExpanding(QPainter &p, QRect clip, RectPart origin); + void paintExpanding( + QPainter &p, + QRect clip, + int finalBottom, + float64 progress, + RectPart origin); protected: void visibleTopBottomUpdated( @@ -208,6 +213,8 @@ private: OverSet, OverButton>; struct ExpandingContext { + float64 progress = 0.; + int finalHeight = 0; bool expanding = false; }; @@ -235,20 +242,23 @@ private: [[nodiscard]] EmojiPtr lookupOverEmoji(const OverEmoji *over) const; void selectEmoji(EmojiPtr emoji); void selectCustom(not_null document); - void paint(QPainter &p, const ExpandingContext &context, QRect clip); + void paint(QPainter &p, ExpandingContext context, QRect clip); void drawCollapsedBadge(QPainter &p, QPoint position, int count); void drawRecent( QPainter &p, + const ExpandingContext &context, QPoint position, crl::time now, bool paused, int index); void drawEmoji( QPainter &p, + const ExpandingContext &context, QPoint position, EmojiPtr emoji); void drawCustom( QPainter &p, + const ExpandingContext &context, QPoint position, crl::time now, bool paused, diff --git a/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp b/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp index a73ff0f8e..162f66e82 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp @@ -351,7 +351,11 @@ void SuggestionsWidget::paintEvent(QPaintEvent *e) { const auto x = i * _oneWidth + (_oneWidth - size) / 2; const auto y = (_oneWidth - size) / 2; if (row.custom) { - row.custom->paint(p, x, y, now, preview, false); + row.custom->paint(p, { + .preview = preview, + .now = now, + .position = { x, y }, + }); } else { Ui::Emoji::Draw(p, emoji, esize, x, y); } diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_footer.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_footer.cpp index d53272ac8..05eb91736 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_footer.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_footer.cpp @@ -1217,19 +1217,29 @@ void StickersListFooter::paintSetIcon( crl::time now, bool paused) const { const auto &icon = _icons[info.index]; - if (context.expanding) { - p.save(); - const auto center = QPoint( - info.adjustedLeft + _singleWidth / 2, - _iconsTop + st().footer / 2); - const auto shift = QPoint(0, anim::interpolate(height() / 2, 0, context.progress)); - p.translate(shift + center); - p.scale(context.progress, context.progress); - p.translate(-center); - } + const auto expandingShift = context.expanding + ? QPoint( + 0, + anim::interpolate(height() / 2, 0, context.progress)) + : QPoint(); if (icon.sticker) { icon.ensureMediaCreated(); const_cast(this)->validateIconAnimation(icon); + } + if (context.expanding) { + if (icon.custom) { + p.translate(expandingShift); + } else { + p.save(); + const auto center = QPoint( + info.adjustedLeft + _singleWidth / 2, + _iconsTop + st().footer / 2); + p.translate(expandingShift + center); + p.scale(context.progress, context.progress); + p.translate(-center); + } + } + if (icon.sticker) { const auto origin = icon.sticker->stickerSetOrigin(); const auto thumb = icon.thumbnailMedia ? icon.thumbnailMedia->image() @@ -1239,7 +1249,15 @@ void StickersListFooter::paintSetIcon( const auto x = info.adjustedLeft + (_singleWidth - icon.pixw) / 2; const auto y = _iconsTop + (st().footer - icon.pixh) / 2; if (icon.custom) { - icon.custom->paint(p, x, y, now, st::emojiIconFg->c, paused); + icon.custom->paint(p, Ui::Text::CustomEmoji::Context{ + .preview = st::emojiIconFg->c, + .size = QSize(icon.pixw, icon.pixh), + .now = now, + .scale = context.progress, + .position = { x, y }, + .paused = paused, + .scaled = context.expanding, + }); } else if (icon.lottie && icon.lottie->ready()) { const auto frame = icon.lottie->frame(); const auto size = frame.size() / cIntRetinaFactor(); @@ -1368,7 +1386,11 @@ void StickersListFooter::paintSetIcon( } } if (context.expanding) { - p.restore(); + if (icon.custom) { + p.translate(-expandingShift); + } else { + p.restore(); + } } } diff --git a/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp b/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp index 76baa4c16..e9518122a 100644 --- a/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp +++ b/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp @@ -356,12 +356,7 @@ void BottomInfo::paintReactions( y += st::msgDateFont->height; widthLeft = availableWidth; } - if (!reaction.custom && reaction.image.isNull()) { - reaction.custom = _reactionsOwner->resolveCustomFor( - reaction.id, - ::Data::Reactions::ImageSize::BottomInfo); - } - if (!reaction.custom && reaction.image.isNull()) { + if (reaction.image.isNull()) { reaction.image = _reactionsOwner->resolveImageFor( reaction.id, ::Data::Reactions::ImageSize::BottomInfo); @@ -375,9 +370,6 @@ void BottomInfo::paintReactions( && (reaction.count < 2 || !reaction.animation->flying()); if (!reaction.image.isNull() && !skipImage) { p.drawImage(image.topLeft(), reaction.image); - } else if (reaction.custom && !skipImage) { - const auto size = Ui::Text::AdjustCustomEmojiSize(st::emojiSize); - reaction.custom->paint(p, x + (st::reactionInfoSize - size) / 2, y + (st::msgDateFont->height - size) / 2, crl::now(), Qt::white, false); } if (animating) { animations.push_back({ diff --git a/Telegram/SourceFiles/history/view/history_view_bottom_info.h b/Telegram/SourceFiles/history/view/history_view_bottom_info.h index 7248b2353..3d7a8d7a8 100644 --- a/Telegram/SourceFiles/history/view/history_view_bottom_info.h +++ b/Telegram/SourceFiles/history/view/history_view_bottom_info.h @@ -16,10 +16,6 @@ namespace Ui { struct ChatPaintContext; } // namespace Ui -namespace Ui::Text { -class CustomEmoji; -} // namespace Ui::Text - namespace Data { class Reactions; } // namespace Data @@ -105,7 +101,6 @@ private: struct Reaction { mutable std::unique_ptr animation; mutable QImage image; - mutable std::unique_ptr custom; ReactionId id; QString countText; int count = 0; diff --git a/Telegram/SourceFiles/history/view/history_view_sticker_toast.cpp b/Telegram/SourceFiles/history/view/history_view_sticker_toast.cpp index db1910f01..804d25b09 100644 --- a/Telegram/SourceFiles/history/view/history_view_sticker_toast.cpp +++ b/Telegram/SourceFiles/history/view/history_view_sticker_toast.cpp @@ -298,16 +298,15 @@ void StickerToast::setupEmojiPreview( widget->paintRequest( ) | rpl::start_with_next([=] { auto p = QPainter(widget); - const auto paused = false; const auto size = Ui::Emoji::GetSizeLarge() / style::DevicePixelRatio(); - instance->object.paint( - p, - (widget->width() - size) / 2, - (widget->height() - size) / 2, - crl::now(), - st::toastBg->c, - paused); + instance->object.paint(p, Ui::Text::CustomEmoji::Context{ + .preview = st::toastBg->c, + .now = crl::now(), + .position = QPoint( + (widget->width() - size) / 2, + (widget->height() - size) / 2), + }); }, widget->lifetime()); } diff --git a/Telegram/SourceFiles/history/view/media/history_view_custom_emoji.cpp b/Telegram/SourceFiles/history/view/media/history_view_custom_emoji.cpp index cfb6efb69..57c6967b8 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_custom_emoji.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_custom_emoji.cpp @@ -277,7 +277,11 @@ void CustomEmoji::paintCustom( } _selectedFrame.fill(Qt::transparent); auto q = QPainter(&_selectedFrame); - emoji->paint(q, 0, 0, context.now, preview, paused); + emoji->paint(q, { + .preview = preview, + .now = context.now, + .paused = paused, + }); q.end(); _selectedFrame = Images::Colored( @@ -285,7 +289,12 @@ void CustomEmoji::paintCustom( context.st->msgStickerOverlay()->c); p.drawImage(x, y, _selectedFrame); } else { - emoji->paint(p, x, y, context.now, preview, paused); + emoji->paint(p, { + .preview = preview, + .now = context.now, + .position = { x, y }, + .paused = paused, + }); } } diff --git a/Telegram/SourceFiles/history/view/media/history_view_large_emoji.cpp b/Telegram/SourceFiles/history/view/media/history_view_large_emoji.cpp index d7d81bf2e..5f3f336f4 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_large_emoji.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_large_emoji.cpp @@ -148,7 +148,11 @@ void LargeEmoji::paintCustom( } _selectedFrame.fill(Qt::transparent); auto q = QPainter(&_selectedFrame); - emoji->paint(q, 0, 0, context.now, preview, paused); + emoji->paint(q, { + .preview = preview, + .now = context.now, + .paused = paused, + }); q.end(); _selectedFrame = Images::Colored( @@ -156,7 +160,12 @@ void LargeEmoji::paintCustom( context.st->msgStickerOverlay()->c); p.drawImage(x + skip, y + skip, _selectedFrame); } else { - emoji->paint(p, x + skip, y + skip, context.now, preview, paused); + emoji->paint(p, { + .preview = preview, + .now = context.now, + .position = { x + skip, y + skip }, + .paused = paused, + }); } } diff --git a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp index 573f8036a..eeca3f6db 100644 --- a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp +++ b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp @@ -18,12 +18,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "chat_helpers/emoji_list_widget.h" #include "chat_helpers/stickers_list_footer.h" #include "window/window_session_controller.h" +#include "base/call_delayed.h" #include "styles/style_chat_helpers.h" #include "styles/style_chat.h" namespace HistoryView::Reactions { namespace { +constexpr auto kExpandDuration = crl::time(300); +constexpr auto kScaleDuration = crl::time(120); +constexpr auto kFullDuration = kExpandDuration + kScaleDuration; +constexpr auto kExpandDelay = crl::time(40); + class ShiftedEmoji final : public Ui::Text::CustomEmoji { public: ShiftedEmoji( @@ -33,13 +39,7 @@ public: QPoint shift); QString entityData() override; - void paint( - QPainter &p, - int x, - int y, - crl::time now, - const QColor &preview, - bool paused) override; + void paint(QPainter &p, const Context &context) override; void unload() override; private: @@ -64,14 +64,10 @@ QString ShiftedEmoji::entityData() { return _real->entityData(); } -void ShiftedEmoji::paint( - QPainter &p, - int x, - int y, - crl::time now, - const QColor &preview, - bool paused) { - _real->paint(p, x + _shift.x(), y + _shift.y(), now, preview, paused); +void ShiftedEmoji::paint(QPainter &p, const Context &context) { + auto copy = context; + copy.position += _shift; + _real->paint(p, copy); } void ShiftedEmoji::unload() { @@ -270,6 +266,7 @@ void Selector::paintCollapsed(QPainter &p) { void Selector::paintExpanding(Painter &p, float64 progress) { const auto rects = paintExpandingBg(p, progress); //paintStripWithoutExpand(p); + progress /= kFullDuration; paintFadingExpandIcon(p, progress); if (_footer) { _footer->paintExpanding( @@ -281,11 +278,16 @@ void Selector::paintExpanding(Painter &p, float64 progress) { _list->paintExpanding( p, rects.list.marginsRemoved(st::reactPanelEmojiPan.margin), + rects.finalBottom, + progress, RectPart::TopRight); } auto Selector::paintExpandingBg(QPainter &p, float64 progress) -> ExpandingRects { + progress = (progress >= kExpandDuration) + ? 1. + : (progress / kExpandDuration); constexpr auto kFramesCount = Ui::RoundAreaWithShadow::kFramesCount; const auto frame = int(base::SafeRound(progress * (kFramesCount - 1))); const auto radiusStart = st::reactStripHeight / 2.; @@ -318,6 +320,7 @@ auto Selector::paintExpandingBg(QPainter &p, float64 progress) .categories = QRect(inner.x(), inner.y(), inner.width(), categories), .list = inner.marginsRemoved({ 0, categories, 0, 0 }), .radius = radius, + .finalBottom = height() - extents.bottom(), }; } @@ -393,7 +396,8 @@ void Selector::paintEvent(QPaintEvent *e) { paintAppearing(p); } else if (!_expanded) { paintCollapsed(p); - } else if (const auto progress = _expanding.value(1.); progress < 1.) { + } else if (const auto progress = _expanding.value(kFullDuration) + ; progress < kFullDuration) { paintExpanding(p, progress); } else { paintExpanded(p); @@ -468,9 +472,14 @@ void Selector::expand() { createList(strong); cacheExpandIcon(); - _paintBuffer = _cachedRound.PrepareImage(size()); - _expanded = true; - _expanding.start([=] { update(); }, 0., 1., st::slideDuration); + [[maybe_unused]] const auto grabbed = Ui::GrabWidget(_scroll); + + base::call_delayed(kExpandDelay, this, [=] { + _paintBuffer = _cachedRound.PrepareImage(size()); + _expanded = true; + const auto full = kExpandDuration + kScaleDuration; + _expanding.start([=] { update(); }, 0., full, full); + }); } void Selector::cacheExpandIcon() { diff --git a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.h b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.h index 93a643454..4084a009f 100644 --- a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.h +++ b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.h @@ -70,6 +70,7 @@ private: QRect categories; QRect list; float64 radius = 0.; + int finalBottom = 0; }; void paintEvent(QPaintEvent *e) override; diff --git a/Telegram/SourceFiles/info/profile/info_profile_cover.cpp b/Telegram/SourceFiles/info/profile/info_profile_cover.cpp index 9e0700e07..15f75a2f8 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_cover.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_cover.cpp @@ -148,13 +148,11 @@ void BadgeView::setBadge(Badge badge, DocumentId emojiStatusId) { _view->paintRequest( ) | rpl::start_with_next([=, check = _view.data()]{ Painter p(check); - _emojiStatus->paint( - p, - 0, - 0, - crl::now(), - st::windowBgOver->c, - _animationPaused && _animationPaused()); + _emojiStatus->paint(p, { + .preview = st::windowBgOver->c, + .now = crl::now(), + .paused = _animationPaused && _animationPaused(), + }); }, _view->lifetime()); } else { const auto icon = (_badge == Badge::Verified) diff --git a/Telegram/SourceFiles/ui/text/custom_emoji_instance.cpp b/Telegram/SourceFiles/ui/text/custom_emoji_instance.cpp index 60c87c9c0..b4977b5c5 100644 --- a/Telegram/SourceFiles/ui/text/custom_emoji_instance.cpp +++ b/Telegram/SourceFiles/ui/text/custom_emoji_instance.cpp @@ -30,6 +30,32 @@ struct CacheHeader { int length = 0; }; +void PaintScaledImage( + QPainter &p, + const QRect &target, + const Cache::Frame &frame, + const Context &context) { + if (context.scaled) { + const auto sx = anim::interpolate( + target.width() / 2, + 0, + context.scale); + const auto sy = (target.height() == target.width()) + ? sx + : anim::interpolate(target.height() / 2, 0, context.scale); + const auto scaled = target.marginsRemoved({ sx, sy, sx, sy }); + if (frame.source.isNull()) { + p.drawImage(scaled, *frame.image); + } else { + p.drawImage(scaled, *frame.image, frame.source); + } + } else if (frame.source.isNull()) { + p.drawImage(target, *frame.image); + } else { + p.drawImage(target, *frame.image, frame.source); + } +} + } // namespace Preview::Preview(QPainterPath path, float64 scale) @@ -40,15 +66,14 @@ Preview::Preview(QImage image, bool exact) : _data(Image{ .data = std::move(image), .exact = exact }) { } -void Preview::paint(QPainter &p, int x, int y, const QColor &preview) { +void Preview::paint(QPainter &p, const Context &context) { if (const auto path = std::get_if(&_data)) { - paintPath(p, x, y, preview, *path); + paintPath(p, context, *path); } else if (const auto image = std::get_if(&_data)) { const auto &data = image->data; const auto factor = style::DevicePixelRatio(); - const auto width = data.width() / factor; - const auto height = data.height() / factor; - p.drawImage(QRect(x, y, width, height), data); + const auto rect = QRect(context.position, data.size() / factor); + PaintScaledImage(p, rect, { .image = &data }, context); } } @@ -72,27 +97,33 @@ QImage Preview::image() const { void Preview::paintPath( QPainter &p, - int x, - int y, - const QColor &preview, + const Context &context, const ScaledPath &path) { auto hq = PainterHighQualityEnabler(p); - p.setBrush(preview); + p.setBrush(context.preview); p.setPen(Qt::NoPen); const auto scale = path.scale; - const auto required = (scale != 1.); + const auto required = (scale != 1.) || context.scaled; if (required) { p.save(); } - p.translate(x, y); + p.translate(context.position); if (required) { p.scale(scale, scale); + const auto center = QPoint( + context.size.width() / 2, + context.size.height() / 2); + if (context.scaled) { + p.translate(center); + p.scale(context.scale, context.scale); + p.translate(-center); + } } p.drawPath(path.path); if (required) { p.restore(); } else { - p.translate(-x, -y); + p.translate(-context.position); } } @@ -306,12 +337,11 @@ void Cache::finish() { PaintFrameResult Cache::paintCurrentFrame( QPainter &p, - int x, - int y, - crl::time now) { + const Context &context) { if (!_frames) { return {}; } + const auto now = context.paused ? 0 : context.now; const auto finishes = now ? currentFrameFinishes() : 0; if (finishes && now >= finishes) { ++_frame; @@ -324,7 +354,8 @@ PaintFrameResult Cache::paintCurrentFrame( } const auto info = frame(std::min(_frame, _frames - 1)); const auto size = _size / style::DevicePixelRatio(); - p.drawImage(QRect(x, y, size, size), *info.image, info.source); + const auto rect = QRect(context.position, QSize(size, size)); + PaintScaledImage(p, rect, info, context); const auto next = currentFrameFinishes(); const auto duration = next ? (next - _shown) : 0; return { @@ -360,8 +391,8 @@ QString Cached::entityData() const { return _entityData; } -PaintFrameResult Cached::paint(QPainter &p, int x, int y, crl::time now) { - return _cache.paintCurrentFrame(p, x, y, now); +PaintFrameResult Cached::paint(QPainter &p, const Context &context) { + return _cache.paintCurrentFrame(p, context); } Preview Cached::makePreview() const { @@ -469,8 +500,8 @@ void Renderer::finish() { } } -PaintFrameResult Renderer::paint(QPainter &p, int x, int y, crl::time now) { - const auto result = _cache.paintCurrentFrame(p, x, y, now); +PaintFrameResult Renderer::paint(QPainter &p, const Context &context) { + const auto result = _cache.paintCurrentFrame(p, context); if (_generator && (!result.painted || _cache.currentFrame() + kPreloadFrames >= _cache.frames())) { @@ -526,13 +557,13 @@ bool Loading::loading() const { return _loader->loading(); } -void Loading::paint(QPainter &p, int x, int y, const QColor &preview) { +void Loading::paint(QPainter &p, const Context &context) { if (!_preview) { if (auto preview = _loader->preview()) { _preview = std::move(preview); } } - _preview.paint(p, x, y, preview); + _preview.paint(p, context); } bool Loading::hasImagePreview() const { @@ -578,15 +609,9 @@ QString Instance::entityData() const { Unexpected("State in Instance::entityData."); } -void Instance::paint( - QPainter &p, - int x, - int y, - crl::time now, - const QColor &preview, - bool paused) { +void Instance::paint(QPainter &p, const Context &context) { if (const auto loading = std::get_if(&_state)) { - loading->paint(p, x, y, preview); + loading->paint(p, context); loading->load([=](Loader::LoadResult result) { if (auto caching = std::get_if(&result)) { caching->renderer->setRepaintCallback([=] { repaint(); }); @@ -599,14 +624,14 @@ void Instance::paint( } }); } else if (const auto caching = std::get_if(&_state)) { - auto result = caching->renderer->paint(p, x, y, paused ? 0 : now); + auto result = caching->renderer->paint(p, context); if (!result.painted) { - caching->preview.paint(p, x, y, preview); + caching->preview.paint(p, context); } else { if (!caching->preview.isExactImage()) { caching->preview = caching->renderer->makePreview(); } - if (result.next > now) { + if (result.next > context.now) { _repaintLater(this, { result.next, result.duration }); } } @@ -614,8 +639,8 @@ void Instance::paint( _state = std::move(*cached); } } else if (const auto cached = std::get_if(&_state)) { - const auto result = cached->paint(p, x, y, paused ? 0 : now); - if (result.next > now) { + const auto result = cached->paint(p, context); + if (result.next > context.now) { _repaintLater(this, { result.next, result.duration }); } } @@ -695,18 +720,12 @@ QString Object::entityData() { return _instance->entityData(); } -void Object::paint( - QPainter &p, - int x, - int y, - crl::time now, - const QColor &preview, - bool paused) { +void Object::paint(QPainter &p, const Context &context) { if (!_using) { _using = true; _instance->incrementUsage(this); } - _instance->paint(p, x, y, now, preview, paused); + _instance->paint(p, context); } void Object::unload() { diff --git a/Telegram/SourceFiles/ui/text/custom_emoji_instance.h b/Telegram/SourceFiles/ui/text/custom_emoji_instance.h index e78f51656..84faae800 100644 --- a/Telegram/SourceFiles/ui/text/custom_emoji_instance.h +++ b/Telegram/SourceFiles/ui/text/custom_emoji_instance.h @@ -21,13 +21,15 @@ class FrameGenerator; namespace Ui::CustomEmoji { +using Context = Ui::Text::CustomEmoji::Context; + class Preview final { public: Preview() = default; Preview(QImage image, bool exact); Preview(QPainterPath path, float64 scale); - void paint(QPainter &p, int x, int y, const QColor &preview); + void paint(QPainter &p, const Context &context); [[nodiscard]] bool isImage() const; [[nodiscard]] bool isExactImage() const; [[nodiscard]] QImage image() const; @@ -48,9 +50,7 @@ private: void paintPath( QPainter &p, - int x, - int y, - const QColor &preview, + const Context &context, const ScaledPath &path); std::variant _data; @@ -86,11 +86,7 @@ public: [[nodiscard]] Preview makePreview() const; - PaintFrameResult paintCurrentFrame( - QPainter &p, - int x, - int y, - crl::time now); + PaintFrameResult paintCurrentFrame(QPainter &p, const Context &context); [[nodiscard]] int currentFrame() const; private: @@ -123,7 +119,7 @@ public: [[nodiscard]] QString entityData() const; [[nodiscard]] Preview makePreview() const; - PaintFrameResult paint(QPainter &p, int x, int y, crl::time now); + PaintFrameResult paint(QPainter &p, const Context &context); [[nodiscard]] Loading unload(); private: @@ -145,7 +141,7 @@ public: explicit Renderer(RendererDescriptor &&descriptor); virtual ~Renderer(); - PaintFrameResult paint(QPainter &p, int x, int y, crl::time now); + PaintFrameResult paint(QPainter &p, const Context &context); [[nodiscard]] std::optional ready(const QString &entityData); [[nodiscard]] std::unique_ptr cancel(); @@ -199,7 +195,7 @@ public: void load(Fn done); [[nodiscard]] bool loading() const; - void paint(QPainter &p, int x, int y, const QColor &preview); + void paint(QPainter &p, const Context &context); [[nodiscard]] bool hasImagePreview() const; [[nodiscard]] Preview imagePreview() const; void updatePreview(Preview preview); @@ -226,13 +222,7 @@ public: Instance &operator=(const Instance&) = delete; [[nodiscard]] QString entityData() const; - void paint( - QPainter &p, - int x, - int y, - crl::time now, - const QColor &preview, - bool paused); + void paint(QPainter &p, const Context &context); [[nodiscard]] bool hasImagePreview() const; [[nodiscard]] Preview imagePreview() const; void updatePreview(Preview preview); @@ -261,13 +251,7 @@ public: ~Object(); QString entityData() override; - void paint( - QPainter &p, - int x, - int y, - crl::time now, - const QColor &preview, - bool paused) override; + void paint(QPainter &p, const Context &context) override; void unload() override; void repaint(); diff --git a/Telegram/SourceFiles/ui/unread_badge.cpp b/Telegram/SourceFiles/ui/unread_badge.cpp index ca16cf82f..0e28a7b7e 100644 --- a/Telegram/SourceFiles/ui/unread_badge.cpp +++ b/Telegram/SourceFiles/ui/unread_badge.cpp @@ -180,13 +180,14 @@ int PeerBadge::drawGetWidth( id, descriptor.customEmojiRepaint); } - _emojiStatus->emoji->paint( - p, - iconx - 2 * _emojiStatus->skip, - icony + _emojiStatus->skip, - descriptor.now, - descriptor.preview, - descriptor.paused); + _emojiStatus->emoji->paint(p, { + .preview = descriptor.preview, + .now = descriptor.now, + .position = QPoint( + iconx - 2 * _emojiStatus->skip, + icony + _emojiStatus->skip), + .paused = descriptor.paused, + }); return iconw - 4 * _emojiStatus->skip; } return 0; diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 8162619cb..01c4ba869 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 8162619cb17456f31d1be378a7ed72dc928e0831 +Subproject commit 01c4ba869a07eabc9eea2b633542a53e9ff6ff4c