chat_style.cpp 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178
  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/chat_style.h"
  8. #include "ui/chat/chat_theme.h"
  9. #include "ui/image/image_prepare.h" // ImageRoundRadius
  10. #include "ui/text/text_custom_emoji.h"
  11. #include "ui/color_contrast.h"
  12. #include "ui/painter.h"
  13. #include "ui/ui_utility.h"
  14. #include "styles/style_chat.h"
  15. #include "styles/style_dialogs.h"
  16. #include "styles/style_widgets.h"
  17. namespace Ui {
  18. namespace {
  19. void EnsureCorners(
  20. CornersPixmaps &corners,
  21. int radius,
  22. const style::color &color,
  23. const style::color *shadow = nullptr) {
  24. if (corners.p[0].isNull()) {
  25. corners = PrepareCornerPixmaps(radius, color, shadow);
  26. }
  27. }
  28. void EnsureBlockquoteCache(
  29. std::unique_ptr<Text::QuotePaintCache> &cache,
  30. Fn<ColorIndexValues()> values) {
  31. if (cache) {
  32. return;
  33. }
  34. cache = std::make_unique<Text::QuotePaintCache>();
  35. const auto &colors = values();
  36. cache->bg = colors.bg;
  37. cache->outlines = colors.outlines;
  38. cache->icon = colors.name;
  39. }
  40. void EnsurePreCache(
  41. std::unique_ptr<Text::QuotePaintCache> &cache,
  42. const style::color &color,
  43. Fn<std::optional<QColor>()> bgOverride) {
  44. if (cache) {
  45. return;
  46. }
  47. cache = std::make_unique<Text::QuotePaintCache>();
  48. const auto bg = bgOverride();
  49. cache->bg = bg.value_or(color->c);
  50. if (!bg) {
  51. cache->bg.setAlpha(kDefaultBgOpacity * 255);
  52. }
  53. cache->outlines[0] = color->c;
  54. cache->outlines[0].setAlpha(kDefaultOutline1Opacity * 255);
  55. cache->outlines[1] = cache->outlines[2] = QColor(0, 0, 0, 0);
  56. cache->header = color->c;
  57. cache->header.setAlpha(kDefaultOutline2Opacity * 255);
  58. cache->icon = cache->outlines[0];
  59. cache->icon.setAlpha(kDefaultOutline3Opacity * 255);
  60. }
  61. } // namespace
  62. not_null<const MessageStyle*> ChatPaintContext::messageStyle() const {
  63. return &st->messageStyle(outbg, selected());
  64. }
  65. not_null<const MessageImageStyle*> ChatPaintContext::imageStyle() const {
  66. return &st->imageStyle(selected());
  67. }
  68. not_null<Text::QuotePaintCache*> ChatPaintContext::quoteCache(
  69. uint8 colorIndex) const {
  70. return !outbg
  71. ? st->coloredQuoteCache(selected(), colorIndex).get()
  72. : messageStyle()->quoteCache[
  73. st->colorPatternIndex(colorIndex)].get();
  74. }
  75. int HistoryServiceMsgRadius() {
  76. static const auto result = [] {
  77. const auto minMessageHeight = st::msgServicePadding.top()
  78. + st::msgServiceFont->height
  79. + st::msgServicePadding.bottom();
  80. return minMessageHeight / 2;
  81. }();
  82. return result;
  83. }
  84. int HistoryServiceMsgInvertedRadius() {
  85. static const auto result = [] {
  86. const auto minRowHeight = st::msgServiceFont->height;
  87. return minRowHeight - HistoryServiceMsgRadius();
  88. }();
  89. return result;
  90. }
  91. int HistoryServiceMsgInvertedShrink() {
  92. static const auto result = [] {
  93. return (HistoryServiceMsgInvertedRadius() * 2) / 3;
  94. }();
  95. return result;
  96. }
  97. ColorIndexValues SimpleColorIndexValues(QColor color, int patternIndex) {
  98. auto bg = color;
  99. bg.setAlpha(kDefaultBgOpacity * 255);
  100. auto result = ColorIndexValues{
  101. .name = color,
  102. .bg = bg,
  103. };
  104. result.outlines[0] = color;
  105. result.outlines[0].setAlpha(kDefaultOutline1Opacity * 255);
  106. if (patternIndex > 1) {
  107. result.outlines[1] = result.outlines[0];
  108. result.outlines[1].setAlpha(kDefaultOutline2Opacity * 255);
  109. result.outlines[2] = result.outlines[0];
  110. result.outlines[2].setAlpha(kDefaultOutline3Opacity * 255);
  111. } else if (patternIndex > 0) {
  112. result.outlines[1] = result.outlines[0];
  113. result.outlines[1].setAlpha(kDefaultOutlineOpacitySecond * 255);
  114. result.outlines[2] = QColor(0, 0, 0, 0);
  115. } else {
  116. result.outlines[1] = result.outlines[2] = QColor(0, 0, 0, 0);
  117. }
  118. return result;
  119. }
  120. int BackgroundEmojiData::CacheIndex(
  121. bool selected,
  122. bool outbg,
  123. bool inbubble,
  124. uint8 colorIndexPlusOne) {
  125. const auto base = colorIndexPlusOne
  126. ? (colorIndexPlusOne - 1)
  127. : (kColorIndexCount + (!inbubble ? 0 : outbg ? 1 : 2));
  128. return (base * 2) + (selected ? 1 : 0);
  129. };
  130. int ColorPatternIndex(
  131. const ColorIndicesCompressed &indices,
  132. uint8 colorIndex,
  133. bool dark) {
  134. Expects(colorIndex >= 0 && colorIndex < kColorIndexCount);
  135. if (!indices.colors
  136. || colorIndex < kSimpleColorIndexCount) {
  137. return 0;
  138. }
  139. auto &data = (*indices.colors)[colorIndex];
  140. auto &colors = dark ? data.dark : data.light;
  141. return colors[2] ? 2 : colors[1] ? 1 : 0;
  142. }
  143. ChatStyle::ChatStyle(rpl::producer<ColorIndicesCompressed> colorIndices) {
  144. if (colorIndices) {
  145. _colorIndicesLifetime = std::move(
  146. colorIndices
  147. ) | rpl::start_with_next([=](ColorIndicesCompressed &&indices) {
  148. _colorIndices = std::move(indices);
  149. });
  150. }
  151. finalize();
  152. make(_historyPsaForwardPalette, st::historyPsaForwardPalette);
  153. make(_imgReplyTextPalette, st::imgReplyTextPalette);
  154. make(_serviceTextPalette, st::serviceTextPalette);
  155. make(_priceTagTextPalette, st::priceTagTextPalette);
  156. make(_historyRepliesInvertedIcon, st::historyRepliesInvertedIcon);
  157. make(_historyViewsInvertedIcon, st::historyViewsInvertedIcon);
  158. make(_historyViewsSendingIcon, st::historyViewsSendingIcon);
  159. make(
  160. _historyViewsSendingInvertedIcon,
  161. st::historyViewsSendingInvertedIcon);
  162. make(_historyPinInvertedIcon, st::historyPinInvertedIcon);
  163. make(_historySendingIcon, st::historySendingIcon);
  164. make(_historySendingInvertedIcon, st::historySendingInvertedIcon);
  165. make(_historySentInvertedIcon, st::historySentInvertedIcon);
  166. make(_historyReceivedInvertedIcon, st::historyReceivedInvertedIcon);
  167. make(_msgBotKbUrlIcon, st::msgBotKbUrlIcon);
  168. make(_msgBotKbPaymentIcon, st::msgBotKbPaymentIcon);
  169. make(_msgBotKbSwitchPmIcon, st::msgBotKbSwitchPmIcon);
  170. make(_msgBotKbWebviewIcon, st::msgBotKbWebviewIcon);
  171. make(_msgBotKbCopyIcon, st::msgBotKbCopyIcon);
  172. make(_historyFastCommentsIcon, st::historyFastCommentsIcon);
  173. make(_historyFastShareIcon, st::historyFastShareIcon);
  174. make(_historyFastTranscribeIcon, st::historyFastTranscribeIcon);
  175. make(_historyFastTranscribeLock, st::historyFastTranscribeLock);
  176. make(_historyGoToOriginalIcon, st::historyGoToOriginalIcon);
  177. make(_historyFastCloseIcon, st::historyFastCloseIcon);
  178. make(_historyFastMoreIcon, st::historyFastMoreIcon);
  179. make(_historyMapPoint, st::historyMapPoint);
  180. make(_historyMapPointInner, st::historyMapPointInner);
  181. make(_youtubeIcon, st::youtubeIcon);
  182. make(_videoIcon, st::videoIcon);
  183. make(_historyPollChoiceRight, st::historyPollChoiceRight);
  184. make(_historyPollChoiceWrong, st::historyPollChoiceWrong);
  185. make(
  186. &MessageStyle::msgBg,
  187. st::msgInBg,
  188. st::msgInBgSelected,
  189. st::msgOutBg,
  190. st::msgOutBgSelected);
  191. make(
  192. &MessageStyle::msgShadow,
  193. st::msgInShadow,
  194. st::msgInShadowSelected,
  195. st::msgOutShadow,
  196. st::msgOutShadowSelected);
  197. make(
  198. &MessageStyle::msgServiceFg,
  199. st::msgInServiceFg,
  200. st::msgInServiceFgSelected,
  201. st::msgOutServiceFg,
  202. st::msgOutServiceFgSelected);
  203. make(
  204. &MessageStyle::msgDateFg,
  205. st::msgInDateFg,
  206. st::msgInDateFgSelected,
  207. st::msgOutDateFg,
  208. st::msgOutDateFgSelected);
  209. make(
  210. &MessageStyle::msgFileThumbLinkFg,
  211. st::msgFileThumbLinkInFg,
  212. st::msgFileThumbLinkInFgSelected,
  213. st::msgFileThumbLinkOutFg,
  214. st::msgFileThumbLinkOutFgSelected);
  215. make(
  216. &MessageStyle::msgFileBg,
  217. st::msgFileInBg,
  218. st::msgFileInBgSelected,
  219. st::msgFileOutBg,
  220. st::msgFileOutBgSelected);
  221. make(
  222. &MessageStyle::msgReplyBarColor,
  223. st::msgInReplyBarColor,
  224. st::msgInReplyBarSelColor,
  225. st::msgOutReplyBarColor,
  226. st::msgOutReplyBarSelColor);
  227. make(
  228. &MessageStyle::msgWaveformActive,
  229. st::msgWaveformInActive,
  230. st::msgWaveformInActiveSelected,
  231. st::msgWaveformOutActive,
  232. st::msgWaveformOutActiveSelected);
  233. make(
  234. &MessageStyle::msgWaveformInactive,
  235. st::msgWaveformInInactive,
  236. st::msgWaveformInInactiveSelected,
  237. st::msgWaveformOutInactive,
  238. st::msgWaveformOutInactiveSelected);
  239. make(
  240. &MessageStyle::historyTextFg,
  241. st::historyTextInFg,
  242. st::historyTextInFgSelected,
  243. st::historyTextOutFg,
  244. st::historyTextOutFgSelected);
  245. make(
  246. &MessageStyle::historyFileNameFg,
  247. st::historyFileNameInFg,
  248. st::historyFileNameInFgSelected,
  249. st::historyFileNameOutFg,
  250. st::historyFileNameOutFgSelected);
  251. make(
  252. &MessageStyle::historyFileRadialFg,
  253. st::historyFileInRadialFg,
  254. st::historyFileInRadialFgSelected,
  255. st::historyFileOutRadialFg,
  256. st::historyFileOutRadialFgSelected);
  257. make(
  258. &MessageStyle::mediaFg,
  259. st::mediaInFg,
  260. st::mediaInFgSelected,
  261. st::mediaOutFg,
  262. st::mediaOutFgSelected);
  263. make(
  264. &MessageStyle::textPalette,
  265. st::inTextPalette,
  266. st::inTextPaletteSelected,
  267. st::outTextPalette,
  268. st::outTextPaletteSelected);
  269. make(
  270. &MessageStyle::semiboldPalette,
  271. st::inSemiboldPalette,
  272. st::inTextPaletteSelected,
  273. st::outSemiboldPalette,
  274. st::outTextPaletteSelected);
  275. make(
  276. &MessageStyle::fwdTextPalette,
  277. st::inFwdTextPalette,
  278. st::inFwdTextPaletteSelected,
  279. st::outFwdTextPalette,
  280. st::outFwdTextPaletteSelected);
  281. make(
  282. &MessageStyle::replyTextPalette,
  283. st::inReplyTextPalette,
  284. st::inReplyTextPaletteSelected,
  285. st::outReplyTextPalette,
  286. st::outReplyTextPaletteSelected);
  287. make(
  288. &MessageStyle::tailLeft,
  289. st::historyBubbleTailInLeft,
  290. st::historyBubbleTailInLeftSelected,
  291. st::historyBubbleTailOutLeft,
  292. st::historyBubbleTailOutLeftSelected);
  293. make(
  294. &MessageStyle::tailRight,
  295. st::historyBubbleTailInRight,
  296. st::historyBubbleTailInRightSelected,
  297. st::historyBubbleTailOutRight,
  298. st::historyBubbleTailOutRightSelected);
  299. make(
  300. &MessageStyle::historyRepliesIcon,
  301. st::historyRepliesInIcon,
  302. st::historyRepliesInSelectedIcon,
  303. st::historyRepliesOutIcon,
  304. st::historyRepliesOutSelectedIcon);
  305. make(
  306. &MessageStyle::historyViewsIcon,
  307. st::historyViewsInIcon,
  308. st::historyViewsInSelectedIcon,
  309. st::historyViewsOutIcon,
  310. st::historyViewsOutSelectedIcon);
  311. make(
  312. &MessageStyle::historyPinIcon,
  313. st::historyPinInIcon,
  314. st::historyPinInSelectedIcon,
  315. st::historyPinOutIcon,
  316. st::historyPinOutSelectedIcon);
  317. make(
  318. &MessageStyle::historySentIcon,
  319. st::historySentIcon,
  320. st::historySentSelectedIcon,
  321. st::historySentIcon,
  322. st::historySentSelectedIcon);
  323. make(
  324. &MessageStyle::historyReceivedIcon,
  325. st::historyReceivedIcon,
  326. st::historyReceivedSelectedIcon,
  327. st::historyReceivedIcon,
  328. st::historyReceivedSelectedIcon);
  329. make(
  330. &MessageStyle::historyPsaIcon,
  331. st::historyPsaIconIn,
  332. st::historyPsaIconInSelected,
  333. st::historyPsaIconOut,
  334. st::historyPsaIconOutSelected);
  335. make(
  336. &MessageStyle::historyCommentsOpen,
  337. st::historyCommentsOpenIn,
  338. st::historyCommentsOpenInSelected,
  339. st::historyCommentsOpenOut,
  340. st::historyCommentsOpenOutSelected);
  341. make(
  342. &MessageStyle::historyComments,
  343. st::historyCommentsIn,
  344. st::historyCommentsInSelected,
  345. st::historyCommentsOut,
  346. st::historyCommentsOutSelected);
  347. make(
  348. &MessageStyle::historyCallArrow,
  349. st::historyCallArrowIn,
  350. st::historyCallArrowInSelected,
  351. st::historyCallArrowOut,
  352. st::historyCallArrowOutSelected);
  353. make(
  354. &MessageStyle::historyCallArrowMissed,
  355. st::historyCallArrowMissedIn,
  356. st::historyCallArrowMissedInSelected,
  357. st::historyCallArrowMissedIn,
  358. st::historyCallArrowMissedInSelected);
  359. make(
  360. &MessageStyle::historyCallIcon,
  361. st::historyCallInIcon,
  362. st::historyCallInIconSelected,
  363. st::historyCallOutIcon,
  364. st::historyCallOutIconSelected);
  365. make(
  366. &MessageStyle::historyCallCameraIcon,
  367. st::historyCallCameraInIcon,
  368. st::historyCallCameraInIconSelected,
  369. st::historyCallCameraOutIcon,
  370. st::historyCallCameraOutIconSelected);
  371. make(
  372. &MessageStyle::historyFilePlay,
  373. st::historyFileInPlay,
  374. st::historyFileInPlaySelected,
  375. st::historyFileOutPlay,
  376. st::historyFileOutPlaySelected);
  377. make(
  378. &MessageStyle::historyFileWaiting,
  379. st::historyFileInWaiting,
  380. st::historyFileInWaitingSelected,
  381. st::historyFileOutWaiting,
  382. st::historyFileOutWaitingSelected);
  383. make(
  384. &MessageStyle::historyFileDownload,
  385. st::historyFileInDownload,
  386. st::historyFileInDownloadSelected,
  387. st::historyFileOutDownload,
  388. st::historyFileOutDownloadSelected);
  389. make(
  390. &MessageStyle::historyFileCancel,
  391. st::historyFileInCancel,
  392. st::historyFileInCancelSelected,
  393. st::historyFileOutCancel,
  394. st::historyFileOutCancelSelected);
  395. make(
  396. &MessageStyle::historyFilePause,
  397. st::historyFileInPause,
  398. st::historyFileInPauseSelected,
  399. st::historyFileOutPause,
  400. st::historyFileOutPauseSelected);
  401. make(
  402. &MessageStyle::historyFileImage,
  403. st::historyFileInImage,
  404. st::historyFileInImageSelected,
  405. st::historyFileOutImage,
  406. st::historyFileOutImageSelected);
  407. make(
  408. &MessageStyle::historyFileDocument,
  409. st::historyFileInDocument,
  410. st::historyFileInDocumentSelected,
  411. st::historyFileOutDocument,
  412. st::historyFileOutDocumentSelected);
  413. make(
  414. &MessageStyle::historyAudioDownload,
  415. st::historyAudioInDownload,
  416. st::historyAudioInDownloadSelected,
  417. st::historyAudioOutDownload,
  418. st::historyAudioOutDownloadSelected);
  419. make(
  420. &MessageStyle::historyAudioCancel,
  421. st::historyAudioInCancel,
  422. st::historyAudioInCancelSelected,
  423. st::historyAudioOutCancel,
  424. st::historyAudioOutCancelSelected);
  425. make(
  426. &MessageStyle::historyQuizTimer,
  427. st::historyQuizTimerIn,
  428. st::historyQuizTimerInSelected,
  429. st::historyQuizTimerOut,
  430. st::historyQuizTimerOutSelected);
  431. make(
  432. &MessageStyle::historyQuizExplain,
  433. st::historyQuizExplainIn,
  434. st::historyQuizExplainInSelected,
  435. st::historyQuizExplainOut,
  436. st::historyQuizExplainOutSelected);
  437. make(
  438. &MessageStyle::historyPollChosen,
  439. st::historyPollInChosen,
  440. st::historyPollInChosenSelected,
  441. st::historyPollOutChosen,
  442. st::historyPollOutChosenSelected);
  443. make(
  444. &MessageStyle::historyPollChoiceRight,
  445. st::historyPollInChoiceRight,
  446. st::historyPollInChoiceRightSelected,
  447. st::historyPollOutChoiceRight,
  448. st::historyPollOutChoiceRightSelected);
  449. make(
  450. &MessageStyle::historyTranscribeIcon,
  451. st::historyTranscribeInIcon,
  452. st::historyTranscribeInIconSelected,
  453. st::historyTranscribeOutIcon,
  454. st::historyTranscribeOutIconSelected);
  455. make(
  456. &MessageStyle::historyTranscribeLock,
  457. st::historyTranscribeInLock,
  458. st::historyTranscribeInLockSelected,
  459. st::historyTranscribeOutLock,
  460. st::historyTranscribeOutLockSelected);
  461. make(
  462. &MessageStyle::historyTranscribeHide,
  463. st::historyTranscribeInHide,
  464. st::historyTranscribeInHideSelected,
  465. st::historyTranscribeOutHide,
  466. st::historyTranscribeOutHideSelected);
  467. make(
  468. &MessageImageStyle::msgDateImgBg,
  469. st::msgDateImgBg,
  470. st::msgDateImgBgSelected);
  471. make(
  472. &MessageImageStyle::msgServiceBg,
  473. st::msgServiceBg,
  474. st::msgServiceBgSelected);
  475. make(
  476. &MessageImageStyle::msgShadow,
  477. st::msgInShadow,
  478. st::msgInShadowSelected);
  479. make(
  480. &MessageImageStyle::historyFileThumbRadialFg,
  481. st::historyFileThumbRadialFg,
  482. st::historyFileThumbRadialFgSelected);
  483. make(
  484. &MessageImageStyle::historyFileThumbPlay,
  485. st::historyFileThumbPlay,
  486. st::historyFileThumbPlaySelected);
  487. make(
  488. &MessageImageStyle::historyFileThumbWaiting,
  489. st::historyFileThumbWaiting,
  490. st::historyFileThumbWaitingSelected);
  491. make(
  492. &MessageImageStyle::historyFileThumbDownload,
  493. st::historyFileThumbDownload,
  494. st::historyFileThumbDownloadSelected);
  495. make(
  496. &MessageImageStyle::historyFileThumbCancel,
  497. st::historyFileThumbCancel,
  498. st::historyFileThumbCancelSelected);
  499. make(
  500. &MessageImageStyle::historyFileThumbPause,
  501. st::historyFileThumbPause,
  502. st::historyFileThumbPauseSelected);
  503. make(
  504. &MessageImageStyle::historyVideoDownload,
  505. st::historyVideoDownload,
  506. st::historyVideoDownloadSelected);
  507. make(
  508. &MessageImageStyle::historyVideoCancel,
  509. st::historyVideoCancel,
  510. st::historyVideoCancelSelected);
  511. make(
  512. &MessageImageStyle::historyVideoMessageMute,
  513. st::historyVideoMessageMute,
  514. st::historyVideoMessageMuteSelected);
  515. make(
  516. &MessageImageStyle::historyVideoMessageTtlIcon,
  517. st::historyVideoMessageTtlIcon,
  518. st::historyVideoMessageTtlIconSelected);
  519. make(
  520. &MessageImageStyle::historyPageEnlarge,
  521. st::historyPageEnlarge,
  522. st::historyPageEnlargeSelected);
  523. make(
  524. &MessageStyle::historyVoiceMessageTTL,
  525. st::historyVoiceMessageInTTL,
  526. st::historyVoiceMessageInTTLSelected,
  527. st::historyVoiceMessageOutTTL,
  528. st::historyVoiceMessageOutTTLSelected);
  529. make(
  530. &MessageStyle::liveLocationLongIcon,
  531. st::liveLocationLongInIcon,
  532. st::liveLocationLongInIconSelected,
  533. st::liveLocationLongOutIcon,
  534. st::liveLocationLongOutIconSelected);
  535. updateDarkValue();
  536. }
  537. ChatStyle::ChatStyle(not_null<const style::palette*> isolated)
  538. : ChatStyle(rpl::producer<ColorIndicesCompressed>()) {
  539. assignPalette(isolated);
  540. }
  541. ChatStyle::~ChatStyle() = default;
  542. void ChatStyle::apply(not_null<ChatTheme*> theme) {
  543. applyCustomPalette(theme->palette());
  544. }
  545. void ChatStyle::updateDarkValue() {
  546. const auto withBg = [&](const QColor &color) {
  547. return CountContrast(windowBg()->c, color);
  548. };
  549. _dark = (withBg({ 0, 0, 0 }) < withBg({ 255, 255, 255 }));
  550. }
  551. void ChatStyle::applyCustomPalette(const style::palette *palette) {
  552. assignPalette(palette ? palette : style::main_palette::get().get());
  553. if (palette) {
  554. _defaultPaletteChangeLifetime.destroy();
  555. } else {
  556. style::PaletteChanged(
  557. ) | rpl::start_with_next([=] {
  558. assignPalette(style::main_palette::get());
  559. }, _defaultPaletteChangeLifetime);
  560. }
  561. }
  562. void ChatStyle::applyAdjustedServiceBg(QColor serviceBg) {
  563. auto r = 0, g = 0, b = 0, a = 0;
  564. serviceBg.getRgb(&r, &g, &b, &a);
  565. msgServiceBg().set(uchar(r), uchar(g), uchar(b), uchar(a));
  566. }
  567. std::span<Text::SpecialColor> ChatStyle::highlightColors() const {
  568. if (_highlightColors.empty()) {
  569. const auto push = [&](const style::color &color) {
  570. _highlightColors.push_back({ &color->p, &color->p });
  571. };
  572. // comment, block-comment, prolog, doctype, cdata
  573. push(statisticsChartLineLightblue());
  574. // punctuation
  575. push(statisticsChartLineRed());
  576. // property, tag, boolean, number,
  577. // constant, symbol, deleted
  578. push(statisticsChartLineRed());
  579. // selector, attr-name, string, char, builtin
  580. push(statisticsChartLineOrange());
  581. // operator, entity, url
  582. push(statisticsChartLineRed());
  583. // atrule, attr-value, keyword, function
  584. push(statisticsChartLineBlue());
  585. // class-name
  586. push(statisticsChartLinePurple());
  587. // inserted
  588. push(statisticsChartLineGreen());
  589. //push(statisticsChartLineLightgreen());
  590. //push(statisticsChartLineGolden());
  591. }
  592. return _highlightColors;
  593. }
  594. void ChatStyle::clearColorIndexCaches() {
  595. for (auto &style : _messageStyles) {
  596. for (auto &cache : style.quoteCache) {
  597. cache = nullptr;
  598. }
  599. for (auto &cache : style.replyCache) {
  600. cache = nullptr;
  601. }
  602. }
  603. for (auto &values : _coloredValues) {
  604. values.reset();
  605. }
  606. for (auto &palette : _coloredTextPalettes) {
  607. palette.linkFg.reset();
  608. }
  609. for (auto &cache : _coloredReplyCaches) {
  610. cache = nullptr;
  611. }
  612. for (auto &cache : _coloredQuoteCaches) {
  613. cache = nullptr;
  614. }
  615. }
  616. void ChatStyle::assignPalette(not_null<const style::palette*> palette) {
  617. *static_cast<style::palette*>(this) = *palette;
  618. style::internal::ResetIcons();
  619. clearColorIndexCaches();
  620. for (auto &style : _messageStyles) {
  621. style.msgBgCornersSmall = {};
  622. style.msgBgCornersLarge = {};
  623. style.preCache = nullptr;
  624. style.textPalette.linkAlwaysActive
  625. = style.semiboldPalette.linkAlwaysActive
  626. = (style.textPalette.linkFg->c == style.historyTextFg->c);
  627. }
  628. for (auto &style : _imageStyles) {
  629. style.msgDateImgBgCorners = {};
  630. style.msgServiceBgCornersSmall = {};
  631. style.msgServiceBgCornersLarge = {};
  632. style.msgShadowCornersSmall = {};
  633. style.msgShadowCornersLarge = {};
  634. }
  635. _serviceBgCornersNormal = {};
  636. _serviceBgCornersInverted = {};
  637. _msgBotKbOverBgAddCornersSmall = {};
  638. _msgBotKbOverBgAddCornersLarge = {};
  639. for (auto &corners : _msgSelectOverlayCorners) {
  640. corners = {};
  641. }
  642. updateDarkValue();
  643. _paletteChanged.fire({});
  644. }
  645. const CornersPixmaps &ChatStyle::serviceBgCornersNormal() const {
  646. EnsureCorners(
  647. _serviceBgCornersNormal,
  648. HistoryServiceMsgRadius(),
  649. msgServiceBg());
  650. return _serviceBgCornersNormal;
  651. }
  652. const CornersPixmaps &ChatStyle::serviceBgCornersInverted() const {
  653. if (_serviceBgCornersInverted.p[0].isNull()) {
  654. _serviceBgCornersInverted = PrepareInvertedCornerPixmaps(
  655. HistoryServiceMsgInvertedRadius(),
  656. msgServiceBg());
  657. }
  658. return _serviceBgCornersInverted;
  659. }
  660. const MessageStyle &ChatStyle::messageStyle(bool outbg, bool selected) const {
  661. auto &result = messageStyleRaw(outbg, selected);
  662. EnsureCorners(
  663. result.msgBgCornersSmall,
  664. BubbleRadiusSmall(),
  665. result.msgBg,
  666. &result.msgShadow);
  667. EnsureCorners(
  668. result.msgBgCornersLarge,
  669. BubbleRadiusLarge(),
  670. result.msgBg,
  671. &result.msgShadow);
  672. const auto &replyBar = result.msgReplyBarColor->c;
  673. for (auto i = 0; i != kColorPatternsCount; ++i) {
  674. EnsureBlockquoteCache(
  675. result.replyCache[i],
  676. [&] { return SimpleColorIndexValues(replyBar, i); });
  677. if (!result.quoteCache[i]) {
  678. result.quoteCache[i] = std::make_unique<Text::QuotePaintCache>(
  679. *result.replyCache[i]);
  680. }
  681. }
  682. const auto preBgOverride = [&] {
  683. return _dark ? QColor(0, 0, 0, 192) : std::optional<QColor>();
  684. };
  685. EnsurePreCache(
  686. result.preCache,
  687. (selected
  688. ? result.textPalette.selectMonoFg
  689. : result.textPalette.monoFg),
  690. preBgOverride);
  691. return result;
  692. }
  693. const MessageImageStyle &ChatStyle::imageStyle(bool selected) const {
  694. auto &result = imageStyleRaw(selected);
  695. EnsureCorners(
  696. result.msgDateImgBgCorners,
  697. (st::msgDateImgPadding.y() * 2 + st::normalFont->height) / 2,
  698. result.msgDateImgBg);
  699. EnsureCorners(
  700. result.msgServiceBgCornersSmall,
  701. BubbleRadiusSmall(),
  702. result.msgServiceBg);
  703. EnsureCorners(
  704. result.msgServiceBgCornersLarge,
  705. BubbleRadiusLarge(),
  706. result.msgServiceBg);
  707. EnsureCorners(
  708. result.msgShadowCornersSmall,
  709. BubbleRadiusSmall(),
  710. result.msgShadow);
  711. EnsureCorners(
  712. result.msgShadowCornersLarge,
  713. BubbleRadiusLarge(),
  714. result.msgShadow);
  715. return result;
  716. }
  717. int ChatStyle::colorPatternIndex(uint8 colorIndex) const {
  718. Expects(colorIndex >= 0 && colorIndex < kColorIndexCount);
  719. if (!_colorIndices.colors
  720. || colorIndex < kSimpleColorIndexCount) {
  721. return 0;
  722. }
  723. auto &data = (*_colorIndices.colors)[colorIndex];
  724. auto &colors = _dark ? data.dark : data.light;
  725. return colors[2] ? 2 : colors[1] ? 1 : 0;
  726. }
  727. ColorIndexValues ChatStyle::computeColorIndexValues(
  728. bool selected,
  729. uint8 colorIndex) const {
  730. if (!_colorIndices.colors) {
  731. colorIndex %= kSimpleColorIndexCount;
  732. }
  733. if (colorIndex < kSimpleColorIndexCount) {
  734. const auto list = std::array{
  735. &historyPeer1NameFg(),
  736. &historyPeer2NameFg(),
  737. &historyPeer3NameFg(),
  738. &historyPeer4NameFg(),
  739. &historyPeer5NameFg(),
  740. &historyPeer6NameFg(),
  741. &historyPeer7NameFg(),
  742. &historyPeer8NameFg(),
  743. };
  744. const auto listSelected = std::array{
  745. &historyPeer1NameFgSelected(),
  746. &historyPeer2NameFgSelected(),
  747. &historyPeer3NameFgSelected(),
  748. &historyPeer4NameFgSelected(),
  749. &historyPeer5NameFgSelected(),
  750. &historyPeer6NameFgSelected(),
  751. &historyPeer7NameFgSelected(),
  752. &historyPeer8NameFgSelected(),
  753. };
  754. const auto paletteIndex = ColorIndexToPaletteIndex(colorIndex);
  755. auto result = ColorIndexValues{
  756. .name = (*(selected ? listSelected : list)[paletteIndex])->c,
  757. };
  758. result.bg = result.name;
  759. result.bg.setAlpha(kDefaultBgOpacity * 255);
  760. result.outlines[0] = result.name;
  761. result.outlines[0].setAlpha(kDefaultOutline1Opacity * 255);
  762. result.outlines[1] = result.outlines[2] = QColor(0, 0, 0, 0);
  763. return result;
  764. }
  765. auto &data = (*_colorIndices.colors)[colorIndex];
  766. auto &colors = _dark ? data.dark : data.light;
  767. if (!colors[0]) {
  768. return computeColorIndexValues(
  769. selected,
  770. colorIndex % kSimpleColorIndexCount);
  771. }
  772. const auto color = [&](int index) {
  773. const auto v = colors[index];
  774. return v
  775. ? QColor((v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF)
  776. : QColor(0, 0, 0, 0);
  777. };
  778. auto result = ColorIndexValues{
  779. .outlines = { color(0), color(1), color(2) }
  780. };
  781. result.bg = result.outlines[0];
  782. result.bg.setAlpha(kDefaultBgOpacity * 255);
  783. result.name = result.outlines[0];
  784. return result;
  785. }
  786. not_null<Text::QuotePaintCache*> ChatStyle::serviceQuoteCache(
  787. bool twoColored) const {
  788. const auto index = (twoColored ? 1 : 0);
  789. const auto &service = msgServiceFg()->c;
  790. EnsureBlockquoteCache(
  791. _serviceQuoteCache[index],
  792. [&] { return SimpleColorIndexValues(service, twoColored); });
  793. return _serviceQuoteCache[index].get();
  794. }
  795. not_null<Text::QuotePaintCache*> ChatStyle::serviceReplyCache(
  796. bool twoColored) const {
  797. const auto index = (twoColored ? 1 : 0);
  798. const auto &service = msgServiceFg()->c;
  799. EnsureBlockquoteCache(
  800. _serviceReplyCache[index],
  801. [&] { return SimpleColorIndexValues(service, twoColored); });
  802. return _serviceReplyCache[index].get();
  803. }
  804. const ColorIndexValues &ChatStyle::coloredValues(
  805. bool selected,
  806. uint8 colorIndex) const {
  807. Expects(colorIndex >= 0 && colorIndex < kColorIndexCount);
  808. const auto shift = (selected ? kColorIndexCount : 0);
  809. auto &result = _coloredValues[shift + colorIndex];
  810. if (!result) {
  811. result.emplace(computeColorIndexValues(selected, colorIndex));
  812. }
  813. return *result;
  814. }
  815. const style::TextPalette &ChatStyle::coloredTextPalette(
  816. bool selected,
  817. uint8 colorIndex) const {
  818. Expects(colorIndex >= 0 && colorIndex < kColorIndexCount);
  819. const auto shift = (selected ? kColorIndexCount : 0);
  820. auto &result = _coloredTextPalettes[shift + colorIndex];
  821. if (!result.linkFg) {
  822. result.linkFg.emplace(coloredValues(selected, colorIndex).name);
  823. make(
  824. result.data,
  825. (selected
  826. ? st::inReplyTextPaletteSelected
  827. : st::inReplyTextPalette));
  828. result.data.linkFg = result.linkFg->color();
  829. result.data.selectLinkFg = result.data.linkFg;
  830. }
  831. return result.data;
  832. }
  833. not_null<BackgroundEmojiData*> ChatStyle::backgroundEmojiData(
  834. uint64 id) const {
  835. return &_backgroundEmojis[id];
  836. }
  837. not_null<Text::QuotePaintCache*> ChatStyle::coloredQuoteCache(
  838. bool selected,
  839. uint8 colorIndex) const {
  840. return coloredCache(_coloredQuoteCaches, selected, colorIndex);
  841. }
  842. not_null<Text::QuotePaintCache*> ChatStyle::coloredReplyCache(
  843. bool selected,
  844. uint8 colorIndex) const {
  845. return coloredCache(_coloredReplyCaches, selected, colorIndex);
  846. }
  847. not_null<Text::QuotePaintCache*> ChatStyle::coloredCache(
  848. ColoredQuotePaintCaches &caches,
  849. bool selected,
  850. uint8 colorIndex) const {
  851. Expects(colorIndex >= 0 && colorIndex < kColorIndexCount);
  852. const auto shift = (selected ? kColorIndexCount : 0);
  853. auto &cache = caches[shift + colorIndex];
  854. EnsureBlockquoteCache(cache, [&] {
  855. return coloredValues(selected, colorIndex);
  856. });
  857. return cache.get();
  858. }
  859. const CornersPixmaps &ChatStyle::msgBotKbOverBgAddCornersSmall() const {
  860. EnsureCorners(
  861. _msgBotKbOverBgAddCornersSmall,
  862. BubbleRadiusSmall(),
  863. msgBotKbOverBgAdd());
  864. return _msgBotKbOverBgAddCornersSmall;
  865. }
  866. const CornersPixmaps &ChatStyle::msgBotKbOverBgAddCornersLarge() const {
  867. EnsureCorners(
  868. _msgBotKbOverBgAddCornersLarge,
  869. BubbleRadiusLarge(),
  870. msgBotKbOverBgAdd());
  871. return _msgBotKbOverBgAddCornersLarge;
  872. }
  873. const CornersPixmaps &ChatStyle::msgSelectOverlayCorners(
  874. CachedCornerRadius radius) const {
  875. const auto index = static_cast<int>(radius);
  876. Assert(index >= 0 && index < int(CachedCornerRadius::kCount));
  877. EnsureCorners(
  878. _msgSelectOverlayCorners[index],
  879. CachedCornerRadiusValue(radius),
  880. msgSelectOverlay());
  881. return _msgSelectOverlayCorners[index];
  882. }
  883. MessageStyle &ChatStyle::messageStyleRaw(bool outbg, bool selected) const {
  884. return _messageStyles[(outbg ? 2 : 0) + (selected ? 1 : 0)];
  885. }
  886. MessageStyle &ChatStyle::messageIn() {
  887. return messageStyleRaw(false, false);
  888. }
  889. MessageStyle &ChatStyle::messageInSelected() {
  890. return messageStyleRaw(false, true);
  891. }
  892. MessageStyle &ChatStyle::messageOut() {
  893. return messageStyleRaw(true, false);
  894. }
  895. MessageStyle &ChatStyle::messageOutSelected() {
  896. return messageStyleRaw(true, true);
  897. }
  898. MessageImageStyle &ChatStyle::imageStyleRaw(bool selected) const {
  899. return _imageStyles[selected ? 1 : 0];
  900. }
  901. MessageImageStyle &ChatStyle::image() {
  902. return imageStyleRaw(false);
  903. }
  904. MessageImageStyle &ChatStyle::imageSelected() {
  905. return imageStyleRaw(true);
  906. }
  907. void ChatStyle::make(style::color &my, const style::color &original) const {
  908. my = _colors[style::main_palette::indexOfColor(original)];
  909. }
  910. void ChatStyle::make(style::icon &my, const style::icon &original) const {
  911. my = original.withPalette(*this);
  912. }
  913. void ChatStyle::make(
  914. style::TextPalette &my,
  915. const style::TextPalette &original) const {
  916. my.linkAlwaysActive = original.linkAlwaysActive;
  917. make(my.linkFg, original.linkFg);
  918. make(my.monoFg, original.monoFg);
  919. make(my.spoilerFg, original.spoilerFg);
  920. make(my.selectBg, original.selectBg);
  921. make(my.selectFg, original.selectFg);
  922. make(my.selectLinkFg, original.selectLinkFg);
  923. make(my.selectMonoFg, original.selectMonoFg);
  924. make(my.selectSpoilerFg, original.selectSpoilerFg);
  925. make(my.selectOverlay, original.selectOverlay);
  926. }
  927. void ChatStyle::make(
  928. style::TwoIconButton &my,
  929. const style::TwoIconButton &original) const {
  930. my = original;
  931. make(my.iconBelow, original.iconBelow);
  932. make(my.iconAbove, original.iconAbove);
  933. make(my.iconBelowOver, original.iconBelowOver);
  934. make(my.iconAboveOver, original.iconAboveOver);
  935. make(my.ripple.color, original.ripple.color);
  936. }
  937. void ChatStyle::make(
  938. style::ScrollArea &my,
  939. const style::ScrollArea &original) const {
  940. my = original;
  941. make(my.bg, original.bg);
  942. make(my.bgOver, original.bgOver);
  943. make(my.barBg, original.barBg);
  944. make(my.barBgOver, original.barBgOver);
  945. make(my.shColor, original.shColor);
  946. }
  947. template <typename Type>
  948. void ChatStyle::make(
  949. Type MessageStyle::*my,
  950. const Type &originalIn,
  951. const Type &originalInSelected,
  952. const Type &originalOut,
  953. const Type &originalOutSelected) {
  954. make(messageIn().*my, originalIn);
  955. make(messageInSelected().*my, originalInSelected);
  956. make(messageOut().*my, originalOut);
  957. make(messageOutSelected().*my, originalOutSelected);
  958. }
  959. template <typename Type>
  960. void ChatStyle::make(
  961. Type MessageImageStyle::*my,
  962. const Type &original,
  963. const Type &originalSelected) {
  964. make(image().*my, original);
  965. make(imageSelected().*my, originalSelected);
  966. }
  967. uint8 DecideColorIndex(uint64 id) {
  968. return id % kSimpleColorIndexCount;
  969. }
  970. uint8 ColorIndexToPaletteIndex(uint8 colorIndex) {
  971. Expects(colorIndex >= 0 && colorIndex < kColorIndexCount);
  972. const int8 map[] = { 0, 7, 4, 1, 6, 3, 5 };
  973. return map[colorIndex % kSimpleColorIndexCount];
  974. }
  975. QColor FromNameFg(
  976. not_null<const ChatStyle*> st,
  977. bool selected,
  978. uint8 colorIndex) {
  979. return st->coloredValues(selected, colorIndex).name;
  980. }
  981. void FillComplexOverlayRect(
  982. QPainter &p,
  983. QRect rect,
  984. const style::color &color,
  985. const CornersPixmaps &corners) {
  986. using namespace Images;
  987. const auto pix = corners.p;
  988. const auto fillRect = [&](QRect rect) {
  989. p.fillRect(rect, color);
  990. };
  991. if (pix[kTopLeft].isNull()
  992. && pix[kTopRight].isNull()
  993. && pix[kBottomLeft].isNull()
  994. && pix[kBottomRight].isNull()) {
  995. fillRect(rect);
  996. return;
  997. }
  998. const auto ratio = style::DevicePixelRatio();
  999. const auto fillCorner = [&](int left, int top, int index) {
  1000. p.drawPixmap(left, top, pix[index]);
  1001. };
  1002. const auto cornerSize = [&](int index) {
  1003. const auto &p = pix[index];
  1004. return p.isNull() ? 0 : p.width() / ratio;
  1005. };
  1006. const auto verticalSkip = [&](int left, int right) {
  1007. return std::max(cornerSize(left), cornerSize(right));
  1008. };
  1009. const auto top = verticalSkip(kTopLeft, kTopRight);
  1010. const auto bottom = verticalSkip(kBottomLeft, kBottomRight);
  1011. if (top) {
  1012. const auto left = cornerSize(kTopLeft);
  1013. const auto right = cornerSize(kTopRight);
  1014. if (left) {
  1015. fillCorner(rect.left(), rect.top(), kTopLeft);
  1016. if (const auto add = top - left) {
  1017. fillRect({ rect.left(), rect.top() + left, left, add });
  1018. }
  1019. }
  1020. if (const auto fill = rect.width() - left - right; fill > 0) {
  1021. fillRect({ rect.left() + left, rect.top(), fill, top });
  1022. }
  1023. if (right) {
  1024. fillCorner(
  1025. rect.left() + rect.width() - right,
  1026. rect.top(),
  1027. kTopRight);
  1028. if (const auto add = top - right) {
  1029. fillRect({
  1030. rect.left() + rect.width() - right,
  1031. rect.top() + right,
  1032. right,
  1033. add,
  1034. });
  1035. }
  1036. }
  1037. }
  1038. if (const auto h = rect.height() - top - bottom; h > 0) {
  1039. fillRect({ rect.left(), rect.top() + top, rect.width(), h });
  1040. }
  1041. if (bottom) {
  1042. const auto left = cornerSize(kBottomLeft);
  1043. const auto right = cornerSize(kBottomRight);
  1044. if (left) {
  1045. fillCorner(
  1046. rect.left(),
  1047. rect.top() + rect.height() - left,
  1048. kBottomLeft);
  1049. if (const auto add = bottom - left) {
  1050. fillRect({
  1051. rect.left(),
  1052. rect.top() + rect.height() - bottom,
  1053. left,
  1054. add,
  1055. });
  1056. }
  1057. }
  1058. if (const auto fill = rect.width() - left - right; fill > 0) {
  1059. fillRect({
  1060. rect.left() + left,
  1061. rect.top() + rect.height() - bottom,
  1062. fill,
  1063. bottom,
  1064. });
  1065. }
  1066. if (right) {
  1067. fillCorner(
  1068. rect.left() + rect.width() - right,
  1069. rect.top() + rect.height() - right,
  1070. kBottomRight);
  1071. if (const auto add = bottom - right) {
  1072. fillRect({
  1073. rect.left() + rect.width() - right,
  1074. rect.top() + rect.height() - bottom,
  1075. right,
  1076. add,
  1077. });
  1078. }
  1079. }
  1080. }
  1081. }
  1082. void FillComplexEllipse(
  1083. QPainter &p,
  1084. not_null<const ChatStyle*> st,
  1085. QRect rect) {
  1086. PainterHighQualityEnabler hq(p);
  1087. p.setPen(Qt::NoPen);
  1088. p.setBrush(st->msgSelectOverlay());
  1089. p.drawEllipse(rect);
  1090. }
  1091. } // namespace Ui