BsMenuItemManager.cpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "BsMenuItemManager.h"
  4. #include "Serialization/BsScriptAssemblyManager.h"
  5. #include "BsMonoAssembly.h"
  6. #include "BsMonoClass.h"
  7. #include "BsMonoMethod.h"
  8. #include "BsMonoField.h"
  9. #include "BsMonoManager.h"
  10. #include "BsMonoUtil.h"
  11. #include "BsScriptObjectManager.h"
  12. #include "GUI/BsShortcutKey.h"
  13. #include "EditorWindow/BsEditorWindowManager.h"
  14. #include "EditorWindow/BsMainEditorWindow.h"
  15. #include "GUI/BsGUIMenuBar.h"
  16. #include "GUI/BsGUIMenu.h"
  17. using namespace std::placeholders;
  18. namespace bs
  19. {
  20. MenuItemManager::MenuItemManager(ScriptAssemblyManager& scriptObjectManager)
  21. :mScriptObjectManager(scriptObjectManager), mMenuItemAttribute(nullptr), mPathField(nullptr),
  22. mShortcutField(nullptr), mPriorityField(nullptr), mSeparatorField(nullptr)
  23. {
  24. mDomainLoadedConn = ScriptObjectManager::instance().onRefreshDomainLoaded.connect(std::bind(&MenuItemManager::reloadAssemblyData, this));
  25. reloadAssemblyData();
  26. }
  27. MenuItemManager::~MenuItemManager()
  28. {
  29. mDomainLoadedConn.disconnect();
  30. }
  31. void MenuItemManager::clearMenuItems()
  32. {
  33. MainEditorWindow* mainWindow = EditorWindowManager::instance().getMainWindow();
  34. UINT32 numItems = (UINT32)mMenuItems.size();
  35. Vector<bool> destroyedItems(numItems, false);
  36. // Remove leaf elements only to avoid deleting child menu items
  37. bool changesMade;
  38. do
  39. {
  40. changesMade = false;
  41. for (UINT32 i = 0; i < numItems; i++)
  42. {
  43. GUIMenuItem* menuItem = mMenuItems[i];
  44. if (!destroyedItems[i] && menuItem->getNumChildren() == 0)
  45. {
  46. mainWindow->getMenuBar().removeMenuItem(menuItem);
  47. destroyedItems[i] = true;
  48. changesMade = true;
  49. }
  50. }
  51. } while (changesMade);
  52. // Remove remaining items regardless (none of their children are our concern). But when running properly there
  53. // should be no entries to remove at this step.
  54. for (UINT32 i = 0; i < numItems; i++)
  55. {
  56. GUIMenuItem* menuItem = mMenuItems[i];
  57. if (!destroyedItems[i])
  58. mainWindow->getMenuBar().removeMenuItem(menuItem);
  59. }
  60. mMenuItems.clear();
  61. }
  62. void MenuItemManager::reloadAssemblyData()
  63. {
  64. clearMenuItems();
  65. // Reload MenuItem attribute from editor assembly
  66. MonoAssembly* editorAssembly = MonoManager::instance().getAssembly(EDITOR_ASSEMBLY);
  67. mMenuItemAttribute = editorAssembly->getClass(EDITOR_NS, "MenuItem");
  68. if (mMenuItemAttribute == nullptr)
  69. BS_EXCEPT(InvalidStateException, "Cannot find MenuItem managed class.");
  70. mPathField = mMenuItemAttribute->getField("path");
  71. mShortcutField = mMenuItemAttribute->getField("shortcut");
  72. mPriorityField = mMenuItemAttribute->getField("priority");
  73. mSeparatorField = mMenuItemAttribute->getField("separator");
  74. MainEditorWindow* mainWindow = EditorWindowManager::instance().getMainWindow();
  75. Vector<String> scriptAssemblyNames = mScriptObjectManager.getScriptAssemblies();
  76. for (auto& assemblyName : scriptAssemblyNames)
  77. {
  78. MonoAssembly* assembly = MonoManager::instance().getAssembly(assemblyName);
  79. // Find new menu item methods
  80. const Vector<MonoClass*>& allClasses = assembly->getAllClasses();
  81. for (auto curClass : allClasses)
  82. {
  83. const Vector<MonoMethod*>& methods = curClass->getAllMethods();
  84. for (auto& curMethod : methods)
  85. {
  86. String path;
  87. ShortcutKey shortcutKey = ShortcutKey::NONE;
  88. INT32 priority = 0;
  89. bool separator = false;
  90. if (parseMenuItemMethod(curMethod, path, shortcutKey, priority, separator))
  91. {
  92. std::function<void()> callback = std::bind(&MenuItemManager::menuItemCallback, curMethod);
  93. if (separator)
  94. {
  95. Vector<String> pathElements = StringUtil::split(path, "/");
  96. String separatorPath;
  97. if (pathElements.size() > 1)
  98. {
  99. const String& lastElem = pathElements[pathElements.size() - 1];
  100. separatorPath = path;
  101. separatorPath.erase(path.size() - lastElem.size() - 1, lastElem.size() + 1);
  102. }
  103. GUIMenuItem* separatorItem = mainWindow->getMenuBar().addMenuItemSeparator(separatorPath, priority);
  104. mMenuItems.push_back(separatorItem);
  105. }
  106. GUIMenuItem* menuItem = mainWindow->getMenuBar().addMenuItem(path, callback, priority, shortcutKey);
  107. mMenuItems.push_back(menuItem);
  108. }
  109. }
  110. }
  111. }
  112. }
  113. bool MenuItemManager::parseMenuItemMethod(MonoMethod* method, String& path, ShortcutKey& shortcut, INT32& priority, bool& separator) const
  114. {
  115. if (!method->hasAttribute(mMenuItemAttribute))
  116. return false;
  117. if (method->getNumParameters() != 0)
  118. return false;
  119. if (!method->isStatic())
  120. return false;
  121. MonoObject* menuItemAttrib = method->getAttribute(mMenuItemAttribute);
  122. MonoString* monoPath;
  123. mPathField->get(menuItemAttrib, &monoPath);
  124. mShortcutField->get(menuItemAttrib, &shortcut);
  125. path = MonoUtil::monoToString(monoPath);
  126. mPriorityField->get(menuItemAttrib, &priority);
  127. mSeparatorField->get(menuItemAttrib, &separator);
  128. return true;
  129. }
  130. void MenuItemManager::menuItemCallback(MonoMethod* method)
  131. {
  132. method->invoke(nullptr, nullptr);
  133. }
  134. }