diff --git a/Source/Editor/GUI/Input/UIntValueBox.cs b/Source/Editor/GUI/Input/UIntValueBox.cs new file mode 100644 index 000000000..a7ad984d7 --- /dev/null +++ b/Source/Editor/GUI/Input/UIntValueBox.cs @@ -0,0 +1,161 @@ +// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved. + +using System; +using FlaxEditor.Utilities; +using FlaxEngine; + +namespace FlaxEditor.GUI.Input +{ + /// + /// Unsigned Integer value editor. + /// + /// + [HideInEditor] + public class UIntValueBox : ValueBox + { + /// + public override uint Value + { + get => _value; + set + { + value = Mathf.Clamp(value, _min, _max); + if (_value != value) + { + // Set value + _value = value; + + // Update + UpdateText(); + OnValueChanged(); + } + } + } + + /// + public override uint MinValue + { + get => _min; + set + { + if (_min != value) + { + if (value > _max) + throw new ArgumentException(); + + _min = value; + Value = Value; + } + } + } + + /// + public override uint MaxValue + { + get => _max; + set + { + if (_max != value) + { + if (value < _min) + throw new ArgumentException(); + + _max = value; + Value = Value; + } + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The value. + /// The x location. + /// The y location. + /// The width. + /// The minimum value. + /// The maximum value. + /// The slide speed. + public UIntValueBox(uint value, float x = 0, float y = 0, float width = 120, uint min = uint.MinValue, uint max = uint.MaxValue, float slideSpeed = 1) + : base(Mathf.Clamp(value, min, max), x, y, width, min, max, slideSpeed) + { + UpdateText(); + } + + /// + /// Sets the value limits. + /// + /// The minimum value (bottom range). + /// The maximum value (upper range). + public void SetLimits(uint min, uint max) + { + _min = min; + _max = Mathf.Max(_min, max); + Value = Value; + } + + /// + /// Sets the limits from the attribute. + /// + /// The limits. + public void SetLimits(RangeAttribute limits) + { + _min = limits.Min <= 0 ? uint.MinValue : (uint)limits.Min; + _max = Math.Max(_min, limits.Max == float.MaxValue ? uint.MaxValue : (uint)limits.Max); + Value = Value; + } + + /// + /// Sets the limits from the attribute. + /// + /// The limits. + public void SetLimits(LimitAttribute limits) + { + _min = limits.Min <= 0 ? uint.MinValue : (uint)limits.Min; + _max = Math.Max(_min, limits.Max == float.MaxValue ? uint.MaxValue : (uint)limits.Max); + _slideSpeed = limits.SliderSpeed; + Value = Value; + } + + /// + /// Sets the limits from the other . + /// + /// The other. + public void SetLimits(UIntValueBox other) + { + _min = other._min; + _max = other._max; + _slideSpeed = other._slideSpeed; + Value = Value; + } + + /// + protected sealed override void UpdateText() + { + var text = _value.ToString(); + + SetText(text); + } + + /// + protected override void TryGetValue() + { + try + { + var value = ShuntingYard.Parse(Text); + Value = (uint)value; + } + catch (Exception ex) + { + // Fall back to previous value + Editor.LogWarning(ex); + } + } + + /// + protected override void ApplySliding(float delta) + { + Value = _startSlideValue + (uint)delta; + } + } +} diff --git a/Source/Editor/Surface/Elements/InputBox.cs b/Source/Editor/Surface/Elements/InputBox.cs index 434cf7a5c..99fd681e9 100644 --- a/Source/Editor/Surface/Elements/InputBox.cs +++ b/Source/Editor/Surface/Elements/InputBox.cs @@ -105,13 +105,13 @@ namespace FlaxEditor.Surface.Elements { public bool CanUse(InputBox box, ref ScriptType type) { - return type.Type == typeof(int) || type.Type == typeof(uint); + return type.Type == typeof(int); } public Control Create(InputBox box, ref Rectangle bounds) { var value = IntegerValue.Get(box.ParentNode, box.Archetype); - var control = new IntValueBox(value, bounds.X, bounds.Y, 40, box.CurrentType.Type == typeof(uint) ? 0 : int.MinValue, int.MaxValue, 0.01f) + var control = new IntValueBox(value, bounds.X, bounds.Y, 40, int.MinValue, int.MaxValue, 0.01f) { Height = bounds.Height, Parent = box.Parent, @@ -145,6 +145,50 @@ namespace FlaxEditor.Surface.Elements } } + class UnsignedIntegerDefaultValueEditor : IDefaultValueEditor + { + public bool CanUse(InputBox box, ref ScriptType type) + { + return type.Type == typeof(uint); + } + + public Control Create(InputBox box, ref Rectangle bounds) + { + var value = UnsignedIntegerValue.Get(box.ParentNode, box.Archetype); + var control = new UIntValueBox(value, bounds.X, bounds.Y, 40, uint.MinValue, uint.MaxValue, 0.01f) + { + Height = bounds.Height, + Parent = box.Parent, + Tag = box, + }; + control.BoxValueChanged += OnValueBoxChanged; + return control; + } + + public bool IsValid(InputBox box, Control control) + { + return control is UIntValueBox; + } + + public void UpdateDefaultValue(InputBox box, Control control) + { + if (control is UIntValueBox intValue) + { + intValue.Value = UnsignedIntegerValue.Get(box.ParentNode, box.Archetype); + } + } + + public void UpdateAttributes(InputBox box, object[] attributes, Control control) + { + } + + private void OnValueBoxChanged(ValueBox control) + { + var box = (InputBox)control.Tag; + UnsignedIntegerValue.Set(box.ParentNode, box.Archetype, control.Value); + } + } + class FloatingDefaultValueEditor : IDefaultValueEditor { public bool CanUse(InputBox box, ref ScriptType type) @@ -817,6 +861,7 @@ namespace FlaxEditor.Surface.Elements { new BooleanDefaultValueEditor(), new IntegerDefaultValueEditor(), + new UnsignedIntegerDefaultValueEditor(), new FloatingDefaultValueEditor(), new StringDefaultValueEditor(), new Vector2DefaultValueEditor(), diff --git a/Source/Editor/Surface/Elements/UnsignedIntegerValue.cs b/Source/Editor/Surface/Elements/UnsignedIntegerValue.cs new file mode 100644 index 000000000..2ccd97b5c --- /dev/null +++ b/Source/Editor/Surface/Elements/UnsignedIntegerValue.cs @@ -0,0 +1,153 @@ +// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved. + +using FlaxEditor.GUI.Input; +using FlaxEngine; +using FlaxEngine.GUI; + +namespace FlaxEditor.Surface.Elements +{ + /// + /// Unsigned Integer value editing element. + /// + /// + /// + [HideInEditor] + public sealed class UnsignedIntegerValue : UIntValueBox, ISurfaceNodeElement + { + /// + public SurfaceNode ParentNode { get; } + + /// + public NodeElementArchetype Archetype { get; } + + /// + public UnsignedIntegerValue(SurfaceNode parentNode, NodeElementArchetype archetype) + : base(Get(parentNode, archetype), archetype.Position.X, archetype.Position.Y, 50, (uint)archetype.ValueMin, (uint)archetype.ValueMax, 0.05f) + { + ParentNode = parentNode; + Archetype = archetype; + + ParentNode.ValuesChanged += OnNodeValuesChanged; + } + + private void OnNodeValuesChanged() + { + Value = Get(ParentNode, Archetype); + } + + /// + public override void Draw() + { + base.Draw(); + + // Draw border + if (!IsFocused) + Render2D.DrawRectangle(new Rectangle(Vector2.Zero, Size), Style.Current.BorderNormal); + } + + /// + protected override void OnValueChanged() + { + base.OnValueChanged(); + Set(ParentNode, Archetype, Value); + } + + /// + /// Gets the unsigned integer value from the specified parent node. Handles type casting and components gather. + /// + /// The parent node. + /// The node element archetype. + /// The result value. + public static uint Get(SurfaceNode parentNode, NodeElementArchetype arch) + { + if (arch.ValueIndex < 0) + return 0; + + uint result; + var value = 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 + + if (value is int valueInt) + result = (uint)valueInt; + else if (value is uint valueUint) + result = valueUint; + else if (value is long valueLong) + result = (uint)valueLong; + else if (value is ulong valueUlong) + result = (uint)valueUlong; + else if (value is float valueFloat) + result = (uint)valueFloat; + else if (value is Vector2 valueVec2) + result = (uint)(arch.BoxID == 0 ? valueVec2.X : valueVec2.Y); + else if (value is Vector3 valueVec3) + result = (uint)(arch.BoxID == 0 ? valueVec3.X : arch.BoxID == 1 ? valueVec3.Y : valueVec3.Z); + else if (value is Vector4 valueVec4) + result = (uint)(arch.BoxID == 0 ? valueVec4.X : arch.BoxID == 1 ? valueVec4.Y : arch.BoxID == 2 ? valueVec4.Z : valueVec4.W); + else + result = 0; + + return result; + } + + /// + /// Sets the unsigned integer value of the specified parent node. Handles type casting and components assignment. + /// + /// The parent node. + /// The node element archetype. + /// The value to set. + public static void Set(SurfaceNode parentNode, NodeElementArchetype arch, uint toSet) + { + if (arch.ValueIndex < 0) + return; + + var value = parentNode.Values[arch.ValueIndex]; + float toSetF = (float)toSet; + + if (value is int) + value = (int)toSet; + else if (value is uint) + value = toSet; + else if (value is long) + value = (long)toSet; + else if (value is ulong) + value = (ulong)toSet; + else if (value is float) + value = toSetF; + else if (value is Vector2 valueVec2) + { + if (arch.BoxID == 0) + valueVec2.X = toSetF; + else + valueVec2.Y = toSetF; + value = valueVec2; + } + else if (value is Vector3 valueVec3) + { + if (arch.BoxID == 0) + valueVec3.X = toSetF; + else if (arch.BoxID == 1) + valueVec3.Y = toSetF; + else + valueVec3.Z = toSetF; + value = valueVec3; + } + else if (value is Vector4 valueVec4) + { + if (arch.BoxID == 0) + valueVec4.X = toSetF; + else if (arch.BoxID == 1) + valueVec4.Y = toSetF; + else if (arch.BoxID == 2) + valueVec4.Z = toSetF; + else + valueVec4.W = toSetF; + value = valueVec4; + } + else + value = 0; + + parentNode.SetValue(arch.ValueIndex, value); + } + } +} diff --git a/Source/Engine/Core/Math/Mathf.cs b/Source/Engine/Core/Math/Mathf.cs index d311799f2..4d4bd89c7 100644 --- a/Source/Engine/Core/Math/Mathf.cs +++ b/Source/Engine/Core/Math/Mathf.cs @@ -316,6 +316,16 @@ namespace FlaxEngine return a <= b ? b : a; } + /// + /// Returns the largest of two or more values. + /// + /// + /// + public static uint Max(uint a, uint b) + { + return a <= b ? b : a; + } + /// /// Returns the largest of two or more values. /// @@ -401,6 +411,16 @@ namespace FlaxEngine return a >= b ? b : a; } + /// + /// Returns the smallest of two or more values. + /// + /// + /// + public static uint Min(uint a, uint b) + { + return a >= b ? b : a; + } + /// /// Returns the smallest of two or more values. /// @@ -1173,6 +1193,18 @@ namespace FlaxEngine return value < min ? min : value > max ? max : value; } + /// + /// Clamps the specified value. + /// + /// The value. + /// The min. + /// The max. + /// The result of clamping a value between min and max + public static uint Clamp(uint value, uint min, uint max) + { + return value < min ? min : value > max ? max : value; + } + /// /// Interpolates between two values using a linear function by a given amount. ///