Integrate process, scenario and typed history authoring checkpoint

This commit is contained in:
2026-09-10 11:14:43 -07:00
22 changed files with 4093 additions and 166 deletions
+81 -5
View File
@@ -1100,11 +1100,13 @@ impl App {
// still validates document, scenario and revision before accepting it.
if matches!(
&message,
Message::ScenarioCanvas(crate::behavior::scenario::CanvasEvent::Scrolled(..))
| Message::TopologyCanvas(
crate::behavior::topology::CanvasEvent::Scrolled(..)
| crate::behavior::topology::CanvasEvent::Viewport(..)
)
Message::ScenarioCanvas(
crate::behavior::scenario::CanvasEvent::Scrolled(..)
| crate::behavior::scenario::CanvasEvent::Restored(..)
) | Message::TopologyCanvas(
crate::behavior::topology::CanvasEvent::Scrolled(..)
| crate::behavior::topology::CanvasEvent::Viewport(..)
)
) {
return self.handle(message);
}
@@ -1362,6 +1364,18 @@ impl App {
Message::ScenarioCanvas(event) => {
use crate::behavior::scenario::{self, CanvasEvent};
let context = event.context();
if let CanvasEvent::Restored(_, token, area, offset) = event {
if self.scenario_context().is_some_and(|current| {
current.document == context.document && current.scenario == context.scenario
}) && self
.behavior
.scenario
.restored(context, token, area, offset)
{
self.canvas_epoch = self.canvas_epoch.wrapping_add(1);
}
return Task::none();
}
if let CanvasEvent::Scrolled(_, area, offset) = event {
// Inner and outer scroll notifications may come from the
// same frame. Accept both across the first epoch bump,
@@ -1380,6 +1394,45 @@ impl App {
return Task::none();
}
match event {
CanvasEvent::OpenUse(_, occurrence) => {
if self.behavior.has_pending() || self.draft_dirty() || self.source_dirty()
{
self.status = "Apply or cancel pending edits before opening a referenced scenario.".into();
return Task::none();
}
let mut state = std::mem::take(&mut self.behavior.scenario);
let target = state.open_use(self.project(), context, occurrence);
self.behavior.scenario = state;
match target {
Ok(target) => {
self.select(target);
self.canvas_epoch = self.canvas_epoch.wrapping_add(1);
}
Err(error) => self.status = error,
}
}
CanvasEvent::Back(_) => {
if self.behavior.has_pending() || self.draft_dirty() || self.source_dirty()
{
self.status =
"Apply or cancel pending edits before returning to the caller."
.into();
return Task::none();
}
let mut state = std::mem::take(&mut self.behavior.scenario);
let caller = state.back(self.project(), context);
self.behavior.scenario = state;
if let Some(caller) = caller {
self.select(caller.occurrence);
self.canvas_epoch = self.canvas_epoch.wrapping_add(1);
if let Some(context) = self.scenario_context() {
self.behavior.scenario.restore_caller(context, caller);
}
} else {
self.status =
"The calling scenario reference is no longer available.".into();
}
}
CanvasEvent::Select(_, id) => {
if !self.behavior.has_pending()
&& crate::behavior::selected_behavior(self.project(), id)
@@ -1405,6 +1458,29 @@ impl App {
return Task::none();
}
match mutation {
CanvasEvent::BeginOccurrence {
tool,
slot,
lifeline,
..
} => {
let mut state = std::mem::take(&mut self.behavior.scenario);
let result = state.begin_occurrence(
self.project(),
context,
tool,
slot,
lifeline,
);
self.behavior.scenario = state;
match result {
Ok(()) => {
self.canvas_epoch = self.canvas_epoch.wrapping_add(1);
self.status = "Complete the selected behavior at the highlighted sequence position.".into();
}
Err(error) => self.status = error,
}
}
CanvasEvent::Begin {
slot,
sender,