mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-06-17 13:53:57 -07:00
The JS-side counterparts of C++ objects are now Proxy objects, in anticipation of SM60 changes that remove the get/set hooks entirely. This makes semantic sense too as they are essentially phantom wrappers around the C++ objects, with no proper JS representation. By using different proxy handlers for different GUI object types, we can further fix issues encountered in D2136 by defining the relevant functions only on objects that should have them. The main complexity with proxy handlers is that Spidermonkey assumes in several places that they are static and data-less, so they cannot be used directly to hold data. This diff works around that issue by storing per-script-interface data in the CGui directly. Further API changes in SM60 make this slightly cleaner. Comments by: Itms Refs #5859 Differential Revision: https://code.wildfiregames.com/D2768 This was SVN commit r24229.
556 lines
16 KiB
C++
556 lines
16 KiB
C++
/* Copyright (C) 2019 Wildfire Games.
|
|
* This file is part of 0 A.D.
|
|
*
|
|
* 0 A.D. is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* 0 A.D. is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/*
|
|
* The base class of an object.
|
|
* All objects are derived from this class.
|
|
* It's an abstract data type, so it can't be used per se.
|
|
* Also contains a Dummy object which is used for completely blank objects.
|
|
*/
|
|
|
|
#ifndef INCLUDED_IGUIOBJECT
|
|
#define INCLUDED_IGUIOBJECT
|
|
|
|
#include "gui/SettingTypes/CGUISize.h"
|
|
#include "gui/SGUIMessage.h"
|
|
#include "lib/input.h" // just for IN_PASS
|
|
#include "ps/XML/Xeromyces.h"
|
|
#include "scriptinterface/ScriptTypes.h"
|
|
|
|
#include <map>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
class CGUI;
|
|
class IGUIObject;
|
|
class IGUISetting;
|
|
|
|
template <typename T>
|
|
class JSI_GUIProxy;
|
|
|
|
using map_pObjects = std::map<CStr, IGUIObject*>;
|
|
|
|
#define GUI_OBJECT(obj) \
|
|
public: \
|
|
static IGUIObject* ConstructObject(CGUI& pGUI) \
|
|
{ return new obj(pGUI); }
|
|
|
|
/**
|
|
* GUI object such as a button or an input-box.
|
|
* Abstract data type !
|
|
*/
|
|
class IGUIObject
|
|
{
|
|
friend class CGUI;
|
|
|
|
// Allow getProperty to access things like GetParent()
|
|
template <typename T>
|
|
friend class JSI_GUIProxy;
|
|
|
|
public:
|
|
NONCOPYABLE(IGUIObject);
|
|
|
|
IGUIObject(CGUI& pGUI);
|
|
virtual ~IGUIObject();
|
|
|
|
/**
|
|
* This function checks if the mouse is hovering the
|
|
* rectangle that the base setting "size" makes.
|
|
* Although it is virtual, so one could derive
|
|
* an object from CButton, which changes only this
|
|
* to checking the circle that "size" makes.
|
|
*
|
|
* This function also returns true if there is a different
|
|
* GUI object shown on top of this one.
|
|
*/
|
|
virtual bool IsMouseOver() const;
|
|
|
|
/**
|
|
* This function returns true if the mouse is hovering
|
|
* over this GUI object and if this GUI object is the
|
|
* topmost object in that screen location.
|
|
* For example when hovering dropdown list items, the
|
|
* buttons beneath the list won't return true here.
|
|
*/
|
|
virtual bool IsMouseHovering() const { return m_MouseHovering; }
|
|
|
|
/**
|
|
* Test if mouse position is over an icon
|
|
*/
|
|
virtual bool MouseOverIcon();
|
|
|
|
//--------------------------------------------------------
|
|
/** @name Leaf Functions */
|
|
//--------------------------------------------------------
|
|
//@{
|
|
|
|
/// Get object name, name is unique
|
|
const CStr& GetName() const { return m_Name; }
|
|
|
|
/// Get object name
|
|
void SetName(const CStr& Name) { m_Name = Name; }
|
|
|
|
// Get Presentable name.
|
|
// Will change all internally set names to something like "<unnamed object>"
|
|
CStr GetPresentableName() const;
|
|
|
|
/**
|
|
* Builds the object hierarchy with references.
|
|
*/
|
|
void AddChild(IGUIObject& pChild);
|
|
|
|
/**
|
|
* Return all child objects of the current object.
|
|
*/
|
|
const std::vector<IGUIObject*>& GetChildren() const { return m_Children; }
|
|
|
|
//@}
|
|
//--------------------------------------------------------
|
|
/** @name Settings Management */
|
|
//--------------------------------------------------------
|
|
//@{
|
|
|
|
/**
|
|
* Registers the given setting variables with the GUI object.
|
|
* Enable XML and JS to modify the given variable.
|
|
*
|
|
* @param Type Setting type
|
|
* @param Name Setting reference name
|
|
*/
|
|
template<typename T>
|
|
void RegisterSetting(const CStr& Name, T& Value);
|
|
|
|
/**
|
|
* Returns whether there is a setting with the given name registered.
|
|
*
|
|
* @param Setting setting name
|
|
* @return True if settings exist.
|
|
*/
|
|
bool SettingExists(const CStr& Setting) const;
|
|
|
|
/**
|
|
* Get a mutable reference to the setting.
|
|
* If no such setting exists, an exception of type std::out_of_range is thrown.
|
|
* If the value is modified, there is no GUIM_SETTINGS_UPDATED message sent.
|
|
*/
|
|
template <typename T>
|
|
T& GetSetting(const CStr& Setting);
|
|
|
|
template <typename T>
|
|
const T& GetSetting(const CStr& Setting) const;
|
|
|
|
/**
|
|
* Set a setting by string, regardless of what type it is.
|
|
* Used to parse setting values from XML files.
|
|
* For example a CRect(10,10,20,20) is created from "10 10 20 20".
|
|
* Returns false if the conversion fails, otherwise true.
|
|
*/
|
|
bool SetSettingFromString(const CStr& Setting, const CStrW& Value, const bool SendMessage);
|
|
|
|
/**
|
|
* Assigns the given value to the setting identified by the given name.
|
|
* Uses move semantics, so do not read from Value after this call.
|
|
*
|
|
* @param SendMessage If true, a GUIM_SETTINGS_UPDATED message will be broadcasted to all GUI objects.
|
|
*/
|
|
template <typename T>
|
|
void SetSetting(const CStr& Setting, T& Value, const bool SendMessage);
|
|
|
|
/**
|
|
* This variant will copy the value.
|
|
*/
|
|
template <typename T>
|
|
void SetSetting(const CStr& Setting, const T& Value, const bool SendMessage);
|
|
|
|
/**
|
|
* Returns whether this object is set to be hidden or ghost.
|
|
*/
|
|
bool IsEnabled() const;
|
|
|
|
/**
|
|
* Returns whether this is object is set to be hidden.
|
|
*/
|
|
bool IsHidden() const;
|
|
|
|
/**
|
|
* Returns whether this object is set to be hidden or ghost.
|
|
*/
|
|
bool IsHiddenOrGhost() const;
|
|
|
|
/**
|
|
* Retrieves the configured sound filename from the given setting name and plays that once.
|
|
*/
|
|
void PlaySound(const CStrW& soundPath) const;
|
|
|
|
/**
|
|
* Send event to this GUI object (HandleMessage and ScriptEvent)
|
|
*
|
|
* @param type Type of GUI message to be handled
|
|
* @param eventName String representation of event name
|
|
* @return IN_HANDLED if event was handled, or IN_PASS if skipped
|
|
*/
|
|
InReaction SendEvent(EGUIMessageType type, const CStr& eventName);
|
|
|
|
/**
|
|
* Same as SendEvent, but passes mouse coordinates and button state as an argument.
|
|
*/
|
|
InReaction SendMouseEvent(EGUIMessageType type, const CStr& eventName);
|
|
|
|
/**
|
|
* All sizes are relative to resolution, and the calculation
|
|
* is not wanted in real time, therefore it is cached, update
|
|
* the cached size with this function.
|
|
*/
|
|
virtual void UpdateCachedSize();
|
|
|
|
/**
|
|
* Reset internal state of this object.
|
|
*/
|
|
virtual void ResetStates();
|
|
|
|
/**
|
|
* Set the script handler for a particular object-specific action
|
|
*
|
|
* @param eventName Name of action
|
|
* @param Code Javascript code to execute when the action occurs
|
|
* @param pGUI GUI instance to associate the script with
|
|
*/
|
|
void RegisterScriptHandler(const CStr& eventName, const CStr& Code, CGUI& pGUI);
|
|
|
|
/**
|
|
* Retrieves the JSObject representing this GUI object.
|
|
*/
|
|
JSObject* GetJSObject();
|
|
|
|
/**
|
|
* The following functions are called from JS.
|
|
*/
|
|
void toString(ScriptInterface& scriptInterface, JS::MutableHandleValue ret);
|
|
void focus(ScriptInterface& scriptInterface, JS::MutableHandleValue ret);
|
|
void blur(ScriptInterface& scriptInterface, JS::MutableHandleValue ret);
|
|
void getComputedSize(ScriptInterface& scriptInterface, JS::MutableHandleValue ret);
|
|
|
|
//@}
|
|
protected:
|
|
//--------------------------------------------------------
|
|
/** @name Called by CGUI and friends
|
|
*
|
|
* Methods that the CGUI will call using
|
|
* its friendship, these should not
|
|
* be called by user.
|
|
* These functions' security are a lot
|
|
* what constitutes the GUI's
|
|
*/
|
|
//--------------------------------------------------------
|
|
//@{
|
|
|
|
public:
|
|
|
|
/**
|
|
* Called on every GUI tick unless the object or one of its parent is hidden/ghost.
|
|
*/
|
|
virtual void Tick() {};
|
|
|
|
/**
|
|
* This function is called with different messages
|
|
* for instance when the mouse enters the object.
|
|
*
|
|
* @param Message GUI Message
|
|
*/
|
|
virtual void HandleMessage(SGUIMessage& UNUSED(Message)) {}
|
|
|
|
/**
|
|
* Calls an IGUIObject member function recursively on this object and its children.
|
|
* Aborts recursion at IGUIObjects that have the isRestricted function return true.
|
|
* The arguments of the callback function must be references.
|
|
*/
|
|
template<typename... Args>
|
|
void RecurseObject(bool(IGUIObject::*isRestricted)() const, void(IGUIObject::*callbackFunction)(Args... args), Args&&... args)
|
|
{
|
|
if (!IsBaseObject())
|
|
{
|
|
if (isRestricted && (this->*isRestricted)())
|
|
return;
|
|
|
|
(this->*callbackFunction)(args...);
|
|
}
|
|
|
|
for (IGUIObject* const& obj : m_Children)
|
|
obj->RecurseObject(isRestricted, callbackFunction, args...);
|
|
}
|
|
|
|
protected:
|
|
/**
|
|
* Draws the object.
|
|
*/
|
|
virtual void Draw() = 0;
|
|
|
|
/**
|
|
* Some objects need to be able to pre-emptively process SDL_Event_.
|
|
*
|
|
* Only the object with focus will have this function called.
|
|
*
|
|
* Returns either IN_PASS or IN_HANDLED. If IN_HANDLED, then
|
|
* the event won't be passed on and processed by other handlers.
|
|
*/
|
|
virtual InReaction PreemptEvent(const SDL_Event_* UNUSED(ev)) { return IN_PASS; }
|
|
|
|
/**
|
|
* Some objects need to handle the text-related SDL_Event_ manually.
|
|
* For instance the input box.
|
|
*
|
|
* Only the object with focus will have this function called.
|
|
*
|
|
* Returns either IN_PASS or IN_HANDLED. If IN_HANDLED, then
|
|
* the key won't be passed on and processed by other handlers.
|
|
* This is used for keys that the GUI uses.
|
|
*/
|
|
virtual InReaction ManuallyHandleKeys(const SDL_Event_* UNUSED(ev)) { return IN_PASS; }
|
|
|
|
/**
|
|
* Loads a style.
|
|
*/
|
|
void LoadStyle(const CStr& StyleName);
|
|
|
|
/**
|
|
* Returns not the Z value, but the actual buffered Z value, i.e. if it's
|
|
* defined relative, then it will check its parent's Z value and add
|
|
* the relativity.
|
|
*
|
|
* @return Actual Z value on the screen.
|
|
*/
|
|
virtual float GetBufferedZ() const;
|
|
|
|
/**
|
|
* Set parent of this object
|
|
*/
|
|
void SetParent(IGUIObject* pParent) { m_pParent = pParent; }
|
|
|
|
public:
|
|
|
|
CGUI& GetGUI() { return m_pGUI; }
|
|
const CGUI& GetGUI() const { return m_pGUI; }
|
|
|
|
/**
|
|
* Take focus!
|
|
*/
|
|
void SetFocus();
|
|
|
|
protected:
|
|
/**
|
|
* Check if object is focused.
|
|
*/
|
|
bool IsFocused() const;
|
|
|
|
/**
|
|
* <b>NOTE!</b> This will not just return m_pParent, when that is
|
|
* need use it! There is one exception to it, when the parent is
|
|
* the top-node (the object that isn't a real object), this
|
|
* will return nullptr, so that the top-node's children are
|
|
* seemingly parentless.
|
|
*
|
|
* @return Pointer to parent
|
|
*/
|
|
IGUIObject* GetParent() const;
|
|
|
|
/**
|
|
* Handle additional children to the \<object\>-tag. In IGUIObject, this function does
|
|
* nothing. In CList and CDropDown, it handles the \<item\>, used to build the data.
|
|
*
|
|
* Returning false means the object doesn't recognize the child. Should be reported.
|
|
* Notice 'false' is default, because an object not using this function, should not
|
|
* have any additional children (and this function should never be called).
|
|
*/
|
|
virtual bool HandleAdditionalChildren(const XMBElement& UNUSED(child), CXeromyces* UNUSED(pFile))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Allow the GUI object to process after all child items were handled.
|
|
* Useful to avoid iterator invalidation with push_back calls.
|
|
*/
|
|
virtual void AdditionalChildrenHandled() {}
|
|
|
|
/**
|
|
* Cached size, real size m_Size is actually dependent on resolution
|
|
* and can have different *real* outcomes, this is the real outcome
|
|
* cached to avoid slow calculations in real time.
|
|
*/
|
|
CRect m_CachedActualSize;
|
|
|
|
/**
|
|
* Execute the script for a particular action.
|
|
* Does nothing if no script has been registered for that action.
|
|
* The mouse coordinates will be passed as the first argument.
|
|
*
|
|
* @param eventName Name of action
|
|
*/
|
|
void ScriptEvent(const CStr& eventName);
|
|
|
|
/**
|
|
* Execute the script for a particular action.
|
|
* Does nothing if no script has been registered for that action.
|
|
* The mouse coordinates will be passed as the first argument.
|
|
*
|
|
* @param eventName Name of action
|
|
*
|
|
* @return True if the script returned something truthy.
|
|
*/
|
|
bool ScriptEventWithReturn(const CStr& eventName);
|
|
|
|
/**
|
|
* Execute the script for a particular action.
|
|
* Does nothing if no script has been registered for that action.
|
|
*
|
|
* @param eventName Name of action
|
|
* @param paramData JS::HandleValueArray arguments to pass to the event.
|
|
*/
|
|
void ScriptEvent(const CStr& eventName, const JS::HandleValueArray& paramData);
|
|
|
|
/**
|
|
* Execute the script for a particular action.
|
|
* Does nothing if no script has been registered for that action.
|
|
*
|
|
* @param eventName Name of action
|
|
* @param paramData JS::HandleValueArray arguments to pass to the event.
|
|
*
|
|
* @return True if the script returned something truthy.
|
|
*/
|
|
bool ScriptEventWithReturn(const CStr& eventName, const JS::HandleValueArray& paramData);
|
|
|
|
/**
|
|
* Assigns a JS function to the event name.
|
|
*/
|
|
void SetScriptHandler(const CStr& eventName, JS::HandleObject Function);
|
|
|
|
/**
|
|
* Deletes an event handler assigned to the given name, if such a handler exists.
|
|
*/
|
|
void UnsetScriptHandler(const CStr& eventName);
|
|
|
|
/**
|
|
* Inputes the object that is currently hovered, this function
|
|
* updates this object accordingly (i.e. if it's the object
|
|
* being inputted one thing happens, and not, another).
|
|
*
|
|
* @param pMouseOver Object that is currently hovered, can be nullptr too!
|
|
*/
|
|
void UpdateMouseOver(IGUIObject* const& pMouseOver);
|
|
|
|
//@}
|
|
private:
|
|
//--------------------------------------------------------
|
|
/** @name Internal functions */
|
|
//--------------------------------------------------------
|
|
//@{
|
|
|
|
/**
|
|
* Creates the JS object representing this page upon first use.
|
|
*/
|
|
virtual void CreateJSObject();
|
|
|
|
/**
|
|
* Updates some internal data depending on the setting changed.
|
|
*/
|
|
void PreSettingChange(const CStr& Setting);
|
|
void SettingChanged(const CStr& Setting, const bool SendMessage);
|
|
|
|
/**
|
|
* Inputs a reference pointer, checks if the new inputted object
|
|
* if hovered, if so, then check if this's Z value is greater
|
|
* than the inputted object... If so then the object is closer
|
|
* and we'll replace the pointer with this.
|
|
* Also Notice input can be nullptr, which means the Z value demand
|
|
* is out. NOTICE you can't input nullptr as const so you'll have
|
|
* to set an object to nullptr.
|
|
*
|
|
* @param pObject Object pointer, can be either the old one, or
|
|
* the new one.
|
|
*/
|
|
void ChooseMouseOverAndClosest(IGUIObject*& pObject);
|
|
|
|
/**
|
|
* Returns whether this is the object all other objects are descendants of.
|
|
*/
|
|
bool IsBaseObject() const;
|
|
|
|
/**
|
|
* Returns whether this object is a child of the base object.
|
|
*/
|
|
bool IsRootObject() const;
|
|
|
|
static void Trace(JSTracer* trc, void* data)
|
|
{
|
|
reinterpret_cast<IGUIObject*>(data)->TraceMember(trc);
|
|
}
|
|
|
|
void TraceMember(JSTracer* trc);
|
|
|
|
// Variables
|
|
protected:
|
|
static const CStr EventNameMouseEnter;
|
|
static const CStr EventNameMouseMove;
|
|
static const CStr EventNameMouseLeave;
|
|
|
|
// Name of object
|
|
CStr m_Name;
|
|
|
|
// Constructed on the heap, will be destroyed along with the the CGUI
|
|
std::vector<IGUIObject*> m_Children;
|
|
|
|
// Pointer to parent
|
|
IGUIObject* m_pParent;
|
|
|
|
//This represents the last click time for each mouse button
|
|
double m_LastClickTime[6];
|
|
|
|
/**
|
|
* This variable is true if the mouse is hovering this object and
|
|
* this object is the topmost object shown in this location.
|
|
*/
|
|
bool m_MouseHovering;
|
|
|
|
/**
|
|
* Settings pool, all an object's settings are located here
|
|
*/
|
|
std::map<CStr, IGUISetting*> m_Settings;
|
|
|
|
// An object can't function stand alone
|
|
CGUI& m_pGUI;
|
|
|
|
// Internal storage for registered script handlers.
|
|
std::map<CStr, JS::Heap<JSObject*> > m_ScriptHandlers;
|
|
|
|
// Cached JSObject representing this GUI object
|
|
JS::PersistentRootedObject m_JSObject;
|
|
|
|
// Cache references to settings for performance
|
|
bool m_Enabled;
|
|
bool m_Hidden;
|
|
CGUISize m_Size;
|
|
CStr m_Style;
|
|
CStr m_Hotkey;
|
|
float m_Z;
|
|
bool m_Absolute;
|
|
bool m_Ghost;
|
|
float m_AspectRatio;
|
|
CStrW m_Tooltip;
|
|
CStr m_TooltipStyle;
|
|
};
|
|
|
|
#endif // INCLUDED_IGUIOBJECT
|