tdesktop/Telegram/SourceFiles/history/history_item_components.h

485 lines
12 KiB
C
Raw Normal View History

/*
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/history_item.h"
#include "ui/empty_userpic.h"
#include "ui/effects/animations.h"
2018-01-25 14:19:14 +00:00
struct WebPageData;
class VoiceSeekClickHandler;
2020-06-08 15:17:33 +00:00
namespace Data {
class Session;
} // namespace Data
namespace HistoryView {
class Element;
class Document;
} // namespace HistoryView
struct HistoryMessageVia : public RuntimeComponent<HistoryMessageVia, HistoryItem> {
2020-06-08 15:17:33 +00:00
void create(not_null<Data::Session*> 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<HistoryMessageViews, HistoryItem> {
2020-09-03 08:21:31 +00:00
static constexpr auto kMaxRecentRepliers = 3;
2020-09-03 07:19:02 +00:00
struct Part {
QString text;
int textWidth = 0;
int count = -1;
};
std::vector<PeerId> recentRepliers;
2020-09-03 07:19:02 +00:00
Part views;
Part replies;
Part repliesSmall;
MsgId repliesInboxReadTillId = 0;
MsgId repliesOutboxReadTillId = 0;
MsgId repliesMaxId = 0;
ChannelId commentsMegagroupId = 0;
2020-09-03 08:21:31 +00:00
MsgId commentsRootId = 0;
};
struct HistoryMessageSigned : public RuntimeComponent<HistoryMessageSigned, HistoryItem> {
void refresh(const QString &date);
int maxWidth() const;
QString author;
2019-06-12 13:26:04 +00:00
Ui::Text::String signature;
2020-10-01 09:57:03 +00:00
bool isElided = false;
bool isAnonymousRank = false;
};
struct HistoryMessageEdited : public RuntimeComponent<HistoryMessageEdited, HistoryItem> {
void refresh(const QString &date, bool displayed);
int maxWidth() const;
TimeId date = 0;
2019-06-12 13:26:04 +00:00
Ui::Text::String text;
};
struct HiddenSenderInfo {
2021-01-20 14:09:45 +00:00
HiddenSenderInfo(const QString &name, bool external);
QString name;
QString firstName;
QString lastName;
PeerId colorPeerId = 0;
Ui::EmptyUserpic userpic;
2019-06-12 13:26:04 +00:00
Ui::Text::String nameText;
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<HistoryMessageForwarded, HistoryItem> {
void create(const HistoryMessageVia *via) const;
TimeId originalDate = 0;
PeerData *originalSender = nullptr;
std::unique_ptr<HiddenSenderInfo> hiddenSenderInfo;
QString originalAuthor;
2020-04-28 19:49:55 +00:00
QString psaType;
MsgId originalId = 0;
2019-06-12 13:26:04 +00:00
mutable Ui::Text::String text = { 1 };
PeerData *savedFromPeer = nullptr;
MsgId savedFromMsgId = 0;
2021-01-20 14:09:45 +00:00
bool imported = false;
};
struct HistoryMessageReply : public RuntimeComponent<HistoryMessageReply, HistoryItem> {
HistoryMessageReply() = default;
HistoryMessageReply(const HistoryMessageReply &other) = delete;
HistoryMessageReply(HistoryMessageReply &&other) = delete;
HistoryMessageReply &operator=(const HistoryMessageReply &other) = delete;
HistoryMessageReply &operator=(HistoryMessageReply &&other) {
2020-09-11 16:22:14 +00:00
replyToPeerId = other.replyToPeerId;
replyToMsgId = other.replyToMsgId;
replyToMsgTop = other.replyToMsgTop;
replyToDocumentId = other.replyToDocumentId;
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<HistoryMessage*> holder, bool force = false);
// Must be called before destructor.
void clearData(not_null<HistoryMessage*> holder);
bool isNameUpdated() const;
void updateName() const;
void resize(int width) const;
void itemRemoved(HistoryMessage *holder, HistoryItem *removed);
enum class PaintFlag {
InBubble = (1 << 0),
Selected = (1 << 1),
};
using PaintFlags = base::flags<PaintFlag>;
friend inline constexpr auto is_flag_type(PaintFlag) { return true; };
void paint(
Painter &p,
not_null<const HistoryView::Element*> holder,
int x,
int y,
int w,
PaintFlags flags) const;
2020-09-11 16:22:14 +00:00
[[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<HistoryMessage*> holder);
2019-12-27 14:29:51 +00:00
void refreshReplyToDocument();
2020-09-11 16:22:14 +00:00
PeerId replyToPeerId = 0;
MsgId replyToMsgId = 0;
MsgId replyToMsgTop = 0;
HistoryItem *replyToMsg = nullptr;
DocumentId replyToDocumentId = 0;
ClickHandlerPtr replyToLnk;
2019-06-12 13:26:04 +00:00
mutable Ui::Text::String replyToName, replyToText;
mutable int replyToVersion = 0;
mutable int maxReplyWidth = 0;
std::unique_ptr<HistoryMessageVia> replyToVia;
int toWidth = 0;
};
struct HistoryMessageMarkupButton {
enum class Type {
Default,
Url,
Callback,
CallbackWithPassword,
RequestPhone,
RequestLocation,
RequestPoll,
SwitchInline,
SwitchInlineSame,
Game,
Buy,
2019-05-22 14:29:02 +00:00
Auth,
};
2019-05-22 14:29:02 +00:00
HistoryMessageMarkupButton(
Type type,
const QString &text,
const QByteArray &data = QByteArray(),
const QString &forwardText = QString(),
2019-05-22 14:29:02 +00:00
int32 buttonId = 0);
static HistoryMessageMarkupButton *Get(
2020-06-08 15:17:33 +00:00
not_null<Data::Session*> owner,
2019-05-22 14:29:02 +00:00
FullMsgId itemId,
int row,
int column);
Type type;
QString text, forwardText;
QByteArray data;
2019-05-22 14:29:02 +00:00
int32 buttonId = 0;
mutable mtpRequestId requestId = 0;
};
2021-07-28 07:33:52 +00:00
struct HistoryMessageReplyMarkup
: public RuntimeComponent<HistoryMessageReplyMarkup, HistoryItem> {
using Button = HistoryMessageMarkupButton;
HistoryMessageReplyMarkup() = default;
2021-07-28 07:33:52 +00:00
HistoryMessageReplyMarkup(ReplyMarkupFlags flags) : flags(flags) {
}
void create(const MTPReplyMarkup &markup);
void create(const HistoryMessageReplyMarkup &markup);
std::vector<std::vector<Button>> rows;
2021-07-28 07:33:52 +00:00
ReplyMarkupFlags flags = 0;
2021-07-05 17:59:23 +00:00
QString placeholder;
std::unique_ptr<ReplyKeyboard> inlineKeyboard;
private:
void createFromButtonRows(const QVector<MTPKeyboardButtonRow> &v);
};
2021-07-26 23:45:01 +00:00
class ReplyMarkupClickHandler : public ClickHandler {
public:
ReplyMarkupClickHandler(
not_null<Data::Session*> 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;
}
2021-07-26 23:45:01 +00:00
void onClick(ClickContext context) const override;
private:
const not_null<Data::Session*> _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 = 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<const HistoryItem*> item) const = 0;
virtual ~Style() {
}
protected:
virtual void paintButtonBg(
Painter &p,
const QRect &rect,
float64 howMuchOver) const = 0;
virtual void paintButtonIcon(
Painter &p,
const QRect &rect,
int outerWidth,
HistoryMessageMarkupButton::Type type) const = 0;
virtual void paintButtonLoading(
Painter &p,
const QRect &rect) const = 0;
virtual int minButtonWidth(
HistoryMessageMarkupButton::Type type) const = 0;
private:
const style::BotKeyboardButton *_st;
2019-04-02 09:13:30 +00:00
void paintButton(Painter &p, int outerWidth, const ReplyKeyboard::Button &button) const;
friend class ReplyKeyboard;
};
ReplyKeyboard(
not_null<const HistoryItem*> item,
std::unique_ptr<Style> &&s);
ReplyKeyboard(const ReplyKeyboard &other) = delete;
ReplyKeyboard &operator=(const ReplyKeyboard &other) = delete;
bool isEnoughSpace(int width, const style::BotKeyboardButton &st) const;
void setStyle(std::unique_ptr<Style> &&s);
void resize(int width, int height);
// what width and height will best fit this keyboard
int naturalWidth() const;
int naturalHeight() const;
2019-04-02 09:13:30 +00:00
void paint(Painter &p, int outerWidth, const QRect &clip) const;
ClickHandlerPtr getLink(QPoint point) const;
void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active);
void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed);
void clearSelection();
void updateMessageId();
private:
friend class Style;
struct Button {
Button();
Button(Button &&other);
Button &operator=(Button &&other);
~Button();
2019-06-12 13:26:04 +00:00
Ui::Text::String text = { 1 };
QRect rect;
int characters = 0;
float64 howMuchOver = 0.;
HistoryMessageMarkupButton::Type type;
std::shared_ptr<ReplyMarkupClickHandler> link;
mutable std::unique_ptr<Ui::RippleAnimation> ripple;
};
struct ButtonCoords {
int i, j;
};
void startAnimation(int i, int j, int direction);
ButtonCoords findButtonCoordsByClickHandler(const ClickHandlerPtr &p);
bool selectedAnimationCallback(crl::time now);
const not_null<const HistoryItem*> _item;
int _width = 0;
std::vector<std::vector<Button>> _rows;
base::flat_map<int, crl::time> _animations;
Ui::Animations::Basic _selectedAnimation;
std::unique_ptr<Style> _st;
ClickHandlerPtr _savedPressed;
ClickHandlerPtr _savedActive;
mutable QPoint _savedCoords;
};
// Special type of Component for the channel actions log.
struct HistoryMessageLogEntryOriginal
: public RuntimeComponent<HistoryMessageLogEntryOriginal, HistoryItem> {
HistoryMessageLogEntryOriginal();
HistoryMessageLogEntryOriginal(HistoryMessageLogEntryOriginal &&other);
HistoryMessageLogEntryOriginal &operator=(HistoryMessageLogEntryOriginal &&other);
~HistoryMessageLogEntryOriginal();
WebPageData *page = nullptr;
};
class FileClickHandler;
struct HistoryDocumentThumbed : public RuntimeComponent<HistoryDocumentThumbed, HistoryView::Document> {
std::shared_ptr<FileClickHandler> _linksavel;
std::shared_ptr<FileClickHandler> _linkopenwithl;
std::shared_ptr<FileClickHandler> _linkcancell;
int _thumbw = 0;
mutable int _linkw = 0;
mutable QString _link;
};
struct HistoryDocumentCaptioned : public RuntimeComponent<HistoryDocumentCaptioned, HistoryView::Document> {
HistoryDocumentCaptioned();
2019-06-12 13:26:04 +00:00
Ui::Text::String _caption;
};
struct HistoryDocumentNamed : public RuntimeComponent<HistoryDocumentNamed, HistoryView::Document> {
QString _name;
int _namew = 0;
};
struct HistoryDocumentVoicePlayback {
HistoryDocumentVoicePlayback(const HistoryView::Document *that);
int32 position = 0;
anim::value progress;
Ui::Animations::Basic progressAnimation;
};
class HistoryDocumentVoice : public RuntimeComponent<HistoryDocumentVoice, HistoryView::Document> {
// We don't use float64 because components should align to pointer even on 32bit systems.
static constexpr float64 kFloatToIntMultiplier = 65536.;
public:
void ensurePlayback(const HistoryView::Document *interfaces) const;
void checkPlaybackFinished() const;
mutable std::unique_ptr<HistoryDocumentVoicePlayback> _playback;
std::shared_ptr<VoiceSeekClickHandler> _seekl;
mutable int _lastDurationMs = 0;
bool seeking() const {
return _seeking;
}
void startSeeking();
void stopSeeking();
float64 seekingStart() const {
return _seekingStart / kFloatToIntMultiplier;
}
void setSeekingStart(float64 seekingStart) const {
_seekingStart = qRound(seekingStart * kFloatToIntMultiplier);
}
float64 seekingCurrent() const {
return _seekingCurrent / kFloatToIntMultiplier;
}
void setSeekingCurrent(float64 seekingCurrent) {
_seekingCurrent = qRound(seekingCurrent * kFloatToIntMultiplier);
}
private:
bool _seeking = false;
mutable int _seekingStart = 0;
mutable int _seekingCurrent = 0;
};