SortMenuItemsByName always sorts folders above everything else

Issue #692 open
Joshua Sanders created an issue

The following code results in menu items being sorted incorrectly as described in the image below it:

  protected override OdinMenuTree BuildMenuTree() {
    var tree = new OdinMenuTree();

    createNewHeroModel = new CreateNewHeroModel();

    // Create any folder with children
    tree.AddAllAssetsAtPath("Hero Models", s_heroModelPath, typeof(HeroModel));

    // Create any set of non-folder menu items
    tree.Add("Create New", createNewHeroModel);
    tree.Add("A", createNewHeroModel);
    tree.Add("B", createNewHeroModel);
    tree.Add("C", createNewHeroModel); 

    // This will sort the non-folder menu items below the folder menu item,
    // regardless of what flag is passed.
    tree.SortMenuItemsByName(false);

    return tree;
  }

I did some digging into this myself and I have found your bug. The API of course is:

    public static IEnumerable<OdinMenuItem> SortMenuItemsByName(
      this OdinMenuTree tree,
      bool placeFoldersFirst = true)

The bug is that placeFoldersFirst is a completely unused flag lol. So that functionality is simply not implemented in the code anywhere. Here is the implementation:

    /// <summary>
    /// Sorts the collection of menu items recursively by name with respects to numbers.
    /// </summary>
    public static IEnumerable<OdinMenuItem> SortMenuItemsByName(
      this IEnumerable<OdinMenuItem> menuItems,
      bool placeFoldersFirst = true)
    {
      Comparison<OdinMenuItem> comparer = (Comparison<OdinMenuItem>) ((a, b) =>
      {
        if (a.ChildMenuItems.Count > 0 && b.ChildMenuItems.Count == 0)
          return -1;
        if (b.ChildMenuItems.Count > 0 && a.ChildMenuItems.Count == 0)
          return 1;
        string smartName1 = a.SmartName;
        string smartName2 = b.SmartName;
        if (string.Compare(Regex.Replace(smartName1, "[\\d-]", string.Empty), Regex.Replace(smartName2, "[\\d-]", string.Empty)) == 0)
        {
          string input1 = smartName1.Substring(smartName1.LastIndexOf('/') + 1);
          string input2 = smartName2.Substring(smartName2.LastIndexOf('/') + 1);
          smartName1 = Regex.Match(input1, "\\d+").Value;
          smartName2 = Regex.Match(input2, "\\d+").Value;
          double result1;
          double result2;
          if (double.TryParse(smartName1, out result1) && double.TryParse(smartName2, out result2))
            return result1.CompareTo(result2);
        }
        return string.Compare(smartName1, smartName2);
      });
      IEnumerable<OdinMenuItem> odinMenuItems = menuItems.ForEach<OdinMenuItem>((Action<OdinMenuItem>) (x => x.ChildMenuItems.Sort(comparer)));
      OdinMenuItem odinMenuItem = menuItems.FirstOrDefault<OdinMenuItem>();
      if (odinMenuItem != null && odinMenuItem.MenuTree != null)
        odinMenuItem.MenuTree.MarkDirty();
      return odinMenuItems;
    }

So uh.. yeah that’s a bummer. Fix please so my menu can be sorted properly!! 🙂

OS: WIn10
Odin verion: 2.1.3.0
Unity Version: 2020.1.2f1
EditorOnlyMode: false

Comments (8)

  1. Log in to comment