peer_list_box.h 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194
  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 "ui/rp_widget.h"
  9. #include "ui/empty_userpic.h"
  10. #include "ui/unread_badge.h"
  11. #include "ui/userpic_view.h"
  12. #include "ui/layers/box_content.h"
  13. #include "base/timer.h"
  14. namespace style {
  15. struct PeerList;
  16. struct PeerListItem;
  17. struct MultiSelect;
  18. } // namespace style
  19. namespace Main {
  20. class Session;
  21. class SessionShow;
  22. } // namespace Main
  23. namespace Ui {
  24. class RippleAnimation;
  25. class RoundImageCheckbox;
  26. class MultiSelect;
  27. template <typename Widget>
  28. class SlideWrap;
  29. class FlatLabel;
  30. struct ScrollToRequest;
  31. class PopupMenu;
  32. struct OutlineSegment;
  33. } // namespace Ui
  34. using PaintRoundImageCallback = Fn<void(
  35. Painter &p,
  36. int x,
  37. int y,
  38. int outerWidth,
  39. int size)>;
  40. [[nodiscard]] PaintRoundImageCallback PaintUserpicCallback(
  41. not_null<PeerData*> peer,
  42. bool respectSavedMessagesChat);
  43. [[nodiscard]] PaintRoundImageCallback ForceRoundUserpicCallback(
  44. not_null<PeerData*> peer);
  45. using PeerListRowId = uint64;
  46. [[nodiscard]] PeerListRowId UniqueRowIdFromString(const QString &d);
  47. class PeerListRow {
  48. public:
  49. enum class State {
  50. Active,
  51. Disabled,
  52. DisabledChecked,
  53. };
  54. explicit PeerListRow(not_null<PeerData*> peer);
  55. PeerListRow(not_null<PeerData*> peer, PeerListRowId id);
  56. virtual ~PeerListRow();
  57. void setDisabledState(State state) {
  58. _disabledState = state;
  59. }
  60. // Checked state is controlled by the box with multiselect,
  61. // not by the row itself, so there is no setChecked() method.
  62. // We can query the checked state from row, but before it is
  63. // added to the box it is always false.
  64. [[nodiscard]] bool checked() const;
  65. [[nodiscard]] bool special() const {
  66. return !_peer;
  67. }
  68. [[nodiscard]] not_null<PeerData*> peer() const {
  69. Expects(!special());
  70. return _peer;
  71. }
  72. [[nodiscard]] PeerListRowId id() const {
  73. return _id;
  74. }
  75. [[nodiscard]] Ui::PeerUserpicView &ensureUserpicView();
  76. [[nodiscard]] virtual QString generateName();
  77. [[nodiscard]] virtual QString generateShortName();
  78. [[nodiscard]] virtual auto generatePaintUserpicCallback(
  79. bool forceRound) -> PaintRoundImageCallback;
  80. virtual void paintUserpicOverlay(
  81. Painter &p,
  82. const style::PeerListItem &st,
  83. int x,
  84. int y,
  85. int outerWidth) {
  86. }
  87. [[nodiscard]] virtual auto generateNameFirstLetters() const
  88. -> const base::flat_set<QChar> &;
  89. [[nodiscard]] virtual auto generateNameWords() const
  90. -> const base::flat_set<QString> &;
  91. [[nodiscard]] virtual const style::PeerListItem &computeSt(
  92. const style::PeerListItem &st) const;
  93. virtual void preloadUserpic();
  94. void setCustomStatus(const QString &status, bool active = false);
  95. void clearCustomStatus();
  96. // Box interface.
  97. virtual int paintNameIconGetWidth(
  98. Painter &p,
  99. Fn<void()> repaint,
  100. crl::time now,
  101. int nameLeft,
  102. int nameTop,
  103. int nameWidth,
  104. int availableWidth,
  105. int outerWidth,
  106. bool selected);
  107. virtual QSize rightActionSize() const {
  108. return QSize();
  109. }
  110. virtual QMargins rightActionMargins() const {
  111. return QMargins();
  112. }
  113. virtual bool rightActionDisabled() const {
  114. return false;
  115. }
  116. virtual void rightActionPaint(
  117. Painter &p,
  118. int x,
  119. int y,
  120. int outerWidth,
  121. bool selected,
  122. bool actionSelected) {
  123. }
  124. virtual void rightActionAddRipple(
  125. QPoint point,
  126. Fn<void()> updateCallback) {
  127. }
  128. virtual void rightActionStopLastRipple() {
  129. }
  130. [[nodiscard]] virtual float64 opacity() {
  131. return 1.;
  132. }
  133. // By default elements code falls back to a simple right action code.
  134. virtual int elementsCount() const;
  135. virtual QRect elementGeometry(int element, int outerWidth) const;
  136. virtual bool elementDisabled(int element) const;
  137. virtual bool elementOnlySelect(int element) const;
  138. virtual void elementAddRipple(
  139. int element,
  140. QPoint point,
  141. Fn<void()> updateCallback);
  142. virtual void elementsStopLastRipple();
  143. virtual void elementsPaint(
  144. Painter &p,
  145. int outerWidth,
  146. bool selected,
  147. int selectedElement);
  148. virtual void refreshName(const style::PeerListItem &st);
  149. const Ui::Text::String &name() const {
  150. return _name;
  151. }
  152. virtual bool useForumLikeUserpic() const;
  153. enum class StatusType {
  154. Online,
  155. LastSeen,
  156. Custom,
  157. CustomActive,
  158. };
  159. virtual void refreshStatus();
  160. crl::time refreshStatusTime() const;
  161. void setAbsoluteIndex(int index) {
  162. _absoluteIndex = index;
  163. }
  164. int absoluteIndex() const {
  165. return _absoluteIndex;
  166. }
  167. bool disabled() const {
  168. return (_disabledState != State::Active);
  169. }
  170. bool isSearchResult() const {
  171. return _isSearchResult;
  172. }
  173. void setIsSearchResult(bool isSearchResult) {
  174. _isSearchResult = isSearchResult;
  175. }
  176. void setSavedMessagesChatStatus(QString savedMessagesStatus) {
  177. _savedMessagesStatus = savedMessagesStatus;
  178. }
  179. void setIsRepliesMessagesChat(bool isRepliesMessagesChat) {
  180. _isRepliesMessagesChat = isRepliesMessagesChat;
  181. }
  182. void setIsVerifyCodesChat(bool isVerifyCodesChat) {
  183. _isVerifyCodesChat = isVerifyCodesChat;
  184. }
  185. template <typename UpdateCallback>
  186. void setChecked(
  187. bool checked,
  188. const style::RoundImageCheckbox &st,
  189. anim::type animated,
  190. UpdateCallback callback) {
  191. if (checked && !_checkbox) {
  192. createCheckbox(st, std::move(callback));
  193. }
  194. setCheckedInternal(checked, animated);
  195. }
  196. void setCustomizedCheckSegments(
  197. std::vector<Ui::OutlineSegment> segments);
  198. void setHidden(bool hidden) {
  199. _hidden = hidden;
  200. }
  201. [[nodiscard]] bool hidden() const {
  202. return _hidden;
  203. }
  204. void finishCheckedAnimation();
  205. void invalidatePixmapsCache();
  206. template <typename MaskGenerator, typename UpdateCallback>
  207. void addRipple(
  208. const style::PeerListItem &st,
  209. MaskGenerator &&maskGenerator,
  210. QPoint point,
  211. UpdateCallback &&updateCallback);
  212. void stopLastRipple();
  213. void paintRipple(
  214. Painter &p,
  215. const style::PeerListItem &st,
  216. int x,
  217. int y,
  218. int outerWidth);
  219. void paintUserpic(
  220. Painter &p,
  221. const style::PeerListItem &st,
  222. int x,
  223. int y,
  224. int outerWidth);
  225. float64 checkedRatio();
  226. void setNameFirstLetters(const base::flat_set<QChar> &firstLetters) {
  227. _nameFirstLetters = firstLetters;
  228. }
  229. const base::flat_set<QChar> &nameFirstLetters() const {
  230. return _nameFirstLetters;
  231. }
  232. void setSkipPeerBadge(bool skip) {
  233. _skipPeerBadge = skip;
  234. }
  235. virtual void lazyInitialize(const style::PeerListItem &st);
  236. virtual void paintStatusText(
  237. Painter &p,
  238. const style::PeerListItem &st,
  239. int x,
  240. int y,
  241. int availableWidth,
  242. int outerWidth,
  243. bool selected);
  244. protected:
  245. bool isInitialized() const {
  246. return _initialized;
  247. }
  248. explicit PeerListRow(PeerListRowId id);
  249. private:
  250. void createCheckbox(
  251. const style::RoundImageCheckbox &st,
  252. Fn<void()> updateCallback);
  253. void setCheckedInternal(bool checked, anim::type animated);
  254. void paintDisabledCheckUserpic(
  255. Painter &p,
  256. const style::PeerListItem &st,
  257. int x,
  258. int y,
  259. int outerWidth) const;
  260. void setStatusText(const QString &text);
  261. PeerListRowId _id = 0;
  262. PeerData *_peer = nullptr;
  263. mutable Ui::PeerUserpicView _userpic;
  264. std::unique_ptr<Ui::RippleAnimation> _ripple;
  265. std::unique_ptr<Ui::RoundImageCheckbox> _checkbox;
  266. Ui::Text::String _name;
  267. Ui::Text::String _status;
  268. Ui::PeerBadge _badge;
  269. StatusType _statusType = StatusType::Online;
  270. crl::time _statusValidTill = 0;
  271. base::flat_set<QChar> _nameFirstLetters;
  272. QString _savedMessagesStatus;
  273. int _absoluteIndex = -1;
  274. State _disabledState = State::Active;
  275. bool _hidden : 1 = false;
  276. bool _initialized : 1 = false;
  277. bool _isSearchResult : 1 = false;
  278. bool _isRepliesMessagesChat : 1 = false;
  279. bool _isVerifyCodesChat : 1 = false;
  280. bool _skipPeerBadge : 1 = false;
  281. };
  282. enum class PeerListSearchMode {
  283. Disabled,
  284. Enabled,
  285. };
  286. struct PeerListState;
  287. class PeerListDelegate {
  288. public:
  289. virtual void peerListSetTitle(rpl::producer<QString> title) = 0;
  290. virtual void peerListSetAdditionalTitle(rpl::producer<QString> title) = 0;
  291. virtual void peerListSetHideEmpty(bool hide) = 0;
  292. virtual void peerListSetDescription(object_ptr<Ui::FlatLabel> description) = 0;
  293. virtual void peerListSetSearchNoResults(object_ptr<Ui::FlatLabel> noResults) = 0;
  294. virtual void peerListSetAboveWidget(object_ptr<Ui::RpWidget> aboveWidget) = 0;
  295. virtual void peerListSetAboveSearchWidget(object_ptr<Ui::RpWidget> aboveWidget) = 0;
  296. virtual void peerListSetBelowWidget(object_ptr<Ui::RpWidget> belowWidget) = 0;
  297. virtual void peerListMouseLeftGeometry() = 0;
  298. virtual void peerListSetSearchMode(PeerListSearchMode mode) = 0;
  299. virtual void peerListAppendRow(std::unique_ptr<PeerListRow> row) = 0;
  300. virtual void peerListAppendSearchRow(std::unique_ptr<PeerListRow> row) = 0;
  301. virtual void peerListAppendFoundRow(not_null<PeerListRow*> row) = 0;
  302. virtual void peerListPrependRow(std::unique_ptr<PeerListRow> row) = 0;
  303. virtual void peerListPrependRowFromSearchResult(not_null<PeerListRow*> row) = 0;
  304. virtual void peerListUpdateRow(not_null<PeerListRow*> row) = 0;
  305. virtual void peerListRemoveRow(not_null<PeerListRow*> row) = 0;
  306. virtual void peerListConvertRowToSearchResult(not_null<PeerListRow*> row) = 0;
  307. virtual bool peerListIsRowChecked(not_null<PeerListRow*> row) = 0;
  308. virtual void peerListSetRowChecked(not_null<PeerListRow*> row, bool checked) = 0;
  309. virtual void peerListSetRowHidden(not_null<PeerListRow*> row, bool hidden) = 0;
  310. virtual void peerListSetForeignRowChecked(
  311. not_null<PeerListRow*> row,
  312. bool checked,
  313. anim::type animated) = 0;
  314. virtual not_null<PeerListRow*> peerListRowAt(int index) = 0;
  315. virtual void peerListRefreshRows() = 0;
  316. virtual void peerListScrollToTop() = 0;
  317. virtual int peerListFullRowsCount() = 0;
  318. virtual PeerListRow *peerListFindRow(PeerListRowId id) = 0;
  319. virtual int peerListSearchRowsCount() = 0;
  320. virtual not_null<PeerListRow*> peerListSearchRowAt(int index) = 0;
  321. virtual std::optional<QPoint> peerListLastRowMousePosition() = 0;
  322. virtual void peerListSortRows(Fn<bool(const PeerListRow &a, const PeerListRow &b)> compare) = 0;
  323. virtual int peerListPartitionRows(Fn<bool(const PeerListRow &a)> border) = 0;
  324. virtual std::shared_ptr<Main::SessionShow> peerListUiShow() = 0;
  325. virtual void peerListSelectSkip(int direction) = 0;
  326. virtual void peerListPressLeftToContextMenu(bool shown) = 0;
  327. virtual bool peerListTrackRowPressFromGlobal(QPoint globalPosition) = 0;
  328. template <typename PeerDataRange>
  329. void peerListAddSelectedPeers(PeerDataRange &&range) {
  330. for (const auto peer : range) {
  331. peerListAddSelectedPeerInBunch(peer);
  332. }
  333. peerListFinishSelectedRowsBunch();
  334. }
  335. template <typename PeerListRowRange>
  336. void peerListAddSelectedRows(PeerListRowRange &&range) {
  337. for (const auto row : range) {
  338. peerListAddSelectedRowInBunch(row);
  339. }
  340. peerListFinishSelectedRowsBunch();
  341. }
  342. virtual void peerListShowRowMenu(
  343. not_null<PeerListRow*> row,
  344. bool highlightRow,
  345. Fn<void(not_null<Ui::PopupMenu*>)> destroyed = nullptr) = 0;
  346. virtual int peerListSelectedRowsCount() = 0;
  347. virtual std::unique_ptr<PeerListState> peerListSaveState() const = 0;
  348. virtual void peerListRestoreState(
  349. std::unique_ptr<PeerListState> state) = 0;
  350. virtual ~PeerListDelegate() = default;
  351. private:
  352. virtual void peerListAddSelectedPeerInBunch(not_null<PeerData*> peer) = 0;
  353. virtual void peerListAddSelectedRowInBunch(not_null<PeerListRow*> row) = 0;
  354. virtual void peerListFinishSelectedRowsBunch() = 0;
  355. };
  356. class PeerListSearchDelegate {
  357. public:
  358. virtual void peerListSearchAddRow(not_null<PeerData*> peer) = 0;
  359. virtual void peerListSearchAddRow(PeerListRowId id) = 0;
  360. virtual void peerListSearchRefreshRows() = 0;
  361. virtual ~PeerListSearchDelegate() = default;
  362. };
  363. class PeerListSearchController {
  364. public:
  365. struct SavedStateBase {
  366. virtual ~SavedStateBase() = default;
  367. };
  368. virtual void searchQuery(const QString &query) = 0;
  369. virtual bool isLoading() = 0;
  370. virtual bool loadMoreRows() = 0;
  371. virtual ~PeerListSearchController() = default;
  372. void setDelegate(not_null<PeerListSearchDelegate*> delegate) {
  373. _delegate = delegate;
  374. }
  375. virtual std::unique_ptr<SavedStateBase> saveState() const {
  376. return nullptr;
  377. }
  378. virtual void restoreState(
  379. std::unique_ptr<SavedStateBase> state) {
  380. }
  381. rpl::lifetime &lifetime() {
  382. return _lifetime;
  383. }
  384. protected:
  385. not_null<PeerListSearchDelegate*> delegate() const {
  386. return _delegate;
  387. }
  388. private:
  389. PeerListSearchDelegate *_delegate = nullptr;
  390. rpl::lifetime _lifetime;
  391. };
  392. class PeerListController : public PeerListSearchDelegate {
  393. public:
  394. struct SavedStateBase {
  395. virtual ~SavedStateBase() = default;
  396. };
  397. // Search works only with RowId == peer->id.
  398. PeerListController(
  399. std::unique_ptr<PeerListSearchController> searchController = {});
  400. void setDelegate(not_null<PeerListDelegate*> delegate) {
  401. _delegate = delegate;
  402. prepare();
  403. }
  404. [[nodiscard]] not_null<PeerListDelegate*> delegate() const {
  405. return _delegate;
  406. }
  407. void setStyleOverrides(
  408. const style::PeerList *listSt,
  409. const style::MultiSelect *selectSt = nullptr) {
  410. _listSt = listSt;
  411. _selectSt = selectSt;
  412. }
  413. const style::PeerList *listSt() const {
  414. return _listSt;
  415. }
  416. const style::MultiSelect *selectSt() const {
  417. return _selectSt;
  418. }
  419. const style::PeerList &computeListSt() const;
  420. const style::MultiSelect &computeSelectSt() const;
  421. virtual Main::Session &session() const = 0;
  422. virtual void prepare() = 0;
  423. virtual void showFinished() {
  424. }
  425. virtual void rowClicked(not_null<PeerListRow*> row) = 0;
  426. virtual void rowMiddleClicked(not_null<PeerListRow*> row) {
  427. }
  428. virtual void rowRightActionClicked(not_null<PeerListRow*> row) {
  429. }
  430. // By default elements code falls back to a simple right action code.
  431. virtual void rowElementClicked(not_null<PeerListRow*> row, int element) {
  432. if (element == 1) {
  433. rowRightActionClicked(row);
  434. }
  435. }
  436. virtual bool rowTrackPress(not_null<PeerListRow*> row) {
  437. return false;
  438. }
  439. virtual void rowTrackPressCancel() {
  440. }
  441. virtual bool rowTrackPressSkipMouseSelection() {
  442. return false;
  443. }
  444. virtual void loadMoreRows() {
  445. }
  446. virtual void itemDeselectedHook(not_null<PeerData*> peer) {
  447. }
  448. virtual bool isForeignRow(PeerListRowId itemId) {
  449. return false;
  450. }
  451. virtual bool handleDeselectForeignRow(PeerListRowId itemId) {
  452. return false;
  453. }
  454. virtual base::unique_qptr<Ui::PopupMenu> rowContextMenu(
  455. QWidget *parent,
  456. not_null<PeerListRow*> row);
  457. bool isSearchLoading() const {
  458. return _searchController ? _searchController->isLoading() : false;
  459. }
  460. virtual std::unique_ptr<PeerListRow> createSearchRow(
  461. not_null<PeerData*> peer) {
  462. return nullptr;
  463. }
  464. virtual std::unique_ptr<PeerListRow> createSearchRow(PeerListRowId id);
  465. virtual std::unique_ptr<PeerListRow> createRestoredRow(
  466. not_null<PeerData*> peer) {
  467. return nullptr;
  468. }
  469. virtual std::unique_ptr<PeerListState> saveState() const;
  470. virtual void restoreState(
  471. std::unique_ptr<PeerListState> state);
  472. [[nodiscard]] virtual int contentWidth() const;
  473. [[nodiscard]] virtual rpl::producer<int> boxHeightValue() const;
  474. [[nodiscard]] virtual int descriptionTopSkipMin() const;
  475. [[nodiscard]] bool isRowSelected(not_null<PeerListRow*> row) {
  476. return delegate()->peerListIsRowChecked(row);
  477. }
  478. virtual bool trackSelectedList() {
  479. return true;
  480. }
  481. virtual bool searchInLocal() {
  482. return true;
  483. }
  484. [[nodiscard]] bool hasComplexSearch() const;
  485. void search(const QString &query);
  486. void peerListSearchAddRow(not_null<PeerData*> peer) override;
  487. void peerListSearchAddRow(PeerListRowId id) override;
  488. void peerListSearchRefreshRows() override;
  489. [[nodiscard]] virtual QString savedMessagesChatStatus() const {
  490. return QString();
  491. }
  492. [[nodiscard]] virtual int customRowHeight() {
  493. Unexpected("PeerListController::customRowHeight.");
  494. }
  495. virtual void customRowPaint(
  496. Painter &p,
  497. crl::time now,
  498. not_null<PeerListRow*> row,
  499. bool selected) {
  500. Unexpected("PeerListController::customRowPaint.");
  501. }
  502. [[nodiscard]] virtual bool customRowSelectionPoint(
  503. not_null<PeerListRow*> row,
  504. int x,
  505. int y) {
  506. Unexpected("PeerListController::customRowSelectionPoint.");
  507. }
  508. [[nodiscard]] virtual Fn<QImage()> customRowRippleMaskGenerator() {
  509. Unexpected("PeerListController::customRowRippleMaskGenerator.");
  510. }
  511. virtual bool overrideKeyboardNavigation(
  512. int direction,
  513. int fromIndex,
  514. int toIndex) {
  515. return false;
  516. }
  517. [[nodiscard]] rpl::lifetime &lifetime() {
  518. return _lifetime;
  519. }
  520. virtual ~PeerListController() = default;
  521. protected:
  522. PeerListSearchController *searchController() const {
  523. return _searchController.get();
  524. }
  525. void setDescriptionText(const QString &text);
  526. void setSearchNoResultsText(const QString &text);
  527. void setDescription(object_ptr<Ui::FlatLabel> description) {
  528. delegate()->peerListSetDescription(std::move(description));
  529. }
  530. void setSearchNoResults(object_ptr<Ui::FlatLabel> noResults) {
  531. delegate()->peerListSetSearchNoResults(std::move(noResults));
  532. }
  533. void sortByName();
  534. private:
  535. PeerListDelegate *_delegate = nullptr;
  536. std::unique_ptr<PeerListSearchController> _searchController = nullptr;
  537. const style::PeerList *_listSt = nullptr;
  538. const style::MultiSelect *_selectSt = nullptr;
  539. rpl::lifetime _lifetime;
  540. };
  541. struct PeerListState {
  542. PeerListState() = default;
  543. PeerListState(PeerListState &&other) = delete;
  544. PeerListState &operator=(PeerListState &&other) = delete;
  545. std::unique_ptr<PeerListController::SavedStateBase> controllerState;
  546. std::vector<not_null<PeerData*>> list;
  547. std::vector<not_null<PeerData*>> filterResults;
  548. QString searchQuery;
  549. };
  550. class PeerListContent : public Ui::RpWidget {
  551. public:
  552. PeerListContent(
  553. QWidget *parent,
  554. not_null<PeerListController*> controller);
  555. struct SkipResult {
  556. int shouldMoveTo = 0;
  557. int reallyMovedTo = 0;
  558. };
  559. SkipResult selectSkip(int direction);
  560. void selectSkipPage(int height, int direction);
  561. void selectLast();
  562. enum class Mode {
  563. Default,
  564. Custom,
  565. };
  566. void setMode(Mode mode);
  567. [[nodiscard]] rpl::producer<int> selectedIndexValue() const;
  568. [[nodiscard]] int selectedIndex() const;
  569. [[nodiscard]] bool hasSelection() const;
  570. [[nodiscard]] bool hasPressed() const;
  571. void clearSelection();
  572. void searchQueryChanged(QString query);
  573. bool submitted();
  574. PeerListRowId updateFromParentDrag(QPoint globalPosition);
  575. void dragLeft();
  576. void setIgnoreHiddenRowsOnSearch(bool value);
  577. // Interface for the controller.
  578. void appendRow(std::unique_ptr<PeerListRow> row);
  579. void appendSearchRow(std::unique_ptr<PeerListRow> row);
  580. void appendFoundRow(not_null<PeerListRow*> row);
  581. void prependRow(std::unique_ptr<PeerListRow> row);
  582. void prependRowFromSearchResult(not_null<PeerListRow*> row);
  583. PeerListRow *findRow(PeerListRowId id);
  584. std::optional<QPoint> lastRowMousePosition() const;
  585. void updateRow(not_null<PeerListRow*> row) {
  586. updateRow(row, RowIndex());
  587. }
  588. void removeRow(not_null<PeerListRow*> row);
  589. void convertRowToSearchResult(not_null<PeerListRow*> row);
  590. int fullRowsCount() const;
  591. not_null<PeerListRow*> rowAt(int index) const;
  592. int searchRowsCount() const;
  593. not_null<PeerListRow*> searchRowAt(int index) const;
  594. void setDescription(object_ptr<Ui::FlatLabel> description);
  595. void setSearchLoading(object_ptr<Ui::FlatLabel> loading);
  596. void setSearchNoResults(object_ptr<Ui::FlatLabel> noResults);
  597. void setAboveWidget(object_ptr<Ui::RpWidget> widget);
  598. void setAboveSearchWidget(object_ptr<Ui::RpWidget> widget);
  599. void setBelowWidget(object_ptr<Ui::RpWidget> width);
  600. void setHideEmpty(bool hide);
  601. void refreshRows();
  602. void mouseLeftGeometry();
  603. void pressLeftToContextMenu(bool shown);
  604. bool trackRowPressFromGlobal(QPoint globalPosition);
  605. void setSearchMode(PeerListSearchMode mode);
  606. void changeCheckState(
  607. not_null<PeerListRow*> row,
  608. bool checked,
  609. anim::type animated);
  610. void setRowHidden(
  611. not_null<PeerListRow*> row,
  612. bool hidden);
  613. template <typename ReorderCallback>
  614. void reorderRows(ReorderCallback &&callback) {
  615. callback(_rows.begin(), _rows.end());
  616. for (auto &searchEntity : _searchIndex) {
  617. callback(searchEntity.second.begin(), searchEntity.second.end());
  618. }
  619. refreshIndices();
  620. if (!_hiddenRows.empty()) {
  621. callback(_filterResults.begin(), _filterResults.end());
  622. }
  623. update();
  624. }
  625. [[nodiscard]] std::unique_ptr<PeerListState> saveState() const;
  626. void restoreState(std::unique_ptr<PeerListState> state);
  627. void showRowMenu(
  628. not_null<PeerListRow*> row,
  629. bool highlightRow,
  630. Fn<void(not_null<Ui::PopupMenu*>)> destroyed);
  631. [[nodiscard]] auto scrollToRequests() const {
  632. return _scrollToRequests.events();
  633. }
  634. [[nodiscard]] auto noSearchSubmits() const {
  635. return _noSearchSubmits.events();
  636. }
  637. ~PeerListContent();
  638. protected:
  639. int resizeGetHeight(int newWidth) override;
  640. void visibleTopBottomUpdated(
  641. int visibleTop,
  642. int visibleBottom) override;
  643. void paintEvent(QPaintEvent *e) override;
  644. void enterEventHook(QEnterEvent *e) override;
  645. void leaveEventHook(QEvent *e) override;
  646. void mouseMoveEvent(QMouseEvent *e) override;
  647. void mousePressEvent(QMouseEvent *e) override;
  648. void mouseReleaseEvent(QMouseEvent *e) override;
  649. void contextMenuEvent(QContextMenuEvent *e) override;
  650. private:
  651. void refreshIndices();
  652. void removeRowAtIndex(std::vector<std::unique_ptr<PeerListRow>> &from, int index);
  653. void handleNameChanged(not_null<PeerData*> peer);
  654. void invalidatePixmapsCache();
  655. struct RowIndex {
  656. RowIndex() {
  657. }
  658. explicit RowIndex(int value) : value(value) {
  659. }
  660. int value = -1;
  661. };
  662. friend inline bool operator==(RowIndex a, RowIndex b) {
  663. return (a.value == b.value);
  664. }
  665. friend inline bool operator!=(RowIndex a, RowIndex b) {
  666. return !(a == b);
  667. }
  668. struct Selected {
  669. Selected() {
  670. }
  671. Selected(RowIndex index, int element)
  672. : index(index)
  673. , element(element) {
  674. }
  675. Selected(int index, int element)
  676. : index(index)
  677. , element(element) {
  678. }
  679. RowIndex index;
  680. int element = 0;
  681. };
  682. friend inline bool operator==(Selected a, Selected b) {
  683. return (a.index == b.index) && (a.element == b.element);
  684. }
  685. friend inline bool operator!=(Selected a, Selected b) {
  686. return !(a == b);
  687. }
  688. struct SelectedSaved {
  689. SelectedSaved(PeerListRowId id, Selected old)
  690. : id(id), old(old) {
  691. }
  692. PeerListRowId id = 0;
  693. Selected old;
  694. };
  695. void setSelected(Selected selected);
  696. void setPressed(Selected pressed);
  697. void setContexted(Selected contexted);
  698. void restoreSelection();
  699. SelectedSaved saveSelectedData(Selected from);
  700. Selected restoreSelectedData(SelectedSaved from);
  701. void selectByMouse(QPoint globalPosition);
  702. void loadProfilePhotos();
  703. void checkScrollForPreload();
  704. void updateRow(not_null<PeerListRow*> row, RowIndex hint);
  705. void updateRow(RowIndex row);
  706. int getRowTop(RowIndex row) const;
  707. PeerListRow *getRow(RowIndex element);
  708. RowIndex findRowIndex(
  709. not_null<PeerListRow*> row,
  710. RowIndex hint = RowIndex());
  711. QRect getElementRect(
  712. not_null<PeerListRow*> row,
  713. RowIndex index,
  714. int element) const;
  715. bool showRowMenu(
  716. RowIndex index,
  717. PeerListRow *row,
  718. QPoint globalPos,
  719. bool highlightRow,
  720. Fn<void(not_null<Ui::PopupMenu*>)> destroyed = nullptr);
  721. crl::time paintRow(Painter &p, crl::time now, RowIndex index);
  722. void addRowEntry(not_null<PeerListRow*> row);
  723. void addToSearchIndex(not_null<PeerListRow*> row);
  724. bool addingToSearchIndex() const;
  725. void removeFromSearchIndex(not_null<PeerListRow*> row);
  726. void setSearchQuery(const QString &query, const QString &normalizedQuery);
  727. bool showingSearch() const {
  728. return !_hiddenRows.empty() || !_searchQuery.isEmpty();
  729. }
  730. int shownRowsCount() const {
  731. return showingSearch() ? _filterResults.size() : _rows.size();
  732. }
  733. template <typename Callback>
  734. bool enumerateShownRows(Callback callback);
  735. template <typename Callback>
  736. bool enumerateShownRows(int from, int to, Callback callback);
  737. int rowsTop() const;
  738. int labelHeight() const;
  739. void clearSearchRows();
  740. void clearAllContent();
  741. void handleMouseMove(QPoint globalPosition);
  742. void mousePressReleased(Qt::MouseButton button);
  743. void initDecorateWidget(Ui::RpWidget *widget);
  744. const style::PeerList &_st;
  745. not_null<PeerListController*> _controller;
  746. PeerListSearchMode _searchMode = PeerListSearchMode::Disabled;
  747. Mode _mode = Mode::Default;
  748. int _rowHeight = 0;
  749. int _visibleTop = 0;
  750. int _visibleBottom = 0;
  751. Selected _selected;
  752. Selected _pressed;
  753. Selected _contexted;
  754. rpl::variable<int> _selectedIndex = -1;
  755. bool _mouseSelection = false;
  756. std::optional<QPoint> _lastMousePosition;
  757. Qt::MouseButton _pressButton = Qt::LeftButton;
  758. std::optional<QPoint> _trackPressStart;
  759. rpl::event_stream<Ui::ScrollToRequest> _scrollToRequests;
  760. std::vector<std::unique_ptr<PeerListRow>> _rows;
  761. std::map<PeerListRowId, not_null<PeerListRow*>> _rowsById;
  762. std::map<PeerData*, std::vector<not_null<PeerListRow*>>> _rowsByPeer;
  763. std::map<QChar, std::vector<not_null<PeerListRow*>>> _searchIndex;
  764. QString _searchQuery;
  765. QString _normalizedSearchQuery;
  766. QString _mentionHighlight;
  767. std::vector<not_null<PeerListRow*>> _filterResults;
  768. base::flat_set<not_null<PeerListRow*>> _hiddenRows;
  769. int _aboveHeight = 0;
  770. int _belowHeight = 0;
  771. bool _hideEmpty = false;
  772. bool _ignoreHiddenRowsOnSearch = false;
  773. object_ptr<Ui::RpWidget> _aboveWidget = { nullptr };
  774. object_ptr<Ui::RpWidget> _aboveSearchWidget = { nullptr };
  775. object_ptr<Ui::RpWidget> _belowWidget = { nullptr };
  776. object_ptr<Ui::FlatLabel> _description = { nullptr };
  777. object_ptr<Ui::FlatLabel> _searchNoResults = { nullptr };
  778. object_ptr<Ui::FlatLabel> _searchLoading = { nullptr };
  779. object_ptr<Ui::RpWidget> _loadingAnimation = { nullptr };
  780. rpl::event_stream<> _noSearchSubmits;
  781. std::vector<std::unique_ptr<PeerListRow>> _searchRows;
  782. base::Timer _repaintByStatus;
  783. base::unique_qptr<Ui::PopupMenu> _contextMenu;
  784. };
  785. class PeerListContentDelegate : public PeerListDelegate {
  786. public:
  787. void setContent(PeerListContent *content) {
  788. _content = content;
  789. }
  790. void peerListSetHideEmpty(bool hide) override {
  791. _content->setHideEmpty(hide);
  792. }
  793. void peerListAppendRow(
  794. std::unique_ptr<PeerListRow> row) override {
  795. _content->appendRow(std::move(row));
  796. }
  797. void peerListAppendSearchRow(
  798. std::unique_ptr<PeerListRow> row) override {
  799. _content->appendSearchRow(std::move(row));
  800. }
  801. void peerListAppendFoundRow(
  802. not_null<PeerListRow*> row) override {
  803. _content->appendFoundRow(row);
  804. }
  805. void peerListPrependRow(
  806. std::unique_ptr<PeerListRow> row) override {
  807. _content->prependRow(std::move(row));
  808. }
  809. void peerListPrependRowFromSearchResult(
  810. not_null<PeerListRow*> row) override {
  811. _content->prependRowFromSearchResult(row);
  812. }
  813. PeerListRow *peerListFindRow(PeerListRowId id) override {
  814. return _content->findRow(id);
  815. }
  816. std::optional<QPoint> peerListLastRowMousePosition() override {
  817. return _content->lastRowMousePosition();
  818. }
  819. void peerListUpdateRow(not_null<PeerListRow*> row) override {
  820. _content->updateRow(row);
  821. }
  822. void peerListRemoveRow(not_null<PeerListRow*> row) override {
  823. _content->removeRow(row);
  824. }
  825. void peerListConvertRowToSearchResult(
  826. not_null<PeerListRow*> row) override {
  827. _content->convertRowToSearchResult(row);
  828. }
  829. void peerListSetRowChecked(
  830. not_null<PeerListRow*> row,
  831. bool checked) override {
  832. _content->changeCheckState(row, checked, anim::type::normal);
  833. }
  834. void peerListSetRowHidden(
  835. not_null<PeerListRow*> row,
  836. bool hidden) override {
  837. _content->setRowHidden(row, hidden);
  838. }
  839. void peerListSetForeignRowChecked(
  840. not_null<PeerListRow*> row,
  841. bool checked,
  842. anim::type animated) override {
  843. }
  844. int peerListFullRowsCount() override {
  845. return _content->fullRowsCount();
  846. }
  847. not_null<PeerListRow*> peerListRowAt(int index) override {
  848. return _content->rowAt(index);
  849. }
  850. int peerListSearchRowsCount() override {
  851. return _content->searchRowsCount();
  852. }
  853. not_null<PeerListRow*> peerListSearchRowAt(int index) override {
  854. return _content->searchRowAt(index);
  855. }
  856. void peerListRefreshRows() override {
  857. _content->refreshRows();
  858. }
  859. void peerListSetDescription(object_ptr<Ui::FlatLabel> description) override {
  860. _content->setDescription(std::move(description));
  861. }
  862. void peerListSetSearchNoResults(object_ptr<Ui::FlatLabel> noResults) override {
  863. _content->setSearchNoResults(std::move(noResults));
  864. }
  865. void peerListSetAboveWidget(object_ptr<Ui::RpWidget> aboveWidget) override {
  866. _content->setAboveWidget(std::move(aboveWidget));
  867. }
  868. void peerListSetAboveSearchWidget(object_ptr<Ui::RpWidget> aboveWidget) override {
  869. _content->setAboveSearchWidget(std::move(aboveWidget));
  870. }
  871. void peerListSetBelowWidget(object_ptr<Ui::RpWidget> belowWidget) override {
  872. _content->setBelowWidget(std::move(belowWidget));
  873. }
  874. void peerListSetSearchMode(PeerListSearchMode mode) override {
  875. _content->setSearchMode(mode);
  876. }
  877. void peerListMouseLeftGeometry() override {
  878. _content->mouseLeftGeometry();
  879. }
  880. void peerListSortRows(
  881. Fn<bool(const PeerListRow &a, const PeerListRow &b)> compare) override {
  882. _content->reorderRows([&](
  883. auto &&begin,
  884. auto &&end) {
  885. std::stable_sort(begin, end, [&](auto &&a, auto &&b) {
  886. return compare(*a, *b);
  887. });
  888. });
  889. }
  890. int peerListPartitionRows(
  891. Fn<bool(const PeerListRow &a)> border) override {
  892. auto result = 0;
  893. _content->reorderRows([&](
  894. auto &&begin,
  895. auto &&end) {
  896. auto edge = std::stable_partition(begin, end, [&](
  897. auto &&current) {
  898. return border(*current);
  899. });
  900. result = (edge - begin);
  901. });
  902. return result;
  903. }
  904. std::unique_ptr<PeerListState> peerListSaveState() const override {
  905. return _content->saveState();
  906. }
  907. void peerListRestoreState(
  908. std::unique_ptr<PeerListState> state) override {
  909. _content->restoreState(std::move(state));
  910. }
  911. void peerListShowRowMenu(
  912. not_null<PeerListRow*> row,
  913. bool highlightRow,
  914. Fn<void(not_null<Ui::PopupMenu*>)> destroyed = nullptr) override;
  915. void peerListSelectSkip(int direction) override {
  916. _content->selectSkip(direction);
  917. }
  918. void peerListPressLeftToContextMenu(bool shown) override {
  919. _content->pressLeftToContextMenu(shown);
  920. }
  921. bool peerListTrackRowPressFromGlobal(QPoint globalPosition) override {
  922. return _content->trackRowPressFromGlobal(globalPosition);
  923. }
  924. protected:
  925. not_null<PeerListContent*> content() const {
  926. return _content;
  927. }
  928. private:
  929. PeerListContent *_content = nullptr;
  930. };
  931. class PeerListContentDelegateSimple : public PeerListContentDelegate {
  932. public:
  933. void peerListSetTitle(rpl::producer<QString> title) override {
  934. }
  935. void peerListSetAdditionalTitle(rpl::producer<QString> title) override {
  936. }
  937. bool peerListIsRowChecked(not_null<PeerListRow*> row) override {
  938. return false;
  939. }
  940. int peerListSelectedRowsCount() override {
  941. return 0;
  942. }
  943. void peerListScrollToTop() override {
  944. }
  945. void peerListAddSelectedPeerInBunch(
  946. not_null<PeerData*> peer) override {
  947. Unexpected("...DelegateSimple::peerListAddSelectedPeerInBunch");
  948. }
  949. void peerListAddSelectedRowInBunch(not_null<PeerListRow*> row) override {
  950. Unexpected("...DelegateSimple::peerListAddSelectedRowInBunch");
  951. }
  952. void peerListFinishSelectedRowsBunch() override {
  953. Unexpected("...DelegateSimple::peerListFinishSelectedRowsBunch");
  954. }
  955. void peerListSetDescription(
  956. object_ptr<Ui::FlatLabel> description) override {
  957. description.destroy();
  958. }
  959. std::shared_ptr<Main::SessionShow> peerListUiShow() override {
  960. Unexpected("...DelegateSimple::peerListUiShow");
  961. }
  962. };
  963. class PeerListContentDelegateShow : public PeerListContentDelegateSimple {
  964. public:
  965. explicit PeerListContentDelegateShow(
  966. std::shared_ptr<Main::SessionShow> show);
  967. std::shared_ptr<Main::SessionShow> peerListUiShow() override;
  968. private:
  969. std::shared_ptr<Main::SessionShow> _show;
  970. };
  971. class PeerListBox
  972. : public Ui::BoxContent
  973. , public PeerListContentDelegate {
  974. public:
  975. PeerListBox(
  976. QWidget*,
  977. std::unique_ptr<PeerListController> controller,
  978. Fn<void(not_null<PeerListBox*>)> init);
  979. [[nodiscard]] std::vector<PeerListRowId> collectSelectedIds();
  980. [[nodiscard]] std::vector<not_null<PeerData*>> collectSelectedRows();
  981. [[nodiscard]] rpl::producer<int> multiSelectHeightValue() const;
  982. [[nodiscard]] rpl::producer<> noSearchSubmits() const;
  983. void peerListSetTitle(rpl::producer<QString> title) override {
  984. setTitle(std::move(title));
  985. }
  986. void peerListSetAdditionalTitle(rpl::producer<QString> title) override {
  987. setAdditionalTitle(std::move(title));
  988. }
  989. void peerListSetSearchMode(PeerListSearchMode mode) override;
  990. void peerListSetRowChecked(
  991. not_null<PeerListRow*> row,
  992. bool checked) override;
  993. void peerListSetForeignRowChecked(
  994. not_null<PeerListRow*> row,
  995. bool checked,
  996. anim::type animated) override;
  997. bool peerListIsRowChecked(not_null<PeerListRow*> row) override;
  998. int peerListSelectedRowsCount() override;
  999. void peerListScrollToTop() override;
  1000. std::shared_ptr<Main::SessionShow> peerListUiShow() override;
  1001. void setAddedTopScrollSkip(int skip);
  1002. void showFinished() override;
  1003. void appendQueryChangedCallback(Fn<void(QString)>);
  1004. protected:
  1005. void prepare() override;
  1006. void setInnerFocus() override;
  1007. void keyPressEvent(QKeyEvent *e) override;
  1008. void resizeEvent(QResizeEvent *e) override;
  1009. void paintEvent(QPaintEvent *e) override;
  1010. private:
  1011. void peerListAddSelectedPeerInBunch(
  1012. not_null<PeerData*> peer) override {
  1013. addSelectItem(peer, anim::type::instant);
  1014. }
  1015. void peerListAddSelectedRowInBunch(not_null<PeerListRow*> row) override {
  1016. addSelectItem(row, anim::type::instant);
  1017. }
  1018. void peerListFinishSelectedRowsBunch() override;
  1019. void addSelectItem(
  1020. not_null<PeerData*> peer,
  1021. anim::type animated);
  1022. void addSelectItem(
  1023. not_null<PeerListRow*> row,
  1024. anim::type animated);
  1025. void addSelectItem(
  1026. uint64 itemId,
  1027. const QString &text,
  1028. PaintRoundImageCallback paintUserpic,
  1029. anim::type animated);
  1030. void createMultiSelect();
  1031. int getTopScrollSkip() const;
  1032. void updateScrollSkips();
  1033. void searchQueryChanged(const QString &query);
  1034. object_ptr<Ui::SlideWrap<Ui::MultiSelect>> _select = { nullptr };
  1035. const std::shared_ptr<Main::SessionShow> _show;
  1036. Fn<void(QString)> _customQueryChangedCallback;
  1037. std::unique_ptr<PeerListController> _controller;
  1038. Fn<void(PeerListBox*)> _init;
  1039. bool _scrollBottomFixed = false;
  1040. int _addedTopScrollSkip = 0;
  1041. };