diff --git a/src/XTMF2.GUI/Controls/ModelSystemCanvas/ModelSystemCanvas.Input.cs b/src/XTMF2.GUI/Controls/ModelSystemCanvas/ModelSystemCanvas.Input.cs index 24ba9f4..5f45da7 100644 --- a/src/XTMF2.GUI/Controls/ModelSystemCanvas/ModelSystemCanvas.Input.cs +++ b/src/XTMF2.GUI/Controls/ModelSystemCanvas/ModelSystemCanvas.Input.cs @@ -949,6 +949,17 @@ protected override void OnPointerReleased(PointerReleasedEventArgs e) return; } + // ── Left-button release: end element resize ────────────────────── + if (_resizing is not null) + { + _resizing.CommitResize(); + _resizing = null; + e.Pointer.Capture(null); + InvalidateAndMeasure(); + e.Handled = true; + return; + } + // ── Left-button release: end element drag ───────────────────────── if (_dragging is null) return; if (_vm is null) return; diff --git a/src/XTMF2.GUI/Controls/ModelSystemCanvas/ModelSystemCanvas.Rendering.cs b/src/XTMF2.GUI/Controls/ModelSystemCanvas/ModelSystemCanvas.Rendering.cs index bd4f1ff..b94f7f9 100644 --- a/src/XTMF2.GUI/Controls/ModelSystemCanvas/ModelSystemCanvas.Rendering.cs +++ b/src/XTMF2.GUI/Controls/ModelSystemCanvas/ModelSystemCanvas.Rendering.cs @@ -676,6 +676,22 @@ private void RenderLinks(DrawingContext ctx) // or whose destination node is inlined (value shown in hook row instead). if (link.Destination is null) continue; if (link.Destination is NodeViewModel destNvm && destNvm.IsInlined) continue; + + // Check if the destination's underlying model belongs to the current boundary. + // Using ContainedWithin (rather than collection membership) keeps undo working: + // after an undo the boundary manager creates a fresh ViewModel for the returned + // node, so the stale VM in link.Destination is no longer in the Nodes collection + // but its UnderlyingNode.ContainedWithin is updated back to the current boundary. + bool destinationInCurrentView = link.Destination switch + { + NodeViewModel nvm => nvm.UnderlyingNode.ContainedWithin == _vm.CurrentBoundary, + StartViewModel svm => svm.UnderlyingStart.ContainedWithin == _vm.CurrentBoundary, + GhostNodeViewModel gvm => gvm.UnderlyingGhostNode.ContainedWithin == _vm.CurrentBoundary, + FunctionInstanceViewModel fivm => fivm.UnderlyingInstance.ContainedWithin == _vm.CurrentBoundary, + FunctionParameterViewModel fpvm => fpvm.UnderlyingParameter.ContainedWithin == _vm.CurrentBoundary, + _ => false + }; + if (!destinationInCurrentView) continue; bool isDisabled = link.UnderlyingLink.IsDisabled; var brush = link.IsSelected diff --git a/src/XTMF2.GUI/Views/TypePickerDialog.axaml.cs b/src/XTMF2.GUI/Views/TypePickerDialog.axaml.cs index f0ec206..358d2d7 100644 --- a/src/XTMF2.GUI/Views/TypePickerDialog.axaml.cs +++ b/src/XTMF2.GUI/Views/TypePickerDialog.axaml.cs @@ -276,6 +276,10 @@ public TypePickerDialog( { TypeListBox.SelectedIndex = idx; TypeListBox.ScrollIntoView(TypeListBox.SelectedItem!); + // Explicitly call OnListSelectionChanged to ensure CanOK is updated. + // This is needed because if the item is already at the selected index, + // SelectionChanged won't fire. + OnListSelectionChanged(); } } }; @@ -357,6 +361,7 @@ private void UpdateFilter() FilteredTypes = new ObservableCollection(source); PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(HasNoResults))); + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CanOK))); // Keep the list box in sync — auto-select first match. if (FilteredTypes.Count > 0)