BsGUIDropDownBox.h 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. #pragma once
  2. #include "BsPrerequisites.h"
  3. #include "BsGUIWidget.h"
  4. #include "BsVector2I.h"
  5. #include "BsEvent.h"
  6. namespace BansheeEngine
  7. {
  8. /**
  9. * @brief Contains items used for initializing one level in a drop down box hierarchy.
  10. */
  11. struct BS_EXPORT GUIDropDownData
  12. {
  13. Vector<GUIDropDownDataEntry> entries;
  14. UnorderedMap<WString, HString> localizedNames;
  15. };
  16. /**
  17. * @brief Represents a single entry in a drop down box.
  18. */
  19. class BS_EXPORT GUIDropDownDataEntry
  20. {
  21. enum class Type
  22. {
  23. Separator,
  24. Entry,
  25. SubMenu
  26. };
  27. public:
  28. /**
  29. * @brief Creates a new separator entry.
  30. */
  31. static GUIDropDownDataEntry separator();
  32. /**
  33. * @brief Creates a new button entry with the specified callback that is triggered
  34. * when button is selected.
  35. */
  36. static GUIDropDownDataEntry button(const WString& label, std::function<void()> callback, const WString& shortcutTag = StringUtil::WBLANK);
  37. /**
  38. * @brief Creates a new sub-menu entry that will open the provided drop down data
  39. * sub-menu when activated.
  40. */
  41. static GUIDropDownDataEntry subMenu(const WString& label, const GUIDropDownData& data);
  42. /**
  43. * @brief Check is the entry a separator.
  44. */
  45. bool isSeparator() const { return mType == Type::Separator; }
  46. /**
  47. * @brief Check is the entry a sub menu.
  48. */
  49. bool isSubMenu() const { return mType == Type::SubMenu; }
  50. /**
  51. * @brief Returns display label of the entry (if an entry is a button or a sub-menu).
  52. */
  53. const WString& getLabel() const { return mLabel; }
  54. /**
  55. * @brief Returns the shortcut key combination string that is to be displayed along the entry label.
  56. */
  57. const WString& getShortcutTag() const { return mShortcutTag; }
  58. /**
  59. * @brief Returns a button callback if the entry (if an entry is a button).
  60. */
  61. std::function<void()> getCallback() const { return mCallback; }
  62. /**
  63. * @brief Returns sub-menu data that is used for creating a sub-menu (if an entry is a sub-menu).
  64. */
  65. const GUIDropDownData& getSubMenuData() const { return mChildData; }
  66. private:
  67. GUIDropDownDataEntry() { }
  68. std::function<void()> mCallback;
  69. GUIDropDownData mChildData;
  70. WString mLabel;
  71. WString mShortcutTag;
  72. Type mType;
  73. };
  74. /**
  75. * @brief Determines how will the drop down box be positioned. Usually the system will attempt to position
  76. * the drop box in a way so all elements can fit, and this class allows you to specify some limitations
  77. * on how that works.
  78. *
  79. * @note For example, list boxes usually want drop down boxes to be placed above or below them, while
  80. * context menus may want to have them placed around a single point in any direction.
  81. */
  82. class BS_EXPORT GUIDropDownAreaPlacement
  83. {
  84. public:
  85. /**
  86. * @brief Determines how will the drop down box be positioned.
  87. */
  88. enum class Type
  89. {
  90. Position,
  91. BoundsVert,
  92. BoundsHorz
  93. };
  94. /**
  95. * @brief Drop down box will be placed at the specified position. By default the system
  96. * prefers the top left corner of the box to correspond to the position, but if
  97. * other corners offer more space for the contents, those will be used instead.
  98. */
  99. static GUIDropDownAreaPlacement aroundPosition(const Vector2I& position);
  100. /**
  101. * @brief Drop down box will be placed at the specified bounds. Box will be horizontally aligned to the left
  102. * of the provided bounds. Vertically system prefers placing the box at the bottom of the bounds, but may choose
  103. * to align it with the top of the bounds if it offers more space for the contents.
  104. */
  105. static GUIDropDownAreaPlacement aroundBoundsVert(const Rect2I& bounds);
  106. /**
  107. * @brief Drop down box will be placed at the specified bounds. Box will be vertically aligned to the top
  108. * of the provided bounds. Horizontally system prefers placing the box at the right of the bounds, but may choose
  109. * to align it with the left of the bounds if it offers more space for the contents.
  110. */
  111. static GUIDropDownAreaPlacement aroundBoundsHorz(const Rect2I& bounds);
  112. /**
  113. * @brief Returns drop down box positioning type.
  114. */
  115. Type getType() const { return mType; }
  116. /**
  117. * @brief Returns bounds around which to position the drop down box
  118. * if one of the bounds positioning types is used.
  119. */
  120. const Rect2I& getBounds() const { return mBounds; }
  121. /**
  122. * @brief Returns position around which to position the drop down box
  123. * if position positioning type is used.
  124. */
  125. const Vector2I& getPosition() const { return mPosition; }
  126. private:
  127. GUIDropDownAreaPlacement() { }
  128. Type mType;
  129. Rect2I mBounds;
  130. Vector2I mPosition;
  131. };
  132. /**
  133. * @brief Type of drop down box types.
  134. */
  135. enum class GUIDropDownType
  136. {
  137. ListBox,
  138. ContextMenu,
  139. MenuBar
  140. };
  141. /**
  142. * @brief This is a generic GUI drop down box class that can be used for:
  143. * list boxes, menu bars or context menus.
  144. */
  145. class BS_EXPORT GUIDropDownBox : public GUIWidget
  146. {
  147. public:
  148. /**
  149. * @brief Creates a new drop down box widget.
  150. *
  151. * @param parent Parent scene object to attach the drop down box to.
  152. * @param target Viewport on which to open the drop down box.
  153. * @param placement Determines how is the drop down box positioned in the visible area.
  154. * @param dropDownData Data to use for initializing menu items of the drop down box.
  155. * @param skin Skin to use for drop down box GUI elements.
  156. * @param type Specific type of drop down box to display.
  157. */
  158. GUIDropDownBox(const HSceneObject& parent, Viewport* target, const GUIDropDownAreaPlacement& placement,
  159. const GUIDropDownData& dropDownData, const GUISkin& skin, GUIDropDownType type);
  160. ~GUIDropDownBox();
  161. private:
  162. /**
  163. * @brief Contains data about a single drop down box sub-menu
  164. */
  165. struct DropDownSubMenu
  166. {
  167. /**
  168. * @brief Represents a single sub-menu page.
  169. */
  170. struct PageInfo
  171. {
  172. UINT32 idx;
  173. UINT32 start;
  174. UINT32 end;
  175. UINT32 height;
  176. };
  177. public:
  178. /**
  179. * @brief Creates a new drop down box sub-menu
  180. *
  181. * @param owner Owner drop down box this sub menu belongs to.
  182. * @param parent Parent sub-menu. Can be null.
  183. * @param placement Determines how is the sub-menu positioned in the visible area.
  184. * @param availableBounds Available bounds (in pixels) in which the sub-menu may be opened.
  185. * @param dropDownData Data to use for initializing menu items of the sub-menu.
  186. * @param skin Skin to use for sub-menu GUI elements.
  187. * @param depthOffset How much to offset the sub-menu depth. We want deeper levels of the
  188. * sub-menu hierarchy to be in front of lower levels, so you should
  189. * increase this value for each level of the sub-menu hierarchy.
  190. */
  191. DropDownSubMenu(GUIDropDownBox* owner, DropDownSubMenu* parent, const GUIDropDownAreaPlacement& placement,
  192. const Rect2I& availableBounds, const GUIDropDownData& dropDownData, GUIDropDownType type, UINT32 depthOffset);
  193. ~DropDownSubMenu();
  194. /**
  195. * @brief Recreates all internal GUI elements for the entries of the current sub-menu page.
  196. */
  197. void updateGUIElements();
  198. /**
  199. * @brief Moves the sub-menu to the previous page and displays its elements, if available.
  200. */
  201. void scrollDown();
  202. /**
  203. * @brief Moves the sub-menu to the next page and displays its elements, if available.
  204. */
  205. void scrollUp();
  206. /**
  207. * @brief Moves the sub-menu to the first page and displays its elements.
  208. */
  209. void scrollToTop();
  210. /**
  211. * @brief Moves the sub-menu to the last page and displays its elements.
  212. */
  213. void scrollToBottom();
  214. /**
  215. * @brief Calculates ranges for all the pages of the sub-menu.
  216. */
  217. Vector<PageInfo> getPageInfos() const;
  218. /**
  219. * @brief Called when the user activates an element with the specified index.
  220. *
  221. * @param bounds Bounds of the GUI element that is used as a visual representation
  222. * of this drop down element.
  223. */
  224. void elementActivated(UINT32 idx, const Rect2I& bounds);
  225. /**
  226. * @brief Called when the user selects an element with the specified index.
  227. *
  228. * @param idx Index of the element that was selected.
  229. */
  230. void elementSelected(UINT32 idx);
  231. /**
  232. * @brief Called when the user wants to close the currently open sub-menu.
  233. */
  234. void closeSubMenu();
  235. /**
  236. * @brief Closes this sub-menu.
  237. */
  238. void close();
  239. /**
  240. * @brief Returns actual visible bounds of the sub-menu.
  241. */
  242. Rect2I getVisibleBounds() const { return mVisibleBounds; }
  243. /**
  244. * @brief Returns the drop box object that owns this sub-menu.
  245. */
  246. GUIDropDownBox* getOwner() const { return mOwner; }
  247. public:
  248. GUIDropDownBox* mOwner;
  249. GUIDropDownType mType;
  250. GUIDropDownData mData;
  251. UINT32 mPage;
  252. INT32 x, y;
  253. UINT32 width, height;
  254. Rect2I mVisibleBounds;
  255. Rect2I mAvailableBounds;
  256. UINT32 mDepthOffset;
  257. bool mOpenedUpward;
  258. GUIButton* mScrollUpBtn;
  259. GUIButton* mScrollDownBtn;
  260. GUIDropDownContent* mContent;
  261. GUITexture* mBackgroundFrame;
  262. GUIArea* mBackgroundArea;
  263. GUIArea* mContentArea;
  264. GUILayout* mContentLayout;
  265. DropDownSubMenu* mParent;
  266. DropDownSubMenu* mSubMenu;
  267. };
  268. private:
  269. friend class GUIDropDownContent;
  270. /**
  271. * @brief Called when the specified sub-menu is opened.
  272. */
  273. void notifySubMenuOpened(DropDownSubMenu* subMenu);
  274. /**
  275. * @brief Called when the specified sub-menu is opened.
  276. */
  277. void notifySubMenuClosed(DropDownSubMenu* subMenu);
  278. /**
  279. * @brief Called when the drop down box loses focus (and should be closed).
  280. */
  281. void dropDownFocusLost();
  282. /**
  283. * @copydoc GUIWidget::onDestroyed
  284. */
  285. void onDestroyed() override;
  286. private:
  287. static const UINT32 DROP_DOWN_BOX_WIDTH;
  288. String mScrollUpStyle;
  289. String mScrollDownStyle;
  290. String mBackgroundStyle;
  291. String mContentStyle;
  292. HSpriteTexture mScrollUpBtnArrow;
  293. HSpriteTexture mScrollDownBtnArrow;
  294. DropDownSubMenu* mRootMenu;
  295. GUIDropDownHitBox* mHitBox;
  296. // Captures mouse clicks so that we don't trigger elements outside the drop down box when we just want to close it.
  297. // (Particular example is clicking on the button that opened the drop down box in the first place. Clicking will cause
  298. // the drop down to lose focus and close, but if the button still processes the mouse click it will be immediately opened again)
  299. GUIDropDownHitBox* mCaptureHitBox;
  300. };
  301. }