Topics list in forum chats list entry.

This commit is contained in:
John Preston 2022-11-11 13:24:37 +04:00
parent 996b6bf46a
commit 4c8187f623
15 changed files with 395 additions and 97 deletions

View File

@ -581,6 +581,8 @@ PRIVATE
dialogs/ui/dialogs_layout.h
dialogs/ui/dialogs_message_view.cpp
dialogs/ui/dialogs_message_view.h
dialogs/ui/dialogs_topics_view.cpp
dialogs/ui/dialogs_topics_view.h
dialogs/ui/dialogs_video_userpic.cpp
dialogs/ui/dialogs_video_userpic.h
editor/color_picker.cpp

View File

@ -179,7 +179,7 @@ void Folder::reorderLastHistories() {
const auto bDate = bItem ? bItem->date() : TimeId(0);
return aDate > bDate;
};
_lastHistories.erase(_lastHistories.begin(), _lastHistories.end());
_lastHistories.clear();
_lastHistories.reserve(kShowChatNamesCount + 1);
auto &&histories = ranges::views::all(
*_chatsList.indexed()
@ -187,11 +187,13 @@ void Folder::reorderLastHistories() {
return row->history();
}) | ranges::views::filter([](History *history) {
return (history != nullptr);
}) | ranges::views::transform([](History *history) {
return not_null<History*>(history);
});
auto nonPinnedChecked = 0;
for (const auto history : histories) {
const auto i = ranges::upper_bound(_lastHistories, history, pred);
const auto i = ranges::upper_bound(
_lastHistories,
not_null(history),
pred);
if (size(_lastHistories) < kShowChatNamesCount
|| i != end(_lastHistories)) {
_lastHistories.insert(i, history);
@ -199,6 +201,10 @@ void Folder::reorderLastHistories() {
if (size(_lastHistories) > kShowChatNamesCount) {
_lastHistories.pop_back();
}
if (!history->isPinnedDialog(FilterId())
&& ++nonPinnedChecked >= kShowChatNamesCount) {
break;
}
}
++_chatListViewVersion;
updateChatListEntry();

View File

@ -37,6 +37,7 @@ constexpr auto kTopicsFirstLoad = 20;
constexpr auto kLoadedTopicsMinCount = 20;
constexpr auto kTopicsPerPage = 500;
constexpr auto kStalePerRequest = 100;
constexpr auto kShowTopicNamesCount = 8;
// constexpr auto kGeneralColorId = 0xA9A9A9;
} // namespace
@ -172,6 +173,11 @@ void Forum::applyTopicDeleted(MsgId rootId) {
const auto raw = i->second.get();
Core::App().notifications().clearFromTopic(raw);
owner().removeChatListEntry(raw);
if (ranges::contains(_lastTopics, not_null(raw))) {
reorderLastTopics();
}
_topicDestroyed.fire(raw);
_topics.erase(i);
@ -183,6 +189,65 @@ void Forum::applyTopicDeleted(MsgId rootId) {
}
}
void Forum::reorderLastTopics() {
// We want first kShowChatNamesCount histories, by last message date.
const auto pred = [](not_null<ForumTopic*> a, not_null<ForumTopic*> b) {
const auto aItem = a->chatListMessage();
const auto bItem = b->chatListMessage();
const auto aDate = aItem ? aItem->date() : TimeId(0);
const auto bDate = bItem ? bItem->date() : TimeId(0);
return aDate > bDate;
};
_lastTopics.clear();
_lastTopics.reserve(kShowTopicNamesCount + 1);
auto &&topics = ranges::views::all(
*_topicsList.indexed()
) | ranges::views::transform([](not_null<Dialogs::Row*> row) {
return row->topic();
});
auto nonPinnedChecked = 0;
for (const auto topic : topics) {
const auto i = ranges::upper_bound(
_lastTopics,
not_null(topic),
pred);
if (size(_lastTopics) < kShowTopicNamesCount
|| i != end(_lastTopics)) {
_lastTopics.insert(i, topic);
}
if (size(_lastTopics) > kShowTopicNamesCount) {
_lastTopics.pop_back();
}
if (!topic->isPinnedDialog(FilterId())
&& ++nonPinnedChecked >= kShowTopicNamesCount) {
break;
}
}
++_lastTopicsVersion;
_history->updateChatListEntry();
}
int Forum::recentTopicsListVersion() const {
return _lastTopicsVersion;
}
void Forum::recentTopicsInvalidate(not_null<ForumTopic*> topic) {
if (ranges::contains(_lastTopics, topic)) {
++_lastTopicsVersion;
_history->updateChatListEntry();
}
}
const std::vector<not_null<ForumTopic*>> &Forum::recentTopics() const {
return _lastTopics;
}
void Forum::listMessageChanged(HistoryItem *from, HistoryItem *to) {
if (from || to) {
reorderLastTopics();
}
}
void Forum::applyReceivedTopics(
const MTPmessages_ForumTopics &topics,
ForumOffsets &updateOffsets) {
@ -344,6 +409,8 @@ ForumTopic *Forum::applyTopicAdded(
if (!creating(rootId)) {
raw->addToChatList(FilterId(), topicsList());
_chatsListChanges.fire({});
reorderLastTopics();
}
return raw;
}
@ -395,6 +462,8 @@ void Forum::created(MsgId rootId, MsgId realId) {
realId,
std::move(topic)
).first->second->setRealRootId(realId);
reorderLastTopics();
}
owner().notifyItemIdChange({ id, rootId });
}

