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/BsGUIElementBase.h"
7#include "GUI/BsGUIOptions.h"
8#include "Math/BsRect2I.h"
9#include "Math/BsVector2I.h"
10#include "Image/BsColor.h"
11
12namespace bs
13{
14 class GUINavGroup;
15
16 /** @addtogroup Implementation
17 * @{
18 */
19
20 enum class GUIElementOption
21 {
22 /**
23 * Enable this option if you want pointer events to pass through this element by default. This will allow elements
24 * underneath this element to receive pointer events.
25 */
26 ClickThrough = 0x01,
27
28 /**
29 * Enable this option if the element accepts keyboard/gamepad input focus. This will allow the element to be
30 * navigated to using keys/buttons.
31 */
32 AcceptsKeyFocus = 0x02
33 };
34
35 typedef Flags<GUIElementOption> GUIElementOptions;
36 BS_FLAGS_OPERATORS(GUIElementOption)
37
38 /**
39 * Represents parent class for all visible GUI elements. Contains methods needed for positioning, rendering and
40 * handling input.
41 */
42 class BS_EXPORT GUIElement : public GUIElementBase
43 {
44 public:
45 /** Different sub-types of GUI elements. */
46 enum class ElementType
47 {
48 Label,
49 Button,
50 Toggle,
51 Texture,
52 InputBox,
53 ListBox,
54 ScrollArea,
55 Layout,
56 Undefined
57 };
58
59 public:
60 GUIElement(String styleName, const GUIDimensions& dimensions, GUIElementOptions options = GUIElementOptions(0));
61 virtual ~GUIElement() = default;
62
63 /**
64 * Change the GUI element focus state.
65 *
66 * @param[in] enabled Give the element focus or take it away.
67 * @param[in] clear If true the focus will be cleared from any elements currently in focus. Otherwise
68 * the element will just be appended to the in-focus list (if enabling focus).
69 */
70 virtual void setFocus(bool enabled, bool clear = false);
71
72 /** Sets the tint of the GUI element. */
73 virtual void setTint(const Color& color);
74
75 /** @copydoc GUIElementBase::resetDimensions */
76 void resetDimensions() override;
77
78 /** Sets new style to be used by the element. */
79 void setStyle(const String& styleName);
80
81 /** Returns the name of the style used by this element. */
82 const String& getStyleName() const { return mStyleName; }
83
84 /** A set of flags controlling various aspects of the GUIElement. See GUIElementOptions. */
85 void setOptionFlags(GUIElementOptions options) { mOptionFlags = options; }
86
87 /** @copydoc setOptionFlags */
88 GUIElementOptions getOptionFlags() const { return mOptionFlags; }
89
90 /**
91 * Assigns a new context menu that will be opened when the element is right clicked. Null is allowed in case no
92 * context menu is wanted.
93 */
94 void setContextMenu(const SPtr<GUIContextMenu>& menu) { mContextMenu = menu; }
95
96 /**
97 * Sets a navigation group that determines in what order are GUI elements visited when using a keyboard or gamepad
98 * to switch between the elements. If you don't set a navigation group the elements will inherit the default
99 * navigation group from their parent GUIWidget. Also see setNavGroupIndex().
100 */
101 void setNavGroup(const SPtr<GUINavGroup>& navGroup);
102
103 /**
104 * Sets the index that determines in what order is the element visited compared to all the other elements in the
105 * nav-group. Elements with lower index will be visited before elements with a higher index. Elements with index
106 * 0 (the default) are special and will have their visit order determines by their position compared to other
107 * elements. The applied index is tied to the nav-group, so if the nav-group changes the index will need to be
108 * re-applied.
109 */
110 void setNavGroupIndex(INT32 index);
111
112 /** @copydoc GUIElementBase::getVisibleBounds */
113 Rect2I getVisibleBounds() override;
114
115 /**
116 * Destroy the element. Removes it from parent and widget, and queues it for deletion. Element memory will be
117 * released delayed, next frame.
118 */
119 static void destroy(GUIElement* element);
120
121 /** Triggered when the element loses or gains focus. */
122 Event<void(bool)> onFocusChanged;
123
124 public: // ***** INTERNAL ******
125 /** @name Internal
126 * @{
127 */
128
129 /**
130 * Returns the number of separate render elements in the GUI element.
131 *
132 * @return The number render elements.
133 *
134 * @note
135 * GUI system attempts to reduce the number of GUI meshes so it will group sprites based on their material and
136 * textures. One render elements represents a group of such sprites that share a material/texture.
137 */
138 virtual UINT32 _getNumRenderElements() const = 0;
139
140 /**
141 * Gets a material for the specified render element index.
142 *
143 * @return Handle to the material.
144 *
145 * @see _getNumRenderElements()
146 */
147 virtual const SpriteMaterialInfo& _getMaterial(UINT32 renderElementIdx, SpriteMaterial** material) const = 0;
148
149 /**
150 * Returns the type of mesh and number of vertices and indices that the specified render element will use. You will
151 * need this value when creating the buffers before calling _fillBuffer().
152 *
153 * @see _getNumRenderElements()
154 * @see _fillBuffer()
155 */
156 virtual void _getMeshInfo(UINT32 renderElementIdx, UINT32& numVertices, UINT32& numIndices, GUIMeshType& type) const = 0;
157
158 /**
159 * Fill the pre-allocated vertex, uv and index buffers with the mesh data for the specified render element.
160 *
161 * @param[out] vertices Previously allocated buffer where to store the vertices. Output is expected
162 * to match the GUIMeshType as returned by _getMeshInfo.
163 * @param[out] indices Previously allocated buffer where to store the indices.
164 * @param[in] vertexOffset At which vertex should the method start filling the buffer.
165 * @param[in] indexOffset At which index should the method start filling the buffer.
166 * @param[in] maxNumVerts Total number of vertices the buffers were allocated for. Used only for memory
167 * safety.
168 * @param[in] maxNumIndices Total number of indices the buffers were allocated for. Used only for memory
169 * safety.
170 * @param[in] renderElementIdx Zero-based index of the render element.
171 *
172 * @see _getNumRenderElements()
173 * @see _getMeshInfo()
174 */
175 virtual void _fillBuffer(UINT8* vertices, UINT32* indices, UINT32 vertexOffset, UINT32 indexOffset,
176 UINT32 maxNumVerts, UINT32 maxNumIndices, UINT32 renderElementIdx) const = 0;
177
178 /**
179 * Recreates the internal render elements. Must be called before fillBuffer if element is dirty. Marks the element
180 * as non dirty.
181 */
182 void _updateRenderElements();
183
184 /** Gets internal element style representing the exact type of GUI element in this object. */
185 virtual ElementType _getElementType() const { return ElementType::Undefined; }
186
187 /**
188 * Called when a mouse event is received on any GUI element the mouse is interacting with. Return true if you have
189 * processed the event and don't want other elements to process it.
190 */
191 virtual bool _mouseEvent(const GUIMouseEvent& ev);
192
193 /**
194 * Called when some text is input and the GUI element has input focus. Return true if you have processed the event
195 * and don't want other elements to process it.
196 */
197 virtual bool _textInputEvent(const GUITextInputEvent& ev);
198
199 /**
200 * Called when a command event is triggered. Return true if you have processed the event and don't want other
201 * elements to process it.
202 */
203 virtual bool _commandEvent(const GUICommandEvent& ev);
204
205 /**
206 * Called when a virtual button is pressed/released and the GUI element has input focus. Return true if you have
207 * processed the event and don't want other elements to process it.
208 */
209 virtual bool _virtualButtonEvent(const GUIVirtualButtonEvent& ev);
210
211 /** Set element part of element depth. Less significant than both widget and area depth. */
212 void _setElementDepth(UINT8 depth);
213
214 /** Retrieve element part of element depth. Less significant than both widget and area depth. */
215 UINT8 _getElementDepth() const;
216
217 /** @copydoc GUIElementBase::_setLayoutData */
218 void _setLayoutData(const GUILayoutData& data) override;
219
220 /** @copydoc GUIElementBase::_changeParentWidget */
221 void _changeParentWidget(GUIWidget* widget) override;
222
223 /**
224 * Returns depth for a specific render element. This contains a combination of widget depth (8 bit(, area depth
225 * (16 bit) and render element depth (8 bit).
226 *
227 * @see _getNumRenderElements
228 */
229 virtual UINT32 _getRenderElementDepth(UINT32 renderElementIdx) const { return _getDepth(); }
230
231 /**
232 * Returns the range of depths that the child elements can be rendered it.
233 *
234 * @note
235 * For example if you are rendering a button with an image and a text you will want the text to be rendered in front
236 * of the image at a different depth, which means the depth range is 2 (0 for text, 1 for background image).
237 */
238 virtual UINT32 _getRenderElementDepthRange() const { return 1; }
239
240 /** Gets internal element style representing the exact type of GUI element in this object. */
241 Type _getType() const override { return GUIElementBase::Type::Element; }
242
243 /** Checks if element has been destroyed and is queued for deletion. */
244 bool _isDestroyed() const override { return mIsDestroyed; }
245
246 /** Update element style based on active GUI skin and style name. */
247 void _refreshStyle();
248
249 /** Gets the currently active element style. */
250 const GUIElementStyle* _getStyle() const { return mStyle; }
251
252 /** Gets GUI element bounds relative to parent widget, clipped by specified clip rect. */
253 const Rect2I& _getClippedBounds() const { return mClippedBounds; }
254
255 /**
256 * Returns GUI element padding. Padding is modified by changing element style and determines minimum distance
257 * between different GUI elements.
258 */
259 const RectOffset& _getPadding() const override;
260
261 /**
262 * Returns GUI element depth. This includes widget and area depth, but does not include specific per-render-element
263 * depth.
264 */
265 UINT32 _getDepth() const { return mLayoutData.depth; }
266
267 /** Returns the navigation group this element belongs to. See setNavGroup(). */
268 SPtr<GUINavGroup> _getNavGroup() const;
269
270 /** Checks is the specified position within GUI element bounds. Position is relative to parent GUI widget. */
271 virtual bool _isInBounds(const Vector2I position) const;
272
273 /** Checks if the GUI element has a custom cursor and outputs the cursor type if it does. */
274 virtual bool _hasCustomCursor(const Vector2I position, CursorType& type) const { return false; }
275
276 /** Checks if the GUI element accepts a drag and drop operation of the specified type. */
277 virtual bool _acceptDragAndDrop(const Vector2I position, UINT32 typeId) const { return false; }
278
279 /** Returns a context menu if a GUI element has one. Otherwise returns nullptr. */
280 virtual SPtr<GUIContextMenu> _getContextMenu() const;
281
282 /** Returns text to display when hovering over the element. Returns empty string if no tooltip. */
283 virtual String _getTooltip() const { return StringUtil::BLANK; }
284
285 /** Returns a clip rectangle relative to the element, used for offsetting the input text. */
286 virtual Vector2I _getTextInputOffset() const { return Vector2I(); }
287
288 /** Returns a clip rectangle relative to the element, used for clipping the input text. */
289 virtual Rect2I _getTextInputRect() const { return Rect2I(); }
290
291 /** @} */
292
293 protected:
294 /** Called whenever render elements are dirty and need to be rebuilt. */
295 virtual void updateRenderElementsInternal();
296
297 /**
298 * Called whenever element clipped bounds need to be recalculated. (for example when width, height or clip
299 * rectangles changes).
300 */
301 virtual void updateClippedBounds();
302
303 /**
304 * Helper method that returns style name used by an element of a certain type. If override style is empty, default
305 * style for that type is returned.
306 */
307 template<class T>
308 static const String& getStyleName(const String& overrideStyle)
309 {
310 if(overrideStyle == StringUtil::BLANK)
311 return T::getGUITypeName();
312
313 return overrideStyle;
314 }
315
316 /**
317 * Attempts to find a sub-style for the specified type in the currently set GUI element style. If one cannot be
318 * found empty string is returned.
319 */
320 const String& getSubStyleName(const String& subStyleTypeName) const;
321
322 /** Method that gets triggered whenever element style changes. */
323 virtual void styleUpdated() { }
324
325 /** Returns clipped bounds excluding the margins. Relative to parent widget. */
326 Rect2I getCachedVisibleBounds() const;
327
328 /** Returns bounds of the content contained within the GUI element. Relative to parent widget. */
329 Rect2I getCachedContentBounds() const;
330
331 /**
332 * Returns a clip rectangle that can be used for clipping the contents of this GUI element. Clip rect is relative
333 * to GUI element origin.
334 */
335 Rect2I getCachedContentClipRect() const;
336
337 /** Returns the tint that is applied to the GUI element. */
338 Color getTint() const;
339
340 bool mIsDestroyed = false;
341 GUIElementOptions mOptionFlags;
342 Rect2I mClippedBounds;
343
344 private:
345 static const Color DISABLED_COLOR;
346
347 const GUIElementStyle* mStyle;
348 String mStyleName;
349
350 SPtr<GUIContextMenu> mContextMenu;
351 SPtr<GUINavGroup> mNavGroup;
352 Color mColor;
353 };
354
355 /** @} */
356}