Add sending gift preview.

This commit is contained in:
John Preston 2024-09-10 14:14:37 +04:00
parent a5be0a685a
commit 37f837dcb7
5 changed files with 616 additions and 39 deletions

View file

@ -1855,6 +1855,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_action_gift_received" = "{user} sent you a gift for {cost}";
"lng_action_gift_received_me" = "You sent to {user} a gift for {cost}";
"lng_action_gift_received_anonymous" = "Unknown user sent you a gift for {cost}";
"lng_action_gift_anonymous" = "Anonymous Gift";
"lng_action_gift_got_subtitle" = "Gift from {user}";
"lng_action_gift_got_stars_text" = "Display this gift on your page or convert it to {cost}.";
"lng_action_suggested_photo_me" = "You suggested this photo for {user}'s Telegram profile.";
"lng_action_suggested_photo" = "{user} suggests this photo for your Telegram profile.";
"lng_action_suggested_photo_button" = "View Photo";
@ -2991,6 +2994,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_gift_stars_limited" = "limited";
"lng_gift_stars_tabs_all" = "All Gifts";
"lng_gift_stars_tabs_limited" = "Limited";
"lng_gift_send_title" = "Send a Gift";
"lng_gift_send_message" = "Enter Message";
"lng_gift_send_anonymous" = "Hide My Name";
"lng_gift_send_anonymous_about" = "You can hide your name and message from visitors to {user}'s profile. {recipient} will still see your name and message.";
"lng_gift_send_button" = "Send a Gift for {cost}";
"lng_accounts_limit_title" = "Limit Reached";
"lng_accounts_limit1#one" = "You have reached the limit of **{count}** connected account.";

View file

