/* This file is part of Telegram Desktop, the official desktop application for the Telegram messaging service. For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once #include "base/flags.h" #include "base/object_ptr.h" #include "base/weak_ptr.h" #include "base/timer.h" #include "dialogs/dialogs_key.h" #include "ui/layers/layer_widget.h" #include "ui/layers/show.h" #include "window/window_adaptive.h" class PhotoData; class MainWidget; class MainWindow; namespace Adaptive { enum class WindowLayout; } // namespace Adaptive namespace ChatHelpers { class TabbedSelector; class EmojiInteractions; } // namespace ChatHelpers namespace Main { class Session; } // namespace Main namespace Settings { enum class Type; } // namespace Settings namespace Calls { struct StartGroupCallArgs; } // namespace Calls namespace Passport { struct FormRequest; class FormController; } // namespace Passport namespace Ui { class LayerWidget; enum class ReportReason; class ChatStyle; class ChatTheme; struct ChatThemeKey; struct ChatPaintContext; struct ChatThemeBackground; struct ChatThemeBackgroundData; class MessageSendingAnimationController; } // namespace Ui namespace Data { struct CloudTheme; enum class CloudThemeType; } // namespace Data namespace HistoryView::Reactions { class CachedIconFactory; } // namespace HistoryView::Reactions namespace Window { class MainWindow; class SectionMemento; class Controller; class FiltersMenu; enum class GifPauseReason { Any = 0, InlineResults = (1 << 0), SavedGifs = (1 << 1), Layer = (1 << 2), RoundPlaying = (1 << 3), MediaPreview = (1 << 4), }; using GifPauseReasons = base::flags; inline constexpr bool is_flag_type(GifPauseReason) { return true; }; struct PeerThemeOverride { PeerData *peer = nullptr; std::shared_ptr theme; }; bool operator==(const PeerThemeOverride &a, const PeerThemeOverride &b); bool operator!=(const PeerThemeOverride &a, const PeerThemeOverride &b); class DateClickHandler : public ClickHandler { public: DateClickHandler(Dialogs::Key chat, QDate date); void setDate(QDate date); void onClick(ClickContext context) const override; private: Dialogs::Key _chat; QDate _date; }; struct SectionShow { enum class Way { Forward, Backward, ClearStack, }; struct OriginMessage { FullMsgId id; }; using Origin = std::variant; SectionShow( Way way = Way::Forward, anim::type animated = anim::type::normal, anim::activation activation = anim::activation::normal) : way(way) , animated(animated) , activation(activation) { } SectionShow( anim::type animated, anim::activation activation = anim::activation::normal) : animated(animated) , activation(activation) { } SectionShow withWay(Way newWay) const { return SectionShow(newWay, animated, activation); } SectionShow withThirdColumn() const { auto copy = *this; copy.thirdColumn = true; return copy; } Way way = Way::Forward; anim::type animated = anim::type::normal; anim::activation activation = anim::activation::normal; bool thirdColumn = false; Origin origin; }; class SessionController; class SessionNavigation : public base::has_weak_ptr { public: explicit SessionNavigation(not_null session); virtual ~SessionNavigation(); Main::Session &session() const; virtual void showSection( std::shared_ptr memento, const SectionShow ¶ms = SectionShow()) = 0; virtual void showBackFromStack( const SectionShow ¶ms = SectionShow()) = 0; virtual not_null parentController() = 0; struct CommentId { MsgId id = 0; }; struct ThreadId { MsgId id = 0; }; using RepliesByLinkInfo = std::variant; struct PeerByLinkInfo { std::variant usernameOrId; QString phone; MsgId messageId = ShowAtUnreadMsgId; RepliesByLinkInfo repliesInfo; QString startToken; std::optional voicechatHash; FullMsgId clickFromMessageId; }; void showPeerByLink(const PeerByLinkInfo &info); void showRepliesForMessage( not_null history, MsgId rootId, MsgId commentId = 0, const SectionShow ¶ms = SectionShow()); void showPeerInfo( PeerId peerId, const SectionShow ¶ms = SectionShow()); void showPeerInfo( not_null peer, const SectionShow ¶ms = SectionShow()); void showPeerInfo( not_null history, const SectionShow ¶ms = SectionShow()); virtual void showPeerHistory( PeerId peerId, const SectionShow ¶ms = SectionShow::Way::ClearStack, MsgId msgId = ShowAtUnreadMsgId) = 0; void showPeerHistory( not_null peer, const SectionShow ¶ms = SectionShow::Way::ClearStack, MsgId msgId = ShowAtUnreadMsgId); void showPeerHistory( not_null history, const SectionShow ¶ms = SectionShow::Way::ClearStack, MsgId msgId = ShowAtUnreadMsgId); void clearSectionStack( const SectionShow ¶ms = SectionShow::Way::ClearStack) { showPeerHistory( PeerId(0), params, ShowAtUnreadMsgId); } void showSettings( Settings::Type type, const SectionShow ¶ms = SectionShow()); void showSettings(const SectionShow ¶ms = SectionShow()); void showPollResults( not_null poll, FullMsgId contextId, const SectionShow ¶ms = SectionShow()); private: void resolvePhone( const QString &phone, Fn)> done); void resolveUsername( const QString &username, Fn)> done); void resolveChannelById( ChannelId channelId, Fn)> done); void resolveDone( const MTPcontacts_ResolvedPeer &result, Fn)> done); void showPeerByLinkResolved( not_null peer, const PeerByLinkInfo &info); const not_null _session; mtpRequestId _resolveRequestId = 0; History *_showingRepliesHistory = nullptr; MsgId _showingRepliesRootId = 0; mtpRequestId _showingRepliesRequestId = 0; }; class SessionController : public SessionNavigation { public: SessionController( not_null session, not_null window); ~SessionController(); [[nodiscard]] Controller &window() const { return *_window; } [[nodiscard]] PeerData *singlePeer() const; [[nodiscard]] bool isPrimary() const; [[nodiscard]] not_null<::MainWindow*> widget() const; [[nodiscard]] not_null content() const; [[nodiscard]] Adaptive &adaptive() const; [[nodiscard]] ChatHelpers::EmojiInteractions &emojiInteractions() const { return *_emojiInteractions; } // We need access to this from MainWidget::MainWidget, where // we can't call content() yet. void setSelectingPeer(bool selecting) { _selectingPeer = selecting; } [[nodiscard]] bool selectingPeer() const { return _selectingPeer; } void setConnectingBottomSkip(int skip); rpl::producer connectingBottomSkipValue() const; QPointer show( object_ptr content, Ui::LayerOptions options = Ui::LayerOption::KeepOther, anim::type animated = anim::type::normal); void hideLayer(anim::type animated = anim::type::normal); [[nodiscard]] auto sendingAnimation() const -> Ui::MessageSendingAnimationController &; [[nodiscard]] auto tabbedSelector() const -> not_null; void takeTabbedSelectorOwnershipFrom(not_null parent); [[nodiscard]] bool hasTabbedSelectorOwnership() const; // This is needed for History TopBar updating when searchInChat // is changed in the Dialogs::Widget of the current window. rpl::variable searchInChat; bool uniqueChatsInSearchResults() const; void openFolder(not_null folder); void closeFolder(); const rpl::variable &openedFolder() const; void setActiveChatEntry(Dialogs::RowDescriptor row); void setActiveChatEntry(Dialogs::Key key); Dialogs::RowDescriptor activeChatEntryCurrent() const; Dialogs::Key activeChatCurrent() const; rpl::producer activeChatEntryChanges() const; rpl::producer activeChatChanges() const; rpl::producer activeChatEntryValue() const; rpl::producer activeChatValue() const; bool jumpToChatListEntry(Dialogs::RowDescriptor row); [[nodiscard]] Dialogs::RowDescriptor resolveChatNext( Dialogs::RowDescriptor from = {}) const; [[nodiscard]] Dialogs::RowDescriptor resolveChatPrevious( Dialogs::RowDescriptor from = {}) const; void showEditPeerBox(PeerData *peer); void enableGifPauseReason(GifPauseReason reason); void disableGifPauseReason(GifPauseReason reason); rpl::producer<> gifPauseLevelChanged() const { return _gifPauseLevelChanged.events(); } bool isGifPausedAtLeastFor(GifPauseReason reason) const; void floatPlayerAreaUpdated(); struct ColumnLayout { int bodyWidth = 0; int dialogsWidth = 0; int chatWidth = 0; int thirdWidth = 0; Adaptive::WindowLayout windowLayout = Adaptive::WindowLayout(); }; [[nodiscard]] ColumnLayout computeColumnLayout() const; int dialogsSmallColumnWidth() const; bool forceWideDialogs() const; void updateColumnLayout(); bool canShowThirdSection() const; bool canShowThirdSectionWithoutResize() const; bool takeThirdSectionFromLayer(); void resizeForThirdSection(); void closeThirdSection(); void showPeer(not_null peer, MsgId msgId = ShowAtUnreadMsgId); void startOrJoinGroupCall( not_null peer, const Calls::StartGroupCallArgs &args); void showSection( std::shared_ptr memento, const SectionShow ¶ms = SectionShow()) override; void showBackFromStack( const SectionShow ¶ms = SectionShow()) override; using SessionNavigation::showPeerHistory; void showPeerHistory( PeerId peerId, const SectionShow ¶ms = SectionShow::Way::ClearStack, MsgId msgId = ShowAtUnreadMsgId) override; void showPeerHistoryAtItem(not_null item); void cancelUploadLayer(not_null item); void showLayer( std::unique_ptr &&layer, Ui::LayerOptions options, anim::type animated = anim::type::normal); void showSpecialLayer( object_ptr &&layer, anim::type animated = anim::type::normal); void hideSpecialLayer( anim::type animated = anim::type::normal) { showSpecialLayer(nullptr, animated); } void removeLayerBlackout(); void showCalendar( Dialogs::Key chat, QDate requestedDate); void showAddContact(); void showNewGroup(); void showNewChannel(); void showPassportForm(const Passport::FormRequest &request); void clearPassportForm(); void openPhoto(not_null photo, FullMsgId contextId); void openPhoto(not_null photo, not_null peer); void openDocument( not_null document, FullMsgId contextId, bool showInMediaView = false); void showChooseReportMessages( not_null peer, Ui::ReportReason reason, Fn done); void clearChooseReportMessages(); void toggleChooseChatTheme(not_null peer); base::Variable &dialogsListFocused() { return _dialogsListFocused; } const base::Variable &dialogsListFocused() const { return _dialogsListFocused; } base::Variable &dialogsListDisplayForced() { return _dialogsListDisplayForced; } const base::Variable &dialogsListDisplayForced() const { return _dialogsListDisplayForced; } not_null parentController() override { return this; } [[nodiscard]] int filtersWidth() const; [[nodiscard]] rpl::producer activeChatsFilter() const; [[nodiscard]] FilterId activeChatsFilterCurrent() const; void setActiveChatsFilter(FilterId id); void toggleFiltersMenu(bool enabled); [[nodiscard]] rpl::producer<> filtersMenuChanged() const; [[nodiscard]] auto defaultChatTheme() const -> const std::shared_ptr & { return _defaultChatTheme; } [[nodiscard]] auto cachedChatThemeValue( const Data::CloudTheme &data, Data::CloudThemeType type) -> rpl::producer>; void setChatStyleTheme(const std::shared_ptr &theme); void clearCachedChatThemes(); void pushLastUsedChatTheme(const std::shared_ptr &theme); void overridePeerTheme( not_null peer, std::shared_ptr theme); void clearPeerThemeOverride(not_null peer); [[nodiscard]] auto peerThemeOverrideValue() const -> rpl::producer { return _peerThemeOverride.value(); } struct PaintContextArgs { not_null theme; int visibleAreaTop = 0; int visibleAreaTopGlobal = 0; int visibleAreaWidth = 0; QRect clip; }; [[nodiscard]] Ui::ChatPaintContext preparePaintContext( PaintContextArgs &&args); [[nodiscard]] not_null chatStyle() const { return _chatStyle.get(); } [[nodiscard]] auto cachedReactionIconFactory() const -> HistoryView::Reactions::CachedIconFactory & { return *_cachedReactionIconFactory; } rpl::lifetime &lifetime() { return _lifetime; } private: struct CachedTheme; void init(); void initSupportMode(); void refreshFiltersMenu(); void checkOpenedFilter(); void suggestArchiveAndMute(); int minimalThreeColumnWidth() const; int countDialogsWidthFromRatio(int bodyWidth) const; int countThirdColumnWidthFromRatio(int bodyWidth) const; struct ShrinkResult { int dialogsWidth; int thirdWidth; }; ShrinkResult shrinkDialogsAndThirdColumns( int dialogsWidth, int thirdWidth, int bodyWidth) const; void pushToChatEntryHistory(Dialogs::RowDescriptor row); bool chatEntryHistoryMove(int steps); void resetFakeUnreadWhileOpened(); void checkInvitePeek(); void pushDefaultChatBackground(); void cacheChatTheme( const Data::CloudTheme &data, Data::CloudThemeType type); void cacheChatThemeDone(std::shared_ptr result); void updateCustomThemeBackground(CachedTheme &theme); [[nodiscard]] Ui::ChatThemeBackgroundData backgroundData( CachedTheme &theme, bool generateGradient = true) const; const not_null _window; const std::unique_ptr _emojiInteractions; using SendingAnimation = Ui::MessageSendingAnimationController; const std::unique_ptr _sendingAnimation; std::unique_ptr _passportForm; std::unique_ptr _filters; GifPauseReasons _gifPauseReasons = 0; rpl::event_stream<> _gifPauseLevelChanged; // Depends on _gifPause*. const std::unique_ptr _tabbedSelector; rpl::variable _activeChatEntry; base::Variable _dialogsListFocused = { false }; base::Variable _dialogsListDisplayForced = { false }; std::deque _chatEntryHistory; int _chatEntryHistoryPosition = -1; bool _selectingPeer = false; base::Timer _invitePeekTimer; rpl::variable _activeChatsFilter; rpl::variable _connectingBottomSkip; PeerData *_showEditPeer = nullptr; rpl::variable _openedFolder; rpl::event_stream<> _filtersMenuChanged; std::shared_ptr _defaultChatTheme; base::flat_map _customChatThemes; rpl::event_stream> _cachedThemesStream; const std::unique_ptr _chatStyle; std::weak_ptr _chatStyleTheme; std::deque> _lastUsedCustomChatThemes; rpl::variable _peerThemeOverride; using ReactionIconFactory = HistoryView::Reactions::CachedIconFactory; std::unique_ptr _cachedReactionIconFactory; rpl::lifetime _lifetime; }; void ActivateWindow(not_null controller); class Show : public Ui::Show { public: explicit Show(not_null navigation); explicit Show(not_null window); ~Show(); void showBox( object_ptr content, Ui::LayerOptions options = Ui::LayerOption::KeepOther) const override; void hideLayer() const override; [[nodiscard]] not_null toastParent() const override; [[nodiscard]] bool valid() const override; operator bool() const override; private: const base::weak_ptr _window; }; } // namespace Window