Henry00IS / ShapeEditor

2D Shape Editor for Unity Editor to create complex 3D meshes out of 2D shapes with RealtimeCSG support.
MIT License
99 stars 9 forks source link

Input numbers for scaling and rotation while dragging #8

Open Henry00IS opened 2 years ago

Henry00IS commented 2 years ago

Just like Blender, allow the user to type numbers while scaling, rotating or translating as a multiplier.

I already wrote the code for a simple keyboard recorder. The problem has been finding an elegant way to add this to tools without having to copy and paste the logic everywhere. Something almost as simple as a C# property for the tools that support this feature (or an input focus related thing).

#if UNITY_EDITOR

using System;
using UnityEditor;
using UnityEngine;

namespace AeternumGames.ShapeEditor
{
    /// <summary>Processes keyboard numbers for typing multipliers during active tools and widgets.</summary>
    [Serializable]
    public class NumericKeyboardRecorder
    {
        /// <summary>The accumulated string of characters.</summary>
        private string accumulator = "";

        private float lastValidFloat = 0.0f;

        /// <summary>Gets or sets (clears) whether the user has typed a value using the keyboard.</summary>
        public bool hasValue
        {
            get
            {
                if (accumulator.Length == 0) return false;

                return true;
            }
            set
            {
                if (!value)
                    Reset();
            }
        }

        /// <summary>The last valid value typed assuming <see cref="hasValue"/> is true.</summary>
        public float value => lastValidFloat;

        /// <summary>The text typed assuming <see cref="hasValue"/> is true.</summary>
        public string text => accumulator;

        /// <summary>Must be called on key down events.</summary>
        public bool OnKeyDown(KeyCode keyCode)
        {
            var character = (char)keyCode;

            // add floating point related characters to the accumulator.
            if (ValidateCharacter(character))
            {
                accumulator += character;
                UpdateValue();
                return true;
            }

            if (hasValue)
            {
                // backspace can be used to remove the last character.
                if (keyCode == KeyCode.Backspace)
                {
                    OnBackspace();
                    UpdateValue();
                    return true;
                }

                // minus can be used to negate the number.
                if (keyCode == KeyCode.Minus)
                {
                    OnMinus();
                    UpdateValue();
                    return true;
                }
            }

            return false;
        }

        // whitelist the input characters related to floating point numbers.
        private bool ValidateCharacter(char character)
        {
            switch (character)
            {
                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                case '.':
                    return true;
            }
            return false;
        }

        private void OnBackspace()
        {
            if (accumulator.Length == 0) return;
            accumulator = accumulator.Remove(accumulator.Length - 1, 1);
        }

        private void OnMinus()
        {
            if (accumulator.ContainsInvarianCulture("-"))
                accumulator = accumulator.Replace("-", "");
            else
                accumulator = "-" + accumulator;
        }

        private void UpdateValue()
        {
            if (ExpressionEvaluator.Evaluate(accumulator, out float number))
                lastValidFloat = number;
        }

        /// <summary>Resets the current input.</summary>
        public void Reset()
        {
            accumulator = "";
            lastValidFloat = 0.0f;
        }
    }
}

#endif