2
0
mirror of https://github.com/telegramdesktop/tdesktop synced 2025-09-01 06:55:58 +00:00

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. // 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 // 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> template <EnumItemsDirection direction, typename Method>
void enumerateItems(Method method); void enumerateItems(Method method);

View File

@@ -916,6 +916,62 @@ void HistoryInner::enumerateDates(Method method) {
enumerateItems<EnumItemsDirection::BottomToTop>(dateCallback); 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( TextSelection HistoryInner::computeRenderSelection(
not_null<const SelectedItems*> selected, not_null<const SelectedItems*> selected,
not_null<Element*> view) const { not_null<Element*> view) const {
@@ -1291,14 +1347,6 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
const auto dateHeight = st::msgServicePadding.bottom() const auto dateHeight = st::msgServicePadding.bottom()
+ st::msgServiceFont->height + st::msgServiceFont->height
+ st::msgServicePadding.top(); + 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.); auto scrollDateOpacity = _scrollDateOpacity.value(_scrollDateShown ? 1. : 0.);
enumerateDates([&](not_null<Element*> view, int itemtop, int dateTop) { enumerateDates([&](not_null<Element*> view, int itemtop, int dateTop) {
// stop the enumeration if the date is above the painted rect // 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(); const auto correctDateTop = itemtop + st::msgServiceMargin.top();
dateInPlace = (dateTop < correctDateTop + dateHeight); 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 // paint the date if it intersects the painted rect
if (dateTop < clip.top() + clip.height()) { if (dateTop < clip.top() + clip.height()) {
auto opacity = (dateInPlace/* || noFloatingDate*/) ? 1. : scrollDateOpacity; auto opacity = dateInPlace ? 1. : scrollDateOpacity;
if (opacity > 0.) { if (opacity > 0.) {
p.setOpacity(opacity); p.setOpacity(opacity);
const auto dateY = false // noFloatingDate const auto dateY = dateTop - st::msgServiceMargin.top();
? itemtop
: (dateTop - st::msgServiceMargin.top());
if (const auto date = view->Get<HistoryView::DateBadge>()) { if (const auto date = view->Get<HistoryView::DateBadge>()) {
date->paint(p, context.st, dateY, _contentWidth, _isChatWide); date->paint(p, context.st, dateY, _contentWidth, _isChatWide);
} else { } else {
@@ -1344,6 +1384,38 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
}); });
p.setOpacity(1.); 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); _reactionsManager->paint(p, context);
} }
@@ -3566,6 +3638,9 @@ void HistoryInner::toggleScrollDateShown() {
void HistoryInner::repaintScrollDateCallback() { void HistoryInner::repaintScrollDateCallback() {
int updateTop = _visibleAreaTop; int updateTop = _visibleAreaTop;
int updateHeight = st::msgServiceMargin.top() + st::msgServicePadding.top() + st::msgServiceFont->height + st::msgServicePadding.bottom(); int updateHeight = st::msgServiceMargin.top() + st::msgServicePadding.top() + st::msgServiceFont->height + st::msgServicePadding.bottom();
if (_history->amMonoforumAdmin()) {
updateHeight *= 2;
}
update(0, updateTop, width(), updateHeight); 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/dragging_scroll_manager.h"
#include "ui/widgets/tooltip.h" #include "ui/widgets/tooltip.h"
#include "ui/widgets/scroll_area.h" #include "ui/widgets/scroll_area.h"
#include "ui/userpic_view.h"
#include "history/view/history_view_top_bar_widget.h" #include "history/view/history_view_top_bar_widget.h"
#include <QtGui/QPainterPath> #include <QtGui/QPainterPath>
@@ -279,7 +280,7 @@ private:
// for each found message (in given direction) in the passed history with passed top offset. // 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 // 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> template <bool TopToBottom, typename Method>
void enumerateItemsInHistory(History *history, int historytop, Method 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. // for each found userpic (from the top to the bottom) using enumerateItems() method.
// //
// Method has "bool (*Method)(not_null<Element*> view, int userpicTop)" signature // 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> template <typename Method>
void enumerateUserpics(Method method); void enumerateUserpics(Method method);
@@ -307,10 +308,18 @@ private:
// for each found date element (from the bottom to the top) using enumerateItems() 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 // 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> template <typename Method>
void enumerateDates(Method 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 scrollDateCheck();
void scrollDateHideByTimer(); void scrollDateHideByTimer();
bool canHaveFromUserpics() const; bool canHaveFromUserpics() const;
@@ -458,6 +467,7 @@ private:
int _contentWidth = 0; int _contentWidth = 0;
int _historyPaddingTop = 0; int _historyPaddingTop = 0;
int _revealHeight = 0; int _revealHeight = 0;
Ui::PeerUserpicView _monoforumSenderUserpicView;
// Save visible area coords for painting / pressing userpics. // Save visible area coords for painting / pressing userpics.
int _visibleAreaTop = 0; int _visibleAreaTop = 0;

View File

@@ -480,7 +480,7 @@ void DateBadge::paint(
void MonoforumSenderBar::init( void MonoforumSenderBar::init(
not_null<PeerData*> parentChat, not_null<PeerData*> parentChat,
not_null<PeerData*> peer) { not_null<PeerData*> peer) {
author = peer; sender = peer;
text.setText(st::semiboldTextStyle, peer->name()); text.setText(st::semiboldTextStyle, peer->name());
const auto skip = st::monoforumBarUserpicSkip; const auto skip = st::monoforumBarUserpicSkip;
const auto userpic = st::msgServicePadding.top() const auto userpic = st::msgServicePadding.top()
@@ -503,8 +503,52 @@ void MonoforumSenderBar::paint(
not_null<const Ui::ChatStyle*> st, not_null<const Ui::ChatStyle*> st,
int y, int y,
int w, int w,
bool chatWide) const { bool chatWide,
Expects(author != nullptr); 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(); int left = st::msgServiceMargin.left();
const auto maxwidth = chatWide const auto maxwidth = chatWide
@@ -523,7 +567,7 @@ void MonoforumSenderBar::paint(
QRect(left, y + st::msgServiceMargin.top(), use, h)); QRect(left, y + st::msgServiceMargin.top(), use, h));
const auto skip = st::monoforumBarUserpicSkip; const auto skip = st::monoforumBarUserpicSkip;
{ if (!skipPatternLine) {
auto pen = st->msgServiceBg()->p; auto pen = st->msgServiceBg()->p;
pen.setWidthF(skip); pen.setWidthF(skip);
pen.setCapStyle(Qt::RoundCap); pen.setCapStyle(Qt::RoundCap);
@@ -540,7 +584,7 @@ void MonoforumSenderBar::paint(
- 2 * skip; - 2 * skip;
const auto available = use - (skip + userpic + skip * 2 + st::msgServicePadding.right()); 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.setFont(st::msgServiceFont);
p.setPen(st->msgServiceFg()); p.setPen(st->msgServiceFg());
@@ -1448,6 +1492,14 @@ bool Element::isInOneDayWithPrevious() const {
return !data()->isEmpty() && !displayDate(); return !data()->isEmpty() && !displayDate();
} }
bool Element::displayMonoforumSender() const {
return Has<MonoforumSenderBar>();
}
bool Element::isInOneBunchWithPrevious() const {
return !data()->isEmpty() && !displayMonoforumSender();
}
void Element::recountAttachToPreviousInBlocks() { void Element::recountAttachToPreviousInBlocks() {
if (isHidden() || data()->isEmpty()) { if (isHidden() || data()->isEmpty()) {
if (const auto next = nextDisplayedInBlocks()) { if (const auto next = nextDisplayedInBlocks()) {

View File

@@ -268,13 +268,36 @@ struct MonoforumSenderBar : RuntimeComponent<MonoforumSenderBar, Element> {
not_null<const Ui::ChatStyle*> st, not_null<const Ui::ChatStyle*> st,
int y, int y,
int w, 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; Ui::Text::String text;
ClickHandlerPtr link; ClickHandlerPtr link;
mutable Ui::PeerUserpicView view; mutable Ui::PeerUserpicView view;
int width = 0; 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 // Any HistoryView::Element can have this Component for
@@ -438,6 +461,9 @@ public:
[[nodiscard]] bool displayDate() const; [[nodiscard]] bool displayDate() const;
[[nodiscard]] bool isInOneDayWithPrevious() const; [[nodiscard]] bool isInOneDayWithPrevious() const;
[[nodiscard]] bool displayMonoforumSender() const;
[[nodiscard]] bool isInOneBunchWithPrevious() const;
virtual void draw(Painter &p, const PaintContext &context) const = 0; virtual void draw(Painter &p, const PaintContext &context) const = 0;
[[nodiscard]] virtual PointState pointState(QPoint point) const = 0; [[nodiscard]] virtual PointState pointState(QPoint point) const = 0;
[[nodiscard]] virtual TextState textState( [[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>()) { if (const auto bar = Get<UnreadBar>()) {
auto unreadbarh = bar->height(); auto unreadbarh = bar->height();
auto dateh = 0; auto aboveh = 0;
if (const auto date = Get<DateBadge>()) { if (const auto date = Get<DateBadge>()) {
dateh = date->height(); aboveh += date->height();
} }
if (context.clip.intersects(QRect(0, dateh, width(), unreadbarh))) { if (const auto sender = Get<MonoforumSenderBar>()) {
p.translate(0, dateh); aboveh += sender->height();
}
if (context.clip.intersects(QRect(0, aboveh, width(), unreadbarh))) {
p.translate(0, aboveh);
bar->paint( bar->paint(
p, p,
context, context,
0, 0,
width(), width(),
delegate()->elementIsChatWide()); delegate()->elementIsChatWide());
p.translate(0, -dateh); p.translate(0, -aboveh);
}
}
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);
} }
} }

View File

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