mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-06-16 05:13:58 -07:00
Simplify GUI Proxy implementation & customisation.
This finishes7c04ea0211and1b67a079fb. 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:
parent
3759fc8a98
commit
fc60d80af1
16 changed files with 221 additions and 290 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
@ -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>;
|
||||
|
|
@ -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>;
|
||||
60
source/gui/Scripting/JSInterface_GUIProxy.cpp
Normal file
60
source/gui/Scripting/JSInterface_GUIProxy.cpp
Normal 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);
|
||||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in a new issue