init
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
#sd-header {
|
||||
border-top-width: 1px;
|
||||
border-right-width: 1px;
|
||||
border-bottom-width: 1px;
|
||||
border-left-width: 1px;
|
||||
border-left-color: rgb(26, 26, 26);
|
||||
border-right-color: rgb(26, 26, 26);
|
||||
border-top-color: rgb(26, 26, 26);
|
||||
border-bottom-color: rgb(26, 26, 26);
|
||||
border-top-left-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
background-color: rgb(51, 51, 51);
|
||||
-unity-text-align: upper-center;
|
||||
-unity-font-style: bold;
|
||||
}
|
||||
|
||||
#sd-header > #unity-dragline-anchor {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
#sd-header > #unity-dragline-anchor > #unity-dragline {
|
||||
left: -5px;
|
||||
right: -5px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
#sd-header Label {
|
||||
padding-top: 2px;
|
||||
padding-right: 2px;
|
||||
padding-bottom: 2px;
|
||||
padding-left: 2px;
|
||||
}
|
||||
|
||||
#sd-add-row {
|
||||
border-top-width: 0;
|
||||
border-right-width: 1px;
|
||||
border-bottom-width: 0;
|
||||
border-left-width: 1px;
|
||||
border-left-color: rgb(26, 26, 26);
|
||||
border-right-color: rgb(26, 26, 26);
|
||||
border-top-color: rgb(26, 26, 26);
|
||||
border-bottom-color: rgb(26, 26, 26);
|
||||
background-color: rgb(70, 70, 70);
|
||||
}
|
||||
|
||||
#sd-add-row:disabled {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.keys-disabled #sd-add-row {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#sd-add-row > PropertyField {
|
||||
}
|
||||
|
||||
#sd-add-row > Label {
|
||||
-unity-text-align: middle-center;
|
||||
}
|
||||
|
||||
#sd-list > ScrollView {
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
padding-top: 0;
|
||||
padding-right: 0;
|
||||
padding-bottom: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
#sd-list #unity-list-view__reorderable-handle {
|
||||
padding-top: 0;
|
||||
padding-right: 0;
|
||||
padding-bottom: 0;
|
||||
padding-left: 0;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#sd-list #unity-list-view__reorderable-item__container {
|
||||
padding-top: 0;
|
||||
padding-right: 0;
|
||||
padding-bottom: 0;
|
||||
padding-left: 0;
|
||||
margin-top: 0;
|
||||
margin-right: 0;
|
||||
margin-bottom: 0;
|
||||
margin-left: -15px;
|
||||
}
|
||||
|
||||
.keys-disabled #sd-list #unity-list-view__footer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#sd-list #unity-list-view__footer:disabled {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.row {
|
||||
flex-direction: row;
|
||||
flex-grow: 0;
|
||||
}
|
||||
|
||||
.row-left {
|
||||
margin-top: 0;
|
||||
margin-right: 0;
|
||||
margin-bottom: 0;
|
||||
margin-left: 0;
|
||||
padding-top: 1px;
|
||||
padding-right: 2px;
|
||||
padding-bottom: 1px;
|
||||
padding-left: 17px;
|
||||
width: 150px;
|
||||
overflow: hidden;
|
||||
border-right-color: rgb(26, 26, 26);
|
||||
border-right-width: 1px;
|
||||
}
|
||||
|
||||
.row-right {
|
||||
margin-top: 0;
|
||||
margin-right: 0;
|
||||
margin-bottom: 0;
|
||||
margin-left: 0;
|
||||
padding-top: 1px;
|
||||
padding-right: 2px;
|
||||
padding-bottom: 1px;
|
||||
padding-left: 2px;
|
||||
overflow: hidden;
|
||||
flex-grow: 1;
|
||||
border-left-width: 1px;
|
||||
border-left-color: rgb(26, 26, 26);
|
||||
flex-shrink: 1;
|
||||
min-width: 180px;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 215036bf11e09364a9da52f92c9534f0
|
||||
ScriptedImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}
|
||||
disableValidation: 0
|
||||
@@ -0,0 +1,14 @@
|
||||
<ui:UXML xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" noNamespaceSchemaLocation="../../../../../../UIElementsSchema/UIElements.xsd" editor-extension-mode="True">
|
||||
<Style src="project://database/Assets/TheVVaS/RPGCoreCommon/Helpers/Editor/Drawers/SerializableDictionary.uss?fileID=7433441132597879392&guid=215036bf11e09364a9da52f92c9534f0&type=3#SerializableDictionary" />
|
||||
<ui:Foldout text="Label">
|
||||
<RPGCoreCommon.Helpers.UIElements.TwoPaneSplitView name="sd-header" fixed-pane-initial-dimension="150" class="row">
|
||||
<ui:Label text="KEY" class="row-left" />
|
||||
<ui:Label text="VALUE" class="row-right" />
|
||||
</RPGCoreCommon.Helpers.UIElements.TwoPaneSplitView>
|
||||
<ui:VisualElement name="sd-add-row" class="row">
|
||||
<uie:PropertyField class="row-left" />
|
||||
<ui:Label text="<-- select key before add" class="row-right" />
|
||||
</ui:VisualElement>
|
||||
<ui:ListView show-add-remove-footer="true" name="sd-list" reorderable="true" show-alternating-row-backgrounds="All" selection-type="Multiple" show-border="true" virtualization-method="DynamicHeight" reorder-mode="Animated" show-bound-collection-size="false" />
|
||||
</ui:Foldout>
|
||||
</ui:UXML>
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f99f6caeffbe77e4c9b1062ec393fbc8
|
||||
ScriptedImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
script: {fileID: 13804, guid: 0000000000000000e000000000000000, type: 0}
|
||||
@@ -0,0 +1,187 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using RPGCoreCommon.Helpers.CustomTypes;
|
||||
using UnityEditor;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace RPGCoreCommon.Helpers.Editor.Drawers
|
||||
{
|
||||
[Serializable]
|
||||
[CustomPropertyDrawer(typeof(SerializableDictionary<,>), true)]
|
||||
public class SerializableDictionaryDrawer : PropertyDrawer
|
||||
{
|
||||
[SerializeField] private VisualTreeAsset _visualTreeAsset;
|
||||
|
||||
private SerializedProperty _property;
|
||||
private SerializedProperty _tempKeyProperty;
|
||||
private SerializedProperty _keysProperty;
|
||||
private SerializedProperty _valuesProperty;
|
||||
|
||||
private SerializableDictionaryAttribute _sdAttribute;
|
||||
private Type _keyType;
|
||||
private Type _valueType;
|
||||
|
||||
public override VisualElement CreatePropertyGUI(SerializedProperty property)
|
||||
{
|
||||
_property = property;
|
||||
_sdAttribute = fieldInfo.GetCustomAttribute<SerializableDictionaryAttribute>() ?? new SerializableDictionaryAttribute();
|
||||
_tempKeyProperty = property.FindPropertyRelative("_tempKey");
|
||||
_keysProperty = property.FindPropertyRelative("_keys");
|
||||
_valuesProperty = property.FindPropertyRelative("_values");
|
||||
|
||||
if (_keysProperty == null || _valuesProperty == null)
|
||||
return new HelpBox("Keys and Values in SerializableDictionary must be serializable!", HelpBoxMessageType.Error);
|
||||
|
||||
var genericTypes = property.boxedValue.GetType().FindGenericDefinitionType(typeof(SerializableDictionary<,>)).GetGenericArguments();
|
||||
_keyType = genericTypes[0];
|
||||
_valueType = genericTypes[1];
|
||||
|
||||
var root = _visualTreeAsset.CloneTree();
|
||||
root.Q<Label>().text = preferredLabel;
|
||||
root.DisableDefaultAligning();
|
||||
|
||||
if (string.IsNullOrEmpty(preferredLabel))
|
||||
{
|
||||
var foldout = root.Q<Foldout>();
|
||||
foldout.Q<Toggle>().style.display = DisplayStyle.None;
|
||||
foldout.Q<Toggle>().value = true;
|
||||
foldout.Q(name: "unity-content").style.marginLeft = 0;
|
||||
}
|
||||
|
||||
var list = root.Q<ListView>("sd-list");
|
||||
list.BindProperty(_valuesProperty);
|
||||
list.makeItem = CreateRow;
|
||||
list.bindItem = BindRow;
|
||||
list.onAdd = OnAdd;
|
||||
list.onRemove = OnRemove;
|
||||
list.itemIndexChanged += OnSwap;
|
||||
|
||||
if (_sdAttribute is not null)
|
||||
{
|
||||
root.Q("sd-header").Q<Label>(className: "row-left").text = _sdAttribute.keyName;
|
||||
root.Q("sd-header").Q<Label>(className: "row-right").text = _sdAttribute.valueName;
|
||||
root.EnableInClassList("keys-disabled", !_sdAttribute.isKeyEditable);
|
||||
}
|
||||
|
||||
var dragline = root.Q("sd-header").Q("unity-dragline-anchor");
|
||||
dragline.RegisterCallback<GeometryChangedEvent>(ev =>
|
||||
{
|
||||
var parentWidth = dragline.parent.worldBound.width;
|
||||
var leftWidth = dragline.style.left.value.value;
|
||||
leftWidth = Mathf.Clamp(leftWidth, 150f, parentWidth-180f);
|
||||
|
||||
dragline.style.left = leftWidth;
|
||||
root.Query(className: "row-left").ForEach(element => element.style.width = leftWidth);
|
||||
});
|
||||
|
||||
var tempKeyField = root.Q("sd-add-row").Q<PropertyField>();
|
||||
tempKeyField.BindProperty(_tempKeyProperty);
|
||||
tempKeyField.HideFoldoutAndLabel();
|
||||
|
||||
// This additional key property should be always empty on beginning to not confuse user.
|
||||
// All because we are using "fake" serialized property to make unity draw field for key before even adding it to dictionary.
|
||||
_tempKeyProperty.boxedValue = _keyType.IsValueType ? Activator.CreateInstance(_keyType) : null;
|
||||
_property.serializedObject.ApplyModifiedProperties();
|
||||
_property.serializedObject.Update();
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
private void OnSwap(int index1, int index2)
|
||||
{
|
||||
_keysProperty.MoveArrayElement(index1, index2);
|
||||
_property.serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
private void OnAdd(BaseListView list)
|
||||
{
|
||||
var tempKey = _tempKeyProperty.boxedValue;
|
||||
|
||||
if (tempKey == null)
|
||||
{
|
||||
var helpBox = new HelpBox("Key is not selected!", HelpBoxMessageType.Error);
|
||||
helpBox.FadeOut(3000, 2000, true);
|
||||
list.parent.parent.Insert(0, helpBox);
|
||||
return;
|
||||
}
|
||||
|
||||
var keyExists = Enumerable.Range(0, _keysProperty.arraySize)
|
||||
.Select(i => _keysProperty.GetArrayElementAtIndex(i))
|
||||
.Any(prop => prop.boxedValue.Equals(tempKey));
|
||||
|
||||
if (keyExists)
|
||||
{
|
||||
var helpBox = new HelpBox("Key already exists!", HelpBoxMessageType.Error);
|
||||
helpBox.FadeOut(3000, 2000, true);
|
||||
list.parent.parent.Insert(0, helpBox);
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset temporary key to default
|
||||
_tempKeyProperty.boxedValue = _keyType.IsValueType ? Activator.CreateInstance(_keyType) : null;
|
||||
|
||||
// New Key
|
||||
_keysProperty.arraySize++;
|
||||
_keysProperty.GetArrayElementAtIndex(_keysProperty.arraySize - 1).boxedValue = tempKey;
|
||||
|
||||
// New Value
|
||||
_valuesProperty.arraySize++;
|
||||
_valuesProperty.GetArrayElementAtIndex(_valuesProperty.arraySize - 1).boxedValue =
|
||||
_valueType.IsValueType ? Activator.CreateInstance(_valueType) : null;
|
||||
|
||||
// Update list and property
|
||||
_property.serializedObject.ApplyModifiedProperties();
|
||||
_property.serializedObject.Update();
|
||||
list.RefreshItems();
|
||||
}
|
||||
|
||||
private void OnRemove(BaseListView list)
|
||||
{
|
||||
var ids = list.selectedIds.SortDescending().ToList();
|
||||
if (ids.Count < 0) return;
|
||||
|
||||
ids.ForEach(i =>
|
||||
{
|
||||
_keysProperty.DeleteArrayElementAtIndex(i);
|
||||
_valuesProperty.DeleteArrayElementAtIndex(i);
|
||||
});
|
||||
|
||||
// Update list and property
|
||||
_property.serializedObject.ApplyModifiedProperties();
|
||||
_property.serializedObject.Update();
|
||||
list.RefreshItems();
|
||||
}
|
||||
|
||||
private VisualElement CreateRow()
|
||||
{
|
||||
var row = new VisualElement();
|
||||
row.AddToClassList("row");
|
||||
|
||||
var keyField = new PropertyField();
|
||||
keyField.DisableDefaultAligning();
|
||||
keyField.AddToClassList("row-left");
|
||||
keyField.label = "";
|
||||
keyField.SetEnabled(_sdAttribute.isKeyEditable);
|
||||
row.Add(keyField);
|
||||
|
||||
var valueField = new PropertyField();
|
||||
valueField.HideFoldoutAndLabel();
|
||||
valueField.SetContextAligningHere();
|
||||
valueField.AddToClassList("row-right");
|
||||
valueField.label = "";
|
||||
row.Add(valueField);
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
private void BindRow(VisualElement row, int i)
|
||||
{
|
||||
if (i >= _keysProperty.arraySize) return;
|
||||
row.Q<PropertyField>(className: "row-left").BindProperty(_keysProperty.GetArrayElementAtIndex(i));
|
||||
row.Q<PropertyField>(className: "row-right").BindProperty(_valuesProperty.GetArrayElementAtIndex(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6ea25817e38041b7a6865551cf7cf6f9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences:
|
||||
- _visualTreeAsset: {fileID: 9197481963319205126, guid: f99f6caeffbe77e4c9b1062ec393fbc8, type: 3}
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using RPGCoreCommon.Helpers.CustomTypes;
|
||||
using RPGCoreCommon.Helpers.Editor.UIElements;
|
||||
using UnityEditor;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace RPGCoreCommon.Helpers.Editor.Drawers
|
||||
{
|
||||
[CustomPropertyDrawer(typeof(SerializableType))]
|
||||
public class SerializableTypeDrawer : PropertyDrawer
|
||||
{
|
||||
private const string NoTypeSelected = "No Type Selected";
|
||||
|
||||
public override VisualElement CreatePropertyGUI(SerializedProperty property)
|
||||
{
|
||||
var typeField = new FakeObjectField(preferredLabel);
|
||||
typeField.TrackPropertyValue(property, _ => UpdateTypeField(property, typeField));
|
||||
typeField.RegisterCallback<ClickEvent>(_ => ShowPopup(property, typeField), TrickleDown.TrickleDown);
|
||||
|
||||
UpdateTypeField(property, typeField);
|
||||
|
||||
return typeField;
|
||||
}
|
||||
|
||||
private void UpdateTypeField(SerializedProperty property, FakeObjectField field)
|
||||
{
|
||||
var typeName = ((SerializableType)property.boxedValue).type?.Name ?? NoTypeSelected;
|
||||
field.Set(typeName, EditorGUIUtility.FindTexture("cs Script Icon"));
|
||||
}
|
||||
|
||||
private void ShowPopup(SerializedProperty property, FakeObjectField field)
|
||||
{
|
||||
new SearchFieldPopup(field, GetOptions(property)).Show();
|
||||
}
|
||||
|
||||
private Dictionary<string, Action> GetOptions(SerializedProperty property)
|
||||
{
|
||||
var attr = fieldInfo.GetCustomAttribute<SerializableTypeAttribute>();
|
||||
var derivedFrom = attr?.derivedFrom ?? typeof(object);
|
||||
var limitedTo = attr?.limitedTo;
|
||||
|
||||
if (limitedTo?.Length > 0)
|
||||
{
|
||||
return limitedTo
|
||||
.GroupBy(type => type.FullName)
|
||||
.Select(group => group.First())
|
||||
.ToDictionary(type => type.FullName, type => new Action(() => SelectType(property, type)));
|
||||
}
|
||||
|
||||
return TypeCache.GetTypesDerivedFrom(derivedFrom)
|
||||
.AppendIf(derivedFrom, attr?.includeSelf ?? false)
|
||||
.Where(type => type.IsClass && type.IsPublic && !type.IsGenericType)
|
||||
.Where(type => (attr?.allowAbstract ?? false) || !type.IsAbstract)
|
||||
.GroupBy(type => type.FullName)
|
||||
.Select(group => group.First())
|
||||
.ToDictionary(type => type.FullName, type => new Action(() => SelectType(property, type)));
|
||||
}
|
||||
|
||||
private void SelectType(SerializedProperty property, Type type)
|
||||
{
|
||||
property.boxedValue = new SerializableType(type);
|
||||
property.serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 71afe6e8d45f4770b749f120130fa18a
|
||||
timeCreated: 1762517198
|
||||
Reference in New Issue
Block a user