/* 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 "ui/effects/animations.h" #include "ui/rp_widget.h" #include "base/timer.h" #include "base/object_ptr.h" #include "chat_helpers/stickers.h" namespace Ui { class ScrollArea; } // namespace Ui namespace Lottie { class SinglePlayer; class FrameRenderer; } // namespace Lottie; namespace Window { class SessionController; } // namespace Window namespace Data { class DocumentMedia; } // namespace Data namespace internal { struct StickerSuggestion { not_null document; std::shared_ptr documentMedia; std::unique_ptr animated; }; using MentionRows = QList; using HashtagRows = QList; using BotCommandRows = QList>; using StickerRows = std::vector; class FieldAutocompleteInner; } // namespace internal class FieldAutocomplete final : public Ui::RpWidget { Q_OBJECT public: FieldAutocomplete( QWidget *parent, not_null controller); ~FieldAutocomplete(); bool clearFilteredBotCommands(); void showFiltered( not_null peer, QString query, bool addInlineBots); void showStickers(EmojiPtr emoji); void setBoundings(QRect boundings); const QString &filter() const; ChatData *chat() const; ChannelData *channel() const; UserData *user() const; int32 innerTop(); int32 innerBottom(); bool eventFilter(QObject *obj, QEvent *e) override; enum class ChooseMethod { ByEnter, ByTab, ByClick, }; bool chooseSelected(ChooseMethod method) const; bool stickersShown() const { return !_srows.empty(); } bool overlaps(const QRect &globalRect) { if (isHidden() || !testAttribute(Qt::WA_OpaquePaintEvent)) return false; return rect().contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size())); } void hideFast(); signals: void mentionChosen(UserData *user, FieldAutocomplete::ChooseMethod method) const; void hashtagChosen(QString hashtag, FieldAutocomplete::ChooseMethod method) const; void botCommandChosen(QString command, FieldAutocomplete::ChooseMethod method) const; void stickerChosen(not_null sticker, FieldAutocomplete::ChooseMethod method) const; void moderateKeyActivate(int key, bool *outHandled) const; public slots: void showAnimated(); void hideAnimated(); protected: void paintEvent(QPaintEvent *e) override; private: void animationCallback(); void hideFinish(); void updateFiltered(bool resetScroll = false); void recount(bool resetScroll = false); internal::StickerRows getStickerSuggestions(); const not_null _controller; QPixmap _cache; internal::MentionRows _mrows; internal::HashtagRows _hrows; internal::BotCommandRows _brows; internal::StickerRows _srows; void rowsUpdated( internal::MentionRows &&mrows, internal::HashtagRows &&hrows, internal::BotCommandRows &&brows, internal::StickerRows &&srows, bool resetScroll); object_ptr _scroll; QPointer _inner; ChatData *_chat = nullptr; UserData *_user = nullptr; ChannelData *_channel = nullptr; EmojiPtr _emoji; uint64 _stickersSeed = 0; enum class Type { Mentions, Hashtags, BotCommands, Stickers, }; Type _type = Type::Mentions; QString _filter; QRect _boundings; bool _addInlineBots; int32 _width, _height; bool _hiding = false; Ui::Animations::Simple _a_opacity; friend class internal::FieldAutocompleteInner; }; namespace internal { class FieldAutocompleteInner final : public Ui::RpWidget , private base::Subscriber { Q_OBJECT public: FieldAutocompleteInner( not_null controller, not_null parent, not_null mrows, not_null hrows, not_null brows, not_null srows); void clearSel(bool hidden = false); bool moveSel(int key); bool chooseSelected(FieldAutocomplete::ChooseMethod method) const; void setRecentInlineBotsInRows(int32 bots); void rowsUpdated(); signals: void mentionChosen(UserData *user, FieldAutocomplete::ChooseMethod method) const; void hashtagChosen(QString hashtag, FieldAutocomplete::ChooseMethod method) const; void botCommandChosen(QString command, FieldAutocomplete::ChooseMethod method) const; void stickerChosen(not_null sticker, FieldAutocomplete::ChooseMethod method) const; void mustScrollTo(int scrollToTop, int scrollToBottom); public slots: void onParentGeometryChanged(); private: void paintEvent(QPaintEvent *e) override; void resizeEvent(QResizeEvent *e) override; void enterEventHook(QEvent *e) override; void leaveEventHook(QEvent *e) override; void mousePressEvent(QMouseEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override; void updateSelectedRow(); void setSel(int sel, bool scroll = false); void showPreview(); void selectByMouse(QPoint global); QSize stickerBoundingBox() const; void setupLottie(StickerSuggestion &suggestion); void repaintSticker(not_null document); std::shared_ptr getLottieRenderer(); const not_null _controller; const not_null _parent; const not_null _mrows; const not_null _hrows; const not_null _brows; const not_null _srows; rpl::lifetime _stickersLifetime; std::weak_ptr _lottieRenderer; int _stickersPerRow = 1; int _recentInlineBotsInRows = 0; int _sel = -1; int _down = -1; std::optional _lastMousePosition; bool _mouseSelection = false; bool _overDelete = false; bool _previewShown = false; base::Timer _previewTimer; }; } // namespace internal