Make monoforum sender badges float.

This commit is contained in:
John Preston 2025-05-22 14:46:00 +04:00
parent 7dc8943840
commit 3dbdecf73d
7 changed files with 208 additions and 81 deletions

View file

@ -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<Element*> view, int itemtop, int itembottom)" signature
// if it returns false the enumeration stops immidiately.
// if it returns false the enumeration stops immediately.
template <EnumItemsDirection direction, typename Method>
void enumerateItems(Method method);

View file

@ -916,6 +916,62 @@ void HistoryInner::enumerateDates(Method method) {
enumerateItems<EnumItemsDirection::BottomToTop>(dateCallback);
}
template <typename Method>
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<Element*> 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<EnumItemsDirection::BottomToTop>(senderCallback);
}
TextSelection HistoryInner::computeRenderSelection(
not_null<const SelectedItems*> selected,
not_null<Element*> 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<Element*> 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<HistoryView::DateBadge>()) {
date->paint(p, context.st, dateY, _contentWidth, _isChatWide);
} else {
@ -1344,6 +1384,38 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
});
p.setOpacity(1.);
enumerateMonoforumSenders([&](not_null<Element*> 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<HistoryView::MonoforumSenderBar>()) {
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);
}

View file

@ -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 <QtGui/QPainterPath>
@ -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<Element*> view, int itemtop, int itembottom)" signature
// if it returns false the enumeration stops immidiately.
// if it returns false the enumeration stops immediately.
template <bool TopToBottom, typename Method>
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<Element*> view, int userpicTop)" signature
// if it returns false the enumeration stops immidiately.
// if it returns false the enumeration stops immediately.
template <typename Method>
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<Element*> view, int itemtop, int dateTop)" signature
// if it returns false the enumeration stops immidiately.
// if it returns false the enumeration stops immediately.
template <typename Method>
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<Element*> view, int itemtop, int dateTop)" signature
// if it returns false the enumeration stops immediately.
template <typename Method>
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;

View file

@ -480,7 +480,7 @@ void DateBadge::paint(
void MonoforumSenderBar::init(
not_null<PeerData*> parentChat,
not_null<PeerData*> 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<const Ui::ChatStyle*> 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<const Ui::ChatStyle*> st,
not_null<Element*> 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<const Ui::ChatStyle*> st,
not_null<PeerData*> 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<MonoforumSenderBar>();
}
bool Element::isInOneBunchWithPrevious() const {
return !data()->isEmpty() && !displayMonoforumSender();
}
void Element::recountAttachToPreviousInBlocks() {
if (isHidden() || data()->isEmpty()) {
if (const auto next = nextDisplayedInBlocks()) {

View file

@ -268,13 +268,36 @@ struct MonoforumSenderBar : RuntimeComponent<MonoforumSenderBar, Element> {
not_null<const Ui::ChatStyle*> st,
int y,
int w,
bool chatWide) const;
bool chatWide,
bool skipPatternLine) const;
static void PaintFor(
Painter &p,
not_null<const Ui::ChatStyle*> st,
not_null<Element*> 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<const Ui::ChatStyle*> st,
not_null<PeerData*> 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(

View file

@ -1133,40 +1133,22 @@ void Message::draw(Painter &p, const PaintContext &context) const {
if (const auto bar = Get<UnreadBar>()) {
auto unreadbarh = bar->height();
auto dateh = 0;
auto aboveh = 0;
if (const auto date = Get<DateBadge>()) {
dateh = date->height();
aboveh += date->height();
}
if (context.clip.intersects(QRect(0, dateh, width(), unreadbarh))) {
p.translate(0, dateh);
if (const auto sender = Get<MonoforumSenderBar>()) {
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<MonoforumSenderBar>()) {
auto barh = monoforumBar->height();
auto skip = 0;
if (const auto date = Get<DateBadge>()) {
skip += date->height();
}
if (const auto bar = Get<UnreadBar>()) {
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);
}
}

View file

@ -547,40 +547,22 @@ void Service::draw(Painter &p, const PaintContext &context) const {
const auto st = context.st;
if (const auto bar = Get<UnreadBar>()) {
auto unreadbarh = bar->height();
auto dateh = 0;
auto aboveh = 0;
if (const auto date = Get<DateBadge>()) {
dateh = date->height();
aboveh += date->height();
}
if (context.clip.intersects(QRect(0, dateh, width(), unreadbarh))) {
p.translate(0, dateh);
if (const auto sender = Get<MonoforumSenderBar>()) {
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<MonoforumSenderBar>()) {
auto barh = monoforumBar->height();
auto skip = 0;
if (const auto date = Get<DateBadge>()) {
skip += date->height();
}
if (const auto bar = Get<UnreadBar>()) {
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);
}
}