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; } } }