Move preview paint to Dialogs::Ui::MessageView.

This commit is contained in:
John Preston 2021-10-01 16:42:44 +04:00
parent 5136cc3c9c
commit 8c21fad642
21 changed files with 265 additions and 151 deletions

View file

@ -484,6 +484,8 @@ PRIVATE
dialogs/dialogs_widget.h dialogs/dialogs_widget.h
dialogs/ui/dialogs_layout.cpp dialogs/ui/dialogs_layout.cpp
dialogs/ui/dialogs_layout.h dialogs/ui/dialogs_layout.h
dialogs/ui/dialogs_message_view.cpp
dialogs/ui/dialogs_message_view.h
editor/color_picker.cpp editor/color_picker.cpp
editor/color_picker.h editor/color_picker.h
editor/controllers/controllers.h editor/controllers/controllers.h

View file

@ -148,7 +148,7 @@ struct MessageUpdate {
NewMaybeAdded = (1U << 7), NewMaybeAdded = (1U << 7),
RepliesUnreadCount = (1U << 8), RepliesUnreadCount = (1U << 8),
LastUsedBit = (1U << 7), LastUsedBit = (1U << 8),
}; };
using Flags = base::flags<Flag>; using Flags = base::flags<Flag>;
friend inline constexpr auto is_flag_type(Flag) { return true; } friend inline constexpr auto is_flag_type(Flag) { return true; }

View file

