| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652 |
- /*
- 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 "ui/empty_userpic.h"
- #include "info/channel_statistics/earn/earn_icons.h"
- #include "ui/chat/chat_style.h"
- #include "ui/effects/animation_value.h"
- #include "ui/emoji_config.h"
- #include "ui/painter.h"
- #include "ui/ui_utility.h"
- #include "styles/style_chat.h"
- #include "styles/style_dialogs.h"
- #include "styles/style_widgets.h" // style::IconButton
- #include "styles/style_info.h" // st::topBarCall
- #include <QtSvg/QSvgRenderer>
- namespace Ui {
- namespace {
- [[nodiscard]] bool IsExternal(const QString &name) {
- return !name.isEmpty()
- && (name.front() == QChar(0))
- && QStringView(name).mid(1) == u"external"_q;
- }
- [[nodiscard]] bool IsInaccessible(const QString &name) {
- return !name.isEmpty()
- && (name.front() == QChar(0))
- && QStringView(name).mid(1) == u"inaccessible"_q;
- }
- void PaintSavedMessagesInner(
- QPainter &p,
- int x,
- int y,
- int size,
- const style::color &fg) {
- // |<----width----->|
- //
- // XXXXXXXXXXXXXXXXXX ---
- // X X |
- // X X |
- // X X |
- // X X height
- // X XX X | ---
- // X XX XX X | |
- // X XX XX X | add
- // X XX XX X | |
- // XX XX --- ---
- const auto thinkness = base::SafeRound(size * 0.055);
- const auto increment = int(thinkness) % 2 + (size % 2);
- const auto width = base::SafeRound(size * 0.15) * 2 + increment;
- const auto height = base::SafeRound(size * 0.19) * 2 + increment;
- const auto add = base::SafeRound(size * 0.064);
- const auto left = x + (size - width) / 2;
- const auto top = y + (size - height) / 2;
- const auto right = left + width;
- const auto bottom = top + height;
- const auto middle = (left + right) / 2;
- const auto half = (top + bottom) / 2;
- p.setBrush(Qt::NoBrush);
- auto pen = fg->p;
- pen.setWidthF(thinkness);
- pen.setCapStyle(Qt::FlatCap);
- {
- // XXXXXXXXXXXXXXXXXX
- // X X
- // X X
- // X X
- // X X
- // X X
- pen.setJoinStyle(Qt::RoundJoin);
- p.setPen(pen);
- QPainterPath path;
- path.moveTo(left, half);
- path.lineTo(left, top);
- path.lineTo(right, top);
- path.lineTo(right, half);
- p.drawPath(path);
- }
- {
- // X X
- // X XX X
- // X XX XX X
- // X XX XX X
- // X XX XX X
- // XX XX
- pen.setJoinStyle(Qt::MiterJoin);
- p.setPen(pen);
- QPainterPath path;
- path.moveTo(left, half);
- path.lineTo(left, bottom);
- path.lineTo(middle, bottom - add);
- path.lineTo(right, bottom);
- path.lineTo(right, half);
- p.drawPath(path);
- }
- }
- void PaintIconInner(
- QPainter &p,
- int x,
- int y,
- int size,
- int defaultSize,
- const style::icon &icon,
- const style::color &fg) {
- if (size == defaultSize) {
- const auto rect = QRect{ x, y, size, size };
- icon.paintInCenter(
- p,
- rect,
- fg->c);
- } else {
- p.save();
- const auto ratio = size / float64(defaultSize);
- p.translate(x + size / 2., y + size / 2.);
- p.scale(ratio, ratio);
- const auto skip = defaultSize;
- const auto rect = QRect{ -skip, -skip, 2 * skip, 2 * skip };
- icon.paintInCenter(
- p,
- rect,
- fg->c);
- p.restore();
- }
- }
- void PaintRepliesMessagesInner(
- QPainter &p,
- int x,
- int y,
- int size,
- const style::color &fg) {
- PaintIconInner(
- p,
- x,
- y,
- size,
- st::defaultDialogRow.photoSize,
- st::dialogsRepliesUserpic,
- fg);
- }
- void PaintHiddenAuthorInner(
- QPainter &p,
- int x,
- int y,
- int size,
- const style::color &fg) {
- PaintIconInner(
- p,
- x,
- y,
- size,
- st::defaultDialogRow.photoSize,
- st::dialogsHiddenAuthorUserpic,
- fg);
- }
- void PaintMyNotesInner(
- QPainter &p,
- int x,
- int y,
- int size,
- const style::color &fg) {
- PaintIconInner(
- p,
- x,
- y,
- size,
- st::defaultDialogRow.photoSize,
- st::dialogsMyNotesUserpic,
- fg);
- }
- void PaintCurrencyInner(
- QPainter &p,
- int x,
- int y,
- int size,
- const style::color &fg) {
- auto svg = QSvgRenderer(Ui::Earn::CurrencySvgColored(fg->c));
- const auto skip = size / 5;
- svg.render(&p, QRect(x, y, size, size).marginsRemoved(
- { skip, skip, skip, skip }));
- }
- void PaintExternalMessagesInner(
- QPainter &p,
- int x,
- int y,
- int size,
- const style::color &fg) {
- PaintIconInner(
- p,
- x,
- y,
- size,
- st::msgPhotoSize,
- st::topBarCall.icon,
- fg);
- }
- void PaintInaccessibleAccountInner(
- QPainter &p,
- int x,
- int y,
- int size,
- const style::color &fg) {
- if (size > st::defaultDialogRow.photoSize) {
- PaintIconInner(
- p,
- x,
- y,
- size,
- st::infoProfilePhotoInnerSize,
- st::infoProfileInaccessibleUserpic,
- fg);
- } else {
- PaintIconInner(
- p,
- x,
- y,
- size,
- st::defaultDialogRow.photoSize,
- st::dialogsInaccessibleUserpic,
- fg);
- }
- }
- [[nodiscard]] QImage Generate(int size, Fn<void(QPainter&)> callback) {
- auto result = QImage(
- QSize(size, size) * style::DevicePixelRatio(),
- QImage::Format_ARGB32_Premultiplied);
- result.setDevicePixelRatio(style::DevicePixelRatio());
- result.fill(Qt::transparent);
- {
- Painter p(&result);
- callback(p);
- }
- return result;
- }
- } // namespace
- EmptyUserpic::EmptyUserpic(const BgColors &colors, const QString &name)
- : _colors(colors) {
- fillString(name);
- }
- QString EmptyUserpic::ExternalName() {
- return QChar(0) + u"external"_q;
- }
- QString EmptyUserpic::InaccessibleName() {
- return QChar(0) + u"inaccessible"_q;
- }
- uint8 EmptyUserpic::ColorIndex(uint64 id) {
- return DecideColorIndex(id);
- }
- EmptyUserpic::BgColors EmptyUserpic::UserpicColor(uint8 colorIndex) {
- const EmptyUserpic::BgColors colors[] = {
- { st::historyPeer1UserpicBg, st::historyPeer1UserpicBg2 },
- { st::historyPeer2UserpicBg, st::historyPeer2UserpicBg2 },
- { st::historyPeer3UserpicBg, st::historyPeer3UserpicBg2 },
- { st::historyPeer4UserpicBg, st::historyPeer4UserpicBg2 },
- { st::historyPeer5UserpicBg, st::historyPeer5UserpicBg2 },
- { st::historyPeer6UserpicBg, st::historyPeer6UserpicBg2 },
- { st::historyPeer7UserpicBg, st::historyPeer7UserpicBg2 },
- { st::historyPeer8UserpicBg, st::historyPeer8UserpicBg2 },
- };
- return colors[ColorIndexToPaletteIndex(colorIndex)];
- }
- void EmptyUserpic::paint(
- QPainter &p,
- int x,
- int y,
- int outerWidth,
- int size,
- Fn<void()> paintBackground) const {
- x = style::RightToLeft() ? (outerWidth - x - size) : x;
- const auto fontsize = (size * 13) / 33;
- auto font = st::historyPeerUserpicFont->f;
- font.setPixelSize(fontsize);
- PainterHighQualityEnabler hq(p);
- {
- auto gradient = QLinearGradient(x, y, x, y + size);
- gradient.setStops({
- { 0., _colors.color1->c },
- { 1., _colors.color2->c }
- });
- p.setBrush(gradient);
- }
- p.setPen(Qt::NoPen);
- paintBackground();
- if (IsExternal(_string)) {
- PaintExternalMessagesInner(p, x, y, size, st::historyPeerUserpicFg);
- } else if (IsInaccessible(_string)) {
- PaintInaccessibleAccountInner(
- p,
- x,
- y,
- size,
- st::historyPeerUserpicFg);
- } else {
- p.setFont(font);
- p.setBrush(Qt::NoBrush);
- p.setPen(st::historyPeerUserpicFg);
- p.drawText(
- QRect(x, y, size, size),
- _string,
- QTextOption(style::al_center));
- }
- }
- void EmptyUserpic::paintCircle(
- QPainter &p,
- int x,
- int y,
- int outerWidth,
- int size) const {
- paint(p, x, y, outerWidth, size, [&] {
- p.drawEllipse(x, y, size, size);
- });
- }
- void EmptyUserpic::paintRounded(
- QPainter &p,
- int x,
- int y,
- int outerWidth,
- int size,
- int radius) const {
- paint(p, x, y, outerWidth, size, [&] {
- p.drawRoundedRect(x, y, size, size, radius, radius);
- });
- }
- void EmptyUserpic::paintSquare(
- QPainter &p,
- int x,
- int y,
- int outerWidth,
- int size) const {
- paint(p, x, y, outerWidth, size, [&] {
- p.fillRect(x, y, size, size, p.brush());
- });
- }
- void EmptyUserpic::PaintSavedMessages(
- QPainter &p,
- int x,
- int y,
- int outerWidth,
- int size) {
- auto bg = QLinearGradient(x, y, x, y + size);
- bg.setStops({
- { 0., st::historyPeerSavedMessagesBg->c },
- { 1., st::historyPeerSavedMessagesBg2->c }
- });
- const auto &fg = st::historyPeerUserpicFg;
- PaintSavedMessages(p, x, y, outerWidth, size, QBrush(bg), fg);
- }
- void EmptyUserpic::PaintSavedMessages(
- QPainter &p,
- int x,
- int y,
- int outerWidth,
- int size,
- QBrush bg,
- const style::color &fg) {
- x = style::RightToLeft() ? (outerWidth - x - size) : x;
- PainterHighQualityEnabler hq(p);
- p.setBrush(std::move(bg));
- p.setPen(Qt::NoPen);
- p.drawEllipse(x, y, size, size);
- PaintSavedMessagesInner(p, x, y, size, fg);
- }
- QImage EmptyUserpic::GenerateSavedMessages(int size) {
- return Generate(size, [&](QPainter &p) {
- PaintSavedMessages(p, 0, 0, size, size);
- });
- }
- void EmptyUserpic::PaintRepliesMessages(
- QPainter &p,
- int x,
- int y,
- int outerWidth,
- int size) {
- auto bg = QLinearGradient(x, y, x, y + size);
- bg.setStops({
- { 0., st::historyPeerSavedMessagesBg->c },
- { 1., st::historyPeerSavedMessagesBg2->c }
- });
- const auto &fg = st::historyPeerUserpicFg;
- PaintRepliesMessages(p, x, y, outerWidth, size, QBrush(bg), fg);
- }
- void EmptyUserpic::PaintRepliesMessages(
- QPainter &p,
- int x,
- int y,
- int outerWidth,
- int size,
- QBrush bg,
- const style::color &fg) {
- x = style::RightToLeft() ? (outerWidth - x - size) : x;
- PainterHighQualityEnabler hq(p);
- p.setBrush(bg);
- p.setPen(Qt::NoPen);
- p.drawEllipse(x, y, size, size);
- PaintRepliesMessagesInner(p, x, y, size, fg);
- }
- QImage EmptyUserpic::GenerateRepliesMessages(int size) {
- return Generate(size, [&](QPainter &p) {
- PaintRepliesMessages(p, 0, 0, size, size);
- });
- }
- void EmptyUserpic::PaintHiddenAuthor(
- QPainter &p,
- int x,
- int y,
- int outerWidth,
- int size) {
- auto bg = QLinearGradient(x, y, x, y + size);
- bg.setStops({
- { 0., st::premiumButtonBg2->c },
- { 1., st::premiumButtonBg3->c },
- });
- const auto &fg = st::premiumButtonFg;
- PaintHiddenAuthor(p, x, y, outerWidth, size, QBrush(bg), fg);
- }
- void EmptyUserpic::PaintHiddenAuthor(
- QPainter &p,
- int x,
- int y,
- int outerWidth,
- int size,
- QBrush bg,
- const style::color &fg) {
- x = style::RightToLeft() ? (outerWidth - x - size) : x;
- PainterHighQualityEnabler hq(p);
- p.setBrush(bg);
- p.setPen(Qt::NoPen);
- p.drawEllipse(x, y, size, size);
- PaintHiddenAuthorInner(p, x, y, size, fg);
- }
- QImage EmptyUserpic::GenerateHiddenAuthor(int size) {
- return Generate(size, [&](QPainter &p) {
- PaintHiddenAuthor(p, 0, 0, size, size);
- });
- }
- void EmptyUserpic::PaintMyNotes(
- QPainter &p,
- int x,
- int y,
- int outerWidth,
- int size) {
- auto bg = QLinearGradient(x, y, x, y + size);
- bg.setStops({
- { 0., st::historyPeerSavedMessagesBg->c },
- { 1., st::historyPeerSavedMessagesBg2->c }
- });
- const auto &fg = st::historyPeerUserpicFg;
- PaintMyNotes(p, x, y, outerWidth, size, QBrush(bg), fg);
- }
- void EmptyUserpic::PaintMyNotes(
- QPainter &p,
- int x,
- int y,
- int outerWidth,
- int size,
- QBrush bg,
- const style::color &fg) {
- x = style::RightToLeft() ? (outerWidth - x - size) : x;
- PainterHighQualityEnabler hq(p);
- p.setBrush(bg);
- p.setPen(Qt::NoPen);
- p.drawEllipse(x, y, size, size);
- PaintMyNotesInner(p, x, y, size, fg);
- }
- QImage EmptyUserpic::GenerateMyNotes(int size) {
- return Generate(size, [&](QPainter &p) {
- PaintMyNotes(p, 0, 0, size, size);
- });
- }
- void EmptyUserpic::PaintCurrency(
- QPainter &p,
- int x,
- int y,
- int outerWidth,
- int size) {
- auto bg = QLinearGradient(x, y, x, y + size);
- bg.setStops({
- { 0., st::historyPeerSavedMessagesBg->c },
- { 1., st::historyPeerSavedMessagesBg2->c }
- });
- const auto &fg = st::historyPeerUserpicFg;
- PaintCurrency(p, x, y, outerWidth, size, QBrush(bg), fg);
- }
- void EmptyUserpic::PaintCurrency(
- QPainter &p,
- int x,
- int y,
- int outerWidth,
- int size,
- QBrush bg,
- const style::color &fg) {
- x = style::RightToLeft() ? (outerWidth - x - size) : x;
- PainterHighQualityEnabler hq(p);
- p.setBrush(bg);
- p.setPen(Qt::NoPen);
- p.drawEllipse(x, y, size, size);
- PaintCurrencyInner(p, x, y, size, fg);
- }
- QImage EmptyUserpic::GenerateCurrency(int size) {
- return Generate(size, [&](QPainter &p) {
- PaintCurrency(p, 0, 0, size, size);
- });
- }
- std::pair<uint64, uint64> EmptyUserpic::uniqueKey() const {
- const auto first = (uint64(0xFFFFFFFFU) << 32)
- | anim::getPremultiplied(_colors.color1->c);
- auto second = uint64(0);
- memcpy(
- &second,
- _string.constData(),
- std::min(sizeof(second), size_t(_string.size()) * sizeof(QChar)));
- return { first, second };
- }
- QPixmap EmptyUserpic::generate(int size) {
- auto result = QImage(
- QSize(size, size) * style::DevicePixelRatio(),
- QImage::Format_ARGB32_Premultiplied);
- result.setDevicePixelRatio(style::DevicePixelRatio());
- result.fill(Qt::transparent);
- {
- auto p = QPainter(&result);
- paintCircle(p, 0, 0, size, size);
- }
- return Ui::PixmapFromImage(std::move(result));
- }
- void EmptyUserpic::fillString(const QString &name) {
- if (IsExternal(name) || IsInaccessible(name)) {
- _string = name;
- return;
- }
- QList<QString> letters;
- QList<int> levels;
- auto level = 0;
- auto letterFound = false;
- auto ch = name.constData(), end = ch + name.size();
- while (ch != end) {
- auto emojiLength = 0;
- if (Ui::Emoji::Find(ch, end, &emojiLength)) {
- ch += emojiLength;
- } else if (ch->isHighSurrogate()) {
- ++ch;
- if (ch != end && ch->isLowSurrogate()) {
- ++ch;
- }
- } else if (!letterFound && ch->isLetterOrNumber()) {
- letterFound = true;
- if (ch + 1 != end && Ui::Text::IsDiacritic(*(ch + 1))) {
- letters.push_back(QString(ch, 2));
- levels.push_back(level);
- ++ch;
- } else {
- letters.push_back(QString(ch, 1));
- levels.push_back(level);
- }
- ++ch;
- } else {
- if (*ch == ' ') {
- level = 0;
- letterFound = false;
- } else if (letterFound && *ch == '-') {
- level = 1;
- letterFound = true;
- }
- ++ch;
- }
- }
- // We prefer the second letter to be after ' ', but it can also be after '-'.
- _string = QString();
- if (!letters.isEmpty()) {
- _string += letters.front();
- auto bestIndex = 0;
- auto bestLevel = 2;
- for (auto i = letters.size(); i != 1;) {
- if (levels[--i] < bestLevel) {
- bestIndex = i;
- bestLevel = levels[i];
- }
- }
- if (bestIndex > 0) {
- _string += letters[bestIndex];
- }
- }
- _string = _string.toUpper();
- }
- EmptyUserpic::~EmptyUserpic() = default;
- } // namespace Ui
|