Reduce duplication in JSI GUI objects implementation.
- Store the functions in an unordered_map. A no-container implementation is doable but likely not worth the added code complexity, maybe with C++20 - Move more things into the _impl.h - Clear out the un-necessary friends declaration in the specific types by moving the functions to the public interface, which makes sense. - Fix a memory leak (JS::PersistentRootedObject weren't deleted). This doesn't change what one needs to do to add a new type, but it does reduce the actual code that's necessary, and makes it less error prone. Differential Revision: https://code.wildfiregames.com/D3214 This was SVN commit r24384.
This commit is contained in:
parent
876f6d5e50
commit
1b67a079fb
@ -23,6 +23,7 @@
|
|||||||
#include "gui/ObjectTypes/CGUIDummyObject.h"
|
#include "gui/ObjectTypes/CGUIDummyObject.h"
|
||||||
#include "gui/ObjectTypes/CTooltip.h"
|
#include "gui/ObjectTypes/CTooltip.h"
|
||||||
#include "gui/Scripting/ScriptFunctions.h"
|
#include "gui/Scripting/ScriptFunctions.h"
|
||||||
|
#include "gui/Scripting/JSInterface_GUIProxy.h"
|
||||||
#include "i18n/L10n.h"
|
#include "i18n/L10n.h"
|
||||||
#include "lib/bits.h"
|
#include "lib/bits.h"
|
||||||
#include "lib/input.h"
|
#include "lib/input.h"
|
||||||
|
@ -47,6 +47,7 @@ struct SGUIImageEffects;
|
|||||||
struct SGUIScrollBarStyle;
|
struct SGUIScrollBarStyle;
|
||||||
|
|
||||||
namespace js { class BaseProxyHandler; }
|
namespace js { class BaseProxyHandler; }
|
||||||
|
class GUIProxyProps;
|
||||||
|
|
||||||
using map_pObjects = std::map<CStr, IGUIObject*>;
|
using map_pObjects = std::map<CStr, IGUIObject*>;
|
||||||
|
|
||||||
@ -247,7 +248,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
const CGUIColor& GetPreDefinedColor(const CStr& name) const { return m_PreDefinedColors.at(name); }
|
const CGUIColor& GetPreDefinedColor(const CStr& name) const { return m_PreDefinedColors.at(name); }
|
||||||
|
|
||||||
void* GetProxyData(const js::BaseProxyHandler* ptr) { return m_ProxyData.at(ptr); }
|
GUIProxyProps* GetProxyData(const js::BaseProxyHandler* ptr) { return m_ProxyData.at(ptr).get(); }
|
||||||
|
|
||||||
shared_ptr<ScriptInterface> GetScriptInterface() { return m_ScriptInterface; };
|
shared_ptr<ScriptInterface> GetScriptInterface() { return m_ScriptInterface; };
|
||||||
|
|
||||||
@ -611,15 +612,16 @@ private:
|
|||||||
std::map<CStr, ConstructObjectFunction> m_ObjectTypes;
|
std::map<CStr, ConstructObjectFunction> m_ObjectTypes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is intended to store the JS Functions returned when accessing some object properties.
|
* This is intended to store the JSFunction when accessing certain properties.
|
||||||
* It's not a great solution, but I can't find a better one at the moment.
|
|
||||||
* The problem is that these functions are per-scriptInterface, and proxy handlers aren't.
|
* The problem is that these functions are per-scriptInterface, and proxy handlers aren't.
|
||||||
* So we know what we want to store, but we don't really have anywhere to store it.
|
* So we know what we want to store, but we don't really have anywhere to store it.
|
||||||
* It would be simpler to recreate the functions on every JS call, but that is slower
|
* It would be simpler to recreate the functions on every JS call, but that is slower
|
||||||
* (this may or may not matter now and in the future).
|
* (this may or may not matter now and in the future).
|
||||||
* Another alternative would be to store them on each proxied object, but that wastes memory.
|
* It's not a great solution, but I can't find a better one at the moment.
|
||||||
|
* An alternative would be to store these on the proxy's prototype,
|
||||||
|
* but that embarks a lot of un-necessary code.
|
||||||
*/
|
*/
|
||||||
std::unordered_map<const js::BaseProxyHandler*, void*> m_ProxyData;
|
std::unordered_map<const js::BaseProxyHandler*, std::unique_ptr<GUIProxyProps>> m_ProxyData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map from hotkey names to objects that listen to the hotkey.
|
* Map from hotkey names to objects that listen to the hotkey.
|
||||||
|
@ -462,15 +462,8 @@ bool IGUIObject::ScriptEventWithReturn(const CStr& eventName, const JS::HandleVa
|
|||||||
void IGUIObject::CreateJSObject()
|
void IGUIObject::CreateJSObject()
|
||||||
{
|
{
|
||||||
ScriptRequest rq(m_pGUI.GetScriptInterface());
|
ScriptRequest rq(m_pGUI.GetScriptInterface());
|
||||||
|
using ProxyHandler = JSI_GUIProxy<std::remove_pointer_t<decltype(this)>>;
|
||||||
js::ProxyOptions options;
|
ProxyHandler::CreateJSObject(rq, this, GetGUI().GetProxyData(&ProxyHandler::Singleton()), m_JSObject);
|
||||||
options.setClass(&JSI_GUIProxy<IGUIObject>::ClassDefinition());
|
|
||||||
|
|
||||||
JS::RootedValue cppObj(rq.cx), data(rq.cx);
|
|
||||||
cppObj.get().setPrivate(this);
|
|
||||||
data.get().setPrivate(GetGUI().GetProxyData(&JSI_GUIProxy<IGUIObject>::Singleton()));
|
|
||||||
m_JSObject.init(rq.cx, js::NewProxyObject(rq.cx, &JSI_GUIProxy<IGUIObject>::Singleton(), cppObj, nullptr, options));
|
|
||||||
js::SetProxyReservedSlot(m_JSObject, 0, data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JSObject* IGUIObject::GetJSObject()
|
JSObject* IGUIObject::GetJSObject()
|
||||||
|
@ -119,23 +119,16 @@ const CGUIColor& CButton::ChooseColor()
|
|||||||
return m_TextColorOver || m_TextColor;
|
return m_TextColorOver || m_TextColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CButton::CreateJSObject()
|
|
||||||
{
|
|
||||||
ScriptRequest rq(m_pGUI.GetScriptInterface());
|
|
||||||
|
|
||||||
js::ProxyOptions options;
|
|
||||||
options.setClass(&JSI_GUIProxy<CButton>::ClassDefinition());
|
|
||||||
|
|
||||||
JS::RootedValue cppObj(rq.cx), data(rq.cx);
|
|
||||||
cppObj.get().setPrivate(this);
|
|
||||||
data.get().setPrivate(GetGUI().GetProxyData(&JSI_GUIProxy<CButton>::Singleton()));
|
|
||||||
m_JSObject.init(rq.cx, js::NewProxyObject(rq.cx, &JSI_GUIProxy<CButton>::Singleton(), cppObj, nullptr, options));
|
|
||||||
js::SetProxyReservedSlot(m_JSObject, 0, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CButton::getTextSize(ScriptInterface& scriptInterface, JS::MutableHandleValue ret)
|
void CButton::getTextSize(ScriptInterface& scriptInterface, JS::MutableHandleValue ret)
|
||||||
{
|
{
|
||||||
ScriptRequest rq(scriptInterface);
|
ScriptRequest rq(scriptInterface);
|
||||||
UpdateText();
|
UpdateText();
|
||||||
ScriptInterface::ToJSVal(rq, ret, m_GeneratedTexts[0].GetSize());
|
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);
|
||||||
|
}
|
||||||
|
@ -27,9 +27,6 @@
|
|||||||
class CButton : public IGUIObject, public IGUITextOwner, public IGUIButtonBehavior
|
class CButton : public IGUIObject, public IGUITextOwner, public IGUIButtonBehavior
|
||||||
{
|
{
|
||||||
GUI_OBJECT(CButton)
|
GUI_OBJECT(CButton)
|
||||||
|
|
||||||
friend JSI_GUIProxy<CButton>;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CButton(CGUI& pGUI);
|
CButton(CGUI& pGUI);
|
||||||
virtual ~CButton();
|
virtual ~CButton();
|
||||||
@ -54,6 +51,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual void Draw();
|
virtual void Draw();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populate @param ret with the object's text size.
|
||||||
|
*/
|
||||||
|
void getTextSize(ScriptInterface& scriptInterface, JS::MutableHandleValue ret);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
* Sets up text, should be called every time changes has been
|
* Sets up text, should be called every time changes has been
|
||||||
@ -73,8 +75,6 @@ protected:
|
|||||||
|
|
||||||
virtual void CreateJSObject();
|
virtual void CreateJSObject();
|
||||||
|
|
||||||
void getTextSize(ScriptInterface& scriptInterface, JS::MutableHandleValue ret);
|
|
||||||
|
|
||||||
// Settings
|
// Settings
|
||||||
float m_BufferZone;
|
float m_BufferZone;
|
||||||
i32 m_CellID;
|
i32 m_CellID;
|
||||||
|
@ -504,14 +504,7 @@ int CList::GetHoveredItem()
|
|||||||
void CList::CreateJSObject()
|
void CList::CreateJSObject()
|
||||||
{
|
{
|
||||||
ScriptRequest rq(m_pGUI.GetScriptInterface());
|
ScriptRequest rq(m_pGUI.GetScriptInterface());
|
||||||
|
using ProxyHandler = JSI_GUIProxy<std::remove_pointer_t<decltype(this)>>;
|
||||||
js::ProxyOptions options;
|
ProxyHandler::CreateJSObject(rq, this, GetGUI().GetProxyData(&ProxyHandler::Singleton()), m_JSObject);
|
||||||
options.setClass(&JSI_GUIProxy<CList>::ClassDefinition());
|
|
||||||
|
|
||||||
JS::RootedValue cppObj(rq.cx), data(rq.cx);
|
|
||||||
cppObj.get().setPrivate(this);
|
|
||||||
data.get().setPrivate(GetGUI().GetProxyData(&JSI_GUIProxy<CList>::Singleton()));
|
|
||||||
m_JSObject.init(rq.cx, js::NewProxyObject(rq.cx, &JSI_GUIProxy<CList>::Singleton(), cppObj, nullptr, options));
|
|
||||||
js::SetProxyReservedSlot(m_JSObject, 0, data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,9 +38,6 @@
|
|||||||
class CList : public IGUIObject, public IGUIScrollBarOwner, public IGUITextOwner
|
class CList : public IGUIObject, public IGUIScrollBarOwner, public IGUITextOwner
|
||||||
{
|
{
|
||||||
GUI_OBJECT(CList)
|
GUI_OBJECT(CList)
|
||||||
|
|
||||||
friend JSI_GUIProxy<CList>;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CList(CGUI& pGUI);
|
CList(CGUI& pGUI);
|
||||||
virtual ~CList();
|
virtual ~CList();
|
||||||
|
@ -257,15 +257,8 @@ bool CText::MouseOverIcon()
|
|||||||
void CText::CreateJSObject()
|
void CText::CreateJSObject()
|
||||||
{
|
{
|
||||||
ScriptRequest rq(m_pGUI.GetScriptInterface());
|
ScriptRequest rq(m_pGUI.GetScriptInterface());
|
||||||
|
using ProxyHandler = JSI_GUIProxy<std::remove_pointer_t<decltype(this)>>;
|
||||||
js::ProxyOptions options;
|
ProxyHandler::CreateJSObject(rq, this, GetGUI().GetProxyData(&ProxyHandler::Singleton()), m_JSObject);
|
||||||
options.setClass(&JSI_GUIProxy<CText>::ClassDefinition());
|
|
||||||
|
|
||||||
JS::RootedValue cppObj(rq.cx), data(rq.cx);
|
|
||||||
cppObj.get().setPrivate(this);
|
|
||||||
data.get().setPrivate(GetGUI().GetProxyData(&JSI_GUIProxy<CText>::Singleton()));
|
|
||||||
m_JSObject.init(rq.cx, js::NewProxyObject(rq.cx, &JSI_GUIProxy<CText>::Singleton(), cppObj, nullptr, options));
|
|
||||||
js::SetProxyReservedSlot(m_JSObject, 0, data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CText::getTextSize(ScriptInterface& scriptInterface, JS::MutableHandleValue ret)
|
void CText::getTextSize(ScriptInterface& scriptInterface, JS::MutableHandleValue ret)
|
||||||
|
@ -30,9 +30,6 @@
|
|||||||
class CText : public IGUIObject, public IGUIScrollBarOwner, public IGUITextOwner
|
class CText : public IGUIObject, public IGUIScrollBarOwner, public IGUITextOwner
|
||||||
{
|
{
|
||||||
GUI_OBJECT(CText)
|
GUI_OBJECT(CText)
|
||||||
|
|
||||||
friend JSI_GUIProxy<CText>;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CText(CGUI& pGUI);
|
CText(CGUI& pGUI);
|
||||||
virtual ~CText();
|
virtual ~CText();
|
||||||
@ -52,6 +49,10 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual bool MouseOverIcon();
|
virtual bool MouseOverIcon();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populate @param ret with the object's text size.
|
||||||
|
*/
|
||||||
|
void getTextSize(ScriptInterface& scriptInterface, JS::MutableHandleValue ret);
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
* Sets up text, should be called every time changes has been
|
* Sets up text, should be called every time changes has been
|
||||||
@ -71,8 +72,6 @@ protected:
|
|||||||
|
|
||||||
virtual void CreateJSObject();
|
virtual void CreateJSObject();
|
||||||
|
|
||||||
void getTextSize(ScriptInterface& scriptInterface, JS::MutableHandleValue ret);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Placement of text. Ignored when scrollbars are active.
|
* Placement of text. Ignored when scrollbars are active.
|
||||||
*/
|
*/
|
||||||
|
@ -17,65 +17,22 @@
|
|||||||
|
|
||||||
#include "precompiled.h"
|
#include "precompiled.h"
|
||||||
|
|
||||||
#include "JSInterface_GUIProxy.h"
|
|
||||||
|
|
||||||
#include "gui/CGUI.h"
|
|
||||||
#include "gui/CGUISetting.h"
|
|
||||||
#include "gui/ObjectBases/IGUIObject.h"
|
|
||||||
#include "gui/ObjectTypes/CButton.h"
|
|
||||||
#include "ps/CLogger.h"
|
|
||||||
#include "scriptinterface/ScriptExtraHeaders.h"
|
|
||||||
#include "scriptinterface/ScriptInterface.h"
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
// Include the definition of the generic templates.
|
|
||||||
#include "JSInterface_GUIProxy_impl.h"
|
#include "JSInterface_GUIProxy_impl.h"
|
||||||
|
|
||||||
namespace
|
#include "gui/ObjectTypes/CButton.h"
|
||||||
{
|
|
||||||
struct SData
|
|
||||||
{
|
|
||||||
JS::PersistentRootedObject m_ToString;
|
|
||||||
JS::PersistentRootedObject m_Focus;
|
|
||||||
JS::PersistentRootedObject m_Blur;
|
|
||||||
JS::PersistentRootedObject m_GetComputedSize;
|
|
||||||
JS::PersistentRootedObject m_GetTextSize;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
using GUIObjectType = CButton;
|
||||||
bool JSI_GUIProxy<CButton>::FuncGetter(JS::HandleObject proxy, const std::string& propName, JS::MutableHandleValue vp) const
|
|
||||||
{
|
|
||||||
const SData& data = *static_cast<const SData*>(js::GetProxyReservedSlot(proxy, 0).toPrivate());
|
|
||||||
if (propName == "toString")
|
|
||||||
return vp.setObjectOrNull(data.m_ToString), true;
|
|
||||||
if (propName == "focus")
|
|
||||||
return vp.setObjectOrNull(data.m_Focus), true;
|
|
||||||
if (propName == "blur")
|
|
||||||
return vp.setObjectOrNull(data.m_Blur), true;
|
|
||||||
if (propName == "getComputedSize")
|
|
||||||
return vp.setObjectOrNull(data.m_GetComputedSize), true;
|
|
||||||
if (propName == "getTextSize")
|
|
||||||
return vp.setObjectOrNull(data.m_GetTextSize), true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
template<>
|
||||||
std::pair<const js::BaseProxyHandler*, void*> JSI_GUIProxy<CButton>::CreateData(ScriptInterface& scriptInterface)
|
void JSI_GUIProxy<GUIObjectType>::CreateFunctions(const ScriptRequest& rq, GUIProxyProps* cache)
|
||||||
{
|
{
|
||||||
SData* data = new SData();
|
#define func(class, func) &JSInterface_GUIProxy::apply_to<GUIObjectType, class, &class::func>
|
||||||
ScriptRequest rq(scriptInterface);
|
cache->setFunction(rq, "toString", func(IGUIObject, toString), 0);
|
||||||
|
cache->setFunction(rq, "focus", func(IGUIObject, focus), 0);
|
||||||
#define func(class, func) &apply_to<CButton, class, &class::func>
|
cache->setFunction(rq, "blur", func(IGUIObject, blur), 0);
|
||||||
data->m_ToString.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, toString), 0, 0, "toString")));
|
cache->setFunction(rq, "getComputedSize", func(IGUIObject, getComputedSize), 0);
|
||||||
data->m_Focus.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, focus), 0, 0, "focus")));
|
cache->setFunction(rq, "getTextSize", func(GUIObjectType, getTextSize), 0);
|
||||||
data->m_Blur.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, blur), 0, 0, "blur")));
|
|
||||||
data->m_GetComputedSize.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, getComputedSize), 0, 0, "getComputedSize")));
|
|
||||||
data->m_GetTextSize.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(CButton, getTextSize), 0, 0, "getTextSize")));
|
|
||||||
#undef func
|
#undef func
|
||||||
|
|
||||||
return { &Singleton(), data };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template class JSI_GUIProxy<CButton>;
|
template class JSI_GUIProxy<GUIObjectType>;
|
||||||
|
@ -17,31 +17,9 @@
|
|||||||
|
|
||||||
#include "precompiled.h"
|
#include "precompiled.h"
|
||||||
|
|
||||||
#include "JSInterface_GUIProxy.h"
|
|
||||||
|
|
||||||
#include "gui/CGUI.h"
|
|
||||||
#include "gui/CGUISetting.h"
|
|
||||||
#include "gui/ObjectBases/IGUIObject.h"
|
|
||||||
#include "gui/ObjectTypes/CList.h"
|
|
||||||
#include "ps/CLogger.h"
|
|
||||||
#include "scriptinterface/ScriptExtraHeaders.h"
|
|
||||||
#include "scriptinterface/ScriptInterface.h"
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
// Include the definition of the generic templates.
|
|
||||||
#include "JSInterface_GUIProxy_impl.h"
|
#include "JSInterface_GUIProxy_impl.h"
|
||||||
|
|
||||||
namespace
|
#include "gui/ObjectTypes/CList.h"
|
||||||
{
|
|
||||||
struct SData
|
|
||||||
{
|
|
||||||
JS::PersistentRootedObject m_ToString;
|
|
||||||
JS::PersistentRootedObject m_Focus;
|
|
||||||
JS::PersistentRootedObject m_Blur;
|
|
||||||
JS::PersistentRootedObject m_GetComputedSize;
|
|
||||||
JS::PersistentRootedObject m_AddItem;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool CList_AddItem(JSContext* cx, uint argc, JS::Value* vp)
|
bool CList_AddItem(JSContext* cx, uint argc, JS::Value* vp)
|
||||||
{
|
{
|
||||||
@ -58,40 +36,19 @@ bool CList_AddItem(JSContext* cx, uint argc, JS::Value* vp)
|
|||||||
e->AddItem(text, text);
|
e->AddItem(text, text);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
using GUIObjectType = CList;
|
||||||
bool JSI_GUIProxy<CList>::FuncGetter(JS::HandleObject proxy, const std::string& propName, JS::MutableHandleValue vp) const
|
|
||||||
|
template<>
|
||||||
|
void JSI_GUIProxy<GUIObjectType>::CreateFunctions(const ScriptRequest& rq, GUIProxyProps* cache)
|
||||||
{
|
{
|
||||||
const SData& data = *static_cast<const SData*>(js::GetProxyReservedSlot(proxy, 0).toPrivate());
|
#define func(class, func) &JSInterface_GUIProxy::apply_to<GUIObjectType, class, &class::func>
|
||||||
if (propName == "toString")
|
cache->setFunction(rq, "toString", func(IGUIObject, toString), 0);
|
||||||
return vp.setObjectOrNull(data.m_ToString), true;
|
cache->setFunction(rq, "focus", func(IGUIObject, focus), 0);
|
||||||
if (propName == "focus")
|
cache->setFunction(rq, "blur", func(IGUIObject, blur), 0);
|
||||||
return vp.setObjectOrNull(data.m_Focus), true;
|
cache->setFunction(rq, "getComputedSize", func(IGUIObject, getComputedSize), 0);
|
||||||
if (propName == "blur")
|
|
||||||
return vp.setObjectOrNull(data.m_Blur), true;
|
|
||||||
if (propName == "getComputedSize")
|
|
||||||
return vp.setObjectOrNull(data.m_GetComputedSize), true;
|
|
||||||
if (propName == "addItem")
|
|
||||||
return vp.setObjectOrNull(data.m_AddItem), true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
std::pair<const js::BaseProxyHandler*, void*> JSI_GUIProxy<CList>::CreateData(ScriptInterface& scriptInterface)
|
|
||||||
{
|
|
||||||
SData* data = new SData();
|
|
||||||
ScriptRequest rq(scriptInterface);
|
|
||||||
|
|
||||||
#define func(class, func) &apply_to<CList, class, &class::func>
|
|
||||||
data->m_ToString.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, toString), 0, 0, "toString")));
|
|
||||||
data->m_Focus.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, focus), 0, 0, "focus")));
|
|
||||||
data->m_Blur.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, blur), 0, 0, "blur")));
|
|
||||||
data->m_GetComputedSize.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, getComputedSize), 0, 0, "getComputedSize")));
|
|
||||||
#undef func
|
#undef func
|
||||||
data->m_AddItem.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, CList_AddItem, 1, 0, "addItem")));
|
cache->setFunction(rq, "addItem", CList_AddItem, 1);
|
||||||
|
|
||||||
return { &Singleton(), data };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template class JSI_GUIProxy<CList>;
|
template class JSI_GUIProxy<GUIObjectType>;
|
||||||
|
@ -17,65 +17,22 @@
|
|||||||
|
|
||||||
#include "precompiled.h"
|
#include "precompiled.h"
|
||||||
|
|
||||||
#include "JSInterface_GUIProxy.h"
|
|
||||||
|
|
||||||
#include "gui/CGUI.h"
|
|
||||||
#include "gui/CGUISetting.h"
|
|
||||||
#include "gui/ObjectBases/IGUIObject.h"
|
|
||||||
#include "gui/ObjectTypes/CText.h"
|
|
||||||
#include "ps/CLogger.h"
|
|
||||||
#include "scriptinterface/ScriptExtraHeaders.h"
|
|
||||||
#include "scriptinterface/ScriptInterface.h"
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
// Include the definition of the generic templates.
|
|
||||||
#include "JSInterface_GUIProxy_impl.h"
|
#include "JSInterface_GUIProxy_impl.h"
|
||||||
|
|
||||||
namespace
|
#include "gui/ObjectTypes/CText.h"
|
||||||
{
|
|
||||||
struct SData
|
|
||||||
{
|
|
||||||
JS::PersistentRootedObject m_ToString;
|
|
||||||
JS::PersistentRootedObject m_Focus;
|
|
||||||
JS::PersistentRootedObject m_Blur;
|
|
||||||
JS::PersistentRootedObject m_GetComputedSize;
|
|
||||||
JS::PersistentRootedObject m_GetTextSize;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
using GUIObjectType = CText;
|
||||||
bool JSI_GUIProxy<CText>::FuncGetter(JS::HandleObject proxy, const std::string& propName, JS::MutableHandleValue vp) const
|
|
||||||
{
|
|
||||||
const SData& data = *static_cast<const SData*>(js::GetProxyReservedSlot(proxy, 0).toPrivate());
|
|
||||||
if (propName == "toString")
|
|
||||||
return vp.setObjectOrNull(data.m_ToString), true;
|
|
||||||
if (propName == "focus")
|
|
||||||
return vp.setObjectOrNull(data.m_Focus), true;
|
|
||||||
if (propName == "blur")
|
|
||||||
return vp.setObjectOrNull(data.m_Blur), true;
|
|
||||||
if (propName == "getComputedSize")
|
|
||||||
return vp.setObjectOrNull(data.m_GetComputedSize), true;
|
|
||||||
if (propName == "getTextSize")
|
|
||||||
return vp.setObjectOrNull(data.m_GetTextSize), true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
template<>
|
||||||
std::pair<const js::BaseProxyHandler*, void*> JSI_GUIProxy<CText>::CreateData(ScriptInterface& scriptInterface)
|
void JSI_GUIProxy<GUIObjectType>::CreateFunctions(const ScriptRequest& rq, GUIProxyProps* cache)
|
||||||
{
|
{
|
||||||
SData* data = new SData();
|
#define func(class, func) &JSInterface_GUIProxy::apply_to<GUIObjectType, class, &class::func>
|
||||||
ScriptRequest rq(scriptInterface);
|
cache->setFunction(rq, "toString", func(IGUIObject, toString), 0);
|
||||||
|
cache->setFunction(rq, "focus", func(IGUIObject, focus), 0);
|
||||||
#define func(class, func) &apply_to<CText, class, &class::func>
|
cache->setFunction(rq, "blur", func(IGUIObject, blur), 0);
|
||||||
data->m_ToString.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, toString), 0, 0, "toString")));
|
cache->setFunction(rq, "getComputedSize", func(IGUIObject, getComputedSize), 0);
|
||||||
data->m_Focus.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, focus), 0, 0, "focus")));
|
cache->setFunction(rq, "getTextSize", func(GUIObjectType, getTextSize), 0);
|
||||||
data->m_Blur.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, blur), 0, 0, "blur")));
|
|
||||||
data->m_GetComputedSize.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, getComputedSize), 0, 0, "getComputedSize")));
|
|
||||||
data->m_GetTextSize.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(CText, getTextSize), 0, 0, "getTextSize")));
|
|
||||||
#undef func
|
#undef func
|
||||||
|
|
||||||
return { &Singleton(), data };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template class JSI_GUIProxy<CText>;
|
template class JSI_GUIProxy<GUIObjectType>;
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
class ScriptInterface;
|
class ScriptInterface;
|
||||||
|
class ScriptRequest;
|
||||||
|
|
||||||
// See JSI_GuiProxy below
|
// See JSI_GuiProxy below
|
||||||
#if GCC_VERSION
|
#if GCC_VERSION
|
||||||
@ -33,6 +34,22 @@ class ScriptInterface;
|
|||||||
# pragma warning(disable: 4265)
|
# pragma warning(disable: 4265)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxies need to store some data whose lifetime is tied to an interface.
|
||||||
|
* This is the virtual interface of that data.
|
||||||
|
*/
|
||||||
|
class GUIProxyProps
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~GUIProxyProps() {};
|
||||||
|
|
||||||
|
// @return true if @param name exists in this cache.
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the js interface with C++ GUI objects.
|
* Handles the js interface with C++ GUI objects.
|
||||||
* Proxy handlers must live for at least as long as the JS runtime
|
* Proxy handlers must live for at least as long as the JS runtime
|
||||||
@ -43,6 +60,17 @@ class ScriptInterface;
|
|||||||
* GUI Objects only exist in C++ and have no JS-only properties.
|
* GUI Objects only exist in C++ and have no JS-only properties.
|
||||||
* As such, there is no "target" JS object that this proxy could point to,
|
* As such, there is no "target" JS object that this proxy could point to,
|
||||||
* and thus we should inherit from BaseProxyHandler and not js::Wrapper.
|
* and thus we should inherit from BaseProxyHandler and not js::Wrapper.
|
||||||
|
*
|
||||||
|
* Proxies can be used with prototypes and almost treated like regular JS objects.
|
||||||
|
* However, the default implementation embarks a lot of code that we don't really need here,
|
||||||
|
* since the only fanciness is that we cache JSFunction* properties.
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
template<typename GUIObjectType>
|
template<typename GUIObjectType>
|
||||||
class JSI_GUIProxy : public js::BaseProxyHandler
|
class JSI_GUIProxy : public js::BaseProxyHandler
|
||||||
@ -54,17 +82,27 @@ public:
|
|||||||
// For convenience, this is the single instantiated JSI_GUIProxy.
|
// For convenience, this is the single instantiated JSI_GUIProxy.
|
||||||
static JSI_GUIProxy& Singleton();
|
static JSI_GUIProxy& Singleton();
|
||||||
|
|
||||||
static std::pair<const js::BaseProxyHandler*, void*> CreateData(ScriptInterface& scriptInterface);
|
// 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);
|
||||||
protected:
|
protected:
|
||||||
// @param family can't be nullptr because that's used for some DOM object and it crashes.
|
// @param family can't be nullptr because that's used for some DOM object and it crashes.
|
||||||
JSI_GUIProxy() : BaseProxyHandler(this, false, false) {};
|
JSI_GUIProxy() : BaseProxyHandler(this, false, false) {};
|
||||||
|
|
||||||
// Note: SM provides no virtual destructor for baseProxyHandler.
|
// Note: SM provides no virtual destructor for baseProxyHandler.
|
||||||
// This also enforces making proxy handlers dataless static variables.
|
// This also enforces making proxy handlers dataless static variables.
|
||||||
~JSI_GUIProxy() {};
|
~JSI_GUIProxy() {};
|
||||||
|
|
||||||
// This handles returning function properties.
|
// The default implementations need to know the type of the GUIProxyProps for this proxy type.
|
||||||
// Specialize this.
|
// This is done by specializing this struct's alias type.
|
||||||
bool FuncGetter(JS::HandleObject proxy, const std::string& propName, JS::MutableHandleValue vp) const;
|
struct PropCache;
|
||||||
|
|
||||||
|
// Specialize this to define the custom properties of this type.
|
||||||
|
static void CreateFunctions(const ScriptRequest& rq, GUIProxyProps* cache);
|
||||||
|
|
||||||
|
// This handles returning custom properties. Specialize this if needed.
|
||||||
|
bool PropGetter(JS::HandleObject proxy, const std::string& propName, JS::MutableHandleValue vp) const;
|
||||||
protected:
|
protected:
|
||||||
// BaseProxyHandler interface below
|
// BaseProxyHandler interface below
|
||||||
|
|
||||||
|
@ -17,6 +17,17 @@
|
|||||||
|
|
||||||
// This file is included directly into actual implementation files.
|
// This file is included directly into actual implementation files.
|
||||||
|
|
||||||
|
#include "JSInterface_GUIProxy.h"
|
||||||
|
|
||||||
|
#include "gui/CGUI.h"
|
||||||
|
#include "gui/CGUISetting.h"
|
||||||
|
#include "gui/ObjectBases/IGUIObject.h"
|
||||||
|
#include "ps/CLogger.h"
|
||||||
|
#include "scriptinterface/ScriptExtraHeaders.h"
|
||||||
|
#include "scriptinterface/ScriptInterface.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
JSClass& JSI_GUIProxy<T>::ClassDefinition()
|
JSClass& JSI_GUIProxy<T>::ClassDefinition()
|
||||||
{
|
{
|
||||||
@ -31,8 +42,12 @@ JSI_GUIProxy<T>& JSI_GUIProxy<T>::Singleton()
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
// 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)>
|
template<class OG, class R, void (R::*funcptr)(ScriptInterface&, JS::MutableHandleValue)>
|
||||||
inline bool apply_to(JSContext* cx, uint argc, JS::Value* vp)
|
inline bool apply_to(JSContext* cx, uint argc, JS::Value* vp)
|
||||||
{
|
{
|
||||||
@ -45,6 +60,76 @@ inline bool apply_to(JSContext* cx, uint argc, JS::Value* vp)
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Default implementation of the cache via unordered_map
|
||||||
|
class MapCache : public GUIProxyProps
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~MapCache() {};
|
||||||
|
|
||||||
|
virtual bool has(const std::string& name) const override
|
||||||
|
{
|
||||||
|
return m_Functions.find(name) != m_Functions.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual JSObject* get(const std::string& name) const override
|
||||||
|
{
|
||||||
|
return m_Functions.at(name).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool setFunction(const ScriptRequest& rq, const std::string& name, JSNative function, int nargs) override
|
||||||
|
{
|
||||||
|
m_Functions[name].init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, function, nargs, 0, name.c_str())));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::unordered_map<std::string, JS::PersistentRootedObject> m_Functions;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// The default propcache is a MapCache.
|
||||||
|
template<typename T>
|
||||||
|
struct JSI_GUIProxy<T>::PropCache
|
||||||
|
{
|
||||||
|
using type = JSInterface_GUIProxy::MapCache;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool JSI_GUIProxy<T>::PropGetter(JS::HandleObject proxy, const std::string& propName, JS::MutableHandleValue vp) const
|
||||||
|
{
|
||||||
|
using PropertyCache = typename PropCache::type;
|
||||||
|
// Since we know at compile time what the type actually is, avoid the virtual call.
|
||||||
|
const PropertyCache* data = static_cast<const PropertyCache*>(static_cast<const GUIProxyProps*>(js::GetProxyReservedSlot(proxy, 0).toPrivate()));
|
||||||
|
if (data->has(propName))
|
||||||
|
{
|
||||||
|
vp.setObjectOrNull(data->get(propName));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::pair<const js::BaseProxyHandler*, GUIProxyProps*> JSI_GUIProxy<T>::CreateData(ScriptInterface& scriptInterface)
|
||||||
|
{
|
||||||
|
using PropertyCache = typename PropCache::type;
|
||||||
|
PropertyCache* data = new PropertyCache();
|
||||||
|
ScriptRequest rq(scriptInterface);
|
||||||
|
CreateFunctions(rq, data);
|
||||||
|
return { &Singleton(), data };
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void JSI_GUIProxy<T>::CreateJSObject(const ScriptRequest& rq, T* ptr, GUIProxyProps* dataPtr, JS::PersistentRootedObject& val)
|
||||||
|
{
|
||||||
|
js::ProxyOptions options;
|
||||||
|
options.setClass(&ClassDefinition());
|
||||||
|
|
||||||
|
JS::RootedValue cppObj(rq.cx), data(rq.cx);
|
||||||
|
cppObj.get().setPrivate(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);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@ -66,7 +151,7 @@ bool JSI_GUIProxy<T>::get(JSContext* cx, JS::HandleObject proxy, JS::HandleValue
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Return function properties. Specializable.
|
// Return function properties. Specializable.
|
||||||
if (FuncGetter(proxy, propName, vp))
|
if (PropGetter(proxy, propName, vp))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Use onWhatever to access event handlers
|
// Use onWhatever to access event handlers
|
||||||
|
@ -17,59 +17,19 @@
|
|||||||
|
|
||||||
#include "precompiled.h"
|
#include "precompiled.h"
|
||||||
|
|
||||||
#include "JSInterface_GUIProxy.h"
|
|
||||||
|
|
||||||
#include "gui/CGUI.h"
|
|
||||||
#include "gui/CGUISetting.h"
|
|
||||||
#include "gui/ObjectBases/IGUIObject.h"
|
|
||||||
#include "gui/ObjectTypes/CText.h"
|
|
||||||
#include "ps/CLogger.h"
|
|
||||||
#include "scriptinterface/ScriptExtraHeaders.h"
|
|
||||||
#include "scriptinterface/ScriptInterface.h"
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
// Include the definition of the generic templates.
|
|
||||||
#include "JSInterface_GUIProxy_impl.h"
|
#include "JSInterface_GUIProxy_impl.h"
|
||||||
|
|
||||||
namespace
|
using GUIObjectType = IGUIObject;
|
||||||
{
|
|
||||||
struct SData
|
|
||||||
{
|
|
||||||
JS::PersistentRootedObject m_ToString;
|
|
||||||
JS::PersistentRootedObject m_Focus;
|
|
||||||
JS::PersistentRootedObject m_Blur;
|
|
||||||
JS::PersistentRootedObject m_GetComputedSize;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
template<>
|
||||||
bool JSI_GUIProxy<IGUIObject>::FuncGetter(JS::HandleObject proxy, const std::string& propName, JS::MutableHandleValue vp) const
|
void JSI_GUIProxy<GUIObjectType>::CreateFunctions(const ScriptRequest& rq, GUIProxyProps* cache)
|
||||||
{
|
{
|
||||||
const SData& data = *static_cast<const SData*>(js::GetProxyReservedSlot(proxy, 0).toPrivate());
|
#define func(class, func) &JSInterface_GUIProxy::apply_to<GUIObjectType, class, &class::func>
|
||||||
if (propName == "toString")
|
cache->setFunction(rq, "toString", func(IGUIObject, toString), 0);
|
||||||
return vp.setObjectOrNull(data.m_ToString), true;
|
cache->setFunction(rq, "focus", func(IGUIObject, focus), 0);
|
||||||
if (propName == "focus")
|
cache->setFunction(rq, "blur", func(IGUIObject, blur), 0);
|
||||||
return vp.setObjectOrNull(data.m_Focus), true;
|
cache->setFunction(rq, "getComputedSize", func(IGUIObject, getComputedSize), 0);
|
||||||
if (propName == "blur")
|
|
||||||
return vp.setObjectOrNull(data.m_Blur), true;
|
|
||||||
if (propName == "getComputedSize")
|
|
||||||
return vp.setObjectOrNull(data.m_GetComputedSize), true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
std::pair<const js::BaseProxyHandler*, void*> JSI_GUIProxy<IGUIObject>::CreateData(ScriptInterface& scriptInterface)
|
|
||||||
{
|
|
||||||
SData* data = new SData();
|
|
||||||
ScriptRequest rq(scriptInterface);
|
|
||||||
#define func(class, func) &apply_to<IGUIObject, class, &class::func>
|
|
||||||
data->m_ToString.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, toString), 0, 0, "toString")));
|
|
||||||
data->m_Focus.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, focus), 0, 0, "focus")));
|
|
||||||
data->m_Blur.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, blur), 0, 0, "blur")));
|
|
||||||
data->m_GetComputedSize.init(rq.cx, JS_GetFunctionObject(JS_NewFunction(rq.cx, func(IGUIObject, getComputedSize), 0, 0, "getComputedSize")));
|
|
||||||
#undef func
|
#undef func
|
||||||
return { &Singleton(), data };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template class JSI_GUIProxy<IGUIObject>;
|
template class JSI_GUIProxy<GUIObjectType>;
|
||||||
|
Loading…
Reference in New Issue
Block a user