diff --git a/Source/Editor/Surface/Archetypes/Constants.cs b/Source/Editor/Surface/Archetypes/Constants.cs index 0a5083c89..e6a9849b3 100644 --- a/Source/Editor/Surface/Archetypes/Constants.cs +++ b/Source/Editor/Surface/Archetypes/Constants.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System; +using FlaxEditor.CustomEditors.Editors; using FlaxEditor.GUI; using FlaxEditor.Scripting; using FlaxEditor.Surface.Elements; @@ -71,6 +72,134 @@ namespace FlaxEditor.Surface.Archetypes } } + private class ArrayNode : SurfaceNode + { + private OutputBox _output; + private TypePickerControl _typePicker; + private Button _addButton; + private Button _removeButton; + + public ArrayNode(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch) + : base(id, context, nodeArch, groupArch) + { + } + + public override void OnValuesChanged() + { + base.OnValuesChanged(); + + UpdateUI(); + } + + public override void OnLoaded() + { + base.OnLoaded(); + + _output = (OutputBox)Elements[0]; + _typePicker = new TypePickerControl + { + Bounds = new Rectangle(FlaxEditor.Surface.Constants.NodeMarginX, FlaxEditor.Surface.Constants.NodeMarginY + FlaxEditor.Surface.Constants.NodeHeaderSize, 160, 16), + Parent = this, + }; + _typePicker.ValueChanged += () => Set(3); + _removeButton = new Button(0, _typePicker.Y + FlaxEditor.Surface.Constants.LayoutOffsetY, 20, 20) + { + Text = "-", + TooltipText = "Remove the last item (smaller array)", + Parent = this + }; + _removeButton.Clicked += () => Set(((Array)Values[0]).Length - 1); + _addButton = new Button(_removeButton.Location, _removeButton.Size) + { + Text = "+", + TooltipText = "Add new item to array (bigger array)", + Parent = this + }; + _addButton.Clicked += () => Set(((Array)Values[0]).Length + 1); + + UpdateUI(); + } + + private void Set(int length) + { + SetValue(0, Array.CreateInstance(TypeUtils.GetType(_typePicker.Value), length)); + } + + public override void OnSurfaceCanEditChanged(bool canEdit) + { + base.OnSurfaceCanEditChanged(canEdit); + + _typePicker.Enabled = canEdit; + _addButton.Enabled = canEdit; + _removeButton.Enabled = canEdit; + } + + public override void OnDestroy() + { + _output = null; + _typePicker = null; + _addButton = null; + _removeButton = null; + + base.OnDestroy(); + } + + private void UpdateUI() + { + var array = (Array)Values[0]; + var arrayType = array.GetType(); + var elementType = new ScriptType(arrayType.GetElementType()); + _typePicker.Value = elementType; + _output.CurrentType = new ScriptType(arrayType); + + var count = array.Length; + var countMin = 0; + var countMax = 32; + for (int i = 0; i < array.Length; i++) + { + var box = (InputBox)AddBox(false, i + 1, i + 1, $"[{i}]", elementType, true); + box.UseCustomValueAccess(GetBoxValue, SetBoxValue); + } + for (int i = count; i <= countMax; i++) + { + var box = GetBox(i + 1); + if (box == null) + break; + RemoveElement(box); + } + + var canEdit = Surface.CanEdit; + _typePicker.Enabled = canEdit; + _addButton.Enabled = count < countMax && canEdit; + _removeButton.Enabled = count > countMin && canEdit; + + Title = string.Format("{0} Array", Surface.GetTypeName(elementType)); + _typePicker.Width = 160.0f; + _addButton.X = 0; + _removeButton.X = 0; + ResizeAuto(); + _addButton.X = Width - _addButton.Width - FlaxEditor.Surface.Constants.NodeMarginX; + _removeButton.X = _addButton.X - _removeButton.Width - 4; + _typePicker.Width = Width - 30; + } + + private object GetBoxValue(InputBox box) + { + var array = (Array)Values[0]; + return array.GetValue(box.ID - 1); + } + + private void SetBoxValue(InputBox box, object value) + { + if (_isDuringValuesEditing || !Surface.CanEdit) + return; + var array = (Array)Values[0]; + array = (Array)array.Clone(); + array.SetValue(value, box.ID - 1); + SetValue(0, array); + } + } + /// /// The nodes for that group. /// @@ -382,6 +511,17 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.UnsignedInteger(0, 0, 0, -1, 0, int.MaxValue) } }, + new NodeArchetype + { + TypeID = 13, + Title = "Array", + Create = (id, context, arch, groupArch) => new ArrayNode(id, context, arch, groupArch), + Description = "Constant array value.", + Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph, + Size = new Vector2(150, 20), + DefaultValues = new object[] { new int[] { 0, 1, 2 } }, + Elements = new[] { NodeElementArchetype.Factory.Output(0, string.Empty, null, 0) } + }, }; /// diff --git a/Source/Editor/Surface/Archetypes/Flow.cs b/Source/Editor/Surface/Archetypes/Flow.cs index 3e79bfe23..5de3d03b6 100644 --- a/Source/Editor/Surface/Archetypes/Flow.cs +++ b/Source/Editor/Surface/Archetypes/Flow.cs @@ -323,7 +323,7 @@ namespace FlaxEditor.Surface.Archetypes Elements = new[] { NodeElementArchetype.Factory.Input(0, string.Empty, false, typeof(void), 0), - NodeElementArchetype.Factory.Input(1, "Array", false, null, 1), + NodeElementArchetype.Factory.Input(1, "Array", true, null, 1), NodeElementArchetype.Factory.Input(2, "Break", false, typeof(void), 2), NodeElementArchetype.Factory.Output(0, "Loop", typeof(void), 3, true), NodeElementArchetype.Factory.Output(1, "Item", typeof(object), 4), diff --git a/Source/Editor/Surface/Elements/BoolValue.cs b/Source/Editor/Surface/Elements/BoolValue.cs index 8fa6dc7e0..badcb3474 100644 --- a/Source/Editor/Surface/Elements/BoolValue.cs +++ b/Source/Editor/Surface/Elements/BoolValue.cs @@ -111,14 +111,15 @@ namespace FlaxEditor.Surface.Elements /// /// The parent node. /// The node element archetype. + /// The custom value override (optional). /// The result value. - public static bool Get(SurfaceNode parentNode, NodeElementArchetype arch) + public static bool Get(SurfaceNode parentNode, NodeElementArchetype arch, object customValue = null) { - if (arch.ValueIndex < 0) + if (arch.ValueIndex < 0 && customValue == null) return false; bool result; - var value = parentNode.Values[arch.ValueIndex]; + var value = customValue ?? parentNode.Values[arch.ValueIndex]; // Note: this value box may edit on component of the vector like Vector3.Y, BoxID from Archetype tells which component pick diff --git a/Source/Editor/Surface/Elements/FloatValue.cs b/Source/Editor/Surface/Elements/FloatValue.cs index 54c5d7bf6..f51441142 100644 --- a/Source/Editor/Surface/Elements/FloatValue.cs +++ b/Source/Editor/Surface/Elements/FloatValue.cs @@ -62,14 +62,15 @@ namespace FlaxEditor.Surface.Elements /// /// The parent node. /// The node element archetype. + /// The custom value override (optional). /// The result value. - public static float Get(SurfaceNode parentNode, NodeElementArchetype arch) + public static float Get(SurfaceNode parentNode, NodeElementArchetype arch, object customValue = null) { - if (arch.ValueIndex < 0) + if (arch.ValueIndex < 0 && customValue == null) return 0; float result; - var value = parentNode.Values[arch.ValueIndex]; + var value = customValue ?? parentNode.Values[arch.ValueIndex]; // Note: this value box may edit on component of the vector like Vector3.Y, BoxID from Archetype tells which component pick diff --git a/Source/Editor/Surface/Elements/InputBox.cs b/Source/Editor/Surface/Elements/InputBox.cs index c830cf499..bbceee101 100644 --- a/Source/Editor/Surface/Elements/InputBox.cs +++ b/Source/Editor/Surface/Elements/InputBox.cs @@ -69,7 +69,7 @@ namespace FlaxEditor.Surface.Elements public Control Create(InputBox box, ref Rectangle bounds) { - var value = BoolValue.Get(box.ParentNode, box.Archetype); + var value = BoolValue.Get(box.ParentNode, box.Archetype, box.Value); var control = new CheckBox(bounds.X, bounds.Y, value, bounds.Height) { Parent = box.Parent, @@ -87,9 +87,7 @@ namespace FlaxEditor.Surface.Elements public void UpdateDefaultValue(InputBox box, Control control) { if (control is CheckBox checkBox) - { - checkBox.Checked = BoolValue.Get(box.ParentNode, box.Archetype); - } + checkBox.Checked = BoolValue.Get(box.ParentNode, box.Archetype, box.Value); } public void UpdateAttributes(InputBox box, object[] attributes, Control control) @@ -99,7 +97,7 @@ namespace FlaxEditor.Surface.Elements private void OnCheckboxStateChanged(CheckBox control) { var box = (InputBox)control.Tag; - box.ParentNode.SetValue(box.Archetype.ValueIndex, control.Checked); + box.Value = control.Checked; } } @@ -112,7 +110,7 @@ namespace FlaxEditor.Surface.Elements public Control Create(InputBox box, ref Rectangle bounds) { - var value = IntegerValue.Get(box.ParentNode, box.Archetype); + var value = IntegerValue.Get(box.ParentNode, box.Archetype, box.Value); var control = new IntValueBox(value, bounds.X, bounds.Y, 40, int.MinValue, int.MaxValue, 0.01f) { Height = bounds.Height, @@ -131,9 +129,7 @@ namespace FlaxEditor.Surface.Elements public void UpdateDefaultValue(InputBox box, Control control) { if (control is IntValueBox intValue) - { - intValue.Value = IntegerValue.Get(box.ParentNode, box.Archetype); - } + intValue.Value = IntegerValue.Get(box.ParentNode, box.Archetype, box.Value); } public void UpdateAttributes(InputBox box, object[] attributes, Control control) @@ -143,7 +139,10 @@ namespace FlaxEditor.Surface.Elements private void OnIntValueBoxChanged(ValueBox control) { var box = (InputBox)control.Tag; - IntegerValue.Set(box.ParentNode, box.Archetype, control.Value); + if (box.HasCustomValueAccess) + box.Value = control.Value; + else + IntegerValue.Set(box.ParentNode, box.Archetype, control.Value); } } @@ -156,7 +155,7 @@ namespace FlaxEditor.Surface.Elements public Control Create(InputBox box, ref Rectangle bounds) { - var value = UnsignedIntegerValue.Get(box.ParentNode, box.Archetype); + var value = UnsignedIntegerValue.Get(box.ParentNode, box.Archetype, box.Value); var control = new UIntValueBox(value, bounds.X, bounds.Y, 40, uint.MinValue, uint.MaxValue, 0.01f) { Height = bounds.Height, @@ -175,9 +174,7 @@ namespace FlaxEditor.Surface.Elements public void UpdateDefaultValue(InputBox box, Control control) { if (control is UIntValueBox intValue) - { - intValue.Value = UnsignedIntegerValue.Get(box.ParentNode, box.Archetype); - } + intValue.Value = UnsignedIntegerValue.Get(box.ParentNode, box.Archetype, box.Value); } public void UpdateAttributes(InputBox box, object[] attributes, Control control) @@ -187,7 +184,10 @@ namespace FlaxEditor.Surface.Elements private void OnValueBoxChanged(ValueBox control) { var box = (InputBox)control.Tag; - UnsignedIntegerValue.Set(box.ParentNode, box.Archetype, control.Value); + if (box.HasCustomValueAccess) + box.Value = control.Value; + else + UnsignedIntegerValue.Set(box.ParentNode, box.Archetype, control.Value); } } @@ -200,7 +200,7 @@ namespace FlaxEditor.Surface.Elements public Control Create(InputBox box, ref Rectangle bounds) { - var value = FloatValue.Get(box.ParentNode, box.Archetype); + var value = FloatValue.Get(box.ParentNode, box.Archetype, box.Value); var control = new FloatValueBox(value, bounds.X, bounds.Y, 40, float.MinValue, float.MaxValue, 0.01f) { Height = bounds.Height, @@ -219,9 +219,7 @@ namespace FlaxEditor.Surface.Elements public void UpdateDefaultValue(InputBox box, Control control) { if (control is FloatValueBox floatValue) - { - floatValue.Value = FloatValue.Get(box.ParentNode, box.Archetype); - } + floatValue.Value = FloatValue.Get(box.ParentNode, box.Archetype, box.Value); } public void UpdateAttributes(InputBox box, object[] attributes, Control control) @@ -231,7 +229,10 @@ namespace FlaxEditor.Surface.Elements private void OnFloatValueBoxChanged(ValueBox control) { var box = (InputBox)control.Tag; - FloatValue.Set(box.ParentNode, box.Archetype, control.Value); + if (box.HasCustomValueAccess) + box.Value = control.Value; + else + FloatValue.Set(box.ParentNode, box.Archetype, control.Value); } } @@ -244,7 +245,7 @@ namespace FlaxEditor.Surface.Elements public Control Create(InputBox box, ref Rectangle bounds) { - var value = box.ParentNode.Values[box.Archetype.ValueIndex] as string; + var value = box.Value as string; var control = new TextBox(false, bounds.X, bounds.Y, 40) { Text = value, @@ -265,7 +266,7 @@ namespace FlaxEditor.Surface.Elements { if (control is TextBox textBox) { - textBox.Text = box.ParentNode.Values[box.Archetype.ValueIndex] as string; + textBox.Text = box.Value as string; } } @@ -276,7 +277,7 @@ namespace FlaxEditor.Surface.Elements private void OnTextBoxTextChanged(TextBoxBase control) { var box = (InputBox)control.Tag; - box.ParentNode.SetValue(box.Archetype.ValueIndex, control.Text); + box.Value = control.Text; } } @@ -341,7 +342,7 @@ namespace FlaxEditor.Surface.Elements private Vector2 GetValue(InputBox box) { var value = Vector2.Zero; - var v = box.ParentNode.Values[box.Archetype.ValueIndex]; + var v = box.Value; if (v is Vector2 vec2) value = vec2; else if (v is Vector3 vec3) @@ -363,7 +364,7 @@ namespace FlaxEditor.Surface.Elements var box = (InputBox)control.Tag; var x = ((FloatValueBox)control.Children[0]).Value; var y = ((FloatValueBox)control.Children[1]).Value; - box.ParentNode.SetValue(box.Archetype.ValueIndex, new Vector2(x, y)); + box.Value = new Vector2(x, y); } } @@ -436,7 +437,7 @@ namespace FlaxEditor.Surface.Elements private Vector3 GetValue(InputBox box) { var value = Vector3.Zero; - var v = box.ParentNode.Values[box.Archetype.ValueIndex]; + var v = box.Value; if (v is Vector2 vec2) value = new Vector3(vec2, 0.0f); else if (v is Vector3 vec3) @@ -459,7 +460,7 @@ namespace FlaxEditor.Surface.Elements var x = ((FloatValueBox)control.Children[0]).Value; var y = ((FloatValueBox)control.Children[1]).Value; var z = ((FloatValueBox)control.Children[2]).Value; - box.ParentNode.SetValue(box.Archetype.ValueIndex, new Vector3(x, y, z)); + box.Value = new Vector3(x, y, z); } } @@ -541,7 +542,7 @@ namespace FlaxEditor.Surface.Elements private Vector4 GetValue(InputBox box) { var value = Vector4.Zero; - var v = box.ParentNode.Values[box.Archetype.ValueIndex]; + var v = box.Value; if (v is Vector2 vec2) value = new Vector4(vec2, 0.0f, 0.0f); else if (v is Vector3 vec3) @@ -565,7 +566,7 @@ namespace FlaxEditor.Surface.Elements var y = ((FloatValueBox)control.Children[1]).Value; var z = ((FloatValueBox)control.Children[2]).Value; var w = ((FloatValueBox)control.Children[3]).Value; - box.ParentNode.SetValue(box.Archetype.ValueIndex, new Vector4(x, y, z, w)); + box.Value = new Vector4(x, y, z, w); } } @@ -638,7 +639,7 @@ namespace FlaxEditor.Surface.Elements private Quaternion GetValue(InputBox box) { var value = Quaternion.Identity; - var v = box.ParentNode.Values[box.Archetype.ValueIndex]; + var v = box.Value; if (v is Quaternion quat) value = quat; else if (v is Transform transform) @@ -653,7 +654,7 @@ namespace FlaxEditor.Surface.Elements var x = ((FloatValueBox)control.Children[0]).Value; var y = ((FloatValueBox)control.Children[1]).Value; var z = ((FloatValueBox)control.Children[2]).Value; - box.ParentNode.SetValue(box.Archetype.ValueIndex, Quaternion.Euler(x, y, z)); + box.Value = Quaternion.Euler(x, y, z); } } @@ -697,7 +698,7 @@ namespace FlaxEditor.Surface.Elements private Vector4 GetValue(InputBox box) { var value = Color.Black; - var v = box.ParentNode.Values[box.Archetype.ValueIndex]; + var v = box.Value; if (v is Vector2 vec2) value = new Color(vec2.X, vec2.Y, 0.0f, 1.0f); else if (v is Vector3 vec3) @@ -716,7 +717,7 @@ namespace FlaxEditor.Surface.Elements private void OnColorValueChanged(ColorValueBox control) { var box = (InputBox)control.Tag; - box.ParentNode.SetValue(box.Archetype.ValueIndex, control.Value); + box.Value = control.Value; } } @@ -745,7 +746,7 @@ namespace FlaxEditor.Surface.Elements private void OnEnumValueChanged(EnumComboBox control) { var box = (InputBox)control.Tag; - box.ParentNode.SetValue(box.Archetype.ValueIndex, control.EnumTypeValue); + box.Value = control.EnumTypeValue; } public bool IsValid(InputBox box, Control control) @@ -768,7 +769,7 @@ namespace FlaxEditor.Surface.Elements private object GetValue(InputBox box) { var value = box.CurrentType.CreateInstance(); - var v = box.ParentNode.Values[box.Archetype.ValueIndex]; + var v = box.Value; if (v != null && v.GetType().IsEnum) value = v; return value; @@ -804,13 +805,13 @@ namespace FlaxEditor.Surface.Elements private void OnTypeValueChanged(TypePickerControl control) { var box = (InputBox)control.Tag; - var v = box.ParentNode.Values[box.Archetype.ValueIndex]; + var v = box.Value; if (v is string) - box.ParentNode.SetValue(box.Archetype.ValueIndex, control.ValueTypeName); + box.Value = control.ValueTypeName; else if (v is Type) - box.ParentNode.SetValue(box.Archetype.ValueIndex, TypeUtils.GetType(control.Value)); + box.Value = TypeUtils.GetType(control.Value); else - box.ParentNode.SetValue(box.Archetype.ValueIndex, control.Value); + box.Value = control.Value; } public bool IsValid(InputBox box, Control control) @@ -830,12 +831,12 @@ namespace FlaxEditor.Surface.Elements { var typeReference = (TypeReferenceAttribute)attributes.FirstOrDefault(x => x.GetType() == typeof(TypeReferenceAttribute)); var type = typeReference != null ? TypeUtils.GetType(typeReference.TypeName) : ScriptType.Null; - ((TypePickerControl)control).Type = type ? type : new ScriptType(typeof(object)); + ((TypePickerControl)control).Type = type ? type : ScriptType.Object; } private string GetValue(InputBox box) { - var v = box.ParentNode.Values[box.Archetype.ValueIndex]; + var v = box.Value; if (v is Type asType) return asType.FullName; if (v is ScriptType asScriptType) @@ -855,6 +856,10 @@ namespace FlaxEditor.Surface.Elements { private Control _defaultValueEditor; private IDefaultValueEditor _editor; + private int _valueIndex; + private Func _customValueGetter; + private Action _customValueSetter; + private bool _isWatchingForValueChange; /// /// The handlers for the input box default value editing. Used to display the default to the UI. @@ -880,13 +885,40 @@ namespace FlaxEditor.Surface.Elements /// public Control DefaultValueEditor => _defaultValueEditor; + /// + /// Gets or sets the value. + /// + public object Value + { + get => _customValueGetter != null ? _customValueGetter(this) : ParentNode.Values[_valueIndex]; + set + { + if (_customValueSetter != null) + _customValueSetter(this, value); + else + ParentNode.SetValue(_valueIndex, value); + } + } + + /// + /// Returns true if node has custom value access. + /// + public bool HasCustomValueAccess => _customValueGetter != null; + + /// + /// Returns true if node has value. + /// + public bool HasValue => _valueIndex != -1 || _customValueGetter != null; + /// public InputBox(SurfaceNode parentNode, NodeElementArchetype archetype) : base(parentNode, archetype, archetype.Position) { // Check if use inlined default value editor - if (Archetype.ValueIndex != -1) + _valueIndex = Archetype.ValueIndex; + if (_valueIndex != -1) { + _isWatchingForValueChange = true; ParentNode.ValuesChanged += UpdateDefaultValue; } } @@ -903,6 +935,24 @@ namespace FlaxEditor.Surface.Elements } } + /// + /// Sets the custom accessor callbacks for the box value. + /// + /// The function to call to get value for the box. + /// The function to call to set value for the box. + public void UseCustomValueAccess(Func getter, Action setter) + { + _customValueGetter = getter; + _customValueSetter = setter; + if (Connections.Count == 0) + CreateDefaultEditor(); + if (!_isWatchingForValueChange) + { + _isWatchingForValueChange = true; + ParentNode.ValuesChanged += UpdateDefaultValue; + } + } + /// public override bool IsOutput => false; @@ -952,7 +1002,7 @@ namespace FlaxEditor.Surface.Elements /// public override void OnConnectionsChanged() { - bool showEditor = Connections.Count == 0 && Archetype.ValueIndex != -1; + bool showEditor = Connections.Count == 0 && HasValue; if (showEditor) { CreateDefaultEditor(); @@ -986,7 +1036,7 @@ namespace FlaxEditor.Surface.Elements /// public override bool OnMouseUp(Vector2 location, MouseButton button) { - if (button == MouseButton.Right && Archetype.ValueIndex != -1) + if (button == MouseButton.Right && HasValue) { var menu = new FlaxEditor.GUI.ContextMenu.ContextMenu(); menu.AddButton("Copy value", OnCopyValue); @@ -1041,8 +1091,7 @@ namespace FlaxEditor.Surface.Elements private void OnCopyValue() { - var value = ParentNode.Values[Archetype.ValueIndex]; - + var value = Value; try { string text; @@ -1087,9 +1136,7 @@ namespace FlaxEditor.Surface.Elements try { if (GetClipboardValue(out var value, true)) - { - ParentNode.SetValue(Archetype.ValueIndex, value); - } + Value = value; } catch (Exception ex) { @@ -1103,7 +1150,7 @@ namespace FlaxEditor.Surface.Elements /// private void CreateDefaultEditor() { - if (_defaultValueEditor != null || Archetype.ValueIndex == -1) + if (_defaultValueEditor != null || !HasValue) return; for (int i = 0; i < DefaultValueEditors.Count; i++) diff --git a/Source/Editor/Surface/Elements/IntegerValue.cs b/Source/Editor/Surface/Elements/IntegerValue.cs index f04578b75..4d31a9ed6 100644 --- a/Source/Editor/Surface/Elements/IntegerValue.cs +++ b/Source/Editor/Surface/Elements/IntegerValue.cs @@ -65,14 +65,15 @@ namespace FlaxEditor.Surface.Elements /// /// The parent node. /// The node element archetype. + /// The custom value override (optional). /// The result value. - public static int Get(SurfaceNode parentNode, NodeElementArchetype arch) + public static int Get(SurfaceNode parentNode, NodeElementArchetype arch, object customValue = null) { - if (arch.ValueIndex < 0) + if (arch.ValueIndex < 0 && customValue == null) return 0; int result; - var value = parentNode.Values[arch.ValueIndex]; + var value = customValue ?? parentNode.Values[arch.ValueIndex]; // Note: this value box may edit on component of the vector like Vector3.Y, BoxID from Archetype tells which component pick diff --git a/Source/Editor/Surface/Elements/UnsignedIntegerValue.cs b/Source/Editor/Surface/Elements/UnsignedIntegerValue.cs index a1822a268..fe0582669 100644 --- a/Source/Editor/Surface/Elements/UnsignedIntegerValue.cs +++ b/Source/Editor/Surface/Elements/UnsignedIntegerValue.cs @@ -57,14 +57,15 @@ namespace FlaxEditor.Surface.Elements /// /// The parent node. /// The node element archetype. + /// The custom value override (optional). /// The result value. - public static uint Get(SurfaceNode parentNode, NodeElementArchetype arch) + public static uint Get(SurfaceNode parentNode, NodeElementArchetype arch, object customValue = null) { - if (arch.ValueIndex < 0) + if (arch.ValueIndex < 0 && customValue == null) return 0; uint result; - var value = parentNode.Values[arch.ValueIndex]; + var value = customValue ?? parentNode.Values[arch.ValueIndex]; // Note: this value box may edit on component of the vector like Vector3.Y, BoxID from Archetype tells which component pick diff --git a/Source/Engine/Core/Types/Variant.cpp b/Source/Engine/Core/Types/Variant.cpp index a2fee9f70..2246a7158 100644 --- a/Source/Engine/Core/Types/Variant.cpp +++ b/Source/Engine/Core/Types/Variant.cpp @@ -32,7 +32,54 @@ #include #endif +namespace +{ + const char* InBuiltTypesTypeNames[37] = + { + // @formatter:off + "",// Null + "System.Void",// Void + "System.Boolean",// Bool + "System.Int32",// Int + "System.UInt32",// Uint + "System.Int64",// Int64 + "System.UInt64",// Uint64 + "System.Single",// Float + "System.Double",// Double + "System.IntPtr",// Pointer + "System.String",// String + "System.Object",// Object + "",// Structure + "FlaxEngine.Asset",// Asset + "System.Byte[]",// Blob + "",// Enum + "FlaxEngine.Vector2",// Vector2 + "FlaxEngine.Vector3",// Vector3 + "FlaxEngine.Vector4",// Vector4 + "FlaxEngine.Color",// Color + "System.Guid",// Guid + "FlaxEngine.BoundingBox",// BoundingBox + "FlaxEngine.BoundingSphere",// BoundingSphere + "FlaxEngine.Quaternion",// Quaternion + "FlaxEngine.Transform",// Transform + "FlaxEngine.Rectangle",// Rectangle + "FlaxEngine.Ray",// Ray + "FlaxEngine.Matrix",// Matrix + "System.Object[]",// Array + "Dictionary",// Dictionary + "System.Object",// ManagedObject + "System.Type",// Typename + "FlaxEngine.Int2"// Int2 + "FlaxEngine.Int3"// Int3 + "FlaxEngine.Int4"// Int4 + "System.Int16",// Int16 + "System.UInt16",// Uint16 + // @formatter:on + }; +} + static_assert(sizeof(VariantType) <= 16, "Invalid VariantType size!"); +static_assert((int32)VariantType::Types::MAX == ARRAY_COUNT(InBuiltTypesTypeNames), "Invalid amount of in-built types infos!"); VariantType::VariantType(Types type, const StringView& typeName) { @@ -54,9 +101,9 @@ VariantType::VariantType(Types type, const StringAnsiView& typeName) int32 length = typeName.Length(); if (length) { - length++; - TypeName = static_cast(Allocator::Allocate(length)); + TypeName = static_cast(Allocator::Allocate(length + 1)); Platform::MemoryCopy(TypeName, typeName.Get(), length); + TypeName[length] = 0; } } @@ -69,23 +116,77 @@ VariantType::VariantType(Types type, _MonoClass* klass) { MString typeName; MUtils::GetClassFullname(klass, typeName); - int32 length = typeName.Length() + 1; - TypeName = static_cast(Allocator::Allocate(length)); + const int32 length = typeName.Length(); + TypeName = static_cast(Allocator::Allocate(length + 1)); Platform::MemoryCopy(TypeName, typeName.Get(), length); + TypeName[length] = 0; } #endif } +VariantType::VariantType(const StringAnsiView& typeName) +{ + // Check case for array + if (typeName.EndsWith(StringAnsiView("[]"), StringSearchCase::CaseSensitive)) + { + new(this) VariantType(Array, StringAnsiView(typeName.Get(), typeName.Length() - 2)); + return; + } + + // Try using in-built type + for (uint32 i = 0; i < ARRAY_COUNT(InBuiltTypesTypeNames); i++) + { + if (typeName == InBuiltTypesTypeNames[i]) + { + new(this) VariantType((Types)i); + return; + } + } + + // Try using scripting type + const ScriptingTypeHandle typeHandle = Scripting::FindScriptingType(typeName); + if (typeHandle) + { + const ScriptingType& type = typeHandle.GetType(); + switch (type.Type) + { + case ScriptingTypes::Script: + case ScriptingTypes::Class: + case ScriptingTypes::Interface: + new(this) VariantType(Object, typeName); + return; + case ScriptingTypes::Structure: + new(this) VariantType(Structure, typeName); + return; + case ScriptingTypes::Enum: + new(this) VariantType(Enum, typeName); + return; + } + } + + // Try using managed class +#if USE_MONO + if (const auto mclass = Scripting::FindClass(typeName)) + { + new(this) VariantType(ManagedObject, typeName); + return; + } +#endif + + new(this) VariantType(); + LOG(Warning, "Missing scripting type \'{0}\'", ::String(typeName)); +} + VariantType::VariantType(const VariantType& other) { Type = other.Type; TypeName = nullptr; - int32 length = StringUtils::Length(other.TypeName); + const int32 length = StringUtils::Length(other.TypeName); if (length) { - length++; - TypeName = static_cast(Allocator::Allocate(length)); + TypeName = static_cast(Allocator::Allocate(length + 1)); Platform::MemoryCopy(TypeName, other.TypeName, length); + TypeName[length] = 0; } } @@ -119,12 +220,12 @@ VariantType& VariantType::operator=(const VariantType& other) Type = other.Type; Allocator::Free(TypeName); TypeName = nullptr; - int32 length = StringUtils::Length(other.TypeName); + const int32 length = StringUtils::Length(other.TypeName); if (length) { - length++; - TypeName = static_cast(Allocator::Allocate(length)); + TypeName = static_cast(Allocator::Allocate(length + 1)); Platform::MemoryCopy(TypeName, other.TypeName, length); + TypeName[length] = 0; } return *this; } @@ -173,67 +274,21 @@ const char* VariantType::GetTypeName() const { if (TypeName) return TypeName; - switch (Type) + return InBuiltTypesTypeNames[Type]; +} + +VariantType VariantType::GetElementType() const +{ + if (Type == Array) { - case Void: - return "System.Void"; - case Bool: - return "System.Boolean"; - case Int16: - return "System.Int16"; - case Uint16: - return "System.UInt16"; - case Int: - return "System.Int32"; - case Uint: - return "System.UInt32"; - case Int64: - return "System.Int64"; - case Uint64: - return "System.UInt64"; - case Float: - return "System.Single"; - case Double: - return "System.Double"; - case Pointer: - return "System.IntPtr"; - case String: - return "System.String"; - case Object: - return "System.Object"; - case Asset: - return "FlaxEngine.Asset"; - case Vector2: - return "FlaxEngine.Vector2"; - case Vector3: - return "FlaxEngine.Vector3"; - case Vector4: - return "FlaxEngine.Vector4"; - case Color: - return "FlaxEngine.Color"; - case Guid: - return "System.Guid"; - case BoundingBox: - return "FlaxEngine.BoundingBox"; - case BoundingSphere: - return "FlaxEngine.BoundingSphere"; - case Quaternion: - return "FlaxEngine.Quaternion"; - case Transform: - return "FlaxEngine.Transform"; - case Rectangle: - return "FlaxEngine.Rectangle"; - case Ray: - return "FlaxEngine.Ray"; - case Matrix: - return "FlaxEngine.Matrix"; - case Typename: - return "System.Type"; - case Array: - return "System.Object[]"; - default: - return ""; + if (TypeName) + { + const StringAnsiView elementTypename(TypeName, StringUtils::Length(TypeName) - 2); + return VariantType(elementTypename); + } + return VariantType(Object); } + return VariantType(); } ::String VariantType::ToString() const @@ -3219,7 +3274,7 @@ void Variant::AllocStructure() { if (typeName.Length() != 0) { - LOG(Warning, "Missing scripting type \'{0}\'", String(typeName.Get())); + LOG(Warning, "Missing scripting type \'{0}\'", String(typeName)); } AsBlob.Data = nullptr; AsBlob.Length = 0; diff --git a/Source/Engine/Core/Types/Variant.h b/Source/Engine/Core/Types/Variant.h index a7700df9d..a9544899f 100644 --- a/Source/Engine/Core/Types/Variant.h +++ b/Source/Engine/Core/Types/Variant.h @@ -93,6 +93,7 @@ public: explicit VariantType(Types type, const StringView& typeName); explicit VariantType(Types type, const StringAnsiView& typeName); explicit VariantType(Types type, struct _MonoClass* klass); + explicit VariantType(const StringAnsiView& typeName); VariantType(const VariantType& other); VariantType(VariantType&& other) noexcept; @@ -119,6 +120,7 @@ public: void SetTypeName(const StringView& typeName); void SetTypeName(const StringAnsiView& typeName); const char* GetTypeName() const; + VariantType GetElementType() const; ::String ToString() const; }; diff --git a/Source/Engine/UI/GUI/Common/Button.cs b/Source/Engine/UI/GUI/Common/Button.cs index fa2b89d7e..d08d03dec 100644 --- a/Source/Engine/UI/GUI/Common/Button.cs +++ b/Source/Engine/UI/GUI/Common/Button.cs @@ -144,6 +144,16 @@ namespace FlaxEngine.GUI } } + /// + /// Initializes a new instance of the class. + /// + /// Position + /// Size + public Button(Vector2 location, Vector2 size) + : this(location.X, location.Y, size.X, size.Y) + { + } + /// /// Called when mouse clicks the button. /// @@ -219,7 +229,7 @@ namespace FlaxEngine.GUI Render2D.DrawRectangle(clientRect, borderColor); // Draw text - Render2D.DrawText(_font.GetFont(), TextMaterial, _text, clientRect, textColor, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(_font?.GetFont(), TextMaterial, _text, clientRect, textColor, TextAlignment.Center, TextAlignment.Center); } /// diff --git a/Source/Engine/Visject/VisjectGraph.cpp b/Source/Engine/Visject/VisjectGraph.cpp index b0093fc7a..9e98fa5f5 100644 --- a/Source/Engine/Visject/VisjectGraph.cpp +++ b/Source/Engine/Visject/VisjectGraph.cpp @@ -117,6 +117,22 @@ void VisjectExecutor::ProcessGroupConstants(Box* box, Node* node, Value& value) case 11: value = node->Values[0]; break; + // Array + case 13: + value = node->Values[0]; + if (value.Type.Type == VariantType::Array) + { + auto& array = value.AsArray(); + const int32 count = Math::Min(array.Count(), node->Boxes.Count() - 1); + const VariantType elementType = value.Type.GetElementType(); + for (int32 i = 0; i < count; i++) + { + auto b = &node->Boxes[i + 1]; + if (b && b->HasConnection()) + array[i] = eatBox(node, b->FirstConnection()).Cast(elementType); + } + } + break; default: break; }