diff --git a/src/DynamoCore/Graph/Annotations/AnnotationModel.cs b/src/DynamoCore/Graph/Annotations/AnnotationModel.cs index eed05a32232..eb01b40f7cc 100644 --- a/src/DynamoCore/Graph/Annotations/AnnotationModel.cs +++ b/src/DynamoCore/Graph/Annotations/AnnotationModel.cs @@ -26,6 +26,11 @@ public class AnnotationModel : ModelBase double lastExpandedWidth = 0; + /// + /// The default height of the group's content area when collapsed. + /// + public const double CollapsedContentHeight = 82; + #region Properties /// @@ -65,16 +70,13 @@ public class AnnotationModel : ModelBase /// public override double Width { - get - { - return width; - } + get => width; set { if (width == value) return; width = value; - RaisePropertyChanged("Width"); + RaisePropertyChanged(nameof(Width)); } } @@ -85,16 +87,46 @@ public override double Width /// public override double Height { - get - { - return height; - } + get => height; set { if (height == value) return; height = value; - RaisePropertyChanged("Height"); + RaisePropertyChanged(nameof(Height)); + } + } + + private double minWidthOnCollapsed; + /// + /// Gets or sets the minimum width of the group when it is collapsed. + /// This value equals the combined width of the group's proxy input and output ports. + /// + public double MinWidthOnCollapsed + { + get => minWidthOnCollapsed; + set + { + if (minWidthOnCollapsed == value) return; + minWidthOnCollapsed = value; + } + } + + private double minCollapsedPortAreaHeight; + /// + /// Gets or sets the minimum height of the port area when the group is collapsed. + /// Used to calculate the total height of the collapsed group based on proxy ports + /// + public double MinCollapsedPortAreaHeight + { + get => minCollapsedPortAreaHeight; + set + { + if (minCollapsedPortAreaHeight != value) + { + minCollapsedPortAreaHeight = value; + RaisePropertyChanged(nameof(MinCollapsedPortAreaHeight)); + } } } @@ -339,7 +371,7 @@ public NodeModel PinnedNode /// public double WidthAdjustment { - get { return widthAdjustment; } + get => widthAdjustment; set { if (value == widthAdjustment) return; @@ -348,6 +380,34 @@ public double WidthAdjustment } } + private double widthAdjustmentCollapsed; + /// + /// Gets or sets the width adjustment applied when the group is collapsed + /// + public double WidthAdjustmentCollapsed + { + get => widthAdjustmentCollapsed; + set + { + if (value == widthAdjustmentCollapsed) return; + widthAdjustmentCollapsed = value; + } + } + + private double widthAdjustmentExpanded; + /// + /// Gets or sets the width adjustment applied when the group is expanded + /// + public double WidthAdjustmentExpanded + { + get => widthAdjustmentExpanded; + set + { + if (value == widthAdjustmentExpanded) return; + widthAdjustmentExpanded = value; + } + } + private double heightAdjustment; /// /// Adjustment margin to be added to the height of the @@ -356,7 +416,7 @@ public double WidthAdjustment /// public double HeightAdjustment { - get { return heightAdjustment; } + get => heightAdjustment; set { if (value == heightAdjustment) return; @@ -365,17 +425,51 @@ public double HeightAdjustment } } + private double heightAdjustmentCollapsed; + /// + /// Gets or sets the height adjustment applied when the group is collapsed + /// + public double HeightAdjustmentCollapsed + { + get => heightAdjustmentCollapsed; + set + { + if (value == heightAdjustmentCollapsed) return; + heightAdjustmentCollapsed = value; + } + } + + private double heightAdjustmentExpanded; + /// + /// Gets or sets the height adjustment applied when the group is expanded + /// + public double HeightAdjustmentExpanded + { + get => heightAdjustmentExpanded; + set + { + if (value == heightAdjustmentExpanded) return; + heightAdjustmentExpanded = value; + } + } + private bool isExpanded = true; /// /// Returns whether or not the group is expanded. /// public bool IsExpanded { - get { return isExpanded; } + get => isExpanded; set { isExpanded = value; - UpdateBoundaryFromSelection(); + + // When collapsing, we defer this until after the collapsed layout + // is computed in CollapsedAnnotationRectangle_IsVisibleChanged. + if (isExpanded) + { + UpdateBoundaryFromSelection(); + } UpdateErrorAndWarningIconVisibility(); } } @@ -444,6 +538,21 @@ internal set } } + private bool isResizedWhileCollapsed; + /// + /// Gets or sets a value indicating whether the group was manually resized while collapsed + /// + public bool IsResizedWhileCollapsed + { + get => isResizedWhileCollapsed; + set + { + if (isResizedWhileCollapsed == value) return; + isResizedWhileCollapsed = value; + RaisePropertyChanged(nameof(IsResizedWhileCollapsed)); + } + } + #endregion /// @@ -577,68 +686,72 @@ internal void UpdateGroupFrozenStatus() /// Updates the group boundary based on the nodes / notes selection. /// internal void UpdateBoundaryFromSelection() - { + { var selectedModelsList = nodes.ToList(); - - if (selectedModelsList.Any()) + if (!selectedModelsList.Any()) { - var groupModels = selectedModelsList.OrderBy(x => x.X).ToList(); + Width = 0; + Height = 0; + return; + } - //Shifting x by 10 and y to the height of textblock - var regionX = groupModels.Min(x => x.X) - ExtendSize; - //Increase the Y value by 10. This provides the extra space between - // a model and textbox. Otherwise there will be some overlap - var regionY = groupModels.Min(y => (y as NoteModel) == null ? (y.Y) : (y.Y - NoteYAdjustment)) - - ExtendSize - (TextBlockHeight == 0.0 ? MinTextHeight : TextBlockHeight); + var groupModels = selectedModelsList.OrderBy(x => x.X).ToList(); - //calculates the distance between the nodes - var xDistance = groupModels.Max(x => (x.X + x.Width)) - regionX; - var yDistance = groupModels.Max(y => (y as NoteModel) == null ? (y.Y + y.Height) : (y.Y + y.Height - NoteYAdjustment)) - regionY; - - // InitialTop is to store the Y value without the Textblock height - this.InitialTop = groupModels.Min(y => (y as NoteModel) == null ? (y.Y) : (y.Y - NoteYAdjustment)); + //Shifting x by 10 and y to the height of textblock + var regionX = groupModels.Min(x => x.X) - ExtendSize; + //Increase the Y value by 10. This provides the extra space between + // a model and textbox. Otherwise there will be some overlap + var regionY = groupModels.Min(y => (y as NoteModel) == null ? (y.Y) : (y.Y - NoteYAdjustment)) - + ExtendSize - (TextBlockHeight == 0.0 ? MinTextHeight : TextBlockHeight); + + //calculates the distance between the nodes + var xDistance = groupModels.Max(x => (x.X + x.Width)) - regionX; + + // InitialTop is to store the Y value without the Textblock height + this.InitialTop = groupModels.Min(y => (y as NoteModel) == null ? (y.Y) : (y.Y - NoteYAdjustment)); + bool positionChanged = regionX != X || regionY != Y; + this.X = regionX; + this.Y = regionY; + + if (IsExpanded) + { + var yDistance = groupModels.Max(y => (y as NoteModel) == null ? (y.Y + y.Height) : (y.Y + y.Height - NoteYAdjustment)) - regionY; var region = new Rect2D { X = regionX, Y = regionY, - Width = xDistance + ExtendSize + WidthAdjustment, + Width = xDistance + ExtendSize + Math.Max(WidthAdjustment, 0), Height = yDistance + ExtendSize + ExtendYHeight + HeightAdjustment - TextBlockHeight }; - bool positionChanged = region.X != X || region.Y != Y; - - this.X = region.X; - this.Y = region.Y; - this.ModelAreaHeight = IsExpanded ? region.Height : ModelAreaHeight; + this.ModelAreaHeight = region.Height; Height = this.ModelAreaHeight + TextBlockHeight; - - if (IsExpanded) - { - Width = Math.Max(region.Width, TextMaxWidth + ExtendSize); - lastExpandedWidth = Width; - } - else - { - //If the annotation is not expanded, then it will remain the same width of the last time it was expanded - Width = lastExpandedWidth; - } + Width = Math.Max(region.Width, TextMaxWidth + ExtendSize); //Initial Height is to store the Actual height of the group. //that is the height should be the initial height without the textblock height. if (this.InitialHeight <= 0.0) this.InitialHeight = region.Height; - - if (positionChanged) - { - RaisePropertyChanged(nameof(Position)); - } } else { - this.Width = 0; - this.Height = 0; + // Width is based on group content when collapsed + Width = Math.Max( + (!IsResizedWhileCollapsed ? xDistance : MinWidthOnCollapsed) + ExtendSize + WidthAdjustment, + TextMaxWidth + ExtendSize + ); + + ModelAreaHeight = MinCollapsedPortAreaHeight + CollapsedContentHeight + (IsResizedWhileCollapsed ? HeightAdjustment : 0); + Height = TextBlockHeight + ModelAreaHeight; + } + + lastExpandedWidth = Width; + + if (positionChanged) + { + RaisePropertyChanged(nameof(Position)); } } @@ -733,6 +846,9 @@ protected override bool UpdateValueCore(UpdateValueParams updateValueParams) case nameof(IsExpanded): IsExpanded = Convert.ToBoolean(value); break; + case nameof(IsResizedWhileCollapsed): + IsResizedWhileCollapsed = Convert.ToBoolean(value); + break; } return base.UpdateValueCore(updateValueParams); @@ -790,6 +906,7 @@ protected override void DeserializeCore(XmlElement element, SaveContext context) this.InitialHeight = helper.ReadDouble("InitialHeight", DoubleValue); this.IsSelected = helper.ReadBoolean(nameof(IsSelected), false); this.IsExpanded = helper.ReadBoolean(nameof(IsExpanded), true); + this.IsResizedWhileCollapsed = helper.ReadBoolean(nameof(IsResizedWhileCollapsed), false); if (IsSelected) DynamoSelection.Instance.Selection.Add(this); @@ -835,6 +952,7 @@ protected override void DeserializeCore(XmlElement element, SaveContext context) RaisePropertyChanged(nameof(AnnotationText)); RaisePropertyChanged(nameof(Nodes)); RaisePropertyChanged(nameof(IsExpanded)); + RaisePropertyChanged(nameof(IsResizedWhileCollapsed)); this.ReportPosition(); } diff --git a/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs b/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs index 26973696005..953e89b7a48 100644 --- a/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs +++ b/src/DynamoCore/Graph/Workspaces/WorkspaceModel.cs @@ -143,6 +143,11 @@ public class ExtraAnnotationViewInfo public string PinnedNode; public double WidthAdjustment; public double HeightAdjustment; + public double WidthAdjustmentCollapsed; + public double HeightAdjustmentCollapsed; + public double WidthAdjustmentExpanded; + public double HeightAdjustmentExpanded; + public bool IsResizedWhileCollapsed; // TODO, Determine if these are required public double Left; @@ -2703,6 +2708,11 @@ private void LoadAnnotation(ExtraAnnotationViewInfo annotationViewInfo) annotationModel.GUID = annotationGuidValue; annotationModel.HeightAdjustment = annotationViewInfo.HeightAdjustment; annotationModel.WidthAdjustment = annotationViewInfo.WidthAdjustment; + annotationModel.HeightAdjustmentCollapsed = annotationViewInfo.HeightAdjustmentCollapsed; + annotationModel.WidthAdjustmentCollapsed = annotationViewInfo.WidthAdjustmentCollapsed; + annotationModel.HeightAdjustmentExpanded = annotationViewInfo.HeightAdjustmentExpanded; + annotationModel.WidthAdjustmentExpanded = annotationViewInfo.WidthAdjustmentExpanded; + annotationModel.IsResizedWhileCollapsed = annotationViewInfo.IsResizedWhileCollapsed; annotationModel.UpdateGroupFrozenStatus(); annotationModel.ModelBaseRequested += annotationModel_GetModelBase; diff --git a/src/DynamoCore/PublicAPI.Unshipped.txt b/src/DynamoCore/PublicAPI.Unshipped.txt index f39f4279158..7a53584685a 100644 --- a/src/DynamoCore/PublicAPI.Unshipped.txt +++ b/src/DynamoCore/PublicAPI.Unshipped.txt @@ -47,6 +47,7 @@ const Dynamo.Engine.LibraryServices.Categories.Constructors = "Create" -> string const Dynamo.Engine.LibraryServices.Categories.MemberFunctions = "Actions" -> string const Dynamo.Engine.LibraryServices.Categories.Operators = "Operators" -> string const Dynamo.Engine.LibraryServices.Categories.Properties = "Query" -> string +const Dynamo.Graph.Annotations.AnnotationModel.CollapsedContentHeight = 82 -> double const Dynamo.Graph.Nodes.BuiltinNodeCategories.ANALYZE_SOLAR = "Analyze.Solar" -> string const Dynamo.Graph.Nodes.BuiltinNodeCategories.CORE = "Core" -> string const Dynamo.Graph.Nodes.BuiltinNodeCategories.CORE_EVALUATE = "Core.Evaluate" -> string @@ -700,6 +701,10 @@ Dynamo.Graph.Annotations.AnnotationModel.GroupStyleId.set -> void Dynamo.Graph.Annotations.AnnotationModel.HasNestedGroups.get -> bool Dynamo.Graph.Annotations.AnnotationModel.HeightAdjustment.get -> double Dynamo.Graph.Annotations.AnnotationModel.HeightAdjustment.set -> void +Dynamo.Graph.Annotations.AnnotationModel.HeightAdjustmentCollapsed.get -> double +Dynamo.Graph.Annotations.AnnotationModel.HeightAdjustmentCollapsed.set -> void +Dynamo.Graph.Annotations.AnnotationModel.HeightAdjustmentExpanded.get -> double +Dynamo.Graph.Annotations.AnnotationModel.HeightAdjustmentExpanded.set -> void Dynamo.Graph.Annotations.AnnotationModel.InitialHeight.get -> double Dynamo.Graph.Annotations.AnnotationModel.InitialHeight.set -> void Dynamo.Graph.Annotations.AnnotationModel.InitialTop.get -> double @@ -707,9 +712,15 @@ Dynamo.Graph.Annotations.AnnotationModel.InitialTop.set -> void Dynamo.Graph.Annotations.AnnotationModel.IsExpanded.get -> bool Dynamo.Graph.Annotations.AnnotationModel.IsExpanded.set -> void Dynamo.Graph.Annotations.AnnotationModel.IsFrozen.get -> bool +Dynamo.Graph.Annotations.AnnotationModel.IsResizedWhileCollapsed.get -> bool +Dynamo.Graph.Annotations.AnnotationModel.IsResizedWhileCollapsed.set -> void Dynamo.Graph.Annotations.AnnotationModel.IsVisible.get -> bool Dynamo.Graph.Annotations.AnnotationModel.loadFromXML.get -> bool Dynamo.Graph.Annotations.AnnotationModel.loadFromXML.set -> void +Dynamo.Graph.Annotations.AnnotationModel.MinCollapsedPortAreaHeight.get -> double +Dynamo.Graph.Annotations.AnnotationModel.MinCollapsedPortAreaHeight.set -> void +Dynamo.Graph.Annotations.AnnotationModel.MinWidthOnCollapsed.get -> double +Dynamo.Graph.Annotations.AnnotationModel.MinWidthOnCollapsed.set -> void Dynamo.Graph.Annotations.AnnotationModel.ModelAreaHeight.get -> double Dynamo.Graph.Annotations.AnnotationModel.ModelAreaHeight.set -> void Dynamo.Graph.Annotations.AnnotationModel.ModelBaseRequested -> System.Func @@ -725,6 +736,10 @@ Dynamo.Graph.Annotations.AnnotationModel.TextMaxWidth.get -> double Dynamo.Graph.Annotations.AnnotationModel.TextMaxWidth.set -> void Dynamo.Graph.Annotations.AnnotationModel.WidthAdjustment.get -> double Dynamo.Graph.Annotations.AnnotationModel.WidthAdjustment.set -> void +Dynamo.Graph.Annotations.AnnotationModel.WidthAdjustmentCollapsed.get -> double +Dynamo.Graph.Annotations.AnnotationModel.WidthAdjustmentCollapsed.set -> void +Dynamo.Graph.Annotations.AnnotationModel.WidthAdjustmentExpanded.get -> double +Dynamo.Graph.Annotations.AnnotationModel.WidthAdjustmentExpanded.set -> void Dynamo.Graph.ConnectorPinModel Dynamo.Graph.ConnectorPinModel.ConnectorId.get -> System.Guid Dynamo.Graph.ConnectorPinModel.ConnectorId.set -> void @@ -1280,10 +1295,13 @@ Dynamo.Graph.Workspaces.ExtraAnnotationViewInfo.GroupStyleId -> System.Guid Dynamo.Graph.Workspaces.ExtraAnnotationViewInfo.HasNestedGroups -> bool Dynamo.Graph.Workspaces.ExtraAnnotationViewInfo.Height -> double Dynamo.Graph.Workspaces.ExtraAnnotationViewInfo.HeightAdjustment -> double +Dynamo.Graph.Workspaces.ExtraAnnotationViewInfo.HeightAdjustmentCollapsed -> double +Dynamo.Graph.Workspaces.ExtraAnnotationViewInfo.HeightAdjustmentExpanded -> double Dynamo.Graph.Workspaces.ExtraAnnotationViewInfo.Id -> string Dynamo.Graph.Workspaces.ExtraAnnotationViewInfo.InitialHeight -> double Dynamo.Graph.Workspaces.ExtraAnnotationViewInfo.InitialTop -> double Dynamo.Graph.Workspaces.ExtraAnnotationViewInfo.IsExpanded -> bool +Dynamo.Graph.Workspaces.ExtraAnnotationViewInfo.IsResizedWhileCollapsed -> bool Dynamo.Graph.Workspaces.ExtraAnnotationViewInfo.Left -> double Dynamo.Graph.Workspaces.ExtraAnnotationViewInfo.Nodes -> System.Collections.Generic.IEnumerable Dynamo.Graph.Workspaces.ExtraAnnotationViewInfo.PinnedNode -> string @@ -1292,6 +1310,8 @@ Dynamo.Graph.Workspaces.ExtraAnnotationViewInfo.Title -> string Dynamo.Graph.Workspaces.ExtraAnnotationViewInfo.Top -> double Dynamo.Graph.Workspaces.ExtraAnnotationViewInfo.Width -> double Dynamo.Graph.Workspaces.ExtraAnnotationViewInfo.WidthAdjustment -> double +Dynamo.Graph.Workspaces.ExtraAnnotationViewInfo.WidthAdjustmentCollapsed -> double +Dynamo.Graph.Workspaces.ExtraAnnotationViewInfo.WidthAdjustmentExpanded -> double Dynamo.Graph.Workspaces.ExtraConnectorPinInfo Dynamo.Graph.Workspaces.ExtraConnectorPinInfo.ConnectorGuid -> string Dynamo.Graph.Workspaces.ExtraConnectorPinInfo.ExtraConnectorPinInfo() -> void diff --git a/src/DynamoCoreWpf/UI/Converters.cs b/src/DynamoCoreWpf/UI/Converters.cs index 9100638bd73..763ce4321d8 100644 --- a/src/DynamoCoreWpf/UI/Converters.cs +++ b/src/DynamoCoreWpf/UI/Converters.cs @@ -3891,21 +3891,32 @@ public object ConvertBack(object value, Type targetType, object parameter, Syste /// /// Returns a dark or light color depending on the contrast ration of the color with the background color - /// Contrast ration should be larger than 4.5:1 + /// If the control is a Thumb (passed via the converter parameter), the dark color is slightly different + /// Contrast ratio should be larger than 4.5:1 /// Contrast calculation algorithm from https://stackoverflow.com/questions/70187918/adapt-given-color-pairs-to-adhere-to-w3c-accessibility-standard-for-epubs/70192373#70192373 + /// + /// Expected values for controlType: + /// - "Thumb" : applies alternate dark color for thumb controls + /// - null : applies default dark color for annotation text and description /// public class TextForegroundSaturationColorConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { + var controlType = parameter as string; + var lightColor = (System.Windows.Media.Color)SharedDictionaryManager.DynamoColorsAndBrushesDictionary["WhiteColor"]; - var darkColor = (System.Windows.Media.Color)SharedDictionaryManager.DynamoColorsAndBrushesDictionary["DarkerGrey"]; + var darkColorText = (System.Windows.Media.Color)SharedDictionaryManager.DynamoColorsAndBrushesDictionary["DarkerGrey"]; + var darkColorThumb = (System.Windows.Media.Color)SharedDictionaryManager.DynamoColorsAndBrushesDictionary["MidGrey"]; var backgroundColor = (System.Windows.Media.Color)value; - var contrastRatio = GetContrastRatio(darkColor, backgroundColor); + var contrastRatio = GetContrastRatio(darkColorText, backgroundColor); - return contrastRatio < 4.5 ? new SolidColorBrush(lightColor) : new SolidColorBrush(darkColor); + if (contrastRatio < 4.5) + return new SolidColorBrush(lightColor); + else + return controlType == "Thumb" ? new SolidColorBrush(darkColorThumb) : new SolidColorBrush(darkColorText); } public object ConvertBack(object value, Type targetType, object parameter, diff --git a/src/DynamoCoreWpf/ViewModels/Core/AnnotationViewModel.cs b/src/DynamoCoreWpf/ViewModels/Core/AnnotationViewModel.cs index a908b901d13..da4e4627796 100644 --- a/src/DynamoCoreWpf/ViewModels/Core/AnnotationViewModel.cs +++ b/src/DynamoCoreWpf/ViewModels/Core/AnnotationViewModel.cs @@ -702,9 +702,15 @@ private void SetGroupInputPorts() // owner newPortViewModels = CreateProxyInPorts(originalInPorts); - if (newPortViewModels == null) return; + // Set the minimum required vertical space to fit the input ports when collapsed + if (newPortViewModels == null) + { + annotationModel.MinCollapsedPortAreaHeight = 0; + return; + } + + annotationModel.MinCollapsedPortAreaHeight = newPortViewModels.Sum(p => p.Height); InPorts.AddRange(newPortViewModels); - return; } /// @@ -717,7 +723,7 @@ private void SetGroupOutPorts() List newPortViewModels; // we need to store the original ports here - // as we need thoese later for when we + // as we need these later for when we // need to collapse the groups content if (this.AnnotationModel.HasNestedGroups) { @@ -735,13 +741,19 @@ private void SetGroupOutPorts() // Create proxies of the ports so we can // visually add them to the group but they - // should still reference their NodeModel - // owner + // should still reference their NodeModel owner newPortViewModels = CreateProxyOutPorts(originalOutPorts); - if (newPortViewModels == null) return; + // If no view models were created, we leave the existing height as is + if (newPortViewModels == null) + return; + + // Update the collapsed port area height to be the greater of the existing value + // and the height needed to render all output ports + var outportsHeight = newPortViewModels.Sum(p => p.Height); + annotationModel.MinCollapsedPortAreaHeight = Math.Max(annotationModel.MinCollapsedPortAreaHeight, outportsHeight); + OutPorts.AddRange(newPortViewModels); - return; } internal IEnumerable GetGroupInPorts(IEnumerable ownerNodes = null) @@ -976,8 +988,11 @@ private void CollapseConnectors() } } - private void RedrawConnectors() + private void RedrawConnectors(bool isCollapsedCheck = false) { + if (isCollapsedCheck && IsExpanded && !annotationModel.IsResizedWhileCollapsed) + return; + var allNodes = this.Nodes .OfType() .SelectMany(x => x.Nodes.OfType()) @@ -1068,6 +1083,7 @@ private void ManageAnnotationMVExpansionAndCollapse() if (annotationModel.IsExpanded) { + ToggleSizeAdjustmentsForCollapse(false); this.ShowGroupContents(); } else @@ -1075,6 +1091,9 @@ private void ManageAnnotationMVExpansionAndCollapse() this.SetGroupInputPorts(); this.SetGroupOutPorts(); this.CollapseGroupContents(true); + ToggleSizeAdjustmentsForCollapse(true); + + RaisePropertyChanged(nameof(Height)); RaisePropertyChanged(nameof(NodeContentCount)); } WorkspaceViewModel.HasUnsavedChanges = true; @@ -1084,6 +1103,35 @@ private void ManageAnnotationMVExpansionAndCollapse() ReportNodesPosition(); } + /// + /// Switches the width and height adjustment values when toggling + /// between collapsed and expanded group states. + /// + private void ToggleSizeAdjustmentsForCollapse(bool isCollapsing) + { + if (isCollapsing) + { + annotationModel.WidthAdjustmentExpanded = annotationModel.WidthAdjustment; + annotationModel.HeightAdjustmentExpanded = annotationModel.HeightAdjustment; + + // Reset to collapsed value so first resize ignores expanded height adjustment + annotationModel.HeightAdjustment = annotationModel.HeightAdjustmentCollapsed; + + if (annotationModel.IsResizedWhileCollapsed) + { + annotationModel.WidthAdjustment = annotationModel.WidthAdjustmentCollapsed; + } + } + else + { + annotationModel.WidthAdjustmentCollapsed = annotationModel.WidthAdjustment; + annotationModel.HeightAdjustmentCollapsed = annotationModel.HeightAdjustment; + + annotationModel.WidthAdjustment = annotationModel.WidthAdjustmentExpanded; + annotationModel.HeightAdjustment = annotationModel.HeightAdjustmentExpanded; + } + } + private void UpdateFontSize(object parameter) { if (parameter == null) return; @@ -1199,6 +1247,7 @@ private void model_PropertyChanged(object sender, System.ComponentModel.Property RaisePropertyChanged("Width"); RaisePropertyChanged(nameof(ModelAreaRect)); UpdateAllGroupedGroups(); + RedrawConnectors(false); break; case "Height": RaisePropertyChanged("Height"); diff --git a/src/DynamoCoreWpf/ViewModels/Core/Converters/SerializationConverters.cs b/src/DynamoCoreWpf/ViewModels/Core/Converters/SerializationConverters.cs index 0a44f399147..cb7429a3080 100644 --- a/src/DynamoCoreWpf/ViewModels/Core/Converters/SerializationConverters.cs +++ b/src/DynamoCoreWpf/ViewModels/Core/Converters/SerializationConverters.cs @@ -127,10 +127,20 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s writer.WriteValue(anno.AnnotationDescriptionText); writer.WritePropertyName(nameof(ExtraAnnotationViewInfo.IsExpanded)); writer.WriteValue(anno.IsExpanded); + writer.WritePropertyName(nameof(ExtraAnnotationViewInfo.IsResizedWhileCollapsed)); + writer.WriteValue(anno.IsResizedWhileCollapsed); writer.WritePropertyName(nameof(ExtraAnnotationViewInfo.WidthAdjustment)); writer.WriteValue(anno.WidthAdjustment); writer.WritePropertyName(nameof(ExtraAnnotationViewInfo.HeightAdjustment)); writer.WriteValue(anno.HeightAdjustment); + writer.WritePropertyName(nameof(ExtraAnnotationViewInfo.WidthAdjustmentCollapsed)); + writer.WriteValue(anno.WidthAdjustmentCollapsed); + writer.WritePropertyName(nameof(ExtraAnnotationViewInfo.HeightAdjustmentCollapsed)); + writer.WriteValue(anno.HeightAdjustmentCollapsed); + writer.WritePropertyName(nameof(ExtraAnnotationViewInfo.WidthAdjustmentExpanded)); + writer.WriteValue(anno.WidthAdjustmentExpanded); + writer.WritePropertyName(nameof(ExtraAnnotationViewInfo.HeightAdjustmentExpanded)); + writer.WriteValue(anno.HeightAdjustmentExpanded); writer.WritePropertyName("Nodes"); writer.WriteStartArray(); foreach (var m in anno.Nodes) diff --git a/src/DynamoCoreWpf/Views/Core/AnnotationView.xaml b/src/DynamoCoreWpf/Views/Core/AnnotationView.xaml index c783a5e6827..ca51ddb1ac0 100644 --- a/src/DynamoCoreWpf/Views/Core/AnnotationView.xaml +++ b/src/DynamoCoreWpf/Views/Core/AnnotationView.xaml @@ -403,6 +403,26 @@ + + - - - - + MouseLeave="Thumb_MouseLeave" /> @@ -976,7 +980,7 @@ - + @@ -1054,6 +1058,7 @@ Background="#EEEEEE" CornerRadius="{Binding Path=ActualHeight, ElementName=NodeCount}" Width="{Binding Path=ActualHeight, ElementName=NodeCount}" + VerticalAlignment="Bottom" Height="32" Margin="10"> + + diff --git a/src/DynamoCoreWpf/Views/Core/AnnotationView.xaml.cs b/src/DynamoCoreWpf/Views/Core/AnnotationView.xaml.cs index 8c5dfbc89d0..ee9e4fd3636 100644 --- a/src/DynamoCoreWpf/Views/Core/AnnotationView.xaml.cs +++ b/src/DynamoCoreWpf/Views/Core/AnnotationView.xaml.cs @@ -45,7 +45,6 @@ public AnnotationView() // Because the size of the CollapsedAnnotationRectangle doesn't necessarily change // when going from Visible to collapse (and other way around), we need to also listen // to IsVisibleChanged. Both of these handlers will set the ModelAreaHeight on the ViewModel - this.CollapsedAnnotationRectangle.SizeChanged += CollapsedAnnotationRectangle_SizeChanged; this.CollapsedAnnotationRectangle.IsVisibleChanged += CollapsedAnnotationRectangle_IsVisibleChanged; } @@ -54,7 +53,6 @@ private void AnnotationView_Unloaded(object sender, RoutedEventArgs e) Loaded -= AnnotationView_Loaded; DataContextChanged -= AnnotationView_DataContextChanged; this.GroupTextBlock.SizeChanged -= GroupTextBlock_SizeChanged; - this.CollapsedAnnotationRectangle.SizeChanged -= CollapsedAnnotationRectangle_SizeChanged; this.CollapsedAnnotationRectangle.IsVisibleChanged -= CollapsedAnnotationRectangle_IsVisibleChanged; } @@ -375,25 +373,23 @@ private void SetTextHeight() this.GroupNameControl.DesiredSize.Height; } - private void CollapsedAnnotationRectangle_SizeChanged(object sender, SizeChangedEventArgs e) - { - SetModelAreaHeight(); - } - private void CollapsedAnnotationRectangle_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) { - SetModelAreaHeight(); - } + var model = ViewModel.AnnotationModel; + if (!model.IsExpanded) + { + // Measure port widths and update min width + Dispatcher.BeginInvoke(new Action(() => + { + var (maxInPortWidth, totalInPortHeight) = MeasurePortBounds(inputPortControl); + var (maxOutPortWidth, totalOutPortHeight) = MeasurePortBounds(outputPortControl); - private void SetModelAreaHeight() - { - // We only want to change the ModelAreaHeight - // if the CollapsedAnnotationRectangle is visible, - // as if its not it will be equal to the height of the - // contained nodes + the TextBlockHeight - if (ViewModel is null || !this.CollapsedAnnotationRectangle.IsVisible) return; - ViewModel.ModelAreaHeight = this.CollapsedAnnotationRectangle.ActualHeight; - ViewModel.AnnotationModel.UpdateBoundaryFromSelection(); + model.MinWidthOnCollapsed = maxInPortWidth + maxOutPortWidth + 10; + model.MinCollapsedPortAreaHeight = Math.Max(totalInPortHeight, totalOutPortHeight); + + model.UpdateBoundaryFromSelection(); + }), System.Windows.Threading.DispatcherPriority.ApplicationIdle); + } } private void contextMenu_Click(object sender, RoutedEventArgs e) @@ -422,6 +418,30 @@ private void AnnotationRectangleThumb_DragDelta(object sender, System.Windows.Co } } + private void CollapsedAnnotationRectangleThumb_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e) + { + // Mark the group as being resized while collapsed + ViewModel.AnnotationModel.IsResizedWhileCollapsed = true; + + var model = ViewModel.AnnotationModel; + var xAdjust = ViewModel.Width + e.HorizontalChange; + var yAdjust = ViewModel.Height + e.VerticalChange; + + double minHeight = model.MinCollapsedPortAreaHeight + model.TextBlockHeight + AnnotationModel.CollapsedContentHeight; + + if (xAdjust >= Math.Max(model.TextMaxWidth, model.MinWidthOnCollapsed)) + { + model.WidthAdjustment += e.HorizontalChange; + ViewModel.WorkspaceViewModel.HasUnsavedChanges = true; + } + + if (yAdjust >= minHeight) + { + model.HeightAdjustment += e.VerticalChange; + ViewModel.WorkspaceViewModel.HasUnsavedChanges = true; + } + } + private void Thumb_MouseEnter(object sender, MouseEventArgs e) { ViewModel.WorkspaceViewModel.CurrentCursor = CursorLibrary.GetCursor(CursorSet.ResizeDiagonal); @@ -471,5 +491,25 @@ private void GroupStyleAnnotation_SubmenuOpened(object sender, RoutedEventArgs e // Tracking loading group style items Logging.Analytics.TrackEvent(Actions.Load, Categories.GroupStyleOperations, nameof(GroupStyleItem) + "s"); } + + private (double maxWidth, double totalHeight) MeasurePortBounds(ItemsControl portControl) + { + double maxWidth = 0; + double totalHeight = 0; + + foreach (var item in portControl.Items) + { + if (portControl.ItemContainerGenerator.ContainerFromItem(item) is FrameworkElement container && container.IsVisible) + { + container.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); + var size = container.DesiredSize; + + maxWidth = Math.Max(maxWidth, size.Width); + totalHeight += size.Height; + } + } + + return (maxWidth, totalHeight); + } } }