Give speed-dial popup GUI_App ownership

This commit is contained in:
Andrew
2026-07-16 16:09:30 +08:00
parent 13f5c777cc
commit 13575601ff
4 changed files with 206 additions and 191 deletions

View File

@@ -8277,7 +8277,20 @@ void GUI_App::open_terminal_dialog()
m_terminal_dlg->Raise(); m_terminal_dlg->Raise();
} }
void GUI_App::open_speed_dial() { open_speed_dial_popup(); } void GUI_App::open_speed_dial()
{
if (!mainframe)
return;
if (!m_speed_dial_popup) {
m_speed_dial_popup = new SpeedDialWebPopup(mainframe);
m_speed_dial_popup->Bind(wxEVT_DESTROY, [this](wxWindowDestroyEvent& event) {
if (event.GetEventObject() == m_speed_dial_popup)
m_speed_dial_popup = nullptr;
event.Skip();
});
}
m_speed_dial_popup->request_show();
}
void GUI_App::open_exportpresetbundledialog(size_t open_on_tab, const std::string& highlight_option) void GUI_App::open_exportpresetbundledialog(size_t open_on_tab, const std::string& highlight_option)
{ {

View File

@@ -87,6 +87,7 @@ class ModelMallDialog;
class PingCodeBindDialog; class PingCodeBindDialog;
class NetworkErrorDialog; class NetworkErrorDialog;
class PluginsDialog; class PluginsDialog;
class SpeedDialWebPopup;
class TerminalDialog; class TerminalDialog;
@@ -554,6 +555,7 @@ public:
PresetBundleDialog* m_preset_bundle_dlg{nullptr}; PresetBundleDialog* m_preset_bundle_dlg{nullptr};
PluginsDialog* m_plugins_dlg{nullptr}; PluginsDialog* m_plugins_dlg{nullptr};
SpeedDialWebPopup* m_speed_dial_popup{nullptr};
TerminalDialog* m_terminal_dlg{nullptr}; TerminalDialog* m_terminal_dlg{nullptr};
ActionRegistry m_action_registry; ActionRegistry m_action_registry;

View File

@@ -130,12 +130,11 @@ if(document.documentElement)
})();)JS"; })();)JS";
} }
class SpeedDialWebPopup : public ::PopupWindow }
{
public: SpeedDialWebPopup::SpeedDialWebPopup(wxWindow* parent)
explicit SpeedDialWebPopup(wxWindow* parent)
: ::PopupWindow(parent, wxBORDER_NONE | wxPU_CONTAINS_CONTROLS) : ::PopupWindow(parent, wxBORDER_NONE | wxPU_CONTAINS_CONTROLS)
{ {
SetBackgroundColour(bg_color()); SetBackgroundColour(bg_color());
auto* sizer = new wxBoxSizer(wxVERTICAL); auto* sizer = new wxBoxSizer(wxVERTICAL);
@@ -153,10 +152,10 @@ public:
SetClientSize(FromDIP(wxSize(kPopupWidth, kPopupMaxHeight))); // DIP -> physical (see resize_to_content) SetClientSize(FromDIP(wxSize(kPopupWidth, kPopupMaxHeight))); // DIP -> physical (see resize_to_content)
if (m_browser) if (m_browser)
WebView::LoadUrl(m_browser, dialog_url()); WebView::LoadUrl(m_browser, dialog_url());
} }
void request_show() void SpeedDialWebPopup::request_show()
{ {
if (IsShown()) { if (IsShown()) {
focus_browser(); focus_browser();
return; return;
@@ -166,19 +165,16 @@ public:
send_actions(); send_actions();
else if (!m_browser) else if (!m_browser)
show_ready(); show_ready();
} }
void focus_browser() void SpeedDialWebPopup::focus_browser()
{ {
if (m_browser) if (m_browser)
m_browser->SetFocus(); m_browser->SetFocus();
} }
private: void SpeedDialWebPopup::on_script_message(wxWebViewEvent& event)
void OnDismiss() override {} {
void on_script_message(wxWebViewEvent& event)
{
nlohmann::json p = nlohmann::json::parse(event.GetString().utf8_string(), nullptr, false); nlohmann::json p = nlohmann::json::parse(event.GetString().utf8_string(), nullptr, false);
if (!p.is_object()) if (!p.is_object())
return; return;
@@ -197,21 +193,21 @@ private:
else if (cmd == "run_action") run_action(p.value("id", ""), p.value("title", "")); else if (cmd == "run_action") run_action(p.value("id", ""), p.value("title", ""));
else if (cmd == "resize") resize_to_content(json_int_or(p, "height", 0)); else if (cmd == "resize") resize_to_content(json_int_or(p, "height", 0));
else if (cmd == "close_page") Dismiss(); else if (cmd == "close_page") Dismiss();
} }
void on_webview_recreated(wxCommandEvent&) void SpeedDialWebPopup::on_webview_recreated(wxCommandEvent&)
{ {
apply_host_theme(); apply_host_theme();
} }
void apply_host_theme() void SpeedDialWebPopup::apply_host_theme()
{ {
if (m_browser) if (m_browser)
WebView::RunScript(m_browser, wxString::FromUTF8(host_theme_apply_js())); WebView::RunScript(m_browser, wxString::FromUTF8(host_theme_apply_js()));
} }
void resize_to_content(int height) void SpeedDialWebPopup::resize_to_content(int height)
{ {
if (height <= 0) if (height <= 0)
return; return;
// why: HTML is the source of truth - size the popup to the measured content height so // why: HTML is the source of truth - size the popup to the measured content height so
@@ -232,23 +228,23 @@ private:
show_ready(); show_ready();
else if (IsShown()) else if (IsShown())
SetPosition(clamped_position(GetPosition(), GetSize())); SetPosition(clamped_position(GetPosition(), GetSize()));
} }
void show_ready() void SpeedDialWebPopup::show_ready()
{ {
if (!m_pending_show) if (!m_pending_show)
return; return;
SetPosition(clamped_position(popup_position(GetSize()), GetSize())); SetPosition(clamped_position(popup_position(GetSize()), GetSize()));
Popup(); Popup();
focus_browser(); focus_browser();
m_pending_show = false; m_pending_show = false;
} }
// Hide FIRST (scripts may open their own modals that must not stack under this // Hide FIRST (scripts may open their own modals that must not stack under this
// launcher), gate on a native run-confirm unless suppressed, then run on the next // launcher), gate on a native run-confirm unless suppressed, then run on the next
// tick via the registry (which re-resolves the capability and bumps stats). // tick via the registry (which re-resolves the capability and bumps stats).
void run_action(const std::string& id, const std::string& title) void SpeedDialWebPopup::run_action(const std::string& id, const std::string& title)
{ {
ActionRegistry& reg = wxGetApp().action_registry(); ActionRegistry& reg = wxGetApp().action_registry();
const AppAction* a = reg.by_id(id); const AppAction* a = reg.by_id(id);
if (!a) if (!a)
@@ -283,46 +279,25 @@ private:
NotificationManager::NotificationLevel::RegularNotificationLevel, NotificationManager::NotificationLevel::RegularNotificationLevel,
into_u8(o.message)); into_u8(o.message));
}); });
} }
// C++ -> JS push. Same escaping as WebViewHostDialog::call_web_handler: the ignore error // C++ -> JS push. Same escaping as WebViewHostDialog::call_web_handler: the ignore error
// handler keeps a stray non-UTF-8 byte in a plugin name from throwing and aborting the send; // handler keeps a stray non-UTF-8 byte in a plugin name from throwing and aborting the send;
// concatenation (not wxString::Format) avoids a '%' in a title being read as a format token. // concatenation (not wxString::Format) avoids a '%' in a title being read as a format token.
void push(const nlohmann::json& j) void SpeedDialWebPopup::push(const nlohmann::json& j)
{ {
if (!m_browser) if (!m_browser)
return; return;
const wxString payload = wxString::FromUTF8(j.dump(-1, ' ', false, nlohmann::json::error_handler_t::ignore)); const wxString payload = wxString::FromUTF8(j.dump(-1, ' ', false, nlohmann::json::error_handler_t::ignore));
WebView::RunScript(m_browser, wxT("window.HandleStudio(") + payload + wxT(")")); WebView::RunScript(m_browser, wxT("window.HandleStudio(") + payload + wxT(")"));
} }
void send_actions() void SpeedDialWebPopup::send_actions()
{ {
nlohmann::json snap = wxGetApp().action_registry().snapshot(); nlohmann::json snap = wxGetApp().action_registry().snapshot();
push({{"command", "list_actions"}, push({{"command", "list_actions"},
{"actions", std::move(snap["actions"])}, {"actions", std::move(snap["actions"])},
{"favourites", std::move(snap["favourites"])}}); {"favourites", std::move(snap["favourites"])}});
}
wxWebView* m_browser{nullptr};
bool m_page_ready{false};
bool m_pending_show{false};
};
}
void open_speed_dial_popup()
{
wxWindow* parent = wxGetApp().mainframe;
if (!parent)
return;
static SpeedDialWebPopup* popup = nullptr;
if (!popup) {
popup = new SpeedDialWebPopup(parent);
popup->Bind(wxEVT_DESTROY, [](wxWindowDestroyEvent&) { popup = nullptr; });
}
popup->request_show();
} }
}} }}

View File

@@ -1,11 +1,36 @@
#ifndef slic3r_GUI_SpeedDialPopup_hpp_ #ifndef slic3r_GUI_SpeedDialPopup_hpp_
#define slic3r_GUI_SpeedDialPopup_hpp_ #define slic3r_GUI_SpeedDialPopup_hpp_
#include <wx/string.h> #include "Widgets/PopupWindow.hpp"
#include <wx/webview.h>
#include <nlohmann/json_fwd.hpp>
#include <string>
namespace Slic3r { namespace GUI { namespace Slic3r { namespace GUI {
void open_speed_dial_popup(); class SpeedDialWebPopup : public ::PopupWindow
{
public:
explicit SpeedDialWebPopup(wxWindow* parent);
void request_show();
void focus_browser();
private:
void OnDismiss() override {}
void on_script_message(wxWebViewEvent& event);
void on_webview_recreated(wxCommandEvent&);
void apply_host_theme();
void resize_to_content(int height);
void show_ready();
void run_action(const std::string& id, const std::string& title);
void push(const nlohmann::json& j);
void send_actions();
wxWebView* m_browser{nullptr};
bool m_page_ready{false};
bool m_pending_show{false};
};
}} }}