mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-15 21:57:10 +02:00
Topics list in forum chats list entry.
This commit is contained in:
parent
996b6bf46a
commit
4c8187f623
15 changed files with 395 additions and 97 deletions
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 });
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
112
Telegram/SourceFiles/dialogs/ui/dialogs_topics_view.cpp
Normal file
112
Telegram/SourceFiles/dialogs/ui/dialogs_topics_view.cpp
Normal 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
|
67
Telegram/SourceFiles/dialogs/ui/dialogs_topics_view.h
Normal file
67
Telegram/SourceFiles/dialogs/ui/dialogs_topics_view.h
Normal 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
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Reference in a new issue