/* 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/widgets/scroll_area.h" #include "ui/chat/chat_style.h" namespace Ui { struct ChatPaintContext; class PopupMenu; } // namespace Ui namespace Data { struct Reaction; class DocumentMedia; } // namespace Data namespace HistoryView { using PaintContext = Ui::ChatPaintContext; struct TextState; } // namespace HistoryView namespace Main { class Session; } // namespace Main namespace Lottie { class Icon; } // namespace Lottie namespace HistoryView::Reactions { enum class ExpandDirection { Up, Down, }; struct ButtonParameters { [[nodiscard]] ButtonParameters translated(QPoint delta) const { auto result = *this; result.center += delta; result.pointer += delta; return result; } FullMsgId context; QPoint center; QPoint pointer; QPoint globalPointer; int reactionsCount = 1; int visibleTop = 0; int visibleBottom = 0; bool outside = false; bool cursorLeft = false; }; enum class ButtonState { Hidden, Shown, Active, Inside, }; class Button final { public: Button( Fn update, ButtonParameters parameters, Fn hideMe); ~Button(); void applyParameters(ButtonParameters parameters); using State = ButtonState; void applyState(State state); [[nodiscard]] bool expandUp() const; [[nodiscard]] bool isHidden() const; [[nodiscard]] QRect geometry() const; [[nodiscard]] int expandedHeight() const; [[nodiscard]] int scroll() const; [[nodiscard]] int scrollMax() const; [[nodiscard]] float64 currentScale() const; [[nodiscard]] float64 currentOpacity() const; [[nodiscard]] float64 expandAnimationOpacity(float64 expandRatio) const; [[nodiscard]] int expandAnimationScroll(float64 expandRatio) const; [[nodiscard]] bool consumeWheelEvent(not_null e); [[nodiscard]] static float64 ScaleForState(State state); [[nodiscard]] static float64 OpacityForScale(float64 scale); private: enum class CollapseType { Scroll, Fade, }; void updateGeometry(Fn update); void applyState(State satte, Fn update); void applyParameters( ButtonParameters parameters, Fn update); void updateExpandDirection(const ButtonParameters ¶meters); const Fn _update; State _state = State::Hidden; float64 _finalScale = 0.; Ui::Animations::Simple _scaleAnimation; Ui::Animations::Simple _opacityAnimation; Ui::Animations::Simple _heightAnimation; QRect _collapsed; QRect _geometry; int _expandedInnerHeight = 0; int _expandedHeight = 0; int _finalHeight = 0; int _scroll = 0; ExpandDirection _expandDirection = ExpandDirection::Up; CollapseType _collapseType = CollapseType::Scroll; base::Timer _expandTimer; base::Timer _hideTimer; std::optional _lastGlobalPosition; }; using IconFactory = Fn( not_null, int)>; class Manager final : public base::has_weak_ptr { public: Manager( QWidget *wheelEventsTarget, rpl::producer uniqueLimitValue, Fn buttonUpdate, IconFactory iconFactory); ~Manager(); using AllowedSublist = std::optional>; void applyList( const std::vector &list, const QString &favorite, bool premiumPossible); void updateAllowedSublist(AllowedSublist filter); void updateAllowSendingPremium(bool allow); [[nodiscard]] const AllowedSublist &allowedSublist() const; void updateUniqueLimit(not_null item); void updateButton(ButtonParameters parameters); void paint(Painter &p, const PaintContext &context); [[nodiscard]] TextState buttonTextState(QPoint position) const; void remove(FullMsgId context); [[nodiscard]] bool consumeWheelEvent(not_null e); struct Chosen { FullMsgId context; QString emoji; std::shared_ptr icon; QRect geometry; explicit operator bool() const { return context && !emoji.isNull(); } }; [[nodiscard]] rpl::producer chosen() const { return _chosen.events(); } [[nodiscard]] std::optional lookupEffectArea( FullMsgId itemId) const; void startEffectsCollection(); [[nodiscard]] auto currentReactionPaintInfo() -> not_null; void recordCurrentReactionEffect(FullMsgId itemId, QPoint origin); bool showContextMenu( QWidget *parent, QContextMenuEvent *e, const QString &favorite); [[nodiscard]] rpl::producer faveRequests() const; [[nodiscard]] rpl::lifetime &lifetime() { return _lifetime; } private: struct ReactionDocument { std::shared_ptr media; std::shared_ptr icon; }; struct ReactionIcons { QString emoji; not_null appearAnimation; not_null selectAnimation; std::shared_ptr appear; std::shared_ptr select; mutable ClickHandlerPtr link; mutable Ui::Animations::Simple selectedScale; bool appearAnimated = false; bool premium = false; bool premiumLock = false; mutable bool selected = false; mutable bool selectAnimated = false; }; struct OverlayImage { not_null cache; QRect source; }; static constexpr auto kFramesCount = 32; void applyListFilters(); void showButtonDelayed(); void stealWheelEvents(not_null target); [[nodiscard]] Chosen lookupChosen(const QString &emoji) const; [[nodiscard]] bool overCurrentButton(QPoint position) const; void removeStaleButtons(); void paintButton( Painter &p, const PaintContext &context, not_null button); void paintButton( Painter &p, const PaintContext &context, not_null button, int frame, float64 scale); void paintAllEmoji( Painter &p, not_null button, int scroll, float64 scale, QPoint position, QPoint mainEmojiPosition); void paintPremiumIcon(QPainter &p, QPoint position, QRectF target) const; void paintInnerGradients( Painter &p, const QColor &background, not_null button, int scroll, float64 expandRatio); void overlayExpandedBorder( Painter &p, QSize size, float64 expandRatio, float64 scale, const QColor &shadow); void paintLongImage( QPainter &p, QRect geometry, const QImage &image, QRect source); void resolveMainReactionIcon(); void setMainReactionIcon(); void clearAppearAnimations(); [[nodiscard]] QRect cacheRect(int frameIndex, int columnIndex) const; [[nodiscard]] QRect overlayCacheRect( int frameIndex, int columnIndex) const; QRect validateShadow( int frameIndex, float64 scale, const QColor &shadow); QRect validateEmoji(int frameIndex, float64 scale); QRect validateFrame( int frameIndex, float64 scale, const QColor &background, const QColor &shadow); OverlayImage validateOverlayMask( int frameIndex, QSize innerSize, float64 radius, float64 scale); OverlayImage validateOverlayShadow( int frameIndex, QSize innerSize, float64 radius, float64 scale, const QColor &shadow, const OverlayImage &mask); void setBackgroundColor(const QColor &background); void setShadowColor(const QColor &shadow); void setSelectedIcon(int index) const; void clearStateForHidden(ReactionIcons &icon); void clearStateForSelectFinished(ReactionIcons &icon); [[nodiscard]] QMargins innerMargins() const; [[nodiscard]] QRect buttonInner() const; [[nodiscard]] QRect buttonInner(not_null button) const; [[nodiscard]] ClickHandlerPtr computeButtonLink(QPoint position) const; [[nodiscard]] ClickHandlerPtr resolveButtonLink( const ReactionIcons &reaction) const; void updateCurrentButton() const; [[nodiscard]] bool onlyMainEmojiVisible() const; [[nodiscard]] bool checkIconLoaded(ReactionDocument &entry) const; void loadIcons(); void checkIcons(); const IconFactory _iconFactory; rpl::event_stream _chosen; std::vector _list; QString _favorite; AllowedSublist _filter; QSize _outer; QRect _inner; QSize _overlayFull; QImage _cacheBg; QImage _cacheParts; QImage _overlayCacheParts; QImage _overlayMaskScaled; QImage _overlayShadowScaled; QImage _shadowBuffer; QImage _expandedBuffer; QImage _topGradient; QImage _bottomGradient; std::array _validBg = { { false } }; std::array _validShadow = { { false } }; std::array _validEmoji = { { false } }; std::array _validOverlayMask = { { false } }; std::array _validOverlayShadow = { { false } }; QColor _background; QColor _gradient; QColor _shadow; std::shared_ptr _mainReactionMedia; std::shared_ptr _mainReactionIcon; QImage _mainReactionImage; rpl::lifetime _mainReactionLifetime; rpl::variable _uniqueLimit = 0; base::flat_map, ReactionDocument> _loadCache; std::vector> _icons; std::optional _premiumIcon; rpl::lifetime _loadCacheLifetime; bool _showingAll = false; bool _allowSendingPremium = false; bool _premiumPossible = false; mutable int _selectedIcon = -1; std::optional _scheduledParameters; base::Timer _buttonShowTimer; const Fn _buttonUpdate; std::unique_ptr