public static BindableElement CreateField <T>(string _label, Type _valueType, T _value, Action <object> _onValueChanged) { Type realValueType = _valueType; // 对字段类型进行修饰,UnityObject的子类型修饰为UnityObject // 枚举类型修饰为Enum if (!fieldDrawerCreatorMap.ContainsKey(_valueType)) { if (typeof(UnityObject).IsAssignableFrom(_valueType)) { _valueType = typeof(UnityObject); } else if (typeof(Enum).IsAssignableFrom(_valueType) && !fieldDrawerCreatorMap.ContainsKey(_valueType)) { _valueType = typeof(Enum); } } // LayerMask需单独创建 if (_value is LayerMask layerMask) { var layerField = new LayerMaskField(_label, layerMask.value); layerField.RegisterValueChangedCallback(e => { _onValueChanged(new LayerMask { value = e.newValue }); }); return(layerField); } if (_value is IList list) { Type elementType = null; if (_valueType.IsArray) { elementType = _valueType.GetElementType(); } else { Type type2 = _valueType; while (!type2.IsGenericType) { type2 = type2.BaseType; } elementType = type2.GetGenericArguments()[0]; } BindableElement bind = Activator.CreateInstance(typeof(ListField <,>).MakeGenericType(_valueType, elementType), _label, _value) as BindableElement; return(bind); } BindableElement fieldDrawer = null; var createFieldSpecificMethod = CreateFieldMethod.MakeGenericMethod(_valueType); fieldDrawer = createFieldSpecificMethod.Invoke(null, new object[] { _label, _value, realValueType, _onValueChanged }) as BindableElement; return(fieldDrawer); }
public static VisualElement GetElementOfLayerMaskField(LayerMask startValue, string fieldName, Action <object> setValue, Action <object> getValue) { LayerMaskField field = new LayerMaskField(fieldName, startValue); field.RegisterValueChangedCallback(s => setValue.Invoke(new LayerMask() { value = s.newValue })); return(field); }
/// <summary> /// 绘制一个字段元素 /// </summary> /// <param name="label">字段名</param> /// <param name="valueType">类型</param> /// <param name="value">值</param> /// <param name="onValueChanged">字段改变回调</param> /// <returns></returns> public static VisualElement DrawField(string label, Type fieldType, object value, Action <object> onValueChanged) { //枚举单独处理(熟悉的操作) if (typeof(Enum).IsAssignableFrom(fieldType)) { fieldType = typeof(Enum); } VisualElement field = null; //这边处理特殊的字段 if (fieldType == typeof(LayerMask)) { var layerField = new LayerMaskField(label, ((LayerMask)value).value); layerField.RegisterValueChangedCallback(e => { onValueChanged(new LayerMask { value = e.newValue }); }); field = layerField; } else { try { var createFieldSpecificMethod = createFieldMethod.MakeGenericMethod(fieldType); try { field = createFieldSpecificMethod.Invoke(null, new object[] { value, onValueChanged, label }) as VisualElement; } catch { } // handle the Object field case if (field == null && (value == null || value is UnityEngine.Object)) { createFieldSpecificMethod = createFieldMethod.MakeGenericMethod(typeof(UnityEngine.Object)); field = createFieldSpecificMethod.Invoke(null, new object[] { value, onValueChanged, label }) as VisualElement; if (field is ObjectField objField) { objField.objectType = fieldType; objField.value = value as UnityEngine.Object; } } } catch (Exception e) { Debug.LogError(e); } } return(field); }
public static VisualElement CreateField(Type fieldType, object value, Action <object> onValueChanged, string label) { if (typeof(Enum).IsAssignableFrom(fieldType)) { fieldType = typeof(Enum); } VisualElement field = null; // Handle special cases here if (fieldType == typeof(LayerMask)) { // LayerMasks inherit from INotifyValueChanged<int> instead of INotifyValueChanged<LayerMask> // so we can't register it inside our factory system :( var layerField = new LayerMaskField(label, ((LayerMask)value).value); layerField.RegisterValueChangedCallback(e => { onValueChanged(new LayerMask { value = e.newValue }); }); field = layerField; } else { try { var createFieldSpecificMethod = createFieldMethod.MakeGenericMethod(fieldType); try { field = createFieldSpecificMethod.Invoke(null, new object[] { value, onValueChanged, label }) as VisualElement; } catch {} // handle the Object field case if (field == null && (value == null || value is UnityEngine.Object)) { createFieldSpecificMethod = createFieldMethod.MakeGenericMethod(typeof(UnityEngine.Object)); field = createFieldSpecificMethod.Invoke(null, new object[] { value, onValueChanged, label }) as VisualElement; if (field is ObjectField objField) { objField.objectType = fieldType; objField.value = value as UnityEngine.Object; } } } catch (Exception e) { Debug.LogError(e); } } return(field); }
void Init() { LayerMaskField field = new LayerMaskField() { value = config.value is LayerMask ? (LayerMask)config.value : default(LayerMask), }; field.EnableInClassList("compositeField", false); field.RegisterValueChangedCallback((e) => { config.OnValueChanged((LayerMask)e.newValue); MarkDirtyRepaint(); }); Add(field); }
BuilderStyleRow CreateAttributeRow(UxmlAttributeDescription attribute) { var attributeType = attribute.GetType(); // Generate field label. var fieldLabel = BuilderNameUtilities.ConvertDashToHuman(attribute.name); BindableElement fieldElement; if (attribute is UxmlStringAttributeDescription) { // Hard-coded if (attribute.name.Equals("value") && currentVisualElement is EnumField enumField) { var uiField = new EnumField("Value"); if (null != enumField.value) { uiField.Init(enumField.value, enumField.includeObsoleteValues); } else { uiField.SetValueWithoutNotify(null); } uiField.RegisterValueChangedCallback(evt => PostAttributeValueChange(uiField, uiField.value.ToString())); fieldElement = uiField; } else if (attribute.name.Equals("value") && currentVisualElement is TagField tagField) { var uiField = new TagField("Value"); uiField.RegisterValueChangedCallback(evt => PostAttributeValueChange(uiField, uiField.value.ToString())); fieldElement = uiField; } else { var uiField = new TextField(fieldLabel); if (attribute.name.Equals("name") || attribute.name.Equals("view-data-key")) { uiField.RegisterValueChangedCallback(e => { OnValidatedAttributeValueChange(e, BuilderNameUtilities.attributeRegex, BuilderConstants.AttributeValidationSpacialCharacters); }); } else if (attribute.name.Equals("binding-path")) { uiField.RegisterValueChangedCallback(e => { OnValidatedAttributeValueChange(e, BuilderNameUtilities.bindingPathAttributeRegex, BuilderConstants.BindingPathAttributeValidationSpacialCharacters); }); } else { uiField.RegisterValueChangedCallback(OnAttributeValueChange); } if (attribute.name.Equals("text") || attribute.name.Equals("label")) { uiField.multiline = true; uiField.AddToClassList(BuilderConstants.InspectorMultiLineTextFieldClassName); } fieldElement = uiField; } } else if (attribute is UxmlFloatAttributeDescription) { var uiField = new FloatField(fieldLabel); uiField.RegisterValueChangedCallback(OnAttributeValueChange); fieldElement = uiField; } else if (attribute is UxmlDoubleAttributeDescription) { var uiField = new DoubleField(fieldLabel); uiField.RegisterValueChangedCallback(OnAttributeValueChange); fieldElement = uiField; } else if (attribute is UxmlIntAttributeDescription) { if (attribute.name.Equals("value") && currentVisualElement is LayerField) { var uiField = new LayerField("Value"); uiField.RegisterValueChangedCallback(OnAttributeValueChange); fieldElement = uiField; } else if (attribute.name.Equals("value") && currentVisualElement is LayerMaskField) { var uiField = new LayerMaskField("Value"); uiField.RegisterValueChangedCallback(OnAttributeValueChange); fieldElement = uiField; } else { var uiField = new IntegerField(fieldLabel); uiField.RegisterValueChangedCallback(OnAttributeValueChange); fieldElement = uiField; } } else if (attribute is UxmlLongAttributeDescription) { var uiField = new LongField(fieldLabel); uiField.RegisterValueChangedCallback(OnAttributeValueChange); fieldElement = uiField; } else if (attribute is UxmlBoolAttributeDescription) { var uiField = new Toggle(fieldLabel); uiField.RegisterValueChangedCallback(OnAttributeValueChange); fieldElement = uiField; } else if (attribute is UxmlColorAttributeDescription) { var uiField = new ColorField(fieldLabel); uiField.RegisterValueChangedCallback(OnAttributeValueChange); fieldElement = uiField; } else if (attributeType.IsGenericType && !attributeType.GetGenericArguments()[0].IsEnum && attributeType.GetGenericArguments()[0] is Type) { var desiredType = attributeType.GetGenericArguments()[0]; var uiField = new TextField(fieldLabel) { isDelayed = true }; var completer = new FieldSearchCompleter <TypeInfo>(uiField); uiField.RegisterCallback <AttachToPanelEvent, FieldSearchCompleter <TypeInfo> >((evt, c) => { // When possible, the popup should have the same width as the input field, so that the auto-complete // characters will try to match said input field. c.popup.anchoredControl = ((VisualElement)evt.target).Q(className: "unity-text-field__input"); }, completer); completer.matcherCallback += (str, info) => info.value.IndexOf(str, StringComparison.OrdinalIgnoreCase) >= 0; completer.itemHeight = 36; completer.dataSourceCallback += () => { return(TypeCache.GetTypesDerivedFrom(desiredType) .Where(t => !t.IsGenericType) // Remove UIBuilder types from the list .Where(t => t.Assembly != GetType().Assembly) .Select(GetTypeInfo)); }; completer.getTextFromDataCallback += info => info.value; completer.makeItem = () => s_TypeNameItemPool.Get(); completer.destroyItem = e => { if (e is BuilderAttributeTypeName typeItem) { s_TypeNameItemPool.Release(typeItem); } }; completer.bindItem = (v, i) => { if (v is BuilderAttributeTypeName l) { l.SetType(completer.results[i].type, completer.textField.text); } }; uiField.RegisterValueChangedCallback(e => OnValidatedTypeAttributeChange(e, desiredType)); fieldElement = uiField; uiField.RegisterCallback <DetachFromPanelEvent, FieldSearchCompleter <TypeInfo> >((evt, c) => { c.popup.RemoveFromHierarchy(); }, completer); uiField.userData = completer; } else if (attributeType.IsGenericType && attributeType.GetGenericArguments()[0].IsEnum) { var propInfo = attributeType.GetProperty("defaultValue"); var defaultEnumValue = propInfo.GetValue(attribute, null) as Enum; if (defaultEnumValue.GetType().GetCustomAttribute <FlagsAttribute>() == null) { // Create and initialize the EnumField. var uiField = new EnumField(fieldLabel); uiField.Init(defaultEnumValue); uiField.RegisterValueChangedCallback(OnAttributeValueChange); fieldElement = uiField; } else { // Create and initialize the EnumField. var uiField = new EnumFlagsField(fieldLabel); uiField.Init(defaultEnumValue); uiField.RegisterValueChangedCallback(OnAttributeValueChange); fieldElement = uiField; } } else { var uiField = new TextField(fieldLabel); uiField.RegisterValueChangedCallback(OnAttributeValueChange); fieldElement = uiField; } // Create row. var styleRow = new BuilderStyleRow(); styleRow.Add(fieldElement); // Link the field. fieldElement.SetProperty(BuilderConstants.InspectorLinkedStyleRowVEPropertyName, styleRow); fieldElement.SetProperty(BuilderConstants.InspectorLinkedAttributeDescriptionVEPropertyName, attribute); // Set initial value. RefreshAttributeField(fieldElement); // Setup field binding path. fieldElement.bindingPath = attribute.name; fieldElement.tooltip = attribute.name; // Context menu. fieldElement.AddManipulator(new ContextualMenuManipulator(BuildAttributeFieldContextualMenu)); return(styleRow); }
public static VisualElement CreateControl(FieldInfo fieldInfo, NodeView view, string label = null) { // This mess is similar to what Unity is doing for ShaderGraph. It's not great. // But the automatic alternatives depend on SerializableObject which is a performance bottleneck. // Ref: https://github.com/Unity-Technologies/ScriptableRenderPipeline/blob/master/com.unity.shadergraph/Editor/Drawing/Controls/DefaultControl.cs Type type = fieldInfo.FieldType; // Builtin unity type editors if (type == typeof(bool)) { return(BuildVal <Toggle, bool>(view, fieldInfo, label)); } if (type == typeof(int)) { return(BuildVal <IntegerField, int>(view, fieldInfo, label)); } if (type == typeof(float)) { return(BuildVal <FloatField, float>(view, fieldInfo, label)); } if (type == typeof(string)) { return(BuildVal <TextField, string>(view, fieldInfo, label)); } if (type == typeof(Rect)) { return(BuildVal <RectField, Rect>(view, fieldInfo, label)); } if (type == typeof(Color)) { return(BuildVal <ColorField, Color>(view, fieldInfo, label)); } if (type == typeof(Vector2)) { return(BuildVal <Vector2Field, Vector2>(view, fieldInfo, label)); } if (type == typeof(Vector3)) { return(BuildVal <Vector3Field, Vector3>(view, fieldInfo, label)); } if (type == typeof(Vector4)) { return(BuildVal <Vector4Field, Vector4>(view, fieldInfo, label)); } if (type == typeof(Gradient)) { return(BuildVal <GradientField, Gradient>(view, fieldInfo, label)); } if (type == typeof(AnimationCurve)) { return(BuildVal <CurveField, AnimationCurve>(view, fieldInfo, label)); } if (type == typeof(LayerMask)) { var value = ((LayerMask)fieldInfo.GetValue(view.Target)).value; var field = new LayerMaskField(label, value); field.RegisterValueChangedCallback((change) => { fieldInfo.SetValue(view.Target, (LayerMask)change.newValue); view.Target.Validate(); view.OnPropertyChange(); }); return(field); } // Implementation (rather than just using EnumField) comes from: // https://github.com/Unity-Technologies/UnityCsReference/blob/1e8347ec4cbda9e8a4929e42a20f39df9bbab9d9/Editor/Mono/UIElements/Controls/PropertyField.cs#L306-L323 if (typeof(Enum).IsAssignableFrom(type)) { var choices = new List <string>(type.GetEnumNames()); var defaultIndex = (int)fieldInfo.GetValue(view.Target); if (type.IsDefined(typeof(FlagsAttribute), false)) { var field = new EnumFlagsField(label, (Enum)fieldInfo.GetValue(view.Target)); field.RegisterValueChangedCallback((change) => { fieldInfo.SetValue(view.Target, change.newValue); view.OnPropertyChange(); }); return(field); } else { var field = new PopupField <string>(label, choices, defaultIndex); field.RegisterValueChangedCallback((change) => { fieldInfo.SetValue(view.Target, field.index); view.OnPropertyChange(); }); return(field); } } // Specialized construct so I can set .objectType on the ObjectField if (typeof(UnityEngine.Object).IsAssignableFrom(type)) { var field = BuildRef <ObjectField, UnityEngine.Object>(view, fieldInfo, label) as ObjectField; if (field != null) { field.objectType = type; } return(field); } // TODO: EnumFlags/Masks (we have MaskField - how do we detect mask types?) // TODO: Specialized common types. Transform, Rotation, Texture2D, etc. // TODO: Custom plugin types return(null); }
private void RefreshUI() { rootVisualElement.Clear(); VisualElement topRowElement = new VisualElement(); topRowElement.AddToClassList("svbm-row"); rootVisualElement.Add(topRowElement); Button refreshButton = new Button(RefreshUI) { name = "Refresh", text = "Refresh" }; refreshButton.AddToClassList("svbm-cell"); topRowElement.Add(refreshButton); Button saveButton = new Button(() => Bookmarks.Instance?.SaveToJson(Bookmarks.path)) { name = "Save", text = "Save" }; saveButton.AddToClassList("svbm-cell"); topRowElement.Add(saveButton); IMGUIContainer header = new IMGUIContainer(OnCustomGUI); header.AddToClassList("sceneLightingButton"); rootVisualElement.Add(header); if (Bookmarks.Count == 0) { return; } for (int i = 0; i < Bookmarks.Count; i++) { var index = i; VisualElement bookmarkElement = new VisualElement(); bookmarkElement.AddToClassList("svbm-row"); Button loadButton = new Button(() => Bookmarks.Instance[index].Load(SceneView.lastActiveSceneView)) { name = "LOAD", text = "LOAD" }; loadButton.AddToClassList("svbm-cell-button"); bookmarkElement.Add(loadButton); Button updateButton = new Button(() => Bookmarks.Instance[index].Save(SceneView.lastActiveSceneView)) { name = "UPDATE", text = "UPDATE" }; updateButton.AddToClassList("svbm-cell-button"); bookmarkElement.Add(updateButton); Button deleteButton = new Button(() => { Bookmarks.Instance.viewpoints.RemoveAt(index); RefreshUI(); }) { name = "DELETE", text = "DELETE" }; deleteButton.AddToClassList("svbm-cell-button"); bookmarkElement.Add(deleteButton); TextField nameField = new TextField("") { tooltip = "Set this bookmark's menu path / name.\nUse '/' to create a submenu." }; nameField.value = Bookmarks.Instance[index].name; nameField.AddToClassList("svbm-cell-text"); nameField.RegisterValueChangedCallback((x) => Bookmarks.Instance[index].name = x.newValue); bookmarkElement.Add(nameField); MaskField overridesMask = new MaskField(Enum.GetNames(typeof(Viewpoint.Overrides)).ToList(), (int)Bookmarks.Instance[index].overrides) { tooltip = "Set this bookmark's Overrides." }; overridesMask.AddToClassList("svbm-cell-mask"); overridesMask.RegisterValueChangedCallback((x) => Bookmarks.Instance[index].overrides = (Viewpoint.Overrides)x.newValue); bookmarkElement.Add(overridesMask); Toggle orthoToggle = new Toggle("") { tooltip = "Enable to set this bookmark's SceneView to Orthographic." }; orthoToggle.value = Bookmarks.Instance[index].settings.ortho; orthoToggle.AddToClassList("svbm-cell-checkbox"); orthoToggle.RegisterValueChangedCallback((x) => Bookmarks.Instance[index].settings.ortho = x.newValue); bookmarkElement.Add(orthoToggle); Toggle is2dToggle = new Toggle("") { tooltip = "Enable to set this bookmark's SceneView to 2D Mode." }; is2dToggle.value = Bookmarks.Instance[index].settings.is2D; is2dToggle.AddToClassList("svbm-cell-checkbox"); is2dToggle.RegisterValueChangedCallback((x) => Bookmarks.Instance[index].settings.is2D = x.newValue); bookmarkElement.Add(is2dToggle); FloatField fovField = new FloatField() { tooltip = "Set this bookmark's Camera Field of View." }; fovField.value = Bookmarks.Instance[index].settings.fov; fovField.AddToClassList("svbm-cell-float"); fovField.RegisterValueChangedCallback((x) => Bookmarks.Instance[index].settings.fov = Mathf.Clamp(x.newValue, 4, 120)); bookmarkElement.Add(fovField); EnumField cameraModeMask = new EnumField("", Bookmarks.Instance[index].settings.mode.drawMode) { tooltip = "Set this bookmark's Camera Shading Mode." }; cameraModeMask.AddToClassList("svbm-cell-mask"); // Catching cases where SceneView.GetBuiltinCameraMode() will fail on some DrawCameraMode such as Normal and User Defined cameraModeMask.RegisterValueChangedCallback((x) => { try { Bookmarks.Instance[index].settings.mode = SceneView.GetBuiltinCameraMode((DrawCameraMode)x.newValue); } catch { cameraModeMask.SetValueWithoutNotify(x.previousValue); } }); bookmarkElement.Add(cameraModeMask); Toggle lightingToggle = new Toggle("") { tooltip = "Enable this bookmark's SceneView Lighting." }; lightingToggle.value = Bookmarks.Instance[index].settings.sceneLighting; lightingToggle.AddToClassList("svbm-cell-checkbox"); lightingToggle.RegisterValueChangedCallback((x) => Bookmarks.Instance[index].settings.sceneLighting = x.newValue); bookmarkElement.Add(lightingToggle); MaskField viewStatesMask = new MaskField(SceneViewStatesLabels, (int)Bookmarks.Instance[index].settings.sceneViewState.GetFlags()) { tooltip = "Set this bookmark's SceneView States." }; viewStatesMask.AddToClassList("svbm-cell-mask"); viewStatesMask.RegisterValueChangedCallback((x) => Bookmarks.Instance[index].settings.sceneViewState.SetFlags((SceneViewExtensions.SceneViewStateFlags)x.newValue)); bookmarkElement.Add(viewStatesMask); Toggle gridToggle = new Toggle("") { tooltip = "Enable this bookmark's SceneView Grid." }; gridToggle.value = Bookmarks.Instance[index].settings.showGrid; gridToggle.AddToClassList("svbm-cell-checkbox"); gridToggle.RegisterValueChangedCallback((x) => Bookmarks.Instance[index].settings.showGrid = x.newValue); bookmarkElement.Add(gridToggle); Toggle gizmosToggle = new Toggle("") { tooltip = "Enable this bookmark's SceneView Gizmos." }; gizmosToggle.value = Bookmarks.Instance[index].settings.drawGizmos; gizmosToggle.AddToClassList("svbm-cell-checkbox"); gizmosToggle.RegisterValueChangedCallback((x) => Bookmarks.Instance[index].settings.drawGizmos = x.newValue); bookmarkElement.Add(gizmosToggle); LayerMaskField visibleLayerMask = new LayerMaskField("", Bookmarks.Instance[index].visibleLayers) { tooltip = "Set this bookmark's Visible Layers." }; visibleLayerMask.AddToClassList("svbm-cell-mask"); visibleLayerMask.RegisterValueChangedCallback((x) => Bookmarks.Instance[index].visibleLayers = x.newValue); bookmarkElement.Add(visibleLayerMask); LayerMaskField lockedLayerMask = new LayerMaskField("", Bookmarks.Instance[index].lockedLayers) { tooltip = "Set this bookmark's Locked Layers." }; lockedLayerMask.AddToClassList("svbm-cell-mask"); lockedLayerMask.RegisterValueChangedCallback((x) => Bookmarks.Instance[index].lockedLayers = x.newValue); bookmarkElement.Add(lockedLayerMask); EnumField shortcutMask = new EnumField("", Bookmarks.Instance[index].shortcut) { tooltip = "Set this bookmark's shortcut\nBeware this will override any other function the shortcut is associated with." }; shortcutMask.AddToClassList("svbm-cell-mask"); shortcutMask.RegisterValueChangedCallback((x) => Bookmarks.Instance[index].shortcut = (KeyCode)x.newValue); bookmarkElement.Add(shortcutMask); Button moveUpButton = new Button(() => { if (index <= 0) { return; } var item = Bookmarks.Instance.viewpoints[index]; Bookmarks.Instance.viewpoints.RemoveAt(index); Bookmarks.Instance.viewpoints.Insert(index - 1, item); RefreshUI(); }) { name = "Up", text = "Up" }; moveUpButton.AddToClassList("svbm-cell-button"); bookmarkElement.Add(moveUpButton); Button moveDownButton = new Button(() => { if (index >= Bookmarks.Instance.viewpoints.Count - 1) { return; } var item = Bookmarks.Instance.viewpoints[index]; Bookmarks.Instance.viewpoints.RemoveAt(index); Bookmarks.Instance.viewpoints.Insert(index + 1, item); RefreshUI(); }) { name = "Down", text = "Dn" }; moveDownButton.AddToClassList("svbm-cell-button"); bookmarkElement.Add(moveDownButton); rootVisualElement.Add(bookmarkElement); } }