public static VisualElement CreateEditorForNodeModel(this VisualElement element, IConstantNodeModel model, Action <IChangeEvent> onValueChanged) { VisualElement editorElement; var ext = ExtensionMethodCache <IConstantEditorBuilder> .GetExtensionMethod(model.GetType(), ConstantEditorBuilder.FilterMethods, ConstantEditorBuilder.KeySelector); var graphAsset = model.AssetModel; if (ext != null) { Action <IChangeEvent> myValueChanged = evt => { if (evt != null) // Enum editor sends null { var p = evt.GetType().GetProperty("newValue"); var newValue = p.GetValue(evt); ((ConstantNodeModel)model).ObjectValue = newValue; onValueChanged(evt); } // TODO ugly but required to actually get the graph to be saved. Note that for some reason, the // first edit works because the graph is already dirty EditorUtility.SetDirty(graphAsset as Object); }; var constantBuilder = new ConstantEditorBuilder(myValueChanged); editorElement = (VisualElement)ext.Invoke(null, new object[] { constantBuilder, model }); } else { Debug.Log($"Could not draw Editor GUI for node of type {model.GetType()}"); editorElement = new Label("<Unknown>"); } return(editorElement); }
public static VisualElement CreateEditorForNodeModel(this VisualElement element, IConstantNodeModel model, Action <IChangeEvent> onValueChanged) { VisualElement editorElement; var ext = ModelUtility.ExtensionMethodCache <IConstantEditorBuilder> .GetExtensionMethod(model.GetType(), ConstantEditorBuilder.FilterMethods, ConstantEditorBuilder.KeySelector); if (ext != null) { var constantBuilder = new ConstantEditorBuilder(onValueChanged); editorElement = (VisualElement)ext.Invoke(null, new object[] { constantBuilder, model }); } else if (model is ConstantNodeModel constantNodeModel && constantNodeModel.NodeAssetReference != null) { var serializedObject = new SerializedObject(constantNodeModel.NodeAssetReference); SerializedProperty serializedProperty = serializedObject.FindProperty("m_NodeModel.value"); var propertyField = new PropertyField(serializedProperty); editorElement = propertyField; editorElement.SetEnabled(!constantNodeModel.IsLocked); // delayed because the initial binding would cause an event otherwise, and then a compilation propertyField.schedule.Execute(() => { var onValueChangedEventCallback = new EventCallback <IChangeEvent>(onValueChanged); // HERE BE DRAGONS // there's no way atm to be notified that a PropertyField's value changed so we build a ChangeEvent<T> // callback registration using reflection, but actually provide an Action<IChangeEvent> Type type = constantNodeModel.Type; Type eventType = typeof(ChangeEvent <>).MakeGenericType(type); MethodInfo genericRegisterCallbackMethod = typeof(VisualElement).GetMethods().Single(m => { var parameterInfos = m.GetParameters(); return(m.Name == nameof(VisualElement.RegisterCallback) && parameterInfos.Length == 2 && parameterInfos[1].ParameterType == typeof(TrickleDown)); }); MethodInfo registerCallbackMethod = genericRegisterCallbackMethod.MakeGenericMethod(eventType); registerCallbackMethod.Invoke(propertyField, new object[] { onValueChangedEventCallback, TrickleDown.NoTrickleDown }); foreach (var floatField in propertyField.Query <FloatField>().ToList()) { floatField.isDelayed = true; floatField.RegisterValueChangedCallback(_ => { onValueChangedEventCallback.Invoke(null); floatField.UnregisterValueChangedCallback(onValueChangedEventCallback); }); } }).ExecuteLater(1); }