mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Implement unique gift view in chat.
This commit is contained in:
parent
13d2f70c3a
commit
2d53ec5d34
19 changed files with 736 additions and 55 deletions
|
@ -799,6 +799,8 @@ PRIVATE
|
||||||
history/view/media/history_view_story_mention.h
|
history/view/media/history_view_story_mention.h
|
||||||
history/view/media/history_view_theme_document.cpp
|
history/view/media/history_view_theme_document.cpp
|
||||||
history/view/media/history_view_theme_document.h
|
history/view/media/history_view_theme_document.h
|
||||||
|
history/view/media/history_view_unique_gift.cpp
|
||||||
|
history/view/media/history_view_unique_gift.h
|
||||||
history/view/media/history_view_userpic_suggestion.cpp
|
history/view/media/history_view_userpic_suggestion.cpp
|
||||||
history/view/media/history_view_userpic_suggestion.h
|
history/view/media/history_view_userpic_suggestion.h
|
||||||
history/view/media/history_view_web_page.cpp
|
history/view/media/history_view_web_page.cpp
|
||||||
|
|
|
@ -774,7 +774,7 @@ std::optional<Data::StarGift> FromTL(
|
||||||
.id = uint64(data.vid().v),
|
.id = uint64(data.vid().v),
|
||||||
.stars = int64(data.vstars().v),
|
.stars = int64(data.vstars().v),
|
||||||
.starsConverted = int64(data.vconvert_stars().v),
|
.starsConverted = int64(data.vconvert_stars().v),
|
||||||
.starsUpgraded = int64(data.vupgrade_stars().value_or_empty()),
|
.starsToUpgrade = int64(data.vupgrade_stars().value_or_empty()),
|
||||||
.document = document,
|
.document = document,
|
||||||
.limitedLeft = remaining.value_or_empty(),
|
.limitedLeft = remaining.value_or_empty(),
|
||||||
.limitedCount = total.value_or_empty(),
|
.limitedCount = total.value_or_empty(),
|
||||||
|
@ -849,7 +849,8 @@ std::optional<Data::UserStarGift> FromTL(
|
||||||
}
|
}
|
||||||
: TextWithEntities()),
|
: TextWithEntities()),
|
||||||
.starsConverted = int64(data.vconvert_stars().value_or_empty()),
|
.starsConverted = int64(data.vconvert_stars().value_or_empty()),
|
||||||
.starsUpgraded = int64(data.vupgrade_stars().value_or_empty()),
|
.starsUpgradedBySender = int64(
|
||||||
|
data.vupgrade_stars().value_or_empty()),
|
||||||
.fromId = (data.vfrom_id()
|
.fromId = (data.vfrom_id()
|
||||||
? peerFromUser(data.vfrom_id()->v)
|
? peerFromUser(data.vfrom_id()->v)
|
||||||
: PeerId()),
|
: PeerId()),
|
||||||
|
|
|
@ -212,6 +212,7 @@ auto GenerateGiftMedia(
|
||||||
push(std::make_unique<MediaGenericTextPart>(
|
push(std::make_unique<MediaGenericTextPart>(
|
||||||
std::move(text),
|
std::move(text),
|
||||||
margins,
|
margins,
|
||||||
|
st::defaultTextStyle,
|
||||||
links,
|
links,
|
||||||
context));
|
context));
|
||||||
};
|
};
|
||||||
|
@ -276,9 +277,9 @@ struct PatternPoint {
|
||||||
};
|
};
|
||||||
[[nodiscard]] const std::vector<PatternPoint> &PatternPoints() {
|
[[nodiscard]] const std::vector<PatternPoint> &PatternPoints() {
|
||||||
static const auto kSmall = 0.7;
|
static const auto kSmall = 0.7;
|
||||||
static const auto kFaded = 0.7;
|
static const auto kFaded = 0.5;
|
||||||
static const auto kLarge = 0.85;
|
static const auto kLarge = 0.85;
|
||||||
static const auto kOpaque = 0.9;
|
static const auto kOpaque = 0.7;
|
||||||
static const auto result = std::vector<PatternPoint>{
|
static const auto result = std::vector<PatternPoint>{
|
||||||
{ { 0.5, 0.066 }, kSmall, kFaded },
|
{ { 0.5, 0.066 }, kSmall, kFaded },
|
||||||
|
|
||||||
|
@ -1088,10 +1089,6 @@ void SendGift(
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] QString ComputeTitle(const Data::UniqueGift &gift) {
|
|
||||||
return gift.title + u" #"_q + QString::number(gift.number);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SendUpgradeRequest(
|
void SendUpgradeRequest(
|
||||||
not_null<Window::SessionController*> controller,
|
not_null<Window::SessionController*> controller,
|
||||||
Settings::SmallBalanceResult result,
|
Settings::SmallBalanceResult result,
|
||||||
|
@ -1117,7 +1114,7 @@ void SendUpgradeRequest(
|
||||||
.text = tr::lng_gift_upgraded_about(
|
.text = tr::lng_gift_upgraded_about(
|
||||||
tr::now,
|
tr::now,
|
||||||
lt_name,
|
lt_name,
|
||||||
Text::Bold(ComputeTitle(*gift)),
|
Text::Bold(Data::UniqueGiftName(*gift)),
|
||||||
Ui::Text::WithEntities),
|
Ui::Text::WithEntities),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1159,7 +1156,7 @@ void UpgradeGift(
|
||||||
.text = tr::lng_gift_upgraded_about(
|
.text = tr::lng_gift_upgraded_about(
|
||||||
tr::now,
|
tr::now,
|
||||||
lt_name,
|
lt_name,
|
||||||
Text::Bold(ComputeTitle(*gift)),
|
Text::Bold(Data::UniqueGiftName(*gift)),
|
||||||
Ui::Text::WithEntities),
|
Ui::Text::WithEntities),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1846,32 +1843,14 @@ void AddUniqueGiftCover(
|
||||||
gift.gradient = CreateGradient(cover->size(), *gift.gift);
|
gift.gradient = CreateGradient(cover->size(), *gift.gift);
|
||||||
}
|
}
|
||||||
p.drawImage(0, 0, gift.gradient);
|
p.drawImage(0, 0, gift.gradient);
|
||||||
const auto paintPoint = [&](const PatternPoint &point) {
|
|
||||||
const auto key = (1. + point.opacity) * 10. + point.scale;
|
PaintPoints(
|
||||||
auto &image = gift.emojis[key];
|
p,
|
||||||
PrepareImage(image, gift.emoji.get(), point, *gift.gift);
|
gift.emojis,
|
||||||
if (!image.isNull()) {
|
gift.emoji.get(),
|
||||||
const auto x = int(point.position.x() * width);
|
*gift.gift,
|
||||||
const auto y = int(point.position.y() * pointsHeight);
|
QRect(0, 0, width, pointsHeight),
|
||||||
if (shown < 1.) {
|
shown);
|
||||||
p.save();
|
|
||||||
p.translate(x, y);
|
|
||||||
p.scale(shown, shown);
|
|
||||||
p.translate(-x, -y);
|
|
||||||
}
|
|
||||||
const auto size = image.size() / ratio;
|
|
||||||
p.drawImage(
|
|
||||||
x - size.width() / 2,
|
|
||||||
y - size.height() / 2,
|
|
||||||
image);
|
|
||||||
if (shown < 1.) {
|
|
||||||
p.restore();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
for (const auto point : PatternPoints()) {
|
|
||||||
paintPoint(point);
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto lottie = gift.lottie.get();
|
const auto lottie = gift.lottie.get();
|
||||||
const auto factor = style::DevicePixelRatio();
|
const auto factor = style::DevicePixelRatio();
|
||||||
|
@ -2095,6 +2074,45 @@ void UpgradeBox(
|
||||||
}, box->lifetime());
|
}, box->lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PaintPoints(
|
||||||
|
QPainter &p,
|
||||||
|
base::flat_map<float64, QImage> &cache,
|
||||||
|
not_null<Text::CustomEmoji*> emoji,
|
||||||
|
const Data::UniqueGift &gift,
|
||||||
|
const QRect &rect,
|
||||||
|
float64 shown) {
|
||||||
|
const auto origin = rect.topLeft();
|
||||||
|
const auto width = rect.width();
|
||||||
|
const auto height = rect.height();
|
||||||
|
const auto ratio = style::DevicePixelRatio();
|
||||||
|
const auto paintPoint = [&](const PatternPoint &point) {
|
||||||
|
const auto key = (1. + point.opacity) * 10. + point.scale;
|
||||||
|
auto &image = cache[key];
|
||||||
|
PrepareImage(image, emoji, point, gift);
|
||||||
|
if (!image.isNull()) {
|
||||||
|
const auto position = origin + QPoint(
|
||||||
|
int(point.position.x() * width),
|
||||||
|
int(point.position.y() * height));
|
||||||
|
if (shown < 1.) {
|
||||||
|
p.save();
|
||||||
|
p.translate(position);
|
||||||
|
p.scale(shown, shown);
|
||||||
|
p.translate(-position);
|
||||||
|
}
|
||||||
|
const auto size = image.size() / ratio;
|
||||||
|
p.drawImage(
|
||||||
|
position - QPoint(size.width() / 2, size.height() / 2),
|
||||||
|
image);
|
||||||
|
if (shown < 1.) {
|
||||||
|
p.restore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for (const auto point : PatternPoints()) {
|
||||||
|
paintPoint(point);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ShowStarGiftUpgradeBox(
|
void ShowStarGiftUpgradeBox(
|
||||||
not_null<Window::SessionController*> controller,
|
not_null<Window::SessionController*> controller,
|
||||||
uint64 stargiftId,
|
uint64 stargiftId,
|
||||||
|
|
|
@ -15,6 +15,10 @@ namespace Window {
|
||||||
class SessionController;
|
class SessionController;
|
||||||
} // namespace Window
|
} // namespace Window
|
||||||
|
|
||||||
|
namespace Ui::Text {
|
||||||
|
class CustomEmoji;
|
||||||
|
} // namespace Ui::Text
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
|
|
||||||
class VerticalLayout;
|
class VerticalLayout;
|
||||||
|
@ -31,6 +35,14 @@ void AddUniqueGiftCover(
|
||||||
rpl::producer<Data::UniqueGift> data,
|
rpl::producer<Data::UniqueGift> data,
|
||||||
rpl::producer<QString> subtitleOverride = nullptr);
|
rpl::producer<QString> subtitleOverride = nullptr);
|
||||||
|
|
||||||
|
void PaintPoints(
|
||||||
|
QPainter &p,
|
||||||
|
base::flat_map<float64, QImage> &cache,
|
||||||
|
not_null<Text::CustomEmoji*> emoji,
|
||||||
|
const Data::UniqueGift &gift,
|
||||||
|
const QRect &rect,
|
||||||
|
float64 shown = 1.);
|
||||||
|
|
||||||
void ShowStarGiftUpgradeBox(
|
void ShowStarGiftUpgradeBox(
|
||||||
not_null<Window::SessionController*> controller,
|
not_null<Window::SessionController*> controller,
|
||||||
uint64 stargiftId,
|
uint64 stargiftId,
|
||||||
|
|
|
@ -77,7 +77,8 @@ struct CreditsHistoryEntry final {
|
||||||
int limitedCount = 0;
|
int limitedCount = 0;
|
||||||
int limitedLeft = 0;
|
int limitedLeft = 0;
|
||||||
int starsConverted = 0;
|
int starsConverted = 0;
|
||||||
int starsUpgraded = 0;
|
int starsToUpgrade = 0;
|
||||||
|
int starsUpgradedBySender = 0;
|
||||||
int floodSkip = 0;
|
int floodSkip = 0;
|
||||||
bool converted : 1 = false;
|
bool converted : 1 = false;
|
||||||
bool anonymous : 1 = false;
|
bool anonymous : 1 = false;
|
||||||
|
|
|
@ -34,6 +34,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/view/media/history_view_service_box.h"
|
#include "history/view/media/history_view_service_box.h"
|
||||||
#include "history/view/media/history_view_story_mention.h"
|
#include "history/view/media/history_view_story_mention.h"
|
||||||
#include "history/view/media/history_view_premium_gift.h"
|
#include "history/view/media/history_view_premium_gift.h"
|
||||||
|
#include "history/view/media/history_view_unique_gift.h"
|
||||||
#include "history/view/media/history_view_userpic_suggestion.h"
|
#include "history/view/media/history_view_userpic_suggestion.h"
|
||||||
#include "dialogs/ui/dialogs_message_view.h"
|
#include "dialogs/ui/dialogs_message_view.h"
|
||||||
#include "ui/image/image.h"
|
#include "ui/image/image.h"
|
||||||
|
@ -2374,6 +2375,16 @@ std::unique_ptr<HistoryView::Media> MediaGiftBox::createView(
|
||||||
not_null<HistoryView::Element*> message,
|
not_null<HistoryView::Element*> message,
|
||||||
not_null<HistoryItem*> realParent,
|
not_null<HistoryItem*> realParent,
|
||||||
HistoryView::Element *replacing) {
|
HistoryView::Element *replacing) {
|
||||||
|
if (const auto raw = _data.unique.get()) {
|
||||||
|
return std::make_unique<HistoryView::MediaGeneric>(
|
||||||
|
message,
|
||||||
|
HistoryView::GenerateUniqueGiftMedia(message, replacing, raw),
|
||||||
|
HistoryView::MediaGenericDescriptor{
|
||||||
|
.maxWidth = st::chatIntroWidth,
|
||||||
|
.paintBg = HistoryView::UniqueGiftBg(message, raw),
|
||||||
|
.service = true,
|
||||||
|
});
|
||||||
|
}
|
||||||
return std::make_unique<HistoryView::ServiceBox>(
|
return std::make_unique<HistoryView::ServiceBox>(
|
||||||
message,
|
message,
|
||||||
std::make_unique<HistoryView::PremiumGift>(message, this));
|
std::make_unique<HistoryView::PremiumGift>(message, this));
|
||||||
|
|
|
@ -132,6 +132,7 @@ enum class GiftType : uchar {
|
||||||
Premium, // count - months
|
Premium, // count - months
|
||||||
Credits, // count - credits
|
Credits, // count - credits
|
||||||
StarGift, // count - stars
|
StarGift, // count - stars
|
||||||
|
StarGiftUpgrade,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GiftCode {
|
struct GiftCode {
|
||||||
|
@ -143,7 +144,8 @@ struct GiftCode {
|
||||||
ChannelData *channel = nullptr;
|
ChannelData *channel = nullptr;
|
||||||
MsgId giveawayMsgId = 0;
|
MsgId giveawayMsgId = 0;
|
||||||
int starsConverted = 0;
|
int starsConverted = 0;
|
||||||
int starsUpgraded = 0;
|
int starsToUpgrade = 0;
|
||||||
|
int starsUpgradedBySender = 0;
|
||||||
int limitedCount = 0;
|
int limitedCount = 0;
|
||||||
int limitedLeft = 0;
|
int limitedLeft = 0;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
|
@ -46,12 +46,16 @@ struct UniqueGift {
|
||||||
UniqueGiftOriginalDetails originalDetails;
|
UniqueGiftOriginalDetails originalDetails;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] inline QString UniqueGiftName(const UniqueGift &gift) {
|
||||||
|
return gift.title + u" #"_q + QString::number(gift.number);
|
||||||
|
}
|
||||||
|
|
||||||
struct StarGift {
|
struct StarGift {
|
||||||
uint64 id = 0;
|
uint64 id = 0;
|
||||||
std::shared_ptr<UniqueGift> unique;
|
std::shared_ptr<UniqueGift> unique;
|
||||||
int64 stars = 0;
|
int64 stars = 0;
|
||||||
int64 starsConverted = 0;
|
int64 starsConverted = 0;
|
||||||
int64 starsUpgraded = 0;
|
int64 starsToUpgrade = 0;
|
||||||
not_null<DocumentData*> document;
|
not_null<DocumentData*> document;
|
||||||
int limitedLeft = 0;
|
int limitedLeft = 0;
|
||||||
int limitedCount = 0;
|
int limitedCount = 0;
|
||||||
|
@ -69,7 +73,7 @@ struct UserStarGift {
|
||||||
StarGift info;
|
StarGift info;
|
||||||
TextWithEntities message;
|
TextWithEntities message;
|
||||||
int64 starsConverted = 0;
|
int64 starsConverted = 0;
|
||||||
int64 starsUpgraded = 0;
|
int64 starsUpgradedBySender = 0;
|
||||||
PeerId fromId = 0;
|
PeerId fromId = 0;
|
||||||
MsgId messageId = 0;
|
MsgId messageId = 0;
|
||||||
TimeId date = 0;
|
TimeId date = 0;
|
||||||
|
|
|
@ -5657,7 +5657,8 @@ void HistoryItem::applyAction(const MTPMessageAction &action) {
|
||||||
}
|
}
|
||||||
: TextWithEntities()),
|
: TextWithEntities()),
|
||||||
.starsConverted = int(data.vconvert_stars().value_or_empty()),
|
.starsConverted = int(data.vconvert_stars().value_or_empty()),
|
||||||
.starsUpgraded = int(data.vupgrade_stars().value_or_empty()),
|
.starsUpgradedBySender = int(
|
||||||
|
data.vupgrade_stars().value_or_empty()),
|
||||||
.type = Data::GiftType::StarGift,
|
.type = Data::GiftType::StarGift,
|
||||||
.upgradable = data.is_can_upgrade(),
|
.upgradable = data.is_can_upgrade(),
|
||||||
.anonymous = data.is_name_hidden(),
|
.anonymous = data.is_name_hidden(),
|
||||||
|
@ -5667,6 +5668,7 @@ void HistoryItem::applyAction(const MTPMessageAction &action) {
|
||||||
};
|
};
|
||||||
if (auto gift = Api::FromTL(&history()->session(), data.vgift())) {
|
if (auto gift = Api::FromTL(&history()->session(), data.vgift())) {
|
||||||
fields.stargiftId = gift->id;
|
fields.stargiftId = gift->id;
|
||||||
|
fields.starsToUpgrade = gift->starsToUpgrade;
|
||||||
fields.document = gift->document;
|
fields.document = gift->document;
|
||||||
fields.limitedCount = gift->limitedCount;
|
fields.limitedCount = gift->limitedCount;
|
||||||
fields.limitedLeft = gift->limitedLeft;
|
fields.limitedLeft = gift->limitedLeft;
|
||||||
|
@ -5680,7 +5682,7 @@ void HistoryItem::applyAction(const MTPMessageAction &action) {
|
||||||
}, [&](const MTPDmessageActionStarGiftUnique &data) {
|
}, [&](const MTPDmessageActionStarGiftUnique &data) {
|
||||||
using Fields = Data::GiftCode;
|
using Fields = Data::GiftCode;
|
||||||
auto fields = Fields{
|
auto fields = Fields{
|
||||||
.type = Data::GiftType::StarGift,
|
.type = Data::GiftType::StarGiftUpgrade,
|
||||||
.saved = data.is_saved(),
|
.saved = data.is_saved(),
|
||||||
};
|
};
|
||||||
if (auto gift = Api::FromTL(&history()->session(), data.vgift())) {
|
if (auto gift = Api::FromTL(&history()->session(), data.vgift())) {
|
||||||
|
|
|
@ -94,6 +94,7 @@ auto GenerateChatIntro(
|
||||||
push(std::make_unique<MediaGenericTextPart>(
|
push(std::make_unique<MediaGenericTextPart>(
|
||||||
std::move(text),
|
std::move(text),
|
||||||
margins,
|
margins,
|
||||||
|
st::defaultTextStyle,
|
||||||
links));
|
links));
|
||||||
};
|
};
|
||||||
const auto title = data.customPhrases()
|
const auto title = data.customPhrases()
|
||||||
|
|
|
@ -74,6 +74,7 @@ auto GenerateGiveawayStart(
|
||||||
push(std::make_unique<MediaGenericTextPart>(
|
push(std::make_unique<MediaGenericTextPart>(
|
||||||
std::move(text),
|
std::move(text),
|
||||||
margins,
|
margins,
|
||||||
|
st::defaultTextStyle,
|
||||||
links));
|
links));
|
||||||
};
|
};
|
||||||
pushText(
|
pushText(
|
||||||
|
@ -242,6 +243,7 @@ auto GenerateGiveawayResults(
|
||||||
push(std::make_unique<MediaGenericTextPart>(
|
push(std::make_unique<MediaGenericTextPart>(
|
||||||
std::move(text),
|
std::move(text),
|
||||||
margins,
|
margins,
|
||||||
|
st::defaultTextStyle,
|
||||||
links));
|
links));
|
||||||
};
|
};
|
||||||
const auto isSingleWinner = (data->winnersCount == 1);
|
const auto isSingleWinner = (data->winnersCount == 1);
|
||||||
|
|
|
@ -74,6 +74,7 @@ MediaGeneric::MediaGeneric(
|
||||||
Fn<void(Fn<void(std::unique_ptr<Part>)>)> generate,
|
Fn<void(Fn<void(std::unique_ptr<Part>)>)> generate,
|
||||||
MediaGenericDescriptor &&descriptor)
|
MediaGenericDescriptor &&descriptor)
|
||||||
: Media(parent)
|
: Media(parent)
|
||||||
|
, _paintBg(std::move(descriptor.paintBg))
|
||||||
, _maxWidthCap(descriptor.maxWidth)
|
, _maxWidthCap(descriptor.maxWidth)
|
||||||
, _service(descriptor.service)
|
, _service(descriptor.service)
|
||||||
, _hideServiceText(descriptor.hideServiceText) {
|
, _hideServiceText(descriptor.hideServiceText) {
|
||||||
|
@ -123,6 +124,8 @@ void MediaGeneric::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 if (_paintBg) {
|
||||||
|
_paintBg(p, context);
|
||||||
} else if (_service) {
|
} else if (_service) {
|
||||||
PainterHighQualityEnabler hq(p);
|
PainterHighQualityEnabler hq(p);
|
||||||
const auto radius = st::msgServiceGiftBoxRadius;
|
const auto radius = st::msgServiceGiftBoxRadius;
|
||||||
|
@ -229,12 +232,13 @@ QMargins MediaGeneric::inBubblePadding() const {
|
||||||
MediaGenericTextPart::MediaGenericTextPart(
|
MediaGenericTextPart::MediaGenericTextPart(
|
||||||
TextWithEntities text,
|
TextWithEntities text,
|
||||||
QMargins margins,
|
QMargins margins,
|
||||||
|
const style::TextStyle &st,
|
||||||
const base::flat_map<uint16, ClickHandlerPtr> &links,
|
const base::flat_map<uint16, ClickHandlerPtr> &links,
|
||||||
const std::any &context)
|
const std::any &context)
|
||||||
: _text(st::msgMinWidth)
|
: _text(st::msgMinWidth)
|
||||||
, _margins(margins) {
|
, _margins(margins) {
|
||||||
_text.setMarkedText(
|
_text.setMarkedText(
|
||||||
st::defaultTextStyle,
|
st,
|
||||||
text,
|
text,
|
||||||
kMarkupTextOptions,
|
kMarkupTextOptions,
|
||||||
context);
|
context);
|
||||||
|
@ -248,16 +252,13 @@ void MediaGenericTextPart::draw(
|
||||||
not_null<const MediaGeneric*> owner,
|
not_null<const MediaGeneric*> owner,
|
||||||
const PaintContext &context,
|
const PaintContext &context,
|
||||||
int outerWidth) const {
|
int outerWidth) const {
|
||||||
const auto service = owner->service();
|
setupPen(p, owner, context);
|
||||||
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 = &(service
|
.palette = &(owner->service()
|
||||||
? context.st->serviceTextPalette()
|
? context.st->serviceTextPalette()
|
||||||
: context.messageStyle()->textPalette),
|
: context.messageStyle()->textPalette),
|
||||||
.spoiler = Ui::Text::DefaultSpoilerCache(),
|
.spoiler = Ui::Text::DefaultSpoilerCache(),
|
||||||
|
@ -267,6 +268,16 @@ void MediaGenericTextPart::draw(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MediaGenericTextPart::setupPen(
|
||||||
|
Painter &p,
|
||||||
|
not_null<const MediaGeneric*> owner,
|
||||||
|
const PaintContext &context) const {
|
||||||
|
const auto service = owner->service();
|
||||||
|
p.setPen(service
|
||||||
|
? context.st->msgServiceFg()
|
||||||
|
: context.messageStyle()->historyTextFg);
|
||||||
|
}
|
||||||
|
|
||||||
TextState MediaGenericTextPart::textState(
|
TextState MediaGenericTextPart::textState(
|
||||||
QPoint point,
|
QPoint point,
|
||||||
StateRequest request,
|
StateRequest request,
|
||||||
|
|
|
@ -15,6 +15,14 @@ class DynamicImage;
|
||||||
class RippleAnimation;
|
class RippleAnimation;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
|
namespace style {
|
||||||
|
struct TextStyle;
|
||||||
|
} // namespace style
|
||||||
|
|
||||||
|
namespace st {
|
||||||
|
extern const style::TextStyle &defaultTextStyle;
|
||||||
|
} // namespace st
|
||||||
|
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
|
|
||||||
class MediaGeneric;
|
class MediaGeneric;
|
||||||
|
@ -45,6 +53,7 @@ public:
|
||||||
|
|
||||||
struct MediaGenericDescriptor {
|
struct MediaGenericDescriptor {
|
||||||
int maxWidth = 0;
|
int maxWidth = 0;
|
||||||
|
Fn<void(Painter&, const PaintContext&)> paintBg;
|
||||||
ClickHandlerPtr serviceLink;
|
ClickHandlerPtr serviceLink;
|
||||||
bool service = false;
|
bool service = false;
|
||||||
bool hideServiceText = false;
|
bool hideServiceText = false;
|
||||||
|
@ -110,17 +119,19 @@ private:
|
||||||
[[nodiscard]] QMargins inBubblePadding() const;
|
[[nodiscard]] QMargins inBubblePadding() const;
|
||||||
|
|
||||||
std::vector<Entry> _entries;
|
std::vector<Entry> _entries;
|
||||||
|
Fn<void(Painter&, const PaintContext&)> _paintBg;
|
||||||
int _maxWidthCap = 0;
|
int _maxWidthCap = 0;
|
||||||
bool _service : 1 = false;
|
bool _service : 1 = false;
|
||||||
bool _hideServiceText : 1 = false;
|
bool _hideServiceText : 1 = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class MediaGenericTextPart final : public MediaGenericPart {
|
class MediaGenericTextPart : public MediaGenericPart {
|
||||||
public:
|
public:
|
||||||
MediaGenericTextPart(
|
MediaGenericTextPart(
|
||||||
TextWithEntities text,
|
TextWithEntities text,
|
||||||
QMargins margins,
|
QMargins margins,
|
||||||
|
const style::TextStyle &st = st::defaultTextStyle,
|
||||||
const base::flat_map<uint16, ClickHandlerPtr> &links = {},
|
const base::flat_map<uint16, ClickHandlerPtr> &links = {},
|
||||||
const std::any &context = {});
|
const std::any &context = {});
|
||||||
|
|
||||||
|
@ -137,6 +148,12 @@ public:
|
||||||
QSize countOptimalSize() override;
|
QSize countOptimalSize() override;
|
||||||
QSize countCurrentSize(int newWidth) override;
|
QSize countCurrentSize(int newWidth) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void setupPen(
|
||||||
|
Painter &p,
|
||||||
|
not_null<const MediaGeneric*> owner,
|
||||||
|
const PaintContext &context) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Ui::Text::String _text;
|
Ui::Text::String _text;
|
||||||
QMargins _margins;
|
QMargins _margins;
|
||||||
|
|
|
@ -266,11 +266,13 @@ void PremiumGift::unloadHeavyPart() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PremiumGift::incomingGift() const {
|
bool PremiumGift::incomingGift() const {
|
||||||
return gift() && !_parent->data()->out();
|
const auto out = _parent->data()->out();
|
||||||
|
return gift() && (starGiftUpgrade() ? out : !out);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PremiumGift::outgoingGift() const {
|
bool PremiumGift::outgoingGift() const {
|
||||||
return gift() && _parent->data()->out();
|
const auto out = _parent->data()->out();
|
||||||
|
return gift() && (starGiftUpgrade() ? !out : out);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PremiumGift::gift() const {
|
bool PremiumGift::gift() const {
|
||||||
|
@ -278,7 +280,12 @@ bool PremiumGift::gift() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PremiumGift::starGift() const {
|
bool PremiumGift::starGift() const {
|
||||||
return _data.type == Data::GiftType::StarGift;
|
return (_data.type == Data::GiftType::StarGift)
|
||||||
|
|| (_data.type == Data::GiftType::StarGiftUpgrade);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PremiumGift::starGiftUpgrade() const {
|
||||||
|
return (_data.type == Data::GiftType::StarGiftUpgrade);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PremiumGift::creditsPrize() const {
|
bool PremiumGift::creditsPrize() const {
|
||||||
|
|
|
@ -51,6 +51,7 @@ private:
|
||||||
[[nodiscard]] bool incomingGift() const;
|
[[nodiscard]] bool incomingGift() const;
|
||||||
[[nodiscard]] bool outgoingGift() const;
|
[[nodiscard]] bool outgoingGift() const;
|
||||||
[[nodiscard]] bool starGift() const;
|
[[nodiscard]] bool starGift() const;
|
||||||
|
[[nodiscard]] bool starGiftUpgrade() const;
|
||||||
[[nodiscard]] bool gift() const;
|
[[nodiscard]] bool gift() const;
|
||||||
[[nodiscard]] bool creditsPrize() const;
|
[[nodiscard]] bool creditsPrize() const;
|
||||||
[[nodiscard]] int credits() const;
|
[[nodiscard]] int credits() const;
|
||||||
|
|
|
@ -0,0 +1,539 @@
|
||||||
|
/*
|
||||||
|
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 "history/view/media/history_view_unique_gift.h"
|
||||||
|
|
||||||
|
#include "boxes/star_gift_box.h"
|
||||||
|
#include "chat_helpers/stickers_lottie.h"
|
||||||
|
#include "core/click_handler_types.h"
|
||||||
|
#include "data/stickers/data_custom_emoji.h"
|
||||||
|
#include "data/data_media_types.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
|
#include "data/data_star_gift.h"
|
||||||
|
#include "history/view/media/history_view_media_generic.h"
|
||||||
|
#include "history/view/history_view_cursor_state.h"
|
||||||
|
#include "history/view/history_view_element.h"
|
||||||
|
#include "history/history.h"
|
||||||
|
#include "history/history_item.h"
|
||||||
|
#include "lang/lang_keys.h"
|
||||||
|
#include "main/main_session.h"
|
||||||
|
#include "settings/settings_credits_graphics.h"
|
||||||
|
#include "ui/chat/chat_style.h"
|
||||||
|
#include "ui/effects/premium_stars_colored.h"
|
||||||
|
#include "ui/effects/ripple_animation.h"
|
||||||
|
#include "ui/layers/generic_box.h"
|
||||||
|
#include "ui/text/text_utilities.h"
|
||||||
|
#include "ui/painter.h"
|
||||||
|
#include "ui/power_saving.h"
|
||||||
|
#include "ui/rect.h"
|
||||||
|
#include "window/window_session_controller.h"
|
||||||
|
#include "styles/style_chat.h"
|
||||||
|
#include "styles/style_credits.h"
|
||||||
|
|
||||||
|
namespace HistoryView {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class TextPartColored final : public MediaGenericTextPart {
|
||||||
|
public:
|
||||||
|
TextPartColored(
|
||||||
|
TextWithEntities text,
|
||||||
|
QMargins margins,
|
||||||
|
QColor color,
|
||||||
|
const style::TextStyle &st = st::defaultTextStyle,
|
||||||
|
const base::flat_map<uint16, ClickHandlerPtr> &links = {},
|
||||||
|
const std::any &context = {});
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setupPen(
|
||||||
|
Painter &p,
|
||||||
|
not_null<const MediaGeneric*> owner,
|
||||||
|
const PaintContext &context) const override;
|
||||||
|
|
||||||
|
QColor _color;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class AttributeTable final : public MediaGenericPart {
|
||||||
|
public:
|
||||||
|
struct Entry {
|
||||||
|
QString label;
|
||||||
|
QString value;
|
||||||
|
};
|
||||||
|
|
||||||
|
AttributeTable(
|
||||||
|
std::vector<Entry> entries,
|
||||||
|
QMargins margins,
|
||||||
|
QColor labelColor);
|
||||||
|
|
||||||
|
void draw(
|
||||||
|
Painter &p,
|
||||||
|
not_null<const MediaGeneric*> owner,
|
||||||
|
const PaintContext &context,
|
||||||
|
int outerWidth) const override;
|
||||||
|
|
||||||
|
QSize countOptimalSize() override;
|
||||||
|
QSize countCurrentSize(int newWidth) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Part {
|
||||||
|
Ui::Text::String label;
|
||||||
|
Ui::Text::String value;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<Part> _parts;
|
||||||
|
QMargins _margins;
|
||||||
|
QColor _labelColor;
|
||||||
|
int _valueLeft = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class ButtonPart final : public MediaGenericPart {
|
||||||
|
public:
|
||||||
|
ButtonPart(
|
||||||
|
const QString &text,
|
||||||
|
QMargins margins,
|
||||||
|
QColor bg,
|
||||||
|
Fn<void()> repaint,
|
||||||
|
ClickHandlerPtr link);
|
||||||
|
|
||||||
|
void draw(
|
||||||
|
Painter &p,
|
||||||
|
not_null<const MediaGeneric*> owner,
|
||||||
|
const PaintContext &context,
|
||||||
|
int outerWidth) const override;
|
||||||
|
TextState textState(
|
||||||
|
QPoint point,
|
||||||
|
StateRequest request,
|
||||||
|
int outerWidth) const override;
|
||||||
|
|
||||||
|
void clickHandlerPressedChanged(
|
||||||
|
const ClickHandlerPtr &p,
|
||||||
|
bool pressed) override;
|
||||||
|
|
||||||
|
QSize countOptimalSize() override;
|
||||||
|
QSize countCurrentSize(int newWidth) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::Text::String _text;
|
||||||
|
QMargins _margins;
|
||||||
|
QColor _bg;
|
||||||
|
QSize _size;
|
||||||
|
|
||||||
|
ClickHandlerPtr _link;
|
||||||
|
std::unique_ptr<Ui::RippleAnimation> _ripple;
|
||||||
|
mutable Ui::Premium::ColoredMiniStars _stars;
|
||||||
|
Fn<void()> _repaint;
|
||||||
|
|
||||||
|
mutable QPoint _lastPoint;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
ButtonPart::ButtonPart(
|
||||||
|
const QString &text,
|
||||||
|
QMargins margins,
|
||||||
|
QColor bg,
|
||||||
|
Fn<void()> repaint,
|
||||||
|
ClickHandlerPtr link)
|
||||||
|
: _text(st::semiboldTextStyle, text)
|
||||||
|
, _margins(margins)
|
||||||
|
, _bg(bg)
|
||||||
|
, _size(
|
||||||
|
(_text.maxWidth()
|
||||||
|
+ st::msgServiceGiftBoxButtonHeight
|
||||||
|
+ st::msgServiceGiftBoxButtonPadding.left()
|
||||||
|
+ st::msgServiceGiftBoxButtonPadding.right()),
|
||||||
|
st::msgServiceGiftBoxButtonHeight)
|
||||||
|
, _link(std::move(link))
|
||||||
|
, _stars([=](const QRect &) {
|
||||||
|
repaint();
|
||||||
|
}, Ui::Premium::MiniStars::Type::SlowStars)
|
||||||
|
, _repaint(std::move(repaint)) {
|
||||||
|
_stars.setColorOverride(QGradientStops{
|
||||||
|
{ 0., QColor(255, 255, 255, 255 * .3) },
|
||||||
|
{ 1., QColor(255, 255, 255) },
|
||||||
|
});
|
||||||
|
const auto padding = _size.height() / 2;
|
||||||
|
_stars.setCenter(
|
||||||
|
Rect(_size) - QMargins(padding, 0, padding, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ButtonPart::draw(
|
||||||
|
Painter &p,
|
||||||
|
not_null<const MediaGeneric*> owner,
|
||||||
|
const PaintContext &context,
|
||||||
|
int outerWidth) const {
|
||||||
|
PainterHighQualityEnabler hq(p);
|
||||||
|
|
||||||
|
const auto position = QPoint(
|
||||||
|
(outerWidth - width()) / 2 + _margins.left(),
|
||||||
|
_margins.top());
|
||||||
|
p.translate(position);
|
||||||
|
|
||||||
|
p.setPen(Qt::NoPen);
|
||||||
|
p.setBrush(_bg);
|
||||||
|
const auto radius = _size.height() / 2.;
|
||||||
|
const auto r = Rect(_size);
|
||||||
|
p.drawRoundedRect(r, radius, radius);
|
||||||
|
|
||||||
|
auto clipPath = QPainterPath();
|
||||||
|
clipPath.addRoundedRect(r, radius, radius);
|
||||||
|
p.setClipPath(clipPath);
|
||||||
|
_stars.paint(p);
|
||||||
|
p.setClipping(false);
|
||||||
|
|
||||||
|
auto white = QColor(255, 255, 255);
|
||||||
|
p.setPen(white);
|
||||||
|
if (_ripple) {
|
||||||
|
const auto opacity = p.opacity();
|
||||||
|
p.setOpacity(st::historyPollRippleOpacity);
|
||||||
|
white.setAlphaF(0.3);
|
||||||
|
_ripple->paint(
|
||||||
|
p,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
width(),
|
||||||
|
&white);
|
||||||
|
p.setOpacity(opacity);
|
||||||
|
}
|
||||||
|
_text.draw(
|
||||||
|
p,
|
||||||
|
0,
|
||||||
|
(_size.height() - _text.minHeight()) / 2,
|
||||||
|
_size.width(),
|
||||||
|
style::al_top);
|
||||||
|
|
||||||
|
p.translate(-position);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextState ButtonPart::textState(
|
||||||
|
QPoint point,
|
||||||
|
StateRequest request,
|
||||||
|
int outerWidth) const {
|
||||||
|
point -= QPoint{
|
||||||
|
(outerWidth - width()) / 2 + _margins.left(),
|
||||||
|
_margins.top()
|
||||||
|
};
|
||||||
|
if (QRect(QPoint(), _size).contains(point)) {
|
||||||
|
auto result = TextState();
|
||||||
|
result.link = _link;
|
||||||
|
_lastPoint = point;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void ButtonPart::clickHandlerPressedChanged(
|
||||||
|
const ClickHandlerPtr &p,
|
||||||
|
bool pressed) {
|
||||||
|
if (p != _link) {
|
||||||
|
return;
|
||||||
|
} else if (pressed) {
|
||||||
|
if (!_ripple) {
|
||||||
|
const auto radius = _size.height() / 2;
|
||||||
|
_ripple = std::make_unique<Ui::RippleAnimation>(
|
||||||
|
st::defaultRippleAnimation,
|
||||||
|
Ui::RippleAnimation::RoundRectMask(_size, radius),
|
||||||
|
_repaint);
|
||||||
|
}
|
||||||
|
_ripple->add(_lastPoint);
|
||||||
|
} else if (_ripple) {
|
||||||
|
_ripple->lastStop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize ButtonPart::countOptimalSize() {
|
||||||
|
return {
|
||||||
|
_margins.left() + _size.width() + _margins.right(),
|
||||||
|
_margins.top() + _size.height() + _margins.bottom(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize ButtonPart::countCurrentSize(int newWidth) {
|
||||||
|
return optimalSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
TextPartColored::TextPartColored(
|
||||||
|
TextWithEntities text,
|
||||||
|
QMargins margins,
|
||||||
|
QColor color,
|
||||||
|
const style::TextStyle &st,
|
||||||
|
const base::flat_map<uint16, ClickHandlerPtr> &links,
|
||||||
|
const std::any &context)
|
||||||
|
: MediaGenericTextPart(text, margins, st, links, context)
|
||||||
|
, _color(color) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextPartColored::setupPen(
|
||||||
|
Painter &p,
|
||||||
|
not_null<const MediaGeneric*> owner,
|
||||||
|
const PaintContext &context) const {
|
||||||
|
p.setPen(_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
AttributeTable::AttributeTable(
|
||||||
|
std::vector<Entry> entries,
|
||||||
|
QMargins margins,
|
||||||
|
QColor labelColor)
|
||||||
|
: _margins(margins)
|
||||||
|
, _labelColor(labelColor) {
|
||||||
|
for (const auto &entry : entries) {
|
||||||
|
_parts.emplace_back();
|
||||||
|
auto &part = _parts.back();
|
||||||
|
part.label.setText(st::defaultTextStyle, entry.label);
|
||||||
|
part.value.setMarkedText(
|
||||||
|
st::defaultTextStyle,
|
||||||
|
Ui::Text::Bold(entry.value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AttributeTable::draw(
|
||||||
|
Painter &p,
|
||||||
|
not_null<const MediaGeneric*> owner,
|
||||||
|
const PaintContext &context,
|
||||||
|
int outerWidth) const {
|
||||||
|
const auto labelRight = _valueLeft - st::chatUniqueTableSkip;
|
||||||
|
const auto palette = &context.st->serviceTextPalette();
|
||||||
|
auto top = _margins.top();
|
||||||
|
const auto paint = [&](
|
||||||
|
const Ui::Text::String &text,
|
||||||
|
int left,
|
||||||
|
int availableWidth,
|
||||||
|
style::align align) {
|
||||||
|
text.draw(p, {
|
||||||
|
.position = { left, top },
|
||||||
|
.outerWidth = outerWidth,
|
||||||
|
.availableWidth = availableWidth,
|
||||||
|
.align = align,
|
||||||
|
.palette = palette,
|
||||||
|
.spoiler = Ui::Text::DefaultSpoilerCache(),
|
||||||
|
.now = context.now,
|
||||||
|
.pausedEmoji = context.paused || On(PowerSaving::kEmojiChat),
|
||||||
|
.pausedSpoiler = context.paused || On(PowerSaving::kChatSpoiler),
|
||||||
|
.elisionLines = 1,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const auto forLabel = labelRight - _margins.left();
|
||||||
|
const auto forValue = width() - _valueLeft - _margins.right();
|
||||||
|
const auto white = QColor(255, 255, 255);
|
||||||
|
for (const auto &part : _parts) {
|
||||||
|
p.setPen(_labelColor);
|
||||||
|
paint(part.label, _margins.left(), forLabel, style::al_topright);
|
||||||
|
p.setPen(white);
|
||||||
|
paint(part.value, _valueLeft, forValue, style::al_topleft);
|
||||||
|
top += st::normalFont->height + st::chatUniqueRowSkip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize AttributeTable::countOptimalSize() {
|
||||||
|
auto maxLabel = 0;
|
||||||
|
auto maxValue = 0;
|
||||||
|
for (const auto &part : _parts) {
|
||||||
|
maxLabel = std::max(maxLabel, part.label.maxWidth());
|
||||||
|
maxValue = std::max(maxValue, part.value.maxWidth());
|
||||||
|
}
|
||||||
|
const auto skip = st::chatUniqueTableSkip;
|
||||||
|
const auto row = st::normalFont->height + st::chatUniqueRowSkip;
|
||||||
|
const auto height = int(_parts.size()) * row - st::chatUniqueRowSkip;
|
||||||
|
return {
|
||||||
|
_margins.left() + maxLabel + skip + maxValue + _margins.right(),
|
||||||
|
_margins.top() + height + _margins.bottom(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize AttributeTable::countCurrentSize(int newWidth) {
|
||||||
|
const auto skip = st::chatUniqueTableSkip;
|
||||||
|
const auto width = newWidth - _margins.left() - _margins.right() - skip;
|
||||||
|
auto maxLabel = 0;
|
||||||
|
auto maxValue = 0;
|
||||||
|
for (const auto &part : _parts) {
|
||||||
|
maxLabel = std::max(maxLabel, part.label.maxWidth());
|
||||||
|
maxValue = std::max(maxValue, part.value.maxWidth());
|
||||||
|
}
|
||||||
|
if (width <= 0 || !maxLabel) {
|
||||||
|
_valueLeft = _margins.left();
|
||||||
|
} else if (!maxValue) {
|
||||||
|
_valueLeft = newWidth - _margins.right();
|
||||||
|
} else {
|
||||||
|
_valueLeft = _margins.left()
|
||||||
|
+ int((int64(maxLabel) * width) / (maxLabel + maxValue))
|
||||||
|
+ skip;
|
||||||
|
}
|
||||||
|
return { newWidth, minHeight() };
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // namespace
|
||||||
|
|
||||||
|
auto GenerateUniqueGiftMedia(
|
||||||
|
not_null<Element*> parent,
|
||||||
|
Element *replacing,
|
||||||
|
not_null<Data::UniqueGift*> gift)
|
||||||
|
-> Fn<void(Fn<void(std::unique_ptr<MediaGenericPart>)>)> {
|
||||||
|
return [=](Fn<void(std::unique_ptr<MediaGenericPart>)> push) {
|
||||||
|
auto pushText = [&](
|
||||||
|
TextWithEntities text,
|
||||||
|
const style::TextStyle &st,
|
||||||
|
QColor color,
|
||||||
|
QMargins margins) {
|
||||||
|
if (text.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
push(std::make_unique<TextPartColored>(
|
||||||
|
std::move(text),
|
||||||
|
margins,
|
||||||
|
color,
|
||||||
|
st));
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto white = QColor(255, 255, 255);
|
||||||
|
const auto sticker = [=] {
|
||||||
|
using Tag = ChatHelpers::StickerLottieSize;
|
||||||
|
return StickerInBubblePart::Data{
|
||||||
|
.sticker = gift->model.document,
|
||||||
|
.size = st::chatIntroStickerSize,
|
||||||
|
.cacheTag = Tag::ChatIntroHelloSticker,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
push(std::make_unique<StickerInBubblePart>(
|
||||||
|
parent,
|
||||||
|
replacing,
|
||||||
|
sticker,
|
||||||
|
st::chatUniqueStickerPadding));
|
||||||
|
pushText(
|
||||||
|
Ui::Text::Bold((!parent->data()->out()
|
||||||
|
? tr::lng_action_gift_sent_subtitle
|
||||||
|
: tr::lng_action_gift_got_subtitle)(
|
||||||
|
tr::now,
|
||||||
|
lt_user,
|
||||||
|
parent->history()->peer->shortName())),
|
||||||
|
st::chatUniqueTitle,
|
||||||
|
white,
|
||||||
|
st::chatUniqueTitlePadding);
|
||||||
|
pushText(
|
||||||
|
Ui::Text::Bold(Data::UniqueGiftName(*gift)),
|
||||||
|
st::defaultTextStyle,
|
||||||
|
gift->backdrop.textColor,
|
||||||
|
st::chatUniqueTextPadding);
|
||||||
|
|
||||||
|
const auto withButton = parent->data()->out();
|
||||||
|
|
||||||
|
auto attributes = std::vector<AttributeTable::Entry>{
|
||||||
|
{ tr::lng_gift_unique_model(tr::now), gift->model.name },
|
||||||
|
{ tr::lng_gift_unique_backdrop(tr::now), gift->backdrop.name },
|
||||||
|
{ tr::lng_gift_unique_symbol(tr::now), gift->pattern.name },
|
||||||
|
};
|
||||||
|
push(std::make_unique<AttributeTable>(
|
||||||
|
std::move(attributes),
|
||||||
|
(withButton
|
||||||
|
? st::chatUniqueTextPadding
|
||||||
|
: st::chatUniqueTableAtBottomPadding),
|
||||||
|
gift->backdrop.textColor));
|
||||||
|
|
||||||
|
if (withButton) {
|
||||||
|
const auto itemId = parent->data()->fullId();
|
||||||
|
auto link = std::make_shared<LambdaClickHandler>([=](
|
||||||
|
ClickContext context) {
|
||||||
|
const auto my = context.other.value<ClickHandlerContext>();
|
||||||
|
if (const auto controller = my.sessionWindow.get()) {
|
||||||
|
const auto owner = &controller->session().data();
|
||||||
|
if (const auto item = owner->message(itemId)) {
|
||||||
|
if (const auto media = item->media()) {
|
||||||
|
if (const auto gift = media->gift()) {
|
||||||
|
controller->show(Box(
|
||||||
|
Settings::StarGiftViewBox,
|
||||||
|
controller,
|
||||||
|
*gift,
|
||||||
|
item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
push(std::make_unique<ButtonPart>(
|
||||||
|
tr::lng_sticker_premium_view(tr::now),
|
||||||
|
st::chatUniqueButtonPadding,
|
||||||
|
gift->backdrop.patternColor,
|
||||||
|
[=] { parent->repaint(); },
|
||||||
|
std::move(link)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Fn<void(Painter&, const Ui::ChatPaintContext &)> UniqueGiftBg(
|
||||||
|
not_null<Element*> view,
|
||||||
|
not_null<Data::UniqueGift*> gift) {
|
||||||
|
struct State {
|
||||||
|
QImage bg;
|
||||||
|
base::flat_map<float64, QImage> cache;
|
||||||
|
std::unique_ptr<Ui::Text::CustomEmoji> pattern;
|
||||||
|
};
|
||||||
|
const auto state = std::make_shared<State>();
|
||||||
|
state->pattern = view->history()->owner().customEmojiManager().create(
|
||||||
|
gift->pattern.document,
|
||||||
|
[=] { view->repaint(); },
|
||||||
|
Data::CustomEmojiSizeTag::Large);
|
||||||
|
[[maybe_unused]] const auto preload = state->pattern->ready();
|
||||||
|
|
||||||
|
return [=](Painter &p, const Ui::ChatPaintContext &context) {
|
||||||
|
auto hq = PainterHighQualityEnabler(p);
|
||||||
|
p.setPen(Qt::NoPen);
|
||||||
|
const auto thickness = st::chatUniqueGiftBorder * 2;
|
||||||
|
auto pen = context.st->msgServiceBg()->p;
|
||||||
|
pen.setWidthF(thickness);
|
||||||
|
p.setPen(pen);
|
||||||
|
p.setBrush(Qt::transparent);
|
||||||
|
const auto radius = st::msgServiceGiftBoxRadius - thickness;
|
||||||
|
const auto media = view->media();
|
||||||
|
const auto full = QRect(0, 0, media->width(), media->height());
|
||||||
|
const auto inner = full.marginsRemoved(
|
||||||
|
{ thickness, thickness, thickness, thickness });
|
||||||
|
p.drawRoundedRect(inner, radius, radius);
|
||||||
|
auto gradient = QRadialGradient(
|
||||||
|
inner.center(),
|
||||||
|
inner.height() / 2);
|
||||||
|
gradient.setStops({
|
||||||
|
{ 0., gift->backdrop.centerColor },
|
||||||
|
{ 1., gift->backdrop.edgeColor },
|
||||||
|
});
|
||||||
|
p.setBrush(gradient);
|
||||||
|
p.setPen(Qt::NoPen);
|
||||||
|
p.drawRoundedRect(inner, radius, radius);
|
||||||
|
|
||||||
|
const auto width = media->width();
|
||||||
|
const auto shift = width / 12;
|
||||||
|
const auto doubled = width + 2 * shift;
|
||||||
|
const auto outer = QRect(-shift, -shift, doubled, doubled);
|
||||||
|
p.setClipRect(inner);
|
||||||
|
Ui::PaintPoints(p, state->cache, state->pattern.get(), *gift, outer);
|
||||||
|
p.setClipping(false);
|
||||||
|
|
||||||
|
p.save();
|
||||||
|
p.translate(inner.topLeft());
|
||||||
|
const auto tag = tr::lng_gift_limited_of_one(tr::now);
|
||||||
|
const auto font = st::semiboldFont;
|
||||||
|
p.setFont(font);
|
||||||
|
p.setPen(Qt::NoPen);
|
||||||
|
const auto twidth = font->width(tag);
|
||||||
|
const auto pos = QPoint(inner.width() - twidth, font->height);
|
||||||
|
const auto add = style::ConvertScale(2);
|
||||||
|
p.setClipRect(
|
||||||
|
-add,
|
||||||
|
-add,
|
||||||
|
inner.width() + 2 * add,
|
||||||
|
inner.height() + 2 * add);
|
||||||
|
p.translate(pos);
|
||||||
|
p.rotate(45.);
|
||||||
|
p.translate(-pos);
|
||||||
|
p.setPen(Qt::NoPen);
|
||||||
|
p.setBrush(gift->backdrop.patternColor);
|
||||||
|
p.drawRect(-5 * twidth, 0, twidth * 12, font->height);
|
||||||
|
p.setPen(gift->backdrop.textColor);
|
||||||
|
p.drawText(pos - QPoint(0, font->descent), tag);
|
||||||
|
p.restore();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace HistoryView
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
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 Painter;
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
class MediaGiftBox;
|
||||||
|
struct UniqueGift;
|
||||||
|
} // namespace Data
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
struct ChatPaintContext;
|
||||||
|
} // namespace Ui
|
||||||
|
|
||||||
|
namespace HistoryView {
|
||||||
|
|
||||||
|
class Element;
|
||||||
|
class MediaGenericPart;
|
||||||
|
|
||||||
|
auto GenerateUniqueGiftMedia(
|
||||||
|
not_null<Element*> parent,
|
||||||
|
Element *replacing,
|
||||||
|
not_null<Data::UniqueGift*> gift)
|
||||||
|
-> Fn<void(Fn<void(std::unique_ptr<MediaGenericPart>)>)>;
|
||||||
|
|
||||||
|
Fn<void(Painter&, const Ui::ChatPaintContext &)> UniqueGiftBg(
|
||||||
|
not_null<Element*> view,
|
||||||
|
not_null<Data::UniqueGift*> gift);
|
||||||
|
|
||||||
|
} // namespace HistoryView
|
|
@ -1342,7 +1342,7 @@ void ReceiptCreditsBox(
|
||||||
e.stargiftId,
|
e.stargiftId,
|
||||||
starGiftSender,
|
starGiftSender,
|
||||||
itemId,
|
itemId,
|
||||||
e.starsUpgraded,
|
e.starsUpgradedBySender ? 0 : e.starsToUpgrade,
|
||||||
[=](bool) { *upgradeGuard = false; });
|
[=](bool) { *upgradeGuard = false; });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1611,7 +1611,8 @@ void UserStarGiftBox(
|
||||||
.limitedCount = data.info.limitedCount,
|
.limitedCount = data.info.limitedCount,
|
||||||
.limitedLeft = data.info.limitedLeft,
|
.limitedLeft = data.info.limitedLeft,
|
||||||
.starsConverted = int(data.info.starsConverted),
|
.starsConverted = int(data.info.starsConverted),
|
||||||
.starsUpgraded = int(data.starsUpgraded),
|
.starsToUpgrade = int(data.info.starsToUpgrade),
|
||||||
|
.starsUpgradedBySender = int(data.starsUpgradedBySender),
|
||||||
.converted = false,
|
.converted = false,
|
||||||
.anonymous = data.anonymous,
|
.anonymous = data.anonymous,
|
||||||
.stargift = true,
|
.stargift = true,
|
||||||
|
@ -1645,7 +1646,8 @@ void StarGiftViewBox(
|
||||||
.limitedCount = data.limitedCount,
|
.limitedCount = data.limitedCount,
|
||||||
.limitedLeft = data.limitedLeft,
|
.limitedLeft = data.limitedLeft,
|
||||||
.starsConverted = data.starsConverted,
|
.starsConverted = data.starsConverted,
|
||||||
.starsUpgraded = data.starsUpgraded,
|
.starsToUpgrade = data.starsToUpgrade,
|
||||||
|
.starsUpgradedBySender = data.starsUpgradedBySender,
|
||||||
.converted = data.converted,
|
.converted = data.converted,
|
||||||
.anonymous = data.anonymous,
|
.anonymous = data.anonymous,
|
||||||
.stargift = true,
|
.stargift = true,
|
||||||
|
|
|
@ -1205,3 +1205,15 @@ botDownloadCancel: IconButton {
|
||||||
rippleAreaSize: 20px;
|
rippleAreaSize: 20px;
|
||||||
ripple: defaultRippleAnimationBgOver;
|
ripple: defaultRippleAnimationBgOver;
|
||||||
}
|
}
|
||||||
|
chatUniqueGiftBorder: 4px;
|
||||||
|
|
||||||
|
chatUniqueStickerPadding: margins(10px, 12px, 10px, 8px);
|
||||||
|
chatUniqueTitle: TextStyle(defaultTextStyle) {
|
||||||
|
font: font(16px semibold);
|
||||||
|
}
|
||||||
|
chatUniqueTitlePadding: margins(12px, 4px, 12px, 2px);
|
||||||
|
chatUniqueTextPadding: margins(12px, 2px, 12px, 8px);
|
||||||
|
chatUniqueTableSkip: 10px;
|
||||||
|
chatUniqueTableAtBottomPadding: margins(12px, 2px, 12px, 20px);
|
||||||
|
chatUniqueRowSkip: 4px;
|
||||||
|
chatUniqueButtonPadding: margins(12px, 4px, 12px, 16px);
|
||||||
|
|
Loading…
Add table
Reference in a new issue