history_view_highlight_manager.cpp 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  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 "history/history_view_highlight_manager.h"
  8. #include "data/data_session.h"
  9. #include "history/history.h"
  10. #include "history/history_item.h"
  11. #include "history/view/history_view_element.h"
  12. #include "ui/chat/chat_style.h"
  13. namespace HistoryView {
  14. ElementHighlighter::ElementHighlighter(
  15. not_null<Data::Session*> data,
  16. ViewForItem viewForItem,
  17. RepaintView repaintView)
  18. : _data(data)
  19. , _viewForItem(std::move(viewForItem))
  20. , _repaintView(std::move(repaintView))
  21. , _animation(*this) {
  22. }
  23. void ElementHighlighter::enqueue(const SelectedQuote &quote) {
  24. const auto data = computeHighlight(quote);
  25. if (_queue.empty() && !_animation.animating()) {
  26. highlight(data);
  27. } else if (_highlighted != data && !base::contains(_queue, data)) {
  28. _queue.push_back(data);
  29. checkNextHighlight();
  30. }
  31. }
  32. void ElementHighlighter::highlight(const SelectedQuote &quote) {
  33. highlight(computeHighlight(quote));
  34. }
  35. void ElementHighlighter::checkNextHighlight() {
  36. if (_animation.animating()) {
  37. return;
  38. }
  39. const auto next = [&] {
  40. while (!_queue.empty()) {
  41. const auto highlight = _queue.front();
  42. _queue.pop_front();
  43. if (const auto item = _data->message(highlight.itemId)) {
  44. if (_viewForItem(item)) {
  45. return highlight;
  46. }
  47. }
  48. }
  49. return Highlight();
  50. }();
  51. if (next) {
  52. highlight(next);
  53. }
  54. }
  55. Ui::ChatPaintHighlight ElementHighlighter::state(
  56. not_null<const HistoryItem*> item) const {
  57. if (item->fullId() == _highlighted.itemId) {
  58. auto result = _animation.state();
  59. result.range = _highlighted.part;
  60. return result;
  61. }
  62. return {};
  63. }
  64. ElementHighlighter::Highlight ElementHighlighter::computeHighlight(
  65. const SelectedQuote &quote) {
  66. Assert(quote.item != nullptr);
  67. const auto item = not_null(quote.item);
  68. const auto owner = &item->history()->owner();
  69. if (const auto group = owner->groups().find(item)) {
  70. const auto leader = group->items.front();
  71. const auto leaderId = leader->fullId();
  72. const auto i = ranges::find(group->items, item);
  73. if (i != end(group->items)) {
  74. const auto index = int(i - begin(group->items));
  75. if (quote.text.empty()) {
  76. return { leaderId, AddGroupItemSelection({}, index) };
  77. } else if (const auto leaderView = _viewForItem(leader)) {
  78. return { leaderId, leaderView->selectionFromQuote(quote) };
  79. }
  80. }
  81. return { leaderId };
  82. } else if (quote.text.empty()) {
  83. return { item->fullId() };
  84. } else if (const auto view = _viewForItem(item)) {
  85. return { item->fullId(), view->selectionFromQuote(quote) };
  86. }
  87. return { item->fullId() };
  88. }
  89. void ElementHighlighter::highlight(Highlight data) {
  90. if (const auto item = _data->message(data.itemId)) {
  91. if (const auto view = _viewForItem(item)) {
  92. if (_highlighted && _highlighted.itemId != data.itemId) {
  93. if (const auto was = _data->message(_highlighted.itemId)) {
  94. if (const auto view = _viewForItem(was)) {
  95. repaintHighlightedItem(view);
  96. }
  97. }
  98. }
  99. _highlighted = data;
  100. _animation.start(!data.part.empty()
  101. && !IsSubGroupSelection(data.part));
  102. repaintHighlightedItem(view);
  103. }
  104. }
  105. }
  106. void ElementHighlighter::repaintHighlightedItem(
  107. not_null<const Element*> view) {
  108. if (view->isHiddenByGroup()) {
  109. if (const auto group = _data->groups().find(view->data())) {
  110. if (const auto leader = _viewForItem(group->items.front())) {
  111. if (!leader->isHiddenByGroup()) {
  112. _repaintView(leader);
  113. return;
  114. }
  115. }
  116. }
  117. }
  118. _repaintView(view);
  119. }
  120. void ElementHighlighter::updateMessage() {
  121. if (const auto item = _data->message(_highlighted.itemId)) {
  122. if (const auto view = _viewForItem(item)) {
  123. repaintHighlightedItem(view);
  124. }
  125. }
  126. }
  127. void ElementHighlighter::clear() {
  128. _animation.cancel();
  129. _highlighted = {};
  130. _lastHighlightedMessageId = FullMsgId();
  131. _queue.clear();
  132. }
  133. ElementHighlighter::AnimationManager::AnimationManager(
  134. ElementHighlighter &parent)
  135. : _parent(parent) {
  136. }
  137. bool ElementHighlighter::AnimationManager::animating() const {
  138. if (_timer && _timer->isActive()) {
  139. return true;
  140. } else if (!anim::Disabled()) {
  141. return _simple.animating();
  142. }
  143. return false;
  144. }
  145. Ui::ChatPaintHighlight ElementHighlighter::AnimationManager::state() const {
  146. if (anim::Disabled()) {
  147. return {
  148. .opacity = !_timer ? 0. : 1.,
  149. .collapsion = !_timer ? 0. : _fadingOut ? 1. : 0.,
  150. };
  151. }
  152. return {
  153. .opacity = ((!_fadingOut && _collapsing)
  154. ? 1.
  155. : _simple.value(_fadingOut ? 0. : 1.)),
  156. .collapsion = ((!_withTextPart || !_collapsing)
  157. ? 0.
  158. : _fadingOut
  159. ? 1.
  160. : _simple.value(1.)),
  161. };
  162. }
  163. MsgId ElementHighlighter::latestSingleHighlightedMsgId() const {
  164. return _highlighted.itemId
  165. ? _highlighted.itemId.msg
  166. : _lastHighlightedMessageId.msg;
  167. }
  168. void ElementHighlighter::AnimationManager::start(bool withTextPart) {
  169. _withTextPart = withTextPart;
  170. const auto finish = [=] {
  171. cancel();
  172. _parent._lastHighlightedMessageId = base::take(
  173. _parent._highlighted.itemId);
  174. _parent.checkNextHighlight();
  175. };
  176. cancel();
  177. if (anim::Disabled()) {
  178. _timer.emplace([=] {
  179. _parent.updateMessage();
  180. if (_withTextPart && !_fadingOut) {
  181. _fadingOut = true;
  182. _timer->callOnce(st::activeFadeOutDuration);
  183. } else {
  184. finish();
  185. }
  186. });
  187. _timer->callOnce(_withTextPart
  188. ? st::activeFadeInDuration
  189. : st::activeFadeOutDuration);
  190. _parent.updateMessage();
  191. } else {
  192. _simple.start(
  193. [=](float64 value) {
  194. _parent.updateMessage();
  195. if (value == 1.) {
  196. if (_withTextPart) {
  197. _timer.emplace([=] {
  198. _parent.updateMessage();
  199. if (_collapsing) {
  200. _fadingOut = true;
  201. } else {
  202. _collapsing = true;
  203. }
  204. _simple.start([=](float64 value) {
  205. _parent.updateMessage();
  206. if (_fadingOut && value == 0.) {
  207. finish();
  208. } else if (!_fadingOut && value == 1.) {
  209. _timer->callOnce(
  210. st::activeFadeOutDuration);
  211. }
  212. },
  213. _fadingOut ? 1. : 0.,
  214. _fadingOut ? 0. : 1.,
  215. (_fadingOut
  216. ? st::activeFadeInDuration
  217. : st::fadeWrapDuration));
  218. });
  219. _timer->callOnce(st::activeFadeInDuration);
  220. } else {
  221. _fadingOut = true;
  222. _simple.start([=](float64 value) {
  223. _parent.updateMessage();
  224. if (value == 0.) {
  225. finish();
  226. }
  227. },
  228. 1.,
  229. 0.,
  230. st::activeFadeOutDuration);
  231. }
  232. }
  233. },
  234. 0.,
  235. 1.,
  236. st::activeFadeInDuration);
  237. }
  238. }
  239. void ElementHighlighter::AnimationManager::cancel() {
  240. _simple.stop();
  241. _timer.reset();
  242. _fadingOut = false;
  243. _collapsed = false;
  244. _collapsing = false;
  245. }
  246. } // namespace HistoryView