mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-10-18 00:15:26 +02:00
Support floating topic bars in forums.
This commit is contained in:
parent
10fe5cdd5d
commit
03770c52fe
14 changed files with 325 additions and 105 deletions
|
@ -77,7 +77,9 @@ DefaultIconEmoji::DefaultIconEmoji(
|
|||
std::move(value) | rpl::start_with_next([=](DefaultIcon value) {
|
||||
_icon = value;
|
||||
_image = QImage();
|
||||
repaint();
|
||||
if (repaint) {
|
||||
repaint();
|
||||
}
|
||||
}, _lifetime);
|
||||
}
|
||||
|
||||
|
|
|
@ -235,7 +235,8 @@ void ChannelData::setFlags(ChannelDataFlags which) {
|
|||
| Flag::CallNotEmpty
|
||||
| Flag::SimilarExpanded
|
||||
| Flag::Signatures
|
||||
| Flag::SignatureProfiles)) {
|
||||
| Flag::SignatureProfiles
|
||||
| Flag::ForumTabs)) {
|
||||
if (const auto history = this->owner().historyLoaded(this)) {
|
||||
if (diff & Flag::CallNotEmpty) {
|
||||
history->updateChatListEntry();
|
||||
|
@ -262,6 +263,9 @@ void ChannelData::setFlags(ChannelDataFlags which) {
|
|||
if (diff & (Flag::Signatures | Flag::SignatureProfiles)) {
|
||||
session().changes().peerUpdated(this, UpdateFlag::Rights);
|
||||
}
|
||||
if (diff & Flag::ForumTabs) {
|
||||
history->forumTabsChanged(which & Flag::ForumTabs);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (const auto raw = takenForum.get()) {
|
||||
|
|
|
@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/painter.h"
|
||||
#include "ui/color_int_conversion.h"
|
||||
#include "ui/text/text_custom_emoji.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "styles/style_dialogs.h"
|
||||
#include "styles/style_chat_helpers.h"
|
||||
|
||||
|
@ -756,6 +757,16 @@ TextWithEntities ForumTopic::titleWithIcon() const {
|
|||
return ForumTopicIconWithTitle(_rootId, _iconId, _title);
|
||||
}
|
||||
|
||||
TextWithEntities ForumTopic::titleWithIconOrLogo() const {
|
||||
if (_iconId || isGeneral()) {
|
||||
return titleWithIcon();
|
||||
}
|
||||
return Ui::Text::SingleCustomEmoji(Data::TopicIconEmojiEntity({
|
||||
.title = _title,
|
||||
.colorId = _colorId,
|
||||
})).append(' ').append(_title);
|
||||
}
|
||||
|
||||
int ForumTopic::titleVersion() const {
|
||||
return _titleVersion;
|
||||
}
|
||||
|
|
|
@ -148,6 +148,7 @@ public:
|
|||
|
||||
[[nodiscard]] QString title() const;
|
||||
[[nodiscard]] TextWithEntities titleWithIcon() const;
|
||||
[[nodiscard]] TextWithEntities titleWithIconOrLogo() const;
|
||||
[[nodiscard]] int titleVersion() const;
|
||||
void applyTitle(const QString &title);
|
||||
[[nodiscard]] DocumentId iconId() const;
|
||||
|
|
|
@ -3464,6 +3464,26 @@ bool History::suggestDraftAllowed() const {
|
|||
return peer->isMonoforum() && !peer->amMonoforumAdmin();
|
||||
}
|
||||
|
||||
bool History::hasForumThreadBars() const {
|
||||
if (amMonoforumAdmin()) {
|
||||
return true;
|
||||
} else if (const auto channel = peer->asChannel()) {
|
||||
return channel->forum() && channel->useSubsectionTabs();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void History::forumTabsChanged(bool forumTabs) {
|
||||
for (auto &block : blocks) {
|
||||
for (auto &view : block->messages) {
|
||||
view->setPendingResize();
|
||||
if (forumTabs || view->Has<HistoryView::ForumThreadBar>()) {
|
||||
view->previousInBlocksChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
not_null<History*> History::migrateToOrMe() const {
|
||||
if (const auto to = peer->migrateTo()) {
|
||||
return owner().history(to);
|
||||
|
|
|
@ -80,6 +80,8 @@ public:
|
|||
void monoforumChanged(Data::SavedMessages *old);
|
||||
[[nodiscard]] bool amMonoforumAdmin() const;
|
||||
[[nodiscard]] bool suggestDraftAllowed() const;
|
||||
[[nodiscard]] bool hasForumThreadBars() const;
|
||||
void forumTabsChanged(bool forumTabs);
|
||||
|
||||
[[nodiscard]] not_null<History*> migrateToOrMe() const;
|
||||
[[nodiscard]] History *migrateFrom() const;
|
||||
|
|
|
@ -928,8 +928,8 @@ void HistoryInner::enumerateDates(Method method) {
|
|||
}
|
||||
|
||||
template <typename Method>
|
||||
void HistoryInner::enumerateMonoforumSenders(Method method) {
|
||||
if (!_history->amMonoforumAdmin()) {
|
||||
void HistoryInner::enumerateForumThreadBars(Method method) {
|
||||
if (!_history->hasForumThreadBars()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -946,28 +946,28 @@ void HistoryInner::enumerateMonoforumSenders(Method method) {
|
|||
// -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) {
|
||||
auto barCallback = [&](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
|
||||
// Call method on a bar 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 (view->displayForumThreadBar() || (!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();
|
||||
// Attach bar to the top of the visible area with the same margin as it has in service message.
|
||||
int barTop = 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);
|
||||
// Do not let the bar go below the single-bar messages pack bottom line.
|
||||
int barHeight = st::msgServicePadding.bottom() + st::msgServiceFont->height + st::msgServicePadding.top();
|
||||
barTop = qMin(barTop, lowestInOneBunchItemBottom - barHeight);
|
||||
|
||||
// Call the template callback function that was passed
|
||||
// and return if it finished everything it needed.
|
||||
if (!method(view, itemtop, senderTop)) {
|
||||
if (!method(view, itemtop, barTop)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -980,7 +980,7 @@ void HistoryInner::enumerateMonoforumSenders(Method method) {
|
|||
return true;
|
||||
};
|
||||
|
||||
enumerateItems<EnumItemsDirection::BottomToTop>(senderCallback);
|
||||
enumerateItems<EnumItemsDirection::BottomToTop>(barCallback);
|
||||
}
|
||||
|
||||
TextSelection HistoryInner::computeRenderSelection(
|
||||
|
@ -1395,31 +1395,31 @@ 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()) {
|
||||
enumerateForumThreadBars([&](not_null<Element*> view, int itemtop, int barTop) {
|
||||
// stop the enumeration if the bar is above the painted rect
|
||||
if (barTop + 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());
|
||||
const auto displayBar = view->displayForumThreadBar();
|
||||
auto barInPlace = displayBar;
|
||||
if (barInPlace) {
|
||||
const auto correctBarTop = itemtop + view->displayedDateHeight() + st::msgServiceMargin.top();
|
||||
barInPlace = (barTop < correctBarTop + 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);
|
||||
// paint the bar if it intersects the painted rect
|
||||
if (barTop < clip.top() + clip.height()) {
|
||||
const auto barY = barTop - st::msgServiceMargin.top();
|
||||
if (const auto bar = view->Get<HistoryView::ForumThreadBar>()) {
|
||||
bar->paint(p, context.st, barY, _contentWidth, _isChatWide, !barInPlace);
|
||||
} else {
|
||||
HistoryView::MonoforumSenderBar::PaintFor(
|
||||
_forumThreadBarWidth = HistoryView::ForumThreadBar::PaintForGetWidth(
|
||||
p,
|
||||
context.st,
|
||||
view,
|
||||
_monoforumSenderUserpicView,
|
||||
senderY,
|
||||
_forumThreadBarUserpicView,
|
||||
barY,
|
||||
_contentWidth,
|
||||
_isChatWide);
|
||||
}
|
||||
|
@ -3698,7 +3698,7 @@ 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()) {
|
||||
if (_history->hasForumThreadBars()) {
|
||||
updateHeight *= 2;
|
||||
}
|
||||
update(0, updateTop, width(), updateHeight);
|
||||
|
@ -4261,6 +4261,54 @@ void HistoryInner::mouseActionUpdate() {
|
|||
}
|
||||
return true;
|
||||
});
|
||||
if (!dragState.link) {
|
||||
enumerateForumThreadBars([&](not_null<Element*> view, int itemtop, int barTop) {
|
||||
// stop the enumeration if the bar is above our point
|
||||
if (barTop + dateHeight <= point.y()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto displayBar = view->displayForumThreadBar();
|
||||
auto barInPlace = displayBar;
|
||||
if (barInPlace) {
|
||||
const auto correctBarTop = itemtop + view->displayedDateHeight() + st::msgServiceMargin.top();
|
||||
barInPlace = (barTop < correctBarTop + st::msgServiceMargin.top());
|
||||
}
|
||||
|
||||
// stop enumeration if we've found a bar under the cursor
|
||||
if (barTop <= point.y()) {
|
||||
const auto item = view->data();
|
||||
auto barWidth = 0;
|
||||
if (const auto bar = view->Get<HistoryView::ForumThreadBar>()) {
|
||||
barWidth = bar->width;
|
||||
} else {
|
||||
barWidth = _forumThreadBarWidth;
|
||||
}
|
||||
auto barLeft = st::msgServiceMargin.left();
|
||||
auto maxwidth = _contentWidth;
|
||||
if (_isChatWide) {
|
||||
maxwidth = qMin(maxwidth, int32(st::msgMaxWidth + 2 * st::msgPhotoSkip + 2 * st::msgMargin.left()));
|
||||
}
|
||||
auto widthForBar = maxwidth - st::msgServiceMargin.left() - st::msgServiceMargin.left();
|
||||
|
||||
barLeft += (widthForBar - barWidth) / 2;
|
||||
|
||||
if (point.x() >= barLeft && point.x() < barLeft + barWidth) {
|
||||
if (!_forumThreadBarLink) {
|
||||
_forumThreadBarLink = std::make_shared<Window::ForumThreadClickHandler>(item);
|
||||
} else {
|
||||
static_cast<Window::ForumThreadClickHandler*>(_forumThreadBarLink.get())->update(item);
|
||||
}
|
||||
dragState = TextState(
|
||||
nullptr,
|
||||
_forumThreadBarLink);
|
||||
_dragStateItem = session().data().message(dragState.itemId);
|
||||
lnkhost = view;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
if (!dragState.link) {
|
||||
StateRequest request;
|
||||
if (_mouseAction == MouseAction::Selecting) {
|
||||
|
|
|
@ -315,13 +315,13 @@ private:
|
|||
template <typename Method>
|
||||
void enumerateDates(Method method);
|
||||
|
||||
// This function finds all monoforum sender elements that are displayed and calls template method
|
||||
// This function finds all forum thread bar 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 enumerateForumThreadBars(Method method);
|
||||
|
||||
void scrollDateCheck();
|
||||
void scrollDateHideByTimer();
|
||||
|
@ -470,7 +470,8 @@ private:
|
|||
int _contentWidth = 0;
|
||||
int _historyPaddingTop = 0;
|
||||
int _revealHeight = 0;
|
||||
Ui::PeerUserpicView _monoforumSenderUserpicView;
|
||||
int _forumThreadBarWidth = 0;
|
||||
Ui::PeerUserpicView _forumThreadBarUserpicView;
|
||||
|
||||
// Save visible area coords for painting / pressing userpics.
|
||||
int _visibleAreaTop = 0;
|
||||
|
@ -572,6 +573,7 @@ private:
|
|||
Element *_scrollDateLastItem = nullptr;
|
||||
int _scrollDateLastItemTop = 0;
|
||||
ClickHandlerPtr _scrollDateLink;
|
||||
ClickHandlerPtr _forumThreadBarLink;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -427,20 +427,37 @@ void DateBadge::paint(
|
|||
ServiceMessagePainter::PaintDate(p, st, text, width, y, w, chatWide);
|
||||
}
|
||||
|
||||
void MonoforumSenderBar::init(
|
||||
void ForumThreadBar::init(
|
||||
not_null<PeerData*> parentChat,
|
||||
not_null<PeerData*> peer) {
|
||||
sender = peer;
|
||||
text.setText(st::semiboldTextStyle, peer->name());
|
||||
not_null<Data::Thread*> thread) {
|
||||
this->thread = thread;
|
||||
const auto sublist = thread->asSublist();
|
||||
if (sublist) {
|
||||
text.setText(st::semiboldTextStyle, sublist->sublistPeer()->name());
|
||||
} else if (const auto topic = thread->asTopic()) {
|
||||
text.setMarkedText(
|
||||
st::semiboldTextStyle,
|
||||
topic->titleWithIconOrLogo(),
|
||||
kMarkupTextOptions,
|
||||
Core::TextContext({ .session = &topic->session() }));
|
||||
}
|
||||
const auto skip = st::monoforumBarUserpicSkip;
|
||||
const auto userpic = st::msgServicePadding.top()
|
||||
+ st::msgServiceFont->height
|
||||
+ st::msgServicePadding.bottom()
|
||||
- 2 * skip;
|
||||
width = skip + userpic + skip * 2 + text.maxWidth() + st::msgServicePadding.right();
|
||||
const auto userpic = sublist
|
||||
? (st::msgServicePadding.top()
|
||||
+ st::msgServiceFont->height
|
||||
+ st::msgServicePadding.bottom()
|
||||
- 2 * skip)
|
||||
: (st::msgServicePadding.left() - 3 * skip);
|
||||
|
||||
width = skip
|
||||
+ userpic
|
||||
+ skip * 2
|
||||
+ text.maxWidth()
|
||||
+ st::topicButtonArrowSkip
|
||||
+ st::msgServicePadding.right();
|
||||
}
|
||||
|
||||
int MonoforumSenderBar::height() const {
|
||||
int ForumThreadBar::height() const {
|
||||
return st::msgServiceMargin.top()
|
||||
+ st::msgServicePadding.top()
|
||||
+ st::msgServiceFont->height
|
||||
|
@ -448,17 +465,29 @@ int MonoforumSenderBar::height() const {
|
|||
+ st::msgServiceMargin.bottom();
|
||||
}
|
||||
|
||||
void MonoforumSenderBar::paint(
|
||||
void ForumThreadBar::paint(
|
||||
Painter &p,
|
||||
not_null<const Ui::ChatStyle*> st,
|
||||
int y,
|
||||
int w,
|
||||
bool chatWide,
|
||||
bool skipPatternLine) const {
|
||||
Paint(p, st, sender, text, width, view, y, w, chatWide, skipPatternLine);
|
||||
if (const auto strong = thread.get()) {
|
||||
Paint(
|
||||
p,
|
||||
st,
|
||||
strong,
|
||||
text,
|
||||
width,
|
||||
view,
|
||||
y,
|
||||
w,
|
||||
chatWide,
|
||||
skipPatternLine);
|
||||
}
|
||||
}
|
||||
|
||||
void MonoforumSenderBar::PaintFor(
|
||||
int ForumThreadBar::PaintForGetWidth(
|
||||
Painter &p,
|
||||
not_null<const Ui::ChatStyle*> st,
|
||||
not_null<Element*> itemView,
|
||||
|
@ -466,31 +495,49 @@ void MonoforumSenderBar::PaintFor(
|
|||
int y,
|
||||
int w,
|
||||
bool chatWide) {
|
||||
const auto sublist = itemView->data()->savedSublist();
|
||||
const auto sender = (sublist && sublist->parentChat())
|
||||
? sublist->sublistPeer().get()
|
||||
const auto item = itemView->data();
|
||||
const auto topic = item->topic();
|
||||
const auto sublist = item->savedSublist();
|
||||
const auto sender = topic
|
||||
? (Data::Thread*)topic
|
||||
: (sublist && sublist->parentChat())
|
||||
? (Data::Thread*)sublist
|
||||
: nullptr;
|
||||
if (!sender || sender->isMonoforum()) {
|
||||
return;
|
||||
auto text = Ui::Text::String();
|
||||
if (!sender
|
||||
|| !topic
|
||||
|| (sublist && sublist->sublistPeer()->isMonoforum())) {
|
||||
return 0;
|
||||
} else if (topic) {
|
||||
text.setMarkedText(
|
||||
st::semiboldTextStyle,
|
||||
topic->titleWithIconOrLogo(),
|
||||
kMarkupTextOptions,
|
||||
Core::TextContext({ .session = &topic->session() }));
|
||||
} else {
|
||||
text.setText(st::semiboldTextStyle, sublist->sublistPeer()->name());
|
||||
}
|
||||
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 userpic = sublist
|
||||
? (st::msgServicePadding.top()
|
||||
+ st::msgServiceFont->height
|
||||
+ st::msgServicePadding.bottom()
|
||||
- 2 * skip)
|
||||
: (st::msgServicePadding.left() - 3 * skip);
|
||||
const auto width = skip
|
||||
+ userpic
|
||||
+ skip * 2
|
||||
+ text.maxWidth()
|
||||
+ st::topicButtonArrowSkip
|
||||
+ st::msgServicePadding.right();
|
||||
Paint(p, st, sender, text, width, userpicView, y, w, chatWide, true);
|
||||
return width;
|
||||
}
|
||||
|
||||
void MonoforumSenderBar::Paint(
|
||||
void ForumThreadBar::Paint(
|
||||
Painter &p,
|
||||
not_null<const Ui::ChatStyle*> st,
|
||||
not_null<PeerData*> sender,
|
||||
not_null<Data::Thread*> thread,
|
||||
const Ui::Text::String &text,
|
||||
int width,
|
||||
Ui::PeerUserpicView &view,
|
||||
|
@ -498,8 +545,6 @@ void MonoforumSenderBar::Paint(
|
|||
int w,
|
||||
bool chatWide,
|
||||
bool skipPatternLine) {
|
||||
Expects(sender != nullptr);
|
||||
|
||||
int left = st::msgServiceMargin.left();
|
||||
const auto maxwidth = chatWide
|
||||
? std::min(w, WideChatWidth())
|
||||
|
@ -528,24 +573,47 @@ void MonoforumSenderBar::Paint(
|
|||
p.drawLine(left + use, top, 2 * w, top);
|
||||
}
|
||||
|
||||
const auto userpic = st::msgServicePadding.top()
|
||||
+ st::msgServiceFont->height
|
||||
+ st::msgServicePadding.bottom()
|
||||
- 2 * skip;
|
||||
const auto available = use - (skip + userpic + skip * 2 + st::msgServicePadding.right());
|
||||
const auto sublist = thread->asSublist();
|
||||
const auto userpic = sublist
|
||||
? (st::msgServicePadding.top()
|
||||
+ st::msgServiceFont->height
|
||||
+ st::msgServicePadding.bottom()
|
||||
- 2 * skip)
|
||||
: (st::msgServicePadding.left() - 3 * skip);
|
||||
const auto available = use
|
||||
- (skip
|
||||
+ userpic
|
||||
+ skip * 2
|
||||
+ st::topicButtonArrowSkip
|
||||
+ st::msgServicePadding.right());
|
||||
|
||||
sender->paintUserpic(p, view, left + skip, y + st::msgServiceMargin.top() + skip, userpic);
|
||||
if (sublist) {
|
||||
sublist->sublistPeer()->paintUserpic(
|
||||
p,
|
||||
view,
|
||||
left + skip,
|
||||
y + st::msgServiceMargin.top() + skip,
|
||||
userpic);
|
||||
}
|
||||
|
||||
const auto textLeft = left + skip + userpic + skip * 2;
|
||||
const auto textTop = y
|
||||
+ st::msgServiceMargin.top()
|
||||
+ st::msgServicePadding.top();
|
||||
p.setFont(st::msgServiceFont);
|
||||
p.setPen(st->msgServiceFg());
|
||||
text.draw(p, {
|
||||
.position = {
|
||||
left + skip + userpic + skip * 2,
|
||||
y + st::msgServiceMargin.top() + st::msgServicePadding.top(),
|
||||
},
|
||||
.position = { textLeft, textTop },
|
||||
.availableWidth = available,
|
||||
.elisionLines = 1,
|
||||
});
|
||||
|
||||
st::topicButtonArrow.paint(
|
||||
p,
|
||||
textLeft + available + st::topicButtonArrowPosition.x(),
|
||||
textTop + st::topicButtonArrowPosition.y(),
|
||||
w,
|
||||
st->msgServiceFg()->c);
|
||||
}
|
||||
|
||||
void ServicePreMessage::init(
|
||||
|
@ -1367,7 +1435,7 @@ void Element::validateTextSkipBlock(bool has, int width, int height) {
|
|||
}
|
||||
|
||||
void Element::previousInBlocksChanged() {
|
||||
recountMonoforumSenderBarInBlocks();
|
||||
recountThreadBarInBlocks();
|
||||
recountDisplayDateInBlocks();
|
||||
recountAttachToPreviousInBlocks();
|
||||
}
|
||||
|
@ -1404,7 +1472,7 @@ bool Element::computeIsAttachToPrevious(not_null<Element*> previous) {
|
|||
if (!Has<DateBadge>()
|
||||
&& !Has<UnreadBar>()
|
||||
&& !Has<ServicePreMessage>()
|
||||
&& !Has<MonoforumSenderBar>()) {
|
||||
&& !Has<ForumThreadBar>()) {
|
||||
const auto prev = previous->data();
|
||||
const auto previousMarkup = prev->inlineReplyMarkup();
|
||||
const auto possible = (std::abs(prev->date() - item->date())
|
||||
|
@ -1516,12 +1584,12 @@ bool Element::isInOneDayWithPrevious() const {
|
|||
return !data()->isEmpty() && !displayDate();
|
||||
}
|
||||
|
||||
bool Element::displayMonoforumSender() const {
|
||||
return Has<MonoforumSenderBar>();
|
||||
bool Element::displayForumThreadBar() const {
|
||||
return Has<ForumThreadBar>();
|
||||
}
|
||||
|
||||
bool Element::isInOneBunchWithPrevious() const {
|
||||
return !data()->isEmpty() && !displayMonoforumSender();
|
||||
return !data()->isEmpty() && !displayForumThreadBar();
|
||||
}
|
||||
|
||||
void Element::recountAttachToPreviousInBlocks() {
|
||||
|
@ -1542,34 +1610,49 @@ void Element::recountAttachToPreviousInBlocks() {
|
|||
setAttachToPrevious(attachToPrevious, previous);
|
||||
}
|
||||
|
||||
void Element::recountMonoforumSenderBarInBlocks() {
|
||||
void Element::recountThreadBarInBlocks() {
|
||||
const auto item = data();
|
||||
const auto topic = item->topic();
|
||||
const auto sublist = item->savedSublist();
|
||||
const auto parentChat = sublist ? sublist->parentChat() : nullptr;
|
||||
const auto barPeer = [&]() -> PeerData* {
|
||||
const auto parentChat = (topic && topic->channel()->useSubsectionTabs())
|
||||
? topic->channel().get()
|
||||
: sublist
|
||||
? sublist->parentChat()
|
||||
: nullptr;
|
||||
const auto barThread = [&]() -> Data::Thread* {
|
||||
if (!parentChat
|
||||
|| isHidden()
|
||||
|| item->isEmpty()
|
||||
|| item->isSponsored()) {
|
||||
return nullptr;
|
||||
}
|
||||
const auto sublistPeer = sublist->sublistPeer();
|
||||
if (const auto previous = previousDisplayedInBlocks()) {
|
||||
const auto prev = previous->data();
|
||||
if (const auto prevSublist = prev->savedSublist()) {
|
||||
if (const auto prevTopic = prev->topic()) {
|
||||
Assert(prevTopic->channel() == parentChat);
|
||||
const auto topicRootId = topic->rootId();
|
||||
if (prevTopic->rootId() == topicRootId) {
|
||||
return nullptr;
|
||||
}
|
||||
} else if (const auto prevSublist = prev->savedSublist()) {
|
||||
Assert(prevSublist->parentChat() == parentChat);
|
||||
const auto sublistPeer = sublist->sublistPeer();
|
||||
if (prevSublist->sublistPeer() == sublistPeer) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (sublistPeer == parentChat) ? nullptr : sublistPeer.get();
|
||||
return topic
|
||||
? (Data::Thread*)topic
|
||||
: (sublist && sublist->sublistPeer() != parentChat)
|
||||
? (Data::Thread*)sublist
|
||||
: nullptr;
|
||||
}();
|
||||
if (barPeer && !Has<MonoforumSenderBar>()) {
|
||||
AddComponents(MonoforumSenderBar::Bit());
|
||||
Get<MonoforumSenderBar>()->init(parentChat, barPeer);
|
||||
} else if (!barPeer && Has<MonoforumSenderBar>()) {
|
||||
RemoveComponents(MonoforumSenderBar::Bit());
|
||||
if (barThread && !Has<ForumThreadBar>()) {
|
||||
AddComponents(ForumThreadBar::Bit());
|
||||
Get<ForumThreadBar>()->init(parentChat, barThread);
|
||||
} else if (!barThread && Has<ForumThreadBar>()) {
|
||||
RemoveComponents(ForumThreadBar::Bit());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ struct HistoryMessageReply;
|
|||
struct PreparedServiceText;
|
||||
|
||||
namespace Data {
|
||||
class Thread;
|
||||
struct Reaction;
|
||||
struct ReactionId;
|
||||
} // namespace Data
|
||||
|
@ -265,8 +266,10 @@ struct DateBadge : RuntimeComponent<DateBadge, Element> {
|
|||
|
||||
};
|
||||
|
||||
struct MonoforumSenderBar : RuntimeComponent<MonoforumSenderBar, Element> {
|
||||
void init(not_null<PeerData*> parentChat, not_null<PeerData*> peer);
|
||||
struct ForumThreadBar : RuntimeComponent<ForumThreadBar, Element> {
|
||||
void init(
|
||||
not_null<PeerData*> parentChat,
|
||||
not_null<Data::Thread*> thread);
|
||||
|
||||
int height() const;
|
||||
void paint(
|
||||
|
@ -276,7 +279,7 @@ struct MonoforumSenderBar : RuntimeComponent<MonoforumSenderBar, Element> {
|
|||
int w,
|
||||
bool chatWide,
|
||||
bool skipPatternLine) const;
|
||||
static void PaintFor(
|
||||
static int PaintForGetWidth(
|
||||
Painter &p,
|
||||
not_null<const Ui::ChatStyle*> st,
|
||||
not_null<Element*> itemView,
|
||||
|
@ -285,9 +288,8 @@ struct MonoforumSenderBar : RuntimeComponent<MonoforumSenderBar, Element> {
|
|||
int w,
|
||||
bool chatWide);
|
||||
|
||||
PeerData *sender = nullptr;
|
||||
base::weak_ptr<Data::Thread> thread;
|
||||
Ui::Text::String text;
|
||||
ClickHandlerPtr link;
|
||||
mutable Ui::PeerUserpicView view;
|
||||
int width = 0;
|
||||
|
||||
|
@ -295,7 +297,7 @@ private:
|
|||
static void Paint(
|
||||
Painter &p,
|
||||
not_null<const Ui::ChatStyle*> st,
|
||||
not_null<PeerData*> sender,
|
||||
not_null<Data::Thread*> thread,
|
||||
const Ui::Text::String &text,
|
||||
int width,
|
||||
Ui::PeerUserpicView &view,
|
||||
|
@ -475,7 +477,7 @@ public:
|
|||
[[nodiscard]] bool displayDate() const;
|
||||
[[nodiscard]] bool isInOneDayWithPrevious() const;
|
||||
|
||||
[[nodiscard]] bool displayMonoforumSender() const;
|
||||
[[nodiscard]] bool displayForumThreadBar() const;
|
||||
[[nodiscard]] bool isInOneBunchWithPrevious() const;
|
||||
|
||||
virtual void draw(Painter &p, const PaintContext &context) const = 0;
|
||||
|
@ -688,7 +690,7 @@ protected:
|
|||
std::unique_ptr<Reactions::InlineList> _reactions;
|
||||
|
||||
private:
|
||||
void recountMonoforumSenderBarInBlocks();
|
||||
void recountThreadBarInBlocks();
|
||||
|
||||
// This should be called only from previousInBlocksChanged()
|
||||
// to add required bits to the Composer mask
|
||||
|
|
|
@ -1092,10 +1092,13 @@ QSize Message::performCountOptimalSize() {
|
|||
|
||||
void Message::refreshTopicButton() {
|
||||
const auto item = data();
|
||||
if (isAttachedToPrevious()
|
||||
|| delegate()->elementHideTopicButton(this)) {
|
||||
if (isAttachedToPrevious() || delegate()->elementHideTopicButton(this)) {
|
||||
_topicButton = nullptr;
|
||||
} else if (const auto topic = item->topic()) {
|
||||
if (topic->channel()->useSubsectionTabs()) {
|
||||
_topicButton = nullptr;
|
||||
return;
|
||||
}
|
||||
if (!_topicButton) {
|
||||
_topicButton = std::make_unique<TopicButton>();
|
||||
}
|
||||
|
@ -1132,8 +1135,8 @@ int Message::marginTop() const {
|
|||
if (const auto bar = Get<UnreadBar>()) {
|
||||
result += bar->height();
|
||||
}
|
||||
if (const auto monoforumBar = Get<MonoforumSenderBar>()) {
|
||||
result += monoforumBar->height();
|
||||
if (const auto bar = Get<ForumThreadBar>()) {
|
||||
result += bar->height();
|
||||
}
|
||||
if (const auto service = Get<ServicePreMessage>()) {
|
||||
result += service->height;
|
||||
|
@ -1181,8 +1184,8 @@ void Message::draw(Painter &p, const PaintContext &context) const {
|
|||
if (const auto date = Get<DateBadge>()) {
|
||||
aboveh += date->height();
|
||||
}
|
||||
if (const auto sender = Get<MonoforumSenderBar>()) {
|
||||
aboveh += sender->height();
|
||||
if (const auto bar = Get<ForumThreadBar>()) {
|
||||
aboveh += bar->height();
|
||||
}
|
||||
if (context.clip.intersects(QRect(0, aboveh, width(), unreadbarh))) {
|
||||
p.translate(0, aboveh);
|
||||
|
|
|
@ -527,8 +527,8 @@ int Service::marginTop() const {
|
|||
if (const auto bar = Get<UnreadBar>()) {
|
||||
result += bar->height();
|
||||
}
|
||||
if (const auto monoforumBar = Get<MonoforumSenderBar>()) {
|
||||
result += monoforumBar->height();
|
||||
if (const auto bar = Get<ForumThreadBar>()) {
|
||||
result += bar->height();
|
||||
}
|
||||
if (const auto service = Get<ServicePreMessage>()) {
|
||||
result += service->height;
|
||||
|
@ -553,8 +553,8 @@ void Service::draw(Painter &p, const PaintContext &context) const {
|
|||
if (const auto date = Get<DateBadge>()) {
|
||||
aboveh += date->height();
|
||||
}
|
||||
if (const auto sender = Get<MonoforumSenderBar>()) {
|
||||
aboveh += sender->height();
|
||||
if (const auto bar = Get<ForumThreadBar>()) {
|
||||
aboveh += bar->height();
|
||||
}
|
||||
if (context.clip.intersects(QRect(0, aboveh, width(), unreadbarh))) {
|
||||
p.translate(0, aboveh);
|
||||
|
|
|
@ -348,6 +348,33 @@ void DateClickHandler::onClick(ClickContext context) const {
|
|||
}
|
||||
}
|
||||
|
||||
ForumThreadClickHandler::ForumThreadClickHandler(not_null<HistoryItem*> item)
|
||||
: _thread(resolveThread(item)) {
|
||||
}
|
||||
|
||||
void ForumThreadClickHandler::update(not_null<HistoryItem*> item) {
|
||||
_thread = resolveThread(item);
|
||||
}
|
||||
|
||||
void ForumThreadClickHandler::onClick(ClickContext context) const {
|
||||
const auto my = context.other.value<ClickHandlerContext>();
|
||||
if (const auto window = my.sessionWindow.get()) {
|
||||
if (const auto strong = _thread.get()) {
|
||||
window->showThread(strong, 0, SectionShow::Way::ClearStack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
base::weak_ptr<Data::Thread> ForumThreadClickHandler::resolveThread(
|
||||
not_null<HistoryItem*> item) const {
|
||||
if (const auto sublist = item->savedSublist()) {
|
||||
return sublist;
|
||||
} else if (const auto topic = item->topic()) {
|
||||
return topic;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MessageHighlightId SearchHighlightId(const QString &query) {
|
||||
auto result = MessageHighlightId{ .quote = { query } };
|
||||
if (!result.quote.empty()) {
|
||||
|
|
|
@ -126,6 +126,21 @@ private:
|
|||
|
||||
};
|
||||
|
||||
class ForumThreadClickHandler : public ClickHandler {
|
||||
public:
|
||||
explicit ForumThreadClickHandler(not_null<HistoryItem*> item);
|
||||
|
||||
void update(not_null<HistoryItem*> item);
|
||||
void onClick(ClickContext context) const override;
|
||||
|
||||
private:
|
||||
[[nodiscard]] base::weak_ptr<Data::Thread> resolveThread(
|
||||
not_null<HistoryItem*> item) const;
|
||||
|
||||
base::weak_ptr<Data::Thread> _thread;
|
||||
|
||||
};
|
||||
|
||||
struct SectionShow {
|
||||
enum class Way {
|
||||
Forward,
|
||||
|
|
Loading…
Add table
Reference in a new issue