I looked into this as well. Here’s one that works:
using System.Collections.Generic;
using System.Reflection;
using UnityEditor.IMGUI.Controls;
/// <summary>
/// Inherit from this instead of <see cref="AdvancedDropdown"/>
/// to have the ability to set it to a particular dropdown item.
/// See <see cref="SetSelectedItem"/>.
/// </summary>
public abstract class BetterAdvancedDropdown : AdvancedDropdown
{
readonly AdvancedDropdownState _state;
protected BetterAdvancedDropdown(AdvancedDropdownState state) : base(state)
{
_state = state;
}
public void SetSelectedItem(BetterAdvancedDropdownItem itemToSelect, bool invokeItemSelectedCallback = true)
{
_state.UpdateSelectionChain(itemToSelect);
if (invokeItemSelectedCallback)
{
ItemSelected(itemToSelect);
}
}
}
public static class AdvancedDropdownUtil
{
/// <summary>
/// <see cref="AdvancedDropdownState.SetSelectedIndex"/>
/// </summary>
static readonly MethodInfo SetSelectedIndexOfItem =
typeof(AdvancedDropdownState).GetMethod("SetSelectedIndex", BindingFlags.Instance | BindingFlags.NonPublic);
static readonly object[] SetSelectedIndexOfItemParams = new object[2];
static readonly FieldInfo ChildrenItems =
typeof(AdvancedDropdownItem).GetField("m_Children", BindingFlags.Instance | BindingFlags.NonPublic);
static void CallSetSelectedIndexOfItem(this AdvancedDropdownState state, BetterAdvancedDropdownItem itemToUpdate, int newSelectedIdx)
{
SetSelectedIndexOfItemParams[0] = itemToUpdate;
SetSelectedIndexOfItemParams[1] = newSelectedIdx;
SetSelectedIndexOfItem.Invoke(state, SetSelectedIndexOfItemParams);
}
/// <summary>
/// Will set the item's parent to select the aforementioned item.
/// This is extended to the item's entire ancestry (so the grandparent will select the parent, etc.) all the way until the root item.
/// This will cause the AdvancedDropdown to "jump" to the specified item the next time <see cref="AdvancedDropdown.Show"/> is called.
/// </summary>
/// <param name="state"></param>
/// <param name="itemToSelect"></param>
public static void UpdateSelectionChain(this AdvancedDropdownState state, BetterAdvancedDropdownItem itemToSelect)
{
var item = itemToSelect;
while (item != null && item.Parent != null)
{
var childrenOfParents = (List<AdvancedDropdownItem>) ChildrenItems.GetValue(item.Parent);
state.CallSetSelectedIndexOfItem(item.Parent, childrenOfParents.IndexOf(item));
item = item.Parent;
}
}
}
/// <summary>
/// Similar to <see cref="AdvancedDropdownItem"/>, except it has a link back to its parent.
/// Note that when you are writing <see cref="AdvancedDropdown.BuildRoot"/>, you have to be
/// the one to assign the <see cref="Parent"/>.
/// </summary>
public class BetterAdvancedDropdownItem : AdvancedDropdownItem
{
/// <summary>
/// The item that contains this one.
/// </summary>
public readonly BetterAdvancedDropdownItem Parent;
public BetterAdvancedDropdownItem(string name, BetterAdvancedDropdownItem parent) : base(name)
{
Parent = parent;
}
}
BetterAdvancedDropdown.SetSelectedItem()
will do the changing.
So to do all this, the only thing that actually needs to be done is to edit the AdvancedDropdownState
. It has a list of the state of each item (AdvancedDropdownState.AdvancedDropdownItemState
), the important part of which is
selectedIndex
, the field that indicates, for each item, which among its children has been selected.
AdvancedDropdownWindow
uses that list of item states to build its ViewStack, which is really just a Stack<AdvancedDropdownItem>
that, if you imagine the dropdown items to be folders, the ViewStack is the list of folders it’s currently in (like the current path in a file browser).
As long as the selectedIndex
in each item state is assigned (see AdvancedDropdownUtil.UpdateSelectionChain()
in that code I posted), AdvancedDropdownWindow
will create the correct ViewStack on its own, so it effectively “jumps” the selection to the item you indicated the next time AdvancedDropdown.Show()
is called.
The catch is that to do this, each AdvancedDropdownItem
now needs to know which its parent is. That’s the purpose of BetterAdvancedDropdownItem
. So in your AdvancedDropdown.BuildRoot()
, you have to assign the item’s parent.
EDIT: No need to assign the item’s index anymore.