Wire exact Scenario group selection and contextual admission

This commit is contained in:
2026-09-24 00:24:08 -07:00
parent 47917dd0ef
commit 879eb1d5c8
13 changed files with 1505 additions and 52 deletions
+44
View File
@@ -25,6 +25,7 @@ mod review_navigation_tests;
#[cfg(test)]
mod scenario_details_tests;
mod scenario_diagram;
mod scenario_multiselect;
mod scenario_spacing;
#[cfg(test)]
mod source_indentation_tests;
@@ -1348,6 +1349,34 @@ impl App {
])
}
pub fn update(&mut self, message: Message) -> Task<Message> {
if let Message::ScenarioCanvas(behavior::scenario::CanvasEvent::Selection(event)) = message
{
return self.scenario_selection_event(*event);
}
// A delayed Scenario menu cannot retire the current reveal/gesture even
// when it uses the same semantic owner after a representation round trip.
if let Message::Menu(crate::model_menu::Event::Open(request)) = &message
&& request.scenario.is_some()
&& crate::model_menu::Opened::new(self, request.clone(), self.menu_sequence).is_none()
{
return Task::none();
}
if let Message::Menu(crate::model_menu::Event::Invoke(token, _)) = &message
&& let Some(menu) = &self.model_menu
&& menu.request.scenario.is_some()
&& (menu.token != *token || !menu.valid(self))
{
return Task::none();
}
if self.scenario_group_selected()
&& matches!(
&message,
Message::Command(Command::MoveStep(..)) | Message::Delete
)
{
self.status = "Group movement and removal need a whole-selection operation. Select one occurrence to edit it individually.".into();
return Task::none();
}
let preview_read_only = self.process_preview_primary().is_some();
let selection_boundary = self.multiselect_boundary();
// Reject delayed gestures before they can retire newer input ownership.
@@ -1518,6 +1547,16 @@ impl App {
}) {
return Task::none();
}
if self.scenario_group_selected()
&& matches!(
event,
behavior::scenario::CanvasEvent::Select(..)
| behavior::scenario::CanvasEvent::Remove(..)
| behavior::scenario::CanvasEvent::Reorder { .. }
)
{
return Task::none();
}
// Passive layout/form reports retain their existing epoch rules;
// camera input uses its separate lifetime below. All other actions
// must match the exact context before shared reveal reconciliation.
@@ -2666,6 +2705,9 @@ impl App {
if request.process.is_some() {
request.process = self.topology_context().map(Box::new);
}
if request.scenario.is_some() {
request.scenario = self.scenario_context().map(Box::new);
}
self.model_menu = crate::model_menu::Opened::new(self, request, self.menu_sequence);
}
Message::Menu(crate::model_menu::Event::Close) => {
@@ -2698,6 +2740,8 @@ impl App {
if matches!(*action, Message::DiagramSelection(_))
|| (menu.request.process.is_some()
&& matches!(*action, Message::TopologyCanvas(_)))
|| (menu.request.scenario.is_some()
&& matches!(*action, Message::ScenarioCanvas(_)))
{
return self.update(*action);
}