BsGUIMenuBar.cpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. #include "BsGUIMenuBar.h"
  2. #include "BsGUIArea.h"
  3. #include "BsGUIElement.h"
  4. #include "BsGUIButton.h"
  5. #include "BsGUITexture.h"
  6. #include "BsGUILayout.h"
  7. #include "BsGUISpace.h"
  8. #include "BsGUIMenu.h"
  9. #include "BsGUIManager.h"
  10. #include "BsBuiltinResources.h"
  11. #include "BsGUIDropDownBoxManager.h"
  12. #include "CmSceneObject.h"
  13. #include "CmPlatform.h"
  14. using namespace CamelotFramework;
  15. using namespace BansheeEngine;
  16. namespace BansheeEditor
  17. {
  18. const UINT32 GUIMenuBar::NUM_ELEMENTS_AFTER_CONTENT = 8;
  19. GUIMenuBar::GUIMenuBar(BS::GUIWidget* parent, RenderWindow* parentWindow)
  20. :mParentWidget(parent), mParentWindow(parentWindow), mMainArea(nullptr), mBackgroundArea(nullptr),
  21. mBgTexture(nullptr), mLogoTexture(nullptr), mSubMenuOpen(false), mSubMenuButton(nullptr)
  22. {
  23. mBackgroundArea = GUIArea::create(*parent, 0, 0, 1, 13, 9900);
  24. mMainArea = GUIArea::create(*parent, 0, 0, 1, 13, 9899);
  25. mBgTexture = GUITexture::create(GUIImageScaleMode::StretchToFit, GUIOptions(GUIOption::flexibleWidth(), GUIOption::flexibleHeight()), "MenuBarBg");
  26. mBackgroundArea->getLayout().addElement(mBgTexture);
  27. mLogoTexture = GUITexture::create(GUIImageScaleMode::StretchToFit, "MenuBarBansheeLogo");
  28. GUILayout& mainLayout = mMainArea->getLayout();
  29. mainLayout.addElement(mLogoTexture);
  30. mainLayout.addSpace(5);
  31. mainLayout.addFlexibleSpace();
  32. mMinBtn = GUIButton::create(HString(L""), "WinMinimizeBtn");
  33. mMaxBtn = GUIButton::create(HString(L""), "WinMaximizeBtn");
  34. mCloseBtn = GUIButton::create(HString(L""), "WinCloseBtn");
  35. mainLayout.addSpace(3);
  36. mainLayout.addElement(mMinBtn);
  37. mainLayout.addSpace(3);
  38. mainLayout.addElement(mMaxBtn);
  39. mainLayout.addSpace(3);
  40. mainLayout.addElement(mCloseBtn);
  41. mainLayout.addSpace(3);
  42. mMinBtn->onClick.connect(std::bind(&GUIMenuBar::onMinimizeClicked, this));
  43. mMaxBtn->onClick.connect(std::bind(&GUIMenuBar::onMaximizeClicked, this));
  44. mCloseBtn->onClick.connect(std::bind(&GUIMenuBar::onCloseClicked, this));
  45. refreshNonClientAreas();
  46. }
  47. GUIMenuBar::~GUIMenuBar()
  48. {
  49. closeSubMenu();
  50. for(auto& menu : mChildMenus)
  51. {
  52. cm_delete<PoolAlloc>(menu.menu);
  53. GUIElement::destroy(menu.button);
  54. }
  55. GUIElement::destroy(mMinBtn);
  56. GUIElement::destroy(mMaxBtn);
  57. GUIElement::destroy(mCloseBtn);
  58. GUIElement::destroy(mBgTexture);
  59. GUIElement::destroy(mLogoTexture);
  60. GUIArea::destroy(mMainArea);
  61. GUIArea::destroy(mBackgroundArea);
  62. }
  63. void GUIMenuBar::setArea(CM::INT32 x, CM::INT32 y, CM::UINT32 width, CM::UINT32 height)
  64. {
  65. mMainArea->setPosition(x, y);
  66. mBackgroundArea->setPosition(x, y);
  67. mMainArea->setSize(width, height);
  68. mBackgroundArea->setSize(width, height);
  69. refreshNonClientAreas();
  70. }
  71. const GUIMenuItem* GUIMenuBar::addMenuItem(const CM::WString& path, std::function<void()> callback)
  72. {
  73. WString strippedPath = path;
  74. WString rootName;
  75. if(!stripPath(strippedPath, rootName))
  76. return nullptr;
  77. const GUIMenuBarData* subMenu = getSubMenu(rootName);
  78. if(subMenu == nullptr)
  79. {
  80. subMenu = addNewButton(rootName);
  81. refreshNonClientAreas();
  82. }
  83. return subMenu->menu->addMenuItem(strippedPath, callback);
  84. }
  85. const GUIMenuItem* GUIMenuBar::addSeparator(const CM::WString& path)
  86. {
  87. WString strippedPath = path;
  88. WString rootName;
  89. if(!stripPath(strippedPath, rootName))
  90. return nullptr;
  91. const GUIMenuBarData* subMenu = getSubMenu(rootName);
  92. if(subMenu == nullptr)
  93. {
  94. subMenu = addNewButton(rootName);
  95. refreshNonClientAreas();
  96. }
  97. return subMenu->menu->addSeparator(strippedPath);
  98. }
  99. GUIMenuBar::GUIMenuBarData* GUIMenuBar::addNewButton(const CM::WString& name)
  100. {
  101. mChildMenus.push_back(GUIMenuBarData());
  102. GUIMenuBarData& newSubMenu = mChildMenus.back();
  103. newSubMenu.name = name;
  104. newSubMenu.menu = cm_new<GUIMenu>();
  105. GUIButton* newButton = GUIButton::create(HString(name), "MenuBarBtn");
  106. newButton->onClick.connect(std::bind(&GUIMenuBar::openSubMenu, this, name));
  107. newButton->onHover.connect(std::bind(&GUIMenuBar::onSubMenuHover, this, name));
  108. mMainArea->getLayout().insertElement(mMainArea->getLayout().getNumChildren() - NUM_ELEMENTS_AFTER_CONTENT, newButton);
  109. newSubMenu.button = newButton;
  110. return &newSubMenu;
  111. }
  112. const GUIMenuItem* GUIMenuBar::getMenuItem(const CM::WString& path) const
  113. {
  114. WString strippedPath = path;
  115. WString rootName;
  116. if(!stripPath(strippedPath, rootName))
  117. return nullptr;
  118. const GUIMenuBarData* subMenu = getSubMenu(rootName);
  119. if(subMenu == nullptr)
  120. return nullptr;
  121. return subMenu->menu->getMenuItem(strippedPath);
  122. }
  123. void GUIMenuBar::removeMenuItem(const CM::WString& path)
  124. {
  125. WString strippedPath = path;
  126. WString rootName;
  127. if(!stripPath(strippedPath, rootName))
  128. return;
  129. if(strippedPath == L"")
  130. {
  131. UINT32 curIdx = 0;
  132. GUIMenuBarData* subMenuToRemove = nullptr;
  133. for(auto& subMenuData : mChildMenus)
  134. {
  135. if(subMenuData.name == rootName)
  136. {
  137. subMenuToRemove = &subMenuData;
  138. break;
  139. }
  140. curIdx++;
  141. }
  142. if(subMenuToRemove == nullptr)
  143. return;
  144. mMainArea->getLayout().removeElement(subMenuToRemove->button);
  145. GUIElement::destroy(subMenuToRemove->button);
  146. cm_delete<PoolAlloc>(subMenuToRemove->menu);
  147. mChildMenus.erase(mChildMenus.begin() + curIdx);
  148. refreshNonClientAreas();
  149. return;
  150. }
  151. const GUIMenuBarData* subMenu = getSubMenu(rootName);
  152. if(subMenu == nullptr)
  153. return;
  154. const GUIMenuItem* item = subMenu->menu->getMenuItem(strippedPath);
  155. if(item == nullptr)
  156. return;
  157. subMenu->menu->removeMenuItem(item);
  158. }
  159. const GUIMenuBar::GUIMenuBarData* GUIMenuBar::getSubMenu(const CM::WString& name) const
  160. {
  161. for(auto& subMenu : mChildMenus)
  162. {
  163. if(subMenu.name == name)
  164. return &subMenu;
  165. }
  166. return nullptr;
  167. }
  168. bool GUIMenuBar::stripPath(CM::WString& path, CM::WString& pathRoot) const
  169. {
  170. Vector<WString>::type pathElements = StringUtil::split(path, L"/");
  171. if(pathElements.size() == 0)
  172. return false;
  173. pathRoot = pathElements[0];
  174. path.erase(0, pathRoot.size());
  175. if(path.size() > 0)
  176. path.erase(0, 1); // Forward slash
  177. return true;
  178. }
  179. void GUIMenuBar::openSubMenu(const CM::WString& name)
  180. {
  181. const GUIMenuBarData* subMenu = getSubMenu(name);
  182. if(subMenu == nullptr)
  183. return;
  184. if(mSubMenuOpen)
  185. {
  186. bool closingExisting = subMenu->button == mSubMenuButton;
  187. closeSubMenu();
  188. if(closingExisting)
  189. return;
  190. }
  191. GUIDropDownData dropDownData = subMenu->menu->getDropDownData();
  192. GUIWidget* widget = subMenu->button->_getParentWidget();
  193. GUIDropDownAreaPlacement placement = GUIDropDownAreaPlacement::aroundBoundsHorz(subMenu->button->getBounds());
  194. GameObjectHandle<GUIDropDownBox> dropDownBox = GUIDropDownBoxManager::instance().openDropDownBox(widget->getTarget(),
  195. placement, dropDownData, widget->getSkin(), GUIDropDownType::MenuBar, std::bind(&GUIMenuBar::onSubMenuClosed, this));
  196. subMenu->button->_setOn(true);
  197. mSubMenuButton = subMenu->button;
  198. mSubMenuOpen = true;
  199. }
  200. void GUIMenuBar::closeSubMenu()
  201. {
  202. if(mSubMenuOpen)
  203. {
  204. GUIDropDownBoxManager::instance().closeDropDownBox();
  205. mSubMenuButton->_setOn(false);
  206. mSubMenuOpen = false;
  207. }
  208. }
  209. void GUIMenuBar::onSubMenuHover(const CM::WString& name)
  210. {
  211. if(mSubMenuOpen)
  212. {
  213. const GUIMenuBarData* subMenu = getSubMenu(name);
  214. if(subMenu == nullptr)
  215. return;
  216. if(mSubMenuButton != subMenu->button)
  217. openSubMenu(name);
  218. }
  219. }
  220. void GUIMenuBar::onSubMenuClosed()
  221. {
  222. mSubMenuButton->_setOn(false);
  223. mSubMenuOpen = false;
  224. }
  225. void GUIMenuBar::onMinimizeClicked()
  226. {
  227. // TODO
  228. }
  229. void GUIMenuBar::onMaximizeClicked()
  230. {
  231. // TODO
  232. }
  233. void GUIMenuBar::onCloseClicked()
  234. {
  235. // TODO
  236. }
  237. void GUIMenuBar::refreshNonClientAreas()
  238. {
  239. // If the size or contents of the area changed this frame the layout won't be updated yet,
  240. // so force the update right away so we get correct element bounds
  241. mMainArea->_update();
  242. CM::Vector<CM::RectI>::type nonClientAreas;
  243. nonClientAreas.push_back(mLogoTexture->getBounds());
  244. if(mChildMenus.size() > 0)
  245. {
  246. RectI lastButtonBounds = mChildMenus.back().button->getBounds();
  247. RectI minButtonBounds = mMinBtn->getBounds();
  248. RectI emptyArea(lastButtonBounds.x + lastButtonBounds.width, mMainArea->y(),
  249. minButtonBounds.x - (lastButtonBounds.x + lastButtonBounds.width), mMainArea->height());
  250. nonClientAreas.push_back(emptyArea);
  251. }
  252. Platform::setCaptionNonClientAreas(*mParentWindow, nonClientAreas);
  253. }
  254. }