diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 8465b57829..b47fa37e7e 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -791,6 +791,8 @@ PRIVATE dialogs/ui/dialogs_topics_view.h dialogs/ui/dialogs_video_userpic.cpp dialogs/ui/dialogs_video_userpic.h + dialogs/dialogs_community_chats.cpp + dialogs/dialogs_community_chats.h dialogs/dialogs_community_chats_list.cpp dialogs/dialogs_community_chats_list.h dialogs/dialogs_entry.cpp diff --git a/Telegram/SourceFiles/dialogs/dialogs_community_chats.cpp b/Telegram/SourceFiles/dialogs/dialogs_community_chats.cpp new file mode 100644 index 0000000000..26a802e94e --- /dev/null +++ b/Telegram/SourceFiles/dialogs/dialogs_community_chats.cpp @@ -0,0 +1,89 @@ +/* +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/dialogs_community_chats.h" + +#include "dialogs/dialogs_row.h" +#include "history/history.h" + +namespace Dialogs { + +CommunityRowsView::CommunityRowsView() = default; + +CommunityRowsView::~CommunityRowsView() = default; + +void CommunityRowsView::clear() { + _rows.clear(); + _tops.clear(); +} + +void CommunityRowsView::add(not_null history, float64 narrowRatio) { + auto row = std::make_unique(Key(history), 0, 0); + row->recountHeight(narrowRatio, FilterId()); + _rows.push_back(std::move(row)); +} + +void CommunityRowsView::finalize() { + _tops.resize(_rows.size() + 1); + auto top = 0; + for (auto i = 0, count = int(_rows.size()); i != count; ++i) { + _tops[i] = top; + top += _rows[i]->height(); + } + _tops.back() = top; +} + +void CommunityRowsView::recountHeights(float64 narrowRatio) { + for (const auto &row : _rows) { + row->recountHeight(narrowRatio, FilterId()); + } + finalize(); +} + +int CommunityRowsView::height() const { + return _tops.empty() ? 0 : _tops.back(); +} + +int CommunityRowsView::indexByY(int y) const { + if (_tops.empty() || y < 0 || y >= _tops.back()) { + return -1; + } + const auto i = std::upper_bound(begin(_tops), end(_tops), y); + return int(i - begin(_tops)) - 1; +} + +int CommunityRowsView::rowTop(int index) const { + Expects(index >= 0 && index < int(_rows.size())); + + return _tops[index]; +} + +Row *CommunityRowsView::rowAt(int index) const { + return (index >= 0 && index < int(_rows.size())) + ? _rows[index].get() + : nullptr; +} + +void CommunityRowsView::paint( + Painter &p, + QRect clip, + Fn, int index, int top)> paintRow) const { + if (_rows.empty() || clip.top() >= height()) { + return; + } + const auto from = std::max(indexByY(clip.top()), 0); + const auto bottom = clip.top() + clip.height(); + for (auto i = from, count = int(_rows.size()); i != count; ++i) { + const auto top = _tops[i]; + if (top >= bottom) { + break; + } + paintRow(_rows[i].get(), i, top); + } +} + +} // namespace Dialogs diff --git a/Telegram/SourceFiles/dialogs/dialogs_community_chats.h b/Telegram/SourceFiles/dialogs/dialogs_community_chats.h new file mode 100644 index 0000000000..11925e5e40 --- /dev/null +++ b/Telegram/SourceFiles/dialogs/dialogs_community_chats.h @@ -0,0 +1,50 @@ +/* +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 History; +class Painter; + +namespace Dialogs { + +class Row; + +class CommunityRowsView final { +public: + CommunityRowsView(); + ~CommunityRowsView(); + + void clear(); + void add(not_null history, float64 narrowRatio); + void finalize(); + + void recountHeights(float64 narrowRatio); + + [[nodiscard]] bool empty() const { + return _rows.empty(); + } + [[nodiscard]] int size() const { + return int(_rows.size()); + } + [[nodiscard]] int height() const; + [[nodiscard]] int indexByY(int y) const; + [[nodiscard]] int rowTop(int index) const; + [[nodiscard]] Row *rowAt(int index) const; + + void paint( + Painter &p, + QRect clip, + Fn, int index, int top)> paintRow) const; + +private: + std::vector> _rows; + std::vector _tops; + +}; + +} // namespace Dialogs diff --git a/Telegram/SourceFiles/dialogs/dialogs_community_chats_list.cpp b/Telegram/SourceFiles/dialogs/dialogs_community_chats_list.cpp index b26fdff724..7ea89c63f0 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_community_chats_list.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_community_chats_list.cpp @@ -57,15 +57,13 @@ CommunityChatsList::CommunityChatsList( CommunityChatsList::~CommunityChatsList() = default; void CommunityChatsList::rebuild() { - const auto wasCount = int(_rows.size()); - _rows.clear(); + const auto wasCount = _view.size(); + _view.clear(); setSelected(-1); setPressed(-1); const auto owner = &_controller->session().data(); const auto add = [&](not_null history) { - auto row = std::make_unique(Key(history), 0, 0); - row->recountHeight(0., FilterId()); - _rows.push_back(std::move(row)); + _view.add(history, 0.); }; if (_kind == CommunityChatsKind::Joined) { for (const auto &row : *_community->chatsList()->indexed()) { @@ -84,27 +82,16 @@ void CommunityChatsList::rebuild() { add(owner->history(linked.peer)); } } - _count = int(_rows.size()); - if (int(_rows.size()) != wasCount) { + _view.finalize(); + _count = _view.size(); + if (_view.size() != wasCount) { resizeToWidth(width()); } update(); } -int CommunityChatsList::rowTop(int index) const { - auto result = 0; - for (auto i = 0; i != index; ++i) { - result += _rows[i]->height(); - } - return result; -} - int CommunityChatsList::resizeGetHeight(int newWidth) { - auto result = 0; - for (const auto &row : _rows) { - result += row->height(); - } - return result; + return _view.height(); } void CommunityChatsList::paintEvent(QPaintEvent *e) { @@ -112,7 +99,7 @@ void CommunityChatsList::paintEvent(QPaintEvent *e) { const auto clip = e->rect(); p.fillRect(clip, st::dialogsBg); - if (_rows.empty()) { + if (_view.empty()) { return; } const auto paused = _controller->isGifPausedAtLeastFor( @@ -127,39 +114,19 @@ void CommunityChatsList::paintEvent(QPaintEvent *e) { .insideCommunity = true, }; const auto pressed = (_pressed >= 0); - auto top = 0; - for (auto i = 0, count = int(_rows.size()); i != count; ++i) { - const auto height = _rows[i]->height(); - if (top + height <= clip.top()) { - top += height; - continue; - } else if (top >= clip.top() + clip.height()) { - break; - } + _view.paint(p, clip, [&](not_null row, int index, int top) { context.active = false; context.selected = pressed - ? (_pressed == i) - : (_selected == i); + ? (_pressed == index) + : (_selected == index); p.translate(0, top); - Ui::RowPainter::Paint(p, _rows[i].get(), nullptr, context); + Ui::RowPainter::Paint(p, row.get(), nullptr, context); p.translate(0, -top); - top += height; - } + }); } void CommunityChatsList::updateSelected(QPoint local) { - auto selected = -1; - if (local.y() >= 0) { - auto top = 0; - for (auto i = 0, count = int(_rows.size()); i != count; ++i) { - const auto bottom = top + _rows[i]->height(); - if (local.y() < bottom) { - selected = i; - break; - } - top = bottom; - } - } + const auto selected = (local.y() >= 0) ? _view.indexByY(local.y()) : -1; setSelected(selected); setCursor((_selected >= 0) ? style::cur_pointer : style::cur_default); } @@ -168,12 +135,12 @@ void CommunityChatsList::setSelected(int selected) { if (_selected == selected) { return; } - if (_selected >= 0 && _selected < int(_rows.size())) { - update(0, rowTop(_selected), width(), _rows[_selected]->height()); + if (const auto row = _view.rowAt(_selected)) { + update(0, _view.rowTop(_selected), width(), row->height()); } _selected = selected; - if (_selected >= 0 && _selected < int(_rows.size())) { - update(0, rowTop(_selected), width(), _rows[_selected]->height()); + if (const auto row = _view.rowAt(_selected)) { + update(0, _view.rowTop(_selected), width(), row->height()); } } @@ -181,8 +148,8 @@ void CommunityChatsList::setPressed(int pressed) { if (_pressed == pressed) { return; } - if (_pressed >= 0 && _pressed < int(_rows.size())) { - _rows[_pressed]->stopLastRipple(); + if (const auto row = _view.rowAt(_pressed)) { + row->stopLastRipple(); } _pressed = pressed; } @@ -197,10 +164,10 @@ void CommunityChatsList::mousePressEvent(QMouseEvent *e) { } updateSelected(e->pos()); setPressed(_selected); - if (_pressed >= 0) { - const auto top = rowTop(_pressed); - const auto height = _rows[_pressed]->height(); - _rows[_pressed]->addRipple( + if (const auto row = _view.rowAt(_pressed)) { + const auto top = _view.rowTop(_pressed); + const auto height = row->height(); + row->addRipple( e->pos() - QPoint(0, top), QSize(width(), height), [=] { update(0, top, width(), height); }); @@ -210,9 +177,11 @@ void CommunityChatsList::mousePressEvent(QMouseEvent *e) { void CommunityChatsList::mouseReleaseEvent(QMouseEvent *e) { const auto pressed = _pressed; setPressed(-1); - if (pressed >= 0 && pressed == _selected && pressed < int(_rows.size())) { - if (const auto history = _rows[pressed]->history()) { - _chatChosen.fire_copy(history); + if (pressed >= 0 && pressed == _selected) { + if (const auto row = _view.rowAt(pressed)) { + if (const auto history = row->history()) { + _chatChosen.fire_copy(history); + } } } } diff --git a/Telegram/SourceFiles/dialogs/dialogs_community_chats_list.h b/Telegram/SourceFiles/dialogs/dialogs_community_chats_list.h index e748c3bdf7..a91777a941 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_community_chats_list.h +++ b/Telegram/SourceFiles/dialogs/dialogs_community_chats_list.h @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "dialogs/dialogs_community_chats.h" #include "ui/rp_widget.h" class History; @@ -59,14 +60,13 @@ private: void updateSelected(QPoint local); void setSelected(int selected); void setPressed(int pressed); - [[nodiscard]] int rowTop(int index) const; const not_null _controller; const not_null _community; const CommunityChatsKind _kind; const not_null _st; - std::vector> _rows; + CommunityRowsView _view; int _selected = -1; int _pressed = -1; rpl::variable _count = 0; diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index 446bd55eb3..2b7a9576e4 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -619,14 +619,11 @@ void InnerWidget::setNarrowRatio(float64 narrowRatio) { _narrowRatio = narrowRatio; auto changed = _shownList->updateHeights(_narrowRatio); if (_openedCommunity) { - const auto recount = [&]( - const std::vector> &rows) { - for (const auto &row : rows) { - const auto was = row->height(); - row->recountHeight(_narrowRatio, FilterId()); - if (row->height() != was) { - changed = true; - } + const auto recount = [&](CommunityRowsView &view) { + const auto was = view.height(); + view.recountHeights(_narrowRatio); + if (view.height() != was) { + changed = true; } }; recount(_communityViewable); @@ -780,15 +777,6 @@ int InnerWidget::searchedOffset() const { return result; } -int InnerWidget::communityListHeight( - const std::vector> &rows) const { - auto result = 0; - for (const auto &row : rows) { - result += row->height(); - } - return result; -} - int InnerWidget::communityViewableTop() const { return dialogsOffset() + _shownList->height(); } @@ -796,8 +784,7 @@ int InnerWidget::communityViewableTop() const { int InnerWidget::communityRequestableTop() const { auto result = communityViewableTop(); if (!_communityViewable.empty()) { - result += st::searchedBarHeight - + communityListHeight(_communityViewable); + result += st::searchedBarHeight + _communityViewable.height(); } return result; } @@ -805,41 +792,37 @@ int InnerWidget::communityRequestableTop() const { int InnerWidget::communitySectionsBottom() const { auto result = communityRequestableTop(); if (!_communityRequestable.empty()) { - result += st::searchedBarHeight - + communityListHeight(_communityRequestable); + result += st::searchedBarHeight + _communityRequestable.height(); } return result; } int InnerWidget::communityRowCount() const { - return int(_communityViewable.size() + _communityRequestable.size()); + return _communityViewable.size() + _communityRequestable.size(); } Row *InnerWidget::communityRowAt(int index) const { if (index < 0 || index >= communityRowCount()) { return nullptr; } - const auto viewable = int(_communityViewable.size()); + const auto viewable = _communityViewable.size(); return (index < viewable) - ? _communityViewable[index].get() - : _communityRequestable[index - viewable].get(); + ? _communityViewable.rowAt(index) + : _communityRequestable.rowAt(index - viewable); } int InnerWidget::communityRowAbsoluteTop(int index) const { - const auto viewable = int(_communityViewable.size()); + const auto viewable = _communityViewable.size(); const auto inViewable = (index < viewable); - const auto &rows = inViewable + const auto &view = inViewable ? _communityViewable : _communityRequestable; const auto localIndex = inViewable ? index : (index - viewable); - auto result = (inViewable + return (inViewable ? communityViewableTop() : communityRequestableTop()) - + st::searchedBarHeight; - for (auto j = 0; j != localIndex; ++j) { - result += rows[j]->height(); - } - return result; + + st::searchedBarHeight + + view.rowTop(localIndex); } void InnerWidget::changeOpenedFolder(Data::Folder *folder) { @@ -919,14 +902,14 @@ void InnerWidget::rebuildCommunitySections() { if (_shownList->getRow(Key(history))) { continue; } - auto row = std::make_unique(Key(history), 0, 0); - row->recountHeight(_narrowRatio, FilterId()); if (Data::IsCommunityChatViewable(linked)) { - _communityViewable.push_back(std::move(row)); + _communityViewable.add(history, _narrowRatio); } else { - _communityRequestable.push_back(std::move(row)); + _communityRequestable.add(history, _narrowRatio); } } + _communityViewable.finalize(); + _communityRequestable.finalize(); } void InnerWidget::changeOpenedCommunity(Data::CommunityInfo *community) { @@ -1318,23 +1301,31 @@ void InnerWidget::paintEvent(QPaintEvent *e) { }; const auto paintSection = [&]( int sectionTop, - const std::vector> &rows, + const CommunityRowsView &view, const QString &text, int flatBase) { - if (rows.empty()) { + if (view.empty()) { return; } p.save(); p.translate(0, sectionTop); paintBar(text); - p.translate(0, st::searchedBarHeight); - for (auto i = 0, count = int(rows.size()); i != count; ++i) { + const auto rowsTop = st::searchedBarHeight; + p.translate(0, rowsTop); + const auto localClip = r.translated( + 0, + -(sectionTop + rowsTop)); + view.paint(p, localClip, [&]( + not_null row, + int index, + int top) { const auto selected = isPressed() - ? (_communityPressed == flatBase + i) - : (_communitySelected == flatBase + i); - paintRow(rows[i].get(), selected, false); - p.translate(0, rows[i]->height()); - } + ? (_communityPressed == flatBase + index) + : (_communitySelected == flatBase + index); + p.translate(0, top); + paintRow(row, selected, false); + p.translate(0, -top); + }); p.restore(); }; if (!_shownList->empty()) { @@ -1352,7 +1343,7 @@ void InnerWidget::paintEvent(QPaintEvent *e) { communityRequestableTop(), _communityRequestable, tr::lng_community_chats_requestable(tr::now), - int(_communityViewable.size())); + _communityViewable.size()); p.translate(0, communitySectionsBottom()); } } else if (_state == WidgetState::Filtered) { @@ -2192,28 +2183,21 @@ void InnerWidget::selectByMouse(QPoint globalPosition) { const auto pick = [&]( int sectionTop, int flatBase, - const std::vector> &rows) { - if (rows.empty()) { + const CommunityRowsView &view) { + if (view.empty()) { return; } - auto rowTop = sectionTop + st::searchedBarHeight; - if (mouseY < rowTop) { - return; - } - const auto count = int(rows.size()); - for (auto i = 0; i != count; ++i) { - const auto bottom = rowTop + rows[i]->height(); - if (mouseY < bottom) { - communitySelected = flatBase + i; - return; - } - rowTop = bottom; + const auto localY = mouseY + - (sectionTop + st::searchedBarHeight); + const auto index = view.indexByY(localY); + if (index >= 0) { + communitySelected = flatBase + index; } }; pick(communityViewableTop(), 0, _communityViewable); pick( communityRequestableTop(), - int(_communityViewable.size()), + _communityViewable.size(), _communityRequestable); } if (_collapsedSelected != collapsedSelected diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h index 890e801ffe..1dcd839429 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/timer.h" #include "data/data_messages.h" #include "dialogs/ui/dialogs_quick_action_context.h" +#include "dialogs/dialogs_community_chats.h" #include "dialogs/dialogs_inner_widget_accessibility.h" #include "dialogs/dialogs_key.h" #include "lang/lang_keys.h" @@ -482,8 +483,6 @@ private: [[nodiscard]] int communityViewableTop() const; [[nodiscard]] int communityRequestableTop() const; [[nodiscard]] int communitySectionsBottom() const; - [[nodiscard]] int communityListHeight( - const std::vector> &rows) const; [[nodiscard]] int communityRowCount() const; [[nodiscard]] Row *communityRowAt(int index) const; [[nodiscard]] int communityRowAbsoluteTop(int index) const; @@ -725,8 +724,8 @@ private: int _searchedSelected = -1; int _searchedPressed = -1; - std::vector> _communityViewable; - std::vector> _communityRequestable; + CommunityRowsView _communityViewable; + CommunityRowsView _communityRequestable; int _communitySelected = -1; int _communityPressed = -1; rpl::lifetime _openedCommunityLifetime;