public void OnGUI()
    {
        if (requestFocus && Event.current.type == EventType.Layout)
        {
            requestFocus = false;
            EditorWindow.FocusWindowIfItsOpen <UTAutomationPlanPopup>();
        }

        EditorGUILayout.LabelField(caption);
        EditorGUILayout.Space();

        GUI.SetNextControlName("AutomationPlanList");
        listData = CUListControl.SelectionList <UTAutomationPlan>(listData, currentItems, itemRenderer, "Plans", GUILayout.ExpandHeight(true));
        if (GUI.GetNameOfFocusedControl() == string.Empty)
        {
            // move focus to the textfield after the dialog is visible
            GUI.FocusControl("AutomationPlanList");
        }
        EditorGUILayout.Space();

        EditorGUILayout.BeginHorizontal();

        EditorGUI.BeginChangeCheck();
        showAllItems = GUILayout.Toggle(showAllItems, "Show hidden plans");
        if (EditorGUI.EndChangeCheck())
        {
            currentItems = showAllItems ? allItems : visibleItems;
            listData.ClearSelection();
            if (currentItems.Count > 0)
            {
                listData[0] = true;
            }
        }

        GUILayout.FlexibleSpace();
        GUI.enabled = !listData.Empty;
        if (GUILayout.Button("Ok"))
        {
            Apply(listData.First);
        }
        GUI.enabled = true;
        if (GUILayout.Button("Cancel"))
        {
            CloseDialog();
        }
        EditorGUILayout.EndHorizontal();
        EditorGUILayout.Space();

        if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Escape)
        {
            CloseDialog();
        }
        wasGuiPaintedAtLeastOnce = true;
    }
Exemple #2
0
    private void DrawActions()
    {
        GUILayout.Label("Settings", UTEditorResources.TitleStyle);
        EditorGUILayout.Space();
        EditorGUI.BeginChangeCheck();

        EditorGUILayout.BeginHorizontal();
        showHidden = EditorGUILayout.Toggle("Show hidden plans", showHidden);
        debugMode  = EditorGUILayout.Toggle("Debug mode", debugMode);
        EditorGUILayout.EndHorizontal();

        EditorGUILayout.BeginHorizontal();
        clearConsole             = EditorGUILayout.Toggle("Clear console", clearConsole);
        returnToPlanListAfterRun = EditorGUILayout.Toggle("Return to plan list after run", returnToPlanListAfterRun);
        EditorGUILayout.EndHorizontal();

        EditorGUILayout.Space();

        if (EditorGUI.EndChangeCheck())
        {
            UTPreferences.DebugMode = debugMode;
            UTPreferences.ShowHiddenPlansInRunner        = showHidden;
            UTPreferences.ClearConsoleBeforeStart        = clearConsole;
            UTPreferences.ReturnToPlanListAfterPlanIsRun = returnToPlanListAfterRun;
        }

        var planList = showHidden ? plans : visiblePlans;

        listData = CUListControl.SelectionList(listData, planList, renderer, "Plans");

        EditorGUILayout.Space();
        EditorGUILayout.BeginHorizontal();
        GUILayout.FlexibleSpace();

        GUI.enabled = listData.Selection.Count > 0 && !UTomateRunner.Instance.IsRunning;
        if (GUILayout.Button("Run Plan"))
        {
            currentWindow = 1;
            RunSelected();
            // some actions will reload the project and not exiting the gui here
            // will yield all kinds of funky exceptions because the gui is trying to draw on a state that
            // no longer exists.
            GUIUtility.ExitGUI();
        }
        GUI.enabled = true;
        EditorGUILayout.EndHorizontal();
        EditorGUILayout.Space();
    }
