Testing macOS fixes

This commit is contained in:
Lam Wei Lun
2026-09-18 19:07:03 +08:00
parent 1159ca5f7f
commit 4ea50a33e4
9 changed files with 191 additions and 36 deletions
+15 -1
View File
@@ -726,6 +726,19 @@ void ActionRegistry::suppress_ask(const std::string& id)
write_section("ask_suppressed", nlohmann::json(arr));
}
bool ActionRegistry::tooltip_expanded() const
{
assert(wxThread::IsMain());
const nlohmann::json j = read_section("tooltip_expanded", nlohmann::json(true));
return j.is_boolean() ? j.get<bool>() : true;
}
void ActionRegistry::set_tooltip_expanded(bool expanded)
{
assert(wxThread::IsMain());
write_section("tooltip_expanded", nlohmann::json(expanded));
}
// ---- snapshot ---------------------------------------------------------------
nlohmann::json ActionRegistry::snapshot()
@@ -810,7 +823,8 @@ nlohmann::json ActionRegistry::snapshot()
return {{"actions", std::move(actions)},
{"favourites", std::move(favourites)},
{"recent", std::move(recent_json)},
{"user_mode", mode_key(wxGetApp().get_mode())}};
{"user_mode", mode_key(wxGetApp().get_mode())},
{"tooltip_expanded", tooltip_expanded()}};
}
// ---- tab options (enumerate the MainFrame notebook's current pages) ----------
+5
View File
@@ -196,6 +196,11 @@ public:
bool should_ask(const std::string& id) const;
void suppress_ask(const std::string& id);
// Footer expand/collapse preference. Global (applies to every action) and persisted; absent
// means expanded, so a fresh config picks the richer default with no migration.
bool tooltip_expanded() const;
void set_tooltip_expanded(bool expanded);
// Flat, frecency-sorted snapshot for the webview:
// {actions:[...], favourites:[...], recent:[...]} (recent = last-N launched by recency).
nlohmann::json snapshot();
+2
View File
@@ -478,6 +478,8 @@ int get_dpi_for_window(const wxWindow *window);
#ifdef __WXOSX__
void dataview_remove_insets(wxDataViewCtrl* dv);
void staticbox_remove_margin(wxStaticBox* sb);
// Clip a top-level window (and its webview) to a rounded rect with a native layer.
void set_window_corner_radius(wxWindow* win, int radius);
#endif
#ifdef __WXGTK__
+17
View File
@@ -1,5 +1,6 @@
#include <unistd.h>
#include <sys/sysctl.h>
#import <Cocoa/Cocoa.h>
#import <wx/osx/cocoa/dataview.h>
#import "GUI_Utils.hpp"
@@ -21,6 +22,22 @@ void staticbox_remove_margin(wxStaticBox* sb) {
[nativeBox setBorderWidth:0];
}
// wxOSX SetShape only clears the window background; it cannot clip to a region. Clipping the
// window's view layer to a rounded rect is what actually rounds the opaque webview inside.
void set_window_corner_radius(wxWindow* win, int radius) {
if (!win)
return;
NSView* view = (NSView*)win->GetHandle();
if (!view)
return;
NSWindow* window = [view window];
[window setOpaque:NO];
[window setBackgroundColor:[NSColor clearColor]];
[view setWantsLayer:YES];
[[view layer] setCornerRadius:radius];
[[view layer] setMasksToBounds:YES];
}
bool is_debugger_present()
// Returns true if the current process is being debugged (either
// running under the debugger or has a debugger attached post facto).
+48 -19
View File
@@ -113,6 +113,8 @@ nlohmann::json speed_dial_ui_strings()
{"sd_mode_develop", _u8L("Developer")},
{"sd_wiki_f1", _u8L("Wiki (F1)")},
{"sd_no_wiki", _u8L("No wiki page for this action")},
{"sd_show_details", _u8L("Show details")},
{"sd_hide_details", _u8L("Hide details")},
};
}
@@ -148,8 +150,8 @@ SpeedDialWebDialog::SpeedDialWebDialog(wxWindow* parent)
// the page inside the fixed-size popup. No-op on the other backends (wxWidgets 3.3 base virtual).
if (wxWebView* wv = browser())
wv->EnableBrowserAcceleratorKeys(false);
// Re-cut the shape region whenever layout changes the client size; SetShape itself
// does not generate size events, so this cannot recurse.
// Re-cut the shape whenever layout changes the client size. wxOSX SetShape resizes the
// NSWindow, which fires this synchronously; apply_rounded_shape() guards re-entry.
Bind(wxEVT_SIZE, [this](wxSizeEvent& event) {
event.Skip();
apply_rounded_shape();
@@ -224,7 +226,9 @@ void SpeedDialWebDialog::handle_web_command(const nlohmann::json& payload)
if (id.is_string())
ids.push_back(id.get<std::string>());
wxGetApp().action_registry().reorder_favourites(ids);
} else if (command == "run_action")
} else if (command == "set_tooltip_expanded")
wxGetApp().action_registry().set_tooltip_expanded(payload.value("expanded", true));
else if (command == "run_action")
run_action(payload.value("id", ""), payload.value("title", ""), payload.value("param", ""));
else if (command == "open_wiki")
open_wiki(payload.value("id", ""));
@@ -259,34 +263,58 @@ void SpeedDialWebDialog::resize_to_content(int height)
const int height_dip = std::max(kPopupMinHeight, std::min(height, max_dip));
SetClientSize(FromDIP(wxSize(kPopupWidth, height_dip)));
Layout();
#ifdef __WXOSX__
// WKWebView can lag the dialog's new client size; force the viewport to match so the page is
// never painted (and clipped by the rounded layer) below the footer.
if (wxWebView* wv = browser()) {
const wxSize client = GetClientSize();
if (wv->GetSize() != client)
wv->SetSize(client);
}
#endif
apply_rounded_shape();
}
// Rounded corners: the webview paints an opaque rectangle, so round the whole top-level window
// with a shape region (same mask trick as FilamentPickerDialog). Binary edges, no anti-aliasing.
// Rounded corners: the webview paints an opaque rectangle, so round the whole top-level window.
// GTK/MSW use a shape region (same mask trick as FilamentPickerDialog, binary edges, no
// anti-aliasing); macOS clips the native view layer instead, since SetShape cannot shape there.
void SpeedDialWebDialog::apply_rounded_shape()
{
// wxOSX SetShape resizes the NSWindow (setContentSize 10x10 then back), which synchronously
// fires wxEVT_SIZE -> apply_rounded_shape() -> SetShape() and recurses until the stack
// overflows. GTK/MSW set a region without resizing, so they are unaffected.
if (m_applying_shape)
return;
// BORDER_NONE means the window is all client area, so the client size is the shape size.
const wxSize size = GetClientSize();
if (size.GetWidth() <= 0 || size.GetHeight() <= 0)
return;
m_applying_shape = true;
#ifdef __WXOSX__
// wxOSX ignores the region (it only clears the window background), so round the native view.
set_window_corner_radius(this, FromDIP(m_corner_radius));
#else
m_shape_bmp.Create(size.GetWidth(), size.GetHeight(), 32);
if (!m_shape_bmp.IsOk())
return;
if (m_shape_bmp.IsOk()) {
wxMemoryDC dc;
dc.SelectObject(m_shape_bmp);
dc.SetBackground(wxBrush(wxColour(0, 0, 0)));
dc.Clear();
dc.SetBrush(wxBrush(wxColour(255, 255, 255, 255)));
dc.SetPen(*wxTRANSPARENT_PEN);
dc.DrawRoundedRectangle(0, 0, size.GetWidth(), size.GetHeight(), FromDIP(m_corner_radius));
dc.SelectObject(wxNullBitmap);
wxMemoryDC dc;
dc.SelectObject(m_shape_bmp);
dc.SetBackground(wxBrush(wxColour(0, 0, 0)));
dc.Clear();
dc.SetBrush(wxBrush(wxColour(255, 255, 255, 255)));
dc.SetPen(*wxTRANSPARENT_PEN);
dc.DrawRoundedRectangle(0, 0, size.GetWidth(), size.GetHeight(), FromDIP(m_corner_radius));
dc.SelectObject(wxNullBitmap);
wxRegion region(m_shape_bmp, wxColour(0, 0, 0));
if (region.IsOk())
SetShape(region);
}
#endif
wxRegion region(m_shape_bmp, wxColour(0, 0, 0));
if (region.IsOk())
SetShape(region);
m_applying_shape = false;
}
void SpeedDialWebDialog::on_dpi_changed(const wxRect&)
@@ -379,7 +407,8 @@ void SpeedDialWebDialog::send_actions()
{"actions", std::move(snap["actions"])},
{"favourites", std::move(snap["favourites"])},
{"recent", std::move(snap["recent"])},
{"user_mode", std::move(snap["user_mode"])}});
{"user_mode", std::move(snap["user_mode"])},
{"tooltip_expanded", std::move(snap["tooltip_expanded"])}});
}
}} // namespace Slic3r::GUI
+3 -1
View File
@@ -31,9 +31,11 @@ private:
void on_dpi_changed(const wxRect& suggested_rect) override;
bool m_page_ready{false};
// Rounded corners via a window shape region, since the webview itself is opaque.
// Rounded corners (shape region on GTK/MSW, native layer on macOS), since the webview is opaque.
int m_corner_radius{7};
wxBitmap m_shape_bmp;
// wxOSX SetShape resizes the window, which re-enters apply_rounded_shape() through wxEVT_SIZE.
bool m_applying_shape{false};
// Guards the CallAfter in on_script_message across dialog destruction, same as
// PluginsDialog::m_alive (PluginsDialog.hpp:249).
std::shared_ptr<std::atomic<bool>> m_alive = std::make_shared<std::atomic<bool>>(true);