Skip to content

Instantly share code, notes, and snippets.

@soraphis
Last active April 19, 2024 13:07
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save soraphis/7c98d1bb77ac9cc088a629335b342a90 to your computer and use it in GitHub Desktop.
Save soraphis/7c98d1bb77ac9cc088a629335b342a90 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
[CustomEditor(typeof(SegmentedStuff))]
public class SegmentationDrawer : Editor
{
private struct SerializedFloatArrayProperty
{
public readonly SerializedProperty property;
public int arraySize => property.arraySize;
public SerializedFloatArrayProperty(SerializedProperty property)
{
if(! property.isArray) throw new ArgumentException();
this.property = property;
}
public void RemoveAt(int index)
{
property.DeleteArrayElementAtIndex(index);
}
public void Insert(int index, float value)
{
property.InsertArrayElementAtIndex(index);
property.GetArrayElementAtIndex(index).floatValue = value;
}
public float this[int index]
{
get => property.GetArrayElementAtIndex(index).floatValue;
set => property.GetArrayElementAtIndex(index).floatValue = value;
}
}
private readonly List<Box> boxes = new List<Box>();
private VisualElement root;
private SerializedFloatArrayProperty floatValues;
private void OnEnable() {
floatValues = new SerializedFloatArrayProperty(serializedObject.FindProperty("floatValues"));
float sum = 0.0f;
for (int i = 0; i < floatValues.arraySize; ++i) sum += floatValues[i];
for (int i = 0; i < floatValues.arraySize; ++i) floatValues[i] /= sum;
}
private int separatorIndex;
private void AddSeparator(int index, VisualElement container) {
var separator = new Box();
separator.AddToClassList("separator");
void DragResize(MouseMoveEvent evt) {
var dx = evt.mouseDelta.x / container.contentRect.width;
var fx = Mathf.Min( floatValues[separatorIndex] + dx, 0);
fx = Mathf.Min(floatValues[separatorIndex+1] - dx, fx);
dx += fx * Mathf.Sign(dx);
floatValues[separatorIndex] += dx;
floatValues[separatorIndex+1] -= dx;
floatValues[separatorIndex] = Mathf.RoundToInt(floatValues[separatorIndex] * 1000) / 1000f;
floatValues[separatorIndex+1] = Mathf.RoundToInt(floatValues[separatorIndex+1] * 1000) / 1000f;
UpdateSizes();
}
separator.RegisterCallback<MouseDownEvent>(evt => {
separatorIndex = index;
container.RegisterCallback<MouseMoveEvent>(DragResize);
});
container.RegisterCallback<MouseUpEvent>(evt => {
separatorIndex = -1;
container.UnregisterCallback<MouseMoveEvent>(DragResize);
});
container.RegisterCallback<MouseLeaveEvent>(evt => {
separatorIndex = -1;
container.UnregisterCallback<MouseMoveEvent>(DragResize);
});
container.Add(separator);
}
private void UpdateSizes() {
float sum = 0.0f;
for (int i = 0; i < floatValues.arraySize; ++i) sum += floatValues[i];
for (int i = 0; i < floatValues.arraySize; ++i) floatValues[i] /= sum;
if (boxes.Count != floatValues.arraySize)
{
root.RemoveAt(0);
root.Insert(0, CreateContent());
return;
}
for (var i = 0; i < boxes.Count; i++) {
boxes[i].style.flexGrow = floatValues[i];
boxes[i].Q<Label>().text = $"{floatValues[i]:P}";
}
if(serializedObject.targetObject != null) // can happen with multiple inspectors
serializedObject.ApplyModifiedProperties();
}
VisualElement CreateContent()
{
var container = new BindableElement();
container.BindProperty(floatValues.property);
container.style.flexDirection = FlexDirection.Row;
boxes.Clear();
for (int i = 0; i < floatValues.arraySize; ++i) {
var b = new Box();
var l = new Label(string.Empty);
b.Add(l);
boxes.Add(b);
int j = i;
b.RegisterCallback<MouseDownEvent>(evt => {
if(evt.button != 0) return;
if (evt.shiftKey && !evt.ctrlKey) {
floatValues[j] /= 2;
floatValues.Insert(j, floatValues[j]);
root.RemoveAt(0);
root.Insert(0, CreateContent());
}
if (!evt.shiftKey && evt.ctrlKey) {
if (floatValues.arraySize > 1) {
int n = j == 0 ? 1 : j - 1;
floatValues[n] += floatValues[j];
floatValues.RemoveAt(j);
root.RemoveAt(0);
root.Insert(0, CreateContent());
}
}
});
}
UpdateSizes();
container.Add(boxes[0]);
for (int i = 1; i < boxes.Count; ++i) {
AddSeparator(i - 1, container);
container.Add(boxes[i]);
}
return container;
}
public override VisualElement CreateInspectorGUI()
{
root = new VisualElement();
root.Bind(serializedObject);
root.styleSheets.Add(AssetDatabase.LoadAssetAtPath<StyleSheet>("Assets/_Gyms/SegmentationDrawer/Editor/SegmentationDrawerStyle.uss"));
root.Add(CreateContent());
root.Add(new Label("Shift-Click to split segment in two"));
root.Add(new Label("Ctrl-Click to delete segment"));
var action = root.schedule.Execute(UpdateSizes);
action.Every(100); // ms
return root;
}
}
using UnityEngine;
public class SegmentedStuff : MonoBehaviour {
public float[] floatValues = new []{1f, 1f, 1f}; // some default values, don't make it empty
}
.unity-box{
height: 30;
flex-grow: 1;
flex-basis: 7;
justify-content: center;
}
.unity-box > Label{
-unity-text-align: middle-center;
overflow: hidden;
font-size: 8;
}
.unity-box:hover{
-unity-background-image-tint-color: #eeeeff;
}
.unity-box.separator{
flex-grow: 0;
flex-basis: 7;
margin: 0 -3;
cursor: split-resize-left-right;
background-image: none;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment