diff --git a/binaries/data/mods/_test.gui/gui/settings/cguisize/assign.js b/binaries/data/mods/_test.gui/gui/settings/cguisize/assign.js new file mode 100644 index 0000000000..0ed45ed31d --- /dev/null +++ b/binaries/data/mods/_test.gui/gui/settings/cguisize/assign.js @@ -0,0 +1,4 @@ +testObject.size = { + "left": 3, + "bottom": 2 +}; diff --git a/binaries/data/mods/_test.gui/gui/settings/cguisize/compoundassignmentoperator.js b/binaries/data/mods/_test.gui/gui/settings/cguisize/compoundassignmentoperator.js new file mode 100644 index 0000000000..d721f2cd8f --- /dev/null +++ b/binaries/data/mods/_test.gui/gui/settings/cguisize/compoundassignmentoperator.js @@ -0,0 +1 @@ +testObject.size.left += 5; diff --git a/binaries/data/mods/_test.gui/gui/settings/cguisize/lazyassign.js b/binaries/data/mods/_test.gui/gui/settings/cguisize/lazyassign.js new file mode 100644 index 0000000000..f984d29c05 --- /dev/null +++ b/binaries/data/mods/_test.gui/gui/settings/cguisize/lazyassign.js @@ -0,0 +1 @@ +testObject.size.left = 5; diff --git a/binaries/data/mods/_test.gui/gui/settings/cguisize/objectassign.js b/binaries/data/mods/_test.gui/gui/settings/cguisize/objectassign.js new file mode 100644 index 0000000000..3e03485123 --- /dev/null +++ b/binaries/data/mods/_test.gui/gui/settings/cguisize/objectassign.js @@ -0,0 +1,3 @@ +Object.assign(testObject.size, { + "rleft": 4, "rbottom": 20 +}); diff --git a/source/gui/CGUI.cpp b/source/gui/CGUI.cpp index 195bb0fdab..ac550d4d01 100644 --- a/source/gui/CGUI.cpp +++ b/source/gui/CGUI.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2024 Wildfire Games. +/* Copyright (C) 2025 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -279,6 +279,7 @@ InReaction CGUI::HandleEvent(const SDL_Event_* ev) void CGUI::TickObjects() { + m_BaseObject->RecurseObject(&IGUIObject::IsHidden, &IGUIObject::DispatchDelayedSettingChanges); m_BaseObject->RecurseObject(&IGUIObject::IsHiddenOrGhostOrOutOfBoundaries, &IGUIObject::Tick); SendEventToAll(EventNameTick); m_Tooltip.Update(FindObjectUnderMouse(), m_MousePos, *this); diff --git a/source/gui/CGUISetting.cpp b/source/gui/CGUISetting.cpp index e1ff781ff0..fe97e6ad58 100644 --- a/source/gui/CGUISetting.cpp +++ b/source/gui/CGUISetting.cpp @@ -119,7 +119,6 @@ TYPE(CVector2D) TYPE(CStr) TYPE(CStrW) // TODO: make these inherit from CGUISimpleSetting directly. -TYPE(CGUISize) TYPE(CGUIColor) TYPE(CGUISpriteInstance) TYPE(CGUIString) diff --git a/source/gui/CGUISetting.h b/source/gui/CGUISetting.h index 82ebcdccc4..252800bf14 100644 --- a/source/gui/CGUISetting.h +++ b/source/gui/CGUISetting.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2023 Wildfire Games. +/* Copyright (C) 2025 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -50,6 +50,18 @@ public: */ virtual void ToJSVal(const ScriptRequest& rq, JS::MutableHandleValue value) = 0; + void DeferSettingChange() + { + delayNotification = true; + } + + void DispatchDelayedSettingChange() + { + if (!std::exchange(delayNotification, false)) + return; + + OnSettingChange(GetName(), true); + } protected: IGUISetting(IGUISetting&& other); IGUISetting& operator=(IGUISetting&& other) = delete; @@ -79,6 +91,7 @@ protected: */ IGUIObject& m_Object; + bool delayNotification{false}; private: CStr m_Name; }; diff --git a/source/gui/ObjectBases/IGUIObject.cpp b/source/gui/ObjectBases/IGUIObject.cpp index 24cbfc9022..511f63cc02 100644 --- a/source/gui/ObjectBases/IGUIObject.cpp +++ b/source/gui/ObjectBases/IGUIObject.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2024 Wildfire Games. +/* Copyright (C) 2025 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -256,6 +256,8 @@ void IGUIObject::UpdateCachedSize() CRect IGUIObject::GetComputedSize() { + // Ensure the size is up to date before we use it. + m_Settings.at("size")->DispatchDelayedSettingChange(); UpdateCachedSize(); return m_CachedActualSize; } @@ -564,3 +566,9 @@ void IGUIObject::DrawInArea(CCanvas2D& canvas, CRect& area) bool IGUIObject::IsHiddenOrGhostOrOutOfBoundaries() const { return !m_IsInsideBoundaries || IsHiddenOrGhost(); } + +void IGUIObject::DispatchDelayedSettingChanges() +{ + for (const auto& setting : m_Settings) + setting.second->DispatchDelayedSettingChange(); +} diff --git a/source/gui/ObjectBases/IGUIObject.h b/source/gui/ObjectBases/IGUIObject.h index 380b858bbd..17a9bff851 100644 --- a/source/gui/ObjectBases/IGUIObject.h +++ b/source/gui/ObjectBases/IGUIObject.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2024 Wildfire Games. +/* Copyright (C) 2025 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -271,6 +271,8 @@ public: virtual void DrawInArea(CCanvas2D& canvas, CRect& area); + virtual void DispatchDelayedSettingChanges(); + protected: /** * Draws the object. diff --git a/source/gui/ObjectBases/IGUIPanel.cpp b/source/gui/ObjectBases/IGUIPanel.cpp index 3ab79c64b7..edb804a570 100644 --- a/source/gui/ObjectBases/IGUIPanel.cpp +++ b/source/gui/ObjectBases/IGUIPanel.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 20244 Wildfire Games. +/* Copyright (C) 2025 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -43,6 +43,8 @@ void IGUIPanel::UpdateCachedSize() CRect IGUIPanel::GetComputedSize() { + // Ensure the size is up to date before we use it. + m_Settings.at("size")->DispatchDelayedSettingChange(); UpdateCachedSize(); return m_CachedLayoutActualSize; } diff --git a/source/gui/Scripting/GuiScriptConversions.cpp b/source/gui/Scripting/GuiScriptConversions.cpp index 504d07b4f9..3098efcc39 100644 --- a/source/gui/Scripting/GuiScriptConversions.cpp +++ b/source/gui/Scripting/GuiScriptConversions.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2024 Wildfire Games. +/* Copyright (C) 2025 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -22,7 +22,6 @@ #include "gui/SettingTypes/CGUIColor.h" #include "gui/SettingTypes/CGUIList.h" #include "gui/SettingTypes/CGUISeries.h" -#include "gui/SettingTypes/CGUISize.h" #include "gui/Scripting/JSInterface_GUIProxy.h" #include "lib/external_libraries/libsdl.h" #include "maths/Size2D.h" @@ -194,16 +193,6 @@ template<> void Script::ToJSVal(const ScriptRequest& rq, JS::MutableHandl "bottom", val.bottom); } -template<> void Script::ToJSVal(const ScriptRequest& rq, JS::MutableHandleValue ret, const CGUISize& val) -{ - val.ToJSVal(rq, ret); -} - -template<> bool Script::FromJSVal(const ScriptRequest& rq, JS::HandleValue v, CGUISize& out) -{ - return out.FromJSVal(rq, v); -} - template<> void Script::ToJSVal(const ScriptRequest& rq, JS::MutableHandleValue ret, const CGUIList& val) { ToJSVal(rq, ret, val.m_Items); diff --git a/source/gui/Scripting/JSInterface_CGUISize.cpp b/source/gui/Scripting/JSInterface_CGUISize.cpp new file mode 100644 index 0000000000..734e39b949 --- /dev/null +++ b/source/gui/Scripting/JSInterface_CGUISize.cpp @@ -0,0 +1,216 @@ +/* Copyright (C) 2025 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_CGUISize.h" + +#include "ps/CStr.h" +#include "gui/CGUI.h" +#include "gui/CGUISetting.h" +#include "gui/ObjectBases/IGUIObject.h" +#include "gui/SettingTypes/CGUISize.h" +#include "gui/Scripting/JSInterface_GUISize.h" +#include "maths/Rect.h" +#include "scriptinterface/Object.h" +#include "scriptinterface/ScriptInterface.h" +#include "scriptinterface/ScriptForward.h" +#include "scriptinterface/ScriptTypes.h" +#include + +namespace +{ +template +bool GetCRectField(JSContext* cx, unsigned argc, JS::Value* vp) +{ + JS::CallArgs args{JS::CallArgsFromVp(argc, vp)}; + JS::RootedObject obj{cx, &args.thisv().toObject()}; + CGUISimpleSetting* wrapper{JS::GetMaybePtrFromReservedSlot>(obj, ScriptInterface::JSObjectReservedSlots::PRIVATE)}; + + args.rval().setDouble(wrapper->GetMutable().*RectMember.*Member); + return true; +} + +template +bool SetCRectField(JSContext* cx, unsigned argc, JS::Value* vp) +{ + JS::CallArgs args{JS::CallArgsFromVp(argc, vp)}; + JS::RootedObject obj{cx, &args.thisv().toObject()}; + CGUISimpleSetting* wrapper{JS::GetMaybePtrFromReservedSlot>(obj, ScriptInterface::JSObjectReservedSlots::PRIVATE)}; + + double val; + if (!JS::ToNumber(cx, args.get(0), &val)) + return false; + + wrapper->GetMutable().*RectMember.*Member = static_cast(val); + args.rval().setUndefined(); + + wrapper->DeferSettingChange(); + return true; +} + +// Produces "10", "-10", "50%", "50%-10", "50%+10", etc +CStr ToPercentString(double pix, double per) +{ + if (per == 0) + return CStr::FromDouble(pix); + + if (pix == 0) + return fmt::format("{}%", per); + + return fmt::format("{}%{:+}", per, pix); +} + +bool toString(JSContext* cx, uint argc, JS::Value* vp) +{ + JS::CallArgs args{JS::CallArgsFromVp(argc, vp)}; + JS::RootedObject obj{cx, &args.thisv().toObject()}; + CGUISimpleSetting* wrapper{JS::GetMaybePtrFromReservedSlot>(obj, ScriptInterface::JSObjectReservedSlots::PRIVATE)}; + CStr buffer; + + buffer += ToPercentString(wrapper->GetMutable().pixel.left, wrapper->GetMutable().percent.left) + " "; + buffer += ToPercentString(wrapper->GetMutable().pixel.top, wrapper->GetMutable().percent.top) + " "; + buffer += ToPercentString(wrapper->GetMutable().pixel.right, wrapper->GetMutable().percent.right) + " "; + buffer += ToPercentString(wrapper->GetMutable().pixel.bottom, wrapper->GetMutable().percent.bottom); + + ScriptRequest rq{cx}; + Script::ToJSVal(rq, args.rval(), buffer); + return true; +} + +JSClass JSI_class = { + "CGUISize", JSCLASS_HAS_RESERVED_SLOTS(1) +}; + +JSFunctionSpec JSI_methods[] = +{ + JS_FN("toString", toString, 0, 0), + JS_FN("toSource", toString, 0, 0), + JS_FS_END +}; + +JSPropertySpec JSI_props[] = +{ + JS_PSGS("top", (GetCRectField<&CRect::top, &CGUISize::pixel>), (SetCRectField<&CRect::top, &CGUISize::pixel>), JSPROP_ENUMERATE), + JS_PSGS("left", (GetCRectField<&CRect::left, &CGUISize::pixel>), (SetCRectField<&CRect::left, &CGUISize::pixel>), JSPROP_ENUMERATE), + JS_PSGS("bottom", (GetCRectField<&CRect::bottom, &CGUISize::pixel>), (SetCRectField<&CRect::bottom, &CGUISize::pixel>), JSPROP_ENUMERATE), + JS_PSGS("right", (GetCRectField<&CRect::right, &CGUISize::pixel>), (SetCRectField<&CRect::right, &CGUISize::pixel>), JSPROP_ENUMERATE), + + JS_PSGS("rtop", (GetCRectField<&CRect::top, &CGUISize::percent>), (SetCRectField<&CRect::top, &CGUISize::percent>), JSPROP_ENUMERATE), + JS_PSGS("rleft", (GetCRectField<&CRect::left, &CGUISize::percent>), (SetCRectField<&CRect::left, &CGUISize::percent>), JSPROP_ENUMERATE), + JS_PSGS("rbottom", (GetCRectField<&CRect::bottom, &CGUISize::percent>), (SetCRectField<&CRect::bottom, &CGUISize::percent>), JSPROP_ENUMERATE), + JS_PSGS("rright", (GetCRectField<&CRect::right, &CGUISize::percent>), (SetCRectField<&CRect::right, &CGUISize::percent>), JSPROP_ENUMERATE), + JS_PS_END +}; +} // end anonymous namespace + + + +void JSI_CGUISize::RegisterScriptClass(ScriptInterface& scriptInterface) +{ + scriptInterface.DefineCustomObjectType(&JSI_class, nullptr, 0, JSI_props, JSI_methods, nullptr, nullptr); +} + +template class CGUISimpleSetting; +template<> +void CGUISimpleSetting::ToJSVal(const ScriptRequest& rq, JS::MutableHandleValue ret) +{ + const ScriptInterface& scriptInterface = rq.GetScriptInterface(); + JS::RootedObject obj{rq.cx, scriptInterface.CreateCustomObject("CGUISize")}; + + JS::SetReservedSlot(obj, ScriptInterface::JSObjectReservedSlots::PRIVATE, JS::PrivateValue(this)); + ret.setObject(*obj); +}; + +template<> +bool CGUISimpleSetting::DoFromString(const CStrW& value) +{ + return CGUI::ParseString(&m_Object.GetGUI(), value, m_Setting); +}; + +template<> +bool CGUISimpleSetting::DoFromJSVal(const ScriptRequest& rq, JS::HandleValue value) +{ + if (value.isString()) + { + CStrW str; + if (!Script::FromJSVal(rq, value, str)) + { + LOGERROR("CGUISize could not read JS string"); + return false; + } + + if (!m_Setting.FromString(str.ToUTF8())) + { + LOGERROR("CGUISize could not parse JS string"); + return false; + } + + return true; + } + + if (!value.isObject()) + { + LOGERROR("CGUISize value is not an String, nor Object"); + return false; + } + + JS::RootedObject obj{rq.cx, &value.toObject()}; + if (JS_InstanceOf(rq.cx, obj, &JSI_class, nullptr)) + { + CGUISimpleSetting* wrapper = JS::GetMaybePtrFromReservedSlot>(obj, ScriptInterface::JSObjectReservedSlots::PRIVATE); + if (this != wrapper) + this->Set(wrapper->m_Setting, false); + return true; + } + + if (JS_InstanceOf(rq.cx, obj, &JSI_GUISize::JSI_class, nullptr)) + LOGWARNING("Assigning an GUISize to CGUISize is deprecated. Please use the object.size = {left:number, top:number, right:number, bottom:number} format instead. This support will be removed in a future version."); + + bool atLeastOnePropertySet{false}; + CRect pixel{}; + CRect percent{}; + auto tryGet = [&](const char* name, float& field) + { + if (!Script::HasProperty(rq, value, name) || !Script::GetProperty(rq, value, name, field)) + return; + + atLeastOnePropertySet = true; + }; + + tryGet("left", pixel.left); + tryGet("top", pixel.top); + tryGet("right", pixel.right); + tryGet("bottom", pixel.bottom); + + tryGet("rtop", percent.top); + tryGet("rleft", percent.left); + tryGet("rbottom", percent.bottom); + tryGet("rright", percent.right); + + // If no properties were set, return false to indicate failure. + if (!atLeastOnePropertySet) + { + LOGERROR("CGUISize could not read JS object"); + return false; + } + + m_Setting.pixel = pixel; + m_Setting.percent = percent; + + return true; +}; diff --git a/source/gui/Scripting/JSInterface_CGUISize.h b/source/gui/Scripting/JSInterface_CGUISize.h new file mode 100644 index 0000000000..98d0494526 --- /dev/null +++ b/source/gui/Scripting/JSInterface_CGUISize.h @@ -0,0 +1,27 @@ +/* Copyright (C) 2025 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_CGUISIZE +#define INCLUDED_JSI_CGUISIZE + +class ScriptInterface; +namespace JSI_CGUISize +{ + void RegisterScriptClass(ScriptInterface& scriptInterface); +} + +#endif // INCLUDED_JSI_GUISIZE diff --git a/source/gui/Scripting/JSInterface_GUIProxy_impl.h b/source/gui/Scripting/JSInterface_GUIProxy_impl.h index ab3142742a..169bc2b450 100644 --- a/source/gui/Scripting/JSInterface_GUIProxy_impl.h +++ b/source/gui/Scripting/JSInterface_GUIProxy_impl.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2023 Wildfire Games. +/* Copyright (C) 2025 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -31,6 +31,9 @@ #include #include +#ifndef INCLUDED_JSI_GUIPROXY_IMP +#define INCLUDED_JSI_GUIPROXY_IMP + template JSI_GUIProxy& JSI_GUIProxy::Singleton() { @@ -319,3 +322,4 @@ bool JSI_GUIProxy::delete_(JSContext* cx, JS::HandleObject proxy, JS::HandleI LOGERROR("Only event handlers can be deleted from GUI objects!"); return result.fail(JSMSG_BAD_PROP_ID); } +#endif // INCLUDED_JSI_GUIPROXY_IMP diff --git a/source/gui/Scripting/ScriptFunctions.cpp b/source/gui/Scripting/ScriptFunctions.cpp index 472bf59fc1..18bb38a9cf 100644 --- a/source/gui/Scripting/ScriptFunctions.cpp +++ b/source/gui/Scripting/ScriptFunctions.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2022 Wildfire Games. +/* Copyright (C) 2025 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -20,6 +20,7 @@ #include "ScriptFunctions.h" #include "graphics/scripting/JSInterface_GameView.h" +#include "gui/Scripting/JSInterface_CGUISize.h" #include "gui/Scripting/JSInterface_GUIManager.h" #include "gui/Scripting/JSInterface_GUISize.h" #include "i18n/scripting/JSInterface_L10n.h" @@ -53,6 +54,7 @@ void GuiScriptingInit(ScriptInterface& scriptInterface) ScriptRequest rq(scriptInterface); JSI_GUISize::RegisterScriptClass(scriptInterface); + JSI_CGUISize::RegisterScriptClass(scriptInterface); JSI_ConfigDB::RegisterScriptFunctions(rq); JSI_Console::RegisterScriptFunctions(rq); JSI_Debug::RegisterScriptFunctions(rq); diff --git a/source/gui/SettingTypes/CGUISize.cpp b/source/gui/SettingTypes/CGUISize.cpp index 843d200115..a914e8b7bc 100644 --- a/source/gui/SettingTypes/CGUISize.cpp +++ b/source/gui/SettingTypes/CGUISize.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2025 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -142,90 +142,3 @@ bool CGUISize::FromString(const CStr& Value) percent.bottom = percents[3]; return true; } - -void CGUISize::ToJSVal(const ScriptRequest& rq, JS::MutableHandleValue ret) const -{ - const ScriptInterface& scriptInterface = rq.GetScriptInterface(); - ret.setObjectOrNull(scriptInterface.CreateCustomObject("GUISize")); - - if (!ret.isObject()) - { - ScriptException::Raise(rq, "CGUISize value is not an Object"); - return; - } - - JS::RootedObject obj(rq.cx, &ret.toObject()); - if (!JS_InstanceOf(rq.cx, obj, &JSI_GUISize::JSI_class, nullptr)) - { - ScriptException::Raise(rq, "CGUISize value is not a CGUISize class instance"); - return; - } - -#define P(x, y, z)\ - if (!Script::SetProperty(rq, ret, #z, x.y)) \ - { \ - ScriptException::Raise(rq, "Could not SetProperty '%s'", #z); \ - return; \ - } - P(pixel, left, left); - P(pixel, top, top); - P(pixel, right, right); - P(pixel, bottom, bottom); - P(percent, left, rleft); - P(percent, top, rtop); - P(percent, right, rright); - P(percent, bottom, rbottom); -#undef P -} - -bool CGUISize::FromJSVal(const ScriptRequest& rq, JS::HandleValue v) -{ - if (v.isString()) - { - CStrW str; - if (!Script::FromJSVal(rq, v, str)) - { - LOGERROR("CGUISize could not read JS string"); - return false; - } - - if (!FromString(str.ToUTF8())) - { - LOGERROR("CGUISize could not parse JS string"); - return false; - } - return true; - } - - if (!v.isObject()) - { - LOGERROR("CGUISize value is not an String, nor Object"); - return false; - } - - JS::RootedObject obj(rq.cx, &v.toObject()); - if (!JS_InstanceOf(rq.cx, obj, &JSI_GUISize::JSI_class, nullptr)) - { - LOGERROR("CGUISize value is not a CGUISize class instance"); - return false; - } - -#define P(x, y, z) \ - if (!Script::GetProperty(rq, v, #z, x.y))\ - {\ - LOGERROR("CGUISize could not get object property '%s'", #z);\ - return false;\ - } - - P(pixel, left, left); - P(pixel, top, top); - P(pixel, right, right); - P(pixel, bottom, bottom); - P(percent, left, rleft); - P(percent, top, rtop); - P(percent, right, rright); - P(percent, bottom, rbottom); -#undef P - - return true; -} diff --git a/source/gui/SettingTypes/CGUISize.h b/source/gui/SettingTypes/CGUISize.h index d9769453d4..244d0d1e08 100644 --- a/source/gui/SettingTypes/CGUISize.h +++ b/source/gui/SettingTypes/CGUISize.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2025 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -66,9 +66,6 @@ public: { return pixel == other.pixel && percent == other.percent; } - - void ToJSVal(const ScriptRequest& rq, JS::MutableHandleValue ret) const; - bool FromJSVal(const ScriptRequest& rq, JS::HandleValue v); }; #endif // INCLUDED_CGUISIZE diff --git a/source/gui/tests/test_GUISetting.h b/source/gui/tests/test_GUISetting.h index 36996b0f55..e3dd5ef435 100644 --- a/source/gui/tests/test_GUISetting.h +++ b/source/gui/tests/test_GUISetting.h @@ -21,8 +21,11 @@ #include "graphics/FontMetrics.h" #include "gui/CGUI.h" #include "gui/CGUISetting.h" +#include "gui/Scripting/JSInterface_CGUISize.h" +#include "gui/Scripting/JSInterface_GUIProxy.h" #include "gui/CGUIText.h" #include "gui/ObjectBases/IGUIObject.h" +#include "gui/SettingTypes/CGUISize.h" #include "gui/SettingTypes/CGUIString.h" #include "i18n/L10n.h" #include "ps/CLogger.h" @@ -31,7 +34,11 @@ #include "ps/ProfileViewer.h" #include "ps/VideoMode.h" #include "renderer/Renderer.h" +#include "scriptinterface/Object.h" +#include "scriptinterface/ScriptContext.h" #include "scriptinterface/ScriptInterface.h" +#include "scriptinterface/ScriptRequest.h" +#include "scriptinterface/ModuleLoader.h" #include #include @@ -51,11 +58,23 @@ public: TestGUIObject(CGUI& gui) : IGUIObject(gui) {} void Draw(CCanvas2D&) {} + void UpdateCachedSize() { + haveUpdateCachedSize = true; + IGUIObject::UpdateCachedSize(); + } + + CGUISimpleSetting* GetSizeSetting() const + { + return static_cast*>(m_Settings.at("size")); + } + + bool haveUpdateCachedSize{false}; }; void setUp() { g_VFS = CreateVfs(); + TS_ASSERT_OK(g_VFS->Mount(L"", DataDir() / "mods" / "_test.gui" / "", VFS_MOUNT_MUST_EXIST)); TS_ASSERT_OK(g_VFS->Mount(L"", DataDir() / "mods" / "_test.minimal" / "", VFS_MOUNT_MUST_EXIST)); TS_ASSERT_OK(g_VFS->Mount(L"cache", DataDir() / "_testcache" / "", 0, VFS_MAX_PRIORITY)); @@ -106,4 +125,57 @@ public: object.SetSettingFromString("A", L"ValueB", false); TS_ASSERT_EQUALS(*settingB, "ValueB"); } + + void test_setting_cguisize() + { + CGUI gui{*g_ScriptContext}; + gui.AddObjectTypes(); + TestGUIObject object{gui}; + + CGUISimpleSetting* setting{object.GetSizeSetting()}; + object.SetSettingFromString("size", L"2 2 20 20", false); + object.haveUpdateCachedSize = false; + + ScriptRequest rq{gui.GetScriptInterface()}; + JS::RootedValue val(rq.cx); + val.setObject(*object.GetJSObject()); + JS::RootedObject global(rq.cx, rq.glob); + JS_DefineProperty(rq.cx, global, "testObject", val, JSPROP_ENUMERATE); + + // Lazy assigment. + TS_ASSERT(gui.GetScriptInterface()->LoadGlobalScriptFile(L"gui/settings/cguisize/lazyassign.js")); + TS_ASSERT_EQUALS(setting->GetMutable().pixel, (CRect{5, 2, 20, 20})); + TS_ASSERT_EQUALS(setting->GetMutable().percent, (CRect{0, 0, 0, 0})); + TS_ASSERT_EQUALS(object.haveUpdateCachedSize, false); + + // Force update of cached size. + object.GetComputedSize(); + object.haveUpdateCachedSize = false; + + // Compound assignment operator. + TS_ASSERT(gui.GetScriptInterface()->LoadGlobalScriptFile(L"gui/settings/cguisize/compoundassignmentoperator.js")); + TS_ASSERT_EQUALS(setting->GetMutable().pixel, (CRect{10, 2, 20, 20})); + TS_ASSERT_EQUALS(setting->GetMutable().percent, (CRect{0, 0, 0, 0})); + TS_ASSERT_EQUALS(object.haveUpdateCachedSize, false); + + // Force update of cached size. + object.GetComputedSize(); + object.haveUpdateCachedSize = false; + + // Object assignment. + TS_ASSERT(gui.GetScriptInterface()->LoadGlobalScriptFile(L"gui/settings/cguisize/objectassign.js")); + TS_ASSERT_EQUALS(setting->GetMutable().pixel, (CRect{10, 2, 20, 20})); + TS_ASSERT_EQUALS(setting->GetMutable().percent, (CRect{4, 0, 0, 20})); + TS_ASSERT_EQUALS(object.haveUpdateCachedSize, false); + + // Force update of cached size. + object.GetComputedSize(); + object.haveUpdateCachedSize = false; + + // assign + TS_ASSERT(gui.GetScriptInterface()->LoadGlobalScriptFile(L"gui/settings/cguisize/assign.js")); + TS_ASSERT_EQUALS(setting->GetMutable().pixel, (CRect{3, 0, 0, 2})); + TS_ASSERT_EQUALS(setting->GetMutable().percent, (CRect{0, 0, 0, 0})); + TS_ASSERT_EQUALS(object.haveUpdateCachedSize, true); + } };