diff --git a/Source/Editor/Surface/ContextMenu/VisjectCM.cs b/Source/Editor/Surface/ContextMenu/VisjectCM.cs index d6dd852b4..2d22b2e3f 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCM.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCM.cs @@ -41,6 +41,9 @@ namespace FlaxEditor.Surface.ContextMenu /// TThe list of surface parameters or null if failed (readonly). public delegate List ParameterGetterDelegate(); + private const float DefaultWidth = 300; + private const float DefaultHeight = 400; + private readonly List _groups = new List(16); private CheckBox _contextSensitiveToggle; private bool _contextSensitiveSearchEnabled = true; @@ -164,7 +167,7 @@ namespace FlaxEditor.Surface.ContextMenu _surfaceStyle = info.Style; // Context menu dimensions - Size = new Float2(_useDescriptionPanel ? 350 : 300, 400); + Size = new Float2(_useDescriptionPanel ? DefaultWidth + 50f : DefaultWidth, DefaultHeight); var headerPanel = new Panel(ScrollBars.None) { @@ -236,7 +239,7 @@ namespace FlaxEditor.Surface.ContextMenu }; _groupsPanel = panel2; - // Create description panel if enabled + // Create description panel elements only when description panel is about to be used if (_useDescriptionPanel) { _descriptionPanel = new Panel(ScrollBars.None) @@ -246,21 +249,20 @@ namespace FlaxEditor.Surface.ContextMenu BackgroundColor = Style.Current.BackgroundNormal, }; - var spriteHandle = info.Style.Icons.BoxClose; _descriptionDeclaringClassImage = new Image(8, 12, 20, 20) { Parent = _descriptionPanel, - Brush = new SpriteBrush(spriteHandle), + Brush = new SpriteBrush(info.Style.Icons.BoxClose), }; - var signatureFontReference = new FontReference(Style.Current.FontMedium.Asset, 9f); + var descriptionFontReference = new FontReference(Style.Current.FontMedium.Asset, 9f); _descriptionSignatureLabel = new Label(32, 8, Width - 40, 0) { Parent = _descriptionPanel, HorizontalAlignment = TextAlignment.Near, VerticalAlignment = TextAlignment.Near, Wrapping = TextWrapping.WrapWords, - Font = signatureFontReference, + Font = descriptionFontReference, Bold = true, AutoHeight = true, }; @@ -272,7 +274,7 @@ namespace FlaxEditor.Surface.ContextMenu HorizontalAlignment = TextAlignment.Near, VerticalAlignment = TextAlignment.Near, Wrapping = TextWrapping.WrapWords, - Font = signatureFontReference, + Font = descriptionFontReference, AutoHeight = true, }; _descriptionLabel.SetAnchorPreset(AnchorPresets.TopLeft, true); @@ -818,6 +820,7 @@ namespace FlaxEditor.Surface.ContextMenu _descriptionInputPanel.RemoveChildren(); _descriptionOutputPanel.RemoveChildren(); + // Fetch description and information from the memberInfo - mainly from nodes that got fetched asynchronously by the visual scripting editor ScriptType declaringType; if (archetype.Tag is ScriptMemberInfo memberInfo) { @@ -830,11 +833,14 @@ namespace FlaxEditor.Surface.ContextMenu declaringType = memberInfo.DeclaringType; _descriptionSignatureLabel.Text = memberInfo.DeclaringType + "." + name; + // We have to add the Instance information manually for members that aren't static if (!memberInfo.IsStatic) AddInputOutputElement(archetype, declaringType, false, $"Instance ({memberInfo.DeclaringType.Name})"); + // We also have to manually add the Return information as well. if (memberInfo.ValueType != ScriptType.Null && memberInfo.ValueType != ScriptType.Void) { + // When a field has a setter we don't want it to show the input as a return output. if (memberInfo.IsField && archetype.Title.StartsWith("Set ")) AddInputOutputElement(archetype, memberInfo.ValueType, false, $"({memberInfo.ValueType.Name})"); else @@ -849,27 +855,24 @@ namespace FlaxEditor.Surface.ContextMenu } else { + // Try to fetch as many informations as possible from the predefined hardcoded nodes _descriptionSignatureLabel.Text = string.IsNullOrEmpty(archetype.Signature) ? archetype.Title : archetype.Signature; declaringType = archetype.DefaultType; + // Some nodes are more delicate. Like Arrays or Dictionaries. In this case we let them fetch the inputs/outputs for us. + // Otherwise, it is not possible to show a proper return type on some nodes. if (archetype.GetInputOutputDescription != null) { - archetype.GetInputOutputDescription.Invoke(archetype, out (string Name, ScriptType Type)[] inputs, out (string Name, ScriptType Type)[] outputs); + archetype.GetInputOutputDescription.Invoke(archetype, out var inputs, out var outputs); - if (inputs != null) + foreach (var input in inputs ?? []) { - for (int i = 0; i < inputs.Length; i++) - { - AddInputOutputElement(archetype, inputs[i].Type, false, $"{inputs[i].Name} ({inputs[i].Type.Name})"); - } + AddInputOutputElement(archetype, input.Type, false, $"{input.Name} ({input.Type.Name})"); } - if (outputs != null) + foreach (var output in outputs ?? []) { - for (int i = 0; i < outputs.Length; i++) - { - AddInputOutputElement(archetype, outputs[i].Type, true, $"{outputs[i].Name} ({outputs[i].Type.Name})"); - } + AddInputOutputElement(archetype, output.Type, true, $"{output.Name} ({output.Type.Name})"); } } else @@ -879,21 +882,21 @@ namespace FlaxEditor.Surface.ContextMenu if (element.Type is not (NodeElementType.Input or NodeElementType.Output)) continue; - bool isOutput = element.Type == NodeElementType.Output; - if (element.ConnectionsType == null) - AddInputOutputElement(archetype, element.ConnectionsType, isOutput, $"{element.Text} ({archetype.ConnectionsHints.ToString()})"); - else - AddInputOutputElement(archetype, element.ConnectionsType, isOutput, $"{element.Text} ({element.ConnectionsType.Name})"); + var typeText = element.ConnectionsType ? element.ConnectionsType.Name : archetype.ConnectionsHints.ToString(); + AddInputOutputElement(archetype, element.ConnectionsType, element.Type == NodeElementType.Output, $"{element.Text} ({typeText})"); } } } + // Set declaring type icon color _surfaceStyle.GetConnectionColor(declaringType, archetype.ConnectionsHints, out var declaringTypeColor); _descriptionDeclaringClassImage.Color = declaringTypeColor; _descriptionDeclaringClassImage.MouseOverColor = declaringTypeColor; + // Calculate the description panel height. (I am doing this manually since working with autoSize on horizontal/vertical panels didn't work, especially with nesting - Nils) float panelHeight = _descriptionSignatureLabel.Height; + // If thee is no description we move the signature label down a bit to align it with the icon. Just a cosmetic check if (string.IsNullOrEmpty(archetype.Description)) { _descriptionSignatureLabel.Y = 15; @@ -903,22 +906,21 @@ namespace FlaxEditor.Surface.ContextMenu { _descriptionSignatureLabel.Y = 8; _descriptionLabel.Y = _descriptionSignatureLabel.Bounds.Bottom + 6f; + // Replacing multiple whitespaces with a linebreak for better readability. (Mainly doing this because the ToolTip fetching system replaces linebreaks with whitespaces - Nils) _descriptionLabel.Text = Regex.Replace(archetype.Description, @"\s{2,}", "\n"); } + // Some padding and moving elements around _descriptionPanelSeparator.Y = _descriptionLabel.Bounds.Bottom + 8f; - panelHeight += _descriptionLabel.Height + 32f; - _descriptionInputPanel.Y = panelHeight; _descriptionOutputPanel.Y = panelHeight; - panelHeight += Mathf.Max(_descriptionInputPanel.Height, _descriptionOutputPanel.Height); // Forcing the description panel to at least have a minimum height to not make the window size change too much in order to reduce jittering // TODO: Remove the Mathf.Max and just set the height to panelHeight once the window jitter issue is fixed - Nils _descriptionPanel.Height = Mathf.Max(135f, panelHeight); - Height = 400 + _descriptionPanel.Height; + Height = DefaultHeight + _descriptionPanel.Height; UpdateWindowSize(); _descriptionPanelVisible = true; @@ -927,6 +929,7 @@ namespace FlaxEditor.Surface.ContextMenu private void AddInputOutputElement(NodeArchetype nodeArchetype, ScriptType type, bool isOutput, string text) { + // Using a panel instead of a vertical panel because auto sizing is unreliable - so we are doing this manually here as well var elementPanel = new Panel() { Parent = isOutput ? _descriptionOutputPanel : _descriptionInputPanel, @@ -934,17 +937,17 @@ namespace FlaxEditor.Surface.ContextMenu Height = 16, AnchorPreset = AnchorPresets.TopLeft }; - - var sprite = _surfaceStyle.Icons.BoxOpen; + _surfaceStyle.GetConnectionColor(type, nodeArchetype.ConnectionsHints, out var typeColor); elementPanel.AddChild(new Image(2, 0, 12, 12) { - Brush = new SpriteBrush(sprite), + Brush = new SpriteBrush(_surfaceStyle.Icons.BoxOpen), Color = typeColor, MouseOverColor = typeColor, AutoFocus = false, }).SetAnchorPreset(AnchorPresets.TopLeft, true); + // Forcing the first letter to be capital and removing the '&' char from pointer-references text = (char.ToUpper(text[0]) + text.Substring(1)).Replace("&", ""); var elementText = new Label(16, 0, Width * 0.5f - 32, 16) { @@ -969,7 +972,7 @@ namespace FlaxEditor.Surface.ContextMenu _descriptionInputPanel.RemoveChildren(); _descriptionOutputPanel.RemoveChildren(); - Height = 400; + Height = DefaultHeight; UpdateWindowSize(); _descriptionPanelVisible = false; }