using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.Recorder.Encoder;
using UnityEditor.Recorder.Input;
using UnityEngine;
namespace UnityEditor.Recorder
{
///
/// The user interface to set up a Recorder.
///
public abstract class RecorderEditor : Editor
{
SerializedProperty m_CaptureEveryNthFrame;
SerializedProperty m_FileNameGenerator;
SerializedProperty m_Take;
internal event Action OnRecorderValidated;
internal event Action OnRecorderDataHasChaged;
SavedBool showOutputFormat;
SavedBool showOutputFile;
SavedBool showInput;
// Used to differentiate if drawing from timeline or RecorderWindow
internal static bool FromRecorderWindow = true;
static class Styles
{
internal static readonly GUIContent InputLabel = new GUIContent("Input");
internal static readonly GUIContent OutputFormatLabel = new GUIContent("Output Format");
internal static readonly GUIContent OutputFileLabel = new GUIContent("Output File");
internal static readonly GUIContent FileNameLabel = new GUIContent("File Name", "Pattern for the name of the output files. It can include a mix of regular text and dynamic placeholders (use the “+ Wildcards” button).");
internal static readonly GUIContent SourceLabel = new GUIContent("Source", "The input type to use for the recording.");
internal static readonly GUIContent TakeNumberLabel = new GUIContent("Take Number", "Value that the Recorder uses to number the recordings. It increases by one after each recording.");
internal static readonly GUIContent RenderStepFrameLabel = new GUIContent("Render Frame Step", "The interval between every frame to render in Play mode during the recording.");
}
///
/// Initializes the attributes and styling of the editor based on the serialized data, when the object becomes active.
///
protected virtual void OnEnable()
{
if (target != null)
{
var pf = new PropertyFinder(serializedObject);
m_CaptureEveryNthFrame = pf.Find(w => w.captureEveryNthFrame);
m_FileNameGenerator = pf.Find(w => w.fileNameGenerator);
m_Take = pf.Find(w => w.take);
showOutputFormat = new SavedBool($"{target.GetType()}.showOutputFormat", true);
showOutputFile = new SavedBool($"{target.GetType()}.showOutputFile", true);;
showInput = new SavedBool($"{target.GetType()}.showInput", true);;
}
}
///
/// Draws a delimiter between different GUI elements.
///
protected static void DrawSeparator()
{
}
/// Draws a foldout header.
/// The title of the header
/// The state of the header
/// Specifies whether to contain the header in a box style [optional].
/// return the state of the foldout header
static bool DrawHeaderFoldout(GUIContent title, SavedBool state, bool isBoxed = false)
{
const float height = 17f;
var backgroundRect = GUILayoutUtility.GetRect(1f, height);
var labelRect = backgroundRect;
labelRect.xMin += 16f;
labelRect.xMax -= 20f;
var foldoutRect = backgroundRect;
foldoutRect.y += 1f;
foldoutRect.width = 13f;
foldoutRect.height = 13f;
foldoutRect.x = labelRect.xMin + 15 * (EditorGUI.indentLevel - 1); //fix for preset
// Background rect should be full-width
backgroundRect.xMin = 0f;
backgroundRect.width += 4f;
if (isBoxed)
{
labelRect.xMin += 5;
foldoutRect.xMin += 5;
backgroundRect.xMin = EditorGUIUtility.singleLineHeight;
backgroundRect.width -= 1;
}
var lineRect = new Rect(backgroundRect);
lineRect.height = EditorGUIUtility.standardVerticalSpacing;
float lineTint = EditorGUIUtility.isProSkin ? 0.01f : 1f;
// Background
float backgroundTint = EditorGUIUtility.isProSkin ? 0.1f : 1f;
EditorGUI.DrawRect(backgroundRect, new Color(backgroundTint, backgroundTint, backgroundTint, 0.2f));
//line
EditorGUI.DrawRect(lineRect, new Color(lineTint, lineTint, lineTint, 0.2f));
// Title
EditorGUI.LabelField(labelRect, title, EditorStyles.boldLabel);
// Active checkbox
state.value = GUI.Toggle(foldoutRect, state.value, GUIContent.none, EditorStyles.foldout);
var e = Event.current;
if (e.type == EventType.MouseDown && backgroundRect.Contains(e.mousePosition) && e.button == 0)
{
state.value = !state;
e.Use();
}
return state;
}
///
/// This is the main entry point for drawing the Recorder editor in the inspector.
///
public override void OnInspectorGUI()
{
if (target == null)
return;
EditorGUI.BeginChangeCheck();
serializedObject.Update();
if (DrawCaptureSection())
{
showInput.value = DrawHeaderFoldout(Styles.InputLabel, showInput, false);
if (showInput)
{
EditorGUILayout.Separator();
CustomGUI();
ImageRenderOptionsGUI();
ExtraOptionsGUI();
if (UnityHelpers.UsingHDRP())
{
AccumulationGUI();
}
EditorGUILayout.Separator();
}
}
showOutputFormat.value = DrawHeaderFoldout(Styles.OutputFormatLabel, showOutputFormat, false);
if (showOutputFormat)
{
EditorGUILayout.Separator();
FileTypeAndFormatGUI();
OnEncodingGui();
EditorGUILayout.Separator();
}
showOutputFile.value = DrawHeaderFoldout(Styles.OutputFileLabel, showOutputFile, false);
if (showOutputFile)
{
EditorGUILayout.Separator();
NameAndPathGUI();
EditorGUILayout.Separator();
}
EditorGUILayout.Separator();
serializedObject.ApplyModifiedProperties();
EditorGUI.EndChangeCheck();
OnValidateSettingsGUI();
}
///
/// Validates the settings of the Recorder.
///
protected virtual void OnValidateSettingsGUI()
{
var oldWarnings = new List();
var targetSettings = target as RecorderSettings;
#pragma warning disable 618
if (!targetSettings.ValidityCheck(oldWarnings))
#pragma warning restore 618
{
foreach (var e in oldWarnings)
EditorGUILayout.HelpBox(e, MessageType.Warning);
}
var warnings = new List();
var errors = new List();
targetSettings.GetWarnings(warnings);
foreach (var w in warnings)
EditorGUILayout.HelpBox(w, MessageType.Warning);
targetSettings.GetErrors(errors);
foreach (var e in errors)
EditorGUILayout.HelpBox(e, MessageType.Error);
if (oldWarnings.Count > 0 || warnings.Count > 0 || errors.Count > 0)
InvokeRecorderValidated();
}
internal void InvokeRecorderValidated()
{
OnRecorderValidated?.Invoke();
}
internal void InvokeRecorderDataHasChanged()
{
OnRecorderDataHasChaged?.Invoke();
}
internal virtual bool DrawCaptureSection()
{
return true;
}
///
/// Displays the information related to the output file(s).
///
protected virtual void NameAndPathGUI()
{
EditorGUILayout.PropertyField(m_FileNameGenerator, Styles.FileNameLabel);
EditorGUILayout.Space();
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(m_Take, Styles.TakeNumberLabel);
}
///
/// Displays the information related to the source of the capture and its rendering options.
///
protected virtual void ImageRenderOptionsGUI()
{
var recorder = (RecorderSettings)target;
foreach (var inputsSetting in recorder.InputsSettings)
{
var p = GetInputSerializedProperty(serializedObject, inputsSetting);
EditorGUILayout.PropertyField(p, Styles.SourceLabel);
}
}
internal static SerializedProperty GetInputSerializedProperty(SerializedObject owner, object fieldValue)
{
var targetObject = (object)owner.targetObject;
var type = targetObject.GetType();
foreach (var info in InputSettingsSelector.GetInputFields(type))
{
if (info.GetValue(targetObject) == fieldValue)
{
return owner.FindProperty(info.Name);
}
if (typeof(InputSettingsSelector).IsAssignableFrom(info.FieldType))
{
var selector = info.GetValue(targetObject);
var fields = InputSettingsSelector.GetInputFields(selector.GetType());
var selectorInput = fields.FirstOrDefault(i => i.GetValue(selector) == fieldValue);
if (selectorInput != null)
{
return owner.FindProperty(info.Name);
}
}
}
return null;
}
///
/// Displays extra options below the capture information, under certain conditions.
///
protected virtual void ExtraOptionsGUI()
{
if (((RecorderSettings)target).FrameRatePlayback == FrameRatePlayback.Variable)
{
EditorGUILayout.PropertyField(m_CaptureEveryNthFrame, Styles.RenderStepFrameLabel);
}
}
///
/// Displays options related to the output format of the recording.
///
protected virtual void FileTypeAndFormatGUI()
{
}
///
/// Displays properties related to the encoding of the file.
///
protected virtual void OnEncodingGui()
{
}
///
/// Override this function to add custom UI at the top of the Input section.
///
internal virtual void CustomGUI()
{
}
///
/// Displays the information related to the capture of multiframe accumulation.
///
protected virtual void AccumulationGUI()
{
var settings = (RecorderSettings)target;
if (settings.IsAccumulationSupported())
{
var prop = serializedObject.FindProperty("_accumulationSettings");
if (prop != null)
EditorGUILayout.PropertyField(prop);
}
}
internal class SavedBool
{
bool m_Value;
string m_Name;
bool m_Loaded;
public SavedBool(string name, bool value)
{
m_Name = name;
m_Loaded = false;
m_Value = value;
}
private void Load()
{
if (m_Loaded)
return;
m_Loaded = true;
m_Value = EditorPrefs.GetBool(m_Name, m_Value);
}
public bool value
{
get { Load(); return m_Value; }
set
{
Load();
if (m_Value == value)
return;
m_Value = value;
EditorPrefs.SetBool(m_Name, value);
}
}
public static implicit operator bool(SavedBool s)
{
return s.value;
}
}
}
}