Use MediaInBubble for chat intro fake-message.

This commit is contained in:
John Preston 2024-03-20 16:50:29 +04:00
parent 0887348611
commit 5381fe5a1a
5 changed files with 277 additions and 54 deletions

View file

@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_document.h" #include "data/data_document.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_user.h" #include "data/data_user.h"
#include "history/view/media/history_view_giveaway.h"
#include "history/view/media/history_view_service_box.h" #include "history/view/media/history_view_service_box.h"
#include "history/view/media/history_view_sticker_player_abstract.h" #include "history/view/media/history_view_sticker_player_abstract.h"
#include "history/view/media/history_view_sticker.h" #include "history/view/media/history_view_sticker.h"
@ -106,6 +107,49 @@ private:
}; };
auto GenerateChatIntro(
not_null<Element*> parent,
Element *replacing,
const Data::ChatIntro &data)
-> Fn<void(Fn<void(std::unique_ptr<MediaInBubble::Part>)>)> {
return [=](Fn<void(std::unique_ptr<MediaInBubble::Part>)> push) {
auto pushText = [&](
TextWithEntities text,
QMargins margins = {},
const base::flat_map<uint16, ClickHandlerPtr> &links = {}) {
if (text.empty()) {
return;
}
push(std::make_unique<TextMediaInBubblePart>(
std::move(text),
margins,
links));
};
const auto title = data
? data.title
: tr::lng_chat_intro_default_title(tr::now);
const auto description = data
? data.description
: tr::lng_chat_intro_default_message(tr::now);
pushText(Ui::Text::Bold(title), st::chatIntroTitleMargin);
pushText({ description }, title.isEmpty()
? st::chatIntroTitleMargin
: st::chatIntroMargin);
const auto sticker = [=] {
using Tag = ChatHelpers::StickerLottieSize;
return StickerInBubblePart::Data{
.sticker = data.sticker,
.size = st::chatIntroStickerSize,
.cacheTag = Tag::ChatIntroHelloSticker,
};
};
push(std::make_unique<StickerInBubblePart>(
parent,
replacing,
sticker));
};
}
PremiumRequiredBox::PremiumRequiredBox(not_null<Element*> parent) PremiumRequiredBox::PremiumRequiredBox(not_null<Element*> parent)
: _parent(parent) { : _parent(parent) {
} }
@ -330,13 +374,18 @@ void AboutView::make(Data::ChatIntro data) {
| MessageFlag::FakeHistoryItem | MessageFlag::FakeHistoryItem
| MessageFlag::Local), | MessageFlag::Local),
.from = _history->peer->id, .from = _history->peer->id,
}, PreparedServiceText{ { data.description } }); }, PreparedServiceText{ { } });
setItem(AdminLog::OwnedItem(_delegate, item), data.sticker); auto owned = AdminLog::OwnedItem(_delegate, item);
owned->overrideMedia(std::make_unique<HistoryView::MediaInBubble>(
_item->overrideMedia(std::make_unique<ServiceBox>( owned.get(),
_item.get(), GenerateChatIntro(owned.get(), _item.get(), data),
std::make_unique<ChatIntroBox>(_item.get(), data))); HistoryView::MediaInBubbleDescriptor{
.maxWidth = st::chatIntroWidth,
.service = true,
.hideServiceText = true,
}));
setItem(std::move(owned), data.sticker);
} }
void AboutView::setItem(AdminLog::OwnedItem item, DocumentData *sticker) { void AboutView::setItem(AdminLog::OwnedItem item, DocumentData *sticker) {

View file

@ -632,6 +632,4 @@ private:
}; };
constexpr auto size = sizeof(Element);
} // namespace HistoryView } // namespace HistoryView

View file

