Index: ps/trunk/source/gui/IGUIObject.h =================================================================== --- ps/trunk/source/gui/IGUIObject.h +++ ps/trunk/source/gui/IGUIObject.h @@ -104,7 +104,6 @@ friend bool JSI_IGUIObject::getProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleValue vp); friend bool JSI_IGUIObject::setProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool UNUSED(strict), JS::MutableHandleValue vp); friend bool JSI_IGUIObject::getComputedSize(JSContext* cx, uint argc, JS::Value* vp); - friend bool JSI_IGUIObject::getTextSize(JSContext* cx, uint argc, JS::Value* vp); public: IGUIObject(CGUI* pGUI); @@ -353,6 +352,11 @@ */ void SetFocus(); + /** + * Workaround to avoid a dynamic_cast which can be 80 times slower than this. + */ + virtual void* GetTextOwner() { return nullptr; } + protected: /** * Check if object is focused. Index: ps/trunk/source/gui/IGUITextOwner.h =================================================================== --- ps/trunk/source/gui/IGUITextOwner.h +++ ps/trunk/source/gui/IGUITextOwner.h @@ -36,6 +36,7 @@ #define INCLUDED_IGUITEXTOWNER #include "GUI.h" +#include "gui/scripting/JSInterface_IGUITextOwner.h" /** * Framework for handling Output text. @@ -44,6 +45,8 @@ */ class IGUITextOwner : virtual public IGUIObject { + friend bool JSI_IGUITextOwner::GetTextSize(JSContext* cx, uint argc, JS::Value* vp); + public: IGUITextOwner(CGUI* pGUI); virtual ~IGUITextOwner(); @@ -54,6 +57,11 @@ void AddText(SGUIText* text); /** + * Subscribe the custom JS methods. + */ + void CreateJSObject() override; + + /** * @see IGUIObject#HandleMessage() */ virtual void HandleMessage(SGUIMessage& Message); @@ -80,6 +88,11 @@ */ virtual bool MouseOverIcon(); + /** + * Workaround to avoid a dynamic_cast which can be 80 times slower than this. + */ + virtual void* GetTextOwner() override { return this; } + protected: /** @@ -101,6 +114,11 @@ * Calculate the position for the text, based on the alignment. */ void CalculateTextPosition(CRect& ObjSize, CPos& TextPos, SGUIText& Text); + + /** + * Calculate the size of the first generated text. + */ + CSize CalculateTextSize(); }; #endif // INCLUDED_IGUITEXTOWNER Index: ps/trunk/source/gui/IGUITextOwner.cpp =================================================================== --- ps/trunk/source/gui/IGUITextOwner.cpp +++ ps/trunk/source/gui/IGUITextOwner.cpp @@ -17,7 +17,10 @@ #include "precompiled.h" +#include "IGUITextOwner.h" + #include "gui/GUI.h" +#include "gui/scripting/JSInterface_IGUITextOwner.h" IGUITextOwner::IGUITextOwner(CGUI* pGUI) : IGUIObject(pGUI), m_GeneratedTextsValid(false) @@ -30,6 +33,14 @@ delete t; } +void IGUITextOwner::CreateJSObject() +{ + IGUIObject::CreateJSObject(); + + JSI_IGUITextOwner::RegisterScriptFunctions( + GetGUI()->GetScriptInterface()->GetContext(), m_JSObject); +} + void IGUITextOwner::AddText(SGUIText* text) { m_GeneratedTexts.push_back(text); @@ -110,6 +121,21 @@ } } +CSize IGUITextOwner::CalculateTextSize() +{ + if (!m_GeneratedTextsValid) + { + SetupText(); + m_GeneratedTextsValid = true; + } + + if (m_GeneratedTexts.empty()) + return CSize(); + + // GUI Object types that use multiple texts may override this function. + return m_GeneratedTexts[0]->m_Size; +} + bool IGUITextOwner::MouseOverIcon() { return false; Index: ps/trunk/source/gui/scripting/GuiScriptConversions.cpp =================================================================== --- ps/trunk/source/gui/scripting/GuiScriptConversions.cpp +++ ps/trunk/source/gui/scripting/GuiScriptConversions.cpp @@ -170,6 +170,34 @@ return FromJSVal(cx, v, out); } +template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const CSize& val) +{ + ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface->CreateObject(ret, "width", val.cx, "height", val.cy); +} + +template<> bool ScriptInterface::FromJSVal(JSContext* cx, JS::HandleValue v, CSize& out) +{ + if (!v.isObject()) + { + JS_ReportError(cx, "CSize value must be an object!"); + return false; + } + + if (!FromJSProperty(cx, v, "width", out.cx)) + { + JS_ReportError(cx, "Failed to get CSize.cx property"); + return false; + } + + if (!FromJSProperty(cx, v, "height", out.cy)) + { + JS_ReportError(cx, "Failed to get CSize.cy property"); + return false; + } + + return true; +} + template<> void ScriptInterface::ToJSVal(JSContext* cx, JS::MutableHandleValue ret, const CPos& val) { ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface->CreateObject(ret, "x", val.x, "y", val.y); 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 @@ -44,7 +44,6 @@ JS_FN("focus", JSI_IGUIObject::focus, 0, 0), JS_FN("blur", JSI_IGUIObject::blur, 0, 0), JS_FN("getComputedSize", JSI_IGUIObject::getComputedSize, 0, 0), - JS_FN("getTextSize", JSI_IGUIObject::getTextSize, 0, 0), JS_FS_END }; @@ -66,6 +65,7 @@ return false; // Skip registered functions and inherited properties + // including JSInterfaces of derived classes if (propName == "constructor" || propName == "prototype" || propName == "toString" || @@ -228,78 +228,6 @@ return true; } -bool JSI_IGUIObject::getTextSize(JSContext* cx, uint argc, JS::Value* vp) -{ - JSAutoRequest rq(cx); - JS::CallReceiver rec = JS::CallReceiverFromVp(vp); - - JS::CallArgs args = JS::CallArgsFromVp(argc, vp); - JS::RootedObject thisObj(cx, &args.thisv().toObject()); - - IGUIObject* obj = (IGUIObject*)JS_GetInstancePrivate(cx, thisObj, &JSI_IGUIObject::JSI_class, NULL); - - if (!obj || !obj->SettingExists("caption")) - return false; - - CStrW font; - if (GUI::GetSetting(obj, "font", font) != PSRETURN_OK || font.empty()) - font = L"default"; - - CGUIString caption; - EGUISettingType Type; - obj->GetSettingType("caption", Type); - if (Type == GUIST_CGUIString) - // CText, CButton, CCheckBox, CRadioButton - GUI::GetSetting(obj, "caption", caption); - else if (Type == GUIST_CStrW) - { - // CInput - CStrW captionStr; - GUI::GetSetting(obj, "caption", captionStr); - caption.SetValue(captionStr); - } - else - return false; - - obj->UpdateCachedSize(); - float width = obj->m_CachedActualSize.GetWidth(); - - if (obj->SettingExists("scrollbar")) - { - bool scrollbar; - GUI::GetSetting(obj, "scrollbar", scrollbar); - if (scrollbar) - { - CStr scrollbar_style; - GUI::GetSetting(obj, "scrollbar_style", scrollbar_style); - const SGUIScrollBarStyle* scrollbar_style_object = obj->GetGUI()->GetScrollBarStyle(scrollbar_style); - if (scrollbar_style_object) - width -= scrollbar_style_object->m_Width; - } - } - - float buffer_zone = 0.f; - GUI::GetSetting(obj, "buffer_zone", buffer_zone); - SGUIText text = obj->GetGUI()->GenerateText(caption, font, width, buffer_zone, obj); - - JS::RootedValue objVal(cx); - try - { - ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface->CreateObject( - &objVal, - "width", text.m_Size.cx, - "height", text.m_Size.cy); - } - catch (PSERROR_Scripting_ConversionFailed&) - { - debug_warn(L"Error creating size object!"); - return false; - } - - rec.rval().set(objVal); - return true; -} - bool JSI_IGUIObject::getComputedSize(JSContext* cx, uint UNUSED(argc), JS::Value* vp) { JSAutoRequest rq(cx); Index: ps/trunk/source/gui/scripting/JSInterface_IGUITextOwner.h =================================================================== --- ps/trunk/source/gui/scripting/JSInterface_IGUITextOwner.h +++ ps/trunk/source/gui/scripting/JSInterface_IGUITextOwner.h @@ -0,0 +1,32 @@ +/* Copyright (C) 2019 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 0 A.D. If not, see . + */ + +#ifndef INCLUDED_JSI_IGUITEXTOWNER +#define INCLUDED_JSI_IGUITEXTOWNER + +#include "scriptinterface/ScriptInterface.h" + +namespace JSI_IGUITextOwner +{ + extern JSFunctionSpec JSI_methods[]; + + void RegisterScriptFunctions(JSContext* cx, JS::HandleObject obj); + + bool GetTextSize(JSContext* cx, uint argc, JS::Value* vp); +} + +#endif // INCLUDED_JSI_IGUITEXTOWNER Index: ps/trunk/source/gui/scripting/JSInterface_IGUITextOwner.cpp =================================================================== --- ps/trunk/source/gui/scripting/JSInterface_IGUITextOwner.cpp +++ ps/trunk/source/gui/scripting/JSInterface_IGUITextOwner.cpp @@ -0,0 +1,59 @@ +/* Copyright (C) 2019 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 0 A.D. If not, see . + */ + +#include "precompiled.h" + +#include "JSInterface_IGUITextOwner.h" + +#include "gui/IGUITextOwner.h" +#include "scriptinterface/ScriptInterface.h" + +JSFunctionSpec JSI_IGUITextOwner::JSI_methods[] = +{ + JS_FN("getTextSize", GetTextSize, 0, 0), + JS_FS_END +}; + +void JSI_IGUITextOwner::RegisterScriptFunctions(JSContext* cx, JS::HandleObject obj) +{ + JS_DefineFunctions(cx, obj, JSI_methods); +} + +bool JSI_IGUITextOwner::GetTextSize(JSContext* cx, uint UNUSED(argc), JS::Value* vp) +{ + JSAutoRequest rq(cx); + JS::CallReceiver rec = JS::CallReceiverFromVp(vp); + JS::RootedObject thisObj(cx, &rec.thisv().toObject()); + + IGUIObject* obj = static_cast(JS_GetInstancePrivate(cx, thisObj, &JSI_IGUIObject::JSI_class, nullptr)); + if (!obj) + { + JS_ReportError(cx, "This is not an IGUIObject!"); + return false; + } + + // Avoid dynamic_cast for performance reasons + IGUITextOwner* objText = static_cast(obj->GetTextOwner()); + if (!objText) + { + JS_ReportError(cx, "This IGUIObject is not an IGUITextOwner!"); + return false; + } + + ScriptInterface::ToJSVal(cx, rec.rval(), objText->CalculateTextSize()); + return true; +}