using System; using UnityEngine; using UnityEditor; using UnityEditorInternal; namespace JeffGrawAssets.FlexibleUI { [CustomEditor(typeof(BlurPreset))] public class BlurPresetEditor : Editor { private static GUIStyle _qualityBoxStyle; private static GUIStyle QualityBoxStyle { get { if (_qualityBoxStyle == null && GUI.skin?.box != null) _qualityBoxStyle = new GUIStyle(GUI.skin.box) { padding = new RectOffset(15, 15, 0, 5) }; return _qualityBoxStyle ?? GUIStyle.none; } } private static GUIStyle _qualityFontStyle; private static GUIStyle QualityFontStyle { get { if (_qualityFontStyle == null && GUI.skin?.label != null) _qualityFontStyle = new GUIStyle(GUI.skin.label) { fontSize = 14, fontStyle = FontStyle.Bold }; return _qualityFontStyle ?? GUIStyle.none; } } private static GUIStyle _qualityToggleStyle; private static GUIStyle QualityToggleStyle { get { if (_qualityToggleStyle == null && GUI.skin?.toggle != null) _qualityToggleStyle = new GUIStyle(GUI.skin.toggle) { fontSize = 14, fontStyle = FontStyle.Bold }; return _qualityToggleStyle ?? GUIStyle.none; } } private static GUIStyle _qualityButtonStyle; private static GUIStyle QualityButtonStyle { get { if (_qualityButtonStyle == null && GUI.skin?.button != null) _qualityButtonStyle = new GUIStyle(GUI.skin.button) { fontSize = 14, fontStyle = FontStyle.Bold }; return _qualityButtonStyle ?? GUIStyle.none; } } private static GUIStyle _sliderBackgroundStyle; private static GUIStyle SliderBackgroundStyle { get { if (_sliderBackgroundStyle == null && GUI.skin?.horizontalSlider != null) _sliderBackgroundStyle = new GUIStyle(GUI.skin.horizontalSlider) { fixedHeight = 10 }; return _sliderBackgroundStyle ?? GUIStyle.none; } } private static GUIStyle _sliderThumbStyle; private static GUIStyle SliderThumbStyle { get { if (_sliderThumbStyle == null && GUI.skin?.horizontalSliderThumb != null) _sliderThumbStyle = new GUIStyle(GUI.skin.horizontalSliderThumb) { fixedHeight = 20, fixedWidth = 80 }; return _sliderThumbStyle ?? GUIStyle.none; } } private static GUIStyle _sectionLabelStyle; private static GUIStyle SectionLabelStyle { get { if (_sectionLabelStyle == null && GUI.skin?.label != null) _sectionLabelStyle = new GUIStyle(GUI.skin.label) { alignment = TextAnchor.MiddleCenter, fontStyle = FontStyle.Bold }; return _sectionLabelStyle ?? GUIStyle.none; } } private static readonly GUIContent AdditionalDistanceContent = new ("Additional Distance/Iteration"); private SerializedProperty settingsProperty; private BlurPreset blurPreset; private ReorderableList downscaleSectionList, blurSectionList; private int selectedQualityLevel; private bool preview; private void OnEnable() { blurPreset = serializedObject.targetObject as BlurPreset; settingsProperty = serializedObject.FindProperty(BlurPreset.SettingsFieldName); selectedQualityLevel = QualitySettings.GetQualityLevel(); } private void OnDisable() { preview = false; blurPreset.preview = -1; } public override void OnInspectorGUI() => DrawGUI(); public void DrawGUI() { serializedObject.Update(); bool addedQualitySetting = blurPreset.TryFillSettings(); if (addedQualitySetting) { EditorUtility.SetDirty(target); serializedObject.ApplyModifiedProperties(); EditorApplication.QueuePlayerLoopUpdate(); SceneView.RepaintAll(); return; } var origBackgroundColor = GUI.backgroundColor; GUI.backgroundColor = Color.black; EditorGUILayout.BeginVertical(QualityBoxStyle); GUI.backgroundColor = Color.cyan; selectedQualityLevel = (int)GUILayout.HorizontalSlider(selectedQualityLevel, 0, (float)BlurPreset.GetQualitySettingsCount() - 1, SliderBackgroundStyle, SliderThumbStyle); EditorGUILayout.Space(22); EditorGUILayout.BeginHorizontal(); EditorGUILayout.BeginVertical(); EditorGUILayout.LabelField($"Quality: {QualitySettings.names[selectedQualityLevel]}", QualityFontStyle); var prevPreviewVal = blurPreset.preview; GUI.color = Color.white; if (selectedQualityLevel != QualitySettings.GetQualityLevel()) { preview = GUILayout.Toggle(preview, "Preview", QualityToggleStyle); GUILayout.FlexibleSpace(); } EditorGUILayout.EndVertical(); blurPreset.preview = preview ? selectedQualityLevel : -1; if (blurPreset.preview != prevPreviewVal) { EditorApplication.QueuePlayerLoopUpdate(); SceneView.RepaintAll(); } if (GUILayout.Button("Apply to all", QualityButtonStyle, GUILayout.Width(120), GUILayout.Height(34))) { var currentProperties = blurPreset.Settings[selectedQualityLevel]; foreach (var qualityLevel in blurPreset.Settings) qualityLevel.CopySettings(currentProperties); EditorUtility.SetDirty(target); settingsProperty.serializedObject.ApplyModifiedProperties(); EditorApplication.QueuePlayerLoopUpdate(); SceneView.RepaintAll(); return; } EditorGUILayout.EndHorizontal(); EditorGUILayout.EndVertical(); GUI.backgroundColor = origBackgroundColor; var currentSettings = settingsProperty.GetArrayElementAtIndex(selectedQualityLevel); DrawBlurProperties(currentSettings, ref downscaleSectionList, ref blurSectionList);; serializedObject.ApplyModifiedProperties(); } public static void DrawBlurProperties(SerializedProperty settings, ref ReorderableList downscaleSectionList, ref ReorderableList blurSectionList) { var originalLabelWidth = EditorGUIUtility.labelWidth; EditorGUILayout.BeginVertical(GUI.skin.box); EditorGUI.BeginChangeCheck(); var refResProp = settings.FindPropertyRelative(nameof(BlurSettings.referenceResolution)); EditorGUILayout.PropertyField(refResProp); if (EditorGUI.EndChangeCheck()) refResProp.intValue = Mathf.Max(refResProp.intValue, 0); EditorGUILayout.PropertyField(settings.FindPropertyRelative(nameof(BlurSettings.hqResample))); EditorGUILayout.EndVertical(); EditorGUILayout.BeginVertical(GUI.skin.box); EditorGUILayout.LabelField("Downscale Pass", SectionLabelStyle); var downscaleSectionsProp = settings.FindPropertyRelative(nameof(BlurSettings.downscaleSections)); downscaleSectionList ??= new ReorderableList(settings.serializedObject, downscaleSectionsProp, true, false, true, true) { drawElementCallback = (rect, index, _, _) => OnDrawBlurItems(rect, index, 8, downscaleSectionsProp), elementHeightCallback = index => GetElementHeight(index, downscaleSectionsProp), drawElementBackgroundCallback = OnDrawBackground }; downscaleSectionList.DoLayoutList(); EditorGUILayout.EndVertical(); EditorGUILayout.BeginVertical(GUI.skin.box); EditorGUILayout.LabelField("Blur Pass", SectionLabelStyle); var blurSectionsProp = settings.FindPropertyRelative(nameof(BlurSettings.blurSections)); blurSectionList ??= new ReorderableList(settings.serializedObject, blurSectionsProp, true, false, true, true) { drawElementCallback = (rect, index, _, _) => OnDrawBlurItems(rect, index, 128, blurSectionsProp), elementHeightCallback = index => GetElementHeight(index, blurSectionsProp), onAddCallback = OnAddToList, drawElementBackgroundCallback = OnDrawBackground }; blurSectionList.DoLayoutList(); EditorGUILayout.Space(2); EditorGUIUtility.labelWidth = originalLabelWidth; EditorGUILayout.PropertyField(settings.FindPropertyRelative(nameof(BlurSettings.blurAdditionalDistancePerIteration)), AdditionalDistanceContent); EditorGUILayout.EndVertical(); EditorGUILayout.BeginVertical(GUI.skin.box); EditorGUILayout.LabelField("Image Adjustment", SectionLabelStyle); EditorGUILayout.PropertyField(settings.FindPropertyRelative(nameof(BlurSettings.ditherStrength))); EditorGUILayout.PropertyField(settings.FindPropertyRelative(nameof(BlurSettings.brightness))); EditorGUILayout.PropertyField(settings.FindPropertyRelative(nameof(BlurSettings.contrast))); EditorGUILayout.PropertyField(settings.FindPropertyRelative(nameof(BlurSettings.vibrancy))); EditorGUILayout.PropertyField(settings.FindPropertyRelative(nameof(BlurSettings.tint))); EditorGUILayout.EndVertical(); float GetElementHeight(int index, SerializedProperty listProp) { var element = listProp.GetArrayElementAtIndex(index); var algoIdx = element.FindPropertyRelative($"{BlurSection.AlgorithmIdxFieldName}").intValue; var isSeparable = BlurAlgorithm.All[algoIdx].SecondKernelIdx >= 0; if (isSeparable) { return EditorGUIUtility.singleLineHeight * 2 + 6f; } return EditorGUIUtility.singleLineHeight + 3; } void OnDrawBackground(Rect backgroundRect, int index, bool isActive, bool isFocused) { if (isFocused) EditorGUI.DrawRect(backgroundRect, new Color(0.3f,0.45f,0.45f,1f)); else ReorderableList.defaultBehaviours.DrawElementBackground(backgroundRect, index, isActive, isFocused, true); } void OnDrawBlurItems(Rect rect, int index, int maxIterations, SerializedProperty listProp) { var isTwoRowRect = rect.height > EditorGUIUtility.singleLineHeight * 2; rect.y += 2.5f; rect.height -= 6.5f; if (isTwoRowRect) rect = new Rect(rect.position, new Vector2(rect.width, rect.height * 0.5f)); var (firstLabelWidth, secondLabelWidth, thirdLabelWidth, firstLabelName, secondLabelName, thirdLabelName) = rect.width < 400 ? (27.5f, 22f, 24f, "Algo", "Str", "Iter") : (57.5f, 52.5f, 60f, "Algorithm", "Strength", "Iterations"); var firstSection = (firstLabelWidth, 65f, 155f); var secondSection = (secondLabelWidth, 20f, 60f); var thirdSection = (thirdLabelWidth, 20f, 60f); var rectArr = EditorHelpers.DivideRect(EditorHelpers.Alignment.Left, rect, 4f, float.MaxValue, 2f, firstSection, secondSection, thirdSection); var element = listProp.GetArrayElementAtIndex(index); var blurSection = element.boxedValue as BlurSection; var algoIdx = Array.IndexOf(BlurAlgorithm.All, blurSection.BlurAlgorithm); EditorGUIUtility.labelWidth = firstLabelWidth; EditorGUI.BeginChangeCheck(); algoIdx = EditorGUI.Popup(rectArr[0], firstLabelName, algoIdx, BlurAlgorithm.Names); if (EditorGUI.EndChangeCheck()) { element.boxedValue = new BlurSection(algoIdx, blurSection.iterations, blurSection.sampleDistance, blurSection.horizontalSamplesPerSide, blurSection.verticalSamplesPerSide);; element.serializedObject.ApplyModifiedProperties(); element.serializedObject.Update(); } EditorGUIUtility.labelWidth = secondLabelWidth; var strengthProp = element.FindPropertyRelative($"{nameof(BlurSection.sampleDistance)}"); EditorGUI.BeginChangeCheck(); EditorGUI.PropertyField(rectArr[1], strengthProp, new GUIContent(secondLabelName)); if (EditorGUI.EndChangeCheck()) strengthProp.floatValue = Mathf.Max(strengthProp.floatValue, 0f); EditorGUIUtility.labelWidth = thirdLabelWidth; var iterationsProp = element.FindPropertyRelative($"{nameof(BlurSection.iterations)}"); EditorGUI.BeginChangeCheck(); EditorGUI.PropertyField(rectArr[2], iterationsProp, new GUIContent(thirdLabelName)); if (EditorGUI.EndChangeCheck()) iterationsProp.intValue = Mathf.Clamp(iterationsProp.intValue, 0, maxIterations); if (!isTwoRowRect) { EditorGUIUtility.labelWidth = originalLabelWidth; return; } var horSamplesProp = element.FindPropertyRelative($"{nameof(BlurSection.horizontalSamplesPerSide)}"); var vertSamplesProp = element.FindPropertyRelative($"{nameof(BlurSection.verticalSamplesPerSide)}"); var sizeReadout = new GUIContent($"({(horSamplesProp.intValue > 0 ? 2 * horSamplesProp.intValue + 1 : 0)} + {(vertSamplesProp.intValue > 0 ? 2 * vertSamplesProp.intValue + 1 : 0)} Taps)"); var readoutLabelWidth = EditorStyles.label.CalcSize(sizeReadout).x; (firstLabelWidth, secondLabelWidth, firstLabelName, secondLabelName) = rect.width < 100 + readoutLabelWidth ? (12.5f, 12.5f, "H", "V") : (22.5f, 26f, "Hor", "Vert"); firstSection = (firstLabelWidth, 22f, 25f); secondSection = (secondLabelWidth, 22f, 25f); thirdSection = (readoutLabelWidth, 0f, 0f); rect.y += EditorGUIUtility.singleLineHeight + 3; rectArr = EditorHelpers.DivideRect(EditorHelpers.Alignment.Left, rect, 4f, 8f, 2f, firstSection, secondSection, thirdSection); EditorGUIUtility.labelWidth = firstLabelWidth; EditorGUI.BeginChangeCheck(); EditorGUI.PropertyField(rectArr[0], horSamplesProp, new GUIContent(firstLabelName)); if (EditorGUI.EndChangeCheck()) horSamplesProp.intValue = Mathf.Clamp(horSamplesProp.intValue, 0, 32); EditorGUIUtility.labelWidth = secondLabelWidth; EditorGUI.BeginChangeCheck(); EditorGUI.PropertyField(rectArr[1], vertSamplesProp, new GUIContent(secondLabelName)); if (EditorGUI.EndChangeCheck()) vertSamplesProp.intValue = Mathf.Clamp(vertSamplesProp.intValue, 0, 32); EditorGUIUtility.labelWidth = readoutLabelWidth; EditorGUI.LabelField(rectArr[2], sizeReadout); EditorGUIUtility.labelWidth = originalLabelWidth; } void OnAddToList(ReorderableList list) { var oldArraySize = list.serializedProperty.arraySize; list.serializedProperty.arraySize++; BlurSection newItem; if (oldArraySize == 0) { newItem = new BlurSection(0, 1, 1.5f); } else { var lastItem = list.serializedProperty.GetArrayElementAtIndex(oldArraySize - 1).boxedValue as BlurSection; newItem = new BlurSection(lastItem.BlurAlgorithm, lastItem.iterations, lastItem.sampleDistance, lastItem.horizontalSamplesPerSide, lastItem.verticalSamplesPerSide); } list.serializedProperty.GetArrayElementAtIndex(oldArraySize).boxedValue = newItem; } } } }