Browser: joints use the shared context menu (Edit Joint, Drive Joint, Suppress, Delete, Rename)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
This commit is contained in:
bherbruckandClaude Opus 5.5 committed 2026-10-08 12:09:31 +00:00
1 parent 13070abc55
commit a901dade04
2 files changed
+44 -5

No files matched your search

+38
View File
@@ -42,6 +42,10 @@ pub enum Target {
Origin,
/// Document Settings › Units.
Units,
/// An assembly joint.
Joint {
id: u64,
},
/// The Named Views folder, and one view (saved or standard) in it.
NamedViews,
NamedView {
@@ -156,6 +160,7 @@ pub enum RenameWhat {
Group(u64),
Canvas(u64),
View(String),
Joint(u64),
}
#[derive(Clone, Debug)]
@@ -220,6 +225,7 @@ pub fn target_bodies(app: &SolveApp, target: &Target) -> Vec<String> {
| Target::Canvas { .. }
| Target::Origin
| Target::Units
| Target::Joint { .. }
| Target::NamedViews
| Target::NamedView { .. } => Vec::new(),
}
@@ -281,6 +287,7 @@ pub fn items(app: &SolveApp, target: &Target) -> Vec<Item> {
Target::Canvas { id } => crate::browser::canvas_items(app, *id),
Target::Origin => crate::browser::origin_items(app),
Target::NamedViews => vec![act("ui.newView", "New Named View", "perspective")],
Target::Joint { id } => joint_items(app, *id),
Target::Units => crate::prefs::UNITS
.iter()
.map(|u| {
@@ -462,6 +469,20 @@ fn sketch_items(app: &SolveApp, id: u64) -> Vec<Item> {
]
}
/// An assembly joint: edit, drive, suppress, delete.
fn joint_items(app: &SolveApp, id: u64) -> Vec<Item> {
let Some(j) = app.session.doc.assembly.joints.iter().find(|j| j.id == id) else { return Vec::new() };
let rigid = j.kind == solvecraft_engine::doc::joints::JointKind::Rigid;
vec![
act("ui.editJoint", "Edit Joint", "joint").with(json!({ "joint": id })),
act("ui.driveJoint", "Drive Joint", "").with(json!({ "joint": id })).on(!rigid),
cmd(app, "joint.edit", if j.suppressed { "Unsuppress" } else { "Suppress" }).with(json!({ "joint": id, "suppressed": !j.suppressed })),
Item::sep(),
cmd(app, "joint.delete", "Delete").key("Del").with(json!({ "joint": id })),
act("ui.rename", "Rename", "").with(json!({ "joint": id })),
]
}
fn component_items(app: &SolveApp, id: u64) -> Vec<Item> {
let doc = &app.session.doc;
let occ = doc.occurrence_of(id);
@@ -670,6 +691,8 @@ pub fn run_item(app: &mut SolveApp, item: &Item, at: Pos2) {
RenameWhat::Canvas(c)
} else if let Some(v) = p.get("view").and_then(Value::as_str) {
RenameWhat::View(v.to_string())
} else if let Some(j) = id_of(&p, "joint") {
RenameWhat::Joint(j)
} else {
return;
};
@@ -751,6 +774,19 @@ pub fn run_item(app: &mut SolveApp, item: &Item, at: Pos2) {
}
}
"ui.canvasVisible" => drop(app.run("canvas.edit", p)),
"ui.editJoint" | "ui.driveJoint" => {
if let Some(id) = id_of(&p, "joint") {
let d = if item.id == "ui.editJoint" {
crate::dialogs_assembly::edit_joint(app, id)
} else {
crate::dialogs_assembly::drive_joint(app, id)
};
if let Some(d) = d {
app.tool = None;
app.dialog = Some(d);
}
}
}
"ui.newView" => drop(crate::browser::save_view(app, None)),
"ui.restoreView" => {
if let Some(v) = p.get("view").and_then(Value::as_str) {
@@ -923,6 +959,7 @@ pub fn start_rename(app: &mut SolveApp, what: RenameWhat, at: Pos2) {
RenameWhat::Group(g) => crate::browser::group_name(app, *g).unwrap_or_default(),
RenameWhat::Canvas(c) => doc.canvases.iter().find(|x| x.id == *c).map(|x| x.name.clone()).unwrap_or_default(),
RenameWhat::View(v) => v.clone(),
RenameWhat::Joint(j) => doc.assembly.joints.iter().find(|x| x.id == *j).map(|x| x.name.clone()).unwrap_or_default(),
};
app.menu.rename = Some(Rename { what, text, at, focused: false });
}
@@ -947,6 +984,7 @@ fn commit_rename(app: &mut SolveApp, r: &Rename) {
RenameWhat::Group(g) => crate::browser::group_action(app, "ui.renameGroup", &json!({ "group": g, "name": name })),
RenameWhat::Canvas(c) => drop(app.run("canvas.edit", json!({ "canvas": c, "name": name }))),
RenameWhat::View(v) => drop(app.run("view.rename", json!({ "view": v, "name": name }))),
RenameWhat::Joint(j) => drop(app.run("joint.edit", json!({ "joint": j, "name": name }))),
}
}
+6 -5
View File
@@ -1005,16 +1005,17 @@ pub fn browser_joints(app: &mut SolveApp, ui: &mut egui::Ui, depth: usize) {
return;
}
let mut dialog: Option<Dialog> = None;
let mut run: Option<(&'static str, Value)> = None;
for (id, name, kind, suppressed) in joints {
let resp = row(ui, depth + 1.0, "joint", &name, suppressed, None).on_hover_text(format!("{kind:?} joint"));
if resp.double_clicked() {
dialog = edit_joint(app, id);
}
resp.context_menu(|ui| joint_menu(app, ui, (id, kind, suppressed), &mut dialog, &mut run));
}
if let Some((c, p)) = run {
let _ = app.run(c, p);
// The same menus as the rest of the browser.
if resp.secondary_clicked() {
let at = resp.interact_pointer_pos().unwrap_or(resp.rect.left_bottom());
crate::context_menu::open_for(app, at, crate::context_menu::Target::Joint { id });
}
let _ = (kind, suppressed);
}
if let Some(d) = dialog {
app.tool = None;