Version 5.10.2: Improve gifts layout.

This commit is contained in:
John Preston 2025-01-08 17:36:25 +04:00
parent 658cb438f8
commit 1ac33d30bd
8 changed files with 144 additions and 64 deletions

View file

@ -298,48 +298,6 @@ auto GenerateGiftMedia(
};
}
struct PatternPoint {
QPointF position;
float64 scale = 1.;
float64 opacity = 1.;
};
[[nodiscard]] const std::vector<PatternPoint> &PatternPoints() {
static const auto kSmall = 0.7;
static const auto kFaded = 0.3;
static const auto kLarge = 0.85;
static const auto kOpaque = 0.5;
static const auto result = std::vector<PatternPoint>{
{ { 0.5, 0.066 }, kSmall, kFaded },
{ { 0.177, 0.168 }, kSmall, kFaded },
{ { 0.822, 0.168 }, kSmall, kFaded },
{ { 0.37, 0.168 }, kLarge, kOpaque },
{ { 0.63, 0.168 }, kLarge, kOpaque },
{ { 0.277, 0.308 }, kSmall, kOpaque },
{ { 0.723, 0.308 }, kSmall, kOpaque },
{ { 0.13, 0.42 }, kSmall, kFaded },
{ { 0.87, 0.42 }, kSmall, kFaded },
{ { 0.27, 0.533 }, kLarge, kOpaque },
{ { 0.73, 0.533 }, kLarge, kOpaque },
{ { 0.2, 0.73 }, kSmall, kFaded },
{ { 0.8, 0.73 }, kSmall, kFaded },
{ { 0.302, 0.825 }, kLarge, kOpaque },
{ { 0.698, 0.825 }, kLarge, kOpaque },
{ { 0.5, 0.876 }, kLarge, kFaded },
{ { 0.144, 0.936 }, kSmall, kFaded },
{ { 0.856, 0.936 }, kSmall, kFaded },
};
return result;
}
[[nodiscard]] QImage CreateGradient(
QSize size,
const Data::UniqueGift &gift) {
@ -2108,6 +2066,7 @@ void AddUniqueGiftCover(
PaintPoints(
p,
PatternPoints(),
gift.emojis,
gift.emoji.get(),
*gift.gift,
@ -2383,8 +2342,83 @@ void UpgradeBox(
AddUniqueCloseButton(box);
}
const std::vector<PatternPoint> &PatternPoints() {
static const auto kSmall = 0.7;
static const auto kFaded = 0.2;
static const auto kLarge = 0.85;
static const auto kOpaque = 0.3;
static const auto result = std::vector<PatternPoint>{
{ { 0.5, 0.066 }, kSmall, kFaded },
{ { 0.177, 0.168 }, kSmall, kFaded },
{ { 0.822, 0.168 }, kSmall, kFaded },
{ { 0.37, 0.168 }, kLarge, kOpaque },
{ { 0.63, 0.168 }, kLarge, kOpaque },
{ { 0.277, 0.308 }, kSmall, kOpaque },
{ { 0.723, 0.308 }, kSmall, kOpaque },
{ { 0.13, 0.42 }, kSmall, kFaded },
{ { 0.87, 0.42 }, kSmall, kFaded },
{ { 0.27, 0.533 }, kLarge, kOpaque },
{ { 0.73, 0.533 }, kLarge, kOpaque },
{ { 0.2, 0.73 }, kSmall, kFaded },
{ { 0.8, 0.73 }, kSmall, kFaded },
{ { 0.302, 0.825 }, kLarge, kOpaque },
{ { 0.698, 0.825 }, kLarge, kOpaque },
{ { 0.5, 0.876 }, kLarge, kFaded },
{ { 0.144, 0.936 }, kSmall, kFaded },
{ { 0.856, 0.936 }, kSmall, kFaded },
};
return result;
}
const std::vector<PatternPoint> &PatternPointsSmall() {
static const auto kSmall = 0.45;
static const auto kFaded = 0.2;
static const auto kLarge = 0.55;
static const auto kOpaque = 0.3;
static const auto result = std::vector<PatternPoint>{
{ { 0.5, 0.066 }, kSmall, kFaded },
{ { 0.177, 0.168 }, kSmall, kFaded },
{ { 0.822, 0.168 }, kSmall, kFaded },
{ { 0.37, 0.168 }, kLarge, kOpaque },
{ { 0.63, 0.168 }, kLarge, kOpaque },
{ { 0.277, 0.308 }, kSmall, kOpaque },
{ { 0.723, 0.308 }, kSmall, kOpaque },
{ { 0.13, 0.42 }, kSmall, kFaded },
{ { 0.87, 0.42 }, kSmall, kFaded },
{ { 0.27, 0.533 }, kLarge, kOpaque },
{ { 0.73, 0.533 }, kLarge, kOpaque },
{ { 0.2, 0.73 }, kSmall, kFaded },
{ { 0.8, 0.73 }, kSmall, kFaded },
{ { 0.302, 0.825 }, kLarge, kOpaque },
{ { 0.698, 0.825 }, kLarge, kOpaque },
{ { 0.5, 0.876 }, kLarge, kFaded },
{ { 0.144, 0.936 }, kSmall, kFaded },
{ { 0.856, 0.936 }, kSmall, kFaded },
};
return result;
}
void PaintPoints(
QPainter &p,
const std::vector<PatternPoint> &points,
base::flat_map<float64, QImage> &cache,
not_null<Text::CustomEmoji*> emoji,
const Data::UniqueGift &gift,
@ -2417,7 +2451,7 @@ void PaintPoints(
}
}
};
for (const auto point : PatternPoints()) {
for (const auto &point : points) {
paintPoint(point);
}
}

View file

@ -42,8 +42,17 @@ void AddUniqueGiftCover(
rpl::producer<Data::UniqueGift> data,
rpl::producer<QString> subtitleOverride = nullptr);
struct PatternPoint {
QPointF position;
float64 scale = 1.;
float64 opacity = 1.;
};
[[nodiscard]] const std::vector<PatternPoint> &PatternPoints();
[[nodiscard]] const std::vector<PatternPoint> &PatternPointsSmall();
void PaintPoints(
QPainter &p,
const std::vector<PatternPoint> &points,
base::flat_map<float64, QImage> &cache,
not_null<Text::CustomEmoji*> emoji,
const Data::UniqueGift &gift,

View file

@ -312,7 +312,8 @@ QImage PremiumGift::cornerTag(const PaintContext &context) {
if (_data.unique) {
badge = {
.text = tr::lng_gift_collectible_tag(tr::now),
.bg = _data.unique->backdrop.patternColor,
.bg1 = _data.unique->backdrop.edgeColor,
.bg2 = _data.unique->backdrop.patternColor,
.fg = QColor(255, 255, 255),
};
} else if (const auto count = _data.limitedCount) {
@ -325,7 +326,7 @@ QImage PremiumGift::cornerTag(const PaintContext &context) {
(((count % 1000) && (count < 10'000))
? Lang::FormatCountDecimal(count)
: Lang::FormatCountToShort(count).string))),
.bg = context.st->msgServiceBg()->c,
.bg1 = context.st->msgServiceBg()->c,
.fg = context.st->msgServiceFg()->c,
};
} else {

View file

@ -525,7 +525,13 @@ Fn<void(Painter&, const Ui::ChatPaintContext &)> UniqueGiftBg(
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);
Ui::PaintPoints(
p,
Ui::PatternPoints(),
state->cache,
state->pattern.get(),
*gift,
outer);
p.setClipping(false);
const auto add = style::ConvertScale(2);
@ -536,7 +542,8 @@ Fn<void(Painter&, const Ui::ChatPaintContext &)> UniqueGiftBg(
inner.height() + 2 * add);
auto badge = Info::PeerGifts::GiftBadge{
.text = tr::lng_gift_collectible_tag(tr::now),
.bg = gift->backdrop.patternColor,
.bg1 = gift->backdrop.edgeColor,
.bg2 = gift->backdrop.patternColor,
.fg = gift->backdrop.textColor,
};
if (state->badgeCache.isNull() || state->badgeKey != badge) {

View file

@ -42,10 +42,14 @@ std::strong_ordering operator<=>(const GiftBadge &a, const GiftBadge &b) {
if (result1 != std::strong_ordering::equal) {
return result1;
}
const auto result2 = (a.bg.rgb() <=> b.bg.rgb());
const auto result2 = (a.bg1.rgb() <=> b.bg1.rgb());
if (result2 != std::strong_ordering::equal) {
return result2;
}
const auto result3 = (a.bg2.rgb() <=> b.bg2.rgb());
if (result3 != std::strong_ordering::equal) {
return result3;
}
return a.fg.rgb() <=> b.fg.rgb();
}
@ -148,6 +152,7 @@ void GiftButton::setDescriptor(const GiftDescriptor &descriptor, Mode mode) {
if (mode != Mode::Full) {
_button = QRect();
_small = true;
return;
}
const auto buttonw = _price.maxWidth();
@ -263,11 +268,11 @@ void GiftButton::cacheUniqueBackground(
if (!_patterned && _uniquePatternEmoji->ready()) {
_patterned = true;
auto p = QPainter(&_uniqueBackgroundCache);
p.setOpacity(0.5);
p.setClipRect(inner);
const auto skip = inner.width() / 3;
Ui::PaintPoints(
p,
Ui::PatternPointsSmall(),
_uniquePatternCache,
_uniquePatternEmoji.get(),
*unique,
@ -338,7 +343,9 @@ void GiftButton::paintEvent(QPaintEvent *e) {
p.drawImage(
QRect(
(width - size.width()) / 2,
(_text.isEmpty()
(_small
? st::giftBoxSmallStickerTop
: _text.isEmpty()
? st::giftBoxStickerStarTop
: st::giftBoxStickerTop),
size.width(),
@ -348,7 +355,9 @@ void GiftButton::paintEvent(QPaintEvent *e) {
if (hidden) {
const auto topleft = QPoint(
(width - st::giftBoxStickerSize.width()) / 2,
(_text.isEmpty()
(_small
? st::giftBoxSmallStickerTop
: _text.isEmpty()
? st::giftBoxStickerStarTop
: st::giftBoxStickerTop));
_delegate->hiddenMark()->paint(
@ -372,8 +381,9 @@ void GiftButton::paintEvent(QPaintEvent *e) {
const auto kMinus = QChar(0x2212);
return GiftBadge{
.text = kMinus + QString::number(data.discountPercent) + '%',
.bg = st::attentionButtonFg->c,
.bg1 = st::attentionButtonFg->c,
.fg = st::windowBg->c,
.small = true,
};
}
return GiftBadge();
@ -383,7 +393,7 @@ void GiftButton::paintEvent(QPaintEvent *e) {
return GiftBadge{
.text = (soldOut
? tr::lng_gift_stars_sold_out(tr::now)
: !data.userpic
: (!data.userpic && !data.info.unique)
? tr::lng_gift_stars_limited(tr::now)
: (count == 1)
? tr::lng_gift_limited_of_one(tr::now)
@ -393,19 +403,23 @@ void GiftButton::paintEvent(QPaintEvent *e) {
(((count % 1000) && (count < 10'000))
? Lang::FormatCountDecimal(count)
: Lang::FormatCountToShort(count).string))),
.bg = (unique
? unique->backdrop.patternColor
.bg1 = (unique
? unique->backdrop.edgeColor
: soldOut
? st::attentionButtonFg->c
: st::windowActiveTextFg->c),
.bg2 = (unique
? unique->backdrop.patternColor
: QColor(0, 0, 0, 0)),
.fg = unique ? QColor(255, 255, 255) : st::windowBg->c,
.small = true,
};
}
return GiftBadge();
});
if (badge) {
const auto rubberOut = _extend.top();
const auto rubberOut = st::lineWidth;
const auto inner = rect().marginsRemoved(_extend);
p.setClipRect(inner.marginsAdded(
{ rubberOut, rubberOut, rubberOut, rubberOut }));
@ -413,8 +427,8 @@ void GiftButton::paintEvent(QPaintEvent *e) {
const auto cached = _delegate->cachedBadge(badge);
const auto width = cached.width() / cached.devicePixelRatio();
p.drawImage(
position.x() + singlew + _extend.top() - width,
position.y() - _extend.top(),
position.x() + singlew + rubberOut - width,
position.y() - rubberOut,
cached);
}
if (!_button.isEmpty()) {
@ -633,7 +647,9 @@ rpl::producer<not_null<DocumentData*>> GiftStickerValue(
}
QImage ValidateRotatedBadge(const GiftBadge &badge, int added) {
const auto &font = st::semiboldFont;
const auto &font = badge.small
? st::giftBoxGiftBadgeFont
: st::semiboldFont;
const auto twidth = font->width(badge.text) + 2 * added;
const auto skip = int(std::ceil(twidth / M_SQRT2));
const auto ratio = style::DevicePixelRatio();
@ -670,12 +686,19 @@ QImage ValidateRotatedBadge(const GiftBadge &badge, int added) {
auto p = QPainter(&result);
auto hq = PainterHighQualityEnabler(p);
p.setPen(Qt::NoPen);
p.setBrush(badge.bg);
p.setBrush(badge.bg1);
p.save();
p.translate(textpos);
p.rotate(45.);
p.drawRect(-5 * twidth, 0, twidth * 12, font->height);
const auto rect = QRect(-5 * twidth, 0, twidth * 12, font->height);
p.drawRect(rect);
if (badge.bg2.alpha() > 0) {
p.setOpacity(0.5);
p.setBrush(badge.bg2);
p.drawRect(rect);
p.setOpacity(1.);
}
p.restore();
p.drawImage(0, 0, scaled);

View file

@ -74,8 +74,10 @@ struct GiftDescriptor : std::variant<GiftTypePremium, GiftTypeStars> {
struct GiftBadge {
QString text;
QColor bg;
QColor bg1;
QColor bg2 = QColor(0, 0, 0, 0);
QColor fg;
bool small = false;
explicit operator bool() const {
return !text.isEmpty();
@ -148,6 +150,7 @@ private:
std::optional<Ui::Premium::ColoredMiniStars> _stars;
bool _subscribed = false;
bool _patterned = false;
bool _small = false;
QRect _button;
QMargins _extend;

View file

@ -1348,6 +1348,7 @@ void ReceiptCreditsBox(
};
const auto canUpgrade = e.stargiftId
&& e.canUpgradeGift
&& (e.in || giftToSelf)
&& !e.uniqueGift;
const auto canUpgradeFree = canUpgrade && (e.starsUpgradedBySender > 0);

View file

@ -109,11 +109,12 @@ giftBoxTabStyle: semiboldTextStyle;
giftBoxTabFg: windowSubTextFg;
giftBoxTabFgActive: windowBoldFg;
giftBoxTabBgActive: windowBgRipple;
giftBoxPadding: margins(20px, 4px, 20px, 24px);
giftBoxPadding: margins(11px, 4px, 11px, 24px);
giftBoxGiftSkip: point(10px, 8px);
giftBoxGiftHeight: 164px;
giftBoxGiftSmall: 124px;
giftBoxGiftSmall: 108px;
giftBoxGiftRadius: 12px;
giftBoxGiftBadgeFont: font(10px semibold);
giftBoxPremiumIconSize: 64px;
giftBoxPremiumIconTop: 10px;
giftBoxPremiumTextTop: 84px;
@ -125,6 +126,7 @@ giftBoxPreviewTextPadding: margins(12px, 4px, 12px, 4px);
giftBoxButtonMargin: margins(12px, 8px, 12px, 12px);
giftBoxStickerTop: 0px;
giftBoxStickerStarTop: 24px;
giftBoxSmallStickerTop: 16px;
giftBoxStickerSize: size(80px, 80px);
giftBoxUserpicSize: 24px;
giftBoxUserpicSkip: 2px;