Initial commit
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "BracerLib.Editor",
|
||||
"rootNamespace": "BracerLib.Editor",
|
||||
"references": [
|
||||
"GUID:a60bafe2eaba2f24fbb33ab1ab52999b"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ed2fb3312eb6acc4e90f5b6ac5964596
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0d3660d7d1878fd4dab8b7a904c7a0be
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,26 @@
|
||||
using BracerLib.Data;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
namespace BracerLib.Editor.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// Referenced via: https://discussions.unity.com/t/select-only-one-layer-in-the-inspector-select-only-one-layer-in-the-inspector/230727
|
||||
/// </summary>
|
||||
[CustomPropertyDrawer(typeof(Layer))]
|
||||
[ExcludeFromCoverage]
|
||||
public class LayerPropertyDrawer : PropertyDrawer
|
||||
{
|
||||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
|
||||
{
|
||||
EditorGUI.BeginProperty(position, GUIContent.none, property);
|
||||
var layerIndex = property.FindPropertyRelative("layerIndex");
|
||||
|
||||
position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
|
||||
if (layerIndex != null)
|
||||
layerIndex.intValue = EditorGUI.LayerField(position, layerIndex.intValue);
|
||||
EditorGUI.EndProperty();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b603be9d2db54a6ba8f16f73927f322c
|
||||
timeCreated: 1773804726
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aebda41daf072e0438978241a08410d4
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using BracerLib.UI;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.UI;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
namespace BracerLib.Editor.UI
|
||||
{
|
||||
//Don't forget to put this file inside a 'Editor' folder
|
||||
[CanEditMultipleObjects]
|
||||
[CustomEditor(typeof(NonDrawingGraphic), false)]
|
||||
[ExcludeFromCoverage]
|
||||
public class NonDrawingGraphicEditor : GraphicEditor
|
||||
{
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
EditorGUI.BeginDisabledGroup(true);
|
||||
EditorGUILayout.PropertyField(m_Script, Array.Empty<GUILayoutOption>());
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
// skipping AppearanceControlsGUI
|
||||
RaycastControlsGUI();
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3bea4dc700684cb5953636c68a1543e7
|
||||
timeCreated: 1686367053
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dfe5690b14e7536429ffc5eee664df2c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using UnityEditor;
|
||||
using UnityEditor.IMGUI.Controls;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
namespace BracerLib.Editor.Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// Referenced via https://github.com/marijnz/unity-autocomplete-search-field
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[Obsolete("Use Odin or some attribute-based item instead.")]
|
||||
[ExcludeFromCoverage]
|
||||
public class EditorSearchField
|
||||
{
|
||||
private static class Styles
|
||||
{
|
||||
public const float RESULT_HEIGHT = 20f;
|
||||
public const float RESULT_BORDER_WIDTH = 2f;
|
||||
public const float RESULT_MARGIN = 15f;
|
||||
public const float RESULT_LABEL_OFFSET = 2f;
|
||||
|
||||
public static readonly GUIStyle ENTRY_EVEN;
|
||||
public static readonly GUIStyle ENTRY_ODD;
|
||||
public static readonly GUIStyle LABEL_STYLE;
|
||||
public static readonly GUIStyle RESULTS_BORDER_STYLE;
|
||||
|
||||
static Styles()
|
||||
{
|
||||
ENTRY_EVEN = new GUIStyle("CN EntryBackEven");
|
||||
ENTRY_ODD = new GUIStyle("CN EntryBackOdd");
|
||||
RESULTS_BORDER_STYLE = new GUIStyle("hostview");
|
||||
|
||||
LABEL_STYLE = new GUIStyle
|
||||
{
|
||||
alignment = TextAnchor.MiddleLeft,
|
||||
richText = true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static void RepaintFocusedWindow()
|
||||
{
|
||||
if (EditorWindow.focusedWindow != null)
|
||||
EditorWindow.focusedWindow.Repaint();
|
||||
}
|
||||
|
||||
private SearchField searchField;
|
||||
private string searchString;
|
||||
|
||||
public event Action<string> OnInputChanged;
|
||||
|
||||
public bool HasSearchString => !string.IsNullOrEmpty(SearchString);
|
||||
public string SearchString
|
||||
{
|
||||
get => searchString;
|
||||
set
|
||||
{
|
||||
searchString = value;
|
||||
OnGUI();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnGUI()
|
||||
{
|
||||
var rect = GUILayoutUtility.GetRect(1, 1, 18, 18, GUILayout.ExpandWidth(true));
|
||||
GUILayout.BeginHorizontal();
|
||||
|
||||
DoSearchField(rect);
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
private void DoSearchField(Rect rect)
|
||||
{
|
||||
if (searchField == null)
|
||||
searchField = new SearchField();
|
||||
|
||||
var result = searchField.OnGUI(rect, searchString);
|
||||
if (result != searchString)
|
||||
OnInputChanged?.Invoke(result);
|
||||
|
||||
searchString = result;
|
||||
|
||||
if (HasSearchbarFocused())
|
||||
RepaintFocusedWindow();
|
||||
}
|
||||
|
||||
private bool HasSearchbarFocused() => GUIUtility.keyboardControl == searchField.searchFieldControlID;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e0320c64c0c142a999d375385c65efee
|
||||
timeCreated: 1727491490
|
||||
@@ -0,0 +1,287 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
namespace BracerLib.Editor.Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// A set-up for a component that can be a searchable list. Deprecated but fun to create.
|
||||
/// Doesn't work for entry adds.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
[ExcludeFromCoverage]
|
||||
[Obsolete]
|
||||
public class SearchableScrollView<T>
|
||||
{
|
||||
public delegate void DataDelegate(int index, string originalValue, string newValue);
|
||||
|
||||
public delegate bool ValidationDelegate(string entry);
|
||||
|
||||
private static class Styles
|
||||
{
|
||||
public static readonly GUIStyle SELECTED;
|
||||
|
||||
static Styles()
|
||||
{
|
||||
var t = new Texture2D(1, 1);
|
||||
t.SetPixel(0, 0, new Color(0.4f, 0.4f, 0.4f));
|
||||
t.Apply();
|
||||
|
||||
SELECTED = new GUIStyle(EditorStyles.label);
|
||||
SELECTED.normal.textColor = Color.white;
|
||||
SELECTED.normal.background = t;
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly string EDIT_FIELD = "selectedTextField";
|
||||
|
||||
private readonly EditorSearchField searchField;
|
||||
private IList<T> data;
|
||||
private IList<string> dataLabels = new List<string>();
|
||||
private IList<string> searchResults;
|
||||
private readonly IDictionary<int, int> filteredLabelsToData = new Dictionary<int, int>();
|
||||
private EventType prevEventType;
|
||||
private int selectedIndex = -1;
|
||||
private bool isEditing;
|
||||
private bool isNewEntry;
|
||||
private bool cancelEdit;
|
||||
private bool editingFinished;
|
||||
private string originalEntryText;
|
||||
private string editedEntryText;
|
||||
private Vector2 scrollPosition;
|
||||
|
||||
public float MaxWidth { get; set; }
|
||||
public IList<T> Data
|
||||
{
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
return;
|
||||
|
||||
data = value;
|
||||
|
||||
searchField.SearchString = string.Empty;
|
||||
|
||||
dataLabels.Clear();
|
||||
dataLabels = data.Select(d => d.ToString()).ToList();
|
||||
|
||||
filteredLabelsToData.Clear();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The modified index of the selection in the list with the search results taken into account.
|
||||
/// </summary>
|
||||
public int SelectedIndex => filteredLabelsToData.ContainsKey(selectedIndex) ? filteredLabelsToData[selectedIndex] : selectedIndex;
|
||||
public ValidationDelegate ValidateEntry { get; set; }
|
||||
public DataDelegate DataEntryAdded { get; set; }
|
||||
public DataDelegate DataEntryEdited { get; set; }
|
||||
public DataDelegate DataEntryRemoved { get; set; }
|
||||
|
||||
private IList<string> TargetData => (searchResults?.Count ?? 0) > 0 ? searchResults : dataLabels;
|
||||
|
||||
public SearchableScrollView()
|
||||
{
|
||||
searchField = new EditorSearchField();
|
||||
searchField.OnInputChanged += SearchFieldInputChanged;
|
||||
}
|
||||
|
||||
public void OnGUI()
|
||||
{
|
||||
HandleEditing();
|
||||
|
||||
GUILayout.BeginVertical();
|
||||
GUILayout.BeginVertical(EditorStyles.helpBox);
|
||||
|
||||
searchField.OnGUI();
|
||||
|
||||
scrollPosition = GUILayout.BeginScrollView(scrollPosition, false, true, GUILayout.MaxWidth(MaxWidth));
|
||||
GUILayout.BeginVertical();
|
||||
|
||||
// Output all keys in the list, with some being modified in style or interact-ability by way of flags
|
||||
var newEntryIndex = -1;
|
||||
var targetData = TargetData;
|
||||
for (var i = 0; i < (targetData?.Count ?? 0); i++)
|
||||
{
|
||||
var target = targetData![i];
|
||||
var selected = selectedIndex == i;
|
||||
|
||||
if (isEditing && selected)
|
||||
{
|
||||
if (originalEntryText == null)
|
||||
{
|
||||
originalEntryText = target;
|
||||
editedEntryText = target;
|
||||
}
|
||||
|
||||
GUI.SetNextControlName(EDIT_FIELD);
|
||||
editedEntryText = GUILayout.TextField(editedEntryText, Styles.SELECTED);
|
||||
GUI.FocusControl(EDIT_FIELD);
|
||||
}
|
||||
else if (cancelEdit && selected)
|
||||
{
|
||||
cancelEdit = false;
|
||||
|
||||
if (isNewEntry)
|
||||
newEntryIndex = i;
|
||||
else
|
||||
targetData[i] = originalEntryText;
|
||||
|
||||
originalEntryText = null;
|
||||
editedEntryText = null;
|
||||
}
|
||||
else if (editingFinished && selected)
|
||||
{
|
||||
editingFinished = false;
|
||||
UpdateListValue(i, originalEntryText, editedEntryText);
|
||||
|
||||
originalEntryText = null;
|
||||
editedEntryText = null;
|
||||
}
|
||||
else if (GUILayout.Button(target, selected ? Styles.SELECTED : EditorStyles.label) && !isEditing)
|
||||
selectedIndex = selected ? -1 : i;
|
||||
}
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
GUILayout.EndVertical();
|
||||
GUILayout.EndScrollView();
|
||||
|
||||
GUILayout.EndVertical();
|
||||
|
||||
GUILayout.Label("F2 = Edit entry\nEnter = Accept and apply changes\nEscape = Cancel entry edit", EditorStyles.helpBox);
|
||||
|
||||
GUILayout.EndVertical();
|
||||
}
|
||||
|
||||
public void AddNewEntry()
|
||||
{
|
||||
searchField.SearchString = string.Empty;
|
||||
dataLabels.Add(string.Empty);
|
||||
|
||||
selectedIndex = dataLabels.Count - 1;
|
||||
isEditing = true;
|
||||
isNewEntry = true;
|
||||
}
|
||||
|
||||
public void DeleteEntry()
|
||||
{
|
||||
if (selectedIndex < 0)
|
||||
return;
|
||||
|
||||
dataLabels.RemoveAt(SelectedIndex);
|
||||
|
||||
DataEntryRemoved?.Invoke(SelectedIndex, null, null);
|
||||
|
||||
searchField.SearchString = string.Empty;
|
||||
}
|
||||
|
||||
private void FinalizeNewEntry()
|
||||
{
|
||||
if (!isEditing)
|
||||
return;
|
||||
|
||||
isEditing = false;
|
||||
editingFinished = true;
|
||||
}
|
||||
|
||||
private void HandleEditing()
|
||||
{
|
||||
var eventType = Event.current.type;
|
||||
var isKeyDown = eventType == EventType.KeyDown;
|
||||
if (selectedIndex < 0 || !isKeyDown || eventType == prevEventType)
|
||||
{
|
||||
if (!isKeyDown)
|
||||
prevEventType = eventType;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
prevEventType = eventType;
|
||||
|
||||
var keyCode = Event.current.keyCode;
|
||||
switch (keyCode)
|
||||
{
|
||||
case KeyCode.Escape:
|
||||
if (!isEditing)
|
||||
return;
|
||||
|
||||
isEditing = false;
|
||||
cancelEdit = true;
|
||||
|
||||
break;
|
||||
case KeyCode.Return:
|
||||
case KeyCode.KeypadEnter:
|
||||
FinalizeNewEntry();
|
||||
|
||||
break;
|
||||
case KeyCode.F2:
|
||||
isEditing = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateListValue(int index, string origText, string editedText)
|
||||
{
|
||||
if (editedText == origText || (ValidateEntry != null && !ValidateEntry(editedText)))
|
||||
{
|
||||
if (isNewEntry)
|
||||
{
|
||||
isNewEntry = false;
|
||||
|
||||
dataLabels.RemoveAt(dataLabels.Count - 1);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (filteredLabelsToData.TryGetValue(index, out var origIndex))
|
||||
{
|
||||
searchResults[index] = editedText;
|
||||
dataLabels[origIndex] = editedText;
|
||||
|
||||
index = origIndex;
|
||||
}
|
||||
else
|
||||
dataLabels[index] = editedText;
|
||||
|
||||
if (isNewEntry)
|
||||
{
|
||||
isNewEntry = false;
|
||||
DataEntryAdded?.Invoke(index, null, editedText);
|
||||
}
|
||||
else
|
||||
DataEntryEdited?.Invoke(index, origText, editedText);
|
||||
}
|
||||
|
||||
private void SearchFieldInputChanged(string searchString)
|
||||
{
|
||||
searchResults ??= new List<string>();
|
||||
|
||||
searchResults.Clear();
|
||||
filteredLabelsToData.Clear();
|
||||
|
||||
if (searchString != null)
|
||||
{
|
||||
var newIndex = 0;
|
||||
searchResults = dataLabels.Where((label, index) =>
|
||||
{
|
||||
var result = label.IndexOf(searchString, StringComparison.OrdinalIgnoreCase) >= 0;
|
||||
|
||||
if (result)
|
||||
filteredLabelsToData.Add(newIndex++, index);
|
||||
|
||||
return result;
|
||||
}).ToList();
|
||||
}
|
||||
else
|
||||
searchResults = dataLabels;
|
||||
|
||||
if (selectedIndex >= searchResults.Count)
|
||||
selectedIndex = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7685e09f2a5b45dc82e8010310780446
|
||||
timeCreated: 1727611465
|
||||
@@ -0,0 +1,21 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.TestTools;
|
||||
|
||||
namespace BracerLib.Editor.Utility
|
||||
{
|
||||
[ExcludeFromCoverage]
|
||||
public static class TextureUtility
|
||||
{
|
||||
public static Texture2D MakeTexture(int width, int height, Color color)
|
||||
{
|
||||
var pixels = new Color[width * height];
|
||||
for (var i = 0; i < pixels.Length; i++)
|
||||
pixels[i] = color;
|
||||
var tex = new Texture2D(width, height);
|
||||
tex.SetPixels(pixels);
|
||||
tex.Apply();
|
||||
|
||||
return tex;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 73762c73e5e84089b53c84bbf6e66665
|
||||
timeCreated: 1716170448
|
||||
Reference in New Issue
Block a user