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:
|
Contract:
|
||||||
- A page must be created with the book as its parent and added once; the book owns and deletes it
|
- 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`).
|
(`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
|
- `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`).
|
`event.GetSelection()` (`interface/wx/bookctrl.h:160-166`).
|
||||||
- `wxSimplebook` has no UI; switch with `ChangeSelection()`. `SetSelection()` sends PAGE_CHANGING/CHANGED
|
- `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
|
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).
|
not prebuilt (its `LazyPage::in_book()` is false).
|
||||||
- Plugin pages are appended by `PluginPages::initialize` (`plugin/host/PluginPages.hpp`) with
|
- 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
|
### Preset tabs
|
||||||
|
|
||||||
@@ -529,6 +531,32 @@ the main frame does nothing to a panel after creating it.
|
|||||||
m_idle.add(m_diff_dialog);
|
m_idle.add(m_diff_dialog);
|
||||||
```
|
```
|
||||||
Cite: `IdleScheduler::tick`, `docs/HLSD/deferred-page-construction.md`.
|
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
|
## Plater and Sidebar
|
||||||
|
|
||||||
|
|||||||
@@ -184,7 +184,8 @@ class MainFrame : public DPIFrame
|
|||||||
std::string m_name{ "prepare_layout" };
|
std::string m_name{ "prepare_layout" };
|
||||||
wxSize m_laid_out_size;
|
wxSize m_laid_out_size;
|
||||||
} m_prepare_layout_prebuild{ *this };
|
} 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;
|
std::vector<LazyBase*> m_lazy_pages;
|
||||||
// The latest EVT_LOAD_PRINTER_URL, applied when the web Device view is built.
|
// The latest EVT_LOAD_PRINTER_URL, applied when the web Device view is built.
|
||||||
wxString m_printer_url;
|
wxString m_printer_url;
|
||||||
|
|||||||
@@ -119,33 +119,13 @@ PrinterWebView::PrinterWebView(wxWindow *parent)
|
|||||||
wxBoxSizer* topsizer = new wxBoxSizer(wxVERTICAL);
|
wxBoxSizer* topsizer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
|
||||||
// Create the webview
|
// Create the webview
|
||||||
m_browser = WebView::CreateWebView(this, "");
|
create_browser();
|
||||||
if (m_browser == nullptr) {
|
m_reset_on_show = WebView::NeedsRecreateOnShow();
|
||||||
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);
|
|
||||||
|
|
||||||
SetSizer(topsizer);
|
SetSizer(topsizer);
|
||||||
|
|
||||||
topsizer->Add(m_browser, wxSizerFlags().Expand().Proportion(1));
|
topsizer->Add(m_browser, wxSizerFlags().Expand().Proportion(1));
|
||||||
|
|
||||||
update_mode();
|
|
||||||
|
|
||||||
// Log backend information
|
// Log backend information
|
||||||
/* m_browser->GetUserAgent() may lead crash
|
/* m_browser->GetUserAgent() may lead crash
|
||||||
if (wxGetApp().get_mode() == comDevelop) {
|
if (wxGetApp().get_mode() == comDevelop) {
|
||||||
@@ -177,12 +157,44 @@ PrinterWebView::~PrinterWebView()
|
|||||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " End";
|
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)
|
void PrinterWebView::load_url(wxString& url, wxString apikey)
|
||||||
{
|
{
|
||||||
// this->Show();
|
// this->Show();
|
||||||
// this->Raise();
|
// this->Raise();
|
||||||
if (m_browser == nullptr)
|
m_url = url;
|
||||||
return;
|
|
||||||
m_apikey = apikey;
|
m_apikey = apikey;
|
||||||
m_apikey_sent = false;
|
m_apikey_sent = false;
|
||||||
m_handler = create_printer_webview_handler(*this);
|
m_handler = create_printer_webview_handler(*this);
|
||||||
@@ -200,6 +212,8 @@ void PrinterWebView::load_url(wxString& url, wxString apikey)
|
|||||||
|
|
||||||
bool PrinterWebView::Show(bool show)
|
bool PrinterWebView::Show(bool show)
|
||||||
{
|
{
|
||||||
|
if (show && std::exchange(m_reset_on_show, false))
|
||||||
|
reset_browser();
|
||||||
if (show && !m_url_deferred.empty()) {
|
if (show && !m_url_deferred.empty()) {
|
||||||
m_browser->LoadURL(m_url_deferred);
|
m_browser->LoadURL(m_url_deferred);
|
||||||
//ORCA: m_url_deferred will be cleared on load success
|
//ORCA: m_url_deferred will be cleared on load success
|
||||||
|
|||||||
@@ -58,13 +58,18 @@ private:
|
|||||||
friend class PrinterWebViewHandler;
|
friend class PrinterWebViewHandler;
|
||||||
|
|
||||||
void SendAPIKey();
|
void SendAPIKey();
|
||||||
|
void create_browser();
|
||||||
|
void reset_browser();
|
||||||
|
|
||||||
wxWebView* m_browser;
|
wxWebView* m_browser;
|
||||||
long m_zoomFactor;
|
long m_zoomFactor;
|
||||||
wxString m_apikey;
|
wxString m_apikey;
|
||||||
bool m_apikey_sent;
|
bool m_apikey_sent;
|
||||||
|
// Last url passed to load_url(), reloaded after reset_browser().
|
||||||
|
wxString m_url;
|
||||||
wxString m_url_deferred;
|
wxString m_url_deferred;
|
||||||
std::unique_ptr<PrinterWebViewHandler> m_handler;
|
std::unique_ptr<PrinterWebViewHandler> m_handler;
|
||||||
|
bool m_reset_on_show{false};
|
||||||
|
|
||||||
// DECLARE_EVENT_TABLE()
|
// DECLARE_EVENT_TABLE()
|
||||||
};
|
};
|
||||||
|
|||||||
+34
-13
@@ -80,16 +80,10 @@ ProjectPanel::ProjectPanel(wxWindow *parent, wxWindowID id, const wxPoint &pos,
|
|||||||
|
|
||||||
wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
|
wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
|
||||||
m_browser = WebView::CreateWebView(this, m_project_home_url);
|
create_browser();
|
||||||
if (m_browser == nullptr) {
|
m_reset_on_show = WebView::NeedsRecreateOnShow();
|
||||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("load web view of project page failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
//m_browser->Hide();
|
//m_browser->Hide();
|
||||||
main_sizer->Add(m_browser, wxSizerFlags().Expand().Proportion(1));
|
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);
|
Bind(EVT_PROJECT_RELOAD, &ProjectPanel::on_reload, this);
|
||||||
|
|
||||||
@@ -117,6 +111,23 @@ void ProjectPanel::shutdown()
|
|||||||
m_reload_task.reset();
|
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>
|
// Helper to convert newlines to <br>
|
||||||
static std::string convert_newlines_to_br(const std::string& text) {
|
static std::string convert_newlines_to_br(const std::string& text) {
|
||||||
std::string result = 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));
|
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) &&
|
if (!cancel_token->load(std::memory_order_acquire) && wxTheApp != nullptr && !wxGetApp().is_closing()) {
|
||||||
!cancel_token->load(std::memory_order_acquire) && wxTheApp != nullptr && !wxGetApp().is_closing()) {
|
|
||||||
wxGetApp().CallAfter([this, cancel_token, strJS] {
|
wxGetApp().CallAfter([this, cancel_token, strJS] {
|
||||||
if (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())
|
||||||
return;
|
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") {
|
else if (strCmd == "request_3mf_info") {
|
||||||
m_web_init_completed.store(true, std::memory_order_release);
|
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") {
|
else if (strCmd == "edit_project_info") {
|
||||||
show_info_editor(true);
|
show_info_editor(true);
|
||||||
@@ -383,6 +400,8 @@ void ProjectPanel::clear_model_info()
|
|||||||
wxGetApp().CallAfter([this, cancel_token, strJS] {
|
wxGetApp().CallAfter([this, cancel_token, strJS] {
|
||||||
if (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())
|
||||||
return;
|
return;
|
||||||
|
// Runs after any store queued by an earlier reload pass, so stale info is never replayed.
|
||||||
|
m_info_script.clear();
|
||||||
RunScript(strJS.ToStdString());
|
RunScript(strJS.ToStdString());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -523,10 +542,12 @@ void ProjectPanel::RunScript(std::string content)
|
|||||||
WebView::RunScript(m_browser, 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();
|
if (show) update_model_data();
|
||||||
return wxPanel::Show(show);
|
return wxPanel::Show(show);
|
||||||
}
|
}
|
||||||
|
|
||||||
}} // namespace Slic3r::GUI
|
}} // namespace Slic3r::GUI
|
||||||
|
|||||||
@@ -80,9 +80,14 @@ private:
|
|||||||
AuxiliaryPanel* m_auxiliary{nullptr};
|
AuxiliaryPanel* m_auxiliary{nullptr};
|
||||||
wxString m_project_home_url;
|
wxString m_project_home_url;
|
||||||
wxString m_root_dir;
|
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};
|
static inline std::atomic<int> m_sequence_id{8000};
|
||||||
|
|
||||||
void show_info_editor(bool show);
|
void show_info_editor(bool show);
|
||||||
|
void create_browser();
|
||||||
|
void reset_browser();
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -62,10 +62,10 @@ namespace GUI {
|
|||||||
WebViewPanel::WebViewPanel(wxWindow *parent)
|
WebViewPanel::WebViewPanel(wxWindow *parent)
|
||||||
: wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize)
|
: 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();
|
wxString strlang = wxGetApp().current_language_code_safe();
|
||||||
if (strlang != "")
|
if (strlang != "")
|
||||||
url += "?lang=" + strlang;
|
m_home_url += "?lang=" + strlang;
|
||||||
|
|
||||||
wxBoxSizer* topsizer = new wxBoxSizer(wxVERTICAL);
|
wxBoxSizer* topsizer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
|
||||||
@@ -109,12 +109,8 @@ WebViewPanel::WebViewPanel(wxWindow *parent)
|
|||||||
m_info = new wxInfoBar(this);
|
m_info = new wxInfoBar(this);
|
||||||
topsizer->Add(m_info, wxSizerFlags().Expand());
|
topsizer->Add(m_info, wxSizerFlags().Expand());
|
||||||
// Create the webview
|
// Create the webview
|
||||||
m_browser = WebView::CreateWebView(this, url);
|
create_browser();
|
||||||
if (m_browser == nullptr) {
|
m_reset_on_show = WebView::NeedsRecreateOnShow();
|
||||||
wxLogError("Could not init m_browser");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_browser->Hide();
|
|
||||||
SetSizer(topsizer);
|
SetSizer(topsizer);
|
||||||
|
|
||||||
topsizer->Add(m_browser, wxSizerFlags().Expand().Proportion(1));
|
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)
|
void WebViewPanel::load_url(wxString& url)
|
||||||
{
|
{
|
||||||
this->Show();
|
this->Show();
|
||||||
|
|||||||
@@ -113,9 +113,15 @@ public:
|
|||||||
int get_model_mall_detail_url(std::string *url, std::string id);
|
int get_model_mall_detail_url(std::string *url, std::string id);
|
||||||
|
|
||||||
void update_mode();
|
void update_mode();
|
||||||
|
|
||||||
|
bool Show(bool show = true) override;
|
||||||
private:
|
private:
|
||||||
|
void create_browser();
|
||||||
|
void reset_browser();
|
||||||
|
|
||||||
wxWebView* m_browser;
|
wxWebView* m_browser;
|
||||||
|
wxString m_home_url;
|
||||||
|
bool m_reset_on_show{false};
|
||||||
wxButton * m_button_stop;
|
wxButton * m_button_stop;
|
||||||
wxTextCtrl *m_url;
|
wxTextCtrl *m_url;
|
||||||
#if !BBL_RELEASE_TO_PUBLIC
|
#if !BBL_RELEASE_TO_PUBLIC
|
||||||
|
|||||||
@@ -381,6 +381,17 @@ void WebView::MarkScriptMessageHandlerAdded(wxWebView * webView)
|
|||||||
if (WebViewRef *ref = webview_ref(webView))
|
if (WebViewRef *ref = webview_ref(webView))
|
||||||
ref->m_script_handler_added = true;
|
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
|
#if wxUSE_WEBVIEW_EDGE
|
||||||
bool WebView::CheckWebViewRuntime()
|
bool WebView::CheckWebViewRuntime()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -23,6 +23,10 @@ public:
|
|||||||
// Marks "wx" as registered so CreateWebView's deferred add skips the duplicate.
|
// Marks "wx" as registered so CreateWebView's deferred add skips the duplicate.
|
||||||
static void MarkScriptMessageHandlerAdded(wxWebView * webView);
|
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();
|
static void RecreateAll();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "PluginPages.hpp"
|
#include "PluginPages.hpp"
|
||||||
|
|
||||||
#include "libslic3r/AppConfig.hpp"
|
#include "libslic3r/AppConfig.hpp"
|
||||||
|
#include "slic3r/GUI/GUI.hpp"
|
||||||
#include "slic3r/GUI/Notebook.hpp"
|
#include "slic3r/GUI/Notebook.hpp"
|
||||||
#include "slic3r/GUI/GUI_App.hpp"
|
#include "slic3r/GUI/GUI_App.hpp"
|
||||||
#include "slic3r/GUI/Widgets/Button.hpp"
|
#include "slic3r/GUI/Widgets/Button.hpp"
|
||||||
@@ -183,8 +184,10 @@ void PluginPages::initialize(Notebook* parent)
|
|||||||
|
|
||||||
void PluginPages::shutdown()
|
void PluginPages::shutdown()
|
||||||
{
|
{
|
||||||
while (!m_pages.empty())
|
// Removing the selected tab selects the tab to its left. In tab order that is a built-in tab,
|
||||||
remove_page(m_pages.begin()->first);
|
// 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;
|
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;
|
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()) {
|
if (!icon.empty()) {
|
||||||
try {
|
try {
|
||||||
boost::filesystem::path icon_path(icon);
|
boost::filesystem::path icon_path(icon);
|
||||||
@@ -234,7 +236,7 @@ bool PluginPages::create_page(const PluginCapabilityId& id)
|
|||||||
if (extension == ".svg" || extension == ".png")
|
if (extension == ".svg" || extension == ".png")
|
||||||
icon_path.replace_extension();
|
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) {
|
} catch (const std::exception& error) {
|
||||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " Failed to load icon for plugin " << id.plugin_key << ": " << error.what();
|
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " Failed to load icon for plugin " << id.plugin_key << ": " << error.what();
|
||||||
} catch (...) {
|
} 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);
|
m_order.push_back(id);
|
||||||
return true;
|
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)
|
void PluginPages::on_plugin_deregister(const std::string& plugin_key)
|
||||||
{
|
{
|
||||||
for (auto it = m_pages.begin(); it != m_pages.end();) {
|
// In tab order, as in shutdown().
|
||||||
if (it->first.plugin_key != plugin_key) {
|
for (const PluginCapabilityId& id : std::vector<PluginCapabilityId>(m_order))
|
||||||
++it;
|
if (id.plugin_key == plugin_key)
|
||||||
continue;
|
remove_page(id);
|
||||||
}
|
|
||||||
|
|
||||||
const PluginCapabilityId id = it->first;
|
|
||||||
++it;
|
|
||||||
remove_page(id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PluginPages::remove_page(const PluginCapabilityId& id)
|
void PluginPages::remove_page(const PluginCapabilityId& id)
|
||||||
@@ -289,8 +293,10 @@ void PluginPages::remove_page(const PluginCapabilityId& id)
|
|||||||
if (it == m_pages.end())
|
if (it == m_pages.end())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
PluginPage* page = it->second;
|
GUI::LazyPage<PluginPage>* page = it->second.page;
|
||||||
page->detach_capability();
|
// 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_pages.erase(it);
|
||||||
m_order.erase(std::remove(m_order.begin(), m_order.end(), id), m_order.end());
|
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();
|
bool up_to_date = page_count >= tab_ids.size();
|
||||||
for (size_t i = 0; up_to_date && i < tab_ids.size(); ++i)
|
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]);
|
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)
|
if (!up_to_date)
|
||||||
break;
|
break;
|
||||||
const bool wanted = std::find(tab_ids.begin(), tab_ids.end(), id) != tab_ids.end();
|
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) {
|
if (!up_to_date) {
|
||||||
const wxString id_to_reselect = m_parent->GetSelectedPageName();
|
const wxString id_to_reselect = m_parent->GetSelectedPageName();
|
||||||
|
|
||||||
for (const auto& [id, page] : m_pages) {
|
// In tab order, as in shutdown().
|
||||||
const int idx = m_parent->FindPage(page);
|
for (const auto& id : m_order) {
|
||||||
|
const int idx = m_parent->FindPage(m_pages.at(id).page);
|
||||||
if (idx != wxNOT_FOUND)
|
if (idx != wxNOT_FOUND)
|
||||||
m_parent->RemovePage(idx);
|
m_parent->RemovePage(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& id : tab_ids) {
|
for (const auto& id : tab_ids) {
|
||||||
PluginPage* page = m_pages.at(id);
|
const Page& entry = m_pages.at(id);
|
||||||
m_parent->InsertPage(m_parent->GetPageCount(), page_tab_id(id), page, wxString::FromUTF8(id.name), "",
|
m_parent->InsertPage(m_parent->GetPageCount(), page_tab_id(id), entry.page, wxString::FromUTF8(id.name), "",
|
||||||
false, page->icon());
|
false, entry.icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!id_to_reselect.empty())
|
if (!id_to_reselect.empty())
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <slic3r/GUI/LazyPage.hpp>
|
||||||
#include <slic3r/GUI/WebPanel.hpp>
|
#include <slic3r/GUI/WebPanel.hpp>
|
||||||
#include <slic3r/plugin/PythonPluginInterface.hpp>
|
#include <slic3r/plugin/PythonPluginInterface.hpp>
|
||||||
#include <slic3r/plugin/pluginTypes/pages/PagesPluginCapability.hpp>
|
#include <slic3r/plugin/pluginTypes/pages/PagesPluginCapability.hpp>
|
||||||
@@ -29,8 +30,6 @@ public:
|
|||||||
|
|
||||||
void detach_capability();
|
void detach_capability();
|
||||||
void push_message(const std::string& message);
|
void push_message(const std::string& message);
|
||||||
void set_icon(const wxBitmap& icon) { m_icon = icon; }
|
|
||||||
const wxBitmap& icon() const { return m_icon; }
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::optional<std::string> page_html() override;
|
std::optional<std::string> page_html() override;
|
||||||
@@ -41,7 +40,6 @@ private:
|
|||||||
|
|
||||||
std::shared_ptr<PagesPluginCapability> m_cap;
|
std::shared_ptr<PagesPluginCapability> m_cap;
|
||||||
std::shared_ptr<std::atomic<PluginPage*>> m_lifetime;
|
std::shared_ptr<std::atomic<PluginPage*>> m_lifetime;
|
||||||
wxBitmap m_icon;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class PluginPages
|
class PluginPages
|
||||||
@@ -73,7 +71,13 @@ private:
|
|||||||
void show_overflow_menu();
|
void show_overflow_menu();
|
||||||
static wxString page_tab_id(const PluginCapabilityId& id);
|
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;
|
std::vector<PluginCapabilityId> m_order;
|
||||||
Notebook* m_parent{nullptr};
|
Notebook* m_parent{nullptr};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user