profile_block_peer_list.cpp 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  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. #include "profile/profile_block_peer_list.h"
  8. #include "ui/effects/ripple_animation.h"
  9. #include "ui/text/text_options.h"
  10. #include "ui/painter.h"
  11. #include "data/data_peer.h"
  12. #include "data/data_cloud_file.h"
  13. #include "main/main_session.h"
  14. #include "styles/style_profile.h"
  15. #include "styles/style_widgets.h"
  16. namespace Profile {
  17. PeerListWidget::Item::Item(not_null<PeerData*> peer) : peer(peer) {
  18. }
  19. PeerListWidget::Item::~Item() = default;
  20. PeerListWidget::PeerListWidget(
  21. QWidget *parent,
  22. PeerData *peer,
  23. const QString &title,
  24. const style::PeerListItem &st,
  25. const QString &removeText)
  26. : BlockWidget(parent, peer, title)
  27. , _st(st)
  28. , _removeText(removeText)
  29. , _removeWidth(st::normalFont->width(_removeText)) {
  30. setMouseTracking(true);
  31. peer->session().downloaderTaskFinished(
  32. ) | rpl::start_with_next([=] {
  33. update();
  34. }, lifetime());
  35. }
  36. int PeerListWidget::resizeGetHeight(int newWidth) {
  37. auto newHeight = getListTop();
  38. newHeight += _items.size() * _st.height;
  39. return newHeight + _st.bottom;
  40. }
  41. void PeerListWidget::visibleTopBottomUpdated(int visibleTop, int visibleBottom) {
  42. _visibleTop = visibleTop;
  43. _visibleBottom = visibleBottom;
  44. if (_preloadMoreCallback) {
  45. if (_visibleTop + PreloadHeightsCount * (_visibleBottom - _visibleTop) > height()) {
  46. _preloadMoreCallback();
  47. }
  48. }
  49. }
  50. void PeerListWidget::paintContents(Painter &p) {
  51. auto left = getListLeft();
  52. auto top = getListTop();
  53. const auto count = int(_items.size());
  54. auto from = floorclamp(_visibleTop - top, _st.height, 0, count);
  55. auto to = ceilclamp(_visibleBottom - top, _st.height, 0, count);
  56. for (auto i = from; i < to; ++i) {
  57. auto y = top + i * _st.height;
  58. auto selected = (_pressed >= 0) ? (i == _pressed) : (i == _selected);
  59. auto selectedRemove = selected && _selectedRemove;
  60. if (_pressed >= 0 && !_pressedRemove) {
  61. selectedRemove = false;
  62. }
  63. paintItem(p, left, y, _items[i], selected, selectedRemove);
  64. }
  65. }
  66. void PeerListWidget::paintItem(Painter &p, int x, int y, Item *item, bool selected, bool selectedKick) {
  67. if (_updateItemCallback) {
  68. _updateItemCallback(item);
  69. }
  70. auto memberRowWidth = rowWidth();
  71. if (selected) {
  72. paintItemRect(p, x, y, memberRowWidth, _st.height);
  73. }
  74. if (auto &ripple = item->ripple) {
  75. ripple->paint(p, x, y, width());
  76. if (ripple->empty()) {
  77. ripple.reset();
  78. }
  79. }
  80. int skip = _st.photoPosition.x();
  81. item->peer->paintUserpicLeft(p, item->userpic, x + _st.photoPosition.x(), y + _st.photoPosition.y(), width(), _st.photoSize);
  82. if (item->name.isEmpty()) {
  83. item->name.setText(
  84. st::semiboldTextStyle,
  85. item->peer->name(),
  86. Ui::NameTextOptions());
  87. }
  88. int nameLeft = x + _st.namePosition.x();
  89. int nameTop = y + _st.namePosition.y();
  90. int nameWidth = memberRowWidth - _st.namePosition.x() - skip;
  91. if (item->hasRemoveLink && selected) {
  92. p.setFont(selectedKick ? st::normalFont->underline() : st::normalFont);
  93. p.setPen(st::windowActiveTextFg);
  94. p.drawTextLeft(nameLeft + nameWidth - _removeWidth, nameTop, width(), _removeText, _removeWidth);
  95. nameWidth -= _removeWidth + skip;
  96. } else if (item->rank) {
  97. p.setFont(st::normalFont);
  98. p.setPen(st::windowActiveTextFg);
  99. p.drawTextLeft(nameLeft + nameWidth - item->rankWidth, nameTop, width(), *item->rank, item->rankWidth);
  100. nameWidth -= item->rankWidth + skip;
  101. }
  102. p.setPen(st::profileMemberNameFg);
  103. item->name.drawLeftElided(p, nameLeft, nameTop, nameWidth, width());
  104. if (item->statusHasOnlineColor) {
  105. p.setPen(_st.statusFgActive);
  106. } else {
  107. p.setPen(selected ? _st.statusFgOver : _st.statusFg);
  108. }
  109. p.setFont(st::normalFont);
  110. p.drawTextLeft(x + _st.statusPosition.x(), y + _st.statusPosition.y(), width(), item->statusText);
  111. }
  112. void PeerListWidget::paintItemRect(Painter &p, int x, int y, int w, int h) const {
  113. p.fillRect(style::rtlrect(x, y, w, h, width()), _st.button.textBgOver);
  114. }
  115. void PeerListWidget::mouseMoveEvent(QMouseEvent *e) {
  116. _mousePosition = e->globalPos();
  117. updateSelection();
  118. }
  119. void PeerListWidget::mousePressEvent(QMouseEvent *e) {
  120. _mousePosition = e->globalPos();
  121. updateSelection();
  122. _pressButton = e->button();
  123. _pressed = _selected;
  124. _pressedRemove = _selectedRemove;
  125. if (_pressed >= 0 && !_pressedRemove) {
  126. const auto item = _items[_pressed];
  127. if (!item->ripple) {
  128. auto memberRowWidth = rowWidth();
  129. auto mask = Ui::RippleAnimation::RectMask(QSize(memberRowWidth, _st.height));
  130. item->ripple = std::make_unique<Ui::RippleAnimation>(_st.button.ripple, std::move(mask), [this, index = _pressed] {
  131. repaintRow(index);
  132. });
  133. }
  134. auto left = getListLeft();
  135. auto top = getListTop() + _st.height * _pressed;
  136. item->ripple->add(e->pos() - QPoint(left, top));
  137. }
  138. }
  139. void PeerListWidget::mouseReleaseEvent(QMouseEvent *e) {
  140. mousePressReleased(e->button());
  141. }
  142. void PeerListWidget::mousePressReleased(Qt::MouseButton button) {
  143. repaintRow(_pressed);
  144. auto pressed = std::exchange(_pressed, -1);
  145. auto pressedRemove = base::take(_pressedRemove);
  146. if (pressed >= 0 && pressed < _items.size()) {
  147. if (const auto &ripple = _items[pressed]->ripple) {
  148. ripple->lastStop();
  149. }
  150. if (pressed == _selected && pressedRemove == _selectedRemove && button == Qt::LeftButton) {
  151. InvokeQueued(this, [this, pressedRemove, peer = _items[pressed]->peer] {
  152. if (auto &callback = (pressedRemove ? _removedCallback : _selectedCallback)) {
  153. callback(peer);
  154. }
  155. });
  156. }
  157. }
  158. setCursor(_selectedRemove ? style::cur_pointer : style::cur_default);
  159. repaintSelectedRow();
  160. }
  161. void PeerListWidget::enterEventHook(QEnterEvent *e) {
  162. _mousePosition = QCursor::pos();
  163. updateSelection();
  164. }
  165. void PeerListWidget::leaveEventHook(QEvent *e) {
  166. _mousePosition = QPoint(-1, -1);
  167. updateSelection();
  168. }
  169. void PeerListWidget::updateSelection() {
  170. auto selected = -1;
  171. auto selectedKick = false;
  172. auto mouse = mapFromGlobal(_mousePosition);
  173. if (rtl()) mouse.setX(width() - mouse.x());
  174. auto left = getListLeft();
  175. auto top = getListTop();
  176. auto memberRowWidth = rowWidth();
  177. if (mouse.x() >= left && mouse.x() < left + memberRowWidth && mouse.y() >= top) {
  178. selected = (mouse.y() - top) / _st.height;
  179. if (selected >= _items.size()) {
  180. selected = -1;
  181. } else if (_items[selected]->hasRemoveLink) {
  182. int skip = _st.photoPosition.x();
  183. int nameLeft = left + _st.namePosition.x();
  184. int nameTop = top + _selected * _st.height + _st.namePosition.y();
  185. int nameWidth = memberRowWidth - _st.namePosition.x() - skip;
  186. if (mouse.x() >= nameLeft + nameWidth - _removeWidth && mouse.x() < nameLeft + nameWidth) {
  187. if (mouse.y() >= nameTop && mouse.y() < nameTop + st::normalFont->height) {
  188. selectedKick = true;
  189. }
  190. }
  191. }
  192. }
  193. setSelected(selected, selectedKick);
  194. }
  195. void PeerListWidget::setSelected(int selected, bool selectedRemove) {
  196. if (_selected == selected && _selectedRemove == selectedRemove) {
  197. return;
  198. }
  199. repaintSelectedRow();
  200. if (_selectedRemove != selectedRemove) {
  201. _selectedRemove = selectedRemove;
  202. if (_pressed < 0) {
  203. setCursor(_selectedRemove ? style::cur_pointer : style::cur_default);
  204. }
  205. }
  206. if (_selected != selected) {
  207. _selected = selected;
  208. repaintSelectedRow();
  209. }
  210. }
  211. void PeerListWidget::repaintSelectedRow() {
  212. repaintRow(_selected);
  213. }
  214. void PeerListWidget::repaintRow(int index) {
  215. if (index >= 0) {
  216. auto left = getListLeft();
  217. rtlupdate(left, getListTop() + index * _st.height, width() - left, _st.height);
  218. }
  219. }
  220. int PeerListWidget::getListLeft() const {
  221. return _st.left;
  222. }
  223. int PeerListWidget::rowWidth() const {
  224. return _st.maximalWidth
  225. ? qMin(width() - getListLeft(), _st.maximalWidth)
  226. : width() - getListLeft();
  227. }
  228. void PeerListWidget::preloadPhotos() {
  229. int top = getListTop();
  230. int preloadFor = (_visibleBottom - _visibleTop) * PreloadHeightsCount;
  231. int from = floorclamp(_visibleTop - top, _st.height, 0, _items.size());
  232. int to = ceilclamp(_visibleBottom + preloadFor - top, _st.height, 0, _items.size());
  233. for (int i = from; i < to; ++i) {
  234. _items[i]->peer->loadUserpic();
  235. }
  236. }
  237. void PeerListWidget::refreshVisibility() {
  238. setVisible(!_items.empty());
  239. }
  240. } // namespace Profile