MenuManagerTests.cpp 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <Tests/ActionManager/ActionManagerFixture.h>
  9. #include <AzToolsFramework/API/ToolsApplicationAPI.h>
  10. #include <QMenu>
  11. #include <QMenuBar>
  12. namespace UnitTest
  13. {
  14. TEST_F(ActionManagerFixture, RegisterMenu)
  15. {
  16. auto outcome = m_menuManagerInterface->RegisterMenu("o3de.menu.test", {});
  17. EXPECT_TRUE(outcome.IsSuccess());
  18. }
  19. TEST_F(ActionManagerFixture, RegisterMenuTwice)
  20. {
  21. m_menuManagerInterface->RegisterMenu("o3de.menu.test", {});
  22. auto outcome = m_menuManagerInterface->RegisterMenu("o3de.menu.test", {});
  23. EXPECT_FALSE(outcome.IsSuccess());
  24. }
  25. TEST_F(ActionManagerFixture, VerifyMenuIsRegistered)
  26. {
  27. auto outcome = m_menuManagerInterface->RegisterMenu("o3de.menu.test", {});
  28. EXPECT_TRUE(m_menuManagerInterface->IsMenuRegistered("o3de.menu.test"));
  29. }
  30. TEST_F(ActionManagerFixture, RegisterMenuBar)
  31. {
  32. auto outcome = m_menuManagerInterface->RegisterMenuBar("o3de.menubar.test", m_mainWindow);
  33. EXPECT_TRUE(outcome.IsSuccess());
  34. }
  35. TEST_F(ActionManagerFixture, RegisterMenuBarTwice)
  36. {
  37. m_menuManagerInterface->RegisterMenuBar("o3de.menubar.test", m_mainWindow);
  38. auto outcome = m_menuManagerInterface->RegisterMenuBar("o3de.menubar.test", m_mainWindow);
  39. EXPECT_FALSE(outcome.IsSuccess());
  40. }
  41. TEST_F(ActionManagerFixture, AddActionToUnregisteredMenu)
  42. {
  43. m_actionManagerInterface->RegisterActionContext("o3de.context.test", {});
  44. m_actionManagerInterface->RegisterAction("o3de.context.test", "o3de.action.test", {}, []{});
  45. auto outcome = m_menuManagerInterface->AddActionToMenu("o3de.menu.test", "o3de.action.test", 42);
  46. EXPECT_FALSE(outcome.IsSuccess());
  47. }
  48. TEST_F(ActionManagerFixture, AddActionToMenu)
  49. {
  50. m_actionManagerInterface->RegisterActionContext("o3de.context.test", {});
  51. m_actionManagerInterface->RegisterAction("o3de.context.test", "o3de.action.test", {}, []{});
  52. m_menuManagerInterface->RegisterMenu("o3de.menu.test", {});
  53. auto outcome = m_menuManagerInterface->AddActionToMenu("o3de.menu.test", "o3de.action.test", 42);
  54. EXPECT_TRUE(outcome.IsSuccess());
  55. }
  56. TEST_F(ActionManagerFixture, AddActionToMenuTwice)
  57. {
  58. m_actionManagerInterface->RegisterActionContext("o3de.context.test", {});
  59. m_actionManagerInterface->RegisterAction("o3de.context.test", "o3de.action.test", {}, []{});
  60. m_menuManagerInterface->RegisterMenu("o3de.menu.test", {});
  61. m_menuManagerInterface->AddActionToMenu("o3de.menu.test", "o3de.action.test", 42);
  62. auto outcome = m_menuManagerInterface->AddActionToMenu("o3de.menu.test", "o3de.action.test", 42);
  63. EXPECT_FALSE(outcome.IsSuccess());
  64. }
  65. TEST_F(ActionManagerFixture, AddActionsToMenu)
  66. {
  67. m_actionManagerInterface->RegisterActionContext("o3de.context.test", {});
  68. m_actionManagerInterface->RegisterAction("o3de.context.test", "o3de.action.test", {}, []{});
  69. m_actionManagerInterface->RegisterAction("o3de.context.test", "o3de.action.test2", {}, []{});
  70. m_menuManagerInterface->RegisterMenu("o3de.menu.test", {});
  71. AZStd::vector<AZStd::pair<AZStd::string, int>> actions;
  72. actions.push_back(AZStd::make_pair("o3de.action.test", 42));
  73. actions.push_back(AZStd::make_pair("o3de.action.test2", 1));
  74. auto outcome = m_menuManagerInterface->AddActionsToMenu("o3de.menu.test", actions);
  75. EXPECT_TRUE(outcome.IsSuccess());
  76. }
  77. TEST_F(ActionManagerFixture, RemoveActionFromMenu)
  78. {
  79. m_actionManagerInterface->RegisterActionContext("o3de.context.test", {});
  80. m_actionManagerInterface->RegisterAction("o3de.context.test", "o3de.action.test", {}, []{});
  81. m_menuManagerInterface->RegisterMenu("o3de.menu.test", {});
  82. m_menuManagerInterface->AddActionToMenu("o3de.menu.test", "o3de.action.test", 42);
  83. auto outcome = m_menuManagerInterface->RemoveActionFromMenu("o3de.menu.test", "o3de.action.test");
  84. EXPECT_TRUE(outcome.IsSuccess());
  85. }
  86. TEST_F(ActionManagerFixture, RemoveMissingActionFromMenu)
  87. {
  88. m_toolBarManagerInterface->RegisterToolBar("o3de.toolbar.test", {});
  89. auto outcome = m_menuManagerInterface->RemoveActionFromMenu("o3de.menu.test", "o3de.action.test");
  90. EXPECT_FALSE(outcome.IsSuccess());
  91. }
  92. TEST_F(ActionManagerFixture, RemoveActionsFromMenu)
  93. {
  94. m_actionManagerInterface->RegisterActionContext("o3de.context.test", {});
  95. m_actionManagerInterface->RegisterAction("o3de.context.test", "o3de.action.test", {}, []{});
  96. m_actionManagerInterface->RegisterAction("o3de.context.test", "o3de.action.test2", {}, []{});
  97. m_menuManagerInterface->RegisterMenu("o3de.menu.test", {});
  98. AZStd::vector<AZStd::pair<AZStd::string, int>> actions;
  99. actions.push_back(AZStd::make_pair("o3de.action.test", 42));
  100. actions.push_back(AZStd::make_pair("o3de.action.test2", 1));
  101. m_menuManagerInterface->AddActionsToMenu("o3de.menu.test", actions);
  102. auto outcome = m_menuManagerInterface->RemoveActionsFromMenu("o3de.menu.test", { "o3de.action.test", "o3de.action.test2" });
  103. EXPECT_TRUE(outcome.IsSuccess());
  104. }
  105. TEST_F(ActionManagerFixture, RemoveMissingActionsFromMenu)
  106. {
  107. m_actionManagerInterface->RegisterActionContext("o3de.context.test", {});
  108. m_actionManagerInterface->RegisterAction("o3de.context.test", "o3de.action.test", {}, []{});
  109. m_actionManagerInterface->RegisterAction("o3de.context.test", "o3de.action.test2", {}, []{});
  110. m_menuManagerInterface->RegisterMenu("o3de.menu.test", {});
  111. AZStd::vector<AZStd::pair<AZStd::string, int>> actions;
  112. actions.push_back(AZStd::make_pair("o3de.action.test", 42));
  113. m_menuManagerInterface->AddActionsToMenu("o3de.menu.test", actions);
  114. auto outcome = m_menuManagerInterface->RemoveActionsFromMenu("o3de.menu.test", { "o3de.action.test", "o3de.action.test2" });
  115. EXPECT_FALSE(outcome.IsSuccess());
  116. }
  117. TEST_F(ActionManagerFixture, GetUnregisteredMenu)
  118. {
  119. QMenu* menu = m_menuManagerInternalInterface->GetMenu("o3de.menu.test");
  120. EXPECT_TRUE(menu == nullptr);
  121. }
  122. TEST_F(ActionManagerFixture, GetMenu)
  123. {
  124. m_menuManagerInterface->RegisterMenu("o3de.menu.test", {});
  125. QMenu* menu = m_menuManagerInternalInterface->GetMenu("o3de.menu.test");
  126. EXPECT_TRUE(menu != nullptr);
  127. }
  128. TEST_F(ActionManagerFixture, VerifyActionInMenu)
  129. {
  130. // Register menu, get it and verify it's empty.
  131. m_menuManagerInterface->RegisterMenu("o3de.menu.test", {});
  132. QMenu* menu = m_menuManagerInternalInterface->GetMenu("o3de.menu.test");
  133. EXPECT_EQ(menu->actions().size(), 0);
  134. // Register a new action and add it to the menu.
  135. m_actionManagerInterface->RegisterActionContext("o3de.context.test", {});
  136. m_actionManagerInterface->RegisterAction("o3de.context.test", "o3de.action.test", {}, []{});
  137. m_menuManagerInterface->AddActionToMenu("o3de.menu.test", "o3de.action.test", 42);
  138. // Manually trigger Menu refresh - Editor will call this once per tick.
  139. m_menuManagerInternalInterface->RefreshMenus();
  140. // Verify the action is now in the menu.
  141. EXPECT_EQ(menu->actions().size(), 1);
  142. }
  143. TEST_F(ActionManagerFixture, VerifyActionOrderInMenu)
  144. {
  145. // Register menu, get it and verify it's empty.
  146. m_menuManagerInterface->RegisterMenu("o3de.menu.test", {});
  147. QMenu* menu = m_menuManagerInternalInterface->GetMenu("o3de.menu.test");
  148. EXPECT_EQ(menu->actions().size(), 0);
  149. // Register a new action and add it to the menu.
  150. m_actionManagerInterface->RegisterActionContext("o3de.context.test", {});
  151. m_actionManagerInterface->RegisterAction("o3de.context.test", "o3de.action.test1", {}, []{});
  152. m_actionManagerInterface->RegisterAction("o3de.context.test", "o3de.action.test2", {}, []{});
  153. m_menuManagerInterface->AddActionToMenu("o3de.menu.test", "o3de.action.test2", 42);
  154. m_menuManagerInterface->AddActionToMenu("o3de.menu.test", "o3de.action.test1", 1);
  155. // Manually trigger Menu refresh - Editor will call this once per tick.
  156. m_menuManagerInternalInterface->RefreshMenus();
  157. // Verify the actions are now in the menu.
  158. EXPECT_EQ(menu->actions().size(), 2);
  159. // Verify the order is correct.
  160. QAction* test1 = m_actionManagerInternalInterface->GetAction("o3de.action.test1");
  161. QAction* test2 = m_actionManagerInternalInterface->GetAction("o3de.action.test2");
  162. const auto& actions = menu->actions();
  163. EXPECT_EQ(actions[0], test1);
  164. EXPECT_EQ(actions[1], test2);
  165. }
  166. TEST_F(ActionManagerFixture, VerifyActionOrderInMenuWithCollision)
  167. {
  168. // Register menu, get it and verify it's empty.
  169. m_menuManagerInterface->RegisterMenu("o3de.menu.test", {});
  170. QMenu* menu = m_menuManagerInternalInterface->GetMenu("o3de.menu.test");
  171. EXPECT_EQ(menu->actions().size(), 0);
  172. // Register a new action and add it to the menu.
  173. m_actionManagerInterface->RegisterActionContext("o3de.context.test", {});
  174. m_actionManagerInterface->RegisterAction("o3de.context.test", "o3de.action.test1", {}, []{});
  175. m_actionManagerInterface->RegisterAction("o3de.context.test", "o3de.action.test2", {}, []{});
  176. m_menuManagerInterface->AddActionToMenu("o3de.menu.test", "o3de.action.test2", 42);
  177. m_menuManagerInterface->AddActionToMenu("o3de.menu.test", "o3de.action.test1", 42);
  178. // Manually trigger Menu refresh - Editor will call this once per tick.
  179. m_menuManagerInternalInterface->RefreshMenus();
  180. // Verify the actions are now in the menu.
  181. EXPECT_EQ(menu->actions().size(), 2);
  182. // Verify the order is correct (when a collision happens, items should be in order of addition).
  183. QAction* test1 = m_actionManagerInternalInterface->GetAction("o3de.action.test1");
  184. QAction* test2 = m_actionManagerInternalInterface->GetAction("o3de.action.test2");
  185. const auto& actions = menu->actions();
  186. EXPECT_EQ(actions[0], test2);
  187. EXPECT_EQ(actions[1], test1);
  188. }
  189. TEST_F(ActionManagerFixture, VerifySeparatorInMenu)
  190. {
  191. // Register menu, get it and verify it's empty.
  192. m_menuManagerInterface->RegisterMenu("o3de.menu.test", {});
  193. QMenu* menu = m_menuManagerInternalInterface->GetMenu("o3de.menu.test");
  194. EXPECT_EQ(menu->actions().size(), 0);
  195. // Add a separator to the menu.
  196. m_menuManagerInterface->AddSeparatorToMenu("o3de.menu.test", 42);
  197. // Manually trigger Menu refresh - Editor will call this once per tick.
  198. m_menuManagerInternalInterface->RefreshMenus();
  199. // Verify the separator is now in the menu.
  200. const auto& actions = menu->actions();
  201. EXPECT_EQ(actions.size(), 1);
  202. EXPECT_TRUE(actions[0]->isSeparator());
  203. }
  204. TEST_F(ActionManagerFixture, VerifySubMenuInMenu)
  205. {
  206. // Register menu and submenu.
  207. m_menuManagerInterface->RegisterMenu("o3de.menu.testMenu", {});
  208. m_menuManagerInterface->RegisterMenu("o3de.menu.testSubMenu", {});
  209. // Add the sub-menu to the menu.
  210. m_menuManagerInterface->AddSubMenuToMenu("o3de.menu.testMenu", "o3de.menu.testSubMenu", 42);
  211. // Add an action to the sub-menu, else it will be empty and not be displayed.
  212. m_actionManagerInterface->RegisterActionContext("o3de.context.test", {});
  213. m_actionManagerInterface->RegisterAction("o3de.context.test", "o3de.action.test", {}, []{});
  214. m_menuManagerInterface->AddActionToMenu("o3de.menu.testSubMenu", "o3de.action.test", 42);
  215. // Manually trigger Menu refresh - Editor will call this once per tick.
  216. m_menuManagerInternalInterface->RefreshMenus();
  217. // Verify the sub-menu is now in the menu.
  218. QMenu* menu = m_menuManagerInternalInterface->GetMenu("o3de.menu.testMenu");
  219. QMenu* submenu = m_menuManagerInternalInterface->GetMenu("o3de.menu.testSubMenu");
  220. const auto& actions = menu->actions();
  221. EXPECT_EQ(actions.size(), 1);
  222. EXPECT_EQ(actions[0]->menu(), submenu);
  223. }
  224. TEST_F(ActionManagerFixture, AddSubMenuToMenuTwice)
  225. {
  226. // Register menu and submenu.
  227. m_menuManagerInterface->RegisterMenu("o3de.menu.testMenu", {});
  228. m_menuManagerInterface->RegisterMenu("o3de.menu.testSubMenu", {});
  229. // Add the sub-menu to the menu.
  230. m_menuManagerInterface->AddSubMenuToMenu("o3de.menu.testMenu", "o3de.menu.testSubMenu", 42);
  231. auto outcome = m_menuManagerInterface->AddSubMenuToMenu("o3de.menu.testMenu", "o3de.menu.testSubMenu", 42);
  232. EXPECT_FALSE(outcome.IsSuccess());
  233. }
  234. TEST_F(ActionManagerFixture, AddSubMenuToItself)
  235. {
  236. // Register menu.
  237. m_menuManagerInterface->RegisterMenu("o3de.menu.testMenu", {});
  238. // Add the menu to itself.
  239. auto outcome = m_menuManagerInterface->AddSubMenuToMenu("o3de.menu.testMenu", "o3de.menu.testMenu", 42);
  240. EXPECT_FALSE(outcome.IsSuccess());
  241. }
  242. TEST_F(ActionManagerFixture, AddSubMenusToMenu)
  243. {
  244. // Register menu and submenus.
  245. m_menuManagerInterface->RegisterMenu("o3de.menu.testMenu", {});
  246. m_menuManagerInterface->RegisterMenu("o3de.menu.testSubMenu1", {});
  247. m_menuManagerInterface->RegisterMenu("o3de.menu.testSubMenu2", {});
  248. // Add an action to the sub-menus, else they will be empty and not be displayed.
  249. m_actionManagerInterface->RegisterActionContext("o3de.context.test", {});
  250. m_actionManagerInterface->RegisterAction("o3de.context.test", "o3de.action.test", {}, []{});
  251. m_menuManagerInterface->AddActionToMenu("o3de.menu.testSubMenu1", "o3de.action.test", 42);
  252. m_menuManagerInterface->AddActionToMenu("o3de.menu.testSubMenu2", "o3de.action.test", 42);
  253. // Add the sub-menus to the menu.
  254. AZStd::vector<AZStd::pair<AZStd::string, int>> testMenus;
  255. testMenus.emplace_back("o3de.menu.testSubMenu1", 100);
  256. testMenus.emplace_back("o3de.menu.testSubMenu2", 200);
  257. m_menuManagerInterface->AddSubMenusToMenu("o3de.menu.testMenu", testMenus);
  258. // Manually trigger Menu refresh - Editor will call this once per tick.
  259. m_menuManagerInternalInterface->RefreshMenus();
  260. // Verify the sub-menus are now in the menu.
  261. QMenu* menu = m_menuManagerInternalInterface->GetMenu("o3de.menu.testMenu");
  262. QMenu* submenu1 = m_menuManagerInternalInterface->GetMenu("o3de.menu.testSubMenu1");
  263. QMenu* submenu2 = m_menuManagerInternalInterface->GetMenu("o3de.menu.testSubMenu2");
  264. const auto& actions = menu->actions();
  265. EXPECT_EQ(actions.size(), 2);
  266. EXPECT_EQ(actions[0]->menu(), submenu1);
  267. EXPECT_EQ(actions[1]->menu(), submenu2);
  268. }
  269. TEST_F(ActionManagerFixture, RemoveSubMenuFromMenu)
  270. {
  271. // Register menu and submenu.
  272. m_menuManagerInterface->RegisterMenu("o3de.menu.testMenu", {});
  273. m_menuManagerInterface->RegisterMenu("o3de.menu.testSubMenu", {});
  274. // Add the sub-menu to the menu.
  275. m_menuManagerInterface->AddSubMenuToMenu("o3de.menu.testMenu", "o3de.menu.testSubMenu", 42);
  276. // Remove the sub-menu from the menu.
  277. m_menuManagerInterface->RemoveSubMenuFromMenu("o3de.menu.testMenu", "o3de.menu.testSubMenu");
  278. // Manually trigger Menu refresh - Editor will call this once per tick.
  279. m_menuManagerInternalInterface->RefreshMenus();
  280. // Verify the sub-menu is not in the menu.
  281. QMenu* menu = m_menuManagerInternalInterface->GetMenu("o3de.menu.testMenu");
  282. const auto& actions = menu->actions();
  283. EXPECT_EQ(actions.size(), 0);
  284. }
  285. TEST_F(ActionManagerFixture, RemoveSubMenuFromMenuWithoutAdding)
  286. {
  287. // Register menu and submenu.
  288. m_menuManagerInterface->RegisterMenu("o3de.menu.testMenu", {});
  289. // Remove the sub-menu from the menu.
  290. auto outcome = m_menuManagerInterface->RemoveSubMenuFromMenu("o3de.menu.testMenu", "o3de.menu.testSubMenu");
  291. EXPECT_FALSE(outcome.IsSuccess());
  292. }
  293. TEST_F(ActionManagerFixture, RemoveSubMenuFromMenuTwice)
  294. {
  295. // Register menu and submenu.
  296. m_menuManagerInterface->RegisterMenu("o3de.menu.testMenu", {});
  297. m_menuManagerInterface->RegisterMenu("o3de.menu.testSubMenu", {});
  298. // Add the sub-menu to the menu.
  299. m_menuManagerInterface->AddSubMenuToMenu("o3de.menu.testMenu", "o3de.menu.testSubMenu", 42);
  300. // Remove the sub-menu from the menu twice.
  301. m_menuManagerInterface->RemoveSubMenuFromMenu("o3de.menu.testMenu", "o3de.menu.testSubMenu");
  302. auto outcome = m_menuManagerInterface->RemoveSubMenuFromMenu("o3de.menu.testMenu", "o3de.menu.testSubMenu");
  303. EXPECT_FALSE(outcome.IsSuccess());
  304. }
  305. TEST_F(ActionManagerFixture, RemoveSubMenusFromMenu)
  306. {
  307. // Register menu and submenus.
  308. m_menuManagerInterface->RegisterMenu("o3de.menu.testMenu", {});
  309. m_menuManagerInterface->RegisterMenu("o3de.menu.testSubMenu1", {});
  310. m_menuManagerInterface->RegisterMenu("o3de.menu.testSubMenu2", {});
  311. m_menuManagerInterface->RegisterMenu("o3de.menu.testSubMenu3", {});
  312. // Add an action to the sub-menus, else they will be empty and not be displayed.
  313. m_actionManagerInterface->RegisterActionContext("o3de.context.test", {});
  314. m_actionManagerInterface->RegisterAction("o3de.context.test", "o3de.action.test", {}, []{});
  315. m_menuManagerInterface->AddActionToMenu("o3de.menu.testSubMenu1", "o3de.action.test", 42);
  316. m_menuManagerInterface->AddActionToMenu("o3de.menu.testSubMenu2", "o3de.action.test", 42);
  317. m_menuManagerInterface->AddActionToMenu("o3de.menu.testSubMenu3", "o3de.action.test", 42);
  318. // Add the sub-menus to the menu.
  319. AZStd::vector<AZStd::pair<AZStd::string, int>> testMenuAdds;
  320. testMenuAdds.emplace_back("o3de.menu.testSubMenu1", 100);
  321. testMenuAdds.emplace_back("o3de.menu.testSubMenu2", 200);
  322. testMenuAdds.emplace_back("o3de.menu.testSubMenu3", 300);
  323. m_menuManagerInterface->AddSubMenusToMenu("o3de.menu.testMenu", testMenuAdds);
  324. // Remove two sub-menus from the menu.
  325. AZStd::vector<AZStd::string> testMenuRemoves;
  326. testMenuRemoves.emplace_back("o3de.menu.testSubMenu1");
  327. testMenuRemoves.emplace_back("o3de.menu.testSubMenu2");
  328. m_menuManagerInterface->RemoveSubMenusFromMenu("o3de.menu.testMenu", testMenuRemoves);
  329. // Manually trigger Menu refresh - Editor will call this once per tick.
  330. m_menuManagerInternalInterface->RefreshMenus();
  331. // Verify only one sub-menu is now in the menu.
  332. QMenu* menu = m_menuManagerInternalInterface->GetMenu("o3de.menu.testMenu");
  333. QMenu* submenu3 = m_menuManagerInternalInterface->GetMenu("o3de.menu.testSubMenu3");
  334. const auto& actions = menu->actions();
  335. EXPECT_EQ(actions.size(), 1);
  336. EXPECT_EQ(actions[0]->menu(), submenu3);
  337. }
  338. TEST_F(ActionManagerFixture, AddUnregisteredWidgetInMenu)
  339. {
  340. // Try to add a widget without registering it.
  341. auto outcome = m_menuManagerInterface->AddWidgetToMenu("o3de.menu.test", "someUnregisteredWidgetIdentifier", 42);
  342. EXPECT_FALSE(outcome.IsSuccess());
  343. }
  344. TEST_F(ActionManagerFixture, VerifyWidgetInMenu)
  345. {
  346. // Register menu and widget action.
  347. m_menuManagerInterface->RegisterMenu("o3de.menu.test", {});
  348. QWidget* widget = new QWidget();
  349. m_actionManagerInterface->RegisterWidgetAction(
  350. "o3de.widgetAction.test", {}, [widget]()
  351. {
  352. // Note: the WidgetAction generator function should create a new widget every time it's called.
  353. // This implementation is technically incorrect, but it allows us to test the correct behavior.
  354. return widget;
  355. }
  356. );
  357. // Add the widget to the menu.
  358. m_menuManagerInterface->AddWidgetToMenu("o3de.menu.test", "o3de.widgetAction.test", 42);
  359. // Manually trigger Menu refresh - Editor will call this once per tick.
  360. m_menuManagerInternalInterface->RefreshMenus();
  361. // Verify the widget is now in the menu.
  362. QMenu* menu = m_menuManagerInternalInterface->GetMenu("o3de.menu.test");
  363. const auto& actions = menu->actions();
  364. EXPECT_EQ(actions.size(), 1);
  365. QWidgetAction* widgetAction = qobject_cast<QWidgetAction*>(actions[0]);
  366. EXPECT_TRUE(widgetAction != nullptr);
  367. EXPECT_TRUE(widgetAction->defaultWidget() == widget);
  368. }
  369. TEST_F(ActionManagerFixture, VerifyComplexMenu)
  370. {
  371. // Combine multiple actions, separators and submenus.
  372. m_menuManagerInterface->RegisterMenu("o3de.menu.testMenu", {});
  373. m_menuManagerInterface->RegisterMenu("o3de.menu.testSubMenu", {});
  374. m_actionManagerInterface->RegisterActionContext("o3de.context.test", {});
  375. m_actionManagerInterface->RegisterAction("o3de.context.test", "o3de.action.test1", {}, []{});
  376. m_actionManagerInterface->RegisterAction("o3de.context.test", "o3de.action.test2", {}, []{});
  377. // Create a menu with this setup. Order of addition is intentionally scrambled to verify sortKeys.
  378. // - Test 1 Action
  379. // - Test 2 Action
  380. // - Separator
  381. // - SubMenu
  382. // - Test 2 Action
  383. //
  384. // Note: it is legal to add the same action to multiple different menus.
  385. m_menuManagerInterface->AddActionToMenu("o3de.menu.testMenu", "o3de.action.test2", 12);
  386. m_menuManagerInterface->AddActionToMenu("o3de.menu.testSubMenu", "o3de.action.test2", 1);
  387. m_menuManagerInterface->AddSubMenuToMenu("o3de.menu.testMenu", "o3de.menu.testSubMenu", 42);
  388. m_menuManagerInterface->AddActionToMenu("o3de.menu.testMenu", "o3de.action.test1", 11);
  389. m_menuManagerInterface->AddSeparatorToMenu("o3de.menu.testMenu", 18);
  390. // Manually trigger Menu refresh - Editor will call this once per tick.
  391. m_menuManagerInternalInterface->RefreshMenus();
  392. // Verify the actions are now in the menu in the expected order.
  393. QMenu* menu = m_menuManagerInternalInterface->GetMenu("o3de.menu.testMenu");
  394. QMenu* submenu = m_menuManagerInternalInterface->GetMenu("o3de.menu.testSubMenu");
  395. QAction* test1 = m_actionManagerInternalInterface->GetAction("o3de.action.test1");
  396. QAction* test2 = m_actionManagerInternalInterface->GetAction("o3de.action.test2");
  397. // Note: separators and sub-menus are still QActions in the context of the menu.
  398. EXPECT_EQ(menu->actions().size(), 4);
  399. // Verify the order is correct.
  400. const auto& actions = menu->actions();
  401. EXPECT_EQ(actions[0], test1);
  402. EXPECT_EQ(actions[1], test2);
  403. EXPECT_TRUE(actions[2]->isSeparator());
  404. EXPECT_EQ(actions[3]->menu(), submenu);
  405. const auto& subactions = submenu->actions();
  406. EXPECT_EQ(subactions[0], test2);
  407. }
  408. TEST_F(ActionManagerFixture, AddMenuToUnregisteredMenuBar)
  409. {
  410. m_menuManagerInterface->RegisterMenu("o3de.menu.test", {});
  411. auto outcome = m_menuManagerInterface->AddMenuToMenuBar("o3de.menubar.test", "o3de.menu.test", 42);
  412. EXPECT_FALSE(outcome.IsSuccess());
  413. }
  414. TEST_F(ActionManagerFixture, AddMenuToMenuBar)
  415. {
  416. m_menuManagerInterface->RegisterMenuBar("o3de.menubar.test", m_mainWindow);
  417. m_menuManagerInterface->RegisterMenu("o3de.menu.test", {});
  418. auto outcome = m_menuManagerInterface->AddMenuToMenuBar("o3de.menubar.test", "o3de.menu.test", 42);
  419. EXPECT_TRUE(outcome.IsSuccess());
  420. }
  421. TEST_F(ActionManagerFixture, AddMenuToMenuBarTwice)
  422. {
  423. m_menuManagerInterface->RegisterMenuBar("o3de.menubar.test", m_mainWindow);
  424. m_menuManagerInterface->RegisterMenu("o3de.menu.test", {});
  425. m_menuManagerInterface->AddMenuToMenuBar("o3de.menubar.test", "o3de.menu.test", 42);
  426. auto outcome = m_menuManagerInterface->AddMenuToMenuBar("o3de.menubar.test", "o3de.menu.test", 42);
  427. EXPECT_FALSE(outcome.IsSuccess());
  428. }
  429. TEST_F(ActionManagerFixture, VerifyMenuInMenuBar)
  430. {
  431. m_menuManagerInterface->RegisterMenuBar("o3de.menubar.test", m_mainWindow);
  432. m_menuManagerInterface->RegisterMenu("o3de.menu.test", {});
  433. // Add the menu to the menu bar.
  434. m_menuManagerInterface->AddMenuToMenuBar("o3de.menubar.test", "o3de.menu.test", 42);
  435. // Manually trigger MenuBar refresh - Editor will call this once per tick.
  436. m_menuManagerInternalInterface->RefreshMenuBars();
  437. // Verify the submenu is now in the menu.
  438. QMenuBar* menubar = m_mainWindow->menuBar();
  439. QMenu* menu = m_menuManagerInternalInterface->GetMenu("o3de.menu.test");
  440. const auto& actions = menubar->actions();
  441. EXPECT_EQ(actions.size(), 1);
  442. EXPECT_EQ(actions[0]->menu(), menu);
  443. }
  444. TEST_F(ActionManagerFixture, VerifyComplexMenuBar)
  445. {
  446. // Register multiple menus.
  447. m_menuManagerInterface->RegisterMenuBar("o3de.menubar.test", m_mainWindow);
  448. m_menuManagerInterface->RegisterMenu("o3de.menu.testMenu1", {});
  449. m_menuManagerInterface->RegisterMenu("o3de.menu.testMenu2", {});
  450. m_menuManagerInterface->RegisterMenu("o3de.menu.testMenu3", {});
  451. // Create a menu bar with this setup. Order of addition is intentionally scrambled to verify sortKeys.
  452. // - Menu 1
  453. // - Menu 2
  454. // - Menu 3
  455. m_menuManagerInterface->AddMenuToMenuBar("o3de.menubar.test", "o3de.menu.testMenu2", 42);
  456. m_menuManagerInterface->AddMenuToMenuBar("o3de.menubar.test", "o3de.menu.testMenu3", 42);
  457. m_menuManagerInterface->AddMenuToMenuBar("o3de.menubar.test", "o3de.menu.testMenu1", 16);
  458. // Manually trigger MenuBar refresh - Editor will call this once per tick.
  459. m_menuManagerInternalInterface->RefreshMenuBars();
  460. // Verify the menus are now in the menu bar in the expected order.
  461. QMenuBar* menubar = m_mainWindow->menuBar();
  462. QMenu* testMenu1 = m_menuManagerInternalInterface->GetMenu("o3de.menu.testMenu1");
  463. QMenu* testMenu2 = m_menuManagerInternalInterface->GetMenu("o3de.menu.testMenu2");
  464. QMenu* testMenu3 = m_menuManagerInternalInterface->GetMenu("o3de.menu.testMenu3");
  465. // Note: menus are represented via a QAction with a submenu property in Qt.
  466. EXPECT_EQ(menubar->actions().size(), 3);
  467. // Verify the order is correct.
  468. const auto& actions = menubar->actions();
  469. EXPECT_EQ(actions[0]->menu(), testMenu1);
  470. EXPECT_EQ(actions[1]->menu(), testMenu2);
  471. EXPECT_EQ(actions[2]->menu(), testMenu3);
  472. }
  473. TEST_F(ActionManagerFixture, GetSortKeyOfActionInMenu)
  474. {
  475. m_menuManagerInterface->RegisterMenu("o3de.menu.test", {});
  476. m_actionManagerInterface->RegisterActionContext("o3de.context.test", {});
  477. m_actionManagerInterface->RegisterAction("o3de.context.test", "o3de.action.test", {}, []{});
  478. // Add the action to the menu.
  479. m_menuManagerInterface->AddActionToMenu("o3de.menu.test", "o3de.action.test", 42);
  480. // Verify the API returns the correct sort key.
  481. auto outcome = m_menuManagerInterface->GetSortKeyOfActionInMenu("o3de.menu.test", "o3de.action.test");
  482. EXPECT_TRUE(outcome.IsSuccess());
  483. EXPECT_EQ(outcome.GetValue(), 42);
  484. }
  485. TEST_F(ActionManagerFixture, GetSortKeyOfUnregisteredActionInMenu)
  486. {
  487. m_menuManagerInterface->RegisterMenu("o3de.menu.test", {});
  488. // Verify the API fails as the action is not registered.
  489. auto outcome = m_menuManagerInterface->GetSortKeyOfActionInMenu("o3de.menu.test", "o3de.action.test");
  490. EXPECT_FALSE(outcome.IsSuccess());
  491. }
  492. TEST_F(ActionManagerFixture, GetSortKeyOfActionNotInMenu)
  493. {
  494. m_menuManagerInterface->RegisterMenu("o3de.menu.test", {});
  495. m_actionManagerInterface->RegisterActionContext("o3de.context.test", {});
  496. m_actionManagerInterface->RegisterAction("o3de.context.test", "o3de.action.test", {}, []{});
  497. // Verify the API fails as the action is registered but was not added to the menu.
  498. auto outcome = m_menuManagerInterface->GetSortKeyOfActionInMenu("o3de.menu.test", "o3de.action.test");
  499. EXPECT_FALSE(outcome.IsSuccess());
  500. }
  501. TEST_F(ActionManagerFixture, GetSortKeyOfSubMenuInMenu)
  502. {
  503. m_menuManagerInterface->RegisterMenu("o3de.menu.testMenu", {});
  504. m_menuManagerInterface->RegisterMenu("o3de.menu.testSubMenu", {});
  505. // Add the sub-menu to the menu.
  506. m_menuManagerInterface->AddSubMenuToMenu("o3de.menu.testMenu", "o3de.menu.testSubMenu", 42);
  507. // Verify the API returns the correct sort key.
  508. auto outcome = m_menuManagerInterface->GetSortKeyOfSubMenuInMenu("o3de.menu.testMenu", "o3de.menu.testSubMenu");
  509. EXPECT_TRUE(outcome.IsSuccess());
  510. EXPECT_EQ(outcome.GetValue(), 42);
  511. }
  512. TEST_F(ActionManagerFixture, GetSortKeyOfUnregisteredSubMenuInMenu)
  513. {
  514. m_menuManagerInterface->RegisterMenu("o3de.menu.testMenu", {});
  515. // Verify the API fails as the sub-menu is not registered.
  516. auto outcome = m_menuManagerInterface->GetSortKeyOfActionInMenu("o3de.menu.testMenu", "o3de.menu.testSubMenu");
  517. EXPECT_FALSE(outcome.IsSuccess());
  518. }
  519. TEST_F(ActionManagerFixture, GetSortKeyOfSubMenuNotInMenu)
  520. {
  521. m_menuManagerInterface->RegisterMenu("o3de.menu.testMenu", {});
  522. m_menuManagerInterface->RegisterMenu("o3de.menu.testSubMenu", {});
  523. // Verify the API fails as the sub-menu is registered but was not added to the menu.
  524. auto outcome = m_menuManagerInterface->GetSortKeyOfActionInMenu("o3de.menu.testMenu", "o3de.menu.testSubMenu");
  525. EXPECT_FALSE(outcome.IsSuccess());
  526. }
  527. TEST_F(ActionManagerFixture, GetSortKeyOfWidgetInMenu)
  528. {
  529. m_menuManagerInterface->RegisterMenu("o3de.menu.test", {});
  530. m_actionManagerInterface->RegisterWidgetAction(
  531. "o3de.widgetAction.test",
  532. {},
  533. []() -> QWidget*
  534. {
  535. return nullptr;
  536. }
  537. );
  538. // Add the widget to the menu.
  539. m_menuManagerInterface->AddWidgetToMenu("o3de.menu.test", "o3de.widgetAction.test", 42);
  540. // Verify the API returns the correct sort key.
  541. auto outcome = m_menuManagerInterface->GetSortKeyOfWidgetInMenu("o3de.menu.test", "o3de.widgetAction.test");
  542. EXPECT_TRUE(outcome.IsSuccess());
  543. EXPECT_EQ(outcome.GetValue(), 42);
  544. }
  545. TEST_F(ActionManagerFixture, GetSortKeyOfUnregisteredWidgetInMenu)
  546. {
  547. m_menuManagerInterface->RegisterMenu("o3de.menu.test", {});
  548. // Verify the API fails as the widget is not registered.
  549. auto outcome = m_menuManagerInterface->GetSortKeyOfWidgetInMenu("o3de.menu.test", "o3de.widgetAction.test");
  550. EXPECT_FALSE(outcome.IsSuccess());
  551. }
  552. TEST_F(ActionManagerFixture, GetSortKeyOfWidgetNotInMenu)
  553. {
  554. m_menuManagerInterface->RegisterMenu("o3de.menu.test", {});
  555. m_actionManagerInterface->RegisterWidgetAction(
  556. "o3de.widgetAction.test",
  557. {},
  558. []() -> QWidget*
  559. {
  560. return nullptr;
  561. }
  562. );
  563. // Verify the API fails as the widget is registered but was not added to the menu.
  564. auto outcome = m_menuManagerInterface->GetSortKeyOfWidgetInMenu("o3de.menu.test", "o3de.widgetAction.test");
  565. EXPECT_FALSE(outcome.IsSuccess());
  566. }
  567. TEST_F(ActionManagerFixture, GetSortKeyOfMenuInMenuBar)
  568. {
  569. m_menuManagerInterface->RegisterMenuBar("o3de.menubar.test", m_mainWindow);
  570. m_menuManagerInterface->RegisterMenu("o3de.menu.test", {});
  571. // Add the menu to the menu bar.
  572. m_menuManagerInterface->AddMenuToMenuBar("o3de.menubar.test", "o3de.menu.test", 42);
  573. // Verify the API returns the correct sort key.
  574. auto outcome = m_menuManagerInterface->GetSortKeyOfMenuInMenuBar("o3de.menubar.test", "o3de.menu.test");
  575. EXPECT_TRUE(outcome.IsSuccess());
  576. EXPECT_EQ(outcome.GetValue(), 42);
  577. }
  578. TEST_F(ActionManagerFixture, GetSortKeyOfUnregisteredMenuInMenuBar)
  579. {
  580. m_menuManagerInterface->RegisterMenuBar("o3de.menubar.test", m_mainWindow);
  581. // Verify the API fails as the menu is not registered.
  582. auto outcome = m_menuManagerInterface->GetSortKeyOfActionInMenu("o3de.menubar.test", "o3de.menu.test");
  583. EXPECT_FALSE(outcome.IsSuccess());
  584. }
  585. TEST_F(ActionManagerFixture, GetSortKeyOfMenuNotInMenuBar)
  586. {
  587. m_menuManagerInterface->RegisterMenuBar("o3de.menubar.test", m_mainWindow);
  588. m_menuManagerInterface->RegisterMenu("o3de.menu.test", {});
  589. // Verify the API fails as the menu is registered but was not added to the menu bar.
  590. auto outcome = m_menuManagerInterface->GetSortKeyOfActionInMenu("o3de.menubar.test", "o3de.menu.test");
  591. EXPECT_FALSE(outcome.IsSuccess());
  592. }
  593. TEST_F(ActionManagerFixture, VerifyHideFromMenusWhenDisabledTrue)
  594. {
  595. // Register menu, get it and verify it's empty.
  596. m_menuManagerInterface->RegisterMenu("o3de.menu.test", {});
  597. QMenu* menu = m_menuManagerInternalInterface->GetMenu("o3de.menu.test");
  598. EXPECT_EQ(menu->actions().size(), 0);
  599. // Register a new action and add it to the menu. MenuVisibility is set to HideWhenDisabled by default.
  600. m_actionManagerInterface->RegisterActionContext("o3de.context.test", {});
  601. m_actionManagerInterface->RegisterAction("o3de.context.test", "o3de.action.test", {}, []{});
  602. m_menuManagerInterface->AddActionToMenu("o3de.menu.test", "o3de.action.test", 42);
  603. // Add enabled state callback.
  604. bool enabledState = true;
  605. m_actionManagerInterface->InstallEnabledStateCallback(
  606. "o3de.action.test",
  607. [&]()
  608. {
  609. return enabledState;
  610. }
  611. );
  612. // Manually trigger Menu refresh - Editor will call this once per tick.
  613. m_menuManagerInternalInterface->RefreshMenus();
  614. // Verify the action is now in the menu.
  615. EXPECT_EQ(menu->actions().size(), 1);
  616. // Set the action as disabled.
  617. enabledState = false;
  618. m_actionManagerInterface->UpdateAction("o3de.action.test");
  619. // Manually trigger Menu refresh - Editor will call this once per tick.
  620. m_menuManagerInternalInterface->RefreshMenus();
  621. // Verify the action is no longer in the menu.
  622. EXPECT_EQ(menu->actions().size(), 0);
  623. }
  624. TEST_F(ActionManagerFixture, VerifyMenuVisibilityAlwaysShow)
  625. {
  626. // Register menu, get it and verify it's empty.
  627. m_menuManagerInterface->RegisterMenu("o3de.menu.test", {});
  628. QMenu* menu = m_menuManagerInternalInterface->GetMenu("o3de.menu.test");
  629. EXPECT_EQ(menu->actions().size(), 0);
  630. // Register a new action and add it to the menu. Have MenuVisibility set to AlwaysShow.
  631. AzToolsFramework::ActionProperties actionProperties;
  632. actionProperties.m_menuVisibility = AzToolsFramework::ActionVisibility::AlwaysShow;
  633. m_actionManagerInterface->RegisterActionContext("o3de.context.test", {});
  634. m_actionManagerInterface->RegisterAction("o3de.context.test", "o3de.action.test", actionProperties, []{});
  635. m_menuManagerInterface->AddActionToMenu("o3de.menu.test", "o3de.action.test", 42);
  636. // Add enabled state callback.
  637. bool enabledState = true;
  638. m_actionManagerInterface->InstallEnabledStateCallback(
  639. "o3de.action.test",
  640. [&]()
  641. {
  642. return enabledState;
  643. }
  644. );
  645. // Manually trigger Menu refresh - Editor will call this once per tick.
  646. m_menuManagerInternalInterface->RefreshMenus();
  647. // Verify the action is now in the menu.
  648. EXPECT_EQ(menu->actions().size(), 1);
  649. // Set the action as disabled.
  650. enabledState = false;
  651. m_actionManagerInterface->UpdateAction("o3de.action.test");
  652. // Manually trigger Menu refresh - Editor will call this once per tick.
  653. m_menuManagerInternalInterface->RefreshMenus();
  654. // Verify the action is still in the menu.
  655. EXPECT_EQ(menu->actions().size(), 1);
  656. }
  657. TEST_F(ActionManagerFixture, VerifyActionIsHiddenWhenChangingMode)
  658. {
  659. // Register menu, get it and verify it's empty.
  660. m_menuManagerInterface->RegisterMenu("o3de.menu.test", {});
  661. QMenu* menu = m_menuManagerInternalInterface->GetMenu("o3de.menu.test");
  662. EXPECT_EQ(menu->actions().size(), 0);
  663. // Register a new action and add it to the default mode.
  664. m_actionManagerInterface->RegisterActionContext("o3de.context.test", {});
  665. m_actionManagerInterface->RegisterAction("o3de.context.test", "o3de.action.test", {}, []{});
  666. m_actionManagerInterface->AssignModeToAction(AzToolsFramework::DefaultActionContextModeIdentifier, "o3de.action.test");
  667. // Add the action to the menu.
  668. m_menuManagerInterface->AddActionToMenu("o3de.menu.test", "o3de.action.test", 42);
  669. // Manually trigger Menu refresh - Editor will call this once per tick.
  670. m_menuManagerInternalInterface->RefreshMenus();
  671. // Verify the action is now in the menu.
  672. EXPECT_EQ(menu->actions().size(), 1);
  673. // Register a new mode and switch to it.
  674. m_actionManagerInterface->RegisterActionContextMode("o3de.context.test", "testMode");
  675. m_actionManagerInterface->SetActiveActionContextMode("o3de.context.test", "testMode");
  676. // Manually trigger Menu refresh - Editor will call this once per tick.
  677. m_menuManagerInternalInterface->RefreshMenus();
  678. // Verify the action is no longer in the menu.
  679. EXPECT_EQ(menu->actions().size(), 0);
  680. }
  681. TEST_F(ActionManagerFixture, VerifyMenuVisibilityAlwaysShowWhenChangingMode)
  682. {
  683. // Register menu, get it and verify it's empty.
  684. m_menuManagerInterface->RegisterMenu("o3de.menu.test", {});
  685. QMenu* menu = m_menuManagerInternalInterface->GetMenu("o3de.menu.test");
  686. EXPECT_EQ(menu->actions().size(), 0);
  687. // Register a new action and add it to the default mode. Have MenuVisibility set to AlwaysShow.
  688. AzToolsFramework::ActionProperties actionProperties;
  689. actionProperties.m_menuVisibility = AzToolsFramework::ActionVisibility::AlwaysShow;
  690. m_actionManagerInterface->RegisterActionContext("o3de.context.test", {});
  691. m_actionManagerInterface->RegisterAction("o3de.context.test", "o3de.action.test", actionProperties, []{});
  692. m_actionManagerInterface->AssignModeToAction(AzToolsFramework::DefaultActionContextModeIdentifier, "o3de.action.test");
  693. // Add the action to the menu.
  694. m_menuManagerInterface->AddActionToMenu("o3de.menu.test", "o3de.action.test", 42);
  695. // Manually trigger Menu refresh - Editor will call this once per tick.
  696. m_menuManagerInternalInterface->RefreshMenus();
  697. // Verify the action is now in the menu.
  698. EXPECT_EQ(menu->actions().size(), 1);
  699. // Register a new mode and switch to it.
  700. m_actionManagerInterface->RegisterActionContextMode("o3de.context.test", "testMode");
  701. m_actionManagerInterface->SetActiveActionContextMode("o3de.context.test", "testMode");
  702. // Manually trigger Menu refresh - Editor will call this once per tick.
  703. m_menuManagerInternalInterface->RefreshMenus();
  704. // Verify the action is still in the menu.
  705. EXPECT_EQ(menu->actions().size(), 1);
  706. }
  707. TEST_F(ActionManagerFixture, VerifySubMenuIsHiddenWhenEmptied)
  708. {
  709. // Register menus
  710. m_menuManagerInterface->RegisterMenu("o3de.menu.testMenu", {});
  711. m_menuManagerInterface->RegisterMenu("o3de.menu.testSubMenu", {});
  712. m_menuManagerInterface->AddSubMenuToMenu("o3de.menu.testMenu", "o3de.menu.testSubMenu", 42);
  713. // Register a new action and add it to the sub-menu.
  714. m_actionManagerInterface->RegisterActionContext("o3de.context.test", {});
  715. m_actionManagerInterface->RegisterAction("o3de.context.test", "o3de.action.test", {}, []{});
  716. m_menuManagerInterface->AddActionToMenu("o3de.menu.testSubMenu", "o3de.action.test", 42);
  717. // Add enabled state callback.
  718. bool enabledState = true;
  719. m_actionManagerInterface->InstallEnabledStateCallback(
  720. "o3de.action.test",
  721. [&]()
  722. {
  723. return enabledState;
  724. }
  725. );
  726. // Manually trigger Menu refresh - Editor will call this once per tick.
  727. m_menuManagerInternalInterface->RefreshMenus();
  728. // Verify the sub-menu is now in the menu.
  729. {
  730. QMenu* menu = m_menuManagerInternalInterface->GetMenu("o3de.menu.testMenu");
  731. QMenu* submenu = m_menuManagerInternalInterface->GetMenu("o3de.menu.testSubMenu");
  732. const auto& actions = menu->actions();
  733. EXPECT_EQ(actions.size(), 1);
  734. EXPECT_EQ(actions[0]->menu(), submenu);
  735. }
  736. // Set the action as disabled.
  737. enabledState = false;
  738. m_actionManagerInterface->UpdateAction("o3de.action.test");
  739. // Manually trigger Menu refresh - Editor will call this once per tick.
  740. m_menuManagerInternalInterface->RefreshMenus();
  741. // Verify the sub-menu is no longer part of the menu since it is empty.
  742. {
  743. QMenu* menu = m_menuManagerInternalInterface->GetMenu("o3de.menu.testMenu");
  744. EXPECT_EQ(menu->actions().size(), 0);
  745. }
  746. }
  747. TEST_F(ActionManagerFixture, VerifySubMenuIsShownWhenFilled)
  748. {
  749. // Register menus
  750. m_menuManagerInterface->RegisterMenu("o3de.menu.testMenu", {});
  751. m_menuManagerInterface->RegisterMenu("o3de.menu.testSubMenu", {});
  752. m_menuManagerInterface->AddSubMenuToMenu("o3de.menu.testMenu", "o3de.menu.testSubMenu", 42);
  753. // Register a new action and add it to the sub-menu.
  754. m_actionManagerInterface->RegisterActionContext("o3de.context.test", {});
  755. m_actionManagerInterface->RegisterAction("o3de.context.test", "o3de.action.test", {}, []{});
  756. m_menuManagerInterface->AddActionToMenu("o3de.menu.testSubMenu", "o3de.action.test", 42);
  757. // Add enabled state callback.
  758. bool enabledState = false;
  759. m_actionManagerInterface->InstallEnabledStateCallback(
  760. "o3de.action.test",
  761. [&]()
  762. {
  763. return enabledState;
  764. }
  765. );
  766. // Manually trigger Menu refresh - Editor will call this once per tick.
  767. m_menuManagerInternalInterface->RefreshMenus();
  768. // Verify the sub-menu is not part of the menu since it is empty.
  769. {
  770. QMenu* menu = m_menuManagerInternalInterface->GetMenu("o3de.menu.testMenu");
  771. EXPECT_EQ(menu->actions().size(), 0);
  772. }
  773. // Set the action as enabled.
  774. enabledState = true;
  775. m_actionManagerInterface->UpdateAction("o3de.action.test");
  776. // Manually trigger Menu refresh - Editor will call this once per tick.
  777. m_menuManagerInternalInterface->RefreshMenus();
  778. // Verify the sub-menu is in the menu again.
  779. {
  780. QMenu* menu = m_menuManagerInternalInterface->GetMenu("o3de.menu.testMenu");
  781. QMenu* submenu = m_menuManagerInternalInterface->GetMenu("o3de.menu.testSubMenu");
  782. const auto& actions = menu->actions();
  783. EXPECT_EQ(actions.size(), 1);
  784. EXPECT_EQ(actions[0]->menu(), submenu);
  785. }
  786. }
  787. TEST_F(ActionManagerFixture, VerifySimpleAddSubMenuCircularDependency)
  788. {
  789. // Register menus
  790. m_menuManagerInterface->RegisterMenu("o3de.menu.testMenu", {});
  791. m_menuManagerInterface->RegisterMenu("o3de.menu.testSubMenu", {});
  792. m_menuManagerInterface->AddSubMenuToMenu("o3de.menu.testMenu", "o3de.menu.testSubMenu", 42);
  793. // Verify I can't add "o3de.menu.testMenu" as a sub-menu for "o3de.menu.testSubMenu"
  794. // as it would cause a circular dependency.
  795. auto outcome = m_menuManagerInterface->AddSubMenuToMenu("o3de.menu.testSubMenu", "o3de.menu.testMenu", 42);
  796. EXPECT_FALSE(outcome.IsSuccess());
  797. }
  798. TEST_F(ActionManagerFixture, VerifyNestedAddSubMenuCircularDependency)
  799. {
  800. // Register menus
  801. m_menuManagerInterface->RegisterMenu("o3de.menu.testMenu", {});
  802. m_menuManagerInterface->RegisterMenu("o3de.menu.testSubMenu", {});
  803. m_menuManagerInterface->RegisterMenu("o3de.menu.testSubSubMenu", {});
  804. m_menuManagerInterface->AddSubMenuToMenu("o3de.menu.testMenu", "o3de.menu.testSubMenu", 42);
  805. m_menuManagerInterface->AddSubMenuToMenu("o3de.menu.testSubMenu", "o3de.menu.testSubSubMenu", 42);
  806. // Verify I can't add "o3de.menu.testMenu" as a sub-menu for "o3de.menu.testSubSubMenu"
  807. // as it would cause a circular dependency.
  808. auto outcome = m_menuManagerInterface->AddSubMenuToMenu("o3de.menu.testSubSubMenu", "o3de.menu.testMenu", 42);
  809. EXPECT_FALSE(outcome.IsSuccess());
  810. }
  811. } // namespace UnitTest