tdesktop/Telegram/SourceFiles/dialogs/ui/dialogs_message_view.cpp

225 lines
5.4 KiB
C++

/*
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.h"
#include "history/history_item.h"
#include "history/view/history_view_item_preview.h"
#include "main/main_session.h"
#include "ui/text/text_options.h"
#include "ui/text/text_utilities.h"
#include "ui/image/image.h"
#include "lang/lang_keys.h"
#include "styles/style_dialogs.h"
namespace {
template <ushort kTag>
struct TextWithTagOffset {
TextWithTagOffset(QString text) : text(text) {
}
static TextWithTagOffset FromString(const QString &text) {
return { text };
}
QString text;
int offset = -1;
};
} // namespace
namespace Lang {
template <ushort kTag>
struct ReplaceTag<TextWithTagOffset<kTag>> {
static TextWithTagOffset<kTag> Call(
TextWithTagOffset<kTag> &&original,
ushort tag,
const TextWithTagOffset<kTag> &replacement);
};
template <ushort kTag>
TextWithTagOffset<kTag> ReplaceTag<TextWithTagOffset<kTag>>::Call(
TextWithTagOffset<kTag> &&original,
ushort tag,
const TextWithTagOffset<kTag> &replacement) {
const auto replacementPosition = FindTagReplacementPosition(
original.text,
tag);
if (replacementPosition < 0) {
return std::move(original);
}
original.text = ReplaceTag<QString>::Replace(
std::move(original.text),
replacement.text,
replacementPosition);
if (tag == kTag) {
original.offset = replacementPosition;
} else if (original.offset > replacementPosition) {
constexpr auto kReplaceCommandLength = 4;
original.offset += replacement.text.size() - kReplaceCommandLength;
}
return std::move(original);
}
} // namespace Lang
namespace Dialogs::Ui {
namespace {
} // namespace
struct MessageView::LoadingContext {
std::any context;
rpl::lifetime lifetime;
};
MessageView::MessageView()
: _senderCache(st::dialogsTextWidthMin)
, _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()) {
options.existing = &_imagesCache;
auto preview = item->toPreview(options);
if (!preview.images.empty() && preview.imagesInTextPosition > 0) {
auto sender = ::Ui::Text::Mid(
preview.text,
0,
preview.imagesInTextPosition);
TextUtilities::Trim(sender);
_senderCache.setMarkedText(
st::dialogsTextStyle,
std::move(sender),
DialogTextOptions());
preview.text = ::Ui::Text::Mid(
preview.text,
preview.imagesInTextPosition);
} else {
_senderCache = { st::dialogsTextWidthMin };
}
TextUtilities::Trim(preview.text);
_textCache.setMarkedText(
st::dialogsTextStyle,
preview.text,
DialogTextOptions());
_textCachedFor = item;
_imagesCache = std::move(preview.images);
if (preview.loadingContext.has_value()) {
if (!_loadingContext) {
_loadingContext = std::make_unique<LoadingContext>();
item->history()->session().downloaderTaskFinished(
) | rpl::start_with_next([=] {
_textCachedFor = nullptr;
}, _loadingContext->lifetime);
}
_loadingContext->context = std::move(preview.loadingContext);
} else {
_loadingContext = nullptr;
}
}
p.setTextPalette(active
? st::dialogsTextPaletteActive
: selected
? st::dialogsTextPaletteOver
: st::dialogsTextPalette);
p.setFont(st::dialogsTextFont);
p.setPen(active
? st::dialogsTextFgActive
: selected
? st::dialogsTextFgOver
: st::dialogsTextFg);
const auto guard = gsl::finally([&] {
p.restoreTextPalette();
});
auto rect = geometry;
if (!_senderCache.isEmpty()) {
_senderCache.drawElided(
p,
rect.left(),
rect.top(),
rect.width(),
rect.height() / st::dialogsTextFont->height);
const auto skip = st::dialogsMiniPreviewSkip
+ st::dialogsMiniPreviewRight;
rect.setLeft(rect.x() + _senderCache.maxWidth() + skip);
}
for (const auto &image : _imagesCache) {
if (rect.width() < st::dialogsMiniPreview) {
break;
}
p.drawImage(
rect.x(),
rect.y() + st::dialogsMiniPreviewTop,
image.data);
rect.setLeft(rect.x()
+ st::dialogsMiniPreview
+ st::dialogsMiniPreviewSkip);
}
if (!_imagesCache.empty()) {
rect.setLeft(rect.x() + st::dialogsMiniPreviewRight);
}
if (rect.isEmpty()) {
return;
}
_textCache.drawElided(
p,
rect.left(),
rect.top(),
rect.width(),
rect.height() / st::dialogsTextFont->height);
}
HistoryView::ItemPreview PreviewWithSender(
HistoryView::ItemPreview &&preview,
const TextWithEntities &sender) {
const auto textWithOffset = tr::lng_dialogs_text_with_from(
tr::now,
lt_from_part,
sender.text,
lt_message,
preview.text.text,
TextWithTagOffset<lt_from_part>::FromString);
preview.text = tr::lng_dialogs_text_with_from(
tr::now,
lt_from_part,
sender,
lt_message,
std::move(preview.text),
Ui::Text::WithEntities);
preview.imagesInTextPosition = (textWithOffset.offset < 0)
? 0
: textWithOffset.offset + sender.text.size();
return std::move(preview);
}
} // namespace Dialogs::Ui