From cf2a44792f1abe4cd67c56cb734dbb133a4701bb Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 10 Mar 2026 22:48:42 +0400 Subject: [PATCH] Add member tag ripple. --- .../history/view/history_view_message.cpp | 130 +++++++++++++++--- .../history/view/history_view_message.h | 3 + 2 files changed, 112 insertions(+), 21 deletions(-) diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index dce702b4af..8cc6624a2b 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -66,6 +66,17 @@ constexpr auto kSummarizeThreshold = 512; constexpr auto kPlayStatusLimit = 2; const auto kPsaTooltipPrefix = "cloud_lng_tooltip_psa_"; +struct SecondRightAction { + std::unique_ptr ripple; + ClickHandlerPtr link; +}; + +struct BadgePillGeometry { + int textWidth = 0; + int width = 0; + int height = 0; +}; + [[nodiscard]] bool IsRippleLink(const ClickHandlerPtr &handler) { switch (handler->getTextEntity().type) { case EntityType::Url: @@ -102,10 +113,22 @@ const auto kPsaTooltipPrefix = "cloud_lng_tooltip_psa_"; }); } -struct SecondRightAction final { - std::unique_ptr ripple; - ClickHandlerPtr link; -}; +[[nodiscard]] BadgePillGeometry ComputeBadgePillGeometry( + not_null badge) { + const auto &padding = st::msgTagBadgePadding; + const auto textWidth = badge->tag.maxWidth(); + const auto contentWidth = padding.left() + + textWidth + + padding.right(); + const auto height = padding.top() + + st::msgFont->height + + padding.bottom(); + return { + .textWidth = textWidth, + .width = std::max(contentWidth, height), + .height = height, + }; +} } // namespace @@ -367,6 +390,7 @@ void Message::refreshRightBadge() { badge->role = role; badge->special = special || (text.isEmpty() && !tagText.empty()); badge->tagLink = nullptr; + badge->ripple = nullptr; if (tagText.empty()) { badge->tag.clear(); } else { @@ -398,9 +422,9 @@ void Message::refreshRightBadge() { badge->width = tagWidth + boostWidth; } else { const auto &padding = st::msgTagBadgePadding; - const auto tagTextWidth = badge->tag.maxWidth(); + const auto textWidth = badge->tag.maxWidth(); const auto contentWidth = padding.left() - + tagTextWidth + + textWidth + padding.right(); const auto pillHeight = padding.top() + st::msgFont->height @@ -1781,40 +1805,64 @@ void Message::paintFromName( if (badge->role != BadgeRole::User) { auto bgColor = badgeColor; bgColor.setAlphaF(0.15); + const auto pill = ComputeBadgePillGeometry(badge); const auto &padding = st::msgTagBadgePadding; - const auto tagTextWidth = badge->tag.maxWidth(); - const auto contentWidth = padding.left() - + tagTextWidth - + padding.right(); - const auto pillHeight = padding.top() - + st::msgFont->height - + padding.bottom(); - const auto pillWidth = std::max(contentWidth, pillHeight); const auto badgeTop = trect.top() - + (st::msgNameFont->height - pillHeight) / 2; + + (st::msgNameFont->height - pill.height) / 2; const auto pillRect = QRect( badgeLeft, badgeTop, - pillWidth, - pillHeight); + pill.width, + pill.height); p.setPen(Qt::NoPen); p.setBrush(bgColor); { auto hq = PainterHighQualityEnabler(p); p.drawRoundedRect( pillRect, - pillHeight / 2., - pillHeight / 2.); + pill.height / 2., + pill.height / 2.); + } + if (badge->ripple) { + auto rippleColor = badgeColor; + rippleColor.setAlphaF(0.1); + badge->ripple->paint( + p, + badgeLeft, + badgeTop, + width(), + &rippleColor); + if (badge->ripple->empty()) { + badge->ripple.reset(); + } } p.setPen(badgeColor); badge->tag.draw(p, { .position = QPoint( - badgeLeft + (pillWidth - tagTextWidth) / 2, + badgeLeft + (pill.width - pill.textWidth) / 2, badgeTop + padding.top()), - .availableWidth = tagTextWidth, + .availableWidth = pill.textWidth, .now = context.now, }); } else if (!badge->tag.isEmpty()) { + if (badge->ripple) { + const auto pill = ComputeBadgePillGeometry(badge); + const auto &padding = st::msgTagBadgePadding; + const auto pillLeft = badgeLeft + - (pill.width - pill.textWidth) / 2; + const auto pillTop = trect.top() - padding.top(); + auto rippleColor = badgeColor; + rippleColor.setAlphaF(0.1); + badge->ripple->paint( + p, + pillLeft, + pillTop, + width(), + &rippleColor); + if (badge->ripple->empty()) { + badge->ripple.reset(); + } + } p.setPen(st::rankUserFg); badge->tag.draw(p, { .position = QPoint(badgeLeft, trect.top()), @@ -2311,6 +2359,9 @@ void Message::clickHandlerPressedChanged( } else { _summarize->stopRipple(); } + } else if (const auto badge = Get() + ; badge && badge->tagLink && handler == badge->tagLink) { + toggleBadgeRipple(pressed); } else if (displayFromName() && handler == fromLink()) { startLinkRipple(); } else if (const auto via = data()->Get() @@ -2374,6 +2425,27 @@ void Message::toggleRightActionRipple(bool pressed) { } } +void Message::toggleBadgeRipple(bool pressed) { + const auto badge = Get(); + if (!badge) { + return; + } else if (pressed) { + if (!badge->ripple) { + const auto pill = ComputeBadgePillGeometry(badge); + auto mask = Ui::RippleAnimation::RoundRectMask( + QSize(pill.width, pill.height), + pill.height / 2); + badge->ripple = std::make_unique( + st::defaultRippleAnimation, + std::move(mask), + [=] { repaint(); }); + } + badge->ripple->add(badge->lastPoint); + } else if (badge->ripple) { + badge->ripple->lastStop(); + } +} + void Message::toggleReplyRipple(bool pressed) { const auto reply = Get(); if (!reply) { @@ -3155,6 +3227,22 @@ bool Message::getStateFromName( } }); } + { + const auto pill = ComputeBadgePillGeometry(badge); + const auto &padding = st::msgTagBadgePadding; + if (badge->role != BadgeRole::User) { + const auto badgeTop = trect.top() + + (st::msgNameFont->height - pill.height) / 2; + badge->lastPoint = point + - QPoint(badgeLeft, badgeTop); + } else { + const auto pillLeft = badgeLeft + - (pill.width - pill.textWidth) / 2; + const auto pillTop = trect.top() - padding.top(); + badge->lastPoint = point + - QPoint(pillLeft, pillTop); + } + } outResult->link = badge->tagLink; return true; } diff --git a/Telegram/SourceFiles/history/view/history_view_message.h b/Telegram/SourceFiles/history/view/history_view_message.h index d14c0edbc1..095f70744e 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.h +++ b/Telegram/SourceFiles/history/view/history_view_message.h @@ -78,6 +78,8 @@ struct RightBadge : RuntimeComponent { BadgeRole role = BadgeRole::User; bool overridden = false; bool special = false; + mutable std::unique_ptr ripple; + mutable QPoint lastPoint; }; struct BottomRippleMask { @@ -242,6 +244,7 @@ private: int radius) const; void toggleRightActionRipple(bool pressed); + void toggleBadgeRipple(bool pressed); void toggleReplyRipple(bool pressed); void toggleSummaryHeaderRipple(bool pressed);