Follow inspected Saved scenario through the actual arrangement picker
This commit is contained in:
@@ -10,6 +10,8 @@ mod hosted;
|
||||
mod reference_authoring;
|
||||
#[cfg(test)]
|
||||
mod review_navigation_tests;
|
||||
#[cfg(test)]
|
||||
mod scenario_details_tests;
|
||||
mod scenario_diagram;
|
||||
mod scenario_spacing;
|
||||
pub(crate) mod source_rename;
|
||||
@@ -2041,6 +2043,11 @@ impl App {
|
||||
if checked_owner(self.project(), requested) != Some(context.scenario) {
|
||||
return Task::none();
|
||||
}
|
||||
// An explicitly selected current view record follows
|
||||
// the arrangement picker, as with the Diagram picker.
|
||||
// Independent occurrence/lifeline selection stays put.
|
||||
let follow_view = matches!(context.representation,
|
||||
Representation::Saved { diagram } if self.selected == diagram);
|
||||
if !self.apply() {
|
||||
return Task::none();
|
||||
}
|
||||
@@ -2058,7 +2065,15 @@ impl App {
|
||||
},
|
||||
other => other,
|
||||
};
|
||||
self.enter_scenario_representation(requested);
|
||||
if self.enter_scenario_representation(requested) && follow_view {
|
||||
self.selected = match requested {
|
||||
Representation::Saved { diagram } => diagram,
|
||||
// Automatic has no Diagram record; its Scenario
|
||||
// owner is the corresponding inspectable target.
|
||||
Representation::Derived { owner } => owner,
|
||||
};
|
||||
self.refresh_draft();
|
||||
}
|
||||
}
|
||||
CanvasEvent::Focus(_) => {
|
||||
self.behavior.scenario.toggle_focus();
|
||||
|
||||
@@ -1029,10 +1029,9 @@ fn repeated_preview_arrows_select_the_relation_and_highlight_the_exact_caller_li
|
||||
let camera = app.behavior.topology.camera_boundary();
|
||||
if call == calls[1]
|
||||
&& let Some((previous_call, previous)) = &previous_camera
|
||||
&& *previous_call != call
|
||||
{
|
||||
if *previous_call != call {
|
||||
assert!(&camera != previous);
|
||||
}
|
||||
assert!(&camera != previous);
|
||||
}
|
||||
if let Some(epoch) = previous_epoch {
|
||||
assert_ne!(app.canvas_epoch, epoch);
|
||||
@@ -1375,7 +1374,7 @@ fn whole_surface_camera_fits_huge_extent_while_target_camera_remains_readable()
|
||||
};
|
||||
let viewport = iced::Size::new(800., 500.);
|
||||
let (zoom, pan) = usage::camera_for(&Target::Whole, bounds, viewport);
|
||||
assert!(zoom < 0.05 && zoom >= 0.0001);
|
||||
assert!((0.0001..0.05).contains(&zoom));
|
||||
assert!(bounds.width * zoom <= viewport.width - 64. + 0.01);
|
||||
assert!(bounds.height * zoom <= viewport.height - 64. + 0.01);
|
||||
assert!((bounds.center_x() * zoom + pan.x - viewport.width / 2.).abs() < 0.01);
|
||||
|
||||
@@ -0,0 +1,281 @@
|
||||
//! Actual arrangement selection and current-record Details admission.
|
||||
use super::*;
|
||||
use crate::behavior::canvas_workspace::tests::Mounted;
|
||||
use crate::behavior::scenario::{CanvasEvent, Context, Shelf};
|
||||
use crate::behavior::scenario_layout::Representation;
|
||||
use iced::{Point, Size, advanced::widget};
|
||||
|
||||
const FIXTURE: &str = include_str!("diagram_usages/tests/saved_scenario.arcadia");
|
||||
|
||||
fn id(app: &App, alias: &str) -> Id {
|
||||
app.project()
|
||||
.identities()
|
||||
.into_iter()
|
||||
.find(|(_, name)| *name == alias)
|
||||
.unwrap()
|
||||
.0
|
||||
}
|
||||
fn setup() -> (App, Id, Id, Id) {
|
||||
let mut app = App::with_project(oa_core::language::parse(FIXTURE).unwrap());
|
||||
let a = id(&app, "incident_readable");
|
||||
let b = id(&app, "incident_wide");
|
||||
let owner = id(&app, "incident_response");
|
||||
let _ = app.update(Message::Select(owner));
|
||||
let _ = app.update(Message::Page(Page::Behavior));
|
||||
choose(&mut app, Representation::Saved { diagram: a });
|
||||
let _ = app.update(Message::Select(a));
|
||||
app.window_size = Size::new(1440., 1000.);
|
||||
(app, a, b, owner)
|
||||
}
|
||||
fn send(app: &mut App, event: impl FnOnce(Context) -> CanvasEvent) {
|
||||
let context = app.scenario_context().unwrap();
|
||||
let _ = app.update(Message::ScenarioCanvas(event(context)));
|
||||
}
|
||||
fn choose(app: &mut App, representation: Representation) {
|
||||
send(app, |context| {
|
||||
CanvasEvent::Representation(context, representation)
|
||||
});
|
||||
}
|
||||
fn model_state(app: &App) -> (u64, bool, bool, bool) {
|
||||
(
|
||||
app.document.revision(),
|
||||
app.document.dirty(),
|
||||
app.document.can_undo(),
|
||||
app.document.can_redo(),
|
||||
)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn actual_arrangement_picker_follows_selected_saved_record_and_opens_b_properties() {
|
||||
let (mut app, a, b, _) = setup();
|
||||
let mut mounted = Mounted::new(&mut app).await;
|
||||
let snapshot = app.document.snapshot();
|
||||
let before = model_state(&app);
|
||||
let captured = app.scenario_context().unwrap();
|
||||
let picker = mounted.geometry(&app).bounds("scenario-arrangement-picker");
|
||||
assert!(
|
||||
app.window_size.height - (picker.y + picker.height) > picker.y,
|
||||
"fixture has room for Iced's menu below the actual picker"
|
||||
);
|
||||
assert_eq!(app.selected, a);
|
||||
mounted.click(&mut app, picker.center()).await;
|
||||
// iced_widget 0.14.2 overlay/menu.rs uses default line height plus
|
||||
// vertical padding for each row. The real workspace orders Automatic,
|
||||
// then the fixture's A/B/C Diagram IDs. Select B from the actual overlay.
|
||||
let row_height =
|
||||
f32::from(iced::advanced::text::LineHeight::default().to_absolute(12.into())) + 10.;
|
||||
let b_row = Point::new(
|
||||
picker.center_x(),
|
||||
picker.y + picker.height + row_height * 2.5,
|
||||
);
|
||||
assert!(b_row.y < app.window_size.height);
|
||||
let events = mounted.click(&mut app, b_row).await;
|
||||
assert!(events.iter().any(|event| matches!(event,
|
||||
Message::ScenarioCanvas(CanvasEvent::Representation(context, Representation::Saved { diagram }))
|
||||
if *context == captured && *diagram == b)), "must exercise the actual arrangement callback");
|
||||
assert_eq!(app.selected, b);
|
||||
assert_eq!(app.draft.as_ref().unwrap().original, b);
|
||||
assert_eq!(
|
||||
app.draft.as_ref().unwrap().value["name"],
|
||||
"B - Incident review negative and far"
|
||||
);
|
||||
assert_eq!(app.active_diagram, Some(b));
|
||||
assert_eq!(
|
||||
app.scenario_context().unwrap().representation,
|
||||
Representation::Saved { diagram: b }
|
||||
);
|
||||
assert_ne!(app.scenario_context().unwrap().visit, captured.visit);
|
||||
|
||||
send(&mut app, CanvasEvent::Focus);
|
||||
send(&mut app, |context| {
|
||||
CanvasEvent::Shelf(context, Shelf::Selection)
|
||||
});
|
||||
mounted.settle(&mut app).await;
|
||||
let geometry = mounted.geometry(&app);
|
||||
let (details, offset) = geometry.scrolls[&widget::Id::new("scenario-details")];
|
||||
let point = geometry.bounds("scenario-properties").center() - offset;
|
||||
assert!(details.contains(point));
|
||||
let events = mounted.click(&mut app, point).await;
|
||||
assert!(events.iter().any(|event| matches!(event,
|
||||
Message::ScenarioCanvas(CanvasEvent::Properties(_, selected)) if *selected == b)));
|
||||
mounted.settle(&mut app).await;
|
||||
let geometry = mounted.geometry(&app);
|
||||
let (inspector, offset) = geometry.scrolls[&widget::Id::new("inspector")];
|
||||
assert!(
|
||||
geometry
|
||||
.text_inputs
|
||||
.iter()
|
||||
.any(|bounds| inspector.contains(bounds.center() - offset)),
|
||||
"current B Properties must expose an actual editable field"
|
||||
);
|
||||
assert!(!app.behavior.scenario.focused());
|
||||
assert!(
|
||||
!geometry
|
||||
.scrolls
|
||||
.contains_key(&widget::Id::new("scenario-details"))
|
||||
);
|
||||
assert_eq!(
|
||||
app.inspector_tab,
|
||||
crate::navigation::InspectorTab::Properties
|
||||
);
|
||||
assert_eq!(app.selected, b);
|
||||
assert_eq!(app.draft.as_ref().unwrap().original, b);
|
||||
assert!(Arc::ptr_eq(&snapshot, &app.document.snapshot()));
|
||||
assert_eq!(app.project(), &snapshot.decoded.project);
|
||||
assert_eq!(app.document.bundle(), &snapshot.bundle);
|
||||
assert_eq!(model_state(&app), before);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arrangement_change_preserves_independent_semantic_and_foreign_view_selection() {
|
||||
for alias in [
|
||||
"initial_report",
|
||||
"reporter",
|
||||
"incident_response",
|
||||
"incident_repair",
|
||||
] {
|
||||
let (mut app, _, b, _) = setup();
|
||||
let selected = id(&app, alias);
|
||||
let _ = app.update(Message::Select(selected));
|
||||
let snapshot = app.document.snapshot();
|
||||
let before = model_state(&app);
|
||||
choose(&mut app, Representation::Saved { diagram: b });
|
||||
assert_eq!(app.selected, selected, "independent selection {alias}");
|
||||
assert_eq!(app.draft.as_ref().unwrap().original, selected);
|
||||
assert_eq!(app.active_diagram, Some(b));
|
||||
assert!(Arc::ptr_eq(&snapshot, &app.document.snapshot()));
|
||||
assert_eq!(app.document.bundle(), &snapshot.bundle);
|
||||
assert_eq!(model_state(&app), before);
|
||||
if alias == "incident_repair" {
|
||||
assert!(!behavior::scenario::properties_target(
|
||||
app.project(),
|
||||
app.scenario_context().unwrap(),
|
||||
selected
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arrangement_follows_view_record_to_automatic_owner_but_not_owner_back_to_saved() {
|
||||
let (mut app, _, b, owner) = setup();
|
||||
let snapshot = app.document.snapshot();
|
||||
let before = model_state(&app);
|
||||
choose(&mut app, Representation::Derived { owner });
|
||||
assert_eq!(app.selected, owner);
|
||||
assert_eq!(app.draft.as_ref().unwrap().original, owner);
|
||||
assert_eq!(app.active_diagram, None);
|
||||
assert!(behavior::scenario::properties_target(
|
||||
app.project(),
|
||||
app.scenario_context().unwrap(),
|
||||
owner
|
||||
));
|
||||
choose(&mut app, Representation::Saved { diagram: b });
|
||||
assert_eq!(
|
||||
app.selected, owner,
|
||||
"Scenario owner is now independent semantic selection"
|
||||
);
|
||||
assert_eq!(app.draft.as_ref().unwrap().original, owner);
|
||||
assert_eq!(app.active_diagram, Some(b));
|
||||
assert!(Arc::ptr_eq(&snapshot, &app.document.snapshot()));
|
||||
assert_eq!(app.document.bundle(), &snapshot.bundle);
|
||||
assert_eq!(model_state(&app), before);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arrangement_applies_only_the_old_view_draft_then_follows_b_with_one_undo() {
|
||||
let (mut app, a, b, _) = setup();
|
||||
let snapshot = app.document.snapshot();
|
||||
let mut expected = app.project().clone();
|
||||
expected.diagrams.get_mut(&a).unwrap().name = "Reviewed A".into();
|
||||
let revision = app.document.revision();
|
||||
let _ = app.update(Message::Field("/name".into(), "Reviewed A".into()));
|
||||
choose(&mut app, Representation::Saved { diagram: b });
|
||||
assert_eq!(app.project(), &expected);
|
||||
assert_eq!(app.selected, b);
|
||||
assert_eq!(app.draft.as_ref().unwrap().original, b);
|
||||
assert!(!app.draft_dirty());
|
||||
assert_eq!(app.document.revision(), revision + 1);
|
||||
let accepted = app.document.snapshot();
|
||||
let _ = app.update(Message::Undo);
|
||||
assert_eq!(app.project(), &snapshot.decoded.project);
|
||||
assert_eq!(app.document.bundle(), &snapshot.bundle);
|
||||
assert!(!app.document.can_undo() && app.document.can_redo());
|
||||
let _ = app.update(Message::Redo);
|
||||
assert_eq!(app.project(), &expected);
|
||||
assert_eq!(app.document.bundle(), &accepted.bundle);
|
||||
assert!(app.document.can_undo() && !app.document.can_redo());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn same_arrangement_uuid_edit_follows_the_reidentified_record_and_round_trips_source() {
|
||||
let (mut app, a, _, _) = setup();
|
||||
let snapshot = app.document.snapshot();
|
||||
let replacement = Id::parse_str("70200000-0000-4000-8000-000000000099").unwrap();
|
||||
let mut expected = app.project().clone();
|
||||
let mut diagram = expected.diagrams.remove(&a).unwrap();
|
||||
diagram.id = replacement;
|
||||
expected.diagrams.insert(replacement, diagram);
|
||||
let _ = app.update(Message::Field("/id".into(), replacement.to_string()));
|
||||
choose(&mut app, Representation::Saved { diagram: a });
|
||||
assert_eq!(app.project(), &expected);
|
||||
assert_eq!(app.selected, replacement);
|
||||
assert_eq!(app.draft.as_ref().unwrap().original, replacement);
|
||||
assert_eq!(app.active_diagram, Some(replacement));
|
||||
assert_eq!(
|
||||
app.scenario_context().unwrap().representation,
|
||||
Representation::Saved {
|
||||
diagram: replacement
|
||||
}
|
||||
);
|
||||
let accepted = app.document.snapshot();
|
||||
let _ = app.update(Message::Undo);
|
||||
assert_eq!(app.project(), &snapshot.decoded.project);
|
||||
assert_eq!(app.document.bundle(), &snapshot.bundle);
|
||||
assert!(!app.document.can_undo() && app.document.can_redo());
|
||||
let _ = app.update(Message::Redo);
|
||||
assert_eq!(app.project(), &expected);
|
||||
assert_eq!(app.document.bundle(), &accepted.bundle);
|
||||
assert!(!app.document.can_redo());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_draft_and_stale_arrangement_preserve_selection_inputs_and_accepted_state() {
|
||||
let (mut app, a, b, _) = setup();
|
||||
let old = app.scenario_context().unwrap();
|
||||
let snapshot = app.document.snapshot();
|
||||
let before = model_state(&app);
|
||||
let _ = app.update(Message::Field("/id".into(), "not-a-uuid".into()));
|
||||
let draft_value = app.draft.as_ref().unwrap().value.clone();
|
||||
choose(&mut app, Representation::Saved { diagram: b });
|
||||
assert_eq!(app.selected, a);
|
||||
assert_eq!(app.active_diagram, Some(a));
|
||||
assert_eq!(
|
||||
app.scenario_context().unwrap().representation,
|
||||
old.representation
|
||||
);
|
||||
assert_eq!(app.draft.as_ref().unwrap().value, draft_value);
|
||||
assert_eq!(app.draft.as_ref().unwrap().inputs["/id"].raw, "not-a-uuid");
|
||||
assert!(app.draft_dirty());
|
||||
assert!(Arc::ptr_eq(&snapshot, &app.document.snapshot()));
|
||||
assert_eq!(model_state(&app), before);
|
||||
let _ = app.update(Message::RevertProperties);
|
||||
choose(&mut app, Representation::Saved { diagram: b });
|
||||
let current = app.scenario_context().unwrap();
|
||||
let _ = app.update(Message::Field("/name".into(), "Retained B input".into()));
|
||||
let selected = app.selected;
|
||||
let _ = app.update(Message::ScenarioCanvas(CanvasEvent::Representation(
|
||||
old,
|
||||
Representation::Saved { diagram: a },
|
||||
)));
|
||||
assert_eq!(app.selected, selected);
|
||||
assert_eq!(app.active_diagram, Some(b));
|
||||
assert_eq!(app.scenario_context().unwrap().visit, current.visit);
|
||||
assert_eq!(
|
||||
app.draft.as_ref().unwrap().inputs["/name"].raw,
|
||||
"Retained B input"
|
||||
);
|
||||
assert!(Arc::ptr_eq(&snapshot, &app.document.snapshot()));
|
||||
assert_eq!(app.document.bundle(), &snapshot.bundle);
|
||||
assert_eq!(model_state(&app), before);
|
||||
}
|
||||
@@ -62,7 +62,7 @@ impl App {
|
||||
// Document prepares and writes the source candidate before
|
||||
// committing its snapshot. Failure leaves camera/selection
|
||||
// and source/history at their accepted values.
|
||||
match self.document.execute(command) {
|
||||
match self.document.execute(*command) {
|
||||
Ok(_) => {
|
||||
self.selected = selected;
|
||||
self.selected_node = None;
|
||||
|
||||
@@ -684,7 +684,7 @@ mod scenario_scroll {
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
actual,
|
||||
vec![if change < 2 || change >= 4 {
|
||||
vec![if !(2..4).contains(&change) {
|
||||
Vector::ZERO
|
||||
} else {
|
||||
Vector::new(0., 180.)
|
||||
|
||||
@@ -183,8 +183,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn tiny_fit_zoom_in_out_preserves_pointer_world_anchor_without_floor_jump() {
|
||||
let viewport = Some(Size::new(700., 500.));
|
||||
let start = fit(bounds(), viewport.unwrap()).unwrap();
|
||||
let size = Size::new(700., 500.);
|
||||
let viewport = Some(size);
|
||||
let start = fit(bounds(), size).unwrap();
|
||||
let (mut pan, mut zoom, mut fitting) = (Vector::ZERO, None, true);
|
||||
let at = Point::new(210., 180.);
|
||||
let world = Point::new(
|
||||
@@ -218,7 +219,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn pan_leaves_fit_from_displayed_position_and_zero_motion_preserves_fit() {
|
||||
let viewport = Some(Size::new(700., 500.));
|
||||
let size = Size::new(700., 500.);
|
||||
let viewport = Some(size);
|
||||
let (mut pan, mut zoom, mut fitting) = (Vector::ZERO, None, true);
|
||||
for event in [
|
||||
Event::Pan(Vector::ZERO),
|
||||
@@ -238,7 +240,7 @@ mod tests {
|
||||
));
|
||||
assert_eq!((pan, zoom, fitting), (Vector::ZERO, None, true));
|
||||
}
|
||||
let start = fit(bounds(), viewport.unwrap()).unwrap();
|
||||
let start = fit(bounds(), size).unwrap();
|
||||
assert!(change(
|
||||
&mut pan,
|
||||
&mut zoom,
|
||||
|
||||
@@ -89,7 +89,7 @@ pub(crate) enum Finish {
|
||||
Click(Id),
|
||||
Noop,
|
||||
Commit {
|
||||
command: Command,
|
||||
command: Box<Command>,
|
||||
selected: Id,
|
||||
scale: f32,
|
||||
pan: Vector,
|
||||
@@ -285,7 +285,7 @@ impl State {
|
||||
Ok(match session.preview {
|
||||
None => Finish::Noop,
|
||||
Some(preview) => Finish::Commit {
|
||||
command: preview.command,
|
||||
command: Box::new(preview.command),
|
||||
selected: session.drag.target.id(),
|
||||
scale: session.scale,
|
||||
pan: session.pan,
|
||||
|
||||
@@ -411,7 +411,7 @@ fn fractional_screen_packet_preserves_subpixel_motion_in_preview_and_finish() {
|
||||
assert_eq!(selected, message);
|
||||
assert_eq!(scale, 0.5);
|
||||
assert_eq!(pan.y.to_bits(), 0x43cb_0001);
|
||||
let committed = oa_core::execute_batch(doc.project(), &[command]).unwrap();
|
||||
let committed = oa_core::execute_batch(doc.project(), &[*command]).unwrap();
|
||||
assert_eq!(committed, expected);
|
||||
assert!(state.spacing_token().is_none());
|
||||
assert_eq!(doc.project(), &original.decoded.project);
|
||||
|
||||
@@ -10,10 +10,10 @@ fn add_occurrence(p: &mut Project, owner: Id, name: &str, body: OccurrenceBody)
|
||||
precondition: String::new(),
|
||||
postcondition: String::new(),
|
||||
};
|
||||
if let Some(steps) = p.elements.get_mut(&owner).unwrap().steps_mut() {
|
||||
if !steps.contains(&id) {
|
||||
steps.push(id);
|
||||
}
|
||||
if let Some(steps) = p.elements.get_mut(&owner).unwrap().steps_mut()
|
||||
&& !steps.contains(&id)
|
||||
{
|
||||
steps.push(id);
|
||||
}
|
||||
id
|
||||
}
|
||||
|
||||
@@ -262,13 +262,20 @@ pub fn view<'a>(app: &'a App, owner: Id, out: Column<'a, Message>) -> Element<'a
|
||||
.find(|choice| choice.representation == context.representation)
|
||||
.cloned();
|
||||
header = header.push(
|
||||
pick_list(choices, selected, move |choice: ArrangementChoice| {
|
||||
Message::ScenarioCanvas(CanvasEvent::Representation(context, choice.representation))
|
||||
})
|
||||
.placeholder("Choose arrangement")
|
||||
.text_size(12)
|
||||
.padding(5)
|
||||
.width(Fill),
|
||||
container(
|
||||
pick_list(choices, selected, move |choice: ArrangementChoice| {
|
||||
Message::ScenarioCanvas(CanvasEvent::Representation(
|
||||
context,
|
||||
choice.representation,
|
||||
))
|
||||
})
|
||||
.placeholder("Choose arrangement")
|
||||
.text_size(12)
|
||||
.padding(5)
|
||||
.width(Fill),
|
||||
)
|
||||
.width(Fill)
|
||||
.id("scenario-arrangement-picker"),
|
||||
);
|
||||
}
|
||||
if capabilities.geometry_navigation {
|
||||
|
||||
@@ -458,8 +458,7 @@ fn test_only_upstream_row_offsets_update_lifetime_segments_and_endpoints_without
|
||||
assert_eq!(scene.activations.len(), 2);
|
||||
for (call, reply) in [(c1, r1), (c2, r2)] {
|
||||
assert!(scene.activations.iter().any(|segment| {
|
||||
segment.call == call
|
||||
&& segment.finish == Finish::Reply(reply)
|
||||
segment.call == call && segment.finish == Finish::Reply(reply)
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,14 +14,14 @@ mod communication_limits;
|
||||
mod data_contracts;
|
||||
#[path = "http/port_anchors.rs"]
|
||||
mod port_anchors;
|
||||
#[path = "http/scenario_layout.rs"]
|
||||
mod scenario_layout;
|
||||
#[path = "http/semantic_transitions.rs"]
|
||||
mod semantic_transitions;
|
||||
#[path = "http/source6_views.rs"]
|
||||
mod source6_views;
|
||||
#[path = "http/source7_contracts.rs"]
|
||||
mod source7_contracts;
|
||||
#[path = "http/scenario_layout.rs"]
|
||||
mod scenario_layout;
|
||||
#[path = "http/source_projects.rs"]
|
||||
mod source_projects;
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# Scenario arrangement selection
|
||||
|
||||
An explicit arrangement change follows an inspector that is showing the current Saved Diagram record. Switching from Saved A to Saved B selects B and refreshes its Properties draft. Switching that view inspection to Automatic selects the Scenario owner, since Automatic has no Diagram record. An occurrence, lifeline, Scenario owner or independently selected foreign view keeps its selection. Returning from Automatic to a Saved view therefore preserves an already selected Scenario owner.
|
||||
|
||||
The follow intention is captured before the existing property Apply. The destination must pass the existing owner/context checks; a rejected draft or stale event cannot change selection. A same-view UUID edit follows the resulting Diagram identity. Successful entry refreshes the selected view's draft without a second Apply or a new document transaction. Passive page navigation and Back retain their existing behavior.
|
||||
|
||||
This corrects the actual Scenario picker (`CanvasEvent::Representation`). Previously, selecting A's whole-view usage and then choosing B left A selected, so the exact-current-view Details guard disabled Properties. Preserving that foreign view selection would make the inspector disagree with an explicit view change. Broadly selecting B after every arrangement change was rejected because it would discard an engineer's independent model-element inspection. The separate `Message::Diagram` handler already follows the current view, but its global UI picker excludes Scenario diagrams and cannot substitute for this workflow.
|
||||
|
||||
The regression module `app::scenario_details_tests` exercises the real picker overlay and Properties button, independent selections, Automatic, valid draft application and exact Undo/Redo, same-view UUID edits, and invalid/stale refusals. A container ID exposes the unchanged picker's bounds to the mounted evaluator. Native acceptance must retain the original A-record → Scenario arrangement B → Details → Properties workflow. Compilation and actual execution status belong to the linked review and [Gitea issue #24](http://217.177.34.108:3000/sorokin.oleg.edu/ARCADIA-Studio/issues/24).
|
||||
Reference in New Issue
Block a user