/* 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 */ #pragma once #include "chat_helpers/tabbed_selector.h" #include "ui/widgets/tooltip.h" #include "ui/round_rect.h" #include "base/timer.h" namespace style { struct EmojiPan; } // namespace style namespace Core { struct RecentEmojiId; } // namespace Core namespace Data { class StickersSet; } // namespace Data namespace tr { template struct phrase; } // namespace tr namespace Ui { class RippleAnimation; class TabbedSearch; } // namespace Ui namespace Ui::Emoji { enum class Section; } // namespace Ui::Emoji namespace Ui::Text { struct CustomEmojiPaintContext; } // namespace Ui::Text namespace Ui::CustomEmoji { class Loader; class Instance; struct RepaintRequest; } // namespace Ui::CustomEmoji namespace Window { class SessionController; } // namespace Window namespace ChatHelpers { inline constexpr auto kEmojiSectionCount = 8; struct StickerIcon; class EmojiColorPicker; class StickersListFooter; class GradientPremiumStar; class LocalStickersManager; enum class EmojiListMode { Full, TopicIcon, EmojiStatus, FullReactions, RecentReactions, }; struct EmojiListDescriptor { not_null session; EmojiListMode mode = EmojiListMode::Full; Window::SessionController *controller = nullptr; Fn paused; std::vector customRecentList; Fn( DocumentId, Fn)> customRecentFactory; const style::EmojiPan *st = nullptr; }; class EmojiListWidget : public TabbedSelector::Inner , public Ui::AbstractTooltipShower { public: using Mode = EmojiListMode; EmojiListWidget( QWidget *parent, not_null controller, Window::GifPauseReason level, Mode mode); EmojiListWidget(QWidget *parent, EmojiListDescriptor &&descriptor); ~EmojiListWidget(); using Section = Ui::Emoji::Section; void refreshRecent() override; void clearSelection() override; object_ptr createFooter() override; void showSet(uint64 setId); [[nodiscard]] uint64 currentSet(int yOffset) const; void setAllowWithoutPremium(bool allow); // Ui::AbstractTooltipShower interface. QString tooltipText() const override; QPoint tooltipPos() const override; bool tooltipWindowActive() const override; void refreshEmoji(); [[nodiscard]] rpl::producer chosen() const; [[nodiscard]] rpl::producer customChosen() const; [[nodiscard]] rpl::producer<> jumpedToPremium() const; void provideRecent(const std::vector &customRecentList); void paintExpanding( QPainter &p, QRect clip, int finalBottom, float64 progress, RectPart origin); base::unique_qptr fillContextMenu( SendMenu::Type type) override; protected: void visibleTopBottomUpdated( int visibleTop, int visibleBottom) override; void mousePressEvent(QMouseEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override; void paintEvent(QPaintEvent *e) override; void leaveEventHook(QEvent *e) override; void leaveToChildEvent(QEvent *e, QWidget *child) override; void enterFromChildEvent(QEvent *e, QWidget *child) override; bool eventHook(QEvent *e) override; TabbedSelector::InnerFooter *getFooter() const override; void processHideFinished() override; void processPanelHideFinished() override; int countDesiredHeight(int newWidth) override; int defaultMinimalHeight() const override; private: struct SectionInfo { int section = 0; int count = 0; int top = 0; int rowsCount = 0; int rowsTop = 0; int rowsBottom = 0; bool premiumRequired = false; bool collapsed = false; }; struct CustomOne { not_null custom; not_null document; EmojiPtr emoji = nullptr; }; struct CustomSet { uint64 id = 0; not_null set; DocumentData *thumbnailDocument = nullptr; QString title; std::vector list; mutable std::unique_ptr ripple; bool painted = false; bool expanded = false; bool canRemove = false; bool premiumRequired = false; }; struct CustomEmojiInstance; struct RightButton { QImage back; QImage backOver; QImage rippleMask; QString text; int textWidth = 0; }; struct RecentOne; struct OverEmoji { int section = 0; int index = 0; inline bool operator==(OverEmoji other) const { return (section == other.section) && (index == other.index); } inline bool operator!=(OverEmoji other) const { return !(*this == other); } }; struct OverSet { int section = 0; inline bool operator==(OverSet other) const { return (section == other.section); } inline bool operator!=(OverSet other) const { return !(*this == other); } }; struct OverButton { int section = 0; inline bool operator==(OverButton other) const { return (section == other.section); } inline bool operator!=(OverButton other) const { return !(*this == other); } }; using OverState = std::variant< v::null_t, OverEmoji, OverSet, OverButton>; struct ExpandingContext { float64 progress = 0.; int finalHeight = 0; bool expanding = false; }; template bool enumerateSections(Callback callback) const; [[nodiscard]] SectionInfo sectionInfo(int section) const; [[nodiscard]] SectionInfo sectionInfoByOffset(int yOffset) const; [[nodiscard]] int sectionsCount() const; void setSingleSize(QSize size); void showPicker(); void pickerHidden(); void colorChosen(EmojiChosen data); bool checkPickerHide(); void refreshCustom(); void unloadNotSeenCustom(int visibleTop, int visibleBottom); void unloadAllCustom(); void unloadCustomIn(const SectionInfo &info); void setupSearch(); [[nodiscard]] std::vector collectPlainSearchResults(); void appendPremiumSearchResults(); void ensureLoaded(int section); void updateSelected(); void setSelected(OverState newSelected); void setPressed(OverState newPressed); [[nodiscard]] EmojiPtr lookupOverEmoji(const OverEmoji *over) const; [[nodiscard]] DocumentData *lookupCustomEmoji( int index, int section) const; [[nodiscard]] EmojiChosen lookupChosen( EmojiPtr emoji, not_null over); [[nodiscard]] FileChosen lookupChosen( not_null custom, const OverEmoji *over, Api::SendOptions options = Api::SendOptions()); void selectEmoji(EmojiChosen data); void selectCustom(FileChosen data); void paint(QPainter &p, ExpandingContext context, QRect clip); void drawCollapsedBadge(QPainter &p, QPoint position, int count); void drawRecent( QPainter &p, const ExpandingContext &context, QPoint position, const RecentOne &recent); void drawEmoji( QPainter &p, const ExpandingContext &context, QPoint position, EmojiPtr emoji); void drawCustom( QPainter &p, const ExpandingContext &context, QPoint position, int set, int index); void validateEmojiPaintContext(const ExpandingContext &context); [[nodiscard]] bool hasRemoveButton(int index) const; [[nodiscard]] QRect removeButtonRect(int index) const; [[nodiscard]] QRect removeButtonRect(const SectionInfo &info) const; [[nodiscard]] bool hasAddButton(int index) const; [[nodiscard]] QRect addButtonRect(int index) const; [[nodiscard]] bool hasUnlockButton(int index) const; [[nodiscard]] QRect unlockButtonRect(int index) const; [[nodiscard]] bool hasButton(int index) const; [[nodiscard]] QRect buttonRect(int index) const; [[nodiscard]] QRect buttonRect( const SectionInfo &info, const RightButton &button) const; [[nodiscard]] const RightButton &rightButton(int index) const; [[nodiscard]] QRect emojiRect(int section, int index) const; [[nodiscard]] int emojiRight() const; [[nodiscard]] int emojiLeft() const; [[nodiscard]] uint64 sectionSetId(int section) const; [[nodiscard]] std::vector fillIcons(); int paintButtonGetWidth( QPainter &p, const SectionInfo &info, bool selected, QRect clip) const; void displaySet(uint64 setId); void removeSet(uint64 setId); void initButton(RightButton &button, const QString &text, bool gradient); [[nodiscard]] std::unique_ptr createButtonRipple( int section); [[nodiscard]] QPoint buttonRippleTopLeft(int section) const; void repaintCustom(uint64 setId); void fillRecent(); void fillRecentFrom(const std::vector &list); [[nodiscard]] not_null resolveCustomEmoji( not_null document, uint64 setId); [[nodiscard]] Ui::Text::CustomEmoji *resolveCustomRecent( Core::RecentEmojiId customId); [[nodiscard]] not_null resolveCustomRecent( DocumentId documentId); [[nodiscard]] Fn repaintCallback( DocumentId documentId, uint64 setId); void applyNextSearchQuery(); Window::SessionController *_controller = nullptr; Mode _mode = Mode::Full; std::unique_ptr _search; const int _staticCount = 0; StickersListFooter *_footer = nullptr; std::unique_ptr _premiumIcon; std::unique_ptr _localSetsManager; Fn( DocumentId, Fn)> _customRecentFactory; int _counts[kEmojiSectionCount]; std::vector _recent; base::flat_set _recentCustomIds; base::flat_set _repaintsScheduled; std::unique_ptr _emojiPaintContext; bool _recentPainted = false; bool _grabbingChosen = false; QVector _emoji[kEmojiSectionCount]; std::vector _custom; base::flat_map _customEmoji; base::flat_map< DocumentId, std::unique_ptr> _customRecent; int _customSingleSize = 0; bool _allowWithoutPremium = false; Ui::RoundRect _overBg; std::vector _nextSearchQuery; std::vector _searchQuery; base::flat_set _searchEmoji; base::flat_set _searchEmojiPrevious; base::flat_set _searchCustomIds; std::vector _searchResults; bool _searchMode = false; int _rowsTop = 0; int _rowsLeft = 0; int _columnCount = 1; QSize _singleSize; QPoint _areaPosition; QPoint _innerPosition; QPoint _customPosition; RightButton _add; RightButton _unlock; RightButton _restore; Ui::RoundRect _collapsedBg; OverState _selected; OverState _pressed; OverState _pickerSelected; QPoint _lastMousePos; object_ptr _picker; base::Timer _showPickerTimer; rpl::event_stream _chosen; rpl::event_stream _customChosen; rpl::event_stream<> _jumpedToPremium; }; tr::phrase<> EmojiCategoryTitle(int index); } // namespace ChatHelpers