177 lines
7.7 KiB
C#
177 lines
7.7 KiB
C#
using System;
|
|
using System.Linq;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
|
|
namespace JeffGrawAssets.FlexibleUI
|
|
{
|
|
public static class EditorHelpers
|
|
{
|
|
public enum Alignment { Left, Right, Center, Justified }
|
|
public enum FlexibleSpaceAllocation { Proportional, SmallestMinSizeFirst, LargestMinSizeFirst, SmallestFlexibleAreaFirst }
|
|
|
|
public static Rect[] DivideRect(Alignment alignment, Rect totalRect, float minElementPadding, float maxElementPadding, float edgePadding, params (float labelWidth, float minFieldWidth, float maxFieldWidth)[] sections)
|
|
=> DivideRect(alignment, FlexibleSpaceAllocation.Proportional, totalRect, minElementPadding, maxElementPadding, edgePadding, sections);
|
|
|
|
public static Rect[] DivideRect(Alignment alignment, FlexibleSpaceAllocation flexibleSpaceAllocation, Rect totalRect, float minElementPadding, float maxElementPadding, float edgePadding, params (float labelWidth, float minFieldWidth, float maxFieldWidth)[] sections)
|
|
{
|
|
if (alignment == Alignment.Right && flexibleSpaceAllocation == FlexibleSpaceAllocation.SmallestFlexibleAreaFirst)
|
|
Debug.Log("got here! " + totalRect.x);
|
|
var n = sections.Length;
|
|
if (n == 0)
|
|
return Array.Empty<Rect>();
|
|
|
|
if (alignment == Alignment.Justified)
|
|
{
|
|
alignment = Alignment.Center;
|
|
maxElementPadding = float.MaxValue;
|
|
}
|
|
|
|
int nonEmptySections = 0;
|
|
for (int i = 0; i < n; i++)
|
|
if (sections[i] != default)
|
|
nonEmptySections++;
|
|
|
|
float totalLabel = minElementPadding * (nonEmptySections - 1), totalMinField = 0f, totalMaxField = 0f;
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
totalLabel += sections[i].labelWidth;
|
|
totalMinField += sections[i].minFieldWidth;
|
|
totalMaxField += sections[i].maxFieldWidth;
|
|
}
|
|
|
|
var contentWidth = totalRect.width - 2 * edgePadding;
|
|
var rects = new Rect[n];
|
|
var totalSectionWidth = 0f;
|
|
|
|
if (flexibleSpaceAllocation == FlexibleSpaceAllocation.Proportional)
|
|
{
|
|
var minWidth = totalLabel + totalMinField;
|
|
var maxWidth = totalLabel + totalMaxField;
|
|
var growthPotential = totalMaxField - totalMinField;
|
|
var targetWidth = Mathf.Clamp(contentWidth, minWidth, maxWidth);
|
|
var growthFactor = growthPotential > 0 ? (targetWidth - minWidth) / growthPotential : 0f;
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
var growth = growthFactor * (sections[i].maxFieldWidth - sections[i].minFieldWidth);
|
|
rects[i].width = sections[i].labelWidth + sections[i].minFieldWidth + growth;
|
|
totalSectionWidth += rects[i].width;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var indices = flexibleSpaceAllocation switch
|
|
{
|
|
FlexibleSpaceAllocation.LargestMinSizeFirst => Enumerable.Range(0, n).OrderByDescending(i => sections[i].minFieldWidth).ToArray(),
|
|
FlexibleSpaceAllocation.SmallestMinSizeFirst => Enumerable.Range(0, n).OrderBy(i => sections[i].minFieldWidth).ToArray(),
|
|
_ => Enumerable.Range(0, n).OrderBy(i => sections[i].maxFieldWidth - sections[i].minFieldWidth).ToArray()
|
|
};
|
|
|
|
var remainingWidth = Mathf.Max(totalRect.width - totalLabel - totalMinField, 0);
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
var idx = indices[i];
|
|
var growth = Mathf.Min(sections[idx].maxFieldWidth - sections[idx].minFieldWidth, remainingWidth);
|
|
rects[idx].width = sections[idx].labelWidth + sections[idx].minFieldWidth + growth;
|
|
totalSectionWidth += rects[idx].width;
|
|
remainingWidth -= growth;
|
|
}
|
|
}
|
|
|
|
var availablePaddingSpace = contentWidth - totalSectionWidth;
|
|
var desiredPadding = nonEmptySections > 1 ? availablePaddingSpace / (nonEmptySections - 1) : 0f;
|
|
var actualPadding = Mathf.Clamp(desiredPadding, minElementPadding, maxElementPadding);
|
|
var totalWidth = totalSectionWidth + (nonEmptySections - 1) * actualPadding;
|
|
|
|
var startX = alignment switch
|
|
{
|
|
Alignment.Left => totalRect.x + edgePadding,
|
|
Alignment.Right => totalRect.x + totalRect.width - edgePadding - totalWidth,
|
|
_ => totalRect.x + edgePadding + (contentWidth - totalWidth) / 2,
|
|
};
|
|
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
var width = rects[i].width;
|
|
rects[i] = new Rect(startX, totalRect.y, width, totalRect.height);
|
|
startX += i < n - 1 && sections[i] != default ? width + actualPadding : width;
|
|
}
|
|
|
|
return rects;
|
|
}
|
|
|
|
public static Rect[] GetRectGrid(int numElements, float rowHeight, float minColumnWidth, float maxColumnWidth = float.PositiveInfinity, float elementPadding = 0f, float edgePadding = 0f, int maxColumns = int.MaxValue)
|
|
{
|
|
if (numElements <= 0)
|
|
return Array.Empty<Rect>();
|
|
|
|
var totalWidth = EditorGUIUtility.currentViewWidth;
|
|
var contentWidth = totalWidth - 2 * edgePadding;
|
|
var numColumns = Mathf.Max(1, Mathf.FloorToInt((contentWidth + elementPadding) / (minColumnWidth + elementPadding)));
|
|
numColumns = Mathf.Min(numColumns, maxColumns);
|
|
var availableWidthPerColumn = (contentWidth - (numColumns - 1) * elementPadding) / numColumns;
|
|
var columnWidth = Mathf.Min(availableWidthPerColumn, maxColumnWidth);
|
|
var numRows = Mathf.CeilToInt((float)numElements / numColumns);
|
|
var gridWidth = numColumns * columnWidth + (numColumns - 1) * elementPadding;
|
|
var gridHeight = numRows * rowHeight + (numRows - 1) * elementPadding;
|
|
var gridRect = GUILayoutUtility.GetRect(gridWidth + 2 * edgePadding, gridHeight);
|
|
|
|
var startX = gridRect.x + edgePadding + (contentWidth - gridWidth) / 2;
|
|
var startY = gridRect.y;
|
|
|
|
var rects = new Rect[numElements];
|
|
for (int i = 0; i < numElements; i++)
|
|
{
|
|
var col = i % numColumns;
|
|
var row = i / numColumns;
|
|
|
|
var x = startX + col * (columnWidth + elementPadding);
|
|
var y = startY + row * (rowHeight + elementPadding);
|
|
|
|
rects[i] = new Rect(x, y, columnWidth, rowHeight);
|
|
}
|
|
|
|
return rects;
|
|
}
|
|
|
|
private static readonly int ScrubControlHash = "JeffUIScrubControl".GetHashCode();
|
|
public static float Scrub(Rect rect)
|
|
{
|
|
var id = GUIUtility.GetControlID(ScrubControlHash, FocusType.Passive, rect);
|
|
EditorGUIUtility.AddCursorRect(rect, MouseCursor.SlideArrow, id);
|
|
|
|
var e = Event.current;
|
|
switch (e.GetTypeForControl(id))
|
|
{
|
|
case EventType.MouseDown:
|
|
if (e.button == 0 && rect.Contains(e.mousePosition))
|
|
{
|
|
GUIUtility.hotControl = id;
|
|
EditorGUIUtility.editingTextField = false;
|
|
EditorGUIUtility.SetWantsMouseJumping(1);
|
|
e.Use();
|
|
}
|
|
break;
|
|
case EventType.MouseUp:
|
|
if (GUIUtility.hotControl == id)
|
|
{
|
|
GUIUtility.hotControl = 0;
|
|
EditorGUIUtility.SetWantsMouseJumping(0);
|
|
e.Use();
|
|
}
|
|
break;
|
|
case EventType.MouseDrag:
|
|
if (GUIUtility.hotControl == id)
|
|
{
|
|
e.Use();
|
|
var factor = 1f;
|
|
if (e.shift) factor *= 4f;
|
|
if (e.alt) factor *= 0.5f;
|
|
return e.delta.x * factor;
|
|
}
|
|
break;
|
|
}
|
|
return 0f;
|
|
}
|
|
}
|
|
} |