Line data Source code
1 : /* Copyright (C) 2021 Wildfire Games.
2 : * This file is part of 0 A.D.
3 : *
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.
8 : *
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.
13 : *
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/>.
16 : */
17 :
18 : /*
19 : * The base class of an object.
20 : * All objects are derived from this class.
21 : * It's an abstract data type, so it can't be used per se.
22 : * Also contains a Dummy object which is used for completely blank objects.
23 : */
24 :
25 : #ifndef INCLUDED_IGUIOBJECT
26 : #define INCLUDED_IGUIOBJECT
27 :
28 : #include "gui/CGUISetting.h"
29 : #include "gui/SettingTypes/CGUIHotkey.h"
30 : #include "gui/SettingTypes/CGUISize.h"
31 : #include "gui/SGUIMessage.h"
32 : #include "lib/input.h" // just for IN_PASS
33 : #include "ps/CStr.h"
34 : #include "ps/XML/Xeromyces.h"
35 : #include "scriptinterface/ScriptTypes.h"
36 :
37 : #include <map>
38 : #include <vector>
39 :
40 : class CCanvas2D;
41 : class CGUI;
42 : class CGUISize;
43 : class IGUIObject;
44 : class IGUIProxyObject;
45 : class IGUISetting;
46 :
47 : template <typename T>
48 : class JSI_GUIProxy;
49 :
50 : #define GUI_OBJECT(obj) \
51 : public: \
52 : static IGUIObject* ConstructObject(CGUI& pGUI) \
53 : { return new obj(pGUI); }
54 :
55 : /**
56 : * GUI object such as a button or an input-box.
57 : * Abstract data type !
58 : */
59 : class IGUIObject
60 : {
61 : friend class CGUI;
62 :
63 : // For triggering message update handlers.
64 : friend class IGUISetting;
65 :
66 : // Allow getProperty to access things like GetParent()
67 : template <typename T>
68 : friend class JSI_GUIProxy;
69 :
70 : public:
71 : NONCOPYABLE(IGUIObject);
72 :
73 : IGUIObject(CGUI& pGUI);
74 : virtual ~IGUIObject();
75 :
76 : /**
77 : * This function checks if the mouse is hovering the
78 : * rectangle that the base setting "size" makes.
79 : * Although it is virtual, so one could derive
80 : * an object from CButton, which changes only this
81 : * to checking the circle that "size" makes.
82 : *
83 : * This function also returns true if there is a different
84 : * GUI object shown on top of this one.
85 : */
86 : virtual bool IsMouseOver() const;
87 :
88 : /**
89 : * This function returns true if the mouse is hovering
90 : * over this GUI object and if this GUI object is the
91 : * topmost object in that screen location.
92 : * For example when hovering dropdown list items, the
93 : * buttons beneath the list won't return true here.
94 : */
95 0 : virtual bool IsMouseHovering() const { return m_MouseHovering; }
96 :
97 : //--------------------------------------------------------
98 : /** @name Leaf Functions */
99 : //--------------------------------------------------------
100 : //@{
101 :
102 : /// Get object name, name is unique
103 0 : const CStr& GetName() const { return m_Name; }
104 :
105 : /// Get object name
106 4 : void SetName(const CStr& Name) { m_Name = Name; }
107 :
108 : // Get Presentable name.
109 : // Will change all internally set names to something like "<unnamed object>"
110 : CStr GetPresentableName() const;
111 :
112 : /**
113 : * Return all child objects of the current object.
114 : */
115 0 : const std::vector<IGUIObject*>& GetChildren() const { return m_Children; }
116 :
117 : //@}
118 : //--------------------------------------------------------
119 : /** @name Settings Management */
120 : //--------------------------------------------------------
121 : //@{
122 :
123 : /**
124 : * Registers the given setting with the GUI object.
125 : * Enable XML and JS to modify the given variable.
126 : */
127 : void RegisterSetting(const CStr& Name, IGUISetting* setting);
128 : void ReregisterSetting(const CStr& Name, IGUISetting* setting);
129 :
130 : /**
131 : * Returns whether there is a setting with the given name registered.
132 : *
133 : * @param Setting setting name
134 : * @return True if settings exist.
135 : */
136 : bool SettingExists(const CStr& Setting) const;
137 :
138 : /**
139 : * Set a setting by string, regardless of what type it is.
140 : * Used to parse setting values from XML files.
141 : * For example a CRect(10,10,20,20) is created from "10 10 20 20".
142 : * @return false if the setting does not exist or the conversion fails, otherwise true.
143 : */
144 : bool SetSettingFromString(const CStr& Setting, const CStrW& Value, const bool SendMessage);
145 :
146 : /**
147 : * Returns whether this object is set to be hidden or ghost.
148 : */
149 : bool IsEnabled() const;
150 :
151 : /**
152 : * Returns whether this is object is set to be hidden.
153 : */
154 : bool IsHidden() const;
155 :
156 0 : void SetHidden(bool hidden) { m_Hidden.Set(hidden, true); }
157 :
158 : /**
159 : * Returns whether this object is set to be hidden or ghost.
160 : */
161 : bool IsHiddenOrGhost() const;
162 :
163 : /**
164 : * Retrieves the configured sound filename from the given setting name and plays that once.
165 : */
166 : void PlaySound(const CStrW& soundPath) const;
167 :
168 : /**
169 : * Send event to this GUI object (HandleMessage and ScriptEvent)
170 : *
171 : * @param type Type of GUI message to be handled
172 : * @param eventName String representation of event name
173 : * @return IN_HANDLED if event was handled, or IN_PASS if skipped
174 : */
175 : InReaction SendEvent(EGUIMessageType type, const CStr& eventName);
176 :
177 : /**
178 : * Same as SendEvent, but passes mouse coordinates and button state as an argument.
179 : */
180 : InReaction SendMouseEvent(EGUIMessageType type, const CStr& eventName);
181 :
182 : /**
183 : * All sizes are relative to resolution, and the calculation
184 : * is not wanted in real time, therefore it is cached, update
185 : * the cached size with this function.
186 : */
187 : virtual void UpdateCachedSize();
188 :
189 : /**
190 : * Updates and returns the size of the object.
191 : */
192 : CRect GetComputedSize();
193 :
194 0 : virtual const CStrW& GetTooltipText() const { return m_Tooltip; }
195 0 : virtual const CStr& GetTooltipStyle() const { return m_TooltipStyle; }
196 :
197 : /**
198 : * Reset internal state of this object.
199 : */
200 : virtual void ResetStates();
201 :
202 : /**
203 : * Set the script handler for a particular object-specific action
204 : *
205 : * @param eventName Name of action
206 : * @param Code Javascript code to execute when the action occurs
207 : * @param pGUI GUI instance to associate the script with
208 : */
209 : void RegisterScriptHandler(const CStr& eventName, const CStr& Code, CGUI& pGUI);
210 :
211 : /**
212 : * Retrieves the JSObject representing this GUI object.
213 : */
214 : JSObject* GetJSObject();
215 :
216 : //@}
217 : protected:
218 : //--------------------------------------------------------
219 : /** @name Called by CGUI and friends
220 : *
221 : * Methods that the CGUI will call using
222 : * its friendship, these should not
223 : * be called by user.
224 : * TODO: this comment is old and the following interface should be cleaned up.
225 : */
226 : //--------------------------------------------------------
227 : //@{
228 :
229 : public:
230 :
231 : /**
232 : * Called on every GUI tick unless the object or one of its parent is hidden/ghost.
233 : */
234 8 : virtual void Tick() {};
235 :
236 : /**
237 : * This function is called with different messages
238 : * for instance when the mouse enters the object.
239 : *
240 : * @param Message GUI Message
241 : */
242 4 : virtual void HandleMessage(SGUIMessage& UNUSED(Message)) {}
243 :
244 : /**
245 : * Calls an IGUIObject member function recursively on this object and its children.
246 : * Aborts recursion at IGUIObjects that have the isRestricted function return true.
247 : * The arguments of the callback function must be references.
248 : */
249 : template<typename... Args>
250 61 : void RecurseObject(bool(IGUIObject::*isRestricted)() const, void(IGUIObject::*callbackFunction)(Args... args), Args&&... args)
251 : {
252 61 : if (!IsBaseObject())
253 : {
254 24 : if (isRestricted && (this->*isRestricted)())
255 0 : return;
256 :
257 24 : (this->*callbackFunction)(args...);
258 : }
259 :
260 85 : for (IGUIObject* const& obj : m_Children)
261 24 : obj->RecurseObject(isRestricted, callbackFunction, args...);
262 : }
263 :
264 : protected:
265 : /**
266 : * Draws the object.
267 : */
268 : virtual void Draw(CCanvas2D& canvas) = 0;
269 :
270 : /**
271 : * Some objects need to be able to pre-emptively process SDL_Event_.
272 : *
273 : * Only the object with focus will have this function called.
274 : *
275 : * Returns either IN_PASS or IN_HANDLED. If IN_HANDLED, then
276 : * the event won't be passed on and processed by other handlers.
277 : */
278 0 : virtual InReaction PreemptEvent(const SDL_Event_* UNUSED(ev)) { return IN_PASS; }
279 :
280 : /**
281 : * Some objects need to handle the text-related SDL_Event_ manually.
282 : * For instance the input box.
283 : *
284 : * Only the object with focus will have this function called.
285 : *
286 : * Returns either IN_PASS or IN_HANDLED. If IN_HANDLED, then
287 : * the key won't be passed on and processed by other handlers.
288 : * This is used for keys that the GUI uses.
289 : */
290 0 : virtual InReaction ManuallyHandleKeys(const SDL_Event_* UNUSED(ev)) { return IN_PASS; }
291 :
292 : /**
293 : * Applies the given style to the object.
294 : *
295 : * Returns false if the style is not recognised (and thus has
296 : * not been applied).
297 : */
298 : bool ApplyStyle(const CStr& StyleName);
299 :
300 : /**
301 : * Returns not the Z value, but the actual buffered Z value, i.e. if it's
302 : * defined relative, then it will check its parent's Z value and add
303 : * the relativity.
304 : *
305 : * @return Actual Z value on the screen.
306 : */
307 : virtual float GetBufferedZ() const;
308 :
309 : /**
310 : * Add an object to the hierarchy.
311 : */
312 : void RegisterChild(IGUIObject* child);
313 :
314 : /**
315 : * Remove an object from the hierarchy.
316 : */
317 : void UnregisterChild(IGUIObject* child);
318 :
319 : /**
320 : * Set parent of this object
321 : */
322 4 : void SetParent(IGUIObject* pParent) { m_pParent = pParent; }
323 :
324 : public:
325 :
326 4 : CGUI& GetGUI() { return m_pGUI; }
327 : const CGUI& GetGUI() const { return m_pGUI; }
328 :
329 : /**
330 : * Take focus!
331 : */
332 : void SetFocus();
333 :
334 : /**
335 : * Release focus.
336 : */
337 : void ReleaseFocus();
338 :
339 : protected:
340 : /**
341 : * Check if object is focused.
342 : */
343 : bool IsFocused() const;
344 :
345 : /**
346 : * <b>NOTE!</b> This will not just return m_pParent, when that is
347 : * need use it! There is one exception to it, when the parent is
348 : * the top-node (the object that isn't a real object), this
349 : * will return nullptr, so that the top-node's children are
350 : * seemingly parentless.
351 : *
352 : * @return Pointer to parent
353 : */
354 : IGUIObject* GetParent() const;
355 :
356 : /**
357 : * Handle additional children to the \<object\>-tag. In IGUIObject, this function does
358 : * nothing. In CList and CDropDown, it handles the \<item\>, used to build the data.
359 : *
360 : * Returning false means the object doesn't recognize the child. Should be reported.
361 : * Notice 'false' is default, because an object not using this function, should not
362 : * have any additional children (and this function should never be called).
363 : */
364 0 : virtual bool HandleAdditionalChildren(const XMBData& UNUSED(file), const XMBElement& UNUSED(child))
365 : {
366 0 : return false;
367 : }
368 :
369 : /**
370 : * Allow the GUI object to process after all child items were handled.
371 : * Useful to avoid iterator invalidation with push_back calls.
372 : */
373 4 : virtual void AdditionalChildrenHandled() {}
374 :
375 : /**
376 : * Cached size, real size m_Size is actually dependent on resolution
377 : * and can have different *real* outcomes, this is the real outcome
378 : * cached to avoid slow calculations in real time.
379 : */
380 : CRect m_CachedActualSize;
381 :
382 : /**
383 : * Execute the script for a particular action.
384 : * Does nothing if no script has been registered for that action.
385 : * The mouse coordinates will be passed as the first argument.
386 : *
387 : * @param eventName Name of action
388 : */
389 : void ScriptEvent(const CStr& eventName);
390 :
391 : /**
392 : * Execute the script for a particular action.
393 : * Does nothing if no script has been registered for that action.
394 : * The mouse coordinates will be passed as the first argument.
395 : *
396 : * @param eventName Name of action
397 : *
398 : * @return True if the script returned something truthy.
399 : */
400 : bool ScriptEventWithReturn(const CStr& eventName);
401 :
402 : /**
403 : * Execute the script for a particular action.
404 : * Does nothing if no script has been registered for that action.
405 : *
406 : * @param eventName Name of action
407 : * @param paramData JS::HandleValueArray arguments to pass to the event.
408 : */
409 : void ScriptEvent(const CStr& eventName, const JS::HandleValueArray& paramData);
410 :
411 : /**
412 : * Execute the script for a particular action.
413 : * Does nothing if no script has been registered for that action.
414 : *
415 : * @param eventName Name of action
416 : * @param paramData JS::HandleValueArray arguments to pass to the event.
417 : *
418 : * @return True if the script returned something truthy.
419 : */
420 : bool ScriptEventWithReturn(const CStr& eventName, const JS::HandleValueArray& paramData);
421 :
422 : /**
423 : * Assigns a JS function to the event name.
424 : */
425 : void SetScriptHandler(const CStr& eventName, JS::HandleObject Function);
426 :
427 : /**
428 : * Deletes an event handler assigned to the given name, if such a handler exists.
429 : */
430 : void UnsetScriptHandler(const CStr& eventName);
431 :
432 : /**
433 : * Inputes the object that is currently hovered, this function
434 : * updates this object accordingly (i.e. if it's the object
435 : * being inputted one thing happens, and not, another).
436 : *
437 : * @param pMouseOver Object that is currently hovered, can be nullptr too!
438 : */
439 : void UpdateMouseOver(IGUIObject* const& pMouseOver);
440 :
441 : //@}
442 : private:
443 : //--------------------------------------------------------
444 : /** @name Internal functions */
445 : //--------------------------------------------------------
446 : //@{
447 :
448 : /**
449 : * Creates the JS object representing this page upon first use.
450 : * This function (and its derived versions) are defined in the GUIProxy implementation file for convenience.
451 : */
452 : virtual void CreateJSObject();
453 :
454 : /**
455 : * Updates some internal data depending on the setting changed.
456 : */
457 : void SettingChanged(const CStr& Setting, const bool SendMessage);
458 :
459 : /**
460 : * Inputs a reference pointer, checks if the new inputted object
461 : * if hovered, if so, then check if this's Z value is greater
462 : * than the inputted object... If so then the object is closer
463 : * and we'll replace the pointer with this.
464 : * Also Notice input can be nullptr, which means the Z value demand
465 : * is out. NOTICE you can't input nullptr as const so you'll have
466 : * to set an object to nullptr.
467 : *
468 : * @param pObject Object pointer, can be either the old one, or
469 : * the new one.
470 : */
471 : void ChooseMouseOverAndClosest(IGUIObject*& pObject);
472 :
473 : /**
474 : * Returns whether this is the object all other objects are descendants of.
475 : */
476 : bool IsBaseObject() const;
477 :
478 : /**
479 : * Returns whether this object is a child of the base object.
480 : */
481 : bool IsRootObject() const;
482 :
483 0 : static void Trace(JSTracer* trc, void* data)
484 : {
485 0 : reinterpret_cast<IGUIObject*>(data)->TraceMember(trc);
486 0 : }
487 :
488 : void TraceMember(JSTracer* trc);
489 :
490 : // Variables
491 : protected:
492 : static const CStr EventNameMouseEnter;
493 : static const CStr EventNameMouseMove;
494 : static const CStr EventNameMouseLeave;
495 :
496 : // Name of object
497 : CStr m_Name;
498 :
499 : // Constructed on the heap, will be destroyed along with the the CGUI
500 : std::vector<IGUIObject*> m_Children;
501 :
502 : // Pointer to parent
503 : IGUIObject* m_pParent;
504 :
505 : //This represents the last click time for each mouse button
506 : double m_LastClickTime[6];
507 :
508 : /**
509 : * This variable is true if the mouse is hovering this object and
510 : * this object is the topmost object shown in this location.
511 : */
512 : bool m_MouseHovering;
513 :
514 : /**
515 : * Settings pool, all an object's settings are located here
516 : */
517 : std::map<CStr, IGUISetting*> m_Settings;
518 :
519 : // An object can't function stand alone
520 : CGUI& m_pGUI;
521 :
522 : // Internal storage for registered script handlers.
523 : std::map<CStr, JS::Heap<JSObject*> > m_ScriptHandlers;
524 :
525 : // Cached JSObject representing this GUI object.
526 : std::unique_ptr<IGUIProxyObject> m_JSObject;
527 :
528 : CGUISimpleSetting<bool> m_Enabled;
529 : CGUISimpleSetting<bool> m_Hidden;
530 : CGUISimpleSetting<CGUISize> m_Size;
531 : CGUISimpleSetting<CStr> m_Style;
532 : CGUIHotkey m_Hotkey;
533 : CGUISimpleSetting<float> m_Z;
534 : CGUISimpleSetting<bool> m_Absolute;
535 : CGUISimpleSetting<bool> m_Ghost;
536 : CGUISimpleSetting<float> m_AspectRatio;
537 : CGUISimpleSetting<CStrW> m_Tooltip;
538 : CGUISimpleSetting<CStr> m_TooltipStyle;
539 : };
540 :
541 : #endif // INCLUDED_IGUIOBJECT
|