using System;
using System.Collections.Generic;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.UIElements;
namespace UnityEditor.Rendering
{
///
/// Editor for the Default Volume Profile
///
public sealed partial class DefaultVolumeProfileEditor
{
const string k_TemplatePath = "Packages/com.unity.render-pipelines.core/Editor/UXML/DefaultVolumeProfileEditor.uxml";
const int k_ContainerMarginLeft = 27;
const int k_ImguiContainerPaddingLeft = 18;
static Lazy s_ImguiContainerScopeStyle = new(() => new GUIStyle
{
padding = new RectOffset(k_ImguiContainerPaddingLeft, 0, 0, 0)
});
static Lazy s_Template = new(() => AssetDatabase.LoadAssetAtPath(k_TemplatePath));
readonly Dictionary m_VolumeComponentHelpUrls = new();
readonly VolumeProfile m_Profile;
readonly SerializedObject m_TargetSerializedObject;
readonly Editor m_BaseEditor;
DefaultVolumeProfileCategories m_Categories;
VisualElement m_Root;
ToolbarSearchField m_SearchField;
///
/// Constructor
///
/// VolumeProfile to display
/// Target serialized object to update when the default volume profile changes
public DefaultVolumeProfileEditor(VolumeProfile profile, SerializedObject targetSerializedObject)
{
m_Profile = profile;
m_TargetSerializedObject = targetSerializedObject;
}
///
/// List of all VolumeComponentEditors
///
public List allEditors
{
get
{
var editors = new List();
foreach (var (_, categoryEditors) in m_Categories.categories)
{
editors.AddRange(categoryEditors);
}
return editors;
}
}
///
/// Create the visual hierarchy
///
/// Root element of the visual hierarchy
public VisualElement Create()
{
m_Root = s_Template.Value.Instantiate();
m_SearchField = m_Root.Q();
m_SearchField.RegisterValueChangedCallback(_ => CreateComponentLists());
m_Categories = new DefaultVolumeProfileCategories(m_Profile);
CreateComponentLists();
Undo.undoRedoPerformed += OnUndoRedoPerformed;
return m_Root;
}
void CreateComponentLists()
{
var componentListElement = m_Root.Q("component-list");
componentListElement.Clear();
var searchString = m_SearchField.value;
bool MatchesSearchString(string title)
{
return searchString.Length == 0 || title.Contains(searchString, StringComparison.OrdinalIgnoreCase);
}
foreach (var (categoryName, categoryEditors) in m_Categories.categories)
{
List filteredCategoryEditors = new();
foreach (var category in categoryEditors)
{
if (MatchesSearchString(category.GetDisplayTitle().text))
filteredCategoryEditors.Add(category);
}
if (filteredCategoryEditors.Count == 0)
continue;
VisualElement categoryTitleLabel = new Label(categoryName);
categoryTitleLabel.AddToClassList("category-header");
componentListElement.Add(categoryTitleLabel);
Func makeItem = () =>
{
var container = new IMGUIContainer();
container.cullingEnabled = true;
return container;
};
Action bindItem = (e, i) =>
{
(e as IMGUIContainer).onGUIHandler = () =>
{
using var indentScope = new SettingsProviderGUIScope();
/* values adapted to the ProjectSettings > Graphics */
/* they also works in the DefaultVolume inspector */
var minWidth = 120;
var indent = 66;
var ratio = 0.45f;
EditorGUIUtility.labelWidth = Mathf.Max(minWidth, (int)((e.worldBound.width - indent) * ratio));
VolumeComponentEditorOnGUI(filteredCategoryEditors[i]);
};
};
ListView listView = new ListView(filteredCategoryEditors, -1, makeItem, bindItem);
listView.virtualizationMethod = CollectionVirtualizationMethod.DynamicHeight;
componentListElement.Add(listView);
foreach (var editor in filteredCategoryEditors)
{
DocumentationUtils.TryGetHelpURL(editor.volumeComponent.GetType(), out string helpUrl);
helpUrl ??= string.Empty;
m_VolumeComponentHelpUrls[editor] = helpUrl;
}
}
}
void OnUndoRedoPerformed()
{
VolumeManager.instance.OnVolumeProfileChanged(m_Profile);
}
///
/// Destroy all Editors owned by this class
///
public void Destroy()
{
m_Categories.Destroy();
Undo.undoRedoPerformed -= OnUndoRedoPerformed;
}
///
/// Rebuild all ListViews to force them to update their expanded state.
///
public void RebuildListViews()
{
// Rebuild is necessary to avoid gradual "animation" effect when collapsing/expanding many foldouts at once.
// This happens because the items have cullingEnabled=true and are not updated until they come to view.
m_Root.Query().ForEach(l => l.Rebuild());
}
void VolumeComponentEditorOnGUI(VolumeComponentEditor editor)
{
using (new EditorGUILayout.VerticalScope(s_ImguiContainerScopeStyle.Value))
{
using var changedScope = new EditorGUI.ChangeCheckScope();
CoreEditorUtils.DrawSplitter();
bool displayContent = CoreEditorUtils.DrawHeaderToggleFoldout(
new GUIContent(editor.GetDisplayTitle()),
editor.expanded,
null,
pos => OnVolumeComponentContextClick(pos, editor),
editor.hasAdditionalProperties ? () => editor.showAdditionalProperties : null,
() => editor.showAdditionalProperties ^= true,
m_VolumeComponentHelpUrls[editor]
);
if (displayContent ^ editor.expanded)
editor.expanded = displayContent;
if (editor.expanded)
{
using var scope = new EditorGUILayout.VerticalScope();
// This rect is drawn to suppress mouse hover highlight in order to match the old imgui
// implementation. Not doing this causes visual bugs with AdditionalProperties animations.
var highlightSuppressRect = scope.rect;
highlightSuppressRect.xMin -= k_ImguiContainerPaddingLeft+k_ContainerMarginLeft;
EditorGUI.DrawRect(highlightSuppressRect, CoreEditorStyles.backgroundColor);
editor.OnInternalInspectorGUI();
}
if (changedScope.changed)
{
m_TargetSerializedObject.ApplyModifiedProperties();
VolumeManager.instance.OnVolumeProfileChanged(m_Profile);
}
}
}
void OnVolumeComponentContextClick(Vector2 position, VolumeComponentEditor targetEditor)
{
var targetComponent = targetEditor.volumeComponent;
var menu = new GenericMenu();
menu.AddItem(VolumeProfileUtils.Styles.collapseAll, false, () =>
{
VolumeProfileUtils.SetComponentEditorsExpanded(allEditors, false);
RebuildListViews();
});
menu.AddItem(VolumeProfileUtils.Styles.expandAll, false, () =>
{
VolumeProfileUtils.SetComponentEditorsExpanded(allEditors, true);
RebuildListViews();
});
menu.AddSeparator(string.Empty);
menu.AddItem(VolumeProfileUtils.Styles.reset, false, () =>
{
VolumeProfileUtils.ResetComponentsInternal(targetEditor.serializedObject, m_Profile, new[] { targetComponent }, true);
});
menu.AddSeparator(string.Empty);
if (targetEditor.hasAdditionalProperties)
menu.AddAdvancedPropertiesBoolMenuItem(() => targetEditor.showAdditionalProperties, () => targetEditor.showAdditionalProperties ^= true);
menu.AddSeparator(string.Empty);
menu.AddItem(VolumeProfileUtils.Styles.openInRenderingDebugger, false, DebugDisplaySettingsVolume.OpenInRenderingDebugger);
menu.AddSeparator(string.Empty);
menu.AddItem(VolumeProfileUtils.Styles.copySettings, false, () => VolumeComponentCopyPaste.CopySettings(targetComponent));
if (VolumeComponentCopyPaste.CanPaste(targetComponent))
menu.AddItem(VolumeProfileUtils.Styles.pasteSettings, false, () => VolumeComponentCopyPaste.PasteSettings(targetComponent));
else
menu.AddDisabledItem(VolumeProfileUtils.Styles.pasteSettings);
menu.DropDown(new Rect(position, Vector2.zero));
}
}
}