diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 6426c64467..21e22150df 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -799,6 +799,8 @@ PRIVATE history/view/history_view_emoji_interactions.h history/view/history_view_empty_list_bubble.cpp history/view/history_view_empty_list_bubble.h + history/view/history_view_fake_items.cpp + history/view/history_view_fake_items.h history/view/history_view_group_call_bar.cpp history/view/history_view_group_call_bar.h history/view/history_view_item_preview.h diff --git a/Telegram/SourceFiles/boxes/reactions_settings_box.cpp b/Telegram/SourceFiles/boxes/reactions_settings_box.cpp index e336244039..8a71e395b8 100644 --- a/Telegram/SourceFiles/boxes/reactions_settings_box.cpp +++ b/Telegram/SourceFiles/boxes/reactions_settings_box.cpp @@ -16,8 +16,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/admin_log/history_admin_log_item.h" #include "history/history.h" #include "history/history_item.h" -#include "history/view/history_view_element.h" #include "history/view/reactions/history_view_reactions_strip.h" +#include "history/view/history_view_element.h" +#include "history/view/history_view_fake_items.h" #include "lang/lang_keys.h" #include "boxes/premium_preview_box.h" #include "main/main_session.h" @@ -43,53 +44,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace { -PeerId GenerateUser(not_null history, const QString &name) { - Expects(history->peer->isUser()); - - const auto peerId = Data::FakePeerIdForJustName(name); - history->owner().processUser(MTP_user( - MTP_flags(MTPDuser::Flag::f_first_name | MTPDuser::Flag::f_min), - peerToBareMTPInt(peerId), - MTP_long(0), - MTP_string(tr::lng_settings_chat_message_reply_from(tr::now)), - MTPstring(), // last name - MTPstring(), // username - MTPstring(), // phone - MTPUserProfilePhoto(), // profile photo - MTPUserStatus(), // status - MTP_int(0), // bot info version - MTPVector(), // restrictions - MTPstring(), // bot placeholder - MTPstring(), // lang code - MTPEmojiStatus(), - MTPVector(), - MTPint(), // stories_max_id - MTPPeerColor(), // color - MTPPeerColor())); // profile_color - return peerId; -} - -AdminLog::OwnedItem GenerateItem( - not_null delegate, - not_null history, - PeerId from, - FullMsgId replyTo, - const QString &text) { - Expects(history->peer->isUser()); - - const auto item = history->addNewLocalMessage({ - .id = history->nextNonHistoryEntryId(), - .flags = (MessageFlag::FakeHistoryItem - | MessageFlag::HasFromId - | MessageFlag::HasReplyInfo), - .from = from, - .replyTo = FullReplyTo{ .messageId = replyTo }, - .date = base::unixtime::now(), - }, TextWithEntities{ .text = text }, MTP_messageMediaEmpty()); - - return AdminLog::OwnedItem(delegate, item); -} - void AddMessage( not_null container, not_null controller, @@ -135,15 +89,15 @@ void AddMessage( const auto history = controller->session().data().history( PeerData::kServiceNotificationsId); - state->reply = GenerateItem( + state->reply = HistoryView::GenerateItem( state->delegate.get(), history, - GenerateUser( + HistoryView::GenerateUser( history, tr::lng_settings_chat_message_reply_from(tr::now)), FullMsgId(), tr::lng_settings_chat_message_reply(tr::now)); - auto message = GenerateItem( + auto message = HistoryView::GenerateItem( state->delegate.get(), history, history->peer->id, diff --git a/Telegram/SourceFiles/history/view/history_view_fake_items.cpp b/Telegram/SourceFiles/history/view/history_view_fake_items.cpp new file mode 100644 index 0000000000..5a6538036f --- /dev/null +++ b/Telegram/SourceFiles/history/view/history_view_fake_items.cpp @@ -0,0 +1,66 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "history/view/history_view_fake_items.h" + +#include "base/unixtime.h" +#include "data/data_session.h" +#include "history/history.h" +#include "history/history_item.h" + +namespace HistoryView { + +AdminLog::OwnedItem GenerateItem( + not_null delegate, + not_null history, + PeerId from, + FullMsgId replyTo, + const QString &text, + EffectId effectId) { + Expects(history->peer->isUser()); + + const auto item = history->addNewLocalMessage({ + .id = history->nextNonHistoryEntryId(), + .flags = (MessageFlag::FakeHistoryItem + | MessageFlag::HasFromId + | MessageFlag::HasReplyInfo), + .from = from, + .replyTo = FullReplyTo{.messageId = replyTo }, + .date = base::unixtime::now(), + .effectId = effectId, + }, TextWithEntities{ .text = text }, MTP_messageMediaEmpty()); + + return AdminLog::OwnedItem(delegate, item); +} + +PeerId GenerateUser(not_null history, const QString &name) { + Expects(history->peer->isUser()); + + const auto peerId = Data::FakePeerIdForJustName(name); + history->owner().processUser(MTP_user( + MTP_flags(MTPDuser::Flag::f_first_name | MTPDuser::Flag::f_min), + peerToBareMTPInt(peerId), + MTP_long(0), + MTP_string(name), + MTPstring(), // last name + MTPstring(), // username + MTPstring(), // phone + MTPUserProfilePhoto(), // profile photo + MTPUserStatus(), // status + MTP_int(0), // bot info version + MTPVector(), // restrictions + MTPstring(), // bot placeholder + MTPstring(), // lang code + MTPEmojiStatus(), + MTPVector(), + MTPint(), // stories_max_id + MTPPeerColor(), // color + MTPPeerColor())); // profile_color + return peerId; +} + +} // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/history_view_fake_items.h b/Telegram/SourceFiles/history/view/history_view_fake_items.h new file mode 100644 index 0000000000..266c280042 --- /dev/null +++ b/Telegram/SourceFiles/history/view/history_view_fake_items.h @@ -0,0 +1,26 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "history/admin_log/history_admin_log_item.h" + +namespace HistoryView { + +[[nodiscard]] AdminLog::OwnedItem GenerateItem( + not_null delegate, + not_null history, + PeerId from, + FullMsgId replyTo, + const QString &text, + EffectId effectId = 0); + +[[nodiscard]] PeerId GenerateUser( + not_null history, + const QString &name); + +} // namespace HistoryView diff --git a/Telegram/SourceFiles/menu/menu_send.cpp b/Telegram/SourceFiles/menu/menu_send.cpp index 536d988e3c..f046629972 100644 --- a/Telegram/SourceFiles/menu/menu_send.cpp +++ b/Telegram/SourceFiles/menu/menu_send.cpp @@ -9,17 +9,25 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_common.h" #include "base/event_filter.h" +#include "base/unixtime.h" #include "boxes/abstract_box.h" #include "chat_helpers/compose/compose_show.h" #include "chat_helpers/stickers_emoji_pack.h" #include "core/shortcuts.h" +#include "history/admin_log/history_admin_log_item.h" #include "history/view/media/history_view_sticker.h" #include "history/view/reactions/history_view_reactions_selector.h" +#include "history/view/history_view_element.h" +#include "history/view/history_view_fake_items.h" #include "history/view/history_view_schedule_box.h" +#include "history/history.h" +#include "history/history_item.h" +#include "history/history_unread_things.h" #include "lang/lang_keys.h" #include "lottie/lottie_single_player.h" #include "ui/chat/chat_style.h" #include "ui/chat/chat_theme.h" +#include "ui/effects/path_shift_gradient.h" #include "ui/effects/ripple_animation.h" #include "ui/widgets/buttons.h" #include "ui/widgets/popup_menu.h" @@ -33,20 +41,39 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_message_reactions.h" #include "data/data_session.h" #include "main/main_session.h" -#include "history/history.h" -#include "history/history_unread_things.h" #include "apiwrap.h" #include "window/themes/window_theme.h" #include "window/section_widget.h" #include "styles/style_chat.h" #include "styles/style_chat_helpers.h" #include "styles/style_menu_icons.h" +#include "styles/style_window.h" #include namespace SendMenu { namespace { +class Delegate final : public HistoryView::DefaultElementDelegate { +public: + Delegate(not_null pathGradient) + : _pathGradient(pathGradient) { + } + +private: + bool elementAnimationsPaused() override { + return false; + } + not_null elementPathShiftGradient() override { + return _pathGradient; + } + HistoryView::Context elementContext() override { + return HistoryView::Context::ContactPreview; + } + + const not_null _pathGradient; +}; + class EffectPreview final : public Ui::RpWidget { public: EffectPreview( @@ -64,6 +91,7 @@ private: void setupGeometry(QPoint position); void setupBackground(); + void setupItem(); void repaintBackground(); void setupLottie(); void setupSend(Details details); @@ -71,10 +99,16 @@ private: [[nodiscard]] bool checkReady(); + const EffectId _effectId = 0; const Data::Reaction _effect; const std::shared_ptr _show; const std::shared_ptr _theme; const std::unique_ptr _chatStyle; + const std::unique_ptr _pathGradient; + const std::unique_ptr _delegate; + const not_null _history; + const AdminLog::OwnedItem _replyTo; + const AdminLog::OwnedItem _item; const std::unique_ptr _send; const Fn _actionWithEffect; @@ -86,6 +120,7 @@ private: QRect _inner; QImage _bg; + QPoint _itemShift; rpl::lifetime _readyCheckLifetime; @@ -169,37 +204,67 @@ EffectPreview::EffectPreview( Fn action, Fn done) : RpWidget(parent) +, _effectId(effect.id.custom()) , _effect(effect) , _show(show) , _theme(Window::Theme::DefaultChatThemeOn(lifetime())) , _chatStyle( std::make_unique( _show->session().colorIndicesValue())) +, _pathGradient( + HistoryView::MakePathShiftGradient(_chatStyle.get(), [=] { update(); })) +, _delegate(std::make_unique(_pathGradient.get())) +, _history(show->session().data().history( + PeerData::kServiceNotificationsId)) +, _replyTo(HistoryView::GenerateItem( + _delegate.get(), + _history, + HistoryView::GenerateUser( + _history, + tr::lng_settings_chat_message_reply_from(tr::now)), + FullMsgId(), + tr::lng_settings_chat_message(tr::now))) +, _item(HistoryView::GenerateItem( + _delegate.get(), + _history, + _history->peer->id, + _replyTo->data()->fullId(), + tr::lng_settings_chat_message_reply(tr::now), + _effectId)) , _send( std::make_unique( this, tr::lng_effect_send(tr::now), st::effectPreviewSend)) -, _actionWithEffect( - ComposeActionWithEffect( - action, - effect.id.custom(), - done)) { +, _actionWithEffect(ComposeActionWithEffect(action, _effectId, done)) { setupGeometry(position); setupBackground(); + setupItem(); setupLottie(); setupSend(details); } void EffectPreview::paintEvent(QPaintEvent *e) { - auto p = QPainter(this); + auto p = Painter(this); p.drawImage(0, 0, _bg); + p.setClipRect(_inner); + p.translate(_itemShift); + auto rect = QRect(0, 0, st::windowMinWidth, _inner.height()); + auto context = _theme->preparePaintContext( + _chatStyle.get(), + rect, + rect, + false); + context.outbg = _item->hasOutLayout(); + _item->draw(p, context); + p.translate(-_itemShift); + if (_lottie && _lottie->ready()) { const auto factor = style::DevicePixelRatio(); auto request = Lottie::FrameRequest(); request.box = _inner.size() * factor; - const auto rightAligned = false;// hasRightLayout(); + const auto rightAligned = _item->hasRightLayout(); if (!rightAligned) { request.mirrorHorizontal = true; } @@ -255,6 +320,22 @@ void EffectPreview::setupBackground() { }, lifetime()); } +void EffectPreview::setupItem() { + _item->resizeGetHeight(st::windowMinWidth); + + const auto icon = _item->effectIconGeometry(); + Assert(!icon.isEmpty()); + + const auto size = _inner.size(); + const auto shift = _item->hasRightLayout() + ? (-size.width() / 3) + : (size.width() / 3); + const auto position = QPoint( + shift + icon.x() + (icon.width() - size.width()) / 2, + icon.y() + (icon.height() - size.height()) / 2); + _itemShift = _inner.topLeft() - position; +} + void EffectPreview::repaintBackground() { const auto ratio = style::DevicePixelRatio(); const auto inner = _inner.size() + QSize(0, _send->height()); @@ -270,25 +351,6 @@ void EffectPreview::repaintBackground() { _theme.get(), QSize(inner.width(), inner.height() * 5), QRect(QPoint(), inner)); - - { // bubble - const auto radius = st::bubbleRadiusLarge; - const auto out = 2 * radius; - p.setPen(Qt::NoPen); - p.setBrush(_chatStyle->msgInShadow()); - const auto skip = st::msgPadding.bottom() - st::msgDateDelta.y(); - p.drawRoundedRect(-out, -out, out + inner.width() / 3, out + inner.height() / 2 + st::normalFont->height / 2 + st::msgShadow, radius, radius); - p.setBrush(_chatStyle->msgInBg()); - p.drawRoundedRect(-out, -out, out + inner.width() / 3, out + inner.height() / 2 + st::normalFont->height / 2, radius, radius); - - if (!_icon.isNull()) { - p.drawImage( - inner.width() / 3 - _icon.width(), - inner.height() / 2 + st::normalFont->height / 2 - _icon.height(), - _icon); - } - } - p.fillRect( QRect(0, _inner.height(), _inner.width(), _send->height()), st::previewMarkRead.bgColor); @@ -300,6 +362,7 @@ void EffectPreview::repaintBackground() { _bg.fill(Qt::transparent); auto p = QPainter(&_bg); + const auto &shadow = st::previewMenu.animation.shadow; const auto shadowed = QRect(_inner.topLeft(), inner); Ui::Shadow::paint(p, shadowed, width(), shadow); @@ -307,9 +370,8 @@ void EffectPreview::repaintBackground() { } void EffectPreview::setupLottie() { - const auto id = _effect.id.custom(); const auto reactions = &_show->session().data().reactions(); - reactions->preloadEffectImageFor(id); + reactions->preloadEffectImageFor(_effectId); if (const auto document = _effect.aroundAnimation) { _media = document->createMediaView();