Remove counterproductive lowercasing of GUI ScriptEvent names, refs #127.

a8f48ff7e0 introduced XeroXMB lowercasing of element and attribute names
as a feature.
cf9d8b9797, 4d390f501c, dda6268466 added bugfixes and TODOs because of
that.
f76d0ffdc6, 44fe226dd2 removed the XeroXMB lowercase feature.
This patch removes the lowercasing GUI bugfixes that don't fix any bug
anymore while increasing code complexity and lowering performance
(string copies).

Do not send mouse coordinates objects for events that do not relate to
the mouse.

Store event names in static const members to:
(1) improve performance, ensuring that the CStr is not reconstructed
every call,
(2) obtain compile errors when misspelling event names,
(3) allow reuse of the strings in inherited and friend classes.

Differential Revision: https://code.wildfiregames.com/D2445
Comments by Matei, Philip on 2006-03-11-QuakeNet-#wfg-Meeting-0126.log
and 2006-06-24-QuakeNet-#wfg-Meeting-0139.log

This was SVN commit r23403.
This commit is contained in:
elexis 2020-01-15 16:00:37 +00:00
parent fedcc2f0f1
commit eafd44cfc5
29 changed files with 226 additions and 137 deletions

View File

@ -62,7 +62,7 @@
<action on="SelectionChange">displayReplayDetails();</action>
<action on="SelectionColumnChange">displayReplayList();</action>
<action on="mouseleftdoubleclickitem">startReplay();</action>
<action on="MouseLeftDoubleClickItem">startReplay();</action>
<!-- Columns -->
<column id="months" color="172 172 212" width="12%">

View File

