mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-03 02:25:55 +00:00
* claim: #6967 * Browser: an exact body name wins over a case-only sketch match (#6967)
This commit is contained in:
@@ -196,30 +196,62 @@ fn is_construction(k: &FeatureKind) -> bool {
|
||||
matches!(k, FeatureKind::ConstructionPlane { .. } | FeatureKind::ConstructionAxis { .. } | FeatureKind::ConstructionPoint { .. })
|
||||
}
|
||||
|
||||
/// The item a key names, or `None` when it names none.
|
||||
///
|
||||
/// The rule, the same for showing and for hiding: an exact name wins over a name that only
|
||||
/// matches it by case, so the body called "Back Lid" is not the sketch called "Back lid"
|
||||
/// (#6967). Among the exact matches the order is the one there always was: a feature (by id
|
||||
/// or by name), then a body, then a joint. With no exact match the name is compared without
|
||||
/// regard to case, as it always was: a feature first, then an origin item.
|
||||
fn shown_item(s: &Session, key: &str) -> Option<Shown> {
|
||||
if let Some(f) = s.doc.find_feature(key) {
|
||||
if matches!(f.kind, FeatureKind::Sketch { .. }) {
|
||||
// A sketch and a body with exactly the same name, a sketch named after the body it
|
||||
// makes (#7206): the name is the body's. The browser names a sketch by its id and a
|
||||
// body only by its name, so the body could not be reached otherwise.
|
||||
if f.name == key && s.world_state().bodies.iter().any(|b| b.name == key) {
|
||||
return Some(Shown::Body(key.to_string()));
|
||||
}
|
||||
return Some(Shown::Sketch(f.id));
|
||||
}
|
||||
if is_construction(&f.kind) {
|
||||
return Some(Shown::Origin(f.name.clone()));
|
||||
}
|
||||
shown_item_exact(s, key).or_else(|| shown_item_ignore_case(s, key))
|
||||
}
|
||||
|
||||
/// The item a key names exactly: a feature, a body or a joint whose name is the key itself.
|
||||
fn shown_item_exact(s: &Session, key: &str) -> Option<Shown> {
|
||||
// A feature is found by id or by name, as `find_feature` does, but the name must be the
|
||||
// key itself here.
|
||||
let feature = match key.parse::<u64>() {
|
||||
Ok(id) => s.doc.feature(id).or_else(|| s.doc.features.iter().find(|f| f.name == key)),
|
||||
Err(_) => s.doc.features.iter().find(|f| f.name == key),
|
||||
};
|
||||
if let Some(shown) = feature.and_then(|f| shown_feature(s, f, key)) {
|
||||
return Some(shown);
|
||||
}
|
||||
if s.world_state().bodies.iter().any(|b| b.name == key) {
|
||||
return Some(Shown::Body(key.to_string()));
|
||||
}
|
||||
if let Some(j) = s.doc.assembly.joints.iter().find(|j| j.name == key) {
|
||||
return Some(Shown::Joint(j.id));
|
||||
s.doc.assembly.joints.iter().find(|j| j.name == key).map(|j| Shown::Joint(j.id))
|
||||
}
|
||||
|
||||
/// The item a key names when nothing matches it exactly: the name is compared without regard to
|
||||
/// case, as it always was.
|
||||
fn shown_item_ignore_case(s: &Session, key: &str) -> Option<Shown> {
|
||||
if let Some(shown) = s.doc.find_feature(key).and_then(|f| shown_feature(s, f, key)) {
|
||||
return Some(shown);
|
||||
}
|
||||
ORIGIN.iter().find(|o| o.eq_ignore_ascii_case(key)).map(|o| Shown::Origin(o.to_string()))
|
||||
}
|
||||
|
||||
/// The item a feature is: a sketch, or construction geometry by name. A feature that is neither
|
||||
/// (an extrude, a fillet) is not an item of its own, and the key goes on to the next kind. A
|
||||
/// sketch that shares its name with a body is the body's (#7206).
|
||||
fn shown_feature(s: &Session, f: &solidcraft_doc::Feature, key: &str) -> Option<Shown> {
|
||||
if matches!(f.kind, FeatureKind::Sketch { .. }) {
|
||||
// A sketch and a body with exactly the same name, a sketch named after the body it
|
||||
// makes (#7206): the name is the body's. The browser names a sketch by its id and a
|
||||
// body only by its name, so the body could not be reached otherwise.
|
||||
if f.name == key && s.world_state().bodies.iter().any(|b| b.name == key) {
|
||||
return Some(Shown::Body(key.to_string()));
|
||||
}
|
||||
return Some(Shown::Sketch(f.id));
|
||||
}
|
||||
if is_construction(&f.kind) {
|
||||
return Some(Shown::Origin(f.name.clone()));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// A browser folder or group key (`Visibility::hidden_folders`): `c0` or `o:<ids>`, then
|
||||
/// `/bodies`, `/sketches`, `/construction`, `/joints`, `/canvases` or `/g<group id>`.
|
||||
fn tree_folder_key(k: &str) -> bool {
|
||||
@@ -1718,6 +1750,41 @@ mod tests {
|
||||
assert_eq!(s.visibility, before);
|
||||
}
|
||||
|
||||
/// #6967: a body and a sketch whose names differ only in case. The exact name is the one
|
||||
/// asked for: "Back Lid" is the body, not the sketch called "Back lid". With no exact match
|
||||
/// the case-insensitive one is the only match, and it is the same rule for showing and for
|
||||
/// hiding.
|
||||
#[test]
|
||||
fn an_exact_body_name_wins_over_a_sketch_that_only_matches_by_case() {
|
||||
let mut s = Session::default();
|
||||
let sketch = run(&mut s, "sketch.create", json!({"plane": "XY"}))["sketch"].as_u64().unwrap();
|
||||
run(&mut s, "sketch.point", json!({"point": [10, 10]}));
|
||||
run(&mut s, "sketch.finish", json!({}));
|
||||
run(&mut s, "timeline.rename", json!({"feature": sketch, "name": "Back lid"}));
|
||||
run(&mut s, "solid.box", json!({"length": 10, "width": 10, "height": 10, "body_name": "Back Lid"}));
|
||||
assert_eq!(names(&s), vec!["Back Lid".to_string()]);
|
||||
|
||||
// Hiding "Back Lid" hides the body, not the sketch that only matches by case.
|
||||
let r = run(&mut s, "browser.visibility", json!({"items": ["Back Lid"], "visible": false}));
|
||||
assert_eq!(r["changed"], 1);
|
||||
assert_eq!(s.visibility.hidden_bodies, vec!["Back Lid".to_string()]);
|
||||
assert!(s.visibility.hidden_sketches.is_empty(), "the sketch is not the item that changed");
|
||||
|
||||
// Showing it again shows the body and leaves the sketch alone.
|
||||
run(&mut s, "browser.visibility", json!({"items": ["Back Lid"], "visible": true}));
|
||||
assert!(s.visibility.hidden_bodies.is_empty());
|
||||
assert!(s.visibility.shown_sketches.is_empty(), "the sketch is not the item that changed");
|
||||
|
||||
// With no exact match the case-insensitive one is the only match, and it is the sketch:
|
||||
// the same rule for showing and for hiding.
|
||||
run(&mut s, "browser.visibility", json!({"items": ["BACK LID"], "visible": false}));
|
||||
assert_eq!(s.visibility.hidden_sketches, vec![sketch]);
|
||||
assert!(s.visibility.hidden_bodies.is_empty());
|
||||
run(&mut s, "browser.visibility", json!({"items": ["BACK LID"], "visible": true}));
|
||||
assert!(s.visibility.hidden_sketches.is_empty());
|
||||
assert_eq!(s.visibility.shown_sketches, vec![sketch]);
|
||||
}
|
||||
|
||||
/// #2219: hiding is a step of its own, so Ctrl+Z brings the body back instead of undoing the
|
||||
/// command before the hide, and Ctrl+Y hides it again.
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user