@ -55,6 +55,8 @@ namespace {
constexpr auto kFastRevokeRestriction = 24 * 60 * TimeId(60); constexpr auto kFastRevokeRestriction = 24 * 60 * TimeId(60);
using ItemPreview = HistoryView::ItemPreview;
[[nodiscard]] Call ComputeCallData(const MTPDmessageActionPhoneCall &call) { [[nodiscard]] Call ComputeCallData(const MTPDmessageActionPhoneCall &call) {
auto result = Call(); auto result = Call();
result.finishReason = [&] { result.finishReason = [&] {
@ -201,11 +203,12 @@ bool Media::canBeGrouped() const {
return false; return false;
} }
QString Media::chatListText(DrawInDialog way) const { ItemPreview Media::toPreview(ToPreviewOptions options) const {
auto result = notificationText(); auto result = notificationText();
return result.isEmpty() auto text = result.isEmpty()
? QString() ? QString()
: textcmdLink(1, TextUtilities::Clean(std::move(result))); : textcmdLink(1, TextUtilities::Clean(std::move(result)));
return { .text = std::move(text) };
} }
bool Media::hasReplyPreview() const { bool Media::hasReplyPreview() const {
@ -341,11 +344,16 @@ QString MediaPhoto::notificationText() const {
parent()->originalText().text); parent()->originalText().text);
} }
QString MediaPhoto::chatListText(DrawInDialog way) const { ItemPreview MediaPhoto::toPreview(ToPreviewOptions options) const {
const auto caption = (way == DrawInDialog::WithoutSenderAndCaption) const auto caption = options.hideCaption
? QString() ? QString()
: parent()->originalText().text; : parent()->originalText().text;
return WithCaptionDialogsText(tr::lng_in_dlg_photo(tr::now), caption); // #TODO minis generate images and support albums
return {
.text = WithCaptionDialogsText(
tr::lng_in_dlg_photo(tr::now),
caption)
};
} }
QString MediaPhoto::pinnedTextSubstring() const { QString MediaPhoto::pinnedTextSubstring() const {
@ -511,16 +519,16 @@ bool MediaFile::replyPreviewLoaded() const {
return _document->replyPreviewLoaded(); return _document->replyPreviewLoaded();
} }
QString MediaFile::chatListText(DrawInDialog way) const { ItemPreview MediaFile::toPreview(ToPreviewOptions options) const {
if (const auto sticker = _document->sticker()) { if (const auto sticker = _document->sticker()) {
return Media::chatListText(way); return Media::toPreview(options);
} }
const auto type = [&] { const auto type = [&] {
using namespace Ui::Text; using namespace Ui::Text;
if (_document->isVideoMessage()) { if (_document->isVideoMessage()) {
return tr::lng_in_dlg_video_message(tr::now); return tr::lng_in_dlg_video_message(tr::now);
} else if (_document->isAnimation()) { } else if (_document->isAnimation()) {
return qsl("GIF"); return u"GIF"_q;
} else if (_document->isVideoFile()) { } else if (_document->isVideoFile()) {
return tr::lng_in_dlg_video(tr::now); return tr::lng_in_dlg_video(tr::now);
} else if (_document->isVoiceMessage()) { } else if (_document->isVoiceMessage()) {
@ -533,10 +541,13 @@ QString MediaFile::chatListText(DrawInDialog way) const {
} }
return tr::lng_in_dlg_file(tr::now); return tr::lng_in_dlg_file(tr::now);
}(); }();
const auto caption = (way == DrawInDialog::WithoutSenderAndCaption) const auto caption = options.hideCaption
? QString() ? QString()
: parent()->originalText().text; : parent()->originalText().text;
return WithCaptionDialogsText(type, caption); // #TODO minis generate images and support albums
return {
.text = WithCaptionDialogsText(type, caption),
};
} }
QString MediaFile::notificationText() const { QString MediaFile::notificationText() const {
@ -855,8 +866,11 @@ Data::CloudImage *MediaLocation::location() const {
return _location; return _location;
} }
QString MediaLocation::chatListText(DrawInDialog way) const { ItemPreview MediaLocation::toPreview(ToPreviewOptions options) const {
return WithCaptionDialogsText(tr::lng_maps_point(tr::now), _title); // #TODO minis generate images
return {
.text = WithCaptionDialogsText(tr::lng_maps_point(tr::now), _title),
};
} }
QString MediaLocation::notificationText() const { QString MediaLocation::notificationText() const {
@ -1049,8 +1063,8 @@ bool MediaWebPage::replyPreviewLoaded() const {
return true; return true;
} }
QString MediaWebPage::chatListText(DrawInDialog way) const { ItemPreview MediaWebPage::toPreview(ToPreviewOptions options) const {
return notificationText(); return { .text = notificationText() };
} }
QString MediaWebPage::notificationText() const { QString MediaWebPage::notificationText() const {

View file

@ -27,7 +27,8 @@ namespace HistoryView {
enum class Context : char; enum class Context : char;
class Element; class Element;
class Media; class Media;
enum class DrawInDialog; struct ItemPreview;
struct ToPreviewOptions;
} // namespace HistoryView } // namespace HistoryView
namespace Data { namespace Data {
@ -73,7 +74,8 @@ public:
not_null<HistoryItem*> parent() const; not_null<HistoryItem*> parent() const;
using DrawInDialog = HistoryView::DrawInDialog; using ToPreviewOptions = HistoryView::ToPreviewOptions;
using ItemPreview = HistoryView::ItemPreview;
virtual std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) = 0; virtual std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) = 0;
@ -95,7 +97,7 @@ public:
virtual bool replyPreviewLoaded() const; virtual bool replyPreviewLoaded() const;
// Returns text with link-start and link-end commands for service-color highlighting. // Returns text with link-start and link-end commands for service-color highlighting.
// Example: "[link1-start]You:[link1-end] [link1-start]Photo,[link1-end] caption text" // Example: "[link1-start]You:[link1-end] [link1-start]Photo,[link1-end] caption text"
virtual QString chatListText(DrawInDialog way) const; virtual ItemPreview toPreview(ToPreviewOptions way) const;
virtual QString notificationText() const = 0; virtual QString notificationText() const = 0;
virtual QString pinnedTextSubstring() const = 0; virtual QString pinnedTextSubstring() const = 0;
virtual TextForMimeData clipboardText() const = 0; virtual TextForMimeData clipboardText() const = 0;
@ -151,7 +153,7 @@ public:
bool hasReplyPreview() const override; bool hasReplyPreview() const override;
Image *replyPreview() const override; Image *replyPreview() const override;
bool replyPreviewLoaded() const override; bool replyPreviewLoaded() const override;
QString chatListText(DrawInDialog way) const override; ItemPreview toPreview(ToPreviewOptions options) const override;
QString notificationText() const override; QString notificationText() const override;
QString pinnedTextSubstring() const override; QString pinnedTextSubstring() const override;
TextForMimeData clipboardText() const override; TextForMimeData clipboardText() const override;
@ -189,7 +191,7 @@ public:
bool hasReplyPreview() const override; bool hasReplyPreview() const override;
Image *replyPreview() const override; Image *replyPreview() const override;
bool replyPreviewLoaded() const override; bool replyPreviewLoaded() const override;
QString chatListText(DrawInDialog way) const override; ItemPreview toPreview(ToPreviewOptions options) const override;
QString notificationText() const override; QString notificationText() const override;
QString pinnedTextSubstring() const override; QString pinnedTextSubstring() const override;
TextForMimeData clipboardText() const override; TextForMimeData clipboardText() const override;
@ -255,7 +257,7 @@ public:
std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override; std::unique_ptr<Media> clone(not_null<HistoryItem*> parent) override;
Data::CloudImage *location() const override; Data::CloudImage *location() const override;
QString chatListText(DrawInDialog way) const override; ItemPreview toPreview(ToPreviewOptions options) const override;
QString notificationText() const override; QString notificationText() const override;
QString pinnedTextSubstring() const override; QString pinnedTextSubstring() const override;
TextForMimeData clipboardText() const override; TextForMimeData clipboardText() const override;
@ -323,7 +325,7 @@ public:
bool hasReplyPreview() const override; bool hasReplyPreview() const override;
Image *replyPreview() const override; Image *replyPreview() const override;
bool replyPreviewLoaded() const override; bool replyPreviewLoaded() const override;
QString chatListText(DrawInDialog way) const override; ItemPreview toPreview(ToPreviewOptions options) const override;
QString notificationText() const override; QString notificationText() const override;
QString pinnedTextSubstring() const override; QString pinnedTextSubstring() const override;
TextForMimeData clipboardText() const override; TextForMimeData clipboardText() const override;

View file

@ -1382,6 +1382,10 @@ void Session::requestItemRepaint(not_null<const HistoryItem*> item) {
} }
} }
} }
const auto history = item->history();
if (history->lastItemDialogsView.dependsOn(item)) {
history->updateChatListEntry();
}
} }
rpl::producer<not_null<const HistoryItem*>> Session::itemRepaintRequest() const { rpl::producer<not_null<const HistoryItem*>> Session::itemRepaintRequest() const {

View file

@ -296,3 +296,6 @@ dialogsScamFont: font(9px semibold);
dialogsScamSkip: 4px; dialogsScamSkip: 4px;
dialogsScamRadius: 2px; dialogsScamRadius: 2px;
dialogsMiniPreview: 32px;
dialogsMiniPreviewSkip: 4px;
dialogsMiniPreviewRight: 6px;

View file

@ -43,8 +43,7 @@ uint64 PinnedDialogPos(int pinnedIndex) {
} // namespace } // namespace
Entry::Entry(not_null<Data::Session*> owner, Type type) Entry::Entry(not_null<Data::Session*> owner, Type type)
: lastItemTextCache(st::dialogsTextWidthMin) : _owner(owner)
, _owner(owner)
, _isFolder(type == Type::Folder) { , _isFolder(type == Type::Folder) {
} }

View file

@ -183,13 +183,10 @@ public:
paintUserpic(p, view, rtl() ? (w - x - size) : x, y, size); paintUserpic(p, view, rtl() ? (w - x - size) : x, y, size);
} }
TimeId chatListTimeId() const { [[nodiscard]] TimeId chatListTimeId() const {
return _timeId; return _timeId;
} }
mutable const HistoryItem *textCachedFor = nullptr; // cache
mutable Ui::Text::String lastItemTextCache;
protected: protected:
void notifyUnreadStateChange(const UnreadState &wasState); void notifyUnreadStateChange(const UnreadState &wasState);
auto unreadStateChangeNotifier(bool required) { auto unreadStateChangeNotifier(bool required) {

View file

@ -154,14 +154,6 @@ InnerWidget::InnerWidget(
dialogRowReplaced(r.old, r.now); dialogRowReplaced(r.old, r.now);
}, lifetime()); }, lifetime());
session().data().itemRepaintRequest(
) | rpl::start_with_next([=](auto item) {
const auto history = item->history();
if (history->textCachedFor == item) {
history->updateChatListEntry();
}
}, lifetime());
session().data().sendActionManager().animationUpdated( session().data().sendActionManager().animationUpdated(
) | rpl::start_with_next([=]( ) | rpl::start_with_next([=](
const Data::SendActionManager::AnimationUpdate &update) { const Data::SendActionManager::AnimationUpdate &update) {
@ -235,16 +227,9 @@ InnerWidget::InnerWidget(
}, lifetime()); }, lifetime());
session().changes().messageUpdates( session().changes().messageUpdates(
Data::MessageUpdate::Flag::DialogRowRepaint Data::MessageUpdate::Flag::DialogRowRefresh
| Data::MessageUpdate::Flag::DialogRowRefresh
) | rpl::start_with_next([=](const Data::MessageUpdate &update) { ) | rpl::start_with_next([=](const Data::MessageUpdate &update) {
const auto item = update.item; refreshDialogRow({ update.item->history(), update.item->fullId() });
if (update.flags & Data::MessageUpdate::Flag::DialogRowRefresh) {
refreshDialogRow({ item->history(), item->fullId() });
}
if (update.flags & Data::MessageUpdate::Flag::DialogRowRepaint) {
repaintDialogRow({ item->history(), item->fullId() });
}
}, lifetime()); }, lifetime());
session().changes().entryUpdates( session().changes().entryUpdates(
@ -1530,7 +1515,7 @@ void InnerWidget::refreshDialogRow(RowDescriptor row) {
if (row.fullId) { if (row.fullId) {
for (const auto &result : _searchResults) { for (const auto &result : _searchResults) {
if (result->item()->fullId() == row.fullId) { if (result->item()->fullId() == row.fullId) {
result->invalidateCache(); result->itemView().itemInvalidated(result->item());
} }
} }
} }

View file

@ -285,8 +285,7 @@ void Row::validateListEntryCache() const {
FakeRow::FakeRow(Key searchInChat, not_null<HistoryItem*> item) FakeRow::FakeRow(Key searchInChat, not_null<HistoryItem*> item)
: _searchInChat(searchInChat) : _searchInChat(searchInChat)
, _item(item) , _item(item) {
, _cache(st::dialogsTextWidthMin) {
} }
} // namespace Dialogs } // namespace Dialogs

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/text/text.h" #include "ui/text/text.h"
#include "ui/effects/animations.h" #include "ui/effects/animations.h"
#include "dialogs/dialogs_key.h" #include "dialogs/dialogs_key.h"
#include "dialogs/ui/dialogs_message_view.h"
class History; class History;
class HistoryItem; class HistoryItem;
@ -138,10 +139,8 @@ public:
[[nodiscard]] not_null<HistoryItem*> item() const { [[nodiscard]] not_null<HistoryItem*> item() const {
return _item; return _item;
} }
[[nodiscard]] Ui::MessageView &itemView() const {
void invalidateCache() { return _itemView;
_cacheFor = nullptr;
_cache = Ui::Text::String();
} }
private: private:
@ -149,8 +148,7 @@ private:
Key _searchInChat; Key _searchInChat;
not_null<HistoryItem*> _item; not_null<HistoryItem*> _item;
mutable const HistoryItem *_cacheFor = nullptr; mutable Ui::MessageView _itemView;
mutable Ui::Text::String _cache;
}; };

View file

@ -775,14 +775,13 @@ void RowPainter::paint(
if (const auto folder = row->folder()) { if (const auto folder = row->folder()) {
PaintListEntryText(p, itemRect, active, selected, row); PaintListEntryText(p, itemRect, active, selected, row);
} else if (history && !actionWasPainted) { } else if (history && !actionWasPainted) {
item->drawInDialog( history->lastItemDialogsView.paint(
p, p,
item,
itemRect, itemRect,
active, active,
selected, selected,
HistoryItem::DrawInDialog::Normal, {});
history->textCachedFor,
history->lastItemTextCache);
} }
}; };
const auto paintCounterCallback = [&] { const auto paintCounterCallback = [&] {
@ -845,15 +844,15 @@ void RowPainter::paint(
} }
return nullptr; return nullptr;
}(); }();
const auto drawInDialogWay = [&] { const auto previewOptions = [&]() -> HistoryView::ToPreviewOptions {
if (const auto searchChat = row->searchInChat()) { if (const auto searchChat = row->searchInChat()) {
if (const auto peer = searchChat.peer()) { if (const auto peer = searchChat.peer()) {
if (!peer->isChannel() || peer->isMegagroup()) { if (!peer->isChannel() || peer->isMegagroup()) {
return HistoryItem::DrawInDialog::WithoutSender; return { .hideSender = true };
} }
} }
} }
return HistoryItem::DrawInDialog::Normal; return {};
}(); }();
const auto unreadCount = displayUnreadInfo const auto unreadCount = displayUnreadInfo
@ -895,14 +894,13 @@ void RowPainter::paint(
texttop, texttop,
availableWidth, availableWidth,
st::dialogsTextFont->height); st::dialogsTextFont->height);
item->drawInDialog( row->itemView().paint(
p, p,
item,
itemRect, itemRect,
active, active,
selected, selected,
drawInDialogWay, previewOptions);
row->_cacheFor,
row->_cache);
}; };
const auto paintCounterCallback = [&] { const auto paintCounterCallback = [&] {
PaintNarrowCounter( PaintNarrowCounter(

View file

@ -0,0 +1,69 @@
/*
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
*/
#include "dialogs/ui/dialogs_message_view.h"
#include "history/history_item.h"
#include "ui/text/text_options.h"
#include "styles/style_dialogs.h"
namespace Dialogs::Ui {
namespace {
} // namespace
MessageView::MessageView()
: _textCache(st::dialogsTextWidthMin) {
}
MessageView::~MessageView() = default;
void MessageView::itemInvalidated(not_null<const HistoryItem*> item) {
if (_textCachedFor == item.get()) {
_textCachedFor = nullptr;
}
}
bool MessageView::dependsOn(not_null<const HistoryItem*> item) const {
return (_textCachedFor == item.get());
}
void MessageView::paint(
Painter &p,
not_null<const HistoryItem*> item,
const QRect &geometry,
bool active,
bool selected,
ToPreviewOptions options) const {
if (geometry.isEmpty()) {
return;
}
if (_textCachedFor != item.get()) {
const auto preview = item->toPreview(options);
_textCache.setText(
st::dialogsTextStyle,
preview.text,
DialogTextOptions());
_textCachedFor = item;
}
p.setTextPalette(active
? st::dialogsTextPaletteActive
: selected
? st::dialogsTextPaletteOver
: st::dialogsTextPalette);
p.setFont(st::dialogsTextFont);
p.setPen(active ? st::dialogsTextFgActive : (selected ? st::dialogsTextFgOver : st::dialogsTextFg));
_textCache.drawElided(
p,
geometry.left(),
geometry.top(),
geometry.width(),
geometry.height() / st::dialogsTextFont->height);
p.restoreTextPalette();
}
} // namespace Dialogs::Ui

View file

@ -0,0 +1,51 @@
/*
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
class HistoryItem;
namespace Ui {
} // namespace Ui
namespace HistoryView {
struct ToPreviewOptions;
struct ItemPreview;
} // namespace HistoryView
namespace Dialogs::Ui {
using namespace ::Ui;
class MessageView final {
public:
MessageView();
~MessageView();
using ToPreviewOptions = HistoryView::ToPreviewOptions;
using ItemPreview = HistoryView::ItemPreview;
void itemInvalidated(not_null<const HistoryItem*> item);
[[nodiscard]] bool dependsOn(not_null<const HistoryItem*> item) const;
void paint(
Painter &p,
not_null<const HistoryItem*> item,
const QRect &geometry,
bool active,
bool selected,
ToPreviewOptions options) const;
private:
mutable const HistoryItem *_textCachedFor = nullptr;
mutable Ui::Text::String _textCache;
mutable std::vector<QImage> _imagesCache;
};
} // namespace Dialogs::Ui

View file

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_peer.h" #include "data/data_peer.h"
#include "data/data_drafts.h" #include "data/data_drafts.h"
#include "dialogs/dialogs_entry.h" #include "dialogs/dialogs_entry.h"
#include "dialogs/ui/dialogs_message_view.h"
#include "history/view/history_view_send_action.h" #include "history/view/history_view_send_action.h"
#include "base/observer.h" #include "base/observer.h"
#include "base/timer.h" #include "base/timer.h"
@ -457,6 +458,7 @@ public:
mtpRequestId sendRequestId = 0; mtpRequestId sendRequestId = 0;
Ui::Text::String cloudDraftTextCache; Ui::Text::String cloudDraftTextCache;
Dialogs::Ui::MessageView lastItemDialogsView;
private: private:
friend class HistoryBlock; friend class HistoryBlock;

View file

@ -49,6 +49,8 @@ namespace {
constexpr auto kNotificationTextLimit = 255; constexpr auto kNotificationTextLimit = 255;
using ItemPreview = HistoryView::ItemPreview;
enum class MediaCheckResult { enum class MediaCheckResult {
Good, Good,
Unsupported, Unsupported,
@ -292,17 +294,7 @@ void HistoryItem::invalidateChatListEntry() {
history()->session().changes().messageUpdated( history()->session().changes().messageUpdated(
this, this,
Data::MessageUpdate::Flag::DialogRowRefresh); Data::MessageUpdate::Flag::DialogRowRefresh);
history()->lastItemDialogsView.itemInvalidated(this);
// invalidate cache for drawInDialog
if (history()->textCachedFor == this) {
history()->textCachedFor = nullptr;
}
//if (const auto feed = history()->peer->feed()) { // #TODO archive
// if (feed->textCachedFor == this) {
// feed->textCachedFor = nullptr;
// feed->updateChatListEntry();
// }
//}
} }
void HistoryItem::finishEditionToEmpty() { void HistoryItem::finishEditionToEmpty() {
@ -946,20 +938,25 @@ QString HistoryItem::notificationText() const {
: result.mid(0, kNotificationTextLimit) + qsl("..."); : result.mid(0, kNotificationTextLimit) + qsl("...");
} }
QString HistoryItem::inDialogsText(DrawInDialog way) const { ItemPreview HistoryItem::toPreview(ToPreviewOptions options) const {
const auto plainText = [&] { auto result = [&]() -> ItemPreview {
if (_media) { if (_media) {
if (_groupId) { if (_groupId) {
return textcmdLink(1, TextUtilities::Clean(tr::lng_in_dlg_album(tr::now))); // #TODO minis generate albums correctly
return {
.text = textcmdLink(
1,
TextUtilities::Clean(tr::lng_in_dlg_album(tr::now))),
};
} }
return _media->chatListText(way); return _media->toPreview(options);
} else if (!emptyText()) { } else if (!emptyText()) {
return TextUtilities::Clean(_text.toString()); return { .text = TextUtilities::Clean(_text.toString()) };
} }
return QString(); return {};
}(); }();
const auto sender = [&]() -> PeerData* { const auto sender = [&]() -> PeerData* {
if (isPost() || isEmpty() || (way != DrawInDialog::Normal)) { if (options.hideSender || isPost() || isEmpty()) {
return nullptr; return nullptr;
} else if (!_history->peer->isUser() || out()) { } else if (!_history->peer->isUser() || out()) {
return displayFrom(); return displayFrom();
@ -969,39 +966,29 @@ QString HistoryItem::inDialogsText(DrawInDialog way) const {
return nullptr; return nullptr;
}(); }();
if (sender) { if (sender) {
auto fromText = sender->isSelf() ? tr::lng_from_you(tr::now) : sender->shortName(); const auto fromText = sender->isSelf()
auto fromWrapped = textcmdLink(1, tr::lng_dialogs_text_from_wrapped(tr::now, lt_from, TextUtilities::Clean(fromText))); ? tr::lng_from_you(tr::now)
return tr::lng_dialogs_text_with_from(tr::now, lt_from_part, fromWrapped, lt_message, plainText); : sender->shortName();
const auto fromWrapped = textcmdLink(
1,
tr::lng_dialogs_text_from_wrapped(
tr::now,
lt_from,
TextUtilities::Clean(fromText)));
result.text = tr::lng_dialogs_text_with_from(
tr::now,
lt_from_part,
fromWrapped,
lt_message,
std::move(result.text));
} }
return plainText; return result;
} }
Ui::Text::IsolatedEmoji HistoryItem::isolatedEmoji() const { Ui::Text::IsolatedEmoji HistoryItem::isolatedEmoji() const {
return Ui::Text::IsolatedEmoji(); return Ui::Text::IsolatedEmoji();
} }
void HistoryItem::drawInDialog(
Painter &p,
const QRect &r,
bool active,
bool selected,
DrawInDialog way,
const HistoryItem *&cacheFor,
Ui::Text::String &cache) const {
if (r.isEmpty()) {
return;
}
if (cacheFor != this) {
cacheFor = this;
cache.setText(st::dialogsTextStyle, inDialogsText(way), Ui::DialogTextOptions());
}
p.setTextPalette(active ? st::dialogsTextPaletteActive : (selected ? st::dialogsTextPaletteOver : st::dialogsTextPalette));
p.setFont(st::dialogsTextFont);
p.setPen(active ? st::dialogsTextFgActive : (selected ? st::dialogsTextFgOver : st::dialogsTextFg));
cache.drawElided(p, r.left(), r.top(), r.width(), r.height() / st::dialogsTextFont->height);
p.restoreTextPalette();
}
HistoryItem::~HistoryItem() { HistoryItem::~HistoryItem() {
applyTTL(0); applyTTL(0);
} }

View file

@ -46,17 +46,25 @@ class SessionController;
} // namespace Window } // namespace Window
namespace HistoryView { namespace HistoryView {
struct TextState; struct TextState;
struct StateRequest; struct StateRequest;
enum class CursorState : char; enum class CursorState : char;
enum class PointState : char; enum class PointState : char;
enum class Context : char; enum class Context : char;
class ElementDelegate; class ElementDelegate;
enum class DrawInDialog {
Normal, struct ToPreviewOptions {
WithoutSender, bool hideSender = false;
WithoutSenderAndCaption, bool hideCaption = false;
bool generateImages = true;
}; };
struct ItemPreview {
QString text;
std::vector<QImage> images;
};
} // namespace HistoryView } // namespace HistoryView
struct HiddenSenderInfo; struct HiddenSenderInfo;
@ -298,13 +306,18 @@ public:
} }
[[nodiscard]] virtual QString notificationText() const; [[nodiscard]] virtual QString notificationText() const;
using DrawInDialog = HistoryView::DrawInDialog; using ToPreviewOptions = HistoryView::ToPreviewOptions;
using ItemPreview = HistoryView::ItemPreview;
// Returns text with link-start and link-end commands for service-color highlighting. // Returns text with link-start and link-end commands for service-color highlighting.
// Example: "[link1-start]You:[link1-end] [link1-start]Photo,[link1-end] caption text" // Example: "[link1-start]You:[link1-end] [link1-start]Photo,[link1-end] caption text"
[[nodiscard]] virtual QString inDialogsText(DrawInDialog way) const; [[nodiscard]] virtual ItemPreview toPreview(
ToPreviewOptions options) const;
[[nodiscard]] virtual QString inReplyText() const { [[nodiscard]] virtual QString inReplyText() const {
return inDialogsText(DrawInDialog::WithoutSender); return toPreview({
.hideSender = true,
.generateImages = false,
}).text;
} }
[[nodiscard]] virtual Ui::Text::IsolatedEmoji isolatedEmoji() const; [[nodiscard]] virtual Ui::Text::IsolatedEmoji isolatedEmoji() const;
[[nodiscard]] virtual TextWithEntities originalText() const { [[nodiscard]] virtual TextWithEntities originalText() const {
@ -335,15 +348,6 @@ public:
virtual void incrementReplyToTopCounter() { virtual void incrementReplyToTopCounter() {
} }
void drawInDialog(
Painter &p,
const QRect &r,
bool active,
bool selected,
DrawInDialog way,
const HistoryItem *&cacheFor,
Ui::Text::String &cache) const;
[[nodiscard]] bool emptyText() const { [[nodiscard]] bool emptyText() const {
return _text.isEmpty(); return _text.isEmpty();
} }

View file

@ -43,6 +43,8 @@ namespace {
constexpr auto kPinnedMessageTextLimit = 16; constexpr auto kPinnedMessageTextLimit = 16;
using ItemPreview = HistoryView::ItemPreview;
[[nodiscard]] bool PeerCallKnown(not_null<PeerData*> peer) { [[nodiscard]] bool PeerCallKnown(not_null<PeerData*> peer) {
if (peer->groupCall() != nullptr) { if (peer->groupCall() != nullptr) {
return true; return true;
@ -923,14 +925,18 @@ bool HistoryService::needCheck() const {
return out() && !isEmpty(); return out() && !isEmpty();
} }
QString HistoryService::inDialogsText(DrawInDialog way) const { ItemPreview HistoryService::toPreview(ToPreviewOptions options) const {
return textcmdLink(1, TextUtilities::Clean(notificationText())); // #TODO minis generate images
return {
.text = textcmdLink(1, TextUtilities::Clean(notificationText())),
};
} }
QString HistoryService::inReplyText() const { QString HistoryService::inReplyText() const {
const auto result = HistoryService::notificationText(); const auto result = HistoryService::notificationText();
const auto text = result.trimmed().startsWith(author()->name) const auto &name = author()->name;
? result.trimmed().mid(author()->name.size()).trimmed() const auto text = result.trimmed().startsWith(name)
? result.trimmed().mid(name.size()).trimmed()
: result; : result;
return textcmdLink(1, text); return textcmdLink(1, text);
} }
@ -1190,19 +1196,7 @@ void HistoryService::updateDependentText() {
void HistoryService::updateText(PreparedText &&text) { void HistoryService::updateText(PreparedText &&text) {
setServiceText(text); setServiceText(text);
history()->owner().requestItemResize(this); history()->owner().requestItemResize(this);
const auto inDialogsHistory = history()->migrateToOrMe(); invalidateChatListEntry();
if (inDialogsHistory->textCachedFor == this) {
inDialogsHistory->textCachedFor = nullptr;
}
//if (const auto feed = history()->peer->feed()) { // #TODO archive
// if (feed->textCachedFor == this) {
// feed->textCachedFor = nullptr;
// feed->updateChatListEntry();
// }
//}
history()->session().changes().messageUpdated(
this,
Data::MessageUpdate::Flag::DialogRowRepaint);
history()->owner().updateDependentMessages(this); history()->owner().updateDependentMessages(this);
} }

View file

@ -111,7 +111,7 @@ public:
bool serviceMsg() const override { bool serviceMsg() const override {
return true; return true;
} }
QString inDialogsText(DrawInDialog way) const override; ItemPreview toPreview(ToPreviewOptions options) const override;
QString inReplyText() const override; QString inReplyText() const override;
std::unique_ptr<HistoryView::Element> createView( std::unique_ptr<HistoryView::Element> createView(

View file

@ -6853,11 +6853,16 @@ void HistoryWidget::updateForwardingTexts() {
} }
if (count < 2) { if (count < 2) {
text = _toForward.items.front()->inDialogsText(keepCaptions // #TODO minis use images
? HistoryItem::DrawInDialog::WithoutSender text = _toForward.items.front()->toPreview({
: HistoryItem::DrawInDialog::WithoutSenderAndCaption); .hideSender = true,
.hideCaption = !keepCaptions,
.generateImages = false,
}).text;
} else { } else {
text = textcmdLink(1, tr::lng_forward_messages(tr::now, lt_count, count)); text = textcmdLink(
1,
tr::lng_forward_messages(tr::now, lt_count, count));
} }
} }
_toForwardFrom.setText(st::msgNameStyle, from, Ui::NameTextOptions()); _toForwardFrom.setText(st::msgNameStyle, from, Ui::NameTextOptions());

View file

@ -804,10 +804,11 @@ void Notification::updateNotifyDisplay() {
p.setTextPalette(st::dialogsTextPalette); p.setTextPalette(st::dialogsTextPalette);
p.setPen(st::dialogsTextFg); p.setPen(st::dialogsTextFg);
p.setFont(st::dialogsTextFont); p.setFont(st::dialogsTextFont);
const auto text = _item const auto text = _item // #TODO minis use images
? _item->inDialogsText(reminder ? _item->toPreview({
? HistoryItem::DrawInDialog::WithoutSender .hideSender = reminder,
: HistoryItem::DrawInDialog::Normal) .generateImages = false,
}).text
: ((!_author.isEmpty() : ((!_author.isEmpty()
? textcmdLink(1, _author) ? textcmdLink(1, _author)
: QString()) : QString())