1 /* Copyright (C) 2022 Wildfire Games.
2 * This file is part of 0 A.D.
4 * 0 A.D. is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
9 * 0 A.D. is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
19 * This is the top class of the whole GUI, all objects
20 * and settings are stored within this class.
26 #include "gui/GUITooltip.h"
27 #include "gui/SettingTypes/CGUIColor.h"
28 #include "gui/SGUIIcon.h"
29 #include "gui/SGUIMessage.h"
30 #include "gui/SGUIStyle.h"
31 #include "lib/input.h"
32 #include "maths/Rect.h"
33 #include "maths/Size2D.h"
34 #include "maths/Vector2D.h"
35 #include "ps/XML/Xeromyces.h"
36 #include "scriptinterface/ScriptForward.h"
40 #include <unordered_map>
41 #include <unordered_set>
44 extern const double SELECT_DBLCLICK_RATE
;
47 class CGUISpriteInstance
;
50 struct SGUIImageEffects
;
51 struct SGUIScrollBarStyle
;
55 using map_pObjects
= std::map
<CStr
, IGUIObject
*>;
58 * The main object that represents a whole GUI page.
66 using ConstructObjectFunction
= IGUIObject
* (*)(CGUI
&);
69 CGUI(const std::shared_ptr
<ScriptContext
>& context
);
73 * Informs the GUI page which GUI object types may be constructed from XML.
75 void AddObjectTypes();
78 * Performs processing that should happen every frame
79 * (including sending the "Tick" event to scripts)
84 * Sends a specified script event to every object
86 * @param eventName String representation of event name
88 void SendEventToAll(const CStr
& eventName
);
91 * Sends a specified script event to every object
93 * @param eventName String representation of event name
94 * @param paramData JS::HandleValueArray storing the arguments passed to the event handler.
96 void SendEventToAll(const CStr
& eventName
, const JS::HandleValueArray
& paramData
);
99 * Displays the whole GUI
101 void Draw(CCanvas2D
& canvas
);
106 * @param Sprite Object referring to the sprite (which also caches
107 * calculations for faster rendering)
108 * @param Canvas Canvas to draw on
109 * @param Rect Position and Size
110 * @param Clipping The sprite shouldn't be drawn outside this rectangle
112 void DrawSprite(const CGUISpriteInstance
& Sprite
, CCanvas2D
& canvas
, const CRect
& Rect
, const CRect
& Clipping
= CRect());
115 * The replacement of Process(), handles an SDL_Event_
117 * @param ev SDL Event, like mouse/keyboard input
119 InReaction
HandleEvent(const SDL_Event_
* ev
);
122 * Load a GUI XML file into the GUI.
124 * <b>VERY IMPORTANT!</b> All \<styles\>-files must be read before
127 * @param Filename Name of file
128 * @param Paths Set of paths; all XML and JS files loaded will be added to this
130 void LoadXmlFile(const VfsPath
& Filename
, std::unordered_set
<VfsPath
>& Paths
);
133 * Called after all XML files linked in the page file were loaded.
135 void LoadedXmlFiles();
138 * Allows the JS side to modify the hotkey setting assigned to a GUI object.
140 void SetObjectHotkey(IGUIObject
* pObject
, const CStr
& hotkeyTag
);
141 void UnsetObjectHotkey(IGUIObject
* pObject
, const CStr
& hotkeyTag
);
144 * Allows the JS side to modify the style setting assigned to a GUI object.
146 void SetObjectStyle(IGUIObject
* pObject
, const CStr
& styleName
);
147 void UnsetObjectStyle(IGUIObject
* pObject
);
150 * Allows the JS side to add or remove global hotkeys.
152 void SetGlobalHotkey(const CStr
& hotkeyTag
, const CStr
& eventName
, JS::HandleValue function
);
153 void UnsetGlobalHotkey(const CStr
& hotkeyTag
, const CStr
& eventName
);
156 * Return the object which is an ancestor of every other GUI object.
158 IGUIObject
* GetBaseObject();
161 * Checks if object exists and return true or false accordingly
163 * @param Name String name of object
164 * @return true if object exists
166 bool ObjectExists(const CStr
& Name
) const;
169 * Returns the GUI object with the desired name, or nullptr
170 * if no match is found,
172 * @param Name String name of object
173 * @return Matching object, or nullptr
175 IGUIObject
* FindObjectByName(const CStr
& Name
) const;
178 * Returns the GUI object under the mouse, or nullptr if none.
180 IGUIObject
* FindObjectUnderMouse();
183 * Returns the current screen coordinates of the cursor.
185 const CVector2D
& GetMousePos() const { return m_MousePos
; }
188 * Returns the currently pressed mouse buttons.
190 const unsigned int& GetMouseButtons() { return m_MouseButtons
; }
192 const SGUIScrollBarStyle
* GetScrollBarStyle(const CStr
& style
) const;
195 * Returns the current GUI window size.
197 CSize2D
GetWindowSize() const;
200 * The GUI needs to have all object types inputted and
201 * their constructors. Also it needs to associate a type
202 * by a string name of the type.
206 * AddObjectType("button", &CButton::ConstructObject);
209 * @param str Reference name of object type
210 * @param pFunc Pointer of function ConstuctObject() in the object
212 * @see CGUI#ConstructObject()
214 void AddObjectType(const CStr
& str
, ConstructObjectFunction pFunc
) { m_ObjectTypes
[str
] = pFunc
; }
217 * Update Resolution, should be called every time the resolution
218 * of the OpenGL screen has been changed, this is because it needs
219 * to re-cache all its actual sizes
221 * Needs no input since screen resolution is global.
223 * @see IGUIObject#UpdateCachedSize()
225 void UpdateResolution();
228 * Check if an icon exists
230 bool HasIcon(const CStr
& name
) const { return (m_Icons
.find(name
) != m_Icons
.end()); }
233 * Get Icon (a const reference, can never be changed)
235 const SGUIIcon
& GetIcon(const CStr
& name
) const { return m_Icons
.at(name
); }
238 * Check if a style exists
240 bool HasStyle(const CStr
& name
) const { return (m_Styles
.find(name
) != m_Styles
.end()); }
243 * Get Style if it exists, otherwise throws an exception.
245 const SGUIStyle
& GetStyle(const CStr
& name
) const { return m_Styles
.at(name
); }
248 * Check if a predefined color of that name exists.
250 bool HasPreDefinedColor(const CStr
& name
) const { return (m_PreDefinedColors
.find(name
) != m_PreDefinedColors
.end()); }
253 * Resolve the predefined color if it exists, otherwise throws an exception.
255 const CGUIColor
& GetPreDefinedColor(const CStr
& name
) const { return m_PreDefinedColors
.at(name
); }
257 GUIProxyProps
* GetProxyData(const js::BaseProxyHandler
* ptr
) { return m_ProxyData
.at(ptr
).get(); }
259 std::shared_ptr
<ScriptInterface
> GetScriptInterface() { return m_ScriptInterface
; };
263 * The CGUI takes ownership of the child object and links the parent with the child.
264 * Returns false on failure to take over ownership of the child object.
266 bool AddObject(IGUIObject
& parent
, IGUIObject
& child
);
269 * You input the name of the object type, and let's
270 * say you input "button", then it will construct a
271 * CGUIObjet* as a CButton.
273 * @param str Name of object type
274 * @return Newly constructed IGUIObject (but constructed as a subclass)
276 IGUIObject
* ConstructObject(const CStr
& str
);
280 * Get Focused Object.
282 IGUIObject
* GetFocusedObject() { return m_FocusedObject
; }
285 * Change focus to new object.
286 * Will send LOST_FOCUS/GOT_FOCUS messages as appropriate.
287 * pObject can be nullptr to remove all focus.
289 void SetFocusedObject(IGUIObject
* pObject
);
292 * Alert the focussed object of this GUIPage that the focus of the page has changed.
294 void SendFocusMessage(EGUIMessageType msg
);
297 * Reads a string value and modifies the given value of type T if successful.
298 * Does not change the value upon conversion failure.
300 * @param pGUI The GUI page which may contain data relevant to the parsing
301 * (for example predefined colors).
302 * @param Value The value in string form, like "0 0 100% 100%"
303 * @param tOutput Parsed value of type T
304 * @return True at success.
306 template <typename T
>
307 static bool ParseString(const CGUI
* pGUI
, const CStrW
& Value
, T
& tOutput
);
311 //--------------------------------------------------------
312 /** @name XML Reading Xeromyces specific subroutines
314 * These does not throw!
315 * Because when reading in XML files, it won't be fatal
316 * if an error occurs, perhaps one particular object
317 * fails, but it'll still continue reading in the next.
318 * All Error are reported with ReportParseError
320 //--------------------------------------------------------
323 Xeromyces_* functions tree
324 <objects> (ReadRootObjects)
326 +-<script> (ReadScript)
328 +-<object> (ReadObject)
332 +-Optional Type Extensions (IGUIObject::ReadExtendedElement) TODO
334 +-<<object>> *recursive*
337 <styles> (ReadRootStyles)
339 +-<style> (ReadStyle)
342 <sprites> (ReadRootSprites)
344 +-<sprite> (ReadSprite)
346 +-<image> (ReadImage)
349 <setup> (ReadRootSetup)
351 +-<tooltip> (ReadToolTip)
353 +-<scrollbar> (ReadScrollBar)
357 +-<color> (ReadColor)
364 * Reads in the root element \<objects\> (the DOMElement).
366 * @param Element The Xeromyces object that represents
368 * @param pFile The Xeromyces object for the file being read
369 * @param Paths Collects the set of all XML/JS files that are loaded
373 void Xeromyces_ReadRootObjects(const XMBData
& xmb
, XMBElement element
, std::unordered_set
<VfsPath
>& Paths
);
376 * Reads in the root element \<sprites\> (the DOMElement).
378 * @param Element The Xeromyces object that represents
380 * @param pFile The Xeromyces object for the file being read
384 void Xeromyces_ReadRootSprites(const XMBData
& xmb
, XMBElement element
);
387 * Reads in the root element \<styles\> (the DOMElement).
389 * @param Element The Xeromyces object that represents
391 * @param pFile The Xeromyces object for the file being read
395 void Xeromyces_ReadRootStyles(const XMBData
& xmb
, XMBElement element
);
398 * Reads in the root element \<setup\> (the DOMElement).
400 * @param Element The Xeromyces object that represents
402 * @param pFile The Xeromyces object for the file being read
406 void Xeromyces_ReadRootSetup(const XMBData
& xmb
, XMBElement element
);
411 * Notice! Recursive function!
413 * Read in an \<object\> (the XMBElement) and stores it
414 * as a child in the pParent.
416 * It will also check the object's children and call this function
417 * on them too. Also it will call all other functions that reads
418 * in other stuff that can be found within an object. Check the
419 * tree in the beginning of this class' Xeromyces_* section.
421 * @param Element The Xeromyces object that represents
423 * @param pFile The Xeromyces object for the file being read
424 * @param pParent Parent to add this object as child in.
425 * @param NameSubst A set of substitution strings that will be
426 * applied to all object names within this object.
427 * @param Paths Output set of file paths that this GUI object
432 IGUIObject
* Xeromyces_ReadObject(const XMBData
& xmb
, XMBElement element
, IGUIObject
* pParent
, std::vector
<std::pair
<CStr
, CStr
> >& NameSubst
, std::unordered_set
<VfsPath
>& Paths
, u32 nesting_depth
);
435 * Reads in the element \<repeat\>, which repeats its child \<object\>s
436 * 'count' times, replacing the string "[n]" (or the value of the attribute
437 * 'var' enclosed in square brackets) in its descendants' names with "[0]",
440 void Xeromyces_ReadRepeat(const XMBData
& xmb
, XMBElement element
, IGUIObject
* pParent
, std::vector
<std::pair
<CStr
, CStr
> >& NameSubst
, std::unordered_set
<VfsPath
>& Paths
, u32 nesting_depth
);
443 * Reads in the element \<script\> (the XMBElement) and executes
446 * @param Element The Xeromyces object that represents
448 * @param pFile The Xeromyces object for the file being read
449 * @param Paths Output set of file paths that this script is loaded from.
453 void Xeromyces_ReadScript(const XMBData
& xmb
, XMBElement element
, std::unordered_set
<VfsPath
>& Paths
);
456 * Reads in the element \<sprite\> (the XMBElement) and stores the
457 * result in a new CGUISprite.
459 * @param Element The Xeromyces object that represents
461 * @param pFile The Xeromyces object for the file being read
465 void Xeromyces_ReadSprite(const XMBData
& xmb
, XMBElement element
);
468 * Reads in the element \<image\> (the XMBElement) and stores the
469 * result within the CGUISprite.
471 * @param Element The Xeromyces object that represents
473 * @param pFile The Xeromyces object for the file being read
474 * @param parent Parent sprite.
478 void Xeromyces_ReadImage(const XMBData
& xmb
, XMBElement element
, CGUISprite
& parent
);
481 * Reads in the element \<effect\> (the XMBElement) and stores the
482 * result within the SGUIImageEffects.
484 * @param Element The Xeromyces object that represents
486 * @param pFile The Xeromyces object for the file being read
487 * @param effects Effects object to add this effect to.
491 void Xeromyces_ReadEffects(const XMBData
& xmb
, XMBElement element
, SGUIImageEffects
& effects
);
494 * Reads in the element \<style\> (the XMBElement) and stores the
495 * result in m_Styles.
497 * @param Element The Xeromyces object that represents
499 * @param pFile The Xeromyces object for the file being read
503 void Xeromyces_ReadStyle(const XMBData
& xmb
, XMBElement element
);
506 * Reads in the element \<scrollbar\> (the XMBElement) and stores the
507 * result in m_ScrollBarStyles.
509 * @param Element The Xeromyces object that represents
511 * @param pFile The Xeromyces object for the file being read
515 void Xeromyces_ReadScrollBarStyle(const XMBData
& xmb
, XMBElement element
);
518 * Reads in the element \<icon\> (the XMBElement) and stores the
521 * @param Element The Xeromyces object that represents
523 * @param pFile The Xeromyces object for the file being read
527 void Xeromyces_ReadIcon(const XMBData
& xmb
, XMBElement element
);
530 * Reads in the element \<tooltip\> (the XMBElement) and stores the
531 * result as an object with the name __tooltip_#.
533 * @param Element The Xeromyces object that represents
535 * @param pFile The Xeromyces object for the file being read
539 void Xeromyces_ReadTooltip(const XMBData
& xmb
, XMBElement element
);
542 * Reads in the element \<color\> (the XMBElement) and stores the
543 * result in m_PreDefinedColors
545 * @param Element The Xeromyces object that represents
547 * @param pFile The Xeromyces object for the file being read
551 void Xeromyces_ReadColor(const XMBData
& xmb
, XMBElement element
);
559 //--------------------------------------------------------
560 /** @name Miscellaneous */
561 //--------------------------------------------------------
564 std::shared_ptr
<ScriptInterface
> m_ScriptInterface
;
567 * don't want to pass this around with the
568 * ChooseMouseOverAndClosest broadcast -
569 * we'd need to pack this and pNearest in a struct
571 CVector2D m_MousePos
;
574 * Indicates which buttons are pressed (bit 0 = LMB,
575 * bit 1 = RMB, bit 2 = MMB)
577 unsigned int m_MouseButtons
;
580 GUITooltip m_Tooltip
;
583 //--------------------------------------------------------
585 //--------------------------------------------------------
589 * Base Object, all its children are considered parentless
590 * because this is not a real object per se.
592 std::unique_ptr
<IGUIObject
> m_BaseObject
;
596 * Say an input box that is selected. That one is focused.
597 * There can only be one focused object.
599 IGUIObject
* m_FocusedObject
;
602 * Just pointers for fast name access, each object
603 * is really constructed within its parent for easy
604 * recursive management.
605 * Notice m_BaseObject won't belong here since it's
606 * not considered a real object.
608 map_pObjects m_pAllObjects
;
611 * Number of object that has been given name automatically.
612 * the name given will be '__internal(#)', the number (#)
613 * being this variable. When an object's name has been set
614 * as followed, the value will increment.
616 int m_InternalNameNumber
;
619 * Function pointers to functions that constructs
620 * IGUIObjects by name... For instance m_ObjectTypes["button"]
621 * is filled with a function that will "return new CButton();"
623 std::map
<CStr
, ConstructObjectFunction
> m_ObjectTypes
;
626 * This is intended to store the JSFunction when accessing certain properties.
627 * The problem is that these functions are per-scriptInterface, and proxy handlers aren't.
628 * So we know what we want to store, but we don't really have anywhere to store it.
629 * It would be simpler to recreate the functions on every JS call, but that is slower
630 * (this may or may not matter now and in the future).
631 * It's not a great solution, but I can't find a better one at the moment.
632 * An alternative would be to store these on the proxy's prototype,
633 * but that embarks a lot of un-necessary code.
635 std::unordered_map
<const js::BaseProxyHandler
*, std::unique_ptr
<GUIProxyProps
>> m_ProxyData
;
638 * Map from hotkey names to objects that listen to the hotkey.
639 * (This is an optimisation to avoid recursing over the whole GUI
640 * tree every time a hotkey is pressed).
642 std::map
<CStr
, std::vector
<IGUIObject
*> > m_HotkeyObjects
;
645 * Map from hotkey names to maps of eventNames to functions that are triggered
646 * when the hotkey goes through the event. Contrary to object hotkeys, this
647 * allows for only one global function per hotkey name per event type.
649 std::map
<CStr
, std::map
<CStr
, JS::PersistentRootedValue
>> m_GlobalHotkeys
;
652 * XML and JS can subscribe handlers to events identified by these names.
653 * Store in static const members to avoid string copies, gain compile errors when misspelling and
654 * to allow reuse in other classes.
656 static const CStr EventNameLoad
;
657 static const CStr EventNameTick
;
658 static const CStr EventNamePress
;
659 static const CStr EventNameKeyDown
;
660 static const CStr EventNameRelease
;
661 static const CStr EventNameMouseRightPress
;
662 static const CStr EventNameMouseLeftPress
;
663 static const CStr EventNameMouseWheelDown
;
664 static const CStr EventNameMouseWheelUp
;
665 static const CStr EventNameMouseLeftDoubleClick
;
666 static const CStr EventNameMouseLeftRelease
;
667 static const CStr EventNameMouseRightDoubleClick
;
668 static const CStr EventNameMouseRightRelease
;
670 //--------------------------------------------------------
672 // These are loaded from XML files and marked as noncopyable and const to
673 // rule out unintentional modification and copy, especially during Draw calls.
674 //--------------------------------------------------------
677 std::map
<CStr
, const CGUIColor
> m_PreDefinedColors
;
680 std::map
<CStr
, std::unique_ptr
<const CGUISprite
>> m_Sprites
;
683 std::map
<CStr
, const SGUIStyle
> m_Styles
;
686 std::map
<CStr
, const SGUIScrollBarStyle
> m_ScrollBarStyles
;
689 std::map
<CStr
, const SGUIIcon
> m_Icons
;
693 * Map from event names to object which listen to a given event.
695 std::unordered_map
<CStr
, std::vector
<IGUIObject
*>> m_EventObjects
;
698 #endif // INCLUDED_CGUI