@ -10,18 +10,30 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_credits.h"
#include "api/api_premium.h"
#include "boxes/peer_list_controllers.h"
#include "boxes/send_credits_box.h"
#include "chat_helpers/stickers_gift_box_pack.h"
#include "chat_helpers/stickers_lottie.h"
#include "data/data_peer.h"
#include "data/data_session.h"
#include "data/data_user.h"
#include "data/stickers/data_custom_emoji.h"
#include "history/admin_log/history_admin_log_item.h"
#include "history/view/media/history_view_sticker_player.h"
#include "history/view/media/history_view_media_generic.h"
#include "history/view/history_view_element.h"
#include "history/history.h"
#include "history/history_item.h"
#include "history/history_item_helpers.h"
#include "lang/lang_keys.h"
#include "main/session/session_show.h"
#include "main/main_session.h"
#include "settings/settings_credits_graphics.h"
#include "settings/settings_credits.h"
#include "settings/settings_premium.h"
#include "ui/chat/chat_style.h"
#include "ui/chat/chat_theme.h"
#include "ui/controls/userpic_button.h"
#include "ui/effects/path_shift_gradient.h"
#include "ui/effects/premium_graphics.h"
#include "ui/effects/premium_stars_colored.h"
#include "ui/effects/ripple_animation.h"
@ -31,8 +43,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/text/format_values.h"
#include "ui/text/text_utilities.h"
#include "ui/vertical_list.h"
#include "ui/widgets/fields/input_field.h"
#include "ui/widgets/label_with_custom_emoji.h"
#include "ui/widgets/shadow.h"
#include "window/themes/window_theme.h"
#include "window/section_widget.h"
#include "window/window_session_controller.h"
#include "styles/style_boxes.h"
#include "styles/style_channel_earn.h"
@ -41,9 +56,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_giveaway.h"
#include "styles/style_layers.h"
#include "styles/style_premium.h"
#include "styles/style_settings.h"
#include "data/stickers/data_stickers.h"
#include "data/data_document.h"
#include "data/data_document_media.h"
namespace Ui {
namespace {
@ -51,6 +68,9 @@ namespace {
constexpr auto kPriceTabAll = 0;
constexpr auto kPriceTabLimited = -1;
constexpr auto kGiftsPerRow = 3;
constexpr auto kGiftMessageLimit = 256;
using namespace HistoryView;
struct GiftTypePremium {
int64 cost = 0;
@ -81,6 +101,259 @@ struct GiftDescriptor : std::variant<GiftTypePremium, GiftTypeStars> {
const GiftDescriptor&) = default;
};
struct GiftDetails {
GiftDescriptor descriptor;
QString text;
bool anonymous = false;
};
class PreviewDelegate final : public DefaultElementDelegate {
public:
PreviewDelegate(
not_null<QWidget*> parent,
not_null<Ui::ChatStyle*> st,
Fn<void()> update);
bool elementAnimationsPaused() override;
not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override;
Context elementContext() override;
private:
const not_null<QWidget*> _parent;
const std::unique_ptr<Ui::PathShiftGradient> _pathGradient;
};
class PreviewWrap final : public Ui::RpWidget {
public:
PreviewWrap(
not_null<QWidget*> parent,
not_null<Main::Session*> session,
rpl::producer<GiftDetails> details);
~PreviewWrap();
private:
void paintEvent(QPaintEvent *e) override;
void resizeTo(int width);
void prepare(rpl::producer<GiftDetails> details);
const not_null<History*> _history;
const std::unique_ptr<Ui::ChatTheme> _theme;
const std::unique_ptr<Ui::ChatStyle> _style;
const std::unique_ptr<PreviewDelegate> _delegate;
AdminLog::OwnedItem _item;
QPoint _position;
};
PreviewDelegate::PreviewDelegate(
not_null<QWidget*> parent,
not_null<Ui::ChatStyle*> st,
Fn<void()> update)
: _parent(parent)
, _pathGradient(MakePathShiftGradient(st, update)) {
}
bool PreviewDelegate::elementAnimationsPaused() {
return _parent->window()->isActiveWindow();
}
auto PreviewDelegate::elementPathShiftGradient()
-> not_null<Ui::PathShiftGradient*> {
return _pathGradient.get();
}
Context PreviewDelegate::elementContext() {
return Context::History;
}
auto GenerateGiftMedia(
not_null<Element*> parent,
Element *replacing,
const GiftDetails &data)
-> Fn<void(Fn<void(std::unique_ptr<MediaGenericPart>)>)> {
return [=](Fn<void(std::unique_ptr<MediaGenericPart>)> push) {
const auto &descriptor = data.descriptor;
auto pushText = [&](
TextWithEntities text,
QMargins margins = {},
const base::flat_map<uint16, ClickHandlerPtr> &links = {}) {
if (text.empty()) {
return;
}
push(std::make_unique<MediaGenericTextPart>(
std::move(text),
margins,
links));
};
const auto sticker = [=] {
using Tag = ChatHelpers::StickerLottieSize;
const auto &session = parent->history()->session();
auto &packs = session.giftBoxStickersPacks();
packs.load();
auto sticker = v::match(descriptor, [&](GiftTypePremium data) {
return packs.lookup(data.months);
}, [&](GiftTypeStars data) {
return data.document
? data.document
: packs.lookup(packs.monthsForStars(data.stars));
});
return StickerInBubblePart::Data{
.sticker = sticker,
.size = st::chatIntroStickerSize,
.cacheTag = Tag::ChatIntroHelloSticker,
.singleTimePlayback = v::is<GiftTypePremium>(descriptor),
};
};
push(std::make_unique<StickerInBubblePart>(
parent,
replacing,
sticker,
st::giftBoxPreviewStickerPadding));
const auto title = data.anonymous
? tr::lng_action_gift_anonymous(tr::now)
: tr::lng_action_gift_got_subtitle(
tr::now,
lt_user,
parent->data()->history()->session().user()->shortName());
auto textFallback = v::match(descriptor, [&](GiftTypePremium data) {
return TextWithEntities{
u"Use all those premium features with joy!"_q
};
}, [&](GiftTypeStars data) {
return tr::lng_action_gift_got_stars_text(
tr::now,
lt_cost,
tr::lng_gift_stars_title(
tr::now,
lt_count,
data.stars,
Ui::Text::Bold),
Ui::Text::WithEntities);
});
auto description = data.text.isEmpty()
? std::move(textFallback)
: TextWithEntities{ data.text };
pushText(Ui::Text::Bold(title), st::giftBoxPreviewTitlePadding);
pushText(std::move(description), st::giftBoxPreviewTextPadding);
};
}
PreviewWrap::PreviewWrap(
not_null<QWidget*> parent,
not_null<Main::Session*> session,
rpl::producer<GiftDetails> details)
: RpWidget(parent)
, _history(session->data().history(session->userPeerId()))
, _theme(Window::Theme::DefaultChatThemeOn(lifetime()))
, _style(std::make_unique<Ui::ChatStyle>(
_history->session().colorIndicesValue()))
, _delegate(std::make_unique<PreviewDelegate>(
parent,
_style.get(),
[=] { update(); }))
, _position(0, st::msgMargin.bottom()) {
_style->apply(_theme.get());
using namespace HistoryView;
session->data().viewRepaintRequest(
) | rpl::start_with_next([=](not_null<const Element*> view) {
if (view == _item.get()) {
update();
}
}, lifetime());
session->downloaderTaskFinished() | rpl::start_with_next([=] {
update();
}, lifetime());
prepare(std::move(details));
}
PreviewWrap::~PreviewWrap() {
_item = {};
}
void PreviewWrap::prepare(rpl::producer<GiftDetails> details) {
std::move(details) | rpl::start_with_next([=](GiftDetails details) {
const auto &descriptor = details.descriptor;
const auto cost = v::match(descriptor, [&](GiftTypePremium data) {
return FillAmountAndCurrency(data.cost, data.currency, true);
}, [&](GiftTypeStars data) {
return tr::lng_gift_stars_title(tr::now, lt_count, data.stars);
});
const auto text = details.anonymous
? tr::lng_action_gift_received_anonymous(tr::now, lt_cost, cost)
: tr::lng_action_gift_received(
tr::now,
lt_user,
_history->session().user()->shortName(),
lt_cost,
cost);
const auto item = _history->makeMessage({
.id = _history->nextNonHistoryEntryId(),
.flags = (MessageFlag::FakeAboutView
| MessageFlag::FakeHistoryItem
| MessageFlag::Local),
.from = _history->peer->id,
}, PreparedServiceText{ { text } });
auto owned = AdminLog::OwnedItem(_delegate.get(), item);
owned->overrideMedia(std::make_unique<MediaGeneric>(
owned.get(),
GenerateGiftMedia(owned.get(), _item.get(), details),
MediaGenericDescriptor{
.maxWidth = st::chatIntroWidth,
.service = true,
}));
_item = std::move(owned);
if (width() >= st::msgMinWidth) {
resizeTo(width());
}
update();
}, lifetime());
widthValue(
) | rpl::filter([=](int width) {
return width >= st::msgMinWidth;
}) | rpl::start_with_next([=](int width) {
resizeTo(width);
}, lifetime());
}
void PreviewWrap::resizeTo(int width) {
const auto height = _position.y()
+ _item->resizeGetHeight(width)
+ _position.y()
+ st::msgServiceMargin.top()
+ st::msgServiceGiftBoxTopSkip
- st::msgServiceMargin.bottom();
resize(width, height);
}
void PreviewWrap::paintEvent(QPaintEvent *e) {
auto p = Painter(this);
const auto clip = e->rect();
if (!clip.isEmpty()) {
p.setClipRect(clip);
Window::SectionWidget::PaintBackground(
p,
_theme.get(),
QSize(width(), window()->height()),
clip);
}
auto context = _theme->preparePaintContext(
_style.get(),
rect(),
e->rect(),
!window()->isActiveWindow());
p.translate(_position);
_item->draw(p, context);
}
[[nodiscard]] rpl::producer<std::vector<GiftTypePremium>> GiftsPremium(
not_null<Main::Session*> session,
not_null<PeerData*> peer) {
@ -389,6 +662,8 @@ public:
[[nodiscard]] virtual std::any textContext() = 0;
[[nodiscard]] virtual QSize buttonSize() = 0;
[[nodiscard]] virtual QImage background() = 0;
[[nodiscard]] virtual DocumentData *lookupSticker(
const GiftDescriptor &descriptor) = 0;
};
class GiftButton final : public AbstractButton {
@ -402,13 +677,17 @@ public:
private:
void paintEvent(QPaintEvent *e) override;
void setDocument(not_null<DocumentData*> document);
const not_null<GiftButtonDelegate*> _delegate;
GiftDescriptor _descriptor;
Text::String _text;
Text::String _price;
QRect _button;
QMargins _extend;
std::unique_ptr<HistoryView::StickerPlayer> _player;
std::unique_ptr<StickerPlayer> _player;
rpl::lifetime _mediaLifetime;
};
@ -423,6 +702,8 @@ void GiftButton::setDescriptor(const GiftDescriptor &descriptor) {
if (_descriptor == descriptor) {
return;
}
auto player = base::take(_player);
_mediaLifetime.destroy();
_descriptor = descriptor;
v::match(descriptor, [&](const GiftTypePremium &data) {
const auto months = data.months;
@ -449,6 +730,10 @@ void GiftButton::setDescriptor(const GiftDescriptor &descriptor) {
kMarkupTextOptions,
_delegate->textContext());
});
if (const auto document = _delegate->lookupSticker(descriptor)) {
setDocument(document);
}
const auto buttonw = _price.maxWidth();
const auto buttonh = st::semiboldFont->height;
const auto inner = QRect(
@ -464,6 +749,44 @@ void GiftButton::setDescriptor(const GiftDescriptor &descriptor) {
_button = QRect(skipx, skipy, outer, inner.height());
}
void GiftButton::setDocument(not_null<DocumentData*> document) {
const auto media = document->createMediaView();
media->checkStickerLarge();
media->goodThumbnailWanted();
rpl::single() | rpl::then(
document->owner().session().downloaderTaskFinished()
) | rpl::filter([=] {
return media->loaded();
}) | rpl::start_with_next([=] {
_mediaLifetime.destroy();
auto result = std::unique_ptr<StickerPlayer>();
const auto sticker = document->sticker();
if (sticker->isLottie()) {
result = std::make_unique<HistoryView::LottiePlayer>(
ChatHelpers::LottiePlayerFromDocument(
media.get(),
ChatHelpers::StickerLottieSize::InlineResults,
st::giftBoxStickerSize,
Lottie::Quality::High));
} else if (sticker->isWebm()) {
result = std::make_unique<HistoryView::WebmPlayer>(
media->owner()->location(),
media->bytes(),
st::giftBoxStickerSize);
} else {
result = std::make_unique<HistoryView::StaticStickerPlayer>(
media->owner()->location(),
media->bytes(),
st::giftBoxStickerSize);
}
result->setRepaintCallback([=] { update(); });
_player = std::move(result);
update();
}, _mediaLifetime);
}
void GiftButton::setGeometry(QRect inner, QMargins extend) {
_extend = extend;
AbstractButton::setGeometry(inner.marginsAdded(extend));
@ -474,6 +797,30 @@ void GiftButton::paintEvent(QPaintEvent *e) {
const auto position = QPoint(_extend.left(), _extend.top());
p.drawImage(0, 0, _delegate->background());
if (_player && _player->ready()) {
const auto paused = !isOver();
auto info = _player->frame(
st::giftBoxStickerSize,
QColor(0, 0, 0, 0),
false,
crl::now(),
paused);
const auto finished = (info.index + 1 == _player->framesCount());
if (!finished || !paused) {
_player->markFrameShown();
}
const auto size = info.image.size() / style::DevicePixelRatio();
p.drawImage(
QRect(
(width() - size.width()) / 2,
(_text.isEmpty()
? st::giftBoxStickerStarTop
: st::giftBoxStickerTop),
size.width(),
size.height()),
info.image);
}
auto hq = PainterHighQualityEnabler(p);
const auto premium = v::is<GiftTypePremium>(_descriptor);
const auto singlew = _delegate->buttonSize().width();
@ -481,12 +828,12 @@ void GiftButton::paintEvent(QPaintEvent *e) {
v::match(_descriptor, [&](const GiftTypePremium &data) {
if (data.discountPercent > 0) {
p.setPen(st::attentionBoxButton.textFg);
p.drawText(QRect(position, QSize(singlew, st::normalFont->height * 2)), '-' + QString::number(data.discountPercent) + '%', style::al_center);
p.drawText(QRect(position, QSize(singlew, st::normalFont->height * 2)), '-' + QString::number(data.discountPercent) + '%', style::al_topright);
}
}, [&](const GiftTypeStars &data) {
if (data.limited) {
p.setPen(st::windowActiveTextFg);
p.drawText(QRect(position, QSize(singlew, st::normalFont->height * 2)), u"limited"_q, style::al_center);
p.drawText(QRect(position, QSize(singlew, st::normalFont->height * 2)), u"limited"_q, style::al_topright);
}
});
p.setBrush(premium ? st::lightButtonBgOver : st::creditsBg3);
@ -520,6 +867,124 @@ void GiftButton::paintEvent(QPaintEvent *e) {
});
}
[[nodiscard]] not_null<Ui::InputField*> AddPartInput(
not_null<Ui::VerticalLayout*> container,
rpl::producer<QString> placeholder,
QString current,
int limit) {
const auto field = container->add(
object_ptr<Ui::InputField>(
container,
st::giftBoxTextField,
Ui::InputField::Mode::NoNewlines,
std::move(placeholder),
current),
st::giftBoxTextPadding);
field->setMaxLength(limit);
Ui::AddLengthLimitLabel(field, limit);
return field;
}
void SendGiftBox(
not_null<Ui::GenericBox*> box,
not_null<Window::SessionController*> window,
not_null<PeerData*> peer,
const GiftDescriptor &descriptor) {
box->setStyle(st::giftBox);
box->setWidth(st::boxWideWidth);
box->setTitle(tr::lng_gift_send_title());
box->addTopButton(st::boxTitleClose, [=] {
box->closeBox();
});
const auto session = &window->session();
const auto context = Core::MarkedTextContext{
.session = session,
.customEmojiRepaint = [] {},
};
auto cost = rpl::single([&] {
return v::match(descriptor, [&](const GiftTypePremium &data) {
if (data.currency == Ui::kCreditsCurrency) {
return Ui::CreditsEmojiSmall(session).append(
Lang::FormatCountDecimal(std::abs(data.cost)));
}
return TextWithEntities{
FillAmountAndCurrency(data.cost, data.currency),
};
}, [&](const GiftTypeStars &data) {
return Ui::CreditsEmojiSmall(session).append(
Lang::FormatCountDecimal(std::abs(data.stars)));
});
}());
const auto button = box->addButton(rpl::single(QString()), [=] {
box->closeBox();
});
SetButtonMarkedLabel(
button,
tr::lng_gift_send_button(
lt_cost,
std::move(cost),
Ui::Text::WithEntities),
session,
st::creditsBoxButtonLabel,
st::giftBox.button.textFg->c);
struct State {
rpl::variable<GiftDetails> details;
};
const auto state = box->lifetime().make_state<State>();
state->details = GiftDetails{
.descriptor = descriptor,
};
const auto container = box->verticalLayout();
container->add(object_ptr<PreviewWrap>(
container,
session,
state->details.value()));
const auto text = AddPartInput(
container,
tr::lng_gift_send_message(),
QString(),
kGiftMessageLimit);
text->changes() | rpl::start_with_next([=] {
auto now = state->details.current();
now.text = text->getLastText();
state->details = std::move(now);
}, text->lifetime());
AddDivider(container);
AddSkip(container);
container->add(
object_ptr<Ui::SettingsButton>(
container,
tr::lng_gift_send_anonymous(),
st::settingsButtonNoIcon)
)->toggleOn(rpl::single(false))->toggledValue(
) | rpl::start_with_next([=](bool toggled) {
auto now = state->details.current();
now.anonymous = toggled;
state->details = std::move(now);
}, container->lifetime());
AddSkip(container);
AddDividerText(container, tr::lng_gift_send_anonymous_about(
lt_user,
rpl::single(peer->shortName()),
lt_recipient,
rpl::single(peer->shortName())));
const auto buttonWidth = st::boxWideWidth
- st::giftBox.buttonPadding.left()
- st::giftBox.buttonPadding.right();
button->resizeToWidth(buttonWidth);
button->widthValue() | rpl::start_with_next([=](int width) {
if (width != buttonWidth) {
button->resizeToWidth(buttonWidth);
}
}, button->lifetime());
}
[[nodiscard]] object_ptr<RpWidget> MakeGiftsList(
not_null<Window::SessionController*> window,
not_null<PeerData*> peer,
@ -563,6 +1028,19 @@ void GiftButton::paintEvent(QPaintEvent *e) {
QImage background() override {
return _bg;
}
DocumentData *lookupSticker(
const GiftDescriptor &descriptor) {
const auto &session = _window->session();
auto &packs = session.giftBoxStickersPacks();
packs.load();
return v::match(descriptor, [&](GiftTypePremium data) {
return packs.lookup(data.months);
}, [&](GiftTypeStars data) {
return data.document
? data.document
: packs.lookup(packs.monthsForStars(data.stars));
});
}
private:
const not_null<Window::SessionController*> _window;
@ -655,6 +1133,10 @@ void GiftButton::paintEvent(QPaintEvent *e) {
} else {
x += single.width() + st::giftBoxGiftSkip.x();
}
button->setClickedCallback([=] {
window->show(Box(SendGiftBox, window, peer, descriptor));
});
}
if (gifts.size() % kGiftsPerRow) {
y += padding.bottom() + single.height();

View file

@ -314,41 +314,22 @@ void SendCreditsBox(
AddChildToWidgetCenter(button.data(), loadingAnimation);
loadingAnimation->showOn(state->confirmButtonBusy.value());
}
{
auto buttonText = tr::lng_credits_box_out_confirm(
lt_count,
rpl::single(form->invoice.amount) | tr::to_count(),
lt_emoji,
rpl::single(CreditsEmojiSmall(session)),
Ui::Text::RichLangValue);
const auto buttonLabel = Ui::CreateChild<Ui::FlatLabel>(
button,
rpl::single(QString()),
st::creditsBoxButtonLabel);
std::move(
buttonText
) | rpl::start_with_next([=](const TextWithEntities &text) {
buttonLabel->setMarkedText(
text,
Core::MarkedTextContext{
.session = session,
.customEmojiRepaint = [=] { buttonLabel->update(); },
});
}, buttonLabel->lifetime());
buttonLabel->setTextColorOverride(
box->getDelegate()->style().button.textFg->c);
button->sizeValue(
) | rpl::start_with_next([=](const QSize &size) {
buttonLabel->moveToLeft(
(size.width() - buttonLabel->width()) / 2,
(size.height() - buttonLabel->height()) / 2);
}, buttonLabel->lifetime());
buttonLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
state->confirmButtonBusy.value(
) | rpl::start_with_next([=](bool busy) {
buttonLabel->setVisible(!busy);
}, buttonLabel->lifetime());
}
SetButtonMarkedLabel(
button,
rpl::combine(
tr::lng_credits_box_out_confirm(
lt_count,
rpl::single(form->invoice.amount) | tr::to_count(),
lt_emoji,
rpl::single(CreditsEmojiSmall(session)),
Ui::Text::RichLangValue),
state->confirmButtonBusy.value()
) | rpl::map([](TextWithEntities &&text, bool busy) {
return busy ? TextWithEntities() : std::move(text);
}),
session,
st::creditsBoxButtonLabel,
box->getDelegate()->style().button.textFg->c);
const auto buttonWidth = st::boxWidth
- rect::m::sum::h(st::giveawayGiftCodeBox.buttonPadding);
@ -408,4 +389,55 @@ TextWithEntities CreditsEmojiSmall(not_null<Main::Session*> session) {
QString(QChar(0x2B50)));
}
not_null<FlatLabel*> SetButtonMarkedLabel(
not_null<RpWidget*> button,
rpl::producer<TextWithEntities> text,
Fn<std::any(Fn<void()> update)> context,
const style::FlatLabel &st,
std::optional<QColor> textFg) {
const auto buttonLabel = Ui::CreateChild<Ui::FlatLabel>(
button,
rpl::single(QString()),
st);
rpl::duplicate(
text
) | rpl::filter([=](const TextWithEntities &text) {
return !text.text.isEmpty();
}) | rpl::start_with_next([=](const TextWithEntities &text) {
buttonLabel->setMarkedText(
text,
context([=] { buttonLabel->update(); }));
}, buttonLabel->lifetime());
if (textFg) {
buttonLabel->setTextColorOverride(textFg);
}
button->sizeValue(
) | rpl::start_with_next([=](const QSize &size) {
buttonLabel->moveToLeft(
(size.width() - buttonLabel->width()) / 2,
(size.height() - buttonLabel->height()) / 2);
}, buttonLabel->lifetime());
buttonLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
buttonLabel->showOn(std::move(
text
) | rpl::map([=](const TextWithEntities &text) {
return !text.text.isEmpty();
}));
return buttonLabel;
}
not_null<FlatLabel*> SetButtonMarkedLabel(
not_null<RpWidget*> button,
rpl::producer<TextWithEntities> text,
not_null<Main::Session*> session,
const style::FlatLabel &st,
std::optional<QColor> textFg) {
return SetButtonMarkedLabel(button, text, [=](Fn<void()> update) {
return Core::MarkedTextContext{
.session = session,
.customEmojiRepaint = update,
};
}, st, textFg);
}
} // namespace Ui

View file

@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class HistoryItem;
namespace style {
struct FlatLabel;
} // namespace style
namespace Main {
class Session;
} // namespace Main
@ -19,7 +23,9 @@ struct CreditsFormData;
namespace Ui {
class RpWidget;
class GenericBox;
class FlatLabel;
void SendCreditsBox(
not_null<Ui::GenericBox*> box,
@ -32,4 +38,18 @@ void SendCreditsBox(
[[nodiscard]] TextWithEntities CreditsEmojiSmall(
not_null<Main::Session*> session);
not_null<FlatLabel*> SetButtonMarkedLabel(
not_null<RpWidget*> button,
rpl::producer<TextWithEntities> text,
Fn<std::any(Fn<void()> update)> context,
const style::FlatLabel &st,
std::optional<QColor> textFg = {});
not_null<FlatLabel*> SetButtonMarkedLabel(
not_null<RpWidget*> button,
rpl::producer<TextWithEntities> text,
not_null<Main::Session*> session,
const style::FlatLabel &st,
std::optional<QColor> textFg = {});
} // namespace Ui

View file

@ -58,6 +58,15 @@ creditsGiftBox: Box(defaultBox) {
shadowIgnoreTopSkip: true;
}
giftBox: Box(defaultBox) {
buttonPadding: margins(22px, 11px, 22px, 22px);
buttonHeight: 42px;
button: RoundButton(defaultActiveButton) {
height: 42px;
textTop: 12px;
style: semiboldTextStyle;
}
}
giftBoxSubtitle: FlatLabel(defaultFlatLabel) {
style: TextStyle(defaultTextStyle) {
font: boxTitleFont;
@ -85,6 +94,32 @@ giftBoxGiftHeight: 164px;
giftBoxGiftRadius: 12px;
giftBoxPremiumIconSize: 64px;
giftBoxPremiumIconTop: 10px;
giftBoxPremiumTextTop: 56px;
giftBoxPremiumTextTop: 84px;
giftBoxButtonBottom: 12px;
giftBoxButtonPadding: margins(8px, 4px, 8px, 4px);
giftBoxPreviewStickerPadding: margins(10px, 12px, 10px, 16px);
giftBoxPreviewTitlePadding: margins(12px, 4px, 12px, 4px);
giftBoxPreviewTextPadding: margins(12px, 4px, 12px, 24px);
giftBoxStickerTop: 0px;
giftBoxStickerStarTop: 24px;
giftBoxStickerSize: size(80px, 80px);
giftBoxTextField: InputField(defaultInputField) {
textBg: transparent;
textMargins: margins(2px, 0px, 32px, 0px);
placeholderFg: placeholderFg;
placeholderFgActive: placeholderFgActive;
placeholderFgError: placeholderFgActive;
placeholderMargins: margins(2px, 0px, 2px, 0px);
placeholderScale: 0.;
placeholderFont: normalFont;
border: 0px;
borderActive: 0px;
heightMin: 20px;
heightMax: 48px;
style: defaultTextStyle;
}
giftBoxTextPadding: margins(20px, 15px, 20px, 11px);