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( [[nodiscard]] QImage CreateGradient(
QSize size, QSize size,
const Data::UniqueGift &gift) { const Data::UniqueGift &gift) {
@ -2108,6 +2066,7 @@ void AddUniqueGiftCover(
PaintPoints( PaintPoints(
p, p,
PatternPoints(),
gift.emojis, gift.emojis,
gift.emoji.get(), gift.emoji.get(),
*gift.gift, *gift.gift,
@ -2383,8 +2342,83 @@ void UpgradeBox(
AddUniqueCloseButton(box); 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( void PaintPoints(
QPainter &p, QPainter &p,
const std::vector<PatternPoint> &points,
base::flat_map<float64, QImage> &cache, base::flat_map<float64, QImage> &cache,
not_null<Text::CustomEmoji*> emoji, not_null<Text::CustomEmoji*> emoji,
const Data::UniqueGift &gift, const Data::UniqueGift &gift,
@ -2417,7 +2451,7 @@ void PaintPoints(
} }
} }
}; };
for (const auto point : PatternPoints()) { for (const auto &point : points) {
paintPoint(point); paintPoint(point);
} }
} }

View file

@ -42,8 +42,17 @@ void AddUniqueGiftCover(
rpl::producer<Data::UniqueGift> data, rpl::producer<Data::UniqueGift> data,
rpl::producer<QString> subtitleOverride = nullptr); 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( void PaintPoints(
QPainter &p, QPainter &p,
const std::vector<PatternPoint> &points,
base::flat_map<float64, QImage> &cache, base::flat_map<float64, QImage> &cache,
not_null<Text::CustomEmoji*> emoji, not_null<Text::CustomEmoji*> emoji,
const Data::UniqueGift &gift, const Data::UniqueGift &gift,

View file

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

View file

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

View file

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

View file

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

View file

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