diff --git a/src/slic3r/plugin/host/PluginHostUi.cpp b/src/slic3r/plugin/host/PluginHostUi.cpp index 46d46b402f..84988b2dff 100644 --- a/src/slic3r/plugin/host/PluginHostUi.cpp +++ b/src/slic3r/plugin/host/PluginHostUi.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -415,6 +416,44 @@ struct UiDockPanelHandle int id{0}; }; +// Polls until the plater is shown on screen, then runs the callback with it (null when the app +// is closing instead) and deletes itself. A plugin loaded at startup opens its panes from +// on_load, before the main window exists, and a pane added before that window is laid out is +// sized against the unsized frame and keeps that width (AuiMgr::track_docked_size). +class PlaterShownWaiter : public wxTimer +{ +public: + explicit PlaterShownWaiter(std::function fn) : m_fn(std::move(fn)) {} + + // True once the callback ran. + bool try_run() + { + const bool closing = GUI::wxGetApp().is_closing(); + GUI::Plater* plater = closing ? nullptr : GUI::wxGetApp().plater(); + if (!closing && (plater == nullptr || !plater->IsShownOnScreen())) + return false; + Stop(); + m_fn(plater); + // Off the timer callback's stack: wxGTK's timeout callback still reads the timer after Notify(). + GUI::wxGetApp().CallAfter([this]() { delete this; }); + return true; + } + + void Notify() override { try_run(); } + +private: + std::function m_fn; +}; + +void run_when_plater_shown(std::function fn) +{ + GUI::wxGetApp().CallAfter([fn = std::move(fn)]() mutable { + auto* waiter = new PlaterShownWaiter(std::move(fn)); + if (!waiter->try_run()) + waiter->Start(100); + }); +} + py::object ui_create_dock_panel(const std::string& html, const std::string& title, int width, int height, py::object on_message, py::object on_close, const std::string& dock) { @@ -434,14 +473,13 @@ py::object ui_create_dock_panel(const std::string& html, const std::string& titl const int new_id = UiRegistry::instance().reserve_id(); UiRegistry::instance().bind(new_id, nullptr, plugin_key); - GUI::wxGetApp().CallAfter([new_id, plugin_key, html, title, dock, w, h, - msg_adapter = std::move(msg_adapter), - close_holder = std::move(close_holder)]() mutable { + run_when_plater_shown([new_id, plugin_key, html, title, dock, w, h, + msg_adapter = std::move(msg_adapter), + close_holder = std::move(close_holder)](GUI::Plater* plater) mutable { if (!UiRegistry::instance().is_open(new_id)) return; - GUI::Plater* plater = GUI::wxGetApp().plater(); - if (plater == nullptr || GUI::wxGetApp().is_closing()) { + if (plater == nullptr) { UiRegistry::instance().remove(new_id); return; }