From a3e8e379dd9e49ddcfb7c835dcc062d241b923b1 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 9 Sep 2021 11:47:02 +0300 Subject: [PATCH] Show userpics in full who read list. --- Telegram/SourceFiles/api/api_who_read.cpp | 23 ++- Telegram/SourceFiles/core/launcher.cpp | 1 - .../history/history_inner_widget.cpp | 10 +- Telegram/SourceFiles/ui/chat/chat.style | 12 ++ .../ui/controls/who_read_context_action.cpp | 145 +++++++++++++++++- 5 files changed, 169 insertions(+), 22 deletions(-) diff --git a/Telegram/SourceFiles/api/api_who_read.cpp b/Telegram/SourceFiles/api/api_who_read.cpp index c7d420633..b14de91af 100644 --- a/Telegram/SourceFiles/api/api_who_read.cpp +++ b/Telegram/SourceFiles/api/api_who_read.cpp @@ -97,6 +97,13 @@ struct State { return result; } +[[nodiscard]] QImage GenerateUserpic(Userpic &userpic, int size) { + size *= style::DevicePixelRatio(); + auto result = userpic.peer->generateUserpicImage(userpic.view, size); + result.setDevicePixelRatio(style::DevicePixelRatio()); + return result; +} + [[nodiscard]] bool ListUnknown( const std::vector &list, not_null item) { @@ -233,13 +240,9 @@ void RegenerateUserpics(not_null state, int small, int large) { continue; } participant.userpicKey = userpic.uniqueKey = key; - participant.userpicLarge = peer->generateUserpicImage( - userpic.view, - large); + participant.userpicLarge = GenerateUserpic(userpic, large); if (i < Ui::WhoReadParticipant::kMaxSmallUserpics) { - participant.userpicSmall = peer->generateUserpicImage( - userpic.view, - small); + participant.userpicSmall = GenerateUserpic(userpic, small); } } } @@ -259,16 +262,12 @@ void RegenerateParticipants(not_null state, int small, int large) { } now.push_back({ .name = peer->name, - .userpicLarge = peer->generateUserpicImage( - userpic.view, - large), + .userpicLarge = GenerateUserpic(userpic, large), .userpicKey = userpic.uniqueKey, .id = id, }); if (now.size() <= Ui::WhoReadParticipant::kMaxSmallUserpics) { - now.back().userpicSmall = peer->generateUserpicImage( - userpic.view, - small); + now.back().userpicSmall = GenerateUserpic(userpic, small); } } RegenerateUserpics(state, small, large); diff --git a/Telegram/SourceFiles/core/launcher.cpp b/Telegram/SourceFiles/core/launcher.cpp index f89ae874f..cdeb89ac1 100644 --- a/Telegram/SourceFiles/core/launcher.cpp +++ b/Telegram/SourceFiles/core/launcher.cpp @@ -330,7 +330,6 @@ int Launcher::exec() { // Must be started before Sandbox is created. Platform::start(); - auto result = executeApplication(); DEBUG_LOG(("Telegram finished, result: %1").arg(result)); diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 5880b567e..bc9825ee2 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -1564,7 +1564,11 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { isUponSelected = hasSelected; } - _menu = base::make_unique_q(this); + const auto hasWhoReadItem = _dragStateItem + && Api::WhoReadExists(_dragStateItem); + _menu = base::make_unique_q( + this, + hasWhoReadItem ? st::whoReadMenu : st::defaultPopupMenu); const auto session = &this->session(); const auto controller = _controller; const auto groupLeaderOrSelf = [](HistoryItem *item) -> HistoryItem* { @@ -1585,13 +1589,13 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { return; } const auto itemId = item->fullId(); - if (Api::WhoReadExists(item)) { + if (hasWhoReadItem) { const auto participantChosen = [=](uint64 id) { controller->showPeerInfo(PeerId(id)); }; _menu->addAction(Ui::WhoReadContextAction( _menu.get(), - Api::WhoRead(item, this, st::defaultWhoRead), + Api::WhoRead(_dragStateItem, this, st::defaultWhoRead), participantChosen)); _menu->addSeparator(); } diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index b111ffb66..b6e09c902 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -865,7 +865,10 @@ ttlItemTimerFont: font(12px); WhoRead { userpics: GroupCallUserpics; + photoLeft: pixels; photoSize: pixels; + photoSkip: pixels; + nameLeft: pixels; itemPadding: margins; } defaultWhoRead: WhoRead { @@ -875,6 +878,15 @@ defaultWhoRead: WhoRead { stroke: 4px; align: align(right); } + photoLeft: 13px; photoSize: 30px; + photoSkip: 5px; + nameLeft: 57px; itemPadding: margins(17px, 8px, 17px, 6px); } +whoReadMenu: PopupMenu(defaultPopupMenu) { + scrollPadding: margins(0px, 6px, 0px, 6px); + menu: Menu(defaultMenu) { + separatorPadding: margins(0px, 6px, 0px, 8px); + } +} diff --git a/Telegram/SourceFiles/ui/controls/who_read_context_action.cpp b/Telegram/SourceFiles/ui/controls/who_read_context_action.cpp index 8187a7db1..66ede3c65 100644 --- a/Telegram/SourceFiles/ui/controls/who_read_context_action.cpp +++ b/Telegram/SourceFiles/ui/controls/who_read_context_action.cpp @@ -17,6 +17,39 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Ui { namespace { +struct EntryData { + QString text; + QImage userpic; + Fn callback; +}; + +class EntryAction final : public Menu::ItemBase { +public: + EntryAction( + not_null parent, + const style::Menu &st, + EntryData &&data); + + void setData(EntryData &&data); + + not_null action() const override; + bool isEnabled() const override; + +private: + int contentHeight() const override; + + void paint(Painter &&p); + + const not_null _dummyAction; + const style::Menu &_st; + const int _height = 0; + + Text::String _text; + int _textWidth = 0; + QImage _userpic; + +}; + class Action final : public Menu::ItemBase { public: Action( @@ -50,6 +83,8 @@ private: const std::unique_ptr _userpics; const style::Menu &_st; + std::vector> _submenuActions; + Text::String _text; int _textWidth = 0; const int _height = 0; @@ -66,13 +101,88 @@ TextParseOptions MenuTextOptions = { Qt::LayoutDirectionAuto, // dir }; +EntryAction::EntryAction( + not_null parent, + const style::Menu &st, + EntryData &&data) +: ItemBase(parent, st) +, _dummyAction(CreateChild(parent.get())) +, _st(st) +, _height(st::defaultWhoRead.photoSkip * 2 + st::defaultWhoRead.photoSize) { + setAcceptBoth(true); + + initResizeHook(parent->sizeValue()); + setData(std::move(data)); + + paintRequest( + ) | rpl::start_with_next([=] { + paint(Painter(this)); + }, lifetime()); + + enableMouseSelecting(); +} + +not_null EntryAction::action() const { + return _dummyAction.get(); +} + +bool EntryAction::isEnabled() const { + return true; +} + +int EntryAction::contentHeight() const { + return _height; +} + +void EntryAction::setData(EntryData &&data) { + setClickedCallback(std::move(data.callback)); + _userpic = std::move(data.userpic); + _text.setMarkedText(_st.itemStyle, { data.text }, MenuTextOptions); + const auto textWidth = _text.maxWidth(); + const auto &padding = _st.itemPadding; + const auto goodWidth = st::defaultWhoRead.nameLeft + + textWidth + + padding.right(); + const auto w = std::clamp(goodWidth, _st.widthMin, _st.widthMax); + _textWidth = w - (goodWidth - textWidth); + setMinWidth(w); + update(); +} + +void EntryAction::paint(Painter &&p) { + const auto enabled = isEnabled(); + const auto selected = isSelected(); + if (selected && _st.itemBgOver->c.alpha() < 255) { + p.fillRect(0, 0, width(), _height, _st.itemBg); + } + p.fillRect(0, 0, width(), _height, selected ? _st.itemBgOver : _st.itemBg); + if (enabled) { + paintRipple(p, 0, 0); + } + const auto photoSize = st::defaultWhoRead.photoSize; + const auto photoTop = (height() - photoSize) / 2; + p.drawImage(st::defaultWhoRead.photoLeft, photoTop, _userpic); + + p.setPen(selected + ? _st.itemFgOver + : enabled + ? _st.itemFg + : _st.itemFgDisabled); + _text.drawLeftElided( + p, + st::defaultWhoRead.nameLeft, + (height() - _st.itemStyle.font->height) / 2, + _textWidth, + width()); +} + Action::Action( not_null parentMenu, rpl::producer content, Fn participantChosen) : ItemBase(parentMenu->menu(), parentMenu->menu()->st()) , _parentMenu(parentMenu) -, _dummyAction(new QAction(parentMenu->menu())) +, _dummyAction(CreateChild(parentMenu->menu().get())) , _participantChosen(std::move(participantChosen)) , _userpics(std::make_unique( st::defaultWhoRead.userpics, @@ -155,9 +265,11 @@ void Action::updateUserpicsFromContent() { const auto count = std::min( int(_content.participants.size()), WhoReadParticipant::kMaxSmallUserpics); + const auto factor = style::DevicePixelRatio(); users.reserve(count); for (auto i = 0; i != count; ++i) { - const auto &participant = _content.participants[i]; + auto &participant = _content.participants[i]; + participant.userpicSmall.setDevicePixelRatio(factor); users.push_back({ .userpic = participant.userpicSmall, .userpicKey = participant.userpicKey, @@ -170,19 +282,40 @@ void Action::updateUserpicsFromContent() { void Action::populateSubmenu() { if (_content.participants.size() < 2) { + _submenuActions.clear(); _parentMenu->removeSubmenu(action()); if (!isEnabled()) { setSelected(false); } return; } + const auto submenu = _parentMenu->ensureSubmenu(action()); - submenu->clearActions(); + if (_submenuActions.size() > _content.participants.size()) { + _submenuActions.clear(); + submenu->clearActions(); + } + auto index = 0; for (const auto &participant : _content.participants) { const auto chosen = [call = _participantChosen, id = participant.id] { call(id); }; - submenu->addAction(participant.name, chosen); + auto data = EntryData{ + .text = participant.name, + .userpic = participant.userpicLarge, + .callback = chosen, + }; + if (index < _submenuActions.size()) { + _submenuActions[index]->setData(std::move(data)); + } else { + auto item = base::make_unique_q( + submenu->menu(), + _st, + std::move(data)); + _submenuActions.push_back(item.get()); + submenu->addAction(std::move(item)); + } + ++index; } _parentMenu->checkSubmenuShow(); } @@ -243,13 +376,13 @@ void Action::refreshDimensions() { const auto goodWidth = padding.left() + textWidth - + _userpicsWidth + + (_userpicsWidth ? (_st.itemStyle.font->spacew + _userpicsWidth) : 0) + padding.right(); const auto w = std::clamp( goodWidth, _st.widthMin, - _st.widthMax); + minWidth()); _textWidth = w - (goodWidth - textWidth); }