BsGUIMenuBar.cpp 9.1 KB

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