Simplify GUI Proxy implementation & customisation.

This finishes 7c04ea0211 and 1b67a079fb.
GUI Proxy objects are now easier to specialize, code duplication is
reduced, code is made safer.
In details:
- the proxy private is always the IGUIObject* pointer
- the private data is accessed through a safer wrapper
- CreateJSObject returns an opaque type to allow easier extension &
prevent errors.
- The implementation of CreateJSObject is moved near the GUI Proxy
template instantiation, and both are wrapped in a convenient macro (this
makes it so that if you use the macro, you can't forget to overload the
method, and vice-versa).
- The common IGUIObject JS interface no longer needs to be repeated.
- All specialisations are again put in the same file, for improved
compile-time & clarity, given there are so few lines now.
- While at it, implement toSource which makes it possible to `uneval`
components (does the same as toString though).


Differential Revision: https://code.wildfiregames.com/D3826
This was SVN commit r25225.
This commit is contained in:
wraitii 2021-04-09 18:01:47 +00:00
parent 3759fc8a98
commit fc60d80af1
16 changed files with 221 additions and 290 deletions

View file

@ -273,6 +273,13 @@ void IGUIObject::UpdateCachedSize()
}
}
CRect IGUIObject::GetComputedSize()
{
UpdateCachedSize();
return m_CachedActualSize;
}
bool IGUIObject::ApplyStyle(const CStr& StyleName)
{
if (!m_pGUI.HasStyle(StyleName))
@ -460,46 +467,14 @@ bool IGUIObject::ScriptEventWithReturn(const CStr& eventName, const JS::HandleVa
return JS::ToBoolean(result);
}
void IGUIObject::CreateJSObject()
{
ScriptRequest rq(m_pGUI.GetScriptInterface());
using ProxyHandler = JSI_GUIProxy<std::remove_pointer_t<decltype(this)>>;
ProxyHandler::CreateJSObject(rq, this, GetGUI().GetProxyData(&ProxyHandler::Singleton()), m_JSObject);
}
JSObject* IGUIObject::GetJSObject()
{
// Cache the object when somebody first asks for it, because otherwise
// we end up doing far too much object allocation.
if (!m_JSObject.initialized())
if (!m_JSObject)
CreateJSObject();
return m_JSObject.get();
}
void IGUIObject::toString(ScriptInterface& scriptInterface, JS::MutableHandleValue ret)
{
ScriptRequest rq(scriptInterface);
ScriptInterface::ToJSVal(rq, ret, "[GUIObject: " + GetName() + "]");
}
void IGUIObject::focus(ScriptInterface& UNUSED(scriptInterface), JS::MutableHandleValue ret)
{
GetGUI().SetFocusedObject(this);
ret.setUndefined();
}
void IGUIObject::blur(ScriptInterface& UNUSED(scriptInterface), JS::MutableHandleValue ret)
{
GetGUI().SetFocusedObject(nullptr);
ret.setUndefined();
}
void IGUIObject::getComputedSize(ScriptInterface& scriptInterface, JS::MutableHandleValue ret)
{
UpdateCachedSize();
ScriptRequest rq(scriptInterface);
ScriptInterface::ToJSVal(rq, ret, m_CachedActualSize);
return m_JSObject->Get();
}
bool IGUIObject::IsEnabled() const
@ -541,6 +516,11 @@ void IGUIObject::SetFocus()
m_pGUI.SetFocusedObject(this);
}
void IGUIObject::ReleaseFocus()
{
m_pGUI.SetFocusedObject(nullptr);
}
bool IGUIObject::IsFocused() const
{
return m_pGUI.GetFocusedObject() == this;

View file

@ -36,6 +36,7 @@
class CGUI;
class IGUIObject;
class IGUIProxyObject;
class IGUISetting;
template <typename T>
@ -214,6 +215,11 @@ public:
*/
virtual void UpdateCachedSize();
/**
* Updates and returns the size of the object.
*/
CRect GetComputedSize();
/**
* Reset internal state of this object.
*/
@ -233,14 +239,6 @@ public:
*/
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:
//--------------------------------------------------------
@ -350,6 +348,11 @@ public:
*/
void SetFocus();
/**
* Release focus.
*/
void ReleaseFocus();
protected:
/**
* Check if object is focused.
@ -461,6 +464,7 @@ private:
/**
* Creates the JS object representing this page upon first use.
* This function (and its derived versions) are defined in the GUIProxy implementation file for convenience.
*/
virtual void CreateJSObject();
@ -536,8 +540,8 @@ protected:
// Internal storage for registered script handlers.
std::map<CStr, JS::Heap<JSObject*> > m_ScriptHandlers;
// Cached JSObject representing this GUI object
JS::PersistentRootedObject m_JSObject;
// Cached JSObject representing this GUI object.
std::unique_ptr<IGUIProxyObject> m_JSObject;
// Cache references to settings for performance
bool m_Enabled;

View file

@ -21,7 +21,6 @@
#include "gui/CGUI.h"
#include "gui/CGUIText.h"
#include "gui/Scripting/JSInterface_GUIProxy.h"
#include "gui/SettingTypes/CGUIColor.h"
CButton::CButton(CGUI& pGUI)
@ -83,6 +82,12 @@ void CButton::UpdateCachedSize()
IGUITextOwner::UpdateCachedSize();
}
CSize2D CButton::GetTextSize()
{
UpdateText();
return m_GeneratedTexts[0].GetSize();
}
void CButton::HandleMessage(SGUIMessage& Message)
{
IGUIObject::HandleMessage(Message);
@ -115,17 +120,3 @@ const CGUIColor& CButton::ChooseColor()
return m_TextColorOver ? m_TextColorOver : m_TextColor;
}
void CButton::getTextSize(ScriptInterface& scriptInterface, JS::MutableHandleValue ret)
{
ScriptRequest rq(scriptInterface);
UpdateText();
ScriptInterface::ToJSVal(rq, ret, m_GeneratedTexts[0].GetSize());
}
void CButton::CreateJSObject()
{
ScriptRequest rq(m_pGUI.GetScriptInterface());
using ProxyHandler = JSI_GUIProxy<std::remove_pointer_t<decltype(this)>>;
ProxyHandler::CreateJSObject(rq, this, GetGUI().GetProxyData(&ProxyHandler::Singleton()), m_JSObject);
}

