mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Move preview paint to Dialogs::Ui::MessageView.
This commit is contained in:
parent
5136cc3c9c
commit
8c21fad642
21 changed files with 265 additions and 151 deletions
|
@ -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
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -296,3 +296,6 @@ dialogsScamFont: font(9px semibold);
|
||||||
dialogsScamSkip: 4px;
|
dialogsScamSkip: 4px;
|
||||||
dialogsScamRadius: 2px;
|
dialogsScamRadius: 2px;
|
||||||
|
|
||||||
|
dialogsMiniPreview: 32px;
|
||||||
|
dialogsMiniPreviewSkip: 4px;
|
||||||
|
dialogsMiniPreviewRight: 6px;
|
||||||
|
|
|
@ -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) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
@ -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;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
69
Telegram/SourceFiles/dialogs/ui/dialogs_message_view.cpp
Normal file
69
Telegram/SourceFiles/dialogs/ui/dialogs_message_view.cpp
Normal 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
|
51
Telegram/SourceFiles/dialogs/ui/dialogs_message_view.h
Normal file
51
Telegram/SourceFiles/dialogs/ui/dialogs_message_view.h
Normal 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
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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())
|
||||||
|
|
Loading…
Add table
Reference in a new issue