Changeset View
Changeset View
Standalone View
Standalone View
ps/trunk/source/gui/CGUI.cpp
/* Copyright (C) 2019 Wildfire Games. | /* Copyright (C) 2020 Wildfire Games. | ||||
* This file is part of 0 A.D. | * This file is part of 0 A.D. | ||||
* | * | ||||
* 0 A.D. is free software: you can redistribute it and/or modify | * 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 | * it under the terms of the GNU General Public License as published by | ||||
* the Free Software Foundation, either version 2 of the License, or | * the Free Software Foundation, either version 2 of the License, or | ||||
* (at your option) any later version. | * (at your option) any later version. | ||||
* | * | ||||
* 0 A.D. is distributed in the hope that it will be useful, | * 0 A.D. is distributed in the hope that it will be useful, | ||||
Show All 32 Lines | |||||
#include <string> | #include <string> | ||||
#include <unordered_set> | #include <unordered_set> | ||||
extern int g_yres; | extern int g_yres; | ||||
const double SELECT_DBLCLICK_RATE = 0.5; | 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 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) | CGUI::CGUI(const shared_ptr<ScriptRuntime>& runtime) | ||||
: m_BaseObject(*this), | : m_BaseObject(*this), | ||||
m_FocusedObject(nullptr), | m_FocusedObject(nullptr), | ||||
m_InternalNameNumber(0), | m_InternalNameNumber(0), | ||||
m_MouseButtons(0) | m_MouseButtons(0) | ||||
{ | { | ||||
m_ScriptInterface.reset(new ScriptInterface("Engine", "GUIPage", runtime)); | m_ScriptInterface.reset(new ScriptInterface("Engine", "GUIPage", runtime)); | ||||
m_ScriptInterface->SetCallbackData(this); | m_ScriptInterface->SetCallbackData(this); | ||||
Show All 30 Lines | if (m_GlobalHotkeys.find(hotkey) != m_GlobalHotkeys.end() && ev->ev.type == SDL_HOTKEYDOWN) | ||||
JS_CallFunctionValue(cx, globalObj, m_GlobalHotkeys[hotkey], JS::HandleValueArray::empty(), &result); | JS_CallFunctionValue(cx, globalObj, m_GlobalHotkeys[hotkey], JS::HandleValueArray::empty(), &result); | ||||
} | } | ||||
std::map<CStr, std::vector<IGUIObject*> >::iterator it = m_HotkeyObjects.find(hotkey); | std::map<CStr, std::vector<IGUIObject*> >::iterator it = m_HotkeyObjects.find(hotkey); | ||||
if (it != m_HotkeyObjects.end()) | if (it != m_HotkeyObjects.end()) | ||||
for (IGUIObject* const& obj : it->second) | for (IGUIObject* const& obj : it->second) | ||||
{ | { | ||||
if (ev->ev.type == SDL_HOTKEYDOWN) | if (ev->ev.type == SDL_HOTKEYDOWN) | ||||
ret = obj->SendEvent(GUIM_PRESSED, "press"); | ret = obj->SendEvent(GUIM_PRESSED, EventNamePress); | ||||
else | else | ||||
ret = obj->SendEvent(GUIM_RELEASED, "release"); | ret = obj->SendEvent(GUIM_RELEASED, EventNameRelease); | ||||
} | } | ||||
} | } | ||||
else if (ev->ev.type == SDL_MOUSEMOTION) | else if (ev->ev.type == SDL_MOUSEMOTION) | ||||
{ | { | ||||
// Yes the mouse position is stored as float to avoid | // Yes the mouse position is stored as float to avoid | ||||
// constant conversions when operating in a | // constant conversions when operating in a | ||||
// float-based environment. | // float-based environment. | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | // TODO Gee: (2004-09-08) Big TODO, don't do the below if the SDL_Event is something like a keypress! | ||||
{ | { | ||||
switch (ev->ev.button.button) | switch (ev->ev.button.button) | ||||
{ | { | ||||
case SDL_BUTTON_LEFT: | case SDL_BUTTON_LEFT: | ||||
// Focus the clicked object (or focus none if nothing clicked on) | // Focus the clicked object (or focus none if nothing clicked on) | ||||
SetFocusedObject(pNearest); | SetFocusedObject(pNearest); | ||||
if (pNearest) | if (pNearest) | ||||
ret = pNearest->SendEvent(GUIM_MOUSE_PRESS_LEFT, "mouseleftpress"); | ret = pNearest->SendMouseEvent(GUIM_MOUSE_PRESS_LEFT, EventNameMouseLeftPress); | ||||
break; | break; | ||||
case SDL_BUTTON_RIGHT: | case SDL_BUTTON_RIGHT: | ||||
if (pNearest) | if (pNearest) | ||||
ret = pNearest->SendEvent(GUIM_MOUSE_PRESS_RIGHT, "mouserightpress"); | ret = pNearest->SendMouseEvent(GUIM_MOUSE_PRESS_RIGHT, EventNameMouseRightPress); | ||||
break; | break; | ||||
default: | default: | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
else if (ev->ev.type == SDL_MOUSEWHEEL && pNearest) | else if (ev->ev.type == SDL_MOUSEWHEEL && pNearest) | ||||
{ | { | ||||
if (ev->ev.wheel.y < 0) | 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) | 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) | else if (ev->ev.type == SDL_MOUSEBUTTONUP) | ||||
{ | { | ||||
switch (ev->ev.button.button) | switch (ev->ev.button.button) | ||||
{ | { | ||||
case SDL_BUTTON_LEFT: | case SDL_BUTTON_LEFT: | ||||
if (pNearest) | if (pNearest) | ||||
{ | { | ||||
double timeElapsed = timer_Time() - pNearest->m_LastClickTime[SDL_BUTTON_LEFT]; | double timeElapsed = timer_Time() - pNearest->m_LastClickTime[SDL_BUTTON_LEFT]; | ||||
pNearest->m_LastClickTime[SDL_BUTTON_LEFT] = timer_Time(); | pNearest->m_LastClickTime[SDL_BUTTON_LEFT] = timer_Time(); | ||||
if (timeElapsed < SELECT_DBLCLICK_RATE) | if (timeElapsed < SELECT_DBLCLICK_RATE) | ||||
ret = pNearest->SendEvent(GUIM_MOUSE_DBLCLICK_LEFT, "mouseleftdoubleclick"); | ret = pNearest->SendMouseEvent(GUIM_MOUSE_DBLCLICK_LEFT, EventNameMouseLeftDoubleClick); | ||||
else | else | ||||
ret = pNearest->SendEvent(GUIM_MOUSE_RELEASE_LEFT, "mouseleftrelease"); | ret = pNearest->SendMouseEvent(GUIM_MOUSE_RELEASE_LEFT, EventNameMouseLeftRelease); | ||||
} | } | ||||
break; | break; | ||||
case SDL_BUTTON_RIGHT: | case SDL_BUTTON_RIGHT: | ||||
if (pNearest) | if (pNearest) | ||||
{ | { | ||||
double timeElapsed = timer_Time() - pNearest->m_LastClickTime[SDL_BUTTON_RIGHT]; | double timeElapsed = timer_Time() - pNearest->m_LastClickTime[SDL_BUTTON_RIGHT]; | ||||
pNearest->m_LastClickTime[SDL_BUTTON_RIGHT] = timer_Time(); | pNearest->m_LastClickTime[SDL_BUTTON_RIGHT] = timer_Time(); | ||||
if (timeElapsed < SELECT_DBLCLICK_RATE) | if (timeElapsed < SELECT_DBLCLICK_RATE) | ||||
ret = pNearest->SendEvent(GUIM_MOUSE_DBLCLICK_RIGHT, "mouserightdoubleclick"); | ret = pNearest->SendMouseEvent(GUIM_MOUSE_DBLCLICK_RIGHT, EventNameMouseRightDoubleClick); | ||||
else | else | ||||
ret = pNearest->SendEvent(GUIM_MOUSE_RELEASE_RIGHT, "mouserightrelease"); | ret = pNearest->SendMouseEvent(GUIM_MOUSE_RELEASE_RIGHT, EventNameMouseRightRelease); | ||||
} | } | ||||
break; | break; | ||||
} | } | ||||
// Reset all states on all visible objects | // Reset all states on all visible objects | ||||
m_BaseObject.RecurseObject(&IGUIObject::IsHidden, &IGUIObject::ResetStates); | m_BaseObject.RecurseObject(&IGUIObject::IsHidden, &IGUIObject::ResetStates); | ||||
// Since the hover state will have been reset, we reload it. | // Since the hover state will have been reset, we reload it. | ||||
Show All 38 Lines | if (GetFocusedObject()) | ||||
// else will return IN_PASS because we never used the button. | // else will return IN_PASS because we never used the button. | ||||
} | } | ||||
return ret; | return ret; | ||||
} | } | ||||
void CGUI::TickObjects() | void CGUI::TickObjects() | ||||
{ | { | ||||
const CStr action = "tick"; | SendEventToAll(EventNameTick); | ||||
m_BaseObject.RecurseObject(nullptr, &IGUIObject::ScriptEvent, action); | |||||
m_Tooltip.Update(FindObjectUnderMouse(), m_MousePos, *this); | 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. | m_BaseObject.RecurseObject(nullptr, &IGUIObject::ScriptEvent, eventName); | ||||
// 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); | |||||
} | } | ||||
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, eventName, paramData); | ||||
m_BaseObject.RecurseObject(nullptr, &IGUIObject::ScriptEvent, EventNameLower, paramData); | |||||
} | } | ||||
void CGUI::Draw() | void CGUI::Draw() | ||||
{ | { | ||||
// Clear the depth buffer, so the GUI is | // Clear the depth buffer, so the GUI is | ||||
// drawn on top of everything else | // drawn on top of everything else | ||||
glClear(GL_DEPTH_BUFFER_BIT); | glClear(GL_DEPTH_BUFFER_BIT); | ||||
▲ Show 20 Lines • Show All 172 Lines • ▼ Show 20 Lines | |||||
void CGUI::LoadedXmlFiles() | void CGUI::LoadedXmlFiles() | ||||
{ | { | ||||
m_BaseObject.RecurseObject(nullptr, &IGUIObject::UpdateCachedSize); | m_BaseObject.RecurseObject(nullptr, &IGUIObject::UpdateCachedSize); | ||||
SGUIMessage msg(GUIM_LOAD); | SGUIMessage msg(GUIM_LOAD); | ||||
m_BaseObject.RecurseObject(nullptr, &IGUIObject::HandleMessage, msg); | m_BaseObject.RecurseObject(nullptr, &IGUIObject::HandleMessage, msg); | ||||
SendEventToAll("load"); | SendEventToAll(EventNameLoad); | ||||
} | } | ||||
//=================================================================== | //=================================================================== | ||||
// XML Reading Xeromyces Specific Sub-Routines | // XML Reading Xeromyces Specific Sub-Routines | ||||
//=================================================================== | //=================================================================== | ||||
void CGUI::Xeromyces_ReadRootObjects(XMBElement Element, CXeromyces* pFile, std::unordered_set<VfsPath>& Paths) | void CGUI::Xeromyces_ReadRootObjects(XMBElement Element, CXeromyces* pFile, std::unordered_set<VfsPath>& Paths) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 192 Lines • ▼ Show 20 Lines | else if (element_name == elmt_action) | ||||
code += g_L10n.Translate(grandchild.GetText()); | code += g_L10n.Translate(grandchild.GetText()); | ||||
else if (grandchild.GetNodeName() == elmt_keep) | else if (grandchild.GetNodeName() == elmt_keep) | ||||
code += grandchild.GetText(); | code += grandchild.GetText(); | ||||
} | } | ||||
else // It’s pure JavaScript code. | else // It’s pure JavaScript code. | ||||
// Read the inline code (concatenating to the file code, if both are specified) | // Read the inline code (concatenating to the file code, if both are specified) | ||||
code += CStr(child.GetText()); | code += CStr(child.GetText()); | ||||
CStr action = CStr(child.GetAttributes().GetNamedItem(attr_on)); | CStr eventName = child.GetAttributes().GetNamedItem(attr_on); | ||||
object->RegisterScriptHandler(eventName, code, *this); | |||||
object->RegisterScriptHandler(action.LowerCase(), code, *this); | |||||
} | } | ||||
else if (element_name == elmt_repeat) | else if (element_name == elmt_repeat) | ||||
{ | { | ||||
Xeromyces_ReadRepeat(child, pFile, object, NameSubst, Paths, nesting_depth); | Xeromyces_ReadRepeat(child, pFile, object, NameSubst, Paths, nesting_depth); | ||||
} | } | ||||
else if (element_name == elmt_translatableAttribute) | else if (element_name == elmt_translatableAttribute) | ||||
{ | { | ||||
// This is an element in the form “<translatableAttribute id="attributeName">attributeValue</translatableAttribute>”. | // This is an element in the form “<translatableAttribute id="attributeName">attributeValue</translatableAttribute>”. | ||||
▲ Show 20 Lines • Show All 559 Lines • Show Last 20 Lines |
Wildfire Games · Phabricator