View file

@ -42,6 +42,11 @@ public:
*/
virtual void UpdateCachedSize();
/**
* @return the object text size.
*/
CSize2D GetTextSize();
/**
* @see IGUIObject#HandleMessage()
*/
@ -52,11 +57,6 @@ public:
*/
virtual void Draw();
/**
* Populate @param ret with the object's text size.
*/
void getTextSize(ScriptInterface& scriptInterface, JS::MutableHandleValue ret);
protected:
/**
* Sets up text, should be called every time changes has been

View file

@ -23,7 +23,6 @@
#include "gui/CGUIScrollBarVertical.h"
#include "gui/SettingTypes/CGUIColor.h"
#include "gui/SettingTypes/CGUIList.h"
#include "gui/Scripting/JSInterface_GUIProxy.h"
#include "lib/external_libraries/libsdl.h"
#include "lib/timer.h"
@ -408,6 +407,11 @@ void CList::AddItem(const CGUIString& str, const CGUIString& data)
SetupText(true);
}
void CList::AddItem(const CGUIString& strAndData)
{
AddItem(strAndData, strAndData);
}
bool CList::HandleAdditionalChildren(const XMBElement& child, CXeromyces* pFile)
{
int elmt_item = pFile->GetElementID("item");
@ -498,11 +502,3 @@ int CList::GetHoveredItem()
return -1;
}
void CList::CreateJSObject()
{
ScriptRequest rq(m_pGUI.GetScriptInterface());
using ProxyHandler = JSI_GUIProxy<std::remove_pointer_t<decltype(this)>>;
ProxyHandler::CreateJSObject(rq, this, GetGUI().GetProxyData(&ProxyHandler::Singleton()), m_JSObject);
}

View file

@ -57,6 +57,11 @@ public:
*/
virtual void AddItem(const CGUIString& str, const CGUIString& data);
/**
* Add an item where both parameters are identical.
*/
void AddItem(const CGUIString& strAndData);
protected:
/**
* Sets up text, should be called every time changes has been

View file

@ -22,7 +22,6 @@
#include "gui/CGUI.h"
#include "gui/CGUIScrollBarVertical.h"
#include "gui/CGUIText.h"
#include "gui/Scripting/JSInterface_GUIProxy.h"
#include "scriptinterface/ScriptInterface.h"
CText::CText(CGUI& pGUI)
@ -133,6 +132,12 @@ void CText::UpdateCachedSize()
IGUITextOwner::UpdateCachedSize();
}
CSize2D CText::GetTextSize()
{
UpdateText();
return m_GeneratedTexts[0].GetSize();
}
void CText::HandleMessage(SGUIMessage& Message)
{
IGUIObject::HandleMessage(Message);
@ -250,18 +255,3 @@ bool CText::MouseOverIcon()
return false;
}
void CText::CreateJSObject()
{
ScriptRequest rq(m_pGUI.GetScriptInterface());
using ProxyHandler = JSI_GUIProxy<std::remove_pointer_t<decltype(this)>>;
ProxyHandler::CreateJSObject(rq, this, GetGUI().GetProxyData(&ProxyHandler::Singleton()), m_JSObject);
}
void CText::getTextSize(ScriptInterface& scriptInterface, JS::MutableHandleValue ret)
{
ScriptRequest rq(scriptInterface);
UpdateText();
ScriptInterface::ToJSVal(rq, ret, m_GeneratedTexts[0].GetSize());
}

View file

@ -44,15 +44,15 @@ public:
*/
virtual void UpdateCachedSize();
/**
* @return the object text size.
*/
CSize2D GetTextSize();
/**
* Test if mouse position is over an icon
*/
virtual bool MouseOverIcon();
/**
* Populate @param ret with the object's text size.
*/
void getTextSize(ScriptInterface& scriptInterface, JS::MutableHandleValue ret);
protected:
/**
* Sets up text, should be called every time changes has been

View file

@ -1,38 +0,0 @@
/* Copyright (C) 2020 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/>.
*/
#include "precompiled.h"
#include "JSInterface_GUIProxy_impl.h"
#include "gui/ObjectTypes/CButton.h"
using GUIObjectType = CButton;
template<>
void JSI_GUIProxy<GUIObjectType>::CreateFunctions(const ScriptRequest& rq, GUIProxyProps* cache)
{
#define func(class, func) &JSInterface_GUIProxy::apply_to<GUIObjectType, class, &class::func>
cache->setFunction(rq, "toString", func(IGUIObject, toString), 0);
cache->setFunction(rq, "focus", func(IGUIObject, focus), 0);
cache->setFunction(rq, "blur", func(IGUIObject, blur), 0);
cache->setFunction(rq, "getComputedSize", func(IGUIObject, getComputedSize), 0);
cache->setFunction(rq, "getTextSize", func(GUIObjectType, getTextSize), 0);
#undef func
}
template class JSI_GUIProxy<GUIObjectType>;

