This commit is contained in:
2026-04-25 23:37:10 +02:00
commit 19d6bd934a
476 changed files with 9198 additions and 0 deletions
+3
View File
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 079fed2826f14a7da75cd8d7d84b67f6
timeCreated: 1759334623
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e35c80ecac124ccfa0a1124809dd8dde
timeCreated: 1762190736
@@ -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&amp;guid=215036bf11e09364a9da52f92c9534f0&amp;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="&lt;-- 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
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 39ab4f14276441b6afcbfcdf67f90a28
timeCreated: 1762713444
@@ -0,0 +1,21 @@
using RPGCoreCommon.Helpers.PropertyAttributeDrawers;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
namespace RPGCoreCommon.Helpers.Editor.PropertyAttributeDrawers
{
[CustomPropertyDrawer(typeof(LayerAttribute))]
public class LayerDrawer : PropertyDrawer
{
public override VisualElement CreatePropertyGUI(SerializedProperty property)
{
if (property.boxedValue is not int)
return new Label($"Field '{property.displayName}' is not INTEGER. {nameof(LayerAttribute)} can be used only on <b>INTEGER</b> field.");
var layerField = new LayerField(property.displayName);
layerField.BindProperty(property);
return layerField;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3a3af142730740288c3078e34cd62718
timeCreated: 1762713459
@@ -0,0 +1,21 @@
using RPGCoreCommon.Helpers.PropertyAttributeDrawers;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
namespace RPGCoreCommon.Helpers.Editor.PropertyAttributeDrawers
{
[CustomPropertyDrawer(typeof(LayerMaskAttribute))]
public class LayerMaskDrawer : PropertyDrawer
{
public override VisualElement CreatePropertyGUI(SerializedProperty property)
{
if (property.boxedValue is not int)
return new Label($"Field '{property.displayName}' is not INTEGER. {nameof(LayerMaskAttribute)} can be used only on <b>INTEGER</b> field.");
var layerMaskField = new LayerMaskField(property.displayName);
layerMaskField.BindProperty(property);
return layerMaskField;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a1a4f0b89e344d8a9aefc5767fdb860c
timeCreated: 1762714412
@@ -0,0 +1,19 @@
using RPGCoreCommon.Helpers.PropertyAttributeDrawers;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
namespace RPGCoreCommon.Helpers.Editor.PropertyAttributeDrawers
{
[CustomPropertyDrawer(typeof(ReadOnlyAttribute))]
public class ReadOnlyDrawer : PropertyDrawer
{
public override VisualElement CreatePropertyGUI(SerializedProperty property)
{
var propertyField = new PropertyField(property);
propertyField.name = "ReadOnly";
propertyField.RegisterCallback<GeometryChangedEvent>(_ => propertyField.SetEnabled(false));
return propertyField;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d6de1b490a8549d3a67957f5a2a8a03e
timeCreated: 1764603191
@@ -0,0 +1,17 @@
using System;
using RPGCoreCommon.Helpers.PropertyAttributeDrawers;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
namespace RPGCoreCommon.Helpers.Editor.PropertyAttributeDrawers
{
[CustomPropertyDrawer(typeof(SerializeReferenceHelperAttribute))]
public class SerializeReferenceHelperDrawer : PropertyDrawer
{
public override VisualElement CreatePropertyGUI(SerializedProperty property)
{
throw new NotImplementedException();
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9ffb1bb18c6d4a7a889c32c17d78a39c
timeCreated: 1773956371
@@ -0,0 +1,18 @@
{
"name": "RPGCoreCommon.Helpers.Editor",
"rootNamespace": "RPGCoreCommon.Helpers.Editor",
"references": [
"RPGCoreCommon.Helpers"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4506747573fa44a9877e5bf69f5bb4a8
timeCreated: 1759335131
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: fc2af81cae08451abf2593fd5fb18538
timeCreated: 1761410197
@@ -0,0 +1,66 @@
using UnityEngine;
using UnityEngine.UIElements;
namespace RPGCoreCommon.Helpers.Editor.UIElements
{
[UxmlElement]
public partial class FakeObjectField : VisualElement
{
private Image _objectImage;
private Label _objectLabel;
public FakeObjectField() : this(null) { }
public FakeObjectField(string labelText = "")
{
var field = new VisualElement();
field.AddToClassList("unity-object-field");
field.AddToClassList("unity-base-field");
field.AddToClassList("unity-base-field__aligned");
field.style.marginLeft = field.style.marginRight = 3;
if (!string.IsNullOrEmpty(labelText))
{
var label = new Label(labelText);
label.AddToClassList("unity-label");
label.AddToClassList("unity-base-field__label");
field.Add(label);
}
var fieldInput = new VisualElement();
fieldInput.AddToClassList("unity-base-field__input");
fieldInput.AddToClassList("unity-object-field__input");
field.Add(fieldInput);
var fieldObject = new VisualElement();
fieldObject.AddToClassList("unity-object-field__object");
fieldInput.Add(fieldObject);
_objectImage = new Image();
_objectImage.AddToClassList("unity-object-field-display__icon");
fieldObject.Add(_objectImage);
_objectLabel = new Label();
_objectLabel.AddToClassList("unity-object-field-display__label");
fieldObject.Add(_objectLabel);
var fieldSelector = new VisualElement();
fieldSelector.AddToClassList("unity-object-field__selector");
fieldInput.Add(fieldSelector);
Add(field);
}
public void Set(string text, Texture image)
{
_objectLabel.text = text;
_objectImage.image = image;
}
public void Unset()
{
_objectLabel.text = null;
_objectImage.image = null;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 42c0552c878947b394d70f8745d0ec3b
timeCreated: 1762538499
@@ -0,0 +1,9 @@
using UnityEngine.UIElements;
namespace RPGCoreCommon.Helpers.Editor.UIElements
{
[UxmlElement]
public partial class InspectorElement : UnityEditor.UIElements.InspectorElement
{
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2a2d499847854192a17c936d66e234b7
timeCreated: 1761410271
@@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
namespace RPGCoreCommon.Helpers.Editor.UIElements
{
public class SearchFieldPopup : PopupWindowContent
{
// Elements
private VisualElement _root;
private ToolbarSearchField _searchField;
private ScrollView _scrollView;
// Runtime
private VisualElement _boundTo;
private Dictionary<string, Action> _menuItems;
public SearchFieldPopup(VisualElement boundTo, Dictionary<string, Action> menuItems)
{
_boundTo = boundTo;
_menuItems = menuItems;
}
public override VisualElement CreateGUI()
{
_root = new VisualElement();
_root.style.maxHeight = 500;
_root.style.maxWidth = _boundTo.worldBound.width;
_searchField = new ToolbarSearchField();
_searchField.style.left = _searchField.style.right = 0;
_searchField.style.width = StyleKeyword.Auto;
_root.Add(_searchField);
_scrollView = new ScrollView();
_root.Add(_scrollView);
// Show all options
_menuItems.DictSelect((name, action) => new Button(() =>
{
action();
Close();
}) { text = name }).ForEach(_scrollView.Add);
// Do auto focus when appear
_root.RegisterCallback<AttachToPanelEvent>(_ => _searchField.Focus());
// ESC - close
// ENTER - select first visible
_root.RegisterCallback<KeyDownEvent>(ev =>
{
if (ev.keyCode == KeyCode.Escape) Close();
if (ev.keyCode == KeyCode.Return) _scrollView.Query<Button>().Visible().First().Click();
}, TrickleDown.TrickleDown);
return _root;
}
public void Show()
{
UnityEditor.PopupWindow.Show(_boundTo.worldBound, this);
}
public void Close()
{
editorWindow.Close();
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: adea6e28eb7a41d691381ba9405fdb74
timeCreated: 1762522548
+3
View File
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5dc28cb462b84ae1a7779750a9fae9a6
timeCreated: 1759334627
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d99760e987ce41308cdba70a87f65816
timeCreated: 1762188549
@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace RPGCoreCommon.Helpers.CustomTypes
{
[Serializable]
public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, ISerializationCallbackReceiver
{
[SerializeField] internal TKey _tempKey;
[SerializeField] internal List<TKey> _keys = new();
[SerializeReference] internal List<TValue> _values = new();
public void OnBeforeSerialize()
{
_keys = Keys.ToList();
_values = Values.ToList();
}
public void OnAfterDeserialize()
{
Clear();
for (var i = 0; i < _keys.Count; i++)
{
if (_keys[i] != null) Add(_keys[i], _values[i]);
}
_keys.Clear();
_values.Clear();
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 24246e0e86ad4108b4d1b8515aeca9e9
timeCreated: 1762188557
@@ -0,0 +1,23 @@
using UnityEngine;
namespace RPGCoreCommon.Helpers.CustomTypes
{
public class SerializableDictionaryAttribute : PropertyAttribute
{
public string keyName { get; private set; } = "KEY";
public string valueName { get; private set; } = "VALUE";
public bool isKeyEditable { get; private set; } = true;
public SerializableDictionaryAttribute()
{
}
public SerializableDictionaryAttribute(string keyName, string valueName, bool isKeyEditable = true)
{
this.keyName = keyName;
this.valueName = valueName;
this.isKeyEditable = isKeyEditable;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4615aa55725644e48fc7cf85bd9c4ba0
timeCreated: 1762345454
@@ -0,0 +1,36 @@
using System;
using UnityEngine;
namespace RPGCoreCommon.Helpers.CustomTypes
{
[Serializable]
public class SerializableType : ISerializationCallbackReceiver, ICloneable
{
public Type type { get; internal set; }
[SerializeField] internal string assemblyQualifiedName;
public SerializableType(Type type)
{
this.type = type;
assemblyQualifiedName = type?.AssemblyQualifiedName;
}
public void OnBeforeSerialize()
{
assemblyQualifiedName = type?.AssemblyQualifiedName ?? assemblyQualifiedName;
}
public void OnAfterDeserialize()
{
if (string.IsNullOrEmpty(assemblyQualifiedName) == false)
type = Type.GetType(assemblyQualifiedName, false);
}
public SerializableType Clone() => new(type);
object ICloneable.Clone() => Clone();
public static implicit operator Type(SerializableType serializableType) => serializableType.type;
public static implicit operator SerializableType(Type type) => new(type);
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9e60b347531c46aaa17029cd96c78bf4
timeCreated: 1762516011
@@ -0,0 +1,26 @@
using System;
using UnityEngine;
namespace RPGCoreCommon.Helpers.CustomTypes
{
[AttributeUsage(AttributeTargets.Field)]
public class SerializableTypeAttribute : PropertyAttribute
{
public bool includeSelf { get; private set; }
public bool allowAbstract { get; private set; }
public Type derivedFrom { get; private set; }
public Type[] limitedTo { get; private set; }
public SerializableTypeAttribute(Type derivedFrom, bool includeSelf = true, bool allowAbstract = false)
{
this.derivedFrom = derivedFrom;
this.includeSelf = includeSelf;
this.allowAbstract = allowAbstract;
}
public SerializableTypeAttribute(Type[] limitedTo)
{
this.limitedTo = limitedTo;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ea3922244e274799a4113964b9da4f83
timeCreated: 1762517259
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a1554117a8b14cbdb719f40f40ea71f2
timeCreated: 1759334728
@@ -0,0 +1,17 @@
using System;
namespace RPGCoreCommon.Helpers.Exceptions
{
public class MemberNotFoundException : Exception
{
public readonly string path;
public readonly Type objType;
public MemberNotFoundException(string path, Type objType)
: base($"Property '{path}' not found in '{objType}'")
{
this.path = path;
this.objType = objType;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 418add21a24b4cee9309453d1ada1530
timeCreated: 1757322594
@@ -0,0 +1,21 @@
using System;
namespace RPGCoreCommon.Helpers.Exceptions
{
public class MemberValueWrongTypeException : Exception
{
public readonly string path;
public readonly Type objType;
public readonly Type expectedType;
public readonly Type wrongType;
public MemberValueWrongTypeException(string path, Type objType, Type expectedType, Type wrongType)
: base($"Property '{path}' in {objType} is of type '{wrongType}', expected type '{expectedType}'")
{
this.path = path;
this.objType = objType;
this.expectedType = expectedType;
this.wrongType = wrongType;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7594d527fc654804a2c564b71fe756bd
timeCreated: 1757322677
@@ -0,0 +1,11 @@
using System;
namespace RPGCoreCommon.Helpers.Exceptions
{
public class NotEnoughElementsForRandomPickException : Exception
{
public NotEnoughElementsForRandomPickException(string message) : base(message)
{
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 25fedc34477644f8b9df4ddcf4c0b701
timeCreated: 1756991204
@@ -0,0 +1,8 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("RPGCoreCommon.Helpers.Editor")]
namespace RPGCoreCommon.Helpers
{
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: fab5a3ff08dd4b35b7d68f496c013e8a
timeCreated: 1762190797
@@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace RPGCoreCommon.Helpers
{
public static class LinqExtensions
{
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> source, Action<T> action)
{
foreach (var element in source) action.Invoke(element);
return source;
}
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> source, Action<int, T> action)
{
var index = 0;
foreach (var element in source) action(index++, element);
return source;
}
public static IEnumerable<T> Sort<T>(this IEnumerable<T> source, Comparison<T> comparison)
{
var list = source.ToList();
list.Sort(comparison);
return list;
}
public static IEnumerable<T> Sort<T>(this IEnumerable<T> source)
{
return source.OrderBy(x => x);
}
public static IEnumerable<T> SortDescending<T>(this IEnumerable<T> source)
{
return source.OrderByDescending(x => x);
}
public static IEnumerable<TResult> DictSelect<TKey, TValue, TResult>(this IDictionary<TKey, TValue> source, Func<TKey, TValue, TResult> func)
{
foreach (var pair in source) yield return func(pair.Key, pair.Value);
}
public static IDictionary<TKey,TValue> DictForEach<TKey, TValue>(this IDictionary<TKey,TValue> source, Action<TKey, TValue> action)
{
foreach (var pair in source) action(pair.Key, pair.Value);
return source;
}
public static string StringJoin(this IEnumerable<string> source, string separator)
{
return string.Join(separator, source);
}
public static IEnumerable<T> AppendIf<T>(this IEnumerable<T> source, T element, bool condition)
{
return condition ? source.Append(element) : source;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3ac97972c1444fa0aaebfdb837a58fc2
timeCreated: 1759338756
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: be7897ed56a743f98832cf13a5f9f866
timeCreated: 1762713378
@@ -0,0 +1,11 @@
using System;
using UnityEngine;
namespace RPGCoreCommon.Helpers.PropertyAttributeDrawers
{
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class LayerAttribute : PropertyAttribute
{
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3349d2cb583b4a56805895c7eb808a72
timeCreated: 1762713403
@@ -0,0 +1,11 @@
using System;
using UnityEngine;
namespace RPGCoreCommon.Helpers.PropertyAttributeDrawers
{
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class LayerMaskAttribute : PropertyAttribute
{
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cfd2f0c410cf43b38901fd4bc70bb606
timeCreated: 1762714485
@@ -0,0 +1,11 @@
using UnityEngine;
namespace RPGCoreCommon.Helpers.PropertyAttributeDrawers
{
public class ReadOnlyAttribute : PropertyAttribute
{
public ReadOnlyAttribute(bool applyToCollection = false) : base(applyToCollection)
{
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 544dc97fbe8f4c258ef3a72e450297eb
timeCreated: 1764603113
@@ -0,0 +1,9 @@
using UnityEngine;
namespace RPGCoreCommon.Helpers.PropertyAttributeDrawers
{
public class SerializeReferenceHelperAttribute : PropertyAttribute
{
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 67561284ae71463fbd4a1ec370d0fafd
timeCreated: 1773956312
@@ -0,0 +1,14 @@
{
"name": "RPGCoreCommon.Helpers",
"rootNamespace": "RPGCoreCommon.Helpers",
"references": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bfaf89ec9b1449debf236030e69884d6
timeCreated: 1759335092
@@ -0,0 +1,138 @@
using System;
using System.Collections.Generic;
using System.Linq;
using RPGCoreCommon.Helpers.Exceptions;
using UnityEngine;
namespace RPGCoreCommon.Helpers
{
/// <summary>
/// This utility class basses on Unity's <see cref="UnityEngine.Random"/> which uses:
/// <ul>
/// <li><see cref="UnityEngine.Random.InitState"/> to manage seed</li>
/// <li><see cref="UnityEngine.Random.value"/> to get random value</li>
/// </ul>
/// </summary>
public static class RandomHelper
{
/// <summary>
/// <inheritdoc cref="UnityEngine.Random.InitState"/>
/// Alias to Unity's <see cref="UnityEngine.Random.InitState"/>.
/// </summary>
/// <param name="seed"><inheritdoc cref="UnityEngine.Random.InitState" path="/param[@name='seed']"/></param>
public static void InitState(int seed)
{
UnityEngine.Random.InitState(seed);
}
/// <summary>
/// Returns <b>float</b> between min (inclusive) and max (inclusive).
/// </summary>
public static float RandomFloat(float min = 0f, float max = 1f)
{
return UnityEngine.Random.value * (max-min) + min;
}
/// <summary>
/// Returns integer between minimum (inclusive) and maximum (inclusive).
/// </summary>
public static int RandomInt(int minInclusive = 0, int maxInclusive = 100)
{
return Mathf.RoundToInt(UnityEngine.Random.value * (maxInclusive-minInclusive)) + minInclusive;
}
/// <summary>
/// Returns random key from given list.
/// </summary>
public static int RandomKey<T>(IEnumerable<T> enumerable)
{
var list = enumerable.ToList();
if (!list.Any()) throw new NotEnoughElementsForRandomPickException("List need at least one element.");
return RandomInt(0, list.Count - 1);
}
/// <summary>
/// Returns random element from given list.
/// </summary>
public static T RandomElement<T>(IEnumerable<T> enumerable)
{
return RandomElements(enumerable, 1).First();
}
/// <summary>
/// Returns new list with randomized elements.<br/>
/// List needs to have at least equal or more elements that given count! If repeatable then list needs to have at least 1 element.<br/>
/// </summary>
/// <exception cref="NotEnoughElementsForRandomPickException">When given list does not have enough elements to choose from.</exception>
public static List<T> RandomElements<T>(IEnumerable<T> enumerable, int count, bool repeatable = true)
{
var list = enumerable.ToList();
var neededCount = repeatable ? 1 : list.Count;
if (list.Count < neededCount) throw new NotEnoughElementsForRandomPickException($"Not enough elements in list. Need={neededCount}, Provided={list.Count}");
if (!repeatable) return list.OrderBy(_ => RandomFloat()).Take(count).ToList();
return new T[count].Select(_ => list[RandomInt(0, list.Count - 1)]).ToList();
}
/// <summary>
/// Returns keys from given list by its weight.
/// </summary>
/// <exception cref="NotEnoughElementsForRandomPickException">When given list does not have any elements to choose from.</exception>
public static int RandomKeyByWeight<T>(IEnumerable<T> enumerable, Func<T, float> weightGetter)
{
var list = enumerable.ToList();
if (!list.Any()) throw new NotEnoughElementsForRandomPickException("List need at least one element.");
var maxWeight = list.Sum(weightGetter.Invoke);
var randomWeight = RandomFloat(0, maxWeight);
for (var i = 0; i < list.Count; i++)
{
randomWeight -= weightGetter.Invoke(list[i]);
if (randomWeight <= 0) return i;
}
return 0;
}
/// <summary>
/// Returns element from given list by its weight.
/// </summary>
/// <exception cref="NotEnoughElementsForRandomPickException">When given list does not have any elements to choose from.</exception>
public static T RandomElementByWeight<T>(IEnumerable<T> enumerable, Func<T, float> weightGetter)
{
return RandomElementsByWeight(enumerable, weightGetter, 1).First();
}
/// <summary>
/// Returns elements from given list by its weight.
/// </summary>
/// <exception cref="NotEnoughElementsForRandomPickException">When given list does not have enough elements to choose from.</exception>
public static List<T> RandomElementsByWeight<T>(IEnumerable<T> enumerable, Func<T, float> weightGetter, int count, bool repeatable = true)
{
var list = enumerable.ToList();
var listResult = new List<T>();
var neededCount = repeatable ? 1 : list.Count;
if (list.Count < neededCount) throw new NotEnoughElementsForRandomPickException($"Not enough elements in list. Need={neededCount}, Provided={list.Count}");
for (var i = 0; i < count; i++)
{
var randomKey = RandomKeyByWeight(list, weightGetter);
listResult.Add(list[randomKey]);
if (!repeatable) list.RemoveAt(randomKey);
}
return listResult;
}
/// <summary>
/// Percentage chance by given float. Simply checks if random float is smaller than given one.
/// </summary>
/// <param name="chance">Chance to get true. <b>0.0f</b> = 0%, <b>1.0f</b> = 100%</param>
public static bool Chance(float chance)
{
return RandomFloat() < Mathf.Clamp01(chance);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: cd6d508bd1616174fb1350b8e4d3aebe
@@ -0,0 +1,168 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using RPGCoreCommon.Helpers.Exceptions;
namespace RPGCoreCommon.Helpers
{
public static class ReflectionExtensions
{
public const BindingFlags DefaultBindingFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase;
/// <summary>Works with any member (type, field, property, method)</summary>
/// <returns>type of this member's value</returns>
public static Type GetMemberValueType(this MemberInfo memberInfo)
{
return memberInfo switch
{
Type type => type,
FieldInfo fieldInfo => fieldInfo.FieldType,
PropertyInfo propertyInfo => propertyInfo.PropertyType,
MethodInfo methodInfo => methodInfo.ReturnType,
_ => null
};
}
/// <summary>Works with any member (type, field, property, method)</summary>
/// <param name="memberInfo">this member</param>
/// <param name="obj">Object to use for this reflection</param>
/// <returns>member's value</returns>
public static object GetMemberValue(this MemberInfo memberInfo, object obj)
{
return memberInfo switch
{
Type => obj,
FieldInfo fi => fi.GetValue(obj),
PropertyInfo pi => pi.GetValue(obj, null),
MethodInfo mi => mi.Invoke(obj, null),
_ => null
};
}
/// <summary>Works with any member (type, field, property, method)</summary>
/// <param name="memberInfo">this member</param>
/// <param name="path">path from current member with dot "." as delimiter</param>
/// <param name="bindingFlags">Custom binding flags to use</param>
/// <returns>TYPE of member's value from given path</returns>
/// <exception cref="MemberNotFoundException">when member is not found</exception>
public static Type GetMemberValueTypeByPath(this MemberInfo memberInfo, string path, BindingFlags bindingFlags = DefaultBindingFlags)
{
var pathParts = path.Split('.');
var memberType = memberInfo.GetMemberValueType();
foreach (var pathPart in pathParts)
{
var member = memberType.GetMember(pathPart, bindingFlags).FirstOrDefault();
if (member == null) throw new MemberNotFoundException(path, memberType.GetMemberValueType());
memberType = member.GetMemberValueType();
}
return memberType;
}
/// <summary>This function supports all members: type, property, field, method</summary>
/// <param name="memberInfo">Type (or any memberInfo) that will be used to get result value. When not provided memberInfo will be taken from obj type</param>
/// <param name="obj">Object from which finding will be performed</param>
/// <param name="path">path from current member with dot "." as delimiter</param>
/// <param name="bindingFlags">Custom binding flags to use</param>
/// <returns>member's value from given path</returns>
/// <exception cref="MemberNotFoundException">When property at given path doesn't exist</exception>
public static object GetMemberValueByPath(this MemberInfo memberInfo, object obj, string path, BindingFlags bindingFlags = DefaultBindingFlags)
{
var pathParts = path.Split('.');
var memberType = memberInfo.GetMemberValueType();
var memberObj = obj;
foreach (var pathPart in pathParts)
{
var member = memberType.GetMember(pathPart, bindingFlags).FirstOrDefault();
if (member == null) throw new MemberNotFoundException(path, memberType.GetMemberValueType());
memberType = member.GetMemberValueType();
memberObj = memberType.GetMemberValue(memberObj);
}
return memberObj;
}
/// <inheritdoc cref="GetMemberValueByPath(MemberInfo, object, string, BindingFlags)"/>
/// <exception cref="MemberValueWrongTypeException">When result is not desired type</exception>
/// <typeparam name="T">Result type</typeparam>
public static T GetMemberValueByPath<T>(this MemberInfo memberInfo, object obj, string path, BindingFlags bindingFlags = DefaultBindingFlags)
{
var result = GetMemberValueByPath(memberInfo, obj, path, bindingFlags);
if (result is T typedResult) return typedResult;
throw new MemberValueWrongTypeException(path, obj.GetType(), typeof(T), result.GetType());
}
/// <inheritdoc cref="GetMemberValueByPath(MemberInfo, object, string, BindingFlags)"/>
public static object GetMemberValueByPath(object obj, string path, BindingFlags bindingFlags = DefaultBindingFlags)
{
return GetMemberValueByPath(obj.GetType(), obj, path, bindingFlags);
}
/// <summary>
/// Tries to find specific generic type in currently implementation.
/// </summary>
/// <param name="type">This type</param>
/// <param name="genericTypeDefinition">Generic type to find</param>
/// <returns>Found generic type with parameters</returns>
/// <exception cref="Exception">Generic type to find isn't generic.</exception>
public static Type FindGenericDefinitionType(this Type type, Type genericTypeDefinition)
{
if (!genericTypeDefinition.IsGenericTypeDefinition) throw new Exception("The generic argument must be a generic type definition.");
var tempType = type;
while (tempType != null && tempType != typeof(object))
{
if (tempType.IsGenericType && tempType.GetGenericTypeDefinition() == genericTypeDefinition) return tempType;
tempType = tempType.BaseType;
}
return null;
}
/// <summary>
/// Checks if specific generic type in currently implementation has type as argument at index.
/// </summary>
/// <param name="type">this type</param>
/// <param name="genericTypeDefinition">Generic type to find</param>
/// <param name="argIndex">index to check</param>
/// <param name="argType">type in generic implementation</param>
/// <returns></returns>
public static bool IsArgTypeInGenericDefinitionType(this Type type, Type genericTypeDefinition, int argIndex, Type argType)
{
return type.FindGenericDefinitionType(genericTypeDefinition).GenericTypeArguments.ElementAtOrDefault(argIndex) == argType;
}
/// <summary>
/// Checks if this member has public read accessor.
/// </summary>
/// <param name="memberInfo">this member</param>
/// <returns>TRUE if member exists and is public</returns>
public static bool IsMemberReadPublic(this MemberInfo memberInfo)
{
return memberInfo switch
{
Type type => type.IsPublic,
PropertyInfo pi => pi.GetGetMethod() != null,
MethodInfo mi => mi.IsPublic,
FieldInfo fi => fi.IsPublic,
_ => false
};
}
public static IEnumerable<Type> GetAllTypes(this Type forType, bool withObject = false)
{
var tempType = forType;
while (tempType is not null)
{
yield return tempType;
tempType = tempType.BaseType;
if (!withObject && tempType == typeof(object)) yield break;
}
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b8692e0796364209851d145aefa07e1c
timeCreated: 1759334645
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5365711d18c94e37abd7c37755b59188
timeCreated: 1761335420
@@ -0,0 +1,9 @@
using UnityEngine.UIElements;
namespace RPGCoreCommon.Helpers.UIElements
{
[UxmlElement]
public partial class HelpBox : UnityEngine.UIElements.HelpBox
{
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9f50a07c4f534d81a419229e92605e27
timeCreated: 1761340341
@@ -0,0 +1,9 @@
using UnityEngine.UIElements;
namespace RPGCoreCommon.Helpers.UIElements
{
[UxmlElement]
public partial class TwoPaneSplitView : UnityEngine.UIElements.TwoPaneSplitView
{
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 1c45b936017280542bca4e473d23d685
@@ -0,0 +1,78 @@
using UnityEngine;
namespace RPGCoreCommon.Helpers
{
public static class VectorExtensions
{
public static Vector3 Scale(this Vector3 vector, float scale)
{
return new Vector3(vector.x * scale, vector.y * scale, vector.z * scale);
}
public static Vector3 Scale(this Vector3 vector, float x, float y, float z)
{
return new Vector3(vector.x * x, vector.y * y, vector.z * z);
}
public static Vector3 Add(this Vector3 vector, float x, float y, float z)
{
return vector.AddX(x).AddY(y).AddZ(z);
}
public static Vector3 AddX(this Vector3 vector, float x)
{
vector.x += x;
return vector;
}
public static Vector3 AddY(this Vector3 vector, float y)
{
vector.y += y;
return vector;
}
public static Vector3 AddZ(this Vector3 vector, float z)
{
vector.z += z;
return vector;
}
public static Vector3 Set(this Vector3 vector, float x, float y, float z)
{
return vector.SetX(x).SetY(y).SetZ(z);
}
public static Vector3 SetX(this Vector3 vector, float x)
{
vector.x = x;
return vector;
}
public static Vector3 SetY(this Vector3 vector, float y)
{
vector.y = y;
return vector;
}
public static Vector3 SetZ(this Vector3 vector, float z)
{
vector.z = z;
return vector;
}
public static Vector3 ToVector3_XZ(this Vector2 vector)
{
return new Vector3(vector.x, 0, vector.y);
}
public static Vector3 ToVector3_XY(this Vector2 vector)
{
return new Vector3(vector.x, vector.y, 0);
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5b93c4334a254d6a9efa9ade68eb4572
timeCreated: 1766008109
@@ -0,0 +1,146 @@
using System;
using System.Linq;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
namespace RPGCoreCommon.Helpers
{
public static class VisualElementExtensions
{
public static void Click(this VisualElement visualElement)
{
using var clickEvent = ClickEvent.GetPooled();
clickEvent.target = visualElement;
visualElement.panel.visualTree.SendEvent(clickEvent);
if (visualElement is not Button button) return;
using var ev = new NavigationSubmitEvent();
ev.target = button;
button.SendEvent(ev);
}
public static void DisableDefaultAligning(this VisualElement visualElement)
{
const string class1 = "unity-base-field__inspector-field";
const string class2 = "unity-base-field__aligned";
visualElement.RegisterCallback<GeometryChangedEvent>(_ =>
{
visualElement.Query(className: class1).ForEach(el => el.RemoveFromClassList(class1));
visualElement.Query(className: class2).ForEach(el => el.RemoveFromClassList(class2));
}, TrickleDown.TrickleDown);
}
public static void SetContextAligningHere(this VisualElement visualElement)
{
visualElement.AddToClassList("unity-inspector-element");
visualElement.AddToClassList("unity-inspector-main-container");
}
/// <summary>
/// Only for <see cref="PropertyField"/> that has array element as <see cref="SerializedProperty"/>.
/// By default, those <see cref="PropertyField"/>s always draw inside <see cref="Foldout"/>.
/// When this function is called only content of foldout will be displayed (foldout will be hidden).
/// </summary>
/// <param name="propertyField">Property field that will draw array element</param>
public static void HideFoldoutAndLabel(this PropertyField propertyField)
{
propertyField.RegisterCallback<GeometryChangedEvent>(_ =>
{
propertyField.label = "";
if (propertyField.Q<Foldout>(name: "unity-foldout-" + propertyField.bindingPath) is {} foldout2)
{
foldout2.Q<Toggle>().style.display = DisplayStyle.None;
foldout2.Q<Toggle>().value = true;
foldout2.Q(name: "unity-content").style.marginLeft = 0;
}
else if (propertyField.Q<Label>(name: "unity-input-" + propertyField.bindingPath) is {} label)
{
label.style.display = DisplayStyle.None;
}
});
}
public static void RemoveFoldoutAndLabel(this PropertyField propertyField)
{
propertyField.RegisterCallback<GeometryChangedEvent>(_ =>
{
if (propertyField.Q<Foldout>(name: "unity-foldout-" + propertyField.bindingPath) is {} foldout)
{
var children = foldout.Q(name: "unity-content").Children().ToList();
children.ForEach(propertyField.Add);
foldout.RemoveFromHierarchy();
}
else if (propertyField.Q<Label>(name: "unity-input-" + propertyField.bindingPath) is {} label)
{
label.RemoveFromHierarchy();
}
});
}
public static void FadeOut(this VisualElement visualElement, float delay, float duration, bool remove)
{
if (visualElement.panel != null)
FadeOutPrivate(visualElement, delay, duration, remove);
else
visualElement.RegisterCallback<AttachToPanelEvent>(_ => FadeOutPrivate(visualElement, delay, duration, remove));
}
private static void FadeOutPrivate(VisualElement visualElement, float delayMs, float durationMs, bool remove)
{
const int waitMs = 50;
visualElement.style.opacity = 1f;
visualElement.schedule
.Execute(_ =>
{
visualElement.style.opacity = visualElement.style.opacity.value - waitMs / durationMs;
if (visualElement.style.opacity.value > 0f) return;
visualElement.style.opacity = 0f;
if (remove) visualElement.RemoveFromHierarchy();
})
.Every(waitMs)
.Until(() => visualElement.style.opacity.value <= 0f)
.ExecuteLater(Convert.ToInt64(delayMs));
}
public static void RemoveOnClick(this VisualElement visualElement, float delayMs = 0f, float durationMs = 250f)
{
visualElement.RegisterCallback<ClickEvent>(_ => FadeOutPrivate(visualElement, delayMs, durationMs, true));
}
public static void InsertBefore(this VisualElement thisElement, VisualElement otherElement)
{
var index = thisElement.parent.IndexOf(thisElement);
thisElement.parent.Insert(index, otherElement);
}
public static void InsertAfter(this VisualElement thisElement, VisualElement otherElement)
{
var index = thisElement.parent.IndexOf(thisElement);
if (index == thisElement.parent.childCount-1)
thisElement.parent.Add(otherElement);
else
thisElement.parent.Insert(index, otherElement);
}
public static bool IsAncestorOf(this VisualElement thisElement, VisualElement otherElement)
{
if (thisElement == null || otherElement == null) return false;
var ancestor = otherElement.parent;
while (ancestor != null)
{
if (ancestor == thisElement) return true;
ancestor = ancestor.parent;
}
return false;
}
public static bool IsDescendantOf(this VisualElement thisElement, VisualElement otherElement)
{
return otherElement.IsAncestorOf(thisElement);
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6b3f87689a16482f8bcb2e1869729299
timeCreated: 1761508983