| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909 |
- /*
- This file is part of Telegram Desktop,
- the official desktop application for the Telegram messaging service.
- For license and copyright information please follow this link:
- https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
- */
- #include "chat_helpers/emoji_list_widget.h"
- #include "api/api_peer_photo.h"
- #include "apiwrap.h"
- #include "base/unixtime.h"
- #include "ui/boxes/confirm_box.h"
- #include "ui/controls/tabbed_search.h"
- #include "ui/text/format_values.h"
- #include "ui/effects/animations.h"
- #include "ui/widgets/menu/menu_add_action_callback.h"
- #include "ui/widgets/menu/menu_add_action_callback_factory.h"
- #include "ui/widgets/buttons.h"
- #include "ui/widgets/popup_menu.h"
- #include "ui/widgets/shadow.h"
- #include "ui/text/custom_emoji_instance.h"
- #include "ui/effects/ripple_animation.h"
- #include "ui/effects/premium_graphics.h"
- #include "ui/emoji_config.h"
- #include "ui/painter.h"
- #include "ui/power_saving.h"
- #include "ui/ui_utility.h"
- #include "ui/cached_round_corners.h"
- #include "boxes/sticker_set_box.h"
- #include "lang/lang_keys.h"
- #include "layout/layout_position.h"
- #include "data/data_emoji_statuses.h"
- #include "data/data_session.h"
- #include "data/data_changes.h"
- #include "data/data_channel.h"
- #include "data/data_document.h"
- #include "data/data_file_origin.h"
- #include "data/data_peer_values.h"
- #include "data/stickers/data_stickers.h"
- #include "data/stickers/data_custom_emoji.h"
- #include "chat_helpers/emoji_keywords.h"
- #include "chat_helpers/stickers_list_widget.h"
- #include "chat_helpers/stickers_list_footer.h"
- #include "emoji_suggestions_data.h"
- #include "emoji_suggestions_helper.h"
- #include "main/main_session.h"
- #include "main/main_session_settings.h"
- #include "core/core_settings.h"
- #include "core/application.h"
- #include "settings/settings_premium.h"
- #include "window/window_session_controller.h"
- #include "styles/style_chat_helpers.h"
- #include "styles/style_menu_icons.h"
- #include <QtWidgets/QApplication>
- namespace ChatHelpers {
- namespace {
- constexpr auto kCollapsedRows = 3;
- constexpr auto kAppearDuration = 0.3;
- constexpr auto kCustomSearchLimit = 256;
- constexpr auto kColorPickerDelay = crl::time(500);
- using Core::RecentEmojiId;
- using Core::RecentEmojiDocument;
- } // namespace
- class EmojiColorPicker final : public Ui::RpWidget {
- public:
- EmojiColorPicker(QWidget *parent, const style::EmojiPan &st);
- void showEmoji(EmojiPtr emoji, bool allLabel = false);
- void clearSelection();
- void handleMouseMove(QPoint globalPos);
- void handleMouseRelease(QPoint globalPos);
- void setSingleSize(QSize size);
- void showAnimated();
- void hideAnimated();
- void hideFast();
- [[nodiscard]] rpl::producer<EmojiChosen> chosen() const;
- [[nodiscard]] rpl::producer<> hidden() const;
- protected:
- void paintEvent(QPaintEvent *e) override;
- void mousePressEvent(QMouseEvent *e) override;
- void mouseReleaseEvent(QMouseEvent *e) override;
- void mouseMoveEvent(QMouseEvent *e) override;
- private:
- void createAllLabel();
- void animationCallback();
- void updateSize();
- [[nodiscard]] int topColorAllSkip() const;
- void drawVariant(QPainter &p, int variant);
- void updateSelected();
- void setSelected(int newSelected);
- const style::EmojiPan &_st;
- bool _ignoreShow = false;
- QVector<EmojiPtr> _variants;
- int _selected = -1;
- int _pressedSel = -1;
- QPoint _lastMousePos;
- QSize _singleSize;
- QPoint _areaPosition;
- QPoint _innerPosition;
- Ui::RoundRect _backgroundRect;
- Ui::RoundRect _overBg;
- bool _hiding = false;
- QPixmap _cache;
- Ui::Animations::Simple _a_opacity;
- std::unique_ptr<Ui::FlatLabel> _allLabel;
- rpl::event_stream<EmojiChosen> _chosen;
- rpl::event_stream<> _hidden;
- };
- struct EmojiListWidget::CustomEmojiInstance {
- std::unique_ptr<Ui::Text::CustomEmoji> emoji;
- bool recentOnly = false;
- };
- struct EmojiListWidget::RecentOne {
- std::shared_ptr<Data::EmojiStatusCollectible> collectible;
- Ui::Text::CustomEmoji *custom = nullptr;
- RecentEmojiId id;
- mutable QImage premiumLock;
- };
- EmojiColorPicker::EmojiColorPicker(
- QWidget *parent,
- const style::EmojiPan &st)
- : RpWidget(parent)
- , _st(st)
- , _backgroundRect(st::emojiPanRadius, _st.bg)
- , _overBg(st::emojiPanRadius, _st.overBg) {
- setMouseTracking(true);
- }
- void EmojiColorPicker::showEmoji(EmojiPtr emoji, bool allLabel) {
- if (!emoji || !emoji->hasVariants()) {
- return;
- }
- if (!allLabel) {
- _allLabel = nullptr;
- } else if (!_allLabel) {
- createAllLabel();
- }
- _ignoreShow = false;
- _variants.resize(emoji->variantsCount() + 1);
- for (auto i = 0, size = int(_variants.size()); i != size; ++i) {
- _variants[i] = emoji->variant(i);
- }
- updateSize();
- if (!_cache.isNull()) {
- _cache = QPixmap();
- }
- showAnimated();
- }
- void EmojiColorPicker::createAllLabel() {
- _allLabel = std::make_unique<Ui::FlatLabel>(
- this,
- tr::lng_emoji_color_all(),
- _st.colorAllLabel);
- _allLabel->show();
- _allLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
- }
- void EmojiColorPicker::updateSize() {
- auto width = st::emojiPanMargins.left()
- + _singleSize.width() * _variants.size()
- + (_variants.size() - 2) * st::emojiColorsPadding
- + st::emojiColorsSep
- + st::emojiPanMargins.right();
- auto height = st::emojiPanMargins.top()
- + 2 * st::emojiColorsPadding
- + _singleSize.height()
- + st::emojiPanMargins.bottom();
- if (_allLabel) {
- _allLabel->resizeToWidth(width
- - st::emojiPanMargins.left()
- - st::emojiPanMargins.right()
- - st::emojiPanColorAllPadding.left()
- - st::emojiPanColorAllPadding.right());
- _allLabel->move(
- st::emojiPanMargins.left() + st::emojiPanColorAllPadding.left(),
- st::emojiPanMargins.top() + st::emojiPanColorAllPadding.top());
- height += topColorAllSkip();
- }
- resize(width, height);
- update();
- updateSelected();
- }
- void EmojiColorPicker::paintEvent(QPaintEvent *e) {
- auto p = QPainter(this);
- auto opacity = _a_opacity.value(_hiding ? 0. : 1.);
- if (opacity < 1.) {
- if (opacity > 0.) {
- p.setOpacity(opacity);
- } else {
- return;
- }
- }
- if (e->rect() != rect()) {
- p.setClipRect(e->rect());
- }
- auto inner = rect().marginsRemoved(st::emojiPanMargins);
- if (!_cache.isNull()) {
- p.drawPixmap(0, 0, _cache);
- return;
- }
- Ui::Shadow::paint(p, inner, width(), _st.showAnimation.shadow);
- _backgroundRect.paint(p, inner);
- const auto skip = topColorAllSkip();
- auto x = st::emojiPanMargins.left() + 2 * st::emojiColorsPadding + _singleSize.width();
- if (rtl()) x = width() - x - st::emojiColorsSep;
- p.fillRect(x, st::emojiPanMargins.top() + skip + st::emojiColorsPadding, st::emojiColorsSep, inner.height() - st::emojiColorsPadding * 2 - skip, st::emojiColorsSepColor);
- if (_variants.isEmpty()) {
- return;
- }
- p.translate(0, skip);
- for (auto i = 0, count = int(_variants.size()); i != count; ++i) {
- drawVariant(p, i);
- }
- }
- void EmojiColorPicker::mousePressEvent(QMouseEvent *e) {
- if (e->button() != Qt::LeftButton) {
- return;
- }
- _lastMousePos = e->globalPos();
- updateSelected();
- _pressedSel = _selected;
- }
- void EmojiColorPicker::mouseReleaseEvent(QMouseEvent *e) {
- handleMouseRelease(e->globalPos());
- }
- void EmojiColorPicker::handleMouseRelease(QPoint globalPos) {
- _lastMousePos = globalPos;
- int32 pressed = _pressedSel;
- _pressedSel = -1;
- updateSelected();
- if (_selected >= 0 && (pressed < 0 || _selected == pressed)) {
- _chosen.fire_copy({ .emoji = _variants[_selected] });
- }
- _ignoreShow = true;
- hideAnimated();
- }
- void EmojiColorPicker::setSingleSize(QSize size) {
- const auto area = st::emojiPanArea;
- _singleSize = size;
- _areaPosition = QPoint(
- (_singleSize.width() - area.width()) / 2,
- (_singleSize.height() - area.height()) / 2);
- const auto esize = Ui::Emoji::GetSizeLarge() / style::DevicePixelRatio();
- _innerPosition = QPoint(
- (area.width() - esize) / 2,
- (area.height() - esize) / 2);
- updateSize();
- }
- void EmojiColorPicker::handleMouseMove(QPoint globalPos) {
- _lastMousePos = globalPos;
- updateSelected();
- }
- void EmojiColorPicker::mouseMoveEvent(QMouseEvent *e) {
- handleMouseMove(e->globalPos());
- }
- void EmojiColorPicker::animationCallback() {
- update();
- if (!_a_opacity.animating()) {
- _cache = QPixmap();
- if (_allLabel) {
- _allLabel->show();
- }
- if (_hiding) {
- hide();
- _hidden.fire({});
- } else {
- _lastMousePos = QCursor::pos();
- updateSelected();
- }
- }
- }
- void EmojiColorPicker::hideFast() {
- clearSelection();
- _a_opacity.stop();
- _cache = QPixmap();
- hide();
- _hidden.fire({});
- }
- rpl::producer<EmojiChosen> EmojiColorPicker::chosen() const {
- return _chosen.events();
- }
- rpl::producer<> EmojiColorPicker::hidden() const {
- return _hidden.events();
- }
- void EmojiColorPicker::hideAnimated() {
- if (_cache.isNull()) {
- if (_allLabel) {
- _allLabel->show();
- }
- _cache = Ui::GrabWidget(this);
- clearSelection();
- }
- _hiding = true;
- if (_allLabel) {
- _allLabel->hide();
- }
- _a_opacity.start([this] { animationCallback(); }, 1., 0., st::emojiPanDuration);
- }
- void EmojiColorPicker::showAnimated() {
- if (_ignoreShow) return;
- if (!isHidden() && !_hiding) {
- return;
- }
- _hiding = false;
- if (_cache.isNull()) {
- if (_allLabel) {
- _allLabel->show();
- }
- _cache = Ui::GrabWidget(this);
- clearSelection();
- }
- show();
- if (_allLabel) {
- _allLabel->hide();
- }
- _a_opacity.start([this] { animationCallback(); }, 0., 1., st::emojiPanDuration);
- }
- void EmojiColorPicker::clearSelection() {
- _pressedSel = -1;
- setSelected(-1);
- _lastMousePos = mapToGlobal(QPoint(-10, -10));
- }
- int EmojiColorPicker::topColorAllSkip() const {
- return _allLabel
- ? (st::emojiPanColorAllPadding.top()
- + _allLabel->height()
- + st::emojiPanColorAllPadding.bottom())
- : 0;
- }
- void EmojiColorPicker::updateSelected() {
- auto newSelected = -1;
- auto p = mapFromGlobal(_lastMousePos);
- auto sx = rtl() ? (width() - p.x()) : p.x(), y = p.y() - st::emojiPanMargins.top() - topColorAllSkip() - st::emojiColorsPadding;
- if (y >= 0 && y < _singleSize.height()) {
- auto x = sx - st::emojiPanMargins.left() - st::emojiColorsPadding;
- if (x >= 0 && x < _singleSize.width()) {
- newSelected = 0;
- } else {
- x -= _singleSize.width() + 2 * st::emojiColorsPadding + st::emojiColorsSep;
- if (x >= 0 && x < _singleSize.width() * (_variants.size() - 1)) {
- newSelected = (x / _singleSize.width()) + 1;
- }
- }
- }
- setSelected(newSelected);
- }
- void EmojiColorPicker::setSelected(int newSelected) {
- if (_selected == newSelected) {
- return;
- }
- const auto skip = topColorAllSkip();
- const auto updateSelectedRect = [&] {
- if (_selected < 0) return;
- auto addedSkip = (_selected > 0)
- ? (2 * st::emojiColorsPadding + st::emojiColorsSep)
- : 0;
- auto left = st::emojiPanMargins.left()
- + st::emojiColorsPadding
- + _selected * _singleSize.width()
- + addedSkip;
- rtlupdate(
- left,
- st::emojiPanMargins.top() + st::emojiColorsPadding + skip,
- _singleSize.width(),
- _singleSize.height());
- };
- updateSelectedRect();
- _selected = newSelected;
- updateSelectedRect();
- setCursor((_selected >= 0) ? style::cur_pointer : style::cur_default);
- }
- void EmojiColorPicker::drawVariant(QPainter &p, int variant) {
- const auto w = QPoint(
- st::emojiPanMargins.left(),
- st::emojiPanMargins.top()
- ) + QPoint(
- (st::emojiColorsPadding
- + variant * _singleSize.width()
- + (variant
- ? (2 * st::emojiColorsPadding + st::emojiColorsSep)
- : 0)),
- st::emojiColorsPadding
- ) + _areaPosition;
- if (variant == _selected) {
- QPoint tl(w);
- if (rtl()) tl.setX(width() - tl.x() - st::emojiPanArea.width());
- _overBg.paint(p, QRect(tl, st::emojiPanArea));
- }
- Ui::Emoji::Draw(
- p,
- _variants[variant],
- Ui::Emoji::GetSizeLarge(),
- w.x() + _innerPosition.x(),
- w.y() + _innerPosition.y());
- }
- std::vector<EmojiStatusId> DocumentListToRecent(
- const std::vector<DocumentId> &documents) {
- return documents | ranges::views::transform([](DocumentId id) {
- return EmojiStatusId{ .documentId = id };
- }) | ranges::to_vector;
- }
- EmojiListWidget::EmojiListWidget(
- QWidget *parent,
- not_null<Window::SessionController*> controller,
- PauseReason level,
- Mode mode)
- : EmojiListWidget(parent, {
- .show = controller->uiShow(),
- .mode = mode,
- .paused = Window::PausedIn(controller, level),
- }) {
- }
- EmojiListWidget::EmojiListWidget(
- QWidget *parent,
- EmojiListDescriptor &&descriptor)
- : Inner(
- parent,
- descriptor.st ? *descriptor.st : st::defaultEmojiPan,
- descriptor.show,
- std::move(descriptor.paused))
- , _show(std::move(descriptor.show))
- , _features(descriptor.features)
- , _onlyUnicodeEmoji(descriptor.mode == Mode::PeerTitle)
- , _mode(_onlyUnicodeEmoji ? Mode::Full : descriptor.mode)
- , _api(&session().mtp())
- , _staticCount(_mode == Mode::Full ? kEmojiSectionCount : 1)
- , _premiumIcon(_mode == Mode::EmojiStatus
- ? std::make_unique<GradientPremiumStar>()
- : nullptr)
- , _localSetsManager(
- std::make_unique<LocalStickersManager>(&session()))
- , _customRecentFactory(std::move(descriptor.customRecentFactory))
- , _freeEffects(std::move(descriptor.freeEffects))
- , _customTextColor(std::move(descriptor.customTextColor))
- , _overBg(st::emojiPanRadius, st().overBg)
- , _premiumMark(std::make_unique<StickerPremiumMark>(
- &session(),
- st::emojiPremiumLock))
- , _collapsedBg(st::emojiPanExpand.height / 2, st().headerFg)
- , _picker(this, st())
- , _showPickerTimer([=] { showPicker(); })
- , _previewTimer([=] { showPreview(); }) {
- setMouseTracking(true);
- if (st().bg->c.alpha() > 0) {
- setAttribute(Qt::WA_OpaquePaintEvent);
- }
- if (_mode != Mode::RecentReactions
- && _mode != Mode::BackgroundEmoji
- && _mode != Mode::ChannelStatus
- && !_onlyUnicodeEmoji) {
- setupSearch();
- }
- if (_mode == Mode::ChannelStatus) {
- session().api().peerPhoto().emojiListValue(
- Api::PeerPhoto::EmojiListType::NoChannelStatus
- ) | rpl::start_with_next([=](const std::vector<DocumentId> &list) {
- _restrictedCustomList = { begin(list), end(list) };
- if (!_custom.empty()) {
- refreshCustom();
- }
- }, lifetime());
- } else if (_mode == Mode::EmojiStatus && _features.collectibleStatus) {
- session().data().emojiStatuses().collectiblesUpdates(
- ) | rpl::start_with_next([=] {
- refreshCustom();
- }, lifetime());
- }
- _customSingleSize = Data::FrameSizeFromTag(
- Data::CustomEmojiManager::SizeTag::Large
- ) / style::DevicePixelRatio();
- _picker->hide();
- for (auto i = 1; i != _staticCount; ++i) {
- const auto section = static_cast<Section>(i);
- _counts[i] = Ui::Emoji::GetSectionCount(section);
- }
- _picker->chosen(
- ) | rpl::start_with_next([=](EmojiChosen data) {
- colorChosen(data);
- }, lifetime());
- _picker->hidden(
- ) | rpl::start_with_next([=] {
- pickerHidden();
- }, lifetime());
- session().changes().peerUpdates(
- Data::PeerUpdate::Flag::EmojiSet
- ) | rpl::filter([=](const Data::PeerUpdate &update) {
- return (update.peer.get() == _megagroupSet);
- }) | rpl::start_with_next([=] {
- refreshCustom();
- resizeToWidth(width());
- }, lifetime());
- session().data().stickers().updated(
- Data::StickersType::Emoji
- ) | rpl::start_with_next([=] {
- refreshCustom();
- resizeToWidth(width());
- }, lifetime());
- rpl::combine(
- Data::AmPremiumValue(&session()),
- session().premiumPossibleValue()
- ) | rpl::skip(1) | rpl::start_with_next([=] {
- refreshCustom();
- resizeToWidth(width());
- }, lifetime());
- rpl::single(
- rpl::empty
- ) | rpl::then(
- style::PaletteChanged()
- ) | rpl::start_with_next([=] {
- initButton(_add, tr::lng_stickers_featured_add(tr::now), false);
- initButton(_unlock, tr::lng_emoji_featured_unlock(tr::now), true);
- initButton(_restore, tr::lng_emoji_premium_restore(tr::now), true);
- }, lifetime());
- if (!descriptor.customRecentList.empty()) {
- fillRecentFrom(descriptor.customRecentList);
- }
- }
- EmojiListWidget::~EmojiListWidget() {
- base::take(_customEmoji);
- }
- void EmojiListWidget::setupSearch() {
- const auto session = &_show->session();
- const auto type = (_mode == Mode::EmojiStatus)
- ? TabbedSearchType::Status
- : (_mode == Mode::UserpicBuilder)
- ? TabbedSearchType::ProfilePhoto
- : TabbedSearchType::Emoji;
- _search = MakeSearch(this, st(), [=](std::vector<QString> &&query) {
- _nextSearchQuery = std::move(query);
- InvokeQueued(this, [=] {
- applyNextSearchQuery();
- });
- _searchQueries.fire_copy(_nextSearchQuery);
- }, session, type);
- }
- rpl::producer<std::vector<QString>> EmojiListWidget::searchQueries() const {
- return _searchQueries.events();
- }
- rpl::producer<int> EmojiListWidget::recentShownCount() const {
- return _recentShownCount.value();
- }
- void EmojiListWidget::applyNextSearchQuery() {
- if (_searchQuery == _nextSearchQuery) {
- return;
- }
- _searchQuery = _nextSearchQuery;
- std::swap(_searchEmoji, _searchEmojiPrevious);
- _searchEmoji.clear();
- const auto finish = [&](bool searching = true) {
- if (!_searchMode && !searching) {
- return;
- }
- const auto modeChanged = (_searchMode != searching);
- clearSelection();
- if (modeChanged) {
- if (_picker) {
- _picker->hideAnimated();
- }
- _colorAllRipple = nullptr;
- for (auto &set : _custom) {
- set.ripple = nullptr;
- }
- _searchMode = searching;
- }
- if (!searching) {
- _searchResults.clear();
- _searchCustomIds.clear();
- }
- resizeToWidth(width());
- _recentShownCount = searching
- ? _searchResults.size()
- : _recent.size();
- update();
- if (modeChanged) {
- visibleTopBottomUpdated(getVisibleTop(), getVisibleBottom());
- }
- updateSelected();
- };
- if (_searchQuery.empty()) {
- finish(false);
- return;
- }
- const auto guard = gsl::finally([&] { finish(); });
- auto plain = collectPlainSearchResults();
- if (_searchEmoji == _searchEmojiPrevious) {
- return;
- }
- _searchResults.clear();
- _searchCustomIds.clear();
- if (_mode != Mode::Full || session().premium()) {
- appendPremiumSearchResults();
- }
- if (_mode == Mode::Full) {
- for (const auto emoji : plain) {
- _searchResults.push_back({
- .id = { emoji },
- });
- }
- }
- }
- void EmojiListWidget::showPreview() {
- if (const auto over = std::get_if<OverEmoji>(&_pressed)) {
- if (const auto custom = lookupCustomEmoji(over)) {
- const auto document = custom.document;
- _show->showMediaPreview(document->stickerSetOrigin(), document);
- _previewShown = true;
- }
- }
- }
- std::vector<EmojiPtr> EmojiListWidget::collectPlainSearchResults() {
- return SearchEmoji(_searchQuery, _searchEmoji);
- }
- void EmojiListWidget::appendPremiumSearchResults() {
- const auto test = session().isTestMode();
- auto &owner = session().data();
- const auto checkCustom = [&](EmojiPtr emoji, DocumentId id) {
- return emoji
- && _searchEmoji.contains(emoji)
- && (_searchResults.size() < kCustomSearchLimit)
- && _searchCustomIds.emplace(id).second;
- };
- for (const auto &recent : _recent) {
- if (!recent.custom) {
- continue;
- }
- const auto &idData = recent.id.data;
- const auto id = std::get_if<Core::RecentEmojiDocument>(&idData);
- if (!id || id->test != test) {
- continue;
- }
- const auto sticker = owner.document(id->id)->sticker();
- const auto emoji = sticker
- ? Ui::Emoji::Find(sticker->alt)
- : nullptr;
- if (checkCustom(emoji, id->id)) {
- _searchResults.push_back(recent);
- }
- }
- for (const auto &set : _custom) {
- for (const auto &one : set.list) {
- const auto id = one.document->id;
- if (checkCustom(one.emoji, id)) {
- _searchResults.push_back({
- .custom = one.custom,
- .id = { RecentEmojiDocument{ .id = id, .test = test } },
- });
- }
- }
- }
- }
- void EmojiListWidget::provideRecent(
- const std::vector<EmojiStatusId> &customRecentList) {
- clearSelection();
- fillRecentFrom(customRecentList);
- resizeToWidth(width());
- }
- void EmojiListWidget::repaintCustom(uint64 setId) {
- if (!_repaintsScheduled.emplace(setId).second) {
- return;
- }
- const auto repaintSearch = (setId == SearchEmojiSectionSetId());
- if (_searchMode) {
- if (repaintSearch) {
- update();
- }
- return;
- }
- const auto repaintRecent = (setId == RecentEmojiSectionSetId());
- enumerateSections([&](const SectionInfo &info) {
- const auto repaint1 = repaintRecent
- && (info.section == int(Section::Recent));
- const auto repaint2 = !repaint1
- && (info.section >= _staticCount)
- && (setId == _custom[info.section - _staticCount].id);
- if (repaint1 || repaint2) {
- update(
- 0,
- info.rowsTop,
- width(),
- info.rowsBottom - info.rowsTop);
- }
- return true;
- });
- }
- rpl::producer<EmojiChosen> EmojiListWidget::chosen() const {
- return _chosen.events();
- }
- rpl::producer<FileChosen> EmojiListWidget::customChosen() const {
- return _customChosen.events();
- }
- rpl::producer<> EmojiListWidget::jumpedToPremium() const {
- return _jumpedToPremium.events();
- }
- rpl::producer<> EmojiListWidget::escapes() const {
- return _search ? _search->escapes() : rpl::never<>();
- }
- void EmojiListWidget::prepareExpanding() {
- if (_search) {
- _searchExpandCache = _search->grab();
- }
- }
- void EmojiListWidget::paintExpanding(
- Painter &p,
- QRect clip,
- int finalBottom,
- float64 geometryProgress,
- float64 fullProgress,
- RectPart origin) {
- const auto searchShift = _search
- ? anim::interpolate(
- st().padding.top() - _search->height(),
- 0,
- geometryProgress)
- : 0;
- const auto shift = clip.topLeft() + QPoint(0, searchShift);
- const auto adjusted = clip.translated(-shift);
- const auto finalHeight = (finalBottom - clip.y());
- if (!_searchExpandCache.isNull()) {
- p.setClipRect(clip);
- p.drawImage(
- clip.x() + st().searchMargin.left(),
- clip.y() + st().searchMargin.top() + searchShift,
- _searchExpandCache);
- }
- p.translate(shift);
- p.setClipRect(adjusted);
- paint(p, ExpandingContext{
- .progress = fullProgress,
- .finalHeight = finalHeight,
- .expanding = true,
- }, adjusted);
- p.translate(-shift);
- }
- void EmojiListWidget::visibleTopBottomUpdated(
- int visibleTop,
- int visibleBottom) {
- Inner::visibleTopBottomUpdated(visibleTop, visibleBottom);
- if (_footer) {
- _footer->validateSelectedIcon(
- currentSet(visibleTop),
- ValidateIconAnimations::Full);
- }
- unloadNotSeenCustom(visibleTop, visibleBottom);
- }
- void EmojiListWidget::unloadNotSeenCustom(
- int visibleTop,
- int visibleBottom) {
- enumerateSections([&](const SectionInfo &info) {
- if (info.rowsBottom <= visibleTop || info.rowsTop >= visibleBottom) {
- unloadCustomIn(info);
- }
- return true;
- });
- }
- void EmojiListWidget::unloadAllCustom() {
- enumerateSections([&](const SectionInfo &info) {
- unloadCustomIn(info);
- return true;
- });
- }
- void EmojiListWidget::unloadCustomIn(const SectionInfo &info) {
- if (!info.section && _recentPainted) {
- _recentPainted = false;
- for (const auto &single : _recent) {
- if (const auto custom = single.custom) {
- custom->unload();
- }
- }
- return;
- } else if (info.section < _staticCount) {
- return;
- }
- auto &custom = _custom[info.section - _staticCount];
- if (!custom.painted) {
- return;
- }
- custom.painted = false;
- for (const auto &single : custom.list) {
- single.custom->unload();
- }
- }
- object_ptr<TabbedSelector::InnerFooter> EmojiListWidget::createFooter() {
- Expects(_footer == nullptr);
- if (_mode == EmojiListMode::RecentReactions
- || _mode == EmojiListMode::MessageEffects) {
- return { nullptr };
- }
- using FooterDescriptor = StickersListFooter::Descriptor;
- const auto flag = powerSavingFlag();
- const auto footerPaused = [method = pausedMethod(), flag]() {
- return On(flag) || method();
- };
- auto result = object_ptr<StickersListFooter>(FooterDescriptor{
- .session = &session(),
- .customTextColor = _customTextColor,
- .paused = footerPaused,
- .parent = this,
- .st = &st(),
- .features = { .stickersSettings = false },
- .forceFirstFrame = (_mode == Mode::BackgroundEmoji),
- });
- _footer = result;
- _footer->setChosen(
- ) | rpl::start_with_next([=](uint64 setId) {
- showSet(setId);
- }, _footer->lifetime());
- return result;
- }
- void EmojiListWidget::afterShown() {
- const auto steal = (_mode == Mode::EmojiStatus)
- || (_mode == Mode::FullReactions)
- || (_mode == Mode::UserpicBuilder);
- if (_search && steal) {
- _search->stealFocus();
- }
- }
- void EmojiListWidget::beforeHiding() {
- if (_search) {
- _search->returnFocus();
- }
- }
- template <typename Callback>
- bool EmojiListWidget::enumerateSections(Callback callback) const {
- Expects(_columnCount > 0);
- auto i = 0;
- auto info = SectionInfo();
- const auto next = [&] {
- info.rowsCount = info.collapsed
- ? kCollapsedRows
- : (info.count + _columnCount - 1) / _columnCount;
- info.rowsTop = info.top
- + (i == 0 ? _rowsTop : st().header);
- info.rowsBottom = info.rowsTop
- + (info.rowsCount * _singleSize.height());
- if (!callback(info)) {
- return false;
- }
- info.top = info.rowsBottom;
- return true;
- };
- if (_searchMode) {
- info.section = i;
- info.count = _searchResults.size();
- return next();
- }
- for (; i != _staticCount; ++i) {
- info.section = i;
- info.count = i ? _counts[i] : _recent.size();
- if (!next()) {
- return false;
- }
- }
- for (auto §ion : _custom) {
- info.section = i++;
- info.premiumRequired = section.premiumRequired;
- info.count = int(section.list.size());
- info.collapsed = !section.expanded
- && (!section.canRemove || section.premiumRequired)
- && (info.count > _columnCount * kCollapsedRows);
- if (!next()) {
- return false;
- }
- }
- return true;
- }
- EmojiListWidget::SectionInfo EmojiListWidget::sectionInfo(int section) const {
- Expects(section >= 0 && section < sectionsCount());
- auto result = SectionInfo();
- enumerateSections([&](const SectionInfo &info) {
- if (info.section == section) {
- result = info;
- return false;
- }
- return true;
- });
- return result;
- }
- EmojiListWidget::SectionInfo EmojiListWidget::sectionInfoByOffset(
- int yOffset) const {
- auto result = SectionInfo();
- const auto count = sectionsCount();
- enumerateSections([&result, count, yOffset](const SectionInfo &info) {
- if (yOffset < info.rowsBottom || info.section == count - 1) {
- result = info;
- return false;
- }
- return true;
- });
- return result;
- }
- int EmojiListWidget::sectionsCount() const {
- return _searchMode ? 1 : (_staticCount + int(_custom.size()));
- }
- void EmojiListWidget::setSingleSize(QSize size) {
- const auto area = st::emojiPanArea;
- _singleSize = size;
- _areaPosition = QPoint(
- (_singleSize.width() - area.width()) / 2,
- (_singleSize.height() - area.height()) / 2);
- const auto esize = Ui::Emoji::GetSizeLarge() / style::DevicePixelRatio();
- _innerPosition = QPoint(
- (area.width() - esize) / 2,
- (area.height() - esize) / 2);
- const auto customSkip = (esize - _customSingleSize) / 2;
- _customPosition = QPoint(customSkip, customSkip);
- _picker->setSingleSize(_singleSize);
- }
- void EmojiListWidget::setColorAllForceRippled(bool force) {
- _colorAllRippleForced = force;
- if (_colorAllRippleForced) {
- _colorAllRippleForcedLifetime = style::PaletteChanged(
- ) | rpl::filter([=] {
- return _colorAllRipple != nullptr;
- }) | rpl::start_with_next([=] {
- _colorAllRipple->forceRepaint();
- });
- if (!_colorAllRipple) {
- _colorAllRipple = createButtonRipple(int(Section::People));
- }
- if (_colorAllRipple->empty()) {
- _colorAllRipple->addFading();
- } else {
- _colorAllRipple->lastUnstop();
- }
- } else {
- if (_colorAllRipple) {
- _colorAllRipple->lastStop();
- }
- _colorAllRippleForcedLifetime.destroy();
- }
- }
- int EmojiListWidget::countDesiredHeight(int newWidth) {
- const auto fullWidth = st().margin.left()
- + newWidth
- + st().margin.right();
- const auto padding = st().padding;
- const auto innerWidth = fullWidth - padding.left() - padding.right();
- _columnCount = std::max(innerWidth / st().desiredSize, 1);
- const auto singleWidth = innerWidth / _columnCount;
- _rowsTop = _search ? _search->height() : padding.top();
- _rowsLeft = padding.left()
- + (innerWidth - _columnCount * singleWidth) / 2
- - st().margin.left();
- setSingleSize({ singleWidth, singleWidth - 2 * st().verticalSizeSub });
- const auto countResult = [this](int minimalLastHeight) {
- const auto info = sectionInfo(sectionsCount() - 1);
- return info.top
- + qMax(info.rowsBottom - info.top, minimalLastHeight);
- };
- const auto minimalHeight = this->minimalHeight();
- const auto minimalLastHeight = std::max(
- minimalHeight - padding.bottom(),
- 0);
- const auto result = countResult(minimalLastHeight);
- return result
- ? qMax(minimalHeight, result + padding.bottom())
- : 0;
- }
- int EmojiListWidget::defaultMinimalHeight() const {
- return Inner::defaultMinimalHeight();
- }
- void EmojiListWidget::ensureLoaded(int section) {
- Expects(section >= 0 && section < sectionsCount());
- if (section == int(Section::Recent)) {
- if (_recent.empty()) {
- fillRecent();
- }
- return;
- } else if (section >= _staticCount || !_emoji[section].empty()) {
- return;
- }
- _emoji[section] = Ui::Emoji::GetSection(static_cast<Section>(section));
- _counts[section] = _emoji[section].size();
- const auto &settings = Core::App().settings();
- for (auto &emoji : _emoji[section]) {
- emoji = settings.lookupEmojiVariant(emoji);
- }
- }
- void EmojiListWidget::fillRecent() {
- if (_mode != Mode::Full) {
- return;
- }
- _recent.clear();
- _recentCustomIds.clear();
- const auto &list = Core::App().settings().recentEmoji();
- _recent.reserve(std::min(int(list.size()), Core::kRecentEmojiLimit) + 1);
- const auto test = session().isTestMode();
- for (const auto &one : list) {
- const auto document = std::get_if<RecentEmojiDocument>(&one.id.data);
- if (document && ((document->test != test) || _onlyUnicodeEmoji)) {
- continue;
- }
- _recent.push_back({
- .custom = resolveCustomRecent(one.id),
- .id = one.id,
- });
- if (document) {
- _recentCustomIds.emplace(document->id);
- }
- if (_recent.size() >= Core::kRecentEmojiLimit) {
- break;
- }
- }
- }
- void EmojiListWidget::fillRecentFrom(
- const std::vector<EmojiStatusId> &list) {
- const auto test = session().isTestMode();
- _recent.clear();
- _recent.reserve(list.size());
- for (const auto &id : list) {
- if (!id && _mode == Mode::EmojiStatus) {
- const auto star = QString::fromUtf8("\xe2\xad\x90\xef\xb8\x8f");
- _recent.push_back({ .id = { Ui::Emoji::Find(star) } });
- } else if (!id
- && (_mode == Mode::BackgroundEmoji
- || _mode == Mode::ChannelStatus)) {
- const auto fakeId = DocumentId(5246772116543512028ULL);
- const auto no = QString::fromUtf8("\xe2\x9b\x94\xef\xb8\x8f");
- _recent.push_back({
- .custom = resolveCustomRecent(fakeId),
- .id = { Ui::Emoji::Find(no) },
- });
- _recentCustomIds.emplace(fakeId);
- } else {
- _recent.push_back({
- .collectible = id.collectible,
- .custom = resolveCustomRecent(id),
- .id = {
- RecentEmojiDocument{ .id = id.documentId, .test = test },
- },
- });
- _recentCustomIds.emplace(id.collectible
- ? id.collectible->documentId
- : id.documentId);
- }
- }
- }
- base::unique_qptr<Ui::PopupMenu> EmojiListWidget::fillContextMenu(
- const SendMenu::Details &details) {
- if (v::is_null(_selected)) {
- return nullptr;
- }
- const auto over = std::get_if<OverEmoji>(&_selected);
- if (!over) {
- return nullptr;
- }
- const auto section = over->section;
- const auto index = over->index;
- auto menu = base::make_unique_q<Ui::PopupMenu>(
- this,
- (_mode == Mode::Full
- ? st::popupMenuWithIcons
- : st::defaultPopupMenu));
- if (_mode == Mode::Full) {
- fillRecentMenu(menu, section, index);
- } else if (_mode == Mode::EmojiStatus || _mode == Mode::ChannelStatus) {
- fillEmojiStatusMenu(menu, section, index);
- }
- if (menu->empty()) {
- return nullptr;
- }
- return menu;
- }
- void EmojiListWidget::fillRecentMenu(
- not_null<Ui::PopupMenu*> menu,
- int section,
- int index) {
- const auto recent = (section == int(Section::Recent));
- const auto addAction = Ui::Menu::CreateAddActionCallback(menu);
- const auto over = OverEmoji{ section, index };
- const auto emoji = lookupOverEmoji(&over);
- const auto custom = lookupCustomEmoji(&over);
- if (custom.collectible) {
- return;
- }
- const auto document = custom.document;
- if (document && document->sticker()) {
- const auto sticker = document->sticker();
- const auto emoji = sticker->alt;
- const auto setId = sticker->set.id;
- if (!emoji.isEmpty()) {
- auto data = TextForMimeData{ emoji, { emoji } };
- data.rich.entities.push_back({
- EntityType::CustomEmoji,
- 0,
- int(emoji.size()),
- Data::SerializeCustomEmojiId(document)
- });
- addAction(tr::lng_emoji_copy(tr::now), [=] {
- TextUtilities::SetClipboardText(data);
- }, &st::menuIconCopy);
- }
- if (recent && setId && _features.openStickerSets) {
- addAction(
- tr::lng_emoji_view_pack(tr::now),
- crl::guard(this, [=] { displaySet(setId); }),
- &st::menuIconShowAll);
- }
- } else if (recent && emoji) {
- addAction(tr::lng_emoji_copy(tr::now), [=] {
- const auto text = emoji->text();
- TextUtilities::SetClipboardText({ text, { text } });
- }, &st::menuIconCopy);
- }
- if (!recent) {
- return;
- }
- auto id = RecentEmojiId{ emoji };
- if (custom) {
- id.data = RecentEmojiDocument{
- .id = custom.document->id,
- .test = custom.document->session().isTestMode(),
- };
- }
- addAction(tr::lng_emoji_remove_recent(tr::now), crl::guard(this, [=] {
- Core::App().settings().hideRecentEmoji(id);
- refreshRecent();
- }), &st::menuIconCancel);
- menu->addSeparator(&st().expandedSeparator);
- const auto resetRecent = [=] {
- const auto sure = [=](Fn<void()> &&close) {
- Core::App().settings().resetRecentEmoji();
- refreshRecent();
- close();
- };
- checkHideWithBox(Ui::MakeConfirmBox({
- .text = tr::lng_emoji_reset_recent_sure(),
- .confirmed = crl::guard(this, sure),
- .confirmText = tr::lng_emoji_reset_recent_button(tr::now),
- .labelStyle = &st().boxLabel,
- }));
- };
- addAction({
- .text = tr::lng_emoji_reset_recent(tr::now),
- .handler = crl::guard(this, resetRecent),
- .icon = &st::menuIconRestoreAttention,
- .isAttention = true,
- });
- }
- void EmojiListWidget::fillEmojiStatusMenu(
- not_null<Ui::PopupMenu*> menu,
- int section,
- int index) {
- const auto chosen = lookupCustomEmoji(index, section);
- if (!chosen) {
- return;
- }
- const auto selectWith = [=](TimeId scheduled) {
- selectCustom(
- lookupChosen(chosen, nullptr, { .scheduled = scheduled }));
- };
- for (const auto &value : { 3600, 3600 * 8, 3600 * 24, 3600 * 24 * 7 }) {
- const auto text = tr::lng_emoji_status_menu_duration_any(
- tr::now,
- lt_duration,
- Ui::FormatMuteFor(value));
- menu->addAction(text, crl::guard(this, [=] {
- selectWith(base::unixtime::now() + value);
- }));
- }
- menu->addAction(
- tr::lng_manage_messages_ttl_after_custom(tr::now),
- crl::guard(this, [=] { selectWith(
- TabbedSelector::kPickCustomTimeId); }));
- }
- void EmojiListWidget::paintEvent(QPaintEvent *e) {
- auto p = Painter(this);
- const auto clip = e ? e->rect() : rect();
- _repaintsScheduled.clear();
- if (_grabbingChosen) {
- p.setCompositionMode(QPainter::CompositionMode_Source);
- p.fillRect(clip, Qt::transparent);
- p.setCompositionMode(QPainter::CompositionMode_SourceOver);
- } else if (st().bg->c.alpha() > 0) {
- p.fillRect(clip, st().bg);
- }
- if (!_searchExpandCache.isNull()) {
- _searchExpandCache = QImage();
- }
- paint(p, {}, clip);
- }
- void EmojiListWidget::validateEmojiPaintContext(
- const ExpandingContext &context) {
- auto value = Ui::Text::CustomEmojiPaintContext{
- .textColor = (_customTextColor
- ? _customTextColor()
- : (_mode == Mode::EmojiStatus || _mode == Mode::ChannelStatus)
- ? anim::color(
- st::stickerPanPremium1,
- st::stickerPanPremium2,
- 0.5)
- : st().textFg->c),
- .size = QSize(_customSingleSize, _customSingleSize),
- .now = crl::now(),
- .scale = context.progress,
- .paused = On(powerSavingFlag()) || paused(),
- .scaled = context.expanding,
- .internal = { .forceFirstFrame = (_mode == Mode::BackgroundEmoji) },
- };
- if (!_emojiPaintContext) {
- _emojiPaintContext = std::make_unique<
- Ui::Text::CustomEmojiPaintContext
- >(std::move(value));
- } else {
- *_emojiPaintContext = std::move(value);
- }
- }
- void EmojiListWidget::paint(
- Painter &p,
- ExpandingContext context,
- QRect clip) {
- validateEmojiPaintContext(context);
- _paintAsPremium = session().premium();
- auto fromColumn = floorclamp(
- clip.x() - _rowsLeft,
- _singleSize.width(),
- 0,
- _columnCount);
- auto toColumn = ceilclamp(
- clip.x() + clip.width() - _rowsLeft,
- _singleSize.width(),
- 0,
- _columnCount);
- if (rtl()) {
- qSwap(fromColumn, toColumn);
- fromColumn = _columnCount - fromColumn;
- toColumn = _columnCount - toColumn;
- }
- const auto expandProgress = context.progress;
- auto selectedButton = std::get_if<OverButton>(!v::is_null(_pressed)
- ? &_pressed
- : &_selected);
- if (_searchResults.empty() && _searchMode) {
- paintEmptySearchResults(p);
- }
- enumerateSections([&](const SectionInfo &info) {
- if (clip.top() >= info.rowsBottom) {
- return true;
- } else if (clip.top() + clip.height() <= info.top) {
- return false;
- }
- const auto buttonSelected = selectedButton
- ? (selectedButton->section == info.section)
- : false;
- const auto titleLeft = (info.premiumRequired
- ? st().headerLockedLeft
- : st().headerLeft) - st().margin.left();
- const auto widthForTitle = emojiRight()
- - titleLeft
- - paintButtonGetWidth(p, info, buttonSelected, clip);
- if (info.section > 0 && clip.top() < info.rowsTop) {
- p.setFont(st::emojiPanHeaderFont);
- p.setPen(st().headerFg);
- auto titleText = (info.section < _staticCount)
- ? ChatHelpers::EmojiCategoryTitle(info.section)(tr::now)
- : _custom[info.section - _staticCount].title;
- auto titleWidth = st::emojiPanHeaderFont->width(titleText);
- if (titleWidth > widthForTitle) {
- titleText = st::emojiPanHeaderFont->elided(titleText, widthForTitle);
- titleWidth = st::emojiPanHeaderFont->width(titleText);
- }
- const auto top = info.top + st().headerTop;
- if (info.premiumRequired) {
- st::emojiPremiumRequired.paint(
- p,
- st().headerLockLeft - st().margin.left(),
- top,
- width());
- }
- const auto textBaseline = top + st::emojiPanHeaderFont->ascent;
- p.setFont(st::emojiPanHeaderFont);
- p.setPen(st().headerFg);
- p.drawText(titleLeft, textBaseline, titleText);
- }
- if (clip.top() + clip.height() > info.rowsTop) {
- ensureLoaded(info.section);
- auto fromRow = floorclamp(
- clip.y() - info.rowsTop,
- _singleSize.height(),
- 0,
- info.rowsCount);
- auto toRow = ceilclamp(
- clip.y() + clip.height() - info.rowsTop,
- _singleSize.height(),
- 0,
- info.rowsCount);
- for (auto i = fromRow; i < toRow; ++i) {
- for (auto j = fromColumn; j < toColumn; ++j) {
- const auto index = i * _columnCount + j;
- if (index >= info.count) {
- break;
- }
- const auto state = OverEmoji{
- .section = info.section,
- .index = index,
- };
- const auto selected = (state == _selected)
- || (!_picker->isHidden()
- && state == _pickerSelected);
- const auto position = QPoint(
- _rowsLeft + j * _singleSize.width(),
- info.rowsTop + i * _singleSize.height()
- );
- const auto w = position + _areaPosition;
- if (context.expanding) {
- const auto y = (position.y() - _rowsTop);
- const auto x = (position.x() - _rowsLeft);
- const auto sum = y
- + std::max(std::min(y, width()) - x, 0);
- const auto maxSum = context.finalHeight
- + std::min(context.finalHeight, width());
- const auto started = (sum / float64(maxSum))
- - kAppearDuration;
- context.progress = (expandProgress <= started)
- ? 0.
- : (expandProgress >= started + kAppearDuration)
- ? 1.
- : ((expandProgress - started) / kAppearDuration);
- }
- if (info.collapsed
- && index + 1 == _columnCount * kCollapsedRows) {
- drawCollapsedBadge(p, w - _areaPosition, info.count);
- continue;
- }
- if (!_grabbingChosen
- && selected
- && st().overBg->c.alpha() > 0) {
- auto tl = w;
- if (rtl()) {
- tl.setX(width() - tl.x() - st::emojiPanArea.width());
- }
- _overBg.paint(p, QRect(tl, st::emojiPanArea));
- }
- if (_searchMode) {
- drawRecent(p, context, w, _searchResults[index]);
- } else if (info.section == int(Section::Recent)) {
- drawRecent(p, context, w, _recent[index]);
- } else if (info.section < _staticCount) {
- drawEmoji(p, context, w, _emoji[info.section][index]);
- } else {
- const auto set = info.section - _staticCount;
- drawCustom(p, context, w, set, index);
- }
- }
- }
- }
- return true;
- });
- }
- void EmojiListWidget::drawCollapsedBadge(
- QPainter &p,
- QPoint position,
- int count) {
- const auto &st = st::emojiPanExpand;
- const auto text = u"+%1"_q.arg(count - _columnCount * kCollapsedRows + 1);
- const auto textWidth = st.style.font->width(text);
- const auto buttonw = std::max(textWidth - st.width, st.height);
- const auto buttonh = st.height;
- const auto buttonx = position.x() + (_singleSize.width() - buttonw) / 2;
- const auto buttony = position.y() + (_singleSize.height() - buttonh) / 2;
- _collapsedBg.paint(p, QRect(buttonx, buttony, buttonw, buttonh));
- p.setPen(this->st().bg);
- p.setFont(st.style.font);
- p.drawText(
- buttonx + (buttonw - textWidth) / 2,
- (buttony + st.textTop + st.style.font->ascent),
- text);
- }
- void EmojiListWidget::drawRecent(
- QPainter &p,
- const ExpandingContext &context,
- QPoint position,
- const RecentOne &recent) {
- _recentPainted = true;
- const auto locked = (_mode == Mode::MessageEffects)
- && !_paintAsPremium
- && v::is<RecentEmojiDocument>(recent.id.data)
- && !_freeEffects.contains(
- v::get<RecentEmojiDocument>(recent.id.data).id);
- auto lockedPainted = false;
- if (locked) {
- if (_premiumMarkFrameCache.isNull()) {
- const auto ratio = style::DevicePixelRatio();
- _premiumMarkFrameCache = QImage(
- QSize(_customSingleSize, _customSingleSize) * ratio,
- QImage::Format_ARGB32_Premultiplied);
- _premiumMarkFrameCache.setDevicePixelRatio(ratio);
- }
- _premiumMarkFrameCache.fill(Qt::transparent);
- }
- if (const auto custom = recent.custom) {
- const auto exactPosition = position
- + _innerPosition
- + _customPosition;
- _emojiPaintContext->scale = context.progress;
- if (_mode == Mode::ChannelStatus) {
- _emojiPaintContext->internal.forceFirstFrame
- = (recent.id == _recent.front().id);
- }
- if (locked) {
- lockedPainted = custom->ready();
- auto q = Painter(&_premiumMarkFrameCache);
- _emojiPaintContext->position = QPoint();
- custom->paint(q, *_emojiPaintContext);
- q.end();
- p.drawImage(exactPosition, _premiumMarkFrameCache);
- } else {
- _emojiPaintContext->position = exactPosition;
- custom->paint(p, *_emojiPaintContext);
- }
- } else if (const auto emoji = std::get_if<EmojiPtr>(&recent.id.data)) {
- if (_mode == Mode::EmojiStatus) {
- position += QPoint(
- (_singleSize.width() - st::emojiStatusDefault.width()) / 2,
- (_singleSize.height() - st::emojiStatusDefault.height()) / 2
- ) - _areaPosition;
- p.drawImage(position, _premiumIcon->image());
- } else {
- drawEmoji(p, context, position, *emoji);
- }
- } else {
- Unexpected("Empty custom emoji in EmojiListWidget::drawRecent.");
- }
- if (locked) {
- _premiumMark->paint(
- p,
- lockedPainted ? _premiumMarkFrameCache : QImage(),
- recent.premiumLock,
- position,
- _singleSize,
- width());
- }
- }
- void EmojiListWidget::drawEmoji(
- QPainter &p,
- const ExpandingContext &context,
- QPoint position,
- EmojiPtr emoji) {
- position += _innerPosition;
- Ui::Emoji::Draw(
- p,
- emoji,
- Ui::Emoji::GetSizeLarge(),
- position.x(),
- position.y());
- }
- void EmojiListWidget::drawCustom(
- QPainter &p,
- const ExpandingContext &context,
- QPoint position,
- int set,
- int index) {
- auto &custom = _custom[set];
- custom.painted = true;
- auto &entry = custom.list[index];
- _emojiPaintContext->scale = context.progress;
- _emojiPaintContext->position = position
- + _innerPosition
- + _customPosition;
- entry.custom->paint(p, *_emojiPaintContext);
- }
- bool EmojiListWidget::checkPickerHide() {
- if (!_picker->isHidden() && !v::is_null(_pickerSelected)) {
- _picker->hideAnimated();
- _pickerSelected = v::null;
- updateSelected();
- return true;
- }
- return false;
- }
- EmojiListWidget::ResolvedCustom EmojiListWidget::lookupCustomEmoji(
- const OverEmoji *over) const {
- return over
- ? lookupCustomEmoji(over->index, over->section)
- : ResolvedCustom();
- }
- EmojiListWidget::ResolvedCustom EmojiListWidget::lookupCustomEmoji(
- int index,
- int section) const {
- if (_searchMode) {
- if (index < _searchResults.size()) {
- const auto document = std::get_if<RecentEmojiDocument>(
- &_searchResults[index].id.data);
- if (document) {
- return { session().data().document(document->id) };
- }
- }
- return {};
- } else if (section == int(Section::Recent) && index < _recent.size()) {
- const auto &recent = _recent[index];
- if (recent.collectible) {
- return {
- session().data().document(recent.collectible->documentId),
- recent.collectible,
- };
- }
- const auto document = std::get_if<RecentEmojiDocument>(
- &recent.id.data);
- if (document) {
- return { session().data().document(document->id) };
- }
- } else if (section >= _staticCount
- && index < _custom[section - _staticCount].list.size()) {
- auto &set = _custom[section - _staticCount];
- auto &entry = set.list[index];
- return { entry.document, entry.collectible };
- }
- return {};
- }
- EmojiPtr EmojiListWidget::lookupOverEmoji(const OverEmoji *over) const {
- const auto section = over ? over->section : -1;
- const auto index = over ? over->index : -1;
- return _searchMode
- ? ((index < _searchResults.size()
- && v::is<EmojiPtr>(_searchResults[index].id.data))
- ? v::get<EmojiPtr>(_searchResults[index].id.data)
- : nullptr)
- : (section == int(Section::Recent)
- && index < _recent.size()
- && v::is<EmojiPtr>(_recent[index].id.data))
- ? v::get<EmojiPtr>(_recent[index].id.data)
- : (section > int(Section::Recent)
- && section < _staticCount
- && index < _emoji[section].size())
- ? _emoji[section][index]
- : nullptr;
- }
- EmojiChosen EmojiListWidget::lookupChosen(
- EmojiPtr emoji,
- not_null<const OverEmoji*> over) {
- const auto rect = emojiRect(over->section, over->index);
- const auto size = st::emojiStatusDefault.size();
- const auto icon = QRect(
- rect.x() + (_singleSize.width() - size.width()) / 2,
- rect.y() + (_singleSize.height() - size.height()) / 2,
- rect.width(),
- rect.height());
- return {
- .emoji = emoji,
- .messageSendingFrom = {
- .type = Ui::MessageSendingAnimationFrom::Type::Emoji,
- .globalStartGeometry = mapToGlobal(icon),
- },
- };
- }
- FileChosen EmojiListWidget::lookupChosen(
- ResolvedCustom custom,
- const OverEmoji *over,
- Api::SendOptions options) {
- Expects(custom.document != nullptr);
- _grabbingChosen = true;
- const auto guard = gsl::finally([&] { _grabbingChosen = false; });
- const auto rect = over ? emojiRect(over->section, over->index) : QRect();
- const auto emoji = over ? QRect(
- rect.topLeft() + _areaPosition + _innerPosition + _customPosition,
- QSize(_customSingleSize, _customSingleSize)
- ) : QRect();
- return {
- .document = custom.document,
- .options = options,
- .messageSendingFrom = {
- .type = Ui::MessageSendingAnimationFrom::Type::Emoji,
- .globalStartGeometry = over ? mapToGlobal(emoji) : QRect(),
- .frame = over ? Ui::GrabWidgetToImage(this, emoji) : QImage(),
- },
- .collectible = custom.collectible,
- };
- }
- void EmojiListWidget::mousePressEvent(QMouseEvent *e) {
- _lastMousePos = e->globalPos();
- updateSelected();
- if (checkPickerHide() || e->button() != Qt::LeftButton) {
- return;
- }
- setPressed(_selected);
- if (const auto over = std::get_if<OverEmoji>(&_selected)) {
- const auto emoji = lookupOverEmoji(over);
- if (emoji && emoji->hasVariants()) {
- _pickerSelected = _selected;
- setCursor(style::cur_default);
- if (!Core::App().settings().hasChosenEmojiVariant(emoji)) {
- showPicker();
- } else {
- _previewTimer.cancel();
- _showPickerTimer.callOnce(kColorPickerDelay);
- }
- } else if (lookupCustomEmoji(over)) {
- _showPickerTimer.cancel();
- _previewTimer.callOnce(QApplication::startDragTime());
- }
- }
- }
- void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) {
- _previewTimer.cancel();
- auto pressed = _pressed;
- setPressed(v::null);
- _lastMousePos = e->globalPos();
- if (!_picker->isHidden()) {
- if (_picker->rect().contains(_picker->mapFromGlobal(_lastMousePos))) {
- return _picker->handleMouseRelease(QCursor::pos());
- } else if (const auto over = std::get_if<OverEmoji>(&_pickerSelected)) {
- const auto emoji = lookupOverEmoji(over);
- if (emoji
- && emoji->hasVariants()
- && Core::App().settings().hasChosenEmojiVariant(emoji)) {
- _picker->hideAnimated();
- _pickerSelected = v::null;
- }
- }
- }
- updateSelected();
- if (_showPickerTimer.isActive()) {
- _showPickerTimer.cancel();
- _pickerSelected = v::null;
- _picker->hide();
- }
- if (_previewShown) {
- _previewShown = false;
- return;
- } else if (v::is_null(_selected) || _selected != pressed) {
- return;
- }
- if (const auto over = std::get_if<OverEmoji>(&_selected)) {
- const auto section = over->section;
- const auto index = over->index;
- if (section >= _staticCount
- && sectionInfo(section).collapsed
- && index + 1 == _columnCount * kCollapsedRows) {
- _custom[section - _staticCount].expanded = true;
- resizeToWidth(width());
- update();
- return;
- } else if (const auto emoji = lookupOverEmoji(over)) {
- if (emoji->hasVariants() && !_picker->isHidden()) {
- return;
- }
- selectEmoji(lookupChosen(emoji, over));
- } else if (const auto custom = lookupCustomEmoji(over)) {
- selectCustom(lookupChosen(custom, over));
- }
- } else if (const auto set = std::get_if<OverSet>(&pressed)) {
- Assert(set->section >= _staticCount
- && set->section < _staticCount + _custom.size());
- displaySet(_custom[set->section - _staticCount].id);
- } else if (auto button = std::get_if<OverButton>(&pressed)) {
- Assert(hasButton(button->section));
- const auto id = hasColorButton(button->section)
- ? 0
- : _custom[button->section - _staticCount].id;
- if (hasColorButton(button->section)) {
- _pickerSelected = pressed;
- showPicker();
- } else if (hasRemoveButton(button->section)) {
- removeSet(id);
- } else if (hasAddButton(button->section)) {
- _localSetsManager->install(id);
- } else if (const auto resolved = _show->resolveWindow()) {
- _jumpedToPremium.fire({});
- switch (_mode) {
- case Mode::Full:
- case Mode::UserpicBuilder:
- Settings::ShowPremium(resolved, u"animated_emoji"_q);
- break;
- case Mode::FullReactions:
- case Mode::RecentReactions:
- Settings::ShowPremium(resolved, u"infinite_reactions"_q);
- break;
- case Mode::EmojiStatus:
- case Mode::ChannelStatus:
- Settings::ShowPremium(resolved, u"emoji_status"_q);
- break;
- case Mode::TopicIcon:
- Settings::ShowPremium(resolved, u"forum_topic_icon"_q);
- break;
- case Mode::BackgroundEmoji:
- Settings::ShowPremium(resolved, u"name_color"_q);
- break;
- }
- }
- }
- }
- void EmojiListWidget::displaySet(uint64 setId) {
- if (setId == Data::Stickers::MegagroupSetId) {
- if (_megagroupSet->mgInfo->emojiSet.id) {
- setId = _megagroupSet->mgInfo->emojiSet.id;
- } else {
- return;
- }
- } else if (setId == Data::Stickers::CollectibleSetId) {
- return;
- }
- const auto &sets = session().data().stickers().sets();
- auto it = sets.find(setId);
- if (it != sets.cend()) {
- checkHideWithBox(Box<StickerSetBox>(_show, it->second.get()));
- }
- }
- void EmojiListWidget::removeMegagroupSet(bool locally) {
- if (locally) {
- session().settings().setGroupEmojiSectionHidden(_megagroupSet->id);
- session().saveSettings();
- refreshCustom();
- return;
- }
- checkHideWithBox(Ui::MakeConfirmBox({
- .text = tr::lng_emoji_remove_group_set(),
- .confirmed = crl::guard(this, [this, group = _megagroupSet](
- Fn<void()> &&close) {
- Expects(group->mgInfo != nullptr);
- if (group->mgInfo->emojiSet) {
- session().api().setGroupEmojiSet(group, {});
- }
- close();
- }),
- .cancelled = [](Fn<void()> &&close) { close(); },
- .labelStyle = &st().boxLabel,
- }));
- }
- void EmojiListWidget::removeSet(uint64 setId) {
- const auto &labelSt = st().boxLabel;
- if (setId == Data::Stickers::MegagroupSetId) {
- const auto i = ranges::find(_custom, setId, &CustomSet::id);
- Assert(i != end(_custom));
- const auto removeLocally = !_megagroupSet->canEditEmoji();
- removeMegagroupSet(removeLocally);
- } else if (setId == Data::Stickers::CollectibleSetId) {
- } else if (auto box = MakeConfirmRemoveSetBox(&session(), labelSt, setId)) {
- checkHideWithBox(std::move(box));
- }
- }
- void EmojiListWidget::selectEmoji(EmojiChosen data) {
- Core::App().settings().incrementRecentEmoji({ data.emoji });
- _chosen.fire(std::move(data));
- }
- void EmojiListWidget::selectCustom(FileChosen data) {
- const auto document = data.document;
- const auto skip = (document->isPremiumEmoji() && !session().premium());
- if (!skip && _mode == Mode::Full) {
- auto &settings = Core::App().settings();
- settings.incrementRecentEmoji({ RecentEmojiDocument{
- document->id,
- document->session().isTestMode(),
- } });
- }
- _customChosen.fire(std::move(data));
- }
- void EmojiListWidget::showPicker() {
- if (v::is_null(_pickerSelected)) {
- return;
- }
- const auto showAt = [&](float64 xCoef, int y, int height) {
- y -= _picker->height() - st::emojiPanRadius + getVisibleTop();
- if (y < st().header) {
- y += _picker->height() + height;
- }
- auto xmax = width() - _picker->width();
- if (rtl()) xCoef = 1. - xCoef;
- _picker->move(qRound(xmax * xCoef), y);
- disableScroll(true);
- };
- if (const auto button = std::get_if<OverButton>(&_pickerSelected)) {
- const auto hand = QString::fromUtf8("\xF0\x9F\x91\x8B");
- const auto emoji = Ui::Emoji::Find(hand);
- Assert(emoji != nullptr && emoji->hasVariants());
- _picker->showEmoji(emoji, true);
- setColorAllForceRippled(true);
- const auto rect = buttonRect(button->section);
- showAt(1., rect.y(), rect.height() - 2 * st::emojiPanRadius);
- } else if (const auto over = std::get_if<OverEmoji>(&_pickerSelected)) {
- const auto emoji = lookupOverEmoji(over);
- if (emoji && emoji->hasVariants()) {
- _picker->showEmoji(emoji);
- const auto coef = float64(over->index % _columnCount)
- / float64(_columnCount - 1);
- const auto h = _singleSize.height() - 2 * st::emojiPanRadius;
- showAt(coef, emojiRect(over->section, over->index).y(), h);
- }
- }
- }
- void EmojiListWidget::pickerHidden() {
- _pickerSelected = v::null;
- update();
- disableScroll(false);
- setColorAllForceRippled(false);
- _lastMousePos = QCursor::pos();
- updateSelected();
- }
- bool EmojiListWidget::hasColorButton(int index) const {
- return (_staticCount > int(Section::People))
- && (index == int(Section::People));
- }
- QRect EmojiListWidget::colorButtonRect(int index) const {
- return colorButtonRect(sectionInfo(index));
- }
- QRect EmojiListWidget::colorButtonRect(const SectionInfo &info) const {
- if (_mode != Mode::Full) {
- return QRect();
- }
- const auto &colorSt = st().colorAll;
- const auto buttonw = colorSt.rippleAreaPosition.x()
- + colorSt.rippleAreaSize;
- const auto buttonh = colorSt.height;
- const auto buttonx = emojiRight() - st::emojiPanColorAllSkip - buttonw;
- const auto buttony = info.top + st::emojiPanRemoveTop;
- return QRect(buttonx, buttony, buttonw, buttonh);
- }
- bool EmojiListWidget::hasRemoveButton(int index) const {
- if (index < _staticCount
- || index >= _staticCount + _custom.size()) {
- return false;
- }
- const auto &set = _custom[index - _staticCount];
- if (set.id == Data::Stickers::MegagroupSetId) {
- Assert(_megagroupSet != nullptr);
- if (index + 1 != _staticCount + _custom.size()) {
- return true;
- }
- return !set.list.empty() && _megagroupSet->canEditEmoji();
- } else if (set.id == Data::Stickers::CollectibleSetId) {
- return false;
- }
- return set.canRemove && !set.premiumRequired;
- }
- QRect EmojiListWidget::removeButtonRect(int index) const {
- return removeButtonRect(sectionInfo(index));
- }
- QRect EmojiListWidget::removeButtonRect(const SectionInfo &info) const {
- if (_mode != Mode::Full) {
- return QRect();
- }
- const auto &removeSt = st().removeSet;
- const auto buttonw = removeSt.rippleAreaPosition.x()
- + removeSt.rippleAreaSize;
- const auto buttonh = removeSt.height;
- const auto buttonx = emojiRight() - st::emojiPanRemoveSkip - buttonw;
- const auto buttony = info.top + st::emojiPanRemoveTop;
- return QRect(buttonx, buttony, buttonw, buttonh);
- }
- bool EmojiListWidget::hasAddButton(int index) const {
- if (index < _staticCount
- || index >= _staticCount + _custom.size()) {
- return false;
- }
- const auto &set = _custom[index - _staticCount];
- return !set.canRemove
- && !set.premiumRequired
- && set.id != Data::Stickers::MegagroupSetId
- && set.id != Data::Stickers::CollectibleSetId;
- }
- QRect EmojiListWidget::addButtonRect(int index) const {
- return buttonRect(sectionInfo(index), _add);
- }
- bool EmojiListWidget::hasUnlockButton(int index) const {
- if (index < _staticCount
- || index >= _staticCount + _custom.size()) {
- return false;
- }
- const auto &set = _custom[index - _staticCount];
- return set.premiumRequired;
- }
- QRect EmojiListWidget::unlockButtonRect(int index) const {
- Expects(index >= _staticCount
- && index < _staticCount + _custom.size());
- return buttonRect(sectionInfo(index), rightButton(index));
- }
- bool EmojiListWidget::hasButton(int index) const {
- if (hasColorButton(index)) {
- return true;
- } else if (index >= _staticCount
- && index < _staticCount + _custom.size()) {
- const auto &custom = _custom[index - _staticCount];
- return (custom.id != Data::Stickers::CollectibleSetId)
- && ((custom.id != Data::Stickers::MegagroupSetId)
- || custom.canRemove);
- }
- return false;
- }
- QRect EmojiListWidget::buttonRect(int index) const {
- return hasColorButton(index)
- ? colorButtonRect(index)
- : hasRemoveButton(index)
- ? removeButtonRect(index)
- : hasAddButton(index)
- ? addButtonRect(index)
- : unlockButtonRect(index);
- }
- QRect EmojiListWidget::buttonRect(
- const SectionInfo &info,
- const RightButton &button) const {
- const auto buttonw = button.textWidth - st::emojiPanButton.width;
- const auto buttonh = st::emojiPanButton.height;
- const auto buttonx = emojiRight() - buttonw - st::emojiPanButtonRight;
- const auto buttony = info.top + st::emojiPanButtonTop;
- return QRect(buttonx, buttony, buttonw, buttonh);
- }
- auto EmojiListWidget::rightButton(int index) const -> const RightButton & {
- Expects(index >= _staticCount
- && index < _staticCount + _custom.size());
- return hasAddButton(index)
- ? _add
- : _custom[index - _staticCount].canRemove
- ? _restore
- : _unlock;
- }
- int EmojiListWidget::emojiRight() const {
- return emojiLeft() + (_columnCount * _singleSize.width());
- }
- int EmojiListWidget::emojiLeft() const {
- return _rowsLeft;
- }
- QRect EmojiListWidget::emojiRect(int section, int index) const {
- Expects(_columnCount > 0);
- const auto info = sectionInfo(section);
- const auto countTillItem = (index - (index % _columnCount));
- const auto rowsToSkip = (countTillItem / _columnCount)
- + ((countTillItem % _columnCount) ? 1 : 0);
- const auto x = _rowsLeft + ((index % _columnCount) * _singleSize.width());
- const auto y = info.rowsTop + rowsToSkip * _singleSize.height();
- return QRect(x, y, _singleSize.width(), _singleSize.height());
- }
- void EmojiListWidget::colorChosen(EmojiChosen data) {
- Expects(data.emoji != nullptr && data.emoji->hasVariants());
- const auto emoji = data.emoji;
- auto &settings = Core::App().settings();
- if (const auto button = std::get_if<OverButton>(&_pickerSelected)) {
- settings.saveAllEmojiVariants(emoji);
- for (auto section = int(Section::People)
- ; section < _staticCount
- ; ++section) {
- for (auto &emoji : _emoji[section]) {
- emoji = settings.lookupEmojiVariant(emoji);
- }
- }
- update();
- } else {
- settings.saveEmojiVariant(emoji);
- const auto over = std::get_if<OverEmoji>(&_pickerSelected);
- if (over
- && over->section > int(Section::Recent)
- && over->section < _staticCount
- && over->index < _emoji[over->section].size()) {
- _emoji[over->section][over->index] = emoji;
- rtlupdate(emojiRect(over->section, over->index));
- }
- selectEmoji(data);
- }
- _picker->hideAnimated();
- }
- void EmojiListWidget::mouseMoveEvent(QMouseEvent *e) {
- _lastMousePos = e->globalPos();
- if (!_picker->isHidden()) {
- if (_picker->rect().contains(_picker->mapFromGlobal(_lastMousePos))) {
- return _picker->handleMouseMove(QCursor::pos());
- } else {
- _picker->clearSelection();
- }
- }
- updateSelected();
- }
- void EmojiListWidget::leaveEventHook(QEvent *e) {
- clearSelection();
- }
- void EmojiListWidget::leaveToChildEvent(QEvent *e, QWidget *child) {
- clearSelection();
- }
- void EmojiListWidget::enterFromChildEvent(QEvent *e, QWidget *child) {
- _lastMousePos = QCursor::pos();
- updateSelected();
- }
- void EmojiListWidget::clearSelection() {
- setPressed(v::null);
- setSelected(v::null);
- _lastMousePos = mapToGlobal(QPoint(-10, -10));
- }
- uint64 EmojiListWidget::currentSet(int yOffset) const {
- return sectionSetId(sectionInfoByOffset(yOffset).section);
- }
- void EmojiListWidget::setAllowWithoutPremium(bool allow) {
- if (_allowWithoutPremium == allow) {
- return;
- }
- _allowWithoutPremium = allow;
- refreshCustom();
- resizeToWidth(width());
- }
- void EmojiListWidget::showMegagroupSet(ChannelData *megagroup) {
- Expects(!megagroup || megagroup->isMegagroup());
- if (_megagroupSet != megagroup) {
- _megagroupSet = megagroup;
- refreshCustom();
- resizeToWidth(width());
- }
- }
- QString EmojiListWidget::tooltipText() const {
- if (_mode != Mode::Full) {
- return {};
- }
- const auto &replacements = Ui::Emoji::internal::GetAllReplacements();
- const auto over = std::get_if<OverEmoji>(&_selected);
- if (const auto emoji = lookupOverEmoji(over)) {
- const auto text = emoji->original()->text();
- // find the replacement belonging to the emoji
- const auto it = ranges::find_if(replacements, [&](const auto &one) {
- return text == Ui::Emoji::QStringFromUTF16(one.emoji);
- });
- if (it != replacements.end()) {
- return Ui::Emoji::QStringFromUTF16(it->replacement);
- }
- }
- return {};
- }
- QPoint EmojiListWidget::tooltipPos() const {
- return _lastMousePos;
- }
- bool EmojiListWidget::tooltipWindowActive() const {
- return Ui::AppInFocus() && Ui::InFocusChain(window());
- }
- TabbedSelector::InnerFooter *EmojiListWidget::getFooter() const {
- return _footer;
- }
- void EmojiListWidget::processHideFinished() {
- if (!_picker->isHidden()) {
- _picker->hideFast();
- _pickerSelected = v::null;
- }
- unloadAllCustom();
- clearSelection();
- }
- void EmojiListWidget::processPanelHideFinished() {
- unloadAllCustom();
- if (_localSetsManager->clearInstalledLocally()) {
- refreshCustom();
- }
- }
- void EmojiListWidget::refreshRecent() {
- if (_mode != Mode::Full) {
- return;
- }
- clearSelection();
- fillRecent();
- resizeToWidth(width());
- update();
- }
- void EmojiListWidget::refreshCustom() {
- if (_mode == Mode::RecentReactions || _mode == Mode::MessageEffects) {
- return;
- }
- auto old = base::take(_custom);
- const auto session = &this->session();
- const auto premiumPossible = session->premiumPossible();
- const auto onlyUnicodeEmoji = _onlyUnicodeEmoji || !premiumPossible;
- const auto premiumMayBeBought = (!onlyUnicodeEmoji)
- && premiumPossible
- && !session->premium()
- && !_allowWithoutPremium;
- const auto owner = &session->data();
- const auto &sets = owner->stickers().sets();
- const auto push = [&](uint64 setId, bool installed) {
- const auto megagroup = _megagroupSet
- && (setId == Data::Stickers::MegagroupSetId);
- const auto lookupId = megagroup
- ? _megagroupSet->mgInfo->emojiSet.id
- : setId;
- if (!lookupId) {
- return;
- } else if (!megagroup
- && !_custom.empty()
- && _custom.front().id == Data::Stickers::MegagroupSetId
- && _megagroupSet->mgInfo->emojiSet.id == setId) {
- // Skip the set that is already added as a megagroup set.
- return;
- } else if (megagroup
- && ranges::contains(_custom, lookupId, &CustomSet::id)) {
- // Skip the set that is already added as a custom set.
- return;
- }
- auto it = sets.find(lookupId);
- if (it == sets.cend()
- || it->second->stickers.isEmpty()
- || (_mode == Mode::BackgroundEmoji && !it->second->textColor())
- || (_mode == Mode::ChannelStatus
- && !it->second->channelStatus())) {
- return;
- }
- const auto canRemove = megagroup
- ? (_megagroupSet->canEditEmoji() || installed)
- : !!(it->second->flags & Data::StickersSetFlag::Installed);
- const auto sortAsInstalled = canRemove
- && (!(it->second->flags & Data::StickersSetFlag::Featured)
- || !_localSetsManager->isInstalledLocally(lookupId));
- if (!megagroup && sortAsInstalled != installed) {
- return;
- }
- auto premium = false;
- const auto &list = it->second->stickers;
- const auto i = ranges::find(old, setId, &CustomSet::id);
- if (i != end(old)) {
- const auto valid = [&] {
- const auto count = int(list.size());
- if (i->list.size() != count) {
- return false;
- }
- for (auto k = 0; k != count; ++k) {
- if (!premium && !megagroup && list[k]->isPremiumEmoji()) {
- premium = true;
- }
- if (i->list[k].document != list[k]) {
- return false;
- }
- }
- return true;
- }();
- if (premium && onlyUnicodeEmoji) {
- return;
- } else if (valid) {
- i->thumbnailDocument = it->second->lookupThumbnailDocument();
- const auto premiumRequired = premium && premiumMayBeBought;
- if (i->canRemove != canRemove
- || i->premiumRequired != premiumRequired) {
- i->canRemove = canRemove;
- i->premiumRequired = premiumRequired;
- i->ripple.reset();
- }
- if (i->canRemove && !i->premiumRequired) {
- i->expanded = false;
- }
- _custom.push_back(std::move(*i));
- return;
- }
- }
- auto set = std::vector<CustomOne>();
- set.reserve(list.size());
- for (const auto document : list) {
- const auto id = EmojiStatusId{ document->id };
- if (_restrictedCustomList.contains(id.documentId)) {
- continue;
- } else if (const auto sticker = document->sticker()) {
- set.push_back({
- .custom = resolveCustomEmoji(id, document, lookupId),
- .document = document,
- .emoji = Ui::Emoji::Find(sticker->alt),
- });
- if (!premium && !megagroup && document->isPremiumEmoji()) {
- premium = true;
- }
- }
- }
- if (premium && onlyUnicodeEmoji) {
- return;
- }
- _custom.push_back({
- .id = setId,
- .set = it->second.get(),
- .thumbnailDocument = it->second->lookupThumbnailDocument(),
- .title = it->second->title,
- .list = std::move(set),
- .canRemove = canRemove,
- .premiumRequired = premium && premiumMayBeBought,
- });
- };
- refreshEmojiStatusCollectibles();
- refreshMegagroupStickers(push, GroupStickersPlace::Visible);
- for (const auto setId : owner->stickers().emojiSetsOrder()) {
- push(setId, true);
- }
- for (const auto setId : owner->stickers().featuredEmojiSetsOrder()) {
- push(setId, false);
- }
- refreshMegagroupStickers(push, GroupStickersPlace::Hidden);
- _footer->refreshIcons(
- fillIcons(),
- currentSet(getVisibleTop()),
- nullptr,
- ValidateIconAnimations::None);
- update();
- }
- Fn<void()> EmojiListWidget::repaintCallback(
- DocumentId documentId,
- uint64 setId) {
- return [=] {
- repaintCustom(setId);
- if (_recentCustomIds.contains(documentId)) {
- repaintCustom(RecentEmojiSectionSetId());
- }
- if (_searchCustomIds.contains(documentId)) {
- repaintCustom(SearchEmojiSectionSetId());
- }
- };
- }
- not_null<Ui::Text::CustomEmoji*> EmojiListWidget::resolveCustomEmoji(
- EmojiStatusId id,
- not_null<DocumentData*> document,
- uint64 setId) {
- const auto documentId = document->id;
- const auto i = _customEmoji.find(id);
- const auto recentOnly = (i != end(_customEmoji)) && i->second.recentOnly;
- if (i != end(_customEmoji) && !recentOnly) {
- return i->second.emoji.get();
- }
- auto instance = document->owner().customEmojiManager().create(
- Data::EmojiStatusCustomId(id),
- repaintCallback(documentId, setId),
- Data::CustomEmojiManager::SizeTag::Large);
- if (recentOnly) {
- for (auto &recent : _recent) {
- if (recent.custom && recent.custom == i->second.emoji.get()) {
- recent.custom = instance.get();
- }
- }
- i->second.emoji = std::move(instance);
- i->second.recentOnly = false;
- return i->second.emoji.get();
- }
- return _customEmoji.emplace(
- id,
- CustomEmojiInstance{ .emoji = std::move(instance) }
- ).first->second.emoji.get();
- }
- Ui::Text::CustomEmoji *EmojiListWidget::resolveCustomRecent(
- RecentEmojiId customId) {
- const auto &data = customId.data;
- if (const auto document = std::get_if<RecentEmojiDocument>(&data)) {
- return resolveCustomRecent(document->id);
- } else if (const auto emoji = std::get_if<EmojiPtr>(&data)) {
- return nullptr;
- }
- Unexpected("Custom recent emoji id.");
- }
- not_null<Ui::Text::CustomEmoji*> EmojiListWidget::resolveCustomRecent(
- DocumentId documentId) {
- return resolveCustomRecent(EmojiStatusId{ documentId });
- }
- not_null<Ui::Text::CustomEmoji*> EmojiListWidget::resolveCustomRecent(
- EmojiStatusId id) {
- const auto i = id.collectible
- ? end(_customRecent)
- : _customRecent.find(id.documentId);
- if (i != end(_customRecent)) {
- return i->second.get();
- }
- const auto j = _customEmoji.find(id);
- if (j != end(_customEmoji)) {
- return j->second.emoji.get();
- }
- const auto documentId = id.collectible
- ? id.collectible->documentId
- : id.documentId;
- auto repaint = repaintCallback(documentId, RecentEmojiSectionSetId());
- if (_customRecentFactory && !id.collectible) {
- return _customRecent.emplace(
- id.documentId,
- _customRecentFactory(id.documentId, std::move(repaint))
- ).first->second.get();
- }
- auto custom = session().data().customEmojiManager().create(
- Data::EmojiStatusCustomId(id),
- std::move(repaint),
- Data::CustomEmojiManager::SizeTag::Large);
- return _customEmoji.emplace(
- id,
- CustomEmojiInstance{ .emoji = std::move(custom), .recentOnly = true }
- ).first->second.emoji.get();
- }
- void EmojiListWidget::refreshEmojiStatusCollectibles() {
- if (_mode != Mode::EmojiStatus || !_features.collectibleStatus) {
- return;
- }
- const auto type = Data::EmojiStatuses::Type::Collectibles;
- const auto &list = session().data().emojiStatuses().list(type);
- const auto setId = Data::Stickers::CollectibleSetId;
- auto set = std::vector<CustomOne>();
- set.reserve(list.size());
- for (const auto &status : list) {
- const auto documentId = status.collectible
- ? status.collectible->documentId
- : status.documentId;
- const auto document = session().data().document(documentId);
- const auto sticker = document->sticker();
- set.push_back({
- .collectible = status.collectible,
- .custom = resolveCustomEmoji(status, document, setId),
- .document = document,
- .emoji = sticker ? Ui::Emoji::Find(sticker->alt) : nullptr,
- });
- }
- if (set.empty()) {
- return;
- }
- const auto collectibles = session().data().stickers().collectibleSet();
- _custom.push_back({
- .id = setId,
- .set = collectibles,
- .thumbnailDocument = nullptr,
- .title = collectibles->title,
- .list = std::move(set),
- .canRemove = false,
- .premiumRequired = !session().premium(),
- });
- }
- void EmojiListWidget::refreshMegagroupStickers(
- Fn<void(uint64 setId, bool installed)> push,
- GroupStickersPlace place) {
- if (!_features.megagroupSet
- || !_megagroupSet
- || !_megagroupSet->mgInfo->emojiSet) {
- return;
- }
- auto canEdit = _megagroupSet->canEditEmoji();
- auto isShownHere = [place](bool hidden) {
- return (hidden == (place == GroupStickersPlace::Hidden));
- };
- auto hidden = session().settings().isGroupEmojiSectionHidden(_megagroupSet->id);
- auto removeHiddenForGroup = [this, &hidden] {
- if (hidden) {
- session().settings().removeGroupEmojiSectionHidden(_megagroupSet->id);
- session().saveSettings();
- hidden = false;
- }
- };
- if (canEdit && hidden) {
- removeHiddenForGroup();
- }
- const auto &set = _megagroupSet->mgInfo->emojiSet;
- if (!set.id || !isShownHere(hidden)) {
- return;
- }
- push(Data::Stickers::MegagroupSetId, !hidden);
- if (!_custom.empty()
- && _custom.back().id == Data::Stickers::MegagroupSetId) {
- return;
- } else if (_megagroupSetIdRequested == set.id) {
- return;
- }
- _megagroupSetIdRequested = set.id;
- _api.request(MTPmessages_GetStickerSet(
- Data::InputStickerSet(set),
- MTP_int(0) // hash
- )).done([=](const MTPmessages_StickerSet &result) {
- result.match([&](const MTPDmessages_stickerSet &data) {
- if (const auto set = session().data().stickers().feedSetFull(data)) {
- refreshCustom();
- if (set->id == _megagroupSetIdRequested) {
- _megagroupSetIdRequested = 0;
- } else {
- LOG(("API Error: Got different set."));
- }
- }
- }, [](const MTPDmessages_stickerSetNotModified &) {
- LOG(("API Error: Unexpected messages.stickerSetNotModified."));
- });
- }).send();
- }
- std::vector<StickerIcon> EmojiListWidget::fillIcons() {
- auto result = std::vector<StickerIcon>();
- result.reserve(2 + _custom.size());
- result.emplace_back(RecentEmojiSectionSetId());
- if (_mode != Mode::Full) {
- } else if (_custom.empty()) {
- using Section = Ui::Emoji::Section;
- for (auto i = int(Section::People); i <= int(Section::Symbols); ++i) {
- result.emplace_back(EmojiSectionSetId(Section(i)));
- }
- } else {
- result.emplace_back(AllEmojiSectionSetId());
- }
- const auto esize = StickersListFooter::IconFrameSize();
- for (const auto &custom : _custom) {
- if (custom.id == Data::Stickers::MegagroupSetId) {
- result.emplace_back(Data::Stickers::MegagroupSetId);
- result.back().megagroup = _megagroupSet;
- continue;
- }
- const auto set = custom.set;
- result.emplace_back(set, custom.thumbnailDocument, esize, esize);
- }
- return result;
- }
- int EmojiListWidget::paintButtonGetWidth(
- QPainter &p,
- const SectionInfo &info,
- bool selected,
- QRect clip) const {
- if (!hasButton(info.section)) {
- return 0;
- }
- auto &ripple = (info.section >= _staticCount)
- ? _custom[info.section - _staticCount].ripple
- : _colorAllRipple;
- const auto colorAll = hasColorButton(info.section);
- if (colorAll || hasRemoveButton(info.section)) {
- const auto rect = colorAll
- ? colorButtonRect(info)
- : removeButtonRect(info);
- if (rect.isEmpty()) {
- return 0;
- } else if (rect.intersects(clip)) {
- const auto &bst = colorAll ? st().colorAll : st().removeSet;
- if (colorAll && _colorAllRippleForced) {
- selected = true;
- }
- if (ripple) {
- ripple->paint(
- p,
- rect.x() + bst.rippleAreaPosition.x(),
- rect.y() + bst.rippleAreaPosition.y(),
- width());
- if (ripple->empty()) {
- ripple.reset();
- }
- }
- const auto &icon = selected ? bst.iconOver : bst.icon;
- icon.paint(
- p,
- (rect.topLeft()
- + QPoint(
- rect.width() - icon.width(),
- rect.height() - icon.height()) / 2),
- width());
- }
- return emojiRight() - rect.x();
- }
- const auto canAdd = hasAddButton(info.section);
- const auto &button = rightButton(info.section);
- const auto rect = buttonRect(info, button);
- p.drawImage(rect.topLeft(), selected ? button.backOver : button.back);
- if (ripple) {
- const auto color = QColor(0, 0, 0, 36);
- ripple->paint(p, rect.x(), rect.y(), width(), &color);
- if (ripple->empty()) {
- ripple.reset();
- }
- }
- p.setPen(!canAdd
- ? st::premiumButtonFg
- : selected
- ? st::emojiPanButton.textFgOver
- : st::emojiPanButton.textFg);
- p.setFont(st::emojiPanButton.style.font);
- p.drawText(
- rect.x() - (st::emojiPanButton.width / 2),
- (rect.y()
- + st::emojiPanButton.textTop
- + st::emojiPanButton.style.font->ascent),
- button.text);
- return emojiRight() - rect.x();
- }
- void EmojiListWidget::paintEmptySearchResults(Painter &p) {
- Inner::paintEmptySearchResults(
- p,
- st::emojiEmpty,
- tr::lng_emoji_nothing_found(tr::now));
- }
- bool EmojiListWidget::eventHook(QEvent *e) {
- if (e->type() == QEvent::ParentChange) {
- if (_picker->parentWidget() != parentWidget()) {
- _picker->setParent(parentWidget());
- }
- _picker->raise();
- }
- return Inner::eventHook(e);
- }
- void EmojiListWidget::updateSelected() {
- if (!v::is_null(_pressed) || !v::is_null(_pickerSelected)) {
- if (!_previewShown) {
- return;
- }
- }
- auto newSelected = OverState{ v::null };
- auto p = mapFromGlobal(_lastMousePos);
- auto info = sectionInfoByOffset(p.y());
- auto section = info.section;
- if (p.y() >= info.top && p.y() < info.rowsTop) {
- if (hasButton(section)
- && myrtlrect(buttonRect(section)).contains(p.x(), p.y())) {
- newSelected = OverButton{ section };
- } else if (_features.openStickerSets
- && section >= _staticCount
- && _mode == Mode::Full) {
- newSelected = OverSet{ section };
- }
- } else if (p.y() >= info.rowsTop && p.y() < info.rowsBottom) {
- auto sx = (rtl() ? width() - p.x() : p.x()) - _rowsLeft;
- if (sx >= 0 && sx < _columnCount * _singleSize.width()) {
- const auto index = qFloor((p.y() - info.rowsTop) / _singleSize.height()) * _columnCount + qFloor(sx / _singleSize.width());
- if (index < info.count) {
- newSelected = OverEmoji{ .section = section, .index = index };
- }
- }
- }
- setSelected(newSelected);
- }
- void EmojiListWidget::setSelected(OverState newSelected) {
- if (_selected == newSelected) {
- return;
- }
- setCursor(!v::is_null(newSelected)
- ? style::cur_pointer
- : style::cur_default);
- const auto updateSelected = [&] {
- if (const auto sticker = std::get_if<OverEmoji>(&_selected)) {
- rtlupdate(emojiRect(sticker->section, sticker->index));
- } else if (const auto button = std::get_if<OverButton>(&_selected)) {
- rtlupdate(buttonRect(button->section));
- }
- };
- updateSelected();
- _selected = newSelected;
- updateSelected();
- const auto hasSelection = !v::is_null(_selected);
- if (hasSelection && Core::App().settings().suggestEmoji()) {
- Ui::Tooltip::Show(1000, this);
- }
- setCursor(hasSelection ? style::cur_pointer : style::cur_default);
- if (hasSelection && !_picker->isHidden()) {
- if (_selected != _pickerSelected) {
- _picker->hideAnimated();
- } else {
- _picker->showAnimated();
- }
- } else if (_previewShown && _pressed != _selected) {
- if (const auto over = std::get_if<OverEmoji>(&_selected)) {
- if (const auto custom = lookupCustomEmoji(over)) {
- const auto document = custom.document;
- _pressed = _selected;
- _show->showMediaPreview(
- document->stickerSetOrigin(),
- document);
- }
- }
- }
- }
- void EmojiListWidget::setPressed(OverState newPressed) {
- if (auto button = std::get_if<OverButton>(&_pressed)) {
- Assert(hasColorButton(button->section)
- || (button->section >= _staticCount
- && button->section < _staticCount + _custom.size()));
- auto &ripple = (button->section >= _staticCount)
- ? _custom[button->section - _staticCount].ripple
- : _colorAllRipple;
- if (ripple) {
- ripple->lastStop();
- }
- }
- _pressed = newPressed;
- if (auto button = std::get_if<OverButton>(&_pressed)) {
- Assert(hasColorButton(button->section)
- || (button->section >= _staticCount
- && button->section < _staticCount + _custom.size()));
- auto &ripple = (button->section >= _staticCount)
- ? _custom[button->section - _staticCount].ripple
- : _colorAllRipple;
- if (!ripple) {
- ripple = createButtonRipple(button->section);
- }
- ripple->add(mapFromGlobal(QCursor::pos()) - buttonRippleTopLeft(button->section));
- }
- }
- void EmojiListWidget::initButton(
- RightButton &button,
- const QString &text,
- bool gradient) {
- button.text = text;
- button.textWidth = st::emojiPanButton.style.font->width(text);
- const auto width = button.textWidth - st::emojiPanButton.width;
- const auto height = st::emojiPanButton.height;
- const auto factor = style::DevicePixelRatio();
- auto prepare = [&](QColor bg, QBrush fg) {
- auto image = QImage(
- QSize(width, height) * factor,
- QImage::Format_ARGB32_Premultiplied);
- image.setDevicePixelRatio(factor);
- image.fill(Qt::transparent);
- auto p = QPainter(&image);
- auto hq = PainterHighQualityEnabler(p);
- p.setPen(Qt::NoPen);
- p.setBrush(fg);
- const auto radius = height / 2.;
- p.drawRoundedRect(QRect(0, 0, width, height), radius, radius);
- p.end();
- return image;
- };
- button.back = prepare(Qt::transparent, [&]() -> QBrush {
- if (gradient) {
- auto result = QLinearGradient(QPointF(0, 0), QPointF(width, 0));
- result.setStops(Ui::Premium::GiftGradientStops());
- return result;
- }
- return st::emojiPanButton.textBg;
- }());
- button.backOver = gradient
- ? button.back
- : prepare(Qt::transparent, st::emojiPanButton.textBgOver);
- button.rippleMask = prepare(Qt::black, Qt::white);
- }
- std::unique_ptr<Ui::RippleAnimation> EmojiListWidget::createButtonRipple(
- int section) {
- Expects(hasButton(section));
- const auto colorAll = hasColorButton(section);
- const auto remove = hasRemoveButton(section);
- const auto &staticSt = colorAll ? st().colorAll : st().removeSet;
- const auto &st = (colorAll || remove)
- ? staticSt.ripple
- : st::emojiPanButton.ripple;
- auto mask = (colorAll || remove)
- ? Ui::RippleAnimation::EllipseMask(QSize(
- staticSt.rippleAreaSize,
- staticSt.rippleAreaSize))
- : rightButton(section).rippleMask;
- return std::make_unique<Ui::RippleAnimation>(
- st,
- std::move(mask),
- [this, section] { rtlupdate(buttonRect(section)); });
- }
- QPoint EmojiListWidget::buttonRippleTopLeft(int section) const {
- Expects(hasButton(section));
- return myrtlrect(buttonRect(section)).topLeft()
- + (hasColorButton(section)
- ? st().colorAll.rippleAreaPosition
- : hasRemoveButton(section)
- ? st().removeSet.rippleAreaPosition
- : QPoint());
- }
- PowerSaving::Flag EmojiListWidget::powerSavingFlag() const {
- const auto reactions = (_mode == Mode::FullReactions)
- || (_mode == Mode::RecentReactions);
- return reactions
- ? PowerSaving::kEmojiReactions
- : PowerSaving::kEmojiPanel;
- }
- void EmojiListWidget::refreshEmoji() {
- refreshRecent();
- refreshCustom();
- }
- void EmojiListWidget::showSet(uint64 setId) {
- clearSelection();
- if (_search && _searchMode) {
- _search->cancel();
- applyNextSearchQuery();
- }
- auto y = 0;
- enumerateSections([&](const SectionInfo &info) {
- if (setId == sectionSetId(info.section)) {
- y = info.top;
- return false;
- }
- return true;
- });
- scrollTo(y);
- _lastMousePos = QCursor::pos();
- update();
- }
- uint64 EmojiListWidget::sectionSetId(int section) const {
- Expects(_searchMode
- || section < _staticCount
- || (section - _staticCount) < _custom.size());
- return _searchMode
- ? SearchEmojiSectionSetId()
- : (section < _staticCount)
- ? EmojiSectionSetId(static_cast<Section>(section))
- : _custom[section - _staticCount].id;
- }
- tr::phrase<> EmojiCategoryTitle(int index) {
- switch (index) {
- case 1: return tr::lng_emoji_category1;
- case 2: return tr::lng_emoji_category2;
- case 3: return tr::lng_emoji_category3;
- case 4: return tr::lng_emoji_category4;
- case 5: return tr::lng_emoji_category5;
- case 6: return tr::lng_emoji_category6;
- case 7: return tr::lng_emoji_category7;
- }
- Unexpected("Index in CategoryTitle.");
- }
- } // namespace ChatHelpers
|