diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 5bf0f0a7b3..7b609c610d 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -924,6 +924,8 @@ PRIVATE history/view/history_view_pinned_tracker.h history/view/history_view_quick_action.cpp history/view/history_view_quick_action.h + history/view/history_view_reaction_preview.cpp + history/view/history_view_reaction_preview.h history/view/history_view_reply.cpp history/view/history_view_reply.h history/view/history_view_requests_bar.cpp diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 567ef58189..560d757b8e 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_service_message.h" #include "history/view/history_view_cursor_state.h" #include "history/view/history_view_context_menu.h" +#include "history/view/history_view_reaction_preview.h" #include "history/view/history_view_quick_action.h" #include "history/view/history_view_emoji_interactions.h" #include "history/history_item_components.h" @@ -2373,6 +2374,11 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { _whoReactedMenuLifetime); e->accept(); return; + } else if (HistoryView::ShowReactionPreview( + _controller, + leaderOrSelf->fullId(), + clickedReaction)) { + return; } } if (!linkPhoneNumber.isEmpty()) { diff --git a/Telegram/SourceFiles/history/view/history_view_reaction_preview.cpp b/Telegram/SourceFiles/history/view/history_view_reaction_preview.cpp new file mode 100644 index 0000000000..5bb2faa6c3 --- /dev/null +++ b/Telegram/SourceFiles/history/view/history_view_reaction_preview.cpp @@ -0,0 +1,164 @@ +/* +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_reaction_preview.h" + +#include "base/call_delayed.h" +#include "boxes/sticker_set_box.h" +#include "data/data_document.h" +#include "data/data_message_reactions.h" +#include "data/data_session.h" +#include "data/stickers/data_custom_emoji.h" +#include "lang/lang_keys.h" +#include "main/main_session.h" +#include "mainwindow.h" +#include "ui/cached_special_layer_shadow_corners.h" +#include "ui/effects/show_animation.h" +#include "ui/widgets/buttons.h" +#include "ui/widgets/labels.h" +#include "ui/widgets/shadow.h" +#include "ui/painter.h" +#include "ui/rect.h" +#include "ui/text/text_utilities.h" +#include "window/window_media_preview.h" +#include "window/window_session_controller.h" +#include "styles/style_chat.h" +#include "styles/style_layers.h" + +namespace HistoryView { + +bool ShowReactionPreview( + not_null controller, + FullMsgId origin, + Data::ReactionId reactionId) { + auto document = (DocumentData*)(nullptr); + if (const auto custom = reactionId.custom()) { + document = controller->session().data().document(custom); + } else if (const auto resolved + = controller->session().data().reactions().lookupTemporary( + reactionId)) { + document = resolved->selectAnimation; + } + if (!document) { + return false; + } + struct State { + base::unique_qptr mediaPreview; + base::unique_qptr clickable; + base::unique_qptr background; + base::unique_qptr label; + }; + const auto state = std::make_shared(); + + const auto mainwidget = controller->widget(); + state->mediaPreview = base::make_unique_q( + mainwidget, + controller); + state->mediaPreview->setCustomDuration(st::defaultToggle.duration); + state->clickable = base::make_unique_q(mainwidget); + const auto hideAll = [=] { + state->clickable->setAttribute(Qt::WA_TransparentForMouseEvents); + state->mediaPreview->hidePreview(); + if (state->label && state->background) { + Ui::Animations::HideWidgets({ + state->background.get(), + state->label.get(), + }); + } + base::call_delayed( + st::defaultToggle.duration, + crl::guard(state->clickable.get(), [=] { + state->clickable.reset(); + })); + }; + state->clickable->setClickedCallback(hideAll); + state->mediaPreview->showPreview(origin, document); + state->clickable->show(); + const auto mediaPreviewRaw = state->mediaPreview.get(); + const auto clickableRaw = state->clickable.get(); + const auto shadowExtend = st::boxRoundShadow.extend; + + if (reactionId.custom() && document->sticker()) { + const auto setId = document->sticker()->set; + const auto packName + = document->owner().customEmojiManager().lookupSetName(setId.id); + if (!packName.isEmpty()) { + state->background = base::make_unique_q( + mainwidget); + const auto show = controller->uiShow(); + state->background->setClickedCallback([=] { + hideAll(); + show->show(Box( + show, + setId, + Data::StickersType::Emoji)); + }); + state->label = base::make_unique_q( + state->background.get(), + tr::lng_context_animated_reaction( + lt_name, + rpl::single(Ui::Text::Colorized(packName)), + Ui::Text::RichLangValue)); + state->label->setAttribute(Qt::WA_TransparentForMouseEvents); + const auto backgroundRaw = state->background.get(); + const auto labelRaw = state->label.get(); + + backgroundRaw->paintOn([=](QPainter &p) { + const auto innerRect = backgroundRaw->rect() - shadowExtend; + Ui::Shadow::paint( + p, + innerRect, + backgroundRaw->width(), + st::boxRoundShadow, + Ui::SpecialLayerShadowCorners(), + RectPart::Full); + auto hq = PainterHighQualityEnabler(p); + p.setPen(Qt::NoPen); + p.setBrush(st::windowBg); + p.drawRoundedRect( + innerRect, + st::boxRadius, + st::boxRadius); + }); + + labelRaw->show(); + Ui::Animations::ShowWidgets({ backgroundRaw }); + } + } + const auto backgroundRaw = state->background.get(); + const auto labelRaw = state->label.get(); + + mainwidget->sizeValue() | rpl::start_with_next([=](QSize size) { + mediaPreviewRaw->setGeometry(Rect(size)); + clickableRaw->setGeometry(Rect(size)); + clickableRaw->raise(); + + if (backgroundRaw && labelRaw) { + const auto maxLabelWidth = labelRaw->textMaxWidth() / 2; + labelRaw->resizeToWidth(maxLabelWidth); + const auto labelHeight = labelRaw->height() * 2; + const auto padding = st::msgServicePadding; + const auto innerWidth = maxLabelWidth + rect::m::sum::h(padding); + const auto innerHeight = labelHeight + rect::m::sum::v(padding); + const auto bgWidth = innerWidth + rect::m::sum::h(shadowExtend); + const auto bgHeight = innerHeight + rect::m::sum::v(shadowExtend); + const auto bgX = (size.width() - bgWidth) / 2; + const auto bgY = (size.height() * 3 / 4) - (bgHeight / 2); + + backgroundRaw->setGeometry(bgX, bgY, bgWidth, bgHeight); + labelRaw->setGeometry( + shadowExtend.left() + padding.left(), + shadowExtend.top() + padding.top(), + maxLabelWidth, + labelHeight); + backgroundRaw->raise(); + } + }, mediaPreviewRaw->lifetime()); + return true; +} + +} // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/history_view_reaction_preview.h b/Telegram/SourceFiles/history/view/history_view_reaction_preview.h new file mode 100644 index 0000000000..daf6664ccb --- /dev/null +++ b/Telegram/SourceFiles/history/view/history_view_reaction_preview.h @@ -0,0 +1,25 @@ +/* +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 + +namespace Data { +struct ReactionId; +} // namespace Data + +namespace Window { +class SessionController; +} // namespace Window + +namespace HistoryView { + +[[nodiscard]] bool ShowReactionPreview( + not_null controller, + FullMsgId origin, + Data::ReactionId reactionId); + +} // namespace HistoryView