/* 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 "history/view/history_view_object.h" #include "base/runtime_composer.h" #include "base/flags.h" class History; class HistoryBlock; class HistoryItem; class HistoryMessage; class HistoryService; struct HistoryMessageReply; namespace Data { struct Reaction; } // namespace Data namespace Window { class SessionController; } // namespace Window namespace Ui { class PathShiftGradient; struct BubblePattern; struct ChatPaintContext; class ChatStyle; } // namespace Ui namespace Lottie { class Icon; } // namespace Lottie namespace HistoryView { enum class PointState : char; enum class InfoDisplayType : char; struct StateRequest; struct TextState; class Media; using PaintContext = Ui::ChatPaintContext; namespace Reactions { struct ButtonParameters; class Animation; } // namespace Reactions enum class Context : char { History, Replies, Pinned, AdminLog, ContactPreview }; class Element; class ElementDelegate { public: virtual Context elementContext() = 0; virtual std::unique_ptr elementCreate( not_null message, Element *replacing = nullptr) = 0; virtual std::unique_ptr elementCreate( not_null message, Element *replacing = nullptr) = 0; virtual bool elementUnderCursor(not_null view) = 0; virtual crl::time elementHighlightTime( not_null item) = 0; virtual bool elementInSelectionMode() = 0; virtual bool elementIntersectsRange( not_null view, int from, int till) = 0; virtual void elementStartStickerLoop(not_null view) = 0; virtual void elementShowPollResults( not_null poll, FullMsgId context) = 0; virtual void elementOpenPhoto( not_null photo, FullMsgId context) = 0; virtual void elementOpenDocument( not_null document, FullMsgId context, bool showInMediaView = false) = 0; virtual void elementCancelUpload(const FullMsgId &context) = 0; virtual void elementShowTooltip( const TextWithEntities &text, Fn hiddenCallback) = 0; virtual bool elementIsGifPaused() = 0; virtual bool elementHideReply(not_null view) = 0; virtual bool elementShownUnread(not_null view) = 0; virtual void elementSendBotCommand( const QString &command, const FullMsgId &context) = 0; virtual void elementHandleViaClick(not_null bot) = 0; virtual bool elementIsChatWide() = 0; virtual not_null elementPathShiftGradient() = 0; virtual void elementReplyTo(const FullMsgId &to) = 0; virtual void elementStartInteraction(not_null view) = 0; virtual void elementShowSpoilerAnimation() = 0; virtual ~ElementDelegate() { } }; [[nodiscard]] std::unique_ptr MakePathShiftGradient( not_null st, Fn update); class SimpleElementDelegate : public ElementDelegate { public: SimpleElementDelegate( not_null controller, Fn update); ~SimpleElementDelegate(); std::unique_ptr elementCreate( not_null message, Element *replacing = nullptr) override; std::unique_ptr elementCreate( not_null message, Element *replacing = nullptr) override; bool elementUnderCursor(not_null view) override; crl::time elementHighlightTime( not_null item) override; bool elementInSelectionMode() override; bool elementIntersectsRange( not_null view, int from, int till) override; void elementStartStickerLoop(not_null view) override; void elementShowPollResults( not_null poll, FullMsgId context) override; void elementOpenPhoto( not_null photo, FullMsgId context) override; void elementOpenDocument( not_null document, FullMsgId context, bool showInMediaView = false) override; void elementCancelUpload(const FullMsgId &context) override; void elementShowTooltip( const TextWithEntities &text, Fn hiddenCallback) override; bool elementIsGifPaused() override; bool elementHideReply(not_null view) override; bool elementShownUnread(not_null view) override; void elementSendBotCommand( const QString &command, const FullMsgId &context) override; void elementHandleViaClick(not_null bot) override; bool elementIsChatWide() override; not_null elementPathShiftGradient() override; void elementReplyTo(const FullMsgId &to) override; void elementStartInteraction(not_null view) override; void elementShowSpoilerAnimation() override; protected: [[nodiscard]] not_null controller() const { return _controller; } private: const not_null _controller; const std::unique_ptr _pathGradient; }; TextSelection UnshiftItemSelection( TextSelection selection, uint16 byLength); TextSelection ShiftItemSelection( TextSelection selection, uint16 byLength); TextSelection UnshiftItemSelection( TextSelection selection, const Ui::Text::String &byText); TextSelection ShiftItemSelection( TextSelection selection, const Ui::Text::String &byText); QString DateTooltipText(not_null view); // Any HistoryView::Element can have this Component for // displaying the unread messages bar above the message. struct UnreadBar : public RuntimeComponent { void init(const QString &string); static int height(); static int marginTop(); void paint( Painter &p, const PaintContext &context, int y, int w, bool chatWide) const; QString text; int width = 0; rpl::lifetime lifetime; }; // Any HistoryView::Element can have this Component for // displaying the day mark above the message. struct DateBadge : public RuntimeComponent { void init(const QString &date); int height() const; void paint( Painter &p, not_null st, int y, int w, bool chatWide) const; QString text; int width = 0; }; struct ReactionAnimationArgs { QString emoji; std::shared_ptr flyIcon; QRect flyFrom; [[nodiscard]] ReactionAnimationArgs translated(QPoint point) const; }; class Element : public Object , public RuntimeComposer , public ClickHandlerHost { public: Element( not_null delegate, not_null data, Element *replacing); enum class Flag : uchar { NeedsResize = 0x01, AttachedToPrevious = 0x02, AttachedToNext = 0x04, HiddenByGroup = 0x08, }; using Flags = base::flags; friend inline constexpr auto is_flag_type(Flag) { return true; } not_null delegate() const; not_null data() const; not_null history() const; Media *media() const; Context context() const; void refreshDataId(); QDateTime dateTime() const; int y() const; void setY(int y); virtual int marginTop() const = 0; virtual int marginBottom() const = 0; void setPendingResize(); bool pendingResize() const; bool isUnderCursor() const; bool isLastAndSelfMessage() const; bool isAttachedToPrevious() const; bool isAttachedToNext() const; int skipBlockWidth() const; int skipBlockHeight() const; virtual int infoWidth() const; virtual int bottomInfoFirstLineWidth() const; virtual bool bottomInfoIsWide() const; bool isHiddenByGroup() const; virtual bool isHidden() const; // For blocks context this should be called only from recountAttachToPreviousInBlocks(). void setAttachToPrevious(bool attachToNext); // For blocks context this should be called only from recountAttachToPreviousInBlocks() // of the next item or when the next item is removed through nextInBlocksRemoved() call. void setAttachToNext(bool attachToNext); // For blocks context this should be called only from recountDisplayDate(). void setDisplayDate(bool displayDate); bool computeIsAttachToPrevious(not_null previous); void createUnreadBar(rpl::producer text); void destroyUnreadBar(); int displayedDateHeight() const; bool displayDate() const; bool isInOneDayWithPrevious() const; virtual void draw(Painter &p, const PaintContext &context) const = 0; [[nodiscard]] virtual PointState pointState(QPoint point) const = 0; [[nodiscard]] virtual TextState textState( QPoint point, StateRequest request) const = 0; virtual void updatePressed(QPoint point) = 0; virtual void drawInfo( Painter &p, const PaintContext &context, int right, int bottom, int width, InfoDisplayType type) const; virtual TextState bottomInfoTextState( int right, int bottom, QPoint point, InfoDisplayType type) const; virtual TextForMimeData selectedText( TextSelection selection) const = 0; [[nodiscard]] virtual TextSelection adjustSelection( TextSelection selection, TextSelectType type) const; [[nodiscard]] virtual auto reactionButtonParameters( QPoint position, const TextState &reactionState) const -> Reactions::ButtonParameters; [[nodiscard]] virtual int reactionsOptimalWidth() const; // ClickHandlerHost interface. void clickHandlerActiveChanged( const ClickHandlerPtr &handler, bool active) override; void clickHandlerPressedChanged( const ClickHandlerPtr &handler, bool pressed) override; // hasFromPhoto() returns true even if we don't display the photo // but we need to skip a place at the left side for this photo [[nodiscard]] virtual bool hasFromPhoto() const; [[nodiscard]] virtual bool displayFromPhoto() const; [[nodiscard]] virtual bool hasFromName() const; [[nodiscard]] virtual bool displayFromName() const; [[nodiscard]] virtual bool displayForwardedFrom() const; [[nodiscard]] virtual bool hasOutLayout() const; [[nodiscard]] virtual bool drawBubble() const; [[nodiscard]] virtual bool hasBubble() const; [[nodiscard]] virtual int minWidthForMedia() const { return 0; } [[nodiscard]] virtual bool hasFastReply() const; [[nodiscard]] virtual bool displayFastReply() const; [[nodiscard]] virtual std::optional rightActionSize() const; virtual void drawRightAction( Painter &p, const PaintContext &context, int left, int top, int outerWidth) const; [[nodiscard]] virtual ClickHandlerPtr rightActionLink() const; [[nodiscard]] virtual bool displayEditedBadge() const; [[nodiscard]] virtual TimeId displayedEditDate() const; [[nodiscard]] virtual bool hasVisibleText() const; [[nodiscard]] virtual HistoryMessageReply *displayedReply() const; virtual void applyGroupAdminChanges( const base::flat_set &changes) { } [[nodiscard]] virtual bool toggleSelectionByHandlerClick( const ClickHandlerPtr &handler) const; struct VerticalRepaintRange { int top = 0; int height = 0; }; [[nodiscard]] virtual VerticalRepaintRange verticalRepaintRange() const; [[nodiscard]] virtual bool isSignedAuthorElided() const; virtual void itemDataChanged(); virtual bool hasHeavyPart() const; virtual void unloadHeavyPart(); void checkHeavyPart(); void paintCustomHighlight( Painter &p, const PaintContext &context, int y, int height, not_null item) const; float64 highlightOpacity(not_null item) const; // Legacy blocks structure. HistoryBlock *block(); const HistoryBlock *block() const; void attachToBlock(not_null block, int index); void removeFromBlock(); void refreshInBlock(); void setIndexInBlock(int index); int indexInBlock() const; Element *previousInBlocks() const; Element *previousDisplayedInBlocks() const; Element *nextInBlocks() const; Element *nextDisplayedInBlocks() const; void previousInBlocksChanged(); void nextInBlocksRemoved(); virtual QRect innerGeometry() const = 0; [[nodiscard]] ClickHandlerPtr fromPhotoLink() const { return fromLink(); } [[nodiscard]] bool markSponsoredViewed(int shownFromTop) const; virtual void animateReaction(ReactionAnimationArgs &&args); void animateUnreadReactions(); [[nodiscard]] virtual auto takeReactionAnimations() -> base::flat_map>; virtual ~Element(); static void Hovered(Element *view); [[nodiscard]] static Element *Hovered(); static void Pressed(Element *view); [[nodiscard]] static Element *Pressed(); static void HoveredLink(Element *view); [[nodiscard]] static Element *HoveredLink(); static void PressedLink(Element *view); [[nodiscard]] static Element *PressedLink(); static void Moused(Element *view); [[nodiscard]] static Element *Moused(); static void ClearGlobal(); protected: void repaint() const; void paintHighlight( Painter &p, const PaintContext &context, int geometryHeight) const; [[nodiscard]] ClickHandlerPtr fromLink() const; virtual void refreshDataIdHook(); private: // This should be called only from previousInBlocksChanged() // to add required bits to the Composer mask // after that always use Has(). void recountDisplayDateInBlocks(); // This should be called only from previousInBlocksChanged() or when // DateBadge or UnreadBar bit is changed in the Composer mask // then the result should be cached in a client side flag // HistoryView::Element::Flag::AttachedToPrevious. void recountAttachToPreviousInBlocks(); QSize countOptimalSize() final override; QSize countCurrentSize(int newWidth) final override; virtual QSize performCountOptimalSize() = 0; virtual QSize performCountCurrentSize(int newWidth) = 0; void refreshMedia(Element *replacing); const not_null _delegate; const not_null _data; std::unique_ptr _media; mutable ClickHandlerPtr _fromLink; bool _isScheduledUntilOnline = false; const QDateTime _dateTime; int _y = 0; Context _context = Context(); Flags _flags = Flag::NeedsResize; HistoryBlock *_block = nullptr; int _indexInBlock = -1; }; } // namespace HistoryView