expandable_peer_list.cpp 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  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 "ui/widgets/expandable_peer_list.h"
  8. #include "data/data_peer.h"
  9. #include "info/profile/info_profile_values.h"
  10. #include "ui/controls/userpic_button.h"
  11. #include "ui/rect.h"
  12. #include "ui/text/text_utilities.h"
  13. #include "ui/vertical_list.h"
  14. #include "ui/widgets/buttons.h"
  15. #include "ui/widgets/participants_check_view.h"
  16. #include "ui/wrap/slide_wrap.h"
  17. #include "ui/wrap/vertical_layout.h"
  18. #include "styles/style_boxes.h"
  19. #include "styles/style_layers.h"
  20. #include "styles/style_widgets.h"
  21. namespace Ui {
  22. namespace {
  23. class Button final : public Ui::RippleButton {
  24. public:
  25. Button(not_null<QWidget*> parent, int count);
  26. [[nodiscard]] not_null<Ui::AbstractCheckView*> checkView() const;
  27. private:
  28. void paintEvent(QPaintEvent *event) override;
  29. QImage prepareRippleMask() const override;
  30. QPoint prepareRippleStartPosition() const override;
  31. std::unique_ptr<Ui::AbstractCheckView> _view;
  32. };
  33. Button::Button(not_null<QWidget*> parent, int count)
  34. : Ui::RippleButton(parent, st::defaultRippleAnimation)
  35. , _view(std::make_unique<Ui::ParticipantsCheckView>(
  36. count,
  37. st::slideWrapDuration,
  38. false,
  39. [=] { update(); })) {
  40. }
  41. not_null<Ui::AbstractCheckView*> Button::checkView() const {
  42. return _view.get();
  43. }
  44. QImage Button::prepareRippleMask() const {
  45. return _view->prepareRippleMask();
  46. }
  47. QPoint Button::prepareRippleStartPosition() const {
  48. return mapFromGlobal(QCursor::pos());
  49. }
  50. void Button::paintEvent(QPaintEvent *event) {
  51. auto p = QPainter(this);
  52. Ui::RippleButton::paintRipple(p, QPoint());
  53. _view->paint(p, 0, 0, width());
  54. }
  55. } // namespace
  56. void AddExpandablePeerList(
  57. not_null<Ui::Checkbox*> checkbox,
  58. not_null<ExpandablePeerListController*> controller,
  59. not_null<Ui::VerticalLayout*> inner) {
  60. const auto &participants = controller->data.participants;
  61. const auto hideRightButton = controller->data.hideRightButton;
  62. const auto checkTopOnAllInner = controller->data.checkTopOnAllInner;
  63. const auto isSingle = controller->data.skipSingle
  64. ? false
  65. : (participants.size() == 1);
  66. if (isSingle) {
  67. const auto p = participants.front();
  68. controller->collectRequests = [=] { return Participants{ p }; };
  69. return;
  70. }
  71. const auto count = int(participants.size());
  72. const auto button = !hideRightButton
  73. ? Ui::CreateChild<Button>(inner, count)
  74. : nullptr;
  75. if (button) {
  76. button->resize(Ui::ParticipantsCheckView::ComputeSize(count));
  77. }
  78. const auto overlay = Ui::CreateChild<Ui::AbstractButton>(inner);
  79. checkbox->geometryValue(
  80. ) | rpl::start_with_next([=](const QRect &rect) {
  81. overlay->setGeometry(rect);
  82. overlay->raise();
  83. if (button) {
  84. button->moveToRight(
  85. st::moderateBoxExpandRight,
  86. rect.top() + (rect.height() - button->height()) / 2,
  87. inner->width());
  88. button->raise();
  89. }
  90. }, overlay->lifetime());
  91. controller->toggleRequestsFromInner.events(
  92. ) | rpl::start_with_next([=](bool toggled) {
  93. checkbox->setChecked(toggled);
  94. }, checkbox->lifetime());
  95. if (button) {
  96. button->setClickedCallback([=] {
  97. button->checkView()->setChecked(
  98. !button->checkView()->checked(),
  99. anim::type::normal);
  100. controller->toggleRequestsFromTop.fire_copy(
  101. button->checkView()->checked());
  102. });
  103. }
  104. overlay->setClickedCallback([=] {
  105. checkbox->setChecked(!checkbox->checked());
  106. controller->checkAllRequests.fire_copy(checkbox->checked());
  107. });
  108. {
  109. const auto wrap = inner->add(
  110. object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
  111. inner,
  112. object_ptr<Ui::VerticalLayout>(inner)));
  113. wrap->toggle(hideRightButton, anim::type::instant);
  114. controller->toggleRequestsFromTop.events(
  115. ) | rpl::start_with_next([=](bool toggled) {
  116. wrap->toggle(toggled, anim::type::normal);
  117. }, wrap->lifetime());
  118. const auto container = wrap->entity();
  119. Ui::AddSkip(container);
  120. auto &lifetime = wrap->lifetime();
  121. const auto clicks = lifetime.make_state<rpl::event_stream<>>();
  122. const auto checkboxes = ranges::views::all(
  123. participants
  124. ) | ranges::views::transform([&](not_null<PeerData*> peer) {
  125. const auto line = container->add(
  126. object_ptr<Ui::AbstractButton>(container));
  127. const auto &st = st::moderateBoxUserpic;
  128. line->resize(line->width(), st.size.height());
  129. using namespace Info::Profile;
  130. auto name = controller->data.bold
  131. ? NameValue(peer) | rpl::map(Ui::Text::Bold)
  132. : NameValue(peer) | rpl::map(Ui::Text::WithEntities);
  133. const auto userpic
  134. = Ui::CreateChild<Ui::UserpicButton>(line, peer, st);
  135. const auto checkbox = Ui::CreateChild<Ui::Checkbox>(
  136. line,
  137. controller->data.messagesCounts
  138. ? rpl::combine(
  139. std::move(name),
  140. rpl::duplicate(controller->data.messagesCounts)
  141. ) | rpl::map([=](const auto &richName, const auto &map) {
  142. const auto it = map.find(peer->id);
  143. return (it == map.end() || !it->second)
  144. ? richName
  145. : TextWithEntities{
  146. (u"(%1) "_q).arg(it->second)
  147. }.append(richName);
  148. })
  149. : std::move(name) | rpl::type_erased(),
  150. st::defaultBoxCheckbox,
  151. std::make_unique<Ui::CheckView>(
  152. st::defaultCheck,
  153. ranges::contains(controller->data.checked, peer->id)));
  154. checkbox->setCheckAlignment(style::al_left);
  155. rpl::combine(
  156. line->widthValue(),
  157. checkbox->widthValue()
  158. ) | rpl::start_with_next([=](int width, int) {
  159. userpic->moveToLeft(
  160. st::boxRowPadding.left()
  161. + checkbox->checkRect().width()
  162. + st::defaultBoxCheckbox.textPosition.x(),
  163. 0);
  164. const auto skip = st::defaultBoxCheckbox.textPosition.x();
  165. checkbox->resizeToWidth(width
  166. - rect::right(userpic)
  167. - skip
  168. - st::boxRowPadding.right());
  169. checkbox->moveToLeft(
  170. rect::right(userpic) + skip,
  171. ((userpic->height() - checkbox->height()) / 2)
  172. + st::defaultBoxCheckbox.margin.top());
  173. }, checkbox->lifetime());
  174. userpic->setAttribute(Qt::WA_TransparentForMouseEvents);
  175. checkbox->setAttribute(Qt::WA_TransparentForMouseEvents);
  176. line->setClickedCallback([=] {
  177. checkbox->setChecked(!checkbox->checked());
  178. clicks->fire({});
  179. });
  180. return checkbox;
  181. }) | ranges::to_vector;
  182. clicks->events(
  183. ) | rpl::start_with_next([=] {
  184. controller->toggleRequestsFromInner.fire_copy(
  185. checkTopOnAllInner
  186. ? ranges::all_of(checkboxes, &Ui::Checkbox::checked)
  187. : ranges::any_of(checkboxes, &Ui::Checkbox::checked));
  188. }, container->lifetime());
  189. controller->checkAllRequests.events(
  190. ) | rpl::start_with_next([=](bool checked) {
  191. for (const auto &c : checkboxes) {
  192. c->setChecked(checked);
  193. }
  194. }, container->lifetime());
  195. controller->collectRequests = [=] {
  196. auto result = Participants();
  197. for (auto i = 0; i < checkboxes.size(); i++) {
  198. if (checkboxes[i]->checked()) {
  199. result.push_back(participants[i]);
  200. }
  201. }
  202. return result;
  203. };
  204. }
  205. }
  206. } // namespace Ui