/* 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 "data/data_cloud_file.h" #include "history/history_item.h" #include "ui/empty_userpic.h" #include "ui/effects/animations.h" struct WebPageData; class VoiceSeekClickHandler; namespace Ui { struct ChatPaintContext; class ChatStyle; } // namespace Ui namespace Data { class Session; } // namespace Data namespace HistoryView { class Element; class Document; } // namespace HistoryView struct HistoryMessageVia : public RuntimeComponent { void create(not_null owner, UserId userId); void resize(int32 availw) const; UserData *bot = nullptr; mutable QString text; mutable int width = 0; mutable int maxWidth = 0; ClickHandlerPtr link; }; struct HistoryMessageViews : public RuntimeComponent { static constexpr auto kMaxRecentRepliers = 3; struct Part { QString text; int textWidth = 0; int count = -1; }; std::vector recentRepliers; Part views; Part replies; Part repliesSmall; MsgId repliesInboxReadTillId = 0; MsgId repliesOutboxReadTillId = 0; MsgId repliesMaxId = 0; int repliesUnreadCount = -1; // unknown ChannelId commentsMegagroupId = 0; MsgId commentsRootId = 0; }; struct HistoryMessageSigned : public RuntimeComponent { QString author; bool isAnonymousRank = false; }; struct HistoryMessageEdited : public RuntimeComponent { TimeId date = 0; }; struct HiddenSenderInfo { HiddenSenderInfo(const QString &name, bool external); QString name; QString firstName; QString lastName; PeerId colorPeerId = 0; Ui::Text::String nameText; Ui::EmptyUserpic emptyUserpic; mutable Data::CloudImage customUserpic; [[nodiscard]] bool paintCustomUserpic( Painter &p, int x, int y, int outerWidth, int size) const; inline bool operator==(const HiddenSenderInfo &other) const { return name == other.name; } inline bool operator!=(const HiddenSenderInfo &other) const { return !(*this == other); } }; struct HistoryMessageForwarded : public RuntimeComponent { void create(const HistoryMessageVia *via) const; TimeId originalDate = 0; PeerData *originalSender = nullptr; std::unique_ptr hiddenSenderInfo; QString originalAuthor; QString psaType; MsgId originalId = 0; mutable Ui::Text::String text = { 1 }; PeerData *savedFromPeer = nullptr; MsgId savedFromMsgId = 0; bool imported = false; }; struct HistoryMessageSponsored : public RuntimeComponent { enum class Type : uchar { User, Group, Broadcast, Post, Bot, }; std::unique_ptr sender; Type type = Type::User; }; struct HistoryMessageReply : public RuntimeComponent { HistoryMessageReply() = default; HistoryMessageReply(const HistoryMessageReply &other) = delete; HistoryMessageReply(HistoryMessageReply &&other) = delete; HistoryMessageReply &operator=(const HistoryMessageReply &other) = delete; HistoryMessageReply &operator=(HistoryMessageReply &&other) { replyToPeerId = other.replyToPeerId; replyToMsgId = other.replyToMsgId; replyToMsgTop = other.replyToMsgTop; replyToDocumentId = other.replyToDocumentId; replyToWebPageId = other.replyToWebPageId; std::swap(replyToMsg, other.replyToMsg); replyToLnk = std::move(other.replyToLnk); replyToName = std::move(other.replyToName); replyToText = std::move(other.replyToText); replyToVersion = other.replyToVersion; maxReplyWidth = other.maxReplyWidth; replyToVia = std::move(other.replyToVia); return *this; } ~HistoryMessageReply() { // clearData() should be called by holder. Expects(replyToMsg == nullptr); Expects(replyToVia == nullptr); } bool updateData(not_null holder, bool force = false); // Must be called before destructor. void clearData(not_null holder); bool isNameUpdated() const; void updateName() const; void resize(int width) const; void itemRemoved(HistoryMessage *holder, HistoryItem *removed); void paint( Painter &p, not_null holder, const Ui::ChatPaintContext &context, int x, int y, int w, bool inBubble) const; [[nodiscard]] PeerId replyToPeer() const { return replyToPeerId; } [[nodiscard]] MsgId replyToId() const { return replyToMsgId; } [[nodiscard]] MsgId replyToTop() const { return replyToMsgTop; } [[nodiscard]] int replyToWidth() const { return maxReplyWidth; } [[nodiscard]] ClickHandlerPtr replyToLink() const { return replyToLnk; } void setReplyToLinkFrom( not_null holder); void refreshReplyToMedia(); PeerId replyToPeerId = 0; MsgId replyToMsgId = 0; MsgId replyToMsgTop = 0; HistoryItem *replyToMsg = nullptr; DocumentId replyToDocumentId = 0; WebPageId replyToWebPageId = 0; ClickHandlerPtr replyToLnk; mutable Ui::Text::String replyToName, replyToText; mutable int replyToVersion = 0; mutable int maxReplyWidth = 0; std::unique_ptr replyToVia; int toWidth = 0; }; struct HistoryMessageReplyMarkup : public RuntimeComponent { using Button = HistoryMessageMarkupButton; void createForwarded(const HistoryMessageReplyMarkup &original); void updateData(HistoryMessageMarkupData &&markup); HistoryMessageMarkupData data; std::unique_ptr inlineKeyboard; }; class ReplyMarkupClickHandler : public ClickHandler { public: ReplyMarkupClickHandler( not_null owner, int row, int column, FullMsgId context); QString tooltip() const override; void setFullDisplayed(bool full) { _fullDisplayed = full; } // Copy to clipboard support. QString copyToClipboardText() const override; QString copyToClipboardContextItemText() const override; // Finds the corresponding button in the items markup struct. // If the button is not found it returns nullptr. // Note: it is possible that we will point to the different button // than the one was used when constructing the handler, but not a big deal. const HistoryMessageMarkupButton *getButton() const; const HistoryMessageMarkupButton *getUrlButton() const; // We hold only FullMsgId, not HistoryItem*, because all click handlers // are activated async and the item may be already destroyed. void setMessageId(const FullMsgId &msgId) { _itemId = msgId; } void onClick(ClickContext context) const override; private: const not_null _owner; FullMsgId _itemId; int _row = 0; int _column = 0; bool _fullDisplayed = true; // Returns the full text of the corresponding button. QString buttonText() const; }; class ReplyKeyboard { private: struct Button; public: class Style { public: Style(const style::BotKeyboardButton &st) : _st(&st) { } virtual void startPaint( Painter &p, const Ui::ChatStyle *st) const = 0; virtual const style::TextStyle &textStyle() const = 0; int buttonSkip() const; int buttonPadding() const; int buttonHeight() const; virtual int buttonRadius() const = 0; virtual void repaint(not_null item) const = 0; virtual ~Style() { } protected: virtual void paintButtonBg( Painter &p, const Ui::ChatStyle *st, const QRect &rect, float64 howMuchOver) const = 0; virtual void paintButtonIcon( Painter &p, const Ui::ChatStyle *st, const QRect &rect, int outerWidth, HistoryMessageMarkupButton::Type type) const = 0; virtual void paintButtonLoading( Painter &p, const Ui::ChatStyle *st, const QRect &rect) const = 0; virtual int minButtonWidth( HistoryMessageMarkupButton::Type type) const = 0; private: const style::BotKeyboardButton *_st; void paintButton( Painter &p, const Ui::ChatStyle *st, int outerWidth, const ReplyKeyboard::Button &button) const; friend class ReplyKeyboard; }; ReplyKeyboard( not_null item, std::unique_ptr