1
0
forked from 0ad/0ad

Support assigning functions to hotkeys from the JS GUI without involving a GUI object.

Allows developers and modders to register and unregister hotkeys without
having to specify, change or overwrite XML files.
Also allows implementing the main menu GUI page with only GUI objects
for one submenu, refs #5387 / D2240.

Differential Revision: https://code.wildfiregames.com/D2260
Idea by nani in D2257.

This was SVN commit r22851.
This commit is contained in:
elexis 2019-09-05 10:42:16 +00:00
parent eb87783f22
commit f192d4a2fa
6 changed files with 86 additions and 13 deletions

View File

@ -1188,6 +1188,18 @@ var g_PlayerMiscElements = {
},
};
var g_Hotkeys = {
"civinfo": () => {
Engine.PushGuiPage("page_civinfo.xml", { "civ": g_CivInfo.code }, storeCivInfoPage);
},
"structree": () => {
Engine.PushGuiPage("page_structree.xml", { "civ": g_CivInfo.civ }, storeCivInfoPage);
},
"cancel": () => {
selectPanel(undefined);
}
};
/**
* Initializes some globals without touching the GUI.
*
@ -1273,6 +1285,7 @@ function initGUIObjects()
{
initSettingObjects();
initSettingsTabButtons();
initHotkeys();
initSPTips();
loadPersistMatchSettings();
@ -1472,6 +1485,12 @@ function initCheckbox(name)
};
}
function initHotkeys()
{
for (let hotkeyName in g_Hotkeys)
Engine.SetGlobalHotkey(hotkeyName, g_Hotkeys[hotkeyName]);
}
function initSettingsTabButtons()
{
for (let tab in g_SettingsTabsGUI)

View File

@ -5,18 +5,6 @@
<script directory="gui/common/"/>
<script directory="gui/gamesetup/"/>
<object hotkey="civinfo">
<action on="Press">Engine.PushGuiPage("page_civinfo.xml", { "civ": g_CivInfo.civ }, storeCivInfoPage);</action>
</object>
<object hotkey="structree">
<action on="Press">Engine.PushGuiPage("page_structree.xml", { "civ": g_CivInfo.civ }, storeCivInfoPage);</action>
</object>
<object hotkey="cancel">
<action on="Press">selectPanel(undefined);</action>
</object>
<!-- Add a translucent black background to fade out the menu page -->
<object type="image" style="ModernWindow" size="0 0 100% 100%">

View File

@ -71,6 +71,18 @@ InReaction CGUI::HandleEvent(const SDL_Event_* ev)
{
const char* hotkey = static_cast<const char*>(ev->ev.user.data1);
if (m_GlobalHotkeys.count(hotkey))
{
HotkeyInputHandler(ev);
ret = IN_HANDLED;
JSContext* cx = m_ScriptInterface->GetContext();
JSAutoRequest rq(cx);
JS::RootedObject globalObj(cx, &GetGlobalObject().toObject());
JS::RootedValue result(cx);
JS_CallFunctionValue(cx, globalObj, m_GlobalHotkeys[hotkey], JS::HandleValueArray::empty(), &result);
}
std::map<CStr, std::vector<IGUIObject*> >::iterator it = m_HotkeyObjects.find(hotkey);
if (it != m_HotkeyObjects.end())
for (IGUIObject* const& obj : it->second)
@ -488,6 +500,32 @@ void CGUI::UnsetObjectHotkey(IGUIObject* pObject, const CStr& hotkeyTag)
assignment.end());
}
void CGUI::SetGlobalHotkey(const CStr& hotkeyTag, JS::HandleValue function)
{
JSContext* cx = m_ScriptInterface->GetContext();
JSAutoRequest rq(cx);
if (hotkeyTag.empty())
{
JS_ReportError(cx, "Cannot assign a function to an empty hotkey identifier!");
return;
}
if (!function.isObject() || !JS_ObjectIsFunction(cx, &function.toObject()))
{
JS_ReportError(cx, "Cannot assign non-function value to global hotkey '%s'", hotkeyTag.c_str());
return;
}
UnsetGlobalHotkey(hotkeyTag);
m_GlobalHotkeys[hotkeyTag].init(cx, function);
}
void CGUI::UnsetGlobalHotkey(const CStr& hotkeyTag)
{
m_GlobalHotkeys.erase(hotkeyTag);
}
const SGUIScrollBarStyle* CGUI::GetScrollBarStyle(const CStr& style) const
{
std::map<CStr, const SGUIScrollBarStyle>::const_iterator it = m_ScrollBarStyles.find(style);

View File

@ -153,6 +153,12 @@ public:
void SetObjectHotkey(IGUIObject* pObject, const CStr& hotkeyTag);
void UnsetObjectHotkey(IGUIObject* pObject, const CStr& hotkeyTag);
/**
* Allows the JS side to add or remove global hotkeys.
*/
void SetGlobalHotkey(const CStr& hotkeyTag, JS::HandleValue function);
void UnsetGlobalHotkey(const CStr& hotkeyTag);
/**
* Return the object which is an ancestor of every other GUI object.
*/
@ -634,6 +640,12 @@ private:
*/
std::map<CStr, std::vector<IGUIObject*> > m_HotkeyObjects;
/**
* Map from hotkey names to functions that are triggered if the hotkey is pressed.
* Contrary to object hotkeys, this allows for only one global function per hotkey name.
*/
std::map<CStr, JS::PersistentRootedValue> m_GlobalHotkeys;
//--------------------------------------------------------
// Databases
// These are loaded from XML files and marked as noncopyable and const to

