Last active
June 16, 2024 18:09
-
-
Save markeahogan/adf37c77f441d4b5666299727fd547d3 to your computer and use it in GitHub Desktop.
Utility functions for IMGUI Rects, useful for drawing PropertyFields in a single line without lots of Rect boilerplate
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
namespace PopupAsylum | |
{ | |
/// <summary> | |
/// Utility functions for IMGUI Rects, useful for drawing PropertyFields in a single line | |
/// Works by returning the Rect to draw the contol and filling the out argument with a Rect for the remaining space | |
/// By passing the same Rect to the out member it can keep eating chunks of the property's Rect | |
/// | |
/// void ExampleDrawer(SerializedProperty property, Rect rect) | |
/// { | |
/// rect = rect.Margin(5); | |
/// EditorGUI.PropertyField(rect.TakeSingleLine(), property.FindRelative("a")); | |
/// EditorGUI.PropertyField(rect.TakeSingleLine(), property.FindRelative("b")); | |
/// EditorGUI.PropertyField(rect.TakeSingleLine(), property.FindRelative("c")); | |
/// } | |
/// </summary> | |
public static class IMGUIRectExtensions | |
{ | |
private static float SingleLine => | |
#if UNITY_EDITOR | |
UnityEditor.EditorGUIUtility.singleLineHeight; | |
#else | |
13; | |
#endif | |
private static float Spacing => | |
#if UNITY_EDITOR | |
UnityEditor.EditorGUIUtility.standardVerticalSpacing; | |
#else | |
2; | |
#endif | |
private static float LabelWidth => | |
#if UNITY_EDITOR | |
UnityEditor.EditorGUIUtility.labelWidth; | |
#else | |
80; | |
#endif | |
/// <summary> | |
/// Returns a Rect inset from the paramater equally on all sides | |
/// </summary> | |
public static Rect Margin(this Rect rect, float all) => Margin(rect, all, all, all, all); | |
/// <summary> | |
/// Returns a Rect inset from the paramater equally on top/bottom and equally on left/right | |
/// </summary> | |
public static Rect Margin(this Rect rect, float leftRight, float topBottom) => Margin(rect, leftRight, leftRight, topBottom, topBottom); | |
/// <summary> | |
/// Returns a Rect inset from the paramater by the specified values | |
/// </summary> | |
public static Rect Margin(this Rect rect, float left, float right, float top, float bottom) | |
{ | |
rect.height -= (top + bottom); | |
rect.width -= (left + right); | |
rect.x += left; | |
rect.y += top; | |
return rect; | |
} | |
/// <summary> | |
/// Returns an array of rects representing the parameter divided vertially, with optional spacing between rects | |
/// </summary> | |
public static Rect[] DivideY(this Rect rect, int count, float spacing = -1) | |
{ | |
DefaultSpacing(ref spacing); | |
var result = new Rect[count]; | |
result[0] = rect; | |
float availableSpace = rect.height - spacing * (count - 1); | |
result[0].height = availableSpace / count; | |
for (int i = 1; i < count; i++) | |
{ | |
result[i] = result[0]; | |
result[i].y += (result[0].height + spacing) * i; | |
} | |
return result; | |
} | |
/// <summary> | |
/// Returns an array of rects representing the parameter divided horizontally, with optional spacing between rects | |
/// </summary> | |
public static Rect[] DivideX(this Rect rect, int count, float spacing = 0) | |
{ | |
DefaultSpacing(ref spacing); | |
var result = new Rect[count]; | |
result[0] = rect; | |
float availableSpace = rect.width - spacing * (count - 1); | |
result[0].width = availableSpace / count; | |
for (int i = 1; i < count; i++) | |
{ | |
result[i] = result[0]; | |
result[i].x += (result[0].width + spacing) * i; | |
} | |
return result; | |
} | |
/// <summary> | |
/// Splits the rect into two at the 'height' and returns the top half, out Rect remaining will contain the bottom half | |
/// If height is negative it will be split relative to the bottom rather than the top | |
/// </summary> | |
public static Rect TakeTop(this ref Rect rect, float height, float spacing = -1) | |
{ | |
DefaultSpacing(ref spacing); | |
var topHalf = rect; | |
var bottomHalf = rect; | |
topHalf.height = Mathf.Min(height < 0 ? rect.height + height : height, rect.height); // using + subtract because height is negative | |
bottomHalf.height = Mathf.Max(rect.height - topHalf.height - spacing, 0); | |
bottomHalf.y += topHalf.height + spacing; | |
rect = bottomHalf; | |
return topHalf; | |
} | |
/// <summary> | |
/// Splits the rect into two at the 'height' and returns the bottom half, out Rect remaining will contain the top half | |
/// If height is negative it will be split relative to the top rather than the bottom | |
/// </summary> | |
public static Rect TakeBottom(this ref Rect rect, float height, float spacing = 0) | |
{ | |
var topHalf = TakeTop(ref rect, -height, 0); | |
topHalf.height -= DefaultSpacing(ref spacing); | |
var bottomHalf = rect; | |
rect = topHalf; | |
return bottomHalf; | |
} | |
/// <summary> | |
/// Splits the rect into two at the 'width' and returns the left half, out Rect remaining will contain the right half | |
/// If width is negative it will be split relative to the right edge rather than the left edge | |
/// </summary> | |
public static Rect TakeLeft(this ref Rect rect, float width, float spacing = 0) | |
{ | |
DefaultSpacing(ref spacing); | |
var leftHalf = rect; | |
var rightHalf = rect; | |
leftHalf.width = Mathf.Min(width < 0 ? rect.width + width : width, rect.width); // using + to subtract because width is negative | |
rightHalf.width = Mathf.Max(rect.width - leftHalf.width - spacing, 0); | |
rightHalf.x += leftHalf.width + spacing; | |
rect = rightHalf; | |
return leftHalf; | |
} | |
/// <summary> | |
/// Splits the rect into two at the 'width' and returns the right half, out Rect remaining will contain the left half | |
/// If width is negative it will be split relative to the left edge rather than the right edge | |
/// </summary> | |
public static Rect TakeRight(this ref Rect rect, float width, float spacing = 0) | |
{ | |
var leftHalf = TakeLeft(ref rect, -width, 0); | |
leftHalf.width -= DefaultSpacing(ref spacing); | |
var rightHalf = rect; | |
rect = leftHalf; | |
return rightHalf; | |
} | |
/// <summary> | |
/// Returns a rect for a single line property and populates bottomHalf with the remaining space | |
/// </summary> | |
/// <param name="rect">the rect to take from</param> | |
/// <param name="bottomHalf">the rect to be overwritten with the remaining space</param> | |
/// <param name="spacing">an extra space to be removed from the bottom half, when <0 standardVerticalSpacing is used</param> | |
/// <returns></returns> | |
public static Rect TakeSingleLine(this ref Rect rect, float spacing = -1) | |
{ | |
return TakeTop(ref rect, SingleLine, spacing); | |
} | |
public static Rect TakeControlLabel(this ref Rect rect) | |
{ | |
return TakeLeft(ref rect, LabelWidth); | |
} | |
/// <summary> | |
/// Calculates the height of a set of single line properties | |
/// </summary> | |
/// <param name="lineCount">the number of single line properties</param> | |
/// <param name="spacing">(optional) override for spacing between lines, when < 0 standardVerticalSpacing will be used</param> | |
/// <returns>The height for the properties</returns> | |
public static float SumSingleLineHeights(int lineCount, float spacing = -1) | |
{ | |
return lineCount * SingleLine + (lineCount - 1) * DefaultSpacing(ref spacing); | |
} | |
/// <summary> | |
/// Populates contentRect with a rect of the specified height to be used in a scroll rect, returns the orignal rect | |
/// </summary> | |
public static Rect ScrollContentY(this Rect rect, float contentHeight, out Rect contentRect) | |
{ | |
contentRect = new Rect(rect); | |
contentRect.height = contentHeight; | |
if (contentHeight > rect.height) contentRect.width -= 14f; | |
return rect; | |
} | |
/// <summary> | |
/// When the passed value if < 0, returns the standard vertical spacing | |
/// </summary> | |
private static float DefaultSpacing(ref float input) => input = (input >= 0 ? input : Spacing); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment