mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Added ability to generate QR code independently from ui scale.
This commit is contained in:
parent
931fa01337
commit
88daa37e34
2 changed files with 294 additions and 133 deletions
|
@ -1121,7 +1121,8 @@ moderateBoxDividerLabel: FlatLabel(boxDividerLabel) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
profileQrFont: font(fsize bold);
|
||||||
profileQrCenterSize: 34px;
|
profileQrCenterSize: 34px;
|
||||||
profileQrBackgroundRadius: 12px;
|
profileQrBackgroundRadius: 12px;
|
||||||
profileQrIcon: icon{{ "qr_mini", windowActiveTextFg }};
|
profileQrIcon: icon{{ "qr_mini", windowActiveTextFg }};
|
||||||
profileQrBackgroundSkip: 36px;
|
profileQrBackgroundMargins: margins(36px, 12px, 36px, 12px);
|
||||||
|
|
|
@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_peer.h"
|
#include "data/data_peer.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
|
#include "info/channel_statistics/boosts/giveaway/boost_badge.h" // InfiniteRadialAnimationWidget.
|
||||||
#include "info/profile/info_profile_values.h"
|
#include "info/profile/info_profile_values.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
|
@ -49,6 +50,8 @@ using Colors = std::vector<QColor>;
|
||||||
[[nodiscard]] QImage TelegramQr(const Qr::Data &data, int pixel, int max) {
|
[[nodiscard]] QImage TelegramQr(const Qr::Data &data, int pixel, int max) {
|
||||||
Expects(data.size > 0);
|
Expects(data.size > 0);
|
||||||
|
|
||||||
|
constexpr auto kCenterRatio = 0.175;
|
||||||
|
|
||||||
if (max > 0 && data.size * pixel > max) {
|
if (max > 0 && data.size * pixel > max) {
|
||||||
pixel = std::max(max / data.size, 1);
|
pixel = std::max(max / data.size, 1);
|
||||||
}
|
}
|
||||||
|
@ -62,10 +65,8 @@ using Colors = std::vector<QColor>;
|
||||||
auto hq = PainterHighQualityEnabler(p);
|
auto hq = PainterHighQualityEnabler(p);
|
||||||
auto svg = QSvgRenderer(u":/gui/plane_white.svg"_q);
|
auto svg = QSvgRenderer(u":/gui/plane_white.svg"_q);
|
||||||
const auto size = qr.rect().size();
|
const auto size = qr.rect().size();
|
||||||
const auto centerWidth = st::profileQrCenterSize
|
|
||||||
* style::DevicePixelRatio();
|
|
||||||
const auto centerRect = Rect(size)
|
const auto centerRect = Rect(size)
|
||||||
- Margins((size.width() - centerWidth) / 2);
|
- Margins((size.width() - (size.width() * kCenterRatio)) / 2);
|
||||||
p.setPen(Qt::NoPen);
|
p.setPen(Qt::NoPen);
|
||||||
p.setBrush(Qt::white);
|
p.setBrush(Qt::white);
|
||||||
p.setCompositionMode(QPainter::CompositionMode_Clear);
|
p.setCompositionMode(QPainter::CompositionMode_Clear);
|
||||||
|
@ -76,12 +77,80 @@ using Colors = std::vector<QColor>;
|
||||||
return qr;
|
return qr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Paint(
|
||||||
|
QPainter &p,
|
||||||
|
const style::font &font,
|
||||||
|
const QString &text,
|
||||||
|
const Colors &backgroundColors,
|
||||||
|
const QMargins &backgroundMargins,
|
||||||
|
const QImage &qrImage,
|
||||||
|
const QRect &qrRect,
|
||||||
|
int qrMaxSize,
|
||||||
|
int qrPixel,
|
||||||
|
int radius,
|
||||||
|
int textMaxHeight,
|
||||||
|
int photoSize) {
|
||||||
|
const auto usualSize = 41;
|
||||||
|
const auto pixel = std::clamp(qrMaxSize / usualSize, 1, qrPixel);
|
||||||
|
const auto size = (qrImage.size() / style::DevicePixelRatio());
|
||||||
|
auto hq = PainterHighQualityEnabler(p);
|
||||||
|
p.setPen(Qt::NoPen);
|
||||||
|
p.setBrush(Qt::white);
|
||||||
|
const auto roundedRect = qrRect
|
||||||
|
+ backgroundMargins
|
||||||
|
+ QMargins(0, photoSize / 2, 0, textMaxHeight);
|
||||||
|
p.drawRoundedRect(roundedRect, radius, radius);
|
||||||
|
if (!qrImage.isNull() && !backgroundColors.empty()) {
|
||||||
|
constexpr auto kDuration = crl::time(10000);
|
||||||
|
const auto angle = (crl::now() % kDuration)
|
||||||
|
/ float64(kDuration) * 360.0;
|
||||||
|
const auto gradientRotation = int(angle / 45.) * 45;
|
||||||
|
const auto gradientRotationAdd = angle - gradientRotation;
|
||||||
|
|
||||||
|
const auto center = QPointF(rect::center(qrRect));
|
||||||
|
const auto radius = std::sqrt(std::pow(qrRect.width() / 2., 2)
|
||||||
|
+ std::pow(qrRect.height() / 2., 2));
|
||||||
|
auto back = Images::GenerateGradient(
|
||||||
|
qrRect.size(),
|
||||||
|
backgroundColors,
|
||||||
|
gradientRotation,
|
||||||
|
1. - (gradientRotationAdd / 45.));
|
||||||
|
p.drawImage(qrRect, back);
|
||||||
|
const auto coloredSize = QSize(back.width(), textMaxHeight);
|
||||||
|
auto colored = QImage(
|
||||||
|
coloredSize * style::DevicePixelRatio(),
|
||||||
|
QImage::Format_ARGB32_Premultiplied);
|
||||||
|
colored.setDevicePixelRatio(style::DevicePixelRatio());
|
||||||
|
colored.fill(Qt::transparent);
|
||||||
|
{
|
||||||
|
// '@' + QString(32, 'W');
|
||||||
|
auto p = QPainter(&colored);
|
||||||
|
auto hq = PainterHighQualityEnabler(p);
|
||||||
|
p.setPen(Qt::black);
|
||||||
|
p.setFont(font);
|
||||||
|
auto option = QTextOption(style::al_center);
|
||||||
|
option.setWrapMode(QTextOption::WrapAnywhere);
|
||||||
|
p.drawText(Rect(coloredSize), text, option);
|
||||||
|
p.setCompositionMode(QPainter::CompositionMode_SourceIn);
|
||||||
|
p.drawImage(0, -back.height() + textMaxHeight, back);
|
||||||
|
}
|
||||||
|
p.drawImage(qrRect, qrImage);
|
||||||
|
p.drawImage(
|
||||||
|
qrRect.x(),
|
||||||
|
rect::bottom(qrRect)
|
||||||
|
+ ((rect::bottom(roundedRect) - rect::bottom(qrRect))
|
||||||
|
- textMaxHeight) / 2,
|
||||||
|
colored);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] not_null<Ui::RpWidget*> PrepareQrWidget(
|
[[nodiscard]] not_null<Ui::RpWidget*> PrepareQrWidget(
|
||||||
not_null<Ui::VerticalLayout*> container,
|
not_null<Ui::VerticalLayout*> container,
|
||||||
not_null<Ui::RpWidget*> topWidget,
|
not_null<Ui::RpWidget*> topWidget,
|
||||||
|
const style::font &font,
|
||||||
rpl::producer<TextWithEntities> username,
|
rpl::producer<TextWithEntities> username,
|
||||||
rpl::producer<QString> links,
|
rpl::producer<QString> links,
|
||||||
rpl::producer<std::vector<QColor>> bgs) {
|
rpl::producer<Colors> bgs) {
|
||||||
const auto divider = container->add(
|
const auto divider = container->add(
|
||||||
object_ptr<Ui::BoxContentDivider>(container));
|
object_ptr<Ui::BoxContentDivider>(container));
|
||||||
struct State final {
|
struct State final {
|
||||||
|
@ -90,138 +159,89 @@ using Colors = std::vector<QColor>;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ui::Animations::Basic updating;
|
Ui::Animations::Basic updating;
|
||||||
QImage qr;
|
QImage qrImage;
|
||||||
std::vector<QColor> bgs;
|
Colors backgroundColors;
|
||||||
rpl::variable<TextWithEntities> username;
|
QString text;
|
||||||
|
int textWidth = 0;
|
||||||
int textMaxHeight = 0;
|
int textMaxHeight = 0;
|
||||||
rpl::variable<QString> link;
|
|
||||||
};
|
};
|
||||||
auto palettes = rpl::single(rpl::empty) | rpl::then(
|
|
||||||
style::PaletteChanged()
|
|
||||||
);
|
|
||||||
const auto result = Ui::CreateChild<Ui::RpWidget>(divider);
|
const auto result = Ui::CreateChild<Ui::RpWidget>(divider);
|
||||||
topWidget->setParent(result);
|
topWidget->setParent(result);
|
||||||
topWidget->setAttribute(Qt::WA_TransparentForMouseEvents);
|
topWidget->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
const auto state = result->lifetime().make_state<State>(
|
const auto state = result->lifetime().make_state<State>(
|
||||||
[=] { result->update(); });
|
[=] { result->update(); });
|
||||||
state->username = rpl::variable<TextWithEntities>(std::move(username));
|
const auto photoSize = st::defaultUserpicButton.photoSize;
|
||||||
state->link = rpl::variable<QString>(std::move(links));
|
const auto backgroundMargins = st::profileQrBackgroundMargins;
|
||||||
std::move(
|
|
||||||
bgs
|
|
||||||
) | rpl::start_with_next([=](const std::vector<QColor> &bgs) {
|
|
||||||
state->bgs = bgs;
|
|
||||||
}, container->lifetime());
|
|
||||||
const auto font = st::mainMenuResetScaleFont;
|
|
||||||
const auto backSkip = st::profileQrBackgroundSkip;
|
|
||||||
const auto qrMaxSize = st::boxWideWidth
|
const auto qrMaxSize = st::boxWideWidth
|
||||||
- rect::m::sum::h(st::boxRowPadding)
|
- rect::m::sum::h(st::boxRowPadding)
|
||||||
- 2 * backSkip;
|
- rect::m::sum::h(backgroundMargins);
|
||||||
rpl::combine(
|
rpl::combine(
|
||||||
state->username.value() | rpl::map([=](const TextWithEntities &u) {
|
std::move(username),
|
||||||
return font->width(u.text);
|
std::move(bgs),
|
||||||
}),
|
std::move(links),
|
||||||
rpl::combine(
|
rpl::single(rpl::empty) | rpl::then(style::PaletteChanged())
|
||||||
state->link.value() | rpl::map([](const QString &code) {
|
) | rpl::start_with_next([=](
|
||||||
return Qr::Encode(code.toUtf8(), Qr::Redundancy::Default);
|
const TextWithEntities &username,
|
||||||
}),
|
const Colors &backgroundColors,
|
||||||
rpl::duplicate(palettes)
|
const QString &link,
|
||||||
) | rpl::map([=](const Qr::Data &code, const auto &) {
|
const auto &) {
|
||||||
return TelegramQr(code, st::introQrPixel, qrMaxSize);
|
state->backgroundColors = backgroundColors;
|
||||||
})
|
state->text = username.text.toUpper();
|
||||||
) | rpl::start_with_next([=](int usernameW, QImage &&image) {
|
state->textWidth = font->width(state->text);
|
||||||
state->qr = std::move(image);
|
state->qrImage = TelegramQr(
|
||||||
const auto qrWidth = state->qr.size().width()
|
Qr::Encode(link.toUtf8(), Qr::Redundancy::Default),
|
||||||
|
st::introQrPixel,
|
||||||
|
qrMaxSize);
|
||||||
|
const auto qrWidth = state->qrImage.width()
|
||||||
/ style::DevicePixelRatio();
|
/ style::DevicePixelRatio();
|
||||||
const auto lines = int(usernameW / qrWidth) + 1;
|
const auto lines = int(state->textWidth / qrWidth) + 1;
|
||||||
state->textMaxHeight = font->height * lines;
|
state->textMaxHeight = font->height * lines;
|
||||||
const auto heightSkip = (font->height * 3);
|
|
||||||
result->resize(
|
result->resize(
|
||||||
qrMaxSize + 2 * backSkip,
|
qrMaxSize + rect::m::sum::h(backgroundMargins),
|
||||||
qrMaxSize + 2 * backSkip + state->textMaxHeight + heightSkip);
|
qrMaxSize
|
||||||
}, result->lifetime());
|
+ rect::m::sum::v(backgroundMargins)
|
||||||
|
+ backgroundMargins.bottom()
|
||||||
|
+ state->textMaxHeight
|
||||||
|
+ photoSize);
|
||||||
|
|
||||||
|
divider->resize(container->width(), result->height());
|
||||||
|
result->moveToLeft((container->width() - result->width()) / 2, 0);
|
||||||
|
topWidget->moveToLeft(
|
||||||
|
(result->width() - topWidget->width()) / 2,
|
||||||
|
-std::numeric_limits<int>::min());
|
||||||
|
topWidget->raise();
|
||||||
|
}, container->lifetime());
|
||||||
result->paintRequest(
|
result->paintRequest(
|
||||||
) | rpl::start_with_next([=](QRect clip) {
|
) | rpl::start_with_next([=](QRect clip) {
|
||||||
auto p = QPainter(result);
|
auto p = QPainter(result);
|
||||||
const auto usualSize = 41;
|
const auto size = (state->qrImage.size() / style::DevicePixelRatio());
|
||||||
const auto pixel = std::clamp(
|
const auto qrRect = Rect(
|
||||||
qrMaxSize / usualSize,
|
|
||||||
1,
|
|
||||||
st::introQrPixel);
|
|
||||||
const auto size = (state->qr.size() / style::DevicePixelRatio());
|
|
||||||
const auto radius = st::profileQrBackgroundRadius;
|
|
||||||
const auto qr = QRect(
|
|
||||||
(result->width() - size.width()) / 2,
|
(result->width() - size.width()) / 2,
|
||||||
backSkip * 3,
|
backgroundMargins.top() + photoSize / 2,
|
||||||
size.width(),
|
size);
|
||||||
size.height());
|
p.translate(0, backgroundMargins.top() + photoSize / 2);
|
||||||
auto hq = PainterHighQualityEnabler(p);
|
Paint(
|
||||||
p.setPen(Qt::NoPen);
|
p,
|
||||||
p.setBrush(Qt::white);
|
font,
|
||||||
p.drawRoundedRect(
|
state->text,
|
||||||
qr
|
state->backgroundColors,
|
||||||
+ QMargins(
|
st::profileQrBackgroundMargins,
|
||||||
backSkip,
|
state->qrImage,
|
||||||
backSkip + backSkip / 2,
|
qrRect,
|
||||||
backSkip,
|
qrMaxSize,
|
||||||
backSkip + state->textMaxHeight),
|
st::introQrPixel,
|
||||||
radius,
|
st::profileQrBackgroundRadius,
|
||||||
radius);
|
state->textMaxHeight,
|
||||||
if (!state->qr.isNull() && !state->bgs.empty()) {
|
photoSize);
|
||||||
constexpr auto kDuration = crl::time(10000);
|
const auto top = Ui::GrabWidget(
|
||||||
const auto angle = (crl::now() % kDuration)
|
topWidget,
|
||||||
/ float64(kDuration) * 360.0;
|
QRect(),
|
||||||
const auto gradientRotation = int(angle / 45.) * 45;
|
Qt::transparent).scaled(
|
||||||
const auto gradientRotationAdd = angle - gradientRotation;
|
Size(photoSize * style::DevicePixelRatio()),
|
||||||
|
Qt::IgnoreAspectRatio,
|
||||||
const auto center = QPointF(rect::center(qr));
|
Qt::SmoothTransformation);
|
||||||
const auto radius = std::sqrt(std::pow(qr.width() / 2., 2)
|
p.drawPixmap((result->width() - photoSize) / 2, -photoSize / 2, top);
|
||||||
+ std::pow(qr.height() / 2., 2));
|
|
||||||
auto back = Images::GenerateGradient(
|
|
||||||
qr.size(),
|
|
||||||
state->bgs,
|
|
||||||
gradientRotation,
|
|
||||||
1. - (gradientRotationAdd / 45.));
|
|
||||||
p.drawImage(qr, back);
|
|
||||||
const auto coloredSize = QSize(
|
|
||||||
back.width(),
|
|
||||||
state->textMaxHeight);
|
|
||||||
auto colored = QImage(
|
|
||||||
coloredSize * style::DevicePixelRatio(),
|
|
||||||
QImage::Format_ARGB32_Premultiplied);
|
|
||||||
colored.setDevicePixelRatio(style::DevicePixelRatio());
|
|
||||||
colored.fill(Qt::transparent);
|
|
||||||
{
|
|
||||||
// '@' + QString(32, 'W');
|
|
||||||
auto p = QPainter(&colored);
|
|
||||||
auto hq = PainterHighQualityEnabler(p);
|
|
||||||
p.setPen(Qt::red);
|
|
||||||
p.setFont(font);
|
|
||||||
auto option = QTextOption(style::al_center);
|
|
||||||
option.setWrapMode(QTextOption::WrapAnywhere);
|
|
||||||
p.drawText(
|
|
||||||
Rect(coloredSize),
|
|
||||||
state->username.current().text.toUpper(),
|
|
||||||
option);
|
|
||||||
p.setCompositionMode(QPainter::CompositionMode_SourceIn);
|
|
||||||
p.drawImage(0, -back.height() + state->textMaxHeight, back);
|
|
||||||
}
|
|
||||||
p.drawImage(qr, state->qr);
|
|
||||||
p.drawImage(qr.x(), qr.y() + qr.height() + backSkip / 2, colored);
|
|
||||||
}
|
|
||||||
}, result->lifetime());
|
}, result->lifetime());
|
||||||
result->sizeValue(
|
|
||||||
) | rpl::start_with_next([=](const QSize &size) {
|
|
||||||
divider->resize(container->width(), size.height());
|
|
||||||
result->moveToLeft((container->width() - size.width()) / 2, 0);
|
|
||||||
|
|
||||||
const auto qrHeight = state->qr.height() / style::DevicePixelRatio();
|
|
||||||
topWidget->moveToLeft(
|
|
||||||
(result->width() - topWidget->width()) / 2,
|
|
||||||
(backSkip
|
|
||||||
+ st::profileQrBackgroundSkip / 2
|
|
||||||
- topWidget->height() / 2));
|
|
||||||
topWidget->raise();
|
|
||||||
}, divider->lifetime());
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,17 +261,31 @@ void FillProfileQrBox(
|
||||||
box->setTitle(tr::lng_group_invite_context_qr());
|
box->setTitle(tr::lng_group_invite_context_qr());
|
||||||
box->verticalLayout()->resizeToWidth(box->width());
|
box->verticalLayout()->resizeToWidth(box->width());
|
||||||
struct State {
|
struct State {
|
||||||
rpl::variable<std::vector<QColor>> bgs;
|
Ui::RpWidget* saveButton = nullptr;
|
||||||
|
rpl::variable<bool> saveButtonBusy = false;
|
||||||
|
rpl::variable<Colors> bgs;
|
||||||
Ui::Animations::Simple animation;
|
Ui::Animations::Simple animation;
|
||||||
rpl::variable<int> chosen = 0;
|
rpl::variable<int> chosen = 0;
|
||||||
|
|
||||||
|
style::font font;
|
||||||
};
|
};
|
||||||
const auto state = box->lifetime().make_state<State>();
|
const auto state = box->lifetime().make_state<State>();
|
||||||
|
const auto createFont = [=](int scale) {
|
||||||
|
return style::font(
|
||||||
|
style::ConvertScale(30, scale),
|
||||||
|
st::profileQrFont->flags(),
|
||||||
|
st::profileQrFont->family());
|
||||||
|
};
|
||||||
|
state->font = createFont(style::Scale());
|
||||||
|
|
||||||
|
const auto userpic = Ui::CreateChild<Ui::UserpicButton>(
|
||||||
|
box,
|
||||||
|
peer,
|
||||||
|
st::defaultUserpicButton);
|
||||||
const auto qr = PrepareQrWidget(
|
const auto qr = PrepareQrWidget(
|
||||||
box->verticalLayout(),
|
box->verticalLayout(),
|
||||||
Ui::CreateChild<Ui::UserpicButton>(
|
userpic,
|
||||||
box,
|
state->font,
|
||||||
peer,
|
|
||||||
st::defaultUserpicButton),
|
|
||||||
Info::Profile::UsernameValue(peer->asUser()),
|
Info::Profile::UsernameValue(peer->asUser()),
|
||||||
Info::Profile::LinkValue(peer) | rpl::map([](const auto &link) {
|
Info::Profile::LinkValue(peer) | rpl::map([](const auto &link) {
|
||||||
return link.url;
|
return link.url;
|
||||||
|
@ -430,20 +464,146 @@ void FillProfileQrBox(
|
||||||
}, box->lifetime());
|
}, box->lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto buttonText = rpl::conditional(
|
||||||
|
state->saveButtonBusy.value() | rpl::map(rpl::mappers::_1),
|
||||||
|
rpl::single(QString()),
|
||||||
|
tr::lng_chat_link_copy());
|
||||||
const auto show = controller->uiShow();
|
const auto show = controller->uiShow();
|
||||||
const auto button = box->addButton(tr::lng_chat_link_copy(), [=] {
|
state->saveButton = box->addButton(std::move(buttonText), [=] {
|
||||||
auto mime = std::make_unique<QMimeData>();
|
const auto buttonWidth = state->saveButton
|
||||||
mime->setImageData(Ui::GrabWidget(qr, {}, Qt::transparent));
|
? state->saveButton->width()
|
||||||
QGuiApplication::clipboard()->setMimeData(mime.release());
|
: 0;
|
||||||
show->showToast(tr::lng_group_invite_qr_copied(tr::now));
|
state->saveButtonBusy = true;
|
||||||
|
if (state->saveButton) {
|
||||||
|
state->saveButton->resizeToWidth(buttonWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto scale = style::kScaleDefault * 3;
|
||||||
|
const auto divider = std::max(1, style::Scale())
|
||||||
|
/ style::kScaleDefault;
|
||||||
|
const auto profileQrBackgroundRadius = style::ConvertScale(
|
||||||
|
st::profileQrBackgroundRadius / divider,
|
||||||
|
scale);
|
||||||
|
const auto introQrPixel = style::ConvertScale(
|
||||||
|
st::introQrPixel / divider,
|
||||||
|
scale);
|
||||||
|
const auto boxWideWidth = style::ConvertScale(
|
||||||
|
st::boxWideWidth / divider,
|
||||||
|
scale);
|
||||||
|
const auto createMargins = [&](const style::margins &margins) {
|
||||||
|
return QMargins(
|
||||||
|
style::ConvertScale(margins.left() / divider, scale),
|
||||||
|
style::ConvertScale(margins.top() / divider, scale),
|
||||||
|
style::ConvertScale(margins.right() / divider, scale),
|
||||||
|
style::ConvertScale(margins.bottom() / divider, scale));
|
||||||
|
};
|
||||||
|
const auto boxRowPadding = createMargins(st::boxRowPadding);
|
||||||
|
const auto backgroundMargins = createMargins(
|
||||||
|
st::profileQrBackgroundMargins);
|
||||||
|
const auto qrMaxSize = boxWideWidth
|
||||||
|
- rect::m::sum::h(boxRowPadding)
|
||||||
|
- rect::m::sum::h(backgroundMargins);
|
||||||
|
const auto photoSize = style::ConvertScale(
|
||||||
|
st::defaultUserpicButton.photoSize,
|
||||||
|
scale);
|
||||||
|
|
||||||
|
const auto font = createFont(scale);
|
||||||
|
const auto username = rpl::variable<TextWithEntities>(
|
||||||
|
Info::Profile::UsernameValue(
|
||||||
|
peer->asUser())).current().text.toUpper();
|
||||||
|
const auto link = rpl::variable<QString>(
|
||||||
|
Info::Profile::LinkValue(peer) | rpl::map([](const auto &l) {
|
||||||
|
return l.url;
|
||||||
|
}));
|
||||||
|
const auto textWidth = font->width(username);
|
||||||
|
const auto top = Ui::GrabWidget(
|
||||||
|
userpic,
|
||||||
|
{},
|
||||||
|
Qt::transparent);
|
||||||
|
const auto weak = Ui::MakeWeak(box);
|
||||||
|
|
||||||
|
crl::async([=] {
|
||||||
|
const auto qrImage = TelegramQr(
|
||||||
|
Qr::Encode(
|
||||||
|
link.current().toUtf8(),
|
||||||
|
Qr::Redundancy::Default),
|
||||||
|
introQrPixel,
|
||||||
|
qrMaxSize);
|
||||||
|
const auto qrWidth = qrImage.width() / style::DevicePixelRatio();
|
||||||
|
const auto lines = int(textWidth / qrWidth) + 1;
|
||||||
|
const auto textMaxHeight = font->height * lines;
|
||||||
|
|
||||||
|
const auto resultSize = QSize(
|
||||||
|
qrMaxSize + rect::m::sum::h(backgroundMargins),
|
||||||
|
qrMaxSize
|
||||||
|
+ rect::m::sum::v(backgroundMargins)
|
||||||
|
+ backgroundMargins.bottom()
|
||||||
|
+ textMaxHeight
|
||||||
|
+ photoSize);
|
||||||
|
|
||||||
|
const auto qrImageSize = qrImage.size()
|
||||||
|
/ style::DevicePixelRatio();
|
||||||
|
const auto qrRect = Rect(
|
||||||
|
(resultSize.width() - qrImageSize.width()) / 2,
|
||||||
|
backgroundMargins.top() + photoSize / 2,
|
||||||
|
qrImageSize);
|
||||||
|
|
||||||
|
auto image = QImage(
|
||||||
|
resultSize * style::DevicePixelRatio(),
|
||||||
|
QImage::Format_ARGB32_Premultiplied);
|
||||||
|
image.fill(Qt::transparent);
|
||||||
|
image.setDevicePixelRatio(style::DevicePixelRatio());
|
||||||
|
{
|
||||||
|
auto p = QPainter(&image);
|
||||||
|
p.translate(0, photoSize / 2 + backgroundMargins.top());
|
||||||
|
Paint(
|
||||||
|
p,
|
||||||
|
font,
|
||||||
|
username,
|
||||||
|
state->bgs.current(),
|
||||||
|
backgroundMargins,
|
||||||
|
qrImage,
|
||||||
|
qrRect,
|
||||||
|
qrMaxSize,
|
||||||
|
introQrPixel,
|
||||||
|
profileQrBackgroundRadius,
|
||||||
|
textMaxHeight,
|
||||||
|
photoSize);
|
||||||
|
|
||||||
|
p.drawPixmap(
|
||||||
|
(resultSize.width() - photoSize) / 2,
|
||||||
|
-photoSize / 2,
|
||||||
|
top.scaled(
|
||||||
|
Size(photoSize * style::DevicePixelRatio()),
|
||||||
|
Qt::IgnoreAspectRatio,
|
||||||
|
Qt::SmoothTransformation));
|
||||||
|
}
|
||||||
|
crl::on_main(weak, [=] {
|
||||||
|
state->saveButtonBusy = false;
|
||||||
|
auto mime = std::make_unique<QMimeData>();
|
||||||
|
mime->setImageData(std::move(image));
|
||||||
|
QGuiApplication::clipboard()->setMimeData(mime.release());
|
||||||
|
show->showToast(tr::lng_group_invite_qr_copied(tr::now));
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (const auto saveButton = state->saveButton) {
|
||||||
|
using namespace Info::Statistics;
|
||||||
|
const auto loadingAnimation = InfiniteRadialAnimationWidget(
|
||||||
|
saveButton,
|
||||||
|
saveButton->height() / 2);
|
||||||
|
AddChildToWidgetCenter(saveButton, loadingAnimation);
|
||||||
|
loadingAnimation->showOn(state->saveButtonBusy.value());
|
||||||
|
}
|
||||||
|
|
||||||
const auto buttonWidth = box->width()
|
const auto buttonWidth = box->width()
|
||||||
- rect::m::sum::h(st::giveawayGiftCodeBox.buttonPadding);
|
- rect::m::sum::h(st::giveawayGiftCodeBox.buttonPadding);
|
||||||
button->widthValue() | rpl::filter([=] {
|
state->saveButton->widthValue() | rpl::filter([=] {
|
||||||
return (button->widthNoMargins() != buttonWidth);
|
return (state->saveButton->widthNoMargins() != buttonWidth);
|
||||||
}) | rpl::start_with_next([=] {
|
}) | rpl::start_with_next([=] {
|
||||||
button->resizeToWidth(buttonWidth);
|
state->saveButton->resizeToWidth(buttonWidth);
|
||||||
}, button->lifetime());
|
}, state->saveButton->lifetime());
|
||||||
box->addTopButton(st::boxTitleClose, [=] { box->closeBox(); });
|
box->addTopButton(st::boxTitleClose, [=] { box->closeBox(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue