diff --git a/src/DynamoCoreWpf/UI/Converters.cs b/src/DynamoCoreWpf/UI/Converters.cs index b7572b2385b..7a8997456dd 100644 --- a/src/DynamoCoreWpf/UI/Converters.cs +++ b/src/DynamoCoreWpf/UI/Converters.cs @@ -1482,7 +1482,7 @@ public object Convert(object value, Type targetType, object parameter, System.Gl { if (value is int zero) { - return zero == 0 ? Visibility.Collapsed : Visibility.Visible; + return zero == 0 ? Visibility.Collapsed : Visibility.Visible; } return Visibility.Collapsed; // If not int or int not zero, return collapsed. @@ -1844,7 +1844,7 @@ public object ConvertBack(object value, Type targetType, object parameter, Syste throw new NotSupportedException(); } } - + public class ZoomToOpacityConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) @@ -1875,7 +1875,7 @@ public object Convert(object value, Type targetType, object parameter, System.Gl if (number > Configurations.ZoomThreshold) return Visibility.Collapsed; - return Visibility.Visible; + return Visibility.Visible; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) @@ -1884,6 +1884,31 @@ public object ConvertBack(object value, Type targetType, object parameter, Syste } } + public class BooleanStyleConverter : IValueConverter + { + public Style TrueStyle { get; set; } + public Style FalseStyle { get; set; } + + //public static readonly DependencyProperty TestValueProperty = DependencyProperty + // .Register(nameof(TestValue), typeof(bool), typeof(BooleanStyleConverter)); + + //public bool TestValue { get => (bool)GetValue(TestValueProperty); set => SetValue(TestValueProperty, value); } + + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + if (value is bool v && v) + { + return TrueStyle; + } + return FalseStyle; + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + return false; + } + } + public class PortNameToWidthConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) @@ -2201,7 +2226,7 @@ public object Convert(object[] values, Type targetType, object parameter, Cultur if (values != null && values.Count() > 0) { if (!string.IsNullOrEmpty((string)values[0]) || !string.IsNullOrEmpty((string)values[1])) - return Visibility.Visible; ; + return Visibility.Visible; ; } return Visibility.Collapsed; } @@ -2798,7 +2823,7 @@ public object Convert(object value, Type targetType, object parameter, CultureIn catch (Exception e) { - Console.WriteLine($"problem attempting to parse fontsize or param {value} {parameter} { e.Message}"); + Console.WriteLine($"problem attempting to parse fontsize or param {value} {parameter} {e.Message}"); return false; } @@ -2877,7 +2902,7 @@ public object Convert(object[] values, Type targetType, object parameter, Cultur //just use defaults, this will enable the text editor. catch (Exception e) { - Console.WriteLine($"problem attempting to parse fontsize or zoom {values[1]} {values[0]}. { e.Message}"); + Console.WriteLine($"problem attempting to parse fontsize or zoom {values[1]} {values[0]}. {e.Message}"); } var factor = zoom * fontsize; @@ -3553,7 +3578,7 @@ public object ConvertBack(object value, Type targetType, object parameter, Syste { throw new NotImplementedException(); } - } + } /// /// Converts the object type to forground color for the object. @@ -3579,7 +3604,7 @@ public object Convert(object[] values, Type targetType, object parameter, System case nameof(TypeCode.String): return resourceDictionary["stringLabelBackground"] as SolidColorBrush; case nameof(TypeCode.Boolean): - return resourceDictionary["boolLabelBackground"] as SolidColorBrush; + return resourceDictionary["boolLabelBackground"] as SolidColorBrush; default: if (values[1].ToString() == "List") { @@ -3596,7 +3621,7 @@ public object Convert(object[] values, Type targetType, object parameter, System return resourceDictionary["PrimaryCharcoal200Brush"] as SolidColorBrush; } } - + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { throw new NotImplementedException(); @@ -3649,9 +3674,9 @@ public object Convert(object value, Type targetType, object parameter, CultureIn if (value.Equals(parameter)) { return new SolidColorBrush(Color.FromRgb(217, 217, 217)); - } + } return new SolidColorBrush(Color.FromRgb(71, 71, 71)); - + } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { @@ -4015,7 +4040,7 @@ public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is string nullOrEmptyString && String.IsNullOrEmpty(nullOrEmptyString)) return Visibility.Visible; - + return Visibility.Collapsed; } diff --git a/src/DynamoCoreWpf/UI/Themes/Modern/DynamoModern.xaml b/src/DynamoCoreWpf/UI/Themes/Modern/DynamoModern.xaml index ceee129adf0..553c45ae650 100644 --- a/src/DynamoCoreWpf/UI/Themes/Modern/DynamoModern.xaml +++ b/src/DynamoCoreWpf/UI/Themes/Modern/DynamoModern.xaml @@ -7,7 +7,8 @@ xmlns:fa="clr-namespace:FontAwesome5;assembly=FontAwesome5.Net" xmlns:nodes="clr-namespace:Dynamo.Nodes;assembly=DynamoCoreWpf" xmlns:p="clr-namespace:Dynamo.Wpf.Properties;assembly=DynamoCoreWpf" - xmlns:ui="clr-namespace:Dynamo.UI;assembly=DynamoCoreWpf"> + xmlns:ui="clr-namespace:Dynamo.UI;assembly=DynamoCoreWpf" + xmlns:conv="clr-namespace:Dynamo.Controls;assembly=DynamoCoreWpf"> @@ -866,25 +867,31 @@ - - - - + + - + + + + - + + + + - - - + + - + + + + - + GetNodeByInputId(string inputId, JObject jsonWorkspace) + Tuple GetNodeByInputId(string inputId, JObject jsonWorkspace) { var nodes = jsonWorkspace["Nodes"]; @@ -441,7 +441,7 @@ Tuple GetNodeByInputId(string inputId, JObject jsonWorkspace) } connectedInputIndex++; } - } + } } return new Tuple(nodeId, connectedInputIndex); @@ -479,6 +479,10 @@ public double Zoom } } + [JsonIgnore] + public bool StopAnimations { get => stopAnimations; set { stopAnimations = value; RaisePropertyChanged(nameof(StopAnimations)); } } + private bool stopAnimations = false; + [JsonIgnore] public bool CanZoomIn { @@ -556,7 +560,7 @@ public WorkspaceViewModel(WorkspaceModel model, DynamoViewModel dynamoViewModel) var errorsColl = new CollectionContainer { Collection = Errors }; WorkspaceElements.Add(errorsColl); - var annotationsColl = new CollectionContainer {Collection = Annotations}; + var annotationsColl = new CollectionContainer { Collection = Annotations }; WorkspaceElements.Add(annotationsColl); //respond to collection changes on the model by creating new view models @@ -579,7 +583,7 @@ public WorkspaceViewModel(WorkspaceModel model, DynamoViewModel dynamoViewModel) Model.ConnectorDeleted += Connectors_ConnectorDeleted; Model.PropertyChanged += ModelPropertyChanged; Model.PopulateJSONWorkspace += Model_PopulateJSONWorkspace; - + DynamoSelection.Instance.Selection.CollectionChanged += RefreshViewOnSelectionChange; DynamoViewModel.CopyCommand.CanExecuteChanged += CopyPasteChanged; @@ -606,7 +610,7 @@ public WorkspaceViewModel(WorkspaceModel model, DynamoViewModel dynamoViewModel) foreach (NoteModel note in Model.Notes) Model_NoteAdded(note); foreach (AnnotationModel annotation in Model.Annotations) Model_AnnotationAdded(annotation); foreach (ConnectorModel connector in Model.Connectors) Connectors_ConnectorAdded(connector); - + NodeAutoCompleteSearchViewModel = new NodeAutoCompleteSearchViewModel(DynamoViewModel) { Visible = true @@ -622,8 +626,8 @@ public WorkspaceViewModel(WorkspaceModel model, DynamoViewModel dynamoViewModel) /// workspace model with view block in string format private string Model_PopulateJSONWorkspace(JObject modelData) { - var jsonData = AddViewBlockToJSON(modelData); - return jsonData.ToString(); + var jsonData = AddViewBlockToJSON(modelData); + return jsonData.ToString(); } public override void Dispose() @@ -722,7 +726,7 @@ internal bool Save(string filePath, bool isBackup = false, EngineController engi // Stage 2: Save string saveContent; - if(saveContext == SaveContext.SaveAs && !isBackup) + if (saveContext == SaveContext.SaveAs && !isBackup) { // For intentional SaveAs either through UI or API calls, replace workspace elements' Guids and workspace Id jo["Uuid"] = Guid.NewGuid().ToString(); @@ -742,15 +746,15 @@ internal bool Save(string filePath, bool isBackup = false, EngineController engi } } } - saveContent = GuidUtility.UpdateWorkspaceGUIDs(jo.ToString()); + saveContent = GuidUtility.UpdateWorkspaceGUIDs(jo.ToString()); } else { saveContent = jo.ToString(); - } + } File.WriteAllText(filePath, saveContent); - + // Handle Workspace or CustomNodeWorkspace related non-serialization internal logic // Only for actual save, update file path and recent file list // The assignation of the JsonRepresentation and Guid is only for the checksum flow, it will grab info only from .dyn files @@ -770,7 +774,7 @@ internal bool Save(string filePath, bool isBackup = false, EngineController engi if (this.Model is CustomNodeWorkspaceModel customNodeWorkspaceModel) { //If the custom node Name is already set and the FileName is already set then we don't need to change the Name with "backup" - if(string.IsNullOrEmpty(customNodeWorkspaceModel.Name) && string.IsNullOrEmpty(customNodeWorkspaceModel.FileName)) + if (string.IsNullOrEmpty(customNodeWorkspaceModel.Name) && string.IsNullOrEmpty(customNodeWorkspaceModel.FileName)) customNodeWorkspaceModel.SetInfo(Path.GetFileNameWithoutExtension(filePath)); } } @@ -852,7 +856,7 @@ private void Model_AnnotationRemoved(AnnotationModel annotation) var matchingAnnotation = Annotations.First(x => x.AnnotationModel == annotation); Annotations.Remove(matchingAnnotation); matchingAnnotation.Dispose(); - + } private void Model_AnnotationsCleared() @@ -901,8 +905,11 @@ void Model_NodeRemoved(NodeModel node) nodeViewModel.Dispose(); PostNodeChangeActions(); + + StopAnimations = Nodes.Count > MaxNodesBeforeAnimationStops; } + private const int MaxNodesBeforeAnimationStops = 150; void Model_NodeAdded(NodeModel node) { var nodeViewModel = new NodeViewModel(this, node); @@ -916,6 +923,8 @@ void Model_NodeAdded(NodeModel node) Errors.Add(nodeViewModel.ErrorBubble); PostNodeChangeActions(); + + StopAnimations = Nodes.Count > MaxNodesBeforeAnimationStops; } void PostNodeChangeActions() @@ -931,7 +940,7 @@ void PostNodeChangeActions() /// The object. public virtual void OnNodeModified(NodeModel obj) { - + } internal void CheckAndSetPeriodicRunCapability() @@ -948,7 +957,7 @@ private void nodeViewModel_SnapInputEvent(PortViewModel portViewModel) { switch (portViewModel.EventType) { - case PortEventType.MouseEnter: + case PortEventType.MouseEnter: IsSnapping = this.CheckActiveConnectorCompatibility(portViewModel); this.portViewModel = portViewModel; break; @@ -1057,7 +1066,7 @@ internal void SelectInRegion(Rect2D region, bool isCrossSelect) var selection = DynamoSelection.Instance.Selection; var childlessModels = Model.Nodes .Concat(Model.Notes) - .Concat(Pins.Select(c=>c.Model)); + .Concat(Pins.Select(c => c.Model)); foreach (var n in childlessModels) { @@ -1226,10 +1235,10 @@ public void AlignSelected(object parameter) switch (alignType) { case "HorizontalCenter": - { - var xAll = GetSelectionAverageX(); - toAlign.ForEach((x) => { x.CenterX = xAll; }); - } + { + var xAll = GetSelectionAverageX(); + toAlign.ForEach((x) => { x.CenterX = xAll; }); + } break; case "HorizontalLeft": { @@ -1264,10 +1273,10 @@ public void AlignSelected(object parameter) } break; case "VerticalCenter": - { - var yAll = GetSelectionAverageY(); - toAlign.ForEach((x) => { x.CenterY = yAll; }); - } + { + var yAll = GetSelectionAverageY(); + toAlign.ForEach((x) => { x.CenterY = yAll; }); + } break; case "VerticalTop": { @@ -1292,7 +1301,7 @@ public void AlignSelected(object parameter) { if (x is ConnectorPinModel pin) { - x.Y = yAll - ConnectorPinViewModel.OneThirdWidth*2; + x.Y = yAll - ConnectorPinViewModel.OneThirdWidth * 2; } else { @@ -1302,67 +1311,67 @@ public void AlignSelected(object parameter) } break; case "VerticalDistribute": - { - var nodesSelected = DynamoSelection.Instance.Selection.Where(node => !(node is AnnotationModel) && node is ILocatable); - if (nodesSelected.Count() <= 2) return; + { + var nodesSelected = DynamoSelection.Instance.Selection.Where(node => !(node is AnnotationModel) && node is ILocatable); + if (nodesSelected.Count() <= 2) return; - var yMin = GetSelectionMinY(); - var yMax = GetSelectionMaxY(); + var yMin = GetSelectionMinY(); + var yMax = GetSelectionMaxY(); - var spacing = 0.0; - var span = yMax - yMin; + var spacing = 0.0; + var span = yMax - yMin; - var nodeHeightSum = - nodesSelected.Where(y => y is ILocatable) - .Cast() - .Sum((y) => y.Height); + var nodeHeightSum = + nodesSelected.Where(y => y is ILocatable) + .Cast() + .Sum((y) => y.Height); - if (span > nodeHeightSum) - { - spacing = (span - nodeHeightSum) - /(nodesSelected.Count() - 1); - } + if (span > nodeHeightSum) + { + spacing = (span - nodeHeightSum) + / (nodesSelected.Count() - 1); + } - var cursor = yMin; - foreach (var node in toAlign.OrderBy(y => y.Y)) - { - node.Y = cursor; - cursor += node.Height + spacing; + var cursor = yMin; + foreach (var node in toAlign.OrderBy(y => y.Y)) + { + node.Y = cursor; + cursor += node.Height + spacing; + } } - } break; case "HorizontalDistribute": - { - var nodesSelected = DynamoSelection.Instance.Selection.Where(node => !(node is AnnotationModel) && node is ILocatable); - if (nodesSelected.Count() <= 2) return; - - var xMin = GetSelectionMinX(); - var xMax = GetSelectionMaxX(); - - var spacing = 0.0; - var span = xMax - xMin; - var nodeWidthSum = - nodesSelected.Where((x) => x is ILocatable) - .Cast() - .Sum((x) => x.Width); - - // If there is more span than total node width, - // distribute the nodes with a gap. If not, leave - // the spacing at 0 and the nodes will distribute - // up against each other. - if (span > nodeWidthSum) { - spacing = (span - nodeWidthSum) - /(nodesSelected.Count() - 1); - } + var nodesSelected = DynamoSelection.Instance.Selection.Where(node => !(node is AnnotationModel) && node is ILocatable); + if (nodesSelected.Count() <= 2) return; + + var xMin = GetSelectionMinX(); + var xMax = GetSelectionMaxX(); + + var spacing = 0.0; + var span = xMax - xMin; + var nodeWidthSum = + nodesSelected.Where((x) => x is ILocatable) + .Cast() + .Sum((x) => x.Width); + + // If there is more span than total node width, + // distribute the nodes with a gap. If not, leave + // the spacing at 0 and the nodes will distribute + // up against each other. + if (span > nodeWidthSum) + { + spacing = (span - nodeWidthSum) + / (nodesSelected.Count() - 1); + } - var cursor = xMin; - foreach (var node in toAlign.OrderBy(x => x.X)) - { - node.X = cursor; - cursor += node.Width + spacing; + var cursor = xMin; + foreach (var node in toAlign.OrderBy(x => x.X)) + { + node.X = cursor; + cursor += node.Width + spacing; + } } - } break; } @@ -1373,7 +1382,7 @@ private static bool CanAlignSelected(object parameter) { return DynamoSelection.Instance.Selection.Count > 1; } - + private void Paste(object param) { var point = InCanvasSearchViewModel.InCanvasSearchPosition; @@ -1390,10 +1399,10 @@ private void ShowHideAllGeometryPreview(object parameter) return; var command = new DynamoModel.UpdateModelValueCommand(Guid.Empty, - modelGuids, "IsVisible", (string) parameter); + modelGuids, "IsVisible", (string)parameter); DynamoViewModel.Model.ExecuteCommand(command); - RefreshViewOnSelectionChange(this,null); + RefreshViewOnSelectionChange(this, null); } private void SetArgumentLacing(object parameter) @@ -1407,7 +1416,7 @@ private void SetArgumentLacing(object parameter) return; var command = new DynamoModel.UpdateModelValueCommand(Guid.Empty, - modelGuids, "ArgumentLacing", (string) parameter); + modelGuids, "ArgumentLacing", (string)parameter); DynamoViewModel.Model.ExecuteCommand(command); RaisePropertyChanged("SelectionArgumentLacing"); @@ -1488,7 +1497,7 @@ private static bool CanCreateNodeFromSelection(object parameter) private bool CanZoom(double zoom) { - return (!(zoom < 0) || !(this.Zoom <= WorkspaceViewModel.ZOOM_MINIMUM)) && (!(zoom > 0) + return (!(zoom < 0) || !(this.Zoom <= WorkspaceViewModel.ZOOM_MINIMUM)) && (!(zoom > 0) || !(this.Zoom >= WorkspaceViewModel.ZOOM_MAXIMUM)); } @@ -1527,7 +1536,7 @@ internal void FitViewInternal(bool toggle = true) maxY = GetSelectionMaxY(); } else - { + { // no selection, fitview all nodes and notes var nodes = Nodes.Select(x => x.NodeModel); var notes = Notes.Select(x => x.Model); @@ -1550,7 +1559,7 @@ internal void FitViewInternal(bool toggle = true) maxX = Math.Max(model.X + model.Width, maxX); minY = Math.Min(model.Y, minY); maxY = Math.Max(model.Y + model.Height, maxY); - } + } } double focusWidth; @@ -1612,7 +1621,7 @@ private void FocusNode(object id) try { var node = DynamoViewModel.Model.CurrentWorkspace.Nodes.First(x => x.GUID.ToString() == id.ToString()); - + //select the element DynamoSelection.Instance.ClearSelection(); DynamoSelection.Instance.Selection.Add(node); @@ -1818,7 +1827,7 @@ private void RefreshViewOnSelectionChange(object sender, NotifyCollectionChanged { AlignSelectedCommand.RaiseCanExecuteChanged(); ShowHideAllGeometryPreviewCommand.RaiseCanExecuteChanged(); - SetArgumentLacingCommand.RaiseCanExecuteChanged(); + SetArgumentLacingCommand.RaiseCanExecuteChanged(); ShowAllWiresCommand.RaiseCanExecuteChanged(); HideAllWiresCommand.RaiseCanExecuteChanged(); RaisePropertyChanged("HasSelection"); @@ -1862,7 +1871,7 @@ internal IEnumerable GetViewModelsInternal(IEnumerable mode return foundModels; } - + } public class ViewModelEventArgs : EventArgs diff --git a/src/DynamoCoreWpf/Views/Core/NodeView.xaml b/src/DynamoCoreWpf/Views/Core/NodeView.xaml index b2f5a5f9ec1..8b4ed08841a 100644 --- a/src/DynamoCoreWpf/Views/Core/NodeView.xaml +++ b/src/DynamoCoreWpf/Views/Core/NodeView.xaml @@ -19,183 +19,278 @@ PreviewMouseLeftButtonDown="OnPreviewMouseLeftButtonDown" PreviewMouseMove="OnNodeViewMouseMove"> - + + + + + - - - - - - + + + + + + - - - + + + - + - - - + + + - - + -