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_session.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_sticker_player_abstract.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)
: _parent(parent) {
}
@ -330,13 +374,18 @@ void AboutView::make(Data::ChatIntro data) {
| MessageFlag::FakeHistoryItem
| MessageFlag::Local),
.from = _history->peer->id,
}, PreparedServiceText{ { data.description } });
}, PreparedServiceText{ { } });
setItem(AdminLog::OwnedItem(_delegate, item), data.sticker);
_item->overrideMedia(std::make_unique<ServiceBox>(
_item.get(),
std::make_unique<ChatIntroBox>(_item.get(), data)));
auto owned = AdminLog::OwnedItem(_delegate, item);
owned->overrideMedia(std::make_unique<HistoryView::MediaInBubble>(
owned.get(),
GenerateChatIntro(owned.get(), _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) {

View file

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

View file

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

View file

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

View file

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