-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -101,3 +101,6 @@ fabric.properties | |
|
||
## Unity editor user settings | ||
UserSettings/* | ||
|
||
# Obsidian | ||
.obsidian/ |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
using System; | ||
using UnityEditor; | ||
using UnityEngine; | ||
|
||
namespace SneakySquirrelLabs.MinMaxRangeAttribute.Editor | ||
{ | ||
[CustomPropertyDrawer(typeof(MinMaxRangeAttribute))] | ||
internal class MinMaxRangeDrawer : PropertyDrawer | ||
{ | ||
#region Fields | ||
|
||
private const float HorizontalSpacing = 5f; | ||
private const float SliderHandlerWidth = 12f; | ||
|
||
private static readonly float VerticalSpacing = EditorGUIUtility.standardVerticalSpacing; | ||
private static GUIStyle LabelStyleField; | ||
|
||
private uint _decimals; | ||
|
||
#endregion | ||
|
||
#region Properties | ||
|
||
private static GUIStyle LabelStyle => LabelStyleField ??= new GUIStyle(GUI.skin.label); | ||
|
||
#endregion | ||
|
||
#region Update | ||
|
||
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) | ||
{ | ||
return base.GetPropertyHeight(property, label) * 2; | ||
} | ||
|
||
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) | ||
{ | ||
if (attribute is not MinMaxRangeAttribute minMaxAttribute) | ||
{ | ||
Debug.LogError("Min max range attribute failed to draw."); | ||
return; | ||
} | ||
|
||
var minLimit = minMaxAttribute.MinLimit; | ||
var maxLimit = minMaxAttribute.MaxLimit; | ||
_decimals = minMaxAttribute.Decimals; | ||
|
||
if (property.propertyType == SerializedPropertyType.Vector2Int) | ||
{ | ||
var value = property.vector2IntValue; | ||
var minValue = (float)value.x; | ||
var maxValue = (float)value.y; | ||
DrawSlider(position, property, minLimit, maxLimit, ref minValue, ref maxValue, BuildIntLabel); | ||
value.x = (int)minValue; | ||
value.y = (int)maxValue; | ||
property.vector2IntValue = value; | ||
} | ||
else if (property.propertyType == SerializedPropertyType.Vector2) | ||
{ | ||
var value = property.vector2Value; | ||
var minValue = value.x; | ||
var maxValue = value.y; | ||
DrawSlider(position, property, minLimit, maxLimit, ref minValue, ref maxValue, BuildFloatLabel); | ||
value.x = minValue; | ||
value.y = maxValue; | ||
property.vector2Value = value; | ||
} | ||
|
||
static void DrawSlider(Rect position, SerializedProperty property, float min, float max, ref float x, | ||
ref float y, Func<float, GUIContent> buildLabel) | ||
{ | ||
var consumedX = 0f; | ||
var firstLineRect = new Rect(position) { height = position.height / 2 - VerticalSpacing}; | ||
|
||
// Field name | ||
consumedX += DrawFieldName(firstLineRect, property); | ||
|
||
// Min label | ||
var minLabel = buildLabel(min); | ||
consumedX += DrawLabel(firstLineRect, consumedX, minLabel); | ||
consumedX += HorizontalSpacing; // Add spacing between min label and slider | ||
|
||
// Slider | ||
var maxLabel = buildLabel(max); | ||
var maxLabelWidth = LabelStyle.CalcSize(maxLabel).x; // We need to know the max label's size | ||
var sliderWidth = firstLineRect.width - consumedX - maxLabelWidth - HorizontalSpacing; | ||
var sliderPosition = new Rect(firstLineRect) { x = firstLineRect.x + consumedX, width = sliderWidth }; | ||
EditorGUI.MinMaxSlider(sliderPosition, ref x, ref y, min, max); | ||
consumedX += sliderWidth + HorizontalSpacing; | ||
|
||
// Max label | ||
DrawLabel(firstLineRect, consumedX, maxLabel); | ||
|
||
// Value labels | ||
var secondLineRect = new Rect(position) { y = position.y, height = firstLineRect.height}; | ||
var valuesY = secondLineRect.y + sliderPosition.height + EditorGUIUtility.standardVerticalSpacing; | ||
// X label | ||
var labelsPosition = new Rect(sliderPosition) { y = valuesY }; | ||
DrawValueLabel(labelsPosition, x, min, max, true, buildLabel); | ||
// Label | ||
DrawValueLabel(labelsPosition, y, min, max, false, buildLabel); | ||
|
||
static float DrawFieldName(Rect position, SerializedProperty property) | ||
{ | ||
var labelPosition = new Rect(position) { width = EditorGUIUtility.labelWidth }; | ||
EditorGUI.LabelField(labelPosition, property.displayName); | ||
return labelPosition.width; | ||
} | ||
|
||
static float DrawLabel(Rect position, float xOffset, GUIContent label) | ||
{ | ||
var size = LabelStyle.CalcSize(label); | ||
var minLabelPosition = new Rect(position) { x = position.x + xOffset, width = size.x}; | ||
EditorGUI.LabelField(minLabelPosition, label, LabelStyle); | ||
return size.x; | ||
} | ||
|
||
static void DrawValueLabel(Rect position, float value, float minLimit, float maxLimit, | ||
bool applyExtraOffset, Func<float, GUIContent> buildLabel) | ||
{ | ||
var label = buildLabel(value); | ||
var labelSize = LabelStyle.CalcSize(label); | ||
var relativePosition = (value - minLimit) / (maxLimit - minLimit); | ||
var offset = SliderHandlerWidth / 2 + (applyExtraOffset ? -labelSize.x : 0); | ||
var totalWidth = position.width - SliderHandlerWidth; | ||
var x = position.x + relativePosition * totalWidth + offset; | ||
var labelPosition = new Rect(position) { x = x, width = labelSize.x }; | ||
EditorGUI.LabelField(labelPosition, label, LabelStyle); | ||
} | ||
} | ||
|
||
static GUIContent BuildIntLabel(float value) => new($"{value:F0}"); | ||
|
||
GUIContent BuildFloatLabel(float value) | ||
{ | ||
var floatLabel = _decimals switch | ||
{ | ||
0 => $"{value:F0}", | ||
1 => $"{value:F1}", | ||
2 => $"{value:F2}", | ||
3 => $"{value:F3}", | ||
_ => throw new NotSupportedException("Min max attribute supports up to 3 decimal places.") | ||
}; | ||
return new GUIContent(floatLabel); | ||
} | ||
} | ||
|
||
#endregion | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
{ | ||
"name": "SneakySquirrelLabs.MinMaxRangeAttribute.Editor", | ||
"rootNamespace": "SneakySquirrelLabs.MinMaxRangeAttribute.Editor", | ||
"references": [ | ||
"GUID:397edf9bfe7ea4399b706e9ee953b982" | ||
], | ||
"includePlatforms": [ | ||
"Editor" | ||
], | ||
"excludePlatforms": [], | ||
"allowUnsafeCode": false, | ||
"overrideReferences": false, | ||
"precompiledReferences": [], | ||
"autoReferenced": true, | ||
"defineConstraints": [], | ||
"versionDefines": [], | ||
"noEngineReferences": false | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
using System; | ||
using UnityEngine; | ||
|
||
namespace SneakySquirrelLabs.MinMaxRangeAttribute | ||
{ | ||
/// <summary> | ||
/// An attribute that simplifies defining bounded ranges (ranges with minimum and maximum limits) on the inspector. | ||
/// </summary> | ||
[AttributeUsage(AttributeTargets.Field)] | ||
public class MinMaxRangeAttribute : PropertyAttribute | ||
{ | ||
#region Fields | ||
|
||
public readonly float MinLimit; | ||
public readonly float MaxLimit; | ||
public readonly uint Decimals; | ||
|
||
#endregion | ||
|
||
#region Setup | ||
|
||
/// <summary> | ||
/// A bounded range for integers. | ||
/// </summary> | ||
/// <param name="minLimit">The minimum acceptable value.</param> | ||
/// <param name="maxLimit">The maximum acceptable value.</param> | ||
public MinMaxRangeAttribute(int minLimit, int maxLimit) | ||
{ | ||
MinLimit = minLimit; | ||
MaxLimit = maxLimit; | ||
} | ||
|
||
/// <summary> | ||
/// A bounded range for floats. | ||
/// </summary> | ||
/// <param name="minLimit">The minimum acceptable value.</param> | ||
/// <param name="maxLimit">The maximum acceptable value.</param> | ||
/// <param name="decimals">How many decimals the inspector labels should display. Values must be in the [0,3] | ||
/// range. Default is 1.</param> | ||
public MinMaxRangeAttribute(float minLimit, float maxLimit, uint decimals = 1) | ||
{ | ||
MinLimit = minLimit; | ||
MaxLimit = maxLimit; | ||
Decimals = decimals; | ||
} | ||
|
||
#endregion | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"name": "SneakySquirrelLabs.MinMaxRangeAttribute", | ||
"rootNamespace": "SneakySquirrelLabs.MinMaxRangeAttribute", | ||
"references": [], | ||
"includePlatforms": [], | ||
"excludePlatforms": [], | ||
"allowUnsafeCode": false, | ||
"overrideReferences": false, | ||
"precompiledReferences": [], | ||
"autoReferenced": true, | ||
"defineConstraints": [], | ||
"versionDefines": [], | ||
"noEngineReferences": false | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
{ | ||
"name": "com.sneakysquirrellabs.minmaxrangeattribute", | ||
"version": "1.0.0", | ||
"displayName": "Min/max range attribute", | ||
"description": "A bounded (i.e., with a minimum and maximum) range attribute for Unity's Vector2 and Vector2Int fields.", | ||
"unity": "2021.3", | ||
"documentationUrl": "https://github.com/matheusamazonas/min_max_range_attribute", | ||
"changelogUrl": "https://github.com/matheusamazonas/min_max_range_attribute/releases", | ||
"licensesUrl": "https://github.com/matheusamazonas/min_max_range_attribute/LICENSE", | ||
"keywords": [ | ||
"editor", | ||
"attribute", | ||
"terrain inspector", | ||
"vector", | ||
"range" | ||
], | ||
"author": { | ||
"name": "Matheus Amazonas", | ||
"email": "matheus.amazonas@gmail.com", | ||
"url": "https://matheusamazonas.net" | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.