Changeset 28145

Timestamp:
Jul 8, 2024, 9:07:04 PM (3 weeks ago)
Author:
phosit
Message:

Get a promise when starting a GUIpage

When calling Engine.PushGuiPage a promise is returned. The promise is settled when the "child" page is closed. That allows to await it inside async functions.
Previously the callback was run right inside the call to Engine.PopGuiPage. Now the continuation of the promise is called at the end of the "tick".

This won't help performance. It will more likely make things worse. Since gui pages aren't opened or closed that frequently, it doesn't matter that much.

Refs: r22676

For the engine side:
The promise is stored in the CGUIManager::SGUIPage (like previously the callback). When the promise is fulfilled it enqueues a callback in the JobQueue of the JSContext.

Comments by: @wraitii, @Stan, @Polakrity, @lyv, @elexis, @vladislavbelov

Differential Revision: https://code.wildfiregames.com/D3807

Location:
ps/trunk
Files:
22 edited

Legend:

Unmodified
Added
Removed
  • ps/trunk/binaries/data/mods/_test.gui/gui/regainFocus/pushWithPopOnInit.js

    r25616 r28145  
    1 Engine.PushGuiPage("regainFocus/page_emptyPage.xml", {}, () => Engine.PopGuiPage());
     1Engine.PushGuiPage("regainFocus/page_emptyPage.xml");
  • ps/trunk/binaries/data/mods/mod/gui/common/functions_msgbox.js

    r27188 r28145  
    1 function messageBox(mbWidth, mbHeight, mbMessage, mbTitle, mbButtonCaptions, mbBtnCode, mbCallbackArgs)
     1async function messageBox(mbWidth, mbHeight, mbMessage, mbTitle, mbButtonCaptions, mbBtnCode,
     2    mbCallbackArgs)
    23{
    3     Engine.PushGuiPage(
     4    Engine.PushGuiPage(
    45        "page_msgbox.xml",
    56        {
     
    910            "title": mbTitle,
    1011            "buttonCaptions": mbButtonCaptions
    11         },
    12         btnCode => {
    13             if (mbBtnCode !== undefined && mbBtnCode[btnCode])
    14                 mbBtnCode[btnCode](mbCallbackArgs ? mbCallbackArgs[btnCode] : undefined);
    1512        });
     13
     14
     15
    1616}
    1717
    18 function timedConfirmation(width, height, message, timeParameter, timeout, title, buttonCaptions, btnCode, callbackArgs)
     18async function timedConfirmation(width, height, message, timeParameter, timeout, title, buttonCaptions,
     19    btnCode, callbackArgs)
    1920{
    20     Engine.PushGuiPage(
     21    Engine.PushGuiPage(
    2122        "page_timedconfirmation.xml",
    2223        {
     
    2829            "title": title,
    2930            "buttonCaptions": buttonCaptions
    30         },
    31         button => {
    32             if (btnCode !== undefined && btnCode[button])
    33                 btnCode[button](callbackArgs ? callbackArgs[button] : undefined);
    3431        });
    35 }
    3632
    37 function colorMixer(color, callback)
    38 {
    39     Engine.PushGuiPage(
    40         "page_colormixer.xml",
    41         color,
    42         result => {
    43             callback(result);
    44         }
    45     );
     33    if (btnCode !== undefined && btnCode[button])
     34        btnCode[button](callbackArgs ? callbackArgs[button] : undefined);
    4635}
    4736
  • ps/trunk/binaries/data/mods/mod/gui/common/terms.js

    r27195 r28145  
    66}
    77
    8 function openTerms(page)
     8function openTerms(page)
    99{
    10     Engine.PushGuiPage(
     10    Engine.PushGuiPage(
    1111        "page_termsdialog.xml",
    1212        {
     
    1717            "termsURL": g_Terms[page].termsURL || undefined,
    1818            "page": page
    19         },
    20         data => {
    21             g_Terms[data.page].accepted = data.accepted;
     19        });
    2220
    23             Engine.ConfigDB_CreateAndSaveValue(
    24                 "user",
    25                 g_Terms[data.page].config,
    26                 data.accepted ? getTermsHash(data.page) : "0");
     21    g_Terms[data.page].accepted = data.accepted;
    2722
    28             if (g_Terms[data.page].callback)
    29                 g_Terms[data.page].callback(data);
    30         }
    31     );
     23    Engine.ConfigDB_CreateAndSaveValue(
     24        "user",
     25        g_Terms[data.page].config,
     26        data.accepted ? getTermsHash(data.page) : "0");
     27
     28    if (g_Terms[data.page].callback)
     29        g_Terms[data.page].callback(data);
    3230}
    3331
  • ps/trunk/binaries/data/mods/mod/gui/modmod/modmodio.js

    r22676 r28145  
    2424}
    2525
    26 function openModIo(data)
     26function openModIo(data)
    2727{
    28     if (data.accepted)
    29         Engine.PushGuiPage("page_modio.xml", {}, initMods);
     28    if (!data.accepted)
     29        return;
     30
     31    await Engine.PushGuiPage("page_modio.xml");
     32    initMods();
    3033}
  • ps/trunk/binaries/data/mods/public/gui/campaigns/default_menu/CampaignMenu.js

    r27867 r28145  
    2020        Engine.GetGUIObjectByName('startButton').onPress = () => this.startScenario();
    2121        Engine.GetGUIObjectByName('backToMain').onPress = () => this.goBackToMainMenu();
    22         Engine.GetGUIObjectByName('savedGamesButton').onPress = Engine.PushGuiPage.bind(Engine,
    23             'page_loadgame.xml', { "campaignRun": this.run.filename }, this.loadSavegame.bind(this));
     22        Engine.GetGUIObjectByName('savedGamesButton').onPress = this.loadSavegame.bind(this);
    2423
    2524        this.mapCache = new MapCache();
     
    2827    }
    2928
    30     loadSavegame(gameId)
    31     {
     29    async loadSavegame()
     30    {
     31        const gameId = await Engine.PushGuiPage(
     32            'page_loadgame.xml',
     33            {
     34                "campaignRun": this.run.filename
     35            });
     36
    3237        if (!gameId)
    3338            return;
  • ps/trunk/binaries/data/mods/public/gui/common/functions_utility.js

    r28085 r28145  
    289289 *  That continues untill there is no @a nextPage property in the completion
    290290 *  value. If there is no @a nextPage in the completion value the
    291  *  @a continuation is called with the completion value.
     291 *  @a co.
    292292 * @param {String} page - The page first opened.
    293293 * @param args - passed to the first page opened.
    294  * @param continuation {function | undefined} - Completion callback, called when
    295  *  there is no @a nextPage property in the completion value.
    296  */
    297 function pageLoop(page, args, continuation)
    298 {
    299     (function recursiveFunction(completionValue)
    300         {
    301             if (completionValue?.nextPage != null)
    302                 Engine.PushGuiPage(completionValue.nextPage, completionValue.args, recursiveFunction);
    303             else
    304                 continuation?.(completionValue);
    305         })({ "nextPage": page, "args": args });
    306 }
     294 */
     295async function pageLoop(page, args)
     296{
     297    let completionValue = { "nextPage": page, "args": args };
     298    while (completionValue?.nextPage != null)
     299        completionValue = await Engine.PushGuiPage(completionValue.nextPage, completionValue.args);
     300
     301    return completionValue;
     302}
  • ps/trunk/binaries/data/mods/public/gui/gamesetup/Pages/GameSetupPage/Panels/Buttons/CivInfoButton.js

    r28085 r28145  
    2424    }
    2525
    26     openPage(page)
     26    openPage(page)
    2727    {
    28         pageLoop(page, this.civInfo.args, data => this.civInfo = data);
     28        );
    2929    }
    3030}
  • ps/trunk/binaries/data/mods/public/gui/locale/locale.js

    r22966 r28145  
    5353}
    5454
    55 function openAdvancedMenu()
     55function openAdvancedMenu()
    5656{
    5757    let localeText = Engine.GetGUIObjectByName("localeText");
    58     Engine.PushGuiPage("page_locale_advanced.xml", { "locale": localeText.caption }, applyFromAdvancedMenu);
    59 }
     58    const locale = await Engine.PushGuiPage("page_locale_advanced.xml", { "locale": localeText.caption });
    6059
    61 function applyFromAdvancedMenu(locale)
    62 {
    6360    if (!locale)
    6461        return;
     
    7370        languageList.selected = index;
    7471
    75     var localeText = Engine.GetGUIObjectByName("localeText");
    7672    localeText.caption = locale;
    7773}
  • ps/trunk/binaries/data/mods/public/gui/options/options.js

    r28010 r28145  
    8181        },
    8282        "initGUI": (option, control) => {
    83             control.children[2].onPress = () => {
    84                 colorMixer(
    85                     control.caption,
    86                     (color) => {
    87                         if (color != control.caption)
    88                         {
    89                             control.caption = color;
    90                             control.onTextEdit();
    91                         }
    92                     }
    93                 );
     83            control.children[2].onPress = async() => {
     84                const color = await Engine.PushGuiPage("page_colormixer.xml", control.caption);
     85
     86                if (color != control.caption)
     87                {
     88                    control.caption = color;
     89                    control.onTextEdit();
     90                }
    9491            };
    9592        },
  • ps/trunk/binaries/data/mods/public/gui/pregame/MainMenuItems.js

    r28085 r28145  
    9191                "caption": translate("Load Game"),
    9292                "tooltip": translate("Load a saved game."),
    93                 "onPress": Engine.PushGuiPage.bind(Engine, "page_loadgame.xml", {}, (gameId) =>
     93                "onPress": async() => {
     94                    const gameId = await Engine.PushGuiPage("page_loadgame.xml");
     95
     96                    if (!gameId)
     97                        return;
     98
     99                    const metadata = Engine.StartSavedGame(gameId);
     100                    if (!metadata)
    94101                    {
    95                         if (!gameId)
    96                             return;
     102                        error("Could not load saved game: " + gameId);
     103                        return;
     104                    }
    97105
    98                         const metadata = Engine.StartSavedGame(gameId);
    99                         if (!metadata)
    100                         {
    101                             error("Could not load saved game: " + gameId);
    102                             return;
    103                         }
    104 
    105                         Engine.SwitchGuiPage("page_loading.xml", {
    106                             "attribs": metadata.initAttributes,
    107                             "playerAssignments": {
    108                                 "local": {
    109                                     "name": metadata.initAttributes.settings.
    110                                         PlayerData[metadata.playerID]?.Name ??
    111                                         singleplayerName(),
    112                                     "player": metadata.playerID
    113                                 }
    114                             },
    115                             "savedGUIData": metadata.gui
    116                         });
    117                     })
     106                    Engine.SwitchGuiPage("page_loading.xml", {
     107                        "attribs": metadata.initAttributes,
     108                        "playerAssignments": {
     109                            "local": {
     110                                "name": metadata.initAttributes.settings.
     111                                    PlayerData[metadata.playerID]?.Name ??
     112                                    singleplayerName(),
     113                                "player": metadata.playerID
     114                            }
     115                        },
     116                        "savedGUIData": metadata.gui
     117                    });
     118                }
    118119            },
    119120            {
     
    222223                "caption": translate("Options"),
    223224                "tooltip": translate("Adjust game settings."),
    224                 "onPress": () => {
    225                     Engine.PushGuiPage(
    226                         "page_options.xml",
    227                         {},
    228                         fireConfigChangeHandlers);
     225                "onPress": async() => {
     226                    fireConfigChangeHandlers(await Engine.PushGuiPage("page_options.xml"));
    229227                }
    230228            },
  • ps/trunk/binaries/data/mods/public/gui/session/MenuButtons.js

    r25083 r28145  
    1717    }
    1818
    19     onPress()
    20     {
    21         closeOpenDialogs();
    22         this.pauseControl.implicitPause();
    23         Engine.PushGuiPage("page_manual.xml", {}, resumeGame);
     19    async onPress()
     20    {
     21        closeOpenDialogs();
     22        this.pauseControl.implicitPause();
     23        await Engine.PushGuiPage("page_manual.xml");
     24        resumeGame();
    2425    }
    2526};
     
    5556    }
    5657
    57     onPress()
    58     {
    59         closeOpenDialogs();
    60         this.pauseControl.implicitPause();
    61 
    62         Engine.PushGuiPage(
     58    onPress()
     59    {
     60        closeOpenDialogs();
     61        this.pauseControl.implicitPause();
     62
     63        Engine.PushGuiPage(
    6364            "page_loadgame.xml",
    6465            {
    6566                "savedGameData": getSavedGameData(),
    6667                "campaignRun": g_CampaignSession ? g_CampaignSession.run.filename : null
    67             },
    68             resumeGame);
     68            }
     69        );
    6970    }
    7071};
     
    9394    }
    9495
    95     onPress()
     96    onPress()
    9697    {
    9798        if (Engine.IsAtlasRunning())
     
    103104        // If they have shared ally vision researched, they are able to see the summary of there allies too.
    104105        let simState = Engine.GuiInterfaceCall("GetExtendedSimulationState");
    105         Engine.PushGuiPage(
     106        Engine.PushGuiPage(
    106107            "page_summary.xml",
    107108            {
     
    118119                    "summarySelection": this.summarySelection
    119120                },
    120             },
    121             data => {
    122                 this.summarySelection = data.summarySelection;
    123                 this.pauseControl.implicitResume();
    124121            });
     122
     123
     124
    125125    }
    126126};
     
    163163    }
    164164
    165     onPress()
    166     {
    167         closeOpenDialogs();
    168         this.pauseControl.implicitPause();
    169 
    170         Engine.PushGuiPage(
    171             "page_options.xml",
    172             {},
    173             changes => {
    174                 fireConfigChangeHandlers(changes);
    175                 resumeGame();
    176             });
     165    async onPress()
     166    {
     167        closeOpenDialogs();
     168        this.pauseControl.implicitPause();
     169
     170        fireConfigChangeHandlers(await Engine.PushGuiPage("page_options.xml"));
     171        resumeGame();
    177172    }
    178173};
     
    187182    }
    188183
    189     onPress()
    190     {
    191         closeOpenDialogs();
    192         this.pauseControl.implicitPause();
    193 
    194         Engine.PushGuiPage(
    195             "hotkeys/page_hotkeys.xml",
    196             {},
    197             () => { resumeGame(); });
     184    async onPress()
     185    {
     186        closeOpenDialogs();
     187        this.pauseControl.implicitPause();
     188
     189        await Engine.PushGuiPage("hotkeys/page_hotkeys.xml");
     190        resumeGame();
    198191    }
    199192};
  • ps/trunk/binaries/data/mods/public/gui/session/SessionMessageBox.js

    r27883 r28145  
    55class SessionMessageBox
    66{
    7     display()
     7    display()
    88    {
    9         this.onPageOpening();
     9        closeOpenDialogs();
     10        g_PauseControl.implicitPause();
    1011
    11         Engine.PushGuiPage(
     12        Engine.PushGuiPage(
    1213            "page_msgbox.xml",
    1314            {
     
    1718                "message": this.Caption,
    1819                "buttonCaptions": this.Buttons ? this.Buttons.map(button => button.caption) : undefined,
    19             },
    20             this.onPageClosed.bind(this));
    21     }
     20            });
    2221
    23     onPageOpening()
    24     {
    25         closeOpenDialogs();
    26         g_PauseControl.implicitPause();
    27     }
    28 
    29     onPageClosed(buttonId)
    30     {
    3122        if (this.Buttons && this.Buttons[buttonId].onPress)
    3223            this.Buttons[buttonId].onPress.call(this);
  • ps/trunk/binaries/data/mods/public/gui/session/selection_panels.js

    r27886 r28145  
    12631263 * @param {string} [civCode] - The template name of the entity that researches the selected technology.
    12641264 */
    1265 function showTemplateDetails(templateName, civCode)
     1265function showTemplateDetails(templateName, civCode)
    12661266{
    12671267    if (inputState != INPUT_NORMAL)
     
    12691269    g_PauseControl.implicitPause();
    12701270
    1271     Engine.PushGuiPage(
     1271    Engine.PushGuiPage(
    12721272        "page_viewer.xml",
    12731273        {
    12741274            "templateName": templateName,
    12751275            "civ": civCode
    1276         },
    1277         resumeGame);
     1276        }
     1277    );
    12781278}
    12791279
  • ps/trunk/binaries/data/mods/public/gui/session/top_panel/CivIcon.js

    r28085 r28145  
    3030    }
    3131
    32     openPage(page)
     32    openPage(page)
    3333    {
    3434        closeOpenDialogs();
    3535        g_PauseControl.implicitPause();
    3636
    37         pageLoop(
     37        pageLoop(
    3838            page,
    3939            {
     
    4343
    4444                // TODO add info about researched techs and unlocked entities
    45             },
    46             data =>
    47             {
    48                 this.dialogSelection = data;
    49                 resumeGame();
    5045            });
     46
    5147    }
    5248
  • ps/trunk/source/gui/GUIManager.cpp

    r28131 r28145  
    3131#include "scriptinterface/FunctionWrapper.h"
    3232#include "scriptinterface/Object.h"
     33
    3334#include "scriptinterface/ScriptContext.h"
    3435#include "scriptinterface/ScriptInterface.h"
     
    113114    }
    114115
    115     PushPage(pageName, initDataClone, JS::UndefinedHandleValue);
    116 }
    117 
    118 void CGUIManager::PushPage(const CStrW& pageName, Script::StructuredClone initData, JS::HandleValue callbackFunction)
     116    PushPage(pageName, initDataClone);
     117}
     118
     119)
    119120{
    120121    // Store the callback handler in the current GUI page before opening the new one
    121     if (!m_PageStack.empty() && !callbackFunction.isUndefined())
    122     {
    123         m_PageStack.back().SetCallbackFunction(m_ScriptInterface, callbackFunction);
    124 
    125         // Make sure we unfocus anything on the current page.
    126         m_PageStack.back().gui->SendFocusMessage(GUIM_LOST_FOCUS);
    127     }
     122    JS::RootedValue promise{m_ScriptInterface.GetGeneralJSContext(), [&]
     123        {
     124            if (m_PageStack.empty())
     125                return JS::UndefinedValue();
     126
     127            // Make sure we unfocus anything on the current page.
     128            m_PageStack.back().gui->SendFocusMessage(GUIM_LOST_FOCUS);
     129            return m_PageStack.back().ReplacePromise(m_ScriptInterface);
     130        }()};
    128131
    129132    // Push the page prior to loading its contents, because that may push
     
    131134    m_PageStack.emplace_back(pageName, initData);
    132135    m_PageStack.back().LoadPage(m_ScriptContext);
     136
     137
    133138}
    134139
     
    145150
    146151    m_PageStack.pop_back();
    147     m_PageStack.back().PerformCallbackFunction(args);
     152    m_PageStack.back().(args);
    148153
    149154    // We return to a page where some object might have been focused.
     
    246251}
    247252
    248 void CGUIManager::SGUIPage::SetCallbackFunction(ScriptInterface& scriptInterface, JS::HandleValue callbackFunc)
    249 {
    250     if (!callbackFunc.isObject())
    251     {
    252         LOGERROR("Given callback handler is not an object!");
    253         return;
    254     }
    255 
    256     ScriptRequest rq(scriptInterface);
    257 
    258     if (!JS_ObjectIsFunction(&callbackFunc.toObject()))
    259     {
    260         LOGERROR("Given callback handler is not a function!");
    261         return;
    262     }
    263 
    264     callbackFunction = std::make_shared<JS::PersistentRootedValue>(scriptInterface.GetGeneralJSContext(), callbackFunc);
    265 }
    266 
    267 void CGUIManager::SGUIPage::PerformCallbackFunction(Script::StructuredClone args)
     253JS::Value CGUIManager::SGUIPage::ReplacePromise(ScriptInterface& scriptInterface)
     254{
     255    JSContext* generalContext{scriptInterface.GetGeneralJSContext()};
     256    callbackFunction = std::make_shared<JS::PersistentRootedObject>(generalContext,
     257        JS::NewPromiseObject(generalContext, nullptr));
     258    return JS::ObjectValue(**callbackFunction);
     259}
     260
     261void CGUIManager::SGUIPage::ResolvePromise(Script::StructuredClone args)
    268262{
    269263    if (!callbackFunction)
     
    275269    JS::RootedObject globalObj(rq.cx, rq.glob);
    276270
    277     JS::RootedValue funcVal(rq.cx, *callbackFunction);
     271    JS::Rooted funcVal(rq.cx, *callbackFunction);
    278272
    279273    // Delete the callback function, so that it is not called again
     
    284278        Script::ReadStructuredClone(rq, args, &argVal);
    285279
    286     JS::RootedValueVector paramData(rq.cx);
    287     ignore_result(paramData.append(argVal));
    288 
    289     JS::RootedValue result(rq.cx);
    290 
    291     if(!JS_CallFunctionValue(rq.cx, globalObj, funcVal, paramData, &result))
    292         ScriptException::CatchPending(rq);
     280    // This only resolves the promise, it doesn't call the continuation.
     281    JS::ResolvePromise(rq.cx, funcVal, argVal);
    293282}
    294283
     
    389378    for (const SGUIPage& p : pageStack)
    390379        p.gui->TickObjects();
     380
     381
    391382}
    392383
  • ps/trunk/source/gui/GUIManager.h

    r28131 r28145  
    6969     * and will still be drawn and receive tick events, but will not receive
    7070     * user inputs.
    71      * If given, the callbackHandler function will be executed once this page is closed.
    72      */
    73     void PushPage(const CStrW& pageName, Script::StructuredClone initData, JS::HandleValue callbackFunc);
     71     * page is closed.
     72     */
     73    );
    7474
    7575    /**
     
    147147
    148148        /**
    149          * Sets the callback handler when a new page is opened that will be performed when the page is closed.
    150          */
    151         void SetCallbackFunction(ScriptInterface& scriptInterface, JS::HandleValue callbackFunc);
     149         * A new promise gets set. A reference to that promise is returned. The promise will settle when
     150         * the page is closed.
     151         */
     152        JS::Value ReplacePromise(ScriptInterface& scriptInterface);
    152153
    153154        /**
    154155         * Execute the stored callback function with the given arguments.
    155156         */
    156         void PerformCallbackFunction(Script::StructuredClone args);
    157 
    158         CStrW m_Name;
     157        void (Script::StructuredClone args);
     158
     159        m_Name;
    159160        std::unordered_set<VfsPath> inputs; // for hotloading
    160161        Script::StructuredClone initData; // data to be passed to the init() function
     
    165166         * Notice that storing it in the SGUIPage instead of CGUI means that it will survive the hotloading CGUI reset.
    166167         */
    167         std::shared_ptr<JS::PersistentRootedValue> callbackFunction;
     168        std::shared_ptr<JS::PersistentRooted> callbackFunction;
    168169    };
    169170
     
    177178     * may crash (as the pusher page will suddenly have moved, and the stack will be confused).
    178179     * Therefore use std::deque over std::vector.
    179      */
    180     using PageStackType = std::deque<SGUIPage>;
     180     * Also the elements have to be destructed back to front.
     181     */
     182    class PageStackType : public std::deque<SGUIPage>
     183    {
     184    public:
     185        ~PageStackType()
     186        {
     187            clear();
     188        }
     189
     190        void clear()
     191        {
     192            while (!std::deque<SGUIPage>::empty())
     193                std::deque<SGUIPage>::pop_back();
     194        }
     195    };
    181196    PageStackType m_PageStack;
    182197
  • ps/trunk/source/gui/Scripting/JSInterface_GUIManager.cpp

    r27965 r28145  
    1 /* Copyright (C) 2021 Wildfire Games.
     1/* Copyright (C) 202 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
     
    3333// Note that the initData argument may only contain clonable data.
    3434// Functions aren't supported for example!
    35 void PushGuiPage(const ScriptRequest& rq, const std::wstring& name, JS::HandleValue initData, JS::HandleValue callbackFunction)
     35// It returns a promise.
     36JS::Value PushGuiPage(const ScriptRequest& rq, const std::wstring& name, JS::HandleValue initData)
    3637{
    37     g_GUI->PushPage(name, Script::WriteStructuredClone(rq, initData), callbackFunction);
     38    );
    3839}
    3940
  • ps/trunk/source/gui/tests/test_GuiManager.h

    r28131 r28145  
    2727#include "ps/Hotkey.h"
    2828#include "ps/XML/Xeromyces.h"
     29
    2930#include "scriptinterface/ScriptRequest.h"
    3031#include "scriptinterface/ScriptInterface.h"
     
    7374
    7475        Script::StructuredClone data = Script::WriteStructuredClone(rq, JS::NullHandleValue);
    75         g_GUI->PushPage(L"event/page_event.xml", data, JS::UndefinedHandleValue);
     76        g_GUI->PushPage(L"event/page_event.xml", data);
    7677
    7778        const ScriptInterface& pageScriptInterface = *(g_GUI->GetActiveGUI()->GetScriptInterface());
     
    136137
    137138        Script::StructuredClone data = Script::WriteStructuredClone(rq, JS::NullHandleValue);
    138         g_GUI->PushPage(L"hotkey/page_hotkey.xml", data, JS::UndefinedHandleValue);
     139        g_GUI->PushPage(L"hotkey/page_hotkey.xml", data);
    139140
    140141        // Press 'a'.
     
    209210        Script::CreateObject(rq, &val);
    210211
     212
    211213        Script::StructuredClone data = Script::WriteStructuredClone(rq, JS::NullHandleValue);
    212         g_GUI->PushPage(L"regainFocus/page_emptyPage.xml", data, JS::UndefinedHandleValue);
     214        g_GUI->PushPage(L"regainFocus/page_emptyPage.xml", data);
    213215
    214216        const ScriptInterface& pageScriptInterface = *(g_GUI->GetActiveGUI()->GetScriptInterface());
     
    216218        JS::RootedValue global(prq.cx, prq.globalValue());
    217219
    218         g_GUI->PushPage(L"regainFocus/page_emptyPage.xml", data, JS::UndefinedHandleValue);
     220        TS_ASSERT_EQUALS(g_GUI->GetPageCount(), 1);
     221        g_GUI->PushPage(L"regainFocus/page_emptyPage.xml", data);
     222        TS_ASSERT_EQUALS(g_GUI->GetPageCount(), 2);
    219223        g_GUI->PopPage(data);
     224
    220225
    221226        // This page instantly pushes an empty page with a callback that pops another page again.
    222         g_GUI->PushPage(L"regainFocus/page_pushWithPopOnInit.xml", data, JS::UndefinedHandleValue);
    223 
    224         // Pop the empty page and trigger the callback (effectively pops twice).
     227        g_GUI->PushPage(L"regainFocus/page_pushWithPopOnInit.xml", data);
     228        TS_ASSERT_EQUALS(g_GUI->GetPageCount(), 3);
     229
     230        // Pop the empty page
    225231        g_GUI->PopPage(data);
     232
     233
     234
    226235    }
    227236};
  • ps/trunk/source/scriptinterface/ScriptContext.cpp

    r27965 r28145  
    1 /* Copyright (C) 2023 Wildfire Games.
     1/* Copyright (C) 202 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
     
    2323#include "ps/GameSetup/Config.h"
    2424#include "ps/Profile.h"
     25
    2526#include "scriptinterface/ScriptExtraHeaders.h"
    2627#include "scriptinterface/ScriptEngine.h"
     
    8485
    8586ScriptContext::ScriptContext(int contextSize, int heapGrowthBytesGCTrigger):
    86     m_LastGCBytes(0),
    87     m_LastGCCheck(0.0f),
    88     m_HeapGrowthBytesGCTrigger(heapGrowthBytesGCTrigger),
    89     m_ContextSize(contextSize)
     87    m_JobQueue{std::make_unique<Script::JobQueue>()},
     88    m_ContextSize{contextSize},
     89    m_HeapGrowthBytesGCTrigger{heapGrowthBytesGCTrigger}
    9090{
    9191    ENSURE(ScriptEngine::IsInitialised() && "The ScriptEngine must be initialized before constructing any ScriptContexts!");
     
    131131
    132132    ScriptEngine::GetSingleton().RegisterContext(m_cx);
     133
     134
    133135}
    134136
     
    269271}
    270272
     273
     274
     275
     276
     277
    271278void ScriptContext::PrepareZonesForIncrementalGC() const
    272279{
  • ps/trunk/source/scriptinterface/ScriptContext.h

    r27965 r28145  
    1 /* Copyright (C) 2023 Wildfire Games.
     1/* Copyright (C) 202 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
     
    2727constexpr int DEFAULT_CONTEXT_SIZE = 16 * 1024 * 1024;
    2828constexpr int DEFAULT_HEAP_GROWTH_BYTES_GCTRIGGER = 2 * 1024 * 1024;
     29
     30
     31
     32
     33
    2934
    3035/**
     
    7681
    7782    /**
     83
     84
     85
     86
     87
     88
     89
     90
    7891     * GetGeneralJSContext returns the context without starting a GC request and without
    7992     * entering any compartment. It should only be used in specific situations, such as
     
    87100
    88101    JSContext* m_cx;
     102
    89103
    90104    void PrepareZonesForIncrementalGC() const;
     
    93107    int m_ContextSize;
    94108    int m_HeapGrowthBytesGCTrigger;
    95     int m_LastGCBytes;
    96     double m_LastGCCheck;
     109    int m_LastGCBytes;
     110    double m_LastGCCheck;
    97111};
    98112
  • ps/trunk/source/scriptinterface/ScriptInterface.cpp

    r27965 r28145  
    1 /* Copyright (C) 2023 Wildfire Games.
     1/* Copyright (C) 202 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
     
    385385ScriptInterface::~ScriptInterface()
    386386{
     387
    387388    if (Threading::IsMainThread())
    388389    {
  • ps/trunk/source/simulation2/Simulation2.cpp

    r28138 r28145  
    586586        cmpPathfinder->StartProcessingMoves(false);
    587587    }
     588
     589
    588590}
    589591
Note: See TracChangeset for help on using the changeset viewer.