From 7023b013ce6c4ceb81b57ebe714b680d5dec47e4 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 17 Jan 2023 19:47:58 +0400 Subject: [PATCH] Initial support of separate windows for accounts. --- Telegram/SourceFiles/apiwrap.cpp | 20 +- Telegram/SourceFiles/boxes/abstract_box.cpp | 6 +- .../SourceFiles/boxes/choose_filter_box.cpp | 5 +- .../calls/group/calls_group_members.cpp | 5 +- Telegram/SourceFiles/core/application.cpp | 247 +++++++++++++----- Telegram/SourceFiles/core/application.h | 23 +- Telegram/SourceFiles/core/sandbox.cpp | 4 +- Telegram/SourceFiles/core/shortcuts.cpp | 2 +- Telegram/SourceFiles/core/ui_integration.cpp | 3 +- Telegram/SourceFiles/core/update_checker.cpp | 2 +- .../data/data_download_manager.cpp | 8 +- .../dialogs/dialogs_inner_widget.cpp | 4 +- .../SourceFiles/history/history_widget.cpp | 5 +- Telegram/SourceFiles/main/main_domain.cpp | 10 +- Telegram/SourceFiles/main/main_session.cpp | 12 +- Telegram/SourceFiles/mainwidget.cpp | 16 +- Telegram/SourceFiles/mainwindow.cpp | 5 +- .../media/player/media_player_instance.cpp | 6 +- .../media/view/media_view_overlay_widget.cpp | 9 +- .../platform/linux/specific_linux.cpp | 4 +- .../SourceFiles/platform/linux/tray_linux.cpp | 5 +- .../touchbar/items/mac_pinned_chats_item.mm | 2 +- Telegram/SourceFiles/platform/mac/tray_mac.mm | 5 +- .../platform/mac/window_title_mac.mm | 4 +- .../platform/win/main_window_win.cpp | 6 +- .../SourceFiles/platform/win/tray_win.cpp | 7 +- .../settings/settings_information.cpp | 17 +- .../window/notifications_manager.cpp | 3 +- .../window/notifications_manager_default.cpp | 8 +- .../SourceFiles/window/window_controller.cpp | 5 + .../SourceFiles/window/window_controller.h | 1 + .../window/window_session_controller.cpp | 7 +- 32 files changed, 323 insertions(+), 143 deletions(-) diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 48ed35483..7e75c8693 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -123,22 +123,18 @@ using UpdatedFileReferences = Data::UpdatedFileReferences; [[nodiscard]] std::shared_ptr ShowForPeer( not_null peer) { - const auto separate = Core::App().separateWindowForPeer(peer); - const auto window = separate ? separate : Core::App().primaryWindow(); - return std::make_shared(window); + return std::make_shared(Core::App().windowFor(peer)); } void ShowChannelsLimitBox(not_null peer) { - const auto primary = Core::App().primaryWindow(); - if (!primary) { - return; + if (const auto window = Core::App().windowFor(peer)) { + window->invokeForSessionController( + &peer->session().account(), + peer, + [&](not_null controller) { + controller->show(Box(ChannelsLimitBox, &peer->session())); + }); } - primary->invokeForSessionController( - &peer->session().account(), - peer, - [&](not_null controller) { - controller->show(Box(ChannelsLimitBox, &peer->session())); - }); } } // namespace diff --git a/Telegram/SourceFiles/boxes/abstract_box.cpp b/Telegram/SourceFiles/boxes/abstract_box.cpp index 7f693e102..5b9f1fd63 100644 --- a/Telegram/SourceFiles/boxes/abstract_box.cpp +++ b/Telegram/SourceFiles/boxes/abstract_box.cpp @@ -20,7 +20,7 @@ void showBox( LayerOptions options, anim::type animated) { const auto window = Core::IsAppLaunched() - ? Core::App().primaryWindow() + ? Core::App().activePrimaryWindow() : nullptr; if (window) { window->show(std::move(content), options, animated); @@ -31,7 +31,7 @@ void showBox( void hideLayer(anim::type animated) { const auto window = Core::IsAppLaunched() - ? Core::App().primaryWindow() + ? Core::App().activePrimaryWindow() : nullptr; if (window) { window->hideLayer(animated); @@ -40,7 +40,7 @@ void hideLayer(anim::type animated) { bool isLayerShown() { const auto window = Core::IsAppLaunched() - ? Core::App().primaryWindow() + ? Core::App().activePrimaryWindow() : nullptr; return window && window->isLayerShown(); } diff --git a/Telegram/SourceFiles/boxes/choose_filter_box.cpp b/Telegram/SourceFiles/boxes/choose_filter_box.cpp index e3452953d..6ea39d2e2 100644 --- a/Telegram/SourceFiles/boxes/choose_filter_box.cpp +++ b/Telegram/SourceFiles/boxes/choose_filter_box.cpp @@ -69,10 +69,11 @@ void ChangeFilterById( MTP_flags(MTPmessages_UpdateDialogFilter::Flag::f_filter), MTP_int(filter.id()), filter.tl() - )).done([=, chat = history->peer->name(), name = filter.title()]{ + )).done([=, chat = history->peer->name(), name = filter.title()] { // Since only the primary window has dialogs list, // We can safely show toast there. - if (const auto controller = Core::App().primaryWindow()) { + const auto account = &history->session().account(); + if (const auto controller = Core::App().windowFor(account)) { auto text = (add ? tr::lng_filters_toast_add : tr::lng_filters_toast_remove)( diff --git a/Telegram/SourceFiles/calls/group/calls_group_members.cpp b/Telegram/SourceFiles/calls/group/calls_group_members.cpp index ebc047926..be53c2aca 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_members.cpp @@ -1194,10 +1194,7 @@ base::unique_qptr Members::Controller::createRowContextMenu( const auto admin = IsGroupCallAdmin(_peer, participantPeer); const auto session = &_peer->session(); const auto getCurrentWindow = [=]() -> Window::SessionController* { - if (const auto window = Core::App().separateWindowForPeer( - participantPeer)) { - return window->sessionController(); - } else if (const auto window = Core::App().primaryWindow()) { + if (const auto window = Core::App().windowFor(participantPeer)) { if (const auto controller = window->sessionController()) { if (&controller->session() == session) { return controller; diff --git a/Telegram/SourceFiles/core/application.cpp b/Telegram/SourceFiles/core/application.cpp index 4f6719405..14714e1e4 100644 --- a/Telegram/SourceFiles/core/application.cpp +++ b/Telegram/SourceFiles/core/application.cpp @@ -186,8 +186,9 @@ Application::~Application() { // Depend on primaryWindow() for now :( Shortcuts::Finish(); + _closingAsyncWindows.clear(); _secondaryWindows.clear(); - _primaryWindow = nullptr; + _primaryWindows.clear(); _mediaView = nullptr; _notifications->clearAllFast(); @@ -280,13 +281,15 @@ void Application::run() { // Create mime database, so it won't be slow later. QMimeDatabase().mimeTypeForName(u"text/plain"_q); - _primaryWindow = std::make_unique(); - _lastActiveWindow = _primaryWindow.get(); + _primaryWindows.emplace(nullptr, std::make_unique()); + _lastActiveWindow + = _lastActivePrimaryWindow + = _primaryWindows.front().second.get(); _domain->activeChanges( ) | rpl::start_with_next([=](not_null account) { - _primaryWindow->showAccount(account); - }, _primaryWindow->widget()->lifetime()); + showAccount(account); + }, _lifetime); ( _domain->activeValue( @@ -303,15 +306,15 @@ void Application::run() { ) | rpl::start_with_next([=](not_null account) { const auto ordered = _domain->orderedAccounts(); const auto it = ranges::find(ordered, account); - if (it != end(ordered)) { + if (_lastActivePrimaryWindow && it != end(ordered)) { const auto index = std::distance(begin(ordered), it); if ((index + 1) > _domain->maxAccounts()) { - _primaryWindow->show(Box( + _lastActivePrimaryWindow->show(Box( AccountsLimitBox, &account->session())); } } - }, _primaryWindow->widget()->lifetime()); + }, _lifetime); QCoreApplication::instance()->installEventFilter(this); @@ -332,20 +335,20 @@ void Application::run() { startTray(); - _primaryWindow->widget()->show(); + _lastActivePrimaryWindow->widget()->show(); - const auto currentGeometry = _primaryWindow->widget()->geometry(); + const auto current = _lastActivePrimaryWindow->widget()->geometry(); _mediaView = std::make_unique(); - _primaryWindow->widget()->Ui::RpWidget::setGeometry(currentGeometry); + _lastActivePrimaryWindow->widget()->Ui::RpWidget::setGeometry(current); DEBUG_LOG(("Application Info: showing.")); - _primaryWindow->finishFirstShow(); + _lastActivePrimaryWindow->finishFirstShow(); - if (!_primaryWindow->locked() && cStartToSettings()) { - _primaryWindow->showSettings(); + if (!_lastActivePrimaryWindow->locked() && cStartToSettings()) { + _lastActivePrimaryWindow->showSettings(); } - _primaryWindow->updateIsActiveFocus(); + _lastActivePrimaryWindow->updateIsActiveFocus(); for (const auto &error : Shortcuts::Errors()) { LOG(("Shortcuts Error: %1").arg(error)); @@ -362,11 +365,6 @@ void Application::run() { _mediaView->show(std::move(request)); } }, _lifetime); - _primaryWindow->openInMediaViewRequests( - ) | rpl::start_to_stream( - _openInMediaViewRequests, - _primaryWindow->lifetime()); - { const auto countries = std::make_shared( _domain.get()); @@ -374,6 +372,25 @@ void Application::run() { [[maybe_unused]] const auto countriesCopy = countries; }); } + + processCreatedWindow(_lastActivePrimaryWindow); +} + +void Application::showAccount(not_null account) { + if (const auto separate = separateWindowForAccount(account)) { + _lastActivePrimaryWindow = separate; + separate->activate(); + } else if (const auto last = activePrimaryWindow()) { + for (auto &[key, window] : _primaryWindows) { + if (window.get() == last && key != account.get()) { + auto found = std::move(window); + _primaryWindows.remove(key); + _primaryWindows.emplace(account, std::move(found)); + break; + } + } + last->showAccount(account); + } } void Application::showOpenGLCrashNotification() { @@ -390,7 +407,7 @@ void Application::showOpenGLCrashNotification() { Core::App().settings().setDisableOpenGL(true); Local::writeSettings(); }; - _primaryWindow->show(Ui::MakeConfirmBox({ + _lastActivePrimaryWindow->show(Ui::MakeConfirmBox({ .text = "" "There may be a problem with your graphics drivers and OpenGL. " "Try updating your drivers.\n\n" @@ -447,15 +464,15 @@ void Application::startSystemDarkModeViewer() { void Application::enumerateWindows(Fn)> callback) const { - if (_primaryWindow) { - callback(_primaryWindow.get()); + for (const auto &window : ranges::views::values(_primaryWindows)) { + callback(window.get()); } for (const auto &window : ranges::views::values(_secondaryWindows)) { callback(window.get()); } } -void Application::processSecondaryWindow( +void Application::processCreatedWindow( not_null window) { window->openInMediaViewRequests( ) | rpl::start_to_stream(_openInMediaViewRequests, window->lifetime()); @@ -468,21 +485,29 @@ void Application::startTray() { ) | rpl::start_with_next([=] { enumerateWindows([&](WindowRaw w) { w->updateIsActive(); }); _tray->updateMenuText(); - }, _primaryWindow->widget()->lifetime()); + }, _lifetime); _tray->showFromTrayRequests( ) | rpl::start_with_next([=] { const auto last = _lastActiveWindow; - enumerateWindows([&](WindowRaw w) { w->widget()->showFromTray(); }); - if (last) { + const auto primary = _lastActivePrimaryWindow; + enumerateWindows([&](WindowRaw w) { + if (w != last && w != primary) { + w->widget()->showFromTray(); + } + }); + if (primary) { + primary->widget()->showFromTray(); + } + if (last && last != primary) { last->widget()->showFromTray(); } - }, _primaryWindow->widget()->lifetime()); + }, _lifetime); _tray->hideToTrayRequests( ) | rpl::start_with_next([=] { enumerateWindows([&](WindowRaw w) { w->widget()->minimizeToTray(); }); - }, _primaryWindow->widget()->lifetime()); + }, _lifetime); } auto Application::prepareEmojiSourceImages() @@ -504,10 +529,10 @@ void Application::clearEmojiSourceImages() { } bool Application::isActiveForTrayMenu() const { - if (_primaryWindow && _primaryWindow->widget()->isActiveForTrayMenu()) { - return true; - } - return ranges::any_of(ranges::views::values(_secondaryWindows), [=]( + return ranges::any_of(ranges::views::values(_primaryWindows), [=]( + const std::unique_ptr &controller) { + return controller->widget()->isActiveForTrayMenu(); + }) || ranges::any_of(ranges::views::values(_secondaryWindows), [=]( const std::unique_ptr &controller) { return controller->widget()->isActiveForTrayMenu(); }); @@ -562,8 +587,8 @@ bool Application::eventFilter(QObject *object, QEvent *e) { cSetStartUrl(url.mid(0, 8192)); checkStartUrl(); } - if (StartUrlRequiresActivate(url)) { - _primaryWindow->activate(); + if (_lastActivePrimaryWindow && StartUrlRequiresActivate(url)) { + _lastActivePrimaryWindow->activate(); } } } break; @@ -804,15 +829,15 @@ void Application::checkLocalTime() { void Application::handleAppActivated() { checkLocalTime(); - if (_primaryWindow) { - _primaryWindow->updateIsActiveFocus(); + if (_lastActiveWindow) { + _lastActiveWindow->updateIsActiveFocus(); } } void Application::handleAppDeactivated() { - if (_primaryWindow) { - _primaryWindow->updateIsActiveBlur(); - } + enumerateWindows([&](not_null w) { + w->updateIsActiveBlur(); + }); const auto session = _lastActiveWindow ? _lastActiveWindow->maybeSession() : nullptr; @@ -845,8 +870,8 @@ void Application::switchDebugMode() { Logs::SetDebugEnabled(true); _launcher->writeDebugModeSetting(); DEBUG_LOG(("Debug logs started.")); - if (_primaryWindow) { - _primaryWindow->hideLayer(); + if (_lastActivePrimaryWindow) { + _lastActivePrimaryWindow->hideLayer(); } } } @@ -959,13 +984,17 @@ bool Application::canApplyLangPackWithoutRestart() const { } void Application::checkSendPaths() { - if (!cSendPaths().isEmpty() && _primaryWindow && !_primaryWindow->locked()) { - _primaryWindow->widget()->sendPaths(); + if (!cSendPaths().isEmpty() + && _lastActivePrimaryWindow + && !_lastActivePrimaryWindow->locked()) { + _lastActivePrimaryWindow->widget()->sendPaths(); } } void Application::checkStartUrl() { - if (!cStartUrl().isEmpty() && _primaryWindow && !_primaryWindow->locked()) { + if (!cStartUrl().isEmpty() + && _lastActivePrimaryWindow + && !_lastActivePrimaryWindow->locked()) { const auto url = cStartUrl(); cSetStartUrl(QString()); if (!openLocalUrl(url, {})) { @@ -1029,8 +1058,8 @@ bool Application::openCustomUrl( const auto my = context.value(); const auto controller = my.sessionWindow.get() ? my.sessionWindow.get() - : _primaryWindow - ? _primaryWindow->sessionController() + : _lastActivePrimaryWindow + ? _lastActivePrimaryWindow->sessionController() : nullptr; using namespace qthelp; @@ -1042,11 +1071,10 @@ bool Application::openCustomUrl( } } return false; - } void Application::preventOrInvoke(Fn &&callback) { - _primaryWindow->preventOrInvoke(std::move(callback)); + _lastActivePrimaryWindow->preventOrInvoke(std::move(callback)); } void Application::lockByPasscode() { @@ -1150,7 +1178,7 @@ void Application::localPasscodeChanged() { } bool Application::hasActiveWindow(not_null session) const { - if (Quitting() || !_primaryWindow) { + if (Quitting() || !_lastActiveWindow) { return false; } else if (_calls->hasActivePanel(session)) { return true; @@ -1161,8 +1189,18 @@ bool Application::hasActiveWindow(not_null session) const { return false; } -Window::Controller *Application::primaryWindow() const { - return _primaryWindow.get(); +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()) { + return window.get(); + } + } + return nullptr; } Window::Controller *Application::separateWindowForPeer( @@ -1194,22 +1232,98 @@ Window::Controller *Application::ensureSeparateWindowForPeer( peer->owner().history(peer), std::make_unique(peer, showAtMsgId) ).first->second.get(); - processSecondaryWindow(result); + processCreatedWindow(result); result->widget()->show(); result->finishFirstShow(); 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->widget()->show(); + 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)) { + return separate; + } + return activePrimaryWindow(); +} + Window::Controller *Application::activeWindow() const { return _lastActiveWindow; } +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()) { + return true; + } + } + return false; + }(); + if (!hasOther) { + return false; + } + _closingAsyncWindows.emplace(window); + crl::on_main(window, [=] { closeWindow(window); }); + return true; +} + void Application::closeWindow(not_null window) { + const auto next = (_primaryWindows.front().second.get() != window) + ? _primaryWindows.front().second.get() + : (_primaryWindows.back().second.get() != window) + ? _primaryWindows.back().second.get() + : nullptr; + if (_lastActivePrimaryWindow == window) { + _lastActivePrimaryWindow = next; + } + if (_lastActiveWindow == window) { + _lastActiveWindow = next; + if (_lastActiveWindow) { + _lastActiveWindow->activate(); + } + } + _closingAsyncWindows.remove(window); + for (auto i = begin(_primaryWindows); i != end(_primaryWindows);) { + if (i->second.get() == window) { + Assert(_lastActiveWindow != window); + Assert(_lastActivePrimaryWindow != window); + i = _primaryWindows.erase(i); + } else { + ++i; + } + } for (auto i = begin(_secondaryWindows); i != end(_secondaryWindows);) { if (i->second.get() == window) { - if (_lastActiveWindow == window) { - _lastActiveWindow = _primaryWindow.get(); - } + Assert(_lastActiveWindow != window); i = _secondaryWindows.erase(i); } else { ++i; @@ -1218,11 +1332,12 @@ void Application::closeWindow(not_null window) { } void Application::closeChatFromWindows(not_null peer) { + if (const auto window = windowFor(peer) + ; window && !window->isPrimary()) { + closeWindow(window); + } for (const auto &[history, window] : _secondaryWindows) { - if (history->peer == peer) { - closeWindow(window.get()); - break; - } else if (const auto session = window->sessionController()) { + if (const auto session = window->sessionController()) { if (session->activeChatCurrent().peer() == peer) { session->showPeerHistory( window->singlePeer()->id, @@ -1230,8 +1345,8 @@ void Application::closeChatFromWindows(not_null peer) { } } } - if (_primaryWindow && _primaryWindow->sessionController()) { - const auto primary = _primaryWindow->sessionController(); + if (const auto window = windowFor(&peer->account())) { + const auto primary = window->sessionController(); if ((primary->activeChatCurrent().peer() == peer) && (&primary->session() == &peer->session())) { primary->clearSectionStack(); @@ -1249,6 +1364,10 @@ void Application::windowActivated(not_null window) { const auto now = window; _lastActiveWindow = window; + if (window->isPrimary()) { + _lastActivePrimaryWindow = window; + } + const auto wasSession = was ? was->maybeSession() : nullptr; const auto nowSession = now->maybeSession(); if (wasSession != nowSession) { @@ -1410,8 +1529,8 @@ void Application::quitPreventFinished() { } void Application::quitDelayed() { - if (_primaryWindow) { - _primaryWindow->widget()->hide(); + for (const auto &[account, window] : _primaryWindows) { + window->widget()->hide(); } for (const auto &[history, window] : _secondaryWindows) { window->widget()->hide(); diff --git a/Telegram/SourceFiles/core/application.h b/Telegram/SourceFiles/core/application.h index 1d13c6f51..5898386b2 100644 --- a/Telegram/SourceFiles/core/application.h +++ b/Telegram/SourceFiles/core/application.h @@ -152,13 +152,25 @@ public: // Windows interface. bool hasActiveWindow(not_null session) const; - [[nodiscard]] Window::Controller *primaryWindow() const; + + // Don't auto-switch. [[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 *windowFor( // Doesn't auto-switch. + not_null peer) const; + [[nodiscard]] Window::Controller *windowFor( // Doesn't auto-switch. + not_null account) const; + [[nodiscard]] bool closeNonLastAsync( + not_null window); void closeWindow(not_null window); void windowActivated(not_null window); bool closeActiveWindow(); @@ -325,9 +337,10 @@ private: void startSystemDarkModeViewer(); void startTray(); + void showAccount(not_null account); void enumerateWindows( Fn)> callback) const; - void processSecondaryWindow(not_null window); + void processCreatedWindow(not_null window); friend void QuitAttempt(); void quitDelayed(); @@ -375,11 +388,15 @@ private: const std::unique_ptr _domain; const std::unique_ptr _exportManager; const std::unique_ptr _calls; - std::unique_ptr _primaryWindow; + base::flat_map< + Main::Account*, + std::unique_ptr> _primaryWindows; + base::flat_set> _closingAsyncWindows; base::flat_map< not_null, std::unique_ptr> _secondaryWindows; Window::Controller *_lastActiveWindow = nullptr; + Window::Controller *_lastActivePrimaryWindow = nullptr; std::unique_ptr _mediaView; const std::unique_ptr _langpack; diff --git a/Telegram/SourceFiles/core/sandbox.cpp b/Telegram/SourceFiles/core/sandbox.cpp index b445f9e51..aec7dba84 100644 --- a/Telegram/SourceFiles/core/sandbox.cpp +++ b/Telegram/SourceFiles/core/sandbox.cpp @@ -661,8 +661,8 @@ void Sandbox::closeApplication() { uint64 Sandbox::execExternal(const QString &cmd) { DEBUG_LOG(("Sandbox Info: executing external command '%1'").arg(cmd)); if (cmd == "show") { - if (Core::IsAppLaunched() && Core::App().primaryWindow()) { - const auto window = Core::App().primaryWindow(); + if (Core::IsAppLaunched() && Core::App().activePrimaryWindow()) { + const auto window = Core::App().activePrimaryWindow(); window->activate(); return Platform::ActivationWindowId(window->widget()); } else if (const auto window = PreLaunchWindow::instance()) { diff --git a/Telegram/SourceFiles/core/shortcuts.cpp b/Telegram/SourceFiles/core/shortcuts.cpp index 91c79525b..06b59760a 100644 --- a/Telegram/SourceFiles/core/shortcuts.cpp +++ b/Telegram/SourceFiles/core/shortcuts.cpp @@ -443,7 +443,7 @@ void Manager::set(const QString &keys, Command command, bool replace) { } auto shortcut = base::make_unique_q( result, - Core::App().primaryWindow()->widget().get(), + Core::App().activePrimaryWindow()->widget().get(), // #TODO windows nullptr, nullptr, Qt::ApplicationShortcut); diff --git a/Telegram/SourceFiles/core/ui_integration.cpp b/Telegram/SourceFiles/core/ui_integration.cpp index 760ae18e8..858d4a70d 100644 --- a/Telegram/SourceFiles/core/ui_integration.cpp +++ b/Telegram/SourceFiles/core/ui_integration.cpp @@ -124,7 +124,8 @@ QString UiIntegration::angleBackendFilePath() { } void UiIntegration::textActionsUpdated() { - if (const auto window = Core::App().primaryWindow()) { + // #TODO windows global menu + if (const auto window = Core::App().activePrimaryWindow()) { window->widget()->updateGlobalMenu(); } } diff --git a/Telegram/SourceFiles/core/update_checker.cpp b/Telegram/SourceFiles/core/update_checker.cpp index 9a1734a35..590354c56 100644 --- a/Telegram/SourceFiles/core/update_checker.cpp +++ b/Telegram/SourceFiles/core/update_checker.cpp @@ -1649,7 +1649,7 @@ void UpdateApplication() { } else { cSetAutoUpdate(true); const auto window = Core::IsAppLaunched() - ? Core::App().primaryWindow() + ? Core::App().activePrimaryWindow() : nullptr; if (window) { if (const auto controller = window->sessionController()) { diff --git a/Telegram/SourceFiles/data/data_download_manager.cpp b/Telegram/SourceFiles/data/data_download_manager.cpp index c121fa968..05080dfcb 100644 --- a/Telegram/SourceFiles/data/data_download_manager.cpp +++ b/Telegram/SourceFiles/data/data_download_manager.cpp @@ -508,9 +508,13 @@ HistoryItem *DownloadManager::lookupLoadingItem( void DownloadManager::loadingStopWithConfirmation( Fn callback, Main::Session *onlyInSession) { - const auto window = Core::App().primaryWindow(); const auto item = lookupLoadingItem(onlyInSession); - if (!window || !item) { + if (!item) { + return; + } + const auto window = Core::App().windowFor( + &item->history()->session().account()); + if (!window) { return; } const auto weak = base::make_weak(&item->history()->session()); diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index a713fea52..e65be7111 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -70,7 +70,7 @@ namespace { constexpr auto kHashtagResultsLimit = 5; constexpr auto kStartReorderThreshold = 30; -base::options::toggle TabbedPanelShowOnClick({ +base::options::toggle CtrlClickChatNewWindow({ .id = kOptionCtrlClickChatNewWindow, .name = "New chat window by Ctrl+Click", .description = "Open chat in a new window by Ctrl+Click " @@ -3310,7 +3310,7 @@ bool InnerWidget::chooseRow( const auto modifyChosenRow = []( ChosenRow row, Qt::KeyboardModifiers modifiers) { - if (TabbedPanelShowOnClick.value()) { + if (CtrlClickChatNewWindow.value()) { row.newWindow = (modifiers & Qt::ControlModifier); } return row; diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index d18024d42..a4725e9f3 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -732,8 +732,9 @@ HistoryWidget::HistoryWidget( if (flags & PeerUpdateFlag::UnavailableReason) { const auto unavailable = _peer->computeUnavailableReason(); if (!unavailable.isEmpty()) { + const auto account = &_peer->account(); closeCurrent(); - if (const auto primary = Core::App().primaryWindow()) { + if (const auto primary = Core::App().windowFor(account)) { primary->show(Ui::MakeInformBox(unavailable)); } return; @@ -3017,7 +3018,7 @@ 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().primaryWindow()) { + if (const auto primary = Core::App().windowFor(&was->account())) { Ui::ShowMultilineToast({ .parentOverride = Window::Show(primary).toastParent(), .text = { (was && was->isMegagroup()) diff --git a/Telegram/SourceFiles/main/main_domain.cpp b/Telegram/SourceFiles/main/main_domain.cpp index 7ddc3dfff..8dbe9fd4a 100644 --- a/Telegram/SourceFiles/main/main_domain.cpp +++ b/Telegram/SourceFiles/main/main_domain.cpp @@ -75,7 +75,7 @@ Storage::StartResult Domain::start(const QByteArray &passcode) { void Domain::finish() { _accountToActivate = -1; - _active = nullptr; + _active.reset(nullptr); base::take(_accounts); } @@ -417,9 +417,13 @@ void Domain::checkForLastProductionConfig( } void Domain::maybeActivate(not_null account) { - Core::App().preventOrInvoke(crl::guard(account, [=] { + if (Core::App().separateWindowForAccount(account)) { activate(account); - })); + } else { + Core::App().preventOrInvoke(crl::guard(account, [=] { + activate(account); + })); + } } void Domain::activate(not_null account) { diff --git a/Telegram/SourceFiles/main/main_session.cpp b/Telegram/SourceFiles/main/main_session.cpp index 2fa259247..a235f729f 100644 --- a/Telegram/SourceFiles/main/main_session.cpp +++ b/Telegram/SourceFiles/main/main_session.cpp @@ -20,6 +20,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "chat_helpers/stickers_emoji_pack.h" #include "chat_helpers/stickers_dice_pack.h" #include "chat_helpers/stickers_gift_box_pack.h" +#include "history/history.h" +#include "history/history_item.h" #include "inline_bots/bot_attach_web_view.h" #include "storage/file_download.h" #include "storage/download_manager_mtproto.h" @@ -411,12 +413,12 @@ bool Session::uploadsInProgress() const { } void Session::uploadsStopWithConfirmation(Fn done) { - const auto window = Core::App().primaryWindow(); - if (!window) { - return; - } const auto id = _uploader->currentUploadId(); - const auto exists = !!data().message(id); + const auto message = data().message(id); + const auto exists = (message != nullptr); + const auto window = message + ? Core::App().windowFor(message->history()->peer) + : Core::App().activePrimaryWindow(); auto box = Box([=](not_null box) { box->addRow( object_ptr( diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 60fd9c81e..9e96d58f1 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -1214,10 +1214,22 @@ bool MainWidget::showHistoryInDifferentWindow( showAtMsgId); separate->activate(); return true; - } else if (isPrimary() || (singlePeer()->id == peerId)) { + } else if (isPrimary()) { + const auto primary = Core::App().separateWindowForAccount( + &peer->account()); + if (primary != &_controller->window()) { + primary->sessionController()->showPeerHistory( + peerId, + params, + showAtMsgId); + primary->activate(); + return true; + } + return false; + } else if (singlePeer()->id == peerId) { return false; } - const auto primary = Core::App().primaryWindow(); + const auto primary = Core::App().activePrimaryWindow(); if (&primary->account() != &session().account()) { primary->showAccount(&session().account()); } diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index af2649392..f5ef973f8 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -666,11 +666,8 @@ void MainWindow::closeEvent(QCloseEvent *e) { e->accept(); Core::Quit(); return; - } else if (!isPrimary()) { + } else if (Core::App().closeNonLastAsync(&controller())) { e->accept(); - crl::on_main(this, [=] { - Core::App().closeWindow(&controller()); - }); return; } e->ignore(); diff --git a/Telegram/SourceFiles/media/player/media_player_instance.cpp b/Telegram/SourceFiles/media/player/media_player_instance.cpp index 7474c6518..3313d28c2 100644 --- a/Telegram/SourceFiles/media/player/media_player_instance.cpp +++ b/Telegram/SourceFiles/media/player/media_player_instance.cpp @@ -308,7 +308,8 @@ void Instance::clearStreamed(not_null data, bool savePosition) { data->streamed = nullptr; _roundPlaying = false; - if (const auto window = Core::App().primaryWindow()) { + // #TODO windows + if (const auto window = Core::App().activePrimaryWindow()) { if (const auto controller = window->sessionController()) { controller->disableGifPauseReason( Window::GifPauseReason::RoundPlaying); @@ -1292,7 +1293,8 @@ void Instance::handleStreamingUpdate( requestRoundVideoRepaint(); }); _roundPlaying = true; - if (const auto window = Core::App().primaryWindow()) { + // #TODO windows + if (const auto window = Core::App().activePrimaryWindow()) { if (const auto controller = window->sessionController()) { controller->enableGifPauseReason( Window::GifPauseReason::RoundPlaying); diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index a23592bba..219c0d0b0 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -4835,7 +4835,14 @@ Window::SessionController *OverlayWidget::findWindow(bool switchTo) const { if (switchTo) { auto controllerPtr = (Window::SessionController*)nullptr; - const auto anyWindow = window ? window : Core::App().primaryWindow(); + const auto account = &_session->account(); + const auto sessionWindow = Core::App().windowFor(account); + const auto anyWindow = (sessionWindow + && &sessionWindow->account() == account) + ? sessionWindow + : window + ? window + : sessionWindow; if (anyWindow) { anyWindow->invokeForSessionController( &_session->account(), diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index 0d90b7dce..a4c42ba1b 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -270,7 +270,7 @@ void LaunchGApplication() { app->signal_activate().connect([] { Core::Sandbox::Instance().customEnterFromEventLoop([] { const auto window = Core::IsAppLaunched() - ? Core::App().primaryWindow() + ? Core::App().activePrimaryWindow() : nullptr; if (window) { window->activate(); @@ -298,7 +298,7 @@ void LaunchGApplication() { } if (Core::StartUrlRequiresActivate(url)) { const auto window = Core::IsAppLaunched() - ? Core::App().primaryWindow() + ? Core::App().activePrimaryWindow() : nullptr; if (window) { window->activate(); diff --git a/Telegram/SourceFiles/platform/linux/tray_linux.cpp b/Telegram/SourceFiles/platform/linux/tray_linux.cpp index 82fe8f7d8..be02e5e0e 100644 --- a/Telegram/SourceFiles/platform/linux/tray_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/tray_linux.cpp @@ -26,8 +26,9 @@ namespace Platform { namespace { [[nodiscard]] QWidget *Parent() { - Expects(Core::App().primaryWindow() != nullptr); - return Core::App().primaryWindow()->widget(); + Expects(Core::App().activePrimaryWindow() != nullptr); + + return Core::App().activePrimaryWindow()->widget(); } } // namespace diff --git a/Telegram/SourceFiles/platform/mac/touchbar/items/mac_pinned_chats_item.mm b/Telegram/SourceFiles/platform/mac/touchbar/items/mac_pinned_chats_item.mm index ac1bc1e4f..43c37c92e 100644 --- a/Telegram/SourceFiles/platform/mac/touchbar/items/mac_pinned_chats_item.mm +++ b/Telegram/SourceFiles/platform/mac/touchbar/items/mac_pinned_chats_item.mm @@ -767,7 +767,7 @@ TimeId CalculateOnlineTill(not_null peer) { return; } - const auto active = Core::App().primaryWindow(); + const auto active = Core::App().activePrimaryWindow(); const auto controller = active ? active->sessionController() : nullptr; const auto openFolder = [=] { const auto folder = _session->data().folderLoaded(Data::Folder::kId); diff --git a/Telegram/SourceFiles/platform/mac/tray_mac.mm b/Telegram/SourceFiles/platform/mac/tray_mac.mm index c31470852..44c3bf7ea 100644 --- a/Telegram/SourceFiles/platform/mac/tray_mac.mm +++ b/Telegram/SourceFiles/platform/mac/tray_mac.mm @@ -223,8 +223,9 @@ void UpdateIcon(const NSStatusItem *status) { } [[nodiscard]] QWidget *Parent() { - Expects(Core::App().primaryWindow() != nullptr); - return Core::App().primaryWindow()->widget(); + Expects(Core::App().activePrimaryWindow() != nullptr); + + return Core::App().activePrimaryWindow()->widget(); } } // namespace diff --git a/Telegram/SourceFiles/platform/mac/window_title_mac.mm b/Telegram/SourceFiles/platform/mac/window_title_mac.mm index e1aed9a19..b18d8c287 100644 --- a/Telegram/SourceFiles/platform/mac/window_title_mac.mm +++ b/Telegram/SourceFiles/platform/mac/window_title_mac.mm @@ -22,8 +22,8 @@ namespace Platform { // account, with 100% scale and without "px" dimensions, because thats // how it will look in real launched macOS app. int PreviewTitleHeight() { - if (auto window = Core::App().primaryWindow()) { - if (auto height = window->widget()->getCustomTitleHeight()) { + if (const auto window = Core::App().activePrimaryWindow()) { + if (const auto height = window->widget()->getCustomTitleHeight()) { return height; } } diff --git a/Telegram/SourceFiles/platform/win/main_window_win.cpp b/Telegram/SourceFiles/platform/win/main_window_win.cpp index 65d5b8468..cae742eb1 100644 --- a/Telegram/SourceFiles/platform/win/main_window_win.cpp +++ b/Telegram/SourceFiles/platform/win/main_window_win.cpp @@ -206,12 +206,8 @@ void MainWindow::shadowsDeactivate() { } void MainWindow::destroyedFromSystem() { - if (isPrimary()) { + if (!Core::App().closeNonLastAsync(&controller())) { Core::Quit(); - } else { - crl::on_main(this, [=] { - Core::App().closeWindow(&controller()); - }); } } diff --git a/Telegram/SourceFiles/platform/win/tray_win.cpp b/Telegram/SourceFiles/platform/win/tray_win.cpp index c43ed5409..9013cd743 100644 --- a/Telegram/SourceFiles/platform/win/tray_win.cpp +++ b/Telegram/SourceFiles/platform/win/tray_win.cpp @@ -90,8 +90,9 @@ constexpr auto kTooltipDelay = crl::time(10000); } [[nodiscard]] QWidget *Parent() { - Expects(Core::App().primaryWindow() != nullptr); - return Core::App().primaryWindow()->widget(); + Expects(Core::App().activePrimaryWindow() != nullptr); + + return Core::App().activePrimaryWindow()->widget(); } } // namespace @@ -135,7 +136,7 @@ void Tray::updateIcon() { } const auto counter = Core::App().unreadBadge(); const auto muted = Core::App().unreadBadgeMuted(); - const auto controller = Core::App().primaryWindow(); + const auto controller = Core::App().activePrimaryWindow(); const auto session = !controller ? nullptr : !controller->sessionController() diff --git a/Telegram/SourceFiles/settings/settings_information.cpp b/Telegram/SourceFiles/settings/settings_information.cpp index 609359ee1..f7834d77f 100644 --- a/Telegram/SourceFiles/settings/settings_information.cpp +++ b/Telegram/SourceFiles/settings/settings_information.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "settings/settings_information.h" +#include "dialogs/dialogs_inner_widget.h" // kOptionCtrlClickChatNewWindow. #include "editor/photo_editor_layer_widget.h" #include "settings/settings_common.h" #include "ui/wrap/vertical_layout.h" @@ -51,6 +52,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_user_names.h" #include "core/file_utilities.h" #include "base/call_delayed.h" +#include "base/options.h" #include "base/unixtime.h" #include "base/random.h" #include "styles/style_dialogs.h" // dialogsPremiumIcon @@ -560,7 +562,7 @@ void SetupAccountsWrap( QWidget *parent, not_null window, not_null account, - Fn callback, + Fn callback, bool locked) { const auto active = (account == &Core::App().activeAccount()); const auto session = &account->session(); @@ -642,7 +644,7 @@ void SetupAccountsWrap( raw->clicks( ) | rpl::start_with_next([=](Qt::MouseButton which) { if (which == Qt::LeftButton) { - callback(); + callback(raw->clickModifiers()); return; } else if (which != Qt::RightButton) { return; @@ -854,17 +856,24 @@ void AccountsList::rebuild() { button = nullptr; } else if (!button) { const auto nextIsLocked = (inner->count() >= premiumLimit); - auto callback = [=] { + auto callback = [=](Qt::KeyboardModifiers modifiers) { if (_reordering) { return; } - if (account == &Core::App().domain().active()) { + if (account == &_controller->session().account()) { _currentAccountActivations.fire({}); return; } + const auto newWindow = (modifiers & Qt::ControlModifier) + && base::options::lookup( + Dialogs::kOptionCtrlClickChatNewWindow).value(); auto activate = [=, guard = _accountSwitchGuard.make_guard()]{ if (guard) { _reorder->finishReordering(); + if (newWindow) { + Core::App().ensureSeparateWindowForAccount( + account); + } Core::App().domain().maybeActivate(account); } }; diff --git a/Telegram/SourceFiles/window/notifications_manager.cpp b/Telegram/SourceFiles/window/notifications_manager.cpp index 7960d8608..39d714ddd 100644 --- a/Telegram/SourceFiles/window/notifications_manager.cpp +++ b/Telegram/SourceFiles/window/notifications_manager.cpp @@ -565,7 +565,8 @@ void System::showNext() { const auto &settings = Core::App().settings(); if (alertThread) { if (settings.flashBounceNotify() && !_manager->skipFlashBounce()) { - if (const auto window = Core::App().primaryWindow()) { + const auto peer = alertThread->peer(); + if (const auto window = Core::App().windowFor(peer)) { if (const auto handle = window->widget()->windowHandle()) { handle->alert(kSystemAlertDuration); // (handle, SLOT(_q_clearAlert())); in the future. diff --git a/Telegram/SourceFiles/window/notifications_manager_default.cpp b/Telegram/SourceFiles/window/notifications_manager_default.cpp index 7d3032a5b..382bf2355 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.cpp +++ b/Telegram/SourceFiles/window/notifications_manager_default.cpp @@ -47,15 +47,17 @@ namespace Notifications { namespace Default { namespace { -QPoint notificationStartPosition() { +[[nodiscard]] QPoint notificationStartPosition() { const auto corner = Core::App().settings().notificationsCorner(); - const auto window = Core::App().primaryWindow(); + const auto window = Core::App().activePrimaryWindow(); const auto r = window ? window->widget()->desktopRect() : QGuiApplication::primaryScreen()->availableGeometry(); const auto isLeft = Core::Settings::IsLeftCorner(corner); const auto isTop = Core::Settings::IsTopCorner(corner); - const auto x = (isLeft == rtl()) ? (r.x() + r.width() - st::notifyWidth - st::notifyDeltaX) : (r.x() + st::notifyDeltaX); + const auto x = (isLeft == rtl()) + ? (r.x() + r.width() - st::notifyWidth - st::notifyDeltaX) + : (r.x() + st::notifyDeltaX); const auto y = isTop ? r.y() : (r.y() + r.height()); return QPoint(x, y); } diff --git a/Telegram/SourceFiles/window/window_controller.cpp b/Telegram/SourceFiles/window/window_controller.cpp index f301ebe8d..cde941da2 100644 --- a/Telegram/SourceFiles/window/window_controller.cpp +++ b/Telegram/SourceFiles/window/window_controller.cpp @@ -43,6 +43,11 @@ namespace Window { Controller::Controller() : Controller(CreateArgs{}) { } +Controller::Controller(not_null account) +: Controller(CreateArgs{}) { + showAccount(account); +} + Controller::Controller( not_null singlePeer, MsgId showAtMsgId) diff --git a/Telegram/SourceFiles/window/window_controller.h b/Telegram/SourceFiles/window/window_controller.h index 3fe280c9c..2f352560d 100644 --- a/Telegram/SourceFiles/window/window_controller.h +++ b/Telegram/SourceFiles/window/window_controller.h @@ -25,6 +25,7 @@ namespace Window { class Controller final : public base::has_weak_ptr { public: Controller(); + explicit Controller(not_null account); Controller( not_null singlePeer, MsgId showAtMsgId); diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index 4233de048..a731b2427 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -71,6 +71,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/boxes/confirm_box.h" #include "mainwidget.h" #include "mainwindow.h" +#include "main/main_account.h" +#include "main/main_domain.h" #include "main/main_session.h" #include "main/main_session_settings.h" #include "apiwrap.h" @@ -977,9 +979,10 @@ void SessionController::showForum( not_null forum, const SectionShow ¶ms) { if (!isPrimary()) { - const auto primary = Core::App().primaryWindow(); + auto primary = Core::App().windowFor(&session().account()); if (&primary->account() != &session().account()) { - primary->showAccount(&session().account()); + Core::App().domain().activate(&session().account()); + primary = Core::App().windowFor(&session().account()); } if (&primary->account() == &session().account()) { primary->sessionController()->showForum(forum, params);