mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 14:17:12 +02:00
Display jump to last topic message bubble.
This commit is contained in:
parent
97356032ac
commit
ede34578da
18 changed files with 486 additions and 320 deletions
|
@ -16,6 +16,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/text/text_options.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "storage/storage_facade.h"
|
||||
#include "core/application.h"
|
||||
|
@ -33,9 +35,67 @@ namespace {
|
|||
constexpr auto kLoadedChatsMinCount = 20;
|
||||
constexpr auto kShowChatNamesCount = 8;
|
||||
|
||||
[[nodiscard]] TextWithEntities ComposeFolderListEntryText(
|
||||
not_null<Folder*> folder) {
|
||||
const auto &list = folder->lastHistories();
|
||||
if (list.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto count = std::max(
|
||||
int(list.size()),
|
||||
folder->chatsList()->fullSize().current());
|
||||
|
||||
const auto throwAwayLastName = (list.size() > 1)
|
||||
&& (count == list.size() + 1);
|
||||
auto &&peers = ranges::views::all(
|
||||
list
|
||||
) | ranges::views::take(
|
||||
list.size() - (throwAwayLastName ? 1 : 0)
|
||||
);
|
||||
const auto wrapName = [](not_null<History*> history) {
|
||||
const auto name = history->peer->name();
|
||||
return TextWithEntities{
|
||||
.text = name,
|
||||
.entities = (history->chatListBadgesState().unread
|
||||
? EntitiesInText{
|
||||
{ EntityType::Semibold, 0, int(name.size()), QString() },
|
||||
{ EntityType::PlainLink, 0, int(name.size()), QString() },
|
||||
}
|
||||
: EntitiesInText{}),
|
||||
};
|
||||
};
|
||||
const auto shown = int(peers.size());
|
||||
const auto accumulated = [&] {
|
||||
Expects(shown > 0);
|
||||
|
||||
auto i = peers.begin();
|
||||
auto result = wrapName(*i);
|
||||
for (++i; i != peers.end(); ++i) {
|
||||
result = tr::lng_archived_last_list(
|
||||
tr::now,
|
||||
lt_accumulated,
|
||||
result,
|
||||
lt_chat,
|
||||
wrapName(*i),
|
||||
Ui::Text::WithEntities);
|
||||
}
|
||||
return result;
|
||||
}();
|
||||
return (shown < count)
|
||||
? tr::lng_archived_last(
|
||||
tr::now,
|
||||
lt_count,
|
||||
(count - shown),
|
||||
lt_chats,
|
||||
accumulated,
|
||||
Ui::Text::WithEntities)
|
||||
: accumulated;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Folder::Folder(not_null<Data::Session*> owner, FolderId id)
|
||||
Folder::Folder(not_null<Session*> owner, FolderId id)
|
||||
: Entry(owner, Type::Folder)
|
||||
, _id(id)
|
||||
, _chatsList(
|
||||
|
@ -76,29 +136,6 @@ FolderId Folder::id() const {
|
|||
|
||||
void Folder::indexNameParts() {
|
||||
// We don't want archive to be filtered in the chats list.
|
||||
//_nameWords.clear();
|
||||
//_nameFirstLetters.clear();
|
||||
//auto toIndexList = QStringList();
|
||||
//auto appendToIndex = [&](const QString &value) {
|
||||
// if (!value.isEmpty()) {
|
||||
// toIndexList.push_back(TextUtilities::RemoveAccents(value));
|
||||
// }
|
||||
//};
|
||||
|
||||
//appendToIndex(_name);
|
||||
//const auto appendTranslit = !toIndexList.isEmpty()
|
||||
// && cRussianLetters().match(toIndexList.front()).hasMatch();
|
||||
//if (appendTranslit) {
|
||||
// appendToIndex(translitRusEng(toIndexList.front()));
|
||||
//}
|
||||
//auto toIndex = toIndexList.join(' ');
|
||||
//toIndex += ' ' + rusKeyboardLayoutSwitch(toIndex);
|
||||
|
||||
//const auto namesList = TextUtilities::PrepareSearchWords(toIndex);
|
||||
//for (const auto &name : namesList) {
|
||||
// _nameWords.insert(name);
|
||||
// _nameFirstLetters.insert(name[0]);
|
||||
//}
|
||||
}
|
||||
|
||||
void Folder::registerOne(not_null<History*> history) {
|
||||
|
@ -110,7 +147,6 @@ void Folder::registerOne(not_null<History*> history) {
|
|||
} else {
|
||||
updateChatListEntry();
|
||||
}
|
||||
applyChatListMessage(history->chatListMessage());
|
||||
reorderLastHistories();
|
||||
}
|
||||
|
||||
|
@ -118,9 +154,6 @@ void Folder::unregisterOne(not_null<History*> history) {
|
|||
if (_chatsList.empty()) {
|
||||
updateChatListExistence();
|
||||
}
|
||||
if (_chatListMessage && _chatListMessage->history() == history) {
|
||||
computeChatListMessage();
|
||||
}
|
||||
reorderLastHistories();
|
||||
}
|
||||
|
||||
|
@ -129,47 +162,11 @@ int Folder::chatListNameVersion() const {
|
|||
}
|
||||
|
||||
void Folder::oneListMessageChanged(HistoryItem *from, HistoryItem *to) {
|
||||
if (!applyChatListMessage(to) && _chatListMessage == from) {
|
||||
computeChatListMessage();
|
||||
}
|
||||
if (from || to) {
|
||||
reorderLastHistories();
|
||||
}
|
||||
}
|
||||
|
||||
bool Folder::applyChatListMessage(HistoryItem *item) {
|
||||
if (!item) {
|
||||
return false;
|
||||
} else if (_chatListMessage
|
||||
&& _chatListMessage->date() >= item->date()) {
|
||||
return false;
|
||||
}
|
||||
_chatListMessage = item;
|
||||
updateChatListEntry();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Folder::computeChatListMessage() {
|
||||
auto &&items = ranges::views::all(
|
||||
*_chatsList.indexed()
|
||||
) | ranges::views::filter([](not_null<Dialogs::Row*> row) {
|
||||
return row->entry()->chatListMessage() != nullptr;
|
||||
});
|
||||
const auto chatListDate = [](not_null<Dialogs::Row*> row) {
|
||||
return row->entry()->chatListMessage()->date();
|
||||
};
|
||||
const auto top = ranges::max_element(
|
||||
items,
|
||||
ranges::less(),
|
||||
chatListDate);
|
||||
if (top == items.end()) {
|
||||
_chatListMessage = nullptr;
|
||||
} else {
|
||||
_chatListMessage = (*top)->entry()->chatListMessage();
|
||||
}
|
||||
updateChatListEntry();
|
||||
}
|
||||
|
||||
void Folder::reorderLastHistories() {
|
||||
// We want first kShowChatNamesCount histories, by last message date.
|
||||
const auto pred = [](not_null<History*> a, not_null<History*> b) {
|
||||
|
@ -219,7 +216,7 @@ void Folder::loadUserpic() {
|
|||
|
||||
void Folder::paintUserpic(
|
||||
Painter &p,
|
||||
std::shared_ptr<Data::CloudImageView> &view,
|
||||
std::shared_ptr<CloudImageView> &view,
|
||||
const Dialogs::Ui::PaintContext &context) const {
|
||||
paintUserpic(
|
||||
p,
|
||||
|
@ -288,8 +285,16 @@ const std::vector<not_null<History*>> &Folder::lastHistories() const {
|
|||
return _lastHistories;
|
||||
}
|
||||
|
||||
uint32 Folder::chatListViewVersion() const {
|
||||
return _chatListViewVersion;
|
||||
void Folder::validateListEntryCache() {
|
||||
if (_listEntryCacheVersion == _chatListViewVersion) {
|
||||
return;
|
||||
}
|
||||
_listEntryCacheVersion = _chatListViewVersion;
|
||||
_listEntryCache.setMarkedText(
|
||||
st::dialogsTextStyle,
|
||||
ComposeFolderListEntryText(this),
|
||||
// Use rich options as long as the entry text does not have user text.
|
||||
Ui::ItemTextDefaultOptions());
|
||||
}
|
||||
|
||||
void Folder::requestChatListMessage() {
|
||||
|
@ -299,7 +304,7 @@ void Folder::requestChatListMessage() {
|
|||
}
|
||||
|
||||
TimeId Folder::adjustedChatListTimeId() const {
|
||||
return _chatListMessage ? _chatListMessage->date() : chatListTimeId();
|
||||
return chatListTimeId();
|
||||
}
|
||||
|
||||
void Folder::applyDialog(const MTPDdialogFolder &data) {
|
||||
|
@ -350,7 +355,7 @@ Dialogs::BadgesState Folder::chatListBadgesState() const {
|
|||
}
|
||||
|
||||
HistoryItem *Folder::chatListMessage() const {
|
||||
return _chatListMessage;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Folder::chatListMessageKnown() const {
|
||||
|
|
|
@ -69,12 +69,13 @@ public:
|
|||
const style::color &overrideFg) const;
|
||||
|
||||
const std::vector<not_null<History*>> &lastHistories() const;
|
||||
uint32 chatListViewVersion() const;
|
||||
void validateListEntryCache();
|
||||
[[nodiscard]] const Ui::Text::String &listEntryCache() const {
|
||||
return _listEntryCache;
|
||||
}
|
||||
|
||||
private:
|
||||
void indexNameParts();
|
||||
bool applyChatListMessage(HistoryItem *item);
|
||||
void computeChatListMessage();
|
||||
|
||||
int chatListNameVersion() const override;
|
||||
|
||||
|
@ -96,8 +97,10 @@ private:
|
|||
base::flat_set<QChar> _nameFirstLetters;
|
||||
|
||||
std::vector<not_null<History*>> _lastHistories;
|
||||
HistoryItem *_chatListMessage = nullptr;
|
||||
uint32 _chatListViewVersion = 0;
|
||||
|
||||
Ui::Text::String _listEntryCache;
|
||||
int _listEntryCacheVersion = 0;
|
||||
int _chatListViewVersion = 0;
|
||||
//rpl::variable<MessagePosition> _unreadPosition;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
|
|
@ -625,6 +625,10 @@ TextWithEntities ForumTopic::titleWithIcon() const {
|
|||
return ForumTopicIconWithTitle(_iconId, _title);
|
||||
}
|
||||
|
||||
int ForumTopic::titleVersion() const {
|
||||
return _titleVersion;
|
||||
}
|
||||
|
||||
void ForumTopic::applyTitle(const QString &title) {
|
||||
if (_title == title) {
|
||||
return;
|
||||
|
@ -647,6 +651,7 @@ void ForumTopic::applyIconId(DocumentId iconId) {
|
|||
return;
|
||||
}
|
||||
_iconId = iconId;
|
||||
++_titleVersion;
|
||||
_icon = iconId
|
||||
? std::make_unique<Ui::Text::LimitedLoopsEmoji>(
|
||||
owner().customEmojiManager().create(
|
||||
|
|
|
@ -114,6 +114,7 @@ public:
|
|||
|
||||
[[nodiscard]] QString title() const;
|
||||
[[nodiscard]] TextWithEntities titleWithIcon() const;
|
||||
[[nodiscard]] int titleVersion() const;
|
||||
void applyTitle(const QString &title);
|
||||
[[nodiscard]] DocumentId iconId() const;
|
||||
void applyIconId(DocumentId iconId);
|
||||
|
|
|
@ -18,6 +18,7 @@ DialogRow {
|
|||
textLeft: pixels;
|
||||
textTop: pixels;
|
||||
topicsSkip: pixels;
|
||||
topicsSkipBig: pixels;
|
||||
topicsHeight: pixels;
|
||||
}
|
||||
|
||||
|
@ -77,9 +78,18 @@ forumDialogRow: DialogRow(defaultDialogRow) {
|
|||
height: 80px;
|
||||
textTop: 32px;
|
||||
topicsSkip: 8px;
|
||||
topicsHeight: 20px;
|
||||
topicsSkipBig: 14px;
|
||||
topicsHeight: 21px;
|
||||
}
|
||||
|
||||
forumDialogJumpArrow: icon{{ "dialogs/dialogs_topic_arrow", dialogsTextFg }};
|
||||
forumDialogJumpArrowOver: icon{{ "dialogs/dialogs_topic_arrow", dialogsTextFgOver }};
|
||||
forumDialogJumpArrowSkip: 8px;
|
||||
forumDialogJumpArrowLeft: 3px;
|
||||
forumDialogJumpArrowTop: 3px;
|
||||
forumDialogJumpPadding: margins(8px, 3px, 8px, 3px);
|
||||
forumDialogJumpRadius: 11px;
|
||||
|
||||
dialogsOnlineBadgeStroke: 2px;
|
||||
dialogsOnlineBadgeSize: 10px;
|
||||
dialogsOnlineBadgeSkip: point(0px, 2px);
|
||||
|
@ -483,7 +493,3 @@ chooseTopicListItem: PeerListItem(defaultPeerListItem) {
|
|||
chooseTopicList: PeerList(defaultPeerList) {
|
||||
item: chooseTopicListItem;
|
||||
}
|
||||
|
||||
dialogsTopicArrow: icon{{ "dialogs/dialogs_topic_arrow", dialogsTextFgService }};
|
||||
dialogsTopicArrowSkip: 13px;
|
||||
dialogsTopicArrowTop: 4px;
|
||||
|
|
|
@ -514,22 +514,43 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
|
|||
Window::GifPauseReason::Any);
|
||||
auto fullWidth = width();
|
||||
auto dialogsClip = r;
|
||||
auto ms = crl::now();
|
||||
const auto ms = crl::now();
|
||||
const auto paintRow = [&](
|
||||
not_null<Row*> row,
|
||||
bool selected,
|
||||
bool mayBeActive) {
|
||||
const auto key = row->key();
|
||||
const auto active = mayBeActive && (activeEntry.key == key);
|
||||
const auto forum = key.history()
|
||||
&& key.history()->peer->isForum();
|
||||
if (forum && !_topicJumpCache) {
|
||||
_topicJumpCache = std::make_unique<Ui::TopicJumpCache>();
|
||||
}
|
||||
Ui::RowPainter::Paint(p, row, validateVideoUserpic(row), {
|
||||
.st = (forum ? &st::forumDialogRow : _st.get()),
|
||||
.topicJumpCache = _topicJumpCache.get(),
|
||||
.folder = _openedFolder,
|
||||
.forum = _openedForum,
|
||||
.filter = _filterId,
|
||||
.now = ms,
|
||||
.width = fullWidth,
|
||||
.active = active,
|
||||
.selected = (_menuRow.key
|
||||
? (row->key() == _menuRow.key)
|
||||
: selected),
|
||||
.paused = videoPaused,
|
||||
.narrow = (fullWidth < st::columnMinimalWidthLeft),
|
||||
});
|
||||
};
|
||||
if (_state == WidgetState::Default) {
|
||||
paintCollapsedRows(p, r);
|
||||
|
||||
const auto &list = _shownList->all();
|
||||
const auto shownBottom = _shownList->height() - skipTopHeight();
|
||||
const auto active = activeEntry.key;
|
||||
const auto selected = _menuRow.key
|
||||
? _menuRow.key
|
||||
: (isPressed()
|
||||
? (_pressed
|
||||
? _pressed->key()
|
||||
: Key())
|
||||
: (_selected
|
||||
? _selected->key()
|
||||
: Key()));
|
||||
const auto selected = isPressed()
|
||||
? (_pressed ? _pressed->key() : Key())
|
||||
: (_selected ? _selected->key() : Key());
|
||||
if (shownBottom) {
|
||||
const auto skip = dialogsOffset();
|
||||
const auto promoted = fixedOnTopCount();
|
||||
|
@ -559,23 +580,7 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
|
|||
if (xadd || yadd) {
|
||||
p.translate(xadd, yadd);
|
||||
}
|
||||
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 = (isForum ? &st::forumDialogRow : _st.get()),
|
||||
.folder = _openedFolder,
|
||||
.forum = _openedForum,
|
||||
.filter = _filterId,
|
||||
.now = ms,
|
||||
.width = fullWidth,
|
||||
.active = isActive,
|
||||
.selected = isSelected,
|
||||
.paused = videoPaused,
|
||||
.narrow = (fullWidth < st::columnMinimalWidthLeft),
|
||||
});
|
||||
paintRow(row, (row->key() == selected), true);
|
||||
if (xadd || yadd) {
|
||||
p.translate(-xadd, -yadd);
|
||||
}
|
||||
|
@ -677,29 +682,11 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
|
|||
int(_filterResults.size()));
|
||||
p.translate(0, filteredHeight(from));
|
||||
for (; from < to; ++from) {
|
||||
const auto selected = isPressed()
|
||||
? (from == _filteredPressed)
|
||||
: (from == _filteredSelected);
|
||||
const auto row = _filterResults[from].row;
|
||||
const auto key = row->key();
|
||||
const auto active = (activeEntry.key == key)
|
||||
&& !activeEntry.fullId;
|
||||
const auto selected = _menuRow.key
|
||||
? (key == _menuRow.key)
|
||||
: (from == (isPressed()
|
||||
? _filteredPressed
|
||||
: _filteredSelected));
|
||||
const auto isForum = key.history()
|
||||
&& key.history()->peer->isForum();
|
||||
Ui::RowPainter::Paint(p, row, validateVideoUserpic(row), {
|
||||
.st = (isForum ? &st::forumDialogRow : _st.get()),
|
||||
.folder = _openedFolder,
|
||||
.forum = _openedForum,
|
||||
.filter = _filterId,
|
||||
.now = ms,
|
||||
.width = fullWidth,
|
||||
.active = active,
|
||||
.selected = selected,
|
||||
.paused = videoPaused,
|
||||
.narrow = (fullWidth < st::columnMinimalWidthLeft),
|
||||
});
|
||||
paintRow(row, selected, !activeEntry.fullId);
|
||||
p.translate(0, row->height());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,6 +49,7 @@ namespace Dialogs::Ui {
|
|||
using namespace ::Ui;
|
||||
class VideoUserpic;
|
||||
struct PaintContext;
|
||||
struct TopicJumpCache;
|
||||
} // namespace Dialogs::Ui
|
||||
|
||||
namespace Dialogs {
|
||||
|
@ -397,6 +398,7 @@ private:
|
|||
|
||||
std::vector<std::unique_ptr<CollapsedRow>> _collapsedRows;
|
||||
not_null<const style::DialogRow*> _st;
|
||||
mutable std::unique_ptr<Ui::TopicJumpCache> _topicJumpCache;
|
||||
int _collapsedSelected = -1;
|
||||
int _collapsedPressed = -1;
|
||||
bool _skipTopDialog = false;
|
||||
|
|
|
@ -26,67 +26,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "styles/style_dialogs.h"
|
||||
|
||||
namespace Dialogs {
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] TextWithEntities ComposeFolderListEntryText(
|
||||
not_null<Data::Folder*> folder) {
|
||||
const auto &list = folder->lastHistories();
|
||||
if (list.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto count = std::max(
|
||||
int(list.size()),
|
||||
folder->chatsList()->fullSize().current());
|
||||
|
||||
const auto throwAwayLastName = (list.size() > 1)
|
||||
&& (count == list.size() + 1);
|
||||
auto &&peers = ranges::views::all(
|
||||
list
|
||||
) | ranges::views::take(
|
||||
list.size() - (throwAwayLastName ? 1 : 0)
|
||||
);
|
||||
const auto wrapName = [](not_null<History*> history) {
|
||||
const auto name = history->peer->name();
|
||||
return TextWithEntities{
|
||||
.text = name,
|
||||
.entities = (history->chatListBadgesState().unread
|
||||
? EntitiesInText{
|
||||
{ EntityType::Semibold, 0, int(name.size()), QString() },
|
||||
{ EntityType::PlainLink, 0, int(name.size()), QString() },
|
||||
}
|
||||
: EntitiesInText{}),
|
||||
};
|
||||
};
|
||||
const auto shown = int(peers.size());
|
||||
const auto accumulated = [&] {
|
||||
Expects(shown > 0);
|
||||
|
||||
auto i = peers.begin();
|
||||
auto result = wrapName(*i);
|
||||
for (++i; i != peers.end(); ++i) {
|
||||
result = tr::lng_archived_last_list(
|
||||
tr::now,
|
||||
lt_accumulated,
|
||||
result,
|
||||
lt_chat,
|
||||
wrapName(*i),
|
||||
Ui::Text::WithEntities);
|
||||
}
|
||||
return result;
|
||||
}();
|
||||
return (shown < count)
|
||||
? tr::lng_archived_last(
|
||||
tr::now,
|
||||
lt_count,
|
||||
(count - shown),
|
||||
lt_chats,
|
||||
accumulated,
|
||||
Ui::Text::WithEntities)
|
||||
: accumulated;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
BasicRow::BasicRow() = default;
|
||||
BasicRow::~BasicRow() = default;
|
||||
|
@ -160,31 +99,13 @@ uint64 Row::sortKey(FilterId filterId) const {
|
|||
return _id.entry()->sortKeyInChatList(filterId);
|
||||
}
|
||||
|
||||
void Row::validateListEntryCache() const {
|
||||
const auto folder = _id.folder();
|
||||
if (!folder) {
|
||||
return;
|
||||
}
|
||||
const auto version = folder->chatListViewVersion();
|
||||
if (_listEntryCacheVersion == version) {
|
||||
return;
|
||||
}
|
||||
_listEntryCacheVersion = version;
|
||||
_listEntryCache.setMarkedText(
|
||||
st::dialogsTextStyle,
|
||||
ComposeFolderListEntryText(folder),
|
||||
// Use rich options as long as the entry text does not have user text.
|
||||
Ui::ItemTextDefaultOptions());
|
||||
}
|
||||
|
||||
void Row::setCornerBadgeShown(
|
||||
bool shown,
|
||||
Fn<void()> updateCallback) const {
|
||||
const auto value = shown ? 1 : 0;
|
||||
if (_cornerBadgeShown == value) {
|
||||
if (_cornerBadgeShown == shown) {
|
||||
return;
|
||||
}
|
||||
_cornerBadgeShown = value;
|
||||
const_cast<Row*>(this)->_cornerBadgeShown = shown;
|
||||
if (_cornerBadgeUserpic && _cornerBadgeUserpic->animation.animating()) {
|
||||
_cornerBadgeUserpic->animation.change(
|
||||
_cornerBadgeShown ? 1. : 0.,
|
||||
|
|
|
@ -119,11 +119,6 @@ public:
|
|||
}
|
||||
[[nodiscard]] uint64 sortKey(FilterId filterId) const;
|
||||
|
||||
void validateListEntryCache() const;
|
||||
[[nodiscard]] const Ui::Text::String &listEntryCache() const {
|
||||
return _listEntryCache;
|
||||
}
|
||||
|
||||
// for any attached data, for example View in contacts list
|
||||
void *attached = nullptr;
|
||||
|
||||
|
@ -151,13 +146,11 @@ private:
|
|||
const Ui::PaintContext &context);
|
||||
|
||||
Key _id;
|
||||
mutable Ui::Text::String _listEntryCache;
|
||||
mutable std::unique_ptr<CornerBadgeUserpic> _cornerBadgeUserpic;
|
||||
int _top = 0;
|
||||
int _height = 0;
|
||||
int _index = 0;
|
||||
mutable int _listEntryCacheVersion : 31 = 0;
|
||||
mutable int _cornerBadgeShown : 1 = 0;
|
||||
bool _cornerBadgeShown = false;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -198,22 +198,22 @@ int PaintWideCounter(
|
|||
return availableWidth - used;
|
||||
}
|
||||
|
||||
void PaintListEntryText(
|
||||
void PaintFolderEntryText(
|
||||
Painter &p,
|
||||
not_null<const Row*> row,
|
||||
not_null<Data::Folder*> folder,
|
||||
const PaintContext &context,
|
||||
QRect rect) {
|
||||
if (rect.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
row->validateListEntryCache();
|
||||
folder->validateListEntryCache();
|
||||
p.setFont(st::dialogsTextFont);
|
||||
p.setPen(context.active
|
||||
? st::dialogsTextFgActive
|
||||
: context.selected
|
||||
? st::dialogsTextFgOver
|
||||
: st::dialogsTextFg);
|
||||
row->listEntryCache().draw(p, {
|
||||
folder->listEntryCache().draw(p, {
|
||||
.position = rect.topLeft(),
|
||||
.availableWidth = rect.width(),
|
||||
.palette = &(context.active
|
||||
|
@ -342,7 +342,14 @@ void PaintRow(
|
|||
}
|
||||
}
|
||||
auto texttop = context.st->textTop;
|
||||
if (promoted && !history->topPromotionMessage().isEmpty()) {
|
||||
if (const auto folder = entry->asFolder()) {
|
||||
const auto rect = QRect(
|
||||
nameleft,
|
||||
texttop,
|
||||
namewidth,
|
||||
st::dialogsTextFont->height);
|
||||
PaintFolderEntryText(p, folder, context, rect);
|
||||
} else if (promoted && !history->topPromotionMessage().isEmpty()) {
|
||||
auto availableWidth = namewidth;
|
||||
p.setFont(st::dialogsTextFont);
|
||||
if (history->cloudDraftTextCache().isEmpty()) {
|
||||
|
@ -911,21 +918,19 @@ void RowPainter::Paint(
|
|||
: thread
|
||||
? &thread->lastItemDialogsView()
|
||||
: nullptr;
|
||||
if (const auto folder = row->folder()) {
|
||||
PaintListEntryText(p, row, context, rect);
|
||||
} else if (view) {
|
||||
if (!view->prepared(item)) {
|
||||
if (view) {
|
||||
const auto forum = context.st->topicsHeight
|
||||
? row->history()->peer->forum()
|
||||
: nullptr;
|
||||
if (!view->prepared(item, forum)) {
|
||||
view->prepare(
|
||||
item,
|
||||
forum,
|
||||
[=] { 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());
|
||||
if (forum) {
|
||||
rect.setHeight(context.st->topicsHeight + rect.height());
|
||||
}
|
||||
view->paint(p, rect, context);
|
||||
}
|
||||
|
@ -1015,10 +1020,10 @@ void RowPainter::Paint(
|
|||
availableWidth,
|
||||
st::dialogsTextFont->height);
|
||||
auto &view = row->itemView();
|
||||
if (!view.prepared(item)) {
|
||||
view.prepare(item, row->repaint(), previewOptions);
|
||||
if (!view.prepared(item, nullptr)) {
|
||||
view.prepare(item, nullptr, row->repaint(), previewOptions);
|
||||
}
|
||||
row->itemView().paint(p, itemRect, context);
|
||||
view.paint(p, itemRect, context);
|
||||
};
|
||||
const auto showSavedMessages = history
|
||||
&& history->peer->isSelf()
|
||||
|
|
|
@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include "ui/cached_round_corners.h"
|
||||
|
||||
namespace style {
|
||||
struct DialogRow;
|
||||
} // namespace style
|
||||
|
@ -35,8 +37,23 @@ using namespace ::Ui;
|
|||
|
||||
class VideoUserpic;
|
||||
|
||||
struct TopicJumpCorners {
|
||||
Ui::CornersPixmaps normal;
|
||||
Ui::CornersPixmaps inverted;
|
||||
QPixmap small;
|
||||
int invertedRadius = 0;
|
||||
int smallKey = 0; // = `-radius` if top right else `radius`.
|
||||
};
|
||||
|
||||
struct TopicJumpCache {
|
||||
TopicJumpCorners corners;
|
||||
TopicJumpCorners over;
|
||||
TopicJumpCorners rippleMask;
|
||||
};
|
||||
|
||||
struct PaintContext {
|
||||
not_null<const style::DialogRow*> st;
|
||||
TopicJumpCache *topicJumpCache = nullptr;
|
||||
Data::Folder *folder = nullptr;
|
||||
Data::Forum *forum = nullptr;
|
||||
FilterId filter = 0;
|
||||
|
|
|
@ -127,15 +127,32 @@ bool MessageView::dependsOn(not_null<const HistoryItem*> item) const {
|
|||
return (_textCachedFor == item.get());
|
||||
}
|
||||
|
||||
bool MessageView::prepared(not_null<const HistoryItem*> item) const {
|
||||
return (_textCachedFor == item.get());
|
||||
bool MessageView::prepared(
|
||||
not_null<const HistoryItem*> item,
|
||||
Data::Forum *forum) const {
|
||||
return (_textCachedFor == item.get())
|
||||
&& (!forum
|
||||
|| (_topics
|
||||
&& _topics->forum() == forum
|
||||
&& _topics->prepared()));
|
||||
}
|
||||
|
||||
void MessageView::prepare(
|
||||
not_null<const HistoryItem*> item,
|
||||
Data::Forum *forum,
|
||||
Fn<void()> customEmojiRepaint,
|
||||
ToPreviewOptions options) {
|
||||
const auto validateTopics = !options.ignoreTopic;
|
||||
if (!forum) {
|
||||
_topics = nullptr;
|
||||
} else if (!_topics || _topics->forum() != forum) {
|
||||
_topics = std::make_unique<TopicsView>(forum);
|
||||
_topics->prepare(item->topicRootId(), customEmojiRepaint);
|
||||
} else if (!_topics->prepared()) {
|
||||
_topics->prepare(item->topicRootId(), customEmojiRepaint);
|
||||
}
|
||||
if (_textCachedFor == item.get()) {
|
||||
return;
|
||||
}
|
||||
options.existing = &_imagesCache;
|
||||
options.ignoreTopic = true;
|
||||
auto preview = item->toPreview(options);
|
||||
|
@ -183,17 +200,21 @@ 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);
|
||||
int MessageView::countWidth() const {
|
||||
auto result = 0;
|
||||
if (!_senderCache.isEmpty()) {
|
||||
result += _senderCache.maxWidth();
|
||||
if (!_imagesCache.empty()) {
|
||||
result += st::dialogsMiniPreviewSkip
|
||||
+ st::dialogsMiniPreviewRight;
|
||||
}
|
||||
}
|
||||
_topics->prepare(
|
||||
geometry,
|
||||
&st::forumDialogRow,
|
||||
std::move(customEmojiRepaint));
|
||||
if (!_imagesCache.empty()) {
|
||||
result += (_imagesCache.size()
|
||||
* (st::dialogsMiniPreview + st::dialogsMiniPreviewSkip))
|
||||
+ st::dialogsMiniPreviewRight;
|
||||
}
|
||||
return result + _textCache.maxWidth();
|
||||
}
|
||||
|
||||
void MessageView::paint(
|
||||
|
@ -223,11 +244,22 @@ void MessageView::paint(
|
|||
: st::dialogsTextPalette));
|
||||
|
||||
auto rect = geometry;
|
||||
const auto checkJump = withTopic && !context.active;
|
||||
const auto jump1 = checkJump ? _topics->jumpToTopicWidth() : 0;
|
||||
if (jump1) {
|
||||
paintJumpToLast(p, rect, context, jump1);
|
||||
}
|
||||
|
||||
if (withTopic) {
|
||||
_topics->paint(p, rect, context);
|
||||
rect.setTop(rect.top() + context.st->topicsHeight);
|
||||
}
|
||||
|
||||
auto finalRight = rect.x() + rect.width();
|
||||
if (jump1) {
|
||||
rect.setWidth(rect.width() - st::forumDialogJumpArrowSkip);
|
||||
finalRight -= st::forumDialogJumpArrowSkip;
|
||||
}
|
||||
const auto lines = rect.height() / st::dialogsTextFont->height;
|
||||
if (!_senderCache.isEmpty()) {
|
||||
_senderCache.draw(p, {
|
||||
|
@ -258,20 +290,143 @@ void MessageView::paint(
|
|||
if (!_imagesCache.empty()) {
|
||||
rect.setLeft(rect.x() + st::dialogsMiniPreviewRight);
|
||||
}
|
||||
if (rect.isEmpty()) {
|
||||
if (!rect.isEmpty()) {
|
||||
_textCache.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() + _textCache.maxWidth());
|
||||
}
|
||||
if (jump1) {
|
||||
const auto x = (rect.width() > st::forumDialogJumpArrowSkip)
|
||||
? rect.x()
|
||||
: finalRight;
|
||||
const auto add = st::forumDialogJumpArrowLeft;
|
||||
const auto y = rect.y() + st::forumDialogJumpArrowTop;
|
||||
(context.selected
|
||||
? st::forumDialogJumpArrowOver
|
||||
: st::forumDialogJumpArrow).paint(p, x + add, y, context.width);
|
||||
}
|
||||
}
|
||||
|
||||
void MessageView::paintJumpToLast(
|
||||
Painter &p,
|
||||
const QRect &rect,
|
||||
const PaintContext &context,
|
||||
int width1) const {
|
||||
if (!context.topicJumpCache) {
|
||||
return;
|
||||
}
|
||||
_textCache.draw(p, {
|
||||
.position = rect.topLeft(),
|
||||
.availableWidth = rect.width(),
|
||||
.palette = palette,
|
||||
.spoiler = Text::DefaultSpoilerCache(),
|
||||
.now = context.now,
|
||||
.paused = context.paused,
|
||||
.elisionLines = lines,
|
||||
FillJumpToLastBg(p, {
|
||||
.st = context.st,
|
||||
.corners = (context.selected
|
||||
? &context.topicJumpCache->over
|
||||
: &context.topicJumpCache->corners),
|
||||
.geometry = rect,
|
||||
.bg = (context.selected
|
||||
? st::dialogsRippleBg
|
||||
: st::dialogsBgOver),
|
||||
.width1 = width1,
|
||||
.width2 = countWidth() + st::forumDialogJumpArrowSkip,
|
||||
});
|
||||
}
|
||||
|
||||
void FillJumpToLastBg(QPainter &p, JumpToLastBg context) {
|
||||
const auto availableWidth = context.geometry.width();
|
||||
const auto use1 = std::min(context.width1, availableWidth);
|
||||
const auto use2 = std::min(context.width2, availableWidth);
|
||||
const auto padding = st::forumDialogJumpPadding;
|
||||
const auto radius = st::forumDialogJumpRadius;
|
||||
const auto &bg = context.bg;
|
||||
auto &normal = context.corners->normal;
|
||||
auto &inverted = context.corners->inverted;
|
||||
auto &small = context.corners->small;
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(bg);
|
||||
const auto origin = context.geometry.topLeft();
|
||||
const auto delta = std::abs(use1 - use2);
|
||||
if (delta <= context.st->topicsSkip / 2) {
|
||||
if (normal.p[0].isNull()) {
|
||||
normal = Ui::PrepareCornerPixmaps(radius, bg);
|
||||
}
|
||||
const auto w = std::max(use1, use2);
|
||||
const auto h = context.st->topicsHeight + st::normalFont->height;
|
||||
const auto fill = QRect(origin, QSize(w, h));
|
||||
Ui::FillRoundRect(p, fill.marginsAdded(padding), bg, normal);
|
||||
} else {
|
||||
const auto h1 = context.st->topicsHeight;
|
||||
const auto h2 = st::normalFont->height;
|
||||
const auto hmin = std::min(h1, h2);
|
||||
const auto wantedInvertedRadius = hmin - radius;
|
||||
const auto invertedr = std::min(wantedInvertedRadius, delta / 2);
|
||||
const auto smallr = std::min(radius, delta - invertedr);
|
||||
const auto smallkey = (use1 < use2) ? smallr : (-smallr);
|
||||
if (normal.p[0].isNull()) {
|
||||
normal = Ui::PrepareCornerPixmaps(radius, bg);
|
||||
}
|
||||
if (inverted.p[0].isNull()
|
||||
|| context.corners->invertedRadius != invertedr) {
|
||||
context.corners->invertedRadius = invertedr;
|
||||
inverted = Ui::PrepareInvertedCornerPixmaps(invertedr, bg);
|
||||
}
|
||||
if (smallr != radius
|
||||
&& (small.isNull() || context.corners->smallKey != smallkey)) {
|
||||
context.corners->smallKey = smallr;
|
||||
auto pixmaps = Ui::PrepareCornerPixmaps(smallr, bg);
|
||||
small = pixmaps.p[(use1 < use2) ? 1 : 3];
|
||||
}
|
||||
const auto rect1 = QRect(origin, QSize(use1, h1));
|
||||
auto no1 = normal;
|
||||
no1.p[2] = QPixmap();
|
||||
if (use1 < use2) {
|
||||
no1.p[3] = QPixmap();
|
||||
} else if (smallr != radius) {
|
||||
no1.p[3] = small;
|
||||
}
|
||||
auto fill1 = rect1.marginsAdded({
|
||||
padding.left(),
|
||||
padding.top(),
|
||||
padding.right(),
|
||||
(use1 < use2 ? -padding.top() : padding.bottom()),
|
||||
});
|
||||
Ui::FillRoundRect(p, fill1, bg, no1);
|
||||
if (use1 < use2) {
|
||||
p.drawPixmap(
|
||||
fill1.x() + fill1.width(),
|
||||
fill1.y() + fill1.height() - invertedr,
|
||||
inverted.p[3]);
|
||||
}
|
||||
const auto add = QPoint(0, h1);
|
||||
const auto rect2 = QRect(origin + add, QSize(use2, h2));
|
||||
const auto fill2 = rect2.marginsAdded({
|
||||
padding.left(),
|
||||
(use2 < use1 ? -padding.bottom() : padding.top()),
|
||||
padding.right(),
|
||||
padding.bottom(),
|
||||
});
|
||||
auto no2 = normal;
|
||||
no2.p[0] = QPixmap();
|
||||
if (use2 < use1) {
|
||||
no2.p[1] = QPixmap();
|
||||
} else if (smallr != radius) {
|
||||
no2.p[1] = small;
|
||||
}
|
||||
Ui::FillRoundRect(p, fill2, bg, no2);
|
||||
if (use2 < use1) {
|
||||
p.drawPixmap(
|
||||
fill2.x() + fill2.width(),
|
||||
fill2.y(),
|
||||
inverted.p[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HistoryView::ItemPreview PreviewWithSender(
|
||||
HistoryView::ItemPreview &&preview,
|
||||
const QString &sender,
|
||||
|
|
|
@ -13,6 +13,10 @@ class Image;
|
|||
class HistoryItem;
|
||||
enum class ImageRoundRadius;
|
||||
|
||||
namespace style {
|
||||
struct DialogRow;
|
||||
} // namespace style
|
||||
|
||||
namespace Ui {
|
||||
} // namespace Ui
|
||||
|
||||
|
@ -32,6 +36,7 @@ using namespace ::Ui;
|
|||
|
||||
struct PaintContext;
|
||||
class TopicsView;
|
||||
struct TopicJumpCorners;
|
||||
|
||||
[[nodiscard]] TextWithEntities DialogsPreviewText(TextWithEntities text);
|
||||
|
||||
|
@ -47,17 +52,15 @@ public:
|
|||
void itemInvalidated(not_null<const HistoryItem*> item);
|
||||
[[nodiscard]] bool dependsOn(not_null<const HistoryItem*> item) const;
|
||||
|
||||
[[nodiscard]] bool prepared(not_null<const HistoryItem*> item) const;
|
||||
[[nodiscard]] bool prepared(
|
||||
not_null<const HistoryItem*> item,
|
||||
Data::Forum *forum) const;
|
||||
void prepare(
|
||||
not_null<const HistoryItem*> item,
|
||||
Data::Forum *forum,
|
||||
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,
|
||||
|
@ -66,6 +69,13 @@ public:
|
|||
private:
|
||||
struct LoadingContext;
|
||||
|
||||
[[nodiscard]] int countWidth() const;
|
||||
void paintJumpToLast(
|
||||
Painter &p,
|
||||
const QRect &rect,
|
||||
const PaintContext &context,
|
||||
int width1) const;
|
||||
|
||||
mutable const HistoryItem *_textCachedFor = nullptr;
|
||||
mutable Text::String _senderCache;
|
||||
mutable std::unique_ptr<TopicsView> _topics;
|
||||
|
@ -80,4 +90,14 @@ private:
|
|||
const QString &sender,
|
||||
TextWithEntities topic);
|
||||
|
||||
struct JumpToLastBg {
|
||||
not_null<const style::DialogRow*> st;
|
||||
not_null<TopicJumpCorners*> corners;
|
||||
QRect geometry;
|
||||
const style::color &bg;
|
||||
int width1 = 0;
|
||||
int width2 = 0;
|
||||
};
|
||||
void FillJumpToLastBg(QPainter &p, JumpToLastBg context);
|
||||
|
||||
} // namespace Dialogs::Ui
|
||||
|
|
|
@ -29,47 +29,76 @@ TopicsView::TopicsView(not_null<Data::Forum*> forum)
|
|||
|
||||
TopicsView::~TopicsView() = default;
|
||||
|
||||
void TopicsView::prepare(
|
||||
const QRect &geometry,
|
||||
not_null<const style::DialogRow*> st,
|
||||
Fn<void()> customEmojiRepaint) {
|
||||
bool TopicsView::prepared() const {
|
||||
return (_version == _forum->recentTopicsListVersion());
|
||||
}
|
||||
|
||||
void TopicsView::prepare(MsgId frontRootId, Fn<void()> customEmojiRepaint) {
|
||||
const auto &list = _forum->recentTopics();
|
||||
_version = _forum->recentTopicsListVersion();
|
||||
_titles.reserve(list.size());
|
||||
auto index = 0;
|
||||
auto available = geometry.width();
|
||||
for (const auto &topic : _forum->recentTopics()) {
|
||||
if (available <= 0) {
|
||||
break;
|
||||
} else if (_titles.size() == index) {
|
||||
for (const auto &topic : list) {
|
||||
const auto from = begin(_titles) + index;
|
||||
const auto rootId = topic->rootId();
|
||||
const auto i = ranges::find(
|
||||
from,
|
||||
end(_titles),
|
||||
rootId,
|
||||
&Title::topicRootId);
|
||||
if (i != end(_titles)) {
|
||||
if (i != from) {
|
||||
ranges::rotate(from, i, i + 1);
|
||||
}
|
||||
} else if (index >= _titles.size()) {
|
||||
_titles.emplace_back();
|
||||
}
|
||||
auto &title = _titles[index];
|
||||
const auto rootId = topic->rootId();
|
||||
auto &title = _titles[index++];
|
||||
title.topicRootId = 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;
|
||||
if (title.unread == unread
|
||||
&& title.version == topic->titleVersion()) {
|
||||
continue;
|
||||
}
|
||||
available -= title.title.maxWidth() + st->topicsSkip;
|
||||
++index;
|
||||
const auto context = Core::MarkedTextContext{
|
||||
.session = &topic->session(),
|
||||
.customEmojiRepaint = customEmojiRepaint,
|
||||
.customEmojiLoopLimit = kIconLoopCount,
|
||||
};
|
||||
auto topicTitle = topic->titleWithIcon();
|
||||
title.version = topic->titleVersion();
|
||||
title.unread = unread;
|
||||
title.title.setMarkedText(
|
||||
st::dialogsTextStyle,
|
||||
(unread
|
||||
? Ui::Text::PlainLink(
|
||||
Ui::Text::Wrapped(
|
||||
std::move(topicTitle),
|
||||
EntityType::Bold))
|
||||
: std::move(topicTitle)),
|
||||
DialogTextOptions(),
|
||||
context);
|
||||
}
|
||||
while (_titles.size() > index) {
|
||||
_titles.pop_back();
|
||||
}
|
||||
const auto i = frontRootId
|
||||
? ranges::find(_titles, frontRootId, &Title::topicRootId)
|
||||
: end(_titles);
|
||||
_jumpToTopic = (i != end(_titles));
|
||||
if (_jumpToTopic) {
|
||||
if (i != begin(_titles)) {
|
||||
ranges::rotate(begin(_titles), i, i + 1);
|
||||
}
|
||||
if (!_titles.front().unread) {
|
||||
_jumpToTopic = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int TopicsView::jumpToTopicWidth() const {
|
||||
return _jumpToTopic ? _titles.front().title.maxWidth() : 0;
|
||||
}
|
||||
|
||||
void TopicsView::paint(
|
||||
|
@ -91,6 +120,7 @@ void TopicsView::paint(
|
|||
: st::dialogsTextPaletteArchive);
|
||||
auto index = 0;
|
||||
auto rect = geometry;
|
||||
auto skipBig = _jumpToTopic && !context.active;
|
||||
for (const auto &title : _titles) {
|
||||
if (rect.width() <= 0) {
|
||||
break;
|
||||
|
@ -104,8 +134,11 @@ void TopicsView::paint(
|
|||
.paused = context.paused,
|
||||
.elisionLines = 1,
|
||||
});
|
||||
rect.setLeft(
|
||||
rect.left() + title.title.maxWidth() + context.st->topicsSkip);
|
||||
const auto skip = skipBig
|
||||
? context.st->topicsSkipBig
|
||||
: context.st->topicsSkip;
|
||||
rect.setLeft(rect.left() + title.title.maxWidth() + skip);
|
||||
skipBig = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,10 +36,10 @@ public:
|
|||
return _forum;
|
||||
}
|
||||
|
||||
void prepare(
|
||||
const QRect &geometry,
|
||||
not_null<const style::DialogRow*> st,
|
||||
Fn<void()> customEmojiRepaint);
|
||||
[[nodiscard]] bool prepared() const;
|
||||
void prepare(MsgId frontRootId, Fn<void()> customEmojiRepaint);
|
||||
|
||||
[[nodiscard]] int jumpToTopicWidth() const;
|
||||
|
||||
void paint(
|
||||
Painter &p,
|
||||
|
@ -54,11 +54,14 @@ private:
|
|||
struct Title {
|
||||
Text::String title;
|
||||
MsgId topicRootId = 0;
|
||||
int version = -1;
|
||||
bool unread = false;
|
||||
};
|
||||
const not_null<Data::Forum*> _forum;
|
||||
|
||||
mutable std::vector<Title> _titles;
|
||||
int _version = -1;
|
||||
bool _jumpToTopic = false;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
|
|
|
@ -220,7 +220,7 @@ const CornersPixmaps &CachedCornerPixmaps(CachedRoundCorners index) {
|
|||
return Corners[index];
|
||||
}
|
||||
|
||||
CornersPixmaps PrepareCornerPixmaps(int32 radius, style::color bg, const style::color *sh) {
|
||||
CornersPixmaps PrepareCornerPixmaps(int radius, style::color bg, const style::color *sh) {
|
||||
auto images = PrepareCorners(radius, bg, sh);
|
||||
auto result = CornersPixmaps();
|
||||
for (int j = 0; j < 4; ++j) {
|
||||
|
@ -240,6 +240,24 @@ CornersPixmaps PrepareCornerPixmaps(ImageRoundRadius radius, style::color bg, co
|
|||
Unexpected("Image round radius in PrepareCornerPixmaps.");
|
||||
}
|
||||
|
||||
CornersPixmaps PrepareInvertedCornerPixmaps(int radius, style::color bg) {
|
||||
const auto size = radius * style::DevicePixelRatio();
|
||||
auto circle = style::colorizeImage(
|
||||
style::createInvertedCircleMask(radius * 2),
|
||||
bg);
|
||||
circle.setDevicePixelRatio(style::DevicePixelRatio());
|
||||
auto result = CornersPixmaps();
|
||||
const auto fill = [&](int index, int xoffset, int yoffset) {
|
||||
result.p[index] = PixmapFromImage(
|
||||
circle.copy(QRect(xoffset, yoffset, size, size)));
|
||||
};
|
||||
fill(0, 0, 0);
|
||||
fill(1, size, 0);
|
||||
fill(2, size, size);
|
||||
fill(3, 0, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] int CachedCornerRadiusValue(CachedCornerRadius tag) {
|
||||
using Radius = CachedCornerRadius;
|
||||
switch (tag) {
|
||||
|
|
|
@ -36,25 +36,28 @@ enum CachedRoundCorners : int {
|
|||
RoundCornersCount
|
||||
};
|
||||
|
||||
void FillRoundRect(QPainter &p, int32 x, int32 y, int32 w, int32 h, style::color bg, CachedRoundCorners index);
|
||||
void FillRoundRect(QPainter &p, int x, int y, int w, int h, style::color bg, CachedRoundCorners index);
|
||||
inline void FillRoundRect(QPainter &p, const QRect &rect, style::color bg, CachedRoundCorners index) {
|
||||
FillRoundRect(p, rect.x(), rect.y(), rect.width(), rect.height(), bg, index);
|
||||
}
|
||||
|
||||
[[nodiscard]] const CornersPixmaps &CachedCornerPixmaps(CachedRoundCorners index);
|
||||
[[nodiscard]] CornersPixmaps PrepareCornerPixmaps(
|
||||
int32 radius,
|
||||
int radius,
|
||||
style::color bg,
|
||||
const style::color *sh = nullptr);
|
||||
[[nodiscard]] CornersPixmaps PrepareCornerPixmaps(
|
||||
ImageRoundRadius radius,
|
||||
style::color bg,
|
||||
const style::color *sh = nullptr);
|
||||
void FillRoundRect(QPainter &p, int32 x, int32 y, int32 w, int32 h, style::color bg, const CornersPixmaps &corners);
|
||||
[[nodiscard]] CornersPixmaps PrepareInvertedCornerPixmaps(
|
||||
int radius,
|
||||
style::color bg);
|
||||
void FillRoundRect(QPainter &p, int x, int y, int w, int h, style::color bg, const CornersPixmaps &corners);
|
||||
inline void FillRoundRect(QPainter &p, const QRect &rect, style::color bg, const CornersPixmaps &corners) {
|
||||
return FillRoundRect(p, rect.x(), rect.y(), rect.width(), rect.height(), bg, corners);
|
||||
}
|
||||
void FillRoundShadow(QPainter &p, int32 x, int32 y, int32 w, int32 h, style::color shadow, const CornersPixmaps &corners);
|
||||
void FillRoundShadow(QPainter &p, int x, int y, int w, int h, style::color shadow, const CornersPixmaps &corners);
|
||||
inline void FillRoundShadow(QPainter &p, const QRect &rect, style::color shadow, const CornersPixmaps &corners) {
|
||||
FillRoundShadow(p, rect.x(), rect.y(), rect.width(), rect.height(), shadow, corners);
|
||||
}
|
||||
|
|
|
@ -480,20 +480,9 @@ const CornersPixmaps &ChatStyle::serviceBgCornersNormal() const {
|
|||
|
||||
const CornersPixmaps &ChatStyle::serviceBgCornersInverted() const {
|
||||
if (_serviceBgCornersInverted.p[0].isNull()) {
|
||||
const auto radius = HistoryServiceMsgInvertedRadius();
|
||||
const auto size = radius * style::DevicePixelRatio();
|
||||
auto circle = style::colorizeImage(
|
||||
style::createInvertedCircleMask(radius * 2),
|
||||
_serviceBgCornersInverted = Ui::PrepareInvertedCornerPixmaps(
|
||||
HistoryServiceMsgInvertedRadius(),
|
||||
msgServiceBg());
|
||||
circle.setDevicePixelRatio(style::DevicePixelRatio());
|
||||
const auto fill = [&](int index, int xoffset, int yoffset) {
|
||||
_serviceBgCornersInverted.p[index] = PixmapFromImage(
|
||||
circle.copy(QRect(xoffset, yoffset, size, size)));
|
||||
};
|
||||
fill(0, 0, 0);
|
||||
fill(1, size, 0);
|
||||
fill(2, size, size);
|
||||
fill(3, 0, size);
|
||||
}
|
||||
return _serviceBgCornersInverted;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue