Index: ps/trunk/source/gui/ObjectBases/IGUIObject.h =================================================================== --- ps/trunk/source/gui/ObjectBases/IGUIObject.h +++ ps/trunk/source/gui/ObjectBases/IGUIObject.h @@ -36,6 +36,7 @@ class CGUI; class IGUIObject; +class IGUIProxyObject; class IGUISetting; template @@ -215,6 +216,11 @@ virtual void UpdateCachedSize(); /** + * Updates and returns the size of the object. + */ + CRect GetComputedSize(); + + /** * Reset internal state of this object. */ virtual void ResetStates(); @@ -233,14 +239,6 @@ */ 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 @@ */ void SetFocus(); + /** + * Release focus. + */ + void ReleaseFocus(); + protected: /** * Check if object is focused. @@ -461,6 +464,7 @@ /** * 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 @@ // Internal storage for registered script handlers. std::map > m_ScriptHandlers; - // Cached JSObject representing this GUI object - JS::PersistentRootedObject m_JSObject; + // Cached JSObject representing this GUI object. + std::unique_ptr m_JSObject; // Cache references to settings for performance bool m_Enabled; Index: ps/trunk/source/gui/ObjectBases/IGUIObject.cpp =================================================================== --- ps/trunk/source/gui/ObjectBases/IGUIObject.cpp +++ ps/trunk/source/gui/ObjectBases/IGUIObject.cpp @@ -273,6 +273,13 @@ } } +CRect IGUIObject::GetComputedSize() +{ + UpdateCachedSize(); + return m_CachedActualSize; +} + + bool IGUIObject::ApplyStyle(const CStr& StyleName) { if (!m_pGUI.HasStyle(StyleName)) @@ -460,46 +467,14 @@ return JS::ToBoolean(result); } -void IGUIObject::CreateJSObject() -{ - ScriptRequest rq(m_pGUI.GetScriptInterface()); - using ProxyHandler = JSI_GUIProxy>; - 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 @@ m_pGUI.SetFocusedObject(this); } +void IGUIObject::ReleaseFocus() +{ + m_pGUI.SetFocusedObject(nullptr); +} + bool IGUIObject::IsFocused() const { return m_pGUI.GetFocusedObject() == this; Index: ps/trunk/source/gui/ObjectTypes/CButton.h =================================================================== --- ps/trunk/source/gui/ObjectTypes/CButton.h +++ ps/trunk/source/gui/ObjectTypes/CButton.h @@ -43,6 +43,11 @@ virtual void UpdateCachedSize(); /** + * @return the object text size. + */ + CSize2D GetTextSize(); + + /** * @see IGUIObject#HandleMessage() */ virtual void HandleMessage(SGUIMessage& Message); @@ -52,11 +57,6 @@ */ 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 Index: ps/trunk/source/gui/ObjectTypes/CButton.cpp =================================================================== --- ps/trunk/source/gui/ObjectTypes/CButton.cpp +++ ps/trunk/source/gui/ObjectTypes/CButton.cpp @@ -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 @@ IGUITextOwner::UpdateCachedSize(); } +CSize2D CButton::GetTextSize() +{ + UpdateText(); + return m_GeneratedTexts[0].GetSize(); +} + void CButton::HandleMessage(SGUIMessage& Message) { IGUIObject::HandleMessage(Message); @@ -115,17 +120,3 @@ 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>; - ProxyHandler::CreateJSObject(rq, this, GetGUI().GetProxyData(&ProxyHandler::Singleton()), m_JSObject); -} Index: ps/trunk/source/gui/ObjectTypes/CList.h =================================================================== --- ps/trunk/source/gui/ObjectTypes/CList.h +++ ps/trunk/source/gui/ObjectTypes/CList.h @@ -57,6 +57,11 @@ */ 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 Index: ps/trunk/source/gui/ObjectTypes/CList.cpp =================================================================== --- ps/trunk/source/gui/ObjectTypes/CList.cpp +++ ps/trunk/source/gui/ObjectTypes/CList.cpp @@ -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 @@ 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 @@ return -1; } - -void CList::CreateJSObject() -{ - ScriptRequest rq(m_pGUI.GetScriptInterface()); - using ProxyHandler = JSI_GUIProxy>; - ProxyHandler::CreateJSObject(rq, this, GetGUI().GetProxyData(&ProxyHandler::Singleton()), m_JSObject); -} - Index: ps/trunk/source/gui/ObjectTypes/CText.h =================================================================== --- ps/trunk/source/gui/ObjectTypes/CText.h +++ ps/trunk/source/gui/ObjectTypes/CText.h @@ -45,14 +45,14 @@ virtual void UpdateCachedSize(); /** - * Test if mouse position is over an icon + * @return the object text size. */ - virtual bool MouseOverIcon(); + CSize2D GetTextSize(); /** - * Populate @param ret with the object's text size. + * Test if mouse position is over an icon */ - void getTextSize(ScriptInterface& scriptInterface, JS::MutableHandleValue ret); + virtual bool MouseOverIcon(); protected: /** * Sets up text, should be called every time changes has been Index: ps/trunk/source/gui/ObjectTypes/CText.cpp =================================================================== --- ps/trunk/source/gui/ObjectTypes/CText.cpp +++ ps/trunk/source/gui/ObjectTypes/CText.cpp @@ -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 @@ IGUITextOwner::UpdateCachedSize(); } +CSize2D CText::GetTextSize() +{ + UpdateText(); + return m_GeneratedTexts[0].GetSize(); +} + void CText::HandleMessage(SGUIMessage& Message) { IGUIObject::HandleMessage(Message); @@ -250,18 +255,3 @@ return false; } - - -void CText::CreateJSObject() -{ - ScriptRequest rq(m_pGUI.GetScriptInterface()); - using ProxyHandler = JSI_GUIProxy>; - 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()); -} Index: ps/trunk/source/gui/Scripting/JSInterface_CButton.cpp =================================================================== --- ps/trunk/source/gui/Scripting/JSInterface_CButton.cpp +++ ps/trunk/source/gui/Scripting/JSInterface_CButton.cpp @@ -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 . - */ - -#include "precompiled.h" - -#include "JSInterface_GUIProxy_impl.h" - -#include "gui/ObjectTypes/CButton.h" - -using GUIObjectType = CButton; - -template<> -void JSI_GUIProxy::CreateFunctions(const ScriptRequest& rq, GUIProxyProps* cache) -{ -#define func(class, func) &JSInterface_GUIProxy::apply_to - 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; Index: ps/trunk/source/gui/Scripting/JSInterface_CList.cpp =================================================================== --- ps/trunk/source/gui/Scripting/JSInterface_CList.cpp +++ ps/trunk/source/gui/Scripting/JSInterface_CList.cpp @@ -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 . - */ - -#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(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::CreateFunctions(const ScriptRequest& rq, GUIProxyProps* cache) -{ -#define func(class, func) &JSInterface_GUIProxy::apply_to - 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; Index: ps/trunk/source/gui/Scripting/JSInterface_CText.cpp =================================================================== --- ps/trunk/source/gui/Scripting/JSInterface_CText.cpp +++ ps/trunk/source/gui/Scripting/JSInterface_CText.cpp @@ -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 . - */ - -#include "precompiled.h" - -#include "JSInterface_GUIProxy_impl.h" - -#include "gui/ObjectTypes/CText.h" - -using GUIObjectType = CText; - -template<> -void JSI_GUIProxy::CreateFunctions(const ScriptRequest& rq, GUIProxyProps* cache) -{ -#define func(class, func) &JSInterface_GUIProxy::apply_to - 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; Index: ps/trunk/source/gui/Scripting/JSInterface_GUIProxy.h =================================================================== --- ps/trunk/source/gui/Scripting/JSInterface_GUIProxy.h +++ ps/trunk/source/gui/Scripting/JSInterface_GUIProxy.h @@ -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 #include class ScriptInterface; class ScriptRequest; +template +class JSI_GUIProxy; + // See JSI_GuiProxy below #if GCC_VERSION # pragma GCC diagnostic push @@ -35,6 +39,37 @@ #endif /** + * JS GUI proxies need to store some private data. + * This class is responsible for deleting that data. + */ +class IGUIProxyObject final +{ + template + friend class JSI_GUIProxy; + friend std::unique_ptr std::make_unique(); +public: + JSObject* Get() const + { + return m_Object.get(); + } + + using PrivateData = IGUIObject*; + template + static T* FromPrivateSlot(JSObject* obj) + { + return static_cast(static_cast(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 @@ 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 @@ * 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 class JSI_GUIProxy : public js::BaseProxyHandler { + // Need to friend other specializations so CreateFunctions() can call the IGUIObject version in all codepaths. + template + friend class JSI_GUIProxy; public: // Access the js::Class of the Proxy. static JSClass& ClassDefinition(); @@ -85,7 +123,8 @@ // Call this in CGUI::AddObjectTypes. static std::pair 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 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 @@ // Specialize this to define the custom properties of this type. static void CreateFunctions(const ScriptRequest& rq, GUIProxyProps* cache); + // Convenience helper for the above. + template + 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: Index: ps/trunk/source/gui/Scripting/JSInterface_GUIProxy.cpp =================================================================== --- ps/trunk/source/gui/Scripting/JSInterface_GUIProxy.cpp +++ ps/trunk/source/gui/Scripting/JSInterface_GUIProxy.cpp @@ -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 . + */ + +#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::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::CreateFunctions(const ScriptRequest& rq, GUIProxyProps* cache) +{ + CreateFunction<&CButton::GetTextSize>(rq, cache, "getTextSize"); +} +DECLARE_GUIPROXY(CButton); + +// CText +template<> void JSI_GUIProxy::CreateFunctions(const ScriptRequest& rq, GUIProxyProps* cache) +{ + CreateFunction<&CText::GetTextSize>(rq, cache, "getTextSize"); +} +DECLARE_GUIPROXY(CText); + +// CList +template<> void JSI_GUIProxy::CreateFunctions(const ScriptRequest& rq, GUIProxyProps* cache) +{ + CreateFunction(&CList::AddItem)>(rq, cache, "addItem"); +} +DECLARE_GUIPROXY(CList); Index: ps/trunk/source/gui/Scripting/JSInterface_GUIProxy_impl.h =================================================================== --- ps/trunk/source/gui/Scripting/JSInterface_GUIProxy_impl.h +++ ps/trunk/source/gui/Scripting/JSInterface_GUIProxy_impl.h @@ -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 @@ 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>; \ + m_JSObject = ProxyHandler::CreateJSObject(rq, this, GetGUI().GetProxyData(&ProxyHandler::Singleton())); \ +} \ +template class JSI_GUIProxy; + // Use a common namespace to avoid duplicating the symbols un-necessarily. namespace JSInterface_GUIProxy { -/** - * Conveniently wrap a simple C++ function to a JSNative. - */ -template -inline bool apply_to(JSContext* cx, uint argc, JS::Value* vp) +template +inline T* GuiObjectGetter(const ScriptRequest&, JS::CallArgs& args) { - JS::CallArgs args = JS::CallArgsFromVp(argc, vp); - OG* e = static_cast(js::GetProxyPrivate(args.thisv().toObjectOrNull()).toPrivate()); - if (!e) - return false; - - (static_cast(e)->*(funcptr))(*(ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface), args.rval()); - - return true; + return IGUIProxyObject::FromPrivateSlot(args.thisv().toObjectOrNull()); } // Default implementation of the cache via unordered_map @@ -77,9 +78,9 @@ 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 @@ using PropertyCache = typename PropCache::type; PropertyCache* data = new PropertyCache(); ScriptRequest rq(scriptInterface); - CreateFunctions(rq, data); + + // Functions common to all children of IGUIObject. + JSI_GUIProxy::CreateFunctions(rq, data); + + // Let derived classes register their own interface. + if constexpr (!std::is_same_v) + CreateFunctions(rq, data); return { &Singleton(), data }; } template -void JSI_GUIProxy::CreateJSObject(const ScriptRequest& rq, T* ptr, GUIProxyProps* dataPtr, JS::PersistentRootedObject& val) +template +void JSI_GUIProxy::CreateFunction(const ScriptRequest& rq, GUIProxyProps* cache, const std::string& name) +{ + cache->setFunction(rq, name, ScriptFunction::Create>(rq, name.c_str())); +} + +template +std::unique_ptr JSI_GUIProxy::CreateJSObject(const ScriptRequest& rq, T* ptr, GUIProxyProps* dataPtr) { js::ProxyOptions options; options.setClass(&ClassDefinition()); + auto ret = std::make_unique(); + ret->m_Ptr = static_cast(ptr); + JS::RootedValue cppObj(rq.cx), data(rq.cx); - cppObj.get().setPrivate(ptr); + cppObj.get().setPrivate(ret->m_Ptr); data.get().setPrivate(static_cast(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 @@ -138,7 +156,7 @@ ScriptInterface* pScriptInterface = ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface; ScriptRequest rq(*pScriptInterface); - T* e = static_cast(js::GetProxyPrivate(proxy.get()).toPrivate()); + T* e = IGUIProxyObject::FromPrivateSlot(proxy.get()); if (!e) return false; @@ -206,7 +224,7 @@ bool JSI_GUIProxy::set(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleValue vp, JS::HandleValue UNUSED(receiver), JS::ObjectOpResult& result) const { - T* e = static_cast(js::GetProxyPrivate(proxy.get()).toPrivate()); + T* e = IGUIProxyObject::FromPrivateSlot(proxy.get()); if (!e) { LOGERROR("C++ GUI Object could not be found"); @@ -261,7 +279,7 @@ template bool JSI_GUIProxy::delete_(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::ObjectOpResult& result) const { - T* e = static_cast(js::GetProxyPrivate(proxy.get()).toPrivate()); + T* e = IGUIProxyObject::FromPrivateSlot(proxy.get()); if (!e) { LOGERROR("C++ GUI Object could not be found"); Index: ps/trunk/source/gui/Scripting/JSInterface_IGUIObject.cpp =================================================================== --- ps/trunk/source/gui/Scripting/JSInterface_IGUIObject.cpp +++ ps/trunk/source/gui/Scripting/JSInterface_IGUIObject.cpp @@ -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 . - */ - -#include "precompiled.h" - -#include "JSInterface_GUIProxy_impl.h" - -using GUIObjectType = IGUIObject; - -template<> -void JSI_GUIProxy::CreateFunctions(const ScriptRequest& rq, GUIProxyProps* cache) -{ -#define func(class, func) &JSInterface_GUIProxy::apply_to - 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; Index: ps/trunk/source/scriptinterface/FunctionWrapper.h =================================================================== --- ps/trunk/source/scriptinterface/FunctionWrapper.h +++ ps/trunk/source/scriptinterface/FunctionWrapper.h @@ -285,7 +285,16 @@ template thisGetter = nullptr, u16 flags = JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT> static JSFunctionSpec Wrap(const char* name) { - return JS_FN(name, (&ToJSNative), args_info::nb_args, flags); + return JS_FN(name, (&ToJSNative), args_info::nb_args, flags); + } + + /** + * Return a JSFunction from a C++ function. + */ + template 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, args_info::nb_args, flags, name); } /**