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 | |
12 | namespace 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 | } |