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 "Renderer/BsRendererExtension.h"
7#include "GUI/BsGUIMouseEvent.h"
8#include "GUI/BsGUITextInputEvent.h"
9#include "GUI/BsGUICommandEvent.h"
10#include "GUI/BsGUIVirtualButtonEvent.h"
11#include "2D/BsSprite.h"
12#include "Utility/BsModule.h"
13#include "Image/BsColor.h"
14#include "Math/BsMatrix4.h"
15#include "Utility/BsEvent.h"
16#include "Material/BsMaterialParam.h"
17#include "Renderer/BsParamBlocks.h"
18#include "RenderAPI/BsSubMesh.h"
19
20namespace bs
21{
22 /** @addtogroup GUI-Internal
23 * @{
24 */
25
26 namespace ct { class GUIRenderer; }
27
28 /**
29 * Manages the rendering and input of all GUI widgets in the scene.
30 *
31 * @note
32 * If adding or modifying GUIManager functionality ensure that GUIManager data never gets modified outside of update()
33 * method or Input callbacks. If you need such functionality add temporary variables that store you changes and then
34 * execute them delayed in update().
35 * @par
36 * This ensures that GUIElements don't recursively modify GUIManager while GUIManager is still using that data.
37 * @par
38 * For example setFocus() usually gets called from within GUIElements, however we don't want elements in focus be
39 * modified immediately since that setFocus() call could have originated in sendCommandEvent and elements in focus array
40 * would be modified while still being iterated upon.
41 */
42 class BS_EXPORT GUIManager : public Module<GUIManager>
43 {
44 /** Valid states of a drag and drop operation. */
45 enum class DragState
46 {
47 NoDrag,
48 HeldWithoutDrag,
49 Dragging
50 };
51
52 /** Data required for rendering a single GUI mesh. */
53 struct GUIMeshData
54 {
55 UINT32 indexOffset = 0;
56 UINT32 indexCount = 0;
57 SpriteMaterial* material;
58 SpriteMaterialInfo matInfo;
59 GUIWidget* widget;
60 bool isLine;
61 };
62
63 /** GUI render data for a single viewport. */
64 struct GUIRenderData
65 {
66 GUIRenderData()
67 :isDirty(true)
68 { }
69
70 SPtr<Mesh> triangleMesh;
71 SPtr<Mesh> lineMesh;
72 Vector<GUIMeshData> cachedMeshes;
73 Vector<GUIWidget*> widgets;
74 bool isDirty;
75 };
76
77 /** Render data for a single GUI group used for notifying the core GUI renderer. */
78 struct GUICoreRenderData
79 {
80 SPtr<ct::Mesh> mesh;
81 SubMesh subMesh;
82 SPtr<ct::Texture> texture;
83 SPtr<ct::SpriteTexture> spriteTexture;
84 SpriteMaterial* material;
85 Color tint;
86 float animationStartTime;
87 Matrix4 worldTransform;
88 SPtr<SpriteMaterialExtraInfo> additionalData;
89 UINT32 bufferIdx;
90 };
91
92 /** Container for a GUI widget. */
93 struct WidgetInfo
94 {
95 WidgetInfo(GUIWidget* _widget)
96 :widget(_widget)
97 { }
98
99 GUIWidget* widget;
100 };
101
102 /** Container for data about a single GUI element and its widget. */
103 struct ElementInfo
104 {
105 ElementInfo(GUIElement* element, GUIWidget* widget)
106 :element(element), widget(widget)
107 { }
108
109 GUIElement* element;
110 GUIWidget* widget;
111 };
112
113 /** Container for data about a single GUI element and its widget currently under the pointer. */
114 struct ElementInfoUnderPointer
115 {
116 ElementInfoUnderPointer(GUIElement* element, GUIWidget* widget)
117 :element(element), widget(widget), usesMouseOver(false),
118 receivedMouseOver(false), isHovering(false)
119 { }
120
121 GUIElement* element;
122 GUIWidget* widget;
123 bool usesMouseOver;
124 bool receivedMouseOver;
125 bool isHovering;
126 };
127
128 /** Container for GUI element in focus. */
129 struct ElementFocusInfo
130 {
131 ElementFocusInfo(GUIElement* element, GUIWidget* widget, bool usesFocus)
132 :element(element), widget(widget), usesFocus(usesFocus)
133 { }
134
135 GUIElement* element;
136 GUIWidget* widget;
137 bool usesFocus;
138 };
139
140 /** Container for GUI elements that need to have their focus state forcefully changed. */
141 struct ElementForcedFocusInfo
142 {
143 GUIElement* element;
144 bool focus;
145 };
146
147 public:
148 GUIManager();
149 ~GUIManager();
150
151 /** Registers a newly created widget with the GUI manager. This should be called by every GUI widget on creation. */
152 void registerWidget(GUIWidget* widget);
153
154 /**
155 * Unregisters a GUI widget from the GUI manager. This should be called by every GUI widget before getting deleted.
156 */
157 void unregisterWidget(GUIWidget* widget);
158
159 /** Called once per frame. */
160 void update();
161
162 /** Queues the GUI element for destruction. Element will be destroyed during the next call to update(). */
163 void queueForDestroy(GUIElement* element);
164
165 /** Forces all GUI elements that are queued for destruction to be destroyed immediately. */
166 void processDestroyQueue();
167
168 /**
169 * Change the GUI element focus state.
170 *
171 * @param[in] element Element whose focus state to change
172 * @param[in] focus Give the element focus or take it away.
173 * @param[in] clear If true the focus will be cleared from any elements currently in focus. Otherwise
174 * the element will just be appended to the in-focus list (if enabling focus).
175 */
176 void setFocus(GUIElement* element, bool focus, bool clear);
177
178 /** Changes the color of the input caret used in input boxes and similar controls. */
179 void setCaretColor(const Color& color) { mCaretColor = color; updateCaretTexture(); }
180
181 /** Changes the text selection highlight color used in input boxes and similar controls. */
182 void setTextSelectionColor(const Color& color) { mTextSelectionColor = color; updateTextSelectionTexture(); }
183
184 /** Returns the default caret texture used for rendering the input caret sprite. */
185 const HSpriteTexture& getCaretTexture() const { return mCaretTexture; }
186
187 /** Returns the default selection highlight texture used for rendering the selection highlight sprites. */
188 const HSpriteTexture& getTextSelectionTexture() const { return mTextSelectionTexture; }
189
190 /** Checks is the input caret visible this frame. */
191 bool getCaretBlinkState() const { return mIsCaretOn; }
192
193 /**
194 * Returns input caret helper tool that allows you to easily position and show an input caret in your GUI controls.
195 */
196 GUIInputCaret* getInputCaretTool() const { return mInputCaret; }
197
198 /**
199 * Returns input selection helper tool that allows you to easily position and show an input selection highlight in
200 * your GUI controls.
201 */
202 GUIInputSelection* getInputSelectionTool() const { return mInputSelection; }
203
204 /**
205 * Allows you to bridge GUI input from a GUI element into another render target.
206 *
207 * @param[in] renderTex The render target to which to bridge the input.
208 * @param[in] element The element from which to bridge input. Input will be transformed according to this
209 * elements position and size. Provide nullptr if you want to remove a bridge for the
210 * specified widget.
211 *
212 * @note
213 * This is useful if you use render textures, where your GUI is rendered off-screen. In such case you need to
214 * display the render texture within another GUIElement in a GUIWidget, but have no way of sending input to the
215 * render texture (normally input is only sent to render windows). This allows you to change that - any GUIWidget
216 * using the bridged render texture as a render target will then receive input when mouse is over the specified
217 * element.
218 * @note
219 * Bridged element needs to remove itself as the bridge when it is destroyed.
220 */
221 void setInputBridge(const RenderTexture* renderTex, const GUIElement* element);
222
223 /**
224 * Converts window coordinates to coordinates relative to the specified bridged render target (target displayed
225 * with a GUI element). Returned coordinates will be relative to the bridge element.
226 *
227 * @return If provided widget has no bridge, coordinates are returned as is.
228 */
229 Vector2I windowToBridgedCoords(const SPtr<RenderTarget>& target, const Vector2I& windowPos) const;
230
231 /**
232 * Returns the render window that holds the GUI element that displays the provided render texture.
233 *
234 * @param[in] target Render texture to find the bridged window for.
235 * @return Window that displays the GUI element with the render texture, or null if the render texture
236 * is not bridged.
237 */
238 SPtr<RenderWindow> getBridgeWindow(const SPtr<RenderTexture>& target) const;
239
240 /** Returns the parent render window of the specified widget. */
241 const RenderWindow* getWidgetWindow(const GUIWidget& widget) const;
242
243 private:
244 friend class ct::GUIRenderer;
245
246 /** Recreates all dirty GUI meshes and makes them ready for rendering. */
247 void updateMeshes();
248
249 /** Recreates the input caret texture. */
250 void updateCaretTexture();
251
252 /** Recreates the input text selection highlight texture. */
253 void updateTextSelectionTexture();
254
255 /**
256 * Destroys the core thread counterpart of the GUI manager.
257 *
258 * @param[in] core Previously constructed core thread GUI manager instance.
259 */
260 void destroyCore(ct::GUIRenderer* core);
261
262 /**
263 * Destroys any elements or widgets queued for destruction.
264 *
265 * @note
266 * Returns true if more elements have been added for destruction (will happen when destruction of one element
267 * queues up destruction of another). Usually needs to be run in a loop with multiple iterations.
268 */
269 bool processDestroyQueueIteration();
270
271 /**
272 * Finds a GUI element under the pointer at the specified screen position. This method will also trigger pointer
273 * move/hover/leave events.
274 *
275 * @param[in] screenMousePos Position of the pointer in screen coordinates.
276 * @param[in] buttonStates States of the three mouse buttons (left, right, middle).
277 * @param[in] shift Is shift key held.
278 * @param[in] control Is control key held.
279 * @param[in] alt Is alt key held.
280 */
281 bool findElementUnderPointer(const Vector2I& screenMousePos, bool buttonStates[3], bool shift, bool control, bool alt);
282
283 /** Called whenever a pointer (for example mouse cursor) is moved. */
284 void onPointerMoved(const PointerEvent& event);
285
286 /** Called whenever a pointer button (for example mouse button) is released. */
287 void onPointerReleased(const PointerEvent& event);
288
289 /** Called whenever a pointer button (for example mouse button) is pressed. */
290 void onPointerPressed(const PointerEvent& event);
291
292 /** Called whenever a pointer button (for example mouse button) is double clicked. */
293 void onPointerDoubleClick(const PointerEvent& event);
294
295 /** Called whenever a text is input. */
296 void onTextInput(const TextInputEvent& event);
297
298 /** Called whenever an input command is input. */
299 void onInputCommandEntered(InputCommandType commandType);
300
301 /** Called whenever a virtual button is pressed. */
302 void onVirtualButtonDown(const VirtualButton& button, UINT32 deviceIdx);
303
304 /** Called by the drag and drop managed to notify us the drag ended. */
305 void onMouseDragEnded(const PointerEvent& event, DragCallbackInfo& dragInfo);
306
307 /** Called when the specified window gains focus. */
308 void onWindowFocusGained(RenderWindow& win);
309
310 /** Called when the specified window loses focus. */
311 void onWindowFocusLost(RenderWindow& win);
312
313 /** Called when the mouse leaves the specified window. */
314 void onMouseLeftWindow(RenderWindow& win);
315
316 /** Converts pointer buttons to mouse buttons. */
317 GUIMouseButton buttonToGUIButton(PointerEventButton pointerButton) const;
318
319 /** Converts screen coordinates to coordinates relative to the specified widget. */
320 Vector2I getWidgetRelativePos(const GUIWidget* widget, const Vector2I& screenPos) const;
321
322 /** Hides the tooltip if any is shown. */
323 void hideTooltip();
324
325 /** Switches the focus to the first element in the tab group. */
326 void tabFocusFirst();
327
328 /** Switches the focus to the next element in the tab group. Usually triggered when the user hits Tab key. */
329 void tabFocusNext();
330
331 /**
332 * Sends a mouse event to the specified GUI element.
333 *
334 * @param[in] element Element to send the event to.
335 * @param[in] event Event data.
336 */
337 bool sendMouseEvent(GUIElement* element, const GUIMouseEvent& event);
338
339 /**
340 * Sends a text input event to the specified GUI element.
341 *
342 * @param[in] element Element to send the event to.
343 * @param[in] event Event data.
344 */
345 bool sendTextInputEvent(GUIElement* element, const GUITextInputEvent& event);
346
347 /**
348 * Sends a command event to the specified GUI element.
349 *
350 * @param[in] element Element to send the event to.
351 * @param[in] event Event data.
352 */
353 bool sendCommandEvent(GUIElement* element, const GUICommandEvent& event);
354
355 /**
356 * Sends a virtual button event to the specified GUI element.
357 *
358 * @param[in] element Element to send the event to.
359 * @param[in] event Event data.
360 */
361 bool sendVirtualButtonEvent(GUIElement* element, const GUIVirtualButtonEvent& event);
362
363 static const UINT32 DRAG_DISTANCE;
364 static const float TOOLTIP_HOVER_TIME;
365
366 static const UINT32 MESH_HEAP_INITIAL_NUM_VERTS;
367 static const UINT32 MESH_HEAP_INITIAL_NUM_INDICES;
368
369 Vector<WidgetInfo> mWidgets;
370 UnorderedMap<const Viewport*, GUIRenderData> mCachedGUIData;
371
372 SPtr<ct::GUIRenderer> mRenderer;
373 bool mCoreDirty;
374
375 SPtr<VertexDataDesc> mTriangleVertexDesc;
376 SPtr<VertexDataDesc> mLineVertexDesc;
377
378 Stack<GUIElement*> mScheduledForDestruction;
379
380 // Element and widget pointer is currently over
381 Vector<ElementInfoUnderPointer> mElementsUnderPointer;
382 Vector<ElementInfoUnderPointer> mNewElementsUnderPointer;
383
384 // Element and widget that's being clicked on
385 GUIMouseButton mActiveMouseButton;
386 Vector<ElementInfo> mActiveElements;
387 Vector<ElementInfo> mNewActiveElements;
388
389 // Element and widget that currently have the keyboard focus
390 Vector<ElementFocusInfo> mElementsInFocus;
391 Vector<ElementFocusInfo> mNewElementsInFocus;
392
393 bool mForcedClearFocus = false;
394 Vector<ElementForcedFocusInfo> mForcedFocusElements;
395
396 // Tooltip
397 bool mShowTooltip;
398 float mTooltipElementHoverStart;
399
400 GUIInputCaret* mInputCaret;
401 GUIInputSelection* mInputSelection;
402
403 bool mSeparateMeshesByWidget;
404 Vector2I mLastPointerScreenPos;
405
406 DragState mDragState;
407 Vector2I mLastPointerClickPos;
408 Vector2I mDragStartPos;
409
410 GUIMouseEvent mMouseEvent;
411 GUITextInputEvent mTextInputEvent;
412 GUICommandEvent mCommandEvent;
413 GUIVirtualButtonEvent mVirtualButtonEvent;
414
415 HSpriteTexture mCaretTexture;
416 Color mCaretColor;
417 float mCaretBlinkInterval;
418 float mCaretLastBlinkTime;
419 bool mIsCaretOn;
420 CursorType mActiveCursor;
421
422 HSpriteTexture mTextSelectionTexture;
423 Color mTextSelectionColor;
424
425 Map<const RenderTexture*, const GUIElement*> mInputBridge;
426
427 HEvent mOnPointerMovedConn;
428 HEvent mOnPointerPressedConn;
429 HEvent mOnPointerReleasedConn;
430 HEvent mOnPointerDoubleClick;
431 HEvent mOnTextInputConn;
432 HEvent mOnInputCommandConn;
433 HEvent mOnVirtualButtonDown;
434
435 HEvent mDragEndedConn;
436
437 HEvent mWindowGainedFocusConn;
438 HEvent mWindowLostFocusConn;
439
440 HEvent mMouseLeftWindowConn;
441 };
442
443 namespace ct
444 {
445 BS_PARAM_BLOCK_BEGIN(GUISpriteParamBlockDef)
446 BS_PARAM_BLOCK_ENTRY(Matrix4, gWorldTransform)
447 BS_PARAM_BLOCK_ENTRY(float, gInvViewportWidth)
448 BS_PARAM_BLOCK_ENTRY(float, gInvViewportHeight)
449 BS_PARAM_BLOCK_ENTRY(float, gViewportYFlip)
450 BS_PARAM_BLOCK_ENTRY(Color, gTint)
451 BS_PARAM_BLOCK_ENTRY(Vector4, gUVSizeOffset)
452 BS_PARAM_BLOCK_END
453
454 extern GUISpriteParamBlockDef gGUISpriteParamBlockDef;
455
456 /** Handles GUI rendering on the core thread. */
457 class BS_EXPORT GUIRenderer : public RendererExtension
458 {
459 friend class bs::GUIManager;
460
461 public:
462 GUIRenderer();
463
464 /** @copydoc RendererExtension::initialize */
465 void initialize(const Any& data) override;
466
467 /** @copydoc RendererExtension::check */
468 bool check(const Camera& camera) override;
469
470 /** @copydoc RendererExtension::render */
471 void render(const Camera& camera) override;
472
473 private:
474 /** Called every frame from the main thread with the time of the current frame. */
475 void update(float time);
476
477 /**
478 * Updates the internal data that determines what will be rendered on the next render() call.
479 *
480 * @param[in] perCameraData GUI mesh/material per viewport.
481 */
482 void updateData(const UnorderedMap<SPtr<Camera>, Vector<GUIManager::GUICoreRenderData>>& perCameraData);
483
484 UnorderedMap<const Camera*, Vector<GUIManager::GUICoreRenderData>> mPerCameraData;
485 Set<SPtr<Camera>> mReferencedCameras;
486 Vector<SPtr<GpuParamBlockBuffer>> mParamBlocks;
487 SPtr<SamplerState> mSamplerState;
488 float mTime = 0.0f;
489 };
490 }
491
492 /** Provides easier access to GUIManager. */
493 BS_EXPORT GUIManager& gGUIManager();
494
495 /** @} */
496}
497