diff --git a/Telegram/SourceFiles/boxes/confirm_box.cpp b/Telegram/SourceFiles/boxes/confirm_box.cpp index 7abf13013ceecd..dc7a05efe26702 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.cpp +++ b/Telegram/SourceFiles/boxes/confirm_box.cpp @@ -30,6 +30,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "ui/widgets/buttons.h" #include "ui/widgets/labels.h" #include "ui/toast/toast.h" +#include "ui/empty_userpic.h" #include "core/click_handler_types.h" #include "storage/localstorage.h" #include "auth_session.h" @@ -643,7 +644,9 @@ ConfirmInviteBox::ConfirmInviteBox(QWidget*, const QString &title, bool isChanne } } if (!_photo) { - _photoEmpty.set(0, title); + _photoEmpty = std::make_unique( + Data::PeerUserpicColor(0), + title); } } @@ -693,7 +696,7 @@ void ConfirmInviteBox::paintEvent(QPaintEvent *e) { if (_photo) { p.drawPixmap((width() - st::confirmInvitePhotoSize) / 2, st::confirmInvitePhotoTop, _photo->pixCircled(st::confirmInvitePhotoSize, st::confirmInvitePhotoSize)); } else { - _photoEmpty.paint(p, (width() - st::confirmInvitePhotoSize) / 2, st::confirmInvitePhotoTop, width(), st::confirmInvitePhotoSize); + _photoEmpty->paint(p, (width() - st::confirmInvitePhotoSize) / 2, st::confirmInvitePhotoTop, width(), st::confirmInvitePhotoSize); } int sumWidth = _participants.size() * _userWidth; @@ -703,3 +706,5 @@ void ConfirmInviteBox::paintEvent(QPaintEvent *e) { left += _userWidth; } } + +ConfirmInviteBox::~ConfirmInviteBox() = default; diff --git a/Telegram/SourceFiles/boxes/confirm_box.h b/Telegram/SourceFiles/boxes/confirm_box.h index 038f329f14fbf7..4d155da7a92856 100644 --- a/Telegram/SourceFiles/boxes/confirm_box.h +++ b/Telegram/SourceFiles/boxes/confirm_box.h @@ -25,6 +25,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org namespace Ui { class Checkbox; class FlatLabel; +class EmptyUserpic; } // namespace Ui class InformBox; @@ -207,6 +208,7 @@ class DeleteMessagesBox : public BoxContent, public RPCSender { class ConfirmInviteBox : public BoxContent, public RPCSender { public: ConfirmInviteBox(QWidget*, const QString &title, bool isChannel, const MTPChatPhoto &photo, int count, const QVector &participants); + ~ConfirmInviteBox(); protected: void prepare() override; @@ -218,7 +220,7 @@ class ConfirmInviteBox : public BoxContent, public RPCSender { object_ptr _title; object_ptr _status; ImagePtr _photo; - EmptyUserpic _photoEmpty; + std::unique_ptr _photoEmpty; QVector _participants; int _userWidth = 0; diff --git a/Telegram/SourceFiles/boxes/send_files_box.cpp b/Telegram/SourceFiles/boxes/send_files_box.cpp index ccf7b2a4aa028d..01766f7c893e55 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.cpp +++ b/Telegram/SourceFiles/boxes/send_files_box.cpp @@ -28,6 +28,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "ui/widgets/checkbox.h" #include "ui/widgets/buttons.h" #include "ui/widgets/input_fields.h" +#include "ui/empty_userpic.h" #include "styles/style_history.h" #include "styles/style_boxes.h" #include "media/media_clip_reader.h" @@ -235,7 +236,9 @@ SendFilesBox::SendFilesBox(QWidget*, const QString &phone, const QString &firstn _nameText.setText(st::semiboldTextStyle, name, _textNameOptions); _statusText = _contactPhone; _statusWidth = qMax(_nameText.maxWidth(), st::normalFont->width(_statusText)); - _contactPhotoEmpty.set(0, name); + _contactPhotoEmpty = std::make_unique( + Data::PeerUserpicColor(0), + name); } void SendFilesBox::prepare() { @@ -400,7 +403,7 @@ void SendFilesBox::paintEvent(QPaintEvent *e) { auto &icon = _fileIsAudio ? st::historyFileOutPlay : _fileIsImage ? st::historyFileOutImage : st::historyFileOutDocument; icon.paintInCenter(p, inner); } else { - _contactPhotoEmpty.paint(p, x + st::msgFilePadding.left(), y + st::msgFilePadding.top(), width(), st::msgFileSize); + _contactPhotoEmpty->paint(p, x + st::msgFilePadding.left(), y + st::msgFilePadding.top(), width(), st::msgFileSize); } } else { QRect rthumb(rtlrect(x + st::msgFileThumbPadding.left(), y + st::msgFileThumbPadding.top(), st::msgFileThumbSize, st::msgFileThumbSize, width())); @@ -457,6 +460,8 @@ void SendFilesBox::onSend(bool ctrlShiftEnter) { closeBox(); } +SendFilesBox::~SendFilesBox() = default; + EditCaptionBox::EditCaptionBox(QWidget*, HistoryMedia *media, FullMsgId msgId) : _msgId(msgId) { Expects(media->canEditCaption()); diff --git a/Telegram/SourceFiles/boxes/send_files_box.h b/Telegram/SourceFiles/boxes/send_files_box.h index 724b0689b4ae77..f17ffefc4791ad 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.h +++ b/Telegram/SourceFiles/boxes/send_files_box.h @@ -27,6 +27,7 @@ namespace Ui { class Checkbox; class RoundButton; class InputArea; +class EmptyUserpic; } // namespace Ui class SendFilesBox : public BoxContent { @@ -44,6 +45,8 @@ class SendFilesBox : public BoxContent { _cancelledCallback = std::move(callback); } + ~SendFilesBox(); + protected: void prepare() override; void setInnerFocus() override; @@ -96,7 +99,7 @@ private slots: QString _contactPhone; QString _contactFirstName; QString _contactLastName; - EmptyUserpic _contactPhotoEmpty; + std::unique_ptr _contactPhotoEmpty; base::lambda information, bool compressed, const QString &caption, bool ctrlShiftEnter)> _confirmedCallback; base::lambda _cancelledCallback; diff --git a/Telegram/SourceFiles/calls/calls_panel.cpp b/Telegram/SourceFiles/calls/calls_panel.cpp index 4bb59dc3600adc..940188513d7ca6 100644 --- a/Telegram/SourceFiles/calls/calls_panel.cpp +++ b/Telegram/SourceFiles/calls/calls_panel.cpp @@ -29,6 +29,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "ui/widgets/shadow.h" #include "ui/effects/ripple_animation.h" #include "ui/wrap/fade_wrap.h" +#include "ui/empty_userpic.h" #include "messenger.h" #include "mainwindow.h" #include "lang/lang_keys.h" @@ -484,7 +485,10 @@ void Panel::createUserpicCache(ImagePtr image) { filled.setDevicePixelRatio(cRetinaFactor()); { Painter p(&filled); - EmptyUserpic(_user->id, _user->name).paintSquare(p, 0, 0, st::callWidth, st::callWidth); + Ui::EmptyUserpic( + Data::PeerUserpicColor(_user->id), + _user->name + ).paintSquare(p, 0, 0, st::callWidth, st::callWidth); } Images::prepareRound(filled, ImageRoundRadius::Large, ImageRoundCorner::TopLeft | ImageRoundCorner::TopRight); _userPhoto = App::pixmapFromImageInPlace(std::move(filled)); diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index b6eb50dc132c8a..14fe3e9854f1ba 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -35,29 +35,25 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "messenger.h" #include "mainwindow.h" #include "window/window_controller.h" +#include "ui/empty_userpic.h" namespace { constexpr auto kUpdateFullPeerTimeout = TimeMs(5000); // Not more than once in 5 seconds. constexpr auto kUserpicSize = 160; -int peerColorIndex(const PeerId &peer) { - auto myId = Auth().userId(); - auto peerId = peerToBareInt(peer); - auto both = (QByteArray::number(peerId) + QByteArray::number(myId)).mid(0, 15); - uchar md5[16]; - hashMd5(both.constData(), both.size(), md5); - return (md5[peerId & 0x0F] & (peerIsUser(peer) ? 0x07 : 0x03)); +} // namespace + +namespace Data { + +int PeerColorIndex(int32 bareId) { + const auto index = std::abs(bareId) % 7; + const int map[] = { 0, 7, 4, 1, 6, 3, 5 }; + return map[index]; } -ImagePtr generateUserpicImage(const style::icon &icon) { - auto data = QImage(icon.size() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); - data.setDevicePixelRatio(cRetinaFactor()); - { - Painter p(&data); - icon.paint(p, 0, 0, icon.width()); - } - return ImagePtr(App::pixmapFromImageInPlace(std::move(data)), "PNG"); +int PeerColorIndex(PeerId peerId) { + return PeerColorIndex(peerToBareInt(peerId)); } style::color PeerUserpicColor(PeerId peerId) { @@ -74,190 +70,7 @@ style::color PeerUserpicColor(PeerId peerId) { return colors[PeerColorIndex(peerId)]; } -} // namespace - -int PeerColorIndex(int32 bareId) { - const auto index = std::abs(bareId) % 7; - const int map[] = { 0, 7, 4, 1, 6, 3, 5 }; - return map[index]; -} - -int PeerColorIndex(PeerId peerId) { - return PeerColorIndex(peerToBareInt(peerId)); -} - -class EmptyUserpic::Impl { -public: - Impl(PeerId peerId, const QString &name) - : _color(PeerUserpicColor(peerId)) { - fillString(name); - } - - void paint(Painter &p, int x, int y, int size); - void paintRounded(Painter &p, int x, int y, int size); - void paintSquare(Painter &p, int x, int y, int size); - StorageKey uniqueKey() const; - -private: - template - void paint(Painter &p, int x, int y, int size, PaintBackground paintBackground); - - void fillString(const QString &name); - - style::color _color; - QString _string; - -}; - -template -void EmptyUserpic::Impl::paint(Painter &p, int x, int y, int size, PaintBackground paintBackground) { - auto fontsize = (size * 13) / 33; - auto font = st::historyPeerUserpicFont->f; - font.setPixelSize(fontsize); - - PainterHighQualityEnabler hq(p); - p.setBrush(_color); - p.setPen(Qt::NoPen); - paintBackground(); - - 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::Impl::paint(Painter &p, int x, int y, int size) { - paint(p, x, y, size, [&p, x, y, size] { - p.drawEllipse(x, y, size, size); - }); -} - -void EmptyUserpic::Impl::paintRounded(Painter &p, int x, int y, int size) { - paint(p, x, y, size, [&p, x, y, size] { - p.drawRoundedRect(x, y, size, size, st::buttonRadius, st::buttonRadius); - }); -} - -void EmptyUserpic::Impl::paintSquare(Painter &p, int x, int y, int size) { - paint(p, x, y, size, [&p, x, y, size] { - p.fillRect(x, y, size, size, p.brush()); - }); -} - -StorageKey EmptyUserpic::Impl::uniqueKey() const { - auto first = 0xFFFFFFFF00000000ULL | anim::getPremultiplied(_color->c); - auto second = uint64(0); - memcpy(&second, _string.constData(), qMin(sizeof(second), _string.size() * sizeof(QChar))); - return StorageKey(first, second); -} - -void EmptyUserpic::Impl::fillString(const QString &name) { - QList letters; - QList levels; - auto level = 0; - auto letterFound = false; - auto ch = name.constData(), end = ch + name.size(); - while (ch != end) { - auto emojiLength = 0; - if (auto emoji = 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 && chIsDiac(*(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; - -EmptyUserpic::EmptyUserpic(PeerId peerId, const QString &name) -: _impl(std::make_unique(peerId, name)) { -} - -EmptyUserpic::EmptyUserpic(const QString &nonce, const QString &name) -: EmptyUserpic(qHash(nonce), name) { -} - -void EmptyUserpic::set(PeerId peerId, const QString &name) { - _impl = std::make_unique(peerId, name); -} - -void EmptyUserpic::clear() { - _impl.reset(); -} - -void EmptyUserpic::paint(Painter &p, int x, int y, int outerWidth, int size) const { - Expects(_impl != nullptr); - _impl->paint(p, rtl() ? (outerWidth - x - size) : x, y, size); -} - -void EmptyUserpic::paintRounded(Painter &p, int x, int y, int outerWidth, int size) const { - Expects(_impl != nullptr); - _impl->paintRounded(p, rtl() ? (outerWidth - x - size) : x, y, size); -} - -void EmptyUserpic::paintSquare(Painter &p, int x, int y, int outerWidth, int size) const { - Expects(_impl != nullptr); - _impl->paintSquare(p, rtl() ? (outerWidth - x - size) : x, y, size); -} - -StorageKey EmptyUserpic::uniqueKey() const { - Expects(_impl != nullptr); - return _impl->uniqueKey(); -} - -QPixmap EmptyUserpic::generate(int size) { - auto result = QImage(QSize(size, size) * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); - result.setDevicePixelRatio(cRetinaFactor()); - result.fill(Qt::transparent); - { - Painter p(&result); - paint(p, 0, 0, size, size); - } - return App::pixmapFromImageInPlace(std::move(result)); -} - -EmptyUserpic::~EmptyUserpic() = default; +} // namespace Data using UpdateFlag = Notify::PeerUpdate::Flag; @@ -285,13 +98,11 @@ void PeerClickHandler::onClick(Qt::MouseButton button) const { } PeerData::PeerData(const PeerId &id) -: id(id) { +: id(id) +, _userpicEmpty(createEmptyUserpic()) { nameText.setText(st::msgNameStyle, QString(), _textNameOptions); - _userpicEmpty.set(id, QString()); } -PeerData::~PeerData() = default; - void PeerData::updateNameDelayed( const QString &newName, const QString &newNameOrPhone, @@ -314,9 +125,7 @@ void PeerData::updateNameDelayed( ++nameVersion; name = newName; nameText.setText(st::msgNameStyle, name, _textNameOptions); - if (useEmptyUserpic()) { - _userpicEmpty.set(id, name); - } + refreshEmptyUserpic(); Notify::PeerUpdate update(this); update.flags |= UpdateFlag::NameChanged; @@ -344,6 +153,16 @@ void PeerData::updateNameDelayed( Notify::PeerUpdated().notify(update, true); } +std::unique_ptr PeerData::createEmptyUserpic() const { + return std::make_unique( + Data::PeerUserpicColor(id), + name); +} + +void PeerData::refreshEmptyUserpic() const { + _userpicEmpty = useEmptyUserpic() ? createEmptyUserpic() : nullptr; +} + ClickHandlerPtr PeerData::createOpenLink() { return MakeShared(this); } @@ -355,11 +174,7 @@ void PeerData::setUserpic( _userpicPhotoId = photoId; _userpic = userpic; _userpicLocation = location; - if (useEmptyUserpic()) { - _userpicEmpty.set(id, name); - } else { - _userpicEmpty.clear(); - } + refreshEmptyUserpic(); } void PeerData::setUserpicPhoto(const MTPPhoto &data) { @@ -381,7 +196,7 @@ ImagePtr PeerData::currentUserpic() const { _userpic->load(); if (_userpic->loaded()) { if (!useEmptyUserpic()) { - _userpicEmpty.clear(); + _userpicEmpty = nullptr; } return _userpic; } @@ -393,7 +208,7 @@ void PeerData::paintUserpic(Painter &p, int x, int y, int size) const { if (auto userpic = currentUserpic()) { p.drawPixmap(x, y, userpic->pixCircled(size, size)); } else { - _userpicEmpty.paint(p, x, y, x + size + x, size); + _userpicEmpty->paint(p, x, y, x + size + x, size); } } @@ -401,7 +216,7 @@ void PeerData::paintUserpicRounded(Painter &p, int x, int y, int size) const { if (auto userpic = currentUserpic()) { p.drawPixmap(x, y, userpic->pixRounded(size, size, ImageRoundRadius::Small)); } else { - _userpicEmpty.paintRounded(p, x, y, x + size + x, size); + _userpicEmpty->paintRounded(p, x, y, x + size + x, size); } } @@ -409,13 +224,13 @@ void PeerData::paintUserpicSquare(Painter &p, int x, int y, int size) const { if (auto userpic = currentUserpic()) { p.drawPixmap(x, y, userpic->pix(size, size)); } else { - _userpicEmpty.paintSquare(p, x, y, x + size + x, size); + _userpicEmpty->paintSquare(p, x, y, x + size + x, size); } } StorageKey PeerData::userpicUniqueKey() const { if (useEmptyUserpic()) { - return _userpicEmpty.uniqueKey(); + return _userpicEmpty->uniqueKey(); } return storageKey(_userpicLocation); } @@ -456,17 +271,6 @@ QPixmap PeerData::genUserpicRounded(int size) const { return App::pixmapFromImageInPlace(std::move(result)); } -const Text &BotCommand::descriptionText() const { - if (_descriptionText.isEmpty() && !_description.isEmpty()) { - _descriptionText.setText(st::defaultTextStyle, _description, _textNameOptions); - } - return _descriptionText; -} - -bool UserData::canShareThisContact() const { - return canShareThisContactFast() || !App::phoneFromSharedContact(peerToUser(id)).isEmpty(); -} - void PeerData::updateUserpic( PhotoId photoId, const MTPFileLocation &location) { @@ -498,16 +302,6 @@ void PeerData::clearUserpic() { }(); } -// see Local::readPeer as well -void UserData::setPhoto(const MTPUserProfilePhoto &photo) { - if (photo.type() == mtpc_userProfilePhoto) { - const auto &data = photo.c_userProfilePhoto(); - updateUserpic(data.vphoto_id.v, data.vphoto_small); - } else { - clearUserpic(); - } -} - void PeerData::fillNames() { _nameWords.clear(); _nameFirstChars.clear(); @@ -530,6 +324,29 @@ void PeerData::fillNames() { } } +PeerData::~PeerData() = default; + +const Text &BotCommand::descriptionText() const { + if (_descriptionText.isEmpty() && !_description.isEmpty()) { + _descriptionText.setText(st::defaultTextStyle, _description, _textNameOptions); + } + return _descriptionText; +} + +bool UserData::canShareThisContact() const { + return canShareThisContactFast() || !App::phoneFromSharedContact(peerToUser(id)).isEmpty(); +} + +// see Local::readPeer as well +void UserData::setPhoto(const MTPUserProfilePhoto &photo) { + if (photo.type() == mtpc_userProfilePhoto) { + const auto &data = photo.c_userProfilePhoto(); + updateUserpic(data.vphoto_id.v, data.vphoto_small); + } else { + clearUserpic(); + } +} + bool UserData::setAbout(const QString &newAbout) { if (_about == newAbout) { return false; diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h index 208c87f6c698dd..87c0119a64a6ca 100644 --- a/Telegram/SourceFiles/data/data_peer.h +++ b/Telegram/SourceFiles/data/data_peer.h @@ -24,53 +24,22 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "data/data_flags.h" #include "data/data_notify_settings.h" -int PeerColorIndex(PeerId peerId); -int PeerColorIndex(int32 bareId); - -class EmptyUserpic { -public: - EmptyUserpic(); - EmptyUserpic(PeerId peerId, const QString &name); - EmptyUserpic(const QString &nonce, const QString &name); - - void set(PeerId peerId, const QString &name); - void clear(); +namespace Ui { +class EmptyUserpic; +} // namespace Ui - explicit operator bool() const { - return (_impl != nullptr); - } - - void paint( - Painter &p, - int x, - int y, - int outerWidth, - int size) const; - void paintRounded( - Painter &p, - int x, - int y, - int outerWidth, - int size) const; - void paintSquare( - Painter &p, - int x, - int y, - int outerWidth, - int size) const; - QPixmap generate(int size); - StorageKey uniqueKey() const; - - ~EmptyUserpic(); +class PeerData; +class UserData; +class ChatData; +class ChannelData; -private: - class Impl; - std::unique_ptr _impl; - friend class Impl; +namespace Data { -}; +int PeerColorIndex(PeerId peerId); +int PeerColorIndex(int32 bareId); +style::color PeerUserpicColor(PeerId peerId); -class PeerData; +} // namespace Data class PeerClickHandler : public ClickHandler { public: @@ -86,10 +55,6 @@ class PeerClickHandler : public ClickHandler { }; -class UserData; -class ChatData; -class ChannelData; - class PeerData { protected: PeerData(const PeerId &id); @@ -270,12 +235,14 @@ class PeerData { private: void fillNames(); + std::unique_ptr createEmptyUserpic() const; + void refreshEmptyUserpic() const; static constexpr auto kUnknownPhotoId = PhotoId(0xFFFFFFFFFFFFFFFFULL); ImagePtr _userpic; PhotoId _userpicPhotoId = kUnknownPhotoId; - mutable EmptyUserpic _userpicEmpty; + mutable std::unique_ptr _userpicEmpty; StorageImageLocation _userpicLocation; Data::NotifySettings _notify; diff --git a/Telegram/SourceFiles/history/history_media_types.cpp b/Telegram/SourceFiles/history/history_media_types.cpp index 0364e70f74a724..04d93954f35735 100644 --- a/Telegram/SourceFiles/history/history_media_types.cpp +++ b/Telegram/SourceFiles/history/history_media_types.cpp @@ -38,6 +38,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "window/window_controller.h" #include "styles/style_history.h" #include "calls/calls_instance.h" +#include "ui/empty_userpic.h" namespace { @@ -2981,8 +2982,8 @@ void HistoryContact::initDimensions() { if (_contact) { _contact->loadUserpic(); } else { - _photoEmpty.set( - _userId ? _userId : _parent->id, + _photoEmpty = std::make_unique( + Data::PeerUserpicColor(_userId ? _userId : _parent->id), _name.originalText()); } if (_contact && _contact->contact > 0) { @@ -3046,7 +3047,7 @@ void HistoryContact::draw(Painter &p, const QRect &r, TextSelection selection, T if (_contact) { _contact->paintUserpic(p, rthumb.x(), rthumb.y(), st::msgFileThumbSize); } else { - _photoEmpty.paint(p, st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top() - topMinus, width, st::msgFileThumbSize); + _photoEmpty->paint(p, st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top() - topMinus, width, st::msgFileThumbSize); } if (selected) { PainterHighQualityEnabler hq(p); @@ -3065,7 +3066,7 @@ void HistoryContact::draw(Painter &p, const QRect &r, TextSelection selection, T nameright = st::msgFilePadding.left(); statustop = st::msgFileStatusTop - topMinus; - _photoEmpty.paint(p, st::msgFilePadding.left(), st::msgFilePadding.top() - topMinus, width, st::msgFileSize); + _photoEmpty->paint(p, st::msgFilePadding.left(), st::msgFilePadding.top() - topMinus, width, st::msgFileSize); } int32 namewidth = width - nameleft - nameright; @@ -3133,6 +3134,8 @@ void HistoryContact::updateSentMedia(const MTPMessageMedia &media) { } } +HistoryContact::~HistoryContact() = default; + HistoryCall::HistoryCall(not_null parent, const MTPDmessageActionPhoneCall &call) : HistoryMedia(parent) , _reason(GetReason(call)) { if (_parent->out()) { diff --git a/Telegram/SourceFiles/history/history_media_types.h b/Telegram/SourceFiles/history/history_media_types.h index 4f697e8766792c..e3876753487472 100644 --- a/Telegram/SourceFiles/history/history_media_types.h +++ b/Telegram/SourceFiles/history/history_media_types.h @@ -33,6 +33,10 @@ class Playback; } // namespace Clip } // namespace Media +namespace Ui { +class EmptyUserpic; +} // namespace Ui + void HistoryInitMedia(); class HistoryFileMedia : public HistoryMedia { @@ -716,6 +720,8 @@ class HistoryContact : public HistoryMedia { return _phone; } + ~HistoryContact(); + private: int32 _userId = 0; UserData *_contact = nullptr; @@ -723,7 +729,7 @@ class HistoryContact : public HistoryMedia { int _phonew = 0; QString _fname, _lname, _phone; Text _name; - EmptyUserpic _photoEmpty; + std::unique_ptr _photoEmpty; ClickHandlerPtr _linkl; int _linkw = 0; diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index 39d60facbb76d7..6d4f9565097655 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -66,7 +66,7 @@ style::color FromNameFg(not_null peer, bool selected) { st::historyPeer7NameFgSelected, st::historyPeer8NameFgSelected, }; - return colors[PeerColorIndex(peer->id)]; + return colors[Data::PeerColorIndex(peer->id)]; } else { const style::color colors[] = { st::historyPeer1NameFg, @@ -78,7 +78,7 @@ style::color FromNameFg(not_null peer, bool selected) { st::historyPeer7NameFg, st::historyPeer8NameFg, }; - return colors[PeerColorIndex(peer->id)]; + return colors[Data::PeerColorIndex(peer->id)]; } } diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.cpp index 922167d649e5e4..50293055a6b163 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.cpp @@ -27,6 +27,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "inline_bots/inline_bot_layout_internal.h" #include "storage/localstorage.h" #include "mainwidget.h" +#include "ui/empty_userpic.h" namespace InlineBots { namespace Layout { @@ -153,8 +154,8 @@ ImagePtr ItemBase::getResultThumb() const { QPixmap ItemBase::getResultContactAvatar(int width, int height) const { if (_result->_type == Result::Type::Contact) { - auto result = EmptyUserpic( - _result->_id, + auto result = Ui::EmptyUserpic( + Data::PeerUserpicColor(qHash(_result->_id)), _result->getLayoutTitle() ).generate(width); if (result.height() != height * cIntRetinaFactor()) { diff --git a/Telegram/SourceFiles/ui/empty_userpic.cpp b/Telegram/SourceFiles/ui/empty_userpic.cpp new file mode 100644 index 00000000000000..f1b617eeef8b93 --- /dev/null +++ b/Telegram/SourceFiles/ui/empty_userpic.cpp @@ -0,0 +1,159 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org +*/ +#include "ui/empty_userpic.h" + +#include "data/data_peer.h" +#include "styles/style_history.h" + +namespace Ui { + +EmptyUserpic::EmptyUserpic(const style::color &color, const QString &name) +: _color(color) { + fillString(name); +} + +template +void EmptyUserpic::paint( + Painter &p, + int x, + int y, + int outerWidth, + int size, + Callback paintBackground) const { + x = rtl() ? (outerWidth - x - size) : x; + + const auto fontsize = (size * 13) / 33; + auto font = st::historyPeerUserpicFont->f; + font.setPixelSize(fontsize); + + PainterHighQualityEnabler hq(p); + p.setBrush(_color); + p.setPen(Qt::NoPen); + paintBackground(); + + 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::paint( + Painter &p, + int x, + int y, + int outerWidth, + int size) const { + paint(p, x, y, outerWidth, size, [&p, x, y, size] { + p.drawEllipse(x, y, size, size); + }); +} + +void EmptyUserpic::paintRounded(Painter &p, int x, int y, int outerWidth, int size) const { + paint(p, x, y, outerWidth, size, [&p, x, y, size] { + p.drawRoundedRect(x, y, size, size, st::buttonRadius, st::buttonRadius); + }); +} + +void EmptyUserpic::paintSquare(Painter &p, int x, int y, int outerWidth, int size) const { + paint(p, x, y, outerWidth, size, [&p, x, y, size] { + p.fillRect(x, y, size, size, p.brush()); + }); +} + +StorageKey EmptyUserpic::uniqueKey() const { + auto first = 0xFFFFFFFF00000000ULL | anim::getPremultiplied(_color->c); + auto second = uint64(0); + memcpy(&second, _string.constData(), qMin(sizeof(second), _string.size() * sizeof(QChar))); + return StorageKey(first, second); +} + +QPixmap EmptyUserpic::generate(int size) { + auto result = QImage(QSize(size, size) * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); + result.setDevicePixelRatio(cRetinaFactor()); + result.fill(Qt::transparent); + { + Painter p(&result); + paint(p, 0, 0, size, size); + } + return App::pixmapFromImageInPlace(std::move(result)); +} + +void EmptyUserpic::fillString(const QString &name) { + QList letters; + QList levels; + + auto level = 0; + auto letterFound = false; + auto ch = name.constData(), end = ch + name.size(); + while (ch != end) { + auto emojiLength = 0; + if (auto emoji = 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 && chIsDiac(*(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 diff --git a/Telegram/SourceFiles/ui/empty_userpic.h b/Telegram/SourceFiles/ui/empty_userpic.h new file mode 100644 index 00000000000000..22e193215be680 --- /dev/null +++ b/Telegram/SourceFiles/ui/empty_userpic.h @@ -0,0 +1,69 @@ +/* +This file is part of Telegram Desktop, +the official desktop version of Telegram messaging app, see https://telegram.org + +Telegram Desktop is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +It is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +In addition, as a special exception, the copyright holders give permission +to link the code of portions of this program with the OpenSSL library. + +Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE +Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org +*/ +#pragma once + +namespace Ui { + +class EmptyUserpic { +public: + EmptyUserpic(const style::color &color, const QString &name); + + void paint( + Painter &p, + int x, + int y, + int outerWidth, + int size) const; + void paintRounded( + Painter &p, + int x, + int y, + int outerWidth, + int size) const; + void paintSquare( + Painter &p, + int x, + int y, + int outerWidth, + int size) const; + QPixmap generate(int size); + StorageKey uniqueKey() const; + + ~EmptyUserpic(); + +private: + template + void paint( + Painter &p, + int x, + int y, + int outerWidth, + int size, + Callback paintBackground) const; + + void fillString(const QString &name); + + style::color _color; + QString _string; + +}; + +} // namespace Ui diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt index 0985caa7d2e53b..faeb62ff0d4b32 100644 --- a/Telegram/gyp/telegram_sources.txt +++ b/Telegram/gyp/telegram_sources.txt @@ -594,6 +594,8 @@ <(src_loc)/ui/countryinput.h <(src_loc)/ui/emoji_config.cpp <(src_loc)/ui/emoji_config.h +<(src_loc)/ui/empty_userpic.cpp +<(src_loc)/ui/empty_userpic.h <(src_loc)/ui/focus_persister.h <(src_loc)/ui/images.cpp <(src_loc)/ui/images.h