tabbed_selector.cpp 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563
  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 "chat_helpers/tabbed_selector.h"
  8. #include "chat_helpers/emoji_list_widget.h"
  9. #include "chat_helpers/stickers_list_widget.h"
  10. #include "chat_helpers/gifs_list_widget.h"
  11. #include "menu/menu_send.h"
  12. #include "ui/controls/swipe_handler.h"
  13. #include "ui/controls/tabbed_search.h"
  14. #include "ui/text/text_utilities.h"
  15. #include "ui/widgets/buttons.h"
  16. #include "ui/widgets/labels.h"
  17. #include "ui/widgets/shadow.h"
  18. #include "ui/widgets/discrete_sliders.h"
  19. #include "ui/widgets/popup_menu.h"
  20. #include "ui/widgets/scroll_area.h"
  21. #include "ui/layers/box_content.h"
  22. #include "ui/image/image_prepare.h"
  23. #include "ui/cached_round_corners.h"
  24. #include "ui/painter.h"
  25. #include "ui/ui_utility.h"
  26. #include "window/window_session_controller.h"
  27. #include "main/main_session.h"
  28. #include "main/main_session_settings.h"
  29. #include "storage/localstorage.h"
  30. #include "data/data_channel.h"
  31. #include "data/data_emoji_statuses.h"
  32. #include "data/data_session.h"
  33. #include "data/data_changes.h"
  34. #include "data/stickers/data_stickers.h"
  35. #include "data/stickers/data_custom_emoji.h" // AllowEmojiWithoutPremium.
  36. #include "boxes/premium_preview_box.h"
  37. #include "lang/lang_keys.h"
  38. #include "mainwindow.h"
  39. #include "apiwrap.h"
  40. #include "styles/style_chat_helpers.h"
  41. #include "styles/style_menu_icons.h"
  42. namespace ChatHelpers {
  43. class TabbedSelector::SlideAnimation : public Ui::RoundShadowAnimation {
  44. public:
  45. enum class Direction {
  46. LeftToRight,
  47. RightToLeft,
  48. };
  49. void setFinalImages(Direction direction, QImage &&left, QImage &&right, QRect inner, bool wasSectionIcons);
  50. void start();
  51. void paintFrame(QPainter &p, const style::EmojiPan &st, float64 dt, float64 opacity);
  52. private:
  53. Direction _direction = Direction::LeftToRight;
  54. QPixmap _leftImage, _rightImage;
  55. int _width = 0;
  56. int _height = 0;
  57. int _innerLeft = 0;
  58. int _innerTop = 0;
  59. int _innerRight = 0;
  60. int _innerBottom = 0;
  61. int _innerWidth = 0;
  62. int _innerHeight = 0;
  63. int _painterInnerLeft = 0;
  64. int _painterInnerTop = 0;
  65. int _painterInnerWidth = 0;
  66. int _painterInnerBottom = 0;
  67. int _painterCategoriesTop = 0;
  68. int _painterInnerHeight = 0;
  69. int _painterInnerRight = 0;
  70. int _frameIntsPerLineAdd = 0;
  71. bool _wasSectionIcons = false;
  72. };
  73. void TabbedSelector::SlideAnimation::setFinalImages(Direction direction, QImage &&left, QImage &&right, QRect inner, bool wasSectionIcons) {
  74. Expects(!started());
  75. _direction = direction;
  76. _leftImage = QPixmap::fromImage(std::move(left).convertToFormat(QImage::Format_ARGB32_Premultiplied), Qt::ColorOnly);
  77. _rightImage = QPixmap::fromImage(std::move(right).convertToFormat(QImage::Format_ARGB32_Premultiplied), Qt::ColorOnly);
  78. Assert(!_leftImage.isNull());
  79. Assert(!_rightImage.isNull());
  80. _width = _leftImage.width();
  81. _height = _rightImage.height();
  82. Assert(!(_width % style::DevicePixelRatio()));
  83. Assert(!(_height % style::DevicePixelRatio()));
  84. Assert(_leftImage.devicePixelRatio() == _rightImage.devicePixelRatio());
  85. Assert(_rightImage.width() == _width);
  86. Assert(_rightImage.height() == _height);
  87. Assert(QRect(0, 0, _width, _height).contains(inner));
  88. _innerLeft = inner.x();
  89. _innerTop = inner.y();
  90. _innerWidth = inner.width();
  91. _innerHeight = inner.height();
  92. Assert(!(_innerLeft % style::DevicePixelRatio()));
  93. Assert(!(_innerTop % style::DevicePixelRatio()));
  94. Assert(!(_innerWidth % style::DevicePixelRatio()));
  95. Assert(!(_innerHeight % style::DevicePixelRatio()));
  96. _innerRight = _innerLeft + _innerWidth;
  97. _innerBottom = _innerTop + _innerHeight;
  98. _painterInnerLeft = _innerLeft / style::DevicePixelRatio();
  99. _painterInnerTop = _innerTop / style::DevicePixelRatio();
  100. _painterInnerRight = _innerRight / style::DevicePixelRatio();
  101. _painterInnerBottom = _innerBottom / style::DevicePixelRatio();
  102. _painterInnerWidth = _innerWidth / style::DevicePixelRatio();
  103. _painterInnerHeight = _innerHeight / style::DevicePixelRatio();
  104. _painterCategoriesTop = _painterInnerBottom - st::defaultEmojiPan.footer;
  105. _wasSectionIcons = wasSectionIcons;
  106. }
  107. void TabbedSelector::SlideAnimation::start() {
  108. Assert(!_leftImage.isNull());
  109. Assert(!_rightImage.isNull());
  110. RoundShadowAnimation::start(_width, _height, _leftImage.devicePixelRatio());
  111. auto checkCorner = [this](const Corner &corner) {
  112. if (!corner.valid()) return;
  113. Assert(corner.width <= _innerWidth);
  114. Assert(corner.height <= _innerHeight);
  115. };
  116. checkCorner(_topLeft);
  117. checkCorner(_topRight);
  118. checkCorner(_bottomLeft);
  119. checkCorner(_bottomRight);
  120. _frameIntsPerLineAdd = (_width - _innerWidth) + _frameIntsPerLineAdded;
  121. }
  122. void TabbedSelector::SlideAnimation::paintFrame(
  123. QPainter &p,
  124. const style::EmojiPan &st,
  125. float64 dt,
  126. float64 opacity) {
  127. Expects(started());
  128. Expects(dt >= 0.);
  129. _frameAlpha = anim::interpolate(1, 256, opacity);
  130. auto leftToRight = (_direction == Direction::LeftToRight);
  131. auto easeOut = anim::easeOutCirc(1., dt);
  132. auto easeIn = anim::easeInCirc(1., dt);
  133. auto arrivingCoord = anim::interpolate(_innerWidth, 0, easeOut);
  134. auto departingCoord = anim::interpolate(0, _innerWidth, easeIn);
  135. if (auto decrease = (arrivingCoord % style::DevicePixelRatio())) {
  136. arrivingCoord -= decrease;
  137. }
  138. if (auto decrease = (departingCoord % style::DevicePixelRatio())) {
  139. departingCoord -= decrease;
  140. }
  141. auto arrivingAlpha = easeIn;
  142. auto departingAlpha = 1. - easeOut;
  143. auto leftCoord = (leftToRight ? arrivingCoord : departingCoord) * -1;
  144. auto leftAlpha = (leftToRight ? arrivingAlpha : departingAlpha);
  145. auto rightCoord = (leftToRight ? departingCoord : arrivingCoord);
  146. auto rightAlpha = (leftToRight ? departingAlpha : arrivingAlpha);
  147. // _innerLeft ..(left).. leftTo ..(both).. bothTo ..(none).. noneTo ..(right).. _innerRight
  148. auto leftTo = _innerLeft
  149. + std::clamp(_innerWidth + leftCoord, 0, _innerWidth);
  150. auto rightFrom = _innerLeft + std::clamp(rightCoord, 0, _innerWidth);
  151. auto painterRightFrom = rightFrom / style::DevicePixelRatio();
  152. if (opacity < 1.) {
  153. _frame.fill(Qt::transparent);
  154. }
  155. {
  156. auto p = QPainter(&_frame);
  157. p.setOpacity(opacity);
  158. p.fillRect(_painterInnerLeft, _painterInnerTop, _painterInnerWidth, _painterCategoriesTop - _painterInnerTop, st.bg);
  159. p.fillRect(_painterInnerLeft, _painterCategoriesTop, _painterInnerWidth, _painterInnerBottom - _painterCategoriesTop, _wasSectionIcons ? st.categoriesBg : st.bg);
  160. p.setCompositionMode(QPainter::CompositionMode_SourceOver);
  161. if (leftTo > _innerLeft) {
  162. p.setOpacity(opacity * leftAlpha);
  163. p.drawPixmap(_painterInnerLeft, _painterInnerTop, _leftImage, _innerLeft - leftCoord, _innerTop, leftTo - _innerLeft, _innerHeight);
  164. }
  165. if (rightFrom < _innerRight) {
  166. p.setOpacity(opacity * rightAlpha);
  167. p.drawPixmap(painterRightFrom, _painterInnerTop, _rightImage, _innerLeft, _innerTop, _innerRight - rightFrom, _innerHeight);
  168. }
  169. }
  170. // Draw corners
  171. //paintCorner(_topLeft, _innerLeft, _innerTop);
  172. //paintCorner(_topRight, _innerRight - _topRight.width, _innerTop);
  173. paintCorner(_bottomLeft, _innerLeft, _innerBottom - _bottomLeft.height);
  174. paintCorner(_bottomRight, _innerRight - _bottomRight.width, _innerBottom - _bottomRight.height);
  175. // Draw shadow upon the transparent
  176. auto outerLeft = _innerLeft;
  177. auto outerTop = _innerTop;
  178. auto outerRight = _innerRight;
  179. auto outerBottom = _innerBottom;
  180. if (_shadow.valid()) {
  181. outerLeft -= _shadow.extend.left();
  182. outerTop -= _shadow.extend.top();
  183. outerRight += _shadow.extend.right();
  184. outerBottom += _shadow.extend.bottom();
  185. }
  186. if (style::DevicePixelRatio() > 1) {
  187. if (auto skipLeft = (outerLeft % style::DevicePixelRatio())) {
  188. outerLeft -= skipLeft;
  189. }
  190. if (auto skipTop = (outerTop % style::DevicePixelRatio())) {
  191. outerTop -= skipTop;
  192. }
  193. if (auto skipRight = (outerRight % style::DevicePixelRatio())) {
  194. outerRight += (style::DevicePixelRatio() - skipRight);
  195. }
  196. if (auto skipBottom = (outerBottom % style::DevicePixelRatio())) {
  197. outerBottom += (style::DevicePixelRatio() - skipBottom);
  198. }
  199. }
  200. if (opacity == 1.) {
  201. // Fill above the frame top with transparent.
  202. auto fillTopInts = (_frameInts + outerTop * _frameIntsPerLine + outerLeft);
  203. auto fillWidth = (outerRight - outerLeft) * sizeof(uint32);
  204. for (auto fillTop = _innerTop - outerTop; fillTop != 0; --fillTop) {
  205. memset(fillTopInts, 0, fillWidth);
  206. fillTopInts += _frameIntsPerLine;
  207. }
  208. // Fill to the left and to the right of the frame with transparent.
  209. auto fillLeft = (_innerLeft - outerLeft) * sizeof(uint32);
  210. auto fillRight = (outerRight - _innerRight) * sizeof(uint32);
  211. if (fillLeft || fillRight) {
  212. auto fillInts = _frameInts + _innerTop * _frameIntsPerLine;
  213. for (auto y = _innerTop; y != _innerBottom; ++y) {
  214. memset(fillInts + outerLeft, 0, fillLeft);
  215. memset(fillInts + _innerRight, 0, fillRight);
  216. fillInts += _frameIntsPerLine;
  217. }
  218. }
  219. // Fill below the frame bottom with transparent.
  220. auto fillBottomInts = (_frameInts + _innerBottom * _frameIntsPerLine + outerLeft);
  221. for (auto fillBottom = outerBottom - _innerBottom; fillBottom != 0; --fillBottom) {
  222. memset(fillBottomInts, 0, fillWidth);
  223. fillBottomInts += _frameIntsPerLine;
  224. }
  225. }
  226. if (_shadow.valid()) {
  227. paintShadow(outerLeft, outerTop, outerRight, outerBottom);
  228. }
  229. // Debug
  230. //auto frameInts = _frameInts;
  231. //auto pattern = anim::shifted((static_cast<uint32>(0xFF) << 24) | (static_cast<uint32>(0xFF) << 16) | (static_cast<uint32>(0xFF) << 8) | static_cast<uint32>(0xFF));
  232. //for (auto y = 0; y != _finalHeight; ++y) {
  233. // for (auto x = 0; x != _finalWidth; ++x) {
  234. // auto source = *frameInts;
  235. // auto sourceAlpha = (source >> 24);
  236. // *frameInts = anim::unshifted(anim::shifted(source) * 256 + pattern * (256 - sourceAlpha));
  237. // ++frameInts;
  238. // }
  239. // frameInts += _frameIntsPerLineAdded;
  240. //}
  241. p.drawImage(
  242. outerLeft / style::DevicePixelRatio(),
  243. outerTop / style::DevicePixelRatio(),
  244. _frame,
  245. outerLeft,
  246. outerTop,
  247. outerRight - outerLeft,
  248. outerBottom - outerTop);
  249. }
  250. TabbedSelector::Tab::Tab(
  251. SelectorTab type,
  252. int index,
  253. object_ptr<Inner> widget)
  254. : _type(type)
  255. , _index(index)
  256. , _widget(std::move(widget))
  257. , _weak(_widget)
  258. , _footer(_widget ? _widget->createFooter() : nullptr) {
  259. if (_footer) {
  260. _footer->setParent(_widget->parentWidget());
  261. }
  262. }
  263. object_ptr<TabbedSelector::Inner> TabbedSelector::Tab::takeWidget() {
  264. return std::move(_widget);
  265. }
  266. void TabbedSelector::Tab::returnWidget(object_ptr<Inner> widget) {
  267. Expects(widget == _weak);
  268. _widget = std::move(widget);
  269. }
  270. void TabbedSelector::Tab::saveScrollTop() {
  271. Expects(widget() != nullptr);
  272. _scrollTop = widget()->getVisibleTop();
  273. }
  274. [[nodiscard]] rpl::producer<std::vector<Ui::EmojiGroup>> GreetingGroupFirst(
  275. not_null<Data::Session*> owner) {
  276. return owner->emojiStatuses().stickerGroupsValue(
  277. ) | rpl::map([](std::vector<Ui::EmojiGroup> &&groups) {
  278. const auto i = ranges::find(
  279. groups,
  280. Ui::EmojiGroupType::Greeting,
  281. &Ui::EmojiGroup::type);
  282. if (i != begin(groups) && i != end(groups)) {
  283. ranges::rotate(begin(groups), i, i + 1);
  284. }
  285. return std::move(groups);
  286. });
  287. }
  288. std::unique_ptr<Ui::TabbedSearch> MakeSearch(
  289. not_null<Ui::RpWidget*> parent,
  290. const style::EmojiPan &st,
  291. Fn<void(std::vector<QString>&&)> callback,
  292. not_null<Main::Session*> session,
  293. TabbedSearchType type) {
  294. using Descriptor = Ui::SearchDescriptor;
  295. const auto owner = &session->data();
  296. auto result = std::make_unique<Ui::TabbedSearch>(parent, st, Descriptor{
  297. .st = st.search,
  298. .groups = ((type == TabbedSearchType::ProfilePhoto)
  299. ? owner->emojiStatuses().profilePhotoGroupsValue()
  300. : (type == TabbedSearchType::Status)
  301. ? owner->emojiStatuses().statusGroupsValue()
  302. : (type == TabbedSearchType::Stickers)
  303. ? owner->emojiStatuses().stickerGroupsValue()
  304. : (type == TabbedSearchType::Greeting)
  305. ? GreetingGroupFirst(owner)
  306. : owner->emojiStatuses().emojiGroupsValue()),
  307. .customEmojiFactory = owner->customEmojiManager().factory(
  308. Data::CustomEmojiManager::SizeTag::SetIcon,
  309. Ui::SearchWithGroups::IconSizeOverride())
  310. });
  311. result->queryValue(
  312. ) | rpl::skip(1) | rpl::start_with_next(
  313. std::move(callback),
  314. parent->lifetime());
  315. return result;
  316. }
  317. TabbedSelector::TabbedSelector(
  318. QWidget *parent,
  319. std::shared_ptr<Show> show,
  320. PauseReason level,
  321. Mode mode)
  322. : TabbedSelector(parent, {
  323. .show = std::move(show),
  324. .st = ((mode == Mode::EmojiStatus
  325. || mode == Mode::ChannelStatus
  326. || mode == Mode::BackgroundEmoji
  327. || mode == Mode::FullReactions)
  328. ? st::statusEmojiPan
  329. : (mode == Mode::RecentReactions)
  330. ? st::backgroundEmojiPan
  331. : st::defaultEmojiPan),
  332. .level = level,
  333. .mode = mode,
  334. }) {
  335. }
  336. TabbedSelector::TabbedSelector(
  337. QWidget *parent,
  338. TabbedSelectorDescriptor &&descriptor)
  339. : RpWidget(parent)
  340. , _st(descriptor.st)
  341. , _features(descriptor.features)
  342. , _show(std::move(descriptor.show))
  343. , _level(descriptor.level)
  344. , _customTextColor(std::move(descriptor.customTextColor))
  345. , _mode(descriptor.mode)
  346. , _panelRounding(Ui::PrepareCornerPixmaps(st::emojiPanRadius, _st.bg))
  347. , _categoriesRounding(
  348. Ui::PrepareCornerPixmaps(st::emojiPanRadius, _st.categoriesBg))
  349. , _topShadow(full() ? object_ptr<Ui::PlainShadow>(this) : nullptr)
  350. , _bottomShadow(this)
  351. , _scroll(this, st::emojiScroll)
  352. , _tabs([&] {
  353. std::vector<Tab> tabs;
  354. if (full()) {
  355. tabs.reserve(3);
  356. tabs.push_back(createTab(SelectorTab::Emoji, 0));
  357. tabs.push_back(createTab(SelectorTab::Stickers, 1));
  358. tabs.push_back(createTab(SelectorTab::Gifs, 2));
  359. } else if (mediaEditor()) {
  360. tabs.reserve(2);
  361. tabs.push_back(createTab(SelectorTab::Stickers, 0));
  362. tabs.push_back(createTab(SelectorTab::Masks, 1));
  363. } else if (_mode == Mode::StickersOnly || _mode == Mode::ChatIntro) {
  364. tabs.reserve(1);
  365. tabs.push_back(createTab(SelectorTab::Stickers, 0));
  366. } else {
  367. tabs.reserve(1);
  368. tabs.push_back(createTab(SelectorTab::Emoji, 0));
  369. }
  370. return tabs;
  371. }())
  372. , _currentTabType(full()
  373. ? session().settings().selectorTab()
  374. : (mediaEditor()
  375. || _mode == Mode::StickersOnly
  376. || _mode == Mode::ChatIntro)
  377. ? SelectorTab::Stickers
  378. : SelectorTab::Emoji)
  379. , _hasEmojiTab(ranges::contains(_tabs, SelectorTab::Emoji, &Tab::type))
  380. , _hasStickersTab(ranges::contains(_tabs, SelectorTab::Stickers, &Tab::type))
  381. , _hasGifsTab(ranges::contains(_tabs, SelectorTab::Gifs, &Tab::type))
  382. , _hasMasksTab(ranges::contains(_tabs, SelectorTab::Masks, &Tab::type))
  383. , _tabbed(_tabs.size() > 1) {
  384. resize(st::emojiPanWidth, st::emojiPanMaxHeight);
  385. for (auto &tab : _tabs) {
  386. if (tab.hasFooter()) {
  387. tab.footer()->hide();
  388. } else {
  389. _noFooter = true;
  390. }
  391. tab.widget()->hide();
  392. }
  393. if (tabbed()) {
  394. createTabsSlider();
  395. }
  396. setWidgetToScrollArea();
  397. for (auto &tab : _tabs) {
  398. const auto widget = tab.widget();
  399. widget->scrollToRequests(
  400. ) | rpl::start_with_next([=, tab = &tab](int y) {
  401. if (tab == currentTab()) {
  402. scrollToY(y);
  403. } else {
  404. tab->saveScrollTop(y);
  405. }
  406. }, widget->lifetime());
  407. widget->disableScrollRequests(
  408. ) | rpl::start_with_next([=, tab = &tab](bool disabled) {
  409. if (tab == currentTab()) {
  410. _scroll->disableScroll(disabled);
  411. }
  412. }, widget->lifetime());
  413. }
  414. rpl::merge(
  415. (hasStickersTab()
  416. ? stickers()->scrollUpdated() | rpl::map_to(0)
  417. : rpl::never<int>() | rpl::type_erased()),
  418. _scroll->scrollTopChanges()
  419. ) | rpl::start_with_next([=] {
  420. handleScroll();
  421. }, lifetime());
  422. if (_topShadow) {
  423. _topShadow->raise();
  424. }
  425. _bottomShadow->raise();
  426. if (_tabsSlider) {
  427. _tabsSlider->raise();
  428. }
  429. if (hasStickersTab()
  430. || hasGifsTab()
  431. || (hasEmojiTab() && _mode == Mode::Full)) {
  432. session().changes().peerUpdates(
  433. Data::PeerUpdate::Flag::Rights
  434. ) | rpl::filter([=](const Data::PeerUpdate &update) {
  435. return (update.peer.get() == _currentPeer);
  436. }) | rpl::start_with_next([=] {
  437. checkRestrictedPeer();
  438. }, lifetime());
  439. }
  440. if (hasStickersTab()) {
  441. session().data().stickers().stickerSetInstalled(
  442. ) | rpl::start_with_next([=](uint64 setId) {
  443. _tabsSlider->setActiveSection(indexByType(SelectorTab::Stickers));
  444. stickers()->showStickerSet(setId);
  445. _showRequests.fire({});
  446. }, lifetime());
  447. rpl::merge(
  448. session().premiumPossibleValue() | rpl::to_empty,
  449. session().data().stickers().updated(hasMasksTab()
  450. ? Data::StickersType::Masks
  451. : Data::StickersType::Stickers)
  452. ) | rpl::start_with_next([=] {
  453. refreshStickers();
  454. }, lifetime());
  455. }
  456. style::PaletteChanged(
  457. ) | rpl::start_with_next([=] {
  458. _panelRounding = Ui::PrepareCornerPixmaps(
  459. st::emojiPanRadius,
  460. _st.bg);
  461. _categoriesRounding = Ui::PrepareCornerPixmaps(
  462. st::emojiPanRadius,
  463. _st.categoriesBg);
  464. }, lifetime());
  465. if (hasEmojiTab() && _mode == Mode::Full) {
  466. session().data().stickers().emojiSetInstalled(
  467. ) | rpl::start_with_next([=](uint64 setId) {
  468. _tabsSlider->setActiveSection(indexByType(SelectorTab::Emoji));
  469. emoji()->showSet(setId);
  470. _showRequests.fire({});
  471. }, lifetime());
  472. }
  473. if (hasEmojiTab()) {
  474. emoji()->refreshEmoji();
  475. }
  476. setAttribute(Qt::WA_OpaquePaintEvent, false);
  477. showAll();
  478. hide();
  479. }
  480. TabbedSelector::~TabbedSelector() = default;
  481. void TabbedSelector::reinstallSwipe(not_null<Ui::RpWidget*> widget) {
  482. _swipeLifetime.destroy();
  483. Ui::Controls::SetupSwipeHandler(widget, _scroll.data(), [=](
  484. Ui::Controls::SwipeContextData data) {
  485. if (data.translation != 0) {
  486. if (!_swipeBackData.callback) {
  487. _swipeBackData = Ui::Controls::SetupSwipeBack(
  488. this,
  489. [=]() -> std::pair<QColor, QColor> {
  490. return {
  491. st::historyForwardChooseBg->c,
  492. st::historyForwardChooseFg->c,
  493. };
  494. },
  495. data.translation < 0);
  496. }
  497. _swipeBackData.callback(data);
  498. return;
  499. } else if (_swipeBackData.lifetime) {
  500. _swipeBackData = {};
  501. }
  502. }, [=](int, Qt::LayoutDirection direction) {
  503. if (!_tabsSlider) {
  504. return Ui::Controls::SwipeHandlerFinishData();
  505. }
  506. const auto activeSection = _tabsSlider->activeSection();
  507. const auto isToLeft = direction == Qt::RightToLeft;
  508. if ((isToLeft && activeSection > 0)
  509. || (!isToLeft && activeSection < _tabs.size() - 1)) {
  510. return Ui::Controls::DefaultSwipeBackHandlerFinishData([=] {
  511. if (_tabsSlider
  512. && _tabsSlider->activeSection() == activeSection) {
  513. _swipeBackData = {};
  514. _tabsSlider->setActiveSection(isToLeft
  515. ? activeSection - 1
  516. : activeSection + 1);
  517. }
  518. });
  519. }
  520. return Ui::Controls::SwipeHandlerFinishData();
  521. }, nullptr, &_swipeLifetime);
  522. }
  523. const style::EmojiPan &TabbedSelector::st() const {
  524. return _st;
  525. }
  526. Main::Session &TabbedSelector::session() const {
  527. return _show->session();
  528. }
  529. PauseReason TabbedSelector::level() const {
  530. return _level;
  531. }
  532. TabbedSelector::Tab TabbedSelector::createTab(SelectorTab type, int index) {
  533. auto createWidget = [&]() -> object_ptr<Inner> {
  534. const auto paused = [show = _show, level = _level] {
  535. return show->paused(level);
  536. };
  537. switch (type) {
  538. case SelectorTab::Emoji: {
  539. using EmojiMode = EmojiListWidget::Mode;
  540. using Descriptor = EmojiListDescriptor;
  541. return object_ptr<EmojiListWidget>(this, Descriptor{
  542. .show = _show,
  543. .mode = (_mode == Mode::EmojiStatus
  544. ? EmojiMode::EmojiStatus
  545. : _mode == Mode::ChannelStatus
  546. ? EmojiMode::ChannelStatus
  547. : _mode == Mode::BackgroundEmoji
  548. ? EmojiMode::BackgroundEmoji
  549. : _mode == Mode::FullReactions
  550. ? EmojiMode::FullReactions
  551. : _mode == Mode::RecentReactions
  552. ? EmojiMode::RecentReactions
  553. : _mode == Mode::PeerTitle
  554. ? EmojiMode::PeerTitle
  555. : EmojiMode::Full),
  556. .customTextColor = _customTextColor,
  557. .paused = paused,
  558. .st = &_st,
  559. .features = _features,
  560. });
  561. }
  562. case SelectorTab::Stickers: {
  563. using StickersMode = StickersListWidget::Mode;
  564. using Descriptor = StickersListDescriptor;
  565. return object_ptr<StickersListWidget>(this, Descriptor{
  566. .show = _show,
  567. .mode = (_mode == Mode::ChatIntro
  568. ? StickersMode::ChatIntro
  569. : StickersMode::Full),
  570. .paused = paused,
  571. .st = &_st,
  572. .features = _features,
  573. });
  574. }
  575. case SelectorTab::Gifs: {
  576. using Descriptor = GifsListDescriptor;
  577. return object_ptr<GifsListWidget>(this, Descriptor{
  578. .show = _show,
  579. .paused = paused,
  580. .st = &_st,
  581. });
  582. }
  583. case SelectorTab::Masks: {
  584. using StickersMode = StickersListWidget::Mode;
  585. using Descriptor = StickersListDescriptor;
  586. return object_ptr<StickersListWidget>(this, Descriptor{
  587. .show = _show,
  588. .mode = StickersMode::Masks,
  589. .paused = paused,
  590. .st = &_st,
  591. .features = _features,
  592. });
  593. }
  594. }
  595. Unexpected("Type in TabbedSelector::createTab.");
  596. };
  597. return Tab{ type, index, createWidget() };
  598. }
  599. bool TabbedSelector::full() const {
  600. return (_mode == Mode::Full);
  601. }
  602. bool TabbedSelector::mediaEditor() const {
  603. return (_mode == Mode::MediaEditor);
  604. }
  605. bool TabbedSelector::tabbed() const {
  606. return _tabbed;
  607. }
  608. bool TabbedSelector::hasEmojiTab() const {
  609. return _hasEmojiTab;
  610. }
  611. bool TabbedSelector::hasStickersTab() const {
  612. return _hasStickersTab;
  613. }
  614. bool TabbedSelector::hasGifsTab() const {
  615. return _hasGifsTab;
  616. }
  617. bool TabbedSelector::hasMasksTab() const {
  618. return _hasMasksTab;
  619. }
  620. rpl::producer<EmojiChosen> TabbedSelector::emojiChosen() const {
  621. return emoji()->chosen();
  622. }
  623. rpl::producer<FileChosen> TabbedSelector::customEmojiChosen() const {
  624. return emoji()->customChosen();
  625. }
  626. rpl::producer<FileChosen> TabbedSelector::fileChosen() const {
  627. auto never = rpl::never<FileChosen>(
  628. ) | rpl::type_erased();
  629. return rpl::merge(
  630. hasStickersTab() ? stickers()->chosen() : never,
  631. hasGifsTab() ? gifs()->fileChosen() : never,
  632. hasMasksTab() ? masks()->chosen() : never);
  633. }
  634. rpl::producer<PhotoChosen> TabbedSelector::photoChosen() const {
  635. return hasGifsTab() ? gifs()->photoChosen() : nullptr;
  636. }
  637. auto TabbedSelector::inlineResultChosen() const
  638. -> rpl::producer<InlineChosen> {
  639. return hasGifsTab() ? gifs()->inlineResultChosen() : nullptr;
  640. }
  641. auto TabbedSelector::choosingStickerUpdated() const
  642. -> rpl::producer<TabbedSelector::Action>{
  643. return hasStickersTab()
  644. ? stickers()->choosingUpdated()
  645. : rpl::never<Action>();
  646. }
  647. rpl::producer<> TabbedSelector::cancelled() const {
  648. return hasGifsTab() ? gifs()->cancelRequests() : nullptr;
  649. }
  650. rpl::producer<> TabbedSelector::checkForHide() const {
  651. auto never = rpl::never<>();
  652. return rpl::merge(
  653. hasStickersTab() ? stickers()->checkForHide() : never,
  654. hasMasksTab() ? masks()->checkForHide() : never,
  655. hasEmojiTab() ? emoji()->checkForHide() : never);
  656. }
  657. rpl::producer<> TabbedSelector::slideFinished() const {
  658. return _slideFinished.events();
  659. }
  660. void TabbedSelector::updateTabsSliderGeometry() {
  661. if (!_tabsSlider) {
  662. return;
  663. }
  664. const auto w = (mediaEditor() && hasMasksTab() && masks()->mySetsEmpty())
  665. ? width() / 2
  666. : width();
  667. _tabsSlider->resizeToWidth(w);
  668. _tabsSlider->moveToLeft(0, 0);
  669. }
  670. void TabbedSelector::resizeEvent(QResizeEvent *e) {
  671. updateTabsSliderGeometry();
  672. if (_topShadow && _tabsSlider) {
  673. _topShadow->setGeometry(
  674. _tabsSlider->x(),
  675. _tabsSlider->bottomNoMargins() - st::lineWidth,
  676. _tabsSlider->width(),
  677. st::lineWidth);
  678. }
  679. updateScrollGeometry(e->oldSize());
  680. updateRestrictedLabelGeometry();
  681. updateFooterGeometry();
  682. update();
  683. }
  684. void TabbedSelector::updateScrollGeometry(QSize oldSize) {
  685. auto scrollWidth = width() - st::emojiPanRadius;
  686. auto scrollHeight = height() - scrollTop() - scrollBottom();
  687. auto inner = currentTab()->widget();
  688. auto innerWidth = scrollWidth - st::emojiScroll.width;
  689. auto setScrollGeometry = [&] {
  690. _scroll->setGeometryToLeft(
  691. st::emojiPanRadius,
  692. scrollTop(),
  693. scrollWidth,
  694. scrollHeight);
  695. };
  696. auto setInnerGeometry = [&] {
  697. auto scrollTop = _scroll->scrollTop();
  698. auto scrollBottom = scrollTop + scrollHeight;
  699. inner->setMinimalHeight(innerWidth, scrollHeight);
  700. inner->setVisibleTopBottom(scrollTop, scrollBottom);
  701. };
  702. if (oldSize.height() > height()) {
  703. setScrollGeometry();
  704. setInnerGeometry();
  705. } else {
  706. setInnerGeometry();
  707. setScrollGeometry();
  708. }
  709. _bottomShadow->setGeometry(
  710. 0,
  711. _scroll->y() + (_dropDown ? 0 : (_scroll->height() - st::lineWidth)),
  712. width(),
  713. st::lineWidth);
  714. }
  715. void TabbedSelector::updateFooterGeometry() {
  716. _footerTop = _dropDown
  717. ? 0
  718. : _noFooter
  719. ? (height() - _roundRadius)
  720. : (height() - _st.footer);
  721. for (auto &tab : _tabs) {
  722. if (tab.hasFooter()) {
  723. tab.footer()->resizeToWidth(width());
  724. tab.footer()->moveToLeft(0, _footerTop);
  725. }
  726. }
  727. }
  728. void TabbedSelector::updateRestrictedLabelGeometry() {
  729. if (!_restrictedLabel) {
  730. return;
  731. }
  732. auto labelWidth = width() - st::stickerPanPadding * 2;
  733. _restrictedLabel->resizeToWidth(labelWidth);
  734. _restrictedLabel->moveToLeft(
  735. (width() - _restrictedLabel->width()) / 2,
  736. (height() / 3 - _restrictedLabel->height() / 2));
  737. }
  738. void TabbedSelector::paintEvent(QPaintEvent *e) {
  739. auto p = QPainter(this);
  740. auto switching = (_slideAnimation != nullptr);
  741. if (switching) {
  742. paintSlideFrame(p);
  743. if (!_a_slide.animating()) {
  744. _slideAnimation.reset();
  745. afterShown();
  746. _slideFinished.fire({});
  747. }
  748. } else {
  749. paintContent(p);
  750. }
  751. }
  752. void TabbedSelector::paintSlideFrame(QPainter &p) {
  753. if (_roundRadius > 0) {
  754. paintBgRoundedPart(p);
  755. } else if (_tabsSlider) {
  756. p.fillRect(0, 0, width(), _tabsSlider->height(), _st.bg);
  757. }
  758. auto slideDt = _a_slide.value(1.);
  759. _slideAnimation->paintFrame(p, _st, slideDt, 1.);
  760. }
  761. void TabbedSelector::paintBgRoundedPart(QPainter &p) {
  762. const auto fill = _dropDown
  763. ? QRect(0, height() - _roundRadius, width(), _roundRadius)
  764. : _tabsSlider
  765. ? QRect(0, 0, width(), _tabsSlider->height())
  766. : QRect(0, 0, width(), _roundRadius);
  767. Ui::FillRoundRect(p, fill, _st.bg, {
  768. .p = {
  769. _dropDown ? QPixmap() : _panelRounding.p[0],
  770. _dropDown ? QPixmap() : _panelRounding.p[1],
  771. _dropDown ? _panelRounding.p[2] : QPixmap(),
  772. _dropDown ? _panelRounding.p[3] : QPixmap(),
  773. },
  774. });
  775. }
  776. void TabbedSelector::paintContent(QPainter &p) {
  777. const auto &footerBg = hasSectionIcons() ? _st.categoriesBg : _st.bg;
  778. if (_roundRadius > 0) {
  779. paintBgRoundedPart(p);
  780. const auto &pixmaps = hasSectionIcons()
  781. ? _categoriesRounding
  782. : _panelRounding;
  783. const auto footerPart = QRect(
  784. 0,
  785. _footerTop,
  786. width(),
  787. _noFooter ? _roundRadius : _st.footer);
  788. Ui::FillRoundRect(p, footerPart, footerBg, {
  789. .p = {
  790. _dropDown ? pixmaps.p[0] : QPixmap(),
  791. _dropDown ? pixmaps.p[1] : QPixmap(),
  792. _dropDown ? QPixmap() : pixmaps.p[2],
  793. _dropDown ? QPixmap() : pixmaps.p[3],
  794. },
  795. });
  796. } else {
  797. if (_tabsSlider) {
  798. p.fillRect(0, 0, width(), _tabsSlider->height(), _st.bg);
  799. }
  800. p.fillRect(0, _footerTop, width(), _st.footer, footerBg);
  801. }
  802. auto sidesTop = marginTop();
  803. auto sidesHeight = height() - sidesTop - marginBottom();
  804. if (_restrictedLabel) {
  805. p.fillRect(0, sidesTop, width(), sidesHeight, st::emojiPanBg);
  806. } else {
  807. p.fillRect(
  808. myrtlrect(
  809. width() - st::emojiScroll.width,
  810. sidesTop,
  811. st::emojiScroll.width,
  812. sidesHeight),
  813. _st.bg);
  814. p.fillRect(
  815. myrtlrect(0, sidesTop, st::emojiPanRadius, sidesHeight),
  816. _st.bg);
  817. }
  818. }
  819. int TabbedSelector::marginTop() const {
  820. return (_dropDown && !_noFooter)
  821. ? _st.footer
  822. : _tabsSlider
  823. ? (_tabsSlider->height() - st::lineWidth)
  824. : _roundRadius;
  825. }
  826. int TabbedSelector::scrollTop() const {
  827. return tabbed()
  828. ? marginTop()
  829. : (_dropDown && !_noFooter)
  830. ? _st.footer
  831. : 0;
  832. }
  833. int TabbedSelector::marginBottom() const {
  834. return (_dropDown || _noFooter) ? _roundRadius : _st.footer;
  835. }
  836. int TabbedSelector::scrollBottom() const {
  837. return (_dropDown || _noFooter) ? 0 : marginBottom();
  838. }
  839. void TabbedSelector::refreshStickers() {
  840. if (hasStickersTab()) {
  841. stickers()->refreshStickers();
  842. if (isHidden() || _currentTabType != SelectorTab::Stickers) {
  843. stickers()->preloadImages();
  844. }
  845. }
  846. if (hasMasksTab()) {
  847. const auto masksList = masks();
  848. masksList->refreshStickers();
  849. if (isHidden() || _currentTabType != SelectorTab::Masks) {
  850. masksList->preloadImages();
  851. }
  852. fillTabsSliderSections();
  853. updateTabsSliderGeometry();
  854. if (hasStickersTab() && masksList->mySetsEmpty()) {
  855. _tabsSlider->setActiveSection(indexByType(SelectorTab::Stickers));
  856. }
  857. }
  858. }
  859. bool TabbedSelector::preventAutoHide() const {
  860. return (hasStickersTab() && stickers()->preventAutoHide())
  861. || (hasMasksTab() && masks()->preventAutoHide())
  862. || (hasEmojiTab() && emoji()->preventAutoHide())
  863. || hasMenu();
  864. }
  865. bool TabbedSelector::hasMenu() const {
  866. return (_menu && !_menu->empty());
  867. }
  868. QImage TabbedSelector::grabForAnimation() {
  869. auto slideAnimationData = base::take(_slideAnimation);
  870. auto slideAnimation = base::take(_a_slide);
  871. showAll();
  872. if (_topShadow) {
  873. _topShadow->hide();
  874. }
  875. if (_tabsSlider) {
  876. _tabsSlider->hide();
  877. }
  878. Ui::SendPendingMoveResizeEvents(this);
  879. auto result = QImage(
  880. size() * style::DevicePixelRatio(),
  881. QImage::Format_ARGB32_Premultiplied);
  882. result.setDevicePixelRatio(style::DevicePixelRatio());
  883. result.fill(Qt::transparent);
  884. render(&result);
  885. _a_slide = base::take(slideAnimation);
  886. _slideAnimation = base::take(slideAnimationData);
  887. return result;
  888. }
  889. bool TabbedSelector::floatPlayerHandleWheelEvent(QEvent *e) {
  890. return _scroll->viewportEvent(e);
  891. }
  892. QRect TabbedSelector::floatPlayerAvailableRect() const {
  893. return mapToGlobal(_scroll->geometry());
  894. }
  895. void TabbedSelector::hideFinished() {
  896. for (auto &tab : _tabs) {
  897. tab.widget()->panelHideFinished();
  898. }
  899. _a_slide.stop();
  900. _slideAnimation.reset();
  901. }
  902. void TabbedSelector::showStarted() {
  903. if (hasStickersTab()) {
  904. session().api().updateStickers();
  905. }
  906. if (hasMasksTab()) {
  907. session().api().updateMasks();
  908. }
  909. if (hasEmojiTab()) {
  910. session().api().updateCustomEmoji();
  911. }
  912. if (hasGifsTab()) {
  913. session().api().updateSavedGifs();
  914. }
  915. currentTab()->widget()->refreshRecent();
  916. currentTab()->widget()->preloadImages();
  917. _a_slide.stop();
  918. _slideAnimation.reset();
  919. showAll();
  920. }
  921. void TabbedSelector::beforeHiding() {
  922. if (!_scroll->isHidden()) {
  923. currentTab()->widget()->beforeHiding();
  924. if (_beforeHidingCallback) {
  925. _beforeHidingCallback(_currentTabType);
  926. }
  927. }
  928. if (Ui::InFocusChain(this)) {
  929. window()->setFocus();
  930. }
  931. }
  932. void TabbedSelector::afterShown() {
  933. if (!_a_slide.animating()) {
  934. showAll();
  935. currentTab()->widget()->afterShown();
  936. if (_afterShownCallback) {
  937. _afterShownCallback(_currentTabType);
  938. }
  939. }
  940. }
  941. void TabbedSelector::setCurrentPeer(PeerData *peer) {
  942. if (hasGifsTab()) {
  943. gifs()->setInlineQueryPeer(peer);
  944. }
  945. _currentPeer = peer;
  946. checkRestrictedPeer();
  947. if (hasEmojiTab()) {
  948. emoji()->showMegagroupSet(peer ? peer->asMegagroup() : nullptr);
  949. }
  950. if (hasStickersTab()) {
  951. stickers()->showMegagroupSet(peer ? peer->asMegagroup() : nullptr);
  952. }
  953. setAllowEmojiWithoutPremium(
  954. peer && Data::AllowEmojiWithoutPremium(peer));
  955. }
  956. void TabbedSelector::provideRecentEmoji(
  957. const std::vector<EmojiStatusId> &customRecentList) {
  958. for (const auto &tab : _tabs) {
  959. if (tab.type() == SelectorTab::Emoji) {
  960. const auto emoji = static_cast<EmojiListWidget*>(tab.widget());
  961. emoji->provideRecent(customRecentList);
  962. }
  963. }
  964. }
  965. void TabbedSelector::checkRestrictedPeer() {
  966. if (_currentPeer) {
  967. const auto error = (_currentTabType == SelectorTab::Stickers)
  968. ? Data::RestrictionError(
  969. _currentPeer,
  970. ChatRestriction::SendStickers)
  971. : (_currentTabType == SelectorTab::Gifs)
  972. ? Data::RestrictionError(
  973. _currentPeer,
  974. ChatRestriction::SendGifs)
  975. : (_currentTabType == SelectorTab::Emoji && _mode == Mode::Full)
  976. ? ((true || Data::RestrictionError(
  977. _currentPeer, // We don't allow input if texts are forbidden.
  978. ChatRestriction::SendInline))
  979. ? Data::RestrictionError(
  980. _currentPeer,
  981. ChatRestriction::SendOther)
  982. : Data::SendError())
  983. : Data::SendError();
  984. const auto changed = (_restrictedLabelKey != error.text);
  985. if (!changed) {
  986. return;
  987. }
  988. _restrictedLabelKey = error.text;
  989. if (error) {
  990. const auto show = _show;
  991. const auto peer = _currentPeer;
  992. _restrictedLabel.create(
  993. this,
  994. rpl::single(error.boostsToLift
  995. ? Ui::Text::Link(error.text)
  996. : TextWithEntities{ error.text }),
  997. st::stickersRestrictedLabel);
  998. const auto lifting = error.boostsToLift;
  999. _restrictedLabel->setClickHandlerFilter([=](auto...) {
  1000. const auto window = show->resolveWindow();
  1001. window->resolveBoostState(peer->asChannel(), lifting);
  1002. return false;
  1003. });
  1004. _restrictedLabel->show();
  1005. updateRestrictedLabelGeometry();
  1006. currentTab()->footer()->hide();
  1007. _scroll->hide();
  1008. _bottomShadow->hide();
  1009. update();
  1010. return;
  1011. }
  1012. } else {
  1013. _restrictedLabelKey = QString();
  1014. }
  1015. if (_restrictedLabel) {
  1016. _restrictedLabel.destroy();
  1017. if (!_a_slide.animating()) {
  1018. currentTab()->footer()->show();
  1019. _scroll->show();
  1020. _bottomShadow->setVisible(_mode == Mode::EmojiStatus);
  1021. update();
  1022. }
  1023. }
  1024. }
  1025. bool TabbedSelector::isRestrictedView() {
  1026. checkRestrictedPeer();
  1027. return (_restrictedLabel != nullptr);
  1028. }
  1029. void TabbedSelector::showAll() {
  1030. if (isRestrictedView()) {
  1031. _restrictedLabel->show();
  1032. } else {
  1033. if (currentTab()->hasFooter()) {
  1034. currentTab()->footer()->show();
  1035. }
  1036. _scroll->show();
  1037. _bottomShadow->setVisible(_mode == Mode::EmojiStatus);
  1038. }
  1039. if (_topShadow) {
  1040. _topShadow->show();
  1041. }
  1042. if (_tabsSlider) {
  1043. _tabsSlider->show();
  1044. }
  1045. }
  1046. void TabbedSelector::hideForSliding() {
  1047. hideChildren();
  1048. if (_topShadow) {
  1049. _topShadow->show();
  1050. }
  1051. if (_tabsSlider) {
  1052. _tabsSlider->show();
  1053. }
  1054. currentTab()->widget()->clearSelection();
  1055. }
  1056. void TabbedSelector::handleScroll() {
  1057. auto scrollTop = _scroll->scrollTop();
  1058. auto scrollBottom = scrollTop + _scroll->height();
  1059. currentTab()->widget()->setVisibleTopBottom(scrollTop, scrollBottom);
  1060. }
  1061. void TabbedSelector::setRoundRadius(int radius) {
  1062. _roundRadius = radius;
  1063. if (_tabsSlider) {
  1064. _tabsSlider->setRippleTopRoundRadius(_roundRadius);
  1065. }
  1066. }
  1067. void TabbedSelector::setAllowEmojiWithoutPremium(bool allow) {
  1068. for (const auto &tab : _tabs) {
  1069. if (tab.type() == SelectorTab::Emoji) {
  1070. const auto emoji = static_cast<EmojiListWidget*>(tab.widget());
  1071. emoji->setAllowWithoutPremium(allow);
  1072. }
  1073. }
  1074. }
  1075. void TabbedSelector::createTabsSlider() {
  1076. _tabsSlider.create(this, _st.tabs);
  1077. fillTabsSliderSections();
  1078. _tabsSlider->setActiveSectionFast(indexByType(_currentTabType));
  1079. _tabsSlider->sectionActivated(
  1080. ) | rpl::start_with_next([=] {
  1081. switchTab();
  1082. }, lifetime());
  1083. }
  1084. void TabbedSelector::fillTabsSliderSections() {
  1085. if (!_tabsSlider) {
  1086. return;
  1087. }
  1088. const auto sections = ranges::views::all(
  1089. _tabs
  1090. ) | ranges::views::filter([&](const Tab &tab) {
  1091. return (tab.type() == SelectorTab::Masks)
  1092. ? !masks()->mySetsEmpty()
  1093. : true;
  1094. }) | ranges::views::transform([&](const Tab &tab) {
  1095. return [&] {
  1096. switch (tab.type()) {
  1097. case SelectorTab::Emoji:
  1098. return tr::lng_switch_emoji;
  1099. case SelectorTab::Stickers:
  1100. return tr::lng_switch_stickers;
  1101. case SelectorTab::Gifs:
  1102. return tr::lng_switch_gifs;
  1103. case SelectorTab::Masks:
  1104. return tr::lng_switch_masks;
  1105. }
  1106. Unexpected("SelectorTab value in fillTabsSliderSections.");
  1107. }()(tr::now);
  1108. }) | ranges::to_vector;
  1109. _tabsSlider->setSections(sections);
  1110. }
  1111. bool TabbedSelector::hasSectionIcons() const {
  1112. return !_restrictedLabel && !_noFooter;
  1113. }
  1114. void TabbedSelector::switchTab() {
  1115. Expects(tabbed());
  1116. const auto tab = _tabsSlider->activeSection();
  1117. Assert(tab >= 0 && tab < _tabs.size());
  1118. const auto newTabType = typeByIndex(tab);
  1119. if (_currentTabType == newTabType) {
  1120. _scroll->scrollToY(0);
  1121. return;
  1122. }
  1123. const auto wasSectionIcons = hasSectionIcons();
  1124. const auto wasIndex = indexByType(_currentTabType);
  1125. currentTab()->saveScrollTop();
  1126. beforeHiding();
  1127. auto wasCache = grabForAnimation();
  1128. auto widget = _scroll->takeWidget<Inner>();
  1129. widget->setParent(this);
  1130. widget->hide();
  1131. if (currentTab()->hasFooter()) {
  1132. currentTab()->footer()->hide();
  1133. }
  1134. currentTab()->returnWidget(std::move(widget));
  1135. _currentTabType = newTabType;
  1136. _restrictedLabel.destroy();
  1137. checkRestrictedPeer();
  1138. currentTab()->widget()->refreshRecent();
  1139. currentTab()->widget()->preloadImages();
  1140. setWidgetToScrollArea();
  1141. auto nowCache = grabForAnimation();
  1142. auto direction = (wasIndex > indexByType(_currentTabType))
  1143. ? SlideAnimation::Direction::LeftToRight
  1144. : SlideAnimation::Direction::RightToLeft;
  1145. if (direction == SlideAnimation::Direction::LeftToRight) {
  1146. std::swap(wasCache, nowCache);
  1147. }
  1148. _slideAnimation = std::make_unique<SlideAnimation>();
  1149. const auto slidingRect = QRect(
  1150. 0,
  1151. _scroll->y() * style::DevicePixelRatio(),
  1152. width() * style::DevicePixelRatio(),
  1153. (height() - _scroll->y()) * style::DevicePixelRatio());
  1154. _slideAnimation->setFinalImages(
  1155. direction,
  1156. std::move(wasCache),
  1157. std::move(nowCache),
  1158. slidingRect,
  1159. wasSectionIcons);
  1160. _slideAnimation->setCornerMasks(
  1161. Images::CornersMask(st::emojiPanRadius));
  1162. _slideAnimation->start();
  1163. hideForSliding();
  1164. getTab(wasIndex)->widget()->hideFinished();
  1165. _a_slide.start(
  1166. [=] { update(); },
  1167. 0.,
  1168. 1.,
  1169. st::emojiPanSlideDuration,
  1170. anim::linear);
  1171. update();
  1172. if (full()) {
  1173. session().settings().setSelectorTab(_currentTabType);
  1174. session().saveSettingsDelayed();
  1175. }
  1176. }
  1177. not_null<EmojiListWidget*> TabbedSelector::emoji() const {
  1178. Expects(hasEmojiTab());
  1179. return static_cast<EmojiListWidget*>(
  1180. getTab(indexByType(SelectorTab::Emoji))->widget());
  1181. }
  1182. not_null<StickersListWidget*> TabbedSelector::stickers() const {
  1183. Expects(hasStickersTab());
  1184. return static_cast<StickersListWidget*>(
  1185. getTab(indexByType(SelectorTab::Stickers))->widget());
  1186. }
  1187. not_null<GifsListWidget*> TabbedSelector::gifs() const {
  1188. Expects(hasGifsTab());
  1189. return static_cast<GifsListWidget*>(
  1190. getTab(indexByType(SelectorTab::Gifs))->widget());
  1191. }
  1192. not_null<StickersListWidget*> TabbedSelector::masks() const {
  1193. Expects(hasMasksTab());
  1194. return static_cast<StickersListWidget*>(
  1195. getTab(indexByType(SelectorTab::Masks))->widget());
  1196. }
  1197. void TabbedSelector::setWidgetToScrollArea() {
  1198. auto inner = _scroll->setOwnedWidget(currentTab()->takeWidget());
  1199. auto innerWidth = _scroll->width() - st::emojiScroll.width;
  1200. auto scrollHeight = _scroll->height();
  1201. inner->setMinimalHeight(innerWidth, scrollHeight);
  1202. inner->moveToLeft(0, 0);
  1203. inner->show();
  1204. if (_tabs.size() > 1) {
  1205. reinstallSwipe(inner);
  1206. }
  1207. _scroll->disableScroll(false);
  1208. scrollToY(currentTab()->getScrollTop());
  1209. handleScroll();
  1210. }
  1211. void TabbedSelector::scrollToY(int y) {
  1212. _scroll->scrollToY(y);
  1213. // Qt render glitch workaround, shadow sometimes disappears if we just scroll to y.
  1214. if (_topShadow) {
  1215. _topShadow->update();
  1216. }
  1217. }
  1218. void TabbedSelector::showMenuWithDetails(SendMenu::Details details) {
  1219. _menu = currentTab()->widget()->fillContextMenu(details);
  1220. if (_menu && !_menu->empty()) {
  1221. _menu->popup(QCursor::pos());
  1222. }
  1223. }
  1224. void TabbedSelector::setDropDown(bool dropDown) {
  1225. if (_dropDown == dropDown) {
  1226. return;
  1227. }
  1228. _dropDown = dropDown;
  1229. updateFooterGeometry();
  1230. updateScrollGeometry(size());
  1231. }
  1232. rpl::producer<> TabbedSelector::contextMenuRequested() const {
  1233. return events(
  1234. ) | rpl::filter([=](not_null<QEvent*> e) {
  1235. return e->type() == QEvent::ContextMenu;
  1236. }) | rpl::to_empty;
  1237. }
  1238. SelectorTab TabbedSelector::typeByIndex(int index) const {
  1239. for (const auto &tab : _tabs) {
  1240. if (tab.index() == index) {
  1241. return tab.type();
  1242. }
  1243. }
  1244. Unexpected("Type in TabbedSelector::typeByIndex.");
  1245. }
  1246. int TabbedSelector::indexByType(SelectorTab type) const {
  1247. for (const auto &tab : _tabs) {
  1248. if (tab.type() == type) {
  1249. return tab.index();
  1250. }
  1251. }
  1252. Unexpected("Index in TabbedSelector::indexByType.");
  1253. }
  1254. not_null<TabbedSelector::Tab*> TabbedSelector::getTab(int index) {
  1255. return &(_tabs[index]);
  1256. }
  1257. not_null<const TabbedSelector::Tab*> TabbedSelector::getTab(int index) const {
  1258. return &_tabs[index];
  1259. }
  1260. not_null<TabbedSelector::Tab*> TabbedSelector::currentTab() {
  1261. return &_tabs[indexByType(_currentTabType)];
  1262. }
  1263. not_null<const TabbedSelector::Tab*> TabbedSelector::currentTab() const {
  1264. return &_tabs[indexByType(_currentTabType)];
  1265. }
  1266. TabbedSelector::Inner::Inner(
  1267. QWidget *parent,
  1268. std::shared_ptr<Show> show,
  1269. PauseReason level)
  1270. : Inner(
  1271. parent,
  1272. st::defaultEmojiPan,
  1273. show,
  1274. [show, level] { return show->paused(level); }) {
  1275. }
  1276. TabbedSelector::Inner::Inner(
  1277. QWidget *parent,
  1278. const style::EmojiPan &st,
  1279. std::shared_ptr<Show> show,
  1280. Fn<bool()> paused)
  1281. : RpWidget(parent)
  1282. , _st(st)
  1283. , _show(std::move(show))
  1284. , _session(&_show->session())
  1285. , _paused(paused) {
  1286. }
  1287. rpl::producer<int> TabbedSelector::Inner::scrollToRequests() const {
  1288. return _scrollToRequests.events();
  1289. }
  1290. rpl::producer<bool> TabbedSelector::Inner::disableScrollRequests() const {
  1291. return _disableScrollRequests.events();
  1292. }
  1293. void TabbedSelector::Inner::scrollTo(int y) {
  1294. _scrollToRequests.fire_copy(y);
  1295. }
  1296. void TabbedSelector::Inner::disableScroll(bool disabled) {
  1297. _disableScrollRequests.fire_copy(disabled);
  1298. }
  1299. void TabbedSelector::Inner::checkHideWithBox(
  1300. object_ptr<Ui::BoxContent> box) {
  1301. const auto raw = QPointer<Ui::BoxContent>(box.data());
  1302. _show->showBox(std::move(box));
  1303. if (!raw) {
  1304. return;
  1305. }
  1306. _preventHideWithBox = true;
  1307. connect(raw, &QObject::destroyed, this, [=] {
  1308. _preventHideWithBox = false;
  1309. _checkForHide.fire({});
  1310. });
  1311. }
  1312. void TabbedSelector::Inner::paintEmptySearchResults(
  1313. Painter &p,
  1314. const style::icon &icon,
  1315. const QString &text) const {
  1316. const auto iconLeft = (width() - icon.width()) / 2;
  1317. const auto iconTop = std::max(
  1318. (height() / 3) - (icon.height() / 2),
  1319. st::normalFont->height);
  1320. icon.paint(p, iconLeft, iconTop, width());
  1321. const auto textWidth = st::normalFont->width(text);
  1322. const auto textTop = std::min(
  1323. iconTop + icon.height() - st::normalFont->height,
  1324. height() - 2 * st::normalFont->height);
  1325. p.setFont(st::normalFont);
  1326. p.setPen(_st.tabs.labelFg);
  1327. p.drawTextLeft(
  1328. (width() - textWidth) / 2,
  1329. textTop,
  1330. width(),
  1331. text,
  1332. textWidth);
  1333. }
  1334. void TabbedSelector::Inner::visibleTopBottomUpdated(
  1335. int visibleTop,
  1336. int visibleBottom) {
  1337. _visibleTop = visibleTop;
  1338. _visibleBottom = visibleBottom;
  1339. }
  1340. void TabbedSelector::Inner::setMinimalHeight(
  1341. int newWidth,
  1342. int newMinimalHeight) {
  1343. if (_minimalHeight != newMinimalHeight) {
  1344. _minimalHeight = newMinimalHeight;
  1345. resizeToWidth(newWidth);
  1346. } else if (newWidth != width()) {
  1347. resizeToWidth(newWidth);
  1348. }
  1349. }
  1350. int TabbedSelector::Inner::resizeGetHeight(int newWidth) {
  1351. auto result = std::max(
  1352. countDesiredHeight(newWidth),
  1353. minimalHeight());
  1354. if (result != height()) {
  1355. update();
  1356. }
  1357. return result;
  1358. }
  1359. int TabbedSelector::Inner::minimalHeight() const {
  1360. return _minimalHeight.value_or(defaultMinimalHeight());
  1361. }
  1362. int TabbedSelector::Inner::defaultMinimalHeight() const {
  1363. return st::emojiPanMaxHeight - _st.footer;
  1364. }
  1365. void TabbedSelector::Inner::hideFinished() {
  1366. processHideFinished();
  1367. if (auto footer = getFooter()) {
  1368. footer->processHideFinished();
  1369. }
  1370. }
  1371. void TabbedSelector::Inner::panelHideFinished() {
  1372. hideFinished();
  1373. processPanelHideFinished();
  1374. if (auto footer = getFooter()) {
  1375. footer->processPanelHideFinished();
  1376. }
  1377. }
  1378. TabbedSelector::InnerFooter::InnerFooter(
  1379. QWidget *parent,
  1380. const style::EmojiPan &st)
  1381. : RpWidget(parent)
  1382. , _st(st) {
  1383. resize(st::emojiPanWidth, _st.footer);
  1384. }
  1385. const style::EmojiPan &TabbedSelector::InnerFooter::st() const {
  1386. return _st;
  1387. }
  1388. } // namespace ChatHelpers