/// --------------------------------------------- /// Movement Pack for Behavior Designer Pro /// Copyright (c) Opsive. All Rights Reserved. /// https://www.opsive.com /// --------------------------------------------- namespace Opsive.BehaviorDesigner.AddOns.SensesPack.Editor { using Opsive.BehaviorDesigner.AddOns.SensesPack.Runtime.Sensors; using Opsive.BehaviorDesigner.Editor; using Opsive.Shared.Editor.UIElements; using Opsive.Shared.Editor.UIElements.Controls; using Opsive.Shared.Editor.UIElements.Controls.Attributes; using Opsive.Shared.Utility; using System; using System.Collections.Generic; using UnityEditor; using UnityEngine; using UnityEngine.UIElements; /// /// Implements AttributeControlBase for the DetectionModeListAttribute type. /// [ControlType(typeof(DetectionModeListAttribute))] public class DetectionModesAttributeControl : AttributeControlBase { /// /// The DetectionModeView displays the contents of the DetectionMode. /// public class DetectionModeView : VisualElement { protected UnityEngine.Object m_UnityObject; protected DetectionMode[] m_DetectionModes; protected Func m_OnChangeEvent; private List m_DetectionModeTypes = new List(); private List m_DetectionModeTypeNames = new List(); private ReorderableList m_ReorderableList; private int m_SelectedIndex = 0; private VisualElement m_DetectionModeContainer; /// /// DetectionModeView constructor. /// /// A reference to the owning Unity Object. /// The DetectionModes being drawn. /// An event that is sent when the value changes. Returns false if the control cannot be changed. public DetectionModeView(UnityEngine.Object unityObject, DetectionMode[] detectionModes, Func onChangeEvent) { m_UnityObject = unityObject; m_DetectionModes = detectionModes; m_OnChangeEvent = onChangeEvent; var graphWindow = BehaviorDesignerWindow.Instance; m_ReorderableList = new ReorderableList(m_DetectionModes, (VisualElement parent, int index) => { parent.Add(new Label()); }, (VisualElement parent, int index) => { (parent.ElementAt(0) as Label).text = m_DetectionModes[index].GetType().Name; }, (VisualElement parent) => { parent.Add(new Label("Detection Modes")); }, (int index) => { ShowSelectedDetectionMode(index); }, () => // Add. { FilterWindow.ShowFilterWindow(graphWindow, new SearchWindowContext(graphWindow.position.position + m_ReorderableList.AddButton.worldBound.position), new Type[] { typeof(DetectionMode) }, FilterWindow.FilterType.Class, "Detection Mode", false, null, OnAdd, (Type type) => { return typeof(DetectionMode).IsAssignableFrom(type); }); }, (int index) => // Remove. { ArrayUtility.RemoveAt(ref m_DetectionModes, index); m_ReorderableList.EnableRemove = m_DetectionModes.Length > 1 && !Application.isPlaying; m_ReorderableList.Refresh(m_DetectionModes); m_OnChangeEvent(m_DetectionModes); m_SelectedIndex = Mathf.Clamp(m_SelectedIndex - 1, 0, m_DetectionModes.Length - 1); ShowSelectedDetectionMode(m_SelectedIndex); }, (int fromIndex, int toIndex) => // Reorder. { m_OnChangeEvent(m_DetectionModes); if (m_SelectedIndex == fromIndex) { ShowSelectedDetectionMode(toIndex); } }); m_ReorderableList.EnableAdd = m_ReorderableList.EnableReorder = !Application.isPlaying; m_ReorderableList.EnableRemove = (m_DetectionModes != null && m_DetectionModes.Length > 1) && !Application.isPlaying; Add(m_ReorderableList); // Show any detection mode fields. m_DetectionModeContainer = new VisualElement(); m_DetectionModeContainer.style.marginBottom = 4; m_DetectionModeContainer.style.display = DisplayStyle.Flex; Add(m_DetectionModeContainer); ShowSelectedDetectionMode(m_SelectedIndex); if (m_DetectionModes != null && m_SelectedIndex < m_DetectionModes.Length) { m_ReorderableList.SelectedIndex = m_SelectedIndex; } } /// /// Adds a new detection mode to the array. /// /// The selected task type. /// The base type that was used to find the element. /// The position of the FilterWindow. private void OnAdd(object selectedObject, Type baseType, Vector2 windowPosition) { if (selectedObject == null) { return; } if (m_DetectionModes == null) { m_DetectionModes = new DetectionMode[1]; } else { Array.Resize(ref m_DetectionModes, m_DetectionModes.Length + 1); } m_DetectionModes[m_DetectionModes.Length - 1] = Activator.CreateInstance((Type)selectedObject) as DetectionMode; m_ReorderableList.Refresh(m_DetectionModes); m_ReorderableList.SelectedIndex = m_DetectionModes.Length - 1; m_ReorderableList.EnableRemove = m_DetectionModes.Length > 1 && !Application.isPlaying; m_OnChangeEvent(m_DetectionModes); } /// /// Shows the selected detection mode. /// /// The index of the detection mode within the detection mode array. private void ShowSelectedDetectionMode(int index) { m_SelectedIndex = index; m_DetectionModeContainer.Clear(); if (m_DetectionModes == null || m_DetectionModes.Length <= m_SelectedIndex) { return; } var label = new Label(); label.enableRichText = true; label.style.marginLeft = 3; label.style.unityTextAlign = TextAnchor.MiddleCenter; label.text = $"{m_DetectionModes[index].GetType().Name}"; label.tooltip = m_DetectionModes[index].GetType().FullName; m_DetectionModeContainer.Add(label); FieldInspectorView.AddFields(m_UnityObject, m_DetectionModes[index], MemberVisibility.Public, m_DetectionModeContainer, (object obj) => { m_DetectionModes[index] = obj as DetectionMode; m_OnChangeEvent?.Invoke(m_DetectionModes); }); } } /// /// Does the control use a label? /// public override bool UseLabel { get { return false; } } /// /// Does the attribute override the type control? /// public override bool OverrideTypeControl => true; /// /// Returns the control that should be used for the specified AttributeControlType. /// /// The input to the control. /// The created control. protected override VisualElement GetControl(AttributeControlInput input) { return new DetectionModeView(input.UnityObject, input.Value as DetectionMode[], input.OnChangeEvent); } } }