private void DrawEnvironmentInspectorForWorker(WorkerBuildConfiguration configurationForWorker)
        {
            EditorGUILayout.LabelField("Environments", EditorStyles.boldLabel);

            DrawEnvironmentInspector(BuildEnvironment.Local, configurationForWorker);
            DrawEnvironmentInspector(BuildEnvironment.Cloud, configurationForWorker);
        }
        private bool DrawWorkerConfiguration(WorkerBuildConfiguration configurationForWorker)
        {
            var workerType = configurationForWorker.WorkerType;

            EditorGUILayout.BeginHorizontal();
            configurationForWorker.ShowFoldout =
                EditorGUILayout.Foldout(configurationForWorker.ShowFoldout, workerType);

            var controlRect = EditorGUILayout.GetControlRect(false);
            var buttonRect  = new Rect(controlRect);

            buttonRect.width = buttonRect.height + 5;
            if (GUI.Button(buttonRect, RemoveWorkerTypeButtonContents))
            {
                return(false);
            }

            EditorGUILayout.EndHorizontal();

            if (configurationForWorker.ShowFoldout)
            {
                using (IndentLevelScope(1))
                {
                    DrawScenesInspectorForWorker(configurationForWorker);
                    DrawEnvironmentInspectorForWorker(configurationForWorker);
                }
            }

            return(true);
        }
        private void DrawScenesInspectorForWorker(WorkerBuildConfiguration configurationForWorker)
        {
            EditorGUILayout.LabelField("Scenes", EditorStyles.boldLabel);

            using (IndentLevelScope(1))
            {
                var scenesToShowInList = configurationForWorker
                                         .ScenesForWorker
                                         .Select((sceneAsset, index) =>
                                                 new SceneItem(sceneAsset, true, scenesInAssetDatabase))
                                         .ToList();

                EditorGUI.BeginChangeCheck();

                var sceneItems = scenesInAssetDatabase
                                 .Where(sceneAsset => !configurationForWorker.ScenesForWorker.Contains(sceneAsset))
                                 .Select(sceneAsset => new SceneItem(sceneAsset, false, scenesInAssetDatabase))
                                 .ToList();

                var horizontalLayout = Screen.width > ScreenWidthForHorizontalLayout;

                using (horizontalLayout ? new EditorGUILayout.HorizontalScope() : null)
                {
                    using (horizontalLayout ? new EditorGUILayout.VerticalScope() : null)
                    {
                        EditorGUILayout.LabelField("Scenes to include (in order)");

                        DrawIndentedList(scenesToShowInList,
                                         ReorderableListFlags.ShowIndices | ReorderableListFlags.EnableReordering,
                                         SceneItem.Drawer);
                    }

                    using (horizontalLayout ? new EditorGUILayout.VerticalScope() : null)
                    {
                        using (horizontalLayout ? IndentLevelScope(-EditorGUI.indentLevel) : null)
                        {
                            EditorGUILayout.LabelField("Exclude");

                            DrawIndentedList(sceneItems,
                                             ReorderableListFlags.None,
                                             SceneItem.Drawer);
                        }
                    }
                }

                if (EditorGUI.EndChangeCheck())
                {
                    EditorUtility.SetDirty(target);
                    Undo.RecordObject(target, "Configure scenes for worker");

                    configurationForWorker.ScenesForWorker = scenesToShowInList.Concat(sceneItems)
                                                             .Where(item => item.Included)
                                                             .Select(item => item.SceneAsset)
                                                             .ToArray();

                    scenesChanged = true;
                }
            }
        }
        private void DrawEnvironmentInspectorForWorker(WorkerBuildConfiguration configurationForWorker)
        {
            DrawEnvironmentInspector(BuildEnvironment.Local, configurationForWorker);

            EditorGUILayout.Space();

            DrawEnvironmentInspector(BuildEnvironment.Cloud, configurationForWorker);
        }
        private void TrackDragDrop(WorkerBuildConfiguration configurationForWorker, EventType currentEventType,
                                   SceneAsset item,
                                   DragAndDropInfo dragState, int itemIndex)
        {
            switch (currentEventType)
            {
            case EventType.MouseDrag:
                DragAndDrop.PrepareStartDrag();

                DragAndDrop.objectReferences = new[] { item };
                DragAndDrop.paths            = new[] { AssetDatabase.GetAssetPath(item) };

                DragAndDrop.StartDrag(item.name);

                dragState.SourceItemIndex = itemIndex;
                Event.current.Use();
                Repaint();

                sourceDragState = dragState;

                break;

            case EventType.DragPerform:
                Event.current.Use();
                DragAndDrop.AcceptDrag();
                Repaint();
                break;

            case EventType.MouseUp:     // Fall through.
            case EventType.DragExited:
                sourceDragState           = null;
                dragState.SourceItemIndex = -1;
                Repaint();
                break;

            case EventType.DragUpdated:
                var sceneAssets = DragAndDrop.objectReferences.OfType <SceneAsset>()
                                  .ToList();
                if (sceneAssets.Any())
                {
                    if (dragState.SourceItemIndex == -1 &&
                        sceneAssets.Intersect(configurationForWorker.ScenesForWorker).Any())
                    {
                        DragAndDrop.visualMode = DragAndDropVisualMode.Rejected;
                    }
                    else
                    {
                        DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
                    }

                    Event.current.Use();
                    Repaint();
                }

                break;
            }
        }
        public override void OnInspectorGUI()
        {
            var workerConfiguration = (SpatialOSBuildConfiguration)target;

            EditorGUILayout.BeginHorizontal();
            workerTypeName = EditorGUILayout.TextField(workerTypeName);
            var controlRect = EditorGUILayout.GetControlRect(false);
            var buttonRect  = new Rect(controlRect);

            buttonRect.width = buttonRect.height + 5;

            if (GUI.Button(buttonRect, AddWorkerTypeButtonContents))
            {
                var workerType = new WorkerPlatform(workerTypeName);
                var exists     = workerConfiguration.WorkerBuildConfigurations.Any(x => x.WorkerPlatform == workerType);
                if (!exists)
                {
                    var config = new WorkerBuildConfiguration
                    {
                        WorkerPlatform   = new WorkerPlatform(workerTypeName),
                        ScenesForWorker  = new SceneAsset[] { },
                        LocalBuildConfig = new BuildEnvironmentConfig()
                        {
                            BuildPlatforms = SpatialBuildPlatforms.Current,
                            BuildOptions   = BuildOptions.Development
                        },
                        CloudBuildConfig = new BuildEnvironmentConfig()
                        {
                            BuildPlatforms = SpatialBuildPlatforms.Current
                        }
                    };
                    workerConfiguration.WorkerBuildConfigurations.Add(config);
                }
            }

            EditorGUILayout.EndHorizontal();

            scenesChanged = false;

            var configs = workerConfiguration.WorkerBuildConfigurations;

            foreach (var workerConfig in configs)
            {
                if (!DrawWorkerConfiguration(workerConfig))
                {
                    configs.Remove(workerConfig);
                    break;
                }
            }

            if (scenesChanged)
            {
                scenesChanged = false;
                workerConfiguration.UpdateEditorScenesForBuild();
            }
        }
        public override void OnInspectorGUI()
        {
            var workerConfiguration = (SpatialOSBuildConfiguration)target;

            EditorGUILayout.BeginHorizontal();
            workerTypeName = EditorGUILayout.TextField(workerTypeName);
            var controlRect = EditorGUILayout.GetControlRect(false);
            var buttonRect  = new Rect(controlRect);

            buttonRect.width = buttonRect.height + 5;

            if (GUI.Button(buttonRect, AddWorkerTypeButtonContents))
            {
                if (workerConfiguration.WorkerBuildConfigurations.All(x => x.WorkerType != workerTypeName))
                {
                    var config = new WorkerBuildConfiguration
                    {
                        WorkerType       = workerTypeName,
                        LocalBuildConfig = new BuildEnvironmentConfig
                        {
                            BuildOptions = BuildOptions.Development,
                        },
                        CloudBuildConfig = new BuildEnvironmentConfig
                        {
                            BuildOptions = BuildOptions.EnableHeadlessMode,
                        }
                    };
                    workerConfiguration.WorkerBuildConfigurations.Add(config);
                }
            }

            EditorGUILayout.EndHorizontal();

            scenesChanged = false;

            var configs = workerConfiguration.WorkerBuildConfigurations;

            foreach (var workerConfig in configs)
            {
                if (!DrawWorkerConfiguration(workerConfig))
                {
                    configs.Remove(workerConfig);
                    break;
                }
            }

            if (scenesChanged)
            {
                scenesChanged = false;
                EditorApplication.delayCall += workerConfiguration.UpdateEditorScenesForBuild;
            }
        }
        private void DrawEnvironmentInspector(BuildEnvironment environment,
                                              WorkerBuildConfiguration configurationForWorker)
        {
            using (IndentLevelScope(1))
            {
                EditorGUILayout.LabelField(environment.ToString());

                var environmentConfiguration =
                    configurationForWorker.GetEnvironmentConfig(environment);

                ConfigureBuildPlatforms(environmentConfiguration);
                ConfigureBuildOptions(environmentConfiguration);
            }
        }
        private SceneAsset[] GetScenesForWorker(WorkerPlatform workerPlatform)
        {
            WorkerBuildConfiguration configurationForWorker = null;

            if (WorkerBuildConfigurations != null)
            {
                configurationForWorker =
                    WorkerBuildConfigurations.FirstOrDefault(config => config.WorkerPlatform == workerPlatform);
            }

            return(configurationForWorker == null
                ? new SceneAsset[0]
                : configurationForWorker.ScenesForWorker);
        }
        private void ResetToDefault()
        {
            // Build default settings
            var client = new WorkerBuildConfiguration()
            {
                WorkerPlatform  = WorkerPlatform.UnityClient,
                ScenesForWorker = AssetDatabase.FindAssets("t:Scene")
                                  .Select(AssetDatabase.GUIDToAssetPath)
                                  .Where(path => path.Contains(WorkerPlatform.UnityClient.ToString()))
                                  .Select(AssetDatabase.LoadAssetAtPath <SceneAsset>).ToArray(),
                LocalBuildConfig = new BuildEnvironmentConfig()
                {
                    BuildPlatforms = SpatialBuildPlatforms.Current,
                    BuildOptions   = BuildOptions.Development
                },
                CloudBuildConfig = new BuildEnvironmentConfig()
                {
                    BuildPlatforms = SpatialBuildPlatforms.Current
                }
            };

            var worker = new WorkerBuildConfiguration()
            {
                WorkerPlatform  = WorkerPlatform.UnityGameLogic,
                ScenesForWorker = AssetDatabase.FindAssets("t:Scene")
                                  .Select(AssetDatabase.GUIDToAssetPath)
                                  .Where(path => path.Contains(WorkerPlatform.UnityGameLogic.ToString()))
                                  .Select(AssetDatabase.LoadAssetAtPath <SceneAsset>).ToArray(),
                LocalBuildConfig = new BuildEnvironmentConfig()
                {
                    BuildPlatforms = SpatialBuildPlatforms.Current,
                    BuildOptions   = BuildOptions.EnableHeadlessMode
                },
                CloudBuildConfig = new BuildEnvironmentConfig()
                {
                    BuildPlatforms = SpatialBuildPlatforms.Linux,
                    BuildOptions   = BuildOptions.EnableHeadlessMode
                }
            };

            WorkerBuildConfigurations = new List <WorkerBuildConfiguration>
            {
                client,
                worker
            };

            isInitialised = true;
        }
        private void DrawEmptySceneBox(WorkerBuildConfiguration configurationForWorker, EventType currentEventType)
        {
            // Allow dropping to form a new list.
            EditorGUILayout.HelpBox("Drop scenes here", MessageType.Info);
            var rect = GUILayoutUtility.GetLastRect();

            if (rect.Contains(Event.current.mousePosition))
            {
                switch (currentEventType)
                {
                case EventType.DragPerform:
                    RecordUndo("Configure scenes for worker");

                    configurationForWorker.ScenesForWorker = DragAndDrop.objectReferences
                                                             .OfType <SceneAsset>().ToList();

                    DragAndDrop.AcceptDrag();
                    Event.current.Use();
                    Repaint();

                    break;

                case EventType.DragUpdated:
                    if (DragAndDrop.objectReferences.OfType <SceneAsset>().Any())
                    {
                        DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
                        Event.current.Use();
                        Repaint();
                    }

                    break;

                case EventType.Repaint:
                    if (DragAndDrop.objectReferences.OfType <SceneAsset>().Any())
                    {
                        EditorGUI.DrawRect(rect, new Color(0, 0.8f, 0, 0.25f));
                    }

                    break;
                }
            }
        }
        private static string GetBuildConfigurationStateIcon(WorkerBuildConfiguration configuration)
        {
            var isWarning = configuration.CloudBuildConfig.BuildTargets.Any(IsBuildTargetWarning) ||
                            configuration.LocalBuildConfig.BuildTargets.Any(IsBuildTargetWarning);

            var isError = configuration.CloudBuildConfig.BuildTargets.Any(IsBuildTargetError) ||
                          configuration.LocalBuildConfig.BuildTargets.Any(IsBuildTargetError);

            if (isError)
            {
                return(BuildConfigEditorStyle.BuiltInErrorIcon);
            }

            if (isWarning)
            {
                return(BuildConfigEditorStyle.BuiltInWarningIcon);
            }

            return(null);
        }
        private void HandleObjectSelectorUpdated(WorkerBuildConfiguration configurationForWorker, int pickerId)
        {
            if (Event.current.commandName == "ObjectSelectorClosed" &&
                EditorGUIUtility.GetObjectPickerControlID() == pickerId)
            {
                var scene = (SceneAsset)EditorGUIUtility.GetObjectPickerObject();

                if (scene == null)
                {
                    return;
                }

                if (configurationForWorker.ScenesForWorker.All(a => a.name != scene.name))
                {
                    RecordUndo("Configure scenes for worker");

                    configurationForWorker.ScenesForWorker.Add(scene);
                }
            }
        }
        public override void OnInspectorGUI()
        {
            if (style == null)
            {
                style = new BuildConfigEditorStyle();
            }

            if (allWorkers == null)
            {
                try
                {
                    var guids    = AssetDatabase.FindAssets("WorkerMenu");
                    var textFile = guids.Select(AssetDatabase.GUIDToAssetPath)
                                   .FirstOrDefault(f => Path.GetExtension(f) == ".txt");
                    if (string.IsNullOrEmpty(textFile))
                    {
                        throw new Exception("Could not find WorkerMenu.txt - you may need to regenerate code.");
                    }

                    allWorkers = File.ReadAllLines(Path.Combine(Application.dataPath, "..", textFile));
                }
                catch (Exception e)
                {
                    allWorkers = new string[0];
                    Debug.LogException(e);
                }
            }

            // Clean up state when drag events end.
            if (sourceDragState != null && Event.current.type == EventType.DragExited)
            {
                sourceDragState.SourceItemIndex = -1;
                sourceDragState = null;
                Repaint();
            }

            var workerConfiguration = (BuildConfig)target;

            BuildConfigEditorStyle.DrawHorizontalLine();

            var configs = workerConfiguration.WorkerBuildConfigurations;

            for (var index = 0; index < configs.Count; index++)
            {
                var workerConfig = configs[index];
                if (!DrawWorkerConfiguration(workerConfig))
                {
                    RecordUndo($"Remove '{workerConfig.WorkerType}'");

                    configs.RemoveAt(index);
                    index--;
                }

                BuildConfigEditorStyle.DrawHorizontalLine();
            }

            using (new EditorGUI.DisabledScope(workerConfiguration.WorkerBuildConfigurations.Count ==
                                               allWorkers.Length))
                using (new GUILayout.HorizontalScope())
                {
                    GUILayout.FlexibleSpace();
                    if (GUILayout.Button("Add new worker type"))
                    {
                        workerChooser = new WorkerChoicePopup(addWorkerButtonRect, workerConfiguration, allWorkers);
                        PopupWindow.Show(addWorkerButtonRect, workerChooser);
                    }

                    if (Event.current.type == EventType.Repaint)
                    {
                        addWorkerButtonRect = GUILayoutUtility.GetLastRect();

                        // Only add the new worker during the Repaint phase - otherwise you'll see errors due to adding new content at the wrong point of the GUI lifecycle.
                        if (workerChooser != null && workerChooser.Choice != -1)
                        {
                            RecordUndo("Add '{Choices[i]}'");

                            var config = new WorkerBuildConfiguration
                            {
                                WorkerType       = workerChooser.Choices[workerChooser.Choice],
                                LocalBuildConfig = new BuildEnvironmentConfig(WorkerBuildData.LocalBuildTargets,
                                                                              WorkerBuildData.GetCurrentBuildTargetConfig()),
                                CloudBuildConfig = new BuildEnvironmentConfig(WorkerBuildData.AllBuildTargets,
                                                                              WorkerBuildData.GetLinuxBuildTargetConfig())
                            };
                            workerConfiguration.WorkerBuildConfigurations.Add(config);
                            workerChooser = null;
                        }
                    }

                    GUILayout.FlexibleSpace();
                }

            if (invalidateCachedContent > 0)
            {
                invalidateCachedContent--;
            }
        }
        private void DrawEnvironmentInspector(BuildEnvironment environment,
                                              WorkerBuildConfiguration configurationForWorker)
        {
            var environmentName = environment.ToString();

            var environmentConfiguration =
                configurationForWorker.GetEnvironmentConfig(environment);

            var hash         = configurationForWorker.WorkerType.GetHashCode() ^ environment.GetHashCode() ^ typeof(FoldoutState).GetHashCode();
            var foldoutState = stateManager.GetStateObjectOrDefault <FoldoutState>(hash);

            if (foldoutState.Content == null || invalidateCachedContent > 0)
            {
                var builtTargets = string.Join(",",
                                               environmentConfiguration.BuildTargets.Where(t => t.Enabled).Select(t => t.Label));

                foldoutState.Content = new GUIContent($"{environmentName} Build Options ({builtTargets})");

                if (environmentConfiguration.BuildTargets.Any(IsBuildTargetError))
                {
                    foldoutState.Icon =
                        new GUIContent(EditorGUIUtility.IconContent(BuildConfigEditorStyle.BuiltInErrorIcon))
                    {
                        tooltip = "Missing build support for one or more build targets."
                    };
                }
                else if (environmentConfiguration.BuildTargets.Any(IsBuildTargetWarning))
                {
                    foldoutState.Icon =
                        new GUIContent(EditorGUIUtility.IconContent(BuildConfigEditorStyle.BuiltInWarningIcon))
                    {
                        tooltip = "Missing build support for one or more build targets."
                    };
                }
                else
                {
                    foldoutState.Icon = null;
                }
            }

            using (new GUILayout.HorizontalScope())
            {
                foldoutState.Expanded =
                    EditorGUILayout.Foldout(foldoutState.Expanded, foldoutState.Content, true);

                GUILayout.FlexibleSpace();
                if (foldoutState.Icon != null)
                {
                    GUILayout.Label(foldoutState.Icon);
                    GUILayout.Space(28);
                }
            }

            using (var check = new EditorGUI.ChangeCheckScope())
            {
                if (foldoutState.Expanded)
                {
                    DrawBuildTargets(environmentConfiguration, hash);
                }

                if (check.changed)
                {
                    // Re-evaluate heading.
                    foldoutState.Content = null;
                }
            }
        }
        private void DrawScenesInspectorForWorker(WorkerBuildConfiguration configurationForWorker)
        {
            DragAndDropInfo dragState;
            var             currentEventType = Event.current.type;

            using (new EditorGUIUtility.IconSizeScope(SmallIconSize))
                using (new EditorGUILayout.HorizontalScope())
                {
                    EditorGUILayout.LabelField("Scenes to include (in order)");
                    var workerControlId = configurationForWorker.WorkerType.GetHashCode() ^ typeof(DragAndDrop).GetHashCode();
                    dragState = stateManager.GetStateObjectOrDefault <DragAndDropInfo>(workerControlId);

                    GUILayout.FlexibleSpace();
                    if (GUILayout.Button(style.AddSceneButtonContents, EditorStyles.miniButton))
                    {
                        EditorGUIUtility.ShowObjectPicker <SceneAsset>(null, false, "t:Scene",
                                                                       workerControlId);
                    }

                    HandleObjectSelectorUpdated(configurationForWorker, workerControlId);
                }

            if (configurationForWorker.ScenesForWorker.Count == 0)
            {
                DrawEmptySceneBox(configurationForWorker, currentEventType);
            }
            else
            {
                int?indexToRemove   = null;
                int?targetItemIndex = null;

                if (currentEventType == EventType.Repaint)
                {
                    dragState.AllItemsRect = Rect.zero;
                    dragState.ItemHeight   = 0;
                }

                for (var i = 0; i < configurationForWorker.ScenesForWorker.Count; i++)
                {
                    var item = configurationForWorker.ScenesForWorker[i];

                    using (new EditorGUILayout.HorizontalScope())
                    {
                        GUILayout.Space(EditorGUI.indentLevel * 16.0f);
                        indexToRemove = DrawSceneItem(i, dragState, item, currentEventType, indexToRemove);

                        var hitRect = new Rect(dragState.AllItemsRect.xMin,
                                               dragState.AllItemsRect.yMin + i * (dragState.ItemHeight +
                                                                                  EditorGUIUtility.standardVerticalSpacing) -
                                               EditorGUIUtility.standardVerticalSpacing / 2.0f, dragState.AllItemsRect.width,
                                               dragState.ItemHeight + EditorGUIUtility.standardVerticalSpacing / 2.0f);

                        if (hitRect.Contains(Event.current.mousePosition))
                        {
                            if (i != dragState.SourceItemIndex)
                            {
                                targetItemIndex = Event.current.mousePosition.y >
                                                  hitRect.yMin + hitRect.height / 2
                                        ? i + 1
                                        : i;
                            }

                            TrackDragDrop(configurationForWorker, currentEventType, item, dragState, i);
                        }
                    }
                }

                List <SceneAsset> list = null;

                if (indexToRemove.HasValue)
                {
                    list = configurationForWorker.ScenesForWorker.ToList();
                    list.RemoveAt(indexToRemove.Value);
                }
                else if (targetItemIndex.HasValue)
                {
                    list = configurationForWorker.ScenesForWorker.ToList();

                    switch (currentEventType)
                    {
                    case EventType.DragPerform:

                        // The drag event is coming from outside of this list, for example:
                        // The asset browser or another worker's scene list.
                        // If the incoming drag contains a duplicate of the item, it's already been rejected in the hit detection code,
                        // so there's no need to validate it again here.
                        if (dragState.SourceItemIndex == -1)
                        {
                            if (targetItemIndex >= list.Count)
                            {
                                list.AddRange(DragAndDrop.objectReferences.OfType <SceneAsset>());
                            }
                            else
                            {
                                list.InsertRange(targetItemIndex.Value,
                                                 DragAndDrop.objectReferences.OfType <SceneAsset>());
                            }
                        }
                        else
                        {
                            var movingItem = list[dragState.SourceItemIndex];

                            if (targetItemIndex >= list.Count)
                            {
                                list.RemoveAt(dragState.SourceItemIndex);
                                list.Add(movingItem);
                            }
                            else
                            {
                                list.RemoveAt(dragState.SourceItemIndex);
                                if (targetItemIndex >= dragState.SourceItemIndex)
                                {
                                    list.Insert(targetItemIndex.Value - 1, movingItem);
                                }
                                else
                                {
                                    list.Insert(targetItemIndex.Value, movingItem);
                                }
                            }
                        }

                        break;

                    case EventType.Repaint:
                        if (DragAndDrop.visualMode == DragAndDropVisualMode.Copy)
                        {
                            var newRect = new Rect(dragState.AllItemsRect.xMin,
                                                   dragState.AllItemsRect.yMin + targetItemIndex.Value *
                                                   dragState.AllItemsRect.height / list.Count,
                                                   dragState.AllItemsRect.width, 2);
                            EditorGUI.DrawRect(newRect, new Color(0.4f, 0.4f, 0.4f, 1));
                        }
                        else if (DragAndDrop.visualMode == DragAndDropVisualMode.Rejected)
                        {
                            var newRect = new Rect(dragState.AllItemsRect);
                            EditorGUI.DrawRect(newRect, new Color(0.8f, 0.0f, 0.0f, 0.25f));
                        }

                        break;
                    }
                }

                if (list != null)
                {
                    RecordUndo("Configure scenes for worker");

                    configurationForWorker.ScenesForWorker = list;
                }
            }
        }
        private bool DrawWorkerConfiguration(WorkerBuildConfiguration configurationForWorker)
        {
            var workerType = configurationForWorker.WorkerType;

            var foldoutState = stateManager.GetStateObjectOrDefault <FoldoutState>(configurationForWorker.WorkerType.GetHashCode());

            using (new EditorGUILayout.HorizontalScope())
            {
                if (foldoutState.Content == null || invalidateCachedContent > 0)
                {
                    var buildStateIcon = GetBuildConfigurationStateIcon(configurationForWorker);
                    if (buildStateIcon != null)
                    {
                        foldoutState.Icon =
                            new GUIContent(EditorGUIUtility.IconContent(buildStateIcon))
                        {
                            tooltip = "Missing build support for one or more build targets."
                        };
                    }
                    else if (configurationForWorker.CloudBuildConfig.BuildTargets.Any(NeedsAndroidSdk) ||
                             configurationForWorker.LocalBuildConfig.BuildTargets.Any(NeedsAndroidSdk))
                    {
                        foldoutState.Icon =
                            new GUIContent(EditorGUIUtility.IconContent(BuildConfigEditorStyle.BuiltInErrorIcon))
                        {
                            tooltip = "Missing Android SDK installation. Go to Preferences > External Tools to set it up."
                        };
                    }
                    else
                    {
                        foldoutState.Icon = null;
                    }

                    foldoutState.Content = new GUIContent(workerType);
                }

                foldoutState.Expanded = EditorGUILayout.Foldout(foldoutState.Expanded, foldoutState.Content, true);

                GUILayout.FlexibleSpace();
                using (new EditorGUIUtility.IconSizeScope(SmallIconSize))
                {
                    if (foldoutState.Icon != null)
                    {
                        using (new EditorGUIUtility.IconSizeScope(Vector2.zero))
                        {
                            GUILayout.Label(foldoutState.Icon);
                        }
                    }

                    if (GUILayout.Button(style.RemoveWorkerTypeButtonContents, EditorStyles.miniButton))
                    {
                        return(false);
                    }
                }
            }

            using (var check = new EditorGUI.ChangeCheckScope())
                using (new EditorGUI.IndentLevelScope())
                {
                    if (foldoutState.Expanded)
                    {
                        DrawScenesInspectorForWorker(configurationForWorker);
                        EditorGUILayout.Space();
                        DrawEnvironmentInspectorForWorker(configurationForWorker);
                    }

                    if (check.changed)
                    {
                        // Re-evaluate heading.
                        foldoutState.Content = null;
                    }
                }

            return(true);
        }