mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2026-07-25 23:12:10 +00:00
723 lines
19 KiB
C++
723 lines
19 KiB
C++
/*
|
|
This file is part of Telegram Desktop,
|
|
the official desktop application for the Telegram messaging service.
|
|
|
|
For license and copyright information please follow this link:
|
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|
*/
|
|
#include "settings/settings_common.h"
|
|
|
|
#include "base/timer.h"
|
|
#include "lottie/lottie_icon.h"
|
|
#include "ui/effects/animations.h"
|
|
#include "ui/effects/premium_graphics.h"
|
|
#include "ui/effects/premium_top_bar.h"
|
|
#include "ui/painter.h"
|
|
#include "ui/rect.h"
|
|
#include "ui/widgets/buttons.h"
|
|
#include "ui/widgets/continuous_sliders.h"
|
|
#include "ui/widgets/elastic_scroll.h"
|
|
#include "ui/widgets/labels.h"
|
|
#include "ui/widgets/scroll_area.h"
|
|
#include "ui/wrap/vertical_layout.h"
|
|
#include "styles/style_layers.h"
|
|
#include "styles/style_menu_icons.h"
|
|
#include "styles/style_settings.h"
|
|
#include "styles/style_widgets.h"
|
|
|
|
#include <QAction>
|
|
|
|
namespace Settings {
|
|
namespace {
|
|
|
|
[[nodiscard]] HighlightArgs MaybeFillFromRipple(
|
|
not_null<QWidget*> target,
|
|
HighlightArgs args) {
|
|
if (!args.rippleShape) {
|
|
return args;
|
|
}
|
|
const auto widget = target.get();
|
|
if (const auto icon = dynamic_cast<Ui::IconButton*>(widget)) {
|
|
const auto &st = icon->st();
|
|
args.shape = HighlightShape::Ellipse;
|
|
args.margin = QMargins(
|
|
st.rippleAreaPosition.x(),
|
|
st.rippleAreaPosition.y(),
|
|
st.width - st.rippleAreaPosition.x() - st.rippleAreaSize,
|
|
st.height - st.rippleAreaPosition.y() - st.rippleAreaSize);
|
|
} else if (const auto round = dynamic_cast<Ui::RoundButton*>(widget)) {
|
|
const auto &st = round->st();
|
|
args.shape = HighlightShape::Rect;
|
|
args.radius = st.radius ? st.radius : st::buttonRadius;
|
|
}
|
|
return args;
|
|
}
|
|
|
|
class HighlightOverlay final : public Ui::RpWidget {
|
|
public:
|
|
HighlightOverlay(not_null<QWidget*> target, HighlightArgs &&args);
|
|
|
|
protected:
|
|
bool eventFilter(QObject *o, QEvent *e) override;
|
|
|
|
private:
|
|
enum class Phase {
|
|
Wait,
|
|
FadeIn,
|
|
Shown,
|
|
FadeOut,
|
|
};
|
|
|
|
void updateGeometryFromTarget();
|
|
void updateZOrder();
|
|
void startFadeIn();
|
|
void startFadeOut();
|
|
void nextPhase();
|
|
void nextPhaseIn(crl::time delay);
|
|
void finish();
|
|
|
|
const QPointer<QWidget> _target;
|
|
const HighlightArgs _args;
|
|
const style::color &_color;
|
|
|
|
Ui::Animations::Simple _animation;
|
|
base::Timer _phaseTimer;
|
|
Phase _phase = Phase::Wait;
|
|
bool _finishing = false;
|
|
|
|
};
|
|
|
|
HighlightOverlay::HighlightOverlay(
|
|
not_null<QWidget*> target,
|
|
HighlightArgs &&args)
|
|
: RpWidget(target->parentWidget())
|
|
, _target(target.get())
|
|
, _args(MaybeFillFromRipple(target, std::move(args)))
|
|
, _color(_args.color ? *_args.color : st::windowBgActive)
|
|
, _phaseTimer([=] { nextPhase(); }) {
|
|
setAttribute(Qt::WA_TransparentForMouseEvents);
|
|
|
|
target->installEventFilter(this);
|
|
|
|
QObject::connect(
|
|
target.get(),
|
|
&QObject::destroyed,
|
|
this,
|
|
[this] { finish(); });
|
|
|
|
updateGeometryFromTarget();
|
|
updateZOrder();
|
|
|
|
paintRequest() | rpl::on_next([=](QRect clip) {
|
|
auto p = QPainter(this);
|
|
|
|
const auto progress = _animation.value(
|
|
(_phase == Phase::Wait || _phase == Phase::FadeOut) ? 0. : 1.);
|
|
const auto alpha = _args.opacity * progress;
|
|
|
|
auto color = _color->c;
|
|
color.setAlphaF(color.alphaF() * alpha);
|
|
|
|
p.setPen(Qt::NoPen);
|
|
p.setBrush(color);
|
|
|
|
const auto r = rect();
|
|
switch (_args.shape) {
|
|
case HighlightShape::Rect:
|
|
if (_args.radius > 0) {
|
|
PainterHighQualityEnabler hq(p);
|
|
p.drawRoundedRect(r, _args.radius, _args.radius);
|
|
} else {
|
|
p.drawRect(r);
|
|
}
|
|
break;
|
|
case HighlightShape::Ellipse:
|
|
PainterHighQualityEnabler hq(p);
|
|
p.drawEllipse(r);
|
|
break;
|
|
}
|
|
}, lifetime());
|
|
|
|
hide();
|
|
nextPhaseIn(_args.showDelay);
|
|
}
|
|
|
|
bool HighlightOverlay::eventFilter(QObject *o, QEvent *e) {
|
|
if (o != _target.data()) {
|
|
return false;
|
|
}
|
|
switch (e->type()) {
|
|
case QEvent::Move:
|
|
case QEvent::Resize:
|
|
updateGeometryFromTarget();
|
|
break;
|
|
case QEvent::ZOrderChange:
|
|
updateZOrder();
|
|
break;
|
|
case QEvent::Show:
|
|
case QEvent::Hide:
|
|
setVisible(!_target->isHidden());
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void HighlightOverlay::updateGeometryFromTarget() {
|
|
if (_target) {
|
|
setGeometry(_target->geometry().marginsRemoved(_args.margin));
|
|
}
|
|
}
|
|
|
|
void HighlightOverlay::updateZOrder() {
|
|
if (!_target) {
|
|
return;
|
|
}
|
|
if (_args.below) {
|
|
stackUnder(_target);
|
|
} else {
|
|
const auto parent = _target->parentWidget();
|
|
const auto siblings = parent ? parent->children() : QObjectList();
|
|
const auto it = ranges::find(siblings, _target.data());
|
|
const auto next = (it != siblings.end()) ? (it + 1) : it;
|
|
if (next != siblings.end()) {
|
|
if (const auto widget = qobject_cast<QWidget*>(*next)) {
|
|
stackUnder(widget);
|
|
return;
|
|
}
|
|
}
|
|
raise();
|
|
}
|
|
}
|
|
|
|
void HighlightOverlay::startFadeIn() {
|
|
show();
|
|
|
|
_phase = Phase::FadeIn;
|
|
_animation.start([=] {
|
|
update();
|
|
if (!_animation.animating()) {
|
|
_phase = Phase::Shown;
|
|
nextPhaseIn(_args.shownDuration);
|
|
}
|
|
}, 0., 1., _args.showDuration, anim::easeOutCubic);
|
|
}
|
|
|
|
void HighlightOverlay::startFadeOut() {
|
|
_phase = Phase::FadeOut;
|
|
_animation.start([=] {
|
|
update();
|
|
if (!_animation.animating()) {
|
|
finish();
|
|
}
|
|
}, 1., 0., _args.hideDuration, anim::easeInCubic);
|
|
}
|
|
|
|
void HighlightOverlay::nextPhase() {
|
|
switch (_phase) {
|
|
case Phase::Wait:
|
|
startFadeIn();
|
|
break;
|
|
case Phase::Shown:
|
|
startFadeOut();
|
|
break;
|
|
default:
|
|
Unexpected("Next phase called during Fade phase.");
|
|
}
|
|
}
|
|
|
|
void HighlightOverlay::nextPhaseIn(crl::time delay) {
|
|
if (!delay) {
|
|
_phaseTimer.cancel();
|
|
nextPhase();
|
|
} else {
|
|
_phaseTimer.callOnce(delay);
|
|
}
|
|
}
|
|
|
|
void HighlightOverlay::finish() {
|
|
if (_finishing) {
|
|
return;
|
|
}
|
|
_finishing = true;
|
|
_phaseTimer.cancel();
|
|
_animation.stop();
|
|
deleteLater();
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void HighlightWidget(QWidget *target, HighlightArgs &&args) {
|
|
if (!target) {
|
|
return;
|
|
}
|
|
if (args.scroll) {
|
|
ScrollToWidget(target);
|
|
}
|
|
new HighlightOverlay(target, std::move(args));
|
|
}
|
|
|
|
void ScrollToWidget(not_null<QWidget*> target) {
|
|
const auto scrollIn = [&](auto &&scroll) {
|
|
if (const auto inner = scroll->widget()) {
|
|
const auto globalPosition = target->mapToGlobal(QPoint(0, 0));
|
|
const auto localPosition = inner->mapFromGlobal(globalPosition);
|
|
const auto localTop = localPosition.y();
|
|
const auto targetHeight = target->height();
|
|
const auto scrollHeight = scroll->height();
|
|
const auto centered = localTop - (scrollHeight - targetHeight) / 2;
|
|
const auto top = std::clamp(centered, 0, scroll->scrollTopMax());
|
|
scroll->scrollToY(top);
|
|
}
|
|
};
|
|
for (auto parent = target->parentWidget()
|
|
; parent
|
|
; parent = parent->parentWidget()) {
|
|
if (const auto scroll = dynamic_cast<Ui::ScrollArea*>(parent)) {
|
|
scrollIn(scroll);
|
|
return;
|
|
}
|
|
if (const auto scroll = dynamic_cast<Ui::ElasticScroll*>(parent)) {
|
|
scrollIn(scroll);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
HighlightArgs SubsectionTitleHighlight() {
|
|
const auto radius = st::roundRadiusSmall;
|
|
return { .margin = { -radius, 0, -radius, 0 }, .radius = radius };
|
|
}
|
|
|
|
AbstractSection::AbstractSection(
|
|
QWidget *parent,
|
|
not_null<Window::SessionController*> controller)
|
|
: _controller(controller) {
|
|
}
|
|
|
|
void AbstractSection::build(
|
|
not_null<Ui::VerticalLayout*> container,
|
|
SectionBuildMethod method) {
|
|
method(
|
|
container,
|
|
_controller,
|
|
showOtherMethod(),
|
|
_showFinished.events());
|
|
}
|
|
|
|
Icon::Icon(IconDescriptor descriptor) : _icon(descriptor.icon) {
|
|
const auto background = [&]() -> const style::color* {
|
|
if (descriptor.type == IconType::Simple) {
|
|
return nullptr;
|
|
}
|
|
return descriptor.background;
|
|
}();
|
|
if (background) {
|
|
const auto radius = (descriptor.type == IconType::Rounded)
|
|
? st::settingsIconRadius
|
|
: (std::min(_icon->width(), _icon->height()) / 2);
|
|
_background.emplace(radius, *background);
|
|
} else if (const auto brush = descriptor.backgroundBrush) {
|
|
const auto radius = (descriptor.type == IconType::Rounded)
|
|
? st::settingsIconRadius
|
|
: (std::min(_icon->width(), _icon->height()) / 2);
|
|
_backgroundBrush.emplace(radius, std::move(*brush));
|
|
}
|
|
}
|
|
|
|
void Icon::paint(QPainter &p, QPoint position) const {
|
|
paint(p, position.x(), position.y());
|
|
}
|
|
|
|
void Icon::paint(QPainter &p, int x, int y) const {
|
|
if (_background) {
|
|
_background->paint(p, { { x, y }, _icon->size() });
|
|
} else if (_backgroundBrush) {
|
|
PainterHighQualityEnabler hq(p);
|
|
p.setPen(Qt::NoPen);
|
|
p.setBrush(_backgroundBrush->second);
|
|
p.drawRoundedRect(
|
|
QRect(QPoint(x, y), _icon->size()),
|
|
_backgroundBrush->first,
|
|
_backgroundBrush->first);
|
|
}
|
|
_icon->paint(p, { x, y }, 2 * x + _icon->width());
|
|
}
|
|
|
|
int Icon::width() const {
|
|
return _icon->width();
|
|
}
|
|
|
|
int Icon::height() const {
|
|
return _icon->height();
|
|
}
|
|
|
|
QSize Icon::size() const {
|
|
return _icon->size();
|
|
}
|
|
|
|
void AddButtonIcon(
|
|
not_null<Ui::AbstractButton*> button,
|
|
const style::SettingsButton &st,
|
|
IconDescriptor &&descriptor) {
|
|
Expects(descriptor.icon != nullptr);
|
|
|
|
struct IconWidget {
|
|
IconWidget(QWidget *parent, IconDescriptor &&descriptor)
|
|
: widget(parent)
|
|
, icon(std::move(descriptor)) {
|
|
}
|
|
Ui::RpWidget widget;
|
|
Icon icon;
|
|
};
|
|
const auto icon = button->lifetime().make_state<IconWidget>(
|
|
button,
|
|
std::move(descriptor));
|
|
icon->widget.setAttribute(Qt::WA_TransparentForMouseEvents);
|
|
icon->widget.resize(icon->icon.size());
|
|
icon->widget.show();
|
|
button->sizeValue(
|
|
) | rpl::on_next([=, left = st.iconLeft](QSize size) {
|
|
icon->widget.moveToLeft(
|
|
left,
|
|
(size.height() - icon->widget.height()) / 2,
|
|
size.width());
|
|
}, icon->widget.lifetime());
|
|
icon->widget.paintRequest(
|
|
) | rpl::on_next([=] {
|
|
auto p = QPainter(&icon->widget);
|
|
icon->icon.paint(p, 0, 0);
|
|
}, icon->widget.lifetime());
|
|
}
|
|
|
|
object_ptr<Button> CreateButtonWithIcon(
|
|
not_null<QWidget*> parent,
|
|
rpl::producer<QString> text,
|
|
const style::SettingsButton &st,
|
|
IconDescriptor &&descriptor) {
|
|
auto result = object_ptr<Button>(parent, std::move(text), st);
|
|
const auto button = result.data();
|
|
if (descriptor) {
|
|
AddButtonIcon(button, st, std::move(descriptor));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
not_null<Button*> AddButtonWithIcon(
|
|
not_null<Ui::VerticalLayout*> container,
|
|
rpl::producer<QString> text,
|
|
const style::SettingsButton &st,
|
|
IconDescriptor &&descriptor) {
|
|
return container->add(
|
|
CreateButtonWithIcon(
|
|
container,
|
|
std::move(text),
|
|
st,
|
|
std::move(descriptor)));
|
|
}
|
|
|
|
void CreateRightLabel(
|
|
not_null<Button*> button,
|
|
v::text::data &&label,
|
|
const style::SettingsButton &st,
|
|
rpl::producer<QString> buttonText,
|
|
Ui::Text::MarkedContext context) {
|
|
const auto name = Ui::CreateChild<Ui::FlatLabel>(
|
|
button.get(),
|
|
st.rightLabel);
|
|
name->show();
|
|
if (v::text::is_plain(label)) {
|
|
rpl::combine(
|
|
button->widthValue(),
|
|
std::move(buttonText),
|
|
v::text::take_plain(std::move(label))
|
|
) | rpl::on_next([=, &st](
|
|
int width,
|
|
const QString &button,
|
|
const QString &text) {
|
|
const auto available = width
|
|
- st.padding.left()
|
|
- st.padding.right()
|
|
- st.style.font->width(button)
|
|
- st::settingsButtonRightSkip;
|
|
name->setText(text);
|
|
name->resizeToNaturalWidth(available);
|
|
name->moveToRight(st::settingsButtonRightSkip, st.padding.top());
|
|
}, name->lifetime());
|
|
} else if (v::text::is_marked(label)) {
|
|
rpl::combine(
|
|
button->widthValue(),
|
|
std::move(buttonText),
|
|
v::text::take_marked(std::move(label))
|
|
) | rpl::on_next([=, &st](
|
|
int width,
|
|
const QString &button,
|
|
const TextWithEntities &text) {
|
|
const auto available = width
|
|
- st.padding.left()
|
|
- st.padding.right()
|
|
- st.style.font->width(button)
|
|
- st::settingsButtonRightSkip;
|
|
name->setMarkedText(text, context);
|
|
name->resizeToNaturalWidth(available);
|
|
name->moveToRight(st::settingsButtonRightSkip, st.padding.top());
|
|
}, name->lifetime());
|
|
}
|
|
name->setAttribute(Qt::WA_TransparentForMouseEvents);
|
|
}
|
|
|
|
not_null<Button*> AddButtonWithLabel(
|
|
not_null<Ui::VerticalLayout*> container,
|
|
rpl::producer<QString> text,
|
|
rpl::producer<QString> label,
|
|
const style::SettingsButton &st,
|
|
IconDescriptor &&descriptor) {
|
|
const auto button = AddButtonWithIcon(
|
|
container,
|
|
rpl::duplicate(text),
|
|
st,
|
|
std::move(descriptor));
|
|
CreateRightLabel(button, std::move(label), st, std::move(text));
|
|
return button;
|
|
}
|
|
|
|
void AddDividerTextWithLottie(
|
|
not_null<Ui::VerticalLayout*> container,
|
|
DividerWithLottieDescriptor &&descriptor) {
|
|
const auto divider = Ui::CreateChild<Ui::BoxContentDivider>(
|
|
container.get(),
|
|
0,
|
|
st::defaultDividerBar,
|
|
descriptor.parts);
|
|
const auto verticalLayout = container->add(
|
|
object_ptr<Ui::VerticalLayout>(container.get()));
|
|
const auto size = descriptor.lottieSize.value_or(
|
|
st::settingsFilterIconSize);
|
|
auto icon = CreateLottieIcon(
|
|
verticalLayout,
|
|
{
|
|
.name = descriptor.lottie,
|
|
.sizeOverride = { size, size },
|
|
},
|
|
descriptor.lottieMargins.value_or(st::settingsFilterIconPadding));
|
|
if (descriptor.showFinished) {
|
|
const auto repeat = descriptor.lottieRepeat.value_or(
|
|
anim::repeat::once);
|
|
std::move(
|
|
descriptor.showFinished
|
|
) | rpl::on_next([animate = std::move(icon.animate), repeat] {
|
|
animate(repeat);
|
|
}, verticalLayout->lifetime());
|
|
}
|
|
verticalLayout->add(std::move(icon.widget));
|
|
|
|
if (descriptor.about) {
|
|
verticalLayout->add(
|
|
object_ptr<Ui::FlatLabel>(
|
|
verticalLayout,
|
|
std::move(descriptor.about),
|
|
st::settingsFilterDividerLabel),
|
|
descriptor.aboutMargins.value_or(
|
|
st::settingsFilterDividerLabelPadding),
|
|
style::al_top)->setTryMakeSimilarLines(true);
|
|
}
|
|
|
|
verticalLayout->geometryValue(
|
|
) | rpl::on_next([=](const QRect &r) {
|
|
divider->setGeometry(r);
|
|
}, divider->lifetime());
|
|
}
|
|
|
|
LottieIcon CreateLottieIcon(
|
|
not_null<QWidget*> parent,
|
|
Lottie::IconDescriptor &&descriptor,
|
|
style::margins padding,
|
|
Fn<QColor()> colorOverride) {
|
|
Expects(!descriptor.frame); // I'm not sure it considers limitFps.
|
|
|
|
descriptor.limitFps = true;
|
|
|
|
auto object = object_ptr<Ui::RpWidget>(parent);
|
|
const auto raw = object.data();
|
|
|
|
const auto width = descriptor.sizeOverride.width();
|
|
raw->resize(QRect(
|
|
QPoint(),
|
|
descriptor.sizeOverride).marginsAdded(padding).size());
|
|
|
|
auto owned = Lottie::MakeIcon(std::move(descriptor));
|
|
const auto icon = owned.get();
|
|
|
|
raw->lifetime().add([kept = std::move(owned)]{});
|
|
const auto looped = raw->lifetime().make_state<bool>(true);
|
|
|
|
const auto start = [=] {
|
|
icon->animate([=] {
|
|
raw->update();
|
|
}, 0, icon->framesCount() - 1);
|
|
};
|
|
const auto animate = [=](anim::repeat repeat) {
|
|
*looped = (repeat == anim::repeat::loop);
|
|
start();
|
|
};
|
|
raw->paintRequest(
|
|
) | rpl::on_next([=] {
|
|
auto p = QPainter(raw);
|
|
const auto left = (raw->width() - width) / 2;
|
|
icon->paint(p, left, padding.top(), colorOverride
|
|
? colorOverride()
|
|
: std::optional<QColor>());
|
|
if (!icon->animating() && icon->frameIndex() > 0 && *looped) {
|
|
start();
|
|
}
|
|
|
|
}, raw->lifetime());
|
|
|
|
return { .widget = std::move(object), .animate = std::move(animate) };
|
|
}
|
|
|
|
SliderWithLabel MakeSliderWithLabel(
|
|
QWidget *parent,
|
|
const style::MediaSlider &sliderSt,
|
|
const style::FlatLabel &labelSt,
|
|
int skip,
|
|
int minLabelWidth,
|
|
bool ignoreWheel) {
|
|
auto result = object_ptr<Ui::RpWidget>(parent);
|
|
const auto raw = result.data();
|
|
const auto height = std::max(
|
|
sliderSt.seekSize.height(),
|
|
labelSt.style.font->height);
|
|
raw->resize(sliderSt.seekSize.width(), height);
|
|
const auto slider = ignoreWheel
|
|
? Ui::CreateChild<Ui::MediaSliderWheelless>(raw, sliderSt)
|
|
: Ui::CreateChild<Ui::MediaSlider>(raw, sliderSt);
|
|
const auto label = Ui::CreateChild<Ui::FlatLabel>(raw, labelSt);
|
|
slider->resize(slider->width(), sliderSt.seekSize.height());
|
|
rpl::combine(
|
|
raw->sizeValue(),
|
|
label->sizeValue()
|
|
) | rpl::on_next([=](QSize outer, QSize size) {
|
|
const auto right = std::max(size.width(), minLabelWidth) + skip;
|
|
label->moveToRight(0, (outer.height() - size.height()) / 2);
|
|
const auto width = std::max(
|
|
sliderSt.seekSize.width(),
|
|
outer.width() - right);
|
|
slider->resizeToWidth(width);
|
|
slider->moveToLeft(0, (outer.height() - slider->height()) / 2);
|
|
}, label->lifetime());
|
|
return {
|
|
.widget = std::move(result),
|
|
.slider = slider,
|
|
.label = label,
|
|
};
|
|
}
|
|
|
|
void AddLottieIconWithCircle(
|
|
not_null<Ui::VerticalLayout*> layout,
|
|
object_ptr<Ui::RpWidget> icon,
|
|
QMargins iconPadding,
|
|
QSize circleSize) {
|
|
const auto iconRow = layout->add(
|
|
std::move(icon),
|
|
iconPadding,
|
|
style::al_top);
|
|
|
|
const auto circle = Ui::CreateChild<Ui::RpWidget>(
|
|
iconRow->parentWidget());
|
|
circle->lower();
|
|
circle->paintOn([=](QPainter &p) {
|
|
auto hq = PainterHighQualityEnabler(p);
|
|
const auto left = (circle->width() - circleSize.width()) / 2;
|
|
const auto top = (circle->height() - circleSize.height()) / 2;
|
|
p.setPen(Qt::NoPen);
|
|
p.setBrush(st::activeButtonBg);
|
|
p.drawEllipse(QRect(QPoint(left, top), circleSize));
|
|
});
|
|
|
|
iconRow->geometryValue() | rpl::on_next([=](const QRect &g) {
|
|
circle->setGeometry(g);
|
|
}, circle->lifetime());
|
|
}
|
|
|
|
void AddPremiumStar(
|
|
not_null<Button*> button,
|
|
bool credits,
|
|
Fn<bool()> isPaused) {
|
|
const auto stops = credits
|
|
? Ui::Premium::CreditsIconGradientStops()
|
|
: Ui::Premium::ButtonGradientStops();
|
|
|
|
const auto ministarsContainer = Ui::CreateChild<Ui::RpWidget>(button);
|
|
const auto &buttonSt = button->st();
|
|
const auto fullHeight = buttonSt.height
|
|
+ rect::m::sum::v(buttonSt.padding);
|
|
using MiniStars = Ui::Premium::ColoredMiniStars;
|
|
const auto ministars = button->lifetime().make_state<MiniStars>(
|
|
ministarsContainer,
|
|
false);
|
|
ministars->setColorOverride(stops);
|
|
|
|
const auto isPausedValue
|
|
= button->lifetime().make_state<rpl::variable<bool>>(isPaused());
|
|
isPausedValue->value() | rpl::on_next([=](bool value) {
|
|
ministars->setPaused(value);
|
|
}, ministarsContainer->lifetime());
|
|
|
|
ministarsContainer->paintRequest(
|
|
) | rpl::on_next([=] {
|
|
(*isPausedValue) = isPaused();
|
|
auto p = QPainter(ministarsContainer);
|
|
{
|
|
constexpr auto kScale = 0.35;
|
|
const auto r = ministarsContainer->rect();
|
|
p.translate(r.center());
|
|
p.scale(kScale, kScale);
|
|
p.translate(-r.center());
|
|
}
|
|
// remove cringe stars animation
|
|
// ministars->paint(p);
|
|
}, ministarsContainer->lifetime());
|
|
|
|
const auto badge = Ui::CreateChild<Ui::RpWidget>(button.get());
|
|
|
|
auto star = [&] {
|
|
const auto factor = style::DevicePixelRatio();
|
|
const auto size = Size(st::settingsButtonNoIcon.style.font->ascent);
|
|
auto image = QImage(
|
|
size * factor,
|
|
QImage::Format_ARGB32_Premultiplied);
|
|
image.setDevicePixelRatio(factor);
|
|
image.fill(Qt::transparent);
|
|
{
|
|
auto p = QPainter(&image);
|
|
auto star = QSvgRenderer(Ui::Premium::ColorizedSvg(stops));
|
|
star.render(&p, Rect(size));
|
|
}
|
|
return image;
|
|
}();
|
|
badge->resize(star.size() / style::DevicePixelRatio());
|
|
badge->paintRequest(
|
|
) | rpl::on_next([=] {
|
|
auto p = QPainter(badge);
|
|
p.drawImage(0, 0, star);
|
|
}, badge->lifetime());
|
|
|
|
button->sizeValue(
|
|
) | rpl::on_next([=](const QSize &s) {
|
|
badge->moveToLeft(
|
|
button->st().iconLeft
|
|
+ (st::menuIconShop.width() - badge->width()) / 2,
|
|
(s.height() - badge->height()) / 2);
|
|
ministarsContainer->moveToLeft(
|
|
badge->x() - (fullHeight - badge->height()) / 2,
|
|
0);
|
|
}, badge->lifetime());
|
|
|
|
ministarsContainer->resize(fullHeight, fullHeight);
|
|
ministars->setCenter(ministarsContainer->rect());
|
|
}
|
|
|
|
} // namespace Settings
|