Use variadic template for RecurseObject from c2a71e41bf and move it from GUIutil to IGUIObject.

Unifies the four copies of the function while making it more versatile.
Supersede the hardcoded GUIRR enum and CheckIfRestricted function with a
flexible member function pointer.
Replace the custom IGUIObject iterator with a casually iterable
GetChildren getter, so that it is more obvious what it iterates on.
Implement TODO from e21ebb37f5, i.e. RecurseObject not iterating over
the base object.

Differential Revision: https://code.wildfiregames.com/D2204
Tested on: gcc 9.1.0, clang 8.0.1, VS2015

This was SVN commit r22744.
This commit is contained in:
elexis 2019-08-21 13:22:25 +00:00
parent 0a29e04f1c
commit d4d5187c9d
9 changed files with 74 additions and 196 deletions

View File

@ -95,9 +95,7 @@ InReaction CGUI::HandleEvent(const SDL_Event_* ev)
m_MousePos = CPos((float)ev->ev.motion.x / g_GuiScale, (float)ev->ev.motion.y / g_GuiScale);
SGUIMessage msg(GUIM_MOUSE_MOTION);
GUI<SGUIMessage>::RecurseObject(GUIRR_HIDDEN | GUIRR_GHOST, m_BaseObject,
&IGUIObject::HandleMessage,
msg);
m_BaseObject->RecurseObject(&IGUIObject::IsHiddenOrGhost, &IGUIObject::HandleMessage, msg);
}
// Update m_MouseButtons. (BUTTONUP is handled later.)
@ -138,8 +136,7 @@ InReaction CGUI::HandleEvent(const SDL_Event_* ev)
// Now we'll call UpdateMouseOver on *all* objects,
// we'll input the one hovered, and they will each
// update their own data and send messages accordingly
GUI<IGUIObject*>::RecurseObject(GUIRR_HIDDEN | GUIRR_GHOST,
m_BaseObject, &IGUIObject::UpdateMouseOver, pNearest);
m_BaseObject->RecurseObject(&IGUIObject::IsHiddenOrGhost, &IGUIObject::UpdateMouseOver, static_cast<IGUIObject* const&>(pNearest));
if (ev->ev.type == SDL_MOUSEBUTTONDOWN)
{
@ -200,12 +197,10 @@ InReaction CGUI::HandleEvent(const SDL_Event_* ev)
}
// Reset all states on all visible objects
GUI<>::RecurseObject(GUIRR_HIDDEN, m_BaseObject,
&IGUIObject::ResetStates);
m_BaseObject->RecurseObject(&IGUIObject::IsHidden, &IGUIObject::ResetStates);
// Since the hover state will have been reset, we reload it.
GUI<IGUIObject*>::RecurseObject(GUIRR_HIDDEN | GUIRR_GHOST,
m_BaseObject, &IGUIObject::UpdateMouseOver, pNearest);
m_BaseObject->RecurseObject(&IGUIObject::IsHiddenOrGhost, &IGUIObject::UpdateMouseOver, static_cast<IGUIObject* const&>(pNearest));
}
}
catch (PSERROR_GUI& e)
@ -257,9 +252,8 @@ InReaction CGUI::HandleEvent(const SDL_Event_* ev)
void CGUI::TickObjects()
{
CStr action = "tick";
GUI<CStr>::RecurseObject(0, m_BaseObject,
&IGUIObject::ScriptEvent, action);
const CStr action = "tick";
m_BaseObject->RecurseObject(nullptr, &IGUIObject::ScriptEvent, action);
m_Tooltip.Update(FindObjectUnderMouse(), m_MousePos, *this);
}
@ -273,12 +267,14 @@ void CGUI::SendEventToAll(const CStr& EventName)
// (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.
GUI<CStr>::RecurseObject(0, m_BaseObject, &IGUIObject::ScriptEvent, EventName.LowerCase());
const CStr EventNameLower = EventName.LowerCase();
m_BaseObject->RecurseObject(nullptr, &IGUIObject::ScriptEvent, EventNameLower);
}
void CGUI::SendEventToAll(const CStr& EventName, JS::HandleValueArray paramData)
void CGUI::SendEventToAll(const CStr& EventName, const JS::HandleValueArray& paramData)
{
GUI<CStr>::RecurseObject(0, m_BaseObject, &IGUIObject::ScriptEvent, EventName.LowerCase(), paramData);
const CStr EventNameLower = EventName.LowerCase();
m_BaseObject->RecurseObject(nullptr, &IGUIObject::ScriptEvent, EventNameLower, paramData);
}
CGUI::CGUI(const shared_ptr<ScriptRuntime>& runtime)
@ -340,9 +336,7 @@ void CGUI::Draw()
try
{
// Recurse IGUIObject::Draw() with restriction: hidden
// meaning all hidden objects won't call Draw (nor will it recurse its children)
GUI<>::RecurseObject(GUIRR_HIDDEN, m_BaseObject, &IGUIObject::Draw);
m_BaseObject->RecurseObject(&IGUIObject::IsHidden, &IGUIObject::Draw);
}
catch (PSERROR_GUI& e)
{
@ -391,7 +385,7 @@ void CGUI::Destroy()
void CGUI::UpdateResolution()
{
// Update ALL cached
GUI<>::RecurseObject(0, m_BaseObject, &IGUIObject::UpdateCachedSize);
m_BaseObject->RecurseObject(nullptr, &IGUIObject::UpdateCachedSize);
}
void CGUI::AddObject(IGUIObject* pObject)
@ -401,10 +395,10 @@ void CGUI::AddObject(IGUIObject* pObject)
m_BaseObject->AddChild(pObject);
// Cache tree
GUI<>::RecurseObject(0, pObject, &IGUIObject::UpdateCachedSize);
pObject->RecurseObject(nullptr, &IGUIObject::UpdateCachedSize);
SGUIMessage msg(GUIM_LOAD);
GUI<SGUIMessage>::RecurseObject(0, pObject, &IGUIObject::HandleMessage, msg);
pObject->RecurseObject(nullptr, &IGUIObject::HandleMessage, msg);
}
catch (PSERROR_GUI&)
{
@ -420,7 +414,7 @@ void CGUI::UpdateObjects()
try
{
// Fill freshly
GUI<map_pObjects>::RecurseObject(0, m_BaseObject, &IGUIObject::AddToPointersMap, AllObjects);
m_BaseObject->RecurseObject(nullptr, &IGUIObject::AddToPointersMap, AllObjects);
}
catch (PSERROR_GUI&)
{
@ -448,10 +442,7 @@ IGUIObject* CGUI::FindObjectByName(const CStr& Name) const
IGUIObject* CGUI::FindObjectUnderMouse() const
{
IGUIObject* pNearest = NULL;
GUI<IGUIObject*>::RecurseObject(GUIRR_HIDDEN | GUIRR_GHOST, m_BaseObject,
&IGUIObject::ChooseMouseOverAndClosest, pNearest);
m_BaseObject->RecurseObject(&IGUIObject::IsHiddenOrGhost, &IGUIObject::ChooseMouseOverAndClosest, pNearest);
return pNearest;
}

View File

@ -103,7 +103,7 @@ public:
* @param EventName String representation of event name
* @param paramData JS::HandleValueArray storing the arguments passed to the event handler.
*/
void SendEventToAll(const CStr& EventName, JS::HandleValueArray paramData);
void SendEventToAll(const CStr& EventName, const JS::HandleValueArray& paramData);
/**
* Displays the whole GUI

View File

@ -35,7 +35,7 @@ void CRadioButton::HandleMessage(SGUIMessage& Message)
switch (Message.type)
{
case GUIM_PRESSED:
for (IGUIObject* const& obj : *GetParent())
for (IGUIObject* const& obj : GetParent()->GetChildren())
{
// Notice, if you use other objects within the parent object that has got
// the setting "checked", it too will change. Hence NO OTHER OBJECTS THAN

View File

@ -125,20 +125,6 @@ struct SGUIMessage
bool skipped;
};
/**
* Recurse restrictions, when we recurse, if an object
* is hidden for instance, you might want it to skip
* the children also
* Notice these are flags! and we don't really need one
* for no restrictions, because then you'll just enter 0
*/
enum
{
GUIRR_HIDDEN = 0x00000001,
GUIRR_DISABLED = 0x00000010,
GUIRR_GHOST = 0x00000100
};
// Text alignments
enum EAlign { EAlign_Left, EAlign_Right, EAlign_Center };
enum EVAlign { EVAlign_Top, EVAlign_Bottom, EVAlign_Center };

View File

@ -164,12 +164,12 @@ PSRETURN GUI<T>::SetSettingWrap(IGUIObject* pObject, const CStr& Setting, const
// If setting was "size", we need to re-cache itself and all children
if (Setting == "size")
{
RecurseObject(0, pObject, &IGUIObject::UpdateCachedSize);
pObject->RecurseObject(nullptr, &IGUIObject::UpdateCachedSize);
}
else if (Setting == "hidden")
{
// Hiding an object requires us to reset it and all children
RecurseObject(0, pObject, &IGUIObject::ResetStates);
pObject->RecurseObject(nullptr, &IGUIObject::ResetStates);
}
if (!SkipMessage)

View File

@ -181,144 +181,6 @@ private:
* Updates some internal data depending on the setting changed.
*/
static PSRETURN SetSettingWrap(IGUIObject* pObject, const CStr& Setting, const bool& SkipMessage, const std::function<void()>& valueSet);
// templated typedef of function pointer
typedef void (IGUIObject::*void_Object_pFunction_argT)(const T& arg);
typedef void (IGUIObject::*void_Object_pFunction_argRefT)(T& arg);
typedef void (IGUIObject::*void_Object_pFunction)();
typedef void (IGUIObject::*void_Object_pFunction_argTJS)(const T& arg, JS::HandleValueArray paramData);
/**
* If you want to call a IGUIObject-function
* on not just an object, but also on ALL of their children
* you want to use this recursion system.
* It recurses an object calling a function on itself
* and all children (and so forth).
*
* <b>Restrictions:</b>\n
* You can also set restrictions, so that if the recursion
* reaches an objects with certain setup, it just doesn't
* call the function on the object, nor it's children for
* that matter. i.e. it cuts that object off from the
* recursion tree. What setups that can cause restrictions
* are hardcoded and specific. Check out the defines
* GUIRR_* for all different setups.
*
* Error reports are either logged or thrown out of RecurseObject.
* Always use it with try/catch!
*
* @param RR Recurse Restrictions, set to 0 if no restrictions
* @param pObject Top object, this is where the iteration starts
* @param pFunc Function to recurse
* @param Argument Argument for pFunc of type T
* @throws PSERROR Depends on what pFunc might throw. PSERROR is standard.
* Itself doesn't throw anything.
*/
static void RecurseObject(int RR, IGUIObject* pObject, void_Object_pFunction_argT pFunc, const T& Argument)
{
// TODO Gee: Don't run this for the base object.
if (CheckIfRestricted(RR, pObject))
return;
(pObject->*pFunc)(Argument);
// Iterate children
for (IGUIObject* const& obj : *pObject)
RecurseObject(RR, obj, pFunc, Argument);
}
/**
* Argument is reference.
*
* @see RecurseObject()
*/
static void RecurseObject(int RR, IGUIObject* pObject, void_Object_pFunction_argRefT pFunc, T& Argument)
{
if (CheckIfRestricted(RR, pObject))
return;
(pObject->*pFunc)(Argument);
// Iterate children
for (IGUIObject* const& obj : *pObject)
RecurseObject(RR, obj, pFunc, Argument);
}
static void RecurseObject(int RR, IGUIObject* pObject, void_Object_pFunction_argTJS pFunc, const T& Argument, JS::HandleValueArray paramData)
{
if (CheckIfRestricted(RR, pObject))
return;
(pObject->*pFunc)(Argument, paramData);
// Iterate children
for (IGUIObject* const& obj : *pObject)
RecurseObject(RR, obj, pFunc, Argument, paramData);
}
/**
* With no argument.
*
* @see RecurseObject()
*/
static void RecurseObject(int RR, IGUIObject* pObject, void_Object_pFunction pFunc)
{
if (CheckIfRestricted(RR, pObject))
return;
(pObject->*pFunc)();
// Iterate children
for (IGUIObject* const& obj : *pObject)
RecurseObject(RR, obj, pFunc);
}
/**
* Checks restrictions for the iteration, for instance if
* you tell the recursor to avoid all hidden objects, it
* will, and this function checks a certain object's
* restriction values.
*
* @param RR What kind of restriction, for instance hidden or disabled
* @param pObject Object
* @return true if restricted
*/
static bool CheckIfRestricted(int RR, IGUIObject* pObject)
{
// Statically initialise some strings, so we don't have to do
// lots of allocation every time this function is called
static const CStr strHidden("hidden");
static const CStr strEnabled("enabled");
static const CStr strGhost("ghost");
if (RR & GUIRR_HIDDEN)
{
bool hidden = true;
GUI<bool>::GetSetting(pObject, strHidden, hidden);
if (hidden)
return true;
}
if (RR & GUIRR_DISABLED)
{
bool enabled = false;
GUI<bool>::GetSetting(pObject, strEnabled, enabled);
if (!enabled)
return true;
}
if (RR & GUIRR_GHOST)
{
bool ghost = true;
GUI<bool>::GetSetting(pObject, strGhost, ghost);
if (ghost)
return true;
}
// false means not restricted
return false;
}
};
#endif // INCLUDED_GUIUTIL

View File

@ -92,7 +92,7 @@ protected:
virtual void ResetStates()
{
// Notify the gui that we aren't hovered anymore
UpdateMouseOver(NULL);
UpdateMouseOver(nullptr);
m_Pressed = false;
m_PressedRight = false;
}

View File

@ -390,7 +390,7 @@ void IGUIObject::ScriptEvent(const CStr& Action)
}
}
void IGUIObject::ScriptEvent(const CStr& Action, JS::HandleValueArray paramData)
void IGUIObject::ScriptEvent(const CStr& Action, const JS::HandleValueArray& paramData)
{
std::map<CStr, JS::Heap<JSObject*> >::iterator it = m_ScriptHandlers.find(Action);
if (it == m_ScriptHandlers.end())
@ -425,6 +425,20 @@ JSObject* IGUIObject::GetJSObject()
return m_JSObject.get();
}
bool IGUIObject::IsHidden()
{
// Statically initialise some strings, so we don't have to do
// lots of allocation every time this function is called
static const CStr strHidden("hidden");
return GUI<bool>::GetSetting(this, strHidden);
}
bool IGUIObject::IsHiddenOrGhost()
{
static const CStr strGhost("ghost");
return IsHidden() || GUI<bool>::GetSetting(this, strGhost);
}
CStr IGUIObject::GetPresentableName() const
{
// __internal(), must be at least 13 letters to be able to be

View File

@ -27,6 +27,7 @@
#include "IGUIObject.h"
#include "gui/CGUI.h"
#include "gui/GUIbase.h"
#include "gui/scripting/JSInterface_IGUIObject.h"
#include "lib/input.h" // just for IN_PASS
@ -37,11 +38,11 @@
#include <vector>
struct SGUIStyle;
class CGUI;
class JSObject;
class IGUISetting;
template <typename T> class GUI;
ERROR_TYPE(GUI, UnableToParse);
/**
@ -128,16 +129,10 @@ public:
*/
void AddChild(IGUIObject* pChild);
//@}
//--------------------------------------------------------
/** @name Iterate
* Used to iterate over all children of this object.
/**
* Return all child objects of the current object.
*/
//--------------------------------------------------------
//@{
vector_pObjects::iterator begin() { return m_Children.begin(); }
vector_pObjects::iterator end() { return m_Children.end(); }
std::vector<IGUIObject*>& GetChildren() { return m_Children; }
//@}
//--------------------------------------------------------
@ -153,6 +148,16 @@ public:
*/
bool SettingExists(const CStr& Setting) const;
/**
* Returns whether this is object is set to be hidden.
*/
bool IsHidden();
/**
* Returns whether this object is set to be hidden or ghost.
*/
bool IsHiddenOrGhost();
/**
* All sizes are relative to resolution, and the calculation
* is not wanted in real time, therefore it is cached, update
@ -235,6 +240,26 @@ public:
*/
virtual void HandleMessage(SGUIMessage& UNUSED(Message)) {}
/**
* Calls an IGUIObject member function recursively on this object and its children.
* Aborts recursion at IGUIObjects that have the isRestricted function return true.
* The arguments of the callback function must be references.
*/
template<typename... Args>
void RecurseObject(bool(IGUIObject::*isRestricted)(), void(IGUIObject::*callbackFunction)(Args... args), Args&&... args)
{
if (this != m_pGUI.GetBaseObject())
{
if (isRestricted && (this->*isRestricted)())
return;
(this->*callbackFunction)(args...);
}
for (IGUIObject* const& obj : m_Children)
obj->RecurseObject(isRestricted, callbackFunction, args...);
}
protected:
/**
* Draws the object.
@ -364,7 +389,7 @@ protected:
* @param Action Name of action
* @param paramData JS::HandleValueArray arguments to pass to the event.
*/
void ScriptEvent(const CStr& Action, JS::HandleValueArray paramData);
void ScriptEvent(const CStr& Action, const JS::HandleValueArray& paramData);
void SetScriptHandler(const CStr& Action, JS::HandleObject Function);