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_layout.h
dialogs/ui/dialogs_message_view.cpp dialogs/ui/dialogs_message_view.cpp
dialogs/ui/dialogs_message_view.h 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.cpp
dialogs/ui/dialogs_video_userpic.h dialogs/ui/dialogs_video_userpic.h
editor/color_picker.cpp editor/color_picker.cpp

View file

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

View file

@ -37,6 +37,7 @@ constexpr auto kTopicsFirstLoad = 20;
constexpr auto kLoadedTopicsMinCount = 20; constexpr auto kLoadedTopicsMinCount = 20;
constexpr auto kTopicsPerPage = 500; constexpr auto kTopicsPerPage = 500;
constexpr auto kStalePerRequest = 100; constexpr auto kStalePerRequest = 100;
constexpr auto kShowTopicNamesCount = 8;
// constexpr auto kGeneralColorId = 0xA9A9A9; // constexpr auto kGeneralColorId = 0xA9A9A9;
} // namespace } // namespace
@ -172,6 +173,11 @@ void Forum::applyTopicDeleted(MsgId rootId) {
const auto raw = i->second.get(); const auto raw = i->second.get();
Core::App().notifications().clearFromTopic(raw); Core::App().notifications().clearFromTopic(raw);
owner().removeChatListEntry(raw); owner().removeChatListEntry(raw);
if (ranges::contains(_lastTopics, not_null(raw))) {
reorderLastTopics();
}
_topicDestroyed.fire(raw); _topicDestroyed.fire(raw);
_topics.erase(i); _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( void Forum::applyReceivedTopics(
const MTPmessages_ForumTopics &topics, const MTPmessages_ForumTopics &topics,
ForumOffsets &updateOffsets) { ForumOffsets &updateOffsets) {
@ -344,6 +409,8 @@ ForumTopic *Forum::applyTopicAdded(
if (!creating(rootId)) { if (!creating(rootId)) {
raw->addToChatList(FilterId(), topicsList()); raw->addToChatList(FilterId(), topicsList());
_chatsListChanges.fire({}); _chatsListChanges.fire({});
reorderLastTopics();
} }
return raw; return raw;
} }
@ -395,6 +462,8 @@ void Forum::created(MsgId rootId, MsgId realId) {
realId, realId,
std::move(topic) std::move(topic)
).first->second->setRealRootId(realId); ).first->second->setRealRootId(realId);
reorderLastTopics();
} }
owner().notifyItemIdChange({ id, rootId }); owner().notifyItemIdChange({ id, rootId });
} }

View file

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

View file

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

View file

