Preserve nested architecture during authoring and distinguish allocation summaries
This commit is contained in:
+111
-17
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user