history_view_element.cpp 54 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217
  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/view/history_view_element.h"
  8. #include "history/view/history_view_service_message.h"
  9. #include "history/view/history_view_message.h"
  10. #include "history/view/media/history_view_media_grouped.h"
  11. #include "history/view/media/history_view_similar_channels.h"
  12. #include "history/view/media/history_view_sticker.h"
  13. #include "history/view/media/history_view_large_emoji.h"
  14. #include "history/view/media/history_view_custom_emoji.h"
  15. #include "history/view/reactions/history_view_reactions_button.h"
  16. #include "history/view/reactions/history_view_reactions.h"
  17. #include "history/view/history_view_cursor_state.h"
  18. #include "history/view/history_view_reply.h"
  19. #include "history/view/history_view_text_helper.h"
  20. #include "history/history.h"
  21. #include "history/history_item_components.h"
  22. #include "history/history_item_helpers.h"
  23. #include "base/unixtime.h"
  24. #include "boxes/premium_preview_box.h"
  25. #include "core/application.h"
  26. #include "core/core_settings.h"
  27. #include "core/click_handler_types.h"
  28. #include "core/ui_integration.h"
  29. #include "main/main_app_config.h"
  30. #include "main/main_session.h"
  31. #include "chat_helpers/stickers_emoji_pack.h"
  32. #include "payments/payments_reaction_process.h" // TryAddingPaidReaction.
  33. #include "window/window_session_controller.h"
  34. #include "ui/effects/path_shift_gradient.h"
  35. #include "ui/effects/reaction_fly_animation.h"
  36. #include "ui/toast/toast.h"
  37. #include "ui/text/text_utilities.h"
  38. #include "ui/item_text_options.h"
  39. #include "ui/painter.h"
  40. #include "ui/rect.h"
  41. #include "data/components/sponsored_messages.h"
  42. #include "data/data_session.h"
  43. #include "data/data_forum.h"
  44. #include "data/data_forum_topic.h"
  45. #include "data/data_message_reactions.h"
  46. #include "data/data_user.h"
  47. #include "lang/lang_keys.h"
  48. #include "styles/style_chat.h"
  49. namespace HistoryView {
  50. namespace {
  51. // A new message from the same sender is attached to previous within 15 minutes.
  52. constexpr int kAttachMessageToPreviousSecondsDelta = 900;
  53. Element *HoveredElement/* = nullptr*/;
  54. Element *PressedElement/* = nullptr*/;
  55. Element *HoveredLinkElement/* = nullptr*/;
  56. Element *PressedLinkElement/* = nullptr*/;
  57. Element *MousedElement/* = nullptr*/;
  58. [[nodiscard]] bool IsAttachedToPreviousInSavedMessages(
  59. not_null<HistoryItem*> previous,
  60. HistoryMessageForwarded *prevForwarded,
  61. not_null<HistoryItem*> item,
  62. HistoryMessageForwarded *forwarded) {
  63. const auto sender = previous->displayFrom();
  64. if ((prevForwarded != nullptr) != (forwarded != nullptr)) {
  65. return false;
  66. } else if (sender != item->displayFrom()) {
  67. return false;
  68. } else if (!prevForwarded || sender) {
  69. return true;
  70. }
  71. const auto previousInfo = prevForwarded->savedFromHiddenSenderInfo
  72. ? prevForwarded->savedFromHiddenSenderInfo.get()
  73. : prevForwarded->originalHiddenSenderInfo.get();
  74. const auto itemInfo = forwarded->savedFromHiddenSenderInfo
  75. ? forwarded->savedFromHiddenSenderInfo.get()
  76. : forwarded->originalHiddenSenderInfo.get();
  77. Assert(previousInfo != nullptr);
  78. Assert(itemInfo != nullptr);
  79. return (*previousInfo == *itemInfo);
  80. }
  81. [[nodiscard]] Window::SessionController *ContextOrSessionWindow(
  82. const ClickHandlerContext &context,
  83. not_null<Main::Session*> session) {
  84. if (const auto controller = context.sessionWindow.get()) {
  85. if (&controller->session() == session) {
  86. return controller;
  87. }
  88. }
  89. return session->tryResolveWindow();
  90. }
  91. [[nodiscard]] TextSelection FindSearchQueryHighlight(
  92. const QString &text,
  93. const QString &query) {
  94. const auto lower = query.toLower();
  95. const auto inside = text.toLower();
  96. const auto find = [&](QStringView part) {
  97. auto skip = 0;
  98. if (const auto from = inside.indexOf(part, skip); from >= 0) {
  99. if (!from || !inside[from - 1].isLetterOrNumber()) {
  100. return int(from);
  101. }
  102. skip = from + 1;
  103. }
  104. return -1;
  105. };
  106. if (const auto from = find(lower); from >= 0) {
  107. const auto till = from + query.size();
  108. if (till >= inside.size() || !inside[till].isLetterOrNumber()) {
  109. return { uint16(from), uint16(till) };
  110. }
  111. }
  112. const auto tillEndOfWord = [&](int from) {
  113. for (auto till = from + 1; till != inside.size(); ++till) {
  114. if (!inside[till].isLetterOrNumber()) {
  115. return TextSelection{ uint16(from), uint16(till) };
  116. }
  117. }
  118. return TextSelection{ uint16(from), uint16(inside.size()) };
  119. };
  120. const auto words = QStringView(lower).split(
  121. QRegularExpression(
  122. u"[\\W]"_q,
  123. QRegularExpression::UseUnicodePropertiesOption),
  124. Qt::SkipEmptyParts);
  125. for (const auto &word : words) {
  126. const auto length = int(word.size());
  127. const auto cut = length / 2;
  128. const auto part = word.mid(0, length - cut);
  129. const auto offset = find(part);
  130. if (offset < 0) {
  131. continue;
  132. }
  133. for (auto i = 0; i != cut; ++i) {
  134. const auto part = word.mid(0, length - i);
  135. if (const auto from = find(part); from >= 0) {
  136. return tillEndOfWord(from);
  137. }
  138. }
  139. return tillEndOfWord(offset);
  140. }
  141. return {};
  142. }
  143. [[nodiscard]] TextSelection ApplyModificationsFrom(
  144. TextSelection result,
  145. const Ui::Text::String &text) {
  146. if (result.empty()) {
  147. return result;
  148. }
  149. for (const auto &modification : text.modifications()) {
  150. if (modification.position >= result.to) {
  151. break;
  152. }
  153. if (modification.added) {
  154. ++result.to;
  155. }
  156. const auto shiftTo = std::min(
  157. int(modification.skipped),
  158. result.to - modification.position);
  159. result.to -= shiftTo;
  160. if (modification.position <= result.from) {
  161. if (modification.added) {
  162. ++result.from;
  163. }
  164. const auto shiftFrom = std::min(
  165. int(modification.skipped),
  166. result.from - modification.position);
  167. result.from -= shiftFrom;
  168. }
  169. }
  170. return result;
  171. }
  172. } // namespace
  173. std::unique_ptr<Ui::PathShiftGradient> MakePathShiftGradient(
  174. not_null<const Ui::ChatStyle*> st,
  175. Fn<void()> update) {
  176. return std::make_unique<Ui::PathShiftGradient>(
  177. st->msgServiceBg(),
  178. st->msgServiceBgSelected(),
  179. std::move(update),
  180. st->paletteChanged());
  181. }
  182. bool DefaultElementDelegate::elementUnderCursor(
  183. not_null<const Element*> view) {
  184. return false;
  185. }
  186. SelectionModeResult DefaultElementDelegate::elementInSelectionMode(
  187. const Element *view) {
  188. return {};
  189. }
  190. bool DefaultElementDelegate::elementIntersectsRange(
  191. not_null<const Element*> view,
  192. int from,
  193. int till) {
  194. return true;
  195. }
  196. void DefaultElementDelegate::elementStartStickerLoop(
  197. not_null<const Element*> view) {
  198. }
  199. void DefaultElementDelegate::elementShowPollResults(
  200. not_null<PollData*> poll,
  201. FullMsgId context) {
  202. }
  203. void DefaultElementDelegate::elementOpenPhoto(
  204. not_null<PhotoData*> photo,
  205. FullMsgId context) {
  206. }
  207. void DefaultElementDelegate::elementOpenDocument(
  208. not_null<DocumentData*> document,
  209. FullMsgId context,
  210. bool showInMediaView) {
  211. }
  212. void DefaultElementDelegate::elementCancelUpload(const FullMsgId &context) {
  213. }
  214. void DefaultElementDelegate::elementShowTooltip(
  215. const TextWithEntities &text,
  216. Fn<void()> hiddenCallback) {
  217. }
  218. bool DefaultElementDelegate::elementHideReply(
  219. not_null<const Element*> view) {
  220. return false;
  221. }
  222. bool DefaultElementDelegate::elementShownUnread(
  223. not_null<const Element*> view) {
  224. return view->data()->unread(view->data()->history());
  225. }
  226. void DefaultElementDelegate::elementSendBotCommand(
  227. const QString &command,
  228. const FullMsgId &context) {
  229. }
  230. void DefaultElementDelegate::elementSearchInList(
  231. const QString &query,
  232. const FullMsgId &context) {
  233. }
  234. void DefaultElementDelegate::elementHandleViaClick(
  235. not_null<UserData*> bot) {
  236. }
  237. bool DefaultElementDelegate::elementIsChatWide() {
  238. return false;
  239. }
  240. void DefaultElementDelegate::elementReplyTo(const FullReplyTo &to) {
  241. }
  242. void DefaultElementDelegate::elementStartInteraction(
  243. not_null<const Element*> view) {
  244. }
  245. void DefaultElementDelegate::elementStartPremium(
  246. not_null<const Element*> view,
  247. Element *replacing) {
  248. }
  249. void DefaultElementDelegate::elementCancelPremium(
  250. not_null<const Element*> view) {
  251. }
  252. void DefaultElementDelegate::elementStartEffect(
  253. not_null<const Element*> view,
  254. Element *replacing) {
  255. }
  256. QString DefaultElementDelegate::elementAuthorRank(
  257. not_null<const Element*> view) {
  258. return {};
  259. }
  260. bool DefaultElementDelegate::elementHideTopicButton(
  261. not_null<const Element*> view) {
  262. return true;
  263. }
  264. SimpleElementDelegate::SimpleElementDelegate(
  265. not_null<Window::SessionController*> controller,
  266. Fn<void()> update)
  267. : _controller(controller)
  268. , _pathGradient(
  269. MakePathShiftGradient(
  270. controller->chatStyle(),
  271. std::move(update))) {
  272. }
  273. SimpleElementDelegate::~SimpleElementDelegate() = default;
  274. bool SimpleElementDelegate::elementAnimationsPaused() {
  275. return _controller->isGifPausedAtLeastFor(Window::GifPauseReason::Any);
  276. }
  277. auto SimpleElementDelegate::elementPathShiftGradient()
  278. -> not_null<Ui::PathShiftGradient*> {
  279. return _pathGradient.get();
  280. }
  281. TextSelection UnshiftItemSelection(
  282. TextSelection selection,
  283. uint16 byLength) {
  284. return (selection == FullSelection)
  285. ? selection
  286. : ::unshiftSelection(selection, byLength);
  287. }
  288. TextSelection ShiftItemSelection(
  289. TextSelection selection,
  290. uint16 byLength) {
  291. return (selection == FullSelection)
  292. ? selection
  293. : ::shiftSelection(selection, byLength);
  294. }
  295. TextSelection UnshiftItemSelection(
  296. TextSelection selection,
  297. const Ui::Text::String &byText) {
  298. return UnshiftItemSelection(selection, byText.length());
  299. }
  300. TextSelection ShiftItemSelection(
  301. TextSelection selection,
  302. const Ui::Text::String &byText) {
  303. return ShiftItemSelection(selection, byText.length());
  304. }
  305. QString DateTooltipText(not_null<Element*> view) {
  306. const auto locale = QLocale();
  307. const auto format = QLocale::LongFormat;
  308. const auto item = view->data();
  309. auto dateText = locale.toString(view->dateTime(), format);
  310. if (item->awaitingVideoProcessing()) {
  311. dateText += '\n' + tr::lng_approximate_about(tr::now);
  312. }
  313. if (const auto editedDate = view->displayedEditDate()) {
  314. dateText += '\n' + tr::lng_edited_date(
  315. tr::now,
  316. lt_date,
  317. locale.toString(base::unixtime::parse(editedDate), format));
  318. }
  319. if (const auto forwarded = item->Get<HistoryMessageForwarded>()) {
  320. if (!forwarded->story && forwarded->psaType.isEmpty()) {
  321. dateText += '\n' + tr::lng_forwarded_date(
  322. tr::now,
  323. lt_date,
  324. locale.toString(
  325. base::unixtime::parse(forwarded->originalDate),
  326. format));
  327. if (forwarded->imported) {
  328. dateText = tr::lng_forwarded_imported(tr::now)
  329. + "\n\n" + dateText;
  330. }
  331. }
  332. }
  333. if (view->isSignedAuthorElided()) {
  334. if (const auto msgsigned = item->Get<HistoryMessageSigned>()) {
  335. dateText += '\n' + tr::lng_signed_author(
  336. tr::now,
  337. lt_user,
  338. msgsigned->author);
  339. }
  340. }
  341. if (item->isScheduled() && item->isSilent()) {
  342. dateText += '\n' + QChar(0xD83D) + QChar(0xDD15);
  343. }
  344. if (const auto stars = item->out() ? item->starsPaid() : 0) {
  345. dateText += '\n' + tr::lng_you_paid_stars(tr::now, lt_count, stars);
  346. }
  347. return dateText;
  348. }
  349. void UnreadBar::init(const QString &string) {
  350. text = string;
  351. width = st::semiboldFont->width(text);
  352. }
  353. int UnreadBar::height() {
  354. return st::historyUnreadBarHeight + st::historyUnreadBarMargin;
  355. }
  356. int UnreadBar::marginTop() {
  357. return st::lineWidth + st::historyUnreadBarMargin;
  358. }
  359. void UnreadBar::paint(
  360. Painter &p,
  361. const PaintContext &context,
  362. int y,
  363. int w,
  364. bool chatWide) const {
  365. const auto previousTranslation = p.transform().dx();
  366. if (previousTranslation != 0) {
  367. p.translate(-previousTranslation, 0);
  368. }
  369. const auto st = context.st;
  370. const auto bottom = y + height();
  371. y += marginTop();
  372. p.fillRect(
  373. 0,
  374. y,
  375. w,
  376. height() - marginTop() - st::lineWidth,
  377. st->historyUnreadBarBg());
  378. p.fillRect(
  379. 0,
  380. bottom - st::lineWidth,
  381. w,
  382. st::lineWidth,
  383. st->historyUnreadBarBorder());
  384. p.setFont(st::historyUnreadBarFont);
  385. p.setPen(st->historyUnreadBarFg());
  386. int maxwidth = w;
  387. if (chatWide) {
  388. maxwidth = qMin(
  389. maxwidth,
  390. st::msgMaxWidth
  391. + 2 * st::msgPhotoSkip
  392. + 2 * st::msgMargin.left());
  393. }
  394. w = maxwidth;
  395. const auto skip = st::historyUnreadBarHeight
  396. - 2 * st::lineWidth
  397. - st::historyUnreadBarFont->height;
  398. p.drawText(
  399. (w - width) / 2,
  400. y + (skip / 2) + st::historyUnreadBarFont->ascent,
  401. text);
  402. if (previousTranslation != 0) {
  403. p.translate(previousTranslation, 0);
  404. }
  405. }
  406. void DateBadge::init(const QString &date) {
  407. text = date;
  408. width = st::msgServiceFont->width(text);
  409. }
  410. int DateBadge::height() const {
  411. return st::msgServiceMargin.top()
  412. + st::msgServicePadding.top()
  413. + st::msgServiceFont->height
  414. + st::msgServicePadding.bottom()
  415. + st::msgServiceMargin.bottom();
  416. }
  417. void DateBadge::paint(
  418. Painter &p,
  419. not_null<const Ui::ChatStyle*> st,
  420. int y,
  421. int w,
  422. bool chatWide) const {
  423. ServiceMessagePainter::PaintDate(p, st, text, width, y, w, chatWide);
  424. }
  425. void ServicePreMessage::init(PreparedServiceText string) {
  426. text = Ui::Text::String(
  427. st::serviceTextStyle,
  428. string.text,
  429. kMarkupTextOptions,
  430. st::msgMinWidth);
  431. for (auto i = 0; i != int(string.links.size()); ++i) {
  432. text.setLink(i + 1, string.links[i]);
  433. }
  434. }
  435. int ServicePreMessage::resizeToWidth(int newWidth, bool chatWide) {
  436. width = newWidth;
  437. if (chatWide) {
  438. accumulate_min(
  439. width,
  440. st::msgMaxWidth + 2 * st::msgPhotoSkip + 2 * st::msgMargin.left());
  441. }
  442. auto contentWidth = width;
  443. contentWidth -= st::msgServiceMargin.left() + st::msgServiceMargin.left(); // two small margins
  444. if (contentWidth < st::msgServicePadding.left() + st::msgServicePadding.right() + 1) {
  445. contentWidth = st::msgServicePadding.left() + st::msgServicePadding.right() + 1;
  446. }
  447. auto maxWidth = text.maxWidth()
  448. + st::msgServicePadding.left()
  449. + st::msgServicePadding.right();
  450. auto minHeight = text.minHeight();
  451. auto nwidth = qMax(contentWidth
  452. - st::msgServicePadding.left()
  453. - st::msgServicePadding.right(), 0);
  454. height = (contentWidth >= maxWidth)
  455. ? minHeight
  456. : text.countHeight(nwidth);
  457. height += st::msgServicePadding.top()
  458. + st::msgServicePadding.bottom()
  459. + st::msgServiceMargin.top()
  460. + st::msgServiceMargin.bottom();
  461. return height;
  462. }
  463. void ServicePreMessage::paint(
  464. Painter &p,
  465. const PaintContext &context,
  466. QRect g,
  467. bool chatWide) const {
  468. const auto top = g.top() - height - st::msgMargin.top();
  469. p.translate(0, top);
  470. const auto rect = QRect(0, 0, width, height)
  471. - st::msgServiceMargin;
  472. const auto trect = rect - st::msgServicePadding;
  473. ServiceMessagePainter::PaintComplexBubble(
  474. p,
  475. context.st,
  476. rect.left(),
  477. rect.width(),
  478. text,
  479. trect);
  480. p.setBrush(Qt::NoBrush);
  481. p.setPen(context.st->msgServiceFg());
  482. p.setFont(st::msgServiceFont);
  483. text.draw(p, {
  484. .position = trect.topLeft(),
  485. .availableWidth = trect.width(),
  486. .align = style::al_top,
  487. .palette = &context.st->serviceTextPalette(),
  488. .now = context.now,
  489. .fullWidthSelection = false,
  490. //.selection = context.selection,
  491. });
  492. p.translate(0, -top);
  493. }
  494. ClickHandlerPtr ServicePreMessage::textState(
  495. QPoint point,
  496. const StateRequest &request,
  497. QRect g) const {
  498. const auto top = g.top() - height - st::msgMargin.top();
  499. const auto rect = QRect(0, top, width, height)
  500. - st::msgServiceMargin;
  501. const auto trect = rect - st::msgServicePadding;
  502. if (trect.contains(point)) {
  503. auto textRequest = request.forText();
  504. textRequest.align = style::al_center;
  505. return text.getState(
  506. point - trect.topLeft(),
  507. trect.width(),
  508. textRequest).link;
  509. }
  510. return {};
  511. }
  512. void FakeBotAboutTop::init() {
  513. if (!text.isEmpty()) {
  514. return;
  515. }
  516. text.setText(
  517. st::msgNameStyle,
  518. tr::lng_bot_description(tr::now),
  519. Ui::NameTextOptions());
  520. maxWidth = st::msgPadding.left()
  521. + text.maxWidth()
  522. + st::msgPadding.right();
  523. height = st::msgNameStyle.font->height + st::botDescSkip;
  524. }
  525. Element::Element(
  526. not_null<ElementDelegate*> delegate,
  527. not_null<HistoryItem*> data,
  528. Element *replacing,
  529. Flag serviceFlag)
  530. : _delegate(delegate)
  531. , _data(data)
  532. , _dateTime((IsItemScheduledUntilOnline(data) || data->shortcutId())
  533. ? QDateTime()
  534. : ItemDateTime(data))
  535. , _text(st::msgMinWidth)
  536. , _flags(serviceFlag
  537. | Flag::NeedsResize
  538. | (IsItemScheduledUntilOnline(data)
  539. ? Flag::ScheduledUntilOnline
  540. : Flag())
  541. | (countIsTopicRootReply() ? Flag::TopicRootReply : Flag()))
  542. , _context(delegate->elementContext()) {
  543. history()->owner().registerItemView(this);
  544. refreshMedia(replacing);
  545. if (_context == Context::History) {
  546. history()->setHasPendingResizedItems();
  547. }
  548. if (data->isFakeAboutView()) {
  549. const auto user = data->history()->peer->asUser();
  550. if (user
  551. && user->isBot()
  552. && !user->isRepliesChat()
  553. && !user->isVerifyCodes()) {
  554. AddComponents(FakeBotAboutTop::Bit());
  555. }
  556. }
  557. }
  558. bool Element::embedReactionsInBubble() const {
  559. return false;
  560. }
  561. not_null<ElementDelegate*> Element::delegate() const {
  562. return _delegate;
  563. }
  564. not_null<HistoryItem*> Element::data() const {
  565. return _data;
  566. }
  567. not_null<History*> Element::history() const {
  568. return _data->history();
  569. }
  570. uint8 Element::colorIndex() const {
  571. return data()->colorIndex();
  572. }
  573. uint8 Element::contentColorIndex() const {
  574. return data()->contentColorIndex();
  575. }
  576. QDateTime Element::dateTime() const {
  577. return _dateTime;
  578. }
  579. Media *Element::media() const {
  580. return _media.get();
  581. }
  582. Context Element::context() const {
  583. return _context;
  584. }
  585. int Element::y() const {
  586. return _y;
  587. }
  588. void Element::setY(int y) {
  589. _y = y;
  590. }
  591. void Element::refreshDataIdHook() {
  592. }
  593. void Element::clearSpecialOnlyEmoji() {
  594. if (!(_flags & Flag::SpecialOnlyEmoji)) {
  595. return;
  596. }
  597. history()->session().emojiStickersPack().remove(this);
  598. _flags &= ~Flag::SpecialOnlyEmoji;
  599. }
  600. void Element::checkSpecialOnlyEmoji() {
  601. if (history()->session().emojiStickersPack().add(this)) {
  602. _flags |= Flag::SpecialOnlyEmoji;
  603. }
  604. }
  605. void Element::hideSpoilers() {
  606. if (_text.hasSpoilers()) {
  607. _text.setSpoilerRevealed(false, anim::type::instant);
  608. }
  609. if (_media) {
  610. _media->hideSpoilers();
  611. }
  612. }
  613. void Element::customEmojiRepaint() {
  614. if (!(_flags & Flag::CustomEmojiRepainting)) {
  615. _flags |= Flag::CustomEmojiRepainting;
  616. history()->owner().requestViewRepaint(this);
  617. }
  618. }
  619. void Element::clearCustomEmojiRepaint() const {
  620. _flags &= ~Flag::CustomEmojiRepainting;
  621. data()->_flags &= ~MessageFlag::CustomEmojiRepainting;
  622. }
  623. void Element::prepareCustomEmojiPaint(
  624. Painter &p,
  625. const PaintContext &context,
  626. const Ui::Text::String &text) const {
  627. if (!text.hasPersistentAnimation()) {
  628. return;
  629. }
  630. clearCustomEmojiRepaint();
  631. p.setInactive(context.paused);
  632. if (!(_flags & Flag::HeavyCustomEmoji)) {
  633. _flags |= Flag::HeavyCustomEmoji;
  634. history()->owner().registerHeavyViewPart(const_cast<Element*>(this));
  635. }
  636. }
  637. void Element::prepareCustomEmojiPaint(
  638. Painter &p,
  639. const PaintContext &context,
  640. const Reactions::InlineList &reactions) const {
  641. if (!reactions.hasCustomEmoji()) {
  642. return;
  643. }
  644. clearCustomEmojiRepaint();
  645. p.setInactive(context.paused);
  646. if (!(_flags & Flag::HeavyCustomEmoji)) {
  647. _flags |= Flag::HeavyCustomEmoji;
  648. history()->owner().registerHeavyViewPart(const_cast<Element*>(this));
  649. }
  650. }
  651. void Element::repaint() const {
  652. history()->owner().requestViewRepaint(this);
  653. }
  654. void Element::paintHighlight(
  655. Painter &p,
  656. const PaintContext &context,
  657. int geometryHeight) const {
  658. if (context.highlight.opacity == 0.) {
  659. return;
  660. }
  661. const auto top = marginTop();
  662. const auto bottom = marginBottom();
  663. const auto fill = qMin(top, bottom);
  664. const auto skiptop = top - fill;
  665. const auto fillheight = fill + geometryHeight + fill;
  666. paintCustomHighlight(p, context, skiptop, fillheight, data());
  667. }
  668. void Element::paintCustomHighlight(
  669. Painter &p,
  670. const PaintContext &context,
  671. int y,
  672. int height,
  673. not_null<const HistoryItem*> item) const {
  674. const auto o = p.opacity();
  675. p.setOpacity(o * context.highlight.opacity);
  676. p.fillRect(0, y, width(), height, context.st->msgSelectOverlay());
  677. p.setOpacity(o);
  678. }
  679. bool Element::isUnderCursor() const {
  680. return _delegate->elementUnderCursor(this);
  681. }
  682. bool Element::isLastAndSelfMessage() const {
  683. if (!hasOutLayout() || data()->_history->peer->isSelf()) {
  684. return false;
  685. }
  686. if (const auto last = data()->_history->lastMessage()) {
  687. return last == data();
  688. }
  689. return false;
  690. }
  691. void Element::setPendingResize() {
  692. _flags |= Flag::NeedsResize;
  693. if (_context == Context::History) {
  694. data()->_history->setHasPendingResizedItems();
  695. }
  696. }
  697. bool Element::pendingResize() const {
  698. return _flags & Flag::NeedsResize;
  699. }
  700. bool Element::isAttachedToPrevious() const {
  701. return _flags & Flag::AttachedToPrevious;
  702. }
  703. bool Element::isAttachedToNext() const {
  704. return _flags & Flag::AttachedToNext;
  705. }
  706. bool Element::isBubbleAttachedToPrevious() const {
  707. return _flags & Flag::BubbleAttachedToPrevious;
  708. }
  709. bool Element::isBubbleAttachedToNext() const {
  710. return _flags & Flag::BubbleAttachedToNext;
  711. }
  712. bool Element::isTopicRootReply() const {
  713. return _flags & Flag::TopicRootReply;
  714. }
  715. int Element::skipBlockWidth() const {
  716. return st::msgDateSpace + infoWidth() - st::msgDateDelta.x();
  717. }
  718. int Element::skipBlockHeight() const {
  719. return st::msgDateFont->height - st::msgDateDelta.y();
  720. }
  721. int Element::infoWidth() const {
  722. return 0;
  723. }
  724. int Element::bottomInfoFirstLineWidth() const {
  725. return 0;
  726. }
  727. bool Element::bottomInfoIsWide() const {
  728. return false;
  729. }
  730. bool Element::isHiddenByGroup() const {
  731. return _flags & Flag::HiddenByGroup;
  732. }
  733. bool Element::isHidden() const {
  734. return isHiddenByGroup();
  735. }
  736. void Element::overrideMedia(std::unique_ptr<Media> media) {
  737. Expects(!history()->owner().groups().find(data()));
  738. _text = Ui::Text::String(st::msgMinWidth);
  739. _textWidth = -1;
  740. _textHeight = 0;
  741. _media = std::move(media);
  742. if (!pendingResize()) {
  743. history()->owner().requestViewResize(this);
  744. }
  745. _flags |= Flag::MediaOverriden;
  746. }
  747. not_null<PurchasedTag*> Element::enforcePurchasedTag() {
  748. if (const auto purchased = Get<PurchasedTag>()) {
  749. return purchased;
  750. }
  751. AddComponents(PurchasedTag::Bit());
  752. return Get<PurchasedTag>();
  753. }
  754. int Element::AdditionalSpaceForSelectionCheckbox(
  755. not_null<const Element*> view,
  756. QRect countedGeometry) {
  757. if (!view->hasOutLayout() || view->delegate()->elementIsChatWide()) {
  758. return 0;
  759. }
  760. if (countedGeometry.isEmpty()) {
  761. countedGeometry = view->innerGeometry();
  762. }
  763. const auto diff = view->width()
  764. - (countedGeometry.x() + countedGeometry.width())
  765. - st::msgPadding.right()
  766. - st::msgSelectionOffset
  767. - view->rightActionSize().value_or(QSize()).width();
  768. return (diff < 0)
  769. ? -(std::min(st::msgSelectionOffset, -diff))
  770. : 0;
  771. }
  772. void Element::refreshMedia(Element *replacing) {
  773. if (_flags & Flag::MediaOverriden) {
  774. return;
  775. }
  776. _flags &= ~Flag::HiddenByGroup;
  777. const auto item = data();
  778. if (!item->computeUnavailableReason().isEmpty()) {
  779. _media = nullptr;
  780. return;
  781. }
  782. if (const auto media = item->media()) {
  783. if (media->canBeGrouped()) {
  784. if (const auto group = history()->owner().groups().find(item)) {
  785. if (group->items.front() != item) {
  786. _media = nullptr;
  787. _flags |= Flag::HiddenByGroup;
  788. } else {
  789. _media = std::make_unique<GroupedMedia>(
  790. this,
  791. group->items);
  792. if (!pendingResize()) {
  793. history()->owner().requestViewResize(this);
  794. }
  795. }
  796. return;
  797. }
  798. }
  799. _media = media->createView(this, replacing);
  800. } else if (item->showSimilarChannels()) {
  801. _media = std::make_unique<SimilarChannels>(this);
  802. } else if (isOnlyCustomEmoji()
  803. && Core::App().settings().largeEmoji()
  804. && !item->isSponsored()) {
  805. _media = std::make_unique<UnwrappedMedia>(
  806. this,
  807. std::make_unique<CustomEmoji>(this, onlyCustomEmoji()));
  808. } else if (isIsolatedEmoji()
  809. && Core::App().settings().largeEmoji()
  810. && !item->isSponsored()) {
  811. const auto emoji = isolatedEmoji();
  812. const auto emojiStickers = &history()->session().emojiStickersPack();
  813. const auto skipPremiumEffect = false;
  814. if (const auto sticker = emojiStickers->stickerForEmoji(emoji)) {
  815. auto content = std::make_unique<Sticker>(
  816. this,
  817. sticker.document,
  818. skipPremiumEffect,
  819. replacing,
  820. sticker.replacements);
  821. content->setEmojiSticker();
  822. _media = std::make_unique<UnwrappedMedia>(
  823. this,
  824. std::move(content));
  825. } else {
  826. _media = std::make_unique<UnwrappedMedia>(
  827. this,
  828. std::make_unique<LargeEmoji>(this, emoji));
  829. }
  830. } else {
  831. _media = nullptr;
  832. }
  833. }
  834. HistoryItem *Element::textItem() const {
  835. return _textItem;
  836. }
  837. Ui::Text::IsolatedEmoji Element::isolatedEmoji() const {
  838. return _text.toIsolatedEmoji();
  839. }
  840. Ui::Text::OnlyCustomEmoji Element::onlyCustomEmoji() const {
  841. return _text.toOnlyCustomEmoji();
  842. }
  843. const Ui::Text::String &Element::text() const {
  844. return _text;
  845. }
  846. OnlyEmojiAndSpaces Element::isOnlyEmojiAndSpaces() const {
  847. if (data()->Has<HistoryMessageTranslation>()) {
  848. return OnlyEmojiAndSpaces::No;
  849. } else if (!_text.isEmpty()) {
  850. return _text.hasNotEmojiAndSpaces()
  851. ? OnlyEmojiAndSpaces::No
  852. : OnlyEmojiAndSpaces::Yes;
  853. } else if (data()->originalText().empty()) {
  854. return OnlyEmojiAndSpaces::Yes;
  855. } else {
  856. return OnlyEmojiAndSpaces::Unknown;
  857. }
  858. }
  859. int Element::textHeightFor(int textWidth) {
  860. validateText();
  861. if (_textWidth != textWidth) {
  862. _textWidth = textWidth;
  863. _textHeight = _text.countHeight(textWidth);
  864. }
  865. return _textHeight;
  866. }
  867. auto Element::contextDependentServiceText() -> TextWithLinks {
  868. const auto item = data();
  869. const auto info = item->Get<HistoryServiceTopicInfo>();
  870. if (!info) {
  871. return {};
  872. }
  873. if (_context == Context::Replies) {
  874. if (info->created()) {
  875. return { { tr::lng_action_topic_created_inside(tr::now) } };
  876. }
  877. return {};
  878. } else if (info->created()) {
  879. return{};
  880. }
  881. const auto peerId = item->history()->peer->id;
  882. const auto topicRootId = item->topicRootId();
  883. if (!peerIsChannel(peerId)) {
  884. return {};
  885. }
  886. const auto from = item->from();
  887. const auto topicUrl = u"internal:url:https://t.me/c/%1/%2"_q
  888. .arg(peerToChannel(peerId).bare)
  889. .arg(topicRootId.bare);
  890. const auto fromLink = [&](int index) {
  891. return Ui::Text::Link(from->name(), index);
  892. };
  893. const auto placeholderLink = [&] {
  894. return Ui::Text::Link(
  895. tr::lng_action_topic_placeholder(tr::now),
  896. topicUrl);
  897. };
  898. const auto wrapTopic = [&](
  899. const QString &title,
  900. std::optional<DocumentId> iconId) {
  901. return Ui::Text::Link(
  902. Data::ForumTopicIconWithTitle(
  903. topicRootId,
  904. iconId.value_or(0),
  905. title),
  906. topicUrl);
  907. };
  908. const auto wrapParentTopic = [&] {
  909. const auto forum = history()->asForum();
  910. if (!forum || forum->topicDeleted(topicRootId)) {
  911. return wrapTopic(
  912. tr::lng_deleted_message(tr::now),
  913. std::nullopt);
  914. } else if (const auto topic = forum->topicFor(topicRootId)) {
  915. return wrapTopic(topic->title(), topic->iconId());
  916. } else {
  917. forum->requestTopic(topicRootId, crl::guard(this, [=] {
  918. itemTextUpdated();
  919. history()->owner().requestViewResize(this);
  920. }));
  921. return wrapTopic(
  922. tr::lng_profile_loading(tr::now),
  923. std::nullopt);
  924. }
  925. };
  926. if (info->closed) {
  927. return {
  928. tr::lng_action_topic_closed(
  929. tr::now,
  930. lt_topic,
  931. wrapParentTopic(),
  932. Ui::Text::WithEntities),
  933. };
  934. } else if (info->reopened) {
  935. return {
  936. tr::lng_action_topic_reopened(
  937. tr::now,
  938. lt_topic,
  939. wrapParentTopic(),
  940. Ui::Text::WithEntities),
  941. };
  942. } else if (info->hidden) {
  943. return {
  944. tr::lng_action_topic_hidden(
  945. tr::now,
  946. lt_topic,
  947. wrapParentTopic(),
  948. Ui::Text::WithEntities),
  949. };
  950. } else if (info->unhidden) {
  951. return {
  952. tr::lng_action_topic_unhidden(
  953. tr::now,
  954. lt_topic,
  955. wrapParentTopic(),
  956. Ui::Text::WithEntities),
  957. };
  958. } else if (info->renamed) {
  959. return {
  960. tr::lng_action_topic_renamed(
  961. tr::now,
  962. lt_from,
  963. fromLink(1),
  964. lt_link,
  965. placeholderLink(),
  966. lt_title,
  967. wrapTopic(
  968. info->title,
  969. (info->reiconed
  970. ? info->iconId
  971. : std::optional<DocumentId>())),
  972. Ui::Text::WithEntities),
  973. { from->createOpenLink() },
  974. };
  975. } else if (info->reiconed) {
  976. if (const auto iconId = info->iconId) {
  977. return {
  978. tr::lng_action_topic_icon_changed(
  979. tr::now,
  980. lt_from,
  981. fromLink(1),
  982. lt_link,
  983. placeholderLink(),
  984. lt_emoji,
  985. Data::SingleCustomEmoji(iconId),
  986. Ui::Text::WithEntities),
  987. { from->createOpenLink() },
  988. };
  989. } else {
  990. return {
  991. tr::lng_action_topic_icon_removed(
  992. tr::now,
  993. lt_from,
  994. fromLink(1),
  995. lt_link,
  996. placeholderLink(),
  997. Ui::Text::WithEntities),
  998. { from->createOpenLink() },
  999. };
  1000. }
  1001. } else {
  1002. return {};
  1003. }
  1004. }
  1005. void Element::validateText() {
  1006. const auto item = data();
  1007. const auto media = item->media();
  1008. const auto storyMention = media && media->storyMention();
  1009. if (media && media->storyExpired()) {
  1010. _media = nullptr;
  1011. _textItem = item;
  1012. if (!storyMention) {
  1013. if (_text.isEmpty()) {
  1014. setTextWithLinks(Ui::Text::Italic(
  1015. tr::lng_forwarded_story_expired(tr::now)));
  1016. }
  1017. return;
  1018. }
  1019. }
  1020. // Albums may show text of a different item than the parent one.
  1021. _textItem = _media ? _media->itemForText() : item.get();
  1022. if (!_textItem) {
  1023. if (!_text.isEmpty()) {
  1024. setTextWithLinks({});
  1025. }
  1026. return;
  1027. }
  1028. const auto &text = _textItem->_text;
  1029. if (_text.isEmpty() == text.empty()) {
  1030. } else if (_flags & Flag::ServiceMessage) {
  1031. const auto contextDependentText = contextDependentServiceText();
  1032. const auto &markedText = contextDependentText.text.empty()
  1033. ? text
  1034. : contextDependentText.text;
  1035. const auto &customLinks = contextDependentText.text.empty()
  1036. ? _textItem->customTextLinks()
  1037. : contextDependentText.links;
  1038. setTextWithLinks(markedText, customLinks);
  1039. } else {
  1040. const auto unavailable = item->computeUnavailableReason();
  1041. if (!unavailable.isEmpty()) {
  1042. setTextWithLinks(Ui::Text::Italic(unavailable));
  1043. } else {
  1044. setTextWithLinks(_textItem->translatedTextWithLocalEntities());
  1045. }
  1046. }
  1047. }
  1048. void Element::setTextWithLinks(
  1049. const TextWithEntities &text,
  1050. const std::vector<ClickHandlerPtr> &links) {
  1051. const auto context = Core::TextContext({
  1052. .session = &history()->session(),
  1053. .repaint = [=] { customEmojiRepaint(); },
  1054. });
  1055. if (_flags & Flag::ServiceMessage) {
  1056. const auto &options = Ui::ItemTextServiceOptions();
  1057. _text.setMarkedText(st::serviceTextStyle, text, options, context);
  1058. auto linkIndex = 0;
  1059. for (const auto &link : links) {
  1060. // Link indices start with 1.
  1061. _text.setLink(++linkIndex, link);
  1062. }
  1063. } else {
  1064. const auto item = data();
  1065. const auto &options = Ui::ItemTextOptions(item);
  1066. clearSpecialOnlyEmoji();
  1067. _text.setMarkedText(st::messageTextStyle, text, options, context);
  1068. if (!item->_text.empty() && _text.isEmpty()){
  1069. // If server has allowed some text that we've trim-ed entirely,
  1070. // just replace it with something so that UI won't look buggy.
  1071. _text.setMarkedText(
  1072. st::messageTextStyle,
  1073. { u":-("_q },
  1074. Ui::ItemTextOptions(item));
  1075. }
  1076. if (!item->media()) {
  1077. checkSpecialOnlyEmoji();
  1078. refreshMedia(nullptr);
  1079. }
  1080. }
  1081. InitElementTextPart(this, _text);
  1082. _textWidth = -1;
  1083. _textHeight = 0;
  1084. }
  1085. void Element::validateTextSkipBlock(bool has, int width, int height) {
  1086. validateText();
  1087. if (!has) {
  1088. if (_text.removeSkipBlock()) {
  1089. _textWidth = -1;
  1090. _textHeight = 0;
  1091. }
  1092. } else if (_text.updateSkipBlock(width, height)) {
  1093. _textWidth = -1;
  1094. _textHeight = 0;
  1095. }
  1096. }
  1097. void Element::previousInBlocksChanged() {
  1098. recountDisplayDateInBlocks();
  1099. recountAttachToPreviousInBlocks();
  1100. }
  1101. void Element::nextInBlocksRemoved() {
  1102. setAttachToNext(false);
  1103. }
  1104. bool Element::markSponsoredViewed(int shownFromTop) const {
  1105. const auto sponsoredTextTop = height()
  1106. - st::msgPadding.bottom()
  1107. - st::historyViewButtonHeight;
  1108. return shownFromTop >= sponsoredTextTop;
  1109. }
  1110. void Element::refreshDataId() {
  1111. if (const auto media = this->media()) {
  1112. media->refreshParentId(data());
  1113. }
  1114. refreshDataIdHook();
  1115. }
  1116. bool Element::computeIsAttachToPrevious(not_null<Element*> previous) {
  1117. const auto mayBeAttached = [](not_null<Element*> view) {
  1118. const auto item = view->data();
  1119. return !item->isService()
  1120. && !item->isEmpty()
  1121. && !item->isPostHidingAuthor()
  1122. && (!item->history()->peer->isMegagroup()
  1123. || !view->hasOutLayout()
  1124. || !item->from()->isChannel());
  1125. };
  1126. const auto item = data();
  1127. if (!Has<DateBadge>()
  1128. && !Has<UnreadBar>()
  1129. && !Has<ServicePreMessage>()) {
  1130. const auto prev = previous->data();
  1131. const auto previousMarkup = prev->inlineReplyMarkup();
  1132. const auto possible = (std::abs(prev->date() - item->date())
  1133. < kAttachMessageToPreviousSecondsDelta)
  1134. && mayBeAttached(this)
  1135. && mayBeAttached(previous)
  1136. && (!previousMarkup || previousMarkup->hiddenBy(prev->media()))
  1137. && (item->topicRootId() == prev->topicRootId());
  1138. if (possible) {
  1139. const auto forwarded = item->Get<HistoryMessageForwarded>();
  1140. const auto prevForwarded = prev->Get<HistoryMessageForwarded>();
  1141. const auto peer = item->history()->peer;
  1142. if (peer->isSelf()
  1143. || peer->isRepliesChat()
  1144. || peer->isVerifyCodes()
  1145. || (forwarded && forwarded->imported)
  1146. || (prevForwarded && prevForwarded->imported)) {
  1147. return IsAttachedToPreviousInSavedMessages(
  1148. prev,
  1149. prevForwarded,
  1150. item,
  1151. forwarded);
  1152. } else {
  1153. return prev->from() == item->from();
  1154. }
  1155. }
  1156. }
  1157. return false;
  1158. }
  1159. ClickHandlerPtr Element::fromLink() const {
  1160. if (_fromLink) {
  1161. return _fromLink;
  1162. }
  1163. const auto item = data();
  1164. if (const auto from = item->displayFrom()) {
  1165. _fromLink = std::make_shared<LambdaClickHandler>([=](
  1166. ClickContext context) {
  1167. if (context.button != Qt::LeftButton) {
  1168. return;
  1169. }
  1170. const auto my = context.other.value<ClickHandlerContext>();
  1171. const auto session = &from->session();
  1172. if (const auto window = ContextOrSessionWindow(my, session)) {
  1173. window->showPeerInfo(from);
  1174. }
  1175. });
  1176. _fromLink->setProperty(kPeerLinkPeerIdProperty, from->id.value);
  1177. return _fromLink;
  1178. }
  1179. if (const auto forwarded = item->Get<HistoryMessageForwarded>()) {
  1180. if (forwarded->imported) {
  1181. static const auto imported = std::make_shared<LambdaClickHandler>([](
  1182. ClickContext context) {
  1183. const auto my = context.other.value<ClickHandlerContext>();
  1184. const auto weak = my.sessionWindow;
  1185. if (const auto strong = weak.get()) {
  1186. strong->showToast(tr::lng_forwarded_imported(tr::now));
  1187. }
  1188. });
  1189. return imported;
  1190. }
  1191. }
  1192. _fromLink = HiddenSenderInfo::ForwardClickHandler();
  1193. return _fromLink;
  1194. }
  1195. void Element::createUnreadBar(rpl::producer<QString> text) {
  1196. if (!AddComponents(UnreadBar::Bit())) {
  1197. return;
  1198. }
  1199. const auto bar = Get<UnreadBar>();
  1200. std::move(
  1201. text
  1202. ) | rpl::start_with_next([=](const QString &text) {
  1203. if (const auto bar = Get<UnreadBar>()) {
  1204. bar->init(text);
  1205. }
  1206. }, bar->lifetime);
  1207. if (data()->mainView() == this) {
  1208. recountAttachToPreviousInBlocks();
  1209. }
  1210. history()->owner().requestViewResize(this);
  1211. }
  1212. void Element::destroyUnreadBar() {
  1213. if (!Has<UnreadBar>()) {
  1214. return;
  1215. }
  1216. RemoveComponents(UnreadBar::Bit());
  1217. if (data()->mainView() == this) {
  1218. recountAttachToPreviousInBlocks();
  1219. }
  1220. history()->owner().requestViewResize(this);
  1221. }
  1222. int Element::displayedDateHeight() const {
  1223. if (auto date = Get<DateBadge>()) {
  1224. return date->height();
  1225. }
  1226. return 0;
  1227. }
  1228. bool Element::displayDate() const {
  1229. return Has<DateBadge>();
  1230. }
  1231. bool Element::isInOneDayWithPrevious() const {
  1232. return !data()->isEmpty() && !displayDate();
  1233. }
  1234. void Element::recountAttachToPreviousInBlocks() {
  1235. if (isHidden() || data()->isEmpty()) {
  1236. if (const auto next = nextDisplayedInBlocks()) {
  1237. next->recountAttachToPreviousInBlocks();
  1238. } else if (const auto previous = previousDisplayedInBlocks()) {
  1239. previous->setAttachToNext(false);
  1240. }
  1241. return;
  1242. }
  1243. auto attachToPrevious = false;
  1244. const auto previous = previousDisplayedInBlocks();
  1245. if (previous) {
  1246. attachToPrevious = computeIsAttachToPrevious(previous);
  1247. previous->setAttachToNext(attachToPrevious, this);
  1248. }
  1249. setAttachToPrevious(attachToPrevious, previous);
  1250. }
  1251. void Element::recountDisplayDateInBlocks() {
  1252. setDisplayDate([&] {
  1253. const auto item = data();
  1254. if (isHidden() || item->isEmpty()) {
  1255. return false;
  1256. }
  1257. if (item->isSponsored()) {
  1258. return false;
  1259. }
  1260. if (const auto previous = previousDisplayedInBlocks()) {
  1261. const auto prev = previous->data();
  1262. return prev->isEmpty()
  1263. || (previous->dateTime().date() != dateTime().date());
  1264. }
  1265. return true;
  1266. }());
  1267. }
  1268. QSize Element::countOptimalSize() {
  1269. _flags &= ~Flag::NeedsResize;
  1270. return performCountOptimalSize();
  1271. }
  1272. QSize Element::countCurrentSize(int newWidth) {
  1273. if (_flags & Flag::NeedsResize) {
  1274. initDimensions();
  1275. }
  1276. return performCountCurrentSize(newWidth);
  1277. }
  1278. bool Element::countIsTopicRootReply() const {
  1279. const auto item = data();
  1280. if (!item->history()->isForum()) {
  1281. return false;
  1282. }
  1283. const auto replyTo = item->replyToId();
  1284. return !replyTo || (item->topicRootId() == replyTo);
  1285. }
  1286. void Element::setDisplayDate(bool displayDate) {
  1287. const auto item = data();
  1288. if (item->hideDisplayDate()) {
  1289. displayDate = false;
  1290. }
  1291. if (displayDate && !Has<DateBadge>()) {
  1292. AddComponents(DateBadge::Bit());
  1293. Get<DateBadge>()->init(
  1294. ItemDateText(item, (_flags & Flag::ScheduledUntilOnline)));
  1295. setPendingResize();
  1296. } else if (!displayDate && Has<DateBadge>()) {
  1297. RemoveComponents(DateBadge::Bit());
  1298. setPendingResize();
  1299. }
  1300. }
  1301. void Element::setServicePreMessage(PreparedServiceText text) {
  1302. if (!text.text.empty()) {
  1303. AddComponents(ServicePreMessage::Bit());
  1304. const auto service = Get<ServicePreMessage>();
  1305. service->init(std::move(text));
  1306. setPendingResize();
  1307. } else if (Has<ServicePreMessage>()) {
  1308. RemoveComponents(ServicePreMessage::Bit());
  1309. setPendingResize();
  1310. }
  1311. }
  1312. void Element::setAttachToNext(bool attachToNext, Element *next) {
  1313. Expects(next || !attachToNext);
  1314. auto pending = false;
  1315. if (attachToNext && !(_flags & Flag::AttachedToNext)) {
  1316. _flags |= Flag::AttachedToNext;
  1317. pending = true;
  1318. } else if (!attachToNext && (_flags & Flag::AttachedToNext)) {
  1319. _flags &= ~Flag::AttachedToNext;
  1320. pending = true;
  1321. }
  1322. const auto bubble = attachToNext && !next->unwrapped();
  1323. if (bubble && !(_flags & Flag::BubbleAttachedToNext)) {
  1324. _flags |= Flag::BubbleAttachedToNext;
  1325. pending = true;
  1326. } else if (!bubble && (_flags & Flag::BubbleAttachedToNext)) {
  1327. _flags &= ~Flag::BubbleAttachedToNext;
  1328. pending = true;
  1329. }
  1330. if (pending) {
  1331. setPendingResize();
  1332. }
  1333. }
  1334. void Element::setAttachToPrevious(bool attachToPrevious, Element *previous) {
  1335. Expects(previous || !attachToPrevious);
  1336. auto pending = false;
  1337. if (attachToPrevious && !(_flags & Flag::AttachedToPrevious)) {
  1338. _flags |= Flag::AttachedToPrevious;
  1339. pending = true;
  1340. } else if (!attachToPrevious && (_flags & Flag::AttachedToPrevious)) {
  1341. _flags &= ~Flag::AttachedToPrevious;
  1342. pending = true;
  1343. }
  1344. const auto bubble = attachToPrevious && !previous->unwrapped();
  1345. if (bubble && !(_flags & Flag::BubbleAttachedToPrevious)) {
  1346. _flags |= Flag::BubbleAttachedToPrevious;
  1347. pending = true;
  1348. } else if (!bubble && (_flags & Flag::BubbleAttachedToPrevious)) {
  1349. _flags &= ~Flag::BubbleAttachedToPrevious;
  1350. pending = true;
  1351. }
  1352. if (pending) {
  1353. setPendingResize();
  1354. }
  1355. }
  1356. bool Element::displayFromPhoto() const {
  1357. return false;
  1358. }
  1359. bool Element::hasFromPhoto() const {
  1360. return false;
  1361. }
  1362. bool Element::hasFromName() const {
  1363. return false;
  1364. }
  1365. bool Element::displayReply() const {
  1366. return Has<Reply>();
  1367. }
  1368. bool Element::displayFromName() const {
  1369. return false;
  1370. }
  1371. TopicButton *Element::displayedTopicButton() const {
  1372. return nullptr;
  1373. }
  1374. bool Element::displayForwardedFrom() const {
  1375. return false;
  1376. }
  1377. bool Element::hasOutLayout() const {
  1378. return false;
  1379. }
  1380. bool Element::hasRightLayout() const {
  1381. return hasOutLayout() && !_delegate->elementIsChatWide();
  1382. }
  1383. bool Element::drawBubble() const {
  1384. return false;
  1385. }
  1386. bool Element::hasBubble() const {
  1387. return false;
  1388. }
  1389. bool Element::unwrapped() const {
  1390. return true;
  1391. }
  1392. std::optional<QSize> Element::rightActionSize() const {
  1393. return std::nullopt;
  1394. }
  1395. void Element::drawRightAction(
  1396. Painter &p,
  1397. const PaintContext &context,
  1398. int left,
  1399. int top,
  1400. int outerWidth) const {
  1401. }
  1402. ClickHandlerPtr Element::rightActionLink(
  1403. std::optional<QPoint> pressPoint) const {
  1404. return ClickHandlerPtr();
  1405. }
  1406. TimeId Element::displayedEditDate() const {
  1407. return TimeId(0);
  1408. }
  1409. bool Element::toggleSelectionByHandlerClick(
  1410. const ClickHandlerPtr &handler) const {
  1411. return false;
  1412. }
  1413. bool Element::allowTextSelectionByHandler(
  1414. const ClickHandlerPtr &handler) const {
  1415. return false;
  1416. }
  1417. bool Element::usesBubblePattern(const PaintContext &context) const {
  1418. return (context.selection != FullSelection)
  1419. && hasOutLayout()
  1420. && context.bubblesPattern
  1421. && !context.viewport.isEmpty()
  1422. && !context.bubblesPattern->pixmap.size().isEmpty();
  1423. }
  1424. bool Element::hasVisibleText() const {
  1425. return false;
  1426. }
  1427. int Element::textualMaxWidth() const {
  1428. return st::msgPadding.left()
  1429. + (hasVisibleText() ? text().maxWidth() : 0)
  1430. + st::msgPadding.right();
  1431. }
  1432. auto Element::verticalRepaintRange() const -> VerticalRepaintRange {
  1433. return {
  1434. .top = 0,
  1435. .height = height()
  1436. };
  1437. }
  1438. bool Element::hasHeavyPart() const {
  1439. return (_flags & Flag::HeavyCustomEmoji);
  1440. }
  1441. void Element::checkHeavyPart() {
  1442. if (!hasHeavyPart() && (!_media || !_media->hasHeavyPart())) {
  1443. history()->owner().unregisterHeavyViewPart(this);
  1444. }
  1445. }
  1446. bool Element::isSignedAuthorElided() const {
  1447. return false;
  1448. }
  1449. void Element::setupReactions(Element *replacing) {
  1450. refreshReactions();
  1451. auto animations = replacing
  1452. ? replacing->takeReactionAnimations()
  1453. : base::flat_map<
  1454. Data::ReactionId,
  1455. std::unique_ptr<Ui::ReactionFlyAnimation>>();
  1456. if (!animations.empty()) {
  1457. const auto repainter = [=] { repaint(); };
  1458. for (const auto &[id, animation] : animations) {
  1459. animation->setRepaintCallback(repainter);
  1460. }
  1461. if (_reactions) {
  1462. _reactions->continueAnimations(std::move(animations));
  1463. }
  1464. }
  1465. }
  1466. void Element::refreshReactions() {
  1467. using namespace Reactions;
  1468. auto reactionsData = InlineListDataFromMessage(this);
  1469. if (reactionsData.reactions.empty()) {
  1470. setReactions(nullptr);
  1471. return;
  1472. }
  1473. if (!_reactions) {
  1474. const auto handlerFactory = [=](ReactionId id) {
  1475. const auto weak = base::make_weak(this);
  1476. return std::make_shared<LambdaClickHandler>([=](
  1477. ClickContext context) {
  1478. const auto strong = weak.get();
  1479. if (!strong) {
  1480. return;
  1481. }
  1482. const auto item = strong->data();
  1483. const auto controller = ExtractController(context);
  1484. if (item->reactionsAreTags()) {
  1485. if (item->history()->session().premium()) {
  1486. const auto tag = Data::SearchTagToQuery(id);
  1487. HashtagClickHandler(tag).onClick(context);
  1488. } else if (controller) {
  1489. ShowPremiumPreviewBox(
  1490. controller,
  1491. PremiumFeature::TagsForMessages);
  1492. }
  1493. return;
  1494. }
  1495. if (id.paid()) {
  1496. Payments::TryAddingPaidReaction(
  1497. item,
  1498. weak.get(),
  1499. 1,
  1500. std::nullopt,
  1501. controller->uiShow());
  1502. return;
  1503. } else {
  1504. const auto source = HistoryReactionSource::Existing;
  1505. item->toggleReaction(id, source);
  1506. }
  1507. if (const auto now = weak.get()) {
  1508. const auto chosen = now->data()->chosenReactions();
  1509. if (id.paid() || ranges::contains(chosen, id)) {
  1510. now->animateReaction({
  1511. .id = id,
  1512. });
  1513. }
  1514. }
  1515. });
  1516. };
  1517. setReactions(std::make_unique<InlineList>(
  1518. &history()->owner().reactions(),
  1519. handlerFactory,
  1520. [=] { customEmojiRepaint(); },
  1521. std::move(reactionsData)));
  1522. } else {
  1523. auto was = _reactions->computeTagsList();
  1524. _reactions->update(std::move(reactionsData), width());
  1525. auto now = _reactions->computeTagsList();
  1526. if (!was.empty() || !now.empty()) {
  1527. auto &owner = history()->owner();
  1528. owner.viewTagsChanged(this, std::move(was), std::move(now));
  1529. }
  1530. }
  1531. }
  1532. void Element::setReactions(std::unique_ptr<Reactions::InlineList> list) {
  1533. auto was = _reactions
  1534. ? _reactions->computeTagsList()
  1535. : std::vector<Data::ReactionId>();
  1536. _reactions = std::move(list);
  1537. auto now = _reactions
  1538. ? _reactions->computeTagsList()
  1539. : std::vector<Data::ReactionId>();
  1540. if (!was.empty() || !now.empty()) {
  1541. auto &owner = history()->owner();
  1542. owner.viewTagsChanged(this, std::move(was), std::move(now));
  1543. }
  1544. }
  1545. bool Element::updateReactions() {
  1546. const auto wasReactions = _reactions
  1547. ? _reactions->currentSize()
  1548. : QSize();
  1549. refreshReactions();
  1550. const auto nowReactions = _reactions
  1551. ? _reactions->currentSize()
  1552. : QSize();
  1553. return (wasReactions != nowReactions);
  1554. }
  1555. void Element::itemDataChanged() {
  1556. if (updateReactions()) {
  1557. history()->owner().requestViewResize(this);
  1558. } else {
  1559. repaint();
  1560. }
  1561. }
  1562. void Element::itemTextUpdated() {
  1563. if (const auto media = _media.get()) {
  1564. media->parentTextUpdated();
  1565. }
  1566. clearSpecialOnlyEmoji();
  1567. _text = Ui::Text::String(st::msgMinWidth);
  1568. _textWidth = -1;
  1569. _textHeight = 0;
  1570. if (_media && !data()->media()) {
  1571. refreshMedia(nullptr);
  1572. }
  1573. }
  1574. void Element::blockquoteExpandChanged() {
  1575. _textWidth = -1;
  1576. _textHeight = 0;
  1577. history()->owner().requestViewResize(this);
  1578. }
  1579. void Element::unloadHeavyPart() {
  1580. history()->owner().unregisterHeavyViewPart(this);
  1581. if (_reactions) {
  1582. _reactions->unloadCustomEmoji();
  1583. }
  1584. if (_media) {
  1585. _media->unloadHeavyPart();
  1586. }
  1587. if (_flags & Flag::HeavyCustomEmoji) {
  1588. _flags &= ~Flag::HeavyCustomEmoji;
  1589. _text.unloadPersistentAnimation();
  1590. if (const auto reply = Get<Reply>()) {
  1591. reply->unloadPersistentAnimation();
  1592. }
  1593. }
  1594. }
  1595. HistoryBlock *Element::block() {
  1596. return _block;
  1597. }
  1598. const HistoryBlock *Element::block() const {
  1599. return _block;
  1600. }
  1601. void Element::attachToBlock(not_null<HistoryBlock*> block, int index) {
  1602. Expects(_data->isHistoryEntry());
  1603. Expects(_block == nullptr);
  1604. Expects(_indexInBlock < 0);
  1605. Expects(index >= 0);
  1606. _block = block;
  1607. _indexInBlock = index;
  1608. _data->setMainView(this);
  1609. previousInBlocksChanged();
  1610. }
  1611. void Element::removeFromBlock() {
  1612. Expects(_block != nullptr);
  1613. _block->remove(this);
  1614. }
  1615. void Element::refreshInBlock() {
  1616. Expects(_block != nullptr);
  1617. _block->refreshView(this);
  1618. }
  1619. void Element::setIndexInBlock(int index) {
  1620. Expects(_block != nullptr);
  1621. Expects(index >= 0);
  1622. _indexInBlock = index;
  1623. }
  1624. int Element::indexInBlock() const {
  1625. Expects((_indexInBlock >= 0) == (_block != nullptr));
  1626. Expects((_block == nullptr) || (_block->messages[_indexInBlock].get() == this));
  1627. return _indexInBlock;
  1628. }
  1629. Element *Element::previousInBlocks() const {
  1630. if (_block && _indexInBlock >= 0) {
  1631. if (_indexInBlock > 0) {
  1632. return _block->messages[_indexInBlock - 1].get();
  1633. }
  1634. if (auto previous = _block->previousBlock()) {
  1635. Assert(!previous->messages.empty());
  1636. return previous->messages.back().get();
  1637. }
  1638. }
  1639. return nullptr;
  1640. }
  1641. Element *Element::previousDisplayedInBlocks() const {
  1642. auto result = previousInBlocks();
  1643. while (result && (result->data()->isEmpty() || result->isHidden())) {
  1644. result = result->previousInBlocks();
  1645. }
  1646. return result;
  1647. }
  1648. Element *Element::nextInBlocks() const {
  1649. if (_block && _indexInBlock >= 0) {
  1650. if (_indexInBlock + 1 < _block->messages.size()) {
  1651. return _block->messages[_indexInBlock + 1].get();
  1652. }
  1653. if (auto next = _block->nextBlock()) {
  1654. Assert(!next->messages.empty());
  1655. return next->messages.front().get();
  1656. }
  1657. }
  1658. return nullptr;
  1659. }
  1660. Element *Element::nextDisplayedInBlocks() const {
  1661. auto result = nextInBlocks();
  1662. while (result && (result->data()->isEmpty() || result->isHidden())) {
  1663. result = result->nextInBlocks();
  1664. }
  1665. return result;
  1666. }
  1667. void Element::drawInfo(
  1668. Painter &p,
  1669. const PaintContext &context,
  1670. int right,
  1671. int bottom,
  1672. int width,
  1673. InfoDisplayType type) const {
  1674. }
  1675. TextState Element::bottomInfoTextState(
  1676. int right,
  1677. int bottom,
  1678. QPoint point,
  1679. InfoDisplayType type) const {
  1680. return TextState();
  1681. }
  1682. TextSelection Element::adjustSelection(
  1683. TextSelection selection,
  1684. TextSelectType type) const {
  1685. return selection;
  1686. }
  1687. SelectedQuote Element::FindSelectedQuote(
  1688. const Ui::Text::String &text,
  1689. TextSelection selection,
  1690. not_null<HistoryItem*> item) {
  1691. if (selection.to > text.length()) {
  1692. return {};
  1693. }
  1694. auto modified = selection;
  1695. for (const auto &modification : text.modifications()) {
  1696. if (modification.position >= selection.to) {
  1697. break;
  1698. } else if (modification.position <= selection.from) {
  1699. modified.from += modification.skipped;
  1700. if (modification.added
  1701. && modification.position < selection.from) {
  1702. --modified.from;
  1703. }
  1704. }
  1705. modified.to += modification.skipped;
  1706. if (modification.added && modified.to > modified.from) {
  1707. --modified.to;
  1708. }
  1709. }
  1710. auto result = item->originalText();
  1711. if (modified.empty() || modified.to > result.text.size()) {
  1712. return {};
  1713. }
  1714. const auto session = &item->history()->session();
  1715. const auto limit = session->appConfig().quoteLengthMax();
  1716. const auto overflown = (modified.from + limit < modified.to);
  1717. if (overflown) {
  1718. modified.to = modified.from + limit;
  1719. }
  1720. result.text = result.text.mid(
  1721. modified.from,
  1722. modified.to - modified.from);
  1723. const auto allowed = std::array{
  1724. EntityType::Bold,
  1725. EntityType::Italic,
  1726. EntityType::Underline,
  1727. EntityType::StrikeOut,
  1728. EntityType::Spoiler,
  1729. EntityType::CustomEmoji,
  1730. };
  1731. for (auto i = result.entities.begin(); i != result.entities.end();) {
  1732. const auto offset = i->offset();
  1733. const auto till = offset + i->length();
  1734. if ((till <= modified.from)
  1735. || (offset >= modified.to)
  1736. || !ranges::contains(allowed, i->type())) {
  1737. i = result.entities.erase(i);
  1738. } else {
  1739. if (till > modified.to) {
  1740. i->shrinkFromRight(till - modified.to);
  1741. }
  1742. i->shiftLeft(modified.from);
  1743. ++i;
  1744. }
  1745. }
  1746. return { item, result, modified.from, overflown };
  1747. }
  1748. TextSelection Element::FindSelectionFromQuote(
  1749. const Ui::Text::String &text,
  1750. const SelectedQuote &quote) {
  1751. Expects(quote.item != nullptr);
  1752. if (quote.text.empty()) {
  1753. return {};
  1754. }
  1755. const auto &original = quote.item->originalText();
  1756. if (quote.offset == kSearchQueryOffsetHint) {
  1757. return ApplyModificationsFrom(
  1758. FindSearchQueryHighlight(original.text, quote.text.text),
  1759. text);
  1760. }
  1761. const auto length = int(original.text.size());
  1762. const auto qlength = int(quote.text.text.size());
  1763. const auto checkAt = [&](int offset) {
  1764. return TextSelection{
  1765. uint16(offset),
  1766. uint16(offset + qlength),
  1767. };
  1768. };
  1769. const auto findOneAfter = [&](int offset) {
  1770. if (offset > length - qlength) {
  1771. return TextSelection();
  1772. }
  1773. const auto i = original.text.indexOf(quote.text.text, offset);
  1774. return (i >= 0) ? checkAt(i) : TextSelection();
  1775. };
  1776. const auto findOneBefore = [&](int offset) {
  1777. if (!offset) {
  1778. return TextSelection();
  1779. }
  1780. const auto end = std::min(offset + qlength - 1, length);
  1781. const auto from = end - length - 1;
  1782. const auto i = original.text.lastIndexOf(quote.text.text, from);
  1783. return (i >= 0) ? checkAt(i) : TextSelection();
  1784. };
  1785. const auto findAfter = [&](int offset) {
  1786. while (true) {
  1787. const auto result = findOneAfter(offset);
  1788. if (!result.empty() || result == TextSelection()) {
  1789. return result;
  1790. }
  1791. offset = result.from;
  1792. }
  1793. };
  1794. const auto findBefore = [&](int offset) {
  1795. while (true) {
  1796. const auto result = findOneBefore(offset);
  1797. if (!result.empty() || result == TextSelection()) {
  1798. return result;
  1799. }
  1800. offset = result.from - 2;
  1801. if (offset < 0) {
  1802. return result;
  1803. }
  1804. }
  1805. };
  1806. const auto findTwoWays = [&](int offset) {
  1807. const auto after = findAfter(offset);
  1808. if (after.empty()) {
  1809. return findBefore(offset);
  1810. } else if (after.from == offset) {
  1811. return after;
  1812. }
  1813. const auto before = findBefore(offset);
  1814. return before.empty()
  1815. ? after
  1816. : (offset - before.from < after.from - offset)
  1817. ? before
  1818. : after;
  1819. };
  1820. auto result = findTwoWays(quote.offset);
  1821. if (result.empty()) {
  1822. return {};
  1823. }
  1824. return ApplyModificationsFrom(result, text);
  1825. }
  1826. Reactions::ButtonParameters Element::reactionButtonParameters(
  1827. QPoint position,
  1828. const TextState &reactionState) const {
  1829. return {};
  1830. }
  1831. int Element::reactionsOptimalWidth() const {
  1832. return 0;
  1833. }
  1834. void Element::clickHandlerActiveChanged(
  1835. const ClickHandlerPtr &handler,
  1836. bool active) {
  1837. if (const auto markup = _data->Get<HistoryMessageReplyMarkup>()) {
  1838. if (const auto keyboard = markup->inlineKeyboard.get()) {
  1839. keyboard->clickHandlerActiveChanged(handler, active);
  1840. }
  1841. }
  1842. HoveredLink(active ? this : nullptr);
  1843. repaint();
  1844. if (const auto media = this->media()) {
  1845. media->clickHandlerActiveChanged(handler, active);
  1846. }
  1847. }
  1848. void Element::clickHandlerPressedChanged(
  1849. const ClickHandlerPtr &handler,
  1850. bool pressed) {
  1851. PressedLink(pressed ? this : nullptr);
  1852. repaint();
  1853. if (const auto media = this->media()) {
  1854. media->clickHandlerPressedChanged(handler, pressed);
  1855. }
  1856. }
  1857. void Element::animateUnreadReactions() {
  1858. const auto &recent = data()->recentReactions();
  1859. for (const auto &[id, list] : recent) {
  1860. if (ranges::contains(list, true, &Data::RecentReaction::unread)) {
  1861. animateReaction({ .id = id });
  1862. }
  1863. }
  1864. }
  1865. auto Element::takeReactionAnimations()
  1866. -> base::flat_map<
  1867. Data::ReactionId,
  1868. std::unique_ptr<Ui::ReactionFlyAnimation>> {
  1869. if (_reactions) {
  1870. return _reactions->takeAnimations();
  1871. }
  1872. return {};
  1873. }
  1874. void Element::animateEffect(Ui::ReactionFlyAnimationArgs &&args) {
  1875. }
  1876. void Element::animateUnreadEffect() {
  1877. }
  1878. auto Element::takeEffectAnimation()
  1879. -> std::unique_ptr<Ui::ReactionFlyAnimation> {
  1880. return nullptr;
  1881. }
  1882. QRect Element::effectIconGeometry() const {
  1883. return QRect();
  1884. }
  1885. Element::~Element() {
  1886. setReactions(nullptr);
  1887. // Delete media while owner still exists.
  1888. clearSpecialOnlyEmoji();
  1889. base::take(_media);
  1890. if (_flags & Flag::HeavyCustomEmoji) {
  1891. _flags &= ~Flag::HeavyCustomEmoji;
  1892. _text.unloadPersistentAnimation();
  1893. checkHeavyPart();
  1894. }
  1895. if (_data->mainView() == this) {
  1896. _data->clearMainView();
  1897. }
  1898. if (_context == Context::History) {
  1899. history()->owner().notifyViewRemoved(this);
  1900. }
  1901. history()->owner().unregisterItemView(this);
  1902. }
  1903. void Element::Hovered(Element *view) {
  1904. HoveredElement = view;
  1905. }
  1906. Element *Element::Hovered() {
  1907. return HoveredElement;
  1908. }
  1909. void Element::Pressed(Element *view) {
  1910. PressedElement = view;
  1911. }
  1912. Element *Element::Pressed() {
  1913. return PressedElement;
  1914. }
  1915. void Element::HoveredLink(Element *view) {
  1916. HoveredLinkElement = view;
  1917. }
  1918. Element *Element::HoveredLink() {
  1919. return HoveredLinkElement;
  1920. }
  1921. void Element::PressedLink(Element *view) {
  1922. PressedLinkElement = view;
  1923. }
  1924. Element *Element::PressedLink() {
  1925. return PressedLinkElement;
  1926. }
  1927. void Element::Moused(Element *view) {
  1928. MousedElement = view;
  1929. }
  1930. Element *Element::Moused() {
  1931. return MousedElement;
  1932. }
  1933. void Element::ClearGlobal() {
  1934. HoveredElement = nullptr;
  1935. PressedElement = nullptr;
  1936. HoveredLinkElement = nullptr;
  1937. PressedLinkElement = nullptr;
  1938. MousedElement = nullptr;
  1939. }
  1940. int FindViewY(not_null<Element*> view, uint16 symbol, int yfrom) {
  1941. auto request = HistoryView::StateRequest();
  1942. request.flags = Ui::Text::StateRequest::Flag::LookupSymbol;
  1943. const auto single = st::messageTextStyle.font->height;
  1944. const auto inner = view->innerGeometry();
  1945. const auto origin = inner.topLeft();
  1946. const auto top = 0;
  1947. const auto bottom = view->height();
  1948. if (origin.y() < top
  1949. || origin.y() + inner.height() > bottom
  1950. || inner.height() <= 0) {
  1951. return yfrom;
  1952. }
  1953. const auto fory = [&](int y) {
  1954. return view->textState(origin + QPoint(0, y), request).symbol;
  1955. };
  1956. yfrom = std::max(yfrom - origin.y(), 0);
  1957. auto ytill = inner.height() - 1;
  1958. auto symbolfrom = fory(yfrom);
  1959. auto symboltill = fory(ytill);
  1960. if ((yfrom >= ytill) || (symbolfrom >= symbol)) {
  1961. return origin.y() + yfrom;
  1962. } else if (symboltill <= symbol) {
  1963. return origin.y() + ytill;
  1964. }
  1965. while (ytill - yfrom >= 2 * single) {
  1966. const auto middle = (yfrom + ytill) / 2;
  1967. const auto found = fory(middle);
  1968. if (found == symbol
  1969. || symbolfrom > found
  1970. || symboltill < found) {
  1971. return middle;
  1972. } else if (found < symbol) {
  1973. yfrom = middle;
  1974. symbolfrom = found;
  1975. } else {
  1976. ytill = middle;
  1977. symboltill = found;
  1978. }
  1979. }
  1980. return origin.y() + (yfrom + ytill) / 2;
  1981. }
  1982. Window::SessionController *ExtractController(const ClickContext &context) {
  1983. const auto my = context.other.value<ClickHandlerContext>();
  1984. if (const auto controller = my.sessionWindow.get()) {
  1985. return controller;
  1986. }
  1987. return nullptr;
  1988. }
  1989. } // namespace HistoryView