Changeset View
Changeset View
Standalone View
Standalone View
source/gui/CGUI.cpp
Show All 37 Lines | |||||
#include "ps/Globals.h" | #include "ps/Globals.h" | ||||
#include "ps/Hotkey.h" | #include "ps/Hotkey.h" | ||||
#include "ps/Profile.h" | #include "ps/Profile.h" | ||||
#include "ps/Pyrogenesis.h" | #include "ps/Pyrogenesis.h" | ||||
#include "ps/XML/Xeromyces.h" | #include "ps/XML/Xeromyces.h" | ||||
#include "renderer/Renderer.h" | #include "renderer/Renderer.h" | ||||
#include "scriptinterface/ScriptContext.h" | #include "scriptinterface/ScriptContext.h" | ||||
#include "scriptinterface/ScriptInterface.h" | #include "scriptinterface/ScriptInterface.h" | ||||
vladislavbelov: It’s bad to have a simulation dependency in GUI without a reason, move the header to some… | |||||
#include <string> | #include <string> | ||||
#include <unordered_map> | #include <unordered_map> | ||||
#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 | ||||
▲ Show 20 Lines • Show All 311 Lines • ▼ Show 20 Lines | IGUIObject* CGUI::ConstructObject(const CStr& str) | ||||
std::map<CStr, ConstructObjectFunction>::iterator it = m_ObjectTypes.find(str); | std::map<CStr, ConstructObjectFunction>::iterator it = m_ObjectTypes.find(str); | ||||
if (it == m_ObjectTypes.end()) | if (it == m_ObjectTypes.end()) | ||||
return nullptr; | return nullptr; | ||||
return (*it->second)(*this); | return (*it->second)(*this); | ||||
} | } | ||||
IGUIObject* CGUI::CreateChild(const XMBFile& file, const XMBElement& element, IGUIObject* parent) | |||||
{ | |||||
std::unordered_set<VfsPath> paths; | |||||
std::vector<std::pair<CStr, CStr>> 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->RemoveChild(child); | |||||
m_pAllObjects.erase(child->m_Name); | |||||
delete child; | |||||
} | |||||
bool CGUI::AddObject(IGUIObject& parent, IGUIObject& child) | bool CGUI::AddObject(IGUIObject& parent, IGUIObject& child) | ||||
{ | { | ||||
if (child.m_Name.empty()) | if (child.m_Name.empty()) | ||||
{ | { | ||||
LOGERROR("Can't register an object without name!"); | LOGERROR("Can't register an object without name!"); | ||||
return false; | return false; | ||||
} | } | ||||
if (m_pAllObjects.find(child.m_Name) != m_pAllObjects.end()) | if (m_pAllObjects.find(child.m_Name) != m_pAllObjects.end()) | ||||
{ | { | ||||
LOGERROR("Can't register more than one object of the name %s", child.m_Name.c_str()); | LOGERROR("Can't register more than one object of the name %s", child.m_Name.c_str()); | ||||
return false; | return false; | ||||
} | } | ||||
m_pAllObjects[child.m_Name] = &child; | m_pAllObjects[child.m_Name] = &child; | ||||
parent.AddChild(child); | parent.AddChild(&child); | ||||
return true; | return true; | ||||
} | } | ||||
IGUIObject* CGUI::GetBaseObject() | IGUIObject* CGUI::GetBaseObject() | ||||
{ | { | ||||
return m_BaseObject.get(); | return m_BaseObject.get(); | ||||
}; | }; | ||||
▲ Show 20 Lines • Show All 124 Lines • ▼ Show 20 Lines | |||||
/** | /** | ||||
* @callgraph | * @callgraph | ||||
*/ | */ | ||||
void CGUI::LoadXmlFile(const VfsPath& Filename, std::unordered_set<VfsPath>& Paths) | void CGUI::LoadXmlFile(const VfsPath& Filename, std::unordered_set<VfsPath>& Paths) | ||||
{ | { | ||||
Paths.insert(Filename); | Paths.insert(Filename); | ||||
CXeromyces XeroFile; | CXeromyces xeroFile; | ||||
if (XeroFile.Load(g_VFS, Filename, "gui") != PSRETURN_OK) | if (xeroFile.Load(g_VFS, Filename, "gui") != PSRETURN_OK) | ||||
return; | return; | ||||
XMBElement node = XeroFile.GetRoot(); | XMBElement node = xeroFile.GetRoot(); | ||||
CStr root_name(XeroFile.GetElementString(node.GetNodeName())); | CStr root_name(xeroFile.GetElementString(node.GetNodeName())); | ||||
if (root_name == "objects") | if (root_name == "objects") | ||||
Xeromyces_ReadRootObjects(node, &XeroFile, Paths); | Xeromyces_ReadRootObjects(xeroFile, node, Paths); | ||||
else if (root_name == "sprites") | else if (root_name == "sprites") | ||||
Xeromyces_ReadRootSprites(node, &XeroFile); | Xeromyces_ReadRootSprites(xeroFile, node); | ||||
else if (root_name == "styles") | else if (root_name == "styles") | ||||
Xeromyces_ReadRootStyles(node, &XeroFile); | Xeromyces_ReadRootStyles(xeroFile, node); | ||||
else if (root_name == "setup") | else if (root_name == "setup") | ||||
Xeromyces_ReadRootSetup(node, &XeroFile); | Xeromyces_ReadRootSetup(xeroFile, node); | ||||
else | else | ||||
LOGERROR("CGUI::LoadXmlFile encountered an unknown XML root node type: %s", root_name.c_str()); | LOGERROR("CGUI::LoadXmlFile encountered an unknown XML root node type: %s", root_name.c_str()); | ||||
} | } | ||||
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(EventNameLoad); | 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(const XMBFile& file, XMBElement element, std::unordered_set<VfsPath>& Paths) | ||||
{ | { | ||||
int el_script = pFile->GetElementID("script"); | int el_script = file.GetElementID("script"); | ||||
std::vector<std::pair<CStr, CStr> > subst; | std::vector<std::pair<CStr, CStr> > subst; | ||||
// Iterate main children | // Iterate main children | ||||
// they should all be <object> or <script> elements | // they should all be <object> or <script> elements | ||||
for (XMBElement child : Element.GetChildNodes()) | for (XMBElement child : element.GetChildNodes()) | ||||
{ | { | ||||
if (child.GetNodeName() == el_script) | if (child.GetNodeName() == el_script) | ||||
// Execute the inline script | // Execute the inline script | ||||
Xeromyces_ReadScript(child, pFile, Paths); | Xeromyces_ReadScript(file, child, Paths); | ||||
else | else | ||||
// Read in this whole object into the GUI | // Read in this whole object into the GUI | ||||
Xeromyces_ReadObject(child, pFile, m_BaseObject.get(), subst, Paths, 0); | Xeromyces_ReadObject(file, child, m_BaseObject.get(), subst, Paths, 0); | ||||
} | } | ||||
} | } | ||||
void CGUI::Xeromyces_ReadRootSprites(XMBElement Element, CXeromyces* pFile) | void CGUI::Xeromyces_ReadRootSprites(const XMBFile& file, XMBElement element) | ||||
{ | { | ||||
for (XMBElement child : Element.GetChildNodes()) | for (XMBElement child : element.GetChildNodes()) | ||||
Xeromyces_ReadSprite(child, pFile); | Xeromyces_ReadSprite(file, child); | ||||
} | } | ||||
void CGUI::Xeromyces_ReadRootStyles(XMBElement Element, CXeromyces* pFile) | void CGUI::Xeromyces_ReadRootStyles(const XMBFile& file, XMBElement element) | ||||
{ | { | ||||
for (XMBElement child : Element.GetChildNodes()) | for (XMBElement child : element.GetChildNodes()) | ||||
Xeromyces_ReadStyle(child, pFile); | Xeromyces_ReadStyle(file, child); | ||||
} | } | ||||
void CGUI::Xeromyces_ReadRootSetup(XMBElement Element, CXeromyces* pFile) | void CGUI::Xeromyces_ReadRootSetup(const XMBFile& file, XMBElement element) | ||||
{ | { | ||||
for (XMBElement child : Element.GetChildNodes()) | for (XMBElement child : element.GetChildNodes()) | ||||
{ | { | ||||
CStr name(pFile->GetElementString(child.GetNodeName())); | CStr name(file.GetElementString(child.GetNodeName())); | ||||
if (name == "scrollbar") | if (name == "scrollbar") | ||||
Xeromyces_ReadScrollBarStyle(child, pFile); | Xeromyces_ReadScrollBarStyle(file, child); | ||||
else if (name == "icon") | else if (name == "icon") | ||||
Xeromyces_ReadIcon(child, pFile); | Xeromyces_ReadIcon(file, child); | ||||
else if (name == "tooltip") | else if (name == "tooltip") | ||||
Xeromyces_ReadTooltip(child, pFile); | Xeromyces_ReadTooltip(file, child); | ||||
else if (name == "color") | else if (name == "color") | ||||
Xeromyces_ReadColor(child, pFile); | Xeromyces_ReadColor(file, child); | ||||
else | else | ||||
debug_warn(L"Invalid data - DTD shouldn't allow this"); | debug_warn(L"Invalid data - DTD shouldn't allow this"); | ||||
} | } | ||||
} | } | ||||
void CGUI::Xeromyces_ReadObject(XMBElement Element, CXeromyces* pFile, IGUIObject* pParent, std::vector<std::pair<CStr, CStr> >& NameSubst, std::unordered_set<VfsPath>& Paths, u32 nesting_depth) | IGUIObject* CGUI::Xeromyces_ReadObject(const XMBFile& file, XMBElement element, IGUIObject* pParent, std::vector<std::pair<CStr, CStr> >& NameSubst, std::unordered_set<VfsPath>& Paths, u32 nesting_depth) | ||||
{ | { | ||||
ENSURE(pParent); | ENSURE(pParent); | ||||
XMBAttributeList attributes = Element.GetAttributes(); | XMBAttributeList attributes = element.GetAttributes(); | ||||
CStr type(attributes.GetNamedItem(pFile->GetAttributeID("type"))); | CStr type(attributes.GetNamedItem(file.GetAttributeID("type"))); | ||||
if (type.empty()) | if (type.empty()) | ||||
type = "empty"; | type = "empty"; | ||||
// Construct object from specified type | // Construct object from specified type | ||||
// henceforth, we need to do a rollback before aborting. | // henceforth, we need to do a rollback before aborting. | ||||
// i.e. releasing this object | // i.e. releasing this object | ||||
IGUIObject* object = ConstructObject(type); | IGUIObject* object = ConstructObject(type); | ||||
if (!object) | if (!object) | ||||
{ | { | ||||
LOGERROR("GUI: Unrecognized object type \"%s\"", type.c_str()); | LOGERROR("GUI: Unrecognized object type \"%s\"", type.c_str()); | ||||
return; | return nullptr; | ||||
} | } | ||||
// Cache some IDs for element attribute names, to avoid string comparisons | // Cache some IDs for element attribute names, to avoid string comparisons | ||||
#define ELMT(x) int elmt_##x = pFile->GetElementID(#x) | #define ELMT(x) int elmt_##x = file.GetElementID(#x) | ||||
#define ATTR(x) int attr_##x = pFile->GetAttributeID(#x) | #define ATTR(x) int attr_##x = file.GetAttributeID(#x) | ||||
ELMT(object); | ELMT(object); | ||||
ELMT(action); | ELMT(action); | ||||
ELMT(script); | ELMT(script); | ||||
ELMT(repeat); | ELMT(repeat); | ||||
ELMT(translatableAttribute); | ELMT(translatableAttribute); | ||||
ELMT(translate); | ELMT(translate); | ||||
ELMT(attribute); | ELMT(attribute); | ||||
ELMT(keep); | ELMT(keep); | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | if (attr.Name == attr_name) | ||||
object->SetName(name); | object->SetName(name); | ||||
NameSet = true; | NameSet = true; | ||||
continue; | continue; | ||||
} | } | ||||
if (attr.Name == attr_z) | if (attr.Name == attr_z) | ||||
ManuallySetZ = true; | ManuallySetZ = true; | ||||
object->SetSettingFromString(pFile->GetAttributeString(attr.Name), attr.Value.FromUTF8(), false); | object->SetSettingFromString(file.GetAttributeString(attr.Name), attr.Value.FromUTF8(), false); | ||||
} | } | ||||
// Check if name isn't set, generate an internal name in that case. | // Check if name isn't set, generate an internal name in that case. | ||||
if (!NameSet) | if (!NameSet) | ||||
{ | { | ||||
object->SetName("__internal(" + CStr::FromInt(m_InternalNameNumber) + ")"); | object->SetName("__internal(" + CStr::FromInt(m_InternalNameNumber) + ")"); | ||||
++m_InternalNameNumber; | ++m_InternalNameNumber; | ||||
} | } | ||||
CStrW caption(Element.GetText().FromUTF8()); | CStrW caption(element.GetText().FromUTF8()); | ||||
if (!caption.empty()) | if (!caption.empty()) | ||||
object->SetSettingFromString("caption", caption, false); | object->SetSettingFromString("caption", caption, false); | ||||
for (XMBElement child : Element.GetChildNodes()) | for (XMBElement child : element.GetChildNodes()) | ||||
{ | { | ||||
// Check what name the elements got | // Check what name the elements got | ||||
int element_name = child.GetNodeName(); | int element_name = child.GetNodeName(); | ||||
if (element_name == elmt_object) | if (element_name == elmt_object) | ||||
{ | { | ||||
// Call this function on the child | // Call this function on the child | ||||
Xeromyces_ReadObject(child, pFile, object, NameSubst, Paths, nesting_depth); | Xeromyces_ReadObject(file, child, object, NameSubst, Paths, nesting_depth); | ||||
} | } | ||||
else if (element_name == elmt_action) | else if (element_name == elmt_action) | ||||
{ | { | ||||
// Scripted <action> element | // Scripted <action> element | ||||
// Check for a 'file' parameter | // Check for a 'file' parameter | ||||
CStrW filename(child.GetAttributes().GetNamedItem(attr_file).FromUTF8()); | CStrW filename(child.GetAttributes().GetNamedItem(attr_file).FromUTF8()); | ||||
Show All 26 Lines | else if (element_name == elmt_action) | ||||
// 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 eventName = child.GetAttributes().GetNamedItem(attr_on); | CStr eventName = child.GetAttributes().GetNamedItem(attr_on); | ||||
object->RegisterScriptHandler(eventName, code, *this); | object->RegisterScriptHandler(eventName, code, *this); | ||||
} | } | ||||
else if (child.GetNodeName() == elmt_script) | else if (child.GetNodeName() == elmt_script) | ||||
{ | { | ||||
Xeromyces_ReadScript(child, pFile, Paths); | Xeromyces_ReadScript(file, child, Paths); | ||||
} | } | ||||
else if (element_name == elmt_repeat) | else if (element_name == elmt_repeat) | ||||
{ | { | ||||
Xeromyces_ReadRepeat(child, pFile, object, NameSubst, Paths, nesting_depth); | Xeromyces_ReadRepeat(file, child, 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>". | ||||
CStr attributeName(child.GetAttributes().GetNamedItem(attr_id)); // Read the attribute name. | CStr attributeName(child.GetAttributes().GetNamedItem(attr_id)); // Read the attribute name. | ||||
if (attributeName.empty()) | if (attributeName.empty()) | ||||
{ | { | ||||
LOGERROR("GUI: 'translatableAttribute' XML element with empty 'id' XML attribute found. (object: %s)", object->GetPresentableName().c_str()); | LOGERROR("GUI: 'translatableAttribute' XML element with empty 'id' XML attribute found. (object: %s)", object->GetPresentableName().c_str()); | ||||
Show All 40 Lines | else if (element_name == elmt_include) | ||||
CStrW directory(child.GetAttributes().GetNamedItem(attr_directory).FromUTF8()); | CStrW directory(child.GetAttributes().GetNamedItem(attr_directory).FromUTF8()); | ||||
if (!filename.empty()) | if (!filename.empty()) | ||||
{ | { | ||||
if (!directory.empty()) | if (!directory.empty()) | ||||
LOGWARNING("GUI: Include element found with file name (%s) and directory name (%s). Only the file will be processed.", utf8_from_wstring(filename), utf8_from_wstring(directory)); | LOGWARNING("GUI: Include element found with file name (%s) and directory name (%s). Only the file will be processed.", utf8_from_wstring(filename), utf8_from_wstring(directory)); | ||||
Paths.insert(filename); | Paths.insert(filename); | ||||
CXeromyces XeroIncluded; | CXeromyces xeroIncluded; | ||||
if (XeroIncluded.Load(g_VFS, filename, "gui") != PSRETURN_OK) | if (xeroIncluded.Load(g_VFS, filename, "gui") != PSRETURN_OK) | ||||
{ | { | ||||
LOGERROR("GUI: Error reading included XML: '%s'", utf8_from_wstring(filename)); | LOGERROR("GUI: Error reading included XML: '%s'", utf8_from_wstring(filename)); | ||||
continue; | continue; | ||||
} | } | ||||
XMBElement node = XeroIncluded.GetRoot(); | XMBElement node = xeroIncluded.GetRoot(); | ||||
if (node.GetNodeName() != XeroIncluded.GetElementID("object")) | if (node.GetNodeName() != xeroIncluded.GetElementID("object")) | ||||
{ | { | ||||
LOGERROR("GUI: Error reading included XML: '%s', root element must have be of type 'object'.", utf8_from_wstring(filename)); | LOGERROR("GUI: Error reading included XML: '%s', root element must have be of type 'object'.", utf8_from_wstring(filename)); | ||||
continue; | continue; | ||||
} | } | ||||
if (nesting_depth+1 >= MAX_OBJECT_DEPTH) | if (nesting_depth+1 >= MAX_OBJECT_DEPTH) | ||||
{ | { | ||||
LOGERROR("GUI: Too many nested GUI includes. Probably caused by a recursive include attribute. Abort rendering '%s'.", utf8_from_wstring(filename)); | LOGERROR("GUI: Too many nested GUI includes. Probably caused by a recursive include attribute. Abort rendering '%s'.", utf8_from_wstring(filename)); | ||||
continue; | continue; | ||||
} | } | ||||
Xeromyces_ReadObject(node, &XeroIncluded, object, NameSubst, Paths, nesting_depth+1); | Xeromyces_ReadObject(xeroIncluded, node, object, NameSubst, Paths, nesting_depth+1); | ||||
} | } | ||||
else if (!directory.empty()) | else if (!directory.empty()) | ||||
{ | { | ||||
if (nesting_depth+1 >= MAX_OBJECT_DEPTH) | if (nesting_depth+1 >= MAX_OBJECT_DEPTH) | ||||
{ | { | ||||
LOGERROR("GUI: Too many nested GUI includes. Probably caused by a recursive include attribute. Abort rendering '%s'.", utf8_from_wstring(directory)); | LOGERROR("GUI: Too many nested GUI includes. Probably caused by a recursive include attribute. Abort rendering '%s'.", utf8_from_wstring(directory)); | ||||
continue; | continue; | ||||
} | } | ||||
VfsPaths pathnames; | VfsPaths pathnames; | ||||
vfs::GetPathnames(g_VFS, directory, L"*.xml", pathnames); | vfs::GetPathnames(g_VFS, directory, L"*.xml", pathnames); | ||||
for (const VfsPath& path : pathnames) | for (const VfsPath& path : pathnames) | ||||
{ | { | ||||
// as opposed to loading scripts, don't care if it's loaded before | // as opposed to loading scripts, don't care if it's loaded before | ||||
// one might use the same parts of the GUI in different situations | // one might use the same parts of the GUI in different situations | ||||
Paths.insert(path); | Paths.insert(path); | ||||
CXeromyces XeroIncluded; | CXeromyces xeroIncluded; | ||||
if (XeroIncluded.Load(g_VFS, path, "gui") != PSRETURN_OK) | if (xeroIncluded.Load(g_VFS, path, "gui") != PSRETURN_OK) | ||||
{ | { | ||||
LOGERROR("GUI: Error reading included XML: '%s'", path.string8()); | LOGERROR("GUI: Error reading included XML: '%s'", path.string8()); | ||||
continue; | continue; | ||||
} | } | ||||
XMBElement node = XeroIncluded.GetRoot(); | XMBElement node = xeroIncluded.GetRoot(); | ||||
if (node.GetNodeName() != XeroIncluded.GetElementID("object")) | if (node.GetNodeName() != xeroIncluded.GetElementID("object")) | ||||
{ | { | ||||
LOGERROR("GUI: Error reading included XML: '%s', root element must have be of type 'object'.", path.string8()); | LOGERROR("GUI: Error reading included XML: '%s', root element must have be of type 'object'.", path.string8()); | ||||
continue; | continue; | ||||
} | } | ||||
Xeromyces_ReadObject(node, &XeroIncluded, object, NameSubst, Paths, nesting_depth+1); | Xeromyces_ReadObject(xeroIncluded, node, object, NameSubst, Paths, nesting_depth+1); | ||||
} | } | ||||
} | } | ||||
else | else | ||||
LOGERROR("GUI: 'include' XML element must have valid 'file' or 'directory' attribute found. (object %s)", object->GetPresentableName().c_str()); | LOGERROR("GUI: 'include' XML element must have valid 'file' or 'directory' attribute found. (object %s)", object->GetPresentableName().c_str()); | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
// Try making the object read the tag. | // Try making the object read the tag. | ||||
if (!object->HandleAdditionalChildren(child, pFile)) | if (!object->HandleAdditionalChildren(file, child)) | ||||
LOGERROR("GUI: (object: %s) Reading unknown children for its type", object->GetPresentableName().c_str()); | LOGERROR("GUI: (object: %s) Reading unknown children for its type", object->GetPresentableName().c_str()); | ||||
} | } | ||||
} | } | ||||
object->AdditionalChildrenHandled(); | object->AdditionalChildrenHandled(); | ||||
if (!ManuallySetZ) | if (!ManuallySetZ) | ||||
{ | { | ||||
// Set it automatically to 10 plus its parents | // Set it automatically to 10 plus its parents | ||||
if (object->m_Absolute) | if (object->m_Absolute) | ||||
// If the object is absolute, we'll have to get the parent's Z buffered, | // If the object is absolute, we'll have to get the parent's Z buffered, | ||||
// and add to that! | // and add to that! | ||||
object->SetSetting<float>("z", pParent->GetBufferedZ() + 10.f, false); | object->SetSetting<float>("z", pParent->GetBufferedZ() + 10.f, false); | ||||
else | else | ||||
// If the object is relative, then we'll just store Z as "10" | // If the object is relative, then we'll just store Z as "10" | ||||
object->SetSetting<float>("z", 10.f, false); | object->SetSetting<float>("z", 10.f, false); | ||||
} | } | ||||
if (!AddObject(*pParent, *object)) | if (!AddObject(*pParent, *object)) | ||||
{ | |||||
delete object; | delete object; | ||||
return nullptr; | |||||
} | |||||
return object; | |||||
} | } | ||||
void CGUI::Xeromyces_ReadRepeat(XMBElement Element, CXeromyces* pFile, IGUIObject* pParent, std::vector<std::pair<CStr, CStr> >& NameSubst, std::unordered_set<VfsPath>& Paths, u32 nesting_depth) | void CGUI::Xeromyces_ReadRepeat(const XMBFile& file, XMBElement element, IGUIObject* pParent, std::vector<std::pair<CStr, CStr> >& NameSubst, std::unordered_set<VfsPath>& Paths, u32 nesting_depth) | ||||
{ | { | ||||
#define ELMT(x) int elmt_##x = pFile->GetElementID(#x) | #define ELMT(x) int elmt_##x = file.GetElementID(#x) | ||||
#define ATTR(x) int attr_##x = pFile->GetAttributeID(#x) | #define ATTR(x) int attr_##x = file.GetAttributeID(#x) | ||||
ELMT(object); | ELMT(object); | ||||
ATTR(count); | ATTR(count); | ||||
ATTR(var); | ATTR(var); | ||||
XMBAttributeList attributes = Element.GetAttributes(); | XMBAttributeList attributes = element.GetAttributes(); | ||||
int count = CStr(attributes.GetNamedItem(attr_count)).ToInt(); | int count = CStr(attributes.GetNamedItem(attr_count)).ToInt(); | ||||
CStr var("["+attributes.GetNamedItem(attr_var)+"]"); | CStr var("["+attributes.GetNamedItem(attr_var)+"]"); | ||||
if (var.size() < 3) | if (var.size() < 3) | ||||
var = "[n]"; | var = "[n]"; | ||||
for (int n = 0; n < count; ++n) | for (int n = 0; n < count; ++n) | ||||
{ | { | ||||
NameSubst.emplace_back(var, "[" + CStr::FromInt(n) + "]"); | NameSubst.emplace_back(var, "[" + CStr::FromInt(n) + "]"); | ||||
XERO_ITER_EL(Element, child) | XERO_ITER_EL(element, child) | ||||
{ | { | ||||
if (child.GetNodeName() == elmt_object) | if (child.GetNodeName() == elmt_object) | ||||
Xeromyces_ReadObject(child, pFile, pParent, NameSubst, Paths, nesting_depth); | Xeromyces_ReadObject(file, child, pParent, NameSubst, Paths, nesting_depth); | ||||
} | } | ||||
NameSubst.pop_back(); | NameSubst.pop_back(); | ||||
} | } | ||||
} | } | ||||
void CGUI::Xeromyces_ReadScript(XMBElement Element, CXeromyces* pFile, std::unordered_set<VfsPath>& Paths) | void CGUI::Xeromyces_ReadScript(const XMBFile& file, XMBElement element, std::unordered_set<VfsPath>& Paths) | ||||
{ | { | ||||
// Check for a 'file' parameter | // Check for a 'file' parameter | ||||
CStrW file(Element.GetAttributes().GetNamedItem(pFile->GetAttributeID("file")).FromUTF8()); | CStrW fileAttr(element.GetAttributes().GetNamedItem(file.GetAttributeID("file")).FromUTF8()); | ||||
// If there is a file specified, open and execute it | // If there is a file specified, open and execute it | ||||
if (!file.empty()) | if (!fileAttr.empty()) | ||||
{ | { | ||||
if (!VfsPath(file).IsDirectory()) | if (!VfsPath(fileAttr).IsDirectory()) | ||||
{ | { | ||||
Paths.insert(file); | Paths.insert(fileAttr); | ||||
m_ScriptInterface->LoadGlobalScriptFile(file); | m_ScriptInterface->LoadGlobalScriptFile(fileAttr); | ||||
} | } | ||||
else | else | ||||
LOGERROR("GUI: Script path %s is not a file path", file.ToUTF8().c_str()); | LOGERROR("GUI: Script path %s is not a file path", fileAttr.ToUTF8().c_str()); | ||||
} | } | ||||
// If it has a directory attribute, read all JS files in that directory | // If it has a directory attribute, read all JS files in that directory | ||||
CStrW directory(Element.GetAttributes().GetNamedItem(pFile->GetAttributeID("directory")).FromUTF8()); | CStrW directoryAttr(element.GetAttributes().GetNamedItem(file.GetAttributeID("directory")).FromUTF8()); | ||||
if (!directory.empty()) | if (!directoryAttr.empty()) | ||||
{ | { | ||||
if (VfsPath(directory).IsDirectory()) | if (VfsPath(directoryAttr).IsDirectory()) | ||||
{ | { | ||||
VfsPaths pathnames; | VfsPaths pathnames; | ||||
vfs::GetPathnames(g_VFS, directory, L"*.js", pathnames); | vfs::GetPathnames(g_VFS, directoryAttr, L"*.js", pathnames); | ||||
for (const VfsPath& path : pathnames) | for (const VfsPath& path : pathnames) | ||||
{ | { | ||||
// Only load new files (so when the insert succeeds) | // Only load new files (so when the insert succeeds) | ||||
if (Paths.insert(path).second) | if (Paths.insert(path).second) | ||||
m_ScriptInterface->LoadGlobalScriptFile(path); | m_ScriptInterface->LoadGlobalScriptFile(path); | ||||
} | } | ||||
} | } | ||||
else | else | ||||
LOGERROR("GUI: Script path %s is not a directory path", directory.ToUTF8().c_str()); | LOGERROR("GUI: Script path %s is not a directory path", directoryAttr.ToUTF8().c_str()); | ||||
} | } | ||||
CStr code(Element.GetText()); | CStr code(element.GetText()); | ||||
if (!code.empty()) | if (!code.empty()) | ||||
m_ScriptInterface->LoadGlobalScript(L"Some XML file", code); | m_ScriptInterface->LoadGlobalScript(L"Some XML file", code); | ||||
} | } | ||||
void CGUI::Xeromyces_ReadSprite(XMBElement Element, CXeromyces* pFile) | void CGUI::Xeromyces_ReadSprite(const XMBFile& file, XMBElement element) | ||||
{ | { | ||||
CGUISprite* Sprite = new CGUISprite; | CGUISprite* Sprite = new CGUISprite; | ||||
// Get name, we know it exists because of DTD requirements | // Get name, we know it exists because of DTD requirements | ||||
CStr name = Element.GetAttributes().GetNamedItem(pFile->GetAttributeID("name")); | CStr name = element.GetAttributes().GetNamedItem(file.GetAttributeID("name")); | ||||
if (m_Sprites.find(name) != m_Sprites.end()) | if (m_Sprites.find(name) != m_Sprites.end()) | ||||
LOGWARNING("GUI sprite name '%s' used more than once; first definition will be discarded", name.c_str()); | LOGWARNING("GUI sprite name '%s' used more than once; first definition will be discarded", name.c_str()); | ||||
// shared_ptr to link the effect to every image, faster than copy. | // shared_ptr to link the effect to every image, faster than copy. | ||||
std::shared_ptr<SGUIImageEffects> effects; | std::shared_ptr<SGUIImageEffects> effects; | ||||
for (XMBElement child : Element.GetChildNodes()) | for (XMBElement child : element.GetChildNodes()) | ||||
{ | { | ||||
CStr ElementName(pFile->GetElementString(child.GetNodeName())); | CStr ElementName(file.GetElementString(child.GetNodeName())); | ||||
if (ElementName == "image") | if (ElementName == "image") | ||||
Xeromyces_ReadImage(child, pFile, *Sprite); | Xeromyces_ReadImage(file, child, *Sprite); | ||||
else if (ElementName == "effect") | else if (ElementName == "effect") | ||||
{ | { | ||||
if (effects) | if (effects) | ||||
LOGERROR("GUI <sprite> must not have more than one <effect>"); | LOGERROR("GUI <sprite> must not have more than one <effect>"); | ||||
else | else | ||||
{ | { | ||||
effects = std::make_shared<SGUIImageEffects>(); | effects = std::make_shared<SGUIImageEffects>(); | ||||
Xeromyces_ReadEffects(child, pFile, *effects); | Xeromyces_ReadEffects(file, child, *effects); | ||||
} | } | ||||
} | } | ||||
else | else | ||||
debug_warn(L"Invalid data - DTD shouldn't allow this"); | debug_warn(L"Invalid data - DTD shouldn't allow this"); | ||||
} | } | ||||
// Apply the effects to every image (unless the image overrides it with | // Apply the effects to every image (unless the image overrides it with | ||||
// different effects) | // different effects) | ||||
if (effects) | if (effects) | ||||
for (SGUIImage* const& img : Sprite->m_Images) | for (SGUIImage* const& img : Sprite->m_Images) | ||||
if (!img->m_Effects) | if (!img->m_Effects) | ||||
img->m_Effects = effects; | img->m_Effects = effects; | ||||
m_Sprites.erase(name); | m_Sprites.erase(name); | ||||
m_Sprites.emplace(name, Sprite); | m_Sprites.emplace(name, Sprite); | ||||
} | } | ||||
void CGUI::Xeromyces_ReadImage(XMBElement Element, CXeromyces* pFile, CGUISprite& parent) | void CGUI::Xeromyces_ReadImage(const XMBFile& file, XMBElement element, CGUISprite& parent) | ||||
{ | { | ||||
SGUIImage* Image = new SGUIImage(); | SGUIImage* Image = new SGUIImage(); | ||||
// TODO Gee: Setup defaults here (or maybe they are in the SGUIImage ctor) | // TODO Gee: Setup defaults here (or maybe they are in the SGUIImage ctor) | ||||
for (XMBAttribute attr : Element.GetAttributes()) | for (XMBAttribute attr : element.GetAttributes()) | ||||
{ | { | ||||
CStr attr_name(pFile->GetAttributeString(attr.Name)); | CStr attr_name(file.GetAttributeString(attr.Name)); | ||||
CStrW attr_value(attr.Value.FromUTF8()); | CStrW attr_value(attr.Value.FromUTF8()); | ||||
if (attr_name == "texture") | if (attr_name == "texture") | ||||
{ | { | ||||
Image->m_TextureName = VfsPath("art/textures/ui") / attr_value; | Image->m_TextureName = VfsPath("art/textures/ui") / attr_value; | ||||
} | } | ||||
else if (attr_name == "size") | else if (attr_name == "size") | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | else if (attr_name == "border") | ||||
else | else | ||||
Image->m_Border = b; | Image->m_Border = b; | ||||
} | } | ||||
else | else | ||||
debug_warn(L"Invalid data - DTD shouldn't allow this"); | debug_warn(L"Invalid data - DTD shouldn't allow this"); | ||||
} | } | ||||
// Look for effects | // Look for effects | ||||
for (XMBElement child : Element.GetChildNodes()) | for (XMBElement child : element.GetChildNodes()) | ||||
{ | { | ||||
CStr ElementName(pFile->GetElementString(child.GetNodeName())); | CStr ElementName(file.GetElementString(child.GetNodeName())); | ||||
if (ElementName == "effect") | if (ElementName == "effect") | ||||
{ | { | ||||
if (Image->m_Effects) | if (Image->m_Effects) | ||||
LOGERROR("GUI <image> must not have more than one <effect>"); | LOGERROR("GUI <image> must not have more than one <effect>"); | ||||
else | else | ||||
{ | { | ||||
Image->m_Effects = std::make_shared<SGUIImageEffects>(); | Image->m_Effects = std::make_shared<SGUIImageEffects>(); | ||||
Xeromyces_ReadEffects(child, pFile, *Image->m_Effects); | Xeromyces_ReadEffects(file, child, *Image->m_Effects); | ||||
} | } | ||||
} | } | ||||
else | else | ||||
debug_warn(L"Invalid data - DTD shouldn't allow this"); | debug_warn(L"Invalid data - DTD shouldn't allow this"); | ||||
} | } | ||||
parent.AddImage(Image); | parent.AddImage(Image); | ||||
} | } | ||||
void CGUI::Xeromyces_ReadEffects(XMBElement Element, CXeromyces* pFile, SGUIImageEffects& effects) | void CGUI::Xeromyces_ReadEffects(const XMBFile& file, XMBElement element, SGUIImageEffects& effects) | ||||
{ | { | ||||
for (XMBAttribute attr : Element.GetAttributes()) | for (XMBAttribute attr : element.GetAttributes()) | ||||
{ | { | ||||
CStr attr_name(pFile->GetAttributeString(attr.Name)); | CStr attr_name(file.GetAttributeString(attr.Name)); | ||||
if (attr_name == "add_color") | if (attr_name == "add_color") | ||||
{ | { | ||||
if (!effects.m_AddColor.ParseString(*this, attr.Value, 0)) | if (!effects.m_AddColor.ParseString(*this, attr.Value, 0)) | ||||
LOGERROR("GUI: Error parsing '%s' (\"%s\")", attr_name, attr.Value); | LOGERROR("GUI: Error parsing '%s' (\"%s\")", attr_name, attr.Value); | ||||
} | } | ||||
else if (attr_name == "grayscale") | else if (attr_name == "grayscale") | ||||
effects.m_Greyscale = true; | effects.m_Greyscale = true; | ||||
else | else | ||||
debug_warn(L"Invalid data - DTD shouldn't allow this"); | debug_warn(L"Invalid data - DTD shouldn't allow this"); | ||||
} | } | ||||
} | } | ||||
void CGUI::Xeromyces_ReadStyle(XMBElement Element, CXeromyces* pFile) | void CGUI::Xeromyces_ReadStyle(const XMBFile& file, XMBElement element) | ||||
{ | { | ||||
SGUIStyle style; | SGUIStyle style; | ||||
CStr name; | CStr name; | ||||
for (XMBAttribute attr : Element.GetAttributes()) | for (XMBAttribute attr : element.GetAttributes()) | ||||
{ | { | ||||
CStr attr_name(pFile->GetAttributeString(attr.Name)); | CStr attr_name(file.GetAttributeString(attr.Name)); | ||||
// The "name" setting is actually the name of the style | // The "name" setting is actually the name of the style | ||||
// and not a new default | // and not a new default | ||||
if (attr_name == "name") | if (attr_name == "name") | ||||
name = attr.Value; | name = attr.Value; | ||||
else | else | ||||
style.m_SettingsDefaults.emplace(attr_name, attr.Value.FromUTF8()); | style.m_SettingsDefaults.emplace(attr_name, attr.Value.FromUTF8()); | ||||
} | } | ||||
m_Styles.erase(name); | m_Styles.erase(name); | ||||
m_Styles.emplace(name, std::move(style)); | m_Styles.emplace(name, std::move(style)); | ||||
} | } | ||||
void CGUI::Xeromyces_ReadScrollBarStyle(XMBElement Element, CXeromyces* pFile) | void CGUI::Xeromyces_ReadScrollBarStyle(const XMBFile& file, XMBElement element) | ||||
{ | { | ||||
SGUIScrollBarStyle scrollbar; | SGUIScrollBarStyle scrollbar; | ||||
CStr name; | CStr name; | ||||
// Setup some defaults. | // Setup some defaults. | ||||
scrollbar.m_MinimumBarSize = 0.f; | scrollbar.m_MinimumBarSize = 0.f; | ||||
// Using 1.0e10 as a substitute for infinity | // Using 1.0e10 as a substitute for infinity | ||||
scrollbar.m_MaximumBarSize = 1.0e10; | scrollbar.m_MaximumBarSize = 1.0e10; | ||||
scrollbar.m_UseEdgeButtons = false; | scrollbar.m_UseEdgeButtons = false; | ||||
for (XMBAttribute attr : Element.GetAttributes()) | for (XMBAttribute attr : element.GetAttributes()) | ||||
{ | { | ||||
CStr attr_name = pFile->GetAttributeString(attr.Name); | CStr attr_name = file.GetAttributeString(attr.Name); | ||||
CStr attr_value(attr.Value); | CStr attr_value(attr.Value); | ||||
if (attr_value == "null") | if (attr_value == "null") | ||||
continue; | continue; | ||||
if (attr_name == "name") | if (attr_name == "name") | ||||
name = attr_value; | name = attr_value; | ||||
else if (attr_name == "show_edge_buttons") | else if (attr_name == "show_edge_buttons") | ||||
▲ Show 20 Lines • Show All 53 Lines • ▼ Show 20 Lines | for (XMBAttribute attr : element.GetAttributes()) | ||||
else if (attr_name == "sprite_bar_vertical_pressed") | else if (attr_name == "sprite_bar_vertical_pressed") | ||||
scrollbar.m_SpriteBarVerticalPressed = attr_value; | scrollbar.m_SpriteBarVerticalPressed = attr_value; | ||||
} | } | ||||
m_ScrollBarStyles.erase(name); | m_ScrollBarStyles.erase(name); | ||||
m_ScrollBarStyles.emplace(name, std::move(scrollbar)); | m_ScrollBarStyles.emplace(name, std::move(scrollbar)); | ||||
} | } | ||||
void CGUI::Xeromyces_ReadIcon(XMBElement Element, CXeromyces* pFile) | void CGUI::Xeromyces_ReadIcon(const XMBFile& file, XMBElement element) | ||||
{ | { | ||||
SGUIIcon icon; | SGUIIcon icon; | ||||
CStr name; | CStr name; | ||||
for (XMBAttribute attr : Element.GetAttributes()) | for (XMBAttribute attr : element.GetAttributes()) | ||||
{ | { | ||||
CStr attr_name(pFile->GetAttributeString(attr.Name)); | CStr attr_name(file.GetAttributeString(attr.Name)); | ||||
CStr attr_value(attr.Value); | CStr attr_value(attr.Value); | ||||
if (attr_value == "null") | if (attr_value == "null") | ||||
continue; | continue; | ||||
if (attr_name == "name") | if (attr_name == "name") | ||||
name = attr_value; | name = attr_value; | ||||
else if (attr_name == "sprite") | else if (attr_name == "sprite") | ||||
Show All 9 Lines | for (XMBAttribute attr : element.GetAttributes()) | ||||
else | else | ||||
debug_warn(L"Invalid data - DTD shouldn't allow this"); | debug_warn(L"Invalid data - DTD shouldn't allow this"); | ||||
} | } | ||||
m_Icons.erase(name); | m_Icons.erase(name); | ||||
m_Icons.emplace(name, std::move(icon)); | m_Icons.emplace(name, std::move(icon)); | ||||
} | } | ||||
void CGUI::Xeromyces_ReadTooltip(XMBElement Element, CXeromyces* pFile) | void CGUI::Xeromyces_ReadTooltip(const XMBFile& file, XMBElement element) | ||||
{ | { | ||||
IGUIObject* object = new CTooltip(*this); | IGUIObject* object = new CTooltip(*this); | ||||
for (XMBAttribute attr : Element.GetAttributes()) | for (XMBAttribute attr : element.GetAttributes()) | ||||
{ | { | ||||
CStr attr_name(pFile->GetAttributeString(attr.Name)); | CStr attr_name(file.GetAttributeString(attr.Name)); | ||||
CStr attr_value(attr.Value); | CStr attr_value(attr.Value); | ||||
if (attr_name == "name") | if (attr_name == "name") | ||||
object->SetName("__tooltip_" + attr_value); | object->SetName("__tooltip_" + attr_value); | ||||
else | else | ||||
object->SetSettingFromString(attr_name, attr_value.FromUTF8(), true); | object->SetSettingFromString(attr_name, attr_value.FromUTF8(), true); | ||||
} | } | ||||
if (!AddObject(*m_BaseObject, *object)) | if (!AddObject(*m_BaseObject, *object)) | ||||
delete object; | delete object; | ||||
} | } | ||||
void CGUI::Xeromyces_ReadColor(XMBElement Element, CXeromyces* pFile) | void CGUI::Xeromyces_ReadColor(const XMBFile& file, XMBElement element) | ||||
{ | { | ||||
XMBAttributeList attributes = Element.GetAttributes(); | XMBAttributeList attributes = element.GetAttributes(); | ||||
CStr name = attributes.GetNamedItem(pFile->GetAttributeID("name")); | CStr name = attributes.GetNamedItem(file.GetAttributeID("name")); | ||||
// Try parsing value | // Try parsing value | ||||
CStr value(Element.GetText()); | CStr value(element.GetText()); | ||||
if (value.empty()) | if (value.empty()) | ||||
return; | return; | ||||
CColor color; | CColor color; | ||||
if (color.ParseString(value)) | if (color.ParseString(value)) | ||||
{ | { | ||||
m_PreDefinedColors.erase(name); | m_PreDefinedColors.erase(name); | ||||
m_PreDefinedColors.emplace( | m_PreDefinedColors.emplace( | ||||
std::piecewise_construct, | std::piecewise_construct, | ||||
std::forward_as_tuple(name), | std::forward_as_tuple(name), | ||||
std::forward_as_tuple(color.r, color.g, color.b, color.a)); | std::forward_as_tuple(color.r, color.g, color.b, color.a)); | ||||
} | } | ||||
else | else | ||||
LOGERROR("GUI: Unable to create custom color '%s'. Invalid color syntax.", name.c_str()); | LOGERROR("GUI: Unable to create custom color '%s'. Invalid color syntax.", name.c_str()); | ||||
} | } |
Wildfire Games · Phabricator
It’s bad to have a simulation dependency in GUI without a reason, move the header to some common place.