A game framework written with osu! in mind.
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
2// See the LICENCE file in the repository root for full licence text.
3
4using System.Collections.Generic;
5using System.Linq;
6using NUnit.Framework;
7using osu.Framework.Graphics;
8using osu.Framework.Graphics.Containers;
9using osu.Framework.Graphics.UserInterface;
10using osuTK.Input;
11
12namespace osu.Framework.Testing
13{
14 /// <summary>
15 /// A test scene that provides a set of helper functions and structures for testing a <see cref="Menu"/>.
16 /// </summary>
17 public abstract class MenuTestScene : ManualInputManagerTestScene
18 {
19 protected MenuStructure Menus;
20
21 [SetUp]
22 public new void SetUp() => Schedule(() =>
23 {
24 Menu menu;
25 Child = new Container
26 {
27 RelativeSizeAxes = Axes.Both,
28 Child = menu = CreateMenu(),
29 };
30
31 Menus = new MenuStructure(menu);
32 });
33
34 /// <summary>
35 /// Click an item in a menu.
36 /// </summary>
37 /// <param name="menuIndex">The level of menu our click targets.</param>
38 /// <param name="itemIndex">The item to click in the menu.</param>
39 protected void ClickItem(int menuIndex, int itemIndex)
40 {
41 InputManager.MoveMouseTo(Menus.GetSubStructure(menuIndex).GetMenuItems()[itemIndex]);
42 InputManager.Click(MouseButton.Left);
43 }
44
45 protected abstract Menu CreateMenu();
46
47 /// <summary>
48 /// Helper class used to retrieve various internal properties/items from a <see cref="Menu"/>.
49 /// </summary>
50 protected class MenuStructure
51 {
52 private readonly Menu menu;
53
54 public MenuStructure(Menu menu)
55 {
56 this.menu = menu;
57 }
58
59 /// <summary>
60 /// Retrieves the <see cref="Menu.DrawableMenuItem"/>s of the <see cref="Menu"/> represented by this <see cref="MenuStructure"/>.
61 /// </summary>
62 public IReadOnlyList<Drawable> GetMenuItems() => menu.ChildrenOfType<FillFlowContainer<Menu.DrawableMenuItem>>().First().Children;
63
64 /// <summary>
65 /// Finds the <see cref="Menu.DrawableMenuItem"/> index in the <see cref="Menu"/> represented by this <see cref="MenuStructure"/> that
66 /// has <see cref="Menu.DrawableMenuItem.State"/> set to <see cref="MenuItemState.Selected"/>.
67 /// </summary>
68 public int GetSelectedIndex()
69 {
70 var items = GetMenuItems();
71
72 for (int i = 0; i < items.Count; i++)
73 {
74 var state = (MenuItemState)(items[i]?.GetType().GetProperty("State")?.GetValue(items[i]) ?? MenuItemState.NotSelected);
75 if (state == MenuItemState.Selected)
76 return i;
77 }
78
79 return -1;
80 }
81
82 /// <summary>
83 /// Sets the <see cref="Menu.DrawableMenuItem"/> <see cref="Menu.DrawableMenuItem.State"/> at the specified index to a specified state.
84 /// </summary>
85 /// <param name="index">The index of the <see cref="Menu.DrawableMenuItem"/> to set the state of.</param>
86 /// <param name="state">The state to be set.</param>
87 public void SetSelectedState(int index, MenuItemState state)
88 {
89 var item = GetMenuItems()[index];
90 item.GetType().GetProperty("State")?.SetValue(item, state);
91 }
92
93 /// <summary>
94 /// Retrieves the sub-<see cref="Menu"/> at an index-offset from the current <see cref="Menu"/>.
95 /// </summary>
96 /// <param name="index">The sub-<see cref="Menu"/> index. An index of 0 is the <see cref="Menu"/> represented by this <see cref="MenuStructure"/>.</param>
97 public Menu GetSubMenu(int index)
98 {
99 var currentMenu = menu;
100
101 for (int i = 0; i < index; i++)
102 {
103 if (currentMenu == null)
104 break;
105
106 var container = (CompositeDrawable)currentMenu.InternalChildren[1];
107
108 // ReSharper disable once ArrangeRedundantParentheses
109 // Broken resharper inspection (https://youtrack.jetbrains.com/issue/RIDER-19843)
110 currentMenu = (container.InternalChildren.Count > 0 ? container.InternalChildren[0] : null) as Menu;
111 }
112
113 return currentMenu;
114 }
115
116 /// <summary>
117 /// Generates a new <see cref="MenuStructure"/> for the a sub-<see cref="Menu"/>.
118 /// </summary>
119 /// <param name="index">The sub-<see cref="Menu"/> index to generate the <see cref="MenuStructure"/> for. An index of 0 is the <see cref="Menu"/> represented by this <see cref="MenuStructure"/>.</param>
120 public MenuStructure GetSubStructure(int index) => new MenuStructure(GetSubMenu(index));
121 }
122 }
123}