using UnityEngine; using UnityEditor; using System.Collections.Generic; using UnityEngine.UIElements; using UnityEditor.UIElements; using System; namespace Unity.Cinemachine.Editor { [CustomEditor(typeof(CinemachineBlenderSettings))] class CinemachineBlenderSettingsEditor : UnityEditor.Editor { CinemachineBlenderSettings Target => target as CinemachineBlenderSettings; /// /// Called when building the Camera popup menus, to get the domain of possible /// cameras. If no delegate is set, will find all top-level virtual cameras in the scene, /// i.e. vcams that are not feeding a specific mixer. /// public GetAllVirtualCamerasDelegate GetAllVirtualCameras = GetToplevelCameras; public delegate void GetAllVirtualCamerasDelegate(List list); // Get all top-level virtual cameras static void GetToplevelCameras(List list) { var candidates = Resources.FindObjectsOfTypeAll(); for (var i = 0; i < candidates.Length; ++i) if (candidates[i].ParentCamera == null) list.Add(candidates[i]); } public override VisualElement CreateInspectorGUI() { var ux = new VisualElement(); var header = ux.AddChild(new VisualElement { style = { flexDirection = FlexDirection.Row, marginBottom = -2 } }); FormatElement(true, header.AddChild(new Label("From")), header.AddChild(new Label("To")), header.AddChild(new Label("Blend"))); header.AddToClassList("unity-collection-view--with-border"); var list = ux.AddChild(new ListView() { reorderable = true, reorderMode = ListViewReorderMode.Animated, showAddRemoveFooter = true, showBorder = true, showBoundCollectionSize = false, showFoldoutHeader = false, style = { borderTopWidth = 0, marginLeft = 0 }, }); var elements = serializedObject.FindProperty(() => Target.CustomBlends); list.BindProperty(elements); // Gather the camera candidates var availableCameras = new List(); Dictionary cameraIndexLookup = new(); Action onCamerasUpdated = null; list.TrackAnyUserActivity(() => { var allCameras = new List(); GetAllVirtualCameras(allCameras); availableCameras.Clear(); availableCameras.Add(string.Empty); availableCameras.Add(CinemachineBlenderSettings.kBlendFromAnyCameraLabel); for (int i = 0; i < allCameras.Count; ++i) if (allCameras[i] != null && !availableCameras.Contains(allCameras[i].Name)) availableCameras.Add(allCameras[i].Name); onCamerasUpdated?.Invoke(); }); list.makeItem = () => { var def = new CinemachineBlenderSettings.CustomBlend(); var row = new BindableElement { style = { flexDirection = FlexDirection.Row }}; var from = row.AddChild(CreateCameraPopup(SerializedPropertyHelper.PropertyName(() => def.From))); var to = row.AddChild(CreateCameraPopup(SerializedPropertyHelper.PropertyName(() => def.To))); var blend = row.AddChild(new PropertyField(null, "") { bindingPath = SerializedPropertyHelper.PropertyName(() => def.Blend)}); FormatElement(false, from, to, blend); return row; // Local function VisualElement CreateCameraPopup(string bindingPath) { var container = new VisualElement { style = { flexDirection = FlexDirection.Row, flexGrow = 1 }}; var textField = container.AddChild(new TextField { bindingPath = bindingPath, isDelayed = true, style = { flexGrow = 1, flexBasis = 20 }}); var warning = container.AddChild(InspectorUtility.MiniHelpIcon($"No in-scene camera matches this name")); textField.RegisterValueChangedCallback((evt) => OnCameraUpdated()); onCamerasUpdated += OnCameraUpdated; void OnCameraUpdated() { warning.tooltip = $"No in-scene camera matches \"{textField.value}\""; warning.SetVisible(availableCameras.FindIndex(x => x == textField.value) < 0); }; var popup = container.AddChild(InspectorUtility.MiniDropdownButton( "Choose from currently-available cameras", new ContextualMenuManipulator((evt) => { for (int i = 0; i < availableCameras.Count; ++i) evt.menu.AppendAction(availableCameras[i], (action) => textField.value = action.name); }))); popup.style.marginRight = 5; return container; } }; return ux; // Local function static void FormatElement(bool isHeader, VisualElement e1, VisualElement e2, VisualElement e3) { e1.style.marginLeft = isHeader ? 2 * InspectorUtility.SingleLineHeight - 3: 0; e1.style.flexBasis = InspectorUtility.SingleLineHeight; e1.style.flexGrow = 3; e2.style.flexBasis = 1; e2.style.flexGrow = 3; e3.style.flexBasis = 1; e3.style.flexGrow = 2; } } } }