dialogs_inner_widget.h 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #pragma once
  8. #include "base/flags.h"
  9. #include "base/object_ptr.h"
  10. #include "base/timer.h"
  11. #include "dialogs/dialogs_key.h"
  12. #include "dialogs/ui/dialogs_quick_action_context.h"
  13. #include "data/data_messages.h"
  14. #include "ui/dragging_scroll_manager.h"
  15. #include "ui/effects/animations.h"
  16. #include "ui/rp_widget.h"
  17. #include "ui/userpic_view.h"
  18. namespace style {
  19. struct DialogRow;
  20. } // namespace style
  21. namespace MTP {
  22. class Error;
  23. } // namespace MTP
  24. namespace Lottie {
  25. class Icon;
  26. } // namespace Lottie
  27. namespace Main {
  28. class Session;
  29. } // namespace Main
  30. namespace Ui {
  31. class IconButton;
  32. class PopupMenu;
  33. class FlatLabel;
  34. struct ScrollToRequest;
  35. namespace Controls {
  36. enum class QuickDialogAction;
  37. } // namespace Controls
  38. } // namespace Ui
  39. namespace Window {
  40. class SessionController;
  41. } // namespace Window
  42. namespace Data {
  43. class ChatFilter;
  44. class Thread;
  45. class Folder;
  46. class Forum;
  47. struct ReactionId;
  48. } // namespace Data
  49. namespace Dialogs::Ui {
  50. using namespace ::Ui;
  51. class VideoUserpic;
  52. struct PaintContext;
  53. struct TopicJumpCache;
  54. } // namespace Dialogs::Ui
  55. namespace Dialogs {
  56. class Row;
  57. class FakeRow;
  58. class IndexedList;
  59. class SearchTags;
  60. class SearchEmpty;
  61. class ChatSearchIn;
  62. enum class HashOrCashtag : uchar;
  63. struct RightButton;
  64. enum class ChatTypeFilter : uchar;
  65. struct ChosenRow {
  66. Key key;
  67. Data::MessagePosition message;
  68. bool userpicClick : 1 = false;
  69. bool filteredRow : 1 = false;
  70. bool newWindow : 1 = false;
  71. };
  72. struct SearchRequestType {
  73. bool migrated : 1 = false;
  74. bool posts : 1 = false;
  75. bool start : 1 = false;
  76. bool peer : 1 = false;
  77. friend inline constexpr auto operator<=>(
  78. SearchRequestType a,
  79. SearchRequestType b) = default;
  80. friend inline constexpr bool operator==(
  81. SearchRequestType a,
  82. SearchRequestType b) = default;
  83. };
  84. enum class SearchRequestDelay : uchar {
  85. InCache,
  86. Instant,
  87. Delayed,
  88. };
  89. enum class WidgetState {
  90. Default,
  91. Filtered,
  92. };
  93. class InnerWidget final : public Ui::RpWidget {
  94. public:
  95. using ChatsFilterTagsKey = int64;
  96. struct ChildListShown {
  97. PeerId peerId = 0;
  98. float64 shown = 0.;
  99. };
  100. InnerWidget(
  101. QWidget *parent,
  102. not_null<Window::SessionController*> controller,
  103. rpl::producer<ChildListShown> childListShown);
  104. void searchReceived(
  105. std::vector<not_null<HistoryItem*>> result,
  106. HistoryItem *inject,
  107. SearchRequestType type,
  108. int fullCount);
  109. void peerSearchReceived(
  110. const QString &query,
  111. const QVector<MTPPeer> &my,
  112. const QVector<MTPPeer> &result);
  113. [[nodiscard]] FilterId filterId() const;
  114. void clearSelection();
  115. void changeOpenedFolder(Data::Folder *folder);
  116. void changeOpenedForum(Data::Forum *forum);
  117. void showSavedSublists();
  118. void selectSkip(int32 direction);
  119. void selectSkipPage(int32 pixels, int32 direction);
  120. void dragLeft();
  121. void setNarrowRatio(float64 narrowRatio);
  122. void clearFilter();
  123. void refresh(bool toTop = false);
  124. void refreshEmpty();
  125. void resizeEmpty();
  126. void showPeerMenu();
  127. [[nodiscard]] bool isUserpicPress() const;
  128. [[nodiscard]] bool isUserpicPressOnWide() const;
  129. void cancelChatPreview();
  130. bool scheduleChatPreview(QPoint positionOverride);
  131. bool showChatPreview();
  132. void chatPreviewShown(bool shown, RowDescriptor row = {});
  133. bool chooseRow(
  134. Qt::KeyboardModifiers modifiers = {},
  135. MsgId pressedTopicRootId = {});
  136. void scrollToEntry(const RowDescriptor &entry);
  137. [[nodiscard]] Data::Folder *shownFolder() const;
  138. [[nodiscard]] Data::Forum *shownForum() const;
  139. [[nodiscard]] WidgetState state() const;
  140. [[nodiscard]] not_null<const style::DialogRow*> st() const {
  141. return _st;
  142. }
  143. [[nodiscard]] bool hasFilteredResults() const;
  144. void searchRequested(bool loading);
  145. void applySearchState(SearchState state);
  146. [[nodiscard]] auto searchTagsChanges() const
  147. -> rpl::producer<std::vector<Data::ReactionId>>;
  148. void onHashtagFilterUpdate(QStringView newFilter);
  149. void appendToFiltered(Key key);
  150. Data::Thread *updateFromParentDrag(QPoint globalPosition);
  151. void setLoadMoreCallback(Fn<void()> callback);
  152. void setLoadMoreFilteredCallback(Fn<void()> callback);
  153. [[nodiscard]] rpl::producer<> listBottomReached() const;
  154. [[nodiscard]] auto changeSearchTabRequests() const
  155. -> rpl::producer<ChatSearchTab>;
  156. [[nodiscard]] auto changeSearchFilterRequests() const
  157. -> rpl::producer<ChatTypeFilter>;
  158. [[nodiscard]] rpl::producer<> cancelSearchRequests() const;
  159. [[nodiscard]] rpl::producer<> cancelSearchFromRequests() const;
  160. [[nodiscard]] rpl::producer<> changeSearchFromRequests() const;
  161. [[nodiscard]] rpl::producer<ChosenRow> chosenRow() const;
  162. [[nodiscard]] rpl::producer<> updated() const;
  163. [[nodiscard]] rpl::producer<int> scrollByDeltaRequests() const;
  164. [[nodiscard]] rpl::producer<Ui::ScrollToRequest> mustScrollTo() const;
  165. [[nodiscard]] rpl::producer<Ui::ScrollToRequest> dialogMoved() const;
  166. [[nodiscard]] rpl::producer<SearchRequestDelay> searchRequests() const;
  167. [[nodiscard]] rpl::producer<QString> completeHashtagRequests() const;
  168. [[nodiscard]] rpl::producer<> refreshHashtagsRequests() const;
  169. [[nodiscard]] RowDescriptor resolveChatNext(RowDescriptor from = {}) const;
  170. [[nodiscard]] RowDescriptor resolveChatPrevious(RowDescriptor from = {}) const;
  171. ~InnerWidget();
  172. void parentGeometryChanged();
  173. bool processTouchEvent(not_null<QTouchEvent*> e);
  174. [[nodiscard]] rpl::producer<> touchCancelRequests() const {
  175. return _touchCancelRequests.events();
  176. }
  177. [[nodiscard]] rpl::producer<UserId> openBotMainAppRequests() const;
  178. void setSwipeContextData(
  179. int64 key,
  180. std::optional<Ui::Controls::SwipeContextData> data);
  181. [[nodiscard]] int64 calcSwipeKey(int top);
  182. void prepareQuickAction(int64 key, Dialogs::Ui::QuickDialogAction);
  183. protected:
  184. void visibleTopBottomUpdated(
  185. int visibleTop,
  186. int visibleBottom) override;
  187. void paintEvent(QPaintEvent *e) override;
  188. void mouseMoveEvent(QMouseEvent *e) override;
  189. void mousePressEvent(QMouseEvent *e) override;
  190. void mouseReleaseEvent(QMouseEvent *e) override;
  191. void resizeEvent(QResizeEvent *e) override;
  192. void enterEventHook(QEnterEvent *e) override;
  193. void leaveEventHook(QEvent *e) override;
  194. void contextMenuEvent(QContextMenuEvent *e) override;
  195. private:
  196. struct CollapsedRow;
  197. struct HashtagResult;
  198. struct PeerSearchResult;
  199. struct TagCache;
  200. enum class JumpSkip {
  201. PreviousOrBegin,
  202. NextOrEnd,
  203. PreviousOrOriginal,
  204. NextOrOriginal,
  205. };
  206. enum class EmptyState : uchar {
  207. None,
  208. Loading,
  209. NoContacts,
  210. EmptyFolder,
  211. EmptyForum,
  212. EmptySavedSublists,
  213. };
  214. struct PinnedRow {
  215. anim::value yadd;
  216. crl::time animStartTime = 0;
  217. };
  218. struct FilterResult {
  219. FilterResult(not_null<Row*> row) : row(row) {
  220. }
  221. not_null<Row*> row;
  222. int top = 0;
  223. [[nodiscard]] Key key() const;
  224. [[nodiscard]] int bottom() const;
  225. };
  226. Main::Session &session() const;
  227. void dialogRowReplaced(Row *oldRow, Row *newRow);
  228. void setState(WidgetState state);
  229. void editOpenedFilter();
  230. void repaintCollapsedFolderRow(not_null<Data::Folder*> folder);
  231. void refreshWithCollapsedRows(bool toTop = false);
  232. bool needCollapsedRowsRefresh() const;
  233. bool chooseCollapsedRow(Qt::KeyboardModifiers modifiers);
  234. void switchToFilter(FilterId filterId);
  235. bool chooseHashtag();
  236. ChosenRow computeChosenRow() const;
  237. bool isRowActive(not_null<Row*> row, const RowDescriptor &entry) const;
  238. bool isSearchResultActive(
  239. not_null<FakeRow*> result,
  240. const RowDescriptor &entry) const;
  241. void repaintDialogRow(FilterId filterId, not_null<Row*> row);
  242. void repaintDialogRow(RowDescriptor row);
  243. void refreshDialogRow(RowDescriptor row);
  244. bool updateEntryHeight(not_null<Entry*> entry);
  245. void clearMouseSelection(bool clearSelection = false);
  246. void mousePressReleased(
  247. QPoint globalPosition,
  248. Qt::MouseButton button,
  249. Qt::KeyboardModifiers modifiers);
  250. void processGlobalForceClick(QPoint globalPosition);
  251. void clearIrrelevantState();
  252. void selectByMouse(QPoint globalPosition);
  253. void preloadRowsData();
  254. void scrollToItem(int top, int height);
  255. void scrollToDefaultSelected();
  256. void setCollapsedPressed(int pressed);
  257. void setPressed(Row *pressed, bool pressedTopicJump, bool pressedBotApp);
  258. void clearPressed();
  259. void setHashtagPressed(int pressed);
  260. void setFilteredPressed(
  261. int pressed,
  262. bool pressedTopicJump,
  263. bool pressedBotApp);
  264. void setPeerSearchPressed(int pressed);
  265. void setPreviewPressed(int pressed);
  266. void setSearchedPressed(int pressed);
  267. bool isPressed() const {
  268. return (_collapsedPressed >= 0)
  269. || _pressed
  270. || (_hashtagPressed >= 0)
  271. || (_filteredPressed >= 0)
  272. || (_peerSearchPressed >= 0)
  273. || (_previewPressed >= 0)
  274. || (_searchedPressed >= 0)
  275. || _pressedMorePosts
  276. || _pressedChatTypeFilter;
  277. }
  278. bool isSelected() const {
  279. return (_collapsedSelected >= 0)
  280. || _selected
  281. || (_hashtagSelected >= 0)
  282. || (_filteredSelected >= 0)
  283. || (_peerSearchSelected >= 0)
  284. || (_previewSelected >= 0)
  285. || (_searchedSelected >= 0)
  286. || _selectedMorePosts
  287. || _selectedChatTypeFilter;
  288. }
  289. bool uniqueSearchResults() const;
  290. bool hasHistoryInResults(not_null<History*> history) const;
  291. int defaultRowTop(not_null<Row*> row) const;
  292. void setupOnlineStatusCheck();
  293. void jumpToTop();
  294. void updateRowCornerStatusShown(not_null<History*> history);
  295. void repaintDialogRowCornerStatus(not_null<History*> history);
  296. bool addBotAppRipple(QPoint origin, Fn<void()> updateCallback);
  297. bool addQuickActionRipple(not_null<Row*> row, Fn<void()> updateCallback);
  298. void setupShortcuts();
  299. RowDescriptor computeJump(
  300. const RowDescriptor &to,
  301. JumpSkip skip) const;
  302. bool jumpToDialogRow(RowDescriptor to);
  303. RowDescriptor chatListEntryBefore(const RowDescriptor &which) const;
  304. RowDescriptor chatListEntryAfter(const RowDescriptor &which) const;
  305. RowDescriptor chatListEntryFirst() const;
  306. RowDescriptor chatListEntryLast() const;
  307. void itemRemoved(not_null<const HistoryItem*> item);
  308. enum class UpdateRowSection {
  309. Default = (1 << 0),
  310. Filtered = (1 << 1),
  311. PeerSearch = (1 << 2),
  312. MessageSearch = (1 << 3),
  313. All = Default | Filtered | PeerSearch | MessageSearch,
  314. };
  315. using UpdateRowSections = base::flags<UpdateRowSection>;
  316. friend inline constexpr auto is_flag_type(UpdateRowSection) { return true; };
  317. void updateSearchResult(not_null<PeerData*> peer);
  318. void updateDialogRow(
  319. RowDescriptor row,
  320. QRect updateRect = QRect(),
  321. UpdateRowSections sections = UpdateRowSection::All);
  322. void fillSupportSearchMenu(not_null<Ui::PopupMenu*> menu);
  323. void fillArchiveSearchMenu(not_null<Ui::PopupMenu*> menu);
  324. void refreshShownList();
  325. [[nodiscard]] int skipTopHeight() const;
  326. [[nodiscard]] int collapsedRowsOffset() const;
  327. [[nodiscard]] int dialogsOffset() const;
  328. [[nodiscard]] int shownHeight(int till = -1) const;
  329. [[nodiscard]] int fixedOnTopCount() const;
  330. [[nodiscard]] int pinnedOffset() const;
  331. [[nodiscard]] int filteredOffset() const;
  332. [[nodiscard]] int filteredIndex(int y) const;
  333. [[nodiscard]] int filteredHeight(int till = -1) const;
  334. [[nodiscard]] int peerSearchOffset() const;
  335. [[nodiscard]] int searchInChatOffset() const;
  336. [[nodiscard]] int previewOffset() const;
  337. [[nodiscard]] int searchedOffset() const;
  338. [[nodiscard]] int searchInChatSkip() const;
  339. [[nodiscard]] int hashtagsOffset() const;
  340. void paintCollapsedRows(
  341. Painter &p,
  342. QRect clip) const;
  343. void paintCollapsedRow(
  344. Painter &p,
  345. not_null<const CollapsedRow*> row,
  346. bool selected) const;
  347. void paintPeerSearchResult(
  348. Painter &p,
  349. not_null<const PeerSearchResult*> result,
  350. const Ui::PaintContext &context);
  351. void paintSearchTags(
  352. Painter &p,
  353. const Ui::PaintContext &context) const;
  354. //void paintSearchInChat(
  355. // Painter &p,
  356. // const Ui::PaintContext &context) const;
  357. //void paintSearchInPeer(
  358. // Painter &p,
  359. // not_null<PeerData*> peer,
  360. // Ui::PeerUserpicView &userpic,
  361. // int top,
  362. // const Ui::Text::String &text) const;
  363. //void paintSearchInSaved(
  364. // Painter &p,
  365. // int top,
  366. // const Ui::Text::String &text) const;
  367. //void paintSearchInReplies(
  368. // Painter &p,
  369. // int top,
  370. // const Ui::Text::String &text) const;
  371. //void paintSearchInTopic(
  372. // Painter &p,
  373. // const Ui::PaintContext &context,
  374. // not_null<Data::ForumTopic*> topic,
  375. // Ui::PeerUserpicView &userpic,
  376. // int top,
  377. // const Ui::Text::String &text) const;
  378. //template <typename PaintUserpic>
  379. //void paintSearchInFilter(
  380. // Painter &p,
  381. // PaintUserpic paintUserpic,
  382. // int top,
  383. // const style::icon *icon,
  384. // const Ui::Text::String &text) const;
  385. void updateSearchIn();
  386. void repaintSearchResult(int index);
  387. void repaintPreviewResult(int index);
  388. [[nodiscard]] bool computeSearchWithPostsPreview() const;
  389. Ui::VideoUserpic *validateVideoUserpic(not_null<Row*> row);
  390. Ui::VideoUserpic *validateVideoUserpic(not_null<History*> history);
  391. Row *shownRowByKey(Key key);
  392. void clearSearchResults(bool clearPeerSearchResults = true);
  393. void clearPreviewResults();
  394. void updateSelectedRow(Key key = Key());
  395. void trackResultsHistory(not_null<History*> history);
  396. [[nodiscard]] QBrush currentBg() const;
  397. [[nodiscard]] RowDescriptor computeChatPreviewRow() const;
  398. [[nodiscard]] const std::vector<Key> &pinnedChatsOrder() const;
  399. void checkReorderPinnedStart(QPoint localPosition);
  400. void startReorderPinned(QPoint localPosition);
  401. int updateReorderIndexGetCount();
  402. bool updateReorderPinned(QPoint localPosition);
  403. void finishReorderPinned();
  404. bool finishReorderOnRelease();
  405. void stopReorderPinned();
  406. int countPinnedIndex(Row *ofRow);
  407. void savePinnedOrder();
  408. bool pinnedShiftAnimationCallback(crl::time now);
  409. void handleChatListEntryRefreshes();
  410. void moveSearchIn();
  411. void dragPinnedFromTouch();
  412. [[nodiscard]] bool hasChatTypeFilter() const;
  413. void saveChatsFilterScrollState(FilterId filterId);
  414. void restoreChatsFilterScrollState(FilterId filterId);
  415. [[nodiscard]] not_null<Ui::QuickActionContext*> ensureQuickAction(
  416. int64 key);
  417. [[nodiscard]] bool lookupIsInBotAppButton(
  418. Row *row,
  419. QPoint localPosition);
  420. [[nodiscard]] RightButton *maybeCacheRightButton(Row *row);
  421. [[nodiscard]] QImage *cacheChatsFilterTag(
  422. const Data::ChatFilter &filter,
  423. uint8 more,
  424. bool active);
  425. const not_null<Window::SessionController*> _controller;
  426. not_null<IndexedList*> _shownList;
  427. FilterId _filterId = 0;
  428. bool _mouseSelection = false;
  429. std::optional<QPoint> _lastMousePosition;
  430. int _lastRowLocalMouseX = -1;
  431. Qt::MouseButton _pressButton = Qt::LeftButton;
  432. Data::Folder *_openedFolder = nullptr;
  433. Data::Forum *_openedForum = nullptr;
  434. rpl::lifetime _openedForumLifetime;
  435. std::vector<std::unique_ptr<CollapsedRow>> _collapsedRows;
  436. not_null<const style::DialogRow*> _st;
  437. mutable std::unique_ptr<Ui::TopicJumpCache> _topicJumpCache;
  438. bool _selectedChatTypeFilter = false;
  439. bool _pressedChatTypeFilter = false;
  440. bool _selectedMorePosts = false;
  441. bool _pressedMorePosts = false;
  442. int _collapsedSelected = -1;
  443. int _collapsedPressed = -1;
  444. bool _skipTopDialog = false;
  445. Row *_selected = nullptr;
  446. Row *_pressed = nullptr;
  447. MsgId _pressedTopicJumpRootId;
  448. bool _selectedTopicJump = false;
  449. bool _pressedTopicJump = false;
  450. RightButton *_pressedBotAppData = nullptr;
  451. bool _selectedBotApp = false;
  452. bool _pressedBotApp = false;
  453. Row *_dragging = nullptr;
  454. int _draggingIndex = -1;
  455. int _aboveIndex = -1;
  456. QPoint _dragStart;
  457. std::vector<PinnedRow> _pinnedRows;
  458. Ui::Animations::Basic _pinnedShiftAnimation;
  459. base::flat_set<Key> _pinnedOnDragStart;
  460. // Remember the last currently dragged row top shift for updating area.
  461. int _aboveTopShift = -1;
  462. int _narrowWidth = 0;
  463. int _visibleTop = 0;
  464. int _visibleBottom = 0;
  465. QString _filter, _hashtagFilter;
  466. std::vector<std::unique_ptr<HashtagResult>> _hashtagResults;
  467. int _hashtagSelected = -1;
  468. int _hashtagPressed = -1;
  469. bool _hashtagDeleteSelected = false;
  470. bool _hashtagDeletePressed = false;
  471. std::vector<FilterResult> _filterResults;
  472. base::flat_map<Key, std::unique_ptr<Row>> _filterResultsGlobal;
  473. int _filteredSelected = -1;
  474. int _filteredPressed = -1;
  475. EmptyState _emptyState = EmptyState::None;
  476. base::flat_set<not_null<History*>> _trackedHistories;
  477. rpl::lifetime _trackedLifetime;
  478. QString _peerSearchQuery;
  479. std::vector<std::unique_ptr<PeerSearchResult>> _peerSearchResults;
  480. int _peerSearchSelected = -1;
  481. int _peerSearchPressed = -1;
  482. std::vector<std::unique_ptr<FakeRow>> _previewResults;
  483. int _previewCount = 0;
  484. int _previewSelected = -1;
  485. int _previewPressed = -1;
  486. int _morePostsWidth = 0;
  487. int _chatTypeFilterWidth = 0;
  488. std::vector<std::unique_ptr<FakeRow>> _searchResults;
  489. int _searchedCount = 0;
  490. int _searchedMigratedCount = 0;
  491. int _searchedSelected = -1;
  492. int _searchedPressed = -1;
  493. WidgetState _state = WidgetState::Default;
  494. std::unique_ptr<ChatSearchIn> _searchIn;
  495. rpl::event_stream<ChatSearchTab> _changeSearchTabRequests;
  496. rpl::event_stream<ChatTypeFilter> _changeSearchFilterRequests;
  497. rpl::event_stream<> _cancelSearchRequests;
  498. rpl::event_stream<> _cancelSearchFromRequests;
  499. rpl::event_stream<> _changeSearchFromRequests;
  500. object_ptr<Ui::RpWidget> _loadingAnimation = { nullptr };
  501. object_ptr<SearchEmpty> _searchEmpty = { nullptr };
  502. SearchState _searchEmptyState;
  503. object_ptr<Ui::FlatLabel> _empty = { nullptr };
  504. Ui::DraggingScrollManager _draggingScroll;
  505. SearchState _searchState;
  506. HashOrCashtag _searchHashOrCashtag = {};
  507. bool _searchWithPostsPreview = false;
  508. History *_searchInMigrated = nullptr;
  509. PeerData *_searchFromShown = nullptr;
  510. Ui::Text::String _searchFromUserText;
  511. std::unique_ptr<SearchTags> _searchTags;
  512. int _searchTagsLeft = 0;
  513. RowDescriptor _menuRow;
  514. base::flat_map<
  515. not_null<PeerData*>,
  516. std::unique_ptr<Ui::VideoUserpic>> _videoUserpics;
  517. base::flat_map<FilterId, int> _chatsFilterScrollStates;
  518. std::unordered_map<ChatsFilterTagsKey, TagCache> _chatsFilterTags;
  519. bool _waitingAllChatListEntryRefreshesForTags = false;
  520. rpl::lifetime _handleChatListEntryTagRefreshesLifetime;
  521. std::unordered_map<PeerId, RightButton> _rightButtons;
  522. Fn<void()> _loadMoreCallback;
  523. Fn<void()> _loadMoreFilteredCallback;
  524. rpl::event_stream<> _listBottomReached;
  525. rpl::event_stream<ChosenRow> _chosenRow;
  526. rpl::event_stream<> _updated;
  527. rpl::event_stream<Ui::ScrollToRequest> _mustScrollTo;
  528. rpl::event_stream<Ui::ScrollToRequest> _dialogMoved;
  529. rpl::event_stream<SearchRequestDelay> _searchRequests;
  530. rpl::event_stream<QString> _completeHashtagRequests;
  531. rpl::event_stream<> _refreshHashtagsRequests;
  532. rpl::event_stream<UserId> _openBotMainAppRequests;
  533. using QuickActionPtr = std::unique_ptr<Ui::QuickActionContext>;
  534. base::flat_map<int64, QuickActionPtr> _quickActions;
  535. RowDescriptor _chatPreviewRow;
  536. bool _chatPreviewScheduled = false;
  537. std::optional<QPoint> _chatPreviewTouchGlobal;
  538. base::Timer _touchDragPinnedTimer;
  539. std::optional<QPoint> _touchDragStartGlobal;
  540. std::optional<QPoint> _touchDragNowGlobal;
  541. rpl::event_stream<> _touchCancelRequests;
  542. rpl::variable<ChildListShown> _childListShown;
  543. float64 _narrowRatio = 0.;
  544. bool _geometryInited = false;
  545. bool _savedSublists = false;
  546. bool _searchLoading = false;
  547. bool _searchWaiting = false;
  548. base::unique_qptr<Ui::PopupMenu> _menu;
  549. };
  550. } // namespace Dialogs