Keep state canvas focus tree stable when placement controls change
This commit is contained in:
@@ -1046,6 +1046,16 @@ async fn mounted_state_delete_uses_state_placement_and_preserves_architecture_pl
|
||||
assert_eq!(app.selected, state);
|
||||
assert_eq!(app.behavior.diagram.placement, Some(node));
|
||||
let before = app.document.snapshot();
|
||||
assert!(crate::behavior::state_edit::unplaced(app.project(), diagram).is_empty());
|
||||
let owner = app.state_context().unwrap();
|
||||
let focused = |mounted: &mut Mounted, app: &App| {
|
||||
mounted.geometry(app).focused.iter().any(|(id, _, focused)| {
|
||||
*focused
|
||||
&& id.as_ref()
|
||||
== Some(&iced::advanced::widget::Id::from("diagram-keyboard-owner"))
|
||||
})
|
||||
};
|
||||
assert!(focused(&mut mounted, &app));
|
||||
let messages = delete(&mut mounted, &mut app, false).await;
|
||||
assert!(messages.iter().any(|message| matches!(
|
||||
message,
|
||||
@@ -1053,13 +1063,35 @@ async fn mounted_state_delete_uses_state_placement_and_preserves_architecture_pl
|
||||
)));
|
||||
assert!(!app.project().contains(node));
|
||||
assert!(app.project().contains(architecture_node) && app.project().contains(state));
|
||||
assert_eq!(
|
||||
crate::behavior::state_edit::unplaced(app.project(), diagram),
|
||||
vec![state]
|
||||
);
|
||||
mounted.settle(&mut app).await;
|
||||
let hidden = app.state_context().unwrap();
|
||||
assert_eq!(
|
||||
(hidden.document, hidden.diagram, hidden.epoch),
|
||||
(owner.document, owner.diagram, owner.epoch),
|
||||
"hiding one placement does not change the logical canvas owner"
|
||||
);
|
||||
assert!(
|
||||
focused(&mut mounted, &app),
|
||||
"showing the unplaced-element picker must retain the actual canvas focus tree"
|
||||
);
|
||||
consistent(&app);
|
||||
let directory = tempfile::tempdir().unwrap();
|
||||
let path = directory.path().join(&app.document.bundle().entry);
|
||||
save_with_busy_view(&mut mounted, &mut app, &path).await;
|
||||
assert!(focused(&mut mounted, &app), "Save retains the same owner");
|
||||
let task = app.update(Message::Undo);
|
||||
mounted.perform(&mut app, task).await;
|
||||
assert_eq!(app.document.bundle(), &before.bundle);
|
||||
assert!(crate::behavior::state_edit::unplaced(app.project(), diagram).is_empty());
|
||||
mounted.settle(&mut app).await;
|
||||
assert!(
|
||||
focused(&mut mounted, &app),
|
||||
"Undo removes the picker without replacing the actual canvas focus tree"
|
||||
);
|
||||
delete(&mut mounted, &mut app, true).await;
|
||||
assert_eq!(
|
||||
app.delete_target.as_ref().unwrap().snapshot.request.object,
|
||||
|
||||
@@ -141,12 +141,16 @@ pub fn view<'a>(app: &'a App, machine: Id, mut out: Column<'a, Message>) -> Elem
|
||||
label: format!("{} · {}", p.named(element), p.elements[&element].kind()),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
// Keep the optional picker in its own stable slot. Inserting it as a
|
||||
// sibling before the canvas would replace the positional Iced focus tree.
|
||||
let missing_spacing = if missing.is_empty() { 0.0_f32 } else { 8.0 };
|
||||
let mut missing_controls = Column::new();
|
||||
if !missing.is_empty() {
|
||||
let position = Position {
|
||||
x: (48. - state.pan.x) / state.zoom,
|
||||
y: (90. - state.pan.y) / state.zoom,
|
||||
};
|
||||
out = out.push(
|
||||
missing_controls = missing_controls.push(
|
||||
pick_list(missing, None::<Choice>, move |c| {
|
||||
event(Event::Place {
|
||||
context,
|
||||
@@ -173,13 +177,20 @@ pub fn view<'a>(app: &'a App, machine: Id, mut out: Column<'a, Message>) -> Elem
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.into();
|
||||
out = out.push(crate::diagram::focus::wrap(
|
||||
canvas.map(event),
|
||||
context,
|
||||
crate::app::diagram_removal::focus_enabled(app),
|
||||
app.busy,
|
||||
Message::DiagramTraverse,
|
||||
));
|
||||
out = out.push(
|
||||
column![
|
||||
missing_controls,
|
||||
crate::diagram::focus::wrap(
|
||||
canvas.map(event),
|
||||
context,
|
||||
crate::app::diagram_removal::focus_enabled(app),
|
||||
app.busy,
|
||||
Message::DiagramTraverse,
|
||||
)
|
||||
]
|
||||
.spacing(missing_spacing)
|
||||
.height(Fill),
|
||||
);
|
||||
let base: Element<'_, Message> = container(out.spacing(8))
|
||||
.padding(14)
|
||||
.width(Fill)
|
||||
|
||||
Reference in New Issue
Block a user