Preserve nested architecture during authoring and distinguish allocation summaries

This commit is contained in:
2026-09-05 17:58:46 +03:00
parent c20a2073d7
commit c7101b970a
13 changed files with 2719 additions and 1653 deletions
+111 -17
View File
@@ -873,7 +873,17 @@ impl App {
.relations
.get(&selected)
.map(|r| vec![r.source(), r.target()])
.unwrap_or_else(|| vec![selected]);
.unwrap_or_else(|| vec![selected])
.into_iter()
.map(|id| {
self.project()
.elements
.get(&id)
.filter(|e| e.kind() == Kind::Port)
.and_then(|e| e.parent)
.unwrap_or(id)
})
.collect::<Vec<_>>();
let nodes = view
.nodes
.values()
@@ -1076,11 +1086,15 @@ impl App {
.cloned()
{
n.collapsed = !n.collapsed;
self.execute(Command::EditNode {
let selected = self.selected;
if self.execute(Command::EditNode {
diagram,
original: n.id,
node: n,
});
}) {
self.selected = selected;
self.refresh_draft();
}
}
}
Message::MoveNode(node, position) => {
@@ -1421,28 +1435,35 @@ impl App {
let id = e.id;
let mut commands = vec![Command::CreateElement(e)];
let mut diagram_target = self.active_diagram;
let preferred = if self.create_kind.is_work() || self.create_kind.is_performer() {
Some(ProjectionKind::Allocation)
} else if ProjectionKind::Capabilities.includes(self.create_kind) {
Some(ProjectionKind::Capabilities)
} else {
None
};
if self.page == Page::Diagram
&& self
.active_diagram
.and_then(|id| self.project().diagrams.get(&id))
.is_some_and(|d| !d.projection.includes(self.create_kind))
&& ProjectionKind::Capabilities.includes(self.create_kind)
&& let Some(preferred) = preferred
{
diagram_target = self
.project()
.diagrams
.values()
.find(|d| d.layer == self.layer && d.projection == ProjectionKind::Capabilities)
.find(|d| d.layer == self.layer && d.projection == preferred)
.map(|d| d.id);
if diagram_target.is_none() {
let view = Id::new_v4();
commands.push(Command::CreateDiagram(Diagram {
id: view,
alias: self.project().fresh_alias("capabilities", "view", view),
name: format!("{} missions and capabilities", self.layer),
alias: self.project().fresh_alias("engineering_view", "view", view),
name: format!("{} {preferred}", self.layer),
description: String::new(),
layer: self.layer,
projection: ProjectionKind::Capabilities,
projection: preferred,
nodes: Default::default(),
}));
diagram_target = Some(view);
@@ -1486,25 +1507,98 @@ impl App {
_ => unreachable!(),
},
);
let count = p.diagrams[&diagram].nodes.len();
let node = p.place(
diagram,
id,
Position {
x: 60. + (count % 3) as f32 * 280.,
y: 60. + (count / 3) as f32 * 150.,
},
);
let owner = if allocated {
Some(self.selected)
} else {
parent
};
// Insertion measures an expanded owner path and commits that reveal
// with the new child, so folded contents cannot overlap the new row.
if let Some(owner) = owner {
let mut expanded = p.diagrams[&diagram].clone();
for node in expanded.nodes.values_mut() {
node.collapsed = false;
}
let expanded_view = oa_core::projection::project_diagram(&p, &expanded);
let mut current = expanded_view
.nodes
.iter()
.find(|n| n.element == owner)
.map(|n| n.id);
while let Some(id) = current {
let node = p
.diagrams
.get_mut(&diagram)
.unwrap()
.nodes
.get_mut(&id)
.unwrap();
if node.collapsed {
node.collapsed = false;
commands.push(Command::EditNode {
diagram,
original: id,
node: node.clone(),
});
}
current = expanded_view
.nodes
.iter()
.find(|n| n.id == id)
.and_then(|n| n.visual_parent);
}
}
// Reserve the expanded size of folded child containers so inserting
// a sibling does not overlap their contents when they are unfolded.
let mut expanded = p.diagrams[&diagram].clone();
for node in expanded.nodes.values_mut() {
node.collapsed = false;
}
let view = oa_core::projection::project_diagram(&p, &expanded);
let position =
if let Some(container) = view.nodes.iter().find(|n| Some(n.element) == owner) {
let y = view
.nodes
.iter()
.filter(|n| n.visual_parent == Some(container.id))
.map(|n| n.position.y + n.size.height + 16.)
.fold(container.position.y + 64., f32::max);
Position {
x: container.position.x + 24.,
y,
}
} else {
Position {
x: view
.nodes
.iter()
.map(|n| n.position.x + n.size.width + 240.)
.fold(60., f32::max),
y: 60.,
}
};
let node = p.place(diagram, id, position);
commands.push(Command::CreateNode {
diagram,
node: p.diagrams[&diagram].nodes[&node].clone(),
});
}
if self.page == Page::Diagram
&& let Some(diagram) = diagram_target
&& let Ok(candidate) = execute_batch(self.project(), &commands)
{
commands.extend(crate::diagram::insertion_adjustments(
&candidate, diagram, id,
));
}
if self.execute(Command::Batch(commands)) {
self.active_diagram = diagram_target;
self.selected = id;
self.inspector_tab = crate::navigation::InspectorTab::Properties;
self.refresh_draft();
if self.page == Page::Diagram {
self.fit();
}
self.status = "Element created · enter its engineering name and properties".into();
}
}