BsMenuItemManager.cpp 4.9 KB

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