From a0d5456a4d95477616f8fd265d2d298009583a1c Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 17 Aug 2022 21:03:57 +0300 Subject: [PATCH] PoC content outside of PopupMenu. --- Telegram/SourceFiles/calls/calls.style | 7 +- .../calls/group/calls_cover_item.cpp | 2 +- .../chat_helpers/chat_helpers.style | 3 + .../SourceFiles/data/data_peer_values.cpp | 10 +- Telegram/SourceFiles/data/data_peer_values.h | 1 + .../history/history_inner_widget.cpp | 27 ++- .../view/history_view_context_menu.cpp | 6 +- .../view/history_view_react_selector.cpp | 178 ++++++++++++++++++ .../view/history_view_react_selector.h | 44 +++++ .../SourceFiles/media/view/media_view.style | 8 +- .../SourceFiles/media/view/media_view_pip.cpp | 3 +- Telegram/SourceFiles/ui/chat/chat.style | 7 +- Telegram/lib_spellcheck | 2 +- Telegram/lib_ui | 2 +- 14 files changed, 275 insertions(+), 25 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls.style b/Telegram/SourceFiles/calls/calls.style index eb5f05c5a1..6087381718 100644 --- a/Telegram/SourceFiles/calls/calls.style +++ b/Telegram/SourceFiles/calls/calls.style @@ -512,9 +512,10 @@ groupCallMenu: Menu(defaultMenu) { itemFgShortcutOver: groupCallMemberNotJoinedStatus; itemFgShortcutDisabled: groupCallMemberNotJoinedStatus; - separatorFg: groupCallMenuBgOver; - separatorPadding: margins(0px, 4px, 0px, 4px); - + separator: MenuSeparator(defaultMenuSeparator) { + padding: margins(0px, 4px, 0px, 4px); + fg: groupCallMenuBgOver; + } arrow: icon {{ "dropdown_submenu_arrow", groupCallMemberNotJoinedStatus }}; ripple: RippleAnimation(defaultRippleAnimation) { diff --git a/Telegram/SourceFiles/calls/group/calls_cover_item.cpp b/Telegram/SourceFiles/calls/group/calls_cover_item.cpp index a448893e13..dc77bbe289 100644 --- a/Telegram/SourceFiles/calls/group/calls_cover_item.cpp +++ b/Telegram/SourceFiles/calls/group/calls_cover_item.cpp @@ -50,7 +50,7 @@ bool CoverItem::isEnabled() const { } int CoverItem::contentHeight() const { - return _st.size + st::groupCallMenu.separatorPadding.bottom(); + return _st.size + st::groupCallMenu.separator.padding.bottom(); } AboutItem::AboutItem( diff --git a/Telegram/SourceFiles/chat_helpers/chat_helpers.style b/Telegram/SourceFiles/chat_helpers/chat_helpers.style index 8d9c5e3ccc..2f830610c4 100644 --- a/Telegram/SourceFiles/chat_helpers/chat_helpers.style +++ b/Telegram/SourceFiles/chat_helpers/chat_helpers.style @@ -311,3 +311,6 @@ inlineRadialSize: 44px; inlineFileSize: 44px; stickersPremiumLock: icon{{ "emoji/premium_lock", premiumButtonFg }}; + +reactStripExtend: margins(21px, 49px, 39px, 0px); +reactStripHeight: 40px; diff --git a/Telegram/SourceFiles/data/data_peer_values.cpp b/Telegram/SourceFiles/data/data_peer_values.cpp index 407ddc8bcd..eff154d101 100644 --- a/Telegram/SourceFiles/data/data_peer_values.cpp +++ b/Telegram/SourceFiles/data/data_peer_values.cpp @@ -533,12 +533,20 @@ ReactionsFilter PeerReactionsFilter(not_null peer) { }); } +int UniqueReactionsLimit(not_null config) { + return config->get("reactions_uniq_max", 11); +} + +int UniqueReactionsLimit(not_null peer) { + return UniqueReactionsLimit(&peer->session().account().appConfig()); +} + rpl::producer UniqueReactionsLimitValue( not_null session) { const auto config = &session->account().appConfig(); return config->value( ) | rpl::map([=] { - return config->get("reactions_uniq_max", 11); + return UniqueReactionsLimit(config); }) | rpl::distinct_until_changed(); } diff --git a/Telegram/SourceFiles/data/data_peer_values.h b/Telegram/SourceFiles/data/data_peer_values.h index 1ec4afbc9b..61866e7fa4 100644 --- a/Telegram/SourceFiles/data/data_peer_values.h +++ b/Telegram/SourceFiles/data/data_peer_values.h @@ -138,6 +138,7 @@ inline auto PeerFullFlagValue( [[nodiscard]] rpl::producer PeerReactionsFilterValue( not_null peer); +[[nodiscard]] int UniqueReactionsLimit(not_null peer); [[nodiscard]] rpl::producer UniqueReactionsLimitValue( not_null session); diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index fdd0641de9..ccc5ceaed1 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -2040,11 +2040,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { e->accept(); return; } - _menu = base::make_unique_q( - this, - (hasWhoReactedItem - ? st::popupMenuExpandedSeparator - : st::popupMenuWithIcons)); + _menu = base::make_unique_q(this, st::popupMenuWithIcons); const auto session = &this->session(); const auto controller = _controller; const auto groupLeaderOrSelf = [](HistoryItem *item) -> HistoryItem* { @@ -2451,10 +2447,25 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { if (_menu->empty()) { _menu = nullptr; - } else { - _menu->popup(e->globalPos()); - e->accept(); + return; } + using namespace HistoryView::Reactions; + const auto desiredPosition = e->globalPos(); + const auto reactItem = Element::Hovered() + ? Element::Hovered()->data().get() + : nullptr; + const auto attached = reactItem + ? AttachSelectorToMenu(_menu.get(), desiredPosition, reactItem) + : AttachSelectorResult::Skipped; + if (attached == AttachSelectorResult::Failed) { + _menu = nullptr; + return; + } else if (attached == AttachSelectorResult::Attached) { + _menu->popupPrepared(); + } else { + _menu->popup(desiredPosition); + } + e->accept(); } bool HistoryInner::hasCopyRestriction(HistoryItem *item) const { diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp index 7adc9dbd40..01c7481858 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp @@ -952,9 +952,7 @@ base::unique_qptr FillContextMenu( auto result = base::make_unique_q( list, - (hasWhoReactedItem - ? st::popupMenuExpandedSeparator - : st::popupMenuWithIcons)); + st::popupMenuWithIcons); if (request.overSelection && !list->hasCopyRestrictionForSelected()) { const auto text = request.selectedItems.empty() @@ -1158,7 +1156,7 @@ void AddWhoReactedAction( } }; if (!menu->empty()) { - menu->addSeparator(); + menu->addSeparator(&st::expandedMenuSeparator); } menu->addAction(Ui::WhoReactedContextAction( menu.get(), diff --git a/Telegram/SourceFiles/history/view/history_view_react_selector.cpp b/Telegram/SourceFiles/history/view/history_view_react_selector.cpp index 8ef2619d15..bd4acffe61 100644 --- a/Telegram/SourceFiles/history/view/history_view_react_selector.cpp +++ b/Telegram/SourceFiles/history/view/history_view_react_selector.cpp @@ -9,10 +9,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_react_button.h" #include "data/data_document.h" +#include "data/data_session.h" +#include "data/data_message_reactions.h" +#include "data/data_peer_values.h" #include "chat_helpers/tabbed_panel.h" #include "chat_helpers/tabbed_selector.h" +#include "ui/widgets/popup_menu.h" +#include "history/history.h" +#include "history/history_item.h" #include "window/window_session_controller.h" #include "window/window_controller.h" +#include "main/main_session.h" #include "mainwindow.h" #include "styles/style_chat_helpers.h" @@ -104,4 +111,175 @@ void Selector::hide(anim::type animated) { } } +PopupSelector::PopupSelector( + not_null parent, + PossibleReactions reactions) +: RpWidget(parent) { +} + +int PopupSelector::countWidth(int desiredWidth, int maxWidth) { + return maxWidth; +} + +QMargins PopupSelector::extentsForShadow() const { + return st::defaultPopupMenu.shadow.extend; +} + +int PopupSelector::extendTopForCategories() const { + return st::emojiFooterHeight; +} + +int PopupSelector::desiredHeight() const { + return st::emojiPanMaxHeight; +} + +void PopupSelector::paintEvent(QPaintEvent *e) { + QPainter(this).fillRect(e->rect(), QColor(0, 128, 0, 128)); +} + +[[nodiscard]] PossibleReactions LookupPossibleReactions( + not_null item) { + if (!item->canReact()) { + return {}; + } + auto result = PossibleReactions(); + const auto peer = item->history()->peer; + const auto session = &peer->session(); + const auto reactions = &session->data().reactions(); + const auto &full = reactions->list(Data::Reactions::Type::Active); + const auto &all = item->reactions(); + const auto my = item->chosenReaction(); + auto myIsUnique = false; + for (const auto &[id, count] : all) { + if (count == 1 && id == my) { + myIsUnique = true; + } + } + const auto notMineCount = int(all.size()) - (myIsUnique ? 1 : 0); + const auto limit = Data::UniqueReactionsLimit(peer); + if (limit > 0 && notMineCount >= limit) { + result.recent.reserve(all.size()); + for (const auto &reaction : full) { + const auto id = reaction.id; + if (all.contains(id)) { + result.recent.push_back(id); + } + } + } else { + const auto filter = Data::PeerReactionsFilter(peer); + result.recent.reserve(filter.allowed + ? filter.allowed->size() + : full.size()); + for (const auto &reaction : full) { + const auto id = reaction.id; + const auto emoji = filter.allowed ? id.emoji() : QString(); + if (filter.allowed + && (emoji.isEmpty() || !filter.allowed->contains(emoji))) { + continue; + } else if (reaction.premium + && !session->premium() + && !all.contains(id)) { + if (session->premiumPossible()) { + result.morePremiumAvailable = true; + } + continue; + } else { + result.recent.push_back(id); + } + } + result.customAllowed = peer->isUser(); + } + const auto i = ranges::find(result.recent, reactions->favorite()); + if (i != end(result.recent) && i != begin(result.recent)) { + std::rotate(begin(result.recent), i, i + 1); + } + return result; +} + +bool SetupSelectorInMenuGeometry( + not_null menu, + QPoint desiredPosition, + not_null selector) { + const auto extend = st::reactStripExtend; + const auto added = extend.left() + extend.right(); + const auto desiredWidth = menu->width() + added; + const auto maxWidth = menu->st().menu.widthMax + added; + const auto width = selector->countWidth(desiredWidth, maxWidth); + const auto extents = selector->extentsForShadow(); + const auto categoriesTop = selector->extendTopForCategories(); + menu->setForceWidth(width - added); + const auto height = menu->height(); + const auto fullTop = extents.top() + categoriesTop + extend.top(); + const auto minimalHeight = extents.top() + + std::min( + selector->desiredHeight(), + categoriesTop + st::emojiPanMinHeight / 2) + + extents.bottom(); + const auto willBeHeightWithoutBottomPadding = fullTop + + height + - menu->st().shadow.extend.top(); + const auto additionalPaddingBottom + = (willBeHeightWithoutBottomPadding < minimalHeight + ? (minimalHeight - willBeHeightWithoutBottomPadding) + : 0); + menu->setAdditionalMenuPadding(QMargins( + extents.left() + extend.left(), + fullTop, + extents.right() + extend.right(), + additionalPaddingBottom + ), QMargins( + extents.left(), + extents.top(), + extents.right(), + std::min(additionalPaddingBottom, extents.bottom()) + )); + if (!menu->prepareGeometryFor(desiredPosition)) { + return false; + } + const auto origin = menu->preparedOrigin(); + if (!additionalPaddingBottom + || origin == Ui::PanelAnimation::Origin::TopLeft + || origin == Ui::PanelAnimation::Origin::TopRight) { + return true; + } + menu->setAdditionalMenuPadding(QMargins( + extents.left() + extend.left(), + fullTop + additionalPaddingBottom, + extents.right() + extend.right(), + 0 + ), QMargins( + extents.left(), + extents.top(), + extents.right(), + 0 + )); + return menu->prepareGeometryFor(desiredPosition); +} + +AttachSelectorResult AttachSelectorToMenu( + not_null menu, + QPoint desiredPosition, + not_null item) { + auto reactions = LookupPossibleReactions(item); + if (reactions.recent.empty() && !reactions.morePremiumAvailable) { + return AttachSelectorResult::Skipped; + } + const auto selector = Ui::CreateChild( + menu.get(), + std::move(reactions)); + if (!SetupSelectorInMenuGeometry(menu, desiredPosition, selector)) { + return AttachSelectorResult::Failed; + } + const auto extents = selector->extentsForShadow(); + const auto categoriesTop = selector->extendTopForCategories(); + selector->setGeometry( + extents.left(), + menu->preparedPadding().top() - st::reactStripExtend.top(), + menu->width() - extents.left() - extents.right(), + st::reactStripHeight); + selector->lower(); + selector->show(); + return AttachSelectorResult::Attached; +} + } // namespace HistoryView::Reactions diff --git a/Telegram/SourceFiles/history/view/history_view_react_selector.h b/Telegram/SourceFiles/history/view/history_view_react_selector.h index 1d3fb88bac..3a6af84163 100644 --- a/Telegram/SourceFiles/history/view/history_view_react_selector.h +++ b/Telegram/SourceFiles/history/view/history_view_react_selector.h @@ -9,6 +9,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/unique_qptr.h" #include "ui/effects/animation_value.h" +#include "ui/rp_widget.h" + +namespace Data { +struct ReactionId; +} // namespace Data namespace ChatHelpers { class TabbedPanel; @@ -18,6 +23,10 @@ namespace Window { class SessionController; } // namespace Window +namespace Ui { +class PopupMenu; +} // namespace Ui + namespace HistoryView::Reactions { struct ChosenReaction; @@ -44,4 +53,39 @@ private: }; +struct PossibleReactions { + std::vector recent; + bool morePremiumAvailable = false; + bool customAllowed = false; +}; + +class PopupSelector final : public Ui::RpWidget { +public: + PopupSelector( + not_null parent, + PossibleReactions reactions); + + int countWidth(int desiredWidth, int maxWidth); + [[nodiscard]] QMargins extentsForShadow() const; + [[nodiscard]] int extendTopForCategories() const; + [[nodiscard]] int desiredHeight() const; + +protected: + void paintEvent(QPaintEvent *e); + +private: + int _columns = 0; + +}; + +enum class AttachSelectorResult { + Skipped, + Failed, + Attached, +}; +AttachSelectorResult AttachSelectorToMenu( + not_null menu, + QPoint desiredPosition, + not_null item); + } // namespace HistoryView::Reactions diff --git a/Telegram/SourceFiles/media/view/media_view.style b/Telegram/SourceFiles/media/view/media_view.style index 672903080d..66cfa7497b 100644 --- a/Telegram/SourceFiles/media/view/media_view.style +++ b/Telegram/SourceFiles/media/view/media_view.style @@ -148,7 +148,9 @@ mediaviewMenu: Menu(menuWithIcons) { itemFgShortcutOver: mediaviewMenuFg; itemFgShortcutDisabled: mediaviewMenuFg; - separatorFg: mediaviewMenuFg; + separator: MenuSeparator(defaultMenuSeparator) { + fg: mediaviewMenuFg; + } ripple: RippleAnimation(defaultRippleAnimation) { color: mediaviewMenuBgRipple; @@ -186,7 +188,9 @@ mediaviewControlsMenu: Menu(defaultMenu) { itemFgShortcutOver: mediaviewPlaybackProgressFg; itemFgShortcutDisabled: mediaviewPlaybackProgressFg; - separatorFg: mediaviewPlaybackIconRipple; + separator: MenuSeparator(defaultMenuSeparator) { + fg: mediaviewPlaybackIconRipple; + } arrow: icon {{ "dropdown_submenu_arrow", mediaviewPlaybackProgressFg }}; diff --git a/Telegram/SourceFiles/media/view/media_view_pip.cpp b/Telegram/SourceFiles/media/view/media_view_pip.cpp index 8b88c99983..e2c09e72e7 100644 --- a/Telegram/SourceFiles/media/view/media_view_pip.cpp +++ b/Telegram/SourceFiles/media/view/media_view_pip.cpp @@ -857,8 +857,7 @@ void PipPanel::updateDecorations() { } }); const auto position = countPosition(); - const auto center = position.geometry.center(); - const auto use = Ui::Platform::TranslucentWindowsSupported(center); + const auto use = Ui::Platform::TranslucentWindowsSupported(); const auto full = use ? st::callShadow.extend : style::margins(); const auto padding = style::margins( (position.attached & RectPart::Left) ? 0 : full.left(), diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index a31cbe5b3d..4fd19bb1b6 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -936,10 +936,13 @@ ttlDividerLabelPadding: margins(22px, 10px, 22px, 19px); ttlItemPadding: margins(0px, 4px, 0px, 4px); ttlItemTimerFont: font(12px); +expandedMenuSeparator: MenuSeparator(defaultMenuSeparator) { + padding: margins(0px, 4px, 0px, 4px); + width: 6px; +} popupMenuExpandedSeparator: PopupMenu(popupMenuWithIcons) { menu: Menu(menuWithIcons) { - separatorPadding: margins(0px, 4px, 0px, 4px); - separatorWidth: 6px; + separator: expandedMenuSeparator; } } diff --git a/Telegram/lib_spellcheck b/Telegram/lib_spellcheck index b2f2b7b6bc..0e386e22cb 160000 --- a/Telegram/lib_spellcheck +++ b/Telegram/lib_spellcheck @@ -1 +1 @@ -Subproject commit b2f2b7b6bce60bc1c6429a6b5ec4bc5891baa060 +Subproject commit 0e386e22cb6ba8a114b569840a635e096dcb645e diff --git a/Telegram/lib_ui b/Telegram/lib_ui index 1cc74a41c4..a76cdf7edf 160000 --- a/Telegram/lib_ui +++ b/Telegram/lib_ui @@ -1 +1 @@ -Subproject commit 1cc74a41c4d1c3fe9824e4bf47c23e3bcd1759e6 +Subproject commit a76cdf7edffb6e9cbef515a0e599c3c14a8a5b53