mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-04 14:20:58 +00:00
Device tab blank for webui printers after switching language (#14547)
* Save device url in all cases and load printer url after hot-reload finishes * Recreate web view from scratch as only URL fix seems not robust enough * Add the same robust browser recreation for WebViewDialog It should eliminate possible issue with blank Home and other pages in the same way as Printer page * Remove redundant fallback leftover * Fix webview reset state and replay Project info on page reload The first-show webview reset now runs only on Windows, reloads the last printer URL and resets the Project page's ready state. The Project tab replays its 3MF info whenever the page reloads, so it no longer goes blank after a theme switch or a slow first load. The Device tab no longer loads an extra time on first open, and the Home tab no longer navigates twice. NeedsRecreateOnShow() logs is_recreating_gui so one language switch shows whether the reset ever fires. * Build plugin pages on first show A language switch rebuilt every plugin page's browser while the main window was being recreated, which left plugin tabs blank on Windows. Plugin pages are now lazy pages, never prebuilt, and are removed left to right so removing pages never builds one only to destroy it. A plugin page's script now starts when its tab is first opened; messages posted before that are dropped. --------- Co-authored-by: SoftFever <softfeverever@gmail.com> Co-authored-by: Noisyfox <timemanager.rick@gmail.com> Co-authored-by: SoftFever <103989404+SoftFever@users.noreply.github.com>
This commit is contained in:
co-authored by
SoftFever
Noisyfox
SoftFever
parent
8a6377f087
commit
1cfb725682
@@ -331,6 +331,10 @@ OrcaSlicer: integer spinners are `::SpinInput` ([below](#spininput)); progress b
|
||||
Contract:
|
||||
- A page must be created with the book as its parent and added once; the book owns and deletes it
|
||||
(`interface/wx/bookctrl.h:253-254, 273`). `RemovePage` detaches without deleting, and you then own it (`:324-330`).
|
||||
- Removing the selected page selects the page before it (the new first page if it was first) through `SetSelection`,
|
||||
so that page is shown and PAGE_CHANGING/CHANGED are sent; removing a page before the selection only shifts the
|
||||
index. This is `wxBookCtrlBase::DoSetSelectionAfterRemoval` (`src/common/bookctrl.cpp:477-495` **[source]**),
|
||||
called from `DoRemovePage` by `wxSimplebook`, `wxChoicebook`, `wxListbook`, `wxToolbook` and Orca's `Notebook`.
|
||||
- `GetSelection()` inside a `PAGE_CHANGED` handler may return the old or the new page depending on the platform; use
|
||||
`event.GetSelection()` (`interface/wx/bookctrl.h:160-166`).
|
||||
- `wxSimplebook` has no UI; switch with `ChangeSelection()`. `SetSelection()` sends PAGE_CHANGING/CHANGED
|
||||
|
||||
@@ -332,7 +332,9 @@ indices: pages come and go per printer and per feature flag.
|
||||
pages depending on the printer and on `use_printer_agents`; a removed page stays registered but is
|
||||
not prebuilt (its `LazyPage::in_book()` is false).
|
||||
- Plugin pages are appended by `PluginPages::initialize` (`plugin/host/PluginPages.hpp`) with
|
||||
namespaced ids (`plugin.<plugin_key>.<name>`) that cannot collide with `TAB_ID_*`.
|
||||
namespaced ids (`plugin.<plugin_key>.<name>`) that cannot collide with `TAB_ID_*`. Each is a
|
||||
`LazyPage<PluginPage>` with order −1, destroyed when its capability goes away.
|
||||
→ [Deferred construction](#deferred-construction-lazy-lazypage-stagedbuild-idlescheduler)
|
||||
|
||||
### Preset tabs
|
||||
|
||||
@@ -529,6 +531,32 @@ the main frame does nothing to a panel after creating it.
|
||||
m_idle.add(m_diff_dialog);
|
||||
```
|
||||
Cite: `IdleScheduler::tick`, `docs/HLSD/deferred-page-construction.md`.
|
||||
- **Rule:** A lazy page that can be destroyed while the main frame lives takes a negative order and
|
||||
stays out of `m_lazy_pages`.
|
||||
**Why:** `m_lazy_pages` and `PrebuildQueue` hold raw `LazyBase*` and nothing removes one
|
||||
(`PrebuildQueue` has only `add` and `clear`). The queue calls `pending()` on every task each slice,
|
||||
and `prebuild_pages_when_idle` reads every entry of `m_lazy_pages`, so a page destroyed while still
|
||||
listed can be read after it is freed. A page only taken out of the book is fine: it stays registered
|
||||
and its `pending()` is false (`MainFrame::show_device`).
|
||||
```cpp
|
||||
// Right (PluginPages::create_page): order -1, and no m_lazy_pages.push_back
|
||||
auto* page = new GUI::LazyPage<PluginPage>(m_parent, name, -1, [capability](wxWindow* parent) {
|
||||
return new PluginPage(parent, capability);
|
||||
});
|
||||
```
|
||||
Cite: `PluginPages::create_page`, `PluginPages::remove_page`.
|
||||
- **Rule:** Remove several lazy pages from a book left to right.
|
||||
**Why:** removing the selected page selects and shows the page before it
|
||||
(`references/controls-dataview.md` §Book controls), and showing an unbuilt `LazyPage` while the frame
|
||||
is shown builds it. In any other order the page before the selected one can be one removed next,
|
||||
built only to be destroyed; left to right it is one that stays (unless the selected page is the
|
||||
book's first).
|
||||
```cpp
|
||||
// Right (PluginPages::shutdown): m_order is the tabs' left-to-right order
|
||||
for (const PluginCapabilityId& id : std::vector<PluginCapabilityId>(m_order))
|
||||
remove_page(id);
|
||||
```
|
||||
Cite: `PluginPages::shutdown`, `PluginPages::relayout`, `PluginPages::on_plugin_deregister`.
|
||||
|
||||
## Plater and Sidebar
|
||||
|
||||
|
||||
@@ -184,7 +184,8 @@ class MainFrame : public DPIFrame
|
||||
std::string m_name{ "prepare_layout" };
|
||||
wxSize m_laid_out_size;
|
||||
} m_prepare_layout_prebuild{ *this };
|
||||
// Every LazyPage, in and out of the book; prebuild_pages_when_idle() registers them.
|
||||
// Every built-in LazyPage, in and out of the book; prebuild_pages_when_idle() registers them.
|
||||
// Plugin pages stay out: PluginPages destroys them at runtime.
|
||||
std::vector<LazyBase*> m_lazy_pages;
|
||||
// The latest EVT_LOAD_PRINTER_URL, applied when the web Device view is built.
|
||||
wxString m_printer_url;
|
||||
|
||||
@@ -119,33 +119,13 @@ PrinterWebView::PrinterWebView(wxWindow *parent)
|
||||
wxBoxSizer* topsizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// Create the webview
|
||||
m_browser = WebView::CreateWebView(this, "");
|
||||
if (m_browser == nullptr) {
|
||||
wxLogError("Could not init m_browser");
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
inject_vue_resize_workaround(m_browser);
|
||||
|
||||
auto cookiesPath = boost::filesystem::path(data_dir() + "/cache/cookies.db");
|
||||
auto wv = static_cast<WebKitWebView*>(m_browser->GetNativeBackend());
|
||||
auto wv_ctx = webkit_web_view_get_context(wv);
|
||||
auto cookieManager = webkit_web_context_get_cookie_manager(wv_ctx);
|
||||
webkit_cookie_manager_set_persistent_storage(cookieManager, cookiesPath.c_str(), WEBKIT_COOKIE_PERSISTENT_STORAGE_SQLITE);
|
||||
#endif
|
||||
|
||||
m_browser->Bind(wxEVT_WEBVIEW_ERROR, &PrinterWebView::OnError, this);
|
||||
m_browser->Bind(wxEVT_WEBVIEW_LOADED, &PrinterWebView::OnLoaded, this);
|
||||
m_browser->Bind(wxEVT_WEBVIEW_NEWWINDOW, &PrinterWebView::OnNewWindow, this);
|
||||
m_browser->Bind(wxEVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED, &PrinterWebView::OnScriptMessage, this);
|
||||
create_browser();
|
||||
m_reset_on_show = WebView::NeedsRecreateOnShow();
|
||||
|
||||
SetSizer(topsizer);
|
||||
|
||||
topsizer->Add(m_browser, wxSizerFlags().Expand().Proportion(1));
|
||||
|
||||
update_mode();
|
||||
|
||||
// Log backend information
|
||||
/* m_browser->GetUserAgent() may lead crash
|
||||
if (wxGetApp().get_mode() == comDevelop) {
|
||||
@@ -177,12 +157,44 @@ PrinterWebView::~PrinterWebView()
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " End";
|
||||
}
|
||||
|
||||
void PrinterWebView::create_browser()
|
||||
{
|
||||
m_browser = WebView::CreateWebView(this, "");
|
||||
|
||||
#ifdef __linux__
|
||||
inject_vue_resize_workaround(m_browser);
|
||||
|
||||
auto cookiesPath = boost::filesystem::path(data_dir() + "/cache/cookies.db");
|
||||
auto wv = static_cast<WebKitWebView*>(m_browser->GetNativeBackend());
|
||||
auto wv_ctx = webkit_web_view_get_context(wv);
|
||||
auto cookieManager = webkit_web_context_get_cookie_manager(wv_ctx);
|
||||
webkit_cookie_manager_set_persistent_storage(cookieManager, cookiesPath.c_str(), WEBKIT_COOKIE_PERSISTENT_STORAGE_SQLITE);
|
||||
#endif
|
||||
|
||||
m_browser->Bind(wxEVT_WEBVIEW_ERROR, &PrinterWebView::OnError, this);
|
||||
m_browser->Bind(wxEVT_WEBVIEW_LOADED, &PrinterWebView::OnLoaded, this);
|
||||
m_browser->Bind(wxEVT_WEBVIEW_NEWWINDOW, &PrinterWebView::OnNewWindow, this);
|
||||
m_browser->Bind(wxEVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED, &PrinterWebView::OnScriptMessage, this);
|
||||
update_mode();
|
||||
}
|
||||
|
||||
void PrinterWebView::reset_browser()
|
||||
{
|
||||
m_browser->Destroy(); // also removes it from the sizer
|
||||
create_browser();
|
||||
GetSizer()->Add(m_browser, wxSizerFlags().Expand().Proportion(1));
|
||||
Layout();
|
||||
|
||||
// OnLoaded may have cleared m_url_deferred already, so requeue the last url for Show().
|
||||
m_apikey_sent = false;
|
||||
m_url_deferred = m_url;
|
||||
}
|
||||
|
||||
void PrinterWebView::load_url(wxString& url, wxString apikey)
|
||||
{
|
||||
// this->Show();
|
||||
// this->Raise();
|
||||
if (m_browser == nullptr)
|
||||
return;
|
||||
m_url = url;
|
||||
m_apikey = apikey;
|
||||
m_apikey_sent = false;
|
||||
m_handler = create_printer_webview_handler(*this);
|
||||
@@ -200,6 +212,8 @@ void PrinterWebView::load_url(wxString& url, wxString apikey)
|
||||
|
||||
bool PrinterWebView::Show(bool show)
|
||||
{
|
||||
if (show && std::exchange(m_reset_on_show, false))
|
||||
reset_browser();
|
||||
if (show && !m_url_deferred.empty()) {
|
||||
m_browser->LoadURL(m_url_deferred);
|
||||
//ORCA: m_url_deferred will be cleared on load success
|
||||
|
||||
@@ -58,13 +58,18 @@ private:
|
||||
friend class PrinterWebViewHandler;
|
||||
|
||||
void SendAPIKey();
|
||||
void create_browser();
|
||||
void reset_browser();
|
||||
|
||||
wxWebView* m_browser;
|
||||
long m_zoomFactor;
|
||||
wxString m_apikey;
|
||||
bool m_apikey_sent;
|
||||
// Last url passed to load_url(), reloaded after reset_browser().
|
||||
wxString m_url;
|
||||
wxString m_url_deferred;
|
||||
std::unique_ptr<PrinterWebViewHandler> m_handler;
|
||||
bool m_reset_on_show{false};
|
||||
|
||||
// DECLARE_EVENT_TABLE()
|
||||
};
|
||||
|
||||
+34
-13
@@ -80,16 +80,10 @@ ProjectPanel::ProjectPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos,
|
||||
|
||||
wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
m_browser = WebView::CreateWebView(this, m_project_home_url);
|
||||
if (m_browser == nullptr) {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("load web view of project page failed");
|
||||
return;
|
||||
}
|
||||
create_browser();
|
||||
m_reset_on_show = WebView::NeedsRecreateOnShow();
|
||||
//m_browser->Hide();
|
||||
main_sizer->Add(m_browser, wxSizerFlags().Expand().Proportion(1));
|
||||
m_browser->Bind(wxEVT_WEBVIEW_NAVIGATED, &ProjectPanel::on_navigated, this);
|
||||
m_browser->Bind(wxEVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED, &ProjectPanel::OnScriptMessage, this, m_browser->GetId());
|
||||
Bind(wxEVT_WEBVIEW_NAVIGATING, &ProjectPanel::onWebNavigating, this, m_browser->GetId());
|
||||
|
||||
Bind(EVT_PROJECT_RELOAD, &ProjectPanel::on_reload, this);
|
||||
|
||||
@@ -117,6 +111,23 @@ void ProjectPanel::shutdown()
|
||||
m_reload_task.reset();
|
||||
}
|
||||
|
||||
void ProjectPanel::create_browser()
|
||||
{
|
||||
m_browser = WebView::CreateWebView(this, m_project_home_url);
|
||||
m_browser->Bind(wxEVT_WEBVIEW_NAVIGATED, &ProjectPanel::on_navigated, this);
|
||||
m_browser->Bind(wxEVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED, &ProjectPanel::OnScriptMessage, this, m_browser->GetId());
|
||||
m_browser->Bind(wxEVT_WEBVIEW_NAVIGATING, &ProjectPanel::onWebNavigating, this);
|
||||
}
|
||||
|
||||
void ProjectPanel::reset_browser()
|
||||
{
|
||||
m_browser->Destroy(); // also removes it from the sizer
|
||||
create_browser();
|
||||
GetSizer()->Insert(0, m_browser, wxSizerFlags().Expand().Proportion(1));
|
||||
Layout();
|
||||
m_web_init_completed.store(false, std::memory_order_release);
|
||||
}
|
||||
|
||||
// Helper to convert newlines to <br>
|
||||
static std::string convert_newlines_to_br(const std::string& text) {
|
||||
std::string result = text;
|
||||
@@ -281,12 +292,13 @@ void ProjectPanel::on_reload(wxCommandEvent& evt)
|
||||
|
||||
wxString strJS = wxString::Format("HandleStudio(%s)", m_Res.dump(-1, ' ', false, json::error_handler_t::ignore));
|
||||
|
||||
if (m_web_init_completed.load(std::memory_order_acquire) &&
|
||||
!cancel_token->load(std::memory_order_acquire) && wxTheApp != nullptr && !wxGetApp().is_closing()) {
|
||||
if (!cancel_token->load(std::memory_order_acquire) && wxTheApp != nullptr && !wxGetApp().is_closing()) {
|
||||
wxGetApp().CallAfter([this, cancel_token, strJS] {
|
||||
if (cancel_token->load(std::memory_order_acquire) || wxTheApp == nullptr || wxGetApp().is_closing())
|
||||
return;
|
||||
RunScript(strJS.ToStdString());
|
||||
m_info_script = strJS.ToStdString();
|
||||
if (m_web_init_completed.load(std::memory_order_acquire))
|
||||
RunScript(m_info_script);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -327,6 +339,11 @@ void ProjectPanel::OnScriptMessage(wxWebViewEvent& evt)
|
||||
}
|
||||
else if (strCmd == "request_3mf_info") {
|
||||
m_web_init_completed.store(true, std::memory_order_release);
|
||||
// Replay the stored info after each page load.
|
||||
CallAfter([this] {
|
||||
if (!m_info_script.empty())
|
||||
RunScript(m_info_script);
|
||||
});
|
||||
}
|
||||
else if (strCmd == "edit_project_info") {
|
||||
show_info_editor(true);
|
||||
@@ -383,6 +400,8 @@ void ProjectPanel::clear_model_info()
|
||||
wxGetApp().CallAfter([this, cancel_token, strJS] {
|
||||
if (cancel_token->load(std::memory_order_acquire) || wxTheApp == nullptr || wxGetApp().is_closing())
|
||||
return;
|
||||
// Runs after any store queued by an earlier reload pass, so stale info is never replayed.
|
||||
m_info_script.clear();
|
||||
RunScript(strJS.ToStdString());
|
||||
});
|
||||
}
|
||||
@@ -523,10 +542,12 @@ void ProjectPanel::RunScript(std::string content)
|
||||
WebView::RunScript(m_browser, content);
|
||||
}
|
||||
|
||||
bool ProjectPanel::Show(bool show)
|
||||
bool ProjectPanel::Show(bool show)
|
||||
{
|
||||
if (show && std::exchange(m_reset_on_show, false))
|
||||
reset_browser();
|
||||
if (show) update_model_data();
|
||||
return wxPanel::Show(show);
|
||||
return wxPanel::Show(show);
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
|
||||
@@ -80,9 +80,14 @@ private:
|
||||
AuxiliaryPanel* m_auxiliary{nullptr};
|
||||
wxString m_project_home_url;
|
||||
wxString m_root_dir;
|
||||
// Last show_3mf_info script, also sent whenever the page asks for it.
|
||||
std::string m_info_script;
|
||||
bool m_reset_on_show{false};
|
||||
static inline std::atomic<int> m_sequence_id{8000};
|
||||
|
||||
void show_info_editor(bool show);
|
||||
void create_browser();
|
||||
void reset_browser();
|
||||
|
||||
|
||||
public:
|
||||
|
||||
@@ -62,10 +62,10 @@ namespace GUI {
|
||||
WebViewPanel::WebViewPanel(wxWindow *parent)
|
||||
: wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize)
|
||||
{
|
||||
wxString url = file_url_from_path(boost::filesystem::path(resources_dir()) / "web/homepage/index.html");
|
||||
m_home_url = file_url_from_path(boost::filesystem::path(resources_dir()) / "web/homepage/index.html");
|
||||
wxString strlang = wxGetApp().current_language_code_safe();
|
||||
if (strlang != "")
|
||||
url += "?lang=" + strlang;
|
||||
m_home_url += "?lang=" + strlang;
|
||||
|
||||
wxBoxSizer* topsizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
@@ -109,12 +109,8 @@ WebViewPanel::WebViewPanel(wxWindow *parent)
|
||||
m_info = new wxInfoBar(this);
|
||||
topsizer->Add(m_info, wxSizerFlags().Expand());
|
||||
// Create the webview
|
||||
m_browser = WebView::CreateWebView(this, url);
|
||||
if (m_browser == nullptr) {
|
||||
wxLogError("Could not init m_browser");
|
||||
return;
|
||||
}
|
||||
m_browser->Hide();
|
||||
create_browser();
|
||||
m_reset_on_show = WebView::NeedsRecreateOnShow();
|
||||
SetSizer(topsizer);
|
||||
|
||||
topsizer->Add(m_browser, wxSizerFlags().Expand().Proportion(1));
|
||||
@@ -264,6 +260,27 @@ WebViewPanel::~WebViewPanel()
|
||||
}
|
||||
|
||||
|
||||
void WebViewPanel::create_browser()
|
||||
{
|
||||
m_browser = WebView::CreateWebView(this, m_home_url);
|
||||
m_browser->Hide();
|
||||
}
|
||||
|
||||
void WebViewPanel::reset_browser()
|
||||
{
|
||||
m_browser->Destroy(); // also removes it from the sizer
|
||||
create_browser();
|
||||
GetSizer()->Add(m_browser, wxSizerFlags().Expand().Proportion(1));
|
||||
Layout();
|
||||
}
|
||||
|
||||
bool WebViewPanel::Show(bool show)
|
||||
{
|
||||
if (show && std::exchange(m_reset_on_show, false))
|
||||
reset_browser();
|
||||
return wxPanel::Show(show);
|
||||
}
|
||||
|
||||
void WebViewPanel::load_url(wxString& url)
|
||||
{
|
||||
this->Show();
|
||||
|
||||
@@ -113,9 +113,15 @@ public:
|
||||
int get_model_mall_detail_url(std::string *url, std::string id);
|
||||
|
||||
void update_mode();
|
||||
|
||||
bool Show(bool show = true) override;
|
||||
private:
|
||||
void create_browser();
|
||||
void reset_browser();
|
||||
|
||||
wxWebView* m_browser;
|
||||
wxString m_home_url;
|
||||
bool m_reset_on_show{false};
|
||||
wxButton * m_button_stop;
|
||||
wxTextCtrl *m_url;
|
||||
#if !BBL_RELEASE_TO_PUBLIC
|
||||
|
||||
@@ -381,6 +381,17 @@ void WebView::MarkScriptMessageHandlerAdded(wxWebView * webView)
|
||||
if (WebViewRef *ref = webview_ref(webView))
|
||||
ref->m_script_handler_added = true;
|
||||
}
|
||||
|
||||
bool WebView::NeedsRecreateOnShow()
|
||||
{
|
||||
const bool recreating = Slic3r::GUI::wxGetApp().is_recreating_gui();
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": is_recreating_gui = " << recreating;
|
||||
#ifdef __WIN32__
|
||||
return recreating;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
#if wxUSE_WEBVIEW_EDGE
|
||||
bool WebView::CheckWebViewRuntime()
|
||||
{
|
||||
|
||||
@@ -23,6 +23,10 @@ public:
|
||||
// Marks "wx" as registered so CreateWebView's deferred add skips the duplicate.
|
||||
static void MarkScriptMessageHandlerAdded(wxWebView * webView);
|
||||
|
||||
// On Windows, a WebView2 backend created during a GUI rebuild (language switch) can come up
|
||||
// ignoring every navigation. A panel that gets true here recreates its view on first Show().
|
||||
static bool NeedsRecreateOnShow();
|
||||
|
||||
static void RecreateAll();
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "PluginPages.hpp"
|
||||
|
||||
#include "libslic3r/AppConfig.hpp"
|
||||
#include "slic3r/GUI/GUI.hpp"
|
||||
#include "slic3r/GUI/Notebook.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/Widgets/Button.hpp"
|
||||
@@ -183,8 +184,10 @@ void PluginPages::initialize(Notebook* parent)
|
||||
|
||||
void PluginPages::shutdown()
|
||||
{
|
||||
while (!m_pages.empty())
|
||||
remove_page(m_pages.begin()->first);
|
||||
// Removing the selected tab selects the tab to its left. In tab order that is a built-in tab,
|
||||
// never an unbuilt plugin page that is removed next and would be built only to be destroyed.
|
||||
for (const PluginCapabilityId& id : std::vector<PluginCapabilityId>(m_order))
|
||||
remove_page(id);
|
||||
m_parent = nullptr;
|
||||
}
|
||||
|
||||
@@ -225,8 +228,7 @@ bool PluginPages::create_page(const PluginCapabilityId& id)
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " Failed to get icon for plugin " << id.plugin_key;
|
||||
}
|
||||
|
||||
auto* page = new PluginPage(m_parent, std::move(capability));
|
||||
|
||||
wxBitmap bitmap;
|
||||
if (!icon.empty()) {
|
||||
try {
|
||||
boost::filesystem::path icon_path(icon);
|
||||
@@ -234,7 +236,7 @@ bool PluginPages::create_page(const PluginCapabilityId& id)
|
||||
if (extension == ".svg" || extension == ".png")
|
||||
icon_path.replace_extension();
|
||||
|
||||
page->set_icon(create_scaled_bitmap(icon_path.string(), m_parent, 20));
|
||||
bitmap = create_scaled_bitmap(icon_path.string(), m_parent, 20);
|
||||
} catch (const std::exception& error) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " Failed to load icon for plugin " << id.plugin_key << ": " << error.what();
|
||||
} catch (...) {
|
||||
@@ -242,7 +244,15 @@ bool PluginPages::create_page(const PluginCapabilityId& id)
|
||||
}
|
||||
}
|
||||
|
||||
m_pages.emplace(id, page);
|
||||
// Built the first time its tab is shown, so a language switch never creates the page's browser
|
||||
// while the main window is being rebuilt. Never prebuilt: the idle queue cannot drop a page
|
||||
// that remove_page() destroys.
|
||||
auto* page = new GUI::LazyPage<PluginPage>(m_parent, GUI::into_u8(page_tab_id(id)), -1,
|
||||
[capability = std::move(capability)](wxWindow* parent) {
|
||||
return new PluginPage(parent, capability);
|
||||
});
|
||||
|
||||
m_pages.emplace(id, Page{page, bitmap});
|
||||
m_order.push_back(id);
|
||||
return true;
|
||||
}
|
||||
@@ -271,16 +281,10 @@ void PluginPages::on_plugin_register(const std::string& plugin_key)
|
||||
|
||||
void PluginPages::on_plugin_deregister(const std::string& plugin_key)
|
||||
{
|
||||
for (auto it = m_pages.begin(); it != m_pages.end();) {
|
||||
if (it->first.plugin_key != plugin_key) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
const PluginCapabilityId id = it->first;
|
||||
++it;
|
||||
remove_page(id);
|
||||
}
|
||||
// In tab order, as in shutdown().
|
||||
for (const PluginCapabilityId& id : std::vector<PluginCapabilityId>(m_order))
|
||||
if (id.plugin_key == plugin_key)
|
||||
remove_page(id);
|
||||
}
|
||||
|
||||
void PluginPages::remove_page(const PluginCapabilityId& id)
|
||||
@@ -289,8 +293,10 @@ void PluginPages::remove_page(const PluginCapabilityId& id)
|
||||
if (it == m_pages.end())
|
||||
return;
|
||||
|
||||
PluginPage* page = it->second;
|
||||
page->detach_capability();
|
||||
GUI::LazyPage<PluginPage>* page = it->second.page;
|
||||
// Only a built page has installed a message sender on the capability.
|
||||
if (PluginPage* built = page->get())
|
||||
built->detach_capability();
|
||||
|
||||
m_pages.erase(it);
|
||||
m_order.erase(std::remove(m_order.begin(), m_order.end(), id), m_order.end());
|
||||
@@ -348,26 +354,27 @@ void PluginPages::relayout()
|
||||
bool up_to_date = page_count >= tab_ids.size();
|
||||
for (size_t i = 0; up_to_date && i < tab_ids.size(); ++i)
|
||||
up_to_date = m_parent->GetPageName(page_count - tab_ids.size() + i) == page_tab_id(tab_ids[i]);
|
||||
for (const auto& [id, page] : m_pages) {
|
||||
for (const auto& [id, entry] : m_pages) {
|
||||
if (!up_to_date)
|
||||
break;
|
||||
const bool wanted = std::find(tab_ids.begin(), tab_ids.end(), id) != tab_ids.end();
|
||||
up_to_date = (m_parent->FindPage(page) != wxNOT_FOUND) == wanted;
|
||||
up_to_date = entry.page->in_book() == wanted;
|
||||
}
|
||||
|
||||
if (!up_to_date) {
|
||||
const wxString id_to_reselect = m_parent->GetSelectedPageName();
|
||||
|
||||
for (const auto& [id, page] : m_pages) {
|
||||
const int idx = m_parent->FindPage(page);
|
||||
// In tab order, as in shutdown().
|
||||
for (const auto& id : m_order) {
|
||||
const int idx = m_parent->FindPage(m_pages.at(id).page);
|
||||
if (idx != wxNOT_FOUND)
|
||||
m_parent->RemovePage(idx);
|
||||
}
|
||||
|
||||
for (const auto& id : tab_ids) {
|
||||
PluginPage* page = m_pages.at(id);
|
||||
m_parent->InsertPage(m_parent->GetPageCount(), page_tab_id(id), page, wxString::FromUTF8(id.name), "",
|
||||
false, page->icon());
|
||||
const Page& entry = m_pages.at(id);
|
||||
m_parent->InsertPage(m_parent->GetPageCount(), page_tab_id(id), entry.page, wxString::FromUTF8(id.name), "",
|
||||
false, entry.icon);
|
||||
}
|
||||
|
||||
if (!id_to_reselect.empty())
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <slic3r/GUI/LazyPage.hpp>
|
||||
#include <slic3r/GUI/WebPanel.hpp>
|
||||
#include <slic3r/plugin/PythonPluginInterface.hpp>
|
||||
#include <slic3r/plugin/pluginTypes/pages/PagesPluginCapability.hpp>
|
||||
@@ -29,8 +30,6 @@ public:
|
||||
|
||||
void detach_capability();
|
||||
void push_message(const std::string& message);
|
||||
void set_icon(const wxBitmap& icon) { m_icon = icon; }
|
||||
const wxBitmap& icon() const { return m_icon; }
|
||||
|
||||
protected:
|
||||
std::optional<std::string> page_html() override;
|
||||
@@ -41,7 +40,6 @@ private:
|
||||
|
||||
std::shared_ptr<PagesPluginCapability> m_cap;
|
||||
std::shared_ptr<std::atomic<PluginPage*>> m_lifetime;
|
||||
wxBitmap m_icon;
|
||||
};
|
||||
|
||||
class PluginPages
|
||||
@@ -73,7 +71,13 @@ private:
|
||||
void show_overflow_menu();
|
||||
static wxString page_tab_id(const PluginCapabilityId& id);
|
||||
|
||||
std::map<PluginCapabilityId, PluginPage*> m_pages;
|
||||
struct Page
|
||||
{
|
||||
GUI::LazyPage<PluginPage>* page{nullptr};
|
||||
wxBitmap icon;
|
||||
};
|
||||
|
||||
std::map<PluginCapabilityId, Page> m_pages;
|
||||
std::vector<PluginCapabilityId> m_order;
|
||||
Notebook* m_parent{nullptr};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user