diff --git a/Telegram/SourceFiles/history/view/history_view_chat_preview.cpp b/Telegram/SourceFiles/history/view/history_view_chat_preview.cpp index 7779b8b28..c394af68b 100644 --- a/Telegram/SourceFiles/history/view/history_view_chat_preview.cpp +++ b/Telegram/SourceFiles/history/view/history_view_chat_preview.cpp @@ -7,10 +7,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "history/view/history_view_chat_preview.h" +#include "data/data_forum_topic.h" #include "data/data_peer.h" +#include "data/data_replies_list.h" +#include "data/data_thread.h" +#include "history/view/reactions/history_view_reactions_button.h" +#include "history/view/history_view_list_widget.h" #include "history/history.h" +#include "history/history_item.h" +#include "main/main_session.h" +#include "ui/chat/chat_style.h" #include "ui/chat/chat_theme.h" #include "ui/widgets/popup_menu.h" +#include "ui/widgets/elastic_scroll.h" #include "ui/widgets/menu/menu_item_base.h" #include "window/themes/window_theme.h" #include "window/section_widget.h" @@ -19,35 +28,159 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace HistoryView { namespace { -class Item final : public Ui::Menu::ItemBase { +class Item final + : public Ui::Menu::ItemBase + , private HistoryView::ListDelegate { public: - Item(not_null parent, not_null history); + Item(not_null parent, not_null thread); not_null action() const override; bool isEnabled() const override; private: - void setupBackground(); - int contentHeight() const override; - void paintEvent(QPaintEvent *e) override; + void setupBackground(); + void updateInnerVisibleArea(); + + Context listContext() override; + bool listScrollTo(int top, bool syntetic = true) override; + void listCancelRequest() override; + void listDeleteRequest() override; + void listTryProcessKeyInput(not_null e) override; + rpl::producer listSource( + Data::MessagePosition aroundId, + int limitBefore, + int limitAfter) override; + bool listAllowsMultiSelect() override; + bool listIsItemGoodForSelection(not_null item) override; + bool listIsLessInOrder( + not_null first, + not_null second) override; + void listSelectionChanged(SelectedItems &&items) override; + void listMarkReadTill(not_null item) override; + void listMarkContentsRead( + const base::flat_set> &items) override; + MessagesBarData listMessagesBar( + const std::vector> &elements) override; + void listContentRefreshed() override; + void listUpdateDateLink( + ClickHandlerPtr &link, + not_null view) override; + bool listElementHideReply(not_null view) override; + bool listElementShownUnread(not_null view) override; + bool listIsGoodForAroundPosition( + not_null view) override; + void listSendBotCommand( + const QString &command, + const FullMsgId &context) override; + void listSearch( + const QString &query, + const FullMsgId &context) override; + void listHandleViaClick(not_null bot) override; + not_null listChatTheme() override; + CopyRestrictionType listCopyRestrictionType( + HistoryItem *item) override; + CopyRestrictionType listCopyRestrictionType() { + return listCopyRestrictionType(nullptr); + } + CopyRestrictionType listCopyMediaRestrictionType( + not_null item) override; + CopyRestrictionType listSelectRestrictionType() override; + auto listAllowedReactionsValue() + -> rpl::producer override; + void listShowPremiumToast(not_null document) override; + void listOpenPhoto( + not_null photo, + FullMsgId context) override; + void listOpenDocument( + not_null document, + FullMsgId context, + bool showInMediaView) override; + void listPaintEmpty( + Painter &p, + const Ui::ChatPaintContext &context) override; + QString listElementAuthorRank(not_null view) override; + History *listTranslateHistory() override; + void listAddTranslatedItems( + not_null tracker) override; + not_null listWindow() override; + not_null listChatStyle() override; + rpl::producer listChatWideValue() override; + std::unique_ptr listMakeReactionsManager( + QWidget *wheelEventsTarget, + Fn update) override; + void listVisibleAreaUpdated() override; + std::shared_ptr listUiShow() override; + void listShowPollResults( + not_null poll, + FullMsgId context) override; + void listCancelUploadLayer(not_null item) override; + bool listAnimationsPaused() override; + auto listSendingAnimation() + -> Ui::MessageSendingAnimationController* override; + Ui::ChatPaintContext listPreparePaintContext( + Ui::ChatPaintContextArgs &&args) override; + bool listMarkingContentRead() override; + bool listIgnorePaintEvent(QWidget *w, QPaintEvent *e) override; + bool listShowReactPremiumError( + not_null item, + const Data::ReactionId &id) override; + void listWindowSetInnerFocus() override; + bool listAllowsDragForward() override; + void listLaunchDrag( + std::unique_ptr data, + Fn finished) override; + const not_null _dummyAction; + const not_null _session; + const not_null _thread; + const not_null _history; + const not_null _peer; const std::shared_ptr _theme; + const std::unique_ptr _chatStyle; + const std::unique_ptr _scroll; + + QPointer _inner; QImage _bg; }; -Item::Item(not_null parent, not_null history) +Item::Item(not_null parent, not_null thread) : Ui::Menu::ItemBase(parent, st::previewMenu.menu) , _dummyAction(new QAction(parent)) -, _theme(Window::Theme::DefaultChatThemeOn(lifetime())) { +, _session(&thread->session()) +, _thread(thread) +, _history(thread->owningHistory()) +, _peer(thread->peer()) +, _theme(Window::Theme::DefaultChatThemeOn(lifetime())) +, _chatStyle(std::make_unique(_session->colorIndicesValue())) +, _scroll(std::make_unique(this)) { setPointerCursor(false); setMinWidth(st::previewMenu.menu.widthMin); resize(minWidth(), contentHeight()); setupBackground(); + + _inner = _scroll->setOwnedWidget(object_ptr( + this, + _session, + static_cast(this))); + _scroll->setGeometry(rect()); + _scroll->scrolls( + ) | rpl::start_with_next([=] { + updateInnerVisibleArea(); + }, lifetime()); + _scroll->setOverscrollBg(QColor(0, 0, 0, 0)); + using Type = Ui::ElasticScroll::OverscrollType; + _scroll->setOverscrollTypes(Type::Real, Type::Real); + + _inner->resizeToWidth(_scroll->width(), _scroll->height()); + + _inner->refreshViewer(); + + _inner->setAttribute(Qt::WA_TransparentForMouseEvents); } not_null Item::action() const { @@ -88,24 +221,328 @@ void Item::paintEvent(QPaintEvent *e) { p.drawImage(0, 0, _bg); } +void Item::updateInnerVisibleArea() { + const auto scrollTop = _scroll->scrollTop(); + _inner->setVisibleTopBottom(scrollTop, scrollTop + _scroll->height()); +} + +Context Item::listContext() { + return Context::ChatPreview; +} + +bool Item::listScrollTo(int top, bool syntetic) { + top = std::clamp(top, 0, _scroll->scrollTopMax()); + if (_scroll->scrollTop() == top) { + updateInnerVisibleArea(); + return false; + } + _scroll->scrollToY(top); + return true; +} + +void Item::listCancelRequest() { +} + +void Item::listDeleteRequest() { +} + +void Item::listTryProcessKeyInput(not_null e) { +} + +rpl::producer Item::listSource( + Data::MessagePosition aroundId, + int limitBefore, + int limitAfter) { + if (const auto topic = _thread->asTopic()) { + return topic->replies()->source( + aroundId, + limitBefore, + limitAfter + ) | rpl::before_next([=] { // after_next makes a copy of value. + //if (!_loaded) { + // _loaded = true; + // crl::on_main(this, [=] { + // updatePinnedVisibility(); + // }); + //} + }); + } + // #TODO + //const auto messageId = aroundId.fullId.msg + // ? aroundId.fullId.msg + // : (ServerMaxMsgId - 1); + + //return SharedMediaMergedViewer( + // &_thread->session(), + // SharedMediaMergedKey( + // SparseIdsMergedSlice::Key( + // _history->peer->id, + // _thread->topicRootId(), + // _migratedPeer ? _migratedPeer->id : 0, + // messageId), + // Storage::SharedMediaType::Pinned), + // limitBefore, + // limitAfter + //) | rpl::map([=](SparseIdsMergedSlice &&slice) { + // auto result = Data::MessagesSlice(); + // result.fullCount = slice.fullCount(); + // result.skippedAfter = slice.skippedAfter(); + // result.skippedBefore = slice.skippedBefore(); + // const auto count = slice.size(); + // result.ids.reserve(count); + // if (const auto msgId = slice.nearest(messageId)) { + // result.nearestToAround = *msgId; + // } + // for (auto i = 0; i != count; ++i) { + // result.ids.push_back(slice[i]); + // } + // return result; + //}); + return rpl::single(Data::MessagesSlice()); +} + +bool Item::listAllowsMultiSelect() { + return false; +} + +bool Item::listIsItemGoodForSelection(not_null item) { + return false; +} + +bool Item::listIsLessInOrder( + not_null first, + not_null second) { + if (first->isRegular() && second->isRegular()) { + const auto firstPeer = first->history()->peer; + const auto secondPeer = second->history()->peer; + if (firstPeer == secondPeer) { + return first->id < second->id; + } else if (firstPeer->isChat()) { + return true; + } + return false; + } else if (first->isRegular()) { + return true; + } else if (second->isRegular()) { + return false; + } + return first->id < second->id; +} + +void Item::listSelectionChanged(SelectedItems &&items) { +} + +void Item::listMarkReadTill(not_null item) { +} + +void Item::listMarkContentsRead( + const base::flat_set> &items) { +} + +MessagesBarData Item::listMessagesBar( + const std::vector> &elements) { + return {};// #TODO +} + +void Item::listContentRefreshed() { +} + +void Item::listUpdateDateLink( + ClickHandlerPtr &link, + not_null view) { +} + +bool Item::listElementHideReply(not_null view) { + return false; +} + +bool Item::listElementShownUnread(not_null view) { + return view->data()->unread(view->data()->history()); +} + +bool Item::listIsGoodForAroundPosition(not_null view) { + return view->data()->isRegular(); +} + +void Item::listSendBotCommand( + const QString &command, + const FullMsgId &context) { +} + +void Item::listSearch( + const QString &query, + const FullMsgId &context) { +} + +void Item::listHandleViaClick(not_null bot) { +} + +not_null Item::listChatTheme() { + return _theme.get(); +} + +CopyRestrictionType Item::listCopyRestrictionType(HistoryItem *item) { + return CopyRestrictionType::None; +} + +CopyRestrictionType Item::listCopyMediaRestrictionType( + not_null item) { + return CopyRestrictionType::None; +} + +CopyRestrictionType Item::listSelectRestrictionType() { + return CopyRestrictionType::None; +} + +auto Item::listAllowedReactionsValue() +-> rpl::producer { + return rpl::single(Data::AllowedReactions()); +} + +void Item::listShowPremiumToast(not_null document) { +} + +void Item::listOpenPhoto( + not_null photo, + FullMsgId context) { +} + +void Item::listOpenDocument( + not_null document, + FullMsgId context, + bool showInMediaView) { +} + +void Item::listPaintEmpty( + Painter &p, + const Ui::ChatPaintContext &context) { + // #TODO +} + +QString Item::listElementAuthorRank(not_null view) { + return {}; +} + +History *Item::listTranslateHistory() { + return nullptr; +} + +void Item::listAddTranslatedItems( + not_null tracker) { +} + +not_null Item::listWindow() { + Unexpected("Item::listWindow."); +} + +not_null Item::listChatStyle() { + return _chatStyle.get(); +} + +rpl::producer Item::listChatWideValue() { + return rpl::single(false); +} + +std::unique_ptr Item::listMakeReactionsManager( + QWidget *wheelEventsTarget, + Fn update) { + return nullptr; +} + +void Item::listVisibleAreaUpdated() { +} + +std::shared_ptr Item::listUiShow() { + Unexpected("Item::listUiShow."); +} + +void Item::listShowPollResults( + not_null poll, + FullMsgId context) { +} + +void Item::listCancelUploadLayer(not_null item) { +} + +bool Item::listAnimationsPaused() { + return false; +} + +auto Item::listSendingAnimation() +-> Ui::MessageSendingAnimationController* { + return nullptr; +} + +Ui::ChatPaintContext Item::listPreparePaintContext( + Ui::ChatPaintContextArgs &&args) { + const auto visibleAreaTopLocal = mapFromGlobal( + args.visibleAreaPositionGlobal).y(); + const auto viewport = QRect( + 0, + args.visibleAreaTop - visibleAreaTopLocal, + args.visibleAreaWidth, + height()); + return args.theme->preparePaintContext( + _chatStyle.get(), + viewport, + args.clip, + false); +} + +bool Item::listMarkingContentRead() { + return false; +} + +bool Item::listIgnorePaintEvent(QWidget *w, QPaintEvent *e) { + return false; +} + +bool Item::listShowReactPremiumError( + not_null item, + const Data::ReactionId &id) { + return false; +} + +void Item::listWindowSetInnerFocus() { +} + +bool Item::listAllowsDragForward() { + return false; +} + +void Item::listLaunchDrag( + std::unique_ptr data, + Fn finished) { +} + } // namespace base::unique_qptr MakeChatPreview( QWidget *parent, not_null entry) { - if (const auto topic = entry->asTopic()) { - return nullptr; - } - const auto history = entry->asHistory(); - if (!history || history->peer->isForum()) { + const auto thread = entry->asThread(); + if (!thread) { return nullptr; + } else if (const auto history = entry->asHistory()) { + if (history->peer->isForum()) { + return nullptr; + } } auto result = base::make_unique_q( parent, st::previewMenu); - result->addAction(base::make_unique_q(result.get(), history)); + result->addAction(base::make_unique_q(result.get(), thread)); + if (const auto topic = thread->asTopic()) { + const auto weak = Ui::MakeWeak(result.get()); + topic->destroyed() | rpl::start_with_next([weak] { + if (const auto strong = weak.data()) { + LOG(("Preview hidden for a destroyed topic.")); + strong->hideMenu(true); + } + }, result->lifetime()); + } return result; } diff --git a/Telegram/SourceFiles/history/view/history_view_element.h b/Telegram/SourceFiles/history/view/history_view_element.h index 6db33ff4b..a11d0831d 100644 --- a/Telegram/SourceFiles/history/view/history_view_element.h +++ b/Telegram/SourceFiles/history/view/history_view_element.h @@ -61,6 +61,7 @@ enum class Context : char { TTLViewer, ShortcutMessages, ScheduledTopic, + ChatPreview, }; enum class OnlyEmojiAndSpaces : char { diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index 4eddd3b49..cc0b4aec7 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -2081,6 +2081,7 @@ bool Message::hasFromPhoto() const { case Context::AdminLog: return true; case Context::History: + case Context::ChatPreview: case Context::TTLViewer: case Context::Pinned: case Context::Replies: @@ -3281,6 +3282,7 @@ bool Message::hasFromName() const { case Context::AdminLog: return true; case Context::History: + case Context::ChatPreview: case Context::TTLViewer: case Context::Pinned: case Context::Replies: diff --git a/Telegram/SourceFiles/ui/chat/chat.style b/Telegram/SourceFiles/ui/chat/chat.style index c72097f25..5c2cbf99d 100644 --- a/Telegram/SourceFiles/ui/chat/chat.style +++ b/Telegram/SourceFiles/ui/chat/chat.style @@ -1084,4 +1084,6 @@ previewMenu: PopupMenu(defaultPopupMenu) { widthMax: 380px; } maxHeight: 420px; + radius: boxRadius; + shadow: boxRoundShadow; }