Index: binaries/data/mods/public/gui/pregame/MainMenuPage.js =================================================================== --- binaries/data/mods/public/gui/pregame/MainMenuPage.js +++ binaries/data/mods/public/gui/pregame/MainMenuPage.js @@ -9,6 +9,46 @@ this.menuHandler = new MainMenuItemHandler(mainMenuItems); this.splashScreenHandler = new SplashScreenHandler(data, hotloadData && hotloadData.splashScreenHandler); + let toto = Engine.GetGUIObjectByName("mainMenuPage"); + let doText = (caption, size) => ({ + "@type": "text", + "@size": size, + "@style": "ModernLabelText", + "@caption": caption, + }); + let added = toto.createChild({ + "@type": "image", + "@size": "200 200 500 500", + "@style": "ModernDialog", + "@z": 200, + "object": [ + doText("turlututu", "0 0 200 200"), + { + "@type": "text", + "@size": "0 200 200 400", + "@style": "ModernLabelText", + "@caption": "ChapoPointu", + "object": { + "@type": "image", + "@size": "600 300 100% 600+40", + "@style": "ModernDialog", + "@z": 250, + } + }] + }); + added.deleteChild(added.children[0]); + + toto.createChild({ + "script": { + "@file": "not_a_file.js", + }, + }); + + + toto.createChild({ + "script": "warn('Hey you')", + }); + new MusicHandler(); new ProjectInformationHandler(projectInformation); new CommunityButtonHandler(communityButtons); Index: source/gui/CGUI.h =================================================================== --- source/gui/CGUI.h +++ source/gui/CGUI.h @@ -250,6 +250,18 @@ shared_ptr GetScriptInterface() { return m_ScriptInterface; }; + /** + * The CGUI creates the object and adds it as a child to parent. + * To the created object, this is the same as being created on page open (same events, etc.) + */ + IGUIObject* CreateChild(const XMBData& file, const XMBElement& element, IGUIObject* parent); + + /** + * Detach the object from its parent and delete it. + * The object must have a parent - use PopPage to delete the root page object. + */ + void DetachAndDeleteChild(IGUIObject* child); + private: /** * The CGUI takes ownership of the child object and links the parent with the child. Index: source/gui/CGUI.cpp =================================================================== --- source/gui/CGUI.cpp +++ source/gui/CGUI.cpp @@ -371,6 +371,37 @@ return (*it->second)(*this); } + +IGUIObject* CGUI::CreateChild(const XMBData& file, const XMBElement& element, IGUIObject* parent) +{ + std::unordered_set paths; + std::vector> nameSubst; + IGUIObject* child = Xeromyces_ReadObject(file, element, parent, nameSubst, paths, 0); + if (!child) + return nullptr; + + child->RecurseObject(nullptr, &IGUIObject::UpdateCachedSize); + SGUIMessage msg(GUIM_LOAD); + child->RecurseObject(nullptr, &IGUIObject::HandleMessage, msg); + child->ScriptEvent(EventNameLoad); + + return child; +} + +void CGUI::DetachAndDeleteChild(IGUIObject* child) +{ + if (!child) + return; + if (!child->m_pParent) + { + LOGERROR("Cannot delete the root object of a page"); + return; + } + child->m_pParent->UnregisterChild(child); + m_pAllObjects.erase(child->m_Name); + delete child; +} + bool CGUI::AddObject(IGUIObject& parent, IGUIObject& child) { if (child.m_Name.empty()) Index: source/gui/ObjectBases/IGUIObject.h =================================================================== --- source/gui/ObjectBases/IGUIObject.h +++ source/gui/ObjectBases/IGUIObject.h @@ -101,6 +101,18 @@ // Will change all internally set names to something like "" CStr GetPresentableName() const; + /** + * Create a child object from the given definition. + * @return nullptr on failure. + */ + IGUIObject* CreateChild(JS::HandleValue); + + /** + * Remove a child & delete it. + * @return whether the child was deleted. + */ + bool DeleteChild(IGUIObject* child); + /** * Return all child objects of the current object. */ Index: source/gui/ObjectBases/IGUIObject.cpp =================================================================== --- source/gui/ObjectBases/IGUIObject.cpp +++ source/gui/ObjectBases/IGUIObject.cpp @@ -26,6 +26,7 @@ #include "ps/CLogger.h" #include "ps/GameSetup/Config.h" #include "ps/Profile.h" +#include "ps/XMB/XMBStorage.h" #include "scriptinterface/ScriptContext.h" #include "scriptinterface/ScriptExtraHeaders.h" #include "scriptinterface/ScriptInterface.h" @@ -86,6 +87,27 @@ // m_Children is deleted along all other GUI Objects in the CGUI destructor } +IGUIObject* IGUIObject::CreateChild(JS::HandleValue value) +{ + XMBData spec; + XMBStorage storage; + storage.LoadJSValue(*m_pGUI.GetScriptInterface().get(), value, "object"); + spec.Initialise(storage); + return m_pGUI.CreateChild(spec, spec.GetRoot(), this); +} + +bool IGUIObject::DeleteChild(IGUIObject* child) +{ + // Don't delete someone else's child. + std::vector::iterator it = std::find(m_Children.begin(), m_Children.end(), child); + if (it == m_Children.end()) + return false; + + // Calls RemoveChild(); + m_pGUI.DetachAndDeleteChild(child); + return true; +} + void IGUIObject::RegisterChild(IGUIObject* child) { child->SetParent(this); Index: source/gui/Scripting/JSInterface_GUIProxy.cpp =================================================================== --- source/gui/Scripting/JSInterface_GUIProxy.cpp +++ source/gui/Scripting/JSInterface_GUIProxy.cpp @@ -33,7 +33,10 @@ CreateFunction<&IGUIObject::SetFocus>(rq, cache, "focus"); CreateFunction<&IGUIObject::ReleaseFocus>(rq, cache, "blur"); CreateFunction<&IGUIObject::GetComputedSize>(rq, cache, "getComputedSize"); + CreateFunction<&IGUIObject::CreateChild>(rq, cache, "createChild"); + CreateFunction<&IGUIObject::DeleteChild>(rq, cache, "deleteChild"); } + DECLARE_GUIPROXY(IGUIObject); // Implement derived types below.