View file

@ -1,54 +0,0 @@
/* Copyright (C) 2020 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/>.
*/
#include "precompiled.h"
#include "JSInterface_GUIProxy_impl.h"
#include "gui/ObjectTypes/CList.h"
bool CList_AddItem(JSContext* cx, uint argc, JS::Value* vp)
{
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
CList* e = static_cast<CList*>(js::GetProxyPrivate(args.thisv().toObjectOrNull()).toPrivate());
if (!e)
return false;
ScriptInterface& scriptInterface = *ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface;
ScriptRequest rq(scriptInterface);
CGUIString text;
if (!ScriptInterface::FromJSVal(rq, args.get(0), text))
return false;
e->AddItem(text, text);
return true;
}
using GUIObjectType = CList;
template<>
void JSI_GUIProxy<GUIObjectType>::CreateFunctions(const ScriptRequest& rq, GUIProxyProps* cache)
{
#define func(class, func) &JSInterface_GUIProxy::apply_to<GUIObjectType, class, &class::func>
cache->setFunction(rq, "toString", func(IGUIObject, toString), 0);
cache->setFunction(rq, "focus", func(IGUIObject, focus), 0);
cache->setFunction(rq, "blur", func(IGUIObject, blur), 0);
cache->setFunction(rq, "getComputedSize", func(IGUIObject, getComputedSize), 0);
#undef func
cache->setFunction(rq, "addItem", CList_AddItem, 1);
}
template class JSI_GUIProxy<GUIObjectType>;

