From 49d5cf939c1cec920797bfffdebb11699f796b4b Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Tue, 7 Nov 2023 16:54:38 +0300 Subject: [PATCH] Added special fading-in top bar with title to giveaway box. --- .../info/boosts/create_giveaway_box.cpp | 209 +++++++++++++++--- .../info/boosts/giveaway/giveaway.style | 15 ++ 2 files changed, 189 insertions(+), 35 deletions(-) diff --git a/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp b/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp index 466584e81..915e74e7c 100644 --- a/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp +++ b/Telegram/SourceFiles/info/boosts/create_giveaway_box.cpp @@ -27,6 +27,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/effects/premium_graphics.h" #include "ui/effects/premium_top_bar.h" #include "ui/layers/generic_box.h" +#include "ui/painter.h" +#include "ui/rect.h" #include "ui/text/format_values.h" #include "ui/text/text_utilities.h" #include "ui/toast/toast.h" @@ -67,6 +69,159 @@ constexpr auto kDoneTooltipDuration = 5 * crl::time(1000); }; } +[[nodiscard]] QWidget *FindFirstShadowInBox(not_null box) { + for (const auto &child : box->children()) { + if (child && child->isWidgetType()) { + const auto w = static_cast(child); + if (w->height() == st::lineWidth) { + return w; + } + } + } + return nullptr; +} + +void AddPremiumTopBarWithDefaultTitleBar( + not_null box, + rpl::producer<> showFinished, + rpl::producer titleText) { + struct State final { + Ui::Animations::Simple animation; + Ui::Text::String title; + + Ui::RpWidget close; + }; + const auto state = box->lifetime().make_state(); + box->setNoContentMargin(true); + + std::move( + titleText + ) | rpl::start_with_next([=](const QString &s) { + state->title.setText(st::startGiveawayBox.title.style, s); + }, box->lifetime()); + + const auto hPadding = rect::m::sum::h(st::boxRowPadding); + const auto titlePaintContext = Ui::Text::PaintContext{ + .position = st::boxTitlePosition, + .outerWidth = (st::boxWideWidth - hPadding), + .availableWidth = (st::boxWideWidth - hPadding), + }; + + const auto isCloseBarShown = [=] { return box->scrollTop() > 0; }; + + const auto closeTopBar = box->setPinnedToTopContent( + object_ptr(box)); + closeTopBar->resize(box->width(), st::boxTitleHeight); + closeTopBar->paintRequest( + ) | rpl::start_with_next([=](const QRect &r) { + auto p = Painter(closeTopBar); + const auto radius = st::boxRadius; + const auto progress = state->animation.value(isCloseBarShown() + ? 1. + : 0.); + const auto resultRect = r + QMargins{ 0, 0, 0, radius }; + { + auto hq = PainterHighQualityEnabler(p); + + if (progress < 1.) { + auto path = QPainterPath(); + path.addRect(resultRect); + path.addRect( + st::boxRowPadding.left(), + 0, + resultRect.width() - hPadding, + resultRect.height()); + p.setClipPath(path); + PainterHighQualityEnabler hq(p); + p.setPen(Qt::NoPen); + p.setBrush(st::boxDividerBg); + p.drawRoundedRect(resultRect, radius, radius); + } + if (progress > 0.) { + p.setOpacity(progress); + + p.setClipping(false); + p.setPen(Qt::NoPen); + p.setBrush(st::boxBg); + p.drawRoundedRect(resultRect, radius, radius); + + p.setPen(st::startGiveawayBox.title.textFg); + p.setBrush(Qt::NoBrush); + state->title.draw(p, titlePaintContext); + } + } + }, closeTopBar->lifetime()); + + { + const auto close = Ui::CreateChild( + closeTopBar.get(), + st::startGiveawayBoxTitleClose); + close->setClickedCallback([=] { box->closeBox(); }); + closeTopBar->widthValue( + ) | rpl::start_with_next([=](int w) { + const auto &pos = st::giveawayGiftCodeCoverClosePosition; + close->moveToRight(pos.x(), pos.y()); + }, box->lifetime()); + close->show(); + } + + const auto bar = Ui::CreateChild( + box.get(), + st::startGiveawayCover, + nullptr, + tr::lng_giveaway_new_title(), + tr::lng_giveaway_new_about(Ui::Text::RichLangValue), + true); + bar->setAttribute(Qt::WA_TransparentForMouseEvents); + + box->addRow( + object_ptr( + box.get(), + st::giveawayGiftCodeTopHeight + - st::boxTitleHeight + + st::boxDividerHeight + + st::settingsSectionSkip, + st::boxDividerBg, + RectPart::Bottom), + {}); + bar->setPaused(true); + bar->setRoundEdges(false); + bar->setMaximumHeight(st::giveawayGiftCodeTopHeight); + bar->setMinimumHeight(st::infoLayerTopBarHeight); + bar->resize(bar->width(), bar->maximumHeight()); + box->widthValue( + ) | rpl::start_with_next([=](int w) { + bar->resizeToWidth(w - hPadding); + bar->moveToLeft(st::boxRowPadding.left(), bar->y()); + }, box->lifetime()); + + std::move( + showFinished + ) | rpl::take(1) | rpl::start_with_next([=] { + closeTopBar->raise(); + if (const auto shadow = FindFirstShadowInBox(box)) { + bar->stackUnder(shadow); + } + bar->setPaused(false); + box->scrolls( + ) | rpl::map(isCloseBarShown) | rpl::distinct_until_changed( + ) | rpl::start_with_next([=](bool showBar) { + state->animation.stop(); + state->animation.start( + [=] { closeTopBar->update(); }, + showBar ? 0. : 1., + showBar ? 1. : 0., + st::slideWrapDuration); + }, box->lifetime()); + box->scrolls( + ) | rpl::start_with_next([=] { + bar->moveToLeft(bar->x(), -box->scrollTop()); + }, box->lifetime()); + }, box->lifetime()); + + bar->show(); +} + } // namespace void CreateGiveawayBox( @@ -77,37 +232,6 @@ void CreateGiveawayBox( const auto weakWindow = base::make_weak(controller->parentController()); - const auto bar = box->verticalLayout()->add( - object_ptr( - box, - st::giveawayGiftCodeCover, - nullptr, - tr::lng_giveaway_new_title(), - tr::lng_giveaway_new_about(Ui::Text::RichLangValue), - true)); - { - bar->setPaused(true); - bar->setMaximumHeight(st::giveawayGiftCodeTopHeight); - bar->setMinimumHeight(st::infoLayerTopBarHeight); - bar->resize(bar->width(), bar->maximumHeight()); - - const auto container = box->verticalLayout(); - const auto &padding = st::giveawayGiftCodeCoverDividerPadding; - Settings::AddSkip(container, padding.top()); - Settings::AddDivider(container); - Settings::AddSkip(container, padding.bottom()); - - const auto close = Ui::CreateChild( - container.get(), - st::boxTitleClose); - close->setClickedCallback([=] { box->closeBox(); }); - box->widthValue( - ) | rpl::start_with_next([=](int) { - const auto &pos = st::giveawayGiftCodeCoverClosePosition; - close->moveToRight(pos.x(), pos.y()); - }, box->lifetime()); - } - using GiveawayType = Giveaway::GiveawayTypeRow::Type; using GiveawayGroup = Ui::RadioenumGroup; struct State final { @@ -132,6 +256,20 @@ void CreateGiveawayBox( const auto state = box->lifetime().make_state(peer); const auto typeGroup = std::make_shared(); + auto showFinished = Ui::BoxShowFinishes(box); + AddPremiumTopBarWithDefaultTitleBar( + box, + rpl::duplicate(showFinished), + rpl::conditional( + state->typeValue.value( + ) | rpl::map(rpl::mappers::_1 == GiveawayType::Random), + tr::lng_giveaway_start(), + tr::lng_giveaway_award())); + { + const auto &padding = st::giveawayGiftCodeCoverDividerPadding; + Settings::AddSkip(box->verticalLayout(), padding.bottom()); + } + const auto loading = box->addRow( object_ptr>( box, @@ -587,7 +725,7 @@ void CreateGiveawayBox( } { // TODO mini-icon. - const auto &stButton = st::premiumGiftBox; + const auto &stButton = st::startGiveawayBox; box->setStyle(stButton); auto button = object_ptr( box, @@ -691,11 +829,12 @@ void CreateGiveawayBox( } state->typeValue.force_assign(GiveawayType::Random); - box->setShowFinishedCallback([=] { + std::move( + showFinished + ) | rpl::take(1) | rpl::start_with_next([=] { if (!loading->toggled()) { return; } - bar->setPaused(false); state->lifetimeApi = state->apiOptions.request( ) | rpl::start_with_error_done([=](const QString &error) { }, [=] { @@ -706,5 +845,5 @@ void CreateGiveawayBox( contentWrap->toggle(true, anim::type::instant); contentWrap->resizeToWidth(box->width()); }); - }); + }, box->lifetime()); } diff --git a/Telegram/SourceFiles/info/boosts/giveaway/giveaway.style b/Telegram/SourceFiles/info/boosts/giveaway/giveaway.style index db9ad6f04..211087975 100644 --- a/Telegram/SourceFiles/info/boosts/giveaway/giveaway.style +++ b/Telegram/SourceFiles/info/boosts/giveaway/giveaway.style @@ -163,3 +163,18 @@ giveawayRefundedLabel: FlatLabel(boxLabel) { textFg: attentionButtonFg; } giveawayRefundedPadding: margins(8px, 10px, 8px, 10px); + +startGiveawayBox: Box(premiumGiftBox) { + shadowIgnoreTopSkip: true; +} +startGiveawayScrollArea: ScrollArea(boxScroll) { + deltax: 3px; + deltat: 50px; +} +startGiveawayBoxTitleClose: IconButton(boxTitleClose) { + ripple: universalRippleAnimation; +} +startGiveawayCover: PremiumCover(giveawayGiftCodeCover) { + bg: boxDividerBg; + additionalShadowForDarkThemes: false; +}