From 9b3acf7162ea4f007ac0839b3a286e4aef68606e Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 17 Sep 2025 13:04:06 +0400 Subject: [PATCH] Make messages nicer, sending animation. --- Telegram/SourceFiles/calls/calls.style | 2 + .../calls/group/calls_group_messages.cpp | 62 +++++-- .../calls/group/calls_group_messages.h | 6 +- .../calls/group/calls_group_messages_ui.cpp | 152 +++++++++++++----- .../calls/group/calls_group_messages_ui.h | 2 + .../view/media/history_view_media_generic.cpp | 1 - 6 files changed, 174 insertions(+), 51 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls.style b/Telegram/SourceFiles/calls/calls.style index 1f39a317b0..daf3b7e6f2 100644 --- a/Telegram/SourceFiles/calls/calls.style +++ b/Telegram/SourceFiles/calls/calls.style @@ -1687,6 +1687,8 @@ groupCallInviteLinkIcon: icon {{ "info/edit/group_manage_links", mediaviewTextLi groupCallMessagePadding: margins(8px, 2px, 8px, 2px); groupCallMessageSkip: 8px; +groupCallUserpic: 20px; +groupCallUserpicPadding: margins(2px, 2px, 4px, 2px); confcallLinkMenu: IconButton(boxTitleClose) { icon: icon {{ "title_menu_dots", boxTitleCloseFg }}; diff --git a/Telegram/SourceFiles/calls/group/calls_group_messages.cpp b/Telegram/SourceFiles/calls/group/calls_group_messages.cpp index 28ae009cbb..2881e062e8 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_messages.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_messages.cpp @@ -20,10 +20,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/ui_utility.h" namespace Calls::Group { +namespace { + +constexpr auto kMessageLifetime = TimeId(20); + +} // namespace Messages::Messages(not_null call, not_null api) : _call(call) -, _api(api) { +, _api(api) +, _destroyTimer([=] { checkDestroying(); }) { Ui::PostponeCall(_call, [=] { _call->real( ) | rpl::start_with_next([=](not_null call) { @@ -71,9 +77,13 @@ void Messages::send(TextWithTags text) { _call->inputCall(), serialized )).done([=](const MTPBool &, const MTP::Response &response) { + if (rand() % 256 < 64) { + AssertIsDebug(); + failed(id, response); + } else sent(id, response); - }).fail([=] { - failed(id); + }).fail([=](const MTP::Error &, const MTP::Response &response) { + failed(id, response); }).send(); } else { auto counter = ::tl::details::LengthCounter(); @@ -94,11 +104,11 @@ void Messages::send(TextWithTags text) { MTP_bytes(bytes::make_span(encrypted)) )).done([=](const MTPBool &, const MTP::Response &response) { sent(id, response); - }).fail([=] { - failed(id); + }).fail([=](const MTP::Error &, const MTP::Response &response) { + failed(id, response); }).send(); } - pushChanges(); + checkDestroying(true); } void Messages::received(const MTPDupdateGroupCallMessage &data) { @@ -152,6 +162,39 @@ void Messages::received( .peer = peer->owner().peer(peerFromMTP(from)), .text = Api::ParseTextWithEntities(&peer->session(), message), }); + checkDestroying(true); +} + +void Messages::checkDestroying(bool afterChanges) { + auto next = TimeId(); + const auto now = base::unixtime::now(); + const auto destroyTime = now - kMessageLifetime; + const auto initial = _messages.size(); + for (auto i = begin(_messages); i != end(_messages);) { + const auto date = i->date; + if (!date) { + ++i; + } else if (date <= destroyTime) { + i = _messages.erase(i); + } else if (!next) { + next = date + kMessageLifetime - now; + ++i; + } else { + ++i; + } + } + if (!next) { + _destroyTimer.cancel(); + } else { + const auto delay = next * crl::time(1000); + if (!_destroyTimer.isActive() + || (_destroyTimer.remainingTime() > delay)) { + _destroyTimer.callOnce(delay); + } + } + if (afterChanges || (_messages.size() < initial)) { + pushChanges(); + } } rpl::producer> Messages::listValue() const { @@ -174,15 +217,16 @@ void Messages::sent(int id, const MTP::Response &response) { const auto i = ranges::find(_messages, id, &Message::id); if (i != end(_messages)) { i->date = Api::UnixtimeFromMsgId(response.outerMsgId); - pushChanges(); + checkDestroying(true); } } -void Messages::failed(int id) { +void Messages::failed(int id, const MTP::Response &response) { const auto i = ranges::find(_messages, id, &Message::id); if (i != end(_messages)) { + i->date = Api::UnixtimeFromMsgId(response.outerMsgId); i->failed = true; - pushChanges(); + checkDestroying(true); } } diff --git a/Telegram/SourceFiles/calls/group/calls_group_messages.h b/Telegram/SourceFiles/calls/group/calls_group_messages.h index 22bc06881d..9c65bbe63d 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_messages.h +++ b/Telegram/SourceFiles/calls/group/calls_group_messages.h @@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "base/timer.h" + namespace Calls { class GroupCall; } // namespace Calls @@ -45,10 +47,11 @@ private: [[nodiscard]] bool ready() const; void sendPending(); void pushChanges(); + void checkDestroying(bool afterChanges = false); void received(const MTPPeer &from, const MTPTextWithEntities &message); void sent(int id, const MTP::Response &response); - void failed(int id); + void failed(int id, const MTP::Response &response); const not_null _call; const not_null _api; @@ -57,6 +60,7 @@ private: std::vector _pending; + base::Timer _destroyTimer; std::vector _messages; rpl::event_stream> _changes; diff --git a/Telegram/SourceFiles/calls/group/calls_group_messages_ui.cpp b/Telegram/SourceFiles/calls/group/calls_group_messages_ui.cpp index 93de2a9696..645fa86a23 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_messages_ui.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_messages_ui.cpp @@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/controls/emoji_button.h" #include "ui/controls/send_button.h" #include "ui/effects/animations.h" +#include "ui/effects/radial_animation.h" #include "ui/text/text_utilities.h" #include "ui/widgets/fields/input_field.h" #include "ui/widgets/scroll_area.h" @@ -55,7 +56,7 @@ struct MessagesUi::MessageView { PeerData *from = nullptr; Ui::Animations::Simple toggleAnimation; Ui::Animations::Simple sentAnimation; - Ui::Animations::Basic sendingAnimation; + std::unique_ptr sendingAnimation; Ui::PeerUserpicView view; Ui::Text::String text; int top = 0; @@ -97,8 +98,10 @@ void MessagesUi::setupList(rpl::producer> messages) { const auto i = ranges::find(from, till, id, &Message::id); if (i == till) { toggleMessage(entry, false); + continue; } else if (entry.failed != i->failed) { setContentFailed(entry); + updateMessageSize(entry); repaintMessage(entry.id); } else if (entry.sending != (i->date == 0)) { animateMessageSent(entry); @@ -121,33 +124,56 @@ void MessagesUi::animateMessageSent(MessageView &entry) { entry.sending = false; entry.sentAnimation.start([=] { repaintMessage(id); - }, 0., 1, st::slideWrapDuration, anim::easeOutCirc); + }, 0., 1, st::slideDuration, anim::easeOutCirc); + repaintMessage(id); } void MessagesUi::updateMessageSize(MessageView &entry) { const auto &padding = st::groupCallMessagePadding; - const auto widthSkip = padding.left() + padding.right(); + const auto hasUserpic = !entry.failed; + const auto userpicPadding = st::groupCallUserpicPadding; + const auto userpicSize = st::groupCallUserpic; + const auto leftSkip = hasUserpic + ? (userpicPadding.left() + userpicSize + userpicPadding.right()) + : padding.left(); + const auto widthSkip = leftSkip + padding.right(); const auto inner = _width - widthSkip; - entry.width = std::min(inner, entry.text.maxWidth()) + widthSkip; + + const auto size = Ui::Text::CountOptimalTextSize( + entry.text, + std::min(st::groupCallWidth / 2, inner), + inner); + + const auto textHeight = size.height(); + entry.width = size.width() + widthSkip; entry.left = (_width - entry.width) / 2; + const auto contentHeight = padding.top() + textHeight + padding.bottom(); + const auto userpicHeight = hasUserpic + ? (userpicPadding.top() + userpicSize + userpicPadding.bottom()) + : 0; + const auto skip = st::groupCallMessageSkip; - const auto textHeight = entry.text.countHeight(entry.width); - entry.realHeight = skip - + padding.top() - + textHeight - + padding.bottom(); - entry.height = entry.toggleAnimation.animating() + entry.realHeight = skip + std::max(contentHeight, userpicHeight); +} + +bool MessagesUi::updateMessageHeight(MessageView &entry) { + const auto height = entry.toggleAnimation.animating() ? anim::interpolate( 0, entry.realHeight, entry.toggleAnimation.value(entry.removed ? 0. : 1.)) : entry.realHeight; + if (entry.height == height) { + return false; + } + entry.height = height; + return true; } void MessagesUi::setContentFailed(MessageView &entry) { - const auto manager = &entry.from->owner().customEmojiManager(); + entry.failed = true; entry.text = Ui::Text::String( st::messageTextStyle, TextWithEntities().append( @@ -155,7 +181,21 @@ void MessagesUi::setContentFailed(MessageView &entry) { ).append(' ').append( Ui::Text::Italic(u"Failed to send the message."_q)), kMarkupTextOptions, - st::groupCallWidth / 2); + st::groupCallWidth / 4); +} + +void MessagesUi::setContent( + MessageView &entry, + const TextWithEntities &text) { + entry.text = Ui::Text::String( + st::messageTextStyle, + text, + kMarkupTextOptions, + st::groupCallWidth / 4, + Core::TextContext({ + .session = &_show->session(), + .repaint = [this, id = entry.id] { repaintMessage(id); }, + })); } void MessagesUi::toggleMessage(MessageView &entry, bool shown) { @@ -166,7 +206,8 @@ void MessagesUi::toggleMessage(MessageView &entry, bool shown) { shown ? 0. : 1., shown ? 1. : 0., st::slideWrapDuration, - anim::easeOutCirc); + shown ? anim::easeOutCirc : anim::easeInCirc); + repaintMessage(id); } void MessagesUi::repaintMessage(int id) { @@ -180,13 +221,10 @@ void MessagesUi::repaintMessage(int id) { return; } if (!i->sending && !i->sentAnimation.animating()) { - i->sendingAnimation.stop(); + i->sendingAnimation = nullptr; } if (i->toggleAnimation.animating() || i->height != i->realHeight) { - const auto shown = i->toggleAnimation.value(i->removed ? 0. : 1.); - const auto height = anim::interpolate(0, i->realHeight, shown); - if (i->height != height) { - i->height = height; + if (updateMessageHeight(*i)) { recountHeights(i, i->top); return; } @@ -227,30 +265,23 @@ void MessagesUi::appendMessage(const Message &data) { }; entry.from = data.peer; entry.sending = !data.date; - entry.failed = data.failed; - if (entry.failed) { + if (data.failed) { setContentFailed(entry); } else { - const auto manager = &entry.from->owner().customEmojiManager(); - entry.text = Ui::Text::String( - st::messageTextStyle, - Ui::Text::SingleCustomEmoji( - manager->peerUserpicEmojiData(entry.from), - u"@"_q).append(' ').append( - Ui::Text::Bold(data.peer->shortName()) - ).append(' ').append(data.text), - kMarkupTextOptions, - st::groupCallWidth / 2, - Core::TextContext({ - .session = &_show->session(), - .repaint = repaint, - })); + setContent( + entry, + Ui::Text::Bold( + data.peer->shortName()).append(' ').append(data.text)); } entry.top = top; updateMessageSize(entry); if (entry.sending) { - entry.sendingAnimation.init(repaint); - entry.sendingAnimation.start(); + using namespace Ui; + const auto &st = st::defaultInfiniteRadialAnimation; + entry.sendingAnimation = std::make_unique( + repaint, + st); + entry.sendingAnimation->start(0); } entry.height = 0; toggleMessage(entry, true); @@ -262,8 +293,7 @@ void MessagesUi::setupMessagesWidget() { object_ptr(_scroll.get())); _messages->paintRequest() | rpl::start_with_next([=](QRect clip) { - auto p = QPainter(_messages); - auto hq = PainterHighQualityEnabler(p); + auto p = Painter(_messages); const auto skip = st::groupCallMessageSkip; const auto padding = st::groupCallMessagePadding; const auto widthSkip = padding.left() + padding.right(); @@ -289,10 +319,49 @@ void MessagesUi::setupMessagesWidget() { } _messageBgRect.paint(p, { entry.left, entry.top, width, use }); + auto leftSkip = padding.left(); + const auto hasUserpic = !entry.failed; + if (hasUserpic) { + const auto userpicSize = st::groupCallUserpic; + const auto userpicPadding = st::groupCallUserpicPadding; + const auto position = QPoint( + entry.left + userpicPadding.left(), + entry.top + userpicPadding.top()); + const auto rect = QRect( + position, + QSize(userpicSize, userpicSize)); + entry.from->paintUserpic(p, entry.view, { + .position = position, + .size = userpicSize, + .shape = Ui::PeerUserpicShape::Circle, + }); + if (const auto animation = entry.sendingAnimation.get()) { + auto hq = PainterHighQualityEnabler(p); + auto pen = st::groupCallBg->p; + const auto shift = userpicPadding.left(); + pen.setWidthF(shift); + p.setPen(pen); + p.setBrush(Qt::NoBrush); + const auto state = animation->computeState(); + const auto sent = entry.sending + ? 0. + : entry.sentAnimation.value(1.); + p.setOpacity(state.shown * (1. - sent)); + p.drawArc( + rect.marginsRemoved({ shift, shift, shift, shift }), + state.arcFrom, + state.arcLength); + p.setOpacity(1.); + } + leftSkip = userpicPadding.left() + + userpicSize + + userpicPadding.right(); + } + p.setPen(st::radialFg); entry.text.draw(p, { .position = { - entry.left + padding.left(), + entry.left + leftSkip, entry.top + padding.top() }, .availableWidth = entry.width - widthSkip, @@ -313,8 +382,11 @@ void MessagesUi::applyWidth() { } auto top = 0; for (auto &entry : _views) { - updateMessageSize(entry); entry.top = top; + + updateMessageSize(entry); + updateMessageHeight(entry); + top += entry.height; } updateGeometries(); diff --git a/Telegram/SourceFiles/calls/group/calls_group_messages_ui.h b/Telegram/SourceFiles/calls/group/calls_group_messages_ui.h index 79d5931642..9e2352f1db 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_messages_ui.h +++ b/Telegram/SourceFiles/calls/group/calls_group_messages_ui.h @@ -48,7 +48,9 @@ private: void setupList(rpl::producer> messages); void toggleMessage(MessageView &entry, bool shown); void setContentFailed(MessageView &entry); + void setContent(MessageView &entry, const TextWithEntities &text); void updateMessageSize(MessageView &entry); + bool updateMessageHeight(MessageView &entry); void animateMessageSent(MessageView &entry); void repaintMessage(int id); void recountHeights(std::vector::iterator i, int top); diff --git a/Telegram/SourceFiles/history/view/media/history_view_media_generic.cpp b/Telegram/SourceFiles/history/view/media/history_view_media_generic.cpp index 6512eec3cc..29243458aa 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media_generic.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_media_generic.cpp @@ -14,7 +14,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_element.h" #include "history/view/history_view_cursor_state.h" #include "ui/chat/chat_style.h" -#include "ui/widgets/tooltip.h" #include "ui/dynamic_image.h" #include "ui/dynamic_thumbnails.h" #include "ui/painter.h"