From 535fd8d52386a726f2222cd6b30588f570d018c3 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 8 Dec 2021 09:28:17 +0400 Subject: [PATCH] Display reactions outside bottom info in groups. --- Telegram/CMakeLists.txt | 2 + Telegram/SourceFiles/data/data_session.cpp | 4 +- .../history/history_inner_widget.cpp | 13 +- .../history/view/history_view_bottom_info.cpp | 6 +- .../history/view/history_view_message.cpp | 172 +++++++++++++++--- .../history/view/history_view_message.h | 9 +- .../history/view/history_view_reactions.cpp | 104 +++++++++++ .../history/view/history_view_reactions.h | 55 ++++++ .../view/media/history_view_media_grouped.cpp | 3 + 9 files changed, 328 insertions(+), 40 deletions(-) create mode 100644 Telegram/SourceFiles/history/view/history_view_reactions.cpp create mode 100644 Telegram/SourceFiles/history/view/history_view_reactions.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 453056f6a..5d367d577 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -652,6 +652,8 @@ PRIVATE history/view/history_view_pinned_section.h history/view/history_view_pinned_tracker.cpp history/view/history_view_pinned_tracker.h + history/view/history_view_reactions.cpp + history/view/history_view_reactions.h history/view/history_view_replies_section.cpp history/view/history_view_replies_section.h history/view/history_view_requests_bar.cpp diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index b3777dee5..e991581b7 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -655,7 +655,7 @@ not_null Session::processChat(const MTPChat &data) { && chat->groupCall()->fullCount() > 0)) ? Flag::CallNotEmpty : Flag()) - | (data.is_noforwards() ? Flag::NoForwards : Flag()); + | (data.is_noforwards() ? Flag() : Flag()); AssertIsDebug(); chat->setFlags((chat->flags() & ~flagsMask) | flagsSet); chat->count = data.vparticipants_count().v; @@ -765,7 +765,7 @@ not_null Session::processChat(const MTPChat &data) { ? (data.is_left() ? Flag::Left : Flag()) | (data.is_creator() ? Flag::Creator : Flag()) : Flag()) - | (data.is_noforwards() ? Flag::NoForwards : Flag()); + | (data.is_noforwards() ? Flag() : Flag()); AssertIsDebug(); channel->setFlags((channel->flags() & ~flagsMask) | flagsSet); channel->setName( diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 953f55ea7..284b69902 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -1699,12 +1699,15 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { this, st::reactionMenu); auto &reactions = item->history()->owner().reactions(); - for (const auto &entry : reactions.list(item->history()->peer)) { - reactionMenu->addAction(entry.emoji, [=] { - item->addReaction(entry.emoji); - }); + const auto &list = reactions.list(item->history()->peer); + if (!list.empty()) { + for (const auto &entry : list) { + reactionMenu->addAction(entry.emoji, [=] { + item->addReaction(entry.emoji); + }); + } + _menu->addAction("Reaction", std::move(reactionMenu)); } - _menu->addAction("Reaction", std::move(reactionMenu)); } if (canSendMessages) { _menu->addAction(tr::lng_context_reply_msg(tr::now), [=] { diff --git a/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp b/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp index 024601f31..249468b36 100644 --- a/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp +++ b/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp @@ -323,8 +323,10 @@ BottomInfo::Data BottomInfoDataFromMessage(not_null message) { const auto item = message->message(); result.date = message->dateTime(); - result.reactions = item->reactions(); - result.chosenReaction = item->chosenReaction(); + if (message->embedReactionsInBottomInfo()) { + result.reactions = item->reactions(); + result.chosenReaction = item->chosenReaction(); + } if (message->hasOutLayout()) { result.flags |= Flag::OutLayout; } diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index ed23925da..8df8d76af 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_message.h" #include "history/view/media/history_view_media.h" #include "history/view/media/history_view_web_page.h" +#include "history/view/history_view_reactions.h" #include "history/view/history_view_group_call_bar.h" // UserpicInRow. #include "history/view/history_view_view_button.h" // ViewButton. #include "history/history.h" @@ -247,6 +248,7 @@ Message::Message( BottomInfoContextFromMessage(this)) { initLogEntryOriginal(); initPsa(); + refreshReactions(); } Message::~Message() { @@ -319,8 +321,13 @@ QSize Message::performCountOptimalSize() { updateViewButtonExistence(); updateMediaInBubbleState(); refreshRightBadge(); - initTime(); + refreshInfoSkipBlock(); + const auto displayInfo = needInfoDisplay(); + const auto reactionsInBubble = _reactions && displayInfo; + if (_reactions) { + _reactions->initDimensions(); + } if (drawBubble()) { const auto forwarded = item->Get(); const auto reply = displayedReply(); @@ -345,22 +352,19 @@ QSize Message::performCountOptimalSize() { // Entry page is always a bubble bottom. auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || (entry/* && entry->isBubbleBottom()*/); auto mediaOnTop = (mediaDisplayed && media->isBubbleTop()) || (entry && entry->isBubbleTop()); - - if (mediaOnBottom || (mediaDisplayed && _viewButton)) { - if (item->_text.removeSkipBlock()) { - item->_textWidth = -1; - item->_textHeight = 0; - } - } else if (item->_text.updateSkipBlock(skipBlockWidth(), skipBlockHeight())) { - item->_textWidth = -1; - item->_textHeight = 0; - } - maxWidth = plainMaxWidth(); if (context() == Context::Replies && item->isDiscussionPost()) { maxWidth = std::max(maxWidth, st::msgMaxWidth); } minHeight = hasVisibleText() ? item->_text.minHeight() : 0; + if (reactionsInBubble) { + accumulate_max(maxWidth, std::min( + st::msgMaxWidth, + (st::msgPadding.left() + + _reactions->maxWidth() + + st::msgPadding.right()))); + minHeight += st::mediaInBubbleSkip + _reactions->minHeight(); + } if (!mediaOnBottom) { minHeight += st::msgPadding.bottom(); if (mediaDisplayed) minHeight += st::mediaInBubbleSkip; @@ -374,9 +378,17 @@ QSize Message::performCountOptimalSize() { // Parts don't participate in maxWidth() in case of media message. if (media->enforceBubbleWidth()) { maxWidth = media->maxWidth(); + const auto innerWidth = maxWidth + - st::msgPadding.left() + - st::msgPadding.right(); if (hasVisibleText() && maxWidth < plainMaxWidth()) { minHeight -= item->_text.minHeight(); - minHeight += item->_text.countHeight(maxWidth - st::msgPadding.left() - st::msgPadding.right()); + minHeight += item->_text.countHeight(innerWidth); + } + if (reactionsInBubble) { + minHeight -= _reactions->minHeight(); + minHeight + += _reactions->countCurrentSize(innerWidth).height(); } } else { accumulate_max(maxWidth, media->maxWidth()); @@ -441,6 +453,13 @@ QSize Message::performCountOptimalSize() { maxWidth = st::msgMinWidth; minHeight = 0; } + if (_reactions && !reactionsInBubble) { + // if we have a text bubble we can resize it to fit the keyboard + // but if we have only media we don't do that + if (hasVisibleText()) { + accumulate_max(maxWidth, _reactions->maxWidth()); + } + } if (const auto markup = item->inlineReplyMarkup()) { if (!markup->inlineKeyboard) { markup->inlineKeyboard = std::make_unique( @@ -518,6 +537,9 @@ void Message::draw(Painter &p, const PaintContext &context) const { auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || (entry/* && entry->isBubbleBottom()*/); auto mediaOnTop = (mediaDisplayed && media->isBubbleTop()) || (entry && entry->isBubbleTop()); + const auto displayInfo = needInfoDisplay(); + const auto reactionsInBubble = _reactions && displayInfo; + auto mediaSelectionIntervals = (!context.selected() && mediaDisplayed) ? media->getBubbleSelectionIntervals(context.selection) : std::vector(); @@ -531,6 +553,9 @@ void Message::draw(Painter &p, const PaintContext &context) const { if (_viewButton) { localMediaBottom -= st::mediaInBubbleSkip + _viewButton->height(); } + if (reactionsInBubble) { + localMediaBottom -= st::mediaInBubbleSkip + _reactions->height(); + } if (!mediaOnBottom) { localMediaBottom -= st::msgPadding.bottom(); } @@ -562,14 +587,23 @@ void Message::draw(Painter &p, const PaintContext &context) const { auto keyboard = item->inlineReplyKeyboard(); if (keyboard) { - auto keyboardHeight = st::msgBotKbButton.margin + keyboard->naturalHeight(); + const auto keyboardHeight = st::msgBotKbButton.margin + keyboard->naturalHeight(); g.setHeight(g.height() - keyboardHeight); - auto keyboardPosition = QPoint(g.left(), g.top() + g.height() + st::msgBotKbButton.margin); + const auto keyboardPosition = QPoint(g.left(), g.top() + g.height() + st::msgBotKbButton.margin); p.translate(keyboardPosition); keyboard->paint(p, context.st, g.width(), context.clip.translated(-keyboardPosition)); p.translate(-keyboardPosition); } + if (_reactions && !reactionsInBubble) { + const auto reactionsHeight = st::mediaInBubbleSkip + _reactions->height(); + g.setHeight(g.height() - reactionsHeight); + const auto reactionsPosition = QPoint(g.left(), g.top() + g.height() + st::mediaInBubbleSkip); + p.translate(reactionsPosition); + _reactions->paint(p, context.st, g.width(), context.clip.translated(-reactionsPosition)); + p.translate(-reactionsPosition); + } + if (bubble) { if (displayFromName() && item->displayFrom() @@ -606,7 +640,6 @@ void Message::draw(Painter &p, const PaintContext &context) const { auto inner = g; paintCommentsButton(p, inner, context); - const auto needDrawInfo = needInfoDisplay(); auto trect = inner.marginsRemoved(st::msgPadding); if (_viewButton) { const auto belowInfo = _viewButton->belowMessageInfo(); @@ -627,6 +660,15 @@ void Message::draw(Painter &p, const PaintContext &context) const { } } + if (reactionsInBubble) { + const auto reactionsHeight = st::mediaInBubbleSkip + _reactions->height(); + trect.setHeight(trect.height() - reactionsHeight); + const auto reactionsPosition = QPoint(trect.left(), trect.top() + trect.height() + st::mediaInBubbleSkip); + p.translate(reactionsPosition); + _reactions->paint(p, context.st, g.width(), context.clip.translated(-reactionsPosition)); + p.translate(-reactionsPosition); + } + if (mediaOnBottom) { trect.setHeight(trect.height() + st::msgPadding.bottom()); } @@ -641,7 +683,7 @@ void Message::draw(Painter &p, const PaintContext &context) const { if (entry) { trect.setHeight(trect.height() - entry->height()); } - if (needDrawInfo) { + if (displayInfo) { trect.setHeight(trect.height() - (_bottomInfo.height() - st::msgDateFont->height)); } @@ -671,7 +713,7 @@ void Message::draw(Painter &p, const PaintContext &context) const { entry->draw(p, entryContext); p.translate(-entryLeft, -entryTop); } - if (needDrawInfo) { + if (displayInfo) { const auto bottomSelected = context.selected() || (!mediaSelectionIntervals.empty() && (mediaSelectionIntervals.back().top @@ -1030,6 +1072,7 @@ PointState Message::pointState(QPoint point) const { const auto media = this->media(); const auto item = message(); + const auto reactionsInBubble = _reactions && needInfoDisplay(); if (drawBubble()) { if (!g.contains(point)) { return PointState::Outside; @@ -1052,6 +1095,11 @@ PointState Message::pointState(QPoint point) const { trect.setHeight(trect.height() - st::mediaInBubbleSkip); } } + if (reactionsInBubble) { + const auto reactionsHeight = st::mediaInBubbleSkip + + _reactions->height(); + trect.setHeight(trect.height() - reactionsHeight); + } if (mediaOnBottom) { trect.setHeight(trect.height() + st::msgPadding.bottom()); } @@ -1198,6 +1246,7 @@ TextState Message::textState( return result; } + const auto reactionsInBubble = _reactions && needInfoDisplay(); auto keyboard = item->inlineReplyKeyboard(); auto keyboardHeight = 0; if (keyboard) { @@ -1242,6 +1291,11 @@ TextState Message::textState( trect.setHeight(trect.height() - st::mediaInBubbleSkip); } } + if (reactionsInBubble) { + const auto reactionsHeight = _reactions->height() + + st::mediaInBubbleSkip; + trect.setHeight(trect.height() - reactionsHeight); + } if (mediaOnBottom) { trect.setHeight(trect.height() + st::msgPadding.bottom() @@ -1828,14 +1882,38 @@ bool Message::isSignedAuthorElided() const { return _bottomInfo.isSignedAuthorElided(); } +bool Message::embedReactionsInBottomInfo() const { + return data()->history()->peer->isUser(); +} + +void Message::refreshReactions() { + const auto item = data(); + const auto &list = item->reactions(); + if (list.empty() || embedReactionsInBottomInfo()) { + _reactions = nullptr; + } else if (!_reactions) { + _reactions = std::make_unique( + ReactionsDataFromMessage(this)); + } else { + _reactions->update(ReactionsDataFromMessage(this), width()); + } +} + void Message::itemDataChanged() { const auto wasInfo = _bottomInfo.currentSize(); + const auto wasReactions = _reactions + ? _reactions->currentSize() + : QSize(); + refreshReactions(); _bottomInfo.update( BottomInfoDataFromMessage(this), BottomInfoContextFromMessage(this), width()); const auto nowInfo = _bottomInfo.currentSize(); - if (wasInfo != nowInfo) { + const auto nowReactions = _reactions + ? _reactions->currentSize() + : QSize(); + if (wasInfo != nowInfo || wasReactions != nowReactions) { history()->owner().requestViewResize(this); } else { history()->owner().requestViewRepaint(this); @@ -2305,7 +2383,9 @@ void Message::updateMediaInBubbleState() { const auto item = message(); const auto media = this->media(); - auto mediaHasSomethingBelow = (_viewButton != nullptr); + const auto reactionsInBubble = (_reactions && needInfoDisplay()); + auto mediaHasSomethingBelow = (_viewButton != nullptr) + || reactionsInBubble; auto mediaHasSomethingAbove = false; auto getMediaHasSomethingAbove = [&] { return displayFromName() @@ -2490,10 +2570,13 @@ int Message::resizeContentGetHeight(int newWidth) { } } } + const auto textWidth = qMax(contentWidth - st::msgPadding.left() - st::msgPadding.right(), 1); + const auto displayInfo = needInfoDisplay(); + const auto reactionsInBubble = _reactions && displayInfo; const auto bottomInfoHeight = _bottomInfo.resizeGetHeight( std::min( _bottomInfo.optimalSize().width(), - contentWidth - st::msgPadding.left() - st::msgPadding.right() - 2 * st::msgDateDelta.x())); + textWidth - 2 * st::msgDateDelta.x())); if (bubble) { auto reply = displayedReply(); @@ -2504,6 +2587,10 @@ int Message::resizeContentGetHeight(int newWidth) { auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || (entry/* && entry->isBubbleBottom()*/); auto mediaOnTop = (mediaDisplayed && media->isBubbleTop()) || (entry && entry->isBubbleTop()); + if (reactionsInBubble) { + _reactions->resizeGetHeight(textWidth); + } + if (contentWidth == maxWidth()) { if (mediaDisplayed) { if (entry) { @@ -2515,7 +2602,6 @@ int Message::resizeContentGetHeight(int newWidth) { } } else { if (hasVisibleText()) { - auto textWidth = qMax(contentWidth - st::msgPadding.left() - st::msgPadding.right(), 1); if (textWidth != item->_textWidth) { item->_textWidth = textWidth; item->_textHeight = item->_text.countHeight(textWidth); @@ -2541,6 +2627,9 @@ int Message::resizeContentGetHeight(int newWidth) { } else if (entry) { newHeight += entry->resizeGetHeight(contentWidth); } + if (reactionsInBubble) { + newHeight += st::mediaInBubbleSkip + _reactions->height(); + } } if (displayFromName()) { @@ -2564,7 +2653,7 @@ int Message::resizeContentGetHeight(int newWidth) { reply->resize(contentWidth - st::msgPadding.left() - st::msgPadding.right()); newHeight += st::msgReplyPadding.top() + st::msgReplyBarSize.height() + st::msgReplyPadding.bottom(); } - if (needInfoDisplay()) { + if (displayInfo) { newHeight += (bottomInfoHeight - st::msgDateFont->height); } @@ -2577,6 +2666,9 @@ int Message::resizeContentGetHeight(int newWidth) { } else { newHeight = 0; } + if (_reactions && !reactionsInBubble) { + newHeight += st::mediaInBubbleSkip + _reactions->resizeGetHeight(contentWidth); + } if (const auto keyboard = item->inlineReplyKeyboard()) { const auto keyboardHeight = st::msgBotKbButton.margin + keyboard->naturalHeight(); newHeight += keyboardHeight; @@ -2591,9 +2683,6 @@ bool Message::needInfoDisplay() const { const auto media = this->media(); const auto mediaDisplayed = media ? media->isDisplayed() : false; const auto entry = logEntryOriginal(); - - // Entry page is always a bubble bottom. - const auto mediaOnBottom = (mediaDisplayed && media->isBubbleBottom()) || (entry/* && entry->isBubbleBottom()*/); return entry ? !entry->customInfoLayout() : (mediaDisplayed @@ -2615,13 +2704,38 @@ QSize Message::performCountCurrentSize(int newWidth) { return { newWidth, newHeight }; } -void Message::initTime() const { +void Message::refreshInfoSkipBlock() { const auto item = message(); - if (item->_text.hasSkipBlock()) { - if (item->_text.updateSkipBlock(skipBlockWidth(), skipBlockHeight())) { + const auto media = this->media(); + const auto hasTextSkipBlock = [&] { + if (item->_text.isEmpty()) { + return false; + } else if (item->Has()) { + return false; + } else if (media && media->isDisplayed()) { + return false; + } else if (_reactions) { + return false; + } + return true; + }(); + const auto skipWidth = skipBlockWidth(); + const auto skipHeight = skipBlockHeight(); + if (_reactions) { + if (needInfoDisplay()) { + _reactions->updateSkipBlock(skipWidth, skipHeight); + } else { + _reactions->removeSkipBlock(); + } + } + if (!hasTextSkipBlock) { + if (item->_text.removeSkipBlock()) { item->_textWidth = -1; item->_textHeight = 0; } + } else if (item->_text.updateSkipBlock(skipWidth, skipHeight)) { + item->_textWidth = -1; + item->_textHeight = 0; } } diff --git a/Telegram/SourceFiles/history/view/history_view_message.h b/Telegram/SourceFiles/history/view/history_view_message.h index d538e9449..0d4803fae 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.h +++ b/Telegram/SourceFiles/history/view/history_view_message.h @@ -19,6 +19,7 @@ struct HistoryMessageForwarded; namespace HistoryView { class ViewButton; +class Reactions; class WebPage; // Special type of Component for the channel actions log. @@ -56,6 +57,8 @@ public: [[nodiscard]] const HistoryMessageEdited *displayedEditBadge() const; [[nodiscard]] HistoryMessageEdited *displayedEditBadge(); + [[nodiscard]] bool embedReactionsInBottomInfo() const; + int marginTop() const override; int marginBottom() const override; void draw(Painter &p, const PaintContext &context) const override; @@ -211,7 +214,7 @@ private: [[nodiscard]] ClickHandlerPtr fastReplyLink() const; [[nodiscard]] bool displayPinIcon() const; - void initTime() const; + void refreshInfoSkipBlock(); [[nodiscard]] int plainMaxWidth() const; [[nodiscard]] int monospaceMaxWidth() const; @@ -225,11 +228,13 @@ private: void psaTooltipToggled(bool shown) const; void refreshRightBadge(); + void refreshReactions(); mutable ClickHandlerPtr _rightActionLink; mutable ClickHandlerPtr _fastReplyLink; - mutable std::unique_ptr _comments; mutable std::unique_ptr _viewButton; + std::unique_ptr _reactions; + mutable std::unique_ptr _comments; Ui::Text::String _rightBadge; int _bubbleWidthLimit = 0; diff --git a/Telegram/SourceFiles/history/view/history_view_reactions.cpp b/Telegram/SourceFiles/history/view/history_view_reactions.cpp new file mode 100644 index 000000000..0c3aa7c71 --- /dev/null +++ b/Telegram/SourceFiles/history/view/history_view_reactions.cpp @@ -0,0 +1,104 @@ +/* +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_reactions.h" + +#include "history/view/history_view_message.h" +#include "history/history_message.h" +#include "ui/text/text_options.h" +#include "ui/text/text_utilities.h" + +namespace HistoryView { + +Reactions::Reactions(Data &&data) +: _data(std::move(data)) +, _reactions(st::msgMinWidth / 2) { + layout(); +} + +void Reactions::update(Data &&data, int availableWidth) { + _data = std::move(data); + layout(); + if (width() > 0) { + resizeGetHeight(std::min(maxWidth(), availableWidth)); + } +} + +void Reactions::updateSkipBlock(int width, int height) { + _reactions.updateSkipBlock(width, height); +} + +void Reactions::removeSkipBlock() { + _reactions.removeSkipBlock(); +} + +void Reactions::layout() { + layoutReactionsText(); + initDimensions(); +} + +void Reactions::layoutReactionsText() { + if (_data.reactions.empty()) { + _reactions.clear(); + return; + } + auto sorted = ranges::view::all( + _data.reactions + ) | ranges::view::transform([](const auto &pair) { + return std::make_pair(pair.first, pair.second); + }) | ranges::to_vector; + ranges::sort(sorted, std::greater<>(), &std::pair::second); + + auto text = TextWithEntities(); + for (const auto &[string, count] : sorted) { + if (!text.text.isEmpty()) { + text.append(" - "); + } + const auto chosen = (_data.chosenReaction == string); + text.append(string); + if (_data.chosenReaction == string) { + text.append(Ui::Text::Bold(QString::number(count))); + } else { + text.append(QString::number(count)); + } + } + + _reactions.setMarkedText( + st::msgDateTextStyle, + text, + Ui::NameTextOptions()); +} + +QSize Reactions::countOptimalSize() { + return QSize(_reactions.maxWidth(), _reactions.minHeight()); +} + +QSize Reactions::countCurrentSize(int newWidth) { + if (newWidth >= maxWidth()) { + return optimalSize(); + } + return { newWidth, _reactions.countHeight(newWidth) }; +} + +void Reactions::paint( + Painter &p, + const Ui::ChatStyle *st, + int outerWidth, + const QRect &clip) const { + _reactions.draw(p, 0, 0, outerWidth); +} + +Reactions::Data ReactionsDataFromMessage(not_null message) { + auto result = Reactions::Data(); + + const auto item = message->message(); + result.reactions = item->reactions(); + result.chosenReaction = item->chosenReaction(); + return result; +} + +} // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/history_view_reactions.h b/Telegram/SourceFiles/history/view/history_view_reactions.h new file mode 100644 index 000000000..af9075556 --- /dev/null +++ b/Telegram/SourceFiles/history/view/history_view_reactions.h @@ -0,0 +1,55 @@ +/* +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/view/history_view_object.h" + +namespace Ui { +class ChatStyle; +} // namespace Ui + +namespace HistoryView { + +class Message; + +class Reactions final : public Object { +public: + struct Data { + base::flat_map reactions; + QString chosenReaction; + }; + + explicit Reactions(Data &&data); + + void update(Data &&data, int availableWidth); + QSize countCurrentSize(int newWidth) override; + + void updateSkipBlock(int width, int height); + void removeSkipBlock(); + + void paint( + Painter &p, + const Ui::ChatStyle *st, + int outerWidth, + const QRect &clip) const; + +private: + void layout(); + void layoutReactionsText(); + + QSize countOptimalSize() override; + + Data _data; + Ui::Text::String _reactions; + +}; + +[[nodiscard]] Reactions::Data ReactionsDataFromMessage( + not_null message); + +} // namespace HistoryView diff --git a/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp b/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp index f094bd6a7..b8eb4a658 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp @@ -531,6 +531,9 @@ TextForMimeData GroupedMedia::selectedText( auto GroupedMedia::getBubbleSelectionIntervals( TextSelection selection) const -> std::vector { + if (_mode != Mode::Column) { + return {}; + } auto result = std::vector(); for (auto i = 0, count = int(_parts.size()); i != count; ++i) { const auto &part = _parts[i];