From ea6821aca20e38d6ef52fb2d37430789aabe1f44 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 23 Oct 2020 11:08:43 +0300 Subject: [PATCH] Improve animation in pinned bar. --- Telegram/SourceFiles/ui/chat/message_bar.cpp | 90 +++++++++++++++++++- Telegram/SourceFiles/ui/chat/message_bar.h | 7 ++ 2 files changed, 94 insertions(+), 3 deletions(-) diff --git a/Telegram/SourceFiles/ui/chat/message_bar.cpp b/Telegram/SourceFiles/ui/chat/message_bar.cpp index 7972583cb..8b4ec1d59 100644 --- a/Telegram/SourceFiles/ui/chat/message_bar.cpp +++ b/Telegram/SourceFiles/ui/chat/message_bar.cpp @@ -13,6 +13,33 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/palette.h" namespace Ui { +namespace { + +[[nodiscard]] int SameFirstPartLength(const QString &a, const QString &b) { + const auto [i, j] = ranges::mismatch(a, b); + return (i - a.begin()); +} + +[[nodiscard]] bool MuchDifferent( + int same, + const QString &a, + const QString &b) { + return (same * 2 < a.size()) || (same * 2 < b.size()); +} + +[[nodiscard]] bool MuchDifferent(const QString &a, const QString &b) { + return MuchDifferent(SameFirstPartLength(a, b), a, b); +} + +[[nodiscard]] bool ComplexTitleAnimation( + int same, + const QString &a, + const QString &b) { + return !MuchDifferent(same, a, b) + && (same != a.size() || same != b.size()); +} + +} // namespace MessageBar::MessageBar(not_null parent, const style::MessageBar &st) : _st(st) @@ -51,10 +78,11 @@ MessageBar::BodyAnimation MessageBar::DetectBodyAnimationType( ? currentAnimation->bodyAnimation : BodyAnimation::None; const auto somethingChanged = (currentContent.text != nextContent.text) + || (currentContent.title != nextContent.title) || (currentContent.index != nextContent.index) || (currentContent.count != nextContent.count); return (now == BodyAnimation::Full - || currentContent.title != nextContent.title + || MuchDifferent(currentContent.title, nextContent.title) || (currentContent.title.isEmpty() && somethingChanged)) ? BodyAnimation::Full : (now == BodyAnimation::Text || somethingChanged) @@ -93,10 +121,21 @@ void MessageBar::tweenTo(MessageBarContent &&content) { : RectPart::None; animation.imageFrom = grabImagePart(); animation.bodyOrTextFrom = grabBodyOrTextPart(animation.bodyAnimation); + const auto sameLength = SameFirstPartLength( + _content.title, + content.title); + if (animation.bodyAnimation == BodyAnimation::Text + && ComplexTitleAnimation(sameLength, _content.title, content.title)) { + animation.titleSame = grabTitleBase(sameLength); + animation.titleFrom = grabTitlePart(sameLength); + } auto was = std::move(_animation); updateFromContent(std::move(content)); animation.imageTo = grabImagePart(); animation.bodyOrTextTo = grabBodyOrTextPart(animation.bodyAnimation); + if (!animation.titleSame.isNull()) { + animation.titleTo = grabTitlePart(sameLength); + } if (was) { _animation = std::move(was); std::swap(*_animation, animation); @@ -154,6 +193,20 @@ QRect MessageBar::imageRect() const { return QRect(left, top, size, size); } +QRect MessageBar::titleRangeRect(int from, int till) const { + auto result = bodyRect(); + result.setHeight(st::msgServiceNameFont->height); + const auto left = from + ? st::msgServiceNameFont->width(_content.title.mid(0, from)) + : 0; + const auto right = (till <= _content.title.size()) + ? st::msgServiceNameFont->width(_content.title.mid(0, till)) + : result.width(); + result.setLeft(result.left() + left); + result.setWidth(right - left); + return result; +} + QRect MessageBar::bodyRect(bool withImage) const { const auto innerLeft = st::msgReplyBarSkip + st::msgReplyBarSkip; const auto imageSkip = st::msgReplyBarSize.height() @@ -196,6 +249,21 @@ QPixmap MessageBar::grabBodyOrTextPart(BodyAnimation type) { : QPixmap(); } +QPixmap MessageBar::grabTitleBase(int till) { + return grabTitleRange(0, till); +} + +QPixmap MessageBar::grabTitlePart(int from) { + Expects(from <= _content.title.size()); + + return grabTitleRange(from, _content.title.size()); +} + +QPixmap MessageBar::grabTitleRange(int from, int till) { + const auto guard = makeGrabGuard(); + return GrabWidget(widget(), titleRangeRect(from, till)); +} + QPixmap MessageBar::grabBodyPart() { const auto guard = makeGrabGuard(); return GrabWidget(widget(), bodyRect()); @@ -324,8 +392,24 @@ void MessageBar::paint(Painter &p) { p.setOpacity(1.); } if (!_animation || _animation->bodyAnimation != BodyAnimation::Full) { - p.setPen(_st.titleFg); - _title.drawLeftElided(p, body.x(), body.y(), body.width(), width); + if (_animation && !_animation->titleSame.isNull()) { + const auto factor = style::DevicePixelRatio(); + p.drawPixmap(body.x(), body.y(), _animation->titleSame); + p.setOpacity(1. - progress); + p.drawPixmap( + body.x() + (_animation->titleSame.width() / factor), + body.y() + shiftFrom, + _animation->titleFrom); + p.setOpacity(progress); + p.drawPixmap( + body.x() + (_animation->titleSame.width() / factor), + body.y() + shiftTo, + _animation->titleTo); + p.setOpacity(1.); + } else { + p.setPen(_st.titleFg); + _title.drawLeftElided(p, body.x(), body.y(), body.width(), width); + } } else { p.setOpacity(1. - progress); p.drawPixmap( diff --git a/Telegram/SourceFiles/ui/chat/message_bar.h b/Telegram/SourceFiles/ui/chat/message_bar.h index bf15d219a..7ecdb9e60 100644 --- a/Telegram/SourceFiles/ui/chat/message_bar.h +++ b/Telegram/SourceFiles/ui/chat/message_bar.h @@ -52,6 +52,9 @@ private: Ui::Animations::Simple barTop; QPixmap bodyOrTextFrom; QPixmap bodyOrTextTo; + QPixmap titleSame; + QPixmap titleFrom; + QPixmap titleTo; QPixmap imageFrom; QPixmap imageTo; BodyAnimation bodyAnimation = BodyAnimation::None; @@ -71,12 +74,16 @@ private: [[nodiscard]] QPixmap prepareImage(const QImage &preview); [[nodiscard]] QRect imageRect() const; + [[nodiscard]] QRect titleRangeRect(int from, int till) const; [[nodiscard]] QRect bodyRect(bool withImage) const; [[nodiscard]] QRect bodyRect() const; [[nodiscard]] QRect textRect() const; auto makeGrabGuard(); [[nodiscard]] QPixmap grabBodyOrTextPart(BodyAnimation type); + [[nodiscard]] QPixmap grabTitleBase(int till); + [[nodiscard]] QPixmap grabTitlePart(int from); + [[nodiscard]] QPixmap grabTitleRange(int from, int till); [[nodiscard]] QPixmap grabImagePart(); [[nodiscard]] QPixmap grabBodyPart(); [[nodiscard]] QPixmap grabTextPart();