diff --git a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h index ed23bdad44..1f571edffc 100644 --- a/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h +++ b/Telegram/SourceFiles/history/admin_log/history_admin_log_inner.h @@ -251,7 +251,7 @@ private: // for each found message (in given direction) in the passed history with passed top offset. // // Method has "bool (*Method)(not_null view, int itemtop, int itembottom)" signature - // if it returns false the enumeration stops immidiately. + // if it returns false the enumeration stops immediately. template void enumerateItems(Method method); diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index dd6031275a..6d01dc5710 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -916,6 +916,62 @@ void HistoryInner::enumerateDates(Method method) { enumerateItems(dateCallback); } +template +void HistoryInner::enumerateMonoforumSenders(Method method) { + if (!_history->amMonoforumAdmin()) { + return; + } + + const auto skip = (_scrollDateOpacity.animating() || _scrollDateShown) + ? int(base::SafeRound( + (_scrollDateOpacity.value(_scrollDateShown ? 1. : 0.) + * (st::msgServicePadding.bottom() + + st::msgServiceFont->height + + st::msgServicePadding.top() + + st::msgServiceMargin.top())))) + : 0; + + // Find and remember the bottom of an single-day messages pack + // -1 means we didn't find a same-day with previous message yet. + auto lowestInOneBunchItemBottom = -1; + + auto senderCallback = [&](not_null view, int itemtop, int itembottom) { + const auto item = view->data(); + if (lowestInOneBunchItemBottom < 0 && view->isInOneBunchWithPrevious()) { + lowestInOneBunchItemBottom = itembottom - view->marginBottom(); + } + + // Call method on a sender for all messages that have it and for those who are not showing it + // because they are in a one day together with the previous message if they are top-most visible. + if (view->displayMonoforumSender() || (!item->isEmpty() && itemtop <= _visibleAreaTop)) { + if (lowestInOneBunchItemBottom < 0) { + lowestInOneBunchItemBottom = itembottom - view->marginBottom(); + } + // Attach sender to the top of the visible area with the same margin as it has in service message. + int senderTop = qMax(itemtop + view->displayedDateHeight(), _visibleAreaTop + skip) + st::msgServiceMargin.top(); + + // Do not let the sender go below the single-sender messages pack bottom line. + int senderHeight = st::msgServicePadding.bottom() + st::msgServiceFont->height + st::msgServicePadding.top(); + senderTop = qMin(senderTop, lowestInOneBunchItemBottom - senderHeight); + + // Call the template callback function that was passed + // and return if it finished everything it needed. + if (!method(view, itemtop, senderTop)) { + return false; + } + } + + // Forget the found bottom of the pack, search for the next one from scratch. + if (!view->isInOneBunchWithPrevious()) { + lowestInOneBunchItemBottom = -1; + } + + return true; + }; + + enumerateItems(senderCallback); +} + TextSelection HistoryInner::computeRenderSelection( not_null selected, not_null view) const { @@ -1291,14 +1347,6 @@ void HistoryInner::paintEvent(QPaintEvent *e) { const auto dateHeight = st::msgServicePadding.bottom() + st::msgServiceFont->height + st::msgServicePadding.top(); - //QDate lastDate; - //if (!_history->isEmpty()) { - // lastDate = _history->blocks.back()->messages.back()->data()->date.date(); - //} - - //// if item top is before this value always show date as a floating date - //int showFloatingBefore = height() - 2 * (_visibleAreaBottom - _visibleAreaTop) - dateHeight; - auto scrollDateOpacity = _scrollDateOpacity.value(_scrollDateShown ? 1. : 0.); enumerateDates([&](not_null view, int itemtop, int dateTop) { // stop the enumeration if the date is above the painted rect @@ -1312,21 +1360,13 @@ void HistoryInner::paintEvent(QPaintEvent *e) { const auto correctDateTop = itemtop + st::msgServiceMargin.top(); dateInPlace = (dateTop < correctDateTop + dateHeight); } - //bool noFloatingDate = (item->date.date() == lastDate && displayDate); - //if (noFloatingDate) { - // if (itemtop < showFloatingBefore) { - // noFloatingDate = false; - // } - //} // paint the date if it intersects the painted rect if (dateTop < clip.top() + clip.height()) { - auto opacity = (dateInPlace/* || noFloatingDate*/) ? 1. : scrollDateOpacity; + auto opacity = dateInPlace ? 1. : scrollDateOpacity; if (opacity > 0.) { p.setOpacity(opacity); - const auto dateY = false // noFloatingDate - ? itemtop - : (dateTop - st::msgServiceMargin.top()); + const auto dateY = dateTop - st::msgServiceMargin.top(); if (const auto date = view->Get()) { date->paint(p, context.st, dateY, _contentWidth, _isChatWide); } else { @@ -1344,6 +1384,38 @@ void HistoryInner::paintEvent(QPaintEvent *e) { }); p.setOpacity(1.); + enumerateMonoforumSenders([&](not_null view, int itemtop, int senderTop) { + // stop the enumeration if the sender is above the painted rect + if (senderTop + dateHeight <= clip.top()) { + return false; + } + + const auto displaySender = view->displayMonoforumSender(); + auto senderInPlace = displaySender; + if (senderInPlace) { + const auto correctSenderTop = itemtop + view->displayedDateHeight() + st::msgServiceMargin.top(); + senderInPlace = (senderTop < correctSenderTop + st::msgServiceMargin.top()); + } + + // paint the sender if it intersects the painted rect + if (senderTop < clip.top() + clip.height()) { + const auto senderY = senderTop - st::msgServiceMargin.top(); + if (const auto sender = view->Get()) { + sender->paint(p, context.st, senderY, _contentWidth, _isChatWide, !senderInPlace); + } else { + HistoryView::MonoforumSenderBar::PaintFor( + p, + context.st, + view, + _monoforumSenderUserpicView, + senderY, + _contentWidth, + _isChatWide); + } + } + return true; + }); + _reactionsManager->paint(p, context); } @@ -3566,6 +3638,9 @@ void HistoryInner::toggleScrollDateShown() { void HistoryInner::repaintScrollDateCallback() { int updateTop = _visibleAreaTop; int updateHeight = st::msgServiceMargin.top() + st::msgServicePadding.top() + st::msgServiceFont->height + st::msgServicePadding.bottom(); + if (_history->amMonoforumAdmin()) { + updateHeight *= 2; + } update(0, updateTop, width(), updateHeight); } diff --git a/Telegram/SourceFiles/history/history_inner_widget.h b/Telegram/SourceFiles/history/history_inner_widget.h index 8d180cf670..1b603babcb 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.h +++ b/Telegram/SourceFiles/history/history_inner_widget.h @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/dragging_scroll_manager.h" #include "ui/widgets/tooltip.h" #include "ui/widgets/scroll_area.h" +#include "ui/userpic_view.h" #include "history/view/history_view_top_bar_widget.h" #include @@ -279,7 +280,7 @@ private: // for each found message (in given direction) in the passed history with passed top offset. // // Method has "bool (*Method)(not_null view, int itemtop, int itembottom)" signature - // if it returns false the enumeration stops immidiately. + // if it returns false the enumeration stops immediately. template void enumerateItemsInHistory(History *history, int historytop, Method method); @@ -299,7 +300,7 @@ private: // for each found userpic (from the top to the bottom) using enumerateItems() method. // // Method has "bool (*Method)(not_null view, int userpicTop)" signature - // if it returns false the enumeration stops immidiately. + // if it returns false the enumeration stops immediately. template void enumerateUserpics(Method method); @@ -307,10 +308,18 @@ private: // for each found date element (from the bottom to the top) using enumerateItems() method. // // Method has "bool (*Method)(not_null view, int itemtop, int dateTop)" signature - // if it returns false the enumeration stops immidiately. + // if it returns false the enumeration stops immediately. template void enumerateDates(Method method); + // This function finds all monoforum sender elements that are displayed and calls template method + // for each found date element (from the bottom to the top) using enumerateItems() method. + // + // Method has "bool (*Method)(not_null view, int itemtop, int dateTop)" signature + // if it returns false the enumeration stops immediately. + template + void enumerateMonoforumSenders(Method method); + void scrollDateCheck(); void scrollDateHideByTimer(); bool canHaveFromUserpics() const; @@ -458,6 +467,7 @@ private: int _contentWidth = 0; int _historyPaddingTop = 0; int _revealHeight = 0; + Ui::PeerUserpicView _monoforumSenderUserpicView; // Save visible area coords for painting / pressing userpics. int _visibleAreaTop = 0; diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp index b89b170eb6..cef5cf4793 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.cpp +++ b/Telegram/SourceFiles/history/view/history_view_element.cpp @@ -480,7 +480,7 @@ void DateBadge::paint( void MonoforumSenderBar::init( not_null parentChat, not_null peer) { - author = peer; + sender = peer; text.setText(st::semiboldTextStyle, peer->name()); const auto skip = st::monoforumBarUserpicSkip; const auto userpic = st::msgServicePadding.top() @@ -503,8 +503,52 @@ void MonoforumSenderBar::paint( not_null st, int y, int w, - bool chatWide) const { - Expects(author != nullptr); + bool chatWide, + bool skipPatternLine) const { + Paint(p, st, sender, text, width, view, y, w, chatWide, skipPatternLine); +} + +void MonoforumSenderBar::PaintFor( + Painter &p, + not_null st, + not_null itemView, + Ui::PeerUserpicView &userpicView, + int y, + int w, + bool chatWide) { + const auto sublist = itemView->data()->savedSublist(); + const auto sender = (sublist && sublist->parentChat()) + ? sublist->sublistPeer().get() + : nullptr; + if (!sender || sender->isMonoforum()) { + return; + } + auto text = Ui::Text::String(st::semiboldTextStyle, sender->name()); + const auto skip = st::monoforumBarUserpicSkip; + const auto userpic = st::msgServicePadding.top() + + st::msgServiceFont->height + + st::msgServicePadding.bottom() + - 2 * skip; + const auto width = skip + + userpic + + skip * 2 + + text.maxWidth() + + st::msgServicePadding.right(); + Paint(p, st, sender, text, width, userpicView, y, w, chatWide, true); +} + +void MonoforumSenderBar::Paint( + Painter &p, + not_null st, + not_null sender, + const Ui::Text::String &text, + int width, + Ui::PeerUserpicView &view, + int y, + int w, + bool chatWide, + bool skipPatternLine) { + Expects(sender != nullptr); int left = st::msgServiceMargin.left(); const auto maxwidth = chatWide @@ -523,7 +567,7 @@ void MonoforumSenderBar::paint( QRect(left, y + st::msgServiceMargin.top(), use, h)); const auto skip = st::monoforumBarUserpicSkip; - { + if (!skipPatternLine) { auto pen = st->msgServiceBg()->p; pen.setWidthF(skip); pen.setCapStyle(Qt::RoundCap); @@ -540,7 +584,7 @@ void MonoforumSenderBar::paint( - 2 * skip; const auto available = use - (skip + userpic + skip * 2 + st::msgServicePadding.right()); - author->paintUserpic(p, view, left + skip, y + st::msgServiceMargin.top() + skip, userpic); + sender->paintUserpic(p, view, left + skip, y + st::msgServiceMargin.top() + skip, userpic); p.setFont(st::msgServiceFont); p.setPen(st->msgServiceFg()); @@ -1448,6 +1492,14 @@ bool Element::isInOneDayWithPrevious() const { return !data()->isEmpty() && !displayDate(); } +bool Element::displayMonoforumSender() const { + return Has(); +} + +bool Element::isInOneBunchWithPrevious() const { + return !data()->isEmpty() && !displayMonoforumSender(); +} + void Element::recountAttachToPreviousInBlocks() { if (isHidden() || data()->isEmpty()) { if (const auto next = nextDisplayedInBlocks()) { diff --git a/Telegram/SourceFiles/history/view/history_view_element.h b/Telegram/SourceFiles/history/view/history_view_element.h index e7c5ac94f9..f3a05a8ed2 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.h +++ b/Telegram/SourceFiles/history/view/history_view_element.h @@ -268,13 +268,36 @@ struct MonoforumSenderBar : RuntimeComponent { not_null st, int y, int w, - bool chatWide) const; + bool chatWide, + bool skipPatternLine) const; + static void PaintFor( + Painter &p, + not_null st, + not_null itemView, + Ui::PeerUserpicView &userpicView, + int y, + int w, + bool chatWide); - PeerData *author = nullptr; + PeerData *sender = nullptr; Ui::Text::String text; ClickHandlerPtr link; mutable Ui::PeerUserpicView view; int width = 0; + +private: + static void Paint( + Painter &p, + not_null st, + not_null sender, + const Ui::Text::String &text, + int width, + Ui::PeerUserpicView &view, + int y, + int w, + bool chatWide, + bool skipPatternLine); + }; // Any HistoryView::Element can have this Component for @@ -438,6 +461,9 @@ public: [[nodiscard]] bool displayDate() const; [[nodiscard]] bool isInOneDayWithPrevious() const; + [[nodiscard]] bool displayMonoforumSender() const; + [[nodiscard]] bool isInOneBunchWithPrevious() const; + virtual void draw(Painter &p, const PaintContext &context) const = 0; [[nodiscard]] virtual PointState pointState(QPoint point) const = 0; [[nodiscard]] virtual TextState textState( diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index 49a7afafce..43e97b6125 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -1133,40 +1133,22 @@ void Message::draw(Painter &p, const PaintContext &context) const { if (const auto bar = Get()) { auto unreadbarh = bar->height(); - auto dateh = 0; + auto aboveh = 0; if (const auto date = Get()) { - dateh = date->height(); + aboveh += date->height(); } - if (context.clip.intersects(QRect(0, dateh, width(), unreadbarh))) { - p.translate(0, dateh); + if (const auto sender = Get()) { + aboveh += sender->height(); + } + if (context.clip.intersects(QRect(0, aboveh, width(), unreadbarh))) { + p.translate(0, aboveh); bar->paint( p, context, 0, width(), delegate()->elementIsChatWide()); - p.translate(0, -dateh); - } - } - - if (const auto monoforumBar = Get()) { - auto barh = monoforumBar->height(); - auto skip = 0; - if (const auto date = Get()) { - skip += date->height(); - } - if (const auto bar = Get()) { - skip += bar->height(); - } - if (context.clip.intersects(QRect(0, skip, width(), barh))) { - p.translate(0, skip); - monoforumBar->paint( - p, - context.st, - 0, - width(), - delegate()->elementIsChatWide()); - p.translate(0, -skip); + p.translate(0, -aboveh); } } diff --git a/Telegram/SourceFiles/history/view/history_view_service_message.cpp b/Telegram/SourceFiles/history/view/history_view_service_message.cpp index 9acf977d62..d215be432a 100644 --- a/Telegram/SourceFiles/history/view/history_view_service_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_service_message.cpp @@ -547,40 +547,22 @@ void Service::draw(Painter &p, const PaintContext &context) const { const auto st = context.st; if (const auto bar = Get()) { auto unreadbarh = bar->height(); - auto dateh = 0; + auto aboveh = 0; if (const auto date = Get()) { - dateh = date->height(); + aboveh += date->height(); } - if (context.clip.intersects(QRect(0, dateh, width(), unreadbarh))) { - p.translate(0, dateh); + if (const auto sender = Get()) { + aboveh += sender->height(); + } + if (context.clip.intersects(QRect(0, aboveh, width(), unreadbarh))) { + p.translate(0, aboveh); bar->paint( p, context, 0, width(), delegate()->elementIsChatWide()); - p.translate(0, -dateh); - } - } - - if (const auto monoforumBar = Get()) { - auto barh = monoforumBar->height(); - auto skip = 0; - if (const auto date = Get()) { - skip += date->height(); - } - if (const auto bar = Get()) { - skip += bar->height(); - } - if (context.clip.intersects(QRect(0, skip, width(), barh))) { - p.translate(0, skip); - monoforumBar->paint( - p, - context.st, - 0, - width(), - delegate()->elementIsChatWide()); - p.translate(0, -skip); + p.translate(0, -aboveh); } }