@ -688,9 +688,9 @@ function updateGroups()
let button = Engine.GetGUIObjectByName("unitGroupButton[" + i + "]");
button.hidden = g_Groups.groups[i].getTotalCount() == 0;
button.onpress = (function(i) { return function() { performGroup((Engine.HotkeyIsPressed("selection.add") ? "add" : "select"), i); }; })(i);
button.ondoublepress = (function(i) { return function() { performGroup("snap", i); }; })(i);
button.onpressright = (function(i) { return function() { performGroup("breakUp", i); }; })(i);
button.onPress = (function(i) { return function() { performGroup((Engine.HotkeyIsPressed("selection.add") ? "add" : "select"), i); }; })(i);
button.onDoublePress = (function(i) { return function() { performGroup("snap", i); }; })(i);
button.onPressRight = (function(i) { return function() { performGroup("breakUp", i); }; })(i);
// Choose the icon of the most common template (or the most costly if it's not unique)
if (g_Groups.groups[i].getTotalCount() > 0)

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2019 Wildfire Games.
/* Copyright (C) 2020 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -47,6 +47,19 @@ extern int g_yres;
const double SELECT_DBLCLICK_RATE = 0.5;
const u32 MAX_OBJECT_DEPTH = 100; // Max number of nesting for GUI includes. Used to detect recursive inclusion
const CStr CGUI::EventNameLoad = "Load";
const CStr CGUI::EventNameTick = "Tick";
const CStr CGUI::EventNamePress = "Press";
const CStr CGUI::EventNameRelease = "Release";
const CStr CGUI::EventNameMouseRightPress = "MouseRightPress";
const CStr CGUI::EventNameMouseLeftPress = "MouseLeftPress";
const CStr CGUI::EventNameMouseWheelDown = "MouseWheelDown";
const CStr CGUI::EventNameMouseWheelUp = "MouseWheelUp";
const CStr CGUI::EventNameMouseLeftDoubleClick = "MouseLeftDoubleClick";
const CStr CGUI::EventNameMouseLeftRelease = "MouseLeftRelease";
const CStr CGUI::EventNameMouseRightDoubleClick = "MouseRightDoubleClick";
const CStr CGUI::EventNameMouseRightRelease = "MouseRightRelease";
CGUI::CGUI(const shared_ptr<ScriptRuntime>& runtime)
: m_BaseObject(*this),
m_FocusedObject(nullptr),
@ -93,9 +106,9 @@ InReaction CGUI::HandleEvent(const SDL_Event_* ev)
for (IGUIObject* const& obj : it->second)
{
if (ev->ev.type == SDL_HOTKEYDOWN)
ret = obj->SendEvent(GUIM_PRESSED, "press");
ret = obj->SendEvent(GUIM_PRESSED, EventNamePress);
else
ret = obj->SendEvent(GUIM_RELEASED, "release");
ret = obj->SendEvent(GUIM_RELEASED, EventNameRelease);
}
}
@ -158,12 +171,12 @@ InReaction CGUI::HandleEvent(const SDL_Event_* ev)
SetFocusedObject(pNearest);
if (pNearest)
ret = pNearest->SendEvent(GUIM_MOUSE_PRESS_LEFT, "mouseleftpress");
ret = pNearest->SendMouseEvent(GUIM_MOUSE_PRESS_LEFT, EventNameMouseLeftPress);
break;
case SDL_BUTTON_RIGHT:
if (pNearest)
ret = pNearest->SendEvent(GUIM_MOUSE_PRESS_RIGHT, "mouserightpress");
ret = pNearest->SendMouseEvent(GUIM_MOUSE_PRESS_RIGHT, EventNameMouseRightPress);
break;
default:
@ -173,9 +186,9 @@ InReaction CGUI::HandleEvent(const SDL_Event_* ev)
else if (ev->ev.type == SDL_MOUSEWHEEL && pNearest)
{
if (ev->ev.wheel.y < 0)
ret = pNearest->SendEvent(GUIM_MOUSE_WHEEL_DOWN, "mousewheeldown");
ret = pNearest->SendMouseEvent(GUIM_MOUSE_WHEEL_DOWN, EventNameMouseWheelDown);
else if (ev->ev.wheel.y > 0)
ret = pNearest->SendEvent(GUIM_MOUSE_WHEEL_UP, "mousewheelup");
ret = pNearest->SendMouseEvent(GUIM_MOUSE_WHEEL_UP, EventNameMouseWheelUp);
}
else if (ev->ev.type == SDL_MOUSEBUTTONUP)
{
@ -186,11 +199,10 @@ InReaction CGUI::HandleEvent(const SDL_Event_* ev)
{
double timeElapsed = timer_Time() - pNearest->m_LastClickTime[SDL_BUTTON_LEFT];
pNearest->m_LastClickTime[SDL_BUTTON_LEFT] = timer_Time();
if (timeElapsed < SELECT_DBLCLICK_RATE)
ret = pNearest->SendEvent(GUIM_MOUSE_DBLCLICK_LEFT, "mouseleftdoubleclick");
ret = pNearest->SendMouseEvent(GUIM_MOUSE_DBLCLICK_LEFT, EventNameMouseLeftDoubleClick);
else
ret = pNearest->SendEvent(GUIM_MOUSE_RELEASE_LEFT, "mouseleftrelease");
ret = pNearest->SendMouseEvent(GUIM_MOUSE_RELEASE_LEFT, EventNameMouseLeftRelease);
}
break;
case SDL_BUTTON_RIGHT:
@ -198,11 +210,10 @@ InReaction CGUI::HandleEvent(const SDL_Event_* ev)
{
double timeElapsed = timer_Time() - pNearest->m_LastClickTime[SDL_BUTTON_RIGHT];
pNearest->m_LastClickTime[SDL_BUTTON_RIGHT] = timer_Time();
if (timeElapsed < SELECT_DBLCLICK_RATE)
ret = pNearest->SendEvent(GUIM_MOUSE_DBLCLICK_RIGHT, "mouserightdoubleclick");
ret = pNearest->SendMouseEvent(GUIM_MOUSE_DBLCLICK_RIGHT, EventNameMouseRightDoubleClick);
else
ret = pNearest->SendEvent(GUIM_MOUSE_RELEASE_RIGHT, "mouserightrelease");
ret = pNearest->SendMouseEvent(GUIM_MOUSE_RELEASE_RIGHT, EventNameMouseRightRelease);
}
break;
}
@ -257,29 +268,18 @@ InReaction CGUI::HandleEvent(const SDL_Event_* ev)
void CGUI::TickObjects()
{
const CStr action = "tick";
m_BaseObject.RecurseObject(nullptr, &IGUIObject::ScriptEvent, action);
SendEventToAll(EventNameTick);
m_Tooltip.Update(FindObjectUnderMouse(), m_MousePos, *this);
}
void CGUI::SendEventToAll(const CStr& EventName)
void CGUI::SendEventToAll(const CStr& eventName)
{
// janwas 2006-03-03: spoke with Ykkrosh about EventName case.
// when registering, case is converted to lower - this avoids surprise
// if someone were to get the case wrong and then not notice their
// handler is never called. however, until now, the other end
// (sending events here) wasn't converting to lower case,
// leading to a similar problem.
// now fixed; case is irrelevant since all are converted to lower.
const CStr EventNameLower = EventName.LowerCase();
m_BaseObject.RecurseObject(nullptr, &IGUIObject::ScriptEvent, EventNameLower);
m_BaseObject.RecurseObject(nullptr, &IGUIObject::ScriptEvent, eventName);
}
void CGUI::SendEventToAll(const CStr& EventName, const JS::HandleValueArray& paramData)
void CGUI::SendEventToAll(const CStr& eventName, const JS::HandleValueArray& paramData)
{
const CStr EventNameLower = EventName.LowerCase();
m_BaseObject.RecurseObject(nullptr, &IGUIObject::ScriptEvent, EventNameLower, paramData);
m_BaseObject.RecurseObject(nullptr, &IGUIObject::ScriptEvent, eventName, paramData);
}
void CGUI::Draw()
@ -468,7 +468,7 @@ void CGUI::LoadedXmlFiles()
SGUIMessage msg(GUIM_LOAD);
m_BaseObject.RecurseObject(nullptr, &IGUIObject::HandleMessage, msg);
SendEventToAll("load");
SendEventToAll(EventNameLoad);
}
//===================================================================
@ -677,9 +677,8 @@ void CGUI::Xeromyces_ReadObject(XMBElement Element, CXeromyces* pFile, IGUIObjec
// Read the inline code (concatenating to the file code, if both are specified)
code += CStr(child.GetText());
CStr action = CStr(child.GetAttributes().GetNamedItem(attr_on));
object->RegisterScriptHandler(action.LowerCase(), code, *this);
CStr eventName = child.GetAttributes().GetNamedItem(attr_on);
object->RegisterScriptHandler(eventName, code, *this);
}
else if (element_name == elmt_repeat)
{

View File

@ -74,17 +74,17 @@ public:
/**
* Sends a specified script event to every object
*
* @param EventName String representation of event name
* @param eventName String representation of event name
*/
void SendEventToAll(const CStr& EventName);
void SendEventToAll(const CStr& eventName);
/**
* Sends a specified script event to every object
*
* @param EventName String representation of event name
* @param eventName String representation of event name
* @param paramData JS::HandleValueArray storing the arguments passed to the event handler.
*/
void SendEventToAll(const CStr& EventName, const JS::HandleValueArray& paramData);
void SendEventToAll(const CStr& eventName, const JS::HandleValueArray& paramData);
/**
* Displays the whole GUI
@ -611,6 +611,24 @@ private:
*/
std::map<CStr, JS::PersistentRootedValue> m_GlobalHotkeys;
/**
* XML and JS can subscribe handlers to events identified by these names.
* Store in static const members to avoid string copies, gain compile errors when misspelling and
* to allow reuse in other classes.
*/
static const CStr EventNameLoad;
static const CStr EventNameTick;
static const CStr EventNamePress;
static const CStr EventNameRelease;
static const CStr EventNameMouseRightPress;
static const CStr EventNameMouseLeftPress;
static const CStr EventNameMouseWheelDown;
static const CStr EventNameMouseWheelUp;
static const CStr EventNameMouseLeftDoubleClick;
static const CStr EventNameMouseLeftRelease;
static const CStr EventNameMouseRightDoubleClick;
static const CStr EventNameMouseRightRelease;
//--------------------------------------------------------
// Databases
// These are loaded from XML files and marked as noncopyable and const to

