mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-03 13:51:37 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6dbf2502d |
@@ -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