View File

@ -61,6 +61,18 @@ JS::Value JSI_GUIManager::GetGUIObjectByName(ScriptInterface::CxPrivate* pCxPriv
return JS::ObjectValue(*guiObj->GetJSObject());
}
void JSI_GUIManager::SetGlobalHotkey(ScriptInterface::CxPrivate* pCxPrivate, const std::string& hotkeyTag, JS::HandleValue function)
{
CGUI* guiPage = static_cast<CGUI*>(pCxPrivate->pCBData);
guiPage->SetGlobalHotkey(hotkeyTag, function);
}
void JSI_GUIManager::UnsetGlobalHotkey(ScriptInterface::CxPrivate* pCxPrivate, const std::string& hotkeyTag)
{
CGUI* guiPage = static_cast<CGUI*>(pCxPrivate->pCBData);
guiPage->UnsetGlobalHotkey(hotkeyTag);
}
std::wstring JSI_GUIManager::SetCursor(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), const std::wstring& name)
{
std::wstring old = g_CursorName;
@ -87,6 +99,8 @@ void JSI_GUIManager::RegisterScriptFunctions(const ScriptInterface& scriptInterf
{
scriptInterface.RegisterFunction<void, std::wstring, JS::HandleValue, JS::HandleValue, &PushGuiPage>("PushGuiPage");
scriptInterface.RegisterFunction<void, std::wstring, JS::HandleValue, &SwitchGuiPage>("SwitchGuiPage");
scriptInterface.RegisterFunction<void, std::string, JS::HandleValue, &SetGlobalHotkey>("SetGlobalHotkey");
scriptInterface.RegisterFunction<void, std::string, &UnsetGlobalHotkey>("UnsetGlobalHotkey");
scriptInterface.RegisterFunction<void, JS::HandleValue, &PopGuiPage>("PopGuiPage");
scriptInterface.RegisterFunction<JS::Value, std::string, &GetGUIObjectByName>("GetGUIObjectByName");
scriptInterface.RegisterFunction<std::wstring, std::wstring, &SetCursor>("SetCursor");

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2018 Wildfire Games.
/* 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
@ -27,6 +27,8 @@ namespace JSI_GUIManager
void SwitchGuiPage(ScriptInterface::CxPrivate* pCxPrivate, const std::wstring& name, JS::HandleValue initData);
void PopGuiPage(ScriptInterface::CxPrivate* pCxPrivate, JS::HandleValue args);
JS::Value GetGUIObjectByName(ScriptInterface::CxPrivate* pCxPrivate, const std::string& name);
void SetGlobalHotkey(ScriptInterface::CxPrivate* pCxPrivate, const std::string& hotkeyTag, JS::HandleValue function);
void UnsetGlobalHotkey(ScriptInterface::CxPrivate* pCxPrivate, const std::string& hotkeyTag);
std::wstring SetCursor(ScriptInterface::CxPrivate* pCxPrivate, const std::wstring& name);
void ResetCursor(ScriptInterface::CxPrivate* pCxPrivate);
bool TemplateExists(ScriptInterface::CxPrivate* pCxPrivate, const std::string& templateName);