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/BsGUIDimensions.h"
7#include "GUI/BsGUILayoutData.h"
8#include "Math/BsRect2I.h"
9#include "Math/BsVector2I.h"
10#include "Utility/BsRectOffset.h"
11
12namespace bs
13{
14 /** @addtogroup Implementation
15 * @{
16 */
17
18 /** Base class for all GUI elements (visible or layout). */
19 class BS_EXPORT GUIElementBase
20 {
21 public:
22 /** Valid types of GUI base elements. */
23 enum class Type
24 {
25 Layout,
26 Element,
27 FixedSpace,
28 FlexibleSpace,
29 Panel
30 };
31
32 protected:
33 /** Flags that signal the state of the GUI element. */
34 enum GUIElementFlags
35 {
36 GUIElem_Dirty = 0x01,
37 GUIElem_Hidden = 0x02,
38 GUIElem_Inactive = 0x04,
39 GUIElem_HiddenSelf = 0x08,
40 GUIElem_InactiveSelf = 0x10,
41 GUIElem_Disabled = 0x20,
42 GUIElem_DisabledSelf = 0x40
43 };
44
45 public:
46 GUIElementBase() = default;
47 GUIElementBase(const GUIDimensions& dimensions);
48 virtual ~GUIElementBase();
49
50 /**
51 * Sets element position relative to parent GUI panel.
52 *
53 * @note
54 * Be aware that this value will be ignored if GUI element is part of a layout since then the layout controls its
55 * placement.
56 */
57 void setPosition(INT32 x, INT32 y);
58
59 /** Sets element width in pixels. */
60 void setWidth(UINT32 width);
61
62 /**
63 * Sets element width in pixels. Element will be resized according to its contents and parent layout but will
64 * always stay within the provided range. If maximum width is zero, the element is allowed to expand as much as
65 * it needs.
66 */
67 void setFlexibleWidth(UINT32 minWidth = 0, UINT32 maxWidth = 0);
68
69 /** Sets element height in pixels. */
70 void setHeight(UINT32 height);
71
72 /** Sets width and height of a GUI element in pixels. */
73 void setSize(UINT32 width, UINT32 height);
74
75 /**
76 * Sets element height in pixels. Element will be resized according to its contents and parent layout but will
77 * always stay within the provided range. If maximum height is zero, the element is allowed to expand as much as
78 * it needs.
79 */
80 void setFlexibleHeight(UINT32 minHeight = 0, UINT32 maxHeight = 0);
81
82 /** Resets element dimensions to their initial values dictated by the element's style. */
83 virtual void resetDimensions();
84
85 /**
86 * Hides or shows this element and recursively applies the same state to all the child elements. This will not
87 * remove the element from the layout, the room for it will still be reserved but it just won't be visible.
88 */
89 void setVisible(bool visible);
90
91 /**
92 * Activates or deactives this element and recursively applies the same state to all the child elements. This has
93 * the same effect as setVisible(), but when disabled it will also remove the element from the layout, essentially
94 * having the same effect is if you destroyed the element.
95 */
96 void setActive(bool active);
97
98 /** Disables or enables the element. Disabled elements cannot be interacted with and have a faded out appearance. */
99 void setDisabled(bool disabled);
100
101 /**
102 * Returns non-clipped bounds of the GUI element. Relative to a parent GUI panel.
103 *
104 * @param[in] relativeTo Parent panel of the provided element relative to which to return the bounds. If null
105 * the bounds relative to the first parent panel are returned. Behavior is undefined if
106 * provided panel is not a parent of the element.
107 *
108 * @note This call can be potentially expensive if the GUI state is dirty.
109 */
110 Rect2I getBounds(GUIPanel* relativeTo = nullptr);
111
112 /**
113 * Sets the bounds of the GUI element. Relative to a parent GUI panel. Equivalent to calling setPosition(),
114 * setWidth() and setHeight().
115 */
116 void setBounds(const Rect2I& bounds);
117
118 /**
119 * Returns non-clipped bounds of the GUI element. Relative to a parent GUI widget.
120 *
121 * @note This call can be potentially expensive if the GUI state is dirty.
122 */
123 Rect2I getGlobalBounds();
124
125 /**
126 * Returns non-clipped bounds of the GUI element in screenspace.
127 *
128 * @note This call can be potentially expensive if the GUI state is dirty.
129 */
130 Rect2I getScreenBounds() const;
131
132 /**
133 * Returns non-clipped visible bounds of the GUI element (bounds exclude the margins). Relative to the parent GUI
134 * panel.
135 *
136 * @note This call can be potentially expensive as the bounds need to be calculated based on current GUI state.
137 */
138 virtual Rect2I getVisibleBounds();
139
140 public: // ***** INTERNAL ******
141 /** @name Internal
142 * @{
143 */
144
145 /**
146 * Updates child elements positions, sizes, clip rectangles and depths so they fit into the provided bounds, while
147 * respecting their layout options.
148 *
149 * @param[in] data Layout data containing the necessary bounds and restrictions to use for calculating the
150 * child element layout data.
151 */
152 virtual void _updateLayout(const GUILayoutData& data);
153
154 /** Calculates optimal sizes of all child elements, as determined by their style and layout options. */
155 virtual void _updateOptimalLayoutSizes();
156
157 /** @copydoc _updateLayout */
158 virtual void _updateLayoutInternal(const GUILayoutData& data);
159
160 /**
161 * Calculates positions & sizes of all elements in the layout. This method expects a pre-allocated array to store
162 * the data in.
163 *
164 * @param[in] layoutArea Parent layout area to position the child elements in.
165 * @param[out] elementAreas Array to hold output areas. Must be the same size as the number of child elements.
166 * @param[in] numElements Size of the element areas array.
167 * @param[in] sizeRanges Ranges of possible sizes used for the child elements. Array must be same size as
168 * elements array.
169 * @param[in] mySizeRange Size range of this element.
170 */
171 virtual void _getElementAreas(const Rect2I& layoutArea, Rect2I* elementAreas, UINT32 numElements,
172 const Vector<LayoutSizeRange>& sizeRanges, const LayoutSizeRange& mySizeRange) const;
173
174 /** Updates layout data that determines GUI elements final position & depth in the GUI widget. */
175 virtual void _setLayoutData(const GUILayoutData& data) { mLayoutData = data; }
176
177 /** Retrieves layout data that determines GUI elements final position & depth in the GUI widget. */
178 const GUILayoutData& _getLayoutData() const { return mLayoutData; }
179
180 /** Sets a new parent for this element. */
181 void _setParent(GUIElementBase* parent);
182
183 /** Returns number of child elements. */
184 UINT32 _getNumChildren() const { return (UINT32)mChildren.size(); }
185
186 /** Return the child element at the specified index.*/
187 GUIElementBase* _getChild(UINT32 idx) const { return mChildren[idx]; }
188
189 /** Returns previously calculated optimal size for this element. */
190 virtual Vector2I _getOptimalSize() const = 0;
191
192 /** Returns layout options that determine how is the element positioned and sized. */
193 const GUIDimensions& _getDimensions() const { return mDimensions; }
194
195 /** Calculates element size range constrained by its layout options. */
196 virtual LayoutSizeRange _calculateLayoutSizeRange() const ;
197
198 /**
199 * Returns element size range constrained by its layout options. This is different from _calculateLayoutSizeRange()
200 * because this method may return cached size range.
201 */
202 virtual LayoutSizeRange _getLayoutSizeRange() const;
203
204 /**
205 * Returns element padding that determines how far apart to space out this element from other elements in a layout.
206 */
207 virtual const RectOffset& _getPadding() const = 0;
208
209 /** Returns specific sub-type of this object. */
210 virtual Type _getType() const = 0;
211
212 /** Returns parent GUI base element. */
213 GUIElementBase* _getParent() const { return mParentElement; }
214
215 /**
216 * Returns the parent element whose layout needs to be updated when this elements contents change.
217 *
218 * @note
219 * Due to the nature of the GUI system, when a child element bounds or contents change, its parents and siblings
220 * usually need their layout bound updated. This function returns the first parent of all the elements that require
221 * updating. This parent usually has fixed bounds or some other property that allows its children to be updated
222 * independently from the even higher-up elements.
223 */
224 GUIElementBase* _getUpdateParent() const { return mUpdateParent; }
225
226 /** Returns parent GUI widget, can be null. */
227 GUIWidget* _getParentWidget() const { return mParentWidget; }
228
229 /** Checks if element is visible or hidden. */
230 bool _isVisible() const { return (mFlags & GUIElem_Hidden) == 0; }
231
232 /**
233 * Checks if element is active or inactive. Inactive elements are not visible, don't take up space
234 * in their parent layouts, and can't be interacted with.
235 */
236 bool _isActive() const { return (mFlags & GUIElem_Inactive) == 0; }
237
238 /** Checks if element is disabled. Disabled elements cannot be interacted with and have a faded out appearance. */
239 bool _isDisabled() const { return (mFlags & GUIElem_Disabled) != 0; }
240
241 /**
242 * Internal version of setVisible() that doesn't modify local visibility, instead it is only meant to be called
243 * on child elements of the element whose visibility was modified.
244 */
245 void _setVisible(bool visible);
246
247 /**
248 * Internal version of setActive() that doesn't modify local state, instead it is only meant to be called
249 * on child elements of the element whose state was modified.
250 *
251 * @copydoc setActive
252 */
253 void _setActive(bool active);
254
255 /**
256 * Internal version of setDisabled() that doesn't modify local state, instead it is only meant to be called
257 * on child elements of the element whose state was modified.
258 *
259 * @copydoc setDisabled
260 */
261 void _setDisabled(bool disabled);
262
263 /**
264 * Changes the active GUI element widget. This allows you to move an element to a different viewport, or change
265 * element style by using a widget with a different skin. You are allowed to pass null here, but elements with no
266 * parent will be unmanaged. You will be responsible for deleting them manually, and they will not render anywhere.
267 */
268 virtual void _changeParentWidget(GUIWidget* widget);
269
270 /**Registers a new child element. */
271 void _registerChildElement(GUIElementBase* element);
272
273 /** Unregisters an existing child element. */
274 void _unregisterChildElement(GUIElementBase* element);
275
276 /** Checks if element has been destroyed and is queued for deletion. */
277 virtual bool _isDestroyed() const { return false; }
278
279 /** Marks the element's dimensions as dirty, triggering a layout rebuild. */
280 void _markLayoutAsDirty();
281
282 /** Marks the element's contents as dirty, which causes the sprite meshes to be recreated from scratch. */
283 void _markContentAsDirty();
284
285 /**
286 * Mark only the elements that operate directly on the sprite mesh without requiring the mesh to be recreated as
287 * dirty. This includes position, depth and clip rectangle. This will cause the parent widget mesh to be rebuilt
288 * from its child element's meshes.
289 */
290 void _markMeshAsDirty();
291
292 /** Returns true if elements contents have changed since last update. */
293 bool _isDirty() const { return (mFlags & GUIElem_Dirty) != 0; }
294
295 /** Marks the element contents to be up to date (meaning it's processed by the GUI system). */
296 void _markAsClean();
297
298 /** @} */
299
300 protected:
301 /** Finds anchor and update parents and recursively assigns them to all children. */
302 void _updateAUParents();
303
304 /** Refreshes update parents of all child elements. */
305 void refreshChildUpdateParents();
306
307 /**
308 * Finds the first parent element whose size doesn't depend on child sizes.
309 *
310 * @note
311 * This allows us to optimize layout updates and trigger them only on such parents when their child elements
312 * contents change, compared to doing them on the entire GUI hierarchy.
313 */
314 GUIElementBase* findUpdateParent();
315
316 /**
317 * Helper method for recursion in _updateAUParents(). Sets the provided anchor parent for all children recursively.
318 * Recursion stops when a child anchor is detected.
319 */
320 void setAnchorParent(GUIPanel* anchorParent);
321
322 /**
323 * Helper method for recursion in _updateAUParents(). Sets the provided update parent for all children recursively.
324 * Recursion stops when a child update parent is detected.
325 */
326 void setUpdateParent(GUIElementBase* updateParent);
327
328 /** Unregisters and destroys all child elements. */
329 void destroyChildElements();
330
331 GUIWidget* mParentWidget = nullptr;
332 GUIPanel* mAnchorParent = nullptr;
333 GUIElementBase* mUpdateParent = nullptr;
334 GUIElementBase* mParentElement = nullptr;
335
336 Vector<GUIElementBase*> mChildren;
337 UINT8 mFlags = GUIElem_Dirty;
338
339 GUIDimensions mDimensions;
340 GUILayoutData mLayoutData;
341 };
342
343 /** @} */
344}