1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#pragma once
4
5#include "BsPrerequisites.h"
6#include "GUI/BsCGUIWidget.h"
7#include "Math/BsRect2I.h"
8#include "GUI/BsDropDownAreaPlacement.h"
9
10namespace bs
11{
12 /** @addtogroup GUI-Internal
13 * @{
14 */
15
16 /** Contains items used for initializing one level in a drop down box hierarchy. */
17 struct BS_EXPORT GUIDropDownData
18 {
19 Vector<GUIDropDownDataEntry> entries;
20 Vector<bool> states;
21 UnorderedMap<String, HString> localizedNames;
22 };
23
24 /** A set of parameters used for initializing a drop down box. */
25 struct DROP_DOWN_BOX_DESC
26 {
27 SPtr<Camera> camera; /**< Camera on which to open the drop down box. */
28 DropDownAreaPlacement placement; /**< Determines how is the drop down box positioned in the visible area. */
29 GUIDropDownData dropDownData; /**< Data to use for initializing menu items of the drop down box. */
30 HGUISkin skin; /**< Skin to use for drop down box GUI elements. */
31 /** Additional bounds that control what is considered the inside or the outside of the drop down box. */
32 Vector<Rect2I> additionalBounds;
33 };
34
35 /** Represents a single entry in a drop down box. */
36 class BS_EXPORT GUIDropDownDataEntry
37 {
38 enum class Type
39 {
40 Separator,
41 Entry,
42 SubMenu
43 };
44
45 public:
46 /** Creates a new separator entry. */
47 static GUIDropDownDataEntry separator();
48
49 /** Creates a new button entry with the specified callback that is triggered when button is selected. */
50 static GUIDropDownDataEntry button(const String& label, std::function<void()> callback,
51 const String& shortcutTag = StringUtil::BLANK);
52
53 /** Creates a new sub-menu entry that will open the provided drop down data sub-menu when activated. */
54 static GUIDropDownDataEntry subMenu(const String& label, const GUIDropDownData& data);
55
56 /** Check is the entry a separator. */
57 bool isSeparator() const { return mType == Type::Separator; }
58
59 /** Check is the entry a sub menu. */
60 bool isSubMenu() const { return mType == Type::SubMenu; }
61
62 /** Returns display label of the entry (if an entry is a button or a sub-menu). */
63 const String& getLabel() const { return mLabel; }
64
65 /** Returns the shortcut key combination string that is to be displayed along the entry label. */
66 const String& getShortcutTag() const { return mShortcutTag; }
67
68 /** Returns a button callback if the entry (if an entry is a button). */
69 std::function<void()> getCallback() const { return mCallback; }
70
71 /** Returns sub-menu data that is used for creating a sub-menu (if an entry is a sub-menu). */
72 const GUIDropDownData& getSubMenuData() const { return mChildData; }
73 private:
74 GUIDropDownDataEntry() { }
75
76 std::function<void()> mCallback;
77 GUIDropDownData mChildData;
78 String mLabel;
79 String mShortcutTag;
80 Type mType;
81 };
82
83 /** Type of drop down box types. */
84 enum class GUIDropDownType
85 {
86 ListBox,
87 MultiListBox,
88 ContextMenu,
89 MenuBar
90 };
91
92 /** This is a generic GUI drop down box class that can be used for: list boxes, menu bars or context menus. */
93 class BS_EXPORT GUIDropDownMenu : public CGUIWidget
94 {
95 public:
96 /**
97 * Creates a new drop down box widget.
98 *
99 * @param[in] parent Parent scene object to attach the drop down box to.
100 * @param[in] desc Various parameters that control the drop down menu features and content.
101 * @param[in] type Specific type of drop down box to display.
102 */
103 GUIDropDownMenu(const HSceneObject& parent, const DROP_DOWN_BOX_DESC& desc, GUIDropDownType type);
104 ~GUIDropDownMenu();
105
106 private:
107 /** Contains data about a single drop down box sub-menu. */
108 struct DropDownSubMenu
109 {
110 /** Represents a single sub-menu page. */
111 struct PageInfo
112 {
113 UINT32 idx;
114 UINT32 start;
115 UINT32 end;
116 UINT32 height;
117 };
118
119 public:
120 /**
121 * Creates a new drop down box sub-menu.
122 *
123 * @param[in] owner Owner drop down box this sub menu belongs to.
124 * @param[in] parent Parent sub-menu. Can be null.
125 * @param[in] placement Determines how is the sub-menu positioned in the visible area.
126 * @param[in] availableBounds Available bounds (in pixels) in which the sub-menu may be opened.
127 * @param[in] dropDownData Data to use for initializing menu items of the sub-menu.
128 * @param[in] skin Skin to use for sub-menu GUI elements.
129 * @param[in] depthOffset How much to offset the sub-menu depth. We want deeper levels of the sub-menu
130 * hierarchy to be in front of lower levels, so you should increase this value for
131 * each level of the sub-menu hierarchy.
132 */
133 DropDownSubMenu(GUIDropDownMenu* owner, DropDownSubMenu* parent, const DropDownAreaPlacement& placement,
134 const Rect2I& availableBounds, const GUIDropDownData& dropDownData, GUIDropDownType type, UINT32 depthOffset);
135 ~DropDownSubMenu();
136
137 /** Recreates all internal GUI elements for the entries of the current sub-menu page. */
138 void updateGUIElements();
139
140 /** Moves the sub-menu to the previous page and displays its elements, if available. */
141 void scrollDown();
142
143 /** Moves the sub-menu to the next page and displays its elements, if available. */
144 void scrollUp();
145
146 /** Moves the sub-menu to the first page and displays its elements. */
147 void scrollToTop();
148
149 /** Moves the sub-menu to the last page and displays its elements. */
150 void scrollToBottom();
151
152 /** Calculates ranges for all the pages of the sub-menu. */
153 Vector<PageInfo> getPageInfos() const;
154
155 /**
156 * Called when the user activates an element with the specified index.
157 *
158 * @param[in] bounds Bounds of the GUI element that is used as a visual representation of this drop down
159 * element.
160 */
161 void elementActivated(UINT32 idx, const Rect2I& bounds);
162
163 /**
164 * Called when the user selects an element with the specified index.
165 *
166 * @param[in] idx Index of the element that was selected.
167 */
168 void elementSelected(UINT32 idx);
169
170 /** Called when the user wants to close the currently open sub-menu. */
171 void closeSubMenu();
172
173 /** Closes this sub-menu. */
174 void close();
175
176 /** Returns the type of the displayed drop down menu. */
177 GUIDropDownType getType() const { return mType; }
178
179 /** Returns actual visible bounds of the sub-menu. */
180 Rect2I getVisibleBounds() const { return mVisibleBounds; }
181
182 /** Returns the drop box object that owns this sub-menu. */
183 GUIDropDownMenu* getOwner() const { return mOwner; }
184
185 public:
186 GUIDropDownMenu* mOwner;
187
188 GUIDropDownType mType;
189 GUIDropDownData mData;
190 UINT32 mPage;
191 INT32 x, y;
192 UINT32 width, height;
193 Rect2I mVisibleBounds;
194 Rect2I mAvailableBounds;
195 UINT32 mDepthOffset;
196 bool mOpenedUpward;
197
198 GUIDropDownContent* mContent;
199 GUITexture* mBackgroundFrame;
200 GUIButton* mScrollUpBtn;
201 GUIButton* mScrollDownBtn;
202 GUITexture* mHandle;
203
204 GUIPanel* mBackgroundPanel;
205 GUIPanel* mContentPanel;
206 GUILayout* mContentLayout;
207 GUIPanel* mSidebarPanel;
208
209 DropDownSubMenu* mParent;
210 DropDownSubMenu* mSubMenu;
211 };
212
213 private:
214 friend class GUIDropDownContent;
215
216 /** Called when the specified sub-menu is opened. */
217 void notifySubMenuOpened(DropDownSubMenu* subMenu);
218
219 /** Called when the specified sub-menu is opened. */
220 void notifySubMenuClosed(DropDownSubMenu* subMenu);
221
222 /** Called when the drop down box loses focus (and should be closed). */
223 void dropDownFocusLost();
224
225 /** @copydoc GUIWidget::onDestroyed */
226 void onDestroyed() override;
227
228 private:
229 static const UINT32 DROP_DOWN_BOX_WIDTH;
230
231 String mScrollUpStyle;
232 String mScrollDownStyle;
233 String mBackgroundStyle;
234 String mContentStyle;
235 String mSideBackgroundStyle;
236 String mHandleStyle;
237
238 DropDownSubMenu* mRootMenu;
239 GUIDropDownHitBox* mFrontHitBox;
240 GUIDropDownHitBox* mBackHitBox;
241
242 // Captures mouse clicks so that we don't trigger elements outside the drop down box when we just want to close it.
243 // (Particular example is clicking on the button that opened the drop down box in the first place. Clicking will cause
244 // the drop down to lose focus and close, but if the button still processes the mouse click it will be immediately opened again)
245 GUIDropDownHitBox* mCaptureHitBox;
246
247 Vector<Rect2I> mAdditionalCaptureBounds;
248 };
249
250 /** @} */
251}