using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor.AnimatedValues;
using UnityEditor.Callbacks;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.Rendering;
namespace UnityEditor.Rendering
{
///
/// A custom editor class that draws a in the Inspector. If you do not
/// provide a custom editor for a , Unity uses the default one.
/// You must use a to let the editor know which
/// component this drawer is for.
///
///
/// Below is an example of a custom :
///
/// using UnityEngine.Rendering;
///
/// [Serializable, VolumeComponentMenu("Custom/Example Component")]
/// public class ExampleComponent : VolumeComponent
/// {
/// public ClampedFloatParameter intensity = new ClampedFloatParameter(0f, 0f, 1f);
/// }
///
/// And its associated editor:
///
/// using UnityEditor.Rendering;
///
/// [CustomEditor(typeof(ExampleComponent))]
/// class ExampleComponentEditor : VolumeComponentEditor
/// {
/// SerializedDataParameter m_Intensity;
///
/// public override void OnEnable()
/// {
/// var o = new PropertyFetcher<ExampleComponent>(serializedObject);
/// m_Intensity = Unpack(o.Find(x => x.intensity));
/// }
///
/// public override void OnInspectorGUI()
/// {
/// PropertyField(m_Intensity);
/// }
/// }
///
///
///
[CustomEditor(typeof(VolumeComponent), true)]
public class VolumeComponentEditor : Editor
{
const string k_KeyPrefix = "CoreRP:VolumeComponent:UI_State:";
EditorPrefBool m_EditorPrefBool;
internal string categoryTitle { get; set; }
///
/// If the editor for this is expanded or not in the inspector
///
public bool expanded
{
get => m_EditorPrefBool.value;
set => m_EditorPrefBool.value = value;
}
internal bool visible { get; private set; }
static class Styles
{
public static readonly GUIContent k_OverrideSettingText = EditorGUIUtility.TrTextContent("", "Override this setting for this volume.");
public static readonly GUIContent k_AllText =
EditorGUIUtility.TrTextContent("ALL", "Toggle all overrides on. To maximize performances you should only toggle overrides that you actually need.");
public static readonly GUIContent k_NoneText = EditorGUIUtility.TrTextContent("NONE", "Toggle all overrides off.");
public static string toggleAllText { get; } = L10n.Tr("Toggle All");
public const int overrideCheckboxWidth = 14;
public const int overrideCheckboxOffset = 9;
}
Vector2? m_OverrideToggleSize;
internal Vector2 overrideToggleSize
{
get
{
if (!m_OverrideToggleSize.HasValue)
m_OverrideToggleSize = CoreEditorStyles.smallTickbox.CalcSize(Styles.k_OverrideSettingText);
return m_OverrideToggleSize.Value;
}
}
///
/// Specifies the this editor is drawing.
///
public VolumeComponent volumeComponent => target as VolumeComponent;
///
/// The copy of the serialized property of the being
/// inspected. Unity uses this to track whether the editor is collapsed in the Inspector or not.
///
[Obsolete("Please use expanded property instead. #from(2022.2)", false)]
public SerializedProperty baseProperty { get; internal set; }
///
/// The serialized property of for the component being
/// inspected.
///
public SerializedProperty activeProperty { get; internal set; }
#region Additional Properties
List m_VolumeNotAdditionalParameters = new List();
///
/// Override this property if your editor makes use of the "Additional Properties" feature.
///
public virtual bool hasAdditionalProperties => volumeComponent.parameterList.Count != m_VolumeNotAdditionalParameters.Count;
///
/// Set to true to show additional properties.
///
public bool showAdditionalProperties
{
get => AdvancedProperties.enabled;
set => AdvancedProperties.enabled = value;
}
///
/// Start a scope for additional properties.
/// This will handle the highlight of the background when toggled on and off.
///
/// True if the additional content should be drawn.
protected bool BeginAdditionalPropertiesScope()
{
if (!showAdditionalProperties || !hasAdditionalProperties)
return false;
AdvancedProperties.BeginGroup();
return true;
}
///
/// End a scope for additional properties.
///
protected void EndAdditionalPropertiesScope()
{
if (hasAdditionalProperties && showAdditionalProperties)
AdvancedProperties.EndGroup();
}
#endregion
///
/// A reference to the parent editor in the Inspector.
///
protected Editor m_Inspector;
///
/// A reference to the parent editor in the Inspector.
///
internal Editor inspector
{
get => m_Inspector;
set => m_Inspector = value;
}
internal void SetVolume(Volume v)
{
volume = v;
}
///
/// Obtains the that is being edited if editing a scene volume, otherwise null.
///
protected Volume volume { get; private set; }
internal void SetVolumeProfile(VolumeProfile p)
{
volumeProfile = p;
}
///
/// Obtains the that is being edited.
///
VolumeProfile volumeProfile { get; set; }
List<(GUIContent displayName, int displayOrder, SerializedDataParameter param)> m_Parameters;
static Dictionary s_ParameterDrawers;
SupportedOnRenderPipelineAttribute m_SupportedOnRenderPipelineAttribute;
Type[] m_LegacyPipelineTypes;
static VolumeComponentEditor()
{
s_ParameterDrawers = new Dictionary();
ReloadDecoratorTypes();
}
[DidReloadScripts]
static void OnEditorReload()
{
ReloadDecoratorTypes();
}
static void ReloadDecoratorTypes()
{
s_ParameterDrawers.Clear();
foreach (var type in TypeCache.GetTypesDerivedFrom())
{
if (type.IsAbstract)
continue;
var attr = type.GetCustomAttribute(false);
if (attr == null)
{
Debug.LogWarning($"{type} is missing the attribute {nameof(VolumeParameterDrawerAttribute)}");
continue;
}
s_ParameterDrawers.Add(attr.parameterType, Activator.CreateInstance(type) as VolumeParameterDrawer);
}
}
///
/// Triggers an Inspector repaint event.
///
public new void Repaint()
{
// Volume Component Editors can be shown in the Graphics Settings window (default volume profile)
// This will force a repaint of the whole window, otherwise, additional properties highlight animation does not work properly.
SettingsService.RepaintAllSettingsWindow();
base.Repaint();
}
internal static string GetAdditionalPropertiesPreferenceKey(Type type)
{
return $"UI_Show_Additional_Properties_{type}";
}
internal void InitAdditionalPropertiesPreference()
{
string key = GetAdditionalPropertiesPreferenceKey(GetType());
AdvancedProperties.UpdateShowAdvancedProperties(key, EditorPrefs.HasKey(key) && EditorPrefs.GetBool(key));
}
internal void Init()
{
activeProperty = serializedObject.FindProperty("active");
string inspectorKey = string.Empty;
bool expandedByDefault = true;
if (!enableOverrides)
{
inspectorKey += "default"; // Ensures the default VolumeProfile editor doesn't share expander state with other editors
expandedByDefault = false;
}
m_EditorPrefBool = new EditorPrefBool(k_KeyPrefix + inspectorKey + volumeComponent.GetType().Name, expandedByDefault);
InitAdditionalPropertiesPreference();
InitParameters();
OnEnable();
var volumeComponentType = volumeComponent.GetType();
m_SupportedOnRenderPipelineAttribute = volumeComponentType.GetCustomAttribute();
#pragma warning disable CS0618
var supportedOn = volumeComponentType.GetCustomAttribute();
m_LegacyPipelineTypes = supportedOn != null ? supportedOn.pipelineTypes : Array.Empty();
#pragma warning restore CS0618
EditorApplication.contextualPropertyMenu += OnPropertyContextMenu;
}
void OnDestroy()
{
EditorApplication.contextualPropertyMenu -= OnPropertyContextMenu;
}
internal void DetermineVisibility(Type renderPipelineAssetType, Type renderPipelineType)
{
if (renderPipelineAssetType == null)
{
visible = false;
return;
}
if (m_SupportedOnRenderPipelineAttribute != null)
{
visible = m_SupportedOnRenderPipelineAttribute.GetSupportedMode(renderPipelineAssetType) != SupportedOnRenderPipelineAttribute.SupportedMode.Unsupported;
return;
}
if (renderPipelineType != null && m_LegacyPipelineTypes.Length > 0)
{
visible = m_LegacyPipelineTypes.Contains(renderPipelineType);
return;
}
visible = true;
}
void InitParameters()
{
VolumeComponent.FindParameters(target, m_VolumeNotAdditionalParameters, field => field.GetCustomAttribute() == null);
}
void GetFields(object o, List<(FieldInfo, SerializedProperty)> infos, SerializedProperty prop = null)
{
if (o == null)
return;
var fields = o.GetType()
.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var field in fields)
{
if (field.FieldType.IsSubclassOf(typeof(VolumeParameter)))
{
if ((field.GetCustomAttributes(typeof(HideInInspector), false).Length == 0) &&
((field.GetCustomAttributes(typeof(SerializeField), false).Length > 0) ||
(field.IsPublic && field.GetCustomAttributes(typeof(NonSerializedAttribute), false).Length == 0)))
infos.Add((field, prop == null ? serializedObject.FindProperty(field.Name) : prop.FindPropertyRelative(field.Name)));
}
else if (!field.FieldType.IsArray && field.FieldType.IsClass)
GetFields(field.GetValue(o), infos, prop == null ? serializedObject.FindProperty(field.Name) : prop.FindPropertyRelative(field.Name));
}
}
///
/// Unity calls this method when the object loads.
///
///
/// You can safely override this method and not call base.OnEnable() unless you want
/// Unity to display all the properties from the automatically.
///
public virtual void OnEnable()
{
// Grab all valid serializable field on the VolumeComponent
// TODO: Should only be done when needed / on demand as this can potentially be wasted CPU when a custom editor is in use
var fields = new List<(FieldInfo, SerializedProperty)>();
GetFields(target, fields);
m_Parameters = fields
.Select(t =>
{
var name = "";
var order = 0;
var (fieldInfo, serializedProperty) = t;
var attr = (DisplayInfoAttribute[])fieldInfo.GetCustomAttributes(typeof(DisplayInfoAttribute), true);
if (attr.Length != 0)
{
name = attr[0].name;
order = attr[0].order;
}
var parameter = new SerializedDataParameter(t.Item2);
return (EditorGUIUtility.TrTextContent(name), order, parameter);
})
.OrderBy(t => t.order)
.ToList();
}
///
/// Unity calls this method when the object goes out of scope.
///
public virtual void OnDisable()
{
}
internal void AddDefaultProfileContextMenuEntries(
GenericMenu menu,
VolumeProfile defaultProfile,
GenericMenu.MenuFunction copyAction)
{
// Host can be either VolumeProfileEditor or VolumeEditor
var profile = volume
? volume.HasInstantiatedProfile() ? volume.profile : volume.sharedProfile
: volumeProfile;
if (defaultProfile != null &&
profile != null &&
defaultProfile != profile)
{
menu.AddItem(EditorGUIUtility.TrTextContent($"Show Default Volume Profile"), false,
() => Selection.activeObject = defaultProfile);
menu.AddItem(EditorGUIUtility.TrTextContent($"Apply Values to Default Volume Profile"), false, copyAction);
}
}
void OnPropertyContextMenu(GenericMenu menu, SerializedProperty property)
{
if (property.serializedObject.targetObject != target)
return;
var targetComponent = property.serializedObject.targetObject as VolumeComponent;
AddDefaultProfileContextMenuEntries(menu, VolumeManager.instance.globalDefaultProfile,
() => VolumeProfileUtils.AssignValuesToProfile(VolumeManager.instance.globalDefaultProfile, targetComponent, property));
}
///
/// Unity calls this method after drawing the header for each VolumeComponentEditor
///
protected virtual void OnBeforeInspectorGUI()
{
}
internal bool OnInternalInspectorGUI()
{
if (serializedObject == null || serializedObject.targetObject == null)
return false;
serializedObject.Update();
using (new EditorGUILayout.VerticalScope())
{
OnBeforeInspectorGUI();
if (enableOverrides)
TopRowFields();
else
GUILayout.Space(4);
OnInspectorGUI();
EditorGUILayout.Space();
}
return serializedObject.ApplyModifiedProperties();
}
///
/// Unity calls this method each time it re-draws the Inspector.
///
///
/// You can safely override this method and not call base.OnInspectorGUI() unless you
/// want Unity to display all the properties from the
/// automatically.
///
public override void OnInspectorGUI()
{
// Display every field as-is
foreach (var parameter in m_Parameters)
{
if (!string.IsNullOrEmpty(parameter.displayName.text))
PropertyField(parameter.param, parameter.displayName);
else
PropertyField(parameter.param);
}
}
///
/// Sets the label for the component header. Override this method to provide
/// a custom label. If you don't, Unity automatically obtains one from the class name.
///
/// A label to display in the component header.
public virtual GUIContent GetDisplayTitle()
{
var title = string.IsNullOrEmpty(volumeComponent.displayName) ? ObjectNames.NicifyVariableName(volumeComponent.GetType().Name) : volumeComponent.displayName;
return EditorGUIUtility.TrTextContent(title, string.Empty);
}
void AddToggleState(GUIContent content, bool state)
{
bool allOverridesSameState = AreOverridesTo(state);
if (GUILayout.Toggle(allOverridesSameState, content, CoreEditorStyles.miniLabelButton, GUILayout.ExpandWidth(false)) && !allOverridesSameState)
SetOverridesTo(state);
}
void TopRowFields()
{
using (new EditorGUILayout.HorizontalScope())
{
AddToggleState(Styles.k_AllText, true);
AddToggleState(Styles.k_NoneText, false);
}
}
///
/// Checks if all the visible parameters have the given state
///
/// The state to check
internal bool AreOverridesTo(bool state)
{
if (hasAdditionalProperties && showAdditionalProperties)
return AreAllOverridesTo(state);
for (int i = 0; i < m_VolumeNotAdditionalParameters.Count; ++i)
{
if (m_VolumeNotAdditionalParameters[i].overrideState != state)
return false;
}
return true;
}
///
/// Sets the given state to all the visible parameters
///
/// The state to check
internal void SetOverridesTo(bool state)
{
if (hasAdditionalProperties && showAdditionalProperties)
SetAllOverridesTo(state);
else
{
Undo.RecordObject(target, Styles.toggleAllText);
volumeComponent.SetOverridesTo(m_VolumeNotAdditionalParameters, state);
serializedObject.Update();
}
}
internal bool AreAllOverridesTo(bool state)
{
for (int i = 0; i < volumeComponent.parameterList.Count; ++i)
{
if (volumeComponent.parameterList[i].overrideState != state)
return false;
}
return true;
}
internal void SetAllOverridesTo(bool state)
{
Undo.RecordObject(target, Styles.toggleAllText);
volumeComponent.SetAllOverridesTo(state);
serializedObject.Update();
}
///
/// Generates and auto-populates a from a serialized
/// .
///
/// A serialized property holding a
///
/// A that encapsulates the provided serialized property.
protected SerializedDataParameter Unpack(SerializedProperty property)
{
Assert.IsNotNull(property);
return new SerializedDataParameter(property);
}
///
/// Draws a given in the editor.
///
/// The property to draw in the editor
/// true if the property field has been rendered
protected bool PropertyField(SerializedDataParameter property)
{
var title = EditorGUIUtility.TrTextContent(property.displayName,
property.GetAttribute()?.tooltip); // avoid property from getting the tooltip of another one with the same name
return PropertyField(property, title);
}
static readonly Dictionary s_HeadersGuiContents = new Dictionary();
///
/// Draws a header into the inspector with the given title
///
/// The title for the header
protected void DrawHeader(string header)
{
if (!s_HeadersGuiContents.TryGetValue(header, out GUIContent content))
{
content = EditorGUIUtility.TrTextContent(header);
s_HeadersGuiContents.Add(header, content);
}
EditorGUILayout.Space(4);
var rect = EditorGUILayout.GetControlRect(false, EditorGUIUtility.singleLineHeight);
EditorGUI.LabelField(rect, content, EditorStyles.boldLabel);
}
///
/// Handles unity built-in decorators (Space, Header, Tooltips, ...) from attributes
///
/// The property to obtain the attributes and handle the decorators
/// A custom label and/or tooltip that might be updated by and/or by
internal void HandleDecorators(SerializedDataParameter property, GUIContent title)
{
foreach (var attr in property.attributes)
{
if (!(attr is PropertyAttribute))
continue;
switch (attr)
{
case SpaceAttribute spaceAttribute:
EditorGUILayout.GetControlRect(false, spaceAttribute.height);
break;
case HeaderAttribute headerAttribute:
DrawHeader(headerAttribute.header);
break;
case TooltipAttribute tooltipAttribute:
if (string.IsNullOrEmpty(title.tooltip))
title.tooltip = tooltipAttribute.tooltip;
break;
case InspectorNameAttribute inspectorNameAttribute:
title.text = inspectorNameAttribute.displayName;
break;
}
}
}
///
/// Get indentation from Indent attribute
///
/// The property to obtain the attributes
/// The relative indent level change
int HandleRelativeIndentation(SerializedDataParameter property)
{
foreach (var attr in property.attributes)
{
if (attr is VolumeComponent.Indent indent)
return indent.relativeAmount;
}
return 0;
}
///
/// Draws a given in the editor using a custom label
/// and tooltip.
///
/// The property to draw in the editor.
/// A custom label and/or tooltip.
/// true if the property field has been rendered
protected bool PropertyField(SerializedDataParameter property, GUIContent title)
{
if (VolumeParameter.IsObjectParameter(property.referenceType))
return DrawEmbeddedField(property, title);
else
return DrawPropertyField(property, title);
}
///
/// Draws a given in the editor using a custom label
/// and tooltip.
///
/// The property to draw in the editor.
/// A custom label and/or tooltip.
private bool DrawPropertyField(SerializedDataParameter property, GUIContent title)
{
using (var scope = new OverridablePropertyScope(property, title, this))
{
if (!scope.displayed)
return false;
// Custom drawer
if (scope.drawer?.OnGUI(property, title) ?? false)
return true;
// Standard Unity drawer
EditorGUILayout.PropertyField(property.value, title);
}
return true;
}
///
/// Draws a given in the editor using a custom label
/// and tooltip. This variant is only for embedded class / struct
///
/// The property to draw in the editor.
/// A custom label and/or tooltip.
private bool DrawEmbeddedField(SerializedDataParameter property, GUIContent title)
{
bool isAdditionalProperty = property.GetAttribute() != null;
bool displayed = !isAdditionalProperty || BeginAdditionalPropertiesScope();
if (!displayed)
return false;
// Custom parameter drawer
s_ParameterDrawers.TryGetValue(property.referenceType, out VolumeParameterDrawer drawer);
if (drawer != null && !drawer.IsAutoProperty())
if (drawer.OnGUI(property, title))
{
if (isAdditionalProperty)
EndAdditionalPropertiesScope();
return true;
}
// Standard Unity drawer
using (new IndentLevelScope())
{
bool expanded = property?.value?.isExpanded ?? true;
expanded = EditorGUILayout.Foldout(expanded, title, true);
if (expanded)
{
// Not the fastest way to do it but that'll do just fine for now
var it = property.value.Copy();
var end = it.GetEndProperty();
bool first = true;
while (it.Next(first) && !SerializedProperty.EqualContents(it, end))
{
PropertyField(Unpack(it));
first = false;
}
}
property.value.isExpanded = expanded;
}
if (isAdditionalProperty)
EndAdditionalPropertiesScope();
return true;
}
///
/// Draw a Color Field but convert the color to gamma space before displaying it in the shader.
/// Using SetColor on a material does the conversion, but setting the color as vector3 in a constant buffer doesn't
/// So we have to do it manually, doing it in the UI avoids having to do a migration step for existing fields
///
/// The color property
protected void ColorFieldLinear(SerializedDataParameter property)
{
var title = EditorGUIUtility.TrTextContent(property.displayName,
property.GetAttribute()?.tooltip);
using (var scope = new OverridablePropertyScope(property, title, this))
{
if (!scope.displayed)
return;
// Standard Unity drawer
CoreEditorUtils.ColorFieldLinear(property.value, title);
}
}
///
/// Draws the override checkbox used by a property in the editor.
///
/// The property to draw the override checkbox for
protected void DrawOverrideCheckbox(SerializedDataParameter property)
{
// Create a rect the height + vspacing of the property that is being overriden
float height = EditorGUI.GetPropertyHeight(property.value) + EditorGUIUtility.standardVerticalSpacing;
var overrideRect = GUILayoutUtility.GetRect(Styles.k_AllText, CoreEditorStyles.miniLabelButton, GUILayout.Height(height),
GUILayout.Width(Styles.overrideCheckboxWidth + Styles.overrideCheckboxOffset), GUILayout.ExpandWidth(false));
// also center vertically the checkbox
overrideRect.yMin += height * 0.5f - overrideToggleSize.y * 0.5f;
overrideRect.xMin += Styles.overrideCheckboxOffset;
property.overrideState.boolValue = GUI.Toggle(overrideRect, property.overrideState.boolValue, Styles.k_OverrideSettingText, CoreEditorStyles.smallTickbox);
}
///
/// Scope for property that handle:
/// - Layout decorator (Space, Header)
/// - Naming decorator (Tooltips, InspectorName)
/// - Overridable checkbox if parameter IsAutoProperty
/// - disabled GUI if Overridable checkbox (case above) is unchecked
/// - additional property scope
/// This is automatically used inside PropertyField method
///
protected struct OverridablePropertyScope : IDisposable
{
bool isAdditionalProperty;
VolumeComponentEditor editor;
IDisposable disabledScope;
IDisposable indentScope;
internal bool haveCustomOverrideCheckbox { get; private set; }
internal VolumeParameterDrawer drawer { get; private set; }
///
/// Either the content property will be displayed or not (can varry with additional property settings)
///
public bool displayed { get; private set; }
///
/// The title modified regarding attribute used on the field
///
public GUIContent label { get; private set; }
///
/// Constructor
///
/// The property that will be drawn
/// The label of this property
/// The editor that will draw it
public OverridablePropertyScope(SerializedDataParameter property, GUIContent label, VolumeComponentEditor editor)
{
disabledScope = null;
indentScope = null;
haveCustomOverrideCheckbox = false;
drawer = null;
displayed = false;
isAdditionalProperty = false;
this.label = label;
this.editor = editor;
Init(property, label, editor);
}
///
/// Constructor
///
/// The property that will be drawn
/// The label of this property
/// The editor that will draw it
public OverridablePropertyScope(SerializedDataParameter property, string label, VolumeComponentEditor editor)
{
disabledScope = null;
indentScope = null;
haveCustomOverrideCheckbox = false;
drawer = null;
displayed = false;
isAdditionalProperty = false;
this.label = EditorGUIUtility.TrTextContent(label);
this.editor = editor;
Init(property, this.label, editor);
}
void Init(SerializedDataParameter property, GUIContent label, VolumeComponentEditor editor)
{
// Below, 3 is horizontal spacing and there is one between label and field and another between override checkbox and label
EditorGUIUtility.labelWidth -= Styles.overrideCheckboxWidth + Styles.overrideCheckboxOffset + 3 + 3;
isAdditionalProperty = property.GetAttribute() != null;
displayed = !isAdditionalProperty || editor.BeginAdditionalPropertiesScope();
s_ParameterDrawers.TryGetValue(property.referenceType, out VolumeParameterDrawer vpd);
drawer = vpd;
//never draw override for embedded class/struct
haveCustomOverrideCheckbox = (displayed && !(drawer?.IsAutoProperty() ?? true))
|| VolumeParameter.IsObjectParameter(property.referenceType);
if (displayed)
{
editor.HandleDecorators(property, label);
int relativeIndentation = editor.HandleRelativeIndentation(property);
int indent = relativeIndentation * 15;
if (haveCustomOverrideCheckbox)
indent += 15;
if (indent != 0)
indentScope = new IndentLevelScope(indent);
if (!haveCustomOverrideCheckbox)
{
EditorGUILayout.BeginHorizontal();
if (editor.enableOverrides)
editor.DrawOverrideCheckbox(property);
disabledScope = new EditorGUI.DisabledScope(!property.overrideState.boolValue);
}
}
}
///
/// Dispose of the class
///
void IDisposable.Dispose()
{
disabledScope?.Dispose();
indentScope?.Dispose();
if (!haveCustomOverrideCheckbox && displayed)
EditorGUILayout.EndHorizontal();
if (isAdditionalProperty)
editor.EndAdditionalPropertiesScope();
EditorGUIUtility.labelWidth += Styles.overrideCheckboxWidth + Styles.overrideCheckboxOffset + 3 + 3;
}
}
///
/// Like EditorGUI.IndentLevelScope but this one will also indent the override checkboxes.
///
protected class IndentLevelScope : GUI.Scope
{
int m_Offset;
///
/// Constructor
///
/// [optional] Change the indentation offset
public IndentLevelScope(int offset = 15)
{
m_Offset = offset;
// When using EditorGUI.indentLevel++, the clicking on the checkboxes does not work properly due to some issues on the C++ side.
// This scope is a work-around for this issue.
GUILayout.BeginHorizontal();
EditorGUILayout.Space(offset, false);
GUIStyle style = new GUIStyle();
GUILayout.BeginVertical(style);
EditorGUIUtility.labelWidth -= m_Offset;
}
///
/// Closes the scope
///
protected override void CloseScope()
{
EditorGUIUtility.labelWidth += m_Offset;
GUILayout.EndVertical();
GUILayout.EndHorizontal();
}
}
///
/// Whether to draw the UI elements related to overrides.
///
public bool enableOverrides { get; set; } = true;
}
}