mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-07-25 06:54:43 +00:00
Improve design of craft ending.
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<title>Icon / Menu / craft_chance</title>
|
||||
<g id="Icon-/-Menu-/-craft_chance" stroke="none" fill="none" fill-rule="nonzero">
|
||||
<path d="M14.2394854,18.3384734 C15.1002335,18.3384734 15.6545373,18.9252891 15.6545373,19.830532 L15.6545373,19.9942945 C15.6545373,20.8813416 15.0641813,21.5 14.2124462,21.5 C13.3607112,21.5 12.7973944,20.8995374 12.7973944,19.9942945 L12.7973944,19.830532 C12.7973944,18.938936 13.3742308,18.3384734 14.2394854,18.3384734 Z M14.8002575,14.8144933 C15.0602829,15.0211239 15.1045571,15.4006755 14.8991467,15.6622455 L10.5569642,21.1915888 C10.3515538,21.4531588 9.97424351,21.4976959 9.71421812,21.2910653 C9.45419274,21.0844347 9.40991856,20.7048831 9.61532898,20.4433131 L13.9575114,14.9139699 C14.1629219,14.6523999 14.5402321,14.6078627 14.8002575,14.8144933 Z M14.2558401,19.2729857 C14.0219885,19.2729857 13.8896197,19.4974831 13.8896197,19.9015784 L13.8896197,19.9689277 C13.8896197,20.3685331 14.0264008,20.6065003 14.2558401,20.6065003 C14.4808671,20.6065003 14.6132359,20.3685331 14.6132359,19.9689277 L14.6132359,19.9015784 C14.6132359,19.501973 14.4852794,19.2729857 14.2558401,19.2729857 Z M12,2.5 C17.2467051,2.5 21.5,6.77856068 21.5,12.0564327 C21.5,14.8051138 20.3392414,17.3671126 18.3459938,19.168073 C18.0818576,19.4067281 17.6754071,19.3847997 17.4381612,19.1190945 C17.2009154,18.8533892 17.2227143,18.4445243 17.4868505,18.2058691 C19.2113831,16.647701 20.2142857,14.4341182 20.2142857,12.0564327 C20.2142857,7.49285915 16.5366247,3.7933518 12,3.7933518 C7.46337527,3.7933518 3.78571429,7.49285915 3.78571429,12.0564327 C3.78571429,14.6032885 4.93761579,16.9582836 6.87881601,18.5174708 C7.1562635,18.7403187 7.20159194,19.1472244 6.98005992,19.42632 C6.7585279,19.7054156 6.35402509,19.7510133 6.07657761,19.5281653 C3.83300652,17.7261116 2.5,15.0008578 2.5,12.0564327 C2.5,6.77856068 6.75329488,2.5 12,2.5 Z M10.2394854,14.8895352 C11.1002335,14.8895352 11.6545373,15.476351 11.6545373,16.3815939 L11.6545373,16.5453564 C11.6545373,17.4324034 11.0641813,18.0510619 10.2124462,18.0510619 C9.36071122,18.0510619 8.79739441,17.4505993 8.79739441,16.5453564 L8.79739441,16.3815939 C8.79739441,15.4899979 9.37423083,14.8895352 10.2394854,14.8895352 Z M10.2558401,15.8240476 C10.0219885,15.8240476 9.88961966,16.048545 9.88961966,16.4526403 L9.88961966,16.5199895 C9.88961966,16.9195949 10.0264008,17.1575622 10.2558401,17.1575622 C10.4808671,17.1575622 10.6132359,16.9195949 10.6132359,16.5199895 L10.6132359,16.4526403 C10.6132359,16.0530349 10.4852794,15.8240476 10.2558401,15.8240476 Z M12.7357164,9.85753889 L15.410911,11.9797736 C15.6896921,12.200931 15.7374641,12.6075537 15.5176127,12.8879908 C15.2977613,13.168428 14.8935398,13.2164838 14.6147587,12.9953264 L12.2503714,11.1196555 L9.86540882,13.0262454 C9.58745192,13.24845 9.18305457,13.2019154 8.96216206,12.9223074 C8.74126956,12.6426993 8.78752939,12.2358997 9.0654863,12.013695 L11.7603373,9.85937373 C12.0456859,9.63126002 12.4495217,9.63050034 12.7357164,9.85753889 Z M12.7357164,6.40860076 L15.410911,8.53083548 C15.6896921,8.75199285 15.7374641,9.15861554 15.5176127,9.4390527 C15.2977613,9.71948986 14.8935398,9.76754563 14.6147587,9.54638827 L12.2503714,7.67071733 L9.86540882,9.57730723 C9.58745192,9.7995119 9.18305457,9.75297727 8.96216206,9.47336922 C8.74126956,9.19376117 8.78752939,8.78696158 9.0654863,8.56475691 L11.7603373,6.4104356 C12.0456859,6.18232188 12.4495217,6.18156221 12.7357164,6.40860076 Z" id="Shape" fill="#FFFFFF"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
@@ -4100,17 +4100,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_collection_remove_from" = "Remove from Collection";
|
||||
"lng_gift_locked_title" = "Gift Locked";
|
||||
"lng_gift_craft_info_title" = "Gift Crafting";
|
||||
"lng_gift_craft_info_about" = "Use your existing gifts to craft new ones.";
|
||||
"lng_gift_craft_combine_title" = "New Models";
|
||||
"lng_gift_craft_combine_about" = "Add up to 4 NFT Gifts to attempt crafting a new upgraded model.";
|
||||
"lng_gift_craft_input_title" = "Input Matters";
|
||||
"lng_gift_craft_input_about" = "Combine more gifts to improve your success chance.";
|
||||
"lng_gift_craft_exclusive_title" = "Exclusive Look";
|
||||
"lng_gift_craft_exclusive_about" = "Reforge gifts into a rarer collectibles with a new look.";
|
||||
"lng_gift_craft_info_about" = "Turn your gifts into rare, epic\nand legendary versions.";
|
||||
"lng_gift_craft_rare_title" = "Get Rare Models";
|
||||
"lng_gift_craft_rare_about" = "Select up to 4 gifts to craft a new exclusive model.";
|
||||
"lng_gift_craft_chance_title" = "Maximize Chances";
|
||||
"lng_gift_craft_chance_about" = "Combine more gifts to increase your odds of success.";
|
||||
"lng_gift_craft_affect_title" = "Affect the Result";
|
||||
"lng_gift_craft_affect_about" = "Use gifts with the same attribute to boost its chance.";
|
||||
"lng_gift_craft_start_button" = "Start Crafting";
|
||||
"lng_gift_craft_title" = "Craft Gift";
|
||||
"lng_gift_craft_about1" = "Add up to **4** gifts to craft new {gift}.";
|
||||
"lng_gift_craft_about2" = "If crafting fails, all selected gifts will be **consumed**.";
|
||||
"lng_gift_craft_about1" = "Add up to **4** gifts to craft new\n{gift}.";
|
||||
"lng_gift_craft_about2" = "If crafting fails, all used gifts\nwill be lost.";
|
||||
"lng_gift_craft_view_all" = "{emoji} View all new variants {arrow}";
|
||||
"lng_gift_craft_button" = "Craft {gift}";
|
||||
"lng_gift_craft_button_chance" = "{percent} Success Chance";
|
||||
@@ -4121,9 +4121,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_gift_craft_select_market#one" = "{count} suitable gift on sale";
|
||||
"lng_gift_craft_select_market#other" = "{count} suitable gifts on sale";
|
||||
"lng_gift_craft_progress" = "Crafting";
|
||||
"lng_gift_craft_progress_about" = "Crafting **{gift}**";
|
||||
"lng_gift_craft_progress_chance" = "{percent} success chance";
|
||||
"lng_gift_craft_progress_fails" = "If crafting fails, all selected gifts will be consumed.";
|
||||
"lng_gift_craft_progress_fails" = "If crafting fails, all used\ngifts will be lost.";
|
||||
"lng_gift_craft_failed" = "Crafting failed. Gifts consumed :(";
|
||||
"lng_gift_craft_failed_title" = "Crafting Failed";
|
||||
"lng_gift_craft_failed_about#one" = "This crafting attempt was unsuccessful.\n**{count}** gift was lost.";
|
||||
"lng_gift_craft_failed_about#other" = "This crafting attempt was unsuccessful.\n**{count}** gifts were lost.";
|
||||
"lng_gift_craft_new_button" = "Craft New Gift";
|
||||
|
||||
"lng_auction_about_title" = "Auction";
|
||||
"lng_auction_about_subtitle" = "Join the battle for exclusive gifts.";
|
||||
|
||||
@@ -666,12 +666,7 @@ QString FormatReadDate(TimeId date, const QDateTime &now) {
|
||||
return tr::lng_mediaview_date_time(
|
||||
tr::now,
|
||||
lt_date,
|
||||
tr::lng_month_day(
|
||||
tr::now,
|
||||
lt_month,
|
||||
Lang::MonthDay(readDate.month())(tr::now),
|
||||
lt_day,
|
||||
QString::number(readDate.day())),
|
||||
langDayOfMonthShort(readDate),
|
||||
lt_time,
|
||||
QLocale().toString(parsed.time(), QLocale::ShortFormat));
|
||||
}
|
||||
|
||||
@@ -18,12 +18,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_session.h"
|
||||
#include "history/view/media/history_view_sticker_player.h"
|
||||
#include "info/peer_gifts/info_peer_gifts_common.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "lottie/lottie_common.h"
|
||||
#include "main/main_session.h"
|
||||
#include "settings/settings_credits_graphics.h"
|
||||
#include "ui/boxes/boost_box.h"
|
||||
#include "ui/image/image_prepare.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/widgets/gradient_round_button.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/top_background_gradient.h"
|
||||
#include "ui/painter.h"
|
||||
@@ -46,6 +49,13 @@ constexpr auto kFailureFadeDuration = crl::time(400);
|
||||
constexpr auto kSuccessFadeInDuration = crl::time(300);
|
||||
constexpr auto kSuccessExpandDuration = crl::time(400);
|
||||
constexpr auto kSuccessExpandStart = crl::time(100);
|
||||
constexpr auto kProgressFadeInDuration = crl::time(300);
|
||||
constexpr auto kFailureFadeInDuration = crl::time(300);
|
||||
|
||||
[[nodiscard]] QString FormatPercent(int permille) {
|
||||
const auto rounded = (permille + 5) / 10;
|
||||
return QString::number(rounded) + '%';
|
||||
}
|
||||
|
||||
struct Rotation {
|
||||
float64 x = 0.;
|
||||
@@ -424,8 +434,7 @@ void PaintCubeFirstFlight(
|
||||
sideBg.setAlphaF(progress);
|
||||
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
const auto offset = shared->craftingOffsetY;
|
||||
const auto forge = shared->forgeRect.translated(0, offset);
|
||||
const auto forge = shared->forgeRect;
|
||||
const auto radius = (1. - progress) * st::boxRadius;
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(overlayBg);
|
||||
@@ -450,35 +459,37 @@ void FailureAnimationCheck(
|
||||
not_null<RpWidget*> canvas,
|
||||
crl::time now) {
|
||||
const auto animation = state->failureAnimation.get();
|
||||
if (!animation) {
|
||||
if (!animation || animation->started) {
|
||||
return;
|
||||
}
|
||||
const auto elapsed = (now - state->animationStartTime);
|
||||
const auto left = (state->animationDuration * Slowing()) - elapsed;
|
||||
if (!animation->started
|
||||
&& left > 0
|
||||
&& left <= kFailureFadeDuration * Slowing()) {
|
||||
animation->started = true;
|
||||
auto &covers = state->shared->covers;
|
||||
for (auto i = 0; i != 5; ++i) {
|
||||
auto &cover = covers[i];
|
||||
if (!cover.shown) {
|
||||
animation->finalCoverIndex = i;
|
||||
if (i != 4) {
|
||||
auto &last = covers.back();
|
||||
cover.backdrop = last.backdrop;
|
||||
cover.pattern = std::move(last.pattern);
|
||||
cover.button1 = last.button1;
|
||||
cover.button2 = last.button2;
|
||||
}
|
||||
cover.shown = true;
|
||||
cover.shownAnimation.start([=] {
|
||||
canvas->update();
|
||||
}, 0., 1., left);
|
||||
break;
|
||||
if (left <= 0 || left > kFailureFadeDuration * Slowing()) {
|
||||
return;
|
||||
}
|
||||
animation->started = true;
|
||||
auto &covers = state->shared->covers;
|
||||
for (auto i = 0; i != 5; ++i) {
|
||||
auto &cover = covers[i];
|
||||
if (!cover.shown) {
|
||||
animation->finalCoverIndex = i;
|
||||
if (i != 4) {
|
||||
auto &last = covers.back();
|
||||
cover.backdrop = last.backdrop;
|
||||
cover.pattern = std::move(last.pattern);
|
||||
cover.button1 = last.button1;
|
||||
cover.button2 = last.button2;
|
||||
}
|
||||
cover.shown = true;
|
||||
cover.shownAnimation.start([=] {
|
||||
canvas->update();
|
||||
}, 0., 1., left);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
state->failureShown = true;
|
||||
state->progressShown = false;
|
||||
}
|
||||
|
||||
void FailureAnimationPrepareFrame(
|
||||
@@ -521,6 +532,11 @@ void FailureAnimationPrepareFrame(
|
||||
p.fillRect(rect, bg);
|
||||
}
|
||||
st::craftFail.paintInCenter(p, rect, st::white->c);
|
||||
p.setOpacity(progress);
|
||||
p.drawImage(
|
||||
st::craftForgePadding,
|
||||
st::craftForgePadding,
|
||||
state->shared->lostRadial);
|
||||
}
|
||||
|
||||
[[nodiscard]] QRect PrepareCraftFrame(
|
||||
@@ -559,11 +575,11 @@ void FailureAnimationPrepareFrame(
|
||||
}
|
||||
|
||||
void PaintCube(
|
||||
QPainter &p,
|
||||
not_null<CraftAnimationState*> state,
|
||||
not_null<RpWidget*> canvas,
|
||||
QPointF center,
|
||||
float64 size) {
|
||||
QPainter &p,
|
||||
not_null<CraftAnimationState*> state,
|
||||
not_null<RpWidget*> canvas,
|
||||
QPointF center,
|
||||
float64 size) {
|
||||
const auto shared = state->shared.get();
|
||||
|
||||
const auto now = crl::now();
|
||||
@@ -824,15 +840,13 @@ void PaintFlyingGift(
|
||||
void PaintSlideOutCorner(
|
||||
QPainter &p,
|
||||
const CraftState::CornerSnapshot &corner,
|
||||
float64 offsetY,
|
||||
float64 progress) {
|
||||
if (corner.originalRect.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto ratio = style::DevicePixelRatio();
|
||||
const auto originalTopLeft = QPointF(corner.originalRect.topLeft());
|
||||
const auto currentTopLeft = originalTopLeft + QPointF(0, offsetY);
|
||||
const auto currentTopLeft = QPointF(corner.originalRect.topLeft());
|
||||
if (corner.giftButton) {
|
||||
const auto frame = corner.gift(0.);
|
||||
const auto giftSize = QSizeF(frame.size()) / ratio;
|
||||
@@ -887,19 +901,6 @@ void PaintSlideOutPhase(
|
||||
float64 progress) {
|
||||
const auto ratio = style::DevicePixelRatio();
|
||||
|
||||
if (!shared->topPart.isNull()) {
|
||||
const auto topSize = QSizeF(shared->topPart.size()) / ratio;
|
||||
const auto slideDistance = topSize.height();
|
||||
const auto offsetY = slideDistance * progress;
|
||||
const auto opacity = 1. - progress;
|
||||
|
||||
p.setOpacity(opacity);
|
||||
p.drawImage(
|
||||
shared->topPartRect.topLeft() - QPointF(0, offsetY),
|
||||
shared->topPart);
|
||||
p.setOpacity(1.);
|
||||
}
|
||||
|
||||
if (!shared->bottomPart.isNull()) {
|
||||
const auto slideDistance = QSizeF(shared->bottomPart.size()).height()
|
||||
/ ratio;
|
||||
@@ -913,13 +914,12 @@ void PaintSlideOutPhase(
|
||||
p.setOpacity(1.);
|
||||
}
|
||||
|
||||
const auto offset = shared->craftingOffsetY;
|
||||
for (const auto &corner : shared->corners) {
|
||||
PaintSlideOutCorner(p, corner, offset, progress);
|
||||
PaintSlideOutCorner(p, corner, progress);
|
||||
}
|
||||
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
const auto forge = shared->forgeRect.translated(0, offset);
|
||||
const auto forge = shared->forgeRect;
|
||||
const auto radius = st::boxRadius;
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(shared->forgeBgOverlay);
|
||||
@@ -932,6 +932,46 @@ void PaintSlideOutPhase(
|
||||
shared->forgePercent);
|
||||
}
|
||||
|
||||
void PaintFailureThumbnails(
|
||||
QPainter &p,
|
||||
not_null<const CraftState*> shared,
|
||||
QSize canvasSize,
|
||||
float64 fadeProgress) {
|
||||
if (fadeProgress <= 0.) {
|
||||
return;
|
||||
}
|
||||
|
||||
p.setOpacity(fadeProgress);
|
||||
|
||||
const auto width = canvasSize.width();
|
||||
const auto giftsCount = int(shared->lostGifts.size());
|
||||
const auto thumbSize = shared->lostGifts.front().thumbnail.size()
|
||||
/ style::DevicePixelRatio();
|
||||
const auto thumbSpacing = st::boxRowPadding.left() / 2;
|
||||
const auto totalThumbWidth = giftsCount * thumbSize.width()
|
||||
+ (giftsCount - 1) * thumbSpacing;
|
||||
const auto available = width
|
||||
- st::boxRowPadding.left()
|
||||
- st::boxRowPadding.right();
|
||||
const auto skip = (totalThumbWidth > available)
|
||||
? (available - giftsCount * thumbSize.width()) / (giftsCount - 1)
|
||||
: thumbSpacing;
|
||||
const auto full = giftsCount * thumbSize.width()
|
||||
+ (giftsCount - 1) * skip;
|
||||
auto x = (width - full) / 2;
|
||||
const auto y = shared->forgeRect.bottom() + st::craftFailureThumbsTop;
|
||||
|
||||
for (const auto &gift : shared->lostGifts) {
|
||||
if (!gift.thumbnail.isNull()) {
|
||||
const auto targetRect = QRect(QPoint(x, y), thumbSize);
|
||||
p.drawImage(targetRect, gift.thumbnail);
|
||||
}
|
||||
x += thumbSize.width() + skip;
|
||||
}
|
||||
|
||||
p.setOpacity(1.);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::array<QPointF, 4> RotateCornersForFace(
|
||||
std::array<QPointF, 4> corners,
|
||||
int rotation) {
|
||||
@@ -1018,8 +1058,7 @@ void StartGiftFlightToFace(
|
||||
|
||||
const auto cubeSize = float64(shared->forgeRect.width());
|
||||
const auto cubeCenter = QPointF(shared->forgeRect.topLeft())
|
||||
+ QPointF(cubeSize, cubeSize) / 2.
|
||||
+ QPointF(0, shared->craftingOffsetY);
|
||||
+ QPointF(cubeSize, cubeSize) / 2.;
|
||||
|
||||
const auto targetCorners = ComputeCubeFaceCorners(
|
||||
cubeCenter,
|
||||
@@ -1090,8 +1129,7 @@ void PaintLoadingAnimation(
|
||||
}
|
||||
|
||||
const auto shared = state->shared.get();
|
||||
const auto forge = shared->forgeRect.translated(
|
||||
{ 0, shared->craftingOffsetY });
|
||||
const auto forge = shared->forgeRect;
|
||||
const auto inner = forge.marginsRemoved({
|
||||
st::craftForgePadding,
|
||||
st::craftForgePadding,
|
||||
@@ -1150,12 +1188,6 @@ void CraftState::paint(
|
||||
return gradient;
|
||||
};
|
||||
auto patternOffsetY = 0.;
|
||||
if (slideProgress > 0.
|
||||
&& containerHeight > 0
|
||||
&& craftingAreaCenterY > 0) {
|
||||
patternOffsetY = (containerHeight / 2. - craftingAreaCenterY)
|
||||
* slideProgress;
|
||||
}
|
||||
const auto paintPattern = [&](
|
||||
QPainter &q,
|
||||
PatternView &pattern,
|
||||
@@ -1279,28 +1311,202 @@ CraftState::EmptySide CraftState::prepareEmptySide(
|
||||
return { .bg = bg, .frame = result };
|
||||
}
|
||||
|
||||
void SetupProgressControls(
|
||||
not_null<CraftAnimationState*> state,
|
||||
not_null<RpWidget*> canvas) {
|
||||
const auto add = [&](not_null<FlatLabel*> label) {
|
||||
state->progressOpacity.value() | rpl::on_next([=](float64 opacity) {
|
||||
label->setOpacity(opacity);
|
||||
}, label->lifetime());
|
||||
};
|
||||
|
||||
const auto controls = CreateChild<VerticalLayout>(canvas);
|
||||
controls->resizeToWidth(canvas->width());
|
||||
controls->move(0, state->shared->craftingBottom);
|
||||
|
||||
const auto title = controls->add(
|
||||
object_ptr<FlatLabel>(
|
||||
controls,
|
||||
tr::lng_gift_craft_progress(),
|
||||
st::uniqueGiftTitle),
|
||||
st::boxRowPadding + st::craftProgressTitleMargin,
|
||||
style::al_top);
|
||||
add(title);
|
||||
title->setTextColorOverride(st::white->c);
|
||||
|
||||
const auto subColor = QColor(255, 255, 255, 178);
|
||||
const auto about = controls->add(
|
||||
object_ptr<FlatLabel>(
|
||||
controls,
|
||||
tr::lng_gift_craft_progress_about(
|
||||
lt_gift,
|
||||
rpl::single(tr::marked(state->shared->giftName)),
|
||||
tr::rich),
|
||||
st::uniqueGiftSubtitle),
|
||||
st::boxRowPadding + st::craftProgressAboutMargin,
|
||||
style::al_top);
|
||||
add(about);
|
||||
about->setTextColorOverride(subColor);
|
||||
|
||||
const auto warning = controls->add(
|
||||
object_ptr<FlatLabel>(
|
||||
controls,
|
||||
tr::lng_gift_craft_progress_fails(),
|
||||
st::craftAbout),
|
||||
st::boxRowPadding + st::craftProgressWarningMargin,
|
||||
style::al_top);
|
||||
add(warning);
|
||||
warning->setTextColorOverride(subColor);
|
||||
|
||||
const auto chance = controls->add(
|
||||
object_ptr<FlatLabel>(
|
||||
controls,
|
||||
tr::lng_gift_craft_progress_chance(
|
||||
tr::now,
|
||||
lt_percent,
|
||||
FormatPercent(state->shared->successPermille)),
|
||||
st::craftChance),
|
||||
st::boxRowPadding + st::craftProgressChanceMargin,
|
||||
style::al_top);
|
||||
add(chance);
|
||||
chance->setTextColorOverride(st::white->c);
|
||||
chance->paintOn([=](QPainter &p) {
|
||||
if (const auto opacity = state->progressOpacity.current()) {
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setOpacity(opacity);
|
||||
p.setBrush(state->shared->forgeBgOverlay);
|
||||
const auto radius = chance->height() / 2.;
|
||||
p.drawRoundedRect(chance->rect(), radius, radius);
|
||||
}
|
||||
});
|
||||
|
||||
controls->showOn(state->progressOpacity.value(
|
||||
) | rpl::map(
|
||||
rpl::mappers::_1 > 0.
|
||||
) | rpl::distinct_until_changed());
|
||||
}
|
||||
|
||||
void SetupFailureControls(
|
||||
not_null<CraftAnimationState*> state,
|
||||
not_null<RpWidget*> canvas) {
|
||||
const auto add = [&](not_null<FlatLabel*> label) {
|
||||
state->failureOpacity.value() | rpl::on_next([=](float64 opacity) {
|
||||
label->setOpacity(opacity);
|
||||
}, label->lifetime());
|
||||
};
|
||||
|
||||
const auto controls = CreateChild<VerticalLayout>(canvas);
|
||||
controls->resizeToWidth(canvas->width());
|
||||
controls->move(0, state->shared->craftingBottom);
|
||||
|
||||
const auto title = controls->add(
|
||||
object_ptr<FlatLabel>(
|
||||
controls,
|
||||
tr::lng_gift_craft_failed_title(),
|
||||
st::uniqueGiftTitle),
|
||||
st::boxRowPadding + st::craftFailureTitleMargin,
|
||||
style::al_top);
|
||||
add(title);
|
||||
title->setTextColorOverride(QColor(0xF8, 0x4A, 0x4A));
|
||||
|
||||
const auto about = controls->add(
|
||||
object_ptr<FlatLabel>(
|
||||
controls,
|
||||
tr::lng_gift_craft_failed_about(
|
||||
lt_count,
|
||||
rpl::single(state->shared->lostGifts.size() * 1.),
|
||||
tr::rich),
|
||||
st::uniqueGiftSubtitle),
|
||||
st::boxRowPadding + st::craftFailureAboutMargin,
|
||||
style::al_top);
|
||||
add(about);
|
||||
about->setTextColorOverride(QColor(0xFF, 0xBC, 0x9B));
|
||||
|
||||
controls->showOn(state->failureOpacity.value(
|
||||
) | rpl::map(
|
||||
rpl::mappers::_1 > 0.
|
||||
) | rpl::distinct_until_changed());
|
||||
}
|
||||
|
||||
void SetupCraftNewButton(
|
||||
not_null<CraftAnimationState*> state,
|
||||
not_null<RpWidget*> canvas) {
|
||||
const auto button = CreateChild<GradientButton>(
|
||||
canvas,
|
||||
QGradientStops{
|
||||
{ 0., QColor(0xE2, 0x75, 0x19) },
|
||||
{ 1., QColor(0xDD, 0x48, 0x19) },
|
||||
});
|
||||
button->setFullRadius(true);
|
||||
button->setClickedCallback([=] {
|
||||
state->retryWithNewGift();
|
||||
});
|
||||
|
||||
const auto buttonLabel = CreateChild<FlatLabel>(
|
||||
button,
|
||||
tr::lng_gift_craft_new_button(),
|
||||
st::creditsBoxButtonLabel);
|
||||
buttonLabel->setTextColorOverride(st::white->c);
|
||||
buttonLabel->setAttribute(
|
||||
Qt::WA_TransparentForMouseEvents);
|
||||
button->sizeValue() | rpl::on_next([=](QSize size) {
|
||||
buttonLabel->moveToLeft(
|
||||
(size.width() - buttonLabel->width()) / 2,
|
||||
st::giftBox.button.textTop);
|
||||
}, buttonLabel->lifetime());
|
||||
|
||||
const auto buttonWidth = canvas->width()
|
||||
- st::craftBoxButtonPadding.left()
|
||||
- st::craftBoxButtonPadding.right();
|
||||
const auto buttonHeight = st::giftBox.button.height;
|
||||
button->setNaturalWidth(buttonWidth);
|
||||
button->setGeometry(
|
||||
st::craftBoxButtonPadding.left(),
|
||||
state->shared->originalButtonY,
|
||||
buttonWidth,
|
||||
buttonHeight);
|
||||
button->show();
|
||||
|
||||
button->shownValue() | rpl::filter(
|
||||
rpl::mappers::_1
|
||||
) | rpl::take(1) | rpl::on_next([=] {
|
||||
button->startGlareAnimation();
|
||||
}, button->lifetime());
|
||||
}
|
||||
|
||||
void StartCraftAnimation(
|
||||
not_null<GenericBox*> box,
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
std::shared_ptr<CraftState> shared,
|
||||
Fn<void(CraftResultCallback)> startRequest,
|
||||
Fn<void()> closeParent) {
|
||||
Fn<void()> closeParent,
|
||||
Fn<void(Fn<void()> closeCurrent)> retryWithNewGift) {
|
||||
const auto container = box->verticalLayout();
|
||||
const auto closeOnFail = crl::guard(box, [=] {
|
||||
if (const auto onstack = closeParent) {
|
||||
onstack();
|
||||
}
|
||||
box->closeBox();
|
||||
show->showToast("failed");// tr::lng_gift_craft_failed(tr::now));
|
||||
});
|
||||
|
||||
while (container->count() > 0) {
|
||||
delete container->widgetAt(0);
|
||||
}
|
||||
|
||||
auto canvas = object_ptr<RpWidget>(container);
|
||||
const auto raw = canvas.data();
|
||||
const auto state = raw->lifetime().make_state<CraftAnimationState>();
|
||||
|
||||
state->retryWithNewGift = [=] {
|
||||
retryWithNewGift(crl::guard(box, [=] { box->closeBox(); }));
|
||||
};
|
||||
|
||||
const auto title = CreateChild<FlatLabel>(
|
||||
raw,
|
||||
tr::lng_gift_craft_title(),
|
||||
st::uniqueGiftTitle);
|
||||
title->naturalWidthValue() | rpl::on_next([=](int titleWidth) {
|
||||
const auto width = st::boxWideWidth;
|
||||
title->moveToLeft(
|
||||
(width - titleWidth) / 2,
|
||||
st::craftTitleMargin.top(),
|
||||
width);
|
||||
}, title->lifetime());
|
||||
title->setTextColorOverride(st::white->c);
|
||||
|
||||
const auto height = shared->containerHeight;
|
||||
const auto craftingHeight = shared->craftingBottom - shared->craftingTop;
|
||||
state->shared = std::move(shared);
|
||||
@@ -1319,6 +1525,38 @@ void StartCraftAnimation(
|
||||
[=] { raw->update(); },
|
||||
st::craftForgeLoading);
|
||||
|
||||
SetupProgressControls(state, raw);
|
||||
SetupFailureControls(state, raw);
|
||||
|
||||
state->progressShown.value(
|
||||
) | rpl::combine_previous(
|
||||
) | rpl::on_next([=](bool wasShown, bool nowShown) {
|
||||
if (wasShown == nowShown) {
|
||||
return;
|
||||
}
|
||||
const auto from = wasShown ? 1. : 0.;
|
||||
const auto to = nowShown ? 1. : 0.;
|
||||
state->progressFadeIn.start([=] {
|
||||
raw->update();
|
||||
}, from, to, kProgressFadeInDuration, anim::easeOutCubic);
|
||||
}, raw->lifetime());
|
||||
|
||||
state->failureShown.value(
|
||||
) | rpl::combine_previous(
|
||||
) | rpl::on_next([=](bool wasShown, bool nowShown) {
|
||||
if (wasShown == nowShown) {
|
||||
return;
|
||||
}
|
||||
const auto from = wasShown ? 1. : 0.;
|
||||
const auto to = nowShown ? 1. : 0.;
|
||||
state->failureFadeIn.start([=] {
|
||||
raw->update();
|
||||
if (!state->failureFadeIn.animating()) {
|
||||
SetupCraftNewButton(state, raw);
|
||||
}
|
||||
}, from, to, kFailureFadeDuration, anim::easeOutCubic);
|
||||
}, raw->lifetime());
|
||||
|
||||
raw->paintOn([=](QPainter &p) {
|
||||
const auto shared = state->shared.get();
|
||||
const auto success = state->successAnimation.get();
|
||||
@@ -1347,27 +1585,31 @@ void StartCraftAnimation(
|
||||
}
|
||||
}
|
||||
|
||||
shared->craftingOffsetY = (shared->containerHeight / 2.)
|
||||
- shared->craftingAreaCenterY;
|
||||
if (slideProgress < 1.) {
|
||||
shared->craftingOffsetY *= slideProgress;
|
||||
PaintSlideOutPhase(p, shared, craftingSize, slideProgress);
|
||||
} else {
|
||||
if (success) {
|
||||
const auto target = success->coverShift;
|
||||
shared->craftingOffsetY -= target * success->expanded;
|
||||
const auto craftingOffsetY = success
|
||||
? (-success->coverShift * success->expanded)
|
||||
: 0.;
|
||||
if (success && success->expanded > 0.) {
|
||||
const auto width = st::boxWideWidth;
|
||||
const auto top = st::craftTitleMargin.top();
|
||||
title->moveToLeft(
|
||||
(width - title->naturalWidth()) / 2,
|
||||
top - (success->expanded * (top + title->height())),
|
||||
width);
|
||||
}
|
||||
const auto cubeSize = float64(shared->forgeRect.width());
|
||||
const auto cubeCenter = QPointF(shared->forgeRect.topLeft())
|
||||
+ QPointF(cubeSize, cubeSize) / 2.
|
||||
+ QPointF(0, shared->craftingOffsetY);
|
||||
+ QPointF(0, craftingOffsetY);
|
||||
|
||||
for (auto i = 0; i < 4; ++i) {
|
||||
const auto &corner = shared->corners[i];
|
||||
if (corner.giftButton && state->giftToSide[i].face < 0) {
|
||||
const auto giftTopLeft = QPointF()
|
||||
+ QPointF(corner.originalRect.topLeft())
|
||||
+ QPointF(0, shared->craftingOffsetY);
|
||||
+ QPointF(0, craftingOffsetY);
|
||||
p.drawImage(giftTopLeft, corner.gift(0));
|
||||
}
|
||||
}
|
||||
@@ -1399,7 +1641,7 @@ void StartCraftAnimation(
|
||||
|
||||
if (state->flightTargetCorners) {
|
||||
const auto sourceRect = QRectF(corner.originalRect)
|
||||
.translated(0, shared->craftingOffsetY);
|
||||
.translated(0, craftingOffsetY);
|
||||
const auto sourceCorners = RectToCorners(sourceRect);
|
||||
const auto interpolatedCorners = InterpolateQuadCorners(
|
||||
sourceCorners,
|
||||
@@ -1416,13 +1658,26 @@ void StartCraftAnimation(
|
||||
cubeCenter,
|
||||
cubeSize,
|
||||
progress,
|
||||
shared->craftingOffsetY);
|
||||
craftingOffsetY);
|
||||
PaintFlyingGift(p, corner, position, progress);
|
||||
}
|
||||
}
|
||||
|
||||
PaintLoadingAnimation(p, state);
|
||||
|
||||
if (const auto opacity = state->failureOpacity.current()) {
|
||||
PaintFailureThumbnails(
|
||||
p,
|
||||
shared,
|
||||
craftingSize,
|
||||
opacity);
|
||||
}
|
||||
}
|
||||
|
||||
state->progressOpacity = state->progressFadeIn.value(
|
||||
state->progressShown.current() ? 1. : 0.);
|
||||
state->failureOpacity = state->failureFadeIn.value(
|
||||
state->failureShown.current() ? 1. : 0.);
|
||||
});
|
||||
|
||||
const auto startFlying = [=] {
|
||||
@@ -1472,6 +1727,8 @@ void StartCraftAnimation(
|
||||
}
|
||||
};
|
||||
|
||||
state->progressShown = true;
|
||||
|
||||
state->slideOutAnimation.start([=] {
|
||||
raw->update();
|
||||
|
||||
@@ -1597,9 +1854,10 @@ void StartCraftAnimation(
|
||||
raw->resizeToWidth(st::boxWideWidth);
|
||||
SendPendingMoveResizeEvents(raw);
|
||||
|
||||
const auto fullHeight = state->shared->containerHeight;
|
||||
success->coverHeight = raw->height();
|
||||
success->coverShift = (fullHeight - raw->height()) / 2;
|
||||
success->coverShift = state->shared->forgeRect.y()
|
||||
+ (state->shared->forgeRect.height() / 2)
|
||||
- (raw->height() / 2);
|
||||
}
|
||||
tryStartFlying();
|
||||
});
|
||||
|
||||
@@ -32,7 +32,9 @@ class GiftButton;
|
||||
|
||||
namespace Ui {
|
||||
|
||||
class FlatLabel;
|
||||
class GenericBox;
|
||||
class RpWidget;
|
||||
class VerticalLayout;
|
||||
class UniqueGiftCoverWidget;
|
||||
|
||||
@@ -104,7 +106,16 @@ struct CraftState {
|
||||
int craftingTop = 0;
|
||||
int craftingBottom = 0;
|
||||
int craftingAreaCenterY = 0;
|
||||
int craftingOffsetY = 0;
|
||||
int originalButtonY = 0;
|
||||
|
||||
QString giftName;
|
||||
int successPermille = 0;
|
||||
struct LostGift {
|
||||
QImage thumbnail;
|
||||
QString number;
|
||||
};
|
||||
std::vector<LostGift> lostGifts;
|
||||
QImage lostRadial;
|
||||
|
||||
void paint(
|
||||
QPainter &p,
|
||||
@@ -178,6 +189,16 @@ struct CraftAnimationState {
|
||||
|
||||
std::unique_ptr<CraftDoneAnimation> successAnimation;
|
||||
std::unique_ptr<CraftFailAnimation> failureAnimation;
|
||||
|
||||
rpl::variable<float64> progressOpacity;
|
||||
Animations::Simple progressFadeIn;
|
||||
rpl::variable<bool> progressShown;
|
||||
|
||||
rpl::variable<float64> failureOpacity;
|
||||
Animations::Simple failureFadeIn;
|
||||
rpl::variable<bool> failureShown;
|
||||
|
||||
Fn<void()> retryWithNewGift;
|
||||
};
|
||||
|
||||
void StartCraftAnimation(
|
||||
@@ -185,6 +206,7 @@ void StartCraftAnimation(
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
std::shared_ptr<CraftState> state,
|
||||
Fn<void(CraftResultCallback)> startRequest,
|
||||
Fn<void()> closeParent);
|
||||
Fn<void()> closeParent,
|
||||
Fn<void(Fn<void()> closeCurrent)> retryWithNewGift);
|
||||
|
||||
} // namespace Ui
|
||||
|
||||
@@ -9,11 +9,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "base/call_delayed.h"
|
||||
#include "base/random.h"
|
||||
#include "boxes/star_gift_auction_box.h"
|
||||
#include "boxes/star_gift_craft_animation.h"
|
||||
#include "boxes/star_gift_preview_box.h"
|
||||
#include "apiwrap.h"
|
||||
#include "api/api_credits.h"
|
||||
#include "api/api_premium.h"
|
||||
#include "boxes/star_gift_box.h"
|
||||
#include "data/components/gift_auctions.h"
|
||||
#include "core/ui_integration.h"
|
||||
#include "data/stickers/data_custom_emoji.h"
|
||||
#include "data/data_document.h"
|
||||
@@ -36,6 +39,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/ui_utility.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "styles/style_chat_helpers.h" // stickerPanDeleteIconFg
|
||||
#include "styles/style_credits.h"
|
||||
#include "styles/style_layers.h"
|
||||
@@ -104,6 +108,12 @@ struct GiftForCraft {
|
||||
const GiftForCraft &) = default;
|
||||
};
|
||||
|
||||
void ShowGiftCraftBoxInternal(
|
||||
not_null<Window::SessionController*> controller,
|
||||
std::vector<GiftForCraft> gifts,
|
||||
Fn<void()> closeParent,
|
||||
bool autoStartCraft);
|
||||
|
||||
struct CraftingView {
|
||||
object_ptr<RpWidget> widget;
|
||||
rpl::producer<int> editRequests;
|
||||
@@ -119,7 +129,8 @@ struct CraftingView {
|
||||
[[nodiscard]] not_null<RpWidget*> MakeRadialPercent(
|
||||
not_null<RpWidget*> parent,
|
||||
const style::CraftRadialPercent &st,
|
||||
rpl::producer<int> permille) {
|
||||
rpl::producer<int> permille,
|
||||
Fn<QString(int)> format = FormatPercent) {
|
||||
auto raw = CreateChild<RpWidget>(parent);
|
||||
|
||||
struct State {
|
||||
@@ -130,7 +141,7 @@ struct CraftingView {
|
||||
|
||||
Animations::Simple animation;
|
||||
NumbersAnimation numbers;
|
||||
int permille = 0;
|
||||
int permille = -1;
|
||||
};
|
||||
const auto state = raw->lifetime().make_state<State>(
|
||||
st,
|
||||
@@ -144,7 +155,7 @@ struct CraftingView {
|
||||
raw->update();
|
||||
}, state->permille, value, st::slideWrapDuration);
|
||||
state->permille = value;
|
||||
state->numbers.setText(FormatPercent(value), value);
|
||||
state->numbers.setText(format(value), value);
|
||||
}, raw->lifetime());
|
||||
state->animation.stop();
|
||||
state->numbers.finishAnimating();
|
||||
@@ -476,6 +487,7 @@ AbstractButton *MakeRemoveButton(
|
||||
craftState->forgePercent = GrabWidgetToImage(state->forgeRadial);
|
||||
|
||||
auto giftsTotal = 0;
|
||||
auto lostIndex = 0;
|
||||
for (auto i = 0; i != 4; ++i) {
|
||||
auto &entry = state->entries[i];
|
||||
auto &corner = craftState->corners[i];
|
||||
@@ -488,6 +500,11 @@ AbstractButton *MakeRemoveButton(
|
||||
if (entry.remove) {
|
||||
corner.removeButton = GrabWidgetToImage(entry.remove);
|
||||
}
|
||||
if (lostIndex < craftState->lostGifts.size()) {
|
||||
craftState->lostGifts[lostIndex].thumbnail
|
||||
= GrabWidgetToImage(entry.button);
|
||||
++lostIndex;
|
||||
}
|
||||
corner.giftButton.reset(entry.button);
|
||||
entry.button->setParent(parent);
|
||||
base::take(entry.button)->hide();
|
||||
@@ -499,6 +516,15 @@ AbstractButton *MakeRemoveButton(
|
||||
}
|
||||
}
|
||||
|
||||
const auto failedCount = MakeRadialPercent(
|
||||
raw,
|
||||
st::craftForgePercent,
|
||||
rpl::single(0),
|
||||
[=](int) { return QString::number(giftsTotal); });
|
||||
failedCount->setGeometry(state->forgeRadial->geometry());
|
||||
craftState->lostRadial = GrabWidgetToImage(failedCount);
|
||||
delete failedCount;
|
||||
|
||||
const auto overlayBg = craftState->forgeBgOverlay = ForgeBgOverlay();
|
||||
const auto backdrop = CraftBackdrops()[giftsTotal - 1].backdrop;
|
||||
craftState->forgeBg1 = anim::color(
|
||||
@@ -815,6 +841,7 @@ void ShowSelectGiftBox(
|
||||
|
||||
void Craft(
|
||||
not_null<GenericBox*> box,
|
||||
not_null<Window::SessionController*> controller,
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
std::shared_ptr<CraftState> state,
|
||||
const std::vector<GiftForCraft> &gifts,
|
||||
@@ -827,7 +854,8 @@ void Craft(
|
||||
const auto giftsCopy = gifts;
|
||||
|
||||
base::call_delayed(delay, box, [=] {
|
||||
const auto shouldSucceed = false;// (base::RandomIndex(5) != 0);
|
||||
//static auto testing = 0;
|
||||
const auto shouldSucceed = false;// (((++testing) / 4) % 2 == 0);
|
||||
const auto count = int(giftsCopy.size());
|
||||
if (shouldSucceed && count > 0) {
|
||||
const auto &chosen = giftsCopy[base::RandomIndex(count)];
|
||||
@@ -847,18 +875,135 @@ void Craft(
|
||||
}
|
||||
});
|
||||
};
|
||||
const auto session = &controller->session();
|
||||
const auto initialGiftId = gifts.front().unique->initialGiftId;
|
||||
auto retryWithNewGift = [=](Fn<void()> closeCurrent) {
|
||||
session->api().request(MTPpayments_GetCraftStarGifts(
|
||||
MTP_long(initialGiftId),
|
||||
MTP_string(),
|
||||
MTP_int(30)
|
||||
)).done(crl::guard(box, [=](const MTPpayments_SavedStarGifts &result) {
|
||||
const auto user = session->user();
|
||||
const auto owner = &session->data();
|
||||
const auto &data = result.data();
|
||||
owner->processUsers(data.vusers());
|
||||
owner->processChats(data.vchats());
|
||||
|
||||
auto list = std::vector<Data::SavedStarGift>();
|
||||
list.reserve(data.vgifts().v.size());
|
||||
for (const auto &gift : data.vgifts().v) {
|
||||
if (auto parsed = Api::FromTL(user, gift)) {
|
||||
list.push_back(std::move(*parsed));
|
||||
}
|
||||
}
|
||||
|
||||
ShowSelectGiftBox(controller, list, [=](GiftForCraft chosen) {
|
||||
ShowGiftCraftBoxInternal(
|
||||
controller,
|
||||
{ chosen },
|
||||
closeParent,
|
||||
false);
|
||||
closeCurrent();
|
||||
}, {});
|
||||
})).send();
|
||||
};
|
||||
StartCraftAnimation(
|
||||
box,
|
||||
std::move(show),
|
||||
std::move(state),
|
||||
std::move(startRequest),
|
||||
closeParent);
|
||||
closeParent,
|
||||
std::move(retryWithNewGift));
|
||||
}
|
||||
|
||||
void AddPreviewNewModels(
|
||||
not_null<VerticalLayout*> container,
|
||||
std::shared_ptr<Main::SessionShow> show,
|
||||
const QString &giftName,
|
||||
Data::UniqueGiftAttributes attributes) {
|
||||
auto exceptional = std::vector<Data::UniqueGiftModel>();
|
||||
for (const auto &model : attributes.models) {
|
||||
if (Data::UniqueGiftAttributeHasSpecialRarity(model)) {
|
||||
exceptional.push_back(model);
|
||||
}
|
||||
}
|
||||
attributes.models = std::move(exceptional);
|
||||
|
||||
auto emoji = TextWithEntities();
|
||||
const auto indices = RandomIndicesSubset(
|
||||
int(attributes.models.size()),
|
||||
std::min(3, int(attributes.models.size())));
|
||||
for (const auto index : indices) {
|
||||
emoji.append(Data::SingleCustomEmoji(
|
||||
attributes.models[index].document));
|
||||
}
|
||||
|
||||
auto badge = object_ptr<AbstractButton>(container);
|
||||
const auto badgeRaw = badge.data();
|
||||
|
||||
const auto label = CreateChild<FlatLabel>(
|
||||
badgeRaw,
|
||||
tr::lng_gift_craft_view_all(
|
||||
lt_emoji,
|
||||
rpl::single(emoji),
|
||||
lt_arrow,
|
||||
rpl::single(Ui::Text::IconEmoji(&st::textMoreIconEmoji)),
|
||||
Text::WithEntities),
|
||||
st::uniqueGiftResalePrice,
|
||||
st::defaultPopupMenu,
|
||||
Core::TextContext({ .session = &show->session() }));
|
||||
label->setTextColorOverride(st::white->c);
|
||||
|
||||
label->sizeValue() | rpl::on_next([=](QSize size) {
|
||||
const auto padding = st::craftPreviewAllPadding;
|
||||
badgeRaw->setNaturalWidth(
|
||||
padding.left() + size.width() + padding.right());
|
||||
badgeRaw->resize(
|
||||
badgeRaw->naturalWidth(),
|
||||
padding.top() + size.height() + padding.bottom());
|
||||
}, label->lifetime());
|
||||
|
||||
badgeRaw->widthValue() | rpl::on_next([=](int width) {
|
||||
const auto padding = st::craftPreviewAllPadding;
|
||||
label->move(padding.left(), padding.top());
|
||||
}, badgeRaw->lifetime());
|
||||
|
||||
badgeRaw->paintOn([=](QPainter &p) {
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
const auto rect = badgeRaw->rect();
|
||||
const auto radius = rect.height() / 2.;
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(ForgeBgOverlay());
|
||||
p.drawRoundedRect(rect, radius, radius);
|
||||
});
|
||||
|
||||
label->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
badgeRaw->setClickedCallback([=] {
|
||||
auto previewAttrs = attributes;
|
||||
previewAttrs.models.erase(
|
||||
ranges::remove_if(previewAttrs.models, [](const auto &m) {
|
||||
return !Data::UniqueGiftAttributeHasSpecialRarity(m);
|
||||
}),
|
||||
end(previewAttrs.models));
|
||||
show->show(Box(
|
||||
StarGiftPreviewBox,
|
||||
giftName,
|
||||
previewAttrs,
|
||||
Data::GiftAttributeIdType::Model,
|
||||
nullptr));
|
||||
});
|
||||
|
||||
container->add(
|
||||
std::move(badge),
|
||||
st::craftPreviewAllMargin,
|
||||
style::al_top);
|
||||
}
|
||||
|
||||
void MakeCraftContent(
|
||||
not_null<GenericBox*> box,
|
||||
not_null<Window::SessionController*> controller,
|
||||
std::vector<GiftForCraft> gifts,
|
||||
Data::UniqueGiftAttributes attributes,
|
||||
Fn<void()> closeParent,
|
||||
bool autoStartCraft) {
|
||||
Expects(!gifts.empty());
|
||||
@@ -902,6 +1047,7 @@ void MakeCraftContent(
|
||||
}
|
||||
}
|
||||
|
||||
const auto giftName = gifts.front().unique->title;
|
||||
state->chosen = std::move(gifts);
|
||||
for (auto i = 0; i != int(state->chosen.current().size()); ++i) {
|
||||
state->craftState->covers[i].shown = true;
|
||||
@@ -1030,8 +1176,15 @@ void MakeCraftContent(
|
||||
|
||||
state->attributes = raw->add(
|
||||
MakeRarityExpectancyPreview(raw, session, state->chosen.value()),
|
||||
st::craftAttributesMargin,
|
||||
style::al_top);
|
||||
|
||||
AddPreviewNewModels(
|
||||
raw,
|
||||
controller->uiShow(),
|
||||
giftName,
|
||||
std::move(attributes));
|
||||
|
||||
raw->paintOn([=](QPainter &p) {
|
||||
const auto &cs = state->craftState;
|
||||
if (cs->craftingStarted) {
|
||||
@@ -1050,7 +1203,7 @@ void MakeCraftContent(
|
||||
|
||||
const auto button = state->button = raw->add(
|
||||
object_ptr<GradientButton>(raw, QGradientStops()),
|
||||
st::giftBox.buttonPadding);
|
||||
st::craftBoxButtonPadding);
|
||||
button->setFullRadius(true);
|
||||
button->startGlareAnimation();
|
||||
|
||||
@@ -1061,9 +1214,17 @@ void MakeCraftContent(
|
||||
state->crafting = true;
|
||||
|
||||
const auto &cs = state->craftState;
|
||||
const auto &gifts = state->chosen.current();
|
||||
cs->giftName = state->name.current();
|
||||
cs->successPermille = state->successPercentPermille.current();
|
||||
|
||||
cs->topPart = GrabWidgetToImage(state->title);
|
||||
cs->topPartRect = state->title->geometry();
|
||||
for (const auto &gift : gifts) {
|
||||
if (gift.unique) {
|
||||
cs->lostGifts.push_back({
|
||||
.number = u"#%1"_q.arg(gift.unique->number),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (state->grabCraftingView) {
|
||||
state->grabCraftingView(cs);
|
||||
@@ -1080,8 +1241,9 @@ void MakeCraftContent(
|
||||
cs->craftingAreaCenterY = cs->forgeRect.center().y();
|
||||
|
||||
const auto aboutPos = state->about->pos();
|
||||
const auto buttonBottom = state->button->pos().y()
|
||||
+ state->button->height();
|
||||
const auto buttonY = state->button->pos().y();
|
||||
const auto buttonBottom = buttonY + state->button->height();
|
||||
cs->originalButtonY = buttonY;
|
||||
const auto bottomRect = QRect(
|
||||
0,
|
||||
aboutPos.y(),
|
||||
@@ -1109,6 +1271,7 @@ void MakeCraftContent(
|
||||
|
||||
Craft(
|
||||
box,
|
||||
controller,
|
||||
controller->uiShow(),
|
||||
state->craftState,
|
||||
state->chosen.current(),
|
||||
@@ -1131,19 +1294,20 @@ void MakeCraftContent(
|
||||
st::resaleButtonSubtitle);
|
||||
|
||||
raw->widthValue() | rpl::on_next([=](int width) {
|
||||
const auto padding = st::giftBox.buttonPadding;
|
||||
const auto padding = st::craftBoxButtonPadding;
|
||||
button->setNaturalWidth(width - padding.left() - padding.right());
|
||||
button->resize(button->naturalWidth(), st::giftBox.button.height);
|
||||
}, raw->lifetime());
|
||||
|
||||
if (autoStartCraft) {
|
||||
base::call_delayed(crl::time(1000), raw, startCrafting);
|
||||
// base::call_delayed(crl::time(1000), raw, startCrafting);
|
||||
}
|
||||
}
|
||||
|
||||
void ShowGiftCraftBoxInternal(
|
||||
void ShowGiftCraftBoxWithAttributes(
|
||||
not_null<Window::SessionController*> controller,
|
||||
std::vector<GiftForCraft> gifts,
|
||||
Data::UniqueGiftAttributes attributes,
|
||||
Fn<void()> closeParent,
|
||||
bool autoStartCraft) {
|
||||
controller->show(Box([=](not_null<GenericBox*> box) {
|
||||
@@ -1154,6 +1318,7 @@ void ShowGiftCraftBoxInternal(
|
||||
box,
|
||||
controller,
|
||||
gifts,
|
||||
attributes,
|
||||
closeParent,
|
||||
autoStartCraft);
|
||||
AddUniqueCloseButton(box);
|
||||
@@ -1177,6 +1342,33 @@ void ShowGiftCraftBoxInternal(
|
||||
}));
|
||||
}
|
||||
|
||||
void ShowGiftCraftBoxInternal(
|
||||
not_null<Window::SessionController*> controller,
|
||||
std::vector<GiftForCraft> gifts,
|
||||
Fn<void()> closeParent,
|
||||
bool autoStartCraft) {
|
||||
const auto giftId = gifts.front().unique->initialGiftId;
|
||||
const auto auctions = &controller->session().giftAuctions();
|
||||
if (auto attrs = auctions->attributes(giftId)) {
|
||||
ShowGiftCraftBoxWithAttributes(
|
||||
controller,
|
||||
std::move(gifts),
|
||||
std::move(*attrs),
|
||||
std::move(closeParent),
|
||||
autoStartCraft);
|
||||
} else {
|
||||
auctions->requestAttributes(giftId, crl::guard(controller, [=] {
|
||||
auto attrs = auctions->attributes(giftId);
|
||||
ShowGiftCraftBoxWithAttributes(
|
||||
controller,
|
||||
gifts,
|
||||
attrs ? std::move(*attrs) : Data::UniqueGiftAttributes{},
|
||||
closeParent,
|
||||
autoStartCraft);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void ShowGiftCraftInfoBox(
|
||||
@@ -1201,19 +1393,19 @@ void ShowGiftCraftInfoBox(
|
||||
|
||||
const auto features = std::vector<FeatureListEntry>{
|
||||
{
|
||||
st::menuIconTools,
|
||||
tr::lng_gift_craft_combine_title(tr::now),
|
||||
tr::lng_gift_craft_combine_about(tr::now, tr::rich),
|
||||
st::menuIconUnique,
|
||||
tr::lng_gift_craft_rare_title(tr::now),
|
||||
tr::lng_gift_craft_rare_about(tr::now, tr::rich),
|
||||
},
|
||||
{
|
||||
st::menuIconCraftTraits,
|
||||
tr::lng_gift_craft_input_title(tr::now),
|
||||
tr::lng_gift_craft_input_about(tr::now, tr::marked),
|
||||
tr::lng_gift_craft_chance_title(tr::now),
|
||||
tr::lng_gift_craft_chance_about(tr::now, tr::marked),
|
||||
},
|
||||
{
|
||||
st::menuIconUnique,
|
||||
tr::lng_gift_craft_exclusive_title(tr::now),
|
||||
tr::lng_gift_craft_exclusive_about(tr::now, tr::marked),
|
||||
st::menuIconCraftChance,
|
||||
tr::lng_gift_craft_affect_title(tr::now),
|
||||
tr::lng_gift_craft_affect_about(tr::now, tr::marked),
|
||||
},
|
||||
};
|
||||
for (const auto &feature : features) {
|
||||
|
||||
@@ -110,6 +110,20 @@ QString langDayOfMonthFull(const QDate &date) {
|
||||
});
|
||||
}
|
||||
|
||||
QString langDayOfMonthShort(const QDate &date) {
|
||||
auto day = date.day();
|
||||
return langDateMaybeWithYear(date, [&](int month, int year) {
|
||||
return QLocale().toString(date, QLocale::ShortFormat);
|
||||
}, [day](int month, int year) {
|
||||
return tr::lng_month_day(
|
||||
tr::now,
|
||||
lt_month,
|
||||
MonthSmall(month)(tr::now),
|
||||
lt_day,
|
||||
QString::number(day));
|
||||
});
|
||||
}
|
||||
|
||||
QString langMonthOfYear(int month, int year) {
|
||||
return (month > 0 && month <= 12)
|
||||
? tr::lng_month_year(
|
||||
|
||||
@@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
[[nodiscard]] QString langDayOfMonth(const QDate &date);
|
||||
[[nodiscard]] QString langDayOfMonthFull(const QDate &date);
|
||||
[[nodiscard]] QString langDayOfMonthShort(const QDate &date);
|
||||
[[nodiscard]] QString langMonthOfYear(int month, int year);
|
||||
[[nodiscard]] QString langMonth(const QDate &date);
|
||||
[[nodiscard]] QString langMonthOfYearFull(int month, int year);
|
||||
|
||||
@@ -598,15 +598,32 @@ craftAbout: FlatLabel(defaultFlatLabel) {
|
||||
align: align(top);
|
||||
minWidth: 128px;
|
||||
}
|
||||
craftAboutMargin: margins(24px, 16px, 24px, 4px);
|
||||
craftAboutMargin: margins(24px, 12px, 24px, 4px);
|
||||
craftChance: FlatLabel(defaultFlatLabel) {
|
||||
style: semiboldTextStyle;
|
||||
textFg: attentionButtonFg;
|
||||
margin: margins(8px, 4px, 8px, 4px);
|
||||
}
|
||||
craftPattern: IconEmoji {
|
||||
icon: icon{{ "chat/filled_forge-36x36", windowFg }};
|
||||
padding: margins(0px, 0px, 0px, 0px);
|
||||
}
|
||||
craftAddIcon: icon {{ "settings/plus", windowFgActive }};
|
||||
craftPreviewPadding: margins(24px, 12px, 40px, 8px);
|
||||
craftPreviewPadding: margins(24px, 8px, 40px, 8px);
|
||||
craftBoxButtonPadding: margins(22px, 12px, 22px, 22px);
|
||||
craftAttributesMargin: margins(22px, 12px, 22px, 16px);
|
||||
craftPreviewAllPadding: margins(6px, 4px, 6px, 4px);
|
||||
craftPreviewAllMargin: margins(22px, 0px, 22px, 8px);
|
||||
craftProgressTitleMargin: margins(0px, 12px, 0px, 16px);
|
||||
craftProgressAboutMargin: margins(0px, 4px, 0px, 16px);
|
||||
craftProgressWarningMargin: margins(0px, 4px, 0px, 16px);
|
||||
craftProgressChanceMargin: margins(0px, 40px, 0px, 16px);
|
||||
craftFailureThumbsTop: 36px;
|
||||
craftFailureTitleMargin: margins(0px, 88px, 0px, 12px);
|
||||
craftFailureAboutMargin: margins(0px, 0px, 0px, 16px);
|
||||
|
||||
craftForge: icon{{ "chat/filled_forge-48x48", windowFg }};
|
||||
craftFail: icon{{ "chat/filled_forge_fail-48x48", windowFg }};
|
||||
craftFail: icon{{ "chat/filled_forge_fail-36x36", windowFg }};
|
||||
craftForgePadding: 12px;
|
||||
craftForgePercent: CraftRadialPercent {
|
||||
stroke: 4px;
|
||||
|
||||
@@ -200,6 +200,7 @@ menuIconTon: icon{{ "payments/ton_emoji-18x18", menuIconColor, point(3px, 2px) }
|
||||
menuIconReorder: icon{{ "menu/reorder-24x24", menuIconColor }};
|
||||
menuIconTools: icon{{ "menu/craft_tools-24x24", menuIconColor }};
|
||||
menuIconCraftTraits: icon{{ "menu/craft_random-24x24", menuIconColor }};
|
||||
menuIconCraftChance: icon{{ "menu/craft_chance-24x24", menuIconColor }};
|
||||
|
||||
menuIconTTLAny: icon {{ "menu/auto_delete_plain", menuIconColor }};
|
||||
menuIconTTLAnyTextPosition: point(11px, 22px);
|
||||
|
||||
@@ -1685,7 +1685,7 @@ SessionController::SessionController(
|
||||
#if _DEBUG // TEST: Auto-open craft box on startup
|
||||
constexpr auto kGiftsCount = 4;
|
||||
crl::on_main(this, [=] {
|
||||
if (rand() % 2 < 0) {
|
||||
if (rand() % 2 >= 0) {
|
||||
return;
|
||||
}
|
||||
const auto user = session->user();
|
||||
|
||||
Reference in New Issue
Block a user