Exemple #3
0
    protected void OpenScene(CUListData sceneListData)            // 打开这个场景
    {
        IList <string> selectedScenes = sceneListData.GetSelectedItems(scenes);

        if (selectedScenes.Count != 1)
        {
            return;
        }
        string sceneName = selectedScenes[0];

        if (sceneLookup.ContainsKey(sceneName))
        {
            string assetPath    = sceneLookup[sceneName];
            Scene  currentScene = SceneManager.GetActiveScene(); // 当前场景

            if (currentScene.path.Equals(assetPath.Replace('\\', '/')))
            {
                MyLog.Green("当前就是这个场景,打开什么");
                return;
            }
            EditorSceneManager.SaveScene(currentScene);
            EditorSceneManager.OpenScene(assetPath);
        }
    }
    public void OnGUI()
    {
        if (requestFocus && Event.current.type == EventType.Layout) {
            requestFocus = false;
            EditorWindow.FocusWindowIfItsOpen<UTAutomationPlanPopup>();
        }

        EditorGUILayout.LabelField(caption);
        EditorGUILayout.Space();

        GUI.SetNextControlName("AutomationPlanList");
        listData = CUListControl.SelectionList<UTAutomationPlan>(listData, currentItems, itemRenderer, "Plans", GUILayout.ExpandHeight(true));
        if (GUI.GetNameOfFocusedControl() == string.Empty) {
            // move focus to the textfield after the dialog is visible
            GUI.FocusControl("AutomationPlanList");
        }
        EditorGUILayout.Space();

        EditorGUILayout.BeginHorizontal();

        EditorGUI.BeginChangeCheck ();
        showAllItems = GUILayout.Toggle(showAllItems, "Show hidden plans");
        if (EditorGUI.EndChangeCheck ()) {
            currentItems = showAllItems ? allItems : visibleItems;
            listData.ClearSelection();
            if (currentItems.Count > 0) {
                listData[0] = true;
            }
        }

        GUILayout.FlexibleSpace();
        GUI.enabled = !listData.Empty;
        if (GUILayout.Button("Ok")) {
            Apply(listData.First);
        }
        GUI.enabled = true;
        if (GUILayout.Button("Cancel")) {
            CloseDialog();
        }
        EditorGUILayout.EndHorizontal();
        EditorGUILayout.Space();

        if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Escape) {
            CloseDialog();
        }
        wasGuiPaintedAtLeastOnce = true;
    }
    private void DrawActions()
    {
        GUILayout.Label ("Settings", UTEditorResources.TitleStyle);
        EditorGUILayout.Space ();
        EditorGUI.BeginChangeCheck ();

        EditorGUILayout.BeginHorizontal ();
        showHidden = EditorGUILayout.Toggle ("Show hidden plans", showHidden);
        debugMode = EditorGUILayout.Toggle ("Debug mode", debugMode);
        EditorGUILayout.EndHorizontal ();

        EditorGUILayout.BeginHorizontal ();
        clearConsole = EditorGUILayout.Toggle ("Clear console", clearConsole);
        returnToPlanListAfterRun = EditorGUILayout.Toggle ("Return to plan list after run", returnToPlanListAfterRun);
        EditorGUILayout.EndHorizontal ();

        EditorGUILayout.Space ();

        if (EditorGUI.EndChangeCheck ()) {
            UTPreferences.DebugMode = debugMode;
            UTPreferences.ShowHiddenPlansInRunner = showHidden;
            UTPreferences.ClearConsoleBeforeStart = clearConsole;
            UTPreferences.ReturnToPlanListAfterPlanIsRun = returnToPlanListAfterRun;
        }

        var planList = showHidden ? plans : visiblePlans;
        listData = CUListControl.SelectionList (listData, planList, renderer, "Plans");

        EditorGUILayout.Space ();
        EditorGUILayout.BeginHorizontal ();
        GUILayout.FlexibleSpace ();

        GUI.enabled = listData.Selection.Count > 0 && !UTomateRunner.Instance.IsRunning;
        if (GUILayout.Button ("Run Plan")) {
            currentWindow = 1;
            RunSelected ();
            // some actions will reload the project and not exiting the gui here
            // will yield all kinds of funky exceptions because the gui is trying to draw on a state that
            // no longer exists.
            GUIUtility.ExitGUI ();

        }
        GUI.enabled = true;
        EditorGUILayout.EndHorizontal ();
        EditorGUILayout.Space ();
    }
