From 9c3ce9b45e8f54d4fb579415d82c5c08583fdbc0 Mon Sep 17 00:00:00 2001 From: Tommaso Bianchi Date: Mon, 3 Aug 2026 11:05:45 +0200 Subject: [PATCH] Design: clicking empty space lets go of the selection, and the status line follows its tab MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two things a click on nothing should already have done. snaporca-od0. A click that hit no geometry left the solid selection standing. A rubber band swept over empty space has always cleared it (pick_bodies_in_rectangle), and the two gestures cannot disagree about the same outcome. The visible cost was in the escalation that landed last commit: "click the face, click away, click the face again" arrived as the SECOND click on the same face and took the whole body, when the click away was the user letting go of it. Now the miss clears and says so. This gives up something real, deliberately: Thicken / Shell / Draft hold their input face in the panel's selection, so a stray click on empty canvas with one of those cards open hands that face back. Their handlers already write the "(pick a solid face)" placeholder and rebuild the ghost when the selection empties, so the card SAYS it lost the pick rather than confirming against a face the viewport has stopped highlighting. An orbit drag never reaches this branch — it exits at the 8px budget — so panning the view still does not deselect. snaporca-dlj. The status line is a wxPopupWindow, which is a TOP-LEVEL window: hiding the Design page does not hide it. Select a face, switch to Prepare, and the chip was still there reading "selected (whole body) — right-click for what applies to it" on a tab with no such selection and no such menu. Same cause, second symptom: a status update arriving while the page is hidden anchored against a client size that is not the size the page will have, and parked the chip on the tab bar. So: an IsShownOnScreen guard in place_status_hud, show_status_hud(bool) to take it down and bring it back with its text intact, driven from the page-changed handler. Verified on both rigs, not by reasoning about it: face 5 selected -> click bed -> "Nothing selected", tint gone -> click the same face -> face 5 again, NOT the body -> click it again with no click away -> whole body, so snaporca-gem is intact. Prepare -> chip gone; back to Design -> chip returns. KEYTRACE across the round trip shows shift+S then R still reaching the canvas (ui_mode 0 -> 1, Rectangle armed), which is the focus theft this popup replaced a wxFrame to avoid. Fork parity unchanged: DesignPanel.cpp 30, DesignCanvas.cpp 16, headers and DesignSketchTool.cpp 0. MainFrame.cpp is outside that set and was edited per fork. --- src/slic3r/GUI/DesignCanvas.cpp | 11 +++++++++++ src/slic3r/GUI/DesignCanvas.hpp | 4 ++++ src/slic3r/GUI/DesignPanel.cpp | 12 ++++++++++++ src/slic3r/GUI/DesignPanel.hpp | 1 + src/slic3r/GUI/DesignSketchTool.cpp | 21 +++++++++++++++++++++ src/slic3r/GUI/MainFrame.cpp | 5 +++++ 6 files changed, 54 insertions(+) diff --git a/src/slic3r/GUI/DesignCanvas.cpp b/src/slic3r/GUI/DesignCanvas.cpp index 1a36467363..b7d72bab14 100644 --- a/src/slic3r/GUI/DesignCanvas.cpp +++ b/src/slic3r/GUI/DesignCanvas.cpp @@ -970,6 +970,10 @@ void DesignCanvas::set_status_text(const wxString& text, const wxColour& colour) void DesignCanvas::place_status_hud() { if (!m_status_hud || !m_canvas_widget || m_status_hud_last.IsEmpty()) return; + // The canvas has a client size even while its page is hidden, and it is not the size the + // page will have when shown — anchoring against it put the chip up on the tab bar, where it + // then stayed until the next status change moved it. Nothing to anchor to: stay down. + if (!m_canvas_widget->IsShownOnScreen()) { m_status_hud->Hide(); return; } const wxSize cs = m_canvas_widget->GetClientSize(); const wxSize hs = m_status_hud->GetSize(); // Clear of the view cube and the two round view buttons, which own the bottom-left corner. @@ -983,6 +987,13 @@ void DesignCanvas::place_status_hud() m_status_hud->Move(bl); } +void DesignCanvas::show_status_hud(bool on) +{ + if (!m_status_hud) return; + if (on) place_status_hud(); // re-anchors first: the page may have been resized while away + else m_status_hud->Hide(); +} + void DesignCanvas::set_body_highlight(bool on) { if (m_body_selected == on) return; diff --git a/src/slic3r/GUI/DesignCanvas.hpp b/src/slic3r/GUI/DesignCanvas.hpp index 4462de096b..c94f7f584b 100644 --- a/src/slic3r/GUI/DesignCanvas.hpp +++ b/src/slic3r/GUI/DesignCanvas.hpp @@ -186,6 +186,10 @@ public: // the panel clips it at ~73 characters with no warning (snaporca-8cc), the viewport's // bottom margin has the whole window width to spare. Empty text hides it. void set_status_text(const wxString& text, const wxColour& colour); + // Take the status line down / bring it back when the Design page leaves and re-enters view. + // A popup is a TOP-LEVEL window: hiding the page it belongs to does not hide it. Keeps the + // text, so coming back needs no re-selection. + void show_status_hud(bool on); void set_operand_bodies(int target_body, int tool_body); // -1,-1 clears void set_body_translucent(bool on); // render the solid see-through (fillet/chamfer preview) void set_body_hidden(bool on); // preview-only: hide base bodies, show only the result ghost diff --git a/src/slic3r/GUI/DesignPanel.cpp b/src/slic3r/GUI/DesignPanel.cpp index b64c1e2b38..a9f04d56a4 100644 --- a/src/slic3r/GUI/DesignPanel.cpp +++ b/src/slic3r/GUI/DesignPanel.cpp @@ -5694,8 +5694,20 @@ wxString DesignPanel::idle_hint() const : _L("No solid yet — select a sketch and right-click it to Extrude."); } +// The Design tab is no longer the visible page (snaporca-dlj). The status line is a popup floating +// over the GL canvas, so it does NOT go away when this page does — it stayed up over Prepare and +// over the home screen, still reading like a live Design selection ("selected (whole body) — +// right-click for what applies to it") on a tab that has no such selection and no such menu. +// Nothing else in the panel needs to know: the popup keeps its text and comes straight back. +void DesignPanel::on_tab_hidden() +{ + if (m_viewport) m_viewport->show_status_hud(false); +} + void DesignPanel::on_tab_shown() { + if (m_viewport) m_viewport->show_status_hud(true); // ...and back on the way in + if (m_active == Tool::None && m_doc.display_mesh.its.indices.empty()) set_status(idle_hint()); // first paint: the tab has never been edited diff --git a/src/slic3r/GUI/DesignPanel.hpp b/src/slic3r/GUI/DesignPanel.hpp index 37535b42c6..a1c265ffd5 100644 --- a/src/slic3r/GUI/DesignPanel.hpp +++ b/src/slic3r/GUI/DesignPanel.hpp @@ -50,6 +50,7 @@ class DesignPanel : public wxPanel public: explicit DesignPanel(wxWindow* parent); void on_tab_shown(); // re-sync bed to the active printer when the Design tab is activated + void on_tab_hidden(); // another tab took over: take the viewport status line down with us // Rebuild off the UI thread (progress dialog only if it turns out to be slow), so a feature // op on a heavy imported solid does not freeze the window. Returns m_doc.recompute()'s result. bool recompute_guarded(const wxString& message); diff --git a/src/slic3r/GUI/DesignSketchTool.cpp b/src/slic3r/GUI/DesignSketchTool.cpp index 637a36e26e..30a73ef6e9 100644 --- a/src/slic3r/GUI/DesignSketchTool.cpp +++ b/src/slic3r/GUI/DesignSketchTool.cpp @@ -8068,6 +8068,27 @@ bool DesignSketchTool::on_mouse_impl(wxMouseEvent& evt, GLCanvas3D& canvas) return true; } m_display_pick = -1; m_display_pick_region = -1; // clicked bare plate -> drop highlight + // ...and the SOLID selection goes with it (snaporca-od0). A click that hits nothing has to + // mean what a rubber band that sweeps nothing already means — pick_bodies_in_rectangle + // clears on an empty sweep, and the two gestures cannot disagree about the same outcome. + // Until now the face survived a click on bare plate, so "click away, then click the face + // again" arrived here as the SECOND click on the same face and escalated to the whole + // body, when the click away was the user letting go of it. + // + // The cost is real and is the intended trade: Thicken / Shell / Draft hold their input + // face in the panel's m_sel_solid_face, so a stray click on empty canvas with one of + // those cards open gives that face back. Their handlers already write the "(pick a solid + // face)" placeholder and rebuild the ghost at level 0, so the card SAYS it lost the pick + // rather than confirming against a face the viewport has stopped highlighting. + // + // Guarded on there being something to clear: this runs on every click that misses, and + // the callback re-renders the open card's preview. + if (m_solid_sel != SolidSel::None) { + clear_solid_selection(); + dp_pick_trace("clicked empty space -> selection cleared"); + if (on_solid_selection_changed) + on_solid_selection_changed(int(m_solid_sel), m_sel_body, m_sel_face, m_sel_edge); + } // Last resort: a click that hit no geometry but landed on a reference/base plane picks it. if (m_dbp_active) { const int h = hit_test_base_pick(canvas, evt); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index aa3c932569..572ef30e47 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1290,6 +1290,11 @@ void MainFrame::init_tabpanel() { else if (panel == m_monitor) { //monitor } +#ifdef SLIC3R_CAD + // Any page that is not Design takes the Design status line down with it — see + // DesignPanel::on_tab_hidden for why the popup does not follow the page on its own. + if (m_design_panel != nullptr && panel != m_design_panel) m_design_panel->on_tab_hidden(); +#endif #ifndef __APPLE__ if (sel == tp3DEditor) { m_topbar->EnableUndoRedoItems();