mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-03-11 08:48:14 +00:00
Support dialog rows with variable height.
This commit is contained in:
parent
248337daf5
commit
37308cde21
@ -386,7 +386,7 @@ void ApiWrap::checkChatInvite(
|
||||
|
||||
void ApiWrap::savePinnedOrder(Data::Folder *folder) {
|
||||
const auto &order = _session->data().pinnedChatsOrder(folder);
|
||||
const auto input = [](const Dialogs::Key &key) {
|
||||
const auto input = [](Dialogs::Key key) {
|
||||
if (const auto history = key.history()) {
|
||||
return MTP_inputDialogPeer(history->peer->input);
|
||||
} else if (const auto folder = key.folder()) {
|
||||
@ -409,7 +409,7 @@ void ApiWrap::savePinnedOrder(Data::Folder *folder) {
|
||||
|
||||
void ApiWrap::savePinnedOrder(not_null<Data::Forum*> forum) {
|
||||
const auto &order = _session->data().pinnedChatsOrder(forum);
|
||||
const auto input = [](const Dialogs::Key &key) {
|
||||
const auto input = [](Dialogs::Key key) {
|
||||
if (const auto topic = key.topic()) {
|
||||
return MTP_int(topic->rootId().bare);
|
||||
}
|
||||
|
@ -795,7 +795,9 @@ ShareBox::Inner::Chat *ShareBox::Inner::getChatAtIndex(int index) {
|
||||
}
|
||||
const auto row = [=] {
|
||||
if (_filter.isEmpty()) {
|
||||
return _chatsIndexed->rowAtY(index, 1);
|
||||
return (index < _chatsIndexed->size())
|
||||
? (_chatsIndexed->begin() + index)->get()
|
||||
: nullptr;
|
||||
}
|
||||
return (index < _filtered.size())
|
||||
? _filtered[index].get()
|
||||
@ -865,9 +867,11 @@ void ShareBox::Inner::loadProfilePhotos(int yFrom) {
|
||||
|
||||
if (_filter.isEmpty()) {
|
||||
if (!_chatsIndexed->empty()) {
|
||||
auto i = _chatsIndexed->cfind(yFrom, _rowHeight);
|
||||
const auto index = yFrom / _rowHeight;
|
||||
auto i = _chatsIndexed->begin()
|
||||
+ std::min(index, _chatsIndexed->size());;
|
||||
for (auto end = _chatsIndexed->cend(); i != end; ++i) {
|
||||
if (((*i)->pos() * _rowHeight) >= yTo) {
|
||||
if (((*i)->index() * _rowHeight) >= yTo) {
|
||||
break;
|
||||
}
|
||||
(*i)->entry()->loadUserpic();
|
||||
@ -973,7 +977,8 @@ void ShareBox::Inner::paintEvent(QPaintEvent *e) {
|
||||
auto indexTo = rowTo * _columnCount;
|
||||
if (_filter.isEmpty()) {
|
||||
if (!_chatsIndexed->empty()) {
|
||||
auto i = _chatsIndexed->cfind(indexFrom, 1);
|
||||
auto i = _chatsIndexed->begin()
|
||||
+ std::min(indexFrom, _chatsIndexed->size());
|
||||
for (auto end = _chatsIndexed->cend(); i != end; ++i) {
|
||||
if (indexFrom >= indexTo) {
|
||||
break;
|
||||
|
@ -1381,8 +1381,8 @@ void Session::setupUserIsContactViewer() {
|
||||
_contactsNoChatsList.addByName(history);
|
||||
}
|
||||
} else if (const auto history = historyLoaded(user)) {
|
||||
_contactsNoChatsList.del(history);
|
||||
_contactsList.del(history);
|
||||
_contactsNoChatsList.remove(history);
|
||||
_contactsList.remove(history);
|
||||
}
|
||||
}, _lifetime);
|
||||
}
|
||||
@ -1915,7 +1915,7 @@ MessageIdsList Session::itemOrItsGroup(not_null<HistoryItem*> item) const {
|
||||
}
|
||||
|
||||
void Session::setChatPinned(
|
||||
const Dialogs::Key &key,
|
||||
Dialogs::Key key,
|
||||
FilterId filterId,
|
||||
bool pinned) {
|
||||
Expects(key.entry()->folderKnown());
|
||||
@ -1927,7 +1927,7 @@ void Session::setChatPinned(
|
||||
notifyPinnedDialogsOrderUpdated();
|
||||
}
|
||||
|
||||
void Session::setPinnedFromEntryList(const Dialogs::Key &key, bool pinned) {
|
||||
void Session::setPinnedFromEntryList(Dialogs::Key key, bool pinned) {
|
||||
Expects(key.entry()->folderKnown());
|
||||
|
||||
const auto list = chatsListFor(key.entry())->pinned();
|
||||
@ -2109,8 +2109,8 @@ void Session::clearPinnedChats(Data::Folder *folder) {
|
||||
|
||||
void Session::reorderTwoPinnedChats(
|
||||
FilterId filterId,
|
||||
const Dialogs::Key &key1,
|
||||
const Dialogs::Key &key2) {
|
||||
Dialogs::Key key1,
|
||||
Dialogs::Key key2) {
|
||||
Expects(key1.entry()->folderKnown() && key2.entry()->folderKnown());
|
||||
Expects(filterId || (key1.entry()->folder() == key2.entry()->folder()));
|
||||
|
||||
@ -2510,14 +2510,14 @@ bool Session::unreadBadgeMuted() const {
|
||||
return computeUnreadBadgeMuted(_chatsList.unreadState());
|
||||
}
|
||||
|
||||
int Session::unreadBadgeIgnoreOne(const Dialogs::Key &key) const {
|
||||
int Session::unreadBadgeIgnoreOne(Dialogs::Key key) const {
|
||||
const auto remove = (key && key.entry()->inChatList())
|
||||
? key.entry()->chatListUnreadState()
|
||||
: Dialogs::UnreadState();
|
||||
return computeUnreadBadge(_chatsList.unreadState() - remove);
|
||||
}
|
||||
|
||||
bool Session::unreadBadgeMutedIgnoreOne(const Dialogs::Key &key) const {
|
||||
bool Session::unreadBadgeMutedIgnoreOne(Dialogs::Key key) const {
|
||||
if (!Core::App().settings().includeMutedCounter()) {
|
||||
return false;
|
||||
}
|
||||
@ -3997,7 +3997,7 @@ void Session::refreshChatListEntry(Dialogs::Key key) {
|
||||
return;
|
||||
} else if (event.existenceChanged) {
|
||||
const auto mainRow = entry->addToChatList(0, mainList);
|
||||
_contactsNoChatsList.del(key, mainRow);
|
||||
_contactsNoChatsList.remove(key, mainRow);
|
||||
} else {
|
||||
event.moved = entry->adjustByPosInChatList(0, mainList);
|
||||
}
|
||||
|
@ -363,11 +363,8 @@ public:
|
||||
not_null<Forum*> forum) const;
|
||||
[[nodiscard]] const std::vector<Dialogs::Key> &pinnedChatsOrder(
|
||||
FilterId filterId) const;
|
||||
void setChatPinned(
|
||||
const Dialogs::Key &key,
|
||||
FilterId filterId,
|
||||
bool pinned);
|
||||
void setPinnedFromEntryList(const Dialogs::Key &key, bool pinned);
|
||||
void setChatPinned(Dialogs::Key key, FilterId filterId, bool pinned);
|
||||
void setPinnedFromEntryList(Dialogs::Key key, bool pinned);
|
||||
void clearPinnedChats(Folder *folder);
|
||||
void applyPinnedChats(
|
||||
Folder *folder,
|
||||
@ -377,8 +374,8 @@ public:
|
||||
const QVector<MTPint> &list);
|
||||
void reorderTwoPinnedChats(
|
||||
FilterId filterId,
|
||||
const Dialogs::Key &key1,
|
||||
const Dialogs::Key &key2);
|
||||
Dialogs::Key key1,
|
||||
Dialogs::Key key2);
|
||||
|
||||
void setSuggestToGigagroup(not_null<ChannelData*> group, bool suggest);
|
||||
[[nodiscard]] bool suggestToGigagroup(
|
||||
@ -474,9 +471,8 @@ public:
|
||||
|
||||
[[nodiscard]] int unreadBadge() const;
|
||||
[[nodiscard]] bool unreadBadgeMuted() const;
|
||||
[[nodiscard]] int unreadBadgeIgnoreOne(const Dialogs::Key &key) const;
|
||||
[[nodiscard]] bool unreadBadgeMutedIgnoreOne(
|
||||
const Dialogs::Key &key) const;
|
||||
[[nodiscard]] int unreadBadgeIgnoreOne(Dialogs::Key key) const;
|
||||
[[nodiscard]] bool unreadBadgeMutedIgnoreOne(Dialogs::Key key) const;
|
||||
[[nodiscard]] int unreadOnlyMutedBadge() const;
|
||||
[[nodiscard]] rpl::producer<> unreadBadgeChanges() const;
|
||||
void notifyUnreadBadgeChanged();
|
||||
|
@ -71,6 +71,9 @@ defaultDialogRow: DialogRow {
|
||||
textLeft: 68px;
|
||||
textTop: 34px;
|
||||
}
|
||||
forumDialogRow: DialogRow {
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
dialogsOnlineBadgeStroke: 2px;
|
||||
dialogsOnlineBadgeSize: 10px;
|
||||
|
@ -300,10 +300,10 @@ PositionChange Entry::adjustByPosInChatList(
|
||||
not_null<MainList*> list) {
|
||||
const auto links = chatListLinks(filterId);
|
||||
Assert(links != nullptr);
|
||||
const auto from = links->main->pos();
|
||||
const auto from = links->main->top();
|
||||
list->indexed()->adjustByDate(*links);
|
||||
const auto to = links->main->pos();
|
||||
return { from, to };
|
||||
const auto to = links->main->top();
|
||||
return { .from = from, .to = to, .height = links->main->height() };
|
||||
}
|
||||
|
||||
void Entry::setChatListTimeId(TimeId date) {
|
||||
@ -315,7 +315,7 @@ void Entry::setChatListTimeId(TimeId date) {
|
||||
}
|
||||
|
||||
int Entry::posInChatList(FilterId filterId) const {
|
||||
return mainChatListLink(filterId)->pos();
|
||||
return mainChatListLink(filterId)->index();
|
||||
}
|
||||
|
||||
not_null<Row*> Entry::addToChatList(
|
||||
|
@ -56,6 +56,7 @@ enum class SortMode {
|
||||
struct PositionChange {
|
||||
int from = -1;
|
||||
int to = -1;
|
||||
int height = 0;
|
||||
};
|
||||
|
||||
struct UnreadState {
|
||||
|
@ -134,7 +134,7 @@ void IndexedList::adjustByName(
|
||||
}
|
||||
for (auto ch : toRemove) {
|
||||
if (auto it = _index.find(ch); it != _index.cend()) {
|
||||
it->second.del(key, mainRow);
|
||||
it->second.remove(key, mainRow);
|
||||
}
|
||||
}
|
||||
if (!toAdd.empty()) {
|
||||
@ -171,7 +171,7 @@ void IndexedList::adjustNames(
|
||||
history->removeChatListEntryByLetter(filterId, ch);
|
||||
}
|
||||
if (auto it = _index.find(ch); it != _index.cend()) {
|
||||
it->second.del(key, mainRow);
|
||||
it->second.remove(key, mainRow);
|
||||
}
|
||||
}
|
||||
for (auto ch : toAdd) {
|
||||
@ -186,11 +186,11 @@ void IndexedList::adjustNames(
|
||||
}
|
||||
}
|
||||
|
||||
void IndexedList::del(Key key, Row *replacedBy) {
|
||||
if (_list.del(key, replacedBy)) {
|
||||
void IndexedList::remove(Key key, Row *replacedBy) {
|
||||
if (_list.remove(key, replacedBy)) {
|
||||
for (const auto &ch : key.entry()->chatListFirstLetters()) {
|
||||
if (auto it = _index.find(ch); it != _index.cend()) {
|
||||
it->second.del(key, replacedBy);
|
||||
if (const auto it = _index.find(ch); it != _index.cend()) {
|
||||
it->second.remove(key, replacedBy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -37,39 +37,48 @@ public:
|
||||
not_null<PeerData*> peer,
|
||||
const base::flat_set<QChar> &oldChars);
|
||||
|
||||
void del(Key key, Row *replacedBy = nullptr);
|
||||
void remove(Key key, Row *replacedBy = nullptr);
|
||||
void clear();
|
||||
|
||||
const List &all() const {
|
||||
[[nodiscard]] const List &all() const {
|
||||
return _list;
|
||||
}
|
||||
const List *filtered(QChar ch) const {
|
||||
[[nodiscard]] const List *filtered(QChar ch) const {
|
||||
const auto i = _index.find(ch);
|
||||
return (i != _index.end()) ? &i->second : nullptr;
|
||||
}
|
||||
std::vector<not_null<Row*>> filtered(const QStringList &words) const;
|
||||
[[nodiscard]] std::vector<not_null<Row*>> filtered(
|
||||
const QStringList &words) const;
|
||||
|
||||
// Part of List interface is duplicated here for all() list.
|
||||
int size() const { return all().size(); }
|
||||
bool empty() const { return all().empty(); }
|
||||
bool contains(Key key) const { return all().contains(key); }
|
||||
Row *getRow(Key key) const { return all().getRow(key); }
|
||||
Row *rowAtY(int32 y, int32 h) const { return all().rowAtY(y, h); }
|
||||
[[nodiscard]] int size() const { return all().size(); }
|
||||
[[nodiscard]] bool empty() const { return all().empty(); }
|
||||
[[nodiscard]] int height() const { return all().height(); }
|
||||
[[nodiscard]] bool contains(Key key) const {
|
||||
return all().contains(key);
|
||||
}
|
||||
[[nodiscard]] Row *getRow(Key key) const { return all().getRow(key); }
|
||||
[[nodiscard]] Row *rowAtY(int y) const { return all().rowAtY(y); }
|
||||
|
||||
using iterator = List::iterator;
|
||||
using const_iterator = List::const_iterator;
|
||||
const_iterator cbegin() const { return all().cbegin(); }
|
||||
const_iterator cend() const { return all().cend(); }
|
||||
const_iterator begin() const { return all().cbegin(); }
|
||||
const_iterator end() const { return all().cend(); }
|
||||
iterator begin() { return all().begin(); }
|
||||
iterator end() { return all().end(); }
|
||||
const_iterator cfind(Row *value) const { return all().cfind(value); }
|
||||
const_iterator find(Row *value) const { return all().cfind(value); }
|
||||
iterator find(Row *value) { return all().find(value); }
|
||||
const_iterator cfind(int y, int h) const { return all().cfind(y, h); }
|
||||
const_iterator find(int y, int h) const { return all().cfind(y, h); }
|
||||
iterator find(int y, int h) { return all().find(y, h); }
|
||||
[[nodiscard]] const_iterator cbegin() const { return all().cbegin(); }
|
||||
[[nodiscard]] const_iterator cend() const { return all().cend(); }
|
||||
[[nodiscard]] const_iterator begin() const { return all().cbegin(); }
|
||||
[[nodiscard]] const_iterator end() const { return all().cend(); }
|
||||
[[nodiscard]] iterator begin() { return all().begin(); }
|
||||
[[nodiscard]] iterator end() { return all().end(); }
|
||||
[[nodiscard]] const_iterator cfind(Row *value) const {
|
||||
return all().cfind(value);
|
||||
}
|
||||
[[nodiscard]] const_iterator find(Row *value) const {
|
||||
return all().cfind(value);
|
||||
}
|
||||
[[nodiscard]] iterator find(Row *value) { return all().find(value); }
|
||||
[[nodiscard]] const_iterator findByY(int y) const {
|
||||
return all().findByY(y);
|
||||
}
|
||||
[[nodiscard]] iterator findByY(int y) { return all().findByY(y); }
|
||||
|
||||
private:
|
||||
void adjustByName(
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -192,6 +192,19 @@ private:
|
||||
EmptyForum,
|
||||
};
|
||||
|
||||
struct PinnedRow {
|
||||
anim::value yadd;
|
||||
crl::time animStartTime = 0;
|
||||
};
|
||||
|
||||
struct FilterResult {
|
||||
not_null<Row*> row;
|
||||
int top = 0;
|
||||
|
||||
[[nodiscard]] Key key() const;
|
||||
[[nodiscard]] int bottom() const;
|
||||
};
|
||||
|
||||
Main::Session &session() const;
|
||||
|
||||
void dialogRowReplaced(Row *oldRow, Row *newRow);
|
||||
@ -220,6 +233,8 @@ private:
|
||||
void clearIrrelevantState();
|
||||
void selectByMouse(QPoint globalPosition);
|
||||
void loadPeerPhotos();
|
||||
void scrollToItem(int top, int height);
|
||||
void scrollToDefaultSelected();
|
||||
void setCollapsedPressed(int pressed);
|
||||
void setPressed(Row *pressed);
|
||||
void setHashtagPressed(int pressed);
|
||||
@ -283,13 +298,18 @@ private:
|
||||
void fillSupportSearchMenu(not_null<Ui::PopupMenu*> menu);
|
||||
void fillArchiveSearchMenu(not_null<Ui::PopupMenu*> menu);
|
||||
|
||||
int dialogsOffset() const;
|
||||
int fixedOnTopCount() const;
|
||||
int pinnedOffset() const;
|
||||
int filteredOffset() const;
|
||||
int peerSearchOffset() const;
|
||||
int searchedOffset() const;
|
||||
int searchInChatSkip() const;
|
||||
void refreshShownList();
|
||||
[[nodiscard]] int skipTopHeight() const;
|
||||
[[nodiscard]] int dialogsOffset() const;
|
||||
[[nodiscard]] int shownHeight(int till = -1) const;
|
||||
[[nodiscard]] int fixedOnTopCount() const;
|
||||
[[nodiscard]] int pinnedOffset() const;
|
||||
[[nodiscard]] int filteredOffset() const;
|
||||
[[nodiscard]] int filteredIndex(int y) const;
|
||||
[[nodiscard]] int filteredHeight(int till = -1) const;
|
||||
[[nodiscard]] int peerSearchOffset() const;
|
||||
[[nodiscard]] int searchedOffset() const;
|
||||
[[nodiscard]] int searchInChatSkip() const;
|
||||
|
||||
void paintCollapsedRows(
|
||||
Painter &p,
|
||||
@ -339,13 +359,12 @@ private:
|
||||
Ui::VideoUserpic *validateVideoUserpic(not_null<Row*> row);
|
||||
Ui::VideoUserpic *validateVideoUserpic(not_null<History*> history);
|
||||
|
||||
Row *shownRowByKey(Key key);
|
||||
void clearSearchResults(bool clearPeerSearchResults = true);
|
||||
void updateSelectedRow(Key key = Key());
|
||||
void trackSearchResultsHistory(not_null<History*> history);
|
||||
void trackSearchResultsForum(Data::Forum *forum);
|
||||
|
||||
[[nodiscard]] not_null<IndexedList*> shownDialogs() const;
|
||||
|
||||
[[nodiscard]] const std::vector<Key> &pinnedChatsOrder() const;
|
||||
void checkReorderPinnedStart(QPoint localPosition);
|
||||
int updateReorderIndexGetCount();
|
||||
@ -363,6 +382,7 @@ private:
|
||||
|
||||
const not_null<Window::SessionController*> _controller;
|
||||
|
||||
not_null<IndexedList*> _shownList;
|
||||
FilterId _filterId = 0;
|
||||
bool _mouseSelection = false;
|
||||
std::optional<QPoint> _lastMousePosition;
|
||||
@ -376,7 +396,7 @@ private:
|
||||
not_null<const style::DialogRow*> _st;
|
||||
int _collapsedSelected = -1;
|
||||
int _collapsedPressed = -1;
|
||||
int _skipTopDialogs = 0;
|
||||
bool _skipTopDialog = false;
|
||||
Row *_selected = nullptr;
|
||||
Row *_pressed = nullptr;
|
||||
|
||||
@ -384,10 +404,6 @@ private:
|
||||
int _draggingIndex = -1;
|
||||
int _aboveIndex = -1;
|
||||
QPoint _dragStart;
|
||||
struct PinnedRow {
|
||||
anim::value yadd;
|
||||
crl::time animStartTime = 0;
|
||||
};
|
||||
std::vector<PinnedRow> _pinnedRows;
|
||||
Ui::Animations::Basic _pinnedShiftAnimation;
|
||||
base::flat_set<Key> _pinnedOnDragStart;
|
||||
@ -405,7 +421,7 @@ private:
|
||||
bool _hashtagDeleteSelected = false;
|
||||
bool _hashtagDeletePressed = false;
|
||||
|
||||
std::vector<not_null<Row*>> _filterResults;
|
||||
std::vector<FilterResult> _filterResults;
|
||||
base::flat_map<Key, std::unique_ptr<Row>> _filterResultsGlobal;
|
||||
int _filteredSelected = -1;
|
||||
int _filteredPressed = -1;
|
||||
|
@ -39,13 +39,13 @@ public:
|
||||
explicit operator bool() const {
|
||||
return (_value != nullptr);
|
||||
}
|
||||
not_null<Entry*> entry() const;
|
||||
History *history() const;
|
||||
Data::Folder *folder() const;
|
||||
Data::ForumTopic *topic() const;
|
||||
Data::Thread *thread() const;
|
||||
History *owningHistory() const;
|
||||
PeerData *peer() const;
|
||||
[[nodiscard]] not_null<Entry*> entry() const;
|
||||
[[nodiscard]] History *history() const;
|
||||
[[nodiscard]] Data::Folder *folder() const;
|
||||
[[nodiscard]] Data::ForumTopic *topic() const;
|
||||
[[nodiscard]] Data::Thread *thread() const;
|
||||
[[nodiscard]] History *owningHistory() const;
|
||||
[[nodiscard]] PeerData *peer() const;
|
||||
|
||||
friend inline constexpr auto operator<=>(Key, Key) noexcept = default;
|
||||
|
||||
|
@ -21,7 +21,7 @@ List::List(SortMode sortMode, FilterId filterId)
|
||||
|
||||
List::const_iterator List::cfind(Row *value) const {
|
||||
return value
|
||||
? (cbegin() + value->pos())
|
||||
? (cbegin() + value->index())
|
||||
: cend();
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@ not_null<Row*> List::addToEnd(Key key) {
|
||||
}
|
||||
const auto result = _rowByKey.emplace(
|
||||
key,
|
||||
std::make_unique<Row>(key, _rows.size())
|
||||
std::make_unique<Row>(key, _rows.size(), height())
|
||||
).first->second.get();
|
||||
_rows.emplace_back(result);
|
||||
if (_sortMode == SortMode::Date) {
|
||||
@ -60,10 +60,10 @@ not_null<Row*> List::addByName(Key key) {
|
||||
}
|
||||
|
||||
void List::adjustByName(not_null<Row*> row) {
|
||||
Expects(row->pos() >= 0 && row->pos() < _rows.size());
|
||||
Expects(row->index() >= 0 && row->index() < _rows.size());
|
||||
|
||||
const auto &key = row->entry()->chatListNameSortKey();
|
||||
const auto index = row->pos();
|
||||
const auto index = row->index();
|
||||
const auto i = _rows.begin() + index;
|
||||
const auto before = std::find_if(i + 1, _rows.end(), [&](Row *row) {
|
||||
return row->entry()->chatListNameSortKey().compare(key) >= 0;
|
||||
@ -85,7 +85,7 @@ void List::adjustByDate(not_null<Row*> row) {
|
||||
Expects(_sortMode == SortMode::Date);
|
||||
|
||||
const auto key = row->sortKey(_filterId);
|
||||
const auto index = row->pos();
|
||||
const auto index = row->index();
|
||||
const auto i = _rows.begin() + index;
|
||||
const auto before = std::find_if(i + 1, _rows.end(), [&](Row *row) {
|
||||
return (row->sortKey(_filterId) <= key);
|
||||
@ -108,7 +108,7 @@ bool List::moveToTop(Key key) {
|
||||
if (i == _rowByKey.cend()) {
|
||||
return false;
|
||||
}
|
||||
const auto index = i->second->pos();
|
||||
const auto index = i->second->index();
|
||||
const auto begin = _rows.begin();
|
||||
rotate(begin, begin + index, begin + index + 1);
|
||||
return true;
|
||||
@ -118,16 +118,20 @@ void List::rotate(
|
||||
std::vector<not_null<Row*>>::iterator first,
|
||||
std::vector<not_null<Row*>>::iterator middle,
|
||||
std::vector<not_null<Row*>>::iterator last) {
|
||||
auto top = (*first)->top();
|
||||
std::rotate(first, middle, last);
|
||||
|
||||
auto count = (last - first);
|
||||
auto index = (first - _rows.begin());
|
||||
while (count--) {
|
||||
(*first++)->_pos = index++;
|
||||
const auto row = *first++;
|
||||
row->_index = index++;
|
||||
row->_top = top;
|
||||
top += row->height();
|
||||
}
|
||||
}
|
||||
|
||||
bool List::del(Key key, Row *replacedBy) {
|
||||
bool List::remove(Key key, Row *replacedBy) {
|
||||
auto i = _rowByKey.find(key);
|
||||
if (i == _rowByKey.cend()) {
|
||||
return false;
|
||||
@ -136,13 +140,34 @@ bool List::del(Key key, Row *replacedBy) {
|
||||
const auto row = i->second.get();
|
||||
row->entry()->owner().dialogsRowReplaced({ row, replacedBy });
|
||||
|
||||
const auto index = row->pos();
|
||||
auto top = row->top();
|
||||
const auto index = row->index();
|
||||
_rows.erase(_rows.begin() + index);
|
||||
for (auto i = index, count = int(_rows.size()); i != count; ++i) {
|
||||
_rows[i]->_pos = i;
|
||||
const auto row = _rows[i];
|
||||
row->_index = i;
|
||||
row->_top = top;
|
||||
top += row->height();
|
||||
}
|
||||
_rowByKey.erase(i);
|
||||
return true;
|
||||
}
|
||||
|
||||
Row *List::rowAtY(int y) const {
|
||||
const auto i = findByY(y);
|
||||
if (i == cend()) {
|
||||
return nullptr;
|
||||
}
|
||||
const auto row = *i;
|
||||
const auto top = row->top();
|
||||
const auto bottom = top + row->height();
|
||||
return (top <= y && bottom > y) ? row.get() : nullptr;
|
||||
}
|
||||
|
||||
List::const_iterator List::findByY(int y) const {
|
||||
return ranges::lower_bound(_rows, y, ranges::less(), [](const Row *row) {
|
||||
return row->top() + row->height();
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace Dialogs
|
||||
|
@ -23,52 +23,49 @@ public:
|
||||
List &operator=(List &&other) = default;
|
||||
~List() = default;
|
||||
|
||||
int size() const {
|
||||
[[nodiscard]] int size() const {
|
||||
return _rows.size();
|
||||
}
|
||||
bool empty() const {
|
||||
[[nodiscard]] bool empty() const {
|
||||
return _rows.empty();
|
||||
}
|
||||
bool contains(Key key) const {
|
||||
[[nodiscard]] int height() const {
|
||||
return _rows.empty()
|
||||
? 0
|
||||
: (_rows.back()->top() + _rows.back()->height());
|
||||
}
|
||||
[[nodiscard]] bool contains(Key key) const {
|
||||
return _rowByKey.find(key) != _rowByKey.end();
|
||||
}
|
||||
Row *getRow(Key key) const {
|
||||
[[nodiscard]] Row *getRow(Key key) const {
|
||||
const auto i = _rowByKey.find(key);
|
||||
return (i != _rowByKey.end()) ? i->second.get() : nullptr;
|
||||
}
|
||||
Row *rowAtY(int y, int h) const {
|
||||
const auto i = cfind(y, h);
|
||||
if (i == cend() || (*i)->pos() != ((y > 0) ? (y / h) : 0)) {
|
||||
return nullptr;
|
||||
}
|
||||
return *i;
|
||||
}
|
||||
[[nodiscard]] Row *rowAtY(int y) const;
|
||||
|
||||
not_null<Row*> addToEnd(Key key);
|
||||
Row *adjustByName(Key key);
|
||||
not_null<Row*> addByName(Key key);
|
||||
bool moveToTop(Key key);
|
||||
void adjustByDate(not_null<Row*> row);
|
||||
bool del(Key key, Row *replacedBy = nullptr);
|
||||
bool remove(Key key, Row *replacedBy = nullptr);
|
||||
|
||||
using const_iterator = std::vector<not_null<Row*>>::const_iterator;
|
||||
using iterator = const_iterator;
|
||||
|
||||
const_iterator cbegin() const { return _rows.cbegin(); }
|
||||
const_iterator cend() const { return _rows.cend(); }
|
||||
const_iterator begin() const { return cbegin(); }
|
||||
const_iterator end() const { return cend(); }
|
||||
iterator begin() { return cbegin(); }
|
||||
iterator end() { return cend(); }
|
||||
const_iterator cfind(Row *value) const;
|
||||
const_iterator find(Row *value) const { return cfind(value); }
|
||||
iterator find(Row *value) { return cfind(value); }
|
||||
const_iterator cfind(int y, int h) const {
|
||||
const auto index = std::max(y, 0) / h;
|
||||
return _rows.begin() + std::min(index, size());
|
||||
[[nodiscard]] const_iterator cbegin() const { return _rows.cbegin(); }
|
||||
[[nodiscard]] const_iterator cend() const { return _rows.cend(); }
|
||||
[[nodiscard]] const_iterator begin() const { return cbegin(); }
|
||||
[[nodiscard]] const_iterator end() const { return cend(); }
|
||||
[[nodiscard]] iterator begin() { return cbegin(); }
|
||||
[[nodiscard]] iterator end() { return cend(); }
|
||||
[[nodiscard]] const_iterator cfind(Row *value) const;
|
||||
[[nodiscard]] const_iterator find(Row *value) const {
|
||||
return cfind(value);
|
||||
}
|
||||
const_iterator find(int y, int h) const { return cfind(y, h); }
|
||||
iterator find(int y, int h) { return cfind(y, h); }
|
||||
[[nodiscard]] iterator find(Row *value) { return cfind(value); }
|
||||
[[nodiscard]] const_iterator findByY(int y) const;
|
||||
[[nodiscard]] iterator findByY(int y) { return findByY(y); }
|
||||
|
||||
private:
|
||||
void adjustByName(not_null<Row*> row);
|
||||
|
@ -89,7 +89,7 @@ void MainList::clear() {
|
||||
_cloudListSize = 0;
|
||||
}
|
||||
|
||||
RowsByLetter MainList::addEntry(const Key &key) {
|
||||
RowsByLetter MainList::addEntry(Key key) {
|
||||
const auto result = _all.addToEnd(key);
|
||||
|
||||
const auto unread = key.entry()->chatListUnreadState();
|
||||
@ -99,8 +99,8 @@ RowsByLetter MainList::addEntry(const Key &key) {
|
||||
return result;
|
||||
}
|
||||
|
||||
void MainList::removeEntry(const Key &key) {
|
||||
_all.del(key);
|
||||
void MainList::removeEntry(Key key) {
|
||||
_all.remove(key);
|
||||
|
||||
const auto unread = key.entry()->chatListUnreadState();
|
||||
unreadEntryChanged(unread, false);
|
||||
|
@ -33,8 +33,8 @@ public:
|
||||
void setAllAreMuted(bool allAreMuted = true);
|
||||
void clear();
|
||||
|
||||
RowsByLetter addEntry(const Key &key);
|
||||
void removeEntry(const Key &key);
|
||||
RowsByLetter addEntry(Key key);
|
||||
void removeEntry(Key key);
|
||||
|
||||
void unreadStateChanged(
|
||||
const UnreadState &wasState,
|
||||
|
@ -31,13 +31,13 @@ void PinnedList::setLimit(int limit) {
|
||||
applyLimit(_limit);
|
||||
}
|
||||
|
||||
void PinnedList::addPinned(const Key &key) {
|
||||
void PinnedList::addPinned(Key key) {
|
||||
Expects(key.entry()->folderKnown());
|
||||
|
||||
addPinnedGetPosition(key);
|
||||
}
|
||||
|
||||
int PinnedList::addPinnedGetPosition(const Key &key) {
|
||||
int PinnedList::addPinnedGetPosition(Key key) {
|
||||
const auto already = ranges::find(_data, key);
|
||||
if (already != end(_data)) {
|
||||
return already - begin(_data);
|
||||
@ -49,7 +49,7 @@ int PinnedList::addPinnedGetPosition(const Key &key) {
|
||||
return position;
|
||||
}
|
||||
|
||||
void PinnedList::setPinned(const Key &key, bool pinned) {
|
||||
void PinnedList::setPinned(Key key, bool pinned) {
|
||||
Expects(key.entry()->folderKnown() || _filterId != 0);
|
||||
|
||||
if (pinned) {
|
||||
@ -128,7 +128,7 @@ void PinnedList::applyList(const std::vector<not_null<History*>> &list) {
|
||||
}
|
||||
}
|
||||
|
||||
void PinnedList::reorder(const Key &key1, const Key &key2) {
|
||||
void PinnedList::reorder(Key key1, Key key2) {
|
||||
const auto index1 = ranges::find(_data, key1) - begin(_data);
|
||||
const auto index2 = ranges::find(_data, key2) - begin(_data);
|
||||
Assert(index1 >= 0 && index1 < _data.size());
|
||||
|
@ -26,10 +26,10 @@ public:
|
||||
|
||||
// Places on the last place in the list otherwise.
|
||||
// Does nothing if already pinned.
|
||||
void addPinned(const Key &key);
|
||||
void addPinned(Key key);
|
||||
|
||||
// if (pinned) places on the first place in the list.
|
||||
void setPinned(const Key &key, bool pinned);
|
||||
void setPinned(Key key, bool pinned);
|
||||
|
||||
void clear();
|
||||
|
||||
@ -40,14 +40,14 @@ public:
|
||||
not_null<Data::Forum*> forum,
|
||||
const QVector<MTPint> &list);
|
||||
void applyList(const std::vector<not_null<History*>> &list);
|
||||
void reorder(const Key &key1, const Key &key2);
|
||||
void reorder(Key key1, Key key2);
|
||||
|
||||
const std::vector<Key> &order() const {
|
||||
return _data;
|
||||
}
|
||||
|
||||
private:
|
||||
int addPinnedGetPosition(const Key &key);
|
||||
int addPinnedGetPosition(Key key);
|
||||
void applyLimit(int limit);
|
||||
|
||||
FilterId _filterId = 0;
|
||||
|
@ -143,9 +143,14 @@ void BasicRow::paintUserpic(
|
||||
context.paused);
|
||||
}
|
||||
|
||||
Row::Row(Key key, int pos) : _id(key), _pos(pos) {
|
||||
Row::Row(Key key, int index, int top) : _id(key), _top(top), _index(index) {
|
||||
if (const auto history = key.history()) {
|
||||
updateCornerBadgeShown(history->peer);
|
||||
_height = history->peer->isForum()
|
||||
? st::forumDialogRow.height
|
||||
: st::defaultDialogRow.height;
|
||||
} else {
|
||||
_height = st::forumTopicRow.height;
|
||||
}
|
||||
}
|
||||
|
||||
@ -173,10 +178,11 @@ void Row::validateListEntryCache() const {
|
||||
void Row::setCornerBadgeShown(
|
||||
bool shown,
|
||||
Fn<void()> updateCallback) const {
|
||||
if (_cornerBadgeShown == shown) {
|
||||
const auto value = shown ? 1 : 0;
|
||||
if (_cornerBadgeShown == value) {
|
||||
return;
|
||||
}
|
||||
_cornerBadgeShown = shown;
|
||||
_cornerBadgeShown = value;
|
||||
if (_cornerBadgeUserpic && _cornerBadgeUserpic->animation.animating()) {
|
||||
_cornerBadgeUserpic->animation.change(
|
||||
_cornerBadgeShown ? 1. : 0.,
|
||||
|
@ -16,6 +16,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
class History;
|
||||
class HistoryItem;
|
||||
|
||||
namespace style {
|
||||
struct DialogRow;
|
||||
} // namespace style
|
||||
|
||||
namespace Data {
|
||||
class CloudImageView;
|
||||
} // namespace Data
|
||||
@ -57,7 +61,8 @@ public:
|
||||
int outerWidth,
|
||||
const QColor *colorOverride = nullptr) const;
|
||||
|
||||
std::shared_ptr<Data::CloudImageView> &userpicView() const {
|
||||
[[nodiscard]] auto userpicView() const
|
||||
-> std::shared_ptr<Data::CloudImageView> & {
|
||||
return _userpic;
|
||||
}
|
||||
|
||||
@ -68,11 +73,18 @@ private:
|
||||
};
|
||||
|
||||
class List;
|
||||
class Row : public BasicRow {
|
||||
class Row final : public BasicRow {
|
||||
public:
|
||||
explicit Row(std::nullptr_t) {
|
||||
}
|
||||
Row(Key key, int pos);
|
||||
Row(Key key, int index, int top);
|
||||
|
||||
[[nodiscard]] int top() const {
|
||||
return _top;
|
||||
}
|
||||
[[nodiscard]] int height() const {
|
||||
return _height;
|
||||
}
|
||||
|
||||
void updateCornerBadgeShown(
|
||||
not_null<PeerData*> peer,
|
||||
@ -102,8 +114,8 @@ public:
|
||||
[[nodiscard]] not_null<Entry*> entry() const {
|
||||
return _id.entry();
|
||||
}
|
||||
[[nodiscard]] int pos() const {
|
||||
return _pos;
|
||||
[[nodiscard]] int index() const {
|
||||
return _index;
|
||||
}
|
||||
[[nodiscard]] uint64 sortKey(FilterId filterId) const;
|
||||
|
||||
@ -139,11 +151,13 @@ private:
|
||||
const Ui::PaintContext &context);
|
||||
|
||||
Key _id;
|
||||
int _pos = 0;
|
||||
mutable uint32 _listEntryCacheVersion = 0;
|
||||
mutable Ui::Text::String _listEntryCache;
|
||||
mutable std::unique_ptr<CornerBadgeUserpic> _cornerBadgeUserpic;
|
||||
mutable bool _cornerBadgeShown = false;
|
||||
int _top = 0;
|
||||
int _height = 0;
|
||||
int _index = 0;
|
||||
mutable int _listEntryCacheVersion : 31 = 0;
|
||||
mutable int _cornerBadgeShown : 1 = 0;
|
||||
|
||||
};
|
||||
|
||||
|
@ -245,6 +245,7 @@ template <typename PaintItemCallback>
|
||||
void PaintRow(
|
||||
Painter &p,
|
||||
not_null<const BasicRow*> row,
|
||||
QRect geometry,
|
||||
not_null<Entry*> entry,
|
||||
VideoUserpic *videoUserpic,
|
||||
PeerData *from,
|
||||
@ -264,7 +265,6 @@ void PaintRow(
|
||||
draft = nullptr;
|
||||
}
|
||||
|
||||
auto fullRect = QRect(0, 0, context.width, context.st->height);
|
||||
auto bg = context.active
|
||||
? st::dialogsBgActive
|
||||
: context.selected
|
||||
@ -273,7 +273,7 @@ void PaintRow(
|
||||
auto ripple = context.active
|
||||
? st::dialogsRippleBgActive
|
||||
: st::dialogsRippleBg;
|
||||
p.fillRect(fullRect, bg);
|
||||
p.fillRect(geometry, bg);
|
||||
row->paintRipple(p, 0, 0, context.width, &ripple->c);
|
||||
|
||||
const auto history = entry->asHistory();
|
||||
@ -932,6 +932,7 @@ void RowPainter::Paint(
|
||||
PaintRow(
|
||||
p,
|
||||
row,
|
||||
QRect(0, 0, context.width, row->height()),
|
||||
entry,
|
||||
videoUserpic,
|
||||
from,
|
||||
@ -1029,6 +1030,7 @@ void RowPainter::Paint(
|
||||
PaintRow(
|
||||
p,
|
||||
row,
|
||||
QRect(0, 0, context.width, context.st->height),
|
||||
entry,
|
||||
nullptr,
|
||||
from,
|
||||
|
@ -205,7 +205,7 @@ base::options::toggle AutoScrollInactiveChat({
|
||||
[[nodiscard]] rpl::producer<PeerData*> ActivePeerValue(
|
||||
not_null<Window::SessionController*> controller) {
|
||||
return controller->activeChatValue(
|
||||
) | rpl::map([](const Dialogs::Key &key) {
|
||||
) | rpl::map([](Dialogs::Key key) {
|
||||
const auto history = key.history();
|
||||
return history ? history->peer.get() : nullptr;
|
||||
});
|
||||
@ -2530,7 +2530,7 @@ void HistoryWidget::refreshSilentToggle() {
|
||||
|
||||
void HistoryWidget::setupScheduledToggle() {
|
||||
controller()->activeChatValue(
|
||||
) | rpl::map([=](const Dialogs::Key &key) -> rpl::producer<> {
|
||||
) | rpl::map([=](Dialogs::Key key) -> rpl::producer<> {
|
||||
if (const auto history = key.history()) {
|
||||
return session().data().scheduledMessages().updates(history);
|
||||
}
|
||||
|
@ -54,6 +54,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "boxes/delete_messages_box.h"
|
||||
#include "boxes/premium_preview_box.h"
|
||||
#include "boxes/peers/edit_participant_box.h"
|
||||
#include "core/crash_reports.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_sponsored_messages.h"
|
||||
#include "data/data_changes.h"
|
||||
@ -136,7 +137,10 @@ void ListWidget::enumerateItems(Method method) {
|
||||
if (TopToBottom) {
|
||||
Assert(itemTop(from->get()) + from->get()->height() > _visibleTop);
|
||||
} else {
|
||||
Assert(itemTop(from->get()) < _visibleBottom);
|
||||
if (itemTop(from->get()) >= _visibleBottom) {
|
||||
setGeometryCrashAnnotations(*from);
|
||||
Unexpected("itemTop(from->get()) >= _visibleBottom");
|
||||
}
|
||||
}
|
||||
|
||||
while (true) {
|
||||
@ -148,7 +152,10 @@ void ListWidget::enumerateItems(Method method) {
|
||||
if (TopToBottom) {
|
||||
Assert(itembottom > _visibleTop);
|
||||
} else {
|
||||
Assert(itemtop < _visibleBottom);
|
||||
if (itemtop >= _visibleBottom) {
|
||||
setGeometryCrashAnnotations(view);
|
||||
Unexpected("itemtop >= _visibleBottom");
|
||||
}
|
||||
}
|
||||
|
||||
if (!method(view, itemtop, itembottom)) {
|
||||
@ -441,6 +448,39 @@ void ListWidget::refreshViewer() {
|
||||
}, _viewerLifetime);
|
||||
}
|
||||
|
||||
void ListWidget::setGeometryCrashAnnotations(not_null<Element*> view) {
|
||||
CrashReports::SetAnnotation(
|
||||
"Geometry",
|
||||
u"size: %1x%2, visibleTop: %3, visibleBottom: %4, top: %5"_q
|
||||
.arg(width())
|
||||
.arg(height())
|
||||
.arg(_visibleTop)
|
||||
.arg(_visibleBottom)
|
||||
.arg(_itemsTop));
|
||||
const auto logItems = [&] {
|
||||
auto items = QStringList();
|
||||
auto top = _itemsTop;
|
||||
auto index = 0;
|
||||
for (const auto &some : _items) {
|
||||
items.push_back(u"(%1)%2=%3,%4,%5"_q
|
||||
.arg(index++)
|
||||
.arg(top)
|
||||
.arg(itemTop(some))
|
||||
.arg(some->y())
|
||||
.arg(some->height()));
|
||||
top += some->height();
|
||||
}
|
||||
return items.join(';');
|
||||
};
|
||||
CrashReports::SetAnnotation("Chosen", u"%1,%2,%3"_q
|
||||
.arg(itemTop(view))
|
||||
.arg(view->y())
|
||||
.arg(view->height()));
|
||||
CrashReports::SetAnnotation("Before", logItems());
|
||||
updateSize();
|
||||
CrashReports::SetAnnotation("After", logItems());
|
||||
}
|
||||
|
||||
void ListWidget::refreshRows(const Data::MessagesSlice &old) {
|
||||
saveScrollState();
|
||||
|
||||
@ -3167,6 +3207,12 @@ void ListWidget::mouseActionFinish(
|
||||
|
||||
auto activated = ClickHandler::unpressed();
|
||||
|
||||
if (_overElement) {
|
||||
AssertIsDebug();
|
||||
setGeometryCrashAnnotations(_overElement);
|
||||
Unexpected("Test");
|
||||
}
|
||||
|
||||
auto simpleSelectionChange = pressState.itemId
|
||||
&& !_pressWasInactive
|
||||
&& (button != Qt::RightButton)
|
||||
|
@ -608,6 +608,8 @@ private:
|
||||
template <typename Method>
|
||||
void enumerateDates(Method method);
|
||||
|
||||
void setGeometryCrashAnnotations(not_null<Element*> view);
|
||||
|
||||
static constexpr auto kMinimalIdsLimit = 24;
|
||||
|
||||
const not_null<ListDelegate*> _delegate;
|
||||
|
@ -401,7 +401,7 @@ void Session::addWindow(not_null<Window::SessionController*> controller) {
|
||||
_windows.remove(controller);
|
||||
});
|
||||
updates().addActiveChat(controller->activeChatChanges(
|
||||
) | rpl::map([=](const Dialogs::Key &chat) {
|
||||
) | rpl::map([=](Dialogs::Key chat) {
|
||||
return chat.peer();
|
||||
}) | rpl::distinct_until_changed());
|
||||
}
|
||||
|
@ -325,7 +325,7 @@ void SetupSendAsButton(
|
||||
not_null<SendAsButton*> button,
|
||||
not_null<Window::SessionController*> window) {
|
||||
auto active = window->activeChatValue(
|
||||
) | rpl::map([=](const Dialogs::Key &key) {
|
||||
) | rpl::map([=](Dialogs::Key key) {
|
||||
return key.history() ? key.history()->peer.get() : nullptr;
|
||||
});
|
||||
SetupSendAsButton(button, std::move(active), window);
|
||||
|
Loading…
Reference in New Issue
Block a user