mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-15 21:57:10 +02:00
Move Ui::Text::String to HistoryView::Element.
This commit is contained in:
parent
140dcb033b
commit
ffb024a5f7
23 changed files with 474 additions and 434 deletions
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "chat_helpers/stickers_emoji_pack.h"
|
||||
|
||||
#include "chat_helpers/stickers_emoji_image_loader.h"
|
||||
#include "history/view/history_view_element.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history.h"
|
||||
#include "lottie/lottie_common.h"
|
||||
|
@ -101,10 +102,10 @@ EmojiPack::EmojiPack(not_null<Main::Session*> session)
|
|||
: _session(session) {
|
||||
refresh();
|
||||
|
||||
session->data().itemRemoved(
|
||||
) | rpl::filter([](not_null<const HistoryItem*> item) {
|
||||
return item->isIsolatedEmoji();
|
||||
}) | rpl::start_with_next([=](not_null<const HistoryItem*> item) {
|
||||
session->data().viewRemoved(
|
||||
) | rpl::filter([](not_null<const ViewElement*> view) {
|
||||
return view->isIsolatedEmoji() || view->isOnlyCustomEmoji();
|
||||
}) | rpl::start_with_next([=](not_null<const ViewElement*> item) {
|
||||
remove(item);
|
||||
}, _lifetime);
|
||||
|
||||
|
@ -122,26 +123,26 @@ EmojiPack::EmojiPack(not_null<Main::Session*> session)
|
|||
|
||||
EmojiPack::~EmojiPack() = default;
|
||||
|
||||
bool EmojiPack::add(not_null<HistoryItem*> item) {
|
||||
if (const auto custom = item->onlyCustomEmoji()) {
|
||||
_onlyCustomItems.emplace(item);
|
||||
bool EmojiPack::add(not_null<ViewElement*> view) {
|
||||
if (const auto custom = view->onlyCustomEmoji()) {
|
||||
_onlyCustomItems.emplace(view);
|
||||
return true;
|
||||
} else if (const auto emoji = item->isolatedEmoji()) {
|
||||
_items[emoji].emplace(item);
|
||||
} else if (const auto emoji = view->isolatedEmoji()) {
|
||||
_items[emoji].emplace(view);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void EmojiPack::remove(not_null<const HistoryItem*> item) {
|
||||
Expects(item->isIsolatedEmoji() || item->isOnlyCustomEmoji());
|
||||
void EmojiPack::remove(not_null<const ViewElement*> view) {
|
||||
Expects(view->isIsolatedEmoji() || view->isOnlyCustomEmoji());
|
||||
|
||||
if (item->isOnlyCustomEmoji()) {
|
||||
_onlyCustomItems.remove(item);
|
||||
} else if (const auto emoji = item->isolatedEmoji()) {
|
||||
if (view->isOnlyCustomEmoji()) {
|
||||
_onlyCustomItems.remove(view);
|
||||
} else if (const auto emoji = view->isolatedEmoji()) {
|
||||
const auto i = _items.find(emoji);
|
||||
Assert(i != end(_items));
|
||||
const auto j = i->second.find(item);
|
||||
const auto j = i->second.find(view);
|
||||
Assert(j != end(i->second));
|
||||
i->second.erase(j);
|
||||
if (i->second.empty()) {
|
||||
|
@ -436,9 +437,20 @@ auto EmojiPack::collectAnimationsIndices(
|
|||
}
|
||||
|
||||
void EmojiPack::refreshAll() {
|
||||
auto items = base::flat_set<not_null<HistoryItem*>>();
|
||||
auto count = 0;
|
||||
for (const auto &[emoji, list] : _items) {
|
||||
refreshItems(list);
|
||||
// refreshItems(list); // This call changes _items!
|
||||
count += int(list.size());
|
||||
}
|
||||
items.reserve(count);
|
||||
for (const auto &[emoji, list] : _items) {
|
||||
// refreshItems(list); // This call changes _items!
|
||||
for (const auto &view : list) {
|
||||
items.emplace(view->data());
|
||||
}
|
||||
}
|
||||
refreshItems(items);
|
||||
refreshItems(_onlyCustomItems);
|
||||
}
|
||||
|
||||
|
@ -458,8 +470,18 @@ void EmojiPack::refreshItems(EmojiPtr emoji) {
|
|||
}
|
||||
|
||||
void EmojiPack::refreshItems(
|
||||
const base::flat_set<not_null<HistoryItem*>> &list) {
|
||||
for (const auto &item : list) {
|
||||
const base::flat_set<not_null<ViewElement*>> &list) {
|
||||
auto items = base::flat_set<not_null<HistoryItem*>>();
|
||||
items.reserve(list.size());
|
||||
for (const auto &view : list) {
|
||||
items.emplace(view->data());
|
||||
}
|
||||
refreshItems(items);
|
||||
}
|
||||
|
||||
void EmojiPack::refreshItems(
|
||||
const base::flat_set<not_null<HistoryItem*>> &items) {
|
||||
for (const auto &item : items) {
|
||||
_session->data().requestItemViewRefresh(item);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,6 +35,10 @@ class UniversalImages;
|
|||
} // namespace Emoji
|
||||
} // namespace Ui
|
||||
|
||||
namespace HistoryView {
|
||||
class Element;
|
||||
} // namespace HistoryView
|
||||
|
||||
namespace Stickers {
|
||||
|
||||
using IsolatedEmoji = Ui::Text::IsolatedEmoji;
|
||||
|
@ -48,6 +52,8 @@ struct LargeEmojiImage {
|
|||
|
||||
class EmojiPack final {
|
||||
public:
|
||||
using ViewElement = HistoryView::Element;
|
||||
|
||||
struct Sticker {
|
||||
DocumentData *document = nullptr;
|
||||
const Lottie::ColorReplacements *replacements = nullptr;
|
||||
|
@ -63,8 +69,8 @@ public:
|
|||
explicit EmojiPack(not_null<Main::Session*> session);
|
||||
~EmojiPack();
|
||||
|
||||
bool add(not_null<HistoryItem*> item);
|
||||
void remove(not_null<const HistoryItem*> item);
|
||||
bool add(not_null<ViewElement*> view);
|
||||
void remove(not_null<const ViewElement*> view);
|
||||
|
||||
[[nodiscard]] Sticker stickerForEmoji(EmojiPtr emoji);
|
||||
[[nodiscard]] Sticker stickerForEmoji(const IsolatedEmoji &emoji);
|
||||
|
@ -106,17 +112,18 @@ private:
|
|||
-> base::flat_map<uint64, base::flat_set<int>>;
|
||||
void refreshAll();
|
||||
void refreshItems(EmojiPtr emoji);
|
||||
void refreshItems(const base::flat_set<not_null<HistoryItem*>> &list);
|
||||
void refreshItems(const base::flat_set<not_null<ViewElement*>> &list);
|
||||
void refreshItems(const base::flat_set<not_null<HistoryItem*>> &items);
|
||||
|
||||
not_null<Main::Session*> _session;
|
||||
const not_null<Main::Session*> _session;
|
||||
base::flat_map<EmojiPtr, not_null<DocumentData*>> _map;
|
||||
base::flat_map<
|
||||
IsolatedEmoji,
|
||||
base::flat_set<not_null<HistoryItem*>>> _items;
|
||||
base::flat_set<not_null<HistoryView::Element*>>> _items;
|
||||
base::flat_map<EmojiPtr, std::weak_ptr<LargeEmojiImage>> _images;
|
||||
mtpRequestId _requestId = 0;
|
||||
|
||||
base::flat_set<not_null<HistoryItem*>> _onlyCustomItems;
|
||||
base::flat_set<not_null<HistoryView::Element*>> _onlyCustomItems;
|
||||
|
||||
int _animationsVersion = 0;
|
||||
base::flat_map<
|
||||
|
|
|
@ -1529,11 +1529,10 @@ rpl::producer<not_null<HistoryItem*>> Session::itemDataChanges() const {
|
|||
|
||||
void Session::requestItemTextRefresh(not_null<HistoryItem*> item) {
|
||||
if (const auto i = _views.find(item); i != _views.end()) {
|
||||
for (const auto view : i->second) {
|
||||
if (const auto media = view->media()) {
|
||||
media->parentTextUpdated();
|
||||
}
|
||||
for (const auto &view : i->second) {
|
||||
view->itemTextUpdated();
|
||||
}
|
||||
requestItemResize(item);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1573,6 +1572,7 @@ rpl::producer<not_null<const HistoryItem*>> Session::itemRemoved(
|
|||
}
|
||||
|
||||
void Session::notifyViewRemoved(not_null<const ViewElement*> view) {
|
||||
_shownSpoilers.remove(view);
|
||||
_viewRemoved.fire_copy(view);
|
||||
}
|
||||
|
||||
|
@ -1672,24 +1672,15 @@ void Session::unloadHeavyViewParts(
|
|||
}
|
||||
}
|
||||
|
||||
void Session::registerShownSpoiler(FullMsgId id) {
|
||||
if (const auto item = message(id)) {
|
||||
_shownSpoilers.emplace(item);
|
||||
}
|
||||
}
|
||||
|
||||
void Session::unregisterShownSpoiler(FullMsgId id) {
|
||||
if (const auto item = message(id)) {
|
||||
_shownSpoilers.remove(item);
|
||||
}
|
||||
void Session::registerShownSpoiler(not_null<ViewElement*> view) {
|
||||
_shownSpoilers.emplace(view);
|
||||
}
|
||||
|
||||
void Session::hideShownSpoilers() {
|
||||
for (const auto &item : _shownSpoilers) {
|
||||
item->hideSpoilers();
|
||||
requestItemTextRefresh(item);
|
||||
for (const auto &view : base::take(_shownSpoilers)) {
|
||||
view->hideSpoilers();
|
||||
requestViewRepaint(view);
|
||||
}
|
||||
_shownSpoilers = base::flat_set<not_null<HistoryItem*>>();
|
||||
}
|
||||
|
||||
void Session::removeMegagroupParticipant(
|
||||
|
@ -2156,7 +2147,6 @@ void Session::removeDependencyMessage(not_null<HistoryItem*> item) {
|
|||
void Session::unregisterMessage(not_null<HistoryItem*> item) {
|
||||
const auto peerId = item->history()->peer->id;
|
||||
const auto itemId = item->id;
|
||||
_shownSpoilers.remove(item);
|
||||
_itemRemoved.fire_copy(item);
|
||||
session().changes().messageUpdated(
|
||||
item,
|
||||
|
|
|
@ -296,8 +296,7 @@ public:
|
|||
int from,
|
||||
int till);
|
||||
|
||||
void registerShownSpoiler(FullMsgId id);
|
||||
void unregisterShownSpoiler(FullMsgId id);
|
||||
void registerShownSpoiler(not_null<ViewElement*> view);
|
||||
void hideShownSpoilers();
|
||||
|
||||
using MegagroupParticipant = std::tuple<
|
||||
|
@ -956,7 +955,7 @@ private:
|
|||
rpl::event_stream<InviteToCall> _invitesToCalls;
|
||||
base::flat_map<uint64, base::flat_set<not_null<UserData*>>> _invitedToCallUsers;
|
||||
|
||||
base::flat_set<not_null<HistoryItem*>> _shownSpoilers;
|
||||
base::flat_set<not_null<ViewElement*>> _shownSpoilers;
|
||||
|
||||
History *_topPromoted = nullptr;
|
||||
|
||||
|
|
|
@ -220,68 +220,71 @@ struct StickerSetIdentifier {
|
|||
}
|
||||
};
|
||||
|
||||
enum class MessageFlag : uint32 {
|
||||
HideEdited = (1U << 0),
|
||||
Legacy = (1U << 1),
|
||||
HasReplyMarkup = (1U << 2),
|
||||
HasFromId = (1U << 3),
|
||||
HasPostAuthor = (1U << 4),
|
||||
HasViews = (1U << 5),
|
||||
HasReplyInfo = (1U << 6),
|
||||
CanViewReactions = (1U << 7),
|
||||
AdminLogEntry = (1U << 8),
|
||||
Post = (1U << 9),
|
||||
Silent = (1U << 10),
|
||||
Outgoing = (1U << 11),
|
||||
Pinned = (1U << 12),
|
||||
MediaIsUnread = (1U << 13),
|
||||
HasUnreadReaction = (1U << 14),
|
||||
MentionsMe = (1U << 15),
|
||||
IsOrWasScheduled = (1U << 16),
|
||||
NoForwards = (1U << 17),
|
||||
enum class MessageFlag : uint64 {
|
||||
HideEdited = (1ULL << 0),
|
||||
Legacy = (1ULL << 1),
|
||||
HasReplyMarkup = (1ULL << 2),
|
||||
HasFromId = (1ULL << 3),
|
||||
HasPostAuthor = (1ULL << 4),
|
||||
HasViews = (1ULL << 5),
|
||||
HasReplyInfo = (1ULL << 6),
|
||||
CanViewReactions = (1ULL << 7),
|
||||
AdminLogEntry = (1ULL << 8),
|
||||
Post = (1ULL << 9),
|
||||
Silent = (1ULL << 10),
|
||||
Outgoing = (1ULL << 11),
|
||||
Pinned = (1ULL << 12),
|
||||
MediaIsUnread = (1ULL << 13),
|
||||
HasUnreadReaction = (1ULL << 14),
|
||||
MentionsMe = (1ULL << 15),
|
||||
IsOrWasScheduled = (1ULL << 16),
|
||||
NoForwards = (1ULL << 17),
|
||||
|
||||
// Needs to return back to inline mode.
|
||||
HasSwitchInlineButton = (1U << 18),
|
||||
HasSwitchInlineButton = (1ULL << 18),
|
||||
|
||||
// For "shared links" indexing.
|
||||
HasTextLinks = (1U << 19),
|
||||
HasTextLinks = (1ULL << 19),
|
||||
|
||||
// Group / channel create or migrate service message.
|
||||
IsGroupEssential = (1U << 20),
|
||||
IsGroupEssential = (1ULL << 20),
|
||||
|
||||
// Edited media is generated on the client
|
||||
// and should not update media from server.
|
||||
IsLocalUpdateMedia = (1U << 21),
|
||||
IsLocalUpdateMedia = (1ULL << 21),
|
||||
|
||||
// Sent from inline bot, need to re-set media when sent.
|
||||
FromInlineBot = (1U << 22),
|
||||
FromInlineBot = (1ULL << 22),
|
||||
|
||||
// Generated on the client side and should be unread.
|
||||
ClientSideUnread = (1U << 23),
|
||||
ClientSideUnread = (1ULL << 23),
|
||||
|
||||
// In a supergroup.
|
||||
HasAdminBadge = (1U << 24),
|
||||
HasAdminBadge = (1ULL << 24),
|
||||
|
||||
// Outgoing message that is being sent.
|
||||
BeingSent = (1U << 25),
|
||||
BeingSent = (1ULL << 25),
|
||||
|
||||
// Outgoing message and failed to be sent.
|
||||
SendingFailed = (1U << 26),
|
||||
SendingFailed = (1ULL << 26),
|
||||
|
||||
// No media and only a several emoji or an only custom emoji text.
|
||||
SpecialOnlyEmoji = (1U << 27),
|
||||
SpecialOnlyEmoji = (1ULL << 27),
|
||||
|
||||
// Message existing in the message history.
|
||||
HistoryEntry = (1U << 28),
|
||||
HistoryEntry = (1ULL << 28),
|
||||
|
||||
// Local message, not existing on the server.
|
||||
Local = (1U << 29),
|
||||
Local = (1ULL << 29),
|
||||
|
||||
// Fake message for some UI element.
|
||||
FakeHistoryItem = (1U << 30),
|
||||
FakeHistoryItem = (1ULL << 30),
|
||||
|
||||
// Contact sign-up message, notification should be skipped for Silent.
|
||||
IsContactSignUp = (1U << 31),
|
||||
IsContactSignUp = (1ULL << 31),
|
||||
|
||||
// Optimization for item text custom emoji repainting.
|
||||
CustomEmojiRepainting = (1ULL << 32),
|
||||
};
|
||||
inline constexpr bool is_flag_type(MessageFlag) { return true; }
|
||||
using MessageFlags = base::flags<MessageFlag>;
|
||||
|
|
|
@ -724,7 +724,7 @@ void GenerateItems(
|
|||
history->nextNonHistoryEntryId(),
|
||||
MessageFlag::AdminLogEntry,
|
||||
date,
|
||||
message,
|
||||
std::move(message),
|
||||
peerToUser(from->id),
|
||||
photo));
|
||||
};
|
||||
|
@ -1060,7 +1060,7 @@ void GenerateItems(
|
|||
history->nextNonHistoryEntryId(),
|
||||
MessageFlag::AdminLogEntry,
|
||||
date,
|
||||
message,
|
||||
std::move(message),
|
||||
peerToUser(from->id)));
|
||||
}
|
||||
};
|
||||
|
@ -1137,7 +1137,7 @@ void GenerateItems(
|
|||
history->nextNonHistoryEntryId(),
|
||||
MessageFlag::AdminLogEntry,
|
||||
date,
|
||||
message,
|
||||
std::move(message),
|
||||
peerToUser(from->id)));
|
||||
}
|
||||
};
|
||||
|
@ -1233,7 +1233,7 @@ void GenerateItems(
|
|||
history->nextNonHistoryEntryId(),
|
||||
MessageFlag::AdminLogEntry,
|
||||
date,
|
||||
message,
|
||||
std::move(message),
|
||||
peerToUser(from->id)));
|
||||
};
|
||||
|
||||
|
@ -1308,7 +1308,7 @@ void GenerateItems(
|
|||
history->nextNonHistoryEntryId(),
|
||||
MessageFlag::AdminLogEntry,
|
||||
date,
|
||||
message,
|
||||
std::move(message),
|
||||
peerToUser(from->id),
|
||||
nullptr));
|
||||
};
|
||||
|
|
|
@ -2312,7 +2312,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
|||
const auto media = (view ? view->media() : nullptr);
|
||||
const auto mediaHasTextForCopy = media && media->hasTextForCopy();
|
||||
if (const auto document = media ? media->getDocument() : nullptr) {
|
||||
if (!item->isIsolatedEmoji() && document->sticker()) {
|
||||
if (!view->isIsolatedEmoji() && document->sticker()) {
|
||||
if (document->sticker()->set) {
|
||||
_menu->addAction(document->isStickerSetInstalled() ? tr::lng_context_pack_info(tr::now) : tr::lng_context_pack_add(tr::now), [=] {
|
||||
showStickerPackInfo(document);
|
||||
|
|
|
@ -376,8 +376,8 @@ void HistoryItem::invalidateChatListEntry() {
|
|||
}
|
||||
|
||||
void HistoryItem::customEmojiRepaint() {
|
||||
if (!_customEmojiRepaintScheduled) {
|
||||
_customEmojiRepaintScheduled = true;
|
||||
if (!(_flags & MessageFlag::CustomEmojiRepainting)) {
|
||||
_flags |= MessageFlag::CustomEmojiRepainting;
|
||||
history()->owner().requestItemRepaint(this);
|
||||
}
|
||||
}
|
||||
|
@ -1250,7 +1250,7 @@ MessageGroupId HistoryItem::groupId() const {
|
|||
}
|
||||
|
||||
bool HistoryItem::isEmpty() const {
|
||||
return _text.isEmpty()
|
||||
return _text.empty()
|
||||
&& !_media
|
||||
&& !Has<HistoryMessageLogEntryOriginal>();
|
||||
}
|
||||
|
@ -1260,7 +1260,7 @@ TextWithEntities HistoryItem::notificationText() const {
|
|||
if (_media && !isService()) {
|
||||
return _media->notificationText();
|
||||
} else if (!emptyText()) {
|
||||
return _text.toTextWithEntities();
|
||||
return _text;
|
||||
}
|
||||
return TextWithEntities();
|
||||
}();
|
||||
|
@ -1271,14 +1271,17 @@ TextWithEntities HistoryItem::notificationText() const {
|
|||
Ui::kQEllipsis);
|
||||
}
|
||||
|
||||
const std::vector<ClickHandlerPtr> &HistoryItem::customTextLinks() const {
|
||||
static const auto result = std::vector<ClickHandlerPtr>();
|
||||
return result;
|
||||
}
|
||||
|
||||
ItemPreview HistoryItem::toPreview(ToPreviewOptions options) const {
|
||||
auto result = [&]() -> ItemPreview {
|
||||
if (_media) {
|
||||
return _media->toPreview(options);
|
||||
} else if (!emptyText()) {
|
||||
return {
|
||||
.text = _text.toTextWithEntities()
|
||||
};
|
||||
return { .text = _text };
|
||||
}
|
||||
return {};
|
||||
}();
|
||||
|
@ -1325,14 +1328,6 @@ TextWithEntities HistoryItem::inReplyText() const {
|
|||
}).text;
|
||||
}
|
||||
|
||||
Ui::Text::IsolatedEmoji HistoryItem::isolatedEmoji() const {
|
||||
return {};
|
||||
}
|
||||
|
||||
Ui::Text::OnlyCustomEmoji HistoryItem::onlyCustomEmoji() const {
|
||||
return {};
|
||||
}
|
||||
|
||||
HistoryItem::~HistoryItem() {
|
||||
applyTTL(0);
|
||||
}
|
||||
|
@ -1470,14 +1465,14 @@ not_null<HistoryItem*> HistoryItem::Create(
|
|||
data.vdate().v,
|
||||
data.vfrom_id() ? peerFromMTP(*data.vfrom_id()) : PeerId(0));
|
||||
} else if (checked == MediaCheckResult::Empty) {
|
||||
const auto text = HistoryService::PreparedText{
|
||||
auto text = HistoryService::PreparedText{
|
||||
tr::lng_message_empty(tr::now, Ui::Text::WithEntities)
|
||||
};
|
||||
return history->makeServiceMessage(
|
||||
id,
|
||||
FlagsFromMTP(id, data.vflags().v, localFlags),
|
||||
data.vdate().v,
|
||||
text,
|
||||
std::move(text),
|
||||
data.vfrom_id() ? peerFromMTP(*data.vfrom_id()) : PeerId(0));
|
||||
} else if (checked == MediaCheckResult::HasTimeToLive) {
|
||||
return history->makeServiceMessage(id, data, localFlags);
|
||||
|
@ -1489,9 +1484,12 @@ not_null<HistoryItem*> HistoryItem::Create(
|
|||
}
|
||||
return history->makeServiceMessage(id, data, localFlags);
|
||||
}, [&](const MTPDmessageEmpty &data) -> HistoryItem* {
|
||||
const auto text = HistoryService::PreparedText{
|
||||
tr::lng_message_empty(tr::now, Ui::Text::WithEntities)
|
||||
};
|
||||
return history->makeServiceMessage(id, localFlags, TimeId(0), text);
|
||||
return history->makeServiceMessage(
|
||||
id,
|
||||
localFlags,
|
||||
TimeId(0),
|
||||
HistoryService::PreparedText{ tr::lng_message_empty(
|
||||
tr::now,
|
||||
Ui::Text::WithEntities) });
|
||||
});
|
||||
}
|
||||
|
|
|
@ -195,14 +195,6 @@ public:
|
|||
[[nodiscard]] bool isGroupMigrate() const {
|
||||
return isGroupEssential() && isEmpty();
|
||||
}
|
||||
[[nodiscard]] bool isIsolatedEmoji() const {
|
||||
return (_flags & MessageFlag::SpecialOnlyEmoji)
|
||||
&& _text.isIsolatedEmoji();
|
||||
}
|
||||
[[nodiscard]] bool isOnlyCustomEmoji() const {
|
||||
return (_flags & MessageFlag::SpecialOnlyEmoji)
|
||||
&& _text.isOnlyCustomEmoji();
|
||||
}
|
||||
[[nodiscard]] bool hasViews() const {
|
||||
return _flags & MessageFlag::HasViews;
|
||||
}
|
||||
|
@ -322,8 +314,6 @@ public:
|
|||
[[nodiscard]] virtual ItemPreview toPreview(
|
||||
ToPreviewOptions options) const;
|
||||
[[nodiscard]] virtual TextWithEntities inReplyText() const;
|
||||
[[nodiscard]] virtual Ui::Text::IsolatedEmoji isolatedEmoji() const;
|
||||
[[nodiscard]] virtual Ui::Text::OnlyCustomEmoji onlyCustomEmoji() const;
|
||||
[[nodiscard]] virtual TextWithEntities originalText() const {
|
||||
return TextWithEntities();
|
||||
}
|
||||
|
@ -331,6 +321,8 @@ public:
|
|||
-> TextWithEntities {
|
||||
return TextWithEntities();
|
||||
}
|
||||
[[nodiscard]] virtual auto customTextLinks() const
|
||||
-> const std::vector<ClickHandlerPtr> &;
|
||||
[[nodiscard]] virtual TextForMimeData clipboardText() const {
|
||||
return TextForMimeData();
|
||||
}
|
||||
|
@ -356,11 +348,9 @@ public:
|
|||
virtual void setRealId(MsgId newId);
|
||||
virtual void incrementReplyToTopCounter() {
|
||||
}
|
||||
virtual void hideSpoilers() {
|
||||
}
|
||||
|
||||
[[nodiscard]] bool emptyText() const {
|
||||
return _text.isEmpty();
|
||||
return _text.empty();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool canPin() const;
|
||||
|
@ -414,9 +404,6 @@ public:
|
|||
[[nodiscard]] bool computeDropForwardedInfo() const;
|
||||
virtual void setText(const TextWithEntities &textWithEntities) {
|
||||
}
|
||||
[[nodiscard]] virtual bool textHasLinks() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] MsgId replyToId() const;
|
||||
[[nodiscard]] MsgId replyToTop() const;
|
||||
|
@ -494,10 +481,7 @@ protected:
|
|||
void applyTTL(const MTPDmessageService &data);
|
||||
void applyTTL(TimeId destroyAt);
|
||||
|
||||
Ui::Text::String _text = { st::msgMinWidth };
|
||||
int _textWidth = -1;
|
||||
int _textHeight = 0;
|
||||
bool _customEmojiRepaintScheduled = false;
|
||||
TextWithEntities _text;
|
||||
|
||||
struct SavedMediaData {
|
||||
TextWithEntities text;
|
||||
|
|
|
@ -15,7 +15,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/history_unread_things.h"
|
||||
#include "history/view/history_view_service_message.h"
|
||||
#include "history/view/history_view_context_menu.h" // CopyPostLink.
|
||||
#include "history/view/history_view_spoiler_click_handler.h"
|
||||
#include "history/view/media/history_view_media.h" // AddTimestampLinks.
|
||||
#include "chat_helpers/stickers_emoji_pack.h"
|
||||
#include "main/main_session.h"
|
||||
|
@ -24,8 +23,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "boxes/share_box.h"
|
||||
#include "ui/text/text_isolated_emoji.h"
|
||||
#include "ui/text/format_values.h"
|
||||
#include "ui/item_text_options.h"
|
||||
#include "core/ui_integration.h"
|
||||
#include "storage/storage_shared_media.h"
|
||||
#include "mtproto/mtproto_config.h"
|
||||
#include "data/notify/data_notify_settings.h"
|
||||
|
@ -100,10 +97,7 @@ namespace {
|
|||
|
||||
[[nodiscard]] TextWithEntities EnsureNonEmpty(
|
||||
const TextWithEntities &text = TextWithEntities()) {
|
||||
if (!text.text.isEmpty()) {
|
||||
return text;
|
||||
}
|
||||
return { QString::fromUtf8(":-("), EntitiesInText() };
|
||||
return !text.text.isEmpty() ? text : TextWithEntities{ u":-("_q };
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -384,7 +378,7 @@ HistoryMessage::HistoryMessage(
|
|||
_media = std::make_unique<Data::MediaCall>(
|
||||
this,
|
||||
Data::ComputeCallData(data));
|
||||
setEmptyText();
|
||||
setTextValue({});
|
||||
}, [](const auto &) {
|
||||
Unexpected("Service message action type in HistoryMessage.");
|
||||
});
|
||||
|
@ -607,7 +601,7 @@ HistoryMessage::HistoryMessage(
|
|||
std::move(markup));
|
||||
|
||||
_media = std::make_unique<Data::MediaGame>(this, game);
|
||||
setEmptyText();
|
||||
setTextValue({});
|
||||
}
|
||||
|
||||
HistoryMessage::HistoryMessage(
|
||||
|
@ -863,10 +857,6 @@ void HistoryMessage::setCommentsItemId(FullMsgId id) {
|
|||
}
|
||||
}
|
||||
|
||||
void HistoryMessage::hideSpoilers() {
|
||||
HistoryView::HideSpoilers(_text);
|
||||
}
|
||||
|
||||
bool HistoryMessage::updateDependencyItem() {
|
||||
if (const auto reply = Get<HistoryMessageReply>()) {
|
||||
const auto documentId = reply->replyToDocumentId;
|
||||
|
@ -875,7 +865,7 @@ bool HistoryMessage::updateDependencyItem() {
|
|||
const auto mediaIdChanged = (documentId != reply->replyToDocumentId)
|
||||
|| (webpageId != reply->replyToWebPageId);
|
||||
if (mediaIdChanged && generateLocalEntitiesByReply()) {
|
||||
reapplyText();
|
||||
history()->owner().requestItemTextRefresh(this);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -1315,7 +1305,7 @@ void HistoryMessage::applyEdition(const MTPDmessageService &message) {
|
|||
const auto wasGrouped = history()->owner().groups().isGrouped(this);
|
||||
setReplyMarkup({});
|
||||
refreshMedia(nullptr);
|
||||
setEmptyText();
|
||||
setTextValue({});
|
||||
changeViewsCount(-1);
|
||||
setForwardsCount(-1);
|
||||
if (wasGrouped) {
|
||||
|
@ -1337,14 +1327,13 @@ void HistoryMessage::applyEdition(const MTPMessageExtendedMedia &media) {
|
|||
void HistoryMessage::updateSentContent(
|
||||
const TextWithEntities &textWithEntities,
|
||||
const MTPMessageMedia *media) {
|
||||
const auto isolated = isolatedEmoji();
|
||||
setText(textWithEntities);
|
||||
if (_flags & MessageFlag::FromInlineBot) {
|
||||
if (!media || !_media || !_media->updateInlineResultMedia(*media)) {
|
||||
refreshSentMedia(media);
|
||||
}
|
||||
_flags &= ~MessageFlag::FromInlineBot;
|
||||
} else if (media || _media || !isolated || isolated != isolatedEmoji()) {
|
||||
} else if (media || _media) {
|
||||
if (!media || !_media || !_media->updateSentMedia(*media)) {
|
||||
refreshSentMedia(media);
|
||||
}
|
||||
|
@ -1509,65 +1498,16 @@ void HistoryMessage::setText(const TextWithEntities &textWithEntities) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_media && _media->consumeMessageText(textWithEntities)) {
|
||||
setEmptyText();
|
||||
return;
|
||||
}
|
||||
|
||||
clearSpecialOnlyEmoji();
|
||||
const auto context = Core::MarkedTextContext{
|
||||
.session = &history()->session(),
|
||||
.customEmojiRepaint = [=] { customEmojiRepaint(); },
|
||||
};
|
||||
_text.setMarkedText(
|
||||
st::messageTextStyle,
|
||||
withLocalEntities(textWithEntities),
|
||||
Ui::ItemTextOptions(this),
|
||||
context);
|
||||
HistoryView::FillTextWithAnimatedSpoilers(_text);
|
||||
if (!textWithEntities.text.isEmpty() && _text.isEmpty()) {
|
||||
// If server has allowed some text that we've trim-ed entirely,
|
||||
// just replace it with something so that UI won't look buggy.
|
||||
_text.setMarkedText(
|
||||
st::messageTextStyle,
|
||||
EnsureNonEmpty(),
|
||||
Ui::ItemTextOptions(this));
|
||||
} else if (!_media) {
|
||||
checkSpecialOnlyEmoji();
|
||||
}
|
||||
|
||||
_textWidth = -1;
|
||||
_textHeight = 0;
|
||||
setTextValue((_media && _media->consumeMessageText(textWithEntities))
|
||||
? TextWithEntities()
|
||||
: std::move(textWithEntities));
|
||||
}
|
||||
|
||||
void HistoryMessage::reapplyText() {
|
||||
setText(originalText());
|
||||
history()->owner().requestItemResize(this);
|
||||
}
|
||||
|
||||
void HistoryMessage::setEmptyText() {
|
||||
clearSpecialOnlyEmoji();
|
||||
_text.setMarkedText(
|
||||
st::messageTextStyle,
|
||||
{ QString(), EntitiesInText() },
|
||||
Ui::ItemTextOptions(this));
|
||||
|
||||
_textWidth = -1;
|
||||
_textHeight = 0;
|
||||
}
|
||||
|
||||
void HistoryMessage::clearSpecialOnlyEmoji() {
|
||||
if (!(_flags & MessageFlag::SpecialOnlyEmoji)) {
|
||||
return;
|
||||
}
|
||||
history()->session().emojiStickersPack().remove(this);
|
||||
_flags &= ~MessageFlag::SpecialOnlyEmoji;
|
||||
}
|
||||
|
||||
void HistoryMessage::checkSpecialOnlyEmoji() {
|
||||
if (history()->session().emojiStickersPack().add(this)) {
|
||||
_flags |= MessageFlag::SpecialOnlyEmoji;
|
||||
void HistoryMessage::setTextValue(TextWithEntities text) {
|
||||
const auto had = !_text.empty();
|
||||
_text = std::move(text);
|
||||
if (had) {
|
||||
history()->owner().requestItemTextRefresh(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1616,19 +1556,8 @@ void HistoryMessage::setReplyMarkup(HistoryMessageMarkupData &&markup) {
|
|||
}
|
||||
}
|
||||
|
||||
Ui::Text::IsolatedEmoji HistoryMessage::isolatedEmoji() const {
|
||||
return _text.toIsolatedEmoji();
|
||||
}
|
||||
|
||||
Ui::Text::OnlyCustomEmoji HistoryMessage::onlyCustomEmoji() const {
|
||||
return _text.toOnlyCustomEmoji();
|
||||
}
|
||||
|
||||
TextWithEntities HistoryMessage::originalText() const {
|
||||
if (emptyText()) {
|
||||
return { QString(), EntitiesInText() };
|
||||
}
|
||||
return _text.toTextWithEntities();
|
||||
return _text;
|
||||
}
|
||||
|
||||
TextWithEntities HistoryMessage::originalTextWithLocalEntities() const {
|
||||
|
@ -1636,14 +1565,7 @@ TextWithEntities HistoryMessage::originalTextWithLocalEntities() const {
|
|||
}
|
||||
|
||||
TextForMimeData HistoryMessage::clipboardText() const {
|
||||
if (emptyText()) {
|
||||
return TextForMimeData();
|
||||
}
|
||||
return _text.toTextForMimeData();
|
||||
}
|
||||
|
||||
bool HistoryMessage::textHasLinks() const {
|
||||
return emptyText() ? false : _text.hasLinks();
|
||||
return TextForMimeData::WithExpandedLinks(_text);
|
||||
}
|
||||
|
||||
bool HistoryMessage::changeViewsCount(int count) {
|
||||
|
@ -1923,7 +1845,7 @@ void HistoryMessage::dependencyItemRemoved(HistoryItem *dependency) {
|
|||
reply->itemRemoved(this, dependency);
|
||||
if (documentId != reply->replyToDocumentId
|
||||
&& generateLocalEntitiesByReply()) {
|
||||
reapplyText();
|
||||
history()->owner().requestItemTextRefresh(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -181,13 +181,10 @@ public:
|
|||
[[nodiscard]] Storage::SharedMediaTypesMask sharedMediaTypes() const override;
|
||||
|
||||
void setText(const TextWithEntities &textWithEntities) override;
|
||||
[[nodiscard]] Ui::Text::IsolatedEmoji isolatedEmoji() const override;
|
||||
[[nodiscard]] Ui::Text::OnlyCustomEmoji onlyCustomEmoji() const override;
|
||||
[[nodiscard]] TextWithEntities originalText() const override;
|
||||
[[nodiscard]] auto originalTextWithLocalEntities() const
|
||||
-> TextWithEntities override;
|
||||
[[nodiscard]] TextForMimeData clipboardText() const override;
|
||||
[[nodiscard]] bool textHasLinks() const override;
|
||||
|
||||
[[nodiscard]] int viewsCount() const override;
|
||||
[[nodiscard]] int repliesCount() const override;
|
||||
|
@ -212,7 +209,6 @@ public:
|
|||
[[nodiscard]] MsgId dependencyMsgId() const override {
|
||||
return replyToId();
|
||||
}
|
||||
void hideSpoilers() override;
|
||||
|
||||
void applySentMessage(const MTPDmessage &data) override;
|
||||
void applySentMessage(
|
||||
|
@ -227,7 +223,7 @@ public:
|
|||
~HistoryMessage();
|
||||
|
||||
private:
|
||||
void setEmptyText();
|
||||
void setTextValue(TextWithEntities text);
|
||||
[[nodiscard]] bool isTooOldForEdit(TimeId now) const;
|
||||
[[nodiscard]] bool isLegacyMessage() const {
|
||||
return _flags & MessageFlag::Legacy;
|
||||
|
@ -235,9 +231,6 @@ private:
|
|||
|
||||
[[nodiscard]] bool checkCommentsLinkedChat(ChannelId id) const;
|
||||
|
||||
void clearSpecialOnlyEmoji();
|
||||
void checkSpecialOnlyEmoji();
|
||||
|
||||
// For an invoice button we replace the button text with a "Receipt" key.
|
||||
// It should show the receipt for the payed invoice. Still let mobile apps do that.
|
||||
void replaceBuyWithReceiptInMarkup();
|
||||
|
@ -271,7 +264,6 @@ private:
|
|||
[[nodiscard]] bool generateLocalEntitiesByReply() const;
|
||||
[[nodiscard]] TextWithEntities withLocalEntities(
|
||||
const TextWithEntities &textWithEntities) const;
|
||||
void reapplyText();
|
||||
|
||||
[[nodiscard]] bool checkRepliesPts(
|
||||
const HistoryMessageRepliesData &data) const;
|
||||
|
|
|
@ -20,7 +20,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/history_item_components.h"
|
||||
#include "history/view/history_view_service_message.h"
|
||||
#include "history/view/history_view_item_preview.h"
|
||||
#include "history/view/history_view_spoiler_click_handler.h"
|
||||
#include "data/data_folder.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_media_types.h"
|
||||
|
@ -32,7 +31,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_group_call.h" // Data::GroupCall::id().
|
||||
#include "core/application.h"
|
||||
#include "core/click_handler_types.h"
|
||||
#include "core/ui_integration.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "base/timer_rpl.h"
|
||||
#include "calls/calls_instance.h" // Core::App().calls().joinGroupCall.
|
||||
|
@ -42,7 +40,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "storage/storage_shared_media.h"
|
||||
#include "payments/payments_checkout_process.h" // CheckoutProcess::Start.
|
||||
#include "ui/text/format_values.h"
|
||||
#include "ui/text/text_options.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
|
||||
namespace {
|
||||
|
@ -636,8 +633,8 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
|
|||
return result;
|
||||
};
|
||||
|
||||
const auto messageText = action.match([&](
|
||||
const MTPDmessageActionChatAddUser &data) {
|
||||
setServiceText(action.match([&](
|
||||
const MTPDmessageActionChatAddUser &data) {
|
||||
return prepareChatAddUserText(data);
|
||||
}, [&](const MTPDmessageActionChatJoinedByLink &data) {
|
||||
return prepareChatJoinedByLink(data);
|
||||
|
@ -714,9 +711,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
|
|||
return PreparedText{
|
||||
tr::lng_message_empty(tr::now, Ui::Text::WithEntities)
|
||||
};
|
||||
});
|
||||
|
||||
setServiceText(messageText);
|
||||
}));
|
||||
|
||||
// Additional information.
|
||||
applyAction(action);
|
||||
|
@ -1219,11 +1214,11 @@ HistoryService::HistoryService(
|
|||
MsgId id,
|
||||
MessageFlags flags,
|
||||
TimeId date,
|
||||
const PreparedText &message,
|
||||
PreparedText &&message,
|
||||
PeerId from,
|
||||
PhotoData *photo)
|
||||
: HistoryItem(history, id, flags, date, from) {
|
||||
setServiceText(message);
|
||||
setServiceText(std::move(message));
|
||||
if (photo) {
|
||||
_media = std::make_unique<Data::MediaPhoto>(
|
||||
this,
|
||||
|
@ -1279,28 +1274,13 @@ ClickHandlerPtr HistoryService::fromLink() const {
|
|||
return _from->createOpenLink();
|
||||
}
|
||||
|
||||
void HistoryService::setServiceText(const PreparedText &prepared) {
|
||||
const auto context = Core::MarkedTextContext{
|
||||
.session = &history()->session(),
|
||||
.customEmojiRepaint = [=] { customEmojiRepaint(); },
|
||||
};
|
||||
_text.setMarkedText(
|
||||
st::serviceTextStyle,
|
||||
prepared.text,
|
||||
Ui::ItemTextServiceOptions(),
|
||||
context);
|
||||
HistoryView::FillTextWithAnimatedSpoilers(_text);
|
||||
auto linkIndex = 0;
|
||||
for (const auto &link : prepared.links) {
|
||||
// Link indices start with 1.
|
||||
_text.setLink(++linkIndex, link);
|
||||
void HistoryService::setServiceText(PreparedText &&prepared) {
|
||||
const auto had = !_text.empty();
|
||||
_text = std::move(prepared.text);
|
||||
_textLinks = std::move(prepared.links);
|
||||
if (had) {
|
||||
history()->owner().requestItemTextRefresh(this);
|
||||
}
|
||||
_textWidth = -1;
|
||||
_textHeight = 0;
|
||||
}
|
||||
|
||||
void HistoryService::hideSpoilers() {
|
||||
HistoryView::HideSpoilers(_text);
|
||||
}
|
||||
|
||||
void HistoryService::markMediaAsReadHook() {
|
||||
|
@ -1506,6 +1486,10 @@ void HistoryService::createFromMtp(const MTPDmessageService &message) {
|
|||
setMessageByAction(message.vaction());
|
||||
}
|
||||
|
||||
const std::vector<ClickHandlerPtr> &HistoryService::customTextLinks() const {
|
||||
return _textLinks;
|
||||
}
|
||||
|
||||
void HistoryService::applyEdition(const MTPDmessageService &message) {
|
||||
clearDependency();
|
||||
UpdateComponents(0);
|
||||
|
@ -1525,8 +1509,6 @@ void HistoryService::removeMedia() {
|
|||
if (!_media) return;
|
||||
|
||||
_media.reset();
|
||||
_textWidth = -1;
|
||||
_textHeight = 0;
|
||||
history()->owner().requestItemResize(this);
|
||||
}
|
||||
|
||||
|
@ -1552,7 +1534,7 @@ void HistoryService::updateDependentText() {
|
|||
}
|
||||
|
||||
void HistoryService::updateText(PreparedText &&text) {
|
||||
setServiceText(text);
|
||||
setServiceText(std::move(text));
|
||||
history()->owner().requestItemResize(this);
|
||||
invalidateChatListEntry();
|
||||
history()->owner().updateDependentMessages(this);
|
||||
|
|
|
@ -77,7 +77,7 @@ class HistoryService : public HistoryItem {
|
|||
public:
|
||||
struct PreparedText {
|
||||
TextWithEntities text;
|
||||
QList<ClickHandlerPtr> links;
|
||||
std::vector<ClickHandlerPtr> links;
|
||||
};
|
||||
|
||||
HistoryService(
|
||||
|
@ -95,7 +95,7 @@ public:
|
|||
MsgId id,
|
||||
MessageFlags flags,
|
||||
TimeId date,
|
||||
const PreparedText &message,
|
||||
PreparedText &&message,
|
||||
PeerId from = 0,
|
||||
PhotoData *photo = nullptr);
|
||||
|
||||
|
@ -113,6 +113,8 @@ public:
|
|||
return true;
|
||||
}
|
||||
|
||||
const std::vector<ClickHandlerPtr> &customTextLinks() const override;
|
||||
|
||||
void applyEdition(const MTPDmessageService &message) override;
|
||||
crl::time getSelfDestructIn(crl::time now) override;
|
||||
|
||||
|
@ -131,9 +133,7 @@ public:
|
|||
not_null<HistoryView::ElementDelegate*> delegate,
|
||||
HistoryView::Element *replacing = nullptr) override;
|
||||
|
||||
void setServiceText(const PreparedText &prepared);
|
||||
|
||||
void hideSpoilers() override;
|
||||
void setServiceText(PreparedText &&prepared);
|
||||
|
||||
~HistoryService();
|
||||
|
||||
|
@ -187,6 +187,8 @@ private:
|
|||
|
||||
friend class HistoryView::Service;
|
||||
|
||||
std::vector<ClickHandlerPtr> _textLinks;
|
||||
|
||||
};
|
||||
|
||||
[[nodiscard]] not_null<HistoryService*> GenerateJoinedMessage(
|
||||
|
|
|
@ -21,11 +21,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/view/reactions/history_view_reactions_button.h"
|
||||
#include "history/view/reactions/history_view_reactions.h"
|
||||
#include "history/view/history_view_cursor_state.h"
|
||||
#include "history/view/history_view_spoiler_click_handler.h"
|
||||
#include "history/history.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "core/application.h"
|
||||
#include "core/core_settings.h"
|
||||
#include "core/click_handler_types.h"
|
||||
#include "core/ui_integration.h"
|
||||
#include "main/main_session.h"
|
||||
#include "main/main_domain.h"
|
||||
#include "chat_helpers/stickers_emoji_pack.h"
|
||||
|
@ -34,6 +36,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/chat/chat_style.h"
|
||||
#include "ui/toast/toast.h"
|
||||
#include "ui/toasts/common_toasts.h"
|
||||
#include "ui/text/text_options.h"
|
||||
#include "ui/item_text_options.h"
|
||||
#include "ui/painter.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_groups.h"
|
||||
|
@ -355,11 +359,16 @@ void DateBadge::paint(
|
|||
Element::Element(
|
||||
not_null<ElementDelegate*> delegate,
|
||||
not_null<HistoryItem*> data,
|
||||
Element *replacing)
|
||||
Element *replacing,
|
||||
Flag serviceFlag)
|
||||
: _delegate(delegate)
|
||||
, _data(data)
|
||||
, _dateTime(IsItemScheduledUntilOnline(data)
|
||||
? QDateTime()
|
||||
: ItemDateTime(data))
|
||||
, _text(st::msgMinWidth)
|
||||
, _isScheduledUntilOnline(IsItemScheduledUntilOnline(data))
|
||||
, _dateTime(_isScheduledUntilOnline ? QDateTime() : ItemDateTime(data))
|
||||
, _flags(serviceFlag | Flag::NeedsResize)
|
||||
, _context(delegate->elementContext()) {
|
||||
history()->owner().registerItemView(this);
|
||||
refreshMedia(replacing);
|
||||
|
@ -403,16 +412,36 @@ void Element::setY(int y) {
|
|||
void Element::refreshDataIdHook() {
|
||||
}
|
||||
|
||||
void Element::clearSpecialOnlyEmoji() {
|
||||
if (!(_flags & Flag::SpecialOnlyEmoji)) {
|
||||
return;
|
||||
}
|
||||
history()->session().emojiStickersPack().remove(this);
|
||||
_flags &= ~Flag::SpecialOnlyEmoji;
|
||||
}
|
||||
|
||||
void Element::checkSpecialOnlyEmoji() {
|
||||
if (history()->session().emojiStickersPack().add(this)) {
|
||||
_flags |= Flag::SpecialOnlyEmoji;
|
||||
}
|
||||
}
|
||||
|
||||
void Element::hideSpoilers() {
|
||||
if (_text.hasSpoilers()) {
|
||||
_text.setSpoilerRevealed(false, anim::type::instant);
|
||||
}
|
||||
}
|
||||
|
||||
void Element::customEmojiRepaint() {
|
||||
if (!_customEmojiRepaintScheduled) {
|
||||
_customEmojiRepaintScheduled = true;
|
||||
if (!(_flags & Flag::CustomEmojiRepainting)) {
|
||||
_flags |= Flag::CustomEmojiRepainting;
|
||||
history()->owner().requestViewRepaint(this);
|
||||
}
|
||||
}
|
||||
|
||||
void Element::clearCustomEmojiRepaint() const {
|
||||
_customEmojiRepaintScheduled = false;
|
||||
data()->_customEmojiRepaintScheduled = false;
|
||||
_flags &= ~Flag::CustomEmojiRepainting;
|
||||
data()->_flags &= ~MessageFlag::CustomEmojiRepainting;
|
||||
}
|
||||
|
||||
void Element::prepareCustomEmojiPaint(
|
||||
|
@ -548,35 +577,33 @@ void Element::refreshMedia(Element *replacing) {
|
|||
_flags &= ~Flag::HiddenByGroup;
|
||||
|
||||
const auto item = data();
|
||||
const auto media = item->media();
|
||||
if (media && media->canBeGrouped()) {
|
||||
if (const auto group = history()->owner().groups().find(item)) {
|
||||
if (group->items.front() != item) {
|
||||
_media = nullptr;
|
||||
_flags |= Flag::HiddenByGroup;
|
||||
} else {
|
||||
_media = std::make_unique<GroupedMedia>(
|
||||
this,
|
||||
group->items);
|
||||
if (!pendingResize()) {
|
||||
history()->owner().requestViewResize(this);
|
||||
if (const auto media = item->media()) {
|
||||
if (media->canBeGrouped()) {
|
||||
if (const auto group = history()->owner().groups().find(item)) {
|
||||
if (group->items.front() != item) {
|
||||
_media = nullptr;
|
||||
_flags |= Flag::HiddenByGroup;
|
||||
} else {
|
||||
_media = std::make_unique<GroupedMedia>(
|
||||
this,
|
||||
group->items);
|
||||
if (!pendingResize()) {
|
||||
history()->owner().requestViewResize(this);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
const auto session = &history()->session();
|
||||
if (const auto media = _data->media()) {
|
||||
_media = media->createView(this, replacing);
|
||||
} else if (_data->isOnlyCustomEmoji()
|
||||
} else if (isOnlyCustomEmoji()
|
||||
&& Core::App().settings().largeEmoji()) {
|
||||
_media = std::make_unique<UnwrappedMedia>(
|
||||
this,
|
||||
std::make_unique<CustomEmoji>(this, _data->onlyCustomEmoji()));
|
||||
} else if (_data->isIsolatedEmoji()
|
||||
std::make_unique<CustomEmoji>(this, onlyCustomEmoji()));
|
||||
} else if (isIsolatedEmoji()
|
||||
&& Core::App().settings().largeEmoji()) {
|
||||
const auto emoji = _data->isolatedEmoji();
|
||||
const auto emojiStickers = &session->emojiStickersPack();
|
||||
const auto emoji = isolatedEmoji();
|
||||
const auto emojiStickers = &history()->session().emojiStickersPack();
|
||||
const auto skipPremiumEffect = false;
|
||||
if (const auto sticker = emojiStickers->stickerForEmoji(emoji)) {
|
||||
_media = std::make_unique<UnwrappedMedia>(
|
||||
|
@ -597,6 +624,90 @@ void Element::refreshMedia(Element *replacing) {
|
|||
}
|
||||
}
|
||||
|
||||
Ui::Text::IsolatedEmoji Element::isolatedEmoji() const {
|
||||
return _text.toIsolatedEmoji();
|
||||
}
|
||||
|
||||
Ui::Text::OnlyCustomEmoji Element::onlyCustomEmoji() const {
|
||||
return _text.toOnlyCustomEmoji();
|
||||
}
|
||||
|
||||
const Ui::Text::String &Element::text() const {
|
||||
return _text;
|
||||
}
|
||||
|
||||
int Element::textHeightFor(int textWidth) {
|
||||
validateText();
|
||||
if (_textWidth != textWidth) {
|
||||
_textWidth = textWidth;
|
||||
_textHeight = _text.countHeight(textWidth);
|
||||
}
|
||||
return _textHeight;
|
||||
}
|
||||
|
||||
void Element::validateText() {
|
||||
const auto item = data();
|
||||
const auto &text = item->_text;
|
||||
if (_text.isEmpty() == text.empty()) {
|
||||
return;
|
||||
}
|
||||
const auto context = Core::MarkedTextContext{
|
||||
.session = &history()->session(),
|
||||
.customEmojiRepaint = [=] { customEmojiRepaint(); },
|
||||
};
|
||||
if (_flags & Flag::ServiceMessage) {
|
||||
_text.setMarkedText(
|
||||
st::serviceTextStyle,
|
||||
text,
|
||||
Ui::ItemTextServiceOptions(),
|
||||
context);
|
||||
auto linkIndex = 0;
|
||||
for (const auto &link : item->customTextLinks()) {
|
||||
// Link indices start with 1.
|
||||
_text.setLink(++linkIndex, link);
|
||||
}
|
||||
} else {
|
||||
clearSpecialOnlyEmoji();
|
||||
const auto context = Core::MarkedTextContext{
|
||||
.session = &history()->session(),
|
||||
.customEmojiRepaint = [=] { customEmojiRepaint(); },
|
||||
};
|
||||
_text.setMarkedText(
|
||||
st::messageTextStyle,
|
||||
item->originalTextWithLocalEntities(),
|
||||
Ui::ItemTextOptions(item),
|
||||
context);
|
||||
if (!text.empty() && _text.isEmpty()) {
|
||||
// If server has allowed some text that we've trim-ed entirely,
|
||||
// just replace it with something so that UI won't look buggy.
|
||||
_text.setMarkedText(
|
||||
st::messageTextStyle,
|
||||
{ u":-("_q },
|
||||
Ui::ItemTextOptions(item));
|
||||
}
|
||||
if (!item->media()) {
|
||||
checkSpecialOnlyEmoji();
|
||||
refreshMedia(nullptr);
|
||||
}
|
||||
}
|
||||
FillTextWithAnimatedSpoilers(this, _text);
|
||||
_textWidth = -1;
|
||||
_textHeight = 0;
|
||||
}
|
||||
|
||||
void Element::validateTextSkipBlock(bool has, int width, int height) {
|
||||
validateText();
|
||||
if (!has) {
|
||||
if (_text.removeSkipBlock()) {
|
||||
_textWidth = -1;
|
||||
_textHeight = 0;
|
||||
}
|
||||
} else if (_text.updateSkipBlock(width, height)) {
|
||||
_textWidth = -1;
|
||||
_textHeight = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Element::previousInBlocksChanged() {
|
||||
recountDisplayDateInBlocks();
|
||||
recountAttachToPreviousInBlocks();
|
||||
|
@ -938,6 +1049,19 @@ bool Element::isSignedAuthorElided() const {
|
|||
void Element::itemDataChanged() {
|
||||
}
|
||||
|
||||
void Element::itemTextUpdated() {
|
||||
if (const auto media = _media.get()) {
|
||||
media->parentTextUpdated();
|
||||
}
|
||||
clearSpecialOnlyEmoji();
|
||||
_text = Ui::Text::String(st::msgMinWidth);
|
||||
_textWidth = -1;
|
||||
_textHeight = 0;
|
||||
if (_media && !data()->media()) {
|
||||
refreshMedia(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void Element::unloadHeavyPart() {
|
||||
history()->owner().unregisterHeavyViewPart(this);
|
||||
if (_media) {
|
||||
|
@ -945,7 +1069,7 @@ void Element::unloadHeavyPart() {
|
|||
}
|
||||
if (_heavyCustomEmoji) {
|
||||
_heavyCustomEmoji = false;
|
||||
data()->_text.unloadPersistentAnimation();
|
||||
_text.unloadPersistentAnimation();
|
||||
if (const auto reply = data()->Get<HistoryMessageReply>()) {
|
||||
reply->replyToText.unloadPersistentAnimation();
|
||||
}
|
||||
|
@ -1125,7 +1249,7 @@ Element::~Element() {
|
|||
base::take(_media);
|
||||
if (_heavyCustomEmoji) {
|
||||
_heavyCustomEmoji = false;
|
||||
data()->_text.unloadPersistentAnimation();
|
||||
_text.unloadPersistentAnimation();
|
||||
checkHeavyPart();
|
||||
}
|
||||
if (_data->mainView() == this) {
|
||||
|
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/view/history_view_object.h"
|
||||
#include "base/runtime_composer.h"
|
||||
#include "base/flags.h"
|
||||
#include "base/weak_ptr.h"
|
||||
|
||||
class History;
|
||||
class HistoryBlock;
|
||||
|
@ -236,54 +237,71 @@ struct DateBadge : public RuntimeComponent<DateBadge, Element> {
|
|||
class Element
|
||||
: public Object
|
||||
, public RuntimeComposer<Element>
|
||||
, public ClickHandlerHost {
|
||||
, public ClickHandlerHost
|
||||
, public base::has_weak_ptr {
|
||||
public:
|
||||
Element(
|
||||
not_null<ElementDelegate*> delegate,
|
||||
not_null<HistoryItem*> data,
|
||||
Element *replacing);
|
||||
|
||||
enum class Flag : uchar {
|
||||
NeedsResize = 0x01,
|
||||
AttachedToPrevious = 0x02,
|
||||
AttachedToNext = 0x04,
|
||||
HiddenByGroup = 0x08,
|
||||
ServiceMessage = 0x01,
|
||||
NeedsResize = 0x02,
|
||||
AttachedToPrevious = 0x04,
|
||||
AttachedToNext = 0x08,
|
||||
HiddenByGroup = 0x10,
|
||||
SpecialOnlyEmoji = 0x20,
|
||||
CustomEmojiRepainting = 0x40,
|
||||
};
|
||||
using Flags = base::flags<Flag>;
|
||||
friend inline constexpr auto is_flag_type(Flag) { return true; }
|
||||
|
||||
not_null<ElementDelegate*> delegate() const;
|
||||
not_null<HistoryItem*> data() const;
|
||||
not_null<History*> history() const;
|
||||
Media *media() const;
|
||||
Context context() const;
|
||||
Element(
|
||||
not_null<ElementDelegate*> delegate,
|
||||
not_null<HistoryItem*> data,
|
||||
Element *replacing,
|
||||
Flag serviceFlag);
|
||||
|
||||
[[nodiscard]] not_null<ElementDelegate*> delegate() const;
|
||||
[[nodiscard]] not_null<HistoryItem*> data() const;
|
||||
[[nodiscard]] not_null<History*> history() const;
|
||||
[[nodiscard]] Media *media() const;
|
||||
[[nodiscard]] Context context() const;
|
||||
void refreshDataId();
|
||||
|
||||
QDateTime dateTime() const;
|
||||
[[nodiscard]] QDateTime dateTime() const;
|
||||
|
||||
int y() const;
|
||||
[[nodiscard]] int y() const;
|
||||
void setY(int y);
|
||||
|
||||
virtual int marginTop() const = 0;
|
||||
virtual int marginBottom() const = 0;
|
||||
[[nodiscard]] virtual int marginTop() const = 0;
|
||||
[[nodiscard]] virtual int marginBottom() const = 0;
|
||||
|
||||
void setPendingResize();
|
||||
bool pendingResize() const;
|
||||
bool isUnderCursor() const;
|
||||
[[nodiscard]] bool pendingResize() const;
|
||||
[[nodiscard]] bool isUnderCursor() const;
|
||||
|
||||
bool isLastAndSelfMessage() const;
|
||||
[[nodiscard]] bool isLastAndSelfMessage() const;
|
||||
|
||||
bool isAttachedToPrevious() const;
|
||||
bool isAttachedToNext() const;
|
||||
[[nodiscard]] bool isAttachedToPrevious() const;
|
||||
[[nodiscard]] bool isAttachedToNext() const;
|
||||
|
||||
int skipBlockWidth() const;
|
||||
int skipBlockHeight() const;
|
||||
virtual int infoWidth() const;
|
||||
virtual int bottomInfoFirstLineWidth() const;
|
||||
virtual bool bottomInfoIsWide() const;
|
||||
[[nodiscard]] int skipBlockWidth() const;
|
||||
[[nodiscard]] int skipBlockHeight() const;
|
||||
[[nodiscard]] virtual int infoWidth() const;
|
||||
[[nodiscard]] virtual int bottomInfoFirstLineWidth() const;
|
||||
[[nodiscard]] virtual bool bottomInfoIsWide() const;
|
||||
|
||||
bool isHiddenByGroup() const;
|
||||
virtual bool isHidden() const;
|
||||
[[nodiscard]] bool isHiddenByGroup() const;
|
||||
[[nodiscard]] virtual bool isHidden() const;
|
||||
|
||||
[[nodiscard]] bool isIsolatedEmoji() const {
|
||||
return (_flags & Flag::SpecialOnlyEmoji)
|
||||
&& _text.isIsolatedEmoji();
|
||||
}
|
||||
[[nodiscard]] bool isOnlyCustomEmoji() const {
|
||||
return (_flags & Flag::SpecialOnlyEmoji)
|
||||
&& _text.isOnlyCustomEmoji();
|
||||
}
|
||||
|
||||
[[nodiscard]] Ui::Text::IsolatedEmoji isolatedEmoji() const;
|
||||
[[nodiscard]] Ui::Text::OnlyCustomEmoji onlyCustomEmoji() const;
|
||||
|
||||
// For blocks context this should be called only from recountAttachToPreviousInBlocks().
|
||||
void setAttachToPrevious(bool attachToNext);
|
||||
|
@ -300,9 +318,9 @@ public:
|
|||
void createUnreadBar(rpl::producer<QString> text);
|
||||
void destroyUnreadBar();
|
||||
|
||||
int displayedDateHeight() const;
|
||||
bool displayDate() const;
|
||||
bool isInOneDayWithPrevious() const;
|
||||
[[nodiscard]] int displayedDateHeight() const;
|
||||
[[nodiscard]] bool displayDate() const;
|
||||
[[nodiscard]] bool isInOneDayWithPrevious() const;
|
||||
|
||||
virtual void draw(Painter &p, const PaintContext &context) const = 0;
|
||||
[[nodiscard]] virtual PointState pointState(QPoint point) const = 0;
|
||||
|
@ -382,8 +400,9 @@ public:
|
|||
[[nodiscard]] virtual bool isSignedAuthorElided() const;
|
||||
|
||||
virtual void itemDataChanged();
|
||||
void itemTextUpdated();
|
||||
|
||||
virtual bool hasHeavyPart() const;
|
||||
[[nodiscard]] virtual bool hasHeavyPart() const;
|
||||
virtual void unloadHeavyPart();
|
||||
void checkHeavyPart();
|
||||
|
||||
|
@ -395,21 +414,21 @@ public:
|
|||
not_null<const HistoryItem*> item) const;
|
||||
|
||||
// Legacy blocks structure.
|
||||
HistoryBlock *block();
|
||||
const HistoryBlock *block() const;
|
||||
[[nodiscard]] HistoryBlock *block();
|
||||
[[nodiscard]] const HistoryBlock *block() const;
|
||||
void attachToBlock(not_null<HistoryBlock*> 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;
|
||||
[[nodiscard]] int indexInBlock() const;
|
||||
[[nodiscard]] Element *previousInBlocks() const;
|
||||
[[nodiscard]] Element *previousDisplayedInBlocks() const;
|
||||
[[nodiscard]] Element *nextInBlocks() const;
|
||||
[[nodiscard]] Element *nextDisplayedInBlocks() const;
|
||||
void previousInBlocksChanged();
|
||||
void nextInBlocksRemoved();
|
||||
|
||||
virtual QRect innerGeometry() const = 0;
|
||||
[[nodiscard]] virtual QRect innerGeometry() const = 0;
|
||||
|
||||
void customEmojiRepaint();
|
||||
void prepareCustomEmojiPaint(
|
||||
|
@ -421,6 +440,7 @@ public:
|
|||
const PaintContext &context,
|
||||
const Reactions::InlineList &reactions) const;
|
||||
void clearCustomEmojiRepaint() const;
|
||||
void hideSpoilers();
|
||||
|
||||
[[nodiscard]] ClickHandlerPtr fromPhotoLink() const {
|
||||
return fromLink();
|
||||
|
@ -461,6 +481,14 @@ protected:
|
|||
|
||||
virtual void refreshDataIdHook();
|
||||
|
||||
[[nodiscard]] const Ui::Text::String &text() const;
|
||||
[[nodiscard]] int textHeightFor(int textWidth);
|
||||
void validateText();
|
||||
void validateTextSkipBlock(bool has, int width, int height);
|
||||
|
||||
void clearSpecialOnlyEmoji();
|
||||
void checkSpecialOnlyEmoji();
|
||||
|
||||
private:
|
||||
// This should be called only from previousInBlocksChanged()
|
||||
// to add required bits to the Composer mask
|
||||
|
@ -483,22 +511,23 @@ private:
|
|||
|
||||
const not_null<ElementDelegate*> _delegate;
|
||||
const not_null<HistoryItem*> _data;
|
||||
HistoryBlock *_block = nullptr;
|
||||
std::unique_ptr<Media> _media;
|
||||
mutable ClickHandlerPtr _fromLink;
|
||||
bool _isScheduledUntilOnline = false;
|
||||
mutable bool _heavyCustomEmoji = false;
|
||||
mutable bool _customEmojiRepaintScheduled = false;
|
||||
|
||||
const QDateTime _dateTime;
|
||||
|
||||
mutable Ui::Text::String _text = { st::msgMinWidth };
|
||||
mutable int _textWidth = -1;
|
||||
mutable int _textHeight = 0;
|
||||
|
||||
int _y = 0;
|
||||
Context _context = Context();
|
||||
|
||||
Flags _flags = Flag::NeedsResize;
|
||||
|
||||
HistoryBlock *_block = nullptr;
|
||||
int _indexInBlock = -1;
|
||||
|
||||
bool _isScheduledUntilOnline = false;
|
||||
mutable bool _heavyCustomEmoji = false;
|
||||
mutable Flags _flags = Flag(0);
|
||||
Context _context = Context();
|
||||
|
||||
};
|
||||
|
||||
} // namespace HistoryView
|
||||
|
|
|
@ -255,7 +255,7 @@ Message::Message(
|
|||
not_null<ElementDelegate*> delegate,
|
||||
not_null<HistoryMessage*> data,
|
||||
Element *replacing)
|
||||
: Element(delegate, data, replacing)
|
||||
: Element(delegate, data, replacing, Flag(0))
|
||||
, _bottomInfo(
|
||||
&data->history()->owner().reactions(),
|
||||
BottomInfoDataFromMessage(this)) {
|
||||
|
@ -447,17 +447,18 @@ auto Message::takeReactionAnimations()
|
|||
}
|
||||
|
||||
QSize Message::performCountOptimalSize() {
|
||||
validateText();
|
||||
updateViewButtonExistence();
|
||||
updateMediaInBubbleState();
|
||||
refreshRightBadge();
|
||||
refreshInfoSkipBlock();
|
||||
|
||||
const auto item = message();
|
||||
const auto media = this->media();
|
||||
|
||||
auto maxWidth = 0;
|
||||
auto minHeight = 0;
|
||||
|
||||
updateViewButtonExistence();
|
||||
updateMediaInBubbleState();
|
||||
refreshRightBadge();
|
||||
refreshInfoSkipBlock();
|
||||
|
||||
const auto reactionsInBubble = _reactions && embedReactionsInBubble();
|
||||
if (_reactions) {
|
||||
_reactions->initDimensions();
|
||||
|
@ -490,7 +491,7 @@ QSize Message::performCountOptimalSize() {
|
|||
if (context() == Context::Replies && item->isDiscussionPost()) {
|
||||
maxWidth = std::max(maxWidth, st::msgMaxWidth);
|
||||
}
|
||||
minHeight = hasVisibleText() ? item->_text.minHeight() : 0;
|
||||
minHeight = hasVisibleText() ? text().minHeight() : 0;
|
||||
if (reactionsInBubble) {
|
||||
const auto reactionsMaxWidth = st::msgPadding.left()
|
||||
+ _reactions->maxWidth()
|
||||
|
@ -529,8 +530,8 @@ QSize Message::performCountOptimalSize() {
|
|||
- st::msgPadding.left()
|
||||
- st::msgPadding.right();
|
||||
if (hasVisibleText() && maxWidth < plainMaxWidth()) {
|
||||
minHeight -= item->_text.minHeight();
|
||||
minHeight += item->_text.countHeight(innerWidth);
|
||||
minHeight -= text().minHeight();
|
||||
minHeight += text().countHeight(innerWidth);
|
||||
}
|
||||
if (reactionsInBubble) {
|
||||
minHeight -= _reactions->minHeight();
|
||||
|
@ -1298,8 +1299,8 @@ void Message::paintText(
|
|||
const auto stm = context.messageStyle();
|
||||
p.setPen(stm->historyTextFg);
|
||||
p.setFont(st::msgFont);
|
||||
prepareCustomEmojiPaint(p, context, item->_text);
|
||||
item->_text.draw(p, {
|
||||
prepareCustomEmojiPaint(p, context, text());
|
||||
text().draw(p, {
|
||||
.position = trect.topLeft(),
|
||||
.availableWidth = trect.width(),
|
||||
.palette = &stm->textPalette,
|
||||
|
@ -1606,7 +1607,7 @@ TextState Message::textState(
|
|||
result = entry->textState(
|
||||
point - QPoint(entryLeft, entryTop),
|
||||
request);
|
||||
result.symbol += item->_text.length() + (mediaDisplayed ? media->fullSelectionLength() : 0);
|
||||
result.symbol += text().length() + (mediaDisplayed ? media->fullSelectionLength() : 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1632,18 +1633,18 @@ TextState Message::textState(
|
|||
|
||||
if (point.y() >= mediaTop && point.y() < mediaTop + mediaHeight) {
|
||||
result = media->textState(point - QPoint(mediaLeft, mediaTop), request);
|
||||
result.symbol += item->_text.length();
|
||||
result.symbol += text().length();
|
||||
} else if (getStateText(point, trect, &result, request)) {
|
||||
checkBottomInfoState();
|
||||
return result;
|
||||
} else if (point.y() >= trect.y() + trect.height()) {
|
||||
result.symbol = item->_text.length();
|
||||
result.symbol = text().length();
|
||||
}
|
||||
} else if (getStateText(point, trect, &result, request)) {
|
||||
checkBottomInfoState();
|
||||
return result;
|
||||
} else if (point.y() >= trect.y() + trect.height()) {
|
||||
result.symbol = item->_text.length();
|
||||
result.symbol = text().length();
|
||||
}
|
||||
}
|
||||
checkBottomInfoState();
|
||||
|
@ -1665,7 +1666,7 @@ TextState Message::textState(
|
|||
}
|
||||
} else if (media && media->isDisplayed()) {
|
||||
result = media->textState(point - g.topLeft(), request);
|
||||
result.symbol += item->_text.length();
|
||||
result.symbol += text().length();
|
||||
}
|
||||
|
||||
if (keyboard && item->isHistoryEntry()) {
|
||||
|
@ -1938,7 +1939,7 @@ bool Message::getStateText(
|
|||
}
|
||||
const auto item = message();
|
||||
if (base::in_range(point.y(), trect.y(), trect.y() + trect.height())) {
|
||||
*outResult = TextState(item, item->_text.getState(
|
||||
*outResult = TextState(item, text().getState(
|
||||
point - trect.topLeft(),
|
||||
trect.width(),
|
||||
request.forText()));
|
||||
|
@ -2004,7 +2005,7 @@ TextForMimeData Message::selectedText(TextSelection selection) const {
|
|||
const auto media = this->media();
|
||||
|
||||
auto logEntryOriginalResult = TextForMimeData();
|
||||
auto textResult = item->_text.toTextForMimeData(selection);
|
||||
auto textResult = text().toTextForMimeData(selection);
|
||||
auto skipped = skipTextSelection(selection);
|
||||
auto mediaDisplayed = (media && media->isDisplayed());
|
||||
auto mediaResult = (mediaDisplayed || isHiddenByGroup())
|
||||
|
@ -2036,8 +2037,8 @@ TextSelection Message::adjustSelection(
|
|||
const auto item = message();
|
||||
const auto media = this->media();
|
||||
|
||||
auto result = item->_text.adjustSelection(selection, type);
|
||||
auto beforeMediaLength = item->_text.length();
|
||||
auto result = text().adjustSelection(selection, type);
|
||||
auto beforeMediaLength = text().length();
|
||||
if (selection.to <= beforeMediaLength) {
|
||||
return result;
|
||||
}
|
||||
|
@ -2370,13 +2371,13 @@ void Message::refreshDataIdHook() {
|
|||
|
||||
int Message::plainMaxWidth() const {
|
||||
return st::msgPadding.left()
|
||||
+ (hasVisibleText() ? message()->_text.maxWidth() : 0)
|
||||
+ (hasVisibleText() ? text().maxWidth() : 0)
|
||||
+ st::msgPadding.right();
|
||||
}
|
||||
|
||||
int Message::monospaceMaxWidth() const {
|
||||
return st::msgPadding.left()
|
||||
+ (hasVisibleText() ? message()->_text.countMaxMonospaceWidth() : 0)
|
||||
+ (hasVisibleText() ? text().countMaxMonospaceWidth() : 0)
|
||||
+ st::msgPadding.right();
|
||||
}
|
||||
|
||||
|
@ -2893,11 +2894,11 @@ TextSelection Message::skipTextSelection(TextSelection selection) const {
|
|||
if (selection.from == 0xFFFF) {
|
||||
return selection;
|
||||
}
|
||||
return HistoryView::UnshiftItemSelection(selection, message()->_text);
|
||||
return HistoryView::UnshiftItemSelection(selection, text());
|
||||
}
|
||||
|
||||
TextSelection Message::unskipTextSelection(TextSelection selection) const {
|
||||
return HistoryView::ShiftItemSelection(selection, message()->_text);
|
||||
return HistoryView::ShiftItemSelection(selection, text());
|
||||
}
|
||||
|
||||
QRect Message::innerGeometry() const {
|
||||
|
@ -3058,15 +3059,7 @@ int Message::resizeContentGetHeight(int newWidth) {
|
|||
entry->resizeGetHeight(contentWidth);
|
||||
}
|
||||
} else {
|
||||
if (hasVisibleText()) {
|
||||
if (textWidth != item->_textWidth) {
|
||||
item->_textWidth = textWidth;
|
||||
item->_textHeight = item->_text.countHeight(textWidth);
|
||||
}
|
||||
newHeight = item->_textHeight;
|
||||
} else {
|
||||
newHeight = 0;
|
||||
}
|
||||
newHeight = hasVisibleText() ? textHeightFor(textWidth) : 0;
|
||||
if (!mediaOnBottom && (!_viewButton || !reactionsInBubble)) {
|
||||
newHeight += st::msgPadding.bottom();
|
||||
if (mediaDisplayed) {
|
||||
|
@ -3181,7 +3174,7 @@ void Message::refreshInfoSkipBlock() {
|
|||
const auto item = message();
|
||||
const auto media = this->media();
|
||||
const auto hasTextSkipBlock = [&] {
|
||||
if (item->_text.isEmpty()) {
|
||||
if (item->_text.empty()) {
|
||||
return false;
|
||||
} else if (item->Has<HistoryMessageLogEntryOriginal>()) {
|
||||
return false;
|
||||
|
@ -3201,15 +3194,7 @@ void Message::refreshInfoSkipBlock() {
|
|||
_reactions->removeSkipBlock();
|
||||
}
|
||||
}
|
||||
if (!hasTextSkipBlock) {
|
||||
if (item->_text.removeSkipBlock()) {
|
||||
item->_textWidth = -1;
|
||||
item->_textHeight = 0;
|
||||
}
|
||||
} else if (item->_text.updateSkipBlock(skipWidth, skipHeight)) {
|
||||
item->_textWidth = -1;
|
||||
item->_textHeight = 0;
|
||||
}
|
||||
validateTextSkipBlock(hasTextSkipBlock, skipWidth, skipHeight);
|
||||
}
|
||||
|
||||
TimeId Message::displayedEditDate() const {
|
||||
|
|
|
@ -10,7 +10,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/view/history_view_element.h"
|
||||
#include "history/view/history_view_bottom_info.h"
|
||||
#include "ui/effects/animations.h"
|
||||
#include "base/weak_ptr.h"
|
||||
|
||||
class HistoryMessage;
|
||||
struct HistoryMessageEdited;
|
||||
|
@ -47,7 +46,7 @@ struct PsaTooltipState : public RuntimeComponent<PsaTooltipState, Element> {
|
|||
mutable bool buttonVisible = true;
|
||||
};
|
||||
|
||||
class Message : public Element, public base::has_weak_ptr {
|
||||
class Message final : public Element {
|
||||
public:
|
||||
Message(
|
||||
not_null<ElementDelegate*> delegate,
|
||||
|
|
|
@ -388,7 +388,7 @@ Service::Service(
|
|||
not_null<ElementDelegate*> delegate,
|
||||
not_null<HistoryService*> data,
|
||||
Element *replacing)
|
||||
: Element(delegate, data, replacing) {
|
||||
: Element(delegate, data, replacing, Flag::ServiceMessage) {
|
||||
}
|
||||
|
||||
not_null<HistoryService*> Service::message() const {
|
||||
|
@ -420,9 +420,7 @@ QSize Service::performCountCurrentSize(int newWidth) {
|
|||
const auto item = message();
|
||||
const auto media = this->media();
|
||||
|
||||
if (item->_text.isEmpty()) {
|
||||
item->_textHeight = 0;
|
||||
} else {
|
||||
if (!text().isEmpty()) {
|
||||
auto contentWidth = newWidth;
|
||||
if (delegate()->elementIsChatWide()) {
|
||||
accumulate_min(contentWidth, st::msgMaxWidth + 2 * st::msgPhotoSkip + 2 * st::msgMargin.left());
|
||||
|
@ -433,15 +431,9 @@ QSize Service::performCountCurrentSize(int newWidth) {
|
|||
}
|
||||
|
||||
auto nwidth = qMax(contentWidth - st::msgServicePadding.left() - st::msgServicePadding.right(), 0);
|
||||
if (nwidth != item->_textWidth) {
|
||||
item->_textWidth = nwidth;
|
||||
item->_textHeight = item->_text.countHeight(nwidth);
|
||||
}
|
||||
if (contentWidth >= maxWidth()) {
|
||||
newHeight += minHeight();
|
||||
} else {
|
||||
newHeight += item->_textHeight;
|
||||
}
|
||||
newHeight += (contentWidth >= maxWidth())
|
||||
? minHeight()
|
||||
: textHeightFor(nwidth);
|
||||
newHeight += st::msgServicePadding.top() + st::msgServicePadding.bottom() + st::msgServiceMargin.top() + st::msgServiceMargin.bottom();
|
||||
if (media) {
|
||||
newHeight += st::msgServiceMargin.top() + media->resizeGetHeight(media->maxWidth());
|
||||
|
@ -454,9 +446,10 @@ QSize Service::performCountCurrentSize(int newWidth) {
|
|||
QSize Service::performCountOptimalSize() {
|
||||
const auto item = message();
|
||||
const auto media = this->media();
|
||||
validateText();
|
||||
|
||||
auto maxWidth = item->_text.maxWidth() + st::msgServicePadding.left() + st::msgServicePadding.right();
|
||||
auto minHeight = item->_text.minHeight();
|
||||
auto maxWidth = text().maxWidth() + st::msgServicePadding.left() + st::msgServicePadding.right();
|
||||
auto minHeight = text().minHeight();
|
||||
if (media) {
|
||||
media->initDimensions();
|
||||
}
|
||||
|
@ -533,14 +526,14 @@ void Service::draw(Painter &p, const PaintContext &context) const {
|
|||
context.st,
|
||||
g.left(),
|
||||
g.width(),
|
||||
item->_text,
|
||||
text(),
|
||||
trect);
|
||||
|
||||
p.setBrush(Qt::NoBrush);
|
||||
p.setPen(st->msgServiceFg());
|
||||
p.setFont(st::msgServiceFont);
|
||||
prepareCustomEmojiPaint(p, context, item->_text);
|
||||
item->_text.draw(p, {
|
||||
prepareCustomEmojiPaint(p, context, text());
|
||||
text().draw(p, {
|
||||
.position = trect.topLeft(),
|
||||
.availableWidth = trect.width(),
|
||||
.align = style::al_top,
|
||||
|
@ -618,7 +611,7 @@ TextState Service::textState(QPoint point, StateRequest request) const {
|
|||
if (trect.contains(point)) {
|
||||
auto textRequest = request.forText();
|
||||
textRequest.align = style::al_center;
|
||||
result = TextState(item, item->_text.getState(
|
||||
result = TextState(item, text().getState(
|
||||
point - trect.topLeft(),
|
||||
trect.width(),
|
||||
textRequest));
|
||||
|
@ -652,13 +645,13 @@ void Service::updatePressed(QPoint point) {
|
|||
}
|
||||
|
||||
TextForMimeData Service::selectedText(TextSelection selection) const {
|
||||
return message()->_text.toTextForMimeData(selection);
|
||||
return text().toTextForMimeData(selection);
|
||||
}
|
||||
|
||||
TextSelection Service::adjustSelection(
|
||||
TextSelection selection,
|
||||
TextSelectType type) const {
|
||||
return message()->_text.adjustSelection(selection, type);
|
||||
return text().adjustSelection(selection, type);
|
||||
}
|
||||
|
||||
EmptyPainter::EmptyPainter(not_null<History*> history) : _history(history) {
|
||||
|
|
|
@ -18,7 +18,7 @@ struct CornersPixmaps;
|
|||
|
||||
namespace HistoryView {
|
||||
|
||||
class Service : public Element {
|
||||
class Service final : public Element {
|
||||
public:
|
||||
Service(
|
||||
not_null<ElementDelegate*> delegate,
|
||||
|
|
|
@ -12,49 +12,55 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/view/history_view_element.h"
|
||||
#include "main/main_session.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "base/weak_ptr.h"
|
||||
|
||||
namespace HistoryView {
|
||||
namespace {
|
||||
|
||||
class AnimatedSpoilerClickHandler final : public ClickHandler {
|
||||
public:
|
||||
explicit AnimatedSpoilerClickHandler(Ui::Text::String &text)
|
||||
: _text(text) {
|
||||
}
|
||||
AnimatedSpoilerClickHandler(
|
||||
not_null<Element*> view,
|
||||
Ui::Text::String &text);
|
||||
|
||||
void onClick(ClickContext context) const override;
|
||||
|
||||
private:
|
||||
base::weak_ptr<Element> _weak;
|
||||
Ui::Text::String &_text;
|
||||
|
||||
};
|
||||
|
||||
AnimatedSpoilerClickHandler::AnimatedSpoilerClickHandler(
|
||||
not_null<Element*> view,
|
||||
Ui::Text::String &text)
|
||||
: _weak(view)
|
||||
, _text(text) {
|
||||
}
|
||||
|
||||
void AnimatedSpoilerClickHandler::onClick(ClickContext context) const {
|
||||
const auto button = context.button;
|
||||
if (button != Qt::LeftButton) {
|
||||
const auto view = _weak.get();
|
||||
if (button != Qt::LeftButton || !view) {
|
||||
return;
|
||||
}
|
||||
const auto my = context.other.value<ClickHandlerContext>();
|
||||
if (const auto d = my.elementDelegate ? my.elementDelegate() : nullptr) {
|
||||
_text.setSpoilerRevealed(true, anim::type::normal);
|
||||
if (const auto controller = my.sessionWindow.get()) {
|
||||
controller->session().data().registerShownSpoiler(my.itemId);
|
||||
controller->session().data().registerShownSpoiler(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void FillTextWithAnimatedSpoilers(Ui::Text::String &text) {
|
||||
void FillTextWithAnimatedSpoilers(
|
||||
not_null<Element*> view,
|
||||
Ui::Text::String &text) {
|
||||
if (text.hasSpoilers()) {
|
||||
text.setSpoilerLink(
|
||||
std::make_shared<AnimatedSpoilerClickHandler>(text));
|
||||
}
|
||||
}
|
||||
|
||||
void HideSpoilers(Ui::Text::String &text) {
|
||||
if (text.hasSpoilers()) {
|
||||
text.setSpoilerRevealed(false, anim::type::instant);
|
||||
std::make_shared<AnimatedSpoilerClickHandler>(view, text));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
namespace HistoryView {
|
||||
|
||||
void FillTextWithAnimatedSpoilers(Ui::Text::String &text);
|
||||
void HideSpoilers(Ui::Text::String &text);
|
||||
class Element;
|
||||
|
||||
void FillTextWithAnimatedSpoilers(
|
||||
not_null<Element*> view,
|
||||
Ui::Text::String &text);
|
||||
|
||||
} // namespace HistoryView
|
||||
|
|
|
@ -208,7 +208,7 @@ Ui::Text::String Media::createCaption(not_null<HistoryItem*> item) const {
|
|||
item->originalTextWithLocalEntities(),
|
||||
Ui::ItemTextOptions(item),
|
||||
context);
|
||||
FillTextWithAnimatedSpoilers(result);
|
||||
FillTextWithAnimatedSpoilers(_parent, result);
|
||||
if (const auto width = _parent->skipBlockWidth()) {
|
||||
result.updateSkipBlock(width, _parent->skipBlockHeight());
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 18580e46a1206f4ba875ed6d4da553d60407288f
|
||||
Subproject commit 06f3c837f6b65868ec1ed048ba8843fbed247677
|
Loading…
Add table
Reference in a new issue