A section stayed on with nothing in the browser to switch it off; only the viewport's context menu had Remove Section. While there is one, the browser now has an Analysis folder listing it. Its eye (and the folder's) hides the section and shows it again on the same plane; a double-click or Edit Section Analysis opens the dialog, and Delete removes it. Browser rows with an eye publish an `eye:<label>` handle for automation. Fixes #822 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
abddd67d22
commit
15b5b51bf5
8 files changed
+187
-3
No files matched your search
@@ -62,6 +62,9 @@ pub struct TreeState {
|
||||
pub timeline_anchor: Option<String>,
|
||||
/// The Edit Canvas / Calibrate panel.
|
||||
pub canvas_panel: Option<CanvasPanel>,
|
||||
/// A Section Analysis hidden with its eye in the Analysis folder: its plane (origin,
|
||||
/// normal), so the eye can bring it back (#822).
|
||||
pub hidden_section: Option<(solidcraft_engine::geom::Vec3, solidcraft_engine::geom::Vec3)>,
|
||||
/// What waits for the Capture Position question: a command id, or [`SAVE`].
|
||||
pub capture_prompt: Option<String>,
|
||||
/// The last row clicked (for Shift ranges).
|
||||
@@ -354,6 +357,7 @@ pub(crate) fn draw_row(ui: &mut egui::Ui, id: egui::Id, row: &Row) -> RowResp {
|
||||
ui.painter().line_segment([er.left_bottom() + vec2(2.0, -2.0), er.right_top() + vec2(-2.0, 2.0)], Stroke::new(1.4, t.text_dim));
|
||||
}
|
||||
out.eye = resp.clicked() && click_on == 2;
|
||||
crate::scenario::publish_handle(&format!("eye:{}", row.label), er.center());
|
||||
}
|
||||
x += 18.0;
|
||||
// Activation radio (components).
|
||||
@@ -680,6 +684,7 @@ fn component_rows(app: &mut SolveApp, ui: &mut egui::Ui, id: u64, depth: usize,
|
||||
named_view_rows(app, ui, d, acts);
|
||||
origin_rows(app, ui, d);
|
||||
canvas_rows(app, ui, d, acts);
|
||||
analysis_rows(app, ui, d, acts);
|
||||
}
|
||||
let _ = t;
|
||||
for folder in ["bodies", "sketches", "construction"] {
|
||||
@@ -952,6 +957,77 @@ pub fn view_items(app: &SolveApp, name: &str) -> Vec<Item> {
|
||||
]
|
||||
}
|
||||
|
||||
/// The Analysis folder: the Section Analysis, while there is one (shown or hidden), with an eye
|
||||
/// to hide and show it, a double-click to edit it and a menu to delete it (#822).
|
||||
fn analysis_rows(app: &mut SolveApp, ui: &mut egui::Ui, depth: usize, acts: &mut Actions) {
|
||||
if app.session.section.is_some() {
|
||||
// A section made (or remade) while an older one was hidden replaces it.
|
||||
app.tree.hidden_section = None;
|
||||
}
|
||||
let visible = app.session.section.is_some();
|
||||
if !visible && app.tree.hidden_section.is_none() {
|
||||
return;
|
||||
}
|
||||
let key = "analysis";
|
||||
let open = is_open(app, key, true);
|
||||
let r = draw_row(
|
||||
ui,
|
||||
ui.id().with(key),
|
||||
&Row { depth, fold: Some(open), eye: Some(visible), icon: "folder", label: "Analysis", ..Default::default() },
|
||||
);
|
||||
if r.fold || r.clicked {
|
||||
toggle(app, key, true);
|
||||
}
|
||||
if r.eye {
|
||||
set_section_visible(app, !visible);
|
||||
}
|
||||
if !open {
|
||||
return;
|
||||
}
|
||||
let r = draw_row(
|
||||
ui,
|
||||
ui.id().with("section_analysis"),
|
||||
&Row { depth: depth + 1, eye: Some(visible), icon: "section", label: "Section Analysis", dim: !visible, ..Default::default() },
|
||||
);
|
||||
if r.eye {
|
||||
set_section_visible(app, !visible);
|
||||
}
|
||||
if r.double {
|
||||
app.start("inspect.section");
|
||||
}
|
||||
if let Some(p) = r.secondary {
|
||||
acts.menu = Some((p, Target::Section));
|
||||
}
|
||||
}
|
||||
|
||||
/// Hide the Section Analysis (keeping its plane to show it again) or show the hidden one.
|
||||
pub fn set_section_visible(app: &mut SolveApp, visible: bool) {
|
||||
if visible {
|
||||
if let Some((origin, normal)) = app.tree.hidden_section {
|
||||
let plane = json!({"origin": [origin.x, origin.y, origin.z], "normal": [normal.x, normal.y, normal.z]});
|
||||
if app.run("inspect.section", json!({ "plane": plane })).is_ok() {
|
||||
app.tree.hidden_section = None;
|
||||
}
|
||||
}
|
||||
} else if let Some(section) = app.session.section
|
||||
&& app.run("inspect.section", json!({"clear": true})).is_ok()
|
||||
{
|
||||
app.tree.hidden_section = Some(section);
|
||||
}
|
||||
}
|
||||
|
||||
/// The menu of the Section Analysis row.
|
||||
pub fn section_items(app: &SolveApp) -> Vec<Item> {
|
||||
let visible = app.session.section.is_some();
|
||||
vec![
|
||||
Item::action("ui.editSection", "Edit Section Analysis", "section"),
|
||||
Item::sep(),
|
||||
Item::action("ui.unsection", "Delete", "delete"),
|
||||
Item::sep(),
|
||||
Item::action("ui.sectionVisible", "Show/Hide", "eye").key("V").with(json!({ "visible": !visible })),
|
||||
]
|
||||
}
|
||||
|
||||
/// The Canvases folder (reference images) with an eye per canvas.
|
||||
fn canvas_rows(app: &mut SolveApp, ui: &mut egui::Ui, depth: usize, acts: &mut Actions) {
|
||||
let canvases: Vec<(u64, String, bool)> = app.session.doc.canvases.iter().map(|c| (c.id, c.name.clone(), c.visible)).collect();
|
||||
@@ -1883,3 +1959,7 @@ fn occurrence_origin(app: &SolveApp, occurrence: u64) -> Option<solidcraft_engin
|
||||
let own = app.session.pending_moves.get(&occurrence).copied().unwrap_or(o.transform);
|
||||
Some(solidcraft_engine::doc::apply_point(&solidcraft_engine::doc::mat_mul(&parent, &own), solidcraft_engine::geom::Vec3::ZERO))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "section_browser_tests.rs"]
|
||||
mod section_browser_tests;
|
||||
@@ -68,6 +68,8 @@ pub enum Target {
|
||||
Canvas {
|
||||
id: u64,
|
||||
},
|
||||
/// The Section Analysis in the browser's Analysis folder.
|
||||
Section,
|
||||
/// A component's Bodies, Sketches or Construction folder.
|
||||
Folder {
|
||||
component: u64,
|
||||
@@ -261,7 +263,8 @@ pub fn target_bodies(app: &SolveApp, target: &Target) -> Vec<String> {
|
||||
| Target::Toolbar
|
||||
| Target::Workspace
|
||||
| Target::NamedViews
|
||||
| Target::NamedView { .. } => Vec::new(),
|
||||
| Target::NamedView { .. }
|
||||
| Target::Section => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,6 +318,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::Section => crate::browser::section_items(app),
|
||||
Target::Joint { id } => joint_items(app, *id),
|
||||
Target::ToolbarCommand { id } => crate::toolbar_custom::command_items(app, id),
|
||||
Target::Toolbar => crate::toolbar_custom::toolbar_items(app),
|
||||
@@ -858,7 +862,12 @@ pub fn run_item(app: &mut SolveApp, item: &Item, at: Pos2) {
|
||||
"ui.tangentChain" => tangent_chain(app),
|
||||
"ui.origin" => app.ui.show_origin = !app.ui.show_origin,
|
||||
"ui.clear" => drop(app.run("select.clear", json!({}))),
|
||||
"ui.unsection" => drop(app.run("inspect.section", json!({"clear": true}))),
|
||||
"ui.unsection" => {
|
||||
app.tree.hidden_section = None;
|
||||
drop(app.run("inspect.section", json!({"clear": true})));
|
||||
}
|
||||
"ui.editSection" => app.start("inspect.section"),
|
||||
"ui.sectionVisible" => crate::browser::set_section_visible(app, p.get("visible").and_then(Value::as_bool).unwrap_or(true)),
|
||||
"ui.fit" => app.animate_view("fit"),
|
||||
"ui.home" => app.animate_view("home"),
|
||||
// One command for the whole selection: all fixed unless all already are.
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"ui.agent_cursor": "Agent-Cursor",
|
||||
"ui.agent_cursor_speed": "Agent-Cursor-Geschwindigkeit",
|
||||
"ui.all": "Alle",
|
||||
"ui.analysis": "Analyse",
|
||||
"ui.angle": "Winkel",
|
||||
"ui.angle_decimals": "Winkel-Dezimalstellen",
|
||||
"ui.animate_view_changes": "Ansichtsänderungen animieren",
|
||||
@@ -100,6 +101,7 @@
|
||||
"ui.drawing": "Zeichnung",
|
||||
"ui.drill_point": "Bohrpunkt",
|
||||
"ui.edit": "Bearbeiten",
|
||||
"ui.edit_section_analysis": "Querschnittsanalyse bearbeiten",
|
||||
"ui.ellipse": "Ellipse",
|
||||
"ui.end_radius": "Endradius",
|
||||
"ui.entities": "Elemente",
|
||||
|
||||
@@ -285,6 +285,7 @@
|
||||
"ui.agent_cursor": "Agent cursor",
|
||||
"ui.agent_cursor_speed": "Agent cursor speed",
|
||||
"ui.all": "All",
|
||||
"ui.analysis": "Analysis",
|
||||
"ui.angle": "Angle",
|
||||
"ui.angle_decimals": "Angle decimals",
|
||||
"ui.animate_view_changes": "Animate view changes",
|
||||
@@ -362,6 +363,7 @@
|
||||
"ui.drawing": "Drawing",
|
||||
"ui.drill_point": "Drill Point",
|
||||
"ui.edit": "Edit",
|
||||
"ui.edit_section_analysis": "Edit Section Analysis",
|
||||
"ui.ellipse": "Ellipse",
|
||||
"ui.end_radius": "End Radius",
|
||||
"ui.entities": "Entities",
|
||||
|
||||
@@ -285,6 +285,7 @@
|
||||
"ui.agent_cursor": "エージェントカーソル",
|
||||
"ui.agent_cursor_speed": "エージェントカーソル速度",
|
||||
"ui.all": "すべて",
|
||||
"ui.analysis": "解析",
|
||||
"ui.angle": "角度",
|
||||
"ui.angle_decimals": "角度の小数点以下の桁数",
|
||||
"ui.animate_view_changes": "視点変更をアニメーション表示",
|
||||
@@ -362,6 +363,7 @@
|
||||
"ui.drawing": "図面",
|
||||
"ui.drill_point": "ドリル先端",
|
||||
"ui.edit": "編集",
|
||||
"ui.edit_section_analysis": "断面解析を編集",
|
||||
"ui.ellipse": "楕円",
|
||||
"ui.end_radius": "終了半径",
|
||||
"ui.entities": "エンティティ",
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"ui.agent_cursor": "에이전트 커서",
|
||||
"ui.agent_cursor_speed": "에이전트 커서 속도",
|
||||
"ui.all": "모두",
|
||||
"ui.analysis": "분석",
|
||||
"ui.angle": "각도",
|
||||
"ui.angle_decimals": "각도 소수점 자릿수",
|
||||
"ui.animate_view_changes": "뷰 변경 애니메이션",
|
||||
@@ -100,6 +101,7 @@
|
||||
"ui.drawing": "도면",
|
||||
"ui.drill_point": "드릴 선단",
|
||||
"ui.edit": "편집",
|
||||
"ui.edit_section_analysis": "단면 분석 편집",
|
||||
"ui.ellipse": "타원",
|
||||
"ui.end_radius": "끝 반지름",
|
||||
"ui.entities": "요소",
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
//! Section Analysis in the browser (#822): an Analysis folder lists it with an eye to hide and
|
||||
//! show it, and its menu edits or deletes it.
|
||||
|
||||
use egui::pos2;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::context_menu::{self, Target};
|
||||
use crate::scenario::Harness;
|
||||
|
||||
fn harness() -> Harness {
|
||||
let mut h = Harness::new();
|
||||
h.app.home.open = false;
|
||||
h.app.ui.show_browser = true;
|
||||
h.frames(3);
|
||||
h
|
||||
}
|
||||
|
||||
fn click(h: &mut Harness, handle: &str) {
|
||||
assert!(crate::scenario::handle_at(handle).is_some(), "{handle} is not drawn: {:?}", crate::scenario::handles(""));
|
||||
h.step(&json!({"click": {"handle": handle}})).unwrap();
|
||||
h.frames(3);
|
||||
}
|
||||
|
||||
fn menu(h: &mut Harness, label: &str) {
|
||||
let items = context_menu::items(&h.app, &Target::Section);
|
||||
let item = items.iter().find(|i| i.label == label).unwrap_or_else(|| panic!("no {label} in {items:?}")).clone();
|
||||
context_menu::run_item(&mut h.app, &item, pos2(0.0, 0.0));
|
||||
h.frames(3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_analysis_folder_without_a_section() {
|
||||
let h = harness();
|
||||
assert!(crate::scenario::handle_at("row:Analysis").is_none());
|
||||
assert!(crate::scenario::handle_at("row:Section Analysis").is_none());
|
||||
drop(h);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_eye_hides_and_shows_the_section_and_delete_removes_it() {
|
||||
let mut h = harness();
|
||||
h.app.run("inspect.section", json!({"plane": "XZ", "offset": 15})).unwrap();
|
||||
h.frames(3);
|
||||
let section = h.app.session.section.expect("a section");
|
||||
assert!(crate::scenario::handle_at("row:Section Analysis").is_some(), "the section is listed under Analysis");
|
||||
|
||||
click(&mut h, "eye:Section Analysis");
|
||||
assert_eq!(h.app.session.section, None, "the eye turns the section off");
|
||||
assert!(crate::scenario::handle_at("row:Section Analysis").is_some(), "a hidden section stays listed");
|
||||
|
||||
click(&mut h, "eye:Section Analysis");
|
||||
let back = h.app.session.section.expect("the eye turns it back on");
|
||||
assert!((back.0 - section.0).len() < 1e-9 && (back.1 - section.1).len() < 1e-9, "the same plane: {back:?} vs {section:?}");
|
||||
|
||||
// The folder's eye does the same.
|
||||
click(&mut h, "eye:Analysis");
|
||||
assert_eq!(h.app.session.section, None);
|
||||
menu(&mut h, "Show/Hide");
|
||||
assert!(h.app.session.section.is_some(), "Show/Hide in the menu brings it back");
|
||||
|
||||
menu(&mut h, "Delete");
|
||||
assert_eq!(h.app.session.section, None);
|
||||
assert!(h.app.tree.hidden_section.is_none());
|
||||
assert!(crate::scenario::handle_at("row:Section Analysis").is_none(), "a deleted section leaves the browser");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deleting_a_hidden_section_forgets_it() {
|
||||
let mut h = harness();
|
||||
h.app.run("inspect.section", json!({"plane": "XY", "offset": 2})).unwrap();
|
||||
h.frames(3);
|
||||
click(&mut h, "eye:Section Analysis");
|
||||
menu(&mut h, "Delete");
|
||||
assert!(h.app.session.section.is_none() && h.app.tree.hidden_section.is_none());
|
||||
assert!(crate::scenario::handle_at("row:Analysis").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn edit_opens_the_section_dialog() {
|
||||
let mut h = harness();
|
||||
h.app.run("inspect.section", json!({"plane": "YZ"})).unwrap();
|
||||
h.frames(3);
|
||||
menu(&mut h, "Edit Section Analysis");
|
||||
assert!(h.app.dialog.is_some(), "the Section Analysis dialog opens");
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
# SolidCraft 英語・日本語 ローカライズ対照リスト (全 844 件)
|
||||
# SolidCraft 英語・日本語 ローカライズ対照リスト (全 846 件)
|
||||
|
||||
| 英語 (English) | 日本語 (Japanese) | キー名 (Key) |
|
||||
| :--- | :--- | :--- |
|
||||
@@ -50,6 +50,7 @@
|
||||
| All Sketch Points | すべてのスケッチ点 | `All Sketch Points` |
|
||||
| Allowed expressions, separated by semicolons | 使用可能な式(セミコロンで区切る) | `Allowed expressions, separated by semicolons` |
|
||||
| Also deletes | 同時に削除される要素: | `Also deletes` |
|
||||
| Analysis | 解析 | `ui.analysis` |
|
||||
| Angle | 角度 | `ui.angle` |
|
||||
| Angle 2 | 角度 2 | `Angle 2` |
|
||||
| Angle decimals | 角度の小数点以下の桁数 | `ui.angle_decimals` |
|
||||
@@ -290,6 +291,7 @@
|
||||
| Edit Joint | ジョイントを編集 | `cmd.joint_edit` |
|
||||
| Edit Physical Material | 物理マテリアルを編集 | `cmd.material_edit` |
|
||||
| Edit Profile Sketch | プロファイルスケッチを編集 | `Edit Profile Sketch` |
|
||||
| Edit Section Analysis | 断面解析を編集 | `ui.edit_section_analysis` |
|
||||
| Edit Sketch | スケッチを編集 | `cmd.sketch_edit` |
|
||||
| Edit Text | テキストを編集 | `cmd.sketch_edit_text` |
|
||||
| Ellipse | 楕円 | `ui.ellipse` |
|
||||
|
||||
Reference in new issue
Block a user