Let Esc and a click on empty space deselect Design feature and body rows

This commit is contained in:
SoftFever
2026-10-05 02:50:38 +08:00
parent a29e3078e8
commit 4cb6ca7ecb
7 changed files with 36 additions and 5 deletions
+3 -2
View File
@@ -134,7 +134,7 @@ contract between them is stated in code rather than spread across handlers.
| `Transient` | a value field or a popup menu | closes it; the tool stays armed |
| `Gesture` | an uncommitted delta — an entity being drawn, a body being dragged | reverts it; committed work is untouched |
| `Tool` | a feature card, an armed sketch tool, a constrain session | exits it; drawn entities survive |
| `Idle` | nothing transient | clears the selection; leaves a sketch session only if it is empty |
| `Idle` | nothing transient | clears the selection, a Feature tree or Bodies row included; leaves a sketch session only if it is empty |
`cad_escape_level()` is a `constexpr` free function over a POD of four booleans rather than a
method on the panel, so the ordering that is the entire contract is checkable without a window,
@@ -211,7 +211,8 @@ that makes no face of its own, such as a Boolean union, answers with the bodies
replay costs up to a recompute, so the panel finds the faces once per row and topology
generation, off the UI thread, and only while no feature card is open. One selection is live at
a time: a viewport pick clears the feature row and a feature row clears the viewport pick, as the
Feature tree and Bodies list do between themselves.
Feature tree and Bodies list do between themselves. `Esc`, a click on empty space and an
empty rubber band all let go of it, whichever list or pick made it.
## Following the app
+5
View File
@@ -899,6 +899,11 @@ void DesignCanvas::set_on_solid_selection_changed(std::function<void(int, int, i
};
}
void DesignCanvas::set_on_empty_pick(std::function<void()> cb)
{
m_sketch_tool.on_empty_pick = std::move(cb);
}
void DesignCanvas::select_body(int body)
{
m_sketch_tool.select_body(body);
+1
View File
@@ -139,6 +139,7 @@ public:
const std::vector<bool>* visible = nullptr,
const std::vector<Transform3d>* xform = nullptr);
void set_on_solid_selection_changed(std::function<void(int, int, int, int)> cb);
void set_on_empty_pick(std::function<void()> cb); // a click or rubber band took nothing
std::vector<int> selected_solid_edges() const; // the Shift/Ctrl+click edge set, last-clicked at the end
void select_body(int body); // Parts-list -> highlight a whole body by index
// Effective display colour of a body: the per-body override (Color tool) when set,
+21 -3
View File
@@ -3826,6 +3826,12 @@ DesignPanel::DesignPanel(wxWindow* parent)
: _L("Nothing selected"));
m_status->Refresh();
});
// A click on nothing drops a list row as it drops a pick. Not while a card is open: the
// feature being edited keeps its row.
m_viewport->set_on_empty_pick([this] {
if (m_active == Tool::None && deselect_rows())
set_status(StatusKind::Info, _L("Nothing selected"));
});
// Visual Extrude gizmo (C5b): dragging/editing the in-canvas depth arrow writes the
// matching spin field and re-previews (which re-feeds the gizmo with the new depth).
@@ -7526,6 +7532,16 @@ int DesignPanel::tree_body_selection() const
return m_parts == nullptr ? -1 : m_parts->selection();
}
// Unselect the Feature tree and Bodies rows; each list's on_select clears what its row lit. True
// if a row was selected.
bool DesignPanel::deselect_rows()
{
const bool any = (m_tree && tree_selection() != wxNOT_FOUND) || tree_body_selection() >= 0;
if (m_tree) m_tree->unselect();
if (m_parts) m_parts->unselect();
return any;
}
void DesignPanel::update_section_flip_btn()
{
if (m_section_flip_btn) m_section_flip_btn->Enable(m_section_on);
@@ -11763,11 +11779,12 @@ void DesignPanel::escape()
}
return;
case CadLevel::Idle:
case CadLevel::Idle: {
// Deselect. In a sketch this is the floor: the session is left through Finish or Cancel,
// both of which say which one they are, and never through a key pressed on the way out of
// something else.
if (m_viewport && m_viewport->clear_any_selection()) {
// something else. The Feature tree and Bodies rows count as selections too.
const bool row = deselect_rows();
if ((m_viewport && m_viewport->clear_any_selection()) || row) {
set_status(StatusKind::Info, wxString());
return;
}
@@ -11783,6 +11800,7 @@ void DesignPanel::escape()
}
return;
}
}
}
bool DesignPanel::menu_can_undo_redo(bool redo) const
+1
View File
@@ -916,6 +916,7 @@ private:
bool m_hl_pending{false};
void request_feature_highlight();
void update_feature_highlight();
bool deselect_rows(); // Esc / a click on nothing: drop the tree and Bodies rows
// Bodies list under the feature tree: one row per body (parallel to m_doc.bodies). Selecting
// one highlights that body and makes it the target for the next op.
DesignRowList* m_parts{nullptr};
+2
View File
@@ -3813,6 +3813,7 @@ void DesignSketchTool::pick_bodies_in_rectangle()
dp_pick_trace("rubber band -> body=%d (%d samples)", best, best_n);
if (on_solid_selection_changed)
on_solid_selection_changed(int(m_solid_sel), m_sel_body, m_sel_face, m_sel_edge);
if (best < 0 && on_empty_pick) on_empty_pick();
}
// Resolve what a pick at (mx,my) would take. CONST, and it writes only into `out`: the hover
@@ -10782,6 +10783,7 @@ bool DesignSketchTool::on_mouse_impl(wxMouseEvent& evt, GLCanvas3D& canvas)
return true;
}
}
if (on_empty_pick) on_empty_pick();
return false; // let the stock canvas orbit / deselect
}
+3
View File
@@ -235,6 +235,9 @@ public:
std::function<void(int body, const Transform3d& xform)> on_body_move_changed;
// Fired on each cycle change: (level 0=None/1=Whole/2=Face/3=Edge, body index, face id, edge id).
std::function<void(int level, int body, int face, int edge)> on_solid_selection_changed;
// A click or rubber band (no live session) that took nothing, so the host can drop the
// selections the tool does not hold, such as the panel's list rows.
std::function<void()> on_empty_pick;
// Click a committed sketch overlay (no live session) -> select that loop: the Sketch
// feature index + the clicked closed-region index within it (-1 = no specific loop).
// entity = the sketch entity index under the cursor when the click landed on a loop