Exemple #6
0
    public static CUListData SelectionList <T>(CUListData listData, IList <T> items, CUItemRenderer <T> itemRenderer, string caption, params GUILayoutOption[] options)
    {
        int controlId          = GUIUtility.GetControlID(ControlHint, FocusType.Keyboard);
        ListControlState state = (ListControlState)GUIUtility.GetStateObject(typeof(ListControlState), controlId);

        if (listData == null)
        {
            listData = new CUListData();
        }

        GUILayout.BeginVertical(options);
        if (!String.IsNullOrEmpty(caption))
        {
            GUILayout.Label(caption, titleStyle);
        }

        listData.scrollPosition = EditorGUILayout.BeginScrollView(listData.scrollPosition, scrollViewStyle);

        int i = 0;
        int contextMenuItem = -1;

        EventType currentEvent = Event.current.GetTypeForControl(controlId);
        Vector2   mousePos     = Event.current.mousePosition;

        Rect elementRect = new Rect();

        float contentHeight = 0;

        Vector2[] itemRects = new Vector2[items.Count];

        // reset drag and drop state
        if (currentEvent == EventType.DragUpdated || currentEvent == EventType.DragPerform || currentEvent == EventType.DragExited)
        {
            state.isDropTarget = false;
        }

        // ignore mouse up event
        if (currentEvent == EventType.MouseDown || currentEvent == EventType.DragUpdated)
        {
            state.delayedClick = false;
        }

        foreach (T item in items)
        {
            elementRect    = GUILayoutUtility.GetRect(50f, itemRenderer.MeasureHeight(item));
            contentHeight += elementRect.height;
            itemRects[i]   = new Vector2(elementRect.y, elementRect.height);

            // avoid
            elementRect.x++;
            elementRect.width--;
            bool hover = elementRect.Contains(mousePos);
            // local event handling
            switch (currentEvent)
            {
            case EventType.ContextClick:
                if (!hover || GUIUtility.hotControl != 0 || !listData.IsContextMenuSupported)
                {
                    break;
                }

                listData.ClearSelection();
                listData[i]     = true;
                contextMenuItem = i;
                break;

            case EventType.MouseDown:
                // ignore context menu click
                if (!hover || GUIUtility.hotControl != 0 || Event.current.button != 0)
                {
                    break;
                }

                GUIUtility.keyboardControl = controlId;

                if (EditorGUI.actionKey)
                {
                    // toggle selection for the current item if it is currently not selected
                    if (!listData[i])
                    {
                        listData[i] = true;
                    }
                    else
                    {
                        // delay deselect until mouse up
                        state.delayedClick = true;
                    }
                }
                else if (Event.current.shift)
                {
                    if (listData.Empty)
                    {
                        // in case nothing is selected, just select the current item
                        listData[i] = true;
                    }
                    else
                    {
                        int index = listData.Pivot;
                        listData.ClearSelection();

                        int direction = index > i ? -1 : 1;
                        while (index != i + direction)
                        {
                            listData[index] = true;
                            index          += direction;
                        }
                    }
                }
                else
                {
                    // just a simple click, change selection to the current item
                    if (!listData[i])
                    {
                        listData.ClearSelection();
                        listData[i] = true;
                    }
                    else
                    {
                        // delay deselect until mouse up
                        state.delayedClick = true;
                    }
                }

                state.mouseDownPosition = mousePos;
                GUIUtility.hotControl   = controlId;
                Event.current.Use();
                break;

            case EventType.MouseUp:
                if (GUIUtility.hotControl != controlId)
                {
                    break;
                }

                if (hover)
                {
                    // now handly any delayed selection if necessary
                    if (state.delayedClick)
                    {
                        if (EditorGUI.actionKey)
                        {
                            listData[i] = false;
                        }
                        else if (!Event.current.shift)
                        {
                            listData.ClearSelection();
                            listData[i] = true;
                        }
                        state.delayedClick = false;
                        Event.current.Use();
                    }

                    if (Event.current.clickCount == 2 && listData.IsExecutionSupported)
                    {
                        listData.ClearSelection();
                        listData[i] = true;
                        if (listData.ExecutionListener.HandleExecution(listData.Selection))
                        {
                            Event.current.Use();
                        }
                    }
                }
                break;

            case EventType.MouseDrag:
                if (GUIUtility.hotControl != controlId)
                {
                    break;
                }

                if (hover && listData.IsDragSupported)
                {
                    if ((state.mouseDownPosition - mousePos).magnitude < 3)
                    {
                        // wait until the mouse was dragged a certain distance before starting the drag operation
                        break;
                    }
                    // mouse down event selects an item, so mouse is over an selected item.
                    // but its not important if the current item is an selected one as long as we are over an item
                    if (listData.DragSource.CanDrag(listData.Selection))
                    {
                        listData.DragSource.InitializeDrag(listData.Selection);
                        GUIUtility.hotControl = 0;
                        Event.current.Use();
                    }
                }
                break;

            case EventType.Repaint:
                itemRenderer.Arrange(item, i, listData[i], GUIUtility.keyboardControl == controlId, elementRect);
                if (DragAndDrop.activeControlID == controlId && state.isDropTarget && state.itemIndex == i)
                {
                    if (state.dropType == CUDropType.AtPosition)
                    {
                        GUI.Box(elementRect, "", CUListStyle.DefaultStyle.dropBeforeHighlight);
                    }
                    else
                    {
                        GUI.Box(elementRect, "", CUListStyle.DefaultStyle.dropIntoHighlight);
                    }
                }
                break;

            case EventType.DragUpdated:
            case EventType.DragPerform:
                if (hover && listData.IsDropSupported)
                {
                    bool canDropInto = listData.DropTarget.CanDrop(i, CUDropType.IntoItem);
                    // check if this is a drop between
                    if (IsDropBefore(elementRect, mousePos, canDropInto) && listData.DropTarget.CanDrop(i, CUDropType.AtPosition))
                    {
                        state.isDropTarget = true;
                        state.itemIndex    = i;
                        state.dropType     = CUDropType.AtPosition;
                        if (currentEvent == EventType.DragPerform)
                        {
                            listData.DropTarget.AcceptDrop(i, CUDropType.AtPosition);
                        }
                    }

                    // now check if this is a drop after
                    if (!state.isDropTarget && IsDropAfter(elementRect, mousePos, canDropInto) && listData.DropTarget.CanDrop(i + 1, CUDropType.AtPosition))
                    {
                        state.isDropTarget = true;
                        state.itemIndex    = i + 1;
                        state.dropType     = CUDropType.AtPosition;
                        if (currentEvent == EventType.DragPerform)
                        {
                            listData.DropTarget.AcceptDrop(i + 1, CUDropType.AtPosition);
                        }
                    }

                    // well its a drop into
                    if (!state.isDropTarget && canDropInto)
                    {
                        state.isDropTarget = true;
                        state.itemIndex    = i;
                        state.dropType     = CUDropType.IntoItem;
                        if (currentEvent == EventType.DragPerform)
                        {
                            listData.DropTarget.AcceptDrop(i, CUDropType.IntoItem);
                        }
                    }

                    if (state.isDropTarget)
                    {
                        DragAndDrop.activeControlID = controlId;
                        Event.current.Use();
                    }
                }
                break;
            }
            i++;
        }

        EditorGUILayout.EndScrollView();
        Rect scrollViewRect = GUILayoutUtility.GetLastRect();

        int scrollToItem = -1;

        // now check if we have to listen for drop events inside the empty part of the list
        switch (currentEvent)
        {
        case EventType.ContextClick:
            if (GUIUtility.hotControl != 0 || !scrollViewRect.Contains(Event.current.mousePosition) || !listData.IsContextMenuSupported)
            {
                break;
            }

            state.contextMenuItemIndex = contextMenuItem;
            Event.current.Use();
            break;

        case EventType.Repaint:
            // drop accepted but not by an item
            if (DragAndDrop.activeControlID == controlId && state.isDropTarget)
            {
                if (state.dropType == CUDropType.AtPosition && state.itemIndex == items.Count)
                {
                    if (items.Count == 0)
                    {
                        // no items, use the scrollviewrect
                        GUI.Box(scrollViewRect, "", CUListStyle.DefaultStyle.dropBeforeHighlight);
                    }
                    else
                    {
                        // add group for clipping otherwise higlight is sometimes visible outside the control
                        // beside that we dont have to convert the relative values of the element rect
                        GUI.BeginGroup(scrollViewRect);
                        GUI.Box(elementRect, "", CUListStyle.DefaultStyle.dropAfterHighlight);
                        GUI.EndGroup();
                    }
                }
                else if (state.dropType == CUDropType.IntoContainer)
                {
                    GUI.Box(scrollViewRect, "", CUListStyle.DefaultStyle.dropIntoHighlight);
                }
            }
            if (state.contextMenuItemIndex != -1)
            {
                listData.ContextMenuHandler.HandleContextMenu(state.contextMenuItemIndex);
                state.contextMenuItemIndex = -1;
            }

            break;

        case EventType.MouseUp:
            if (GUIUtility.hotControl != controlId)
            {
                break;
            }
            // release lock if required
            GUIUtility.hotControl = 0;
            Event.current.Use();
            break;

        case EventType.DragUpdated:
        case EventType.DragPerform:
            if (listData.IsDropSupported && !state.isDropTarget)
            {
                if (listData.DropTarget.CanDrop(items.Count, CUDropType.AtPosition))
                {
                    state.isDropTarget = true;
                    state.itemIndex    = items.Count;
                    state.dropType     = CUDropType.AtPosition;
                    if (currentEvent == EventType.DragPerform)
                    {
                        listData.DropTarget.AcceptDrop(items.Count, CUDropType.AtPosition);
                    }
                }
                else if (listData.DropTarget.CanDrop(-1, CUDropType.IntoContainer))
                {
                    state.isDropTarget = true;
                    state.itemIndex    = -1;
                    state.dropType     = CUDropType.IntoContainer;
                    if (currentEvent == EventType.DragPerform)
                    {
                        listData.DropTarget.AcceptDrop(-1, CUDropType.IntoContainer);
                    }
                }

                if (state.isDropTarget)
                {
                    DragAndDrop.activeControlID = controlId;
                    Event.current.Use();
                }
            }
            break;

        case EventType.KeyDown:
            if (GUIUtility.keyboardControl == controlId)
            {
                if (Event.current.keyCode != KeyCode.None)
                {
                    if (Event.current.keyCode == KeyCode.UpArrow)
                    {
                        // move selection up
                        int newSelection = Mathf.Max(listData.Pivot - 1, 0);
                        listData.ClearSelection();
                        listData[newSelection] = true;
                        scrollToItem           = newSelection;
                        Event.current.Use();
                    }
                    else if (Event.current.keyCode == KeyCode.DownArrow)
                    {
                        // move selection down
                        int newSelection = Mathf.Min(listData.Pivot + 1, items.Count - 1);
                        listData.ClearSelection();
                        listData[newSelection] = true;
                        scrollToItem           = newSelection;
                        Event.current.Use();
                    }
                    else
                    {
                        bool handled = false;
                        if ((Event.current.keyCode == KeyCode.Return || Event.current.keyCode == KeyCode.KeypadEnter) && listData.IsExecutionSupported)
                        {
                            if (listData.ExecutionListener.HandleExecution(listData.Selection))
                            {
                                handled = true;
                                Event.current.Use();
                            }
                        }

                        // only keycode ist available
                        if (!handled && listData.IsKeyInputSupported)
                        {
                            if (listData.KeyListener.HandleKeyEvent(Event.current.keyCode, Event.current.character, Event.current.modifiers))
                            {
                                Event.current.Use();
                            }
                        }
                    }
                }
                else
                {
                    // only character is available
                    if (listData.IsKeyInputSupported)
                    {
                        if (listData.KeyListener.HandleKeyEvent(Event.current.keyCode, Event.current.character, Event.current.modifiers))
                        {
                            Event.current.Use();
                        }
                    }
                }
            }
            break;
        }

        if (scrollToItem != -1)
        {
            // calculate the new offset (GUI.ScrollTo doesn't seem to work)
            // we have the position and height of the item we want to show, the height of the scrollview content,
            // the current scroll position and the height of the scrollview itself

            Rect viewport = new Rect(0, listData.scrollPosition.y, 100, scrollViewRect.height);             // real width is not important
            Rect itemRect = new Rect(10, itemRects[scrollToItem].x, 40, itemRects[scrollToItem].y);

            if (viewport.yMin > itemRect.yMin || viewport.yMax < itemRect.yMax)
            {
                // we have to change the scroll position
                if (viewport.yMin > itemRect.yMin)
                {
                    // show item at top edge
                    listData.scrollPosition.y = itemRect.yMin;
                }
                else
                {
                    listData.scrollPosition.y = itemRect.yMax - viewport.height;
                }
            }
        }

        GUILayout.EndVertical();
        return(listData);
    }
