From 69d21f73ef387da9ffb022acaea593aeeee729a3 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 28 Jun 2024 21:44:06 +0400 Subject: [PATCH] Separate window for forums/topics/sublists/archive. --- Telegram/CMakeLists.txt | 2 + .../SourceFiles/boxes/choose_filter_box.cpp | 2 +- Telegram/SourceFiles/calls/calls_call.cpp | 9 +- .../chat_helpers/compose/compose_show.cpp | 8 +- Telegram/SourceFiles/core/application.cpp | 237 ++++++++---------- Telegram/SourceFiles/core/application.h | 38 ++- .../data/data_download_manager.cpp | 2 +- .../dialogs/dialogs_inner_widget.cpp | 16 +- .../dialogs/dialogs_inner_widget.h | 2 +- .../SourceFiles/dialogs/dialogs_widget.cpp | 66 +++-- Telegram/SourceFiles/dialogs/dialogs_widget.h | 1 + .../dialogs/ui/dialogs_suggestions.cpp | 1 + .../history/history_item_helpers.cpp | 4 +- .../SourceFiles/history/history_widget.cpp | 9 +- .../view/history_view_top_bar_widget.cpp | 47 +++- .../view/history_view_top_bar_widget.h | 1 + Telegram/SourceFiles/main/main_domain.cpp | 16 +- Telegram/SourceFiles/main/main_session.cpp | 3 +- Telegram/SourceFiles/mainwidget.cpp | 68 ++--- Telegram/SourceFiles/mainwidget.h | 12 +- Telegram/SourceFiles/mainwindow.cpp | 12 +- .../media/view/media_view_overlay_widget.cpp | 2 +- .../settings/settings_information.cpp | 6 +- Telegram/SourceFiles/window/main_window.cpp | 18 +- Telegram/SourceFiles/window/main_window.h | 5 +- .../window/notifications_manager.cpp | 2 +- .../SourceFiles/window/window_controller.cpp | 76 +++--- .../SourceFiles/window/window_controller.h | 26 +- .../SourceFiles/window/window_main_menu.cpp | 49 ++-- .../SourceFiles/window/window_peer_menu.cpp | 109 +++++--- .../SourceFiles/window/window_separate_id.cpp | 77 ++++++ .../SourceFiles/window/window_separate_id.h | 69 +++++ .../window/window_session_controller.cpp | 155 +++++++++--- .../window/window_session_controller.h | 17 +- 34 files changed, 753 insertions(+), 414 deletions(-) create mode 100644 Telegram/SourceFiles/window/window_separate_id.cpp create mode 100644 Telegram/SourceFiles/window/window_separate_id.h diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 2b41b09fc..e3051087e 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -1546,6 +1546,8 @@ PRIVATE window/window_peer_menu.cpp window/window_peer_menu.h window/window_section_common.h + window/window_separate_id.cpp + window/window_separate_id.h window/window_session_controller.cpp window/window_session_controller.h window/window_session_controller_link_info.h diff --git a/Telegram/SourceFiles/boxes/choose_filter_box.cpp b/Telegram/SourceFiles/boxes/choose_filter_box.cpp index bc61e9fe2..5af8577bf 100644 --- a/Telegram/SourceFiles/boxes/choose_filter_box.cpp +++ b/Telegram/SourceFiles/boxes/choose_filter_box.cpp @@ -81,7 +81,7 @@ void ChangeFilterById( MTP_int(filter.id()), filter.tl() )).done([=, chat = history->peer->name(), name = filter.title()] { - const auto account = &history->session().account(); + const auto account = not_null(&history->session().account()); if (const auto controller = Core::App().windowFor(account)) { controller->showToast((add ? tr::lng_filters_toast_add diff --git a/Telegram/SourceFiles/calls/calls_call.cpp b/Telegram/SourceFiles/calls/calls_call.cpp index 527839363..9f1272859 100644 --- a/Telegram/SourceFiles/calls/calls_call.cpp +++ b/Telegram/SourceFiles/calls/calls_call.cpp @@ -706,7 +706,8 @@ bool Call::handleUpdate(const MTPPhoneCall &call) { } } if (data.is_need_rating() && _id && _accessHash) { - const auto window = Core::App().windowFor(_user); + const auto window = Core::App().windowFor( + Window::SeparateId(_user)); const auto session = &_user->session(); const auto callId = _id; const auto callAccessHash = _accessHash; @@ -1402,7 +1403,8 @@ void Call::handleRequestError(const QString &error) { _user->name()) : QString(); if (!inform.isEmpty()) { - if (const auto window = Core::App().windowFor(_user)) { + if (const auto window = Core::App().windowFor( + Window::SeparateId(_user))) { window->show(Ui::MakeInformBox(inform)); } else { Ui::show(Ui::MakeInformBox(inform)); @@ -1420,7 +1422,8 @@ void Call::handleControllerError(const QString &error) { ? tr::lng_call_error_audio_io(tr::now) : QString(); if (!inform.isEmpty()) { - if (const auto window = Core::App().windowFor(_user)) { + if (const auto window = Core::App().windowFor( + Window::SeparateId(_user))) { window->show(Ui::MakeInformBox(inform)); } else { Ui::show(Ui::MakeInformBox(inform)); diff --git a/Telegram/SourceFiles/chat_helpers/compose/compose_show.cpp b/Telegram/SourceFiles/chat_helpers/compose/compose_show.cpp index 01bc33b8c..ba904b77c 100644 --- a/Telegram/SourceFiles/chat_helpers/compose/compose_show.cpp +++ b/Telegram/SourceFiles/chat_helpers/compose/compose_show.cpp @@ -30,15 +30,15 @@ ResolveWindow ResolveWindowDefault() { return (Window::SessionController*)nullptr; }; auto &app = Core::App(); + const auto account = not_null(&session->account()); if (const auto a = check(app.activeWindow())) { return a; } else if (const auto b = check(app.activePrimaryWindow())) { return b; - } else if (const auto c = check(app.windowFor(&session->account()))) { + } else if (const auto c = check(app.windowFor(account))) { return c; - } else if (const auto d = check( - app.ensureSeparateWindowForAccount( - &session->account()))) { + } else if (const auto d = check(app.ensureSeparateWindowFor( + account))) { return d; } return nullptr; diff --git a/Telegram/SourceFiles/core/application.cpp b/Telegram/SourceFiles/core/application.cpp index 8e16868c9..21d3aa7ca 100644 --- a/Telegram/SourceFiles/core/application.cpp +++ b/Telegram/SourceFiles/core/application.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/application.h" #include "data/data_abstract_structure.h" +#include "data/data_forum.h" #include "data/data_photo.h" #include "data/data_document.h" #include "data/data_session.h" @@ -91,6 +92,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "payments/payments_checkout_process.h" #include "export/export_manager.h" #include "webrtc/webrtc_environment.h" +#include "window/window_separate_id.h" #include "window/window_session_controller.h" #include "window/window_controller.h" #include "boxes/abstract_box.h" @@ -209,8 +211,7 @@ Application::~Application() { setLastActiveWindow(nullptr); _windowInSettings = _lastActivePrimaryWindow = nullptr; _closingAsyncWindows.clear(); - _secondaryWindows.clear(); - _primaryWindows.clear(); + _windows.clear(); _mediaView = nullptr; _notifications->clearAllFast(); @@ -315,8 +316,8 @@ void Application::run() { // Create mime database, so it won't be slow later. QMimeDatabase().mimeTypeForName(u"text/plain"_q); - _primaryWindows.emplace(nullptr, std::make_unique()); - setLastActiveWindow(_primaryWindows.front().second.get()); + _windows.emplace(nullptr, std::make_unique()); + setLastActiveWindow(_windows.front().second.get()); _windowInSettings = _lastActivePrimaryWindow = _lastActiveWindow; _domain->activeChanges( @@ -405,7 +406,7 @@ void Application::run() { } void Application::showAccount(not_null account) { - if (const auto separate = separateWindowForAccount(account)) { + if (const auto separate = separateWindowFor(account)) { _lastActivePrimaryWindow = separate; separate->activate(); } else if (const auto last = activePrimaryWindow()) { @@ -413,13 +414,13 @@ void Application::showAccount(not_null account) { } } -void Application::checkWindowAccount(not_null window) { - const auto account = window->maybeAccount(); - for (auto &[key, existing] : _primaryWindows) { - if (existing.get() == window && key != account) { +void Application::checkWindowId(not_null window) { + const auto id = window->id(); + for (auto &[existingId, existing] : _windows) { + if (existing.get() == window && existingId != id) { auto found = std::move(existing); - _primaryWindows.remove(key); - _primaryWindows.emplace(account, std::move(found)); + _windows.remove(existingId); + _windows.emplace(id, std::move(found)); break; } } @@ -495,10 +496,7 @@ void Application::startSystemDarkModeViewer() { void Application::enumerateWindows(Fn)> callback) const { - for (const auto &window : ranges::views::values(_primaryWindows)) { - callback(window.get()); - } - for (const auto &window : ranges::views::values(_secondaryWindows)) { + for (const auto &window : ranges::views::values(_windows)) { callback(window.get()); } } @@ -607,10 +605,7 @@ void Application::clearEmojiSourceImages() { } bool Application::isActiveForTrayMenu() const { - return ranges::any_of(ranges::views::values(_primaryWindows), [=]( - const std::unique_ptr &controller) { - return controller->widget()->isActiveForTrayMenu(); - }) || ranges::any_of(ranges::views::values(_secondaryWindows), [=]( + return ranges::any_of(ranges::views::values(_windows), [=]( const std::unique_ptr &controller) { return controller->widget()->isActiveForTrayMenu(); }); @@ -1287,44 +1282,36 @@ Window::Controller *Application::activePrimaryWindow() const { return _lastActivePrimaryWindow; } -Window::Controller *Application::separateWindowForAccount( - not_null account) const { - for (const auto &[openedAccount, window] : _primaryWindows) { - if (openedAccount == account.get()) { +Window::Controller *Application::separateWindowFor( + Window::SeparateId id) const { + for (const auto &[existingId, window] : _windows) { + if (existingId == id) { return window.get(); } } return nullptr; } -Window::Controller *Application::separateWindowForPeer( - not_null peer) const { - for (const auto &[history, window] : _secondaryWindows) { - if (history->peer == peer) { - return window.get(); - } - } - return nullptr; -} - -Window::Controller *Application::ensureSeparateWindowForPeer( - not_null peer, +Window::Controller *Application::ensureSeparateWindowFor( + Window::SeparateId id, MsgId showAtMsgId) { const auto activate = [&](not_null window) { window->activate(); return window; }; - - if (const auto existing = separateWindowForPeer(peer)) { - existing->sessionController()->showPeerHistory( - peer, - Window::SectionShow::Way::ClearStack, - showAtMsgId); + if (const auto existing = separateWindowFor(id)) { + if (id.thread && id.type == Window::SeparateType::Chat) { + existing->sessionController()->showThread( + id.thread, + showAtMsgId, + Window::SectionShow::Way::ClearStack); + } return activate(existing); } - const auto result = _secondaryWindows.emplace( - peer->owner().history(peer), - std::make_unique(peer, showAtMsgId) + + const auto result = _windows.emplace( + id, + std::make_unique(id, showAtMsgId) ).first->second.get(); processCreatedWindow(result); result->firstShow(); @@ -1332,55 +1319,63 @@ Window::Controller *Application::ensureSeparateWindowForPeer( return activate(result); } -Window::Controller *Application::ensureSeparateWindowForAccount( - not_null account) { - const auto activate = [&](not_null window) { - window->activate(); - return window; - }; - - if (const auto existing = separateWindowForAccount(account)) { - return activate(existing); - } - const auto result = _primaryWindows.emplace( - account, - std::make_unique(account) - ).first->second.get(); - processCreatedWindow(result); - result->firstShow(); - result->finishFirstShow(); - return activate(result); -} - -Window::Controller *Application::windowFor(not_null peer) const { - if (const auto separate = separateWindowForPeer(peer)) { - return separate; - } - return windowFor(&peer->account()); -} - -Window::Controller *Application::windowFor( - not_null account) const { - if (const auto separate = separateWindowForAccount(account)) { +Window::Controller *Application::windowFor(Window::SeparateId id) const { + if (const auto separate = separateWindowFor(id)) { return separate; + } else if (id && id.primary()) { + return windowFor(not_null(id.account)); } return activePrimaryWindow(); } +Window::Controller *Application::windowForShowingHistory( + not_null peer) const { + if (const auto separate = separateWindowFor(peer)) { + return separate; + } + auto result = (Window::Controller*)nullptr; + enumerateWindows([&](not_null window) { + if (const auto controller = window->sessionController()) { + const auto current = controller->activeChatCurrent(); + if (const auto history = current.history()) { + if (history->peer == peer) { + result = window; + } + } + } + }); + return result; +} + +Window::Controller *Application::windowForShowingForum( + not_null forum) const { + const auto id = Window::SeparateId( + Window::SeparateType::Forum, + forum->history()); + if (const auto separate = separateWindowFor(id)) { + return separate; + } + auto result = (Window::Controller*)nullptr; + enumerateWindows([&](not_null window) { + if (const auto controller = window->sessionController()) { + const auto current = controller->shownForum().current(); + if (forum == current) { + result = window; + } + } + }); + return result; +} + Window::Controller *Application::findWindow( not_null widget) const { const auto window = widget->window(); if (_lastActiveWindow && _lastActiveWindow->widget() == window) { return _lastActiveWindow; } - for (const auto &[account, primary] : _primaryWindows) { - if (primary->widget() == window) { - return primary.get(); - } - } - for (const auto &[history, secondary] : _secondaryWindows) { - if (secondary->widget() == window) { - return secondary.get(); + for (const auto &[id, controller] : _windows) { + if (controller->widget() == window) { + return controller.get(); } } return nullptr; @@ -1392,10 +1387,11 @@ Window::Controller *Application::activeWindow() const { bool Application::closeNonLastAsync(not_null window) { const auto hasOther = [&] { - for (const auto &[account, primary] : _primaryWindows) { - if (!_closingAsyncWindows.contains(primary.get()) - && primary.get() != window - && primary->maybeSession()) { + for (const auto &[id, controller] : _windows) { + if (id.primary() + && !_closingAsyncWindows.contains(controller.get()) + && controller.get() != window + && controller->maybeSession()) { return true; } } @@ -1457,10 +1453,10 @@ void Application::closeWindow(not_null window) { : nullptr; const auto next = nextFromStack ? nextFromStack - : (_primaryWindows.front().second.get() != window) - ? _primaryWindows.front().second.get() - : (_primaryWindows.back().second.get() != window) - ? _primaryWindows.back().second.get() + : (_windows.front().second.get() != window) + ? _windows.front().second.get() + : (_windows.back().second.get() != window) + ? _windows.back().second.get() : nullptr; Assert(next != window); @@ -1481,20 +1477,12 @@ void Application::closeWindow(not_null window) { } } _closingAsyncWindows.remove(window); - for (auto i = begin(_primaryWindows); i != end(_primaryWindows);) { + for (auto i = begin(_windows); i != end(_windows);) { if (i->second.get() == window) { Assert(_lastActiveWindow != window); Assert(_lastActivePrimaryWindow != window); Assert(_windowInSettings != window); - i = _primaryWindows.erase(i); - } else { - ++i; - } - } - for (auto i = begin(_secondaryWindows); i != end(_secondaryWindows);) { - if (i->second.get() == window) { - Assert(_lastActiveWindow != window); - i = _secondaryWindows.erase(i); + i = _windows.erase(i); } else { ++i; } @@ -1502,36 +1490,34 @@ void Application::closeWindow(not_null window) { const auto account = domain().started() ? &domain().active() : nullptr; - if (account && !_primaryWindows.contains(account) && _lastActiveWindow) { + if (account + && !_windows.contains(Window::SeparateId(account)) + && _lastActiveWindow) { domain().activate(&_lastActiveWindow->account()); } } void Application::closeChatFromWindows(not_null peer) { - if (const auto window = windowFor(peer) - ; window && !window->isPrimary()) { - closeWindow(window); - } - for (const auto &[history, window] : _secondaryWindows) { - if (const auto session = window->sessionController()) { - if (session->activeChatCurrent().peer() == peer) { - session->showPeerHistory( - window->singlePeer()->id, - Window::SectionShow::Way::ClearStack); - } - } - } - if (const auto window = windowFor(&peer->account())) { - if (const auto primary = window->sessionController()) { - if (primary->activeChatCurrent().peer() == peer) { - primary->clearSectionStack(); - } - if (const auto forum = primary->shownForum().current()) { - if (peer->forum() == forum) { - primary->closeForum(); + const auto closeOne = [&] { + for (const auto &[id, window] : _windows) { + if (id.thread && id.thread->peer() == peer) { + closeWindow(window.get()); + return true; + } else if (const auto controller = window->sessionController()) { + if (controller->activeChatCurrent().peer() == peer) { + controller->showByInitialId(); + } + if (const auto forum = controller->shownForum().current()) { + if (peer->forum() == forum) { + controller->closeForum(); + } } } } + return false; + }; + + while (closeOne()) { } } @@ -1737,11 +1723,8 @@ void Application::quitPreventFinished() { } void Application::quitDelayed() { - for (const auto &[account, window] : _primaryWindows) { - window->widget()->hide(); - } - for (const auto &[history, window] : _secondaryWindows) { - window->widget()->hide(); + for (const auto &[id, controller] : _windows) { + controller->widget()->hide(); } if (!_private->quitTimer.isActive()) { _private->quitTimer.setCallback([] { Sandbox::QuitWhenStarted(); }); diff --git a/Telegram/SourceFiles/core/application.h b/Telegram/SourceFiles/core/application.h index 08a29a35e..77fe09b7f 100644 --- a/Telegram/SourceFiles/core/application.h +++ b/Telegram/SourceFiles/core/application.h @@ -7,9 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "base/timer.h" #include "mtproto/mtproto_auth_key.h" #include "mtproto/mtproto_proxy_data.h" -#include "base/timer.h" +#include "window/window_separate_id.h" class History; @@ -29,11 +30,9 @@ namespace Window { class Controller; } // namespace Window -namespace Window { -namespace Notifications { +namespace Window::Notifications { class System; -} // namespace Notifications -} // namespace Window +} // namespace Window::Notifications namespace ChatHelpers { class EmojiKeywords; @@ -170,19 +169,17 @@ public: not_null widget) const; [[nodiscard]] Window::Controller *activeWindow() const; [[nodiscard]] Window::Controller *activePrimaryWindow() const; - [[nodiscard]] Window::Controller *separateWindowForAccount( - not_null account) const; - [[nodiscard]] Window::Controller *separateWindowForPeer( - not_null peer) const; - Window::Controller *ensureSeparateWindowForPeer( - not_null peer, - MsgId showAtMsgId); - Window::Controller *ensureSeparateWindowForAccount( - not_null account); + [[nodiscard]] Window::Controller *separateWindowFor( + Window::SeparateId id) const; + Window::Controller *ensureSeparateWindowFor( + Window::SeparateId id, + MsgId showAtMsgId = 0); [[nodiscard]] Window::Controller *windowFor( // Doesn't auto-switch. + Window::SeparateId id) const; + [[nodiscard]] Window::Controller *windowForShowingHistory( not_null peer) const; - [[nodiscard]] Window::Controller *windowFor( // Doesn't auto-switch. - not_null account) const; + [[nodiscard]] Window::Controller *windowForShowingForum( + not_null forum) const; [[nodiscard]] bool closeNonLastAsync( not_null window); void closeWindow(not_null window); @@ -195,7 +192,7 @@ public: void checkSystemDarkMode(); [[nodiscard]] bool isActiveForTrayMenu() const; void closeChatFromWindows(not_null peer); - void checkWindowAccount(not_null window); + void checkWindowId(not_null window); void activate(); // Media view interface. @@ -423,12 +420,9 @@ private: const std::unique_ptr _calls; const std::unique_ptr _iv; base::flat_map< - Main::Account*, - std::unique_ptr> _primaryWindows; + Window::SeparateId, + std::unique_ptr> _windows; base::flat_set> _closingAsyncWindows; - base::flat_map< - not_null, - std::unique_ptr> _secondaryWindows; std::vector> _windowStack; Window::Controller *_lastActiveWindow = nullptr; Window::Controller *_lastActivePrimaryWindow = nullptr; diff --git a/Telegram/SourceFiles/data/data_download_manager.cpp b/Telegram/SourceFiles/data/data_download_manager.cpp index 2fa5f05d4..29a1899e0 100644 --- a/Telegram/SourceFiles/data/data_download_manager.cpp +++ b/Telegram/SourceFiles/data/data_download_manager.cpp @@ -531,7 +531,7 @@ void DownloadManager::loadingStopWithConfirmation( return; } const auto window = Core::App().windowFor( - &item->history()->session().account()); + not_null(&item->history()->session().account())); if (!window) { return; } diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index fe5651774..1ac569e9c 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -3676,7 +3676,7 @@ void InnerWidget::preloadRowsData() { } } -bool InnerWidget::chooseCollapsedRow() { +bool InnerWidget::chooseCollapsedRow(Qt::KeyboardModifiers modifiers) { if (_state != WidgetState::Default) { return false; } else if ((_collapsedSelected < 0) @@ -3769,7 +3769,15 @@ bool InnerWidget::chooseHashtag() { ChosenRow InnerWidget::computeChosenRow() const { if (_state == WidgetState::Default) { - if (_selected) { + if ((_collapsedSelected >= 0) + && (_collapsedSelected < _collapsedRows.size())) { + const auto &row = _collapsedRows[_collapsedSelected]; + Assert(row->folder != nullptr); + return { + .key = row->folder, + .message = Data::UnreadMessagePosition, + }; + } else if (_selected) { return { .key = _selected->key(), .message = Data::UnreadMessagePosition, @@ -3813,9 +3821,7 @@ bool InnerWidget::isUserpicPressOnWide() const { bool InnerWidget::chooseRow( Qt::KeyboardModifiers modifiers, MsgId pressedTopicRootId) { - if (chooseCollapsedRow()) { - return true; - } else if (chooseHashtag()) { + if (chooseHashtag()) { return true; } const auto modifyChosenRow = [&]( diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h index 033418af1..a0b80ef67 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.h @@ -252,7 +252,7 @@ private: void repaintCollapsedFolderRow(not_null folder); void refreshWithCollapsedRows(bool toTop = false); bool needCollapsedRowsRefresh() const; - bool chooseCollapsedRow(); + bool chooseCollapsedRow(Qt::KeyboardModifiers modifiers); void switchToFilter(FilterId filterId); bool chooseHashtag(); ChosenRow computeChosenRow() const; diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp index 913425a79..82c8f0384 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp @@ -562,6 +562,8 @@ void Widget::chosenRow(const ChosenRow &row) { if (topicJump) { if (controller()->shownForum().current() == topicJump->forum()) { controller()->closeForum(); + } else if (row.newWindow) { + controller()->showInNewWindow(Window::SeparateId(topicJump)); } else { if (!controller()->adaptive().isOneColumn()) { controller()->showForum( @@ -575,11 +577,17 @@ void Widget::chosenRow(const ChosenRow &row) { } return; } else if (const auto topic = row.key.topic()) { - session().data().saveViewAsMessages(topic->forum(), false); - controller()->showThread( - topic, - row.message.fullId.msg, - Window::SectionShow::Way::ClearStack); + if (row.newWindow) { + controller()->showInNewWindow( + Window::SeparateId(topic), + row.message.fullId.msg); + } else { + session().data().saveViewAsMessages(topic->forum(), false); + controller()->showThread( + topic, + row.message.fullId.msg, + Window::SectionShow::Way::ClearStack); + } } else if (history && row.userpicClick && (row.message.fullId.msg == ShowAtUnreadMsgId) @@ -595,16 +603,19 @@ void Widget::chosenRow(const ChosenRow &row) { const auto forum = history->peer->forum(); if (controller()->shownForum().current() == forum) { controller()->closeForum(); - return; - } - controller()->showForum( - forum, - Window::SectionShow().withChildColumn()); - if (forum->channel()->viewForumAsMessages()) { - controller()->showThread( - history, - ShowAtUnreadMsgId, - Window::SectionShow::Way::ClearStack); + } else if (row.newWindow) { + controller()->showInNewWindow( + Window::SeparateId(Window::SeparateType::Forum, history)); + } else { + controller()->showForum( + forum, + Window::SectionShow().withChildColumn()); + if (forum->channel()->viewForumAsMessages()) { + controller()->showThread( + history, + ShowAtUnreadMsgId, + Window::SectionShow::Way::ClearStack); + } } return; } else if (history) { @@ -630,6 +641,12 @@ void Widget::chosenRow(const ChosenRow &row) { return; } } + if (row.newWindow) { + controller()->showInNewWindow(Window::SeparateId( + Window::SeparateType::Archive, + &session())); + return; + } controller()->openFolder(folder); hideChildList(); } @@ -1847,13 +1864,21 @@ void Widget::slideFinished() { void Widget::escape() { if (!cancelSearch({ .jumpBackToSearchedChat = true })) { - if (controller()->shownForum().current()) { - controller()->closeForum(); + if (const auto forum = controller()->shownForum().current()) { + const auto id = controller()->windowId(); + const auto initial = id.forum(); + if (!initial) { + controller()->closeForum(); + } else if (initial != forum) { + controller()->showForum(initial); + } } else if (controller()->openedFolder().current()) { - controller()->closeFolder(); + if (!controller()->windowId().folder()) { + controller()->closeFolder(); + } } else if (controller()->activeChatEntryCurrent().key) { controller()->content()->dialogsCancelled(); - } else { + } else if (controller()->isPrimary()) { const auto filters = &session().data().chatsFilters(); const auto &list = filters->list(); const auto first = list.empty() ? FilterId() : list.front().id(); @@ -2704,6 +2729,9 @@ void Widget::updateForceDisplayWide() { void Widget::showForum( not_null forum, const Window::SectionShow ¶ms) { + if (_openedForum == forum) { + return; + } const auto nochat = !controller()->mainSectionShown(); if (!params.childColumn || (Core::App().settings().dialogsWidthRatio(nochat) == 0.) diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.h b/Telegram/SourceFiles/dialogs/dialogs_widget.h index 37fdcaae2..347683d0c 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_widget.h +++ b/Telegram/SourceFiles/dialogs/dialogs_widget.h @@ -57,6 +57,7 @@ namespace Window { class SessionController; class ConnectionState; struct SectionShow; +struct SeparateId; } // namespace Window namespace Dialogs::Stories { diff --git a/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp b/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp index ad6fe1f69..a19200d8a 100644 --- a/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp +++ b/Telegram/SourceFiles/dialogs/ui/dialogs_suggestions.cpp @@ -41,6 +41,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/dynamic_thumbnails.h" #include "ui/painter.h" #include "ui/unread_badge_paint.h" +#include "window/window_separate_id.h" #include "window/window_session_controller.h" #include "window/window_peer_menu.h" #include "styles/style_chat.h" diff --git a/Telegram/SourceFiles/history/history_item_helpers.cpp b/Telegram/SourceFiles/history/history_item_helpers.cpp index 9a6423bcc..30303e5f8 100644 --- a/Telegram/SourceFiles/history/history_item_helpers.cpp +++ b/Telegram/SourceFiles/history/history_item_helpers.cpp @@ -317,7 +317,7 @@ ClickHandlerPtr JumpToMessageClickHandler( TextWithEntities highlightPart, int highlightPartOffsetHint) { return std::make_shared([=] { - const auto separate = Core::App().separateWindowForPeer(peer); + const auto separate = Core::App().separateWindowFor(peer); const auto controller = separate ? separate->sessionController() : peer->session().tryResolveWindow(); @@ -347,7 +347,7 @@ ClickHandlerPtr JumpToStoryClickHandler( not_null peer, StoryId storyId) { return std::make_shared([=] { - const auto separate = Core::App().separateWindowForPeer(peer); + const auto separate = Core::App().separateWindowFor(peer); const auto controller = separate ? separate->sessionController() : peer->session().tryResolveWindow(); diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index b19c0222f..164909c20 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -823,7 +823,7 @@ HistoryWidget::HistoryWidget( if (flags & PeerUpdateFlag::UnavailableReason) { const auto unavailable = _peer->computeUnavailableReason(); if (!unavailable.isEmpty()) { - const auto account = &_peer->account(); + const auto account = not_null(&_peer->account()); closeCurrent(); if (const auto primary = Core::App().windowFor(account)) { primary->showToast(unavailable); @@ -3342,7 +3342,8 @@ void HistoryWidget::messagesFailed(const MTP::Error &error, int requestId) { || error.type() == u"USER_BANNED_IN_CHANNEL"_q) { auto was = _peer; closeCurrent(); - if (const auto primary = Core::App().windowFor(&was->account())) { + const auto wasAccount = not_null(&was->account()); + if (const auto primary = Core::App().windowFor(wasAccount)) { primary->showToast((was && was->isMegagroup()) ? tr::lng_group_not_accessible(tr::now) : tr::lng_channel_not_accessible(tr::now)); @@ -4974,8 +4975,8 @@ bool HistoryWidget::updateCmdStartShown() { } bool HistoryWidget::searchInChatEmbedded(Dialogs::Key chat, QString query) { - const auto peer = chat.peer(); - if (!peer || peer != controller()->singlePeer()) { + const auto peer = chat.peer(); // windows todo + if (!peer || Window::SeparateId(peer) != controller()->windowId()) { return false; } else if (_peer != peer) { const auto weak = Ui::MakeWeak(this); diff --git a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp index df5d902c2..92bd21800 100644 --- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp @@ -44,6 +44,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_peer_values.h" #include "data/data_group_call.h" // GroupCall::input. #include "data/data_folder.h" +#include "data/data_forum.h" #include "data/data_saved_sublist.h" #include "data/data_session.h" #include "data/data_stories.h" @@ -714,10 +715,13 @@ void TopBarWidget::mousePressEvent(QMouseEvent *e) { && !showSelectedState() && !_chooseForReportReason; if (handleClick) { + const auto archiveTop = (_activeChat.section == Section::ChatsList) + && _activeChat.key.folder(); if ((_animatingMode && _back->rect().contains(e->pos())) - || (_activeChat.section == Section::ChatsList - && _activeChat.key.folder())) { - backClicked(); + || archiveTop) { + if (!rootChatsListBar()) { + backClicked(); + } } else { infoClicked(); } @@ -890,9 +894,22 @@ void TopBarWidget::setCustomTitle(const QString &title) { } } +bool TopBarWidget::rootChatsListBar() const { + if (_activeChat.section != Section::ChatsList) { + return false; + } + const auto id = _controller->windowId(); + const auto separateFolder = id.folder(); + const auto separateForum = id.forum(); + const auto active = _activeChat.key; + return (separateForum && separateForum->history() == active.history()) + || (separateFolder && separateFolder == active.folder()); +} + void TopBarWidget::refreshInfoButton() { if (_activeChat.key.topic() - || _activeChat.section == Section::ChatsList) { + || (_activeChat.section == Section::ChatsList + && !rootChatsListBar())) { _info.destroy(); } else if (const auto peer = _activeChat.key.peer()) { auto info = object_ptr( @@ -989,6 +1006,14 @@ void TopBarWidget::updateControlsGeometry() { _leftTaken += _back->width(); } if (_info && !_info->isHidden()) { + if (_back->isHidden() && _narrowRatio > 0.) { + const auto &infoSt = st::topBarInfoButton; + const auto middle = (_narrowWidth - infoSt.photoSize) / 2; + _leftTaken = anim::interpolate( + _leftTaken, + middle - infoSt.photoPosition.x(), + _narrowRatio); + } _info->moveToLeft(_leftTaken, otherButtonsTop); _leftTaken += _info->width(); } else if (_activeChat.key.topic() @@ -997,7 +1022,9 @@ void TopBarWidget::updateControlsGeometry() { } if (_searchField) { - const auto fieldLeft = _leftTaken; + const auto fieldLeft = _back->isHidden() + ? st::topBarArrowPadding.right() + : _leftTaken; const auto fieldTop = searchFieldTop + (height() - _searchField->height()) / 2; const auto fieldRight = st::dialogsFilterSkip @@ -1075,9 +1102,10 @@ void TopBarWidget::updateControlsVisibility() { _sendNow->setVisible(_canSendNow); const auto isOneColumn = _controller->adaptive().isOneColumn(); - auto backVisible = isOneColumn - || !_controller->content()->stackIsEmpty() - || (_activeChat.section == Section::ChatsList); + const auto backVisible = !rootChatsListBar() + && (isOneColumn + || (_activeChat.section == Section::ChatsList) + || !_controller->content()->stackIsEmpty()); _back->setVisible(backVisible && !_chooseForReportReason); _cancelChoose->setVisible(_chooseForReportReason.has_value()); if (_info) { @@ -1085,7 +1113,8 @@ void TopBarWidget::updateControlsVisibility() { && (isOneColumn || !_primaryWindow)); } if (_unreadBadge) { - _unreadBadge->setVisible(!_chooseForReportReason); + _unreadBadge->setVisible(!_chooseForReportReason + && !rootChatsListBar()); } const auto topic = _activeChat.key.topic(); const auto section = _activeChat.section; diff --git a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.h b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.h index 5c94220b1..6b81c26a0 100644 --- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.h +++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.h @@ -131,6 +131,7 @@ protected: private: struct EmojiInteractionSeenAnimation; + [[nodiscard]] bool rootChatsListBar() const; void refreshInfoButton(); void refreshLang(); void updateSearchVisibility(); diff --git a/Telegram/SourceFiles/main/main_domain.cpp b/Telegram/SourceFiles/main/main_domain.cpp index aafbcd2ac..9b4b12067 100644 --- a/Telegram/SourceFiles/main/main_domain.cpp +++ b/Telegram/SourceFiles/main/main_domain.cpp @@ -318,8 +318,8 @@ not_null Domain::add(MTP::Environment environment) { void Domain::addActivated(MTP::Environment environment, bool newWindow) { const auto added = [&](not_null account) { if (newWindow) { - Core::App().ensureSeparateWindowForAccount(account); - } else if (const auto window = Core::App().separateWindowForAccount( + Core::App().ensureSeparateWindowFor(account); + } else if (const auto window = Core::App().separateWindowFor( account)) { window->activate(); } else { @@ -371,11 +371,11 @@ void Domain::watchSession(not_null account) { void Domain::closeAccountWindows(not_null account) { auto another = (Main::Account*)nullptr; for (auto i = _accounts.begin(); i != _accounts.end(); ++i) { - const auto other = i->account.get(); + const auto other = not_null(i->account.get()); if (other == account) { continue; - } else if (Core::App().separateWindowForAccount(other)) { - const auto that = Core::App().separateWindowForAccount(account); + } else if (Core::App().separateWindowFor(other)) { + const auto that = Core::App().separateWindowFor(account); if (that) { that->close(); } @@ -411,7 +411,7 @@ void Domain::removeRedundantAccounts() { const auto was = _accounts.size(); for (auto i = _accounts.begin(); i != _accounts.end();) { - if (Core::App().separateWindowForAccount(i->account.get()) + if (Core::App().separateWindowFor(not_null(i->account.get())) || i->account->sessionExists()) { ++i; continue; @@ -442,7 +442,7 @@ void Domain::checkForLastProductionConfig( } void Domain::maybeActivate(not_null account) { - if (Core::App().separateWindowForAccount(account)) { + if (Core::App().separateWindowFor(account)) { activate(account); } else { Core::App().preventOrInvoke(crl::guard(account, [=] { @@ -452,7 +452,7 @@ void Domain::maybeActivate(not_null account) { } void Domain::activate(not_null account) { - if (const auto window = Core::App().separateWindowForAccount(account)) { + if (const auto window = Core::App().separateWindowFor(account)) { window->activate(); } if (_active.current() == account.get()) { diff --git a/Telegram/SourceFiles/main/main_session.cpp b/Telegram/SourceFiles/main/main_session.cpp index 951ec735a..ea6111574 100644 --- a/Telegram/SourceFiles/main/main_session.cpp +++ b/Telegram/SourceFiles/main/main_session.cpp @@ -497,7 +497,8 @@ Window::SessionController *Session::tryResolveWindow( if (forPeer) { auto primary = (Window::SessionController*)nullptr; for (const auto &window : _windows) { - if (window->singlePeer() == forPeer) { + const auto thread = window->windowId().thread; + if (thread && thread->peer() == forPeer) { return window; } else if (window->isPrimary()) { primary = window; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 025b66cf8..45ad3ef62 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -37,6 +37,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_connecting_widget.h" #include "window/window_top_bar_wrap.h" #include "window/notifications_manager.h" +#include "window/window_separate_id.h" #include "window/window_slide_animation.h" #include "window/window_history_hider.h" #include "window/window_controller.h" @@ -232,19 +233,19 @@ MainWidget::MainWidget( , _controller(controller) , _dialogsWidth(st::columnMinimalWidthLeft) , _thirdColumnWidth(st::columnMinimalWidthThird) -, _sideShadow(isPrimary() - ? base::make_unique_q(this) - : nullptr) -, _dialogs(isPrimary() +, _dialogs(windowId().hasChatsList() ? base::make_unique_q( this, _controller, Dialogs::Widget::Layout::Main) : nullptr) , _history(std::in_place, this, _controller) +, _sideShadow(_dialogs + ? base::make_unique_q(this) + : nullptr) , _playerPlaylist(this, _controller) , _changelogs(Core::Changelogs::Create(&controller->session())) { - if (isPrimary()) { + if (_dialogs) { setupConnectingWidget(); } @@ -732,7 +733,7 @@ void MainWidget::hideSingleUseKeyboard(FullMsgId replyToId) { void MainWidget::searchMessages(const QString &query, Dialogs::Key inChat) { auto tags = Data::SearchTagsFromQuery(query); - if (controller()->isPrimary()) { + if (_dialogs) { auto state = Dialogs::SearchState{ .inChat = ((tags.empty() || inChat.sublist()) ? inChat @@ -758,7 +759,7 @@ void MainWidget::searchMessages(const QString &query, Dialogs::Key inChat) { if ((!_mainSection || !_mainSection->searchInChatEmbedded(inChat, query)) && !_history->searchInChatEmbedded(inChat, query)) { - const auto account = &session().account(); + const auto account = not_null(&session().account()); if (const auto window = Core::App().windowFor(account)) { if (const auto controller = window->sessionController()) { controller->content()->searchMessages(query, inChat); @@ -1238,36 +1239,35 @@ bool MainWidget::showHistoryInDifferentWindow( PeerId peerId, const SectionShow ¶ms, MsgId showAtMsgId) { + if (!peerId) { + return false; + } const auto peer = session().data().peer(peerId); - const auto account = &session().account(); - auto primary = Core::App().separateWindowForAccount(account); - if (const auto separate = Core::App().separateWindowForPeer(peer)) { - if (separate == &_controller->window()) { - return false; + if (const auto separateChat = _controller->windowId().chat()) { + if (const auto history = separateChat->asHistory()) { + if (history->peer == peer) { + return false; + } } - separate->sessionController()->showPeerHistory( + } + const auto window = Core::App().windowForShowingHistory(peer); + if (window == &_controller->window()) { + return false; + } else if (window) { + window->sessionController()->showPeerHistory( peerId, params, showAtMsgId); - separate->activate(); + window->activate(); return true; - } else if (isPrimary()) { - if (primary && primary != &_controller->window()) { - primary->sessionController()->showPeerHistory( - peerId, - params, - showAtMsgId); - primary->activate(); - return true; - } + } else if (windowId().hasChatsList()) { return false; - } else if (!peerId) { - return true; - } else if (singlePeer()->id == peerId) { - return false; - } else if (!primary) { + } + const auto account = not_null(&session().account()); + auto primary = Core::App().separateWindowFor(account); + if (!primary) { Core::App().domain().activate(account); - primary = Core::App().separateWindowForAccount(account); + primary = Core::App().separateWindowFor(account); } if (primary && &primary->account() == account) { primary->sessionController()->showPeerHistory( @@ -1293,7 +1293,7 @@ void MainWidget::showHistory( } const auto unavailable = peer->computeUnavailableReason(); if (!unavailable.isEmpty()) { - Assert(isPrimary()); + Assert(isPrimary()); // windows todo if (params.activation != anim::activation::background) { _controller->show(Ui::MakeInformBox(unavailable)); _controller->window().activate(); @@ -1510,7 +1510,7 @@ void MainWidget::showMessage( void MainWidget::showForum( not_null forum, const SectionShow ¶ms) { - Expects(isPrimary() || (singlePeer() && singlePeer()->forum() == forum)); + Expects(_dialogs != nullptr); _dialogs->showForum(forum, params); @@ -1846,8 +1846,8 @@ void MainWidget::checkMainSectionToLayer() { updateMainSectionShown(); } -PeerData *MainWidget::singlePeer() const { - return _controller->singlePeer(); +Window::SeparateId MainWidget::windowId() const { + return _controller->windowId(); } bool MainWidget::isPrimary() const { @@ -1957,7 +1957,7 @@ void MainWidget::showBackFromStack( } if (_stack.empty()) { - if (isPrimary()) { + if (_dialogs) { _controller->clearSectionStack(params); } crl::on_main(this, [=] { diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 3653070ac..b97c7f98f 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -90,6 +90,7 @@ struct SectionSlideParams; struct SectionShow; enum class Column; class HistoryHider; +struct SeparateId; } // namespace Window namespace Calls { @@ -121,7 +122,7 @@ public: [[nodiscard]] Main::Session &session() const; [[nodiscard]] not_null controller() const; - [[nodiscard]] PeerData *singlePeer() const; + [[nodiscard]] Window::SeparateId windowId() const; [[nodiscard]] bool isPrimary() const; [[nodiscard]] bool isMainSectionShown() const; [[nodiscard]] bool isThirdSectionShown() const; @@ -350,10 +351,6 @@ private: int _thirdColumnWidth = 0; Ui::Animations::Simple _a_dialogsWidth; - const base::unique_qptr _sideShadow; - object_ptr _thirdShadow = { nullptr }; - object_ptr _firstColumnResizeArea = { nullptr }; - object_ptr _thirdColumnResizeArea = { nullptr }; const base::unique_qptr _dialogs; const base::unique_qptr _history; object_ptr _mainSection = { nullptr }; @@ -361,6 +358,11 @@ private: std::shared_ptr _thirdSectionFromStack; std::unique_ptr _connecting; + const base::unique_qptr _sideShadow; + object_ptr _thirdShadow = { nullptr }; + object_ptr _firstColumnResizeArea = { nullptr }; + object_ptr _thirdColumnResizeArea = { nullptr }; + base::weak_ptr _currentCall; base::weak_ptr _currentGroupCall; rpl::lifetime _currentCallLifetime; diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index 2e705713f..df1f2ef25 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -269,13 +269,11 @@ void MainWindow::setupMain( auto created = object_ptr(bodyWidget(), sessionController()); clearWidgets(); _main = std::move(created); - if (const auto peer = singlePeer()) { - updateControlsGeometry(); - _main->controller()->showPeerHistory( - peer, - Window::SectionShow::Way::ClearStack, - singlePeerShowAtMsgId); - } + updateControlsGeometry(); + Ui::SendPendingMoveResizeEvents(_main); + _main->controller()->showByInitialId( + Window::SectionShow::Way::ClearStack, + singlePeerShowAtMsgId); if (_passcodeLock) { _main->hide(); } else { diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 0dde74e30..a6320b61e 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -6158,7 +6158,7 @@ Window::SessionController *OverlayWidget::findWindow(bool switchTo) const { if (switchTo) { auto controllerPtr = (Window::SessionController*)nullptr; - const auto account = &_session->account(); + const auto account = not_null(&_session->account()); const auto sessionWindow = Core::App().windowFor(account); const auto anyWindow = (sessionWindow && &sessionWindow->account() == account) diff --git a/Telegram/SourceFiles/settings/settings_information.cpp b/Telegram/SourceFiles/settings/settings_information.cpp index 7be04db17..911d44942 100644 --- a/Telegram/SourceFiles/settings/settings_information.cpp +++ b/Telegram/SourceFiles/settings/settings_information.cpp @@ -983,13 +983,13 @@ void AccountsList::rebuild() { _reorder->finishReordering(); if (newWindow) { _closeRequests.fire({}); - Core::App().ensureSeparateWindowForAccount( - account); + Core::App().ensureSeparateWindowFor(account); } Core::App().domain().maybeActivate(account); } }; - if (const auto window = Core::App().separateWindowForAccount(account)) { + if (const auto window = Core::App().separateWindowFor( + account)) { _closeRequests.fire({}); window->activate(); } else { diff --git a/Telegram/SourceFiles/window/main_window.cpp b/Telegram/SourceFiles/window/main_window.cpp index 1276b264f..e50ffd737 100644 --- a/Telegram/SourceFiles/window/main_window.cpp +++ b/Telegram/SourceFiles/window/main_window.cpp @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/platform/ui_platform_window.h" #include "platform/platform_window_title.h" #include "history/history.h" +#include "window/window_separate_id.h" #include "window/window_session_controller.h" #include "window/window_lock_widgets.h" #include "window/window_controller.h" @@ -387,8 +388,8 @@ Main::Account &MainWindow::account() const { return _controller->account(); } -PeerData *MainWindow::singlePeer() const { - return _controller->singlePeer(); +Window::SeparateId MainWindow::id() const { + return _controller->id(); } bool MainWindow::isPrimary() const { @@ -602,20 +603,27 @@ WindowPosition MainWindow::initialPosition() const { ? Core::AdjustToScale( Core::App().settings().windowPosition(), u"Window"_q) - : active->widget()->nextInitialChildPosition(isPrimary()); + : active->widget()->nextInitialChildPosition(id()); } -WindowPosition MainWindow::nextInitialChildPosition(bool primary) { +WindowPosition MainWindow::nextInitialChildPosition(SeparateId childId) { const auto rect = geometry().marginsRemoved(frameMargins()); const auto position = rect.topLeft(); const auto adjust = [&](int value) { - return primary ? value : (value * 3 / 4); + return (value * 3 / 4); }; + const auto secondaryWithChatsList = !childId.primary() && childId.hasChatsList(); const auto width = OptionNewWindowsSizeAsFirst.value() ? Core::App().settings().windowPosition().w + : childId.primary() + ? st::windowDefaultWidth + : childId.hasChatsList() + ? (st::columnMinimalWidthLeft + adjust(st::windowDefaultWidth)) : adjust(st::windowDefaultWidth); const auto height = OptionNewWindowsSizeAsFirst.value() ? Core::App().settings().windowPosition().h + : childId.primary() + ? st::windowDefaultHeight : adjust(st::windowDefaultHeight); const auto skip = ChildSkip(); const auto delta = _lastChildIndex diff --git a/Telegram/SourceFiles/window/main_window.h b/Telegram/SourceFiles/window/main_window.h index 49f73e6bb..85d495e7e 100644 --- a/Telegram/SourceFiles/window/main_window.h +++ b/Telegram/SourceFiles/window/main_window.h @@ -33,6 +33,7 @@ class Controller; class SessionController; class TitleWidget; struct TermsLock; +struct SeparateId; [[nodiscard]] const QImage &Logo(); [[nodiscard]] const QImage &LogoNoMargin(); @@ -66,7 +67,7 @@ public: [[nodiscard]] Window::Controller &controller() const { return *_controller; } - [[nodiscard]] PeerData *singlePeer() const; + [[nodiscard]] Window::SeparateId id() const; [[nodiscard]] bool isPrimary() const; [[nodiscard]] Main::Account &account() const; [[nodiscard]] Window::SessionController *sessionController() const; @@ -200,7 +201,7 @@ private: [[nodiscard]] Core::WindowPosition initialPosition() const; [[nodiscard]] Core::WindowPosition nextInitialChildPosition( - bool primary); + SeparateId childId); [[nodiscard]] QRect countInitialGeometry(Core::WindowPosition position); bool computeIsActive() const; diff --git a/Telegram/SourceFiles/window/notifications_manager.cpp b/Telegram/SourceFiles/window/notifications_manager.cpp index 460858972..40f8664f2 100644 --- a/Telegram/SourceFiles/window/notifications_manager.cpp +++ b/Telegram/SourceFiles/window/notifications_manager.cpp @@ -1096,7 +1096,7 @@ void Manager::openNotificationMessage( && item->isRegular() && (item->out() || (item->mentionsMe() && !history->peer->isUser())); const auto topic = item ? item->topic() : nullptr; - const auto separate = Core::App().separateWindowForPeer(history->peer); + const auto separate = Core::App().separateWindowFor(history->peer); const auto window = separate ? separate->sessionController() : history->session().tryResolveWindow(); diff --git a/Telegram/SourceFiles/window/window_controller.cpp b/Telegram/SourceFiles/window/window_controller.cpp index abe2fa3dc..69bca734f 100644 --- a/Telegram/SourceFiles/window/window_controller.cpp +++ b/Telegram/SourceFiles/window/window_controller.cpp @@ -28,7 +28,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_session_controller.h" #include "window/themes/window_theme_editor.h" #include "ui/boxes/confirm_box.h" -#include "data/data_peer.h" +#include "data/data_thread.h" #include "apiwrap.h" // ApiWrap::acceptTerms. #include "styles/style_layers.h" @@ -93,23 +93,18 @@ Show::operator bool() const { } // namespace -Controller::Controller() : Controller(CreateArgs{}) { +Controller::Controller() : Controller(CreateArgs{ nullptr }) { } -Controller::Controller(not_null account) -: Controller(CreateArgs{}) { - showAccount(account); -} - -Controller::Controller( - not_null singlePeer, - MsgId showAtMsgId) -: Controller(CreateArgs{ singlePeer.get() }) { - showAccount(&singlePeer->account(), showAtMsgId); +Controller::Controller(SeparateId id, MsgId showAtMsgId) +: Controller(CreateArgs{ id }) { + if (id) { + showAccount(id.account, showAtMsgId); + } } Controller::Controller(CreateArgs &&args) -: _singlePeer(args.singlePeer) +: _id(args.id) , _isActiveTimer([=] { updateIsActive(); }) , _widget(this) , _adaptive(std::make_unique()) { @@ -125,6 +120,20 @@ Controller::~Controller() { _sessionController = nullptr; } +SeparateId Controller::id() const { + return _id; +} + +bool Controller::isPrimary() const { + return _id.primary(); +} + +Main::Account &Controller::account() const { + Expects(_id.account != nullptr); + + return *_id.account; +} + void Controller::showAccount(not_null account) { showAccount(account, ShowAtUnreadMsgId); } @@ -132,20 +141,22 @@ void Controller::showAccount(not_null account) { void Controller::showAccount( not_null account, MsgId singlePeerShowAtMsgId) { - Expects(isPrimary() || &_singlePeer->account() == account); + Expects(isPrimary() || _id.account == account); - const auto prevSessionUniqueId = (_account && _account->sessionExists()) - ? _account->session().uniqueId() + const auto prevAccount = _id.account; + const auto prevSession = maybeSession(); + const auto prevSessionUniqueId = prevSession + ? prevSession->uniqueId() : 0; _accountLifetime.destroy(); - _account = account; - Core::App().checkWindowAccount(this); + _id.account = account; + Core::App().checkWindowId(this); - const auto updateOnlineOfPrevSesssion = crl::guard(_account, [=] { + const auto updateOnlineOfPrevSesssion = crl::guard(account, [=] { if (!prevSessionUniqueId) { return; } - for (auto &[index, account] : _account->domain().accounts()) { + for (auto &[index, account] : _id.account->domain().accounts()) { if (const auto anotherSession = account->maybeSession()) { if (anotherSession->uniqueId() == prevSessionUniqueId) { anotherSession->updates().updateOnline(crl::now()); @@ -155,12 +166,15 @@ void Controller::showAccount( } }); - _account->sessionValue( - ) | rpl::start_with_next([=](Main::Session *session) { - if (!isPrimary() && (&_singlePeer->session() != session)) { + if (!isPrimary()) { + _id.account->sessionChanges( + ) | rpl::start_with_next([=](Main::Session *session) { Core::App().closeWindow(this); - return; - } + }, _accountLifetime); + } + + _id.account->sessionValue( + ) | rpl::start_with_next([=](Main::Session *session) { const auto was = base::take(_sessionController); _sessionController = session ? std::make_unique(session, this) @@ -205,10 +219,6 @@ void Controller::showAccount( }, _accountLifetime); } -PeerData *Controller::singlePeer() const { - return _singlePeer; -} - void Controller::setupSideBar() { Expects(_sessionController != nullptr); @@ -321,7 +331,7 @@ void Controller::finishFirstShow() { } Main::Session *Controller::maybeSession() const { - return _account ? _account->maybeSession() : nullptr; + return _id.account ? _id.account->maybeSession() : nullptr; } auto Controller::sessionControllerValue() const @@ -356,7 +366,7 @@ void Controller::setupPasscodeLock() { } void Controller::clearPasscodeLock() { - if (!_account) { + if (!_id) { showAccount(&Core::App().activeAccount()); } else { _widget.clearPasscodeLock(); @@ -482,7 +492,7 @@ void Controller::invokeForSessionController( PeerData *singlePeer, Fn)> &&callback) { const auto separateWindow = singlePeer - ? Core::App().separateWindowForPeer(singlePeer) + ? Core::App().separateWindowFor(not_null(singlePeer)) : nullptr; const auto separateSession = separateWindow ? separateWindow->sessionController() @@ -490,7 +500,7 @@ void Controller::invokeForSessionController( if (separateSession) { return callback(separateSession); } - _account->domain().activate(std::move(account)); + _id.account->domain().activate(std::move(account)); if (_sessionController) { callback(_sessionController.get()); } diff --git a/Telegram/SourceFiles/window/window_controller.h b/Telegram/SourceFiles/window/window_controller.h index eadd32e2f..3eb6184e9 100644 --- a/Telegram/SourceFiles/window/window_controller.h +++ b/Telegram/SourceFiles/window/window_controller.h @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwindow.h" #include "window/window_adaptive.h" +#include "window/window_separate_id.h" namespace Main { class Account; @@ -36,32 +37,20 @@ namespace Window { class Controller final : public base::has_weak_ptr { public: Controller(); - explicit Controller(not_null account); - Controller( - not_null singlePeer, - MsgId showAtMsgId); + Controller(SeparateId id, MsgId showAtMsgId); ~Controller(); Controller(const Controller &other) = delete; Controller &operator=(const Controller &other) = delete; void showAccount(not_null account); - [[nodiscard]] PeerData *singlePeer() const; - [[nodiscard]] bool isPrimary() const { - return (singlePeer() == nullptr); - } + [[nodiscard]] SeparateId id() const; + [[nodiscard]] bool isPrimary() const; [[nodiscard]] not_null<::MainWindow*> widget() { return &_widget; } - [[nodiscard]] Main::Account &account() const { - Expects(_account != nullptr); - - return *_account; - } - [[nodiscard]] Main::Account *maybeAccount() const { - return _account; - } + [[nodiscard]] Main::Account &account() const; [[nodiscard]] Main::Session *maybeSession() const; [[nodiscard]] SessionController *sessionController() const { return _sessionController.get(); @@ -155,7 +144,7 @@ public: private: struct CreateArgs { - PeerData *singlePeer = nullptr; + SeparateId id; }; explicit Controller(CreateArgs &&args); @@ -173,8 +162,7 @@ private: void showTermsDecline(); void showTermsDelete(); - PeerData *_singlePeer = nullptr; - Main::Account *_account = nullptr; + SeparateId _id; base::Timer _isActiveTimer; ::MainWindow _widget; const std::unique_ptr _adaptive; diff --git a/Telegram/SourceFiles/window/window_main_menu.cpp b/Telegram/SourceFiles/window/window_main_menu.cpp index bcddeda4d..194d366c1 100644 --- a/Telegram/SourceFiles/window/window_main_menu.cpp +++ b/Telegram/SourceFiles/window/window_main_menu.cpp @@ -47,6 +47,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/text/text_utilities.h" #include "ui/unread_badge_paint.h" #include "ui/vertical_list.h" +#include "ui/widgets/menu/menu_add_action_callback_factory.h" #include "ui/widgets/popup_menu.h" #include "ui/widgets/scroll_area.h" #include "ui/widgets/shadow.h" @@ -551,9 +552,15 @@ void MainMenu::setupArchive() { const auto folder = [=] { return controller->session().data().folderLoaded(Data::Folder::kId); }; - const auto showArchive = [=] { + const auto showArchive = [=](Qt::KeyboardModifiers modifiers) { if (const auto f = folder()) { - controller->openFolder(f); + if (modifiers & Qt::ControlModifier) { + controller->showInNewWindow(Window::SeparateId( + Window::SeparateType::Archive, + &controller->session())); + } else { + controller->openFolder(f); + } controller->window().hideSettingsAndLayer(); } }; @@ -583,7 +590,7 @@ void MainMenu::setupArchive() { button->clicks( ) | rpl::start_with_next([=](Qt::MouseButton which) { if (which == Qt::LeftButton) { - showArchive(); + showArchive(button->clickModifiers()); return; } else if (which != Qt::RightButton) { return; @@ -591,35 +598,13 @@ void MainMenu::setupArchive() { _contextMenu = base::make_unique_q( this, st::popupMenuExpandedSeparator); - const auto addAction = PeerMenuCallback([&]( - PeerMenuCallback::Args a) { - return _contextMenu->addAction( - a.text, - std::move(a.handler), - a.icon); - }); - - const auto hide = [=] { - controller->session().settings().setArchiveInMainMenu(false); - controller->session().saveSettingsDelayed(); - controller->window().hideSettingsAndLayer(); - }; - addAction( - tr::lng_context_archive_to_list(tr::now), - std::move(hide), - &st::menuIconFromMainMenu); - - MenuAddMarkAsReadChatListAction( - controller, - [f = folder()] { return f->chatsList(); }, - addAction); - - _contextMenu->addSeparator(); - Settings::PreloadArchiveSettings(&controller->session()); - addAction(tr::lng_context_archive_settings(tr::now), [=] { - controller->show(Box(Settings::ArchiveSettingsBox, controller)); - }, &st::menuIconManage); - + Window::FillDialogsEntryMenu( + _controller, + Dialogs::EntryState{ + .key = folder(), + .section = Dialogs::EntryState::Section::ContextMenu, + }, + Ui::Menu::CreateAddActionCallback(_contextMenu)); _contextMenu->popup(QCursor::pos()); }, button->lifetime()); diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index d3aecb1c5..712e022b0 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -59,6 +59,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "history/history_item_helpers.h" // GetErrorTextForSending. #include "history/view/history_view_context_menu.h" +#include "window/window_separate_id.h" #include "window/window_session_controller.h" #include "window/window_controller.h" #include "settings/settings_advanced.h" @@ -650,20 +651,48 @@ void Filler::addToggleUnreadMark() { } void Filler::addNewWindow() { + const auto controller = _controller; + if (_folder) { + _addAction(tr::lng_context_new_window(tr::now), [=] { + Ui::PreventDelayedActivation(); + controller->showInNewWindow(SeparateId( + SeparateType::Archive, + &controller->session())); + }, &st::menuIconNewWindow); + AddSeparatorAndShiftUp(_addAction); + return; + } else if (const auto weak = base::make_weak(_sublist)) { + _addAction(tr::lng_context_new_window(tr::now), [=] { + Ui::PreventDelayedActivation(); + if (const auto sublist = weak.get()) { + const auto peer = sublist->peer(); + controller->showInNewWindow(SeparateId( + SeparateType::SavedSublist, + peer->owner().history(peer))); + } + }, &st::menuIconNewWindow); + AddSeparatorAndShiftUp(_addAction); + return; + } const auto history = _request.key.history(); if (!_peer - || _topic - || _peer->isForum() || (history && history->useTopPromotion() && !history->topPromotionType().isEmpty())) { return; } const auto peer = _peer; - const auto controller = _controller; + const auto thread = _topic + ? not_null(_topic) + : _peer->owner().history(_peer); + const auto weak = base::make_weak(thread); _addAction(tr::lng_context_new_window(tr::now), [=] { Ui::PreventDelayedActivation(); - controller->showInNewWindow(peer); + if (const auto strong = weak.get()) { + controller->showInNewWindow(SeparateId( + peer->isForum() ? SeparateType::Forum : SeparateType::Chat, + strong)); + } }, &st::menuIconNewWindow); AddSeparatorAndShiftUp(_addAction); } @@ -1253,6 +1282,12 @@ void Filler::addViewAsMessages() { FullMsgId(), }, callback, QApplication::activePopupWidget()); return true; + } else if (base::IsCtrlPressed()) { + Ui::PreventDelayedActivation(); + controller->showInNewWindow(SeparateId( + SeparateType::Chat, + peer->owner().history(peer))); + return true; } return false; }; @@ -1432,27 +1467,39 @@ void Filler::fillArchiveActions() { if (_folder->id() != Data::Folder::kId) { return; } + addNewWindow(); + const auto controller = _controller; const auto hidden = controller->session().settings().archiveCollapsed(); - const auto text = hidden - ? tr::lng_context_archive_expand(tr::now) - : tr::lng_context_archive_collapse(tr::now); - _addAction(text, [=] { - controller->session().settings().setArchiveCollapsed(!hidden); - controller->session().saveSettingsDelayed(); - }, hidden ? &st::menuIconExpand : &st::menuIconCollapse); - - _addAction(tr::lng_context_archive_to_menu(tr::now), [=] { - controller->showToast({ - .text = { tr::lng_context_archive_to_menu_info(tr::now) }, - .st = &st::windowArchiveToast, - .duration = kArchivedToastDuration, - }); - - controller->session().settings().setArchiveInMainMenu( - !controller->session().settings().archiveInMainMenu()); - controller->session().saveSettingsDelayed(); - }, &st::menuIconToMainMenu); + { + const auto text = hidden + ? tr::lng_context_archive_expand(tr::now) + : tr::lng_context_archive_collapse(tr::now); + _addAction(text, [=] { + controller->session().settings().setArchiveCollapsed(!hidden); + controller->session().saveSettingsDelayed(); + }, hidden ? &st::menuIconExpand : &st::menuIconCollapse); + } + const auto inmenu = controller->session().settings().archiveInMainMenu(); + { + const auto text = inmenu + ? tr::lng_context_archive_to_list(tr::now) + : tr::lng_context_archive_to_menu(tr::now); + _addAction(text, [=] { + if (!inmenu) { + controller->showToast({ + .text = { + tr::lng_context_archive_to_menu_info(tr::now) + }, + .st = &st::windowArchiveToast, + .duration = kArchivedToastDuration, + }); + } + controller->session().settings().setArchiveInMainMenu(!inmenu); + controller->session().saveSettingsDelayed(); + controller->window().hideSettingsAndLayer(); + }, inmenu ? &st::menuIconFromMainMenu : &st::menuIconToMainMenu); + } MenuAddMarkAsReadChatListAction( controller, @@ -1460,6 +1507,7 @@ void Filler::fillArchiveActions() { _addAction); _addAction({ .isSeparator = true }); + Settings::PreloadArchiveSettings(&controller->session()); _addAction(tr::lng_context_archive_settings(tr::now), [=] { controller->show(Box(Settings::ArchiveSettingsBox, controller)); @@ -1467,6 +1515,7 @@ void Filler::fillArchiveActions() { } void Filler::fillSavedSublistActions() { + addNewWindow(); addTogglePin(); } @@ -1983,17 +2032,17 @@ QPointer ShowForwardMessagesBox( ForwardToSelf(show, draft); return true; } - auto controller = Core::App().windowFor(peer); + const auto id = SeparateId( + (peer->isForum() + ? SeparateType::Forum + : SeparateType::Chat), + thread); + auto controller = Core::App().windowFor(id); if (!controller) { return false; } if (controller->maybeSession() != &peer->session()) { - controller = peer->isForum() - ? Core::App().ensureSeparateWindowForAccount( - &peer->account()) - : Core::App().ensureSeparateWindowForPeer( - peer, - ShowAtUnreadMsgId); + controller = Core::App().ensureSeparateWindowFor(id); if (controller->maybeSession() != &peer->session()) { return false; } diff --git a/Telegram/SourceFiles/window/window_separate_id.cpp b/Telegram/SourceFiles/window/window_separate_id.cpp new file mode 100644 index 000000000..f2ed89183 --- /dev/null +++ b/Telegram/SourceFiles/window/window_separate_id.cpp @@ -0,0 +1,77 @@ +/* +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 +*/ +#include "window/window_separate_id.h" + +#include "data/data_folder.h" +#include "data/data_peer.h" +#include "data/data_saved_messages.h" +#include "data/data_session.h" +#include "data/data_thread.h" +#include "history/history.h" +#include "main/main_account.h" +#include "main/main_session.h" + +namespace Window { + +SeparateId::SeparateId(std::nullptr_t) { +} + +SeparateId::SeparateId(not_null account) +: account(account) { +} + +SeparateId::SeparateId(SeparateType type, not_null session) +: type(type) +, account(&session->account()) { +} + +SeparateId::SeparateId(SeparateType type, not_null thread) +: type(type) +, account(&thread->session().account()) +, thread(thread) { +} + +SeparateId::SeparateId(not_null thread) +: SeparateId(SeparateType::Chat, thread) { +} + +SeparateId::SeparateId(not_null peer) +: SeparateId(SeparateType::Chat, peer->owner().history(peer)) { +} + +bool SeparateId::primary() const { + return (type == SeparateType::Primary); +} + +Data::Thread *SeparateId::chat() const { + return (type == SeparateType::Chat) ? thread : nullptr; +} + +Data::Forum *SeparateId::forum() const { + return (type == SeparateType::Forum) ? thread->asForum() : nullptr; +} + +Data::Folder *SeparateId::folder() const { + return (type == SeparateType::Archive) + ? account->session().data().folder(Data::Folder::kId).get() + : nullptr; +} + +Data::SavedSublist *SeparateId::sublist() const { + return (type == SeparateType::SavedSublist) + ? thread->owner().savedMessages().sublist(thread->peer()).get() + : nullptr; +} + +bool SeparateId::hasChatsList() const { + return (type == SeparateType::Primary) + || (type == SeparateType::Archive) + || (type == SeparateType::Forum); +} + +} // namespace Window diff --git a/Telegram/SourceFiles/window/window_separate_id.h b/Telegram/SourceFiles/window/window_separate_id.h new file mode 100644 index 000000000..bcbe72860 --- /dev/null +++ b/Telegram/SourceFiles/window/window_separate_id.h @@ -0,0 +1,69 @@ +/* +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 + +class PeerData; + +namespace Data { +class Thread; +class Folder; +class Forum; +class SavedSublist; +} // namespace Data + +namespace Main { +class Account; +class Session; +} // namespace Main + +namespace Window { + +enum class SeparateType { + Primary, + Archive, + Chat, + Forum, + SavedSublist, +}; + +struct SeparateId { + SeparateId(std::nullptr_t); + SeparateId(not_null account); + SeparateId(SeparateType type, not_null session); + SeparateId(SeparateType type, not_null thread); + SeparateId(not_null thread); + SeparateId(not_null peer); + + SeparateType type = SeparateType::Primary; + Main::Account *account = nullptr; + Data::Thread *thread = nullptr; // For types except Main and Archive. + + [[nodiscard]] bool valid() const { + return account != nullptr; + } + explicit operator bool() const { + return valid(); + } + + [[nodiscard]] bool primary() const; + [[nodiscard]] Data::Thread *chat() const; + [[nodiscard]] Data::Forum *forum() const; + [[nodiscard]] Data::Folder *folder() const; + [[nodiscard]] Data::SavedSublist *sublist() const; + + [[nodiscard]] bool hasChatsList() const; + + friend inline auto operator<=>( + const SeparateId &, + const SeparateId &) = default; + friend inline bool operator==( + const SeparateId &, + const SeparateId &) = default; +}; + +} // namespace Window diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index 59d12ab35..34d3bf489 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_chat_preview.h" #include "window/window_controller.h" #include "window/window_filters_menu.h" +#include "window/window_separate_id.h" #include "info/channel_statistics/earn/info_earn_inner_widget.h" #include "info/info_memento.h" #include "info/info_controller.h" @@ -26,11 +27,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL //#include "history/view/reactions/history_view_reactions_button.h" #include "history/view/history_view_replies_section.h" #include "history/view/history_view_scheduled_section.h" +#include "history/view/history_view_sublist_section.h" #include "media/player/media_player_instance.h" #include "media/view/media_view_open_common.h" #include "data/stickers/data_custom_emoji.h" #include "data/data_document_resolver.h" #include "data/data_download_manager.h" +#include "data/data_saved_messages.h" #include "data/data_session.h" #include "data/data_file_origin.h" #include "data/data_folder.h" @@ -1100,6 +1103,36 @@ void SessionNavigation::showPeerHistory( showPeerHistory(history->peer->id, params, msgId); } +void SessionNavigation::showByInitialId( + const SectionShow ¶ms, + MsgId msgId) { + const auto parent = parentController(); + const auto id = parent->window().id(); + auto instant = params; + instant.animated = anim::type::instant; + switch (id.type) { + case SeparateType::Archive: + clearSectionStack(instant); + parent->openFolder(id.folder()); + break; + case SeparateType::Forum: + clearSectionStack(instant); + parent->showForum(id.forum(), instant); + break; + case SeparateType::Primary: + clearSectionStack(instant); + break; + case SeparateType::Chat: + showThread(id.thread, msgId, instant); + break; + case SeparateType::SavedSublist: + showSection( + std::make_shared(id.sublist()), + instant); + break; + } +} + void SessionNavigation::showSettings( Settings::Type type, const SectionShow ¶ms) { @@ -1182,6 +1215,7 @@ SessionController::SessionController( std::make_unique(session)) , _chatPreviewManager(std::make_unique(this)) , _isPrimary(window->isPrimary()) +, _hasDialogs(window->id().hasChatsList()) , _sendingAnimation( std::make_unique(this)) , _tabbedSelector( @@ -1191,6 +1225,7 @@ SessionController::SessionController( GifPauseReason::TabbedPanel)) , _invitePeekTimer([=] { checkInvitePeek(); }) , _activeChatsFilter(session->data().chatsFilters().defaultId()) +, _openedFolder(window->id().folder()) , _defaultChatTheme(std::make_shared()) , _chatStyle(std::make_unique(session->colorIndicesValue())) , _giftPremiumValidator(this) { @@ -1373,8 +1408,8 @@ void SessionController::suggestArchiveAndMute() { })); } -PeerData *SessionController::singlePeer() const { - return _window->singlePeer(); +SeparateId SessionController::windowId() const { + return _window->id(); } bool SessionController::isPrimary() const { @@ -1466,7 +1501,7 @@ void SessionController::setupShortcuts() { if (account == &session().account()) { return false; } - const auto window = app->separateWindowForAccount(account); + const auto window = app->separateWindowFor(account); if (window) { window->activate(); } else { @@ -1523,7 +1558,9 @@ void SessionController::checkOpenedFilter() { } void SessionController::activateFirstChatsFilter() { - if (_filtersActivated || !session().data().chatsFilters().loaded()) { + if (_filtersActivated + || !isPrimary() + || !session().data().chatsFilters().loaded()) { return; } _filtersActivated = true; @@ -1536,8 +1573,24 @@ bool SessionController::uniqueChatsInSearchResults() const { && !_searchInChat.current(); } +bool SessionController::openFolderInDifferentWindow( + not_null folder) { + const auto id = SeparateId(SeparateType::Archive, &session()); + if (const auto separate = Core::App().separateWindowFor(id)) { + if (separate == _window) { + return false; + } + separate->sessionController()->showByInitialId(); + separate->activate(); + return true; + } + return false; +} + void SessionController::openFolder(not_null folder) { - if (_openedFolder.current() != folder) { + if (openFolderInDifferentWindow(folder)) { + return; + } else if (_openedFolder.current() != folder) { resetFakeUnreadWhileOpened(); } if (activeChatsFilterCurrent() != 0) { @@ -1550,22 +1603,44 @@ void SessionController::openFolder(not_null folder) { } void SessionController::closeFolder() { + if (_openedFolder.current() + && windowId().type == SeparateType::Archive) { + Core::App().closeWindow(_window); + return; + } _openedFolder = nullptr; } +bool SessionController::showForumInDifferentWindow( + not_null forum, + const SectionShow ¶ms) { + const auto window = Core::App().windowForShowingForum(forum); + if (window == _window) { + return false; + } else if (window) { + window->sessionController()->showForum(forum, params); + window->activate(); + return true; + } else if (windowId().hasChatsList()) { + return false; + } + const auto account = not_null(&session().account()); + auto primary = Core::App().separateWindowFor(account); + if (!primary) { + Core::App().domain().activate(account); + primary = Core::App().separateWindowFor(account); + } + if (primary && &primary->account() == account) { + primary->sessionController()->showForum(forum, params); + primary->activate(); + } + return true; +} + void SessionController::showForum( not_null forum, const SectionShow ¶ms) { - if (!isPrimary()) { - auto primary = Core::App().windowFor(&session().account()); - if (&primary->account() != &session().account()) { - Core::App().domain().activate(&session().account()); - primary = Core::App().windowFor(&session().account()); - } - if (&primary->account() == &session().account()) { - primary->sessionController()->showForum(forum, params); - } - primary->activate(); + if (showForumInDifferentWindow(forum, params)) { return; } _shownForumLifetime.destroy(); @@ -1598,6 +1673,18 @@ void SessionController::showForum( } void SessionController::closeForum() { + if (const auto forum = _shownForum.current()) { + const auto id = windowId(); + if (id.type == SeparateType::Forum) { + const auto initial = id.thread->asForum(); + if (!initial || initial == forum) { + Core::App().closeWindow(_window); + } else { + showForum(initial); + } + return; + } + } _shownForumLifetime.destroy(); _shownForum = nullptr; } @@ -1889,7 +1976,7 @@ int SessionController::dialogsSmallColumnWidth() const { } int SessionController::minimalThreeColumnWidth() const { - return (_isPrimary ? st::columnMinimalWidthLeft : 0) + return (_hasDialogs ? st::columnMinimalWidthLeft : 0) + st::columnMinimalWidthMain + st::columnMinimalWidthThird; } @@ -1903,7 +1990,7 @@ auto SessionController::computeColumnLayout() const -> ColumnLayout { auto useOneColumnLayout = [&] { auto minimalNormal = st::columnMinimalWidthLeft + st::columnMinimalWidthMain; - if (_isPrimary && bodyWidth < minimalNormal) { + if (_hasDialogs && bodyWidth < minimalNormal) { return true; } return false; @@ -1945,7 +2032,7 @@ auto SessionController::computeColumnLayout() const -> ColumnLayout { } int SessionController::countDialogsWidthFromRatio(int bodyWidth) const { - if (!_isPrimary) { + if (!_hasDialogs) { return 0; } const auto nochat = !mainSectionShown(); @@ -1979,8 +2066,8 @@ SessionController::ShrinkResult SessionController::shrinkDialogsAndThirdColumns( if (thirdWidthNew < st::columnMinimalWidthThird) { thirdWidthNew = st::columnMinimalWidthThird; dialogsWidthNew = bodyWidth - thirdWidthNew - chatWidth; - Assert(!_isPrimary || dialogsWidthNew >= st::columnMinimalWidthLeft); - } else if (_isPrimary && dialogsWidthNew < st::columnMinimalWidthLeft) { + Assert(!_hasDialogs || dialogsWidthNew >= st::columnMinimalWidthLeft); + } else if (_hasDialogs && dialogsWidthNew < st::columnMinimalWidthLeft) { dialogsWidthNew = st::columnMinimalWidthLeft; thirdWidthNew = bodyWidth - dialogsWidthNew - chatWidth; Assert(thirdWidthNew >= st::columnMinimalWidthThird); @@ -2089,9 +2176,11 @@ void SessionController::closeThirdSection() { } } -bool SessionController::canShowSeparateWindow( - not_null peer) const { - return !peer->isForum() && peer->computeUnavailableReason().isEmpty(); +bool SessionController::canShowSeparateWindow(SeparateId id) const { + if (const auto thread = id.thread) { + return thread->peer()->computeUnavailableReason().isEmpty(); + } + return true; } void SessionController::showPeer(not_null peer, MsgId msgId) { @@ -2314,21 +2403,20 @@ void SessionController::clearChooseReportMessages() const { } void SessionController::showInNewWindow( - not_null peer, + SeparateId id, MsgId msgId) { - if (!canShowSeparateWindow(peer)) { - showThread( - peer->owner().history(peer), - msgId, - Window::SectionShow::Way::ClearStack); + if (!canShowSeparateWindow(id)) { + Assert(id.thread != nullptr); + showThread(id.thread, msgId, SectionShow::Way::ClearStack); return; } const auto active = activeChatCurrent(); - const auto fromActive = active.history() - ? (active.history()->peer == peer) + // windows check active forum / active archive + const auto fromActive = active.thread() + ? (active.thread() == id.thread) : false; const auto toSeparate = [=] { - Core::App().ensureSeparateWindowForPeer(peer, msgId); + Core::App().ensureSeparateWindowFor(id, msgId); }; if (fromActive) { window().preventOrInvoke([=] { @@ -2485,6 +2573,9 @@ FilterId SessionController::activeChatsFilterCurrent() const { void SessionController::setActiveChatsFilter( FilterId id, const SectionShow ¶ms) { + if (!isPrimary()) { + return; + } const auto changed = (activeChatsFilterCurrent() != id); if (changed) { resetFakeUnreadWhileOpened(); diff --git a/Telegram/SourceFiles/window/window_session_controller.h b/Telegram/SourceFiles/window/window_session_controller.h index 35e690903..d738a048e 100644 --- a/Telegram/SourceFiles/window/window_session_controller.h +++ b/Telegram/SourceFiles/window/window_session_controller.h @@ -91,6 +91,7 @@ class FiltersMenu; class ChatPreviewManager; struct PeerByLinkInfo; +struct SeparateId; struct PeerThemeOverride { PeerData *peer = nullptr; @@ -232,6 +233,10 @@ public: ShowAtUnreadMsgId); } + void showByInitialId( + const SectionShow ¶ms = SectionShow::Way::ClearStack, + MsgId msgId = ShowAtUnreadMsgId); + void showSettings( Settings::Type type, const SectionShow ¶ms = SectionShow()); @@ -326,7 +331,7 @@ public: [[nodiscard]] Controller &window() const { return *_window; } - [[nodiscard]] PeerData *singlePeer() const; + [[nodiscard]] SeparateId windowId() const; [[nodiscard]] bool isPrimary() const; [[nodiscard]] not_null<::MainWindow*> widget() const; [[nodiscard]] not_null content() const; @@ -432,7 +437,7 @@ public: void resizeForThirdSection(); void closeThirdSection(); - [[nodiscard]] bool canShowSeparateWindow(not_null peer) const; + [[nodiscard]] bool canShowSeparateWindow(SeparateId id) const; void showPeer(not_null peer, MsgId msgId = ShowAtUnreadMsgId); void startOrJoinGroupCall(not_null peer); @@ -509,7 +514,7 @@ public: void clearChooseReportMessages() const; void showInNewWindow( - not_null peer, + SeparateId id, MsgId msgId = ShowAtUnreadMsgId); void toggleChooseChatTheme( @@ -667,10 +672,16 @@ private: void checkNonPremiumLimitToastDownload(DocumentId id); void checkNonPremiumLimitToastUpload(FullMsgId id); + bool openFolderInDifferentWindow(not_null folder); + bool showForumInDifferentWindow( + not_null forum, + const SectionShow ¶ms); + const not_null _window; const std::unique_ptr _emojiInteractions; const std::unique_ptr _chatPreviewManager; const bool _isPrimary = false; + const bool _hasDialogs = false; mutable std::shared_ptr _cachedShow;