diff --git a/Telegram/SourceFiles/dialogs/dialogs_row.cpp b/Telegram/SourceFiles/dialogs/dialogs_row.cpp index cffed86a4..9bc44f0b4 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_row.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_row.cpp @@ -27,6 +27,63 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Dialogs { +constexpr auto kTopLayer = 1; +constexpr auto kNoneLayer = 0; + +Row::CornerLayersManager::CornerLayersManager() = default; + +bool Row::CornerLayersManager::isSameLayer(Layer layer) const { + return isFinished() && (_nextLayer == layer); +} + +void Row::CornerLayersManager::setLayer( + Layer layer, + Fn updateCallback) { + if (_nextLayer == layer) { + return; + } + _lastFrameShown = false; + _prevLayer = _nextLayer; + _nextLayer = layer; + if (_animation.animating()) { + _animation.change( + 1., + st::dialogsOnlineBadgeDuration * (1. - _animation.value(1.))); + } else if (updateCallback) { + _animation.start( + std::move(updateCallback), + 0., + 1., + st::dialogsOnlineBadgeDuration); + } +} + +float64 Row::CornerLayersManager::progressForLayer(Layer layer) const { + return (_nextLayer == layer) + ? progress() + : (_prevLayer == layer) + ? (1. - progress()) + : 0.; +} + +float64 Row::CornerLayersManager::progress() const { + return _animation.value(1.); +} + +bool Row::CornerLayersManager::isFinished() const { + return (progress() == 1.) && _lastFrameShown; +} + +void Row::CornerLayersManager::markFrameShown() { + if (progress() == 1.) { + _lastFrameShown = true; + } +} + +bool Row::CornerLayersManager::isDisplayedNone() const { + return (progress() == 1.) && (_nextLayer == 0); +} + BasicRow::BasicRow() = default; BasicRow::~BasicRow() = default; @@ -123,28 +180,25 @@ uint64 Row::sortKey(FilterId filterId) const { } void Row::setCornerBadgeShown( - bool shown, + CornerLayersManager::Layer nextLayer, Fn updateCallback) const { - const auto value = (shown ? 1 : 0); - if (_cornerBadgeShown == value) { - return; - } - const_cast(this)->_cornerBadgeShown = value; - if (_cornerBadgeUserpic && _cornerBadgeUserpic->animation.animating()) { - _cornerBadgeUserpic->animation.change( - _cornerBadgeShown ? 1. : 0., - st::dialogsOnlineBadgeDuration); - } else if (updateCallback) { - ensureCornerBadgeUserpic(); - _cornerBadgeUserpic->animation.start( - std::move(updateCallback), - _cornerBadgeShown ? 0. : 1., - _cornerBadgeShown ? 1. : 0., - st::dialogsOnlineBadgeDuration); + const auto cornerBadgeShown = (nextLayer ? 1 : 0); + if (_cornerBadgeShown == cornerBadgeShown) { + if (!cornerBadgeShown) { + return; + } else if (_cornerBadgeUserpic + && _cornerBadgeUserpic->layersManager.isSameLayer(nextLayer)) { + return; + } } + const_cast(this)->_cornerBadgeShown = cornerBadgeShown; + ensureCornerBadgeUserpic(); + _cornerBadgeUserpic->layersManager.setLayer( + nextLayer, + std::move(updateCallback)); if (!_cornerBadgeShown && _cornerBadgeUserpic - && !_cornerBadgeUserpic->animation.animating()) { + && _cornerBadgeUserpic->layersManager.isDisplayedNone()) { _cornerBadgeUserpic = nullptr; } } @@ -154,16 +208,17 @@ void Row::updateCornerBadgeShown( Fn updateCallback) const { const auto user = peer->asUser(); const auto now = user ? base::unixtime::now() : TimeId(); - const auto shown = [&] { - if (user) { - return Data::IsUserOnline(user, now); - } else if (const auto channel = peer->asChannel()) { - return Data::ChannelHasActiveCall(channel); + const auto nextLayer = [&] { + if (user && Data::IsUserOnline(user, now)) { + return kTopLayer; + } else if (peer->isChannel() + && Data::ChannelHasActiveCall(peer->asChannel())) { + return kTopLayer; } - return false; + return kNoneLayer; }(); - setCornerBadgeShown(shown, std::move(updateCallback)); - if (shown && user) { + setCornerBadgeShown(nextLayer, std::move(updateCallback)); + if ((nextLayer == kTopLayer) && user) { peer->owner().watchForOffline(user, now); } } @@ -198,6 +253,7 @@ void Row::PaintCornerBadgeFrame( PainterHighQualityEnabler hq(q); q.setCompositionMode(QPainter::CompositionMode_Source); + const auto progress = data->layersManager.progressForLayer(kTopLayer); const auto size = peer->isUser() ? st::dialogsOnlineBadgeSize : st::dialogsCallBadgeSize; @@ -205,10 +261,10 @@ void Row::PaintCornerBadgeFrame( const auto skip = peer->isUser() ? st::dialogsOnlineBadgeSkip : st::dialogsCallBadgeSkip; - const auto shrink = (size / 2) * (1. - data->shown); + const auto shrink = (size / 2) * (1. - progress); auto pen = QPen(Qt::transparent); - pen.setWidthF(stroke * data->shown); + pen.setWidthF(stroke * progress); q.setPen(pen); q.setBrush(data->active ? st::dialogsOnlineBadgeFgActive @@ -229,10 +285,10 @@ void Row::paintUserpic( const Ui::PaintContext &context) const { updateCornerBadgeShown(peer); - const auto shown = _cornerBadgeUserpic - ? _cornerBadgeUserpic->animation.value(_cornerBadgeShown ? 1. : 0.) - : (_cornerBadgeShown ? 1. : 0.); - if (!historyForCornerBadge || shown == 0.) { + const auto cornerBadgeShown = !_cornerBadgeUserpic + ? _cornerBadgeShown + : !_cornerBadgeUserpic->layersManager.isDisplayedNone(); + if (!historyForCornerBadge || !cornerBadgeShown) { BasicRow::paintUserpic( p, peer, @@ -261,15 +317,15 @@ void Row::paintUserpic( } const auto key = peer->userpicUniqueKey(userpicView()); const auto frameIndex = videoUserpic ? videoUserpic->frameIndex() : -1; - if (_cornerBadgeUserpic->shown != shown + if (!_cornerBadgeUserpic->layersManager.isFinished() || _cornerBadgeUserpic->key != key || _cornerBadgeUserpic->active != context.active || _cornerBadgeUserpic->frameIndex != frameIndex || videoUserpic) { - _cornerBadgeUserpic->shown = shown; _cornerBadgeUserpic->key = key; _cornerBadgeUserpic->active = context.active; _cornerBadgeUserpic->frameIndex = frameIndex; + _cornerBadgeUserpic->layersManager.markFrameShown(); PaintCornerBadgeFrame( _cornerBadgeUserpic.get(), peer, @@ -290,7 +346,8 @@ void Row::paintUserpic( : st::dialogsBg; const auto size = st::dialogsCallBadgeSize; const auto skip = st::dialogsCallBadgeSkip; - p.setOpacity(shown); + p.setOpacity( + _cornerBadgeUserpic->layersManager.progressForLayer(kTopLayer)); p.translate(context.st->padding.left(), context.st->padding.top()); actionPainter->paintSpeaking( p, diff --git a/Telegram/SourceFiles/dialogs/dialogs_row.h b/Telegram/SourceFiles/dialogs/dialogs_row.h index 58cd551eb..6e8f3c484 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_row.h +++ b/Telegram/SourceFiles/dialogs/dialogs_row.h @@ -143,17 +143,37 @@ public: private: friend class List; + class CornerLayersManager { + public: + using Layer = int; + CornerLayersManager(); + + [[nodiscard]] bool isSameLayer(Layer layer) const; + [[nodiscard]] bool isDisplayedNone() const; + [[nodiscard]] float64 progressForLayer(Layer layer) const; + [[nodiscard]] float64 progress() const; + [[nodiscard]] bool isFinished() const; + void setLayer(Layer layer, Fn updateCallback); + void markFrameShown(); + + private: + bool _lastFrameShown = false; + Layer _prevLayer = 0; + Layer _nextLayer = 0; + Ui::Animations::Simple _animation; + + }; + struct CornerBadgeUserpic { InMemoryKey key; - float64 shown = 0.; + CornerLayersManager layersManager; int frameIndex = -1; bool active = false; QImage frame; - Ui::Animations::Simple animation; }; void setCornerBadgeShown( - bool shown, + CornerLayersManager::Layer nextLayer, Fn updateCallback) const; void ensureCornerBadgeUserpic() const; static void PaintCornerBadgeFrame(