diff --git a/Telegram/SourceFiles/core/application.cpp b/Telegram/SourceFiles/core/application.cpp index d23ae9c2ac..aeb5a6008c 100644 --- a/Telegram/SourceFiles/core/application.cpp +++ b/Telegram/SourceFiles/core/application.cpp @@ -46,6 +46,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "iv/iv_delegate_impl.h" #include "iv/iv_instance.h" #include "iv/iv_data.h" +#include "iv/editor/iv_editor_session.h" #include "lang/lang_translator.h" #include "lang/lang_cloud_manager.h" #include "lang/lang_hardcoded.h" @@ -214,6 +215,7 @@ void Application::closeAdditionalWindows() { } } _iv->closeAll(); + Iv::Editor::CloseAllWindows(); } Application::~Application() { diff --git a/Telegram/SourceFiles/iv/editor/iv_editor_session.cpp b/Telegram/SourceFiles/iv/editor/iv_editor_session.cpp index 57b7a51b57..e91631b1cc 100644 --- a/Telegram/SourceFiles/iv/editor/iv_editor_session.cpp +++ b/Telegram/SourceFiles/iv/editor/iv_editor_session.cpp @@ -983,6 +983,7 @@ private: void showWindow() { _backgroundHold = shared_from_this(); + registerLiveAndTrackSession(); auto descriptor = ShowWindowDescriptor{ .session = _session, .peer = _peer, @@ -1039,6 +1040,63 @@ private: } } +public: + static void CloseAll() { + auto live = std::vector>(); + std::swap(live, Live()); + for (const auto &weak : live) { + if (const auto strong = weak.lock()) { + strong->forceClose(); + } + } + } + +private: + // Registry of all editor sessions that currently own a window, so that + // they can be force-closed on session clear or application shutdown. + [[nodiscard]] static std::vector> &Live() { + static auto result = std::vector>(); + return result; + } + + void registerLiveAndTrackSession() { + auto &live = Live(); + live.erase( + std::remove_if( + live.begin(), + live.end(), + [](const std::weak_ptr &weak) { + return weak.expired(); + }), + live.end()); + live.push_back(weak_from_this()); + + _session->data().sessionDataAboutToBeCleared( + ) | rpl::on_next([weak = weak_from_this()] { + // Holds a strong reference for the duration of the call, so that + // dropping the self-hold inside forceClose() doesn't run + // ~ArticleSession re-entrantly while this handler is on the stack. + if (const auto strong = weak.lock()) { + strong->forceClose(); + } + }, _lifetime); + } + + // Destroys the editor window synchronously and releases the self-hold. + // The caller must hold a strong reference (see CloseAll() and the session + // clear handler) so that the eventual ~ArticleSession runs after this + // returns rather than re-entrantly. + void forceClose() { + if (!_windowHost && !_backgroundHold) { + return; + } + _editor = nullptr; + _submitButton = nullptr; + _windowHost = nullptr; + _editorShow = nullptr; + _backgroundHold = nullptr; + } + void handleMediaDialogResult( QPointer editor, FileDialog::OpenResult &&result) { @@ -1931,4 +1989,8 @@ void ShowEditBox( }); } +void CloseAllWindows() { + ArticleSession::CloseAll(); +} + } // namespace Iv::Editor diff --git a/Telegram/SourceFiles/iv/editor/iv_editor_session.h b/Telegram/SourceFiles/iv/editor/iv_editor_session.h index 42b5c36bc4..6b23e4ff44 100644 --- a/Telegram/SourceFiles/iv/editor/iv_editor_session.h +++ b/Telegram/SourceFiles/iv/editor/iv_editor_session.h @@ -29,4 +29,10 @@ void ShowEditBox( not_null controller, not_null item); +// Synchronously destroys all open editor windows. Called on application +// shutdown (before ~Sandbox) so that no editor top-level widget survives +// to be destroyed from ~QApplication, where the lib_ui native event filter +// would re-enter the already destroyed Sandbox machinery and crash. +void CloseAllWindows(); + } // namespace Iv::Editor