Added context menu to list in ChooseStarGiftRecipient.

This commit is contained in:
23rd 2025-04-18 14:32:58 +03:00 committed by John Preston
parent d1898b9a0b
commit ecac3a0f16

View file

@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/unixtime.h" #include "base/unixtime.h"
#include "boxes/filters/edit_filter_chats_list.h" #include "boxes/filters/edit_filter_chats_list.h"
#include "boxes/peers/edit_peer_color_box.h" #include "boxes/peers/edit_peer_color_box.h"
#include "boxes/peers/prepare_short_info_box.h"
#include "boxes/gift_premium_box.h" #include "boxes/gift_premium_box.h"
#include "boxes/peer_list_controllers.h" #include "boxes/peer_list_controllers.h"
#include "boxes/premium_preview_box.h" #include "boxes/premium_preview_box.h"
@ -70,6 +71,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/effects/path_shift_gradient.h" #include "ui/effects/path_shift_gradient.h"
#include "ui/effects/premium_graphics.h" #include "ui/effects/premium_graphics.h"
#include "ui/effects/premium_stars_colored.h" #include "ui/effects/premium_stars_colored.h"
#include "ui/effects/ripple_animation.h"
#include "ui/layers/generic_box.h" #include "ui/layers/generic_box.h"
#include "ui/new_badges.h" #include "ui/new_badges.h"
#include "ui/painter.h" #include "ui/painter.h"
@ -92,6 +94,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_chat.h" #include "styles/style_chat.h"
#include "styles/style_chat_helpers.h" #include "styles/style_chat_helpers.h"
#include "styles/style_credits.h" #include "styles/style_credits.h"
#include "styles/style_info.h"
#include "styles/style_layers.h" #include "styles/style_layers.h"
#include "styles/style_menu_icons.h" #include "styles/style_menu_icons.h"
#include "styles/style_premium.h" #include "styles/style_premium.h"
@ -118,6 +121,13 @@ constexpr auto kGiftsPreloadTimeout = 3 * crl::time(1000);
using namespace HistoryView; using namespace HistoryView;
using namespace Info::PeerGifts; using namespace Info::PeerGifts;
enum class PickType {
Activate,
SendMessage,
OpenProfile,
};
using PickCallback = Fn<void(not_null<PeerData*>, PickType)>;
struct PremiumGiftsDescriptor { struct PremiumGiftsDescriptor {
std::vector<GiftTypePremium> list; std::vector<GiftTypePremium> list;
std::shared_ptr<Api::PremiumGiftCodeOptions> api; std::shared_ptr<Api::PremiumGiftCodeOptions> api;
@ -142,6 +152,80 @@ struct GiftDetails {
bool byStars = false; bool byStars = false;
}; };
class PeerRow final : public PeerListRow {
public:
using PeerListRow::PeerListRow;
QSize rightActionSize() const override;
QMargins rightActionMargins() const override;
void rightActionPaint(
Painter &p,
int x,
int y,
int outerWidth,
bool selected,
bool actionSelected) override;
void rightActionAddRipple(
QPoint point,
Fn<void()> updateCallback) override;
void rightActionStopLastRipple() override;
private:
std::unique_ptr<Ui::RippleAnimation> _actionRipple;
};
QSize PeerRow::rightActionSize() const {
return QSize(
st::inviteLinkThreeDotsIcon.width(),
st::inviteLinkThreeDotsIcon.height());
}
QMargins PeerRow::rightActionMargins() const {
return QMargins(
0,
(st::inviteLinkList.item.height - rightActionSize().height()) / 2,
st::inviteLinkThreeDotsSkip,
0);
}
void PeerRow::rightActionPaint(
Painter &p,
int x,
int y,
int outerWidth,
bool selected,
bool actionSelected) {
if (_actionRipple) {
_actionRipple->paint(p, x, y, outerWidth);
if (_actionRipple->empty()) {
_actionRipple.reset();
}
}
(actionSelected
? st::inviteLinkThreeDotsIconOver
: st::inviteLinkThreeDotsIcon).paint(p, x, y, outerWidth);
}
void PeerRow::rightActionAddRipple(QPoint point, Fn<void()> updateCallback) {
if (!_actionRipple) {
auto mask = Ui::RippleAnimation::EllipseMask(
Size(st::inviteLinkThreeDotsIcon.height()));
_actionRipple = std::make_unique<Ui::RippleAnimation>(
st::defaultRippleAnimation,
std::move(mask),
std::move(updateCallback));
}
_actionRipple->add(point);
}
void PeerRow::rightActionStopLastRipple() {
if (_actionRipple) {
_actionRipple->lastStop();
}
}
class PreviewDelegate final : public DefaultElementDelegate { class PreviewDelegate final : public DefaultElementDelegate {
public: public:
PreviewDelegate( PreviewDelegate(
@ -2258,6 +2342,24 @@ void GiftBox(
} }
} }
[[nodiscard]] base::unique_qptr<Ui::PopupMenu> CreateRowContextMenu(
QWidget *parent,
not_null<PeerData*> peer,
PickCallback pick) {
auto result = base::make_unique_q<Ui::PopupMenu>(
parent,
st::popupMenuWithIcons);
result->addAction(
tr::lng_context_send_message(tr::now),
[=] { pick(peer, PickType::SendMessage); },
&st::menuIconChatBubble);
result->addAction(
tr::lng_context_view_profile(tr::now),
[=] { pick(peer, PickType::OpenProfile); },
&st::menuIconProfile);
return result;
}
struct CustomList { struct CustomList {
object_ptr<Ui::RpWidget> content = { nullptr }; object_ptr<Ui::RpWidget> content = { nullptr };
Fn<bool(int, int, int)> overrideKey; Fn<bool(int, int, int)> overrideKey;
@ -2267,16 +2369,19 @@ struct CustomList {
class Controller final : public ContactsBoxController { class Controller final : public ContactsBoxController {
public: public:
Controller( Controller(not_null<Main::Session*> session, PickCallback pick);
not_null<Main::Session*> session,
Fn<void(not_null<PeerData*>)> choose);
void noSearchSubmit(); void noSearchSubmit();
bool overrideKeyboardNavigation( bool overrideKeyboardNavigation(
int direction, int direction,
int fromIndex, int fromIndex,
int toIndex) override; int toIndex) override final;
void rowRightActionClicked(not_null<PeerListRow*> row) override final;
base::unique_qptr<Ui::PopupMenu> rowContextMenu(
QWidget *parent,
not_null<PeerListRow*> row) override final;
private: private:
std::unique_ptr<PeerListRow> createRow( std::unique_ptr<PeerListRow> createRow(
@ -2285,11 +2390,13 @@ private:
void prepareViewHook() override; void prepareViewHook() override;
void rowClicked(not_null<PeerListRow*> row) override; void rowClicked(not_null<PeerListRow*> row) override;
const Fn<void(not_null<PeerData*>)> _choose; const PickCallback _pick;
const std::vector<UserId> _contactBirthdays; const std::vector<UserId> _contactBirthdays;
CustomList _selfOption; CustomList _selfOption;
CustomList _birthdayOptions; CustomList _birthdayOptions;
base::unique_qptr<Ui::PopupMenu> _menu;
bool _skipUpDirectionSelect = false; bool _skipUpDirectionSelect = false;
}; };
@ -2297,16 +2404,16 @@ private:
[[nodiscard]] CustomList MakeCustomList( [[nodiscard]] CustomList MakeCustomList(
not_null<Main::Session*> session, not_null<Main::Session*> session,
Fn<void(not_null<PeerListController*>)> fill, Fn<void(not_null<PeerListController*>)> fill,
Fn<void(not_null<PeerData*>)> activate, PickCallback pick,
rpl::producer<QString> below) { rpl::producer<QString> below) {
class SelfController final : public PeerListController { class CustomController final : public PeerListController {
public: public:
SelfController( CustomController(
not_null<Main::Session*> session, not_null<Main::Session*> session,
Fn<void(not_null<PeerListController*>)> fill, Fn<void(not_null<PeerListController*>)> fill,
Fn<void(not_null<PeerData*>)> activate) PickCallback pick)
: _session(session) : _session(session)
, _activate(std::move(activate)) , _pick(std::move(pick))
, _fill(std::move(fill)) { , _fill(std::move(fill)) {
} }
@ -2318,17 +2425,37 @@ private:
void loadMoreRows() override { void loadMoreRows() override {
} }
void rowClicked(not_null<PeerListRow*> row) override { void rowClicked(not_null<PeerListRow*> row) override {
_activate(row->peer()); _pick(row->peer(), PickType::Activate);
} }
Main::Session &session() const override { Main::Session &session() const override {
return *_session; return *_session;
} }
void rowRightActionClicked(not_null<PeerListRow*> row) override {
delegate()->peerListShowRowMenu(row, true);
}
base::unique_qptr<Ui::PopupMenu> rowContextMenu(
QWidget *parent,
not_null<PeerListRow*> row) override {
auto result = CreateRowContextMenu(parent, row->peer(), _pick);
if (result) {
base::take(_menu);
_menu = base::unique_qptr<Ui::PopupMenu>(result.get());
}
return result;
}
private: private:
const not_null<Main::Session*> _session; const not_null<Main::Session*> _session;
Fn<void(not_null<PeerData*>)> _activate; PickCallback _pick;
Fn<void(not_null<PeerListController*>)> _fill; Fn<void(not_null<PeerListController*>)> _fill;
base::unique_qptr<Ui::PopupMenu> _menu;
}; };
auto result = object_ptr<Ui::VerticalLayout>((QWidget*)nullptr); auto result = object_ptr<Ui::VerticalLayout>((QWidget*)nullptr);
@ -2340,10 +2467,10 @@ private:
PeerListContentDelegateSimple PeerListContentDelegateSimple
>(); >();
const auto controller const auto controller
= container->lifetime().make_state<SelfController>( = container->lifetime().make_state<CustomController>(
session, session,
fill, fill,
activate); pick);
controller->setStyleOverrides(&st::peerListSingleRow); controller->setStyleOverrides(&st::peerListSingleRow);
const auto content = container->add(object_ptr<PeerListContent>( const auto content = container->add(object_ptr<PeerListContent>(
@ -2392,18 +2519,18 @@ private:
.overrideKey = overrideKey, .overrideKey = overrideKey,
.activate = [=] { .activate = [=] {
if (content->hasSelection()) { if (content->hasSelection()) {
activate(content->rowAt(content->selectedIndex())->peer()); pick(
content->rowAt(content->selectedIndex())->peer(),
PickType::Activate);
} }
}, },
.hasSelection = hasSelection, .hasSelection = hasSelection,
}; };
} }
Controller::Controller( Controller::Controller(not_null<Main::Session*> session, PickCallback pick)
not_null<Main::Session*> session,
Fn<void(not_null<PeerData*>)> choose)
: ContactsBoxController(session) : ContactsBoxController(session)
, _choose(std::move(choose)) , _pick(std::move(pick))
, _contactBirthdays( , _contactBirthdays(
session->data().knownContactBirthdays().value_or(std::vector<UserId>{})) session->data().knownContactBirthdays().value_or(std::vector<UserId>{}))
, _selfOption( , _selfOption(
@ -2415,7 +2542,7 @@ Controller::Controller(
controller->delegate()->peerListAppendRow(std::move(row)); controller->delegate()->peerListAppendRow(std::move(row));
controller->delegate()->peerListRefreshRows(); controller->delegate()->peerListRefreshRows();
}, },
[=](not_null<PeerData*> peer) { _choose(peer); }, _pick,
_contactBirthdays.empty() _contactBirthdays.empty()
? tr::lng_contacts_header() ? tr::lng_contacts_header()
: tr::lng_gift_subtitle_birthdays())) : tr::lng_gift_subtitle_birthdays()))
@ -2458,7 +2585,7 @@ Controller::Controller(
}); });
for (const auto user : usersWithBirthdays) { for (const auto user : usersWithBirthdays) {
auto row = std::make_unique<PeerListRow>(user); auto row = std::make_unique<PeerRow>(user);
if (auto s = status(user->birthday()); !s.isEmpty()) { if (auto s = status(user->birthday()); !s.isEmpty()) {
row->setCustomStatus(std::move(s)); row->setCustomStatus(std::move(s));
} }
@ -2467,13 +2594,34 @@ Controller::Controller(
controller->delegate()->peerListRefreshRows(); controller->delegate()->peerListRefreshRows();
}, },
[=](not_null<PeerData*> peer) { _choose(peer); }, _pick,
_contactBirthdays.empty() _contactBirthdays.empty()
? rpl::producer<QString>(nullptr) ? rpl::producer<QString>(nullptr)
: tr::lng_contacts_header())) { : tr::lng_contacts_header())) {
setStyleOverrides(&st::peerListSmallSkips); setStyleOverrides(&st::peerListSmallSkips);
} }
void Controller::rowRightActionClicked(not_null<PeerListRow*> row) {
delegate()->peerListShowRowMenu(row, true);
}
base::unique_qptr<Ui::PopupMenu> Controller::rowContextMenu(
QWidget *parent,
not_null<PeerListRow*> row) {
auto result = CreateRowContextMenu(parent, row->peer(), _pick);
if (result) {
// First clear _menu value, so that we don't check row positions yet.
base::take(_menu);
// Here unique_qptr is used like a shared pointer, where
// not the last destroyed pointer destroys the object, but the first.
_menu = base::unique_qptr<Ui::PopupMenu>(result.get());
}
return result;
}
void Controller::noSearchSubmit() { void Controller::noSearchSubmit() {
if (const auto onstack = _selfOption.activate) { if (const auto onstack = _selfOption.activate) {
onstack(); onstack();
@ -2543,7 +2691,7 @@ std::unique_ptr<PeerListRow> Controller::createRow(
|| user->isInaccessible()) { || user->isInaccessible()) {
return nullptr; return nullptr;
} }
return ContactsBoxController::createRow(user); return std::make_unique<PeerRow>(user);
} }
void Controller::prepareViewHook() { void Controller::prepareViewHook() {
@ -2554,7 +2702,7 @@ void Controller::prepareViewHook() {
} }
void Controller::rowClicked(not_null<PeerListRow*> row) { void Controller::rowClicked(not_null<PeerListRow*> row) {
_choose(row->peer()); _pick(row->peer(), PickType::Activate);
} }
} // namespace } // namespace
@ -2568,8 +2716,15 @@ void ChooseStarGiftRecipient(
lifetime->destroy(); lifetime->destroy();
auto controller = std::make_unique<Controller>( auto controller = std::make_unique<Controller>(
session, session,
[=](not_null<PeerData*> peer) { [=](not_null<PeerData*> peer, PickType type) {
ShowStarGiftBox(window, peer); if (type == PickType::Activate) {
ShowStarGiftBox(window, peer);
} else if (type == PickType::SendMessage) {
using Way = Window::SectionShow::Way;
window->showPeerHistory(peer, Way::Forward);
} else if (type == PickType::OpenProfile) {
window->show(PrepareShortInfoBox(peer, window));
}
}); });
const auto controllerRaw = controller.get(); const auto controllerRaw = controller.get();
auto initBox = [=](not_null<PeerListBox*> box) { auto initBox = [=](not_null<PeerListBox*> box) {