From a8f35ed484e0b6620646e7f2ddc86a213994002f Mon Sep 17 00:00:00 2001 From: Ian Chua Date: Mon, 12 Oct 2026 18:51:00 +0800 Subject: [PATCH] feat: add orcacloud to homepage --- resources/web/homepage/css/home.css | 14 +++++ resources/web/homepage/index.html | 2 +- resources/web/homepage/js/home.js | 28 +++++++++ src/slic3r/GUI/GUI_App.cpp | 14 +++++ src/slic3r/GUI/WebViewDialog.cpp | 94 ++++++++++++++++++++++++++++- src/slic3r/GUI/WebViewDialog.hpp | 11 ++++ 6 files changed, 159 insertions(+), 4 deletions(-) diff --git a/resources/web/homepage/css/home.css b/resources/web/homepage/css/home.css index 950e330b86..d245285014 100644 --- a/resources/web/homepage/css/home.css +++ b/resources/web/homepage/css/home.css @@ -548,6 +548,20 @@ body overflow-y:auto; } +/*---- Cloud mode: OrcaCloud is a native pane to the right of the app panel, so this page + is reduced to its left nav column (the class is toggled by SetCloudMode in js/home.js). ----*/ +body.cloud-mode #RightBoard +{ + display: none; +} + +body.cloud-mode #LeftBoard +{ + /*---- 262px of column plus its 1px separator is the width the app gives the nav. ----*/ + height: 100%; + overflow-y: auto; +} + #ContentBoard { overflow-y:auto; diff --git a/resources/web/homepage/index.html b/resources/web/homepage/index.html index e75e8302f7..64b85b13ae 100644 --- a/resources/web/homepage/index.html +++ b/resources/web/homepage/index.html @@ -90,7 +90,7 @@
Recent
-
+
OrcaCloud
diff --git a/resources/web/homepage/js/home.js b/resources/web/homepage/js/home.js index 1109b662d9..6d361cf8d1 100644 --- a/resources/web/homepage/js/home.js +++ b/resources/web/homepage/js/home.js @@ -145,8 +145,36 @@ function HandleStudio( pVal ) } } +/*---- OrcaCloud opens as a native pane beside this page, so the shell only switches its + own cloud-mode state and asks the app to show or hide that pane (WebViewPanel::set_cloud_mode). ----*/ +var m_CloudMode=false; + +function SetCloudMode( bOn ) +{ + if( m_CloudMode==bOn ) + return; + + m_CloudMode=bOn; + $("body").toggleClass("cloud-mode", bOn); +} + +function GotoCloud() +{ + $(".BtnItem").removeClass("BtnItemSelected"); + $(".BtnShortcut").addClass("BtnItemSelected"); + SetCloudMode(true); + SendSimpleCommand("homepage_open_cloud"); +} + function GotoMenu( strMenu ) { + /*---- Any board in the right region means leaving the cloud pane. ----*/ + if( m_CloudMode ) + { + SetCloudMode(false); + SendSimpleCommand("homepage_close_cloud"); + } + let MenuList=$(".BtnItem"); let nAll=MenuList.length; diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 636ba985f5..b3b1186281 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -5419,6 +5419,20 @@ std::string GUI_App::handle_web_request(std::string cmd) else if (command_str.compare("homepage_openproject") == 0) { this->request_open_project({}); } + else if (command_str.compare("homepage_open_cloud") == 0) { + // Deferred: set_cloud_mode() may create a second native webview, which must + // not run inside this webview's script-message callback. + CallAfter([]() { + if (WebViewPanel* home = WebViewPanel::if_built()) + home->set_cloud_mode(true); + }); + } + else if (command_str.compare("homepage_close_cloud") == 0) { + CallAfter([]() { + if (WebViewPanel* home = WebViewPanel::if_built()) + home->set_cloud_mode(false); + }); + } else if (command_str.compare("get_recent_projects") == 0) { if (mainframe) { if (WebViewPanel* home = WebViewPanel::if_built()) { diff --git a/src/slic3r/GUI/WebViewDialog.cpp b/src/slic3r/GUI/WebViewDialog.cpp index 08f91f9f16..3e584cd716 100644 --- a/src/slic3r/GUI/WebViewDialog.cpp +++ b/src/slic3r/GUI/WebViewDialog.cpp @@ -55,6 +55,13 @@ namespace GUI { #define LOGIN_INFO_UPDATE_TIMER_ID 10002 +// OrcaCloud is a native pane of its own: the site refuses to be framed +// (x-frame-options: DENY / CSP frame-ancestors 'none'). +static const char *const kCloudUrl = "https://cloud.orcaslicer.com"; +// #LeftBoard in resources/web/homepage/css/home.css is a 262px column plus a 1px right +// border; the homepage keeps that width as the nav column while the cloud pane is open. +static const int kLeftNavWidth = 263; + // The home page's strings (resources/web/homepage), keyed by their data-i18n and T() ids. static json home_page_strings() { @@ -134,7 +141,11 @@ WebViewPanel::WebViewPanel(wxWindow *parent) m_reset_on_show = WebView::NeedsRecreateOnShow(); SetSizer(topsizer); - topsizer->Add(m_browser, wxSizerFlags().Expand().Proportion(1)); + // The homepage shell is the left nav column; when OrcaCloud opens, its page lives in a + // second native pane beside it (the site refuses to be framed). + m_content_sizer = new wxBoxSizer(wxHORIZONTAL); + m_content_sizer->Add(m_browser, wxSizerFlags().Expand().Proportion(1)); + topsizer->Add(m_content_sizer, wxSizerFlags().Expand().Proportion(1)); // Log backend information /* m_browser->GetUserAgent() may lead crash @@ -290,12 +301,86 @@ void WebViewPanel::create_browser() m_browser->Hide(); } +// The cloud page gets its own native view, created on first use and kept alive afterwards +// so its session and scroll position survive hiding. +void WebViewPanel::create_cloud_browser() +{ + m_cloud_browser = WebView::CreateWebView(this, wxEmptyString); + + // Webview events are command events, so they would otherwise propagate up to the + // panel's handlers, which drive the homepage view. Keep the cloud pane's events here. + m_cloud_browser->Bind(wxEVT_WEBVIEW_NAVIGATING, &WebViewPanel::OnCloudWebViewEvent, this); + m_cloud_browser->Bind(wxEVT_WEBVIEW_NAVIGATED, &WebViewPanel::OnCloudWebViewEvent, this); + m_cloud_browser->Bind(wxEVT_WEBVIEW_LOADED, &WebViewPanel::OnCloudWebViewEvent, this); + m_cloud_browser->Bind(wxEVT_WEBVIEW_TITLE_CHANGED, &WebViewPanel::OnCloudWebViewEvent, this); + m_cloud_browser->Bind(wxEVT_WEBVIEW_ERROR, &WebViewPanel::OnCloudWebViewEvent, this); + m_cloud_browser->Bind(wxEVT_WEBVIEW_NEWWINDOW, &WebViewPanel::OnCloudWebViewEvent, this); + m_cloud_browser->Bind(wxEVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED, &WebViewPanel::OnCloudWebViewEvent, this); + + m_cloud_browser->Hide(); + if (m_content_sizer) + m_content_sizer->Add(m_cloud_browser, wxSizerFlags().Expand().Proportion(1)); +} + +// Consumed on the cloud view so it never reaches the homepage handlers; new windows open in +// the system browser, the same way PrinterWebView treats them. +void WebViewPanel::OnCloudWebViewEvent(wxWebViewEvent& evt) +{ + if (evt.GetEventType() == wxEVT_WEBVIEW_NEWWINDOW) { + const wxString url = evt.GetURL(); + if (!url.empty()) + wxLaunchDefaultBrowser(url); + evt.Veto(); + } +} + +void WebViewPanel::apply_cloud_layout(bool cloud_on) +{ + if (!m_content_sizer) + return; + + // Cloud mode pins the homepage to the nav column width; otherwise it fills the panel. + if (m_browser) { + if (wxSizerItem* item = m_content_sizer->GetItem(m_browser)) + item->SetProportion(cloud_on ? 0 : 1); + // kLeftNavWidth is in DIP: the web view's CSS px are DIP, so scale to physical pixels. + m_browser->SetMinSize(cloud_on ? m_browser->FromDIP(wxSize(kLeftNavWidth, -1)) : wxDefaultSize); + } + Layout(); +} + +void WebViewPanel::set_cloud_mode(bool on) +{ + if (on) { + if (!m_cloud_browser) { + create_cloud_browser(); + WebView::LoadUrl(m_cloud_browser, kCloudUrl); + } + m_cloud_browser->Show(); + } else if (m_cloud_browser) { + m_cloud_browser->Hide(); + } + apply_cloud_layout(on && m_cloud_browser != nullptr); +} + void WebViewPanel::reset_browser() { + // Rebuild both views the same way, keeping the cloud pane's open/closed state. + const bool cloud_on = m_cloud_browser && m_cloud_browser->IsShown(); + m_browser->Destroy(); // also removes it from the sizer create_browser(); - GetSizer()->Add(m_browser, wxSizerFlags().Expand().Proportion(1)); - Layout(); + m_content_sizer->Insert(size_t(0), m_browser, wxSizerFlags().Expand().Proportion(1)); + + if (m_cloud_browser) { + m_cloud_browser->Destroy(); + m_cloud_browser = nullptr; + create_cloud_browser(); + WebView::LoadUrl(m_cloud_browser, kCloudUrl); + m_cloud_browser->Show(cloud_on); + } + + apply_cloud_layout(cloud_on); } bool WebViewPanel::Show(bool show) @@ -710,6 +795,9 @@ void WebViewPanel::OnDocumentLoaded(wxWebViewEvent& evt) if (wxGetApp().get_mode() == comDevelop) wxLogMessage("%s", "Document loaded; url='" + evt.GetURL() + "'"); } + // A reloaded homepage loses its cloud-mode class; restore it if the pane is open. + if (m_cloud_browser && m_cloud_browser->IsShown()) + RunScript("SetCloudMode(true)"); UpdateState(); SendCloudProvidersInfo(); } diff --git a/src/slic3r/GUI/WebViewDialog.hpp b/src/slic3r/GUI/WebViewDialog.hpp index 8a4ecbf35d..a67311e0e9 100644 --- a/src/slic3r/GUI/WebViewDialog.hpp +++ b/src/slic3r/GUI/WebViewDialog.hpp @@ -28,6 +28,7 @@ #include "wx/fs_mem.h" #include "wx/stdpaths.h" #include +#include #include #include "wx/textctrl.h" #include @@ -114,12 +115,22 @@ public: void update_mode(); + // Shows/hides the native OrcaCloud pane beside this homepage nav column. + void set_cloud_mode(bool on); + bool Show(bool show = true) override; private: void create_browser(); + void create_cloud_browser(); void reset_browser(); + // Consumes the cloud pane's webview events so they stay off the homepage handlers. + void OnCloudWebViewEvent(wxWebViewEvent& evt); + // Re-applies the content sizer's proportions and min sizes for the cloud state. + void apply_cloud_layout(bool cloud_on); wxWebView* m_browser; + wxWebView* m_cloud_browser{nullptr}; + wxBoxSizer* m_content_sizer{nullptr}; wxString m_home_url; bool m_reset_on_show{false}; wxButton * m_button_stop;