View file

@ -1,38 +0,0 @@
/* Copyright (C) 2020 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/>.
*/
#include "precompiled.h"
#include "JSInterface_GUIProxy_impl.h"
#include "gui/ObjectTypes/CText.h"
using GUIObjectType = CText;
template<>
void JSI_GUIProxy<GUIObjectType>::CreateFunctions(const ScriptRequest& rq, GUIProxyProps* cache)
{
#define func(class, func) &JSInterface_GUIProxy::apply_to<GUIObjectType, class, &class::func>
cache->setFunction(rq, "toString", func(IGUIObject, toString), 0);
cache->setFunction(rq, "focus", func(IGUIObject, focus), 0);
cache->setFunction(rq, "blur", func(IGUIObject, blur), 0);
cache->setFunction(rq, "getComputedSize", func(IGUIObject, getComputedSize), 0);
cache->setFunction(rq, "getTextSize", func(GUIObjectType, getTextSize), 0);
#undef func
}
template class JSI_GUIProxy<GUIObjectType>;

View file

@ -0,0 +1,60 @@
/* Copyright (C) 2021 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/>.
*/
#include "precompiled.h"
#include "JSInterface_GUIProxy_impl.h"
#include "gui/ObjectBases/IGUIObject.h"
#include "gui/ObjectTypes/CButton.h"
#include "gui/ObjectTypes/CList.h"
#include "gui/ObjectTypes/CText.h"
// Called for every specialization - adds the common interface.
template<>
void JSI_GUIProxy<IGUIObject>::CreateFunctions(const ScriptRequest& rq, GUIProxyProps* cache)
{
CreateFunction<&IGUIObject::GetName>(rq, cache, "toString");
CreateFunction<&IGUIObject::GetName>(rq, cache, "toSource");
CreateFunction<&IGUIObject::SetFocus>(rq, cache, "focus");
CreateFunction<&IGUIObject::ReleaseFocus>(rq, cache, "blur");
CreateFunction<&IGUIObject::GetComputedSize>(rq, cache, "getComputedSize");
}
DECLARE_GUIPROXY(IGUIObject);
// Implement derived types below.
// CButton
template<> void JSI_GUIProxy<CButton>::CreateFunctions(const ScriptRequest& rq, GUIProxyProps* cache)
{
CreateFunction<&CButton::GetTextSize>(rq, cache, "getTextSize");
}
DECLARE_GUIPROXY(CButton);
// CText
template<> void JSI_GUIProxy<CText>::CreateFunctions(const ScriptRequest& rq, GUIProxyProps* cache)
{
CreateFunction<&CText::GetTextSize>(rq, cache, "getTextSize");
}
DECLARE_GUIPROXY(CText);
// CList
template<> void JSI_GUIProxy<CList>::CreateFunctions(const ScriptRequest& rq, GUIProxyProps* cache)
{
CreateFunction<static_cast<void(CList::*)(const CGUIString&)>(&CList::AddItem)>(rq, cache, "addItem");
}
DECLARE_GUIPROXY(CList);

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2019 Wildfire Games.
/* Copyright (C) 2021 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -20,11 +20,15 @@
#include "scriptinterface/ScriptExtraHeaders.h"
#include <memory>
#include <utility>
class ScriptInterface;
class ScriptRequest;
template <typename T>
class JSI_GUIProxy;
// See JSI_GuiProxy below
#if GCC_VERSION
# pragma GCC diagnostic push
@ -34,6 +38,37 @@ class ScriptRequest;
# pragma warning(disable: 4265)
#endif
/**
* JS GUI proxies need to store some private data.
* This class is responsible for deleting that data.
*/
class IGUIProxyObject final
{
template<typename T>
friend class JSI_GUIProxy;
friend std::unique_ptr<IGUIProxyObject> std::make_unique<IGUIProxyObject>();
public:
JSObject* Get() const
{
return m_Object.get();
}
using PrivateData = IGUIObject*;
template<typename T>
static T* FromPrivateSlot(JSObject* obj)
{
return static_cast<T*>(static_cast<PrivateData>(js::GetProxyPrivate(obj).toPrivate()));
}
protected:
IGUIProxyObject() = default;
IGUIProxyObject(const IGUIProxyObject&) = delete;
IGUIProxyObject(IGUIProxyObject&&) = delete;
JS::PersistentRootedObject m_Object;
PrivateData m_Ptr;
};
/**
* Proxies need to store some data whose lifetime is tied to an interface.
* This is the virtual interface of that data.
@ -47,7 +82,7 @@ public:
virtual bool has(const std::string& name) const = 0;
// @return the JSFunction matching @param name. Must call has() first as it can assume existence.
virtual JSObject* get(const std::string& name) const = 0;
virtual bool setFunction(const ScriptRequest& rq, const std::string& name, JSNative function, int nargs) = 0;
virtual bool setFunction(const ScriptRequest& rq, const std::string& name, JSFunction* function) = 0;
};
/**
@ -67,14 +102,17 @@ public:
* As such, these GUI proxies don't have one and instead override get/set directly.
*
* To add a new JSI_GUIProxy, you'll need to:
* - overload the GUI Object's CreateJSObject with the correct types and pointers
* - change the CGUI::AddObjectTypes method
* - explicitly instantiate the template.
* - overload CreateJSObject in your class header.
* - change the CGUI::AddObjectTypes method.
* - explicitly instantiate the template & CreateJSObject via DECLARE_GUIPROXY.
*
*/
template<typename GUIObjectType>
class JSI_GUIProxy : public js::BaseProxyHandler
{
// Need to friend other specializations so CreateFunctions() can call the IGUIObject version in all codepaths.
template<typename T>
friend class JSI_GUIProxy;
public:
// Access the js::Class of the Proxy.
static JSClass& ClassDefinition();
@ -85,7 +123,8 @@ public:
// Call this in CGUI::AddObjectTypes.
static std::pair<const js::BaseProxyHandler*, GUIProxyProps*> CreateData(ScriptInterface& scriptInterface);
static void CreateJSObject(const ScriptRequest& rq, GUIObjectType* ptr, GUIProxyProps* data, JS::PersistentRootedObject& val);
// Create the JS object, the proxy, the data and wrap it in a convenient unique_ptr.
static std::unique_ptr<IGUIProxyObject> CreateJSObject(const ScriptRequest& rq, GUIObjectType* ptr, GUIProxyProps* data);
protected:
// @param family can't be nullptr because that's used for some DOM object and it crashes.
JSI_GUIProxy() : BaseProxyHandler(this, false, false) {};
@ -101,6 +140,10 @@ protected:
// Specialize this to define the custom properties of this type.
static void CreateFunctions(const ScriptRequest& rq, GUIProxyProps* cache);
// Convenience helper for the above.
template<auto callable>
static void CreateFunction(const ScriptRequest& rq, GUIProxyProps* cache, const std::string& name);
// This handles returning custom properties. Specialize this if needed.
bool PropGetter(JS::HandleObject proxy, const std::string& propName, JS::MutableHandleValue vp) const;
protected:

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2020 Wildfire Games.
/* Copyright (C) 2021 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -23,6 +23,7 @@
#include "gui/CGUISetting.h"
#include "gui/ObjectBases/IGUIObject.h"
#include "ps/CLogger.h"
#include "scriptinterface/FunctionWrapper.h"
#include "scriptinterface/ScriptExtraHeaders.h"
#include "scriptinterface/ScriptInterface.h"
@ -42,23 +43,23 @@ JSI_GUIProxy<T>& JSI_GUIProxy<T>::Singleton()
return s;
}
// Call this for every specialised type. You will need to override IGUIObject::CreateJSObject() in your class interface.
#define DECLARE_GUIPROXY(Type) \
void Type::CreateJSObject() \
{ \
ScriptRequest rq(m_pGUI.GetScriptInterface()); \
using ProxyHandler = JSI_GUIProxy<std::remove_pointer_t<decltype(this)>>; \
m_JSObject = ProxyHandler::CreateJSObject(rq, this, GetGUI().GetProxyData(&ProxyHandler::Singleton())); \
} \
template class JSI_GUIProxy<Type>;
// Use a common namespace to avoid duplicating the symbols un-necessarily.
namespace JSInterface_GUIProxy
{
/**
* Conveniently wrap a simple C++ function to a JSNative.
*/
template<class OG, class R, void (R::*funcptr)(ScriptInterface&, JS::MutableHandleValue)>
inline bool apply_to(JSContext* cx, uint argc, JS::Value* vp)
template<typename T>
inline T* GuiObjectGetter(const ScriptRequest&, JS::CallArgs& args)
{
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
OG* e = static_cast<OG*>(js::GetProxyPrivate(args.thisv().toObjectOrNull()).toPrivate());
if (!e)
return false;
(static_cast<R*>(e)->*(funcptr))(*(ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface), args.rval());
return true;
return IGUIProxyObject::FromPrivateSlot<T>(args.thisv().toObjectOrNull());
}
// Default implementation of the cache via unordered_map
@ -77,9 +78,9 @@ public:
return m_Functions.at(name).get();
}
virtual bool setFunction(const ScriptRequest& rq, const std::string& name, JSNative function, int nargs) override
virtual bool setFunction(const ScriptRequest& rq, const std::string& name, JSFunction* function) override
{
m_Functions[name].init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, function, nargs, 0, name.c_str())));
m_Functions[name].init(rq.cx, JS_GetFunctionObject(function));
return true;
}
@ -115,21 +116,38 @@ std::pair<const js::BaseProxyHandler*, GUIProxyProps*> JSI_GUIProxy<T>::CreateDa
using PropertyCache = typename PropCache::type;
PropertyCache* data = new PropertyCache();
ScriptRequest rq(scriptInterface);
CreateFunctions(rq, data);
// Functions common to all children of IGUIObject.
JSI_GUIProxy<IGUIObject>::CreateFunctions(rq, data);
// Let derived classes register their own interface.
if constexpr (!std::is_same_v<T, IGUIObject>)
CreateFunctions(rq, data);
return { &Singleton(), data };
}
template<typename T>
void JSI_GUIProxy<T>::CreateJSObject(const ScriptRequest& rq, T* ptr, GUIProxyProps* dataPtr, JS::PersistentRootedObject& val)
template<auto callable>
void JSI_GUIProxy<T>::CreateFunction(const ScriptRequest& rq, GUIProxyProps* cache, const std::string& name)
{
cache->setFunction(rq, name, ScriptFunction::Create<callable, JSInterface_GUIProxy::GuiObjectGetter<T>>(rq, name.c_str()));
}
template<typename T>
std::unique_ptr<IGUIProxyObject> JSI_GUIProxy<T>::CreateJSObject(const ScriptRequest& rq, T* ptr, GUIProxyProps* dataPtr)
{
js::ProxyOptions options;
options.setClass(&ClassDefinition());
auto ret = std::make_unique<IGUIProxyObject>();
ret->m_Ptr = static_cast<IGUIObject*>(ptr);
JS::RootedValue cppObj(rq.cx), data(rq.cx);
cppObj.get().setPrivate(ptr);
cppObj.get().setPrivate(ret->m_Ptr);
data.get().setPrivate(static_cast<void*>(dataPtr));
val.init(rq.cx, js::NewProxyObject(rq.cx, &Singleton(), cppObj, nullptr, options));
js::SetProxyReservedSlot(val, 0, data);
ret->m_Object.init(rq.cx, js::NewProxyObject(rq.cx, &Singleton(), cppObj, nullptr, options));
js::SetProxyReservedSlot(ret->m_Object, 0, data);
return ret;
}
template <typename T>
@ -138,7 +156,7 @@ bool JSI_GUIProxy<T>::get(JSContext* cx, JS::HandleObject proxy, JS::HandleValue
ScriptInterface* pScriptInterface = ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface;
ScriptRequest rq(*pScriptInterface);
T* e = static_cast<T*>(js::GetProxyPrivate(proxy.get()).toPrivate());
T* e = IGUIProxyObject::FromPrivateSlot<T>(proxy.get());
if (!e)
return false;
@ -206,7 +224,7 @@ template <typename T>
bool JSI_GUIProxy<T>::set(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleValue vp,
JS::HandleValue UNUSED(receiver), JS::ObjectOpResult& result) const
{
T* e = static_cast<T*>(js::GetProxyPrivate(proxy.get()).toPrivate());
T* e = IGUIProxyObject::FromPrivateSlot<T>(proxy.get());
if (!e)
{
LOGERROR("C++ GUI Object could not be found");
@ -261,7 +279,7 @@ bool JSI_GUIProxy<T>::set(JSContext* cx, JS::HandleObject proxy, JS::HandleId id
template<typename T>
bool JSI_GUIProxy<T>::delete_(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::ObjectOpResult& result) const
{
T* e = static_cast<T*>(js::GetProxyPrivate(proxy.get()).toPrivate());
T* e = IGUIProxyObject::FromPrivateSlot<T>(proxy.get());
if (!e)
{
LOGERROR("C++ GUI Object could not be found");

View file

@ -1,35 +0,0 @@
/* Copyright (C) 2020 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/>.
*/
#include "precompiled.h"
#include "JSInterface_GUIProxy_impl.h"
using GUIObjectType = IGUIObject;
template<>
void JSI_GUIProxy<GUIObjectType>::CreateFunctions(const ScriptRequest& rq, GUIProxyProps* cache)
{
#define func(class, func) &JSInterface_GUIProxy::apply_to<GUIObjectType, class, &class::func>
cache->setFunction(rq, "toString", func(IGUIObject, toString), 0);
cache->setFunction(rq, "focus", func(IGUIObject, focus), 0);
cache->setFunction(rq, "blur", func(IGUIObject, blur), 0);
cache->setFunction(rq, "getComputedSize", func(IGUIObject, getComputedSize), 0);
#undef func
}
template class JSI_GUIProxy<GUIObjectType>;

View file

@ -285,7 +285,16 @@ public:
template <auto callable, GetterFor<decltype(callable)> thisGetter = nullptr, u16 flags = JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT>
static JSFunctionSpec Wrap(const char* name)
{
return JS_FN(name, (&ToJSNative<callable>), args_info<decltype(callable)>::nb_args, flags);
return JS_FN(name, (&ToJSNative<callable, thisGetter>), args_info<decltype(callable)>::nb_args, flags);
}
/**
* Return a JSFunction from a C++ function.
*/
template <auto callable, GetterFor<decltype(callable)> thisGetter = nullptr, u16 flags = JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT>
static JSFunction* Create(const ScriptRequest& rq, const char* name)
{
return JS_NewFunction(rq.cx, &ToJSNative<callable, thisGetter>, args_info<decltype(callable)>::nb_args, flags, name);
}
/**