View File

@ -31,6 +31,8 @@
CGUIManager* g_GUI = nullptr;
const CStr CGUIManager::EventNameWindowResized = "WindowResized";
// General TODOs:
//
@ -377,7 +379,7 @@ void CGUIManager::UpdateResolution()
for (const SGUIPage& p : pageStack)
{
p.gui->UpdateResolution();
p.gui->SendEventToAll("WindowResized");
p.gui->SendEventToAll(EventNameWindowResized);
}
}

View File

@ -161,6 +161,8 @@ private:
shared_ptr<JS::PersistentRootedValue> callbackFunction;
};
const static CStr EventNameWindowResized;
shared_ptr<CGUI> top() const;
shared_ptr<ScriptRuntime> m_ScriptRuntime;

View File

@ -21,6 +21,11 @@
#include "gui/CGUISprite.h"
const CStr IGUIButtonBehavior::EventNamePress = "Press";
const CStr IGUIButtonBehavior::EventNamePressRight = "PressRight";
const CStr IGUIButtonBehavior::EventNameDoublePress = "DoublePress";
const CStr IGUIButtonBehavior::EventNameDoublePressRight = "DoublePressRight";
IGUIButtonBehavior::IGUIButtonBehavior(IGUIObject& pObject)
: m_pObject(pObject),
m_Pressed(),
@ -69,7 +74,7 @@ void IGUIButtonBehavior::HandleMessage(SGUIMessage& Message)
// Since GUIM_MOUSE_PRESS_LEFT also gets called twice in a
// doubleclick event, we let it handle playing sounds.
m_pObject.SendEvent(GUIM_DOUBLE_PRESSED, "doublepress");
m_pObject.SendEvent(GUIM_DOUBLE_PRESSED, EventNameDoublePress);
break;
case GUIM_MOUSE_PRESS_LEFT:
@ -80,7 +85,7 @@ void IGUIButtonBehavior::HandleMessage(SGUIMessage& Message)
}
m_pObject.PlaySound(m_SoundPressed);
m_pObject.SendEvent(GUIM_PRESSED, "press");
m_pObject.SendEvent(GUIM_PRESSED, EventNamePress);
m_Pressed = true;
break;
@ -90,7 +95,7 @@ void IGUIButtonBehavior::HandleMessage(SGUIMessage& Message)
// Since GUIM_MOUSE_PRESS_RIGHT also gets called twice in a
// doubleclick event, we let it handle playing sounds.
m_pObject.SendEvent(GUIM_DOUBLE_PRESSED_MOUSE_RIGHT, "doublepressright");
m_pObject.SendEvent(GUIM_DOUBLE_PRESSED_MOUSE_RIGHT, EventNameDoublePressRight);
break;
case GUIM_MOUSE_PRESS_RIGHT:
@ -102,7 +107,7 @@ void IGUIButtonBehavior::HandleMessage(SGUIMessage& Message)
// Button was right-clicked
m_pObject.PlaySound(m_SoundPressed);
m_pObject.SendEvent(GUIM_PRESSED_MOUSE_RIGHT, "pressright");
m_pObject.SendEvent(GUIM_PRESSED_MOUSE_RIGHT, EventNamePressRight);
m_PressedRight = true;
break;

View File

@ -60,6 +60,11 @@ public:
const CGUISpriteInstance& GetButtonSprite(const CGUISpriteInstance& sprite, const CGUISpriteInstance& sprite_over, const CGUISpriteInstance& sprite_pressed, const CGUISpriteInstance& sprite_disabled) const;
protected:
static const CStr EventNamePress;
static const CStr EventNamePressRight;
static const CStr EventNameDoublePress;
static const CStr EventNameDoublePressRight;
/**
* @see IGUIObject#ResetStates()
*/

View File