@ -73,10 +73,21 @@ bool MediaInBubble::Part::hasHeavyPart() {
void MediaInBubble::Part::unloadHeavyPart() { void MediaInBubble::Part::unloadHeavyPart() {
} }
auto MediaInBubble::Part::stickerTakePlayer(
not_null<DocumentData*> data,
const Lottie::ColorReplacements *replacements
) -> std::unique_ptr<StickerPlayer> {
return nullptr;
}
MediaInBubble::MediaInBubble( MediaInBubble::MediaInBubble(
not_null<Element*> parent, not_null<Element*> parent,
Fn<void(Fn<void(std::unique_ptr<Part>)>)> generate) Fn<void(Fn<void(std::unique_ptr<Part>)>)> generate,
: Media(parent) { MediaInBubbleDescriptor &&descriptor)
: Media(parent)
, _maxWidthCap(descriptor.maxWidth)
, _service(descriptor.service)
, _hideServiceText(descriptor.hideServiceText) {
generate([&](std::unique_ptr<Part> part) { generate([&](std::unique_ptr<Part> part) {
_entries.push_back({ _entries.push_back({
.object = std::move(part), .object = std::move(part),
@ -92,7 +103,9 @@ MediaInBubble::~MediaInBubble() {
} }
QSize MediaInBubble::countOptimalSize() { QSize MediaInBubble::countOptimalSize() {
const auto maxWidth = st::chatGiveawayWidth; const auto maxWidth = _maxWidthCap
? _maxWidthCap
: st::chatGiveawayWidth;
auto top = 0; auto top = 0;
for (auto &entry : _entries) { for (auto &entry : _entries) {
@ -104,19 +117,26 @@ QSize MediaInBubble::countOptimalSize() {
} }
QSize MediaInBubble::countCurrentSize(int newWidth) { QSize MediaInBubble::countCurrentSize(int newWidth) {
return { maxWidth(), minHeight()}; return { maxWidth(), minHeight() };
} }
void MediaInBubble::draw(Painter &p, const PaintContext &context) const { void MediaInBubble::draw(Painter &p, const PaintContext &context) const {
const auto outer = width(); const auto outer = width();
if (outer < st::msgPadding.left() + st::msgPadding.right() + 1) { if (outer < st::msgPadding.left() + st::msgPadding.right() + 1) {
return; return;
} else {
PainterHighQualityEnabler hq(p);
const auto radius = st::msgServiceGiftBoxRadius;
p.setPen(Qt::NoPen);
p.setBrush(context.st->msgServiceBg());
p.drawRoundedRect(QRect(0, 0, width(), height()), radius, radius);
} }
auto translated = 0; auto translated = 0;
for (const auto &entry : _entries) { for (const auto &entry : _entries) {
const auto raw = entry.object.get(); const auto raw = entry.object.get();
const auto height = raw->height(); const auto height = raw->height();
raw->draw(p, context, outer); raw->draw(p, this, context, outer);
translated += height; translated += height;
p.translate(0, height); p.translate(0, height);
} }
@ -159,10 +179,28 @@ void MediaInBubble::clickHandlerPressedChanged(
} }
} }
std::unique_ptr<StickerPlayer> MediaInBubble::stickerTakePlayer(
not_null<DocumentData*> data,
const Lottie::ColorReplacements *replacements) {
for (const auto &entry : _entries) {
if (auto result = entry.object->stickerTakePlayer(
data,
replacements)) {
return result;
}
}
return nullptr;
}
bool MediaInBubble::hideFromName() const { bool MediaInBubble::hideFromName() const {
return !parent()->data()->Has<HistoryMessageForwarded>(); return !parent()->data()->Has<HistoryMessageForwarded>();
} }
bool MediaInBubble::hideServiceText() const {
return _hideServiceText;
}
bool MediaInBubble::hasHeavyPart() const { bool MediaInBubble::hasHeavyPart() const {
for (const auto &entry : _entries) { for (const auto &entry : _entries) {
if (entry.object->hasHeavyPart()) { if (entry.object->hasHeavyPart()) {
@ -200,15 +238,21 @@ TextMediaInBubblePart::TextMediaInBubblePart(
void TextMediaInBubblePart::draw( void TextMediaInBubblePart::draw(
Painter &p, Painter &p,
not_null<const MediaInBubble*> owner,
const PaintContext &context, const PaintContext &context,
int outerWidth) const { int outerWidth) const {
p.setPen(context.messageStyle()->historyTextFg); const auto service = owner->service();
p.setPen(service
? context.st->msgServiceFg()
: context.messageStyle()->historyTextFg);
_text.draw(p, { _text.draw(p, {
.position = { (outerWidth - width()) / 2, _margins.top() }, .position = { (outerWidth - width()) / 2, _margins.top() },
.outerWidth = outerWidth, .outerWidth = outerWidth,
.availableWidth = width(), .availableWidth = width(),
.align = style::al_top, .align = style::al_top,
.palette = &context.messageStyle()->textPalette, .palette = &(service
? context.st->serviceTextPalette()
: context.messageStyle()->textPalette),
.now = context.now, .now = context.now,
}); });
} }
@ -253,6 +297,7 @@ TextDelimeterPart::TextDelimeterPart(
void TextDelimeterPart::draw( void TextDelimeterPart::draw(
Painter &p, Painter &p,
not_null<const MediaInBubble*> owner,
const PaintContext &context, const PaintContext &context,
int outerWidth) const { int outerWidth) const {
const auto stm = context.messageStyle(); const auto stm = context.messageStyle();
@ -294,18 +339,18 @@ QSize TextDelimeterPart::countCurrentSize(int newWidth) {
return { newWidth, minHeight() }; return { newWidth, minHeight() };
} }
StickerWithBadgePart::StickerWithBadgePart( StickerInBubblePart::StickerInBubblePart(
not_null<Element*> parent, not_null<Element*> parent,
Fn<Data()> lookup, Element *replacing,
QString badge) Fn<Data()> lookup)
: _parent(parent) : _parent(parent)
, _lookup(std::move(lookup)) , _lookup(std::move(lookup)) {
, _badgeText(badge) { ensureCreated(replacing);
ensureCreated();
} }
void StickerWithBadgePart::draw( void StickerInBubblePart::draw(
Painter &p, Painter &p,
not_null<const MediaInBubble*> owner,
const PaintContext &context, const PaintContext &context,
int outerWidth) const { int outerWidth) const {
const auto stickerSize = st::msgServiceGiftBoxStickerSize; const auto stickerSize = st::msgServiceGiftBoxStickerSize;
@ -314,51 +359,99 @@ void StickerWithBadgePart::draw(
st::chatGiveawayStickerTop + _skipTop, st::chatGiveawayStickerTop + _skipTop,
stickerSize, stickerSize,
stickerSize); stickerSize);
ensureCreated();
if (_sticker) { if (_sticker) {
_sticker->draw(p, context, sticker); _sticker->draw(p, context, sticker);
paintBadge(p, context);
} else {
ensureCreated();
} }
} }
bool StickerWithBadgePart::hasHeavyPart() { bool StickerInBubblePart::hasHeavyPart() {
return _sticker && _sticker->hasHeavyPart(); return _sticker && _sticker->hasHeavyPart();
} }
void StickerWithBadgePart::unloadHeavyPart() { void StickerInBubblePart::unloadHeavyPart() {
if (_sticker) { if (_sticker) {
_sticker->unloadHeavyPart(); _sticker->unloadHeavyPart();
} }
} }
QSize StickerWithBadgePart::countOptimalSize() { std::unique_ptr<StickerPlayer> StickerInBubblePart::stickerTakePlayer(
not_null<DocumentData*> data,
const Lottie::ColorReplacements *replacements) {
return _sticker
? _sticker->stickerTakePlayer(data, replacements)
: nullptr;
}
QSize StickerInBubblePart::countOptimalSize() {
const auto size = st::msgServiceGiftBoxStickerSize; const auto size = st::msgServiceGiftBoxStickerSize;
return { size, st::chatGiveawayStickerTop + size }; return { size, st::chatGiveawayStickerTop + size };
} }
QSize StickerWithBadgePart::countCurrentSize(int newWidth) { QSize StickerInBubblePart::countCurrentSize(int newWidth) {
return { newWidth, minHeight() }; return { newWidth, minHeight() };
} }
void StickerWithBadgePart::ensureCreated() const { void StickerInBubblePart::ensureCreated(Element *replacing) const {
if (_sticker) { if (_sticker) {
return; return;
} else if (const auto data = _lookup()) { } else if (const auto data = _lookup()) {
const auto document = data.sticker; const auto sticker = data.sticker;
if (const auto sticker = document->sticker()) { if (const auto info = sticker->sticker()) {
const auto skipPremiumEffect = false; const auto skipPremiumEffect = true;
_skipTop = data.skipTop; _skipTop = data.skipTop;
_sticker.emplace(_parent, document, skipPremiumEffect, _parent); _sticker.emplace(_parent, sticker, skipPremiumEffect, replacing);
_sticker->setDiceIndex(sticker->alt, 1); if (data.singleTimePlayback) {
_sticker->initSize(data.isGiftBoxSticker _sticker->setDiceIndex(info->alt, 1);
? st::msgServiceGiftBoxStickerSize }
: 0); _sticker->initSize(data.size);
_sticker->setCustomCachingTag(data.cacheTag);
} }
} }
} }
StickerWithBadgePart::StickerWithBadgePart(
not_null<Element*> parent,
Element *replacing,
Fn<Data()> lookup,
QString badge)
: _sticker(parent, replacing, std::move(lookup))
, _badgeText(badge) {
}
void StickerWithBadgePart::draw(
Painter &p,
not_null<const MediaInBubble*> owner,
const PaintContext &context,
int outerWidth) const {
_sticker.draw(p, owner, context, outerWidth);
if (_sticker.resolved()) {
paintBadge(p, context);
}
}
bool StickerWithBadgePart::hasHeavyPart() {
return _sticker.hasHeavyPart();
}
void StickerWithBadgePart::unloadHeavyPart() {
_sticker.unloadHeavyPart();
}
std::unique_ptr<StickerPlayer> StickerWithBadgePart::stickerTakePlayer(
not_null<DocumentData*> data,
const Lottie::ColorReplacements *replacements) {
return _sticker.stickerTakePlayer(data, replacements);
}
QSize StickerWithBadgePart::countOptimalSize() {
return _sticker.countOptimalSize();
}
QSize StickerWithBadgePart::countCurrentSize(int newWidth) {
return _sticker.countCurrentSize(newWidth);
}
void StickerWithBadgePart::paintBadge( void StickerWithBadgePart::paintBadge(
Painter &p, Painter &p,
const PaintContext &context) const { const PaintContext &context) const {
@ -383,7 +476,7 @@ void StickerWithBadgePart::paintBadge(
p.drawRoundedRect(inner, radius, radius); p.drawRoundedRect(inner, radius, radius);
} }
if (!_parent->usesBubblePattern(context)) { if (!_sticker.parent()->usesBubblePattern(context)) {
paintContent(p); paintContent(p);
} else { } else {
Ui::PaintPatternBubblePart( Ui::PaintPatternBubblePart(
@ -458,6 +551,7 @@ PeerBubbleListPart::~PeerBubbleListPart() = default;
void PeerBubbleListPart::draw( void PeerBubbleListPart::draw(
Painter &p, Painter &p,
not_null<const MediaInBubble*> owner,
const PaintContext &context, const PaintContext &context,
int outerWidth) const { int outerWidth) const {
if (_peers.empty()) { if (_peers.empty()) {
@ -647,10 +741,15 @@ auto GenerateGiveawayStart(
const auto sticker = [=] { const auto sticker = [=] {
const auto &session = parent->history()->session(); const auto &session = parent->history()->session();
auto &packs = session.giftBoxStickersPacks(); auto &packs = session.giftBoxStickersPacks();
return Data{ packs.lookup(months), 0, true }; return Data{
.sticker = packs.lookup(months),
.size = st::msgServiceGiftBoxStickerSize,
.singleTimePlayback = true,
};
}; };
push(std::make_unique<StickerWithBadgePart>( push(std::make_unique<StickerWithBadgePart>(
parent, parent,
nullptr,
sticker, sticker,
tr::lng_prizes_badge( tr::lng_prizes_badge(
tr::now, tr::now,
@ -778,11 +877,15 @@ auto GenerateGiveawayResults(
const auto &session = parent->history()->session(); const auto &session = parent->history()->session();
auto &packs = session.diceStickersPacks(); auto &packs = session.diceStickersPacks();
const auto &emoji = Stickers::DicePacks::kPartyPopper; const auto &emoji = Stickers::DicePacks::kPartyPopper;
const auto skip = st::chatGiveawayWinnersTopSkip; return Data{
return Data{ packs.lookup(emoji, 0), skip }; .sticker = packs.lookup(emoji, 0),
.skipTop = st::chatGiveawayWinnersTopSkip,
.singleTimePlayback = true,
};
}; };
push(std::make_unique<StickerWithBadgePart>( push(std::make_unique<StickerWithBadgePart>(
parent, parent,
nullptr,
sticker, sticker,
tr::lng_prizes_badge( tr::lng_prizes_badge(
tr::now, tr::now,

View file

@ -22,6 +22,12 @@ class RippleAnimation;
namespace HistoryView { namespace HistoryView {
struct MediaInBubbleDescriptor {
int maxWidth = 0;
bool service = false;
bool hideServiceText = false;
};
class MediaInBubble final : public Media { class MediaInBubble final : public Media {
public: public:
class Part : public Object { class Part : public Object {
@ -30,6 +36,7 @@ public:
virtual void draw( virtual void draw(
Painter &p, Painter &p,
not_null<const MediaInBubble*> owner,
const PaintContext &context, const PaintContext &context,
int outerWidth) const = 0; int outerWidth) const = 0;
[[nodiscard]] virtual TextState textState( [[nodiscard]] virtual TextState textState(
@ -41,13 +48,22 @@ public:
bool pressed); bool pressed);
[[nodiscard]] virtual bool hasHeavyPart(); [[nodiscard]] virtual bool hasHeavyPart();
virtual void unloadHeavyPart(); virtual void unloadHeavyPart();
[[nodiscard]] virtual auto stickerTakePlayer(
not_null<DocumentData*> data,
const Lottie::ColorReplacements *replacements
) -> std::unique_ptr<StickerPlayer>;
}; };
MediaInBubble( MediaInBubble(
not_null<Element*> parent, not_null<Element*> parent,
Fn<void(Fn<void(std::unique_ptr<Part>)>)> generate); Fn<void(Fn<void(std::unique_ptr<Part>)>)> generate,
MediaInBubbleDescriptor &&descriptor = {});
~MediaInBubble(); ~MediaInBubble();
[[nodiscard]] bool service() const {
return _service;
}
void draw(Painter &p, const PaintContext &context) const override; void draw(Painter &p, const PaintContext &context) const override;
TextState textState(QPoint point, StateRequest request) const override; TextState textState(QPoint point, StateRequest request) const override;
@ -59,12 +75,16 @@ public:
bool pressed) override; bool pressed) override;
bool needsBubble() const override { bool needsBubble() const override {
return true; return !_service;;
} }
bool customInfoLayout() const override { bool customInfoLayout() const override {
return false; return false;
} }
std::unique_ptr<StickerPlayer> stickerTakePlayer(
not_null<DocumentData*> data,
const Lottie::ColorReplacements *replacements) override;
bool toggleSelectionByHandlerClick( bool toggleSelectionByHandlerClick(
const ClickHandlerPtr &p) const override { const ClickHandlerPtr &p) const override {
return true; return true;
@ -74,6 +94,7 @@ public:
} }
bool hideFromName() const override; bool hideFromName() const override;
bool hideServiceText() const override;
void unloadHeavyPart() override; void unloadHeavyPart() override;
bool hasHeavyPart() const override; bool hasHeavyPart() const override;
@ -89,6 +110,9 @@ private:
[[nodiscard]] QMargins inBubblePadding() const; [[nodiscard]] QMargins inBubblePadding() const;
std::vector<Entry> _entries; std::vector<Entry> _entries;
int _maxWidthCap = 0;
bool _service : 1 = false;
bool _hideServiceText : 1 = false;
}; };
@ -101,6 +125,7 @@ public:
void draw( void draw(
Painter &p, Painter &p,
not_null<const MediaInBubble*> owner,
const PaintContext &context, const PaintContext &context,
int outerWidth) const override; int outerWidth) const override;
TextState textState( TextState textState(
@ -123,6 +148,7 @@ public:
void draw( void draw(
Painter &p, Painter &p,
not_null<const MediaInBubble*> owner,
const PaintContext &context, const PaintContext &context,
int outerWidth) const override; int outerWidth) const override;
@ -135,24 +161,34 @@ private:
}; };
class StickerWithBadgePart final : public MediaInBubble::Part { class StickerInBubblePart final : public MediaInBubble::Part {
public: public:
struct Data { struct Data {
DocumentData *sticker = nullptr; DocumentData *sticker = nullptr;
int skipTop = 0; int skipTop = 0;
bool isGiftBoxSticker = false; int size = 0;
ChatHelpers::StickerLottieSize cacheTag = {};
bool singleTimePlayback = false;
explicit operator bool() const { explicit operator bool() const {
return sticker != nullptr; return sticker != nullptr;
} }
}; };
StickerWithBadgePart( StickerInBubblePart(
not_null<Element*> parent, not_null<Element*> parent,
Fn<Data()> lookup, Element *replacing,
QString badge); Fn<Data()> lookup);
[[nodiscard]] not_null<Element*> parent() const {
return _parent;
}
[[nodiscard]] bool resolved() const {
return _sticker.has_value();
}
void draw( void draw(
Painter &p, Painter &p,
not_null<const MediaInBubble*> owner,
const PaintContext &context, const PaintContext &context,
int outerWidth) const override; int outerWidth) const override;
bool hasHeavyPart() override; bool hasHeavyPart() override;
@ -161,16 +197,50 @@ public:
QSize countOptimalSize() override; QSize countOptimalSize() override;
QSize countCurrentSize(int newWidth) override; QSize countCurrentSize(int newWidth) override;
std::unique_ptr<StickerPlayer> stickerTakePlayer(
not_null<DocumentData*> data,
const Lottie::ColorReplacements *replacements) override;
private: private:
void ensureCreated() const; void ensureCreated(Element *replacing = nullptr) const;
void validateBadge(const PaintContext &context) const;
void paintBadge(Painter &p, const PaintContext &context) const;
const not_null<Element*> _parent; const not_null<Element*> _parent;
Fn<Data()> _lookup; Fn<Data()> _lookup;
QString _badgeText;
mutable int _skipTop = 0; mutable int _skipTop = 0;
mutable std::optional<Sticker> _sticker; mutable std::optional<Sticker> _sticker;
};
class StickerWithBadgePart final : public MediaInBubble::Part {
public:
using Data = StickerInBubblePart::Data;
StickerWithBadgePart(
not_null<Element*> parent,
Element *replacing,
Fn<Data()> lookup,
QString badge);
void draw(
Painter &p,
not_null<const MediaInBubble*> owner,
const PaintContext &context,
int outerWidth) const override;
bool hasHeavyPart() override;
void unloadHeavyPart() override;
QSize countOptimalSize() override;
QSize countCurrentSize(int newWidth) override;
std::unique_ptr<StickerPlayer> stickerTakePlayer(
not_null<DocumentData*> data,
const Lottie::ColorReplacements *replacements) override;
private:
void validateBadge(const PaintContext &context) const;
void paintBadge(Painter &p, const PaintContext &context) const;
StickerInBubblePart _sticker;
QString _badgeText;
mutable QColor _badgeFg; mutable QColor _badgeFg;
mutable QColor _badgeBorder; mutable QColor _badgeBorder;
mutable QImage _badge; mutable QImage _badge;
@ -187,6 +257,7 @@ public:
void draw( void draw(
Painter &p, Painter &p,
not_null<const MediaInBubble*> owner,
const PaintContext &context, const PaintContext &context,
int outerWidth) const override; int outerWidth) const override;
TextState textState( TextState textState(

View file

@ -1064,3 +1064,5 @@ historyIvIconPadding: margins(2px, 2px, 2px, 0px);
chatIntroStickerSize: 96px; chatIntroStickerSize: 96px;
chatIntroWidth: 224px; chatIntroWidth: 224px;
chatIntroTitleMargin: margins(11px, 16px, 11px, 4px);
chatIntroMargin: margins(11px, 0px, 11px, 0px);