@ -17,6 +17,8 @@ DialogRow {
nameTop: pixels; nameTop: pixels;
textLeft: pixels; textLeft: pixels;
textTop: pixels; textTop: pixels;
topicsSkip: pixels;
topicsHeight: pixels;
} }
ForumTopicIcon { ForumTopicIcon {
@ -71,8 +73,11 @@ defaultDialogRow: DialogRow {
textLeft: 68px; textLeft: 68px;
textTop: 34px; textTop: 34px;
} }
forumDialogRow: DialogRow { forumDialogRow: DialogRow(defaultDialogRow) {
height: 80px; height: 80px;
textTop: 32px;
topicsSkip: 8px;
topicsHeight: 20px;
} }
dialogsOnlineBadgeStroke: 2px; dialogsOnlineBadgeStroke: 2px;
@ -151,6 +156,21 @@ dialogsTextPaletteArchiveActive: TextPalette(defaultTextPalette) {
monoFg: dialogsTextFgActive; monoFg: dialogsTextFgActive;
spoilerFg: 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; dialogsEmptyHeight: 160px;
dialogsEmptySkip: 2px; dialogsEmptySkip: 2px;

View file

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

View file

@ -216,17 +216,11 @@ void PaintListEntryText(
row->listEntryCache().draw(p, { row->listEntryCache().draw(p, {
.position = rect.topLeft(), .position = rect.topLeft(),
.availableWidth = rect.width(), .availableWidth = rect.width(),
.palette = &(row->folder() .palette = &(context.active
? (context.active ? st::dialogsTextPaletteArchiveActive
? st::dialogsTextPaletteArchiveActive : context.selected
: context.selected ? st::dialogsTextPaletteArchiveOver
? st::dialogsTextPaletteArchiveOver : st::dialogsTextPaletteArchive),
: st::dialogsTextPaletteArchive)
: (context.active
? st::dialogsTextPaletteActive
: context.selected
? st::dialogsTextPaletteOver
: st::dialogsTextPalette)),
.spoiler = Text::DefaultSpoilerCache(), .spoiler = Text::DefaultSpoilerCache(),
.now = context.now, .now = context.now,
.paused = context.paused, .paused = context.paused,
@ -897,7 +891,7 @@ void RowPainter::Paint(
: context.selected : context.selected
? st::dialogsTextFgServiceOver ? st::dialogsTextFgServiceOver
: st::dialogsTextFgService; : st::dialogsTextFgService;
const auto rect = QRect( auto rect = QRect(
nameleft, nameleft,
texttop, texttop,
availableWidth, availableWidth,
@ -926,6 +920,13 @@ void RowPainter::Paint(
[=] { entry->updateChatListEntry(); }, [=] { entry->updateChatListEntry(); },
{ .ignoreTopic = (!history || !peer->isForum()) }); { .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->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 "history/view/history_view_item_preview.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "dialogs/ui/dialogs_layout.h" #include "dialogs/ui/dialogs_layout.h"
#include "dialogs/ui/dialogs_topics_view.h"
#include "ui/text/text_options.h" #include "ui/text/text_options.h"
#include "ui/text/text_utilities.h" #include "ui/text/text_utilities.h"
#include "ui/image/image.h" #include "ui/image/image.h"
@ -111,7 +112,6 @@ struct MessageView::LoadingContext {
MessageView::MessageView() MessageView::MessageView()
: _senderCache(st::dialogsTextWidthMin) : _senderCache(st::dialogsTextWidthMin)
, _topicCache(st::dialogsTextWidthMin)
, _textCache(st::dialogsTextWidthMin) { , _textCache(st::dialogsTextWidthMin) {
} }
@ -135,11 +135,11 @@ void MessageView::prepare(
not_null<const HistoryItem*> item, not_null<const HistoryItem*> item,
Fn<void()> customEmojiRepaint, Fn<void()> customEmojiRepaint,
ToPreviewOptions options) { ToPreviewOptions options) {
const auto validateTopics = !options.ignoreTopic;
options.existing = &_imagesCache; options.existing = &_imagesCache;
options.ignoreTopic = true;
auto preview = item->toPreview(options); auto preview = item->toPreview(options);
const auto hasImages = !preview.images.empty(); const auto hasImages = !preview.images.empty();
const auto hasArrow = (preview.arrowInTextPosition > 0)
&& (preview.imagesInTextPosition > preview.arrowInTextPosition);
const auto history = item->history(); const auto history = item->history();
const auto context = Core::MarkedTextContext{ const auto context = Core::MarkedTextContext{
.session = &history->session(), .session = &history->session(),
@ -149,7 +149,7 @@ void MessageView::prepare(
const auto senderTill = (preview.arrowInTextPosition > 0) const auto senderTill = (preview.arrowInTextPosition > 0)
? preview.arrowInTextPosition ? preview.arrowInTextPosition
: preview.imagesInTextPosition; : preview.imagesInTextPosition;
if ((hasImages || hasArrow) && senderTill > 0) { if (hasImages && senderTill > 0) {
auto sender = Text::Mid(preview.text, 0, senderTill); auto sender = Text::Mid(preview.text, 0, senderTill);
TextUtilities::Trim(sender); TextUtilities::Trim(sender);
_senderCache.setMarkedText( _senderCache.setMarkedText(
@ -157,24 +157,8 @@ void MessageView::prepare(
std::move(sender), std::move(sender),
DialogTextOptions()); DialogTextOptions());
const auto topicTill = preview.imagesInTextPosition; const auto topicTill = preview.imagesInTextPosition;
if (hasArrow && hasImages) { preview.text = Text::Mid(preview.text, senderTill);
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 };
}
} else { } else {
_topicCache = { st::dialogsTextWidthMin };
_senderCache = { st::dialogsTextWidthMin }; _senderCache = { st::dialogsTextWidthMin };
} }
TextUtilities::Trim(preview.text); 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( void MessageView::paint(
Painter &p, Painter &p,
const QRect &geometry, const QRect &geometry,
@ -212,13 +209,25 @@ void MessageView::paint(
: context.selected : context.selected
? st::dialogsTextFgOver ? st::dialogsTextFgOver
: st::dialogsTextFg); : st::dialogsTextFg);
const auto palette = &(context.active const auto withTopic = _topics && context.st->topicsHeight;
? st::dialogsTextPaletteActive const auto palette = &(withTopic
: context.selected ? (context.active
? st::dialogsTextPaletteOver ? st::dialogsTextPaletteInTopicActive
: st::dialogsTextPalette); : context.selected
? st::dialogsTextPaletteInTopicOver
: st::dialogsTextPaletteInTopic)
: (context.active
? st::dialogsTextPaletteActive
: context.selected
? st::dialogsTextPaletteOver
: st::dialogsTextPalette));
auto rect = geometry; 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; const auto lines = rect.height() / st::dialogsTextFont->height;
if (!_senderCache.isEmpty()) { if (!_senderCache.isEmpty()) {
_senderCache.draw(p, { _senderCache.draw(p, {
@ -228,32 +237,6 @@ void MessageView::paint(
.elisionLines = lines, .elisionLines = lines,
}); });
rect.setLeft(rect.x() + _senderCache.maxWidth()); 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()) { if (!_imagesCache.empty()) {
const auto skip = st::dialogsMiniPreviewSkip const auto skip = st::dialogsMiniPreviewSkip
+ st::dialogsMiniPreviewRight; + st::dialogsMiniPreviewRight;

View file

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

View file

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