using System; using System.Globalization; using System.Linq; using System.Reflection; using UnityEngine; using UnityEngine.UIElements; namespace UnityEditor.ShaderGraph.Drawing.Controls { [AttributeUsage(AttributeTargets.Property)] class MultiFloatControlAttribute : Attribute, IControlAttribute { string m_Label; string m_SubLabel1; string m_SubLabel2; string m_SubLabel3; string m_SubLabel4; public MultiFloatControlAttribute(string label = null, string subLabel1 = "X", string subLabel2 = "Y", string subLabel3 = "Z", string subLabel4 = "W") { m_SubLabel1 = subLabel1; m_SubLabel2 = subLabel2; m_SubLabel3 = subLabel3; m_SubLabel4 = subLabel4; m_Label = label; } public VisualElement InstantiateControl(AbstractMaterialNode node, PropertyInfo propertyInfo) { if (!MultiFloatControlView.validTypes.Contains(propertyInfo.PropertyType)) return null; return new MultiFloatControlView(m_Label, m_SubLabel1, m_SubLabel2, m_SubLabel3, m_SubLabel4, node, propertyInfo); } } class MultiFloatControlView : VisualElement { public static Type[] validTypes = { typeof(float), typeof(Vector2), typeof(Vector3), typeof(Vector4) }; AbstractMaterialNode m_Node; PropertyInfo m_PropertyInfo; Vector4 m_Value; int m_UndoGroup = -1; public MultiFloatControlView(string label, string subLabel1, string subLabel2, string subLabel3, string subLabel4, AbstractMaterialNode node, PropertyInfo propertyInfo) { var components = Array.IndexOf(validTypes, propertyInfo.PropertyType) + 1; if (components == -1) throw new ArgumentException("Property must be of type float, Vector2, Vector3 or Vector4.", "propertyInfo"); styleSheets.Add(Resources.Load("Styles/Controls/MultiFloatControlView")); m_Node = node; m_PropertyInfo = propertyInfo; label = label ?? ObjectNames.NicifyVariableName(propertyInfo.Name); if (!string.IsNullOrEmpty(label)) Add(new Label(label)); m_Value = GetValue(); AddField(0, subLabel1); if (components > 1) AddField(1, subLabel2); if (components > 2) AddField(2, subLabel3); if (components > 3) AddField(3, subLabel4); } void AddField(int index, string subLabel) { var dummy = new VisualElement { name = "dummy" }; var label = new Label(subLabel); dummy.Add(label); Add(dummy); var field = new FloatField { userData = index, value = m_Value[index] }; var dragger = new FieldMouseDragger(field); dragger.SetDragZone(label); field.RegisterCallback(Repaint); field.RegisterCallback(Repaint); field.RegisterValueChangedCallback(evt => { var value = GetValue(); value[index] = (float)evt.newValue; SetValue(value); m_UndoGroup = -1; this.MarkDirtyRepaint(); }); field.Q("unity-text-input").RegisterCallback(evt => { if (m_UndoGroup == -1) { m_UndoGroup = Undo.GetCurrentGroup(); m_Node.owner.owner.RegisterCompleteObjectUndo("Change " + m_Node.name); } float newValue; if (!float.TryParse(evt.newData, NumberStyles.Float, CultureInfo.InvariantCulture.NumberFormat, out newValue)) newValue = 0f; var value = GetValue(); value[index] = newValue; SetValue(value); this.MarkDirtyRepaint(); }, TrickleDown.TrickleDown); field.Q("unity-text-input").RegisterCallback(evt => { if (evt.keyCode == KeyCode.Escape && m_UndoGroup > -1) { Undo.RevertAllDownToGroup(m_UndoGroup); m_UndoGroup = -1; m_Value = GetValue(); evt.StopPropagation(); } this.MarkDirtyRepaint(); }, TrickleDown.TrickleDown); Add(field); } object ValueToPropertyType(Vector4 value) { if (m_PropertyInfo.PropertyType == typeof(float)) return value.x; if (m_PropertyInfo.PropertyType == typeof(Vector2)) return (Vector2)value; if (m_PropertyInfo.PropertyType == typeof(Vector3)) return (Vector3)value; return value; } Vector4 GetValue() { var value = m_PropertyInfo.GetValue(m_Node, null); if (m_PropertyInfo.PropertyType == typeof(float)) return new Vector4((float)value, 0f, 0f, 0f); if (m_PropertyInfo.PropertyType == typeof(Vector2)) return (Vector2)value; if (m_PropertyInfo.PropertyType == typeof(Vector3)) return (Vector3)value; return (Vector4)value; } void SetValue(Vector4 value) { m_PropertyInfo.SetValue(m_Node, ValueToPropertyType(value), null); } void Repaint(MouseEventBase evt) where T : MouseEventBase, new() { evt.StopPropagation(); this.MarkDirtyRepaint(); } } }