pinned_bar.cpp 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  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/chat/pinned_bar.h"
  8. #include "ui/chat/message_bar.h"
  9. #include "ui/effects/spoiler_mess.h"
  10. #include "ui/widgets/shadow.h"
  11. #include "ui/widgets/buttons.h"
  12. #include "ui/wrap/fade_wrap.h"
  13. #include "styles/style_chat_helpers.h"
  14. #include "styles/palette.h"
  15. #include <QtGui/QtEvents>
  16. namespace Ui {
  17. PinnedBar::PinnedBar(
  18. not_null<QWidget*> parent,
  19. Fn<bool()> customEmojiPaused,
  20. rpl::producer<> customEmojiPausedChanges)
  21. : _wrap(parent, object_ptr<RpWidget>(parent))
  22. , _shadow(std::make_unique<PlainShadow>(_wrap.parentWidget()))
  23. , _customEmojiPaused(std::move(customEmojiPaused)) {
  24. _wrap.hide(anim::type::instant);
  25. _shadow->hide();
  26. _shadow->showOn(rpl::combine(
  27. _wrap.shownValue(),
  28. _wrap.heightValue(),
  29. rpl::mappers::_1 && rpl::mappers::_2 > 0
  30. ) | rpl::filter([=](bool shown) {
  31. return (shown == _shadow->isHidden());
  32. }));
  33. _wrap.entity()->paintRequest(
  34. ) | rpl::start_with_next([=](QRect clip) {
  35. QPainter(_wrap.entity()).fillRect(clip, st::historyPinnedBg);
  36. }, lifetime());
  37. _wrap.setAttribute(Qt::WA_OpaquePaintEvent);
  38. if (customEmojiPausedChanges) {
  39. std::move(
  40. customEmojiPausedChanges
  41. ) | rpl::start_with_next([=] {
  42. _wrap.entity()->update();
  43. }, lifetime());
  44. }
  45. }
  46. PinnedBar::~PinnedBar() {
  47. _right.button.destroy();
  48. }
  49. void PinnedBar::setContent(rpl::producer<Ui::MessageBarContent> content) {
  50. _contentLifetime.destroy();
  51. auto copy = std::move(
  52. content
  53. ) | rpl::start_spawning(_contentLifetime);
  54. rpl::duplicate(
  55. copy
  56. ) | rpl::filter([=](const MessageBarContent &content) {
  57. return !content.title.isEmpty() || !content.text.text.isEmpty();
  58. }) | rpl::start_with_next([=](MessageBarContent &&content) {
  59. const auto creating = !_bar;
  60. if (creating) {
  61. createControls();
  62. }
  63. // In most cases the new right button should arrive
  64. // before we want to get its width.
  65. const auto right = _right.button ? _right.button->width() : 0;
  66. content.margins = { 0, 0, right, 0 };
  67. _bar->set(std::move(content));
  68. if (creating) {
  69. _bar->finishAnimating();
  70. }
  71. }, _contentLifetime);
  72. std::move(
  73. copy
  74. ) | rpl::map([=](const MessageBarContent &content) {
  75. return content.title.isEmpty() || content.text.text.isEmpty();
  76. }) | rpl::start_with_next_done([=](bool hidden) {
  77. _shouldBeShown = !hidden;
  78. if (!_forceHidden) {
  79. _wrap.toggle(_shouldBeShown, anim::type::normal);
  80. } else if (!_shouldBeShown) {
  81. _bar = nullptr;
  82. }
  83. }, [=] {
  84. _forceHidden = true;
  85. _wrap.toggle(false, anim::type::normal);
  86. }, _contentLifetime);
  87. }
  88. void PinnedBar::setRightButton(object_ptr<Ui::RpWidget> button) {
  89. const auto hasPrevious = (_right.button != nullptr);
  90. if (auto previous = _right.button.release()) {
  91. using Unique = base::unique_qptr<Ui::FadeWrapScaled<Ui::RpWidget>>;
  92. _right.previousButtonLifetime = previous->shownValue(
  93. ) | rpl::filter(!rpl::mappers::_1) | rpl::start_with_next([=] {
  94. _right.previousButtonLifetime.destroy();
  95. });
  96. previous->hide(anim::type::normal);
  97. _right.previousButtonLifetime.make_state<Unique>(Unique{ previous });
  98. }
  99. _right.button.create(_wrap.entity(), std::move(button));
  100. if (_right.button) {
  101. _right.button->setParent(_wrap.entity());
  102. if (hasPrevious) {
  103. _right.button->setDuration(st::defaultMessageBar.duration);
  104. _right.button->show(anim::type::normal);
  105. } else {
  106. _right.button->setDuration(0);
  107. _right.button->show(anim::type::instant);
  108. }
  109. }
  110. if (_bar) {
  111. updateControlsGeometry(_wrap.geometry());
  112. }
  113. }
  114. void PinnedBar::updateControlsGeometry(QRect wrapGeometry) {
  115. _bar->widget()->resizeToWidth(wrapGeometry.width());
  116. if (_right.button) {
  117. _right.button->moveToRight(0, 0);
  118. }
  119. }
  120. void PinnedBar::setShadowGeometryPostprocess(Fn<QRect(QRect)> postprocess) {
  121. _shadowGeometryPostprocess = std::move(postprocess);
  122. updateShadowGeometry(_wrap.geometry());
  123. }
  124. void PinnedBar::updateShadowGeometry(QRect wrapGeometry) {
  125. const auto regular = QRect(
  126. wrapGeometry.x(),
  127. wrapGeometry.y() + wrapGeometry.height(),
  128. wrapGeometry.width(),
  129. st::lineWidth);
  130. _shadow->setGeometry(_shadowGeometryPostprocess
  131. ? _shadowGeometryPostprocess(regular)
  132. : regular);
  133. }
  134. void PinnedBar::createControls() {
  135. Expects(!_bar);
  136. _bar = std::make_unique<MessageBar>(
  137. _wrap.entity(),
  138. st::defaultMessageBar,
  139. _customEmojiPaused);
  140. if (_right.button) {
  141. _right.button->raise();
  142. }
  143. // Clicks.
  144. _bar->widget()->setCursor(style::cur_pointer);
  145. _bar->widget()->events(
  146. ) | rpl::filter([=](not_null<QEvent*> event) {
  147. return (event->type() == QEvent::MouseButtonPress)
  148. && (static_cast<QMouseEvent*>(event.get())->button()
  149. == Qt::LeftButton);
  150. }) | rpl::map([=] {
  151. return _bar->widget()->events(
  152. ) | rpl::filter([](not_null<QEvent*> event) {
  153. return (event->type() == QEvent::MouseButtonRelease);
  154. }) | rpl::take(1) | rpl::filter([=](not_null<QEvent*> event) {
  155. return _bar->widget()->rect().contains(
  156. static_cast<QMouseEvent*>(event.get())->pos());
  157. });
  158. }) | rpl::flatten_latest(
  159. ) | rpl::to_empty | rpl::start_to_stream(
  160. _barClicks,
  161. _bar->widget()->lifetime());
  162. _bar->widget()->move(0, 0);
  163. _bar->widget()->show();
  164. _wrap.entity()->resize(
  165. _wrap.entity()->width(),
  166. _bar->widget()->height());
  167. _wrap.geometryValue(
  168. ) | rpl::start_with_next([=](QRect rect) {
  169. updateShadowGeometry(rect);
  170. updateControlsGeometry(rect);
  171. }, _bar->widget()->lifetime());
  172. _wrap.shownValue(
  173. ) | rpl::skip(
  174. 1
  175. ) | rpl::filter([=](bool shown) {
  176. return !shown && !_forceHidden;
  177. }) | rpl::start_with_next([=] {
  178. _bar = nullptr;
  179. }, _bar->widget()->lifetime());
  180. Ensures(_bar != nullptr);
  181. }
  182. void PinnedBar::show() {
  183. if (!_forceHidden) {
  184. return;
  185. }
  186. _forceHidden = false;
  187. if (_shouldBeShown) {
  188. _wrap.show(anim::type::instant);
  189. } else if (!_wrap.isHidden() && !_wrap.animating()) {
  190. _wrap.hide(anim::type::instant);
  191. }
  192. }
  193. void PinnedBar::hide() {
  194. if (_forceHidden) {
  195. return;
  196. }
  197. _forceHidden = true;
  198. _wrap.hide(anim::type::instant);
  199. }
  200. void PinnedBar::raise() {
  201. _wrap.raise();
  202. _shadow->raise();
  203. }
  204. void PinnedBar::customEmojiRepaint() {
  205. if (_bar) {
  206. _bar->customEmojiRepaint();
  207. }
  208. }
  209. void PinnedBar::finishAnimating() {
  210. _wrap.finishAnimating();
  211. }
  212. void PinnedBar::move(int x, int y) {
  213. _wrap.move(x, y);
  214. }
  215. void PinnedBar::resizeToWidth(int width) {
  216. _wrap.entity()->resizeToWidth(width);
  217. }
  218. int PinnedBar::height() const {
  219. return !_forceHidden
  220. ? _wrap.height()
  221. : _shouldBeShown
  222. ? st::historyReplyHeight
  223. : 0;
  224. }
  225. rpl::producer<int> PinnedBar::heightValue() const {
  226. return _wrap.heightValue();
  227. }
  228. rpl::producer<> PinnedBar::barClicks() const {
  229. return _barClicks.events();
  230. }
  231. rpl::producer<> PinnedBar::contextMenuRequested() const {
  232. return _wrap.entity()->paintRequest(
  233. ) | rpl::filter([=] {
  234. return _bar && _bar->widget();
  235. }) | rpl::map([=] {
  236. return _bar->widget()->events(
  237. ) | rpl::filter([](not_null<QEvent*> event) {
  238. return (event->type() == QEvent::ContextMenu);
  239. }) | rpl::to_empty;
  240. }) | rpl::flatten_latest();
  241. }
  242. } // namespace Ui