diff --git a/src/DynamoCore/Core/UndoRedoRecorder.cs b/src/DynamoCore/Core/UndoRedoRecorder.cs index 2ef56d8c5a4..d5606fb03de 100644 --- a/src/DynamoCore/Core/UndoRedoRecorder.cs +++ b/src/DynamoCore/Core/UndoRedoRecorder.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Xml; diff --git a/src/DynamoCore/Graph/Annotations/AnnotationModel.cs b/src/DynamoCore/Graph/Annotations/AnnotationModel.cs index 1dc80cce685..599db37962e 100644 --- a/src/DynamoCore/Graph/Annotations/AnnotationModel.cs +++ b/src/DynamoCore/Graph/Annotations/AnnotationModel.cs @@ -262,7 +262,6 @@ public double TextBlockHeight } } - private double textMaxWidth; /// /// Returns the maxWidth of text area of the group @@ -657,6 +656,9 @@ protected override bool UpdateValueCore(UpdateValueParams updateValueParams) case nameof(AnnotationDescriptionText): AnnotationDescriptionText = value; break; + case nameof(IsExpanded): + IsExpanded = Convert.ToBoolean(value); + break; } return base.UpdateValueCore(updateValueParams); @@ -680,6 +682,7 @@ void SerializeCore(XmlElement element, SaveContext context) helper.SetAttribute("TextblockHeight", this.TextBlockHeight); helper.SetAttribute("backgrouund", (this.Background == null ? "" : this.Background.ToString())); helper.SetAttribute(nameof(IsSelected), IsSelected); + helper.SetAttribute(nameof(IsExpanded), this.IsExpanded); //Serialize Selected models XmlDocument xmlDoc = element.OwnerDocument; @@ -711,12 +714,13 @@ protected override void DeserializeCore(XmlElement element, SaveContext context) this.textBlockHeight = helper.ReadDouble("TextblockHeight", DoubleValue); this.InitialTop = helper.ReadDouble("InitialTop", DoubleValue); this.InitialHeight = helper.ReadDouble("InitialHeight", DoubleValue); - this.IsSelected = helper.ReadBoolean(nameof(IsSelected), false); + this.IsSelected = helper.ReadBoolean(nameof(IsSelected), false); + this.IsExpanded = helper.ReadBoolean(nameof(IsExpanded), true); if (IsSelected) DynamoSelection.Instance.Selection.Add(this); else - DynamoSelection.Instance.Selection.Remove(this); + DynamoSelection.Instance.Selection.Remove(this); //Deserialize Selected models if (element.HasChildNodes) @@ -756,6 +760,7 @@ protected override void DeserializeCore(XmlElement element, SaveContext context) RaisePropertyChanged(nameof(GroupStyleId)); RaisePropertyChanged(nameof(AnnotationText)); RaisePropertyChanged(nameof(Nodes)); + RaisePropertyChanged(nameof(IsExpanded)); this.ReportPosition(); } diff --git a/src/DynamoCoreWpf/ViewModels/Core/AnnotationViewModel.cs b/src/DynamoCoreWpf/ViewModels/Core/AnnotationViewModel.cs index ee95fe3f69b..5c208f3abbe 100644 --- a/src/DynamoCoreWpf/ViewModels/Core/AnnotationViewModel.cs +++ b/src/DynamoCoreWpf/ViewModels/Core/AnnotationViewModel.cs @@ -260,25 +260,20 @@ public bool IsExpanded get => annotationModel.IsExpanded; set { - annotationModel.IsExpanded = value; - InPorts.Clear(); - OutPorts.Clear(); - if (value) + // This change is triggered by the user interaction in the View. + // Before we updating the value in the Model and ViewModel + // we record the current state in the UndoRedoStack. + // This ensures that any modifications can be reverted by the user. + var undoRecorder = WorkspaceViewModel.Model.UndoRecorder; + using (undoRecorder.BeginActionGroup()) { - this.ShowGroupContents(); + undoRecorder.RecordModificationForUndo(annotationModel); } - else - { - this.SetGroupInputPorts(); - this.SetGroupOutPorts(); - this.CollapseGroupContents(true); - RaisePropertyChanged(nameof(NodeContentCount)); - } - WorkspaceViewModel.HasUnsavedChanges = true; - AddGroupToGroupCommand.RaiseCanExecuteChanged(); - RaisePropertyChanged(nameof(IsExpanded)); - RedrawConnectors(); - ReportNodesPosition(); + + annotationModel.IsExpanded = value; + + // Methods to collapse or expand the group based on the new value of IsExpanded. + ManageAnnotationMVExpansionAndCollapse(); } } @@ -1051,6 +1046,36 @@ private void UpdateConnectorsAndPortsOnShowContents(IEnumerable nodes } } + + /// + /// Manages the expansion or collapse of the annotation group in the view model. + /// + private void ManageAnnotationMVExpansionAndCollapse() + { + if (InPorts.Any() || OutPorts.Any()) + { + InPorts.Clear(); + OutPorts.Clear(); + } + + if (annotationModel.IsExpanded) + { + this.ShowGroupContents(); + } + else + { + this.SetGroupInputPorts(); + this.SetGroupOutPorts(); + this.CollapseGroupContents(true); + RaisePropertyChanged(nameof(NodeContentCount)); + } + WorkspaceViewModel.HasUnsavedChanges = true; + AddGroupToGroupCommand.RaiseCanExecuteChanged(); + RaisePropertyChanged(nameof(IsExpanded)); + RedrawConnectors(); + ReportNodesPosition(); + } + private void UpdateFontSize(object parameter) { if (parameter == null) return; @@ -1203,6 +1228,10 @@ private void model_PropertyChanged(object sender, System.ComponentModel.Property RaisePropertyChanged(nameof(AnnotationModel.Position)); UpdateProxyPortsPosition(); break; + case nameof(IsExpanded): + ManageAnnotationMVExpansionAndCollapse(); + break; + } } diff --git a/src/DynamoCoreWpf/Views/Core/AnnotationView.xaml.cs b/src/DynamoCoreWpf/Views/Core/AnnotationView.xaml.cs index e11e93d31fb..8c5dfbc89d0 100644 --- a/src/DynamoCoreWpf/Views/Core/AnnotationView.xaml.cs +++ b/src/DynamoCoreWpf/Views/Core/AnnotationView.xaml.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Windows; using System.Windows.Controls; using System.Windows.Data; diff --git a/test/DynamoCoreWpfTests/AnnotationViewModelTests.cs b/test/DynamoCoreWpfTests/AnnotationViewModelTests.cs index 6aa8a7e0373..845a254f876 100644 --- a/test/DynamoCoreWpfTests/AnnotationViewModelTests.cs +++ b/test/DynamoCoreWpfTests/AnnotationViewModelTests.cs @@ -705,7 +705,44 @@ public void UndoDeletingTheGroupShouldBringTheGroupAndModelsBack() annotation = ViewModel.Model.CurrentWorkspace.Annotations.FirstOrDefault(); Assert.IsNotNull(annotation); Assert.AreEqual(1, annotation.Nodes.Count()); + } + /// + /// Tests the Undo functionality for group collapse/expand and rename actions + /// + [Test] + [Category("DynamoUI")] + public void UndoGroupCollapseAndRenameShouldRestorePreviousStates() + { + OpenModel(@"core\annotationViewModelTests\groupsTestFile.dyn"); + + string newName = "A1B2C3"; + var workspaceVm = ViewModel.CurrentSpaceViewModel; + var groupVm = workspaceVm.Annotations.First(); + groupVm.IsExpanded = true; + + // Assert that initial conditions are met + Assert.IsNotNull(groupVm, "Expected an initial group to be present"); + Assert.IsTrue(groupVm.IsExpanded, "Group should be expanded"); + Assert.IsFalse(groupVm.AnnotationText.Equals(newName)); + + // Rename and collapse the group + groupVm.AnnotationText = newName; + groupVm.IsExpanded = false; + + // Assert initial action + Assert.IsFalse(groupVm.IsExpanded, "Group should be collapsed"); + Assert.AreEqual(newName, groupVm.AnnotationText, "Group should be renamed"); + + // Undo collapse + ViewModel.UndoCommand.Execute(null); + Assert.IsTrue(groupVm.IsExpanded, "Group should be expanded after undo"); + Assert.AreEqual(newName, groupVm.AnnotationText, "Name should remain renamed after undo"); + + // Undo rename + ViewModel.UndoCommand.Execute(null); + Assert.IsTrue(groupVm.IsExpanded, "Group should remain expanded after second undo"); + Assert.AreNotEqual(newName, groupVm.AnnotationText, "Group name should revert to original"); } [Test]