From 6049c6e2347ba6a043f5a9e89084ba183e7a1805 Mon Sep 17 00:00:00 2001 From: SoftFever Date: Tue, 10 Mar 2026 00:55:09 +0800 Subject: [PATCH] Optimize linux experience (#12705) * optimize resize feature on Linux * fix the black screen issue on app startup on Linux * Fixed an issue that Filament Grouping dialog always popup on Linux --- src/slic3r/GUI/GUI_App.cpp | 4 + src/slic3r/GUI/MainFrame.cpp | 243 +++++++++++++++++------------------ src/slic3r/GUI/MainFrame.hpp | 10 +- 3 files changed, 132 insertions(+), 125 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 731b89e714..157b168372 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -873,7 +873,9 @@ void GUI_App::post_init() } if (!switch_to_3d) { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", begin load_gl_resources"; +#ifndef __linux__ mainframe->Freeze(); +#endif plater_->canvas3D()->enable_render(false); mainframe->select_tab(size_t(MainFrame::tp3DEditor)); plater_->select_view_3D("3D"); @@ -910,7 +912,9 @@ void GUI_App::post_init() mainframe->select_tab(size_t(0)); if (app_config->get("default_page") == "1") mainframe->select_tab(size_t(1)); +#ifndef __linux__ mainframe->Thaw(); +#endif BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", end load_gl_resources"; } diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index d7c0da0127..80b801a4c0 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -105,138 +105,109 @@ enum class ERescaleTarget }; #ifdef __WXGTK__ -// Intercepts mouse events globally to provide resize handles for the -// borderless (CSD) window. When the cursor is within BORDER_PX of a -// window edge, a resize cursor is shown and left-click initiates a -// WM-integrated resize via gtk_window_begin_resize_drag(). -class MainFrame::GtkResizeBorderHandler : public wxEventFilter +// A thin transparent panel placed at a window edge to handle resize. +// Works regardless of underlying content (GLCanvas3D, wxWebView, etc.) +// because these panels are Raise()'d above all siblings, so their GDK +// windows receive pointer events even over WebKit2GTK or GL surfaces. +class ResizeEdgePanel : public wxPanel { public: - static constexpr int BORDER_PX = 8; + enum Edge { Bottom, Left, Right }; - explicit GtkResizeBorderHandler(MainFrame* frame) - : m_frame(frame) + static constexpr int BORDER_PX = 5; + + ResizeEdgePanel(MainFrame* frame, Edge edge) + : wxPanel(frame, wxID_ANY, wxDefaultPosition, wxDefaultSize, + wxBORDER_NONE | wxTRANSPARENT_WINDOW) + , m_frame(frame) + , m_edge(edge) { - wxEvtHandler::AddFilter(this); - } - - ~GtkResizeBorderHandler() override - { - wxEvtHandler::RemoveFilter(this); - } - - int FilterEvent(wxEvent& event) override - { - const wxEventType t = event.GetEventType(); - if (t != wxEVT_MOTION && t != wxEVT_LEFT_DOWN) - return Event_Skip; - - if (!m_frame || !m_frame->IsShown() || m_frame->IsMaximized() || m_frame->IsFullScreen()) - return Event_Skip; - - if (!gtk_widget_get_realized(m_frame->m_widget) || gtk_widget_get_window(m_frame->m_widget) == nullptr) - return Event_Skip; - - wxPoint mouse = ::wxGetMousePosition(); - wxRect rect = m_frame->GetScreenRect(); - - // Don't steal interactions from the custom top bar area. - // Keep corner resizing available by only excluding the pure top edge. - if (m_frame->topbar() != nullptr) { - const wxRect topbar_rect = m_frame->topbar()->GetScreenRect(); - if (topbar_rect.Contains(mouse)) { - const bool near_left_corner = mouse.x < rect.x + BORDER_PX; - const bool near_right_corner = mouse.x > rect.x + rect.width - BORDER_PX; - if (!near_left_corner && !near_right_corner) - return Event_Skip; - } - } - - GdkWindowEdge edge; - if (!hit_test(mouse, rect, edge)) { - // Cursor is not near any edge — restore default cursor if we changed it. - if (m_cursor_set) { - gdk_window_set_cursor(gtk_widget_get_window(m_frame->m_widget), NULL); - m_cursor_set = false; - m_last_edge_valid = false; - } - return Event_Skip; - } - - // Set the appropriate resize cursor. - set_cursor_for_edge(edge); - - if (t == wxEVT_LEFT_DOWN) { - gtk_window_begin_resize_drag( - GTK_WINDOW(m_frame->m_widget), - edge, - 1, // left mouse button - mouse.x, mouse.y, - gtk_get_current_event_time()); - return Event_Processed; - } - - // For motion, keep app interaction working (menus, hover, etc.). - return Event_Skip; + SetBackgroundStyle(wxBG_STYLE_TRANSPARENT); + Bind(wxEVT_MOTION, &ResizeEdgePanel::OnCursorUpdate, this); + Bind(wxEVT_ENTER_WINDOW, &ResizeEdgePanel::OnCursorUpdate, this); + Bind(wxEVT_LEFT_DOWN, &ResizeEdgePanel::OnLeftDown, this); + Bind(wxEVT_LEAVE_WINDOW, &ResizeEdgePanel::OnLeave, this); + Bind(wxEVT_PAINT, &ResizeEdgePanel::OnPaint, this); } private: - bool hit_test(const wxPoint& mouse, const wxRect& rect, GdkWindowEdge& edge) const + void OnPaint(wxPaintEvent&) { - bool left = mouse.x >= rect.x && mouse.x < rect.x + BORDER_PX; - bool right = mouse.x > rect.x + rect.width - BORDER_PX && mouse.x <= rect.x + rect.width; - bool top = mouse.y >= rect.y && mouse.y < rect.y + BORDER_PX; - bool bottom = mouse.y > rect.y + rect.height - BORDER_PX && mouse.y <= rect.y + rect.height; - - if (!left && !right && !top && !bottom) - return false; - - if (top && left) edge = GDK_WINDOW_EDGE_NORTH_WEST; - else if (top && right) edge = GDK_WINDOW_EDGE_NORTH_EAST; - else if (bottom && left) edge = GDK_WINDOW_EDGE_SOUTH_WEST; - else if (bottom && right) edge = GDK_WINDOW_EDGE_SOUTH_EAST; - else if (top) edge = GDK_WINDOW_EDGE_NORTH; - else if (bottom) edge = GDK_WINDOW_EDGE_SOUTH; - else if (left) edge = GDK_WINDOW_EDGE_WEST; - else edge = GDK_WINDOW_EDGE_EAST; - return true; + wxPaintDC dc(this); + // Transparent — draw nothing } - void set_cursor_for_edge(GdkWindowEdge edge) + GdkWindowEdge get_gdk_edge(const wxPoint& pos) const { - if (m_last_edge_valid && m_cursor_set && edge == m_last_edge) - return; - - const char* cursor_name = nullptr; - switch (edge) { - case GDK_WINDOW_EDGE_NORTH: cursor_name = "n-resize"; break; - case GDK_WINDOW_EDGE_SOUTH: cursor_name = "s-resize"; break; - case GDK_WINDOW_EDGE_WEST: cursor_name = "w-resize"; break; - case GDK_WINDOW_EDGE_EAST: cursor_name = "e-resize"; break; - case GDK_WINDOW_EDGE_NORTH_WEST: cursor_name = "nw-resize"; break; - case GDK_WINDOW_EDGE_NORTH_EAST: cursor_name = "ne-resize"; break; - case GDK_WINDOW_EDGE_SOUTH_WEST: cursor_name = "sw-resize"; break; - case GDK_WINDOW_EDGE_SOUTH_EAST: cursor_name = "se-resize"; break; - default: return; + wxSize size = GetSize(); + switch (m_edge) { + case Bottom: + if (pos.x < BORDER_PX) return GDK_WINDOW_EDGE_SOUTH_WEST; + if (pos.x > size.x - BORDER_PX) return GDK_WINDOW_EDGE_SOUTH_EAST; + return GDK_WINDOW_EDGE_SOUTH; + case Left: + if (pos.y < BORDER_PX) return GDK_WINDOW_EDGE_NORTH_WEST; + if (pos.y > size.y - BORDER_PX) return GDK_WINDOW_EDGE_SOUTH_WEST; + return GDK_WINDOW_EDGE_WEST; + case Right: + if (pos.y < BORDER_PX) return GDK_WINDOW_EDGE_NORTH_EAST; + if (pos.y > size.y - BORDER_PX) return GDK_WINDOW_EDGE_SOUTH_EAST; + return GDK_WINDOW_EDGE_EAST; } + return GDK_WINDOW_EDGE_SOUTH; + } - GdkDisplay* display = gtk_widget_get_display(m_frame->m_widget); - GdkCursor* cursor = gdk_cursor_new_from_name(display, cursor_name); - if (!cursor) + void OnCursorUpdate(wxMouseEvent& evt) + { + GdkWindowEdge edge = get_gdk_edge(evt.GetPosition()); + const char* name; + switch (edge) { + case GDK_WINDOW_EDGE_NORTH: name = "n-resize"; break; + case GDK_WINDOW_EDGE_SOUTH: name = "s-resize"; break; + case GDK_WINDOW_EDGE_WEST: name = "w-resize"; break; + case GDK_WINDOW_EDGE_EAST: name = "e-resize"; break; + case GDK_WINDOW_EDGE_NORTH_WEST: name = "nw-resize"; break; + case GDK_WINDOW_EDGE_NORTH_EAST: name = "ne-resize"; break; + case GDK_WINDOW_EDGE_SOUTH_WEST: name = "sw-resize"; break; + case GDK_WINDOW_EDGE_SOUTH_EAST: name = "se-resize"; break; + default: name = "s-resize"; break; + } + if (name == m_last_cursor_name) return; + m_last_cursor_name = name; + + GdkDisplay* display = gtk_widget_get_display(m_widget); + GdkCursor* cursor = gdk_cursor_new_from_name(display, name); + if (cursor) { + gdk_window_set_cursor(gtk_widget_get_window(m_widget), cursor); + g_object_unref(cursor); + } + } + + void OnLeave(wxMouseEvent&) + { + m_last_cursor_name = nullptr; + gdk_window_set_cursor(gtk_widget_get_window(m_widget), nullptr); + } + + void OnLeftDown(wxMouseEvent& evt) + { + if (m_frame->IsMaximized() || m_frame->IsFullScreen()) return; - gdk_window_set_cursor(gtk_widget_get_window(m_frame->m_widget), cursor); - g_object_unref(cursor); + GdkWindowEdge edge = get_gdk_edge(evt.GetPosition()); + wxPoint mouse = ClientToScreen(evt.GetPosition()); - m_cursor_set = true; - m_last_edge = edge; - m_last_edge_valid = true; + gtk_window_begin_resize_drag( + GTK_WINDOW(m_frame->m_widget), + edge, + 1, // left button + mouse.x, mouse.y, + gtk_get_current_event_time()); } MainFrame* m_frame; - bool m_cursor_set{false}; - GdkWindowEdge m_last_edge{}; - bool m_last_edge_valid{false}; + Edge m_edge; + const char* m_last_cursor_name{nullptr}; }; #endif // __WXGTK__ @@ -332,15 +303,11 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, BORDERLESS_FRAME_ #endif #ifdef __WXGTK__ - // Zero out the decoration hints that wxGTK computed from the window style. - // When GTKHandleRealized() later calls gdk_window_set_decorations(), it - // will apply zero decorations (no WM title bar). m_gdkDecor = 0; - // Install app-level resize border handler as a fallback. - // This keeps resize behavior for undecorated windows without using GTK CSD, - // avoiding CSD repaint/maximize artifacts on some WMs. - m_resize_border_handler = new GtkResizeBorderHandler(this); + m_edge_bottom = new ResizeEdgePanel(this, ResizeEdgePanel::Bottom); + m_edge_left = new ResizeEdgePanel(this, ResizeEdgePanel::Left); + m_edge_right = new ResizeEdgePanel(this, ResizeEdgePanel::Right); #endif if (!wxGetApp().app_config->has("user_mode")) { @@ -504,6 +471,9 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, BORDERLESS_FRAME_ #endif Refresh(); Layout(); +#ifdef __WXGTK__ + update_edge_panels(); +#endif wxQueueEvent(wxGetApp().plater(), new SimpleEvent(EVT_NOTICE_CHILDE_SIZE_CHANGED)); }); @@ -1085,13 +1055,38 @@ void MainFrame::update_layout() Thaw(); } +#ifdef __WXGTK__ +void MainFrame::update_edge_panels() +{ + if (!m_edge_bottom) return; + + bool hide = IsMaximized() || IsFullScreen(); + m_edge_bottom->Show(!hide); + m_edge_left->Show(!hide); + m_edge_right->Show(!hide); + if (hide) return; + + constexpr int B = ResizeEdgePanel::BORDER_PX; + wxSize cs = GetClientSize(); + m_edge_bottom->SetSize(0, cs.y - B, cs.x, B); + m_edge_left->SetSize(0, 0, B, cs.y); + m_edge_right->SetSize(cs.x - B, 0, B, cs.y); + + m_edge_bottom->Raise(); + m_edge_left->Raise(); + m_edge_right->Raise(); +} +#endif + // Called when closing the application and when switching the application language. void MainFrame::shutdown() { BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "MainFrame::shutdown enter"; #ifdef __WXGTK__ - delete m_resize_border_handler; - m_resize_border_handler = nullptr; + // Edge panels are child windows — wxWidgets destroys them automatically. + m_edge_bottom = nullptr; + m_edge_left = nullptr; + m_edge_right = nullptr; #endif // BBS: backup Slic3r::set_backup_callback(nullptr); @@ -1852,7 +1847,9 @@ wxBoxSizer* MainFrame::create_side_tools() auto curr_plate = m_plater->get_partplate_list().get_curr_plate(); #ifdef __linux__ - slice = try_pop_up_before_slice(m_slice_select == eSliceAll, m_plater, curr_plate, true); + PresetBundle* preset = wxGetApp().preset_bundle; + bool force_show_fila_group_dlg = (preset && preset->is_bbl_vendor() && preset->get_printer_extruder_count() == 2); + slice = try_pop_up_before_slice(m_slice_select == eSliceAll, m_plater, curr_plate, force_show_fila_group_dlg); #else slice = try_pop_up_before_slice(m_slice_select == eSliceAll, m_plater, curr_plate, false); #endif diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 8fa5454cae..c7f27d52af 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -52,6 +52,9 @@ class PrintHostQueueDialog; class Plater; class MainFrame; class ParamsDialog; +#ifdef __WXGTK__ +class ResizeEdgePanel; +#endif enum QuickSlice { @@ -424,8 +427,11 @@ public: #endif // _WIN32 #ifdef __WXGTK__ - class GtkResizeBorderHandler; - GtkResizeBorderHandler* m_resize_border_handler{nullptr}; + friend class ResizeEdgePanel; + ResizeEdgePanel* m_edge_bottom{nullptr}; + ResizeEdgePanel* m_edge_left{nullptr}; + ResizeEdgePanel* m_edge_right{nullptr}; + void update_edge_panels(); #endif // __WXGTK__ };