@ -27,6 +27,10 @@
#include "scriptinterface/ScriptInterface.h"
#include "soundmanager/ISoundManager.h"
const CStr IGUIObject::EventNameMouseEnter = "MouseEnter";
const CStr IGUIObject::EventNameMouseMove = "MouseMove";
const CStr IGUIObject::EventNameMouseLeave = "MouseLeave";
IGUIObject::IGUIObject(CGUI& pGUI)
: m_pGUI(pGUI),
m_pParent(),
@ -178,18 +182,18 @@ void IGUIObject::UpdateMouseOver(IGUIObject* const& pMouseOver)
if (pMouseOver == this)
{
if (!m_MouseHovering)
SendEvent(GUIM_MOUSE_ENTER, "mouseenter");
SendMouseEvent(GUIM_MOUSE_ENTER,EventNameMouseEnter);
m_MouseHovering = true;
SendEvent(GUIM_MOUSE_OVER, "mousemove");
SendMouseEvent(GUIM_MOUSE_OVER, EventNameMouseMove);
}
else
{
if (m_MouseHovering)
{
m_MouseHovering = false;
SendEvent(GUIM_MOUSE_LEAVE, "mouseleave");
SendMouseEvent(GUIM_MOUSE_LEAVE, EventNameMouseLeave);
}
}
}
@ -292,7 +296,7 @@ float IGUIObject::GetBufferedZ() const
return m_Z;
}
void IGUIObject::RegisterScriptHandler(const CStr& Action, const CStr& Code, CGUI& pGUI)
void IGUIObject::RegisterScriptHandler(const CStr& eventName, const CStr& Code, CGUI& pGUI)
{
JSContext* cx = pGUI.GetScriptInterface()->GetContext();
JSAutoRequest rq(cx);
@ -303,12 +307,12 @@ void IGUIObject::RegisterScriptHandler(const CStr& Action, const CStr& Code, CGU
const char* paramNames[paramCount] = { "mouse" };
// Location to report errors from
CStr CodeName = GetName()+" "+Action;
CStr CodeName = GetName() + " " + eventName;
// Generate a unique name
static int x = 0;
char buf[64];
sprintf_s(buf, ARRAY_SIZE(buf), "__eventhandler%d (%s)", x++, Action.c_str());
sprintf_s(buf, ARRAY_SIZE(buf), "__eventhandler%d (%s)", x++, eventName.c_str());
JS::CompileOptions options(cx);
options.setFileAndLine(CodeName.c_str(), 0);
@ -318,25 +322,25 @@ void IGUIObject::RegisterScriptHandler(const CStr& Action, const CStr& Code, CGU
JS::AutoObjectVector emptyScopeChain(cx);
if (!JS::CompileFunction(cx, emptyScopeChain, options, buf, paramCount, paramNames, Code.c_str(), Code.length(), &func))
{
LOGERROR("RegisterScriptHandler: Failed to compile the script for %s", Action.c_str());
LOGERROR("RegisterScriptHandler: Failed to compile the script for %s", eventName.c_str());
return;
}
JS::RootedObject funcObj(cx, JS_GetFunctionObject(func));
SetScriptHandler(Action, funcObj);
SetScriptHandler(eventName, funcObj);
}
void IGUIObject::SetScriptHandler(const CStr& Action, JS::HandleObject Function)
void IGUIObject::SetScriptHandler(const CStr& eventName, JS::HandleObject Function)
{
if (m_ScriptHandlers.empty())
JS_AddExtraGCRootsTracer(m_pGUI.GetScriptInterface()->GetJSRuntime(), Trace, this);
m_ScriptHandlers[Action] = JS::Heap<JSObject*>(Function);
m_ScriptHandlers[eventName] = JS::Heap<JSObject*>(Function);
}
void IGUIObject::UnsetScriptHandler(const CStr& Action)
void IGUIObject::UnsetScriptHandler(const CStr& eventName)
{
std::map<CStr, JS::Heap<JSObject*> >::iterator it = m_ScriptHandlers.find(Action);
std::map<CStr, JS::Heap<JSObject*> >::iterator it = m_ScriptHandlers.find(eventName);
if (it == m_ScriptHandlers.end())
return;
@ -347,25 +351,28 @@ void IGUIObject::UnsetScriptHandler(const CStr& Action)
JS_RemoveExtraGCRootsTracer(m_pGUI.GetScriptInterface()->GetJSRuntime(), Trace, this);
}
InReaction IGUIObject::SendEvent(EGUIMessageType type, const CStr& EventName)
InReaction IGUIObject::SendEvent(EGUIMessageType type, const CStr& eventName)
{
PROFILE2_EVENT("gui event");
PROFILE2_ATTR("type: %s", EventName.c_str());
PROFILE2_ATTR("type: %s", eventName.c_str());
PROFILE2_ATTR("object: %s", m_Name.c_str());
SGUIMessage msg(type);
HandleMessage(msg);
ScriptEvent(EventName);
ScriptEvent(eventName);
return (msg.skipped ? IN_PASS : IN_HANDLED);
return msg.skipped ? IN_PASS : IN_HANDLED;
}
void IGUIObject::ScriptEvent(const CStr& Action)
InReaction IGUIObject::SendMouseEvent(EGUIMessageType type, const CStr& eventName)
{
std::map<CStr, JS::Heap<JSObject*> >::iterator it = m_ScriptHandlers.find(Action);
if (it == m_ScriptHandlers.end())
return;
PROFILE2_EVENT("gui mouse event");
PROFILE2_ATTR("type: %s", eventName.c_str());
PROFILE2_ATTR("object: %s", m_Name.c_str());
SGUIMessage msg(type);
HandleMessage(msg);
JSContext* cx = m_pGUI.GetScriptInterface()->GetContext();
JSAutoRequest rq(cx);
@ -381,23 +388,27 @@ void IGUIObject::ScriptEvent(const CStr& Action)
"x", mousePos.x,
"y", mousePos.y,
"buttons", m_pGUI.GetMouseButtons());
JS::AutoValueVector paramData(cx);
paramData.append(mouse);
JS::RootedObject obj(cx, GetJSObject());
JS::RootedValue handlerVal(cx, JS::ObjectValue(*it->second));
JS::RootedValue result(cx);
bool ok = JS_CallFunctionValue(cx, obj, handlerVal, paramData, &result);
if (!ok)
{
// We have no way to propagate the script exception, so just ignore it
// and hope the caller checks JS_IsExceptionPending
}
ScriptEvent(eventName, paramData);
return msg.skipped ? IN_PASS : IN_HANDLED;
}
void IGUIObject::ScriptEvent(const CStr& Action, const JS::HandleValueArray& paramData)
void IGUIObject::ScriptEvent(const CStr& eventName)
{
std::map<CStr, JS::Heap<JSObject*> >::iterator it = m_ScriptHandlers.find(Action);
if (m_ScriptHandlers.find(eventName) == m_ScriptHandlers.end())
return;
JSContext* cx = m_pGUI.GetScriptInterface()->GetContext();
JSAutoRequest rq(cx);
JS::AutoValueVector paramData(cx);
ScriptEvent(eventName, paramData);
}
void IGUIObject::ScriptEvent(const CStr& eventName, const JS::HandleValueArray& paramData)
{
std::map<CStr, JS::Heap<JSObject*> >::iterator it = m_ScriptHandlers.find(eventName);
if (it == m_ScriptHandlers.end())
return;
@ -408,7 +419,7 @@ void IGUIObject::ScriptEvent(const CStr& Action, const JS::HandleValueArray& par
JS::RootedValue result(cx);
if (!JS_CallFunctionValue(cx, obj, handlerVal, paramData, &result))
JS_ReportError(cx, "Errors executing script action \"%s\"", Action.c_str());
JS_ReportError(cx, "Errors executing script event \"%s\"", eventName.c_str());
}
void IGUIObject::CreateJSObject()

View File

@ -199,10 +199,15 @@ public:
* Send event to this GUI object (HandleMessage and ScriptEvent)
*
* @param type Type of GUI message to be handled
* @param EventName String representation of event name
* @param eventName String representation of event name
* @return IN_HANDLED if event was handled, or IN_PASS if skipped
*/
InReaction SendEvent(EGUIMessageType type, const CStr& EventName);
InReaction SendEvent(EGUIMessageType type, const CStr& eventName);
/**
* Same as SendEvent, but passes mouse coordinates and button state as an argument.
*/
InReaction SendMouseEvent(EGUIMessageType type, const CStr& eventName);
/**
* All sizes are relative to resolution, and the calculation
@ -219,11 +224,11 @@ public:
/**
* Set the script handler for a particular object-specific action
*
* @param Action Name of action
* @param eventName Name of action
* @param Code Javascript code to execute when the action occurs
* @param pGUI GUI instance to associate the script with
*/
void RegisterScriptHandler(const CStr& Action, const CStr& Code, CGUI& pGUI);
void RegisterScriptHandler(const CStr& eventName, const CStr& Code, CGUI& pGUI);
/**
* Inheriting classes may append JS functions to the JS object representing this class.
@ -373,28 +378,28 @@ protected:
* Does nothing if no script has been registered for that action.
* The mouse coordinates will be passed as the first argument.
*
* @param Action Name of action
* @param eventName Name of action
*/
void ScriptEvent(const CStr& Action);
void ScriptEvent(const CStr& eventName);
/**
* Execute the script for a particular action.
* Does nothing if no script has been registered for that action.
*
* @param Action Name of action
* @param eventName Name of action
* @param paramData JS::HandleValueArray arguments to pass to the event.
*/
void ScriptEvent(const CStr& Action, const JS::HandleValueArray& paramData);
void ScriptEvent(const CStr& eventName, const JS::HandleValueArray& paramData);
/**
* Assigns a JS function to the event name.
*/
void SetScriptHandler(const CStr& Action, JS::HandleObject Function);
void SetScriptHandler(const CStr& eventName, JS::HandleObject Function);
/**
* Deletes an event handler assigned to the given name, if such a handler exists.
*/
void UnsetScriptHandler(const CStr& Action);
void UnsetScriptHandler(const CStr& eventName);
/**
* Inputes the object that is currently hovered, this function
@ -456,6 +461,10 @@ private:
// Variables
protected:
static const CStr EventNameMouseEnter;
static const CStr EventNameMouseMove;
static const CStr EventNameMouseLeave;
// Name of object
CStr m_Name;

View File

@ -37,6 +37,10 @@
extern int g_yres;
const CStr CInput::EventNameTextEdit = "TextEdit";
const CStr CInput::EventNamePress = "Press";
const CStr CInput::EventNameTab = "Tab";
CInput::CInput(CGUI& pGUI)
:
IGUIObject(pGUI),
@ -157,7 +161,7 @@ InReaction CInput::ManuallyHandleEvent(const SDL_Event_* ev)
m_iBufferPos_Tail = -1;
UpdateAutoScroll();
SendEvent(GUIM_TEXTEDIT, "textedit");
SendEvent(GUIM_TEXTEDIT, EventNameTextEdit);
return IN_HANDLED;
}
@ -206,7 +210,7 @@ InReaction CInput::ManuallyHandleEvent(const SDL_Event_* ev)
UpdateText(m_iBufferPos, m_iBufferPos, m_iBufferPos+1);
UpdateAutoScroll();
SendEvent(GUIM_TEXTEDIT, "textedit");
SendEvent(GUIM_TEXTEDIT, EventNameTextEdit);
return IN_HANDLED;
}
@ -244,7 +248,7 @@ void CInput::ManuallyMutableHandleKeyDownEvent(const SDL_Keycode keyCode)
{
case SDLK_TAB:
{
SendEvent(GUIM_TAB, "tab");
SendEvent(GUIM_TAB, EventNameTab);
// Don't send a textedit event, because it should only
// be sent if the GUI control changes the text
break;
@ -275,7 +279,7 @@ void CInput::ManuallyMutableHandleKeyDownEvent(const SDL_Keycode keyCode)
}
UpdateAutoScroll();
SendEvent(GUIM_TEXTEDIT, "textedit");
SendEvent(GUIM_TEXTEDIT, EventNameTextEdit);
break;
}
case SDLK_DELETE:
@ -297,7 +301,7 @@ void CInput::ManuallyMutableHandleKeyDownEvent(const SDL_Keycode keyCode)
}
UpdateAutoScroll();
SendEvent(GUIM_TEXTEDIT, "textedit");
SendEvent(GUIM_TEXTEDIT, EventNameTextEdit);
break;
}
case SDLK_KP_ENTER:
@ -307,7 +311,7 @@ void CInput::ManuallyMutableHandleKeyDownEvent(const SDL_Keycode keyCode)
// otherwise a '\n' character will be added.
if (!m_MultiLine)
{
SendEvent(GUIM_PRESSED, "press");
SendEvent(GUIM_PRESSED, EventNamePress);
break;
}
@ -343,7 +347,7 @@ void CInput::ManuallyMutableHandleKeyDownEvent(const SDL_Keycode keyCode)
++m_iBufferPos;
UpdateAutoScroll();
SendEvent(GUIM_TEXTEDIT, "textedit");
SendEvent(GUIM_TEXTEDIT, EventNameTextEdit);
break;
}
}
@ -617,7 +621,7 @@ InReaction CInput::ManuallyHandleHotkeyEvent(const SDL_Event_* ev)
sys_clipboard_free(text);
SendEvent(GUIM_TEXTEDIT, "textedit");
SendEvent(GUIM_TEXTEDIT, EventNameTextEdit);
}
return IN_HANDLED;
@ -653,7 +657,7 @@ InReaction CInput::ManuallyHandleHotkeyEvent(const SDL_Event_* ev)
{
DeleteCurSelection();
UpdateAutoScroll();
SendEvent(GUIM_TEXTEDIT, "textedit");
SendEvent(GUIM_TEXTEDIT, EventNameTextEdit);
}
}
@ -700,7 +704,7 @@ InReaction CInput::ManuallyHandleHotkeyEvent(const SDL_Event_* ev)
UpdateBufferPositionSetting();
DeleteCurSelection();
SendEvent(GUIM_TEXTEDIT, "textedit");
SendEvent(GUIM_TEXTEDIT, EventNameTextEdit);
}
UpdateAutoScroll();
return IN_HANDLED;
@ -739,7 +743,7 @@ InReaction CInput::ManuallyHandleHotkeyEvent(const SDL_Event_* ev)
DeleteCurSelection();
}
UpdateAutoScroll();
SendEvent(GUIM_TEXTEDIT, "textedit");
SendEvent(GUIM_TEXTEDIT, EventNameTextEdit);
return IN_HANDLED;
}
else if (hotkey == "text.move.left")

View File

@ -190,6 +190,10 @@ protected:
/// If the cursor should be drawn or not.
bool m_CursorVisState;
static const CStr EventNameTextEdit;
static const CStr EventNamePress;
static const CStr EventNameTab;
// Settings
i32 m_BufferPosition;
float m_BufferZone;

View File

@ -26,6 +26,11 @@
#include "lib/external_libraries/libsdl.h"
#include "lib/timer.h"
const CStr CList::EventNameSelectionChange = "SelectionChange";
const CStr CList::EventNameHoverChange = "HoverChange";
const CStr CList::EventNameMouseLeftClickItem = "MouseLeftClickItem";
const CStr CList::EventNameMouseLeftDoubleClickItem = "MouseLeftDoubleClickItem";
CList::CList(CGUI& pGUI)
: IGUIObject(pGUI),
IGUITextOwner(*static_cast<IGUIObject*>(this)),
@ -172,8 +177,7 @@ void CList::HandleMessage(SGUIMessage& Message)
if (m_AutoScroll)
UpdateAutoScroll();
// TODO only works if lower-case, shouldn't it be made case sensitive instead?
ScriptEvent("selectionchange");
ScriptEvent(EventNameSelectionChange);
}
if (Message.value == "scrollbar")
@ -204,9 +208,9 @@ void CList::HandleMessage(SGUIMessage& Message)
PlaySound(m_SoundSelected);
if (timer_Time() - m_LastItemClickTime < SELECT_DBLCLICK_RATE && hovered == m_PrevSelectedItem)
this->SendEvent(GUIM_MOUSE_DBLCLICK_LEFT_ITEM, "mouseleftdoubleclickitem");
this->SendMouseEvent(GUIM_MOUSE_DBLCLICK_LEFT_ITEM, EventNameMouseLeftDoubleClickItem);
else
this->SendEvent(GUIM_MOUSE_PRESS_LEFT_ITEM, "mouseleftclickitem");
this->SendMouseEvent(GUIM_MOUSE_PRESS_LEFT_ITEM, EventNameMouseLeftClickItem);
m_LastItemClickTime = timer_Time();
m_PrevSelectedItem = hovered;
@ -219,7 +223,7 @@ void CList::HandleMessage(SGUIMessage& Message)
break;
SetSetting<i32>("hovered", -1, true);
ScriptEvent("hoverchange");
ScriptEvent(EventNameHoverChange);
break;
}
@ -230,7 +234,7 @@ void CList::HandleMessage(SGUIMessage& Message)
break;
SetSetting<i32>("hovered", hovered, true);
ScriptEvent("hoverchange");
ScriptEvent(EventNameHoverChange);
break;
}

View File

@ -138,6 +138,11 @@ protected:
CGUIList m_ListData;
private:
static const CStr EventNameSelectionChange;
static const CStr EventNameHoverChange;
static const CStr EventNameMouseLeftClickItem;
static const CStr EventNameMouseLeftDoubleClickItem;
// Whether the list's items have been modified since last handling a message.
bool m_Modified;

View File

@ -64,6 +64,8 @@ static unsigned int ScaleColor(unsigned int color, float x)
return (0xff000000 | b | g<<8 | r<<16);
}
const CStr CMiniMap::EventNameWorldClick = "WorldClick";
CMiniMap::CMiniMap(CGUI& pGUI) :
IGUIObject(pGUI),
m_TerrainTexture(0), m_TerrainData(0), m_MapSize(0), m_Terrain(0), m_TerrainDirty(true), m_MapScale(1.f),
@ -249,7 +251,7 @@ void CMiniMap::FireWorldClickEvent(int UNUSED(button), int UNUSED(clicks))
JS::AutoValueVector paramData(cx);
paramData.append(coords);
ScriptEvent("worldclick", paramData);
ScriptEvent(EventNameWorldClick, paramData);
}
// This sets up and draws the rectangle on the minimap

View File

@ -58,6 +58,8 @@ protected:
void FireWorldClickEvent(int button, int clicks);
static const CStr EventNameWorldClick;
// the terrain we are mini-mapping
const CTerrain* m_Terrain;

View File

@ -29,6 +29,8 @@
const float SORT_SPRITE_DIM = 16.0f;
const CPos COLUMN_SHIFT = CPos(0, 4);
const CStr COList::EventNameSelectionColumnChange = "SelectionColumnChange";
COList::COList(CGUI& pGUI)
: CList(pGUI),
m_SpriteHeading(),
@ -165,7 +167,7 @@ void COList::HandleMessage(SGUIMessage& Message)
else
SetSetting<i32>("selected_column_order", -m_SelectedColumnOrder, true);
ScriptEvent("selectioncolumnchange");
ScriptEvent(EventNameSelectionColumnChange);
PlaySound(m_SoundSelected);
return;
}

View File

@ -83,6 +83,8 @@ protected:
CGUISpriteInstance m_SpriteNotSorted;
private:
static const CStr EventNameSelectionColumnChange;
// Width of space available for columns
float m_TotalAvailableColumnWidth;
float m_HeadingHeight;

View File

@ -22,6 +22,8 @@
#include "gui/CGUI.h"
#include "maths/MathUtil.h"
const CStr CSlider::EventNameValueChange = "ValueChange";
CSlider::CSlider(CGUI& pGUI)
: IGUIObject(pGUI),
m_IsPressed(),
@ -127,7 +129,7 @@ void CSlider::Draw()
void CSlider::UpdateValue()
{
SetSetting<float>("value", m_Value, true);
ScriptEvent("valuechange");
ScriptEvent(EventNameValueChange);
}
CRect CSlider::GetButtonRect() const

View File

@ -30,6 +30,7 @@ public:
virtual ~CSlider();
protected:
static const CStr EventNameValueChange;
/**
* @see IGUIObject#HandleMessage()

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2019 Wildfire Games.
/* Copyright (C) 2020 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -82,7 +82,7 @@ bool JSI_IGUIObject::getProperty(JSContext* cx, JS::HandleObject obj, JS::Handle
// Use onWhatever to access event handlers
if (propName.substr(0, 2) == "on")
{
CStr eventName(CStr(propName.substr(2)).LowerCase());
CStr eventName(propName.substr(2));
std::map<CStr, JS::Heap<JSObject*>>::iterator it = e->m_ScriptHandlers.find(eventName);
if (it == e->m_ScriptHandlers.end())
vp.setNull();
@ -163,7 +163,7 @@ bool JSI_IGUIObject::setProperty(JSContext* cx, JS::HandleObject obj, JS::Handle
return result.fail(JSMSG_NOT_FUNCTION);
}
CStr eventName(CStr(propName.substr(2)).LowerCase());
CStr eventName(propName.substr(2));
e->SetScriptHandler(eventName, vpObj);
return result.succeed();
@ -194,7 +194,7 @@ bool JSI_IGUIObject::deleteProperty(JSContext* cx, JS::HandleObject obj, JS::Han
// event handlers
if (propName.substr(0, 2) == "on")
{
CStr eventName(CStr(propName.substr(2)).LowerCase());
CStr eventName(propName.substr(2));
e->UnsetScriptHandler(eventName);
return result.succeed();
}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2019 Wildfire Games.
/* Copyright (C) 2020 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -59,6 +59,8 @@ extern GameLoopState* g_AtlasGameLoop;
**/
CGame *g_Game=NULL;
const CStr CGame::EventNameSimulationUpdate = "SimulationUpdate";
/**
* Constructor
*
@ -401,7 +403,7 @@ void CGame::Update(const double deltaRealTime, bool doInterpolate)
{
{
PROFILE3("gui sim update");
g_GUI->SendEventToAll("SimulationUpdate");
g_GUI->SendEventToAll(EventNameSimulationUpdate);
}
GetView()->GetLOSTexture().MakeDirty();

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2019 Wildfire Games.
/* Copyright (C) 2020 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -203,6 +203,8 @@ public:
{ return *m_ReplayLogger; }
private:
static const CStr EventNameSimulationUpdate;
void RegisterInit(const JS::HandleValue attribs, const std::string& savedState);
IReplayLogger* m_ReplayLogger;

View File

@ -113,6 +113,8 @@ shared_ptr<ScriptRuntime> g_ScriptRuntime;
static const int SANE_TEX_QUALITY_DEFAULT = 5; // keep in sync with code
static const CStr g_EventNameGameLoadProgress = "GameLoadProgress";
bool g_InDevelopmentCopy;
bool g_CheckedIfInDevelopmentCopy = false;
@ -193,7 +195,7 @@ void GUI_DisplayLoadProgress(int percent, const wchar_t* pending_task)
scriptInterface.ToJSVal(cx, &valPendingTask, pending_task);
paramData.append(valPendingTask);
g_GUI->SendEventToAll("GameLoadProgress", paramData);
g_GUI->SendEventToAll(g_EventNameGameLoadProgress, paramData);
}
bool ShouldRender()

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2015 Wildfire Games.
/* Copyright (C) 2020 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -17,9 +17,6 @@
/*
Xeromyces - XMB reading library
*/
/*
Brief outline:
@ -33,10 +30,6 @@ Main limitations:
considered identical.
* Tries to avoid using strings - you usually have to load the
numeric IDs and use them instead.
* Case-sensitive (but converts all element/attribute names in
the XML file to lowercase, so you only have to be careful in
the code)
Theoretical file structure:

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2019 Wildfire Games.
/* Copyright (C) 2020 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -24,6 +24,9 @@
#include "ps/Util.h"
#include "simulation2/Simulation2.h"
const CStr CReplayTurnManager::EventNameReplayFinished = "ReplayFinished";
const CStr CReplayTurnManager::EventNameReplayOutOfSync = "ReplayOutOfSync";
CReplayTurnManager::CReplayTurnManager(CSimulation2& simulation, IReplayLogger& replay)
: CLocalTurnManager(simulation, replay)
{
@ -57,7 +60,7 @@ void CReplayTurnManager::StoreFinalReplayTurn(u32 turn)
void CReplayTurnManager::NotifyFinishedUpdate(u32 turn)
{
if (turn == 1 && m_FinalTurn == 0)
g_GUI->SendEventToAll("ReplayFinished");
g_GUI->SendEventToAll(EventNameReplayFinished);
if (turn > m_FinalTurn)
return;
@ -99,7 +102,7 @@ void CReplayTurnManager::NotifyFinishedUpdate(u32 turn)
scriptInterface.ToJSVal(cx, &expectedHashVal, expectedHash);
paramData.append(expectedHashVal);
g_GUI->SendEventToAll("ReplayOutOfSync", paramData);
g_GUI->SendEventToAll(EventNameReplayOutOfSync, paramData);
}
void CReplayTurnManager::DoTurn(u32 turn)
@ -120,5 +123,5 @@ void CReplayTurnManager::DoTurn(u32 turn)
}
if (turn == m_FinalTurn)
g_GUI->SendEventToAll("ReplayFinished");
g_GUI->SendEventToAll(EventNameReplayFinished);
}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2017 Wildfire Games.
/* Copyright (C) 2020 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -41,6 +41,9 @@ private:
void DoTurn(u32 turn);
static const CStr EventNameReplayFinished;
static const CStr EventNameReplayOutOfSync;
// Contains the commands of every player on each turn
std::map<u32, std::vector<std::pair<player_id_t, std::string>>> m_ReplayCommands;

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2019 Wildfire Games.
/* Copyright (C) 2020 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -40,6 +40,8 @@ const int COMMAND_DELAY = 2;
#define NETTURN_LOG(...)
#endif
const CStr CTurnManager::EventNameSavegameLoaded = "SavegameLoaded";
CTurnManager::CTurnManager(CSimulation2& simulation, u32 defaultTurnLength, int clientId, IReplayLogger& replay)
: m_Simulation2(simulation), m_CurrentTurn(0), m_ReadyTurn(1), m_TurnLength(defaultTurnLength),
m_PlayerId(-1), m_ClientId(clientId), m_DeltaSimTime(0), m_HasSyncError(false), m_Replay(replay),
@ -357,7 +359,7 @@ void CTurnManager::QuickLoad()
JS::AutoValueArray<1> paramData(cx);
paramData[0].set(quickSaveMetadataClone);
g_GUI->SendEventToAll("SavegameLoaded", paramData);
g_GUI->SendEventToAll(EventNameSavegameLoaded, paramData);
LOGMESSAGERENDER("Quickloaded game");
}

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2019 Wildfire Games.
/* Copyright (C) 2020 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -18,21 +18,22 @@
#ifndef INCLUDED_TURNMANAGER
#define INCLUDED_TURNMANAGER
#include "ps/CStr.h"
#include "simulation2/helpers/SimulationCommand.h"
#include <list>
#include <map>
#include <vector>
class CSimulationMessage;
class CSimulation2;
class IReplayLogger;
extern const u32 DEFAULT_TURN_LENGTH_SP;
extern const u32 DEFAULT_TURN_LENGTH_MP;
extern const int COMMAND_DELAY;
class CSimulationMessage;
class CSimulation2;
class IReplayLogger;
/**
* This file defines the base class of the turn managers for clients, local games and replays.
* The basic idea of our turn managing system across a network is as in this article:
@ -186,6 +187,8 @@ protected:
u32 m_FinalTurn;
private:
static const CStr EventNameSavegameLoaded;
size_t m_TimeWarpNumTurns; // 0 if disabled
std::list<std::string> m_TimeWarpStates;
std::string m_QuickSaveState; // TODO: should implement a proper disk-based quicksave system