using System; using System.Collections.Generic; using System.Reflection; using JetBrains.Annotations; using UnityEditor; using UnityEngine; using UTools = UnityEditor.Tools; namespace Module.NavigationTool.Editor.Toolbar { [UsedImplicitly] internal sealed class ToolBuildTargetPicker : AbstractToolbarDrawer { public override bool Visible => ToolbarBuildSettings.IsBuildEnabled; public override bool Enabled => !Application.isPlaying; public override EToolbarPlacement Placement => EToolbarPlacement.Left; public override int Priority => (int)EToolbarPriority.Low; private static bool IS_DIRTY = true; private static int[] HASHES = new int[0]; private static BuildTargetGroup[] GROUPS = new BuildTargetGroup[0]; private static BuildTarget[] TARGETS = new BuildTarget[0]; private static GUIContent[] TARGET_LIST = new GUIContent[0]; private static int SELECTED_INDEX = -1; private static void Initialize() { if (!IS_DIRTY) return; BuildTarget target = EditorUserBuildSettings.activeBuildTarget; BuildTargetGroup group = BuildPipeline.GetBuildTargetGroup(target); PopulateBuildTargetList(); SELECTED_INDEX = Array.IndexOf(HASHES, GetHashCode(group, target)); IS_DIRTY = false; } public override void Update() { Initialize(); } protected override void Draw(Rect rect) { BuildTarget target = EditorUserBuildSettings.activeBuildTarget; BuildTargetGroup group = BuildPipeline.GetBuildTargetGroup(target); GUIContent content = SELECTED_INDEX != -1 ? GetButtonContent(group) : GUIContent.none; if (GUI.Button(rect, content, styles.buttonNoPadding)) { // Note: Do not discard the parameters: userData and options, since they throw an error in 2019, if there are two of them EditorUtility.DisplayCustomMenu(rect, TARGET_LIST, SELECTED_INDEX, (userData, options, selected) => { if (selected != -1 && selected != SELECTED_INDEX) SetBuildTargetTo(selected); }, null); } } public override float CalculateWidth() { return 40.0f; } private static void SetBuildTargetTo(int index) { if (index < 0) return; BuildTargetGroup group = GROUPS[index]; BuildTarget target = TARGETS[index]; if (!EditorUserBuildSettings.SwitchActiveBuildTarget(@group, target)) return; IS_DIRTY = true; SELECTED_INDEX = index; } private static void PopulateBuildTargetList() { var hashes = new List(); var groups = new List(); var targets = new List(); var contentList = new List(); Type type = typeof(BuildTarget); Array values = Enum.GetValues(type); for (var i = 0; i < values.Length; i++) { var target = (BuildTarget)values.GetValue(i); BuildTargetGroup group = BuildPipeline.GetBuildTargetGroup(target); if (target < 0 || !BuildPipeline.IsBuildTargetSupported(group, target)) continue; if (!IsValid(type, target)) continue; hashes.Add(GetHashCode(group, target)); groups.Add(group); targets.Add(target); contentList.Add(new GUIContent(ObjectNames.NicifyVariableName(target.ToString()))); } HASHES = hashes.ToArray(); GROUPS = groups.ToArray(); TARGETS = targets.ToArray(); TARGET_LIST = contentList.ToArray(); } private static bool IsValid(Type type, BuildTarget target) { MemberInfo[] members = type.GetMember(target.ToString(), BindingFlags.Public | BindingFlags.Static); if (members.Length == 0) return false; for (var j = 0; j < members.Length; j++) { if (members[j].GetCustomAttribute() == null) return true; } return false; } private static GUIContent GetButtonContent(BuildTargetGroup group) { const string TOOLTIP = "Select target platform for build"; var name = $"BuildSettings.{group}.Small"; if (group == BuildTargetGroup.WSA) name = "BuildSettings.Metro.Small"; GUIContent iconContent = EditorGUIUtility.IconContent(name); var content = new GUIContent(iconContent.image, TOOLTIP); return content; } private static int GetHashCode(BuildTargetGroup group, BuildTarget target) { return ((int)group << 16) + (int)target; } } }