View File

@ -90,6 +90,12 @@ public:
void clearAllUnreadReactions();
void enumerateTopics(Fn<void(not_null<ForumTopic*>)> action) const;
void listMessageChanged(HistoryItem *from, HistoryItem *to);
[[nodiscard]] int recentTopicsListVersion() const;
void recentTopicsInvalidate(not_null<ForumTopic*> topic);
[[nodiscard]] auto recentTopics() const
-> const std::vector<not_null<ForumTopic*>> &;
[[nodiscard]] rpl::lifetime &lifetime() {
return _lifetime;
}
@ -100,6 +106,7 @@ private:
std::vector<Fn<void()>> callbacks;
};
void reorderLastTopics();
void requestSomeStale();
void finishTopicRequest(MsgId rootId);
@ -119,6 +126,9 @@ private:
base::flat_set<MsgId> _creatingRootIds;
std::vector<not_null<ForumTopic*>> _lastTopics;
int _lastTopicsVersion = 0;
rpl::event_stream<> _chatsListChanges;
rpl::event_stream<> _chatsListLoadedEvents;

View File

@ -178,6 +178,9 @@ ForumTopic::ForumTopic(not_null<Forum*> forum, MsgId rootId)
}) | rpl::start_with_next([=](
std::optional<int> previous,
std::optional<int> now) {
if (previous.value_or(0) != now.value_or(0)) {
_forum->recentTopicsInvalidate(this);
}
notifyUnreadStateChange(unreadStateFor(
previous.value_or(0),
previous.has_value()));
@ -489,7 +492,9 @@ void ForumTopic::setLastMessage(HistoryItem *item) {
void ForumTopic::setChatListMessage(HistoryItem *item) {
if (_chatListMessage && *_chatListMessage == item) {
return;
} else if (item) {
}
const auto was = _chatListMessage.value_or(nullptr);
if (item) {
if (item->isSponsored()) {
return;
}
@ -505,6 +510,7 @@ void ForumTopic::setChatListMessage(HistoryItem *item) {
_chatListMessage = nullptr;
updateChatListEntry();
}
_forum->listMessageChanged(was, item);
}
void ForumTopic::loadUserpic() {
@ -625,6 +631,7 @@ void ForumTopic::applyTitle(const QString &title) {
}
_title = title;
++_titleVersion;
_forum->recentTopicsInvalidate(this);
_defaultIcon = QImage();
indexTitleParts();
updateChatListEntry();

View File

@ -17,6 +17,8 @@ DialogRow {
nameTop: pixels;
textLeft: pixels;
textTop: pixels;
topicsSkip: pixels;
topicsHeight: pixels;
}
ForumTopicIcon {
@ -71,8 +73,11 @@ defaultDialogRow: DialogRow {
textLeft: 68px;
textTop: 34px;
}
forumDialogRow: DialogRow {
forumDialogRow: DialogRow(defaultDialogRow) {
height: 80px;
textTop: 32px;
topicsSkip: 8px;
topicsHeight: 20px;
}
dialogsOnlineBadgeStroke: 2px;
@ -151,6 +156,21 @@ dialogsTextPaletteArchiveActive: TextPalette(defaultTextPalette) {
monoFg: dialogsTextFgActive;
spoilerFg: dialogsTextFgActive;
}
dialogsTextPaletteInTopic: TextPalette(defaultTextPalette) {
linkFg: dialogsNameFg;
monoFg: dialogsTextFg;
spoilerFg: dialogsTextFg;
}
dialogsTextPaletteInTopicOver: TextPalette(defaultTextPalette) {
linkFg: dialogsNameFgOver;
monoFg: dialogsTextFgOver;
spoilerFg: dialogsTextFgOver;
}
dialogsTextPaletteInTopicActive: TextPalette(defaultTextPalette) {
linkFg: dialogsNameFgActive;
monoFg: dialogsTextFgActive;
spoilerFg: dialogsTextFgActive;
}
dialogsEmptyHeight: 160px;
dialogsEmptySkip: 2px;

View File

@ -532,12 +532,21 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
: Key()));
if (shownBottom) {
const auto skip = dialogsOffset();
auto reorderingPinned = (_aboveIndex >= 0 && !_pinnedRows.empty());
if (reorderingPinned) {
dialogsClip = dialogsClip.marginsAdded(QMargins(0, _st->height, 0, _st->height));
}
const auto promoted = fixedOnTopCount();
const auto reorderingPinned = (_aboveIndex >= 0)
&& !_pinnedRows.empty();
const auto reorderingIndex = promoted + _aboveIndex;
const auto reorderingRow = (reorderingIndex < list.size())
? (list.cbegin() + reorderingIndex)->get()
: nullptr;
if (reorderingRow) {
dialogsClip = dialogsClip.marginsAdded({
0,
reorderingRow->height(),
0,
reorderingRow->height(),
});
}
const auto skippedTop = skipTopHeight();
const auto paintDialog = [&](not_null<Row*> row) {
const auto pinned = row->index() - promoted;
@ -552,8 +561,10 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
const auto key = row->key();
const auto isActive = (key == active);
const auto isSelected = (key == selected);
const auto isForum = key.history()
&& key.history()->peer->isForum();
Ui::RowPainter::Paint(p, row, validateVideoUserpic(row), {
.st = _st,
.st = (isForum ? &st::forumDialogRow : _st.get()),
.folder = _openedFolder,
.forum = _openedForum,
.filter = _filterId,
@ -600,14 +611,10 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
}
// Paint the dragged chat above all others.
if (_aboveIndex >= 0) {
const auto index = promoted + _aboveIndex;
if (index < list.size()) {
const auto row = *(list.cbegin() + index);
p.translate(0, row->top() - top);
paintDialog(*i);
p.translate(0, top - row->top());
}
if (reorderingRow) {
p.translate(0, reorderingRow->top() - top);
paintDialog(reorderingRow);
p.translate(0, top - reorderingRow->top());
}
}
} else {
@ -678,8 +685,10 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
: (from == (isPressed()
? _filteredPressed
: _filteredSelected));
const auto isForum = key.history()
&& key.history()->peer->isForum();
Ui::RowPainter::Paint(p, row, validateVideoUserpic(row), {
.st = _st,
.st = (isForum ? &st::forumDialogRow : _st.get()),
.folder = _openedFolder,
.forum = _openedForum,
.filter = _filterId,
@ -1422,25 +1431,26 @@ bool InnerWidget::updateReorderPinned(QPoint localPosition) {
return false;
}
const auto draggingHeight = _dragging->height();
auto yaddWas = _pinnedRows[_draggingIndex].yadd.current();
auto shift = 0;
auto now = crl::now();
if (_dragStart.y() > localPosition.y() && _draggingIndex > 0) {
shift = -floorclamp(_dragStart.y() - localPosition.y() + (_st->height / 2), _st->height, 0, _draggingIndex);
shift = -floorclamp(_dragStart.y() - localPosition.y() + (draggingHeight / 2), draggingHeight, 0, _draggingIndex);
for (auto from = _draggingIndex, to = _draggingIndex + shift; from > to; --from) {
_shownList->movePinned(_dragging, -1);
std::swap(_pinnedRows[from], _pinnedRows[from - 1]);
_pinnedRows[from].yadd = anim::value(_pinnedRows[from].yadd.current() - _st->height, 0);
_pinnedRows[from].yadd = anim::value(_pinnedRows[from].yadd.current() - draggingHeight, 0);
_pinnedRows[from].animStartTime = now;
}
} else if (_dragStart.y() < localPosition.y() && _draggingIndex + 1 < pinnedCount) {
shift = floorclamp(localPosition.y() - _dragStart.y() + (_st->height / 2), _st->height, 0, pinnedCount - _draggingIndex - 1);
shift = floorclamp(localPosition.y() - _dragStart.y() + (draggingHeight / 2), draggingHeight, 0, pinnedCount - _draggingIndex - 1);
for (auto from = _draggingIndex, to = _draggingIndex + shift; from < to; ++from) {
_shownList->movePinned(_dragging, 1);
std::swap(_pinnedRows[from], _pinnedRows[from + 1]);
_pinnedRows[from].yadd = anim::value(_pinnedRows[from].yadd.current() + _st->height, 0);
_pinnedRows[from].yadd = anim::value(_pinnedRows[from].yadd.current() + draggingHeight, 0);
_pinnedRows[from].animStartTime = now;
}
}

View File

@ -149,6 +149,8 @@ Row::Row(Key key, int index, int top) : _id(key), _top(top), _index(index) {
_height = history->peer->isForum()
? st::forumDialogRow.height
: st::defaultDialogRow.height;
} else if (key.folder()) {
_height = st::defaultDialogRow.height;
} else {
_height = st::forumTopicRow.height;
}

View File

@ -216,17 +216,11 @@ void PaintListEntryText(
row->listEntryCache().draw(p, {
.position = rect.topLeft(),
.availableWidth = rect.width(),
.palette = &(row->folder()
? (context.active
? st::dialogsTextPaletteArchiveActive
: context.selected
? st::dialogsTextPaletteArchiveOver
: st::dialogsTextPaletteArchive)
: (context.active
? st::dialogsTextPaletteActive
: context.selected
? st::dialogsTextPaletteOver
: st::dialogsTextPalette)),
.palette = &(context.active
? st::dialogsTextPaletteArchiveActive
: context.selected
? st::dialogsTextPaletteArchiveOver
: st::dialogsTextPaletteArchive),
.spoiler = Text::DefaultSpoilerCache(),
.now = context.now,
.paused = context.paused,
@ -897,7 +891,7 @@ void RowPainter::Paint(
: context.selected
? st::dialogsTextFgServiceOver
: st::dialogsTextFgService;
const auto rect = QRect(
auto rect = QRect(
nameleft,
texttop,
availableWidth,
@ -926,6 +920,13 @@ void RowPainter::Paint(
[=] { entry->updateChatListEntry(); },
{ .ignoreTopic = (!history || !peer->isForum()) });
}
if (const auto topics = context.st->topicsHeight) {
view->prepareTopics(
row->history()->peer->forum(),
rect,
[=] { entry->updateChatListEntry(); });
rect.setHeight(topics + rect.height());
}
view->paint(p, rect, context);
}
};

View File

@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_item_preview.h"
#include "main/main_session.h"
#include "dialogs/ui/dialogs_layout.h"
#include "dialogs/ui/dialogs_topics_view.h"
#include "ui/text/text_options.h"
#include "ui/text/text_utilities.h"
#include "ui/image/image.h"
@ -111,7 +112,6 @@ struct MessageView::LoadingContext {
MessageView::MessageView()
: _senderCache(st::dialogsTextWidthMin)
, _topicCache(st::dialogsTextWidthMin)
, _textCache(st::dialogsTextWidthMin) {
}
@ -135,11 +135,11 @@ void MessageView::prepare(
not_null<const HistoryItem*> item,
Fn<void()> customEmojiRepaint,
ToPreviewOptions options) {
const auto validateTopics = !options.ignoreTopic;
options.existing = &_imagesCache;
options.ignoreTopic = true;
auto preview = item->toPreview(options);
const auto hasImages = !preview.images.empty();
const auto hasArrow = (preview.arrowInTextPosition > 0)
&& (preview.imagesInTextPosition > preview.arrowInTextPosition);
const auto history = item->history();
const auto context = Core::MarkedTextContext{
.session = &history->session(),
@ -149,7 +149,7 @@ void MessageView::prepare(
const auto senderTill = (preview.arrowInTextPosition > 0)
? preview.arrowInTextPosition
: preview.imagesInTextPosition;
if ((hasImages || hasArrow) && senderTill > 0) {
if (hasImages && senderTill > 0) {
auto sender = Text::Mid(preview.text, 0, senderTill);
TextUtilities::Trim(sender);
_senderCache.setMarkedText(
@ -157,24 +157,8 @@ void MessageView::prepare(
std::move(sender),
DialogTextOptions());
const auto topicTill = preview.imagesInTextPosition;
if (hasArrow && hasImages) {
auto topic = Text::Mid(
preview.text,
senderTill,
topicTill - senderTill);
TextUtilities::Trim(topic);
_topicCache.setMarkedText(
st::dialogsTextStyle,
std::move(topic),
DialogTextOptions(),
context);
preview.text = Text::Mid(preview.text, topicTill);
} else {
preview.text = Text::Mid(preview.text, senderTill);
_topicCache = { st::dialogsTextWidthMin };
}
preview.text = Text::Mid(preview.text, senderTill);
} else {
_topicCache = { st::dialogsTextWidthMin };
_senderCache = { st::dialogsTextWidthMin };
}
TextUtilities::Trim(preview.text);
@ -199,6 +183,19 @@ void MessageView::prepare(
}
}
void MessageView::prepareTopics(
not_null<Data::Forum*> forum,
const QRect &geometry,
Fn<void()> customEmojiRepaint) {
if (!_topics || _topics->forum() != forum) {
_topics = std::make_unique<TopicsView>(forum);
}
_topics->prepare(
geometry,
&st::forumDialogRow,
std::move(customEmojiRepaint));
}
void MessageView::paint(
Painter &p,
const QRect &geometry,
@ -212,13 +209,25 @@ void MessageView::paint(
: context.selected
? st::dialogsTextFgOver
: st::dialogsTextFg);
const auto palette = &(context.active
? st::dialogsTextPaletteActive
: context.selected
? st::dialogsTextPaletteOver
: st::dialogsTextPalette);
const auto withTopic = _topics && context.st->topicsHeight;
const auto palette = &(withTopic
? (context.active
? st::dialogsTextPaletteInTopicActive
: context.selected
? st::dialogsTextPaletteInTopicOver
: st::dialogsTextPaletteInTopic)
: (context.active
? st::dialogsTextPaletteActive
: context.selected
? st::dialogsTextPaletteOver
: st::dialogsTextPalette));
auto rect = geometry;
if (withTopic) {
_topics->paint(p, rect, context);
rect.setTop(rect.top() + context.st->topicsHeight);
}
const auto lines = rect.height() / st::dialogsTextFont->height;
if (!_senderCache.isEmpty()) {
_senderCache.draw(p, {
@ -228,32 +237,6 @@ void MessageView::paint(
.elisionLines = lines,
});
rect.setLeft(rect.x() + _senderCache.maxWidth());
if (!_topicCache.isEmpty() || _imagesCache.empty()) {
const auto skip = st::dialogsTopicArrowSkip;
if (rect.width() >= skip) {
const auto &icon = st::dialogsTopicArrow;
icon.paint(
p,
rect.x() + (skip - icon.width()) / 2,
rect.y() + st::dialogsTopicArrowTop,
geometry.width());
}
rect.setLeft(rect.x() + skip);
}
if (!_topicCache.isEmpty()) {
if (!rect.isEmpty()) {
_topicCache.draw(p, {
.position = rect.topLeft(),
.availableWidth = rect.width(),
.palette = palette,
.spoiler = Text::DefaultSpoilerCache(),
.now = context.now,
.paused = context.paused,
.elisionLines = lines,
});
}
rect.setLeft(rect.x() + _topicCache.maxWidth());
}
if (!_imagesCache.empty()) {
const auto skip = st::dialogsMiniPreviewSkip
+ st::dialogsMiniPreviewRight;

View File

@ -16,6 +16,10 @@ enum class ImageRoundRadius;
namespace Ui {
} // namespace Ui
namespace Data {
class Forum;
} // namespace Data
namespace HistoryView {
struct ToPreviewOptions;
struct ItemPreviewImage;
@ -27,6 +31,7 @@ namespace Dialogs::Ui {
using namespace ::Ui;
struct PaintContext;
class TopicsView;
[[nodiscard]] TextWithEntities DialogsPreviewText(TextWithEntities text);
@ -47,6 +52,12 @@ public:
not_null<const HistoryItem*> item,
Fn<void()> customEmojiRepaint,
ToPreviewOptions options);
void prepareTopics(
not_null<Data::Forum*> forum,
const QRect &geometry,
Fn<void()> customEmojiRepaint);
void paint(
Painter &p,
const QRect &geometry,
@ -57,7 +68,7 @@ private:
mutable const HistoryItem *_textCachedFor = nullptr;
mutable Text::String _senderCache;
mutable Text::String _topicCache;
mutable std::unique_ptr<TopicsView> _topics;
mutable Text::String _textCache;
mutable std::vector<ItemPreviewImage> _imagesCache;
mutable std::unique_ptr<LoadingContext> _loadingContext;

View File

@ -0,0 +1,112 @@
/*
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 "dialogs/ui/dialogs_topics_view.h"
#include "dialogs/ui/dialogs_layout.h"
#include "data/data_forum.h"
#include "data/data_forum_topic.h"
#include "core/ui_integration.h"
#include "ui/painter.h"
#include "ui/text/text_options.h"
#include "ui/text/text_utilities.h"
#include "styles/style_dialogs.h"
namespace Dialogs::Ui {
namespace {
constexpr auto kIconLoopCount = 1;
} // namespace
TopicsView::TopicsView(not_null<Data::Forum*> forum)
: _forum(forum) {
}
TopicsView::~TopicsView() = default;
void TopicsView::prepare(
const QRect &geometry,
not_null<const style::DialogRow*> st,
Fn<void()> customEmojiRepaint) {
auto index = 0;
auto available = geometry.width();
for (const auto &topic : _forum->recentTopics()) {
if (available <= 0) {
break;
} else if (_titles.size() == index) {
_titles.emplace_back();
}
auto &title = _titles[index];
const auto rootId = topic->rootId();
const auto unread = topic->chatListBadgesState().unread;
if (title.topicRootId != rootId || title.unread != unread) {
const auto context = Core::MarkedTextContext{
.session = &topic->session(),
.customEmojiRepaint = customEmojiRepaint,
.customEmojiLoopLimit = kIconLoopCount,
};
auto topicTitle = topic->titleWithIcon();
title.title.setMarkedText(
st::dialogsTextStyle,
(unread
? Ui::Text::PlainLink(
Ui::Text::Wrapped(
std::move(topicTitle),
EntityType::Bold))
: std::move(topicTitle)),
DialogTextOptions(),
context);
title.topicRootId = rootId;
title.unread = unread;
}
available -= title.title.maxWidth() + st->topicsSkip;
++index;
}
while (_titles.size() > index) {
_titles.pop_back();
}
}
void TopicsView::paint(
Painter &p,
const QRect &geometry,
const PaintContext &context) const {
auto available = geometry.width();
p.setFont(st::dialogsTextFont);
p.setPen(context.active
? st::dialogsTextFgActive
: context.selected
? st::dialogsTextFgOver
: st::dialogsTextFg);
const auto palette = &(context.active
? st::dialogsTextPaletteArchiveActive
: context.selected
? st::dialogsTextPaletteArchiveOver
: st::dialogsTextPaletteArchive);
auto index = 0;
auto rect = geometry;
for (const auto &title : _titles) {
if (rect.width() <= 0) {
break;
}
title.title.draw(p, {
.position = rect.topLeft(),
.availableWidth = rect.width(),
.palette = palette,
.spoiler = Text::DefaultSpoilerCache(),
.now = context.now,
.paused = context.paused,
.elisionLines = 1,
});
rect.setLeft(
rect.left() + title.title.maxWidth() + context.st->topicsSkip);
}
}
} // namespace Dialogs::Ui

View File

@ -0,0 +1,67 @@
/*
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
class Painter;
namespace style {
struct DialogRow;
} // namespace style
namespace Data {
class Forum;
class ForumTopic;
} // namespace Data
namespace Ui {
} // namespace Ui
namespace Dialogs::Ui {
using namespace ::Ui;
struct PaintContext;
class TopicsView final {
public:
explicit TopicsView(not_null<Data::Forum*> forum);
~TopicsView();
[[nodiscard]] not_null<Data::Forum*> forum() const {
return _forum;
}
void prepare(
const QRect &geometry,
not_null<const style::DialogRow*> st,
Fn<void()> customEmojiRepaint);
void paint(
Painter &p,
const QRect &geometry,
const PaintContext &context) const;
[[nodiscard]] rpl::lifetime &lifetime() {
return _lifetime;
}
private:
struct Title {
Text::String title;
MsgId topicRootId = 0;
bool unread = false;
};
const not_null<Data::Forum*> _forum;
mutable std::vector<Title> _titles;
rpl::lifetime _lifetime;
};
} // namespace Dialogs::Ui

View File

@ -3011,6 +3011,11 @@ void History::forumChanged(Data::Forum *old) {
}) | rpl::start_with_next([=](const Dialogs::UnreadState &old) {
notifyUnreadStateChange(old);
}, forum->lifetime());
forum->chatsListChanges(
) | rpl::start_with_next([=] {
updateChatListEntry();
}, forum->lifetime());
} else {
_flags &= ~Flag::IsForum;
}

View File

@ -3206,13 +3206,6 @@ void ListWidget::mouseActionFinish(
};
auto activated = ClickHandler::unpressed();
if (_overElement) {
AssertIsDebug();
setGeometryCrashAnnotations(_overElement);
Unexpected("Test");
}
auto simpleSelectionChange = pressState.itemId
&& !_pressWasInactive
&& (button != Qt::RightButton)