mirror of
https://hubproxy.babadafafafafa.cn/https://github.com/telegramdesktop/tdesktop
synced 2026-09-20 16:13:46 +08:00
Replaced menu of who reacted with full window preview of reaction.
This commit is contained in:
@@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "history/history_item_components.h"
|
||||
#include "history/history_item_helpers.h"
|
||||
#include "history/history_item_text.h"
|
||||
#include "history/view/history_view_reaction_preview.h"
|
||||
#include "history/view/history_view_schedule_box.h"
|
||||
#include "history/view/media/history_view_media.h"
|
||||
#include "history/view/media/menu/history_view_poll_menu.h"
|
||||
@@ -35,6 +36,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "info/info_memento.h"
|
||||
#include "iv/iv_rich_message_html_export.h"
|
||||
#include "ui/effects/ripple_animation.h"
|
||||
#include "ui/widgets/dropdown_menu.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "ui/widgets/menu/menu_action.h"
|
||||
#include "ui/widgets/menu/menu_add_action_callback_factory.h"
|
||||
@@ -1551,6 +1553,180 @@ void EditTagBox(
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] Fn<void(Ui::WhoReadParticipant)> MakeParticipantChosen(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<HistoryItem*> item) {
|
||||
const auto itemId = item->fullId();
|
||||
const auto originPeer = item->history()->peer;
|
||||
return [=](Ui::WhoReadParticipant who) {
|
||||
const auto participant = originPeer->owner().peer(PeerId(who.id));
|
||||
Reactions::ShowReactionParticipantInfo(
|
||||
controller,
|
||||
participant,
|
||||
originPeer,
|
||||
itemId.msg,
|
||||
who.dateReacted);
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] Fn<void()> MakeShowAllReactionsChosen(
|
||||
not_null<Window::SessionController*> controller,
|
||||
FullMsgId itemId,
|
||||
Data::ReactionId id) {
|
||||
return [=] {
|
||||
if (const auto item = controller->session().data().message(itemId)) {
|
||||
controller->showSection(std::make_shared<Info::Memento>(
|
||||
nullptr,
|
||||
itemId,
|
||||
HistoryView::Reactions::DefaultSelectedTab(item, id)));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template <typename Menu>
|
||||
[[nodiscard]] int AppendReactionPackAction(
|
||||
not_null<Menu*> menu,
|
||||
const Data::ReactionId &id,
|
||||
not_null<Window::SessionController*> controller) {
|
||||
const auto custom = id.custom();
|
||||
if (!custom) {
|
||||
return 0;
|
||||
}
|
||||
const auto owner = &controller->session().data();
|
||||
const auto sticker = owner->document(custom)->sticker();
|
||||
if (!sticker || !sticker->set.id) {
|
||||
return 0;
|
||||
}
|
||||
AddEmojiPacksAction(
|
||||
menu,
|
||||
{ sticker->set },
|
||||
EmojiPacksSource::Reaction,
|
||||
controller);
|
||||
return 2;
|
||||
}
|
||||
|
||||
[[nodiscard]] QString WidestReactionDate(TimeId around) {
|
||||
const auto parsed = base::unixtime::parse(around);
|
||||
const auto day = langDayOfMonthShort(parsed.date());
|
||||
const auto &font = st::whoReadDateStyle.font;
|
||||
auto result = QString();
|
||||
const auto check = [&](const QString &date) {
|
||||
if (font->width(date) > font->width(result)) {
|
||||
result = date;
|
||||
}
|
||||
};
|
||||
for (const auto &time : {
|
||||
QLocale().toString(parsed.time(), QLocale::ShortFormat),
|
||||
QLocale().toString(QTime(23, 59), QLocale::ShortFormat),
|
||||
}) {
|
||||
check(tr::lng_mediaview_today(tr::now, lt_time, time));
|
||||
check(tr::lng_mediaview_yesterday(tr::now, lt_time, time));
|
||||
check(tr::lng_mediaview_date_time(
|
||||
tr::now,
|
||||
lt_date,
|
||||
day,
|
||||
lt_time,
|
||||
time));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] auto ReactionPreviewPreloaderEntries(
|
||||
not_null<HistoryItem*> item,
|
||||
const Data::ReactionId &id)
|
||||
-> std::vector<Ui::WhoReactedEntryData> {
|
||||
const auto entryHeight = st::defaultWhoRead.photoSkip * 2
|
||||
+ st::defaultWhoRead.photoSize;
|
||||
const auto &list = item->reactions();
|
||||
const auto i = ranges::find(list, id, &Data::MessageReaction::id);
|
||||
const auto count = std::clamp(
|
||||
(i != end(list)) ? i->count : 1,
|
||||
1,
|
||||
st::whoReadDropdownMenuMaxHeight / entryHeight + 1);
|
||||
|
||||
const auto &recent = item->recentReactions();
|
||||
const auto j = recent.find(id);
|
||||
const auto known = (j != end(recent)) ? int(j->second.size()) : 0;
|
||||
const auto date = WidestReactionDate(item->date());
|
||||
const auto customEntityData = Data::ReactionEntityData(id);
|
||||
auto result = std::vector<Ui::WhoReactedEntryData>();
|
||||
result.reserve(count);
|
||||
for (auto k = 0; k != count; ++k) {
|
||||
result.push_back({
|
||||
.text = (k < known) ? j->second[k].peer->name() : QString(),
|
||||
.date = date,
|
||||
.customEntityData = customEntityData,
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool ShowReactionPreviewMenu(
|
||||
not_null<QWidget*> context,
|
||||
not_null<HistoryItem*> item,
|
||||
const Data::ReactionId &id,
|
||||
not_null<Window::SessionController*> controller) {
|
||||
if (id.paid()) {
|
||||
return false;
|
||||
}
|
||||
struct State {
|
||||
int addedToBottom = 0;
|
||||
};
|
||||
const auto itemId = item->fullId();
|
||||
const auto participantChosen = MakeParticipantChosen(controller, item);
|
||||
const auto showAllChosen = MakeShowAllReactionsChosen(
|
||||
controller,
|
||||
itemId,
|
||||
id);
|
||||
const auto preloaderEntries = ReactionPreviewPreloaderEntries(item, id);
|
||||
return ShowReactionPreview(controller, itemId, id, false, [=](
|
||||
ReactionPreviewMenu preview) {
|
||||
const auto menu = not_null(preview.menu);
|
||||
const auto refreshGeometry = preview.refreshGeometry;
|
||||
const auto moderateReactionChosen = MakeModerateReactionChosen(
|
||||
controller,
|
||||
itemId,
|
||||
item->history()->peer,
|
||||
preview.hide);
|
||||
using ListMenu = Ui::WhoReactedListMenu;
|
||||
const auto filler = menu->lifetime().make_state<ListMenu>(
|
||||
Data::ReactedMenuFactory(&controller->session()),
|
||||
participantChosen,
|
||||
showAllChosen,
|
||||
moderateReactionChosen);
|
||||
const auto state = menu->lifetime().make_state<State>();
|
||||
const auto appendBottom = [=] {
|
||||
state->addedToBottom = AppendReactionPackAction(
|
||||
menu,
|
||||
id,
|
||||
controller);
|
||||
};
|
||||
|
||||
filler->populatePreloader(
|
||||
menu,
|
||||
preloaderEntries,
|
||||
appendBottom);
|
||||
refreshGeometry();
|
||||
|
||||
Api::WhoReacted(
|
||||
item,
|
||||
id,
|
||||
context,
|
||||
st::defaultWhoRead
|
||||
) | rpl::filter([=](const Ui::WhoReadContent &content) {
|
||||
return content.state != Ui::WhoReadState::Unknown;
|
||||
}) | rpl::on_next([=](Ui::WhoReadContent &&content) {
|
||||
filler->populate(
|
||||
menu,
|
||||
content,
|
||||
nullptr,
|
||||
state->addedToBottom,
|
||||
appendBottom);
|
||||
refreshGeometry();
|
||||
}, menu->lifetime());
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::optional<QString> CurrentVoiceTimecode(FullMsgId itemId) {
|
||||
@@ -2576,22 +2752,15 @@ void ShowWhoReactedMenu(
|
||||
if (item->reactionsAreTags()) {
|
||||
ShowTagMenu(menu, position, context, item, id, controller);
|
||||
return;
|
||||
} else if (ShowReactionPreviewMenu(context, item, id, controller)) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct State {
|
||||
int addedToBottom = 0;
|
||||
};
|
||||
const auto itemId = item->fullId();
|
||||
const auto participantChosen = [=](Ui::WhoReadParticipant who) {
|
||||
const auto originPeer = item->history()->peer;
|
||||
const auto participant = originPeer->owner().peer(PeerId(who.id));
|
||||
Reactions::ShowReactionParticipantInfo(
|
||||
controller,
|
||||
participant,
|
||||
originPeer,
|
||||
itemId.msg,
|
||||
who.dateReacted);
|
||||
};
|
||||
const auto participantChosen = MakeParticipantChosen(controller, item);
|
||||
const auto moderateReactionChosen = MakeModerateReactionChosen(
|
||||
controller,
|
||||
itemId,
|
||||
@@ -2601,15 +2770,10 @@ void ShowWhoReactedMenu(
|
||||
(*menu)->hideMenu();
|
||||
}
|
||||
});
|
||||
const auto showAllChosen = [=, itemId = item->fullId()]{
|
||||
if (const auto item = controller->session().data().message(itemId)) {
|
||||
controller->showSection(std::make_shared<Info::Memento>(
|
||||
nullptr,
|
||||
itemId,
|
||||
HistoryView::Reactions::DefaultSelectedTab(item, id)));
|
||||
}
|
||||
};
|
||||
const auto owner = &controller->session().data();
|
||||
const auto showAllChosen = MakeShowAllReactionsChosen(
|
||||
controller,
|
||||
itemId,
|
||||
id);
|
||||
const auto filler = lifetime.make_state<Ui::WhoReactedListMenu>(
|
||||
Data::ReactedMenuFactory(&controller->session()),
|
||||
participantChosen,
|
||||
@@ -2626,19 +2790,10 @@ void ShowWhoReactedMenu(
|
||||
}) | rpl::on_next([=, &lifetime](Ui::WhoReadContent &&content) {
|
||||
const auto creating = !*menu;
|
||||
const auto appendBottom = [=] {
|
||||
state->addedToBottom = 0;
|
||||
if (const auto custom = id.custom()) {
|
||||
if (const auto set = owner->document(custom)->sticker()) {
|
||||
if (set->set.id) {
|
||||
state->addedToBottom = 2;
|
||||
AddEmojiPacksAction(
|
||||
menu->get(),
|
||||
{ set->set },
|
||||
EmojiPacksSource::Reaction,
|
||||
controller);
|
||||
}
|
||||
}
|
||||
}
|
||||
state->addedToBottom = AppendReactionPackAction(
|
||||
not_null(menu->get()),
|
||||
id,
|
||||
controller);
|
||||
};
|
||||
if (creating) {
|
||||
*menu = base::make_unique_q<Ui::PopupMenu>(
|
||||
@@ -2695,8 +2850,9 @@ std::vector<StickerSetIdentifier> CollectEmojiPacks(
|
||||
return result;
|
||||
}
|
||||
|
||||
void AddEmojiPacksAction(
|
||||
not_null<Ui::PopupMenu*> menu,
|
||||
template <typename Menu>
|
||||
void AddEmojiPacksActionTo(
|
||||
not_null<Menu*> menu,
|
||||
std::vector<StickerSetIdentifier> packIds,
|
||||
EmojiPacksSource source,
|
||||
not_null<Window::SessionController*> controller) {
|
||||
@@ -2770,7 +2926,7 @@ void AddEmojiPacksAction(
|
||||
}();
|
||||
auto button = base::make_unique_q<Ui::Menu::MultilineAction>(
|
||||
menu->menu(),
|
||||
menu->st().menu,
|
||||
menu->menu()->st(),
|
||||
st::historyHasCustomEmoji,
|
||||
st::historyHasCustomEmojiPosition,
|
||||
std::move(text));
|
||||
@@ -2792,6 +2948,22 @@ void AddEmojiPacksAction(
|
||||
menu->addAction(std::move(button));
|
||||
}
|
||||
|
||||
void AddEmojiPacksAction(
|
||||
not_null<Ui::PopupMenu*> menu,
|
||||
std::vector<StickerSetIdentifier> packIds,
|
||||
EmojiPacksSource source,
|
||||
not_null<Window::SessionController*> controller) {
|
||||
AddEmojiPacksActionTo(menu, std::move(packIds), source, controller);
|
||||
}
|
||||
|
||||
void AddEmojiPacksAction(
|
||||
not_null<Ui::DropdownMenu*> menu,
|
||||
std::vector<StickerSetIdentifier> packIds,
|
||||
EmojiPacksSource source,
|
||||
not_null<Window::SessionController*> controller) {
|
||||
AddEmojiPacksActionTo(menu, std::move(packIds), source, controller);
|
||||
}
|
||||
|
||||
void AddEmojiPacksAction(
|
||||
not_null<Ui::PopupMenu*> menu,
|
||||
not_null<HistoryItem*> item,
|
||||
|
||||
@@ -21,6 +21,7 @@ class SessionShow;
|
||||
} // namespace Main
|
||||
|
||||
namespace Ui {
|
||||
class DropdownMenu;
|
||||
class PopupMenu;
|
||||
class Show;
|
||||
enum class ReportReason;
|
||||
@@ -149,6 +150,11 @@ void AddEmojiPacksAction(
|
||||
std::vector<StickerSetIdentifier> packIds,
|
||||
EmojiPacksSource source,
|
||||
not_null<Window::SessionController*> controller);
|
||||
void AddEmojiPacksAction(
|
||||
not_null<Ui::DropdownMenu*> menu,
|
||||
std::vector<StickerSetIdentifier> packIds,
|
||||
EmojiPacksSource source,
|
||||
not_null<Window::SessionController*> controller);
|
||||
void AddEmojiPacksAction(
|
||||
not_null<Ui::PopupMenu*> menu,
|
||||
not_null<HistoryItem*> item,
|
||||
|
||||
@@ -28,6 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "ui/painter.h"
|
||||
#include "ui/rect.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/ui_utility.h"
|
||||
#include "window/window_media_preview.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_chat.h"
|
||||
@@ -124,17 +125,16 @@ template <typename MediaData>
|
||||
return { state, hideAll };
|
||||
}
|
||||
|
||||
void SetupPreviewMenu(
|
||||
Fn<void()> SetupPreviewMenu(
|
||||
not_null<Window::SessionController*> controller,
|
||||
const PreviewOverlay &overlay,
|
||||
const style::DropdownMenu &menuSt,
|
||||
Fn<void(not_null<Ui::DropdownMenu*>)> fillMenu) {
|
||||
const auto &state = overlay.state;
|
||||
const auto mainwidget = controller->widget()->bodyWidget();
|
||||
if (fillMenu) {
|
||||
state->mediaPreview->setHideEmoji(true);
|
||||
auto menu = object_ptr<Ui::DropdownMenu>(
|
||||
mainwidget,
|
||||
st::dropdownMenuWithIcons);
|
||||
auto menu = object_ptr<Ui::DropdownMenu>(mainwidget, menuSt);
|
||||
menu->setAutoHiding(false);
|
||||
menu->setHiddenCallback(
|
||||
crl::guard(state->clickable.get(), overlay.hideAll));
|
||||
@@ -153,24 +153,38 @@ void SetupPreviewMenu(
|
||||
};
|
||||
|
||||
const auto mediaPreviewRaw = state->mediaPreview.get();
|
||||
mainwidget->sizeValue() | rpl::on_next([=](QSize size) {
|
||||
const auto updateLayout = [=] {
|
||||
const auto size = mainwidget->size();
|
||||
mediaPreviewRaw->setGeometry(Rect(size));
|
||||
|
||||
if (wrapRaw) {
|
||||
const auto menuRaw = wrapRaw->entity();
|
||||
menuRaw->showFast();
|
||||
const auto gap = st::defaultMenu.itemPadding.top();
|
||||
const auto menuH = menuRaw->height();
|
||||
const auto shift = -(gap + menuH) / 2;
|
||||
mediaPreviewRaw->setContentShift(shift);
|
||||
|
||||
const auto menuX = (size.width() - menuRaw->width()) / 2;
|
||||
const auto menuY = mediaPreviewRaw->contentBottom() + gap;
|
||||
wrapRaw->move(menuX, menuY);
|
||||
wrapRaw->show(anim::type::normal);
|
||||
wrapRaw->raise();
|
||||
const auto menuRaw = wrapRaw ? wrapRaw->entity() : nullptr;
|
||||
if (!menuRaw || menuRaw->empty()) {
|
||||
return;
|
||||
} else if (wrapRaw->isHidden()) {
|
||||
Ui::SendPendingMoveResizeEvents(wrapRaw);
|
||||
}
|
||||
menuRaw->showFast();
|
||||
const auto gap = st::defaultMenu.itemPadding.top();
|
||||
const auto menuH = menuRaw->height();
|
||||
const auto shift = -(gap + menuH) / 2;
|
||||
mediaPreviewRaw->setContentShift(shift);
|
||||
|
||||
const auto menuX = (size.width() - menuRaw->width()) / 2;
|
||||
const auto menuY = mediaPreviewRaw->contentBottom() + gap;
|
||||
wrapRaw->move(menuX, menuY);
|
||||
wrapRaw->show(anim::type::normal);
|
||||
wrapRaw->raise();
|
||||
};
|
||||
mainwidget->sizeValue() | rpl::on_next([=](QSize) {
|
||||
updateLayout();
|
||||
}, mediaPreviewRaw->lifetime());
|
||||
if (wrapRaw) {
|
||||
wrapRaw->entity()->sizeValue(
|
||||
) | rpl::skip(1) | rpl::on_next([=](QSize) {
|
||||
updateLayout();
|
||||
}, wrapRaw->lifetime());
|
||||
}
|
||||
return updateLayout;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -183,6 +197,7 @@ bool ShowStickerPreview(
|
||||
SetupPreviewMenu(
|
||||
controller,
|
||||
CreatePreviewOverlay(controller, origin, document),
|
||||
st::dropdownMenuWithIcons,
|
||||
std::move(fillMenu));
|
||||
return true;
|
||||
}
|
||||
@@ -195,6 +210,7 @@ bool ShowPhotoPreview(
|
||||
SetupPreviewMenu(
|
||||
controller,
|
||||
CreatePreviewOverlay(controller, origin, photo),
|
||||
st::dropdownMenuWithIcons,
|
||||
std::move(fillMenu));
|
||||
return true;
|
||||
}
|
||||
@@ -203,7 +219,8 @@ bool ShowReactionPreview(
|
||||
not_null<Window::SessionController*> controller,
|
||||
FullMsgId origin,
|
||||
Data::ReactionId reactionId,
|
||||
bool emojiPreview) {
|
||||
bool emojiPreview,
|
||||
Fn<void(ReactionPreviewMenu)> setupMenu) {
|
||||
auto document = (DocumentData*)(nullptr);
|
||||
if (const auto custom = reactionId.custom()) {
|
||||
document = controller->session().data().document(custom);
|
||||
@@ -219,8 +236,36 @@ bool ShowReactionPreview(
|
||||
const auto &state = overlay.state;
|
||||
|
||||
const auto mainwidget = controller->widget()->bodyWidget();
|
||||
const auto shadowExtend = st::boxRoundShadow.extend;
|
||||
|
||||
if (setupMenu) {
|
||||
const auto layout = std::make_shared<Fn<void()>>();
|
||||
const auto refreshGeometry = [=] {
|
||||
if (const auto callback = *layout) {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
*layout = SetupPreviewMenu(
|
||||
controller,
|
||||
overlay,
|
||||
st::whoReadDropdownMenu,
|
||||
[&](not_null<Ui::DropdownMenu*> menu) {
|
||||
const auto maxHeight = st::whoReadDropdownMenuMaxHeight;
|
||||
menu->setMaxHeight(std::clamp(
|
||||
(mainwidget->height()
|
||||
- st::maxStickerSize
|
||||
- st::whoReadDropdownMenuSkip * 2),
|
||||
maxHeight / 4,
|
||||
maxHeight));
|
||||
setupMenu({
|
||||
.menu = menu,
|
||||
.refreshGeometry = refreshGeometry,
|
||||
.hide = overlay.hideAll,
|
||||
});
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
const auto shadowExtend = st::boxRoundShadow.extend;
|
||||
if (reactionId.custom() && document->sticker()) {
|
||||
const auto setId = document->sticker()->set;
|
||||
const auto packName
|
||||
|
||||
@@ -25,6 +25,13 @@ class SessionController;
|
||||
|
||||
namespace HistoryView {
|
||||
|
||||
struct ReactionPreviewMenu {
|
||||
Ui::DropdownMenu *menu = nullptr;
|
||||
Fn<void()> refreshGeometry;
|
||||
|
||||
Fn<void()> hide;
|
||||
};
|
||||
|
||||
bool ShowStickerPreview(
|
||||
not_null<Window::SessionController*> controller,
|
||||
FullMsgId origin,
|
||||
@@ -46,6 +53,7 @@ bool ShowReactionPreview(
|
||||
not_null<Window::SessionController*> controller,
|
||||
FullMsgId origin,
|
||||
Data::ReactionId reactionId,
|
||||
bool emojiPreview = false);
|
||||
bool emojiPreview = false,
|
||||
Fn<void(ReactionPreviewMenu)> setupMenu = nullptr);
|
||||
|
||||
} // namespace HistoryView
|
||||
|
||||
@@ -647,6 +647,16 @@ whoReadMenu: PopupMenu(popupMenuExpandedSeparator) {
|
||||
scrollPadding: margins(0px, 6px, 0px, 4px);
|
||||
maxHeight: 400px;
|
||||
}
|
||||
whoReadDropdownMenu: DropdownMenu(dropdownMenuWithIcons) {
|
||||
wrap: InnerDropdown(defaultInnerDropdown) {
|
||||
scrollPadding: margins(0px, 6px, 0px, 4px);
|
||||
}
|
||||
menu: Menu(menuWithIcons) {
|
||||
separator: expandedMenuSeparator;
|
||||
}
|
||||
}
|
||||
whoReadDropdownMenuMaxHeight: 400px;
|
||||
whoReadDropdownMenuSkip: 24px;
|
||||
whoReadNameWithDateTop: 3px;
|
||||
whoReadDateTop: 20px;
|
||||
whoReadDateSkip: 15px;
|
||||
|
||||
@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
#include "base/call_delayed.h"
|
||||
#include "ui/widgets/menu/menu_action.h"
|
||||
#include "ui/widgets/dropdown_menu.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "ui/effects/ripple_animation.h"
|
||||
#include "ui/chat/group_call_userpics.h"
|
||||
@@ -1162,7 +1163,25 @@ void WhoReactedEntryAction::paint(Painter &&p) {
|
||||
_textWidth,
|
||||
width());
|
||||
}
|
||||
if (_type == WhoReactedType::RefRecipient
|
||||
if (preloader) {
|
||||
if (withDate) {
|
||||
auto hq = PainterHighQualityEnabler(p);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(preloaderBrush);
|
||||
const auto &font = st::whoReadDateStyle.font;
|
||||
const auto height = font->height / 2;
|
||||
const auto width = std::min(
|
||||
st::whoReadDateSkip + _date.maxWidth(),
|
||||
_textWidth);
|
||||
p.drawRoundedRect(
|
||||
st::defaultWhoRead.nameLeft,
|
||||
st::whoReadDateTop + (font->height - height) / 2,
|
||||
width,
|
||||
height,
|
||||
height / 2.,
|
||||
height / 2.);
|
||||
}
|
||||
} else if (_type == WhoReactedType::RefRecipient
|
||||
|| _type == WhoReactedType::RefRecipientNow) {
|
||||
p.setPen(selected ? _st.itemFgShortcutOver : _st.itemFgShortcut);
|
||||
_date.drawLeftElided(
|
||||
@@ -1298,6 +1317,7 @@ WhoReactedListMenu::WhoReactedListMenu(
|
||||
|
||||
void WhoReactedListMenu::clear() {
|
||||
_actions.clear();
|
||||
_minimalWidth = 0;
|
||||
}
|
||||
|
||||
void WhoReactedListMenu::populate(
|
||||
@@ -1306,13 +1326,80 @@ void WhoReactedListMenu::populate(
|
||||
Fn<void()> refillTopActions,
|
||||
int addedToBottom,
|
||||
Fn<void()> appendBottomActions) {
|
||||
populateTo(
|
||||
menu,
|
||||
content,
|
||||
std::move(refillTopActions),
|
||||
addedToBottom,
|
||||
std::move(appendBottomActions));
|
||||
}
|
||||
|
||||
void WhoReactedListMenu::populate(
|
||||
not_null<DropdownMenu*> menu,
|
||||
const WhoReadContent &content,
|
||||
Fn<void()> refillTopActions,
|
||||
int addedToBottom,
|
||||
Fn<void()> appendBottomActions) {
|
||||
populateTo(
|
||||
menu,
|
||||
content,
|
||||
std::move(refillTopActions),
|
||||
addedToBottom,
|
||||
std::move(appendBottomActions));
|
||||
}
|
||||
|
||||
void WhoReactedListMenu::populatePreloader(
|
||||
not_null<DropdownMenu*> menu,
|
||||
std::vector<WhoReactedEntryData> entries,
|
||||
Fn<void()> appendBottomActions) {
|
||||
for (auto &entry : entries) {
|
||||
entry.type = WhoReactedType::Preloader;
|
||||
entry.userpic = QImage();
|
||||
entry.callback = nullptr;
|
||||
entry.closeCallback = nullptr;
|
||||
auto item = base::make_unique_q<WhoReactedEntryAction>(
|
||||
menu->menu(),
|
||||
_customEmojiFactory,
|
||||
menu->menu()->st(),
|
||||
std::move(entry));
|
||||
accumulate_max(_minimalWidth, item->minWidth());
|
||||
_actions.push_back(item.get());
|
||||
menu->addAction(std::move(item));
|
||||
}
|
||||
applyMinimalWidth();
|
||||
if (appendBottomActions) {
|
||||
appendBottomActions();
|
||||
}
|
||||
}
|
||||
|
||||
void WhoReactedListMenu::applyMinimalWidth() {
|
||||
if (!_minimalWidth) {
|
||||
return;
|
||||
}
|
||||
for (const auto &action : _actions) {
|
||||
if (action->minWidth() < _minimalWidth) {
|
||||
action->setMinWidth(_minimalWidth);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Menu>
|
||||
void WhoReactedListMenu::populateTo(
|
||||
not_null<Menu*> menu,
|
||||
const WhoReadContent &content,
|
||||
Fn<void()> refillTopActions,
|
||||
int addedToBottom,
|
||||
Fn<void()> appendBottomActions) {
|
||||
constexpr auto kRebuildOnAnyChange = !std::is_same_v<Menu, PopupMenu>;
|
||||
const auto reactions = ranges::count_if(
|
||||
content.participants,
|
||||
[](const auto &p) { return !p.customEntityData.isEmpty(); });
|
||||
const auto addShowAll = (content.fullReactionsCount > reactions);
|
||||
const auto actionsCount = int(content.participants.size())
|
||||
+ (addShowAll ? 1 : 0);
|
||||
if (_actions.size() > actionsCount) {
|
||||
if (kRebuildOnAnyChange
|
||||
? (_actions.size() != actionsCount)
|
||||
: (_actions.size() > actionsCount)) {
|
||||
_actions.clear();
|
||||
menu->clearActions();
|
||||
if (refillTopActions) {
|
||||
@@ -1331,11 +1418,17 @@ void WhoReactedListMenu::populate(
|
||||
menu->menu()->st(),
|
||||
std::move(data));
|
||||
_actions.push_back(item.get());
|
||||
const auto count = int(menu->actions().size());
|
||||
if (addedToBottom > 0 && addedToBottom <= count) {
|
||||
menu->insertAction(count - addedToBottom, std::move(item));
|
||||
} else {
|
||||
if constexpr (kRebuildOnAnyChange) {
|
||||
menu->addAction(std::move(item));
|
||||
} else {
|
||||
const auto count = int(menu->actions().size());
|
||||
if (addedToBottom > 0 && addedToBottom <= count) {
|
||||
menu->insertAction(
|
||||
count - addedToBottom,
|
||||
std::move(item));
|
||||
} else {
|
||||
menu->addAction(std::move(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
++index;
|
||||
@@ -1369,6 +1462,7 @@ void WhoReactedListMenu::populate(
|
||||
.callback = _showAllChosen,
|
||||
});
|
||||
}
|
||||
applyMinimalWidth();
|
||||
if (!addedToBottom && appendBottomActions) {
|
||||
appendBottomActions();
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
namespace Ui {
|
||||
|
||||
class DropdownMenu;
|
||||
class PopupMenu;
|
||||
|
||||
struct WhoReadParticipant {
|
||||
@@ -162,14 +163,35 @@ public:
|
||||
Fn<void()> refillTopActions = nullptr,
|
||||
int addedToBottom = 0,
|
||||
Fn<void()> appendBottomActions = nullptr);
|
||||
void populate(
|
||||
not_null<DropdownMenu*> menu,
|
||||
const WhoReadContent &content,
|
||||
Fn<void()> refillTopActions = nullptr,
|
||||
int addedToBottom = 0,
|
||||
Fn<void()> appendBottomActions = nullptr);
|
||||
|
||||
void populatePreloader(
|
||||
not_null<DropdownMenu*> menu,
|
||||
std::vector<WhoReactedEntryData> entries,
|
||||
Fn<void()> appendBottomActions = nullptr);
|
||||
|
||||
private:
|
||||
template <typename Menu>
|
||||
void populateTo(
|
||||
not_null<Menu*> menu,
|
||||
const WhoReadContent &content,
|
||||
Fn<void()> refillTopActions,
|
||||
int addedToBottom,
|
||||
Fn<void()> appendBottomActions);
|
||||
void applyMinimalWidth();
|
||||
|
||||
const Text::CustomEmojiFactory _customEmojiFactory;
|
||||
const Fn<void(WhoReadParticipant)> _participantChosen;
|
||||
const Fn<void()> _showAllChosen;
|
||||
const Fn<void(WhoReadParticipant)> _moderateReactionChosen;
|
||||
|
||||
std::vector<not_null<WhoReactedEntryAction*>> _actions;
|
||||
int _minimalWidth = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user