diff --git a/Telegram/Resources/colors.palette b/Telegram/Resources/colors.palette index 581147caf..325e91020 100644 --- a/Telegram/Resources/colors.palette +++ b/Telegram/Resources/colors.palette @@ -258,29 +258,22 @@ historyForwardChooseBg: #0000004c; historyForwardChooseFg: windowFgActive; historyPeer1NameFg: #c03d33; // red -historyPeer1UserpicBg: #ed9482; -historyPeer1UserpicFg: #d3644b; +historyPeer1UserpicBg: #e57979; historyPeer2NameFg: #4fad2d; // green -historyPeer2UserpicBg: #a8db92; -historyPeer2UserpicFg: #75c057; +historyPeer2UserpicBg: #80d066; historyPeer3NameFg: #d09306; // yellow -historyPeer3UserpicBg: #efd289; -historyPeer3UserpicFg: #e4a861; +historyPeer3UserpicBg: #ecd074; historyPeer4NameFg: windowActiveTextFg; // blue -historyPeer4UserpicBg: #8fbfe9; -historyPeer4UserpicFg: #649fd3; +historyPeer4UserpicBg: #6fb1e4; historyPeer5NameFg: #8544d6; // purple -historyPeer5UserpicBg: #9992e4; -historyPeer5UserpicFg: #7b72cf; +historyPeer5UserpicBg: #cc90e2; historyPeer6NameFg: #cd4073; // pink -historyPeer6UserpicBg: #ffa9c3; -historyPeer6UserpicFg: #e87497; +historyPeer6UserpicBg: #f078ae; historyPeer7NameFg: #2996ad; // sea -historyPeer7UserpicBg: #8eccdb; -historyPeer7UserpicFg: #5eb2c7; +historyPeer7UserpicBg: #73cdd0; historyPeer8NameFg: #ce671b; // orange -historyPeer8UserpicBg: #f7b37c; -historyPeer8UserpicFg: #de8d62; +historyPeer8UserpicBg: #fba76f; +historyPeerUserpicFg: windowFgActive; historyScrollBarBg: #556e837a; historyScrollBarBgOver: #556e83bc; diff --git a/Telegram/Resources/icons/userpic_channel.png b/Telegram/Resources/icons/userpic_channel.png deleted file mode 100644 index 1f5fd21f2..000000000 Binary files a/Telegram/Resources/icons/userpic_channel.png and /dev/null differ diff --git a/Telegram/Resources/icons/userpic_channel@2x.png b/Telegram/Resources/icons/userpic_channel@2x.png deleted file mode 100644 index d3301baaf..000000000 Binary files a/Telegram/Resources/icons/userpic_channel@2x.png and /dev/null differ diff --git a/Telegram/Resources/icons/userpic_chat.png b/Telegram/Resources/icons/userpic_chat.png deleted file mode 100644 index 778e505c7..000000000 Binary files a/Telegram/Resources/icons/userpic_chat.png and /dev/null differ diff --git a/Telegram/Resources/icons/userpic_chat@2x.png b/Telegram/Resources/icons/userpic_chat@2x.png deleted file mode 100644 index cc2492297..000000000 Binary files a/Telegram/Resources/icons/userpic_chat@2x.png and /dev/null differ diff --git a/Telegram/Resources/icons/userpic_person.png b/Telegram/Resources/icons/userpic_person.png deleted file mode 100644 index fb28bf3c5..000000000 Binary files a/Telegram/Resources/icons/userpic_person.png and /dev/null differ diff --git a/Telegram/Resources/icons/userpic_person@2x.png b/Telegram/Resources/icons/userpic_person@2x.png deleted file mode 100644 index 4b63a7101..000000000 Binary files a/Telegram/Resources/icons/userpic_person@2x.png and /dev/null differ diff --git a/Telegram/Resources/sample.tdesktop-theme b/Telegram/Resources/sample.tdesktop-theme index 05c99a2d6..a1f2d88b2 100644 --- a/Telegram/Resources/sample.tdesktop-theme +++ b/Telegram/Resources/sample.tdesktop-theme @@ -214,29 +214,22 @@ historyUnreadBarFg: #538bb4; historyForwardChooseBg: #0000004c; historyForwardChooseFg: #ffffff; // windowActiveTextFg; historyPeer1NameFg: #c03d33; -historyPeer1UserpicBg: #ed9482; -historyPeer1UserpicFg: #d3644b; +historyPeer1UserpicBg: #e57979; historyPeer2NameFg: #4fad2d; -historyPeer2UserpicBg: #a8db92; -historyPeer2UserpicFg: #75c057; +historyPeer2UserpicBg: #80d066; historyPeer3NameFg: #d09306; -historyPeer3UserpicBg: #efd289; -historyPeer3UserpicFg: #e4a861; +historyPeer3UserpicBg: #ecd074; historyPeer4NameFg: #168acd; // windowShadowFg; -historyPeer4UserpicBg: #8fbfe9; -historyPeer4UserpicFg: #649fd3; +historyPeer4UserpicBg: #6fb1e4; historyPeer5NameFg: #8544d6; -historyPeer5UserpicBg: #9992e4; -historyPeer5UserpicFg: #7b72cf; +historyPeer5UserpicBg: #cc90e2; historyPeer6NameFg: #cd4073; -historyPeer6UserpicBg: #ffa9c3; -historyPeer6UserpicFg: #e87497; +historyPeer6UserpicBg: #f078ae; historyPeer7NameFg: #2996ad; -historyPeer7UserpicBg: #8eccdb; -historyPeer7UserpicFg: #5eb2c7; +historyPeer7UserpicBg: #73cdd0; historyPeer8NameFg: #ce671b; -historyPeer8UserpicBg: #f7b37c; -historyPeer8UserpicFg: #de8d62; +historyPeer8UserpicBg: #fba76f; +historyPeerUserpicFg: #ffffff; // windowActiveTextFg; historyScrollBarBg: #556e837a; historyScrollBarBgOver: #556e83bc; historyScrollBg: #556e834c; diff --git a/Telegram/SourceFiles/boxes/addcontactbox.cpp b/Telegram/SourceFiles/boxes/addcontactbox.cpp index 037a5e19d..02a570031 100644 --- a/Telegram/SourceFiles/boxes/addcontactbox.cpp +++ b/Telegram/SourceFiles/boxes/addcontactbox.cpp @@ -1201,7 +1201,7 @@ void RevokePublicLinkBox::resizeEvent(QResizeEvent *e) { void RevokePublicLinkBox::paintChat(Painter &p, const ChatRow &row, bool selected) const { auto peer = row.peer; - peer->paintUserpicLeft(p, st::contactsPhotoSize, st::contactsPadding.left(), st::contactsPadding.top(), width()); + peer->paintUserpicLeft(p, st::contactsPadding.left(), st::contactsPadding.top(), width(), st::contactsPhotoSize); p.setPen(st::contactsNameFg); diff --git a/Telegram/SourceFiles/boxes/confirmbox.cpp b/Telegram/SourceFiles/boxes/confirmbox.cpp index f11226cf3..cba2f8959 100644 --- a/Telegram/SourceFiles/boxes/confirmbox.cpp +++ b/Telegram/SourceFiles/boxes/confirmbox.cpp @@ -462,7 +462,6 @@ void RichDeleteMessageBox::deleteAndClear() { ConfirmInviteBox::ConfirmInviteBox(QWidget*, const QString &title, const MTPChatPhoto &photo, int count, const QVector<UserData*> &participants) : _title(this, st::confirmInviteTitle) , _status(this, st::confirmInviteStatus) -, _photo(chatDefPhoto(0)) , _participants(participants) { _title->setText(title); QString status; @@ -483,6 +482,9 @@ ConfirmInviteBox::ConfirmInviteBox(QWidget*, const QString &title, const MTPChat } } } + if (!_photo) { + _photoEmpty.set(0, title); + } } void ConfirmInviteBox::prepare() { @@ -528,12 +530,16 @@ void ConfirmInviteBox::paintEvent(QPaintEvent *e) { Painter p(this); - p.drawPixmap((width() - st::confirmInvitePhotoSize) / 2, st::confirmInvitePhotoTop, _photo->pixCircled(st::confirmInvitePhotoSize, st::confirmInvitePhotoSize)); + 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); + } int sumWidth = _participants.size() * _userWidth; int left = (width() - sumWidth) / 2; for_const (auto user, _participants) { - user->paintUserpicLeft(p, st::confirmInviteUserPhotoSize, left + (_userWidth - st::confirmInviteUserPhotoSize) / 2, st::confirmInviteUserPhotoTop, width()); + user->paintUserpicLeft(p, left + (_userWidth - st::confirmInviteUserPhotoSize) / 2, st::confirmInviteUserPhotoTop, width(), st::confirmInviteUserPhotoSize); left += _userWidth; } } diff --git a/Telegram/SourceFiles/boxes/confirmbox.h b/Telegram/SourceFiles/boxes/confirmbox.h index f14b1ac80..068577d6a 100644 --- a/Telegram/SourceFiles/boxes/confirmbox.h +++ b/Telegram/SourceFiles/boxes/confirmbox.h @@ -207,6 +207,7 @@ private: object_ptr<Ui::FlatLabel> _title; object_ptr<Ui::FlatLabel> _status; ImagePtr _photo; + EmptyUserpic _photoEmpty; QVector<UserData*> _participants; int _userWidth = 0; diff --git a/Telegram/SourceFiles/boxes/contactsbox.cpp b/Telegram/SourceFiles/boxes/contactsbox.cpp index 12b908698..2e656a255 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.cpp +++ b/Telegram/SourceFiles/boxes/contactsbox.cpp @@ -955,7 +955,7 @@ void ContactsBox::Inner::paintDialog(Painter &p, TimeMs ms, PeerData *peer, Cont checkedRatio = data->checkbox->checkedAnimationRatio(); data->checkbox->paint(p, ms, st::contactsPadding.left(), st::contactsPadding.top(), width()); } else { - peer->paintUserpicLeft(p, st::contactsPhotoSize, st::contactsPadding.left(), st::contactsPadding.top(), width()); + peer->paintUserpicLeft(p, st::contactsPadding.left(), st::contactsPadding.top(), width(), st::contactsPhotoSize); } int namex = st::contactsPadding.left() + st::contactsPhotoSize + st::contactsPadding.left(); @@ -1013,7 +1013,7 @@ void ContactsBox::Inner::paintDisabledCheckUserpic(Painter &p, PeerData *peer, i auto iconBorderPen = st::contactsPhotoCheckbox.check.border->p; iconBorderPen.setWidth(st::contactsPhotoCheckbox.selectWidth); - peer->paintUserpicLeft(p, userpicRadius * 2, userpicLeft, userpicTop, width()); + peer->paintUserpicLeft(p, userpicLeft, userpicTop, width(), userpicRadius * 2); { PainterHighQualityEnabler hq(p); diff --git a/Telegram/SourceFiles/boxes/contactsbox.h b/Telegram/SourceFiles/boxes/contactsbox.h index 89bd546c0..6078ec410 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.h +++ b/Telegram/SourceFiles/boxes/contactsbox.h @@ -43,7 +43,7 @@ QString cantInviteError(); inline Ui::RoundImageCheckbox::PaintRoundImage PaintUserpicCallback(PeerData *peer) { return [peer](Painter &p, int x, int y, int outerWidth, int size) { - peer->paintUserpicLeft(p, size, x, y, outerWidth); + peer->paintUserpicLeft(p, x, y, outerWidth, size); }; } diff --git a/Telegram/SourceFiles/boxes/members_box.cpp b/Telegram/SourceFiles/boxes/members_box.cpp index e3a6054a6..daccc73fb 100644 --- a/Telegram/SourceFiles/boxes/members_box.cpp +++ b/Telegram/SourceFiles/boxes/members_box.cpp @@ -271,7 +271,7 @@ void MembersBox::Inner::paintDialog(Painter &p, TimeMs ms, PeerData *peer, Membe data->ripple.reset(); } } - peer->paintUserpicLeft(p, st::contactsPhotoSize, st::contactsPadding.left(), st::contactsPadding.top(), width()); + peer->paintUserpicLeft(p, st::contactsPadding.left(), st::contactsPadding.top(), width(), st::contactsPhotoSize); p.setPen(st::contactsNameFg); diff --git a/Telegram/SourceFiles/boxes/send_files_box.cpp b/Telegram/SourceFiles/boxes/send_files_box.cpp index a12e23b28..8848a29e3 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.cpp +++ b/Telegram/SourceFiles/boxes/send_files_box.cpp @@ -127,9 +127,11 @@ SendFilesBox::SendFilesBox(QWidget*, const QString &phone, const QString &firstn : _contactPhone(phone) , _contactFirstName(firstname) , _contactLastName(lastname) { - _nameText.setText(st::semiboldTextStyle, lng_full_name(lt_first_name, _contactFirstName, lt_last_name, _contactLastName), _textNameOptions); + auto name = lng_full_name(lt_first_name, _contactFirstName, lt_last_name, _contactLastName); + _nameText.setText(st::semiboldTextStyle, name, _textNameOptions); _statusText = _contactPhone; _statusWidth = qMax(_nameText.maxWidth(), st::normalFont->width(_statusText)); + _contactPhotoEmpty.set(0, name); } void SendFilesBox::prepare() { @@ -280,7 +282,7 @@ void SendFilesBox::paintEvent(QPaintEvent *e) { auto &icon = _fileIsImage ? st::historyFileOutImage : st::historyFileOutDocument; icon.paintInCenter(p, inner); } else { - p.drawPixmapLeft(x + st::msgFilePadding.left(), y + st::msgFilePadding.top(), width(), userDefPhoto(1)->pixCircled(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())); diff --git a/Telegram/SourceFiles/boxes/send_files_box.h b/Telegram/SourceFiles/boxes/send_files_box.h index cca2759f3..62ab8a922 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.h +++ b/Telegram/SourceFiles/boxes/send_files_box.h @@ -89,6 +89,7 @@ private: QString _contactPhone; QString _contactFirstName; QString _contactLastName; + EmptyUserpic _contactPhotoEmpty; base::lambda<void(const QStringList &files, bool compressed, const QString &caption, bool ctrlShiftEnter)> _confirmedCallback; base::lambda<void()> _cancelledCallback; diff --git a/Telegram/SourceFiles/core/utils.h b/Telegram/SourceFiles/core/utils.h index 1f6433dd2..7626b9efc 100644 --- a/Telegram/SourceFiles/core/utils.h +++ b/Telegram/SourceFiles/core/utils.h @@ -320,21 +320,24 @@ inline T snap(const T &v, const T &_min, const T &_max) { template <typename T> class ManagedPtr { public: - ManagedPtr() : ptr(0) { - } - ManagedPtr(T *p) : ptr(p) { + ManagedPtr() = default; + ManagedPtr(T *p) : _data(p) { } T *operator->() const { - return ptr; + return _data; } T *v() const { - return ptr; + return _data; + } + + explicit operator bool() const { + return _data != nullptr; } protected: + using Parent = ManagedPtr<T>; + T *_data = nullptr; - T *ptr; - typedef ManagedPtr<T> Parent; }; QString translitRusEng(const QString &rus); diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp index ba405597f..5cf0f3451 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp @@ -66,7 +66,7 @@ void paintRow(Painter &p, const RippleRow *row, History *history, HistoryItem *i if (onlyBackground) return; auto userpicPeer = (history->peer->migrateTo() ? history->peer->migrateTo() : history->peer); - userpicPeer->paintUserpicLeft(p, st::dialogsPhotoSize, st::dialogsPadding.x(), st::dialogsPadding.y(), fullWidth); + userpicPeer->paintUserpicLeft(p, st::dialogsPadding.x(), st::dialogsPadding.y(), fullWidth, st::dialogsPhotoSize); auto nameleft = st::dialogsPadding.x() + st::dialogsPhotoSize + st::dialogsPhotoPadding; auto namewidth = fullWidth - nameleft - st::dialogsPadding.x(); diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index 01e73622d..9f985aa0a 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -297,7 +297,7 @@ void DialogsInner::paintPeerSearchResult(Painter &p, const PeerSearchResult *res auto peer = result->peer; auto userpicPeer = (peer->migrateTo() ? peer->migrateTo() : peer); - userpicPeer->paintUserpicLeft(p, st::dialogsPhotoSize, st::dialogsPadding.x(), st::dialogsPadding.y(), getFullWidth()); + userpicPeer->paintUserpicLeft(p, st::dialogsPadding.x(), st::dialogsPadding.y(), getFullWidth(), st::dialogsPhotoSize); auto nameleft = st::dialogsPadding.x() + st::dialogsPhotoSize + st::dialogsPhotoPadding; auto namewidth = fullWidth - nameleft - st::dialogsPadding.x(); @@ -344,7 +344,7 @@ void DialogsInner::paintSearchInPeer(Painter &p, int fullWidth, bool onlyBackgro p.fillRect(fullRect, st::dialogsBg); if (onlyBackground) return; - _searchInPeer->paintUserpicLeft(p, st::dialogsPhotoSize, st::dialogsPadding.x(), st::dialogsPadding.y(), getFullWidth()); + _searchInPeer->paintUserpicLeft(p, st::dialogsPadding.x(), st::dialogsPadding.y(), getFullWidth(), st::dialogsPhotoSize); auto nameleft = st::dialogsPadding.x() + st::dialogsPhotoSize + st::dialogsPhotoPadding; auto namewidth = fullWidth - nameleft - st::dialogsPadding.x() * 2 - st::dialogsCancelSearch.width; diff --git a/Telegram/SourceFiles/history/field_autocomplete.cpp b/Telegram/SourceFiles/history/field_autocomplete.cpp index b78c84945..69e606dac 100644 --- a/Telegram/SourceFiles/history/field_autocomplete.cpp +++ b/Telegram/SourceFiles/history/field_autocomplete.cpp @@ -618,7 +618,7 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) { } } user->loadUserpic(); - user->paintUserpicLeft(p, st::mentionPhotoSize, st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), width()); + user->paintUserpicLeft(p, st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), width(), st::mentionPhotoSize); p.setPen(selected ? st::mentionNameFgOver : st::mentionNameFg); user->nameText.drawElided(p, 2 * st::mentionPadding.left() + st::mentionPhotoSize, i * st::mentionHeight + st::mentionTop, namewidth); @@ -663,7 +663,7 @@ void FieldAutocompleteInner::paintEvent(QPaintEvent *e) { toHighlight += '@' + user->username; } user->loadUserpic(); - user->paintUserpicLeft(p, st::mentionPhotoSize, st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), width()); + user->paintUserpicLeft(p, st::mentionPadding.left(), i * st::mentionHeight + st::mentionPadding.top(), width(), st::mentionPhotoSize); auto commandText = '/' + toHighlight; diff --git a/Telegram/SourceFiles/history/history.style b/Telegram/SourceFiles/history/history.style index b5c7d2c6e..d029356fd 100644 --- a/Telegram/SourceFiles/history/history.style +++ b/Telegram/SourceFiles/history/history.style @@ -145,23 +145,6 @@ historyViewsOutIcon: icon {{ "history_views", historyOutIconFg }}; historyViewsOutSelectedIcon: icon {{ "history_views", historyOutIconFgSelected }}; historyViewsInvertedIcon: icon {{ "history_views", historySendingInvertedIconFg }}; -historyPeer1UserpicPerson: icon {{ size(120px, 120px), historyPeer1UserpicBg }, { "userpic_person", historyPeer1UserpicFg }}; -historyPeer1UserpicChat: icon {{ size(120px, 120px), historyPeer1UserpicBg }, { "userpic_chat", historyPeer1UserpicFg }}; -historyPeer1UserpicChannel: icon {{ size(120px, 120px), historyPeer1UserpicBg }, { "userpic_channel", historyPeer1UserpicFg }}; -historyPeer2UserpicPerson: icon {{ size(120px, 120px), historyPeer2UserpicBg }, { "userpic_person", historyPeer2UserpicFg }}; -historyPeer2UserpicChat: icon {{ size(120px, 120px), historyPeer2UserpicBg }, { "userpic_chat", historyPeer2UserpicFg }}; -historyPeer2UserpicChannel: icon {{ size(120px, 120px), historyPeer2UserpicBg }, { "userpic_channel", historyPeer2UserpicFg }}; -historyPeer3UserpicPerson: icon {{ size(120px, 120px), historyPeer3UserpicBg }, { "userpic_person", historyPeer3UserpicFg }}; -historyPeer3UserpicChat: icon {{ size(120px, 120px), historyPeer3UserpicBg }, { "userpic_chat", historyPeer3UserpicFg }}; -historyPeer3UserpicChannel: icon {{ size(120px, 120px), historyPeer3UserpicBg }, { "userpic_channel", historyPeer3UserpicFg }}; -historyPeer4UserpicPerson: icon {{ size(120px, 120px), historyPeer4UserpicBg }, { "userpic_person", historyPeer4UserpicFg }}; -historyPeer4UserpicChat: icon {{ size(120px, 120px), historyPeer4UserpicBg }, { "userpic_chat", historyPeer4UserpicFg }}; -historyPeer4UserpicChannel: icon {{ size(120px, 120px), historyPeer4UserpicBg }, { "userpic_channel", historyPeer4UserpicFg }}; -historyPeer5UserpicPerson: icon {{ size(120px, 120px), historyPeer5UserpicBg }, { "userpic_person", historyPeer5UserpicFg }}; -historyPeer6UserpicPerson: icon {{ size(120px, 120px), historyPeer6UserpicBg }, { "userpic_person", historyPeer6UserpicFg }}; -historyPeer7UserpicPerson: icon {{ size(120px, 120px), historyPeer7UserpicBg }, { "userpic_person", historyPeer7UserpicFg }}; -historyPeer8UserpicPerson: icon {{ size(120px, 120px), historyPeer8UserpicBg }, { "userpic_person", historyPeer8UserpicFg }}; - historyComposeField: FlatTextarea { textColor: historyComposeAreaFg; bgColor: historyComposeAreaBg; @@ -391,6 +374,8 @@ historyBubbleTailInRightSelected: icon {{ "bubble_tail-flip_horizontal", msgInBg historyBubbleTailOutRight: icon {{ "bubble_tail-flip_horizontal", msgOutBg }}; historyBubbleTailOutRightSelected: icon {{ "bubble_tail-flip_horizontal", msgOutBgSelected }}; +historyPeerUserpicFont: semiboldFont; + historyStatusFg: windowSubTextFg; historyStatusFgActive: windowActiveTextFg; historyStatusFgTyping: historyStatusFgActive; diff --git a/Telegram/SourceFiles/history/history_media_types.cpp b/Telegram/SourceFiles/history/history_media_types.cpp index 8c0f92b93..a41fb4daa 100644 --- a/Telegram/SourceFiles/history/history_media_types.cpp +++ b/Telegram/SourceFiles/history/history_media_types.cpp @@ -2235,16 +2235,17 @@ HistoryContact::HistoryContact(HistoryItem *parent, int32 userId, const QString , _lname(last) , _phone(App::formatPhone(phone)) { _name.setText(st::semiboldTextStyle, lng_full_name(lt_first_name, first, lt_last_name, last).trimmed(), _textNameOptions); - _phonew = st::normalFont->width(_phone); } void HistoryContact::initDimensions() { _maxw = st::msgFileMinWidth; - _contact = _userId ? App::userLoaded(_userId) : 0; + _contact = _userId ? App::userLoaded(_userId) : nullptr; if (_contact) { _contact->loadUserpic(); + } else { + _photoEmpty.set(qAbs(_userId ? _userId : _parent->id) % kUserColorsCount, _name.originalText()); } if (_contact && _contact->contact > 0) { _linkl = sendMessageClickHandler(_contact); @@ -2305,12 +2306,15 @@ void HistoryContact::draw(Painter &p, const QRect &r, TextSelection selection, T QRect rthumb(rtlrect(st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top() - topMinus, st::msgFileThumbSize, st::msgFileThumbSize, width)); if (_contact) { - _contact->paintUserpic(p, st::msgFileThumbSize, rthumb.x(), rthumb.y()); + _contact->paintUserpic(p, rthumb.x(), rthumb.y(), st::msgFileThumbSize); } else { - p.drawPixmap(rthumb.topLeft(), userDefPhoto(qAbs(_userId) % kUserColorsCount)->pixCircled(st::msgFileThumbSize, st::msgFileThumbSize)); + _photoEmpty.paint(p, st::msgFileThumbPadding.left(), st::msgFileThumbPadding.top() - topMinus, width, st::msgFileThumbSize); } if (selected) { - App::roundRect(p, rthumb, p.textPalette().selectOverlay, SelectedOverlaySmallCorners); + PainterHighQualityEnabler hq(p); + p.setBrush(p.textPalette().selectOverlay); + p.setPen(Qt::NoPen); + p.drawEllipse(rthumb); } bool over = ClickHandler::showAsActive(_linkl); @@ -2323,8 +2327,7 @@ void HistoryContact::draw(Painter &p, const QRect &r, TextSelection selection, T nameright = st::msgFilePadding.left(); statustop = st::msgFileStatusTop - topMinus; - QRect inner(rtlrect(st::msgFilePadding.left(), st::msgFilePadding.top() - topMinus, st::msgFileSize, st::msgFileSize, width)); - p.drawPixmap(inner.topLeft(), userDefPhoto(qAbs(_parent->id) % kUserColorsCount)->pixCircled(st::msgFileSize, st::msgFileSize)); + _photoEmpty.paint(p, st::msgFilePadding.left(), st::msgFilePadding.top() - topMinus, width, st::msgFileSize); } int32 namewidth = width - nameleft - nameright; diff --git a/Telegram/SourceFiles/history/history_media_types.h b/Telegram/SourceFiles/history/history_media_types.h index 0e9dc6f62..e16987a65 100644 --- a/Telegram/SourceFiles/history/history_media_types.h +++ b/Telegram/SourceFiles/history/history_media_types.h @@ -636,6 +636,7 @@ private: int _phonew = 0; QString _fname, _lname, _phone; Text _name; + EmptyUserpic _photoEmpty; ClickHandlerPtr _linkl; int _linkw = 0; diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index 10fe2546b..fcd03cf5e 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -553,7 +553,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) { // paint the userpic if it intersects the painted rect if (userpicTop + st::msgPhotoSize > r.top()) { - message->from()->paintUserpicLeft(p, st::msgPhotoSize, st::historyPhotoLeft, userpicTop, message->history()->width); + message->from()->paintUserpicLeft(p, st::historyPhotoLeft, userpicTop, message->history()->width, st::msgPhotoSize); } return true; }); diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.cpp index 4b9675248..3ccb9c1c4 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_item.cpp @@ -146,7 +146,11 @@ ImagePtr ItemBase::getResultThumb() const { QPixmap ItemBase::getResultContactAvatar(int width, int height) const { if (_result->_type == Result::Type::Contact) { - return userDefPhoto(qHash(_result->_id) % kUserColorsCount)->pixCircled(width, height); + auto result = EmptyUserpic(qHash(_result->_id) % kUserColorsCount, _result->getLayoutTitle()).generate(width); + if (result.height() != height * cIntRetinaFactor()) { + result = result.scaled(QSize(width, height) * cIntRetinaFactor(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + } + return std_::move(result); } return QPixmap(); } diff --git a/Telegram/SourceFiles/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp index be4196344..ba0780a9c 100644 --- a/Telegram/SourceFiles/localstorage.cpp +++ b/Telegram/SourceFiles/localstorage.cpp @@ -3874,7 +3874,7 @@ PeerData *_readPeer(FileReadDescriptor &from, int32 fileVersion = 0) { user->inputUser = MTP_inputUser(MTP_int(peerToUser(user->id)), MTP_long((user->access == UserNoAccess) ? 0 : user->access)); } - user->setUserpic(photoLoc.isNull() ? ImagePtr(userDefPhoto(user->colorIndex)) : ImagePtr(photoLoc)); + user->setUserpic(photoLoc.isNull() ? ImagePtr() : ImagePtr(photoLoc)); } } else if (result->isChat()) { ChatData *chat = result->asChat(); @@ -3902,7 +3902,7 @@ PeerData *_readPeer(FileReadDescriptor &from, int32 fileVersion = 0) { chat->input = MTP_inputPeerChat(MTP_int(peerToChat(chat->id))); chat->inputChat = MTP_int(peerToChat(chat->id)); - chat->setUserpic(photoLoc.isNull() ? ImagePtr(chatDefPhoto(chat->colorIndex)) : ImagePtr(photoLoc)); + chat->setUserpic(photoLoc.isNull() ? ImagePtr() : ImagePtr(photoLoc)); } } else if (result->isChannel()) { ChannelData *channel = result->asChannel(); @@ -3924,7 +3924,7 @@ PeerData *_readPeer(FileReadDescriptor &from, int32 fileVersion = 0) { channel->input = MTP_inputPeerChannel(MTP_int(peerToChannel(channel->id)), MTP_long(access)); channel->inputChannel = MTP_inputChannel(MTP_int(peerToChannel(channel->id)), MTP_long(access)); - channel->setUserpic(photoLoc.isNull() ? ImagePtr((channel->isMegagroup() ? chatDefPhoto(channel->colorIndex) : channelDefPhoto(channel->colorIndex))) : ImagePtr(photoLoc)); + channel->setUserpic(photoLoc.isNull() ? ImagePtr() : ImagePtr(photoLoc)); } } if (!wasLoaded) { diff --git a/Telegram/SourceFiles/profile/profile_block_peer_list.cpp b/Telegram/SourceFiles/profile/profile_block_peer_list.cpp index 069b413c3..c4ddcc820 100644 --- a/Telegram/SourceFiles/profile/profile_block_peer_list.cpp +++ b/Telegram/SourceFiles/profile/profile_block_peer_list.cpp @@ -96,7 +96,7 @@ void PeerListWidget::paintItem(Painter &p, int x, int y, Item *item, bool select } int skip = st::profileMemberPhotoPosition.x(); - item->peer->paintUserpicLeft(p, st::profileMemberPhotoSize, x + st::profileMemberPhotoPosition.x(), y + st::profileMemberPhotoPosition.y(), width()); + item->peer->paintUserpicLeft(p, x + st::profileMemberPhotoPosition.x(), y + st::profileMemberPhotoPosition.y(), width(), st::profileMemberPhotoSize); if (item->name.isEmpty()) { item->name.setText(st::msgNameStyle, App::peerName(item->peer), _textNameOptions); diff --git a/Telegram/SourceFiles/profile/profile_common_groups_section.cpp b/Telegram/SourceFiles/profile/profile_common_groups_section.cpp index 4930077ab..3b5849397 100644 --- a/Telegram/SourceFiles/profile/profile_common_groups_section.cpp +++ b/Telegram/SourceFiles/profile/profile_common_groups_section.cpp @@ -236,7 +236,7 @@ void InnerWidget::paintRow(Painter &p, int index, TimeMs ms) { x += st::profileCommonGroupsPadding.left(); y += st::profileCommonGroupsPadding.top(); - item->peer->paintUserpic(p, st::profileCommonGroupsPhotoSize, rtl() ? (width() - x - st::profileCommonGroupsPhotoSize) : x, y); + item->peer->paintUserpic(p, rtl() ? (width() - x - st::profileCommonGroupsPhotoSize) : x, y, st::profileCommonGroupsPhotoSize); x += st::profileCommonGroupsPhotoSize + st::profileCommonGroupsNameLeft; y += st::profileCommonGroupsNameTop; diff --git a/Telegram/SourceFiles/profile/profile_userpic_button.cpp b/Telegram/SourceFiles/profile/profile_userpic_button.cpp index 3d346493a..fa401d104 100644 --- a/Telegram/SourceFiles/profile/profile_userpic_button.cpp +++ b/Telegram/SourceFiles/profile/profile_userpic_button.cpp @@ -115,7 +115,7 @@ QPixmap UserpicButton::prepareUserpicPixmap() const { image.fill(Qt::transparent); { Painter p(&image); - _peer->paintUserpic(p, width(), 0, 0); + _peer->paintUserpic(p, 0, 0, width()); } return App::pixmapFromImageInPlace(std_::move(image)); } diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index b0bc174ed..8d894744e 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -74,40 +74,140 @@ style::color peerColor(int index) { return peerColors[index]; } -ImagePtr userDefPhoto(int index) { - static const ImagePtr userDefPhotos[kUserColorsCount] = { - generateUserpicImage(st::historyPeer1UserpicPerson), - generateUserpicImage(st::historyPeer2UserpicPerson), - generateUserpicImage(st::historyPeer3UserpicPerson), - generateUserpicImage(st::historyPeer4UserpicPerson), - generateUserpicImage(st::historyPeer5UserpicPerson), - generateUserpicImage(st::historyPeer6UserpicPerson), - generateUserpicImage(st::historyPeer7UserpicPerson), - generateUserpicImage(st::historyPeer8UserpicPerson), +style::color peerUserpicColor(int index) { + static style::color peerColors[kUserColorsCount] = { + st::historyPeer1UserpicBg, + st::historyPeer2UserpicBg, + st::historyPeer3UserpicBg, + st::historyPeer4UserpicBg, + st::historyPeer5UserpicBg, + st::historyPeer6UserpicBg, + st::historyPeer7UserpicBg, + st::historyPeer8UserpicBg, }; - return userDefPhotos[index]; + return peerColors[index]; } -ImagePtr chatDefPhoto(int index) { - static const ImagePtr chatDefPhotos[kChatColorsCount] = { - generateUserpicImage(st::historyPeer1UserpicChat), - generateUserpicImage(st::historyPeer2UserpicChat), - generateUserpicImage(st::historyPeer3UserpicChat), - generateUserpicImage(st::historyPeer4UserpicChat), - }; - return chatDefPhotos[index]; +class EmptyUserpic::Impl { +public: + Impl(int index, const QString &name) : _color(peerUserpicColor(index)) { + fillString(name); + } + + void paint(Painter &p, int x, int y, int size); + +private: + void fillString(const QString &name); + + style::color _color; + QString _string; + +}; + +void EmptyUserpic::Impl::paint(Painter &p, int x, int y, int size) { + auto fontsize = (size * 13) / 33; + auto font = st::historyPeerUserpicFont->f; + font.setPixelSize(fontsize); + + PainterHighQualityEnabler hq(p); + p.setBrush(_color); + p.setPen(Qt::NoPen); + p.drawEllipse(x, y, size, size); + + p.setFont(font); + p.setBrush(Qt::NoBrush); + p.setPen(st::historyPeerUserpicFg); + p.drawText(QRect(x, y, size, size), _string, QTextOption(style::al_center)); } -ImagePtr channelDefPhoto(int index) { - static const ImagePtr channelDefPhotos[kChannelColorsCount] = { - generateUserpicImage(st::historyPeer1UserpicChannel), - generateUserpicImage(st::historyPeer2UserpicChannel), - generateUserpicImage(st::historyPeer3UserpicChannel), - generateUserpicImage(st::historyPeer4UserpicChannel), - }; - return channelDefPhotos[index]; +void EmptyUserpic::Impl::fillString(const QString &name) { + 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 (auto emoji = emojiFromText(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(int index, const QString &name) : _impl(std_::make_unique<Impl>(index, name)) { +} + +void EmptyUserpic::set(int index, const QString &name) { + _impl = std_::make_unique<Impl>(index, name); +} + +void EmptyUserpic::clear() { + _impl.reset(); +} + +void EmptyUserpic::paint(Painter &p, int x, int y, int outerWidth, int size) const { + t_assert(_impl != nullptr); + _impl->paint(p, rtl() ? (outerWidth - x - size) : x, y, size); +} + +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; + using UpdateFlag = Notify::PeerUpdate::Flag; NotifySettings globalNotifyAll, globalNotifyUsers, globalNotifyChats; @@ -115,9 +215,9 @@ NotifySettingsPtr globalNotifyAllPtr = UnknownNotifySettings, globalNotifyUsersP PeerData::PeerData(const PeerId &id) : id(id) , colorIndex(peerColorIndex(id)) -, color(peerColor(colorIndex)) -, _userpic(isUser() ? userDefPhoto(colorIndex) : ((isChat() || isMegagroup()) ? chatDefPhoto(colorIndex) : channelDefPhoto(colorIndex))) { +, color(peerColor(colorIndex)) { nameText.setText(st::msgNameStyle, QString(), _textNameOptions); + _userpicEmpty.set(colorIndex, QString()); } void PeerData::updateNameDelayed(const QString &newName, const QString &newNameOrPhone, const QString &newUsername) { @@ -138,6 +238,9 @@ void PeerData::updateNameDelayed(const QString &newName, const QString &newNameO ++nameVersion; name = newName; nameText.setText(st::msgNameStyle, name, _textNameOptions); + if (!_userpic) { + _userpicEmpty.set(colorIndex, name); + } Notify::PeerUpdate update(this); update.flags |= UpdateFlag::NameChanged; @@ -170,39 +273,55 @@ void PeerData::updateNameDelayed(const QString &newName, const QString &newNameO void PeerData::setUserpic(ImagePtr userpic) { _userpic = userpic; + if (!_userpic || !_userpic->loaded()) { + _userpicEmpty.set(colorIndex, name); + } else { + _userpicEmpty.clear(); + } } ImagePtr PeerData::currentUserpic() const { - if (_userpic->loaded()) { - return _userpic; + if (_userpic) { + _userpic->load(); + if (_userpic->loaded()) { + _userpicEmpty.clear(); + return _userpic; + } } - _userpic->load(); - - if (isUser()) { - return userDefPhoto(colorIndex); - } else if (isMegagroup() || isChat()) { - return chatDefPhoto(colorIndex); - } - return channelDefPhoto(colorIndex); + return ImagePtr(); } -void PeerData::paintUserpic(Painter &p, int size, int x, int y) const { - p.drawPixmap(x, y, currentUserpic()->pixCircled(size, size)); +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); + } } StorageKey PeerData::userpicUniqueKey() const { - if (photoLoc.isNull() || !_userpic->loaded()) { + if (photoLoc.isNull() || !_userpic || !_userpic->loaded()) { return StorageKey(0, (isUser() ? 0x1000 : ((isChat() || isMegagroup()) ? 0x2000 : 0x3000)) | colorIndex); } return storageKey(photoLoc); } void PeerData::saveUserpic(const QString &path, int size) const { - currentUserpic()->pixRounded(size, size, ImageRoundRadius::Small).save(path, "PNG"); + genUserpic(size).save(path, "PNG"); } QPixmap PeerData::genUserpic(int size) const { - return currentUserpic()->pixRounded(size, size, ImageRoundRadius::Small); + if (auto userpic = currentUserpic()) { + return userpic->pixRounded(size, size, ImageRoundRadius::Small); + } + auto result = QImage(QSize(size, size) * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); + result.setDevicePixelRatio(cRetinaFactor()); + result.fill(Qt::transparent); + { + Painter p(&result); + paintUserpic(p, 0, 0, size); + } + return App::pixmapFromImageInPlace(std_::move(result)); } const Text &BotCommand::descriptionText() const { @@ -225,17 +344,17 @@ void UserData::setPhoto(const MTPUserProfilePhoto &p) { // see Local::readPeer a const auto &d(p.c_userProfilePhoto()); newPhotoId = d.vphoto_id.v; newPhotoLoc = App::imageLocation(160, 160, d.vphoto_small); - newPhoto = newPhotoLoc.isNull() ? userDefPhoto(colorIndex) : ImagePtr(newPhotoLoc); + newPhoto = newPhotoLoc.isNull() ? ImagePtr() : ImagePtr(newPhotoLoc); //App::feedPhoto(App::photoFromUserPhoto(peerToUser(id), MTP_int(unixtime()), p)); } break; default: { newPhotoId = 0; if (id == ServiceUserId) { - if (_userpic.v() == userDefPhoto(colorIndex).v()) { + if (!_userpic) { newPhoto = ImagePtr(App::pixmapFromImageInPlace(App::wnd()->iconLarge().scaledToWidth(160, Qt::SmoothTransformation)), "PNG"); } } else { - newPhoto = userDefPhoto(colorIndex); + newPhoto = ImagePtr(); } newPhotoLoc = StorageImageLocation(); } break; @@ -423,13 +542,13 @@ void ChatData::setPhoto(const MTPChatPhoto &p, const PhotoId &phId) { // see Loc newPhotoId = phId; } newPhotoLoc = App::imageLocation(160, 160, d.vphoto_small); - newPhoto = newPhotoLoc.isNull() ? chatDefPhoto(colorIndex) : ImagePtr(newPhotoLoc); -// photoFull = ImagePtr(640, 640, d.vphoto_big, chatDefPhoto(colorIndex)); + newPhoto = newPhotoLoc.isNull() ? ImagePtr() : ImagePtr(newPhotoLoc); +// photoFull = newPhoto ? ImagePtr(640, 640, d.vphoto_big, ImagePtr()) : ImagePtr(); } break; default: { newPhotoId = 0; newPhotoLoc = StorageImageLocation(); - newPhoto = chatDefPhoto(colorIndex); + newPhoto = ImagePtr(); // photoFull = ImagePtr(); } break; } @@ -479,13 +598,13 @@ void ChannelData::setPhoto(const MTPChatPhoto &p, const PhotoId &phId) { // see newPhotoId = phId; } newPhotoLoc = App::imageLocation(160, 160, d.vphoto_small); - newPhoto = newPhotoLoc.isNull() ? (isMegagroup() ? chatDefPhoto(colorIndex) : channelDefPhoto(colorIndex)) : ImagePtr(newPhotoLoc); -// photoFull = ImagePtr(640, 640, d.vphoto_big, (isMegagroup() ? chatDefPhoto(colorIndex) : channelDefPhoto(colorIndex))); + newPhoto = newPhotoLoc.isNull() ? ImagePtr() : ImagePtr(newPhotoLoc); +// photoFull = newPhoto ? ImagePtr(640, 640, d.vphoto_big, newPhoto) : ImagePtr(); } break; default: { newPhotoId = 0; newPhotoLoc = StorageImageLocation(); - newPhoto = (isMegagroup() ? chatDefPhoto(colorIndex) : channelDefPhoto(colorIndex)); + newPhoto = ImagePtr(); // photoFull = ImagePtr(); } break; } diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index f30c21c4e..854d17175 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -204,9 +204,30 @@ static constexpr int kChatColorsCount = 4; static constexpr int kChannelColorsCount = 4; style::color peerColor(int index); -ImagePtr userDefPhoto(int index); -ImagePtr chatDefPhoto(int index); -ImagePtr channelDefPhoto(int index); + +class EmptyUserpic { +public: + EmptyUserpic(); + EmptyUserpic(int index, const QString &name); + + void set(int index, const QString &name); + void clear(); + + explicit operator bool() const { + return (_impl != nullptr); + } + + void paint(Painter &p, int x, int y, int outerWidth, int size) const; + QPixmap generate(int size); + + ~EmptyUserpic(); + +private: + class Impl; + std_::unique_ptr<Impl> _impl; + friend class Impl; + +}; static const PhotoId UnknownPeerPhotoId = 0xFFFFFFFFFFFFFFFFULL; @@ -292,9 +313,9 @@ public: style::color color; void setUserpic(ImagePtr userpic); - void paintUserpic(Painter &p, int size, int x, int y) const; - void paintUserpicLeft(Painter &p, int size, int x, int y, int w) const { - paintUserpic(p, size, rtl() ? (w - x - size) : x, y); + void paintUserpic(Painter &p, int x, int y, int size) const; + void paintUserpicLeft(Painter &p, int x, int y, int w, int size) const { + paintUserpic(p, rtl() ? (w - x - size) : x, y, size); } void loadUserpic(bool loadFirst = false, bool prior = true) { _userpic->load(loadFirst, prior); @@ -331,6 +352,7 @@ protected: ImagePtr _userpic; ImagePtr currentUserpic() const; + mutable EmptyUserpic _userpicEmpty; private: void fillNames(); diff --git a/Telegram/SourceFiles/ui/buttons/peer_avatar_button.cpp b/Telegram/SourceFiles/ui/buttons/peer_avatar_button.cpp index 136c04817..7b264c806 100644 --- a/Telegram/SourceFiles/ui/buttons/peer_avatar_button.cpp +++ b/Telegram/SourceFiles/ui/buttons/peer_avatar_button.cpp @@ -36,7 +36,7 @@ PeerAvatarButton::PeerAvatarButton(QWidget *parent, PeerData *peer, const style: void PeerAvatarButton::paintEvent(QPaintEvent *e) { if (_peer) { Painter p(this); - _peer->paintUserpic(p, _st.photoSize, (_st.size - _st.photoSize) / 2, (_st.size - _st.photoSize) / 2); + _peer->paintUserpic(p, (_st.size - _st.photoSize) / 2, (_st.size - _st.photoSize) / 2, _st.photoSize); } } diff --git a/Telegram/SourceFiles/ui/images.h b/Telegram/SourceFiles/ui/images.h index bf54efad0..ac338b93b 100644 --- a/Telegram/SourceFiles/ui/images.h +++ b/Telegram/SourceFiles/ui/images.h @@ -444,6 +444,11 @@ public: ImagePtr(int32 width, int32 height, const MTPFileLocation &location, ImagePtr def = ImagePtr()); ImagePtr(int32 width, int32 height) : Parent(internal::getImage(width, height)) { } + + explicit operator bool() const { + return (_data != nullptr) && !_data->isNull(); + } + }; inline QSize shrinkToKeepAspect(int32 width, int32 height, int32 towidth, int32 toheight) { diff --git a/Telegram/SourceFiles/window/notifications_manager_default.cpp b/Telegram/SourceFiles/window/notifications_manager_default.cpp index cfed32531..b83406a61 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.cpp +++ b/Telegram/SourceFiles/window/notifications_manager_default.cpp @@ -612,7 +612,7 @@ void Notification::updateNotifyDisplay() { if (!options.hideNameAndPhoto) { _history->peer->loadUserpic(true, true); - _history->peer->paintUserpicLeft(p, st::notifyPhotoSize, st::notifyPhotoPos.x(), st::notifyPhotoPos.y(), width()); + _history->peer->paintUserpicLeft(p, st::notifyPhotoPos.x(), st::notifyPhotoPos.y(), width(), st::notifyPhotoSize); } else { static QPixmap icon = App::pixmapFromImageInPlace(App::wnd()->iconLarge().scaled(st::notifyPhotoSize, st::notifyPhotoSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); icon.setDevicePixelRatio(cRetinaFactor()); @@ -680,7 +680,7 @@ void Notification::updatePeerPhoto() { auto img = _cache.toImage(); { Painter p(&img); - _peer->paintUserpicLeft(p, st::notifyPhotoSize, st::notifyPhotoPos.x(), st::notifyPhotoPos.y(), width()); + _peer->paintUserpicLeft(p, st::notifyPhotoPos.x(), st::notifyPhotoPos.y(), width(), st::notifyPhotoSize); } _cache = App::pixmapFromImageInPlace(std_::move(img)); update(); diff --git a/Telegram/SourceFiles/window/window_theme_preview.cpp b/Telegram/SourceFiles/window/window_theme_preview.cpp index 19b717f1f..3f1bc9d08 100644 --- a/Telegram/SourceFiles/window/window_theme_preview.cpp +++ b/Telegram/SourceFiles/window/window_theme_preview.cpp @@ -34,6 +34,63 @@ namespace Window { namespace Theme { namespace { +QString fillLetters(const QString &name) { + 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 (auto emoji = emojiFromText(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 '-'. + auto result = QString(); + if (!letters.isEmpty()) { + result += 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) { + result += letters[bestIndex]; + } + } + return result.toUpper(); +} + class Generator { public: Generator(const Instance &theme, const CurrentData ¤t); @@ -48,6 +105,7 @@ private: }; struct Row { Text name; + QString letters; enum class Type { User, Group, @@ -107,7 +165,7 @@ private: void paintBubble(const Bubble &bubble); void paintService(QString text); - void paintUserpic(int x, int y, Row::Type type, int index); + void paintUserpic(int x, int y, Row::Type type, int index, QString letters); void setTextPalette(const style::TextPalette &st); void restoreTextPalette(); @@ -156,6 +214,9 @@ void Generator::prepare() { void Generator::addRow(QString name, int peerIndex, QString date, QString text) { Row row; row.name.setText(st::msgNameStyle, name, _textNameOptions); + + row.letters = fillLetters(name); + row.peerIndex = peerIndex; row.date = date; row.text.setRichText(st::dialogsTextStyle, text, _textDlgOptions); @@ -503,7 +564,7 @@ void Generator::paintRow(const Row &row) { if (row.active || row.selected) { _p->fillRect(fullRect, row.active ? st::dialogsBgActive[_palette] : st::dialogsBgOver[_palette]); } - paintUserpic(x + st::dialogsPadding.x(), y + st::dialogsPadding.y(), row.type, row.peerIndex); + paintUserpic(x + st::dialogsPadding.x(), y + st::dialogsPadding.y(), row.type, row.peerIndex, row.letters); auto nameleft = x + st::dialogsPadding.x() + st::dialogsPhotoSize + st::dialogsPhotoPadding; auto namewidth = x + fullWidth - nameleft - st::dialogsPadding.x(); @@ -763,38 +824,33 @@ void Generator::paintService(QString text) { _historyBottom = bubbleTop - st::msgServiceMargin.top(); } -void Generator::paintUserpic(int x, int y, Row::Type type, int index) { - const style::icon *userIcons[] = { - &st::historyPeer1UserpicPerson, - &st::historyPeer2UserpicPerson, - &st::historyPeer3UserpicPerson, - &st::historyPeer4UserpicPerson, - &st::historyPeer5UserpicPerson, - &st::historyPeer6UserpicPerson, - &st::historyPeer7UserpicPerson, - &st::historyPeer8UserpicPerson, +void Generator::paintUserpic(int x, int y, Row::Type type, int index, QString letters) { + style::color colors[] = { + st::historyPeer1UserpicBg, + st::historyPeer2UserpicBg, + st::historyPeer3UserpicBg, + st::historyPeer4UserpicBg, + st::historyPeer5UserpicBg, + st::historyPeer6UserpicBg, + st::historyPeer7UserpicBg, + st::historyPeer8UserpicBg, }; - const style::icon *chatIcons[] = { - &st::historyPeer1UserpicChat, - &st::historyPeer2UserpicChat, - &st::historyPeer3UserpicChat, - &st::historyPeer4UserpicChat, - }; - const style::icon *channelIcons[] = { - &st::historyPeer1UserpicChannel, - &st::historyPeer2UserpicChannel, - &st::historyPeer3UserpicChannel, - &st::historyPeer4UserpicChannel, - }; - auto userpic = (type == Row::Type::User) ? userIcons[index % base::array_size(userIcons)] : (type == Row::Type::Group) ? chatIcons[index % base::array_size(chatIcons)] : channelIcons[index % base::array_size(channelIcons)]; + auto color = colors[index % base::array_size(colors)]; - auto image = QImage(userpic->width() * cIntRetinaFactor(), userpic->height() * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); + auto image = QImage(st::dialogsPhotoSize * cIntRetinaFactor(), st::dialogsPhotoSize * cIntRetinaFactor(), QImage::Format_ARGB32_Premultiplied); image.setDevicePixelRatio(cRetinaFactor()); + image.fill(color[_palette]->c); { Painter p(&image); - userpic->paint(p, 0, 0, userpic->width()); + auto fontsize = (st::dialogsPhotoSize * 13) / 33; + auto font = st::historyPeerUserpicFont->f; + font.setPixelSize(fontsize); + + p.setFont(font); + p.setBrush(Qt::NoBrush); + p.setPen(st::historyPeerUserpicFg[_palette]); + p.drawText(QRect(0, 0, st::dialogsPhotoSize, st::dialogsPhotoSize), letters, QTextOption(style::al_center)); } - image = std_::move(image).scaled(st::dialogsPhotoSize * cIntRetinaFactor(), st::dialogsPhotoSize * cIntRetinaFactor(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); Images::prepareCircle(image); _p->drawImage(rtl() ? (_rect.width() - x - st::dialogsPhotoSize) : x, y, image); }