Exemple #7
0
 public static CUListData SelectionList(CUListData listData, IList <object> items, string caption, params GUILayoutOption[] options)
 {
     return(SelectionList(listData, items, defaultItemRenderer, caption, options));
 }
Exemple #8
0
 public static CUListData SelectionList <T>(CUListData listData, IList <T> items, CUItemRenderer <T> itemRenderer, params GUILayoutOption[] options)
 {
     return(SelectionList(listData, items, itemRenderer, null, options));
 }
    /// <summary>
    /// Draws the list of scenes in the inspector GUI.
    /// </summary>
    protected void SceneGUI()
    {
        sceneListHeight = CUResizableContainer.BeginVertical(sceneListHeight);
        sceneListData   = CUListControl.SelectionList(sceneListData, scenes, sceneRenderer, "Available Scenes");
        CUResizableContainer.EndVertical();

        GUI.enabled = !sceneListData.Empty;
        EditorGUILayout.BeginHorizontal();
        if (GUILayout.Button("Level"))
        {
            ChangeToLevel();
        }
        if (GUILayout.Button("Screen"))
        {
            ChangeToScreen();
        }
        if (GUILayout.Button("Ignore"))
        {
            ChangeToIgnore();
        }
        EditorGUILayout.EndHorizontal();

        GUI.enabled = !sceneListData.Empty && IsScreen(scenes[sceneListData.First]);
        EditorGUILayout.BeginHorizontal();
        if (GUILayout.Button("First Screen"))
        {
            ChangeToFirstScreen();
        }
        if (GUILayout.Button("After last Level"))
        {
            ChangeToFirstScreenAfterLevel();
        }

        EditorGUILayout.EndHorizontal();
        GUI.enabled = true;
        EditorGUILayout.Space();

        levelListHeight = CUResizableContainer.BeginVertical(levelListHeight);
        levelListData   = CUListControl.SelectionList(levelListData, Target.levels, levelRenderer, "Levels");
        CUResizableContainer.EndVertical();

        GUI.enabled = !levelListData.Empty;
        EditorGUILayout.BeginHorizontal();
        GUILayout.FlexibleSpace();
        if (GUILayout.Button("First"))
        {
            MoveToFirst();
        }
        if (GUILayout.Button("Up"))
        {
            MoveUp();
        }
        if (GUILayout.Button("Down"))
        {
            MoveDown();
        }
        if (GUILayout.Button("Last"))
        {
            MoveToLast();
        }
        EditorGUILayout.EndHorizontal();
        GUI.enabled = true;
    }
    /// <summary>
    /// Draws the list of scenes in the inspector GUI.
    /// </summary>
    protected void SceneGUI()
    {
        SMWorkflowActionType action = (SMWorkflowActionType)EditorGUILayout.EnumPopup(new GUIContent("Action after Group", ""), Target.actionAfterGroup);

        if (action != Target.actionAfterGroup)
        {
            ChangeToWorkflowAction(action);
        }
        GUILayout.Space(5);

        sceneListHeight = CUResizableContainer.BeginVertical(sceneListHeight);
        sceneListData   = CUListControl.SelectionList(sceneListData, scenes, sceneRenderer, "Available Scenes");
        CUResizableContainer.EndVertical();

        GUI.enabled = !sceneListData.Empty && SelectedGroup != null;
        EditorGUILayout.BeginHorizontal();
        if (GUILayout.Button("Level"))
        {
            ChangeToLevel();
        }
        GUI.enabled = !sceneListData.Empty;
        if (GUILayout.Button("Screen"))
        {
            ChangeToScreen();
        }
        if (GUILayout.Button("Ignore"))
        {
            ChangeToIgnore();
        }
        EditorGUILayout.EndHorizontal();

        GUI.enabled = !sceneListData.Empty && IsScreen(scenes[sceneListData.First]);
        EditorGUILayout.BeginHorizontal();
        if (GUILayout.Button("First Screen"))
        {
            ChangeToFirstScreen();
        }
        if (GUILayout.Button("After last Level"))
        {
            ChangeToFirstScreenAfterLevel();
        }

        GUI.enabled = GUI.enabled && Target.actionAfterGroup == SMWorkflowActionType.LoadScreen;
        if (GUILayout.Button("After Group"))
        {
            ChangeToFirstScreenAfterGroup();
        }

        EditorGUILayout.EndHorizontal();
        GUI.enabled = true;
        EditorGUILayout.Space();

        levelListHeight = CUResizableContainer.BeginVertical(levelListHeight);
        EditorGUILayout.BeginHorizontal();
        groupListWidth = CUResizableContainer.BeginHorizontal(groupListWidth);
        int lastGroup = groupListData.First;

        groupListData = CUListControl.SelectionList(groupListData, Target.groups, levelRenderer, "Groups");
        if (lastGroup != groupListData.First)
        {
            // group changed reset level selection
            levelListData.ClearSelection();
        }

        CUResizableContainer.EndHorizontal();
        GUI.enabled   = SelectedGroup != null;
        levelListData = CUListControl.SelectionList(levelListData, currentGroupLevels, levelRenderer, "Levels");
        GUI.enabled   = true;
        EditorGUILayout.EndHorizontal();
        CUResizableContainer.EndVertical();

        EditorGUILayout.BeginHorizontal();
        if (GUILayout.Button("+"))
        {
            AddGroup();
        }
        GUI.enabled = !groupListData.Empty && Target.groups.Length > 1;
        if (GUILayout.Button("-"))
        {
            RemoveGroup();
        }
        GUI.enabled = !groupListData.Empty;
        if (GUILayout.Button("Rename"))
        {
            RenameGroup();
        }
        GUI.enabled = true;
        GUILayout.FlexibleSpace();
        GUI.enabled = !levelListData.Empty;
        if (GUILayout.Button("First"))
        {
            MoveToFirst();
        }
        if (GUILayout.Button("Up"))
        {
            MoveUp();
        }
        if (GUILayout.Button("Down"))
        {
            MoveDown();
        }
        if (GUILayout.Button("Last"))
        {
            MoveToLast();
        }
        GUI.enabled = true;
        EditorGUILayout.EndHorizontal();
    }