using System;
using System.Runtime.CompilerServices;
using UnityEngine;
using UnityEngine.Rendering.HighDefinition;
namespace UnityEditor.Rendering.HighDefinition
{
///
/// Serialized version of .
///
internal class SerializedScalableSetting
{
public SerializedProperty values;
public SerializedProperty schemaId;
public SerializedScalableSetting(SerializedProperty property)
{
values = property.FindPropertyRelative("m_Values");
schemaId = property.FindPropertyRelative("m_SchemaId.m_Id");
}
/// Get the value of level .
/// The type of the scalable setting.
/// The level to get.
///
/// The value of the level if the level was found.
///
/// default(T) when:
/// - The level does not exists (level index is out of range)
/// - The level value has multiple different values
///
/// true when the value was evaluated, false when the value could not be evaluated.
public bool TryGetLevelValue(int level, out T value)
where T : struct
{
if (level < values.arraySize && level >= 0)
{
var levelValue = values.GetArrayElementAtIndex(level);
if (levelValue.hasMultipleDifferentValues)
{
value = default;
return false;
}
else
{
value = levelValue.GetInline();
return true;
}
}
else
{
value = default;
return false;
}
}
}
internal static class SerializedScalableSettingUI
{
///
/// Draw the scalable setting as a single line field with multiple values.
///
/// There will be one value per level.
///
/// The type of the scalable setting.
/// The scalable setting to draw.
/// The label of the field.
public static void ValueGUI(this SerializedScalableSetting self, GUIContent label)
where T : struct
{
var schema = ScalableSettingSchema.GetSchemaOrNull(new ScalableSettingSchemaId(self.schemaId.stringValue))
?? ScalableSettingSchema.GetSchemaOrNull(ScalableSettingSchemaId.With3Levels);
var rect = GUILayoutUtility.GetRect(0, float.Epsilon, EditorGUIUtility.singleLineHeight, EditorGUIUtility.singleLineHeight);
// Magic Number !!
rect.x += 3;
rect.width -= 6;
// Magic Number !!
var contentRect = EditorGUI.PrefixLabel(rect, label);
EditorGUI.showMixedValue = self.values.hasMultipleDifferentValues;
var count = schema.levelCount;
if (self.values.arraySize != count)
self.values.arraySize = count;
if (typeof(T) == typeof(bool))
LevelValuesFieldGUI(contentRect, self, count, schema);
else if (typeof(T) == typeof(int))
LevelValuesFieldGUI(contentRect, self, count, schema);
else if (typeof(T) == typeof(float))
LevelValuesFieldGUI(contentRect, self, count, schema);
else if (typeof(T).IsEnum)
LevelValuesFieldGUI(contentRect, self, count, schema);
EditorGUI.showMixedValue = false;
}
///
/// Draw the value fields for each levels of the scalable setting.
///
/// Assumes that the generic type is the type stored in the .
///
/// The type of the scalable setting.
/// Rect used to draw the GUI.
/// The scalable setting to draw.
/// The number of level to draw.
/// The schema to use when drawing the levels.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static void LevelValuesFieldGUI(
Rect rect,
SerializedScalableSetting scalableSetting,
int count,
ScalableSettingSchema schema
)
where T : struct
{
var labels = new GUIContent[count];
Array.Copy(schema.levelNames, labels, count);
var values = new T[count];
for (var i = 0; i < count; ++i)
values[i] = scalableSetting.values.GetArrayElementAtIndex(i).GetInline();
EditorGUI.BeginChangeCheck();
MultiField(rect, labels, values);
if (EditorGUI.EndChangeCheck())
{
for (var i = 0; i < count; ++i)
scalableSetting.values.GetArrayElementAtIndex(i).SetInline(values[i]);
}
}
/// Draw multiple fields in a single line.
/// The type to render.
/// The rect to use to draw the GUI.
/// The labels for each sub value field.
/// The current values of the fields.
static void MultiField(Rect position, GUIContent[] subLabels, T[] values)
where T : struct
{
// The number of slots we need to fit into this rectangle
var length = values.Length;
// Let's compute the space allocated for every field including the label
var num = position.width / (float)length;
// Reset the indentation
var indentLevel = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
// Save labelWidth
float labelWidth = EditorGUIUtility.labelWidth;
// Variable to keep track of the current pixel shift in the rectangle we were assigned for this whole section.
float pixelShift = 0;
// Loop through the levels
for (var index = 0; index < values.Length; ++index)
{
// Let's first compute what is the width of the label of this scalable setting level
// We make sure that the label doesn't go beyond the space available for this scalable setting level
EditorGUIUtility.labelWidth = Mathf.Clamp(CalcPrefixLabelWidth(subLabels[index], (GUIStyle)null), 0, num);
// Define the rectangle for the field
var fieldSlot = new Rect(position.x + pixelShift, position.y, num, position.height);
// Draw the right field depending on its type.
if (typeof(T) == typeof(int))
values[index] = (T)(object)EditorGUI.DelayedIntField(fieldSlot, subLabels[index], (int)(object)values[index]);
else if (typeof(T) == typeof(bool))
values[index] = (T)(object)EditorGUI.Toggle(fieldSlot, subLabels[index], (bool)(object)values[index]);
else if (typeof(T) == typeof(float))
values[index] = (T)(object)EditorGUI.FloatField(fieldSlot, subLabels[index], (float)(object)values[index]);
else if (typeof(T).IsEnum)
values[index] = (T)(object)EditorGUI.EnumPopup(fieldSlot, subLabels[index], (Enum)(object)values[index]);
else
throw new ArgumentOutOfRangeException($"<{typeof(T)}> is not a supported type for multi field");
// Shift by the slot that was used for the field
pixelShift += num;
}
EditorGUIUtility.labelWidth = labelWidth;
EditorGUI.indentLevel = indentLevel;
}
static float CalcPrefixLabelWidth(GUIContent label, GUIStyle style = null)
{
if (style == null)
style = EditorStyles.label;
return style.CalcSize(label).x;
}
}
}