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);
        }
Example #2
0
        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);
        }
Example #4
0
        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);
        }
Example #5
0
        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);
        }
Example #6
0
        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);
            }
        }