// Ryzom - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
#ifndef NL_INTERFACE_ELEMENT_H
#define NL_INTERFACE_ELEMENT_H
#include "nel/misc/types_nl.h"
#include "nel/misc/string_mapper.h"
#include "nel/misc/smart_ptr.h"
#include "interface_property.h"
#include "reflect.h"
// ----------------------------------------------------------------------------
class CInterfaceGroup;
// ----------------------------------------------------------------------------
enum THotSpot
{
Hotspot_BL = 36, // 100100,
Hotspot_BM = 34, // 100010,
Hotspot_BR = 33, // 100001,
Hotspot_ML = 20, // 010100,
Hotspot_MM = 18, // 010010
Hotspot_MR = 17, // 010001
Hotspot_TL = 12, // 001100
Hotspot_TM = 10, // 001010
Hotspot_TR = 9, // 001001
Hotspot_xR = 1, // 000001
Hotspot_xM = 2, // 000010
Hotspot_xL = 4, // 000100
Hotspot_Bx = 32, // 100000
Hotspot_Mx = 16, // 010000
Hotspot_Tx = 8, // 001000
Hotspot_TTAuto = 0, // Special For Tooltip PosRef. Auto mode. see CCtrlBase and tooltip info
};
class CInterfaceLink;
class CInterfaceElement;
class CInterfaceGroup;
class CViewBase;
class CCtrlBase;
class CGroupContainer;
class IActionHandler;
/**
* A visitor to walk a tree of interface elements and apply a teartment on them.
*
* For each vsited element, visitElement() is called
* If the element is a control, then visitCtrl() is called, and then visitElement()
* If the element is a view, then visitView() is called, and then visitElement()
* If the element is a group, then visitGoup() is called, and then visitElement()
*
* \author Nicolas Vizerie
* \author Nevrax France
* \date 2003
*/
class CInterfaceElementVisitor
{
public:
virtual void visit(CInterfaceElement * /* elem */) {}
virtual void visitGroup(CInterfaceGroup * /* group */) {}
virtual void visitView(CViewBase * /* view */) {}
virtual void visitCtrl(CCtrlBase * /* ctrl */) {}
};
/** Reflectable refcounted object
* NB nico : added this intermediate class so that the binding from lua to the reflection
* system that are found in CLuaIHM can be reused for other objects as well
* NOTE: The class is named 'CReflectableRefPtrTarget' and not 'CReflectableRefCount'
* because the refcount part is only used for ref pointing in the ui
*/
class CReflectableRefPtrTarget : public CReflectable, public NLMISC::CRefCount
{
public:
virtual ~CReflectableRefPtrTarget();
};
#define DECLARE_UI_CLASS(_class_) \
virtual std::string getClassName() {return #_class_;} \
static NLMISC::IClassable *creator() {return new _class_(CViewBase::TCtorParam());}
#define REGISTER_UI_CLASS(_class_) \
class CRegisterUIClassHelper_##_class_ \
{ \
public: \
CRegisterUIClassHelper_##_class_() \
{ \
NLMISC::CClassRegistry::init(); \
NLMISC::CClassRegistry::registerClass(#_class_, _class_::creator, typeid(_class_).name()); \
} \
} RegisterUIClassHelper_##_class_;
/**
* class describing a localisable interface element, i.e. : an element with coordinates
* \author Nicolas Brigand
* \author Nevrax France
* \date 2002
*/
class CInterfaceElement : public CReflectableRefPtrTarget, public NLMISC::IStreamable
{
public:
enum EStrech
{
none=0,
width=1,
height=2
};
/// Constructor
CInterfaceElement()
{
_Parent = NULL;
_XReal = _YReal = _WReal = _HReal = 0;
_X = _Y = _W = _H = 0;
//_Snap = 1;
_PosRef = Hotspot_BL;
_ParentPosRef = Hotspot_BL;
_ParentPos = NULL;
_SizeRef = 0;
_SizeDivW = 10;
_SizeDivH = 10;
_ParentSize = NULL;
_Links = NULL;
_Active= true;
// default to 3 pass
_InvalidCoords= 3;
_ModulateGlobalColor= true;
_RenderLayer= 0;
_AvoidResizeParent= false;
}
// dtor
virtual ~CInterfaceElement();
/** Cloning
* Cloning is actually performed using a serial / unserial in a memory stream
* NB Nico : if too slow, should use a CFastStream version instead, that is designedto work in memory only
*/
virtual CInterfaceElement *clone();
// help to serialize an action handler
static void serialAH(NLMISC::IStream &f, IActionHandler *&ah);
/// Parse the element and initalize it
virtual bool parse (xmlNodePtr cur, CInterfaceGroup *parentGroup);
/// Debug info on memory
virtual uint32 getMemory () { return (uint32)(sizeof(*this)+_Id.size()); }
/// helper: display a parse error with the id of the lement
void parseError (CInterfaceGroup *parentGroup, const char *reason = NULL);
/// Accessors : GET
const std::string& getId() const { return _Id; }
std::string getShortId() const;
std::string getIdByValue() const { return _Id; }
CInterfaceGroup* getParent() const { return _Parent; }
CInterfaceElement* getParentPos() const { return _ParentPos; }
CInterfaceElement* getParentSize() const { return _ParentSize; }
/// Get the master group of this element (recurs call.
CInterfaceElement* getMasterGroup() const;
// get a possible group container
CGroupContainer *getParentContainer();
bool getActive() const { return _Active; }
sint32 getX() const { return _X; }
sint32 getY() const { return _Y; }
sint32 getW() const { return (_Active?_W:0); }
sint32 getW(bool bTestActive) const { return (bTestActive?(_Active?_W:0):_W); }
sint32 getH() const { return (_Active?_H:0); }
sint32 getH(bool bTestActive) const { return (bTestActive?(_Active?_H:0):_H); }
/**
* Get the max width used by the window.
*
* The view must return the largest width its content can take if it will be resized at maximum.
* Typical use : for a CTextView multiline, it returns the size of the whole string.
*
* This method is used by the container CCGroupTable that need to know this information about its children in its resizing algorithm.
*/
virtual sint32 getMaxUsedW() const { return INT_MAX; };
/**
* Get the min width used by the window.
*
* The view must return the smallest width its content can take if it will be resized at minimum.
* Typical use : for a CTextView multiline without word clipping, it returns the size of the largest word.
*
* This method is used by the container CCGroupTable that need to know this information about its children in its resizing algorithm.
*/
virtual sint32 getMinUsedW() const { return 0; };
//bool isSnapped() const { return (_Snap>1); }
//sint32 getSnapping() const { return _Snap; }
sint32 getXReal() const { return _XReal; }
sint32 getYReal() const { return _YReal; }
sint32 getWReal() const { return (_Active?_WReal:0); }
sint32 getHReal() const { return (_Active?_HReal:0); }
THotSpot getPosRef () const { return _PosRef; }
THotSpot getParentPosRef () const { return _ParentPosRef; }
sint32 getSizeRef() const { return _SizeRef; } // none == 0, w == 1, h == 2, wh == 3
void setSizeRef(sint32 value) { _SizeRef = value; }
/// Accessors : SET
void setId (const std::string &newID) { _Id = newID; }
virtual void setIdRecurse(const std::string &newID);
void setParent (CInterfaceGroup *pIG) { _Parent = pIG; }
void setParentPos (CInterfaceElement *pIG) { _ParentPos = pIG; }
void setParentSize (CInterfaceElement *pIG) { _ParentSize = pIG; }
virtual void setActive (bool state);
void setX (sint32 x) { _X = x; }
void setXAndInvalidateCoords (sint32 x) { _X = x; invalidateCoords(); }
void setY (sint32 y) { _Y = y; }
void setYAndInvalidateCoords (sint32 y) { _Y = y; invalidateCoords(); }
void setW (sint32 w);
void setWAndInvalidateCoords (sint32 w) { setW(w); invalidateCoords(); }
void setH (sint32 h);
void setHAndInvalidateCoords (sint32 h) { setH(h); invalidateCoords(); }
void setPosRef (THotSpot hs) { _PosRef = hs; }
void setParentPosRef (THotSpot hs) { _ParentPosRef = hs; }
// Get the coordinate of a corner on screen
void getCorner(sint32 &px, sint32 &py, THotSpot hotSpot);
/** Test if the given coordinates are inside this element
*/
bool isIn(sint x, sint y) const;
/** Test if the given box intersect the element
*/
bool isIn(sint x, sint y, uint width, uint height) const;
/** Test if another interface element intersect this one
*/
bool isIn(const CInterfaceElement &other) const;
/**
* get the window containing the element
* \return NULL if the element is not on the window, otherwise returns a pointer to the window
*/
CInterfaceGroup* getRootWindow();
/** get the element Depth, ie the number of parent he has (0 if _Parent==NULL)
* \warning slow test. don't take into account CCtrlBase::getDeltaDepth() (since method not virtual)
*/
uint getParentDepth() const;
/**
* true if the element and all its parents are active (recurs up to the root window)
*/
bool isActiveThroughParents() const;
/**
* move the element (add dx and dy to its coords)
* \param dx : value added to _X
* \param dy : value added to _Y
*/
virtual void move (sint32 dx, sint32 dy);
//void resizeBR (sint32 sizex, sint32 sizey);
//void stopResizeBR();
//void startResizeBR();
// Some tools
void relativeSInt64Read (CInterfaceProperty &rIP, const std::string &prop, const char *val,
const std::string &defVal);
void relativeSInt32Read (CInterfaceProperty &rIP, const std::string &prop, const char *val,
const std::string &defVal);
void relativeBoolRead (CInterfaceProperty &rIP, const std::string &prop, const char *val,
const std::string &defVal);
void relativeRGBARead (CInterfaceProperty &rIP, const std::string &prop, const char *val,
const std::string &defVal);
// Parse tools
static THotSpot convertHotSpot (const char *ptr); //
static void convertHotSpotCouple (const char *ptr, THotSpot &parentPosRef, THotSpot &posRef);
static NLMISC::CRGBA convertColor (const char *ptr);
static bool convertBool (const char *ptr);
static NLMISC::CVector convertVector (const char *ptr);
/** Convert a value that is in the form like width="50" or width="10%"
* if the value is absolute like '50' then 'pixels' is filled, else
* the ratio is remapped to the [0, 1] range and is copied in 'ratio'
*/
static void convertPixelsOrRatio(const char *ptr, sint32 &pixels, float &ratio);
// add an interface link to that element (kept in a smart ptr)
void addLink(CInterfaceLink *link);
// remove a link from that element; There's one less reference on the link (which is referenced by a smart ptr)
void removeLink(CInterfaceLink *link);
/** Update all links for this instance and its sons.
* Derivers should override this to update their sons.
*/
virtual void updateAllLinks();
/** This allow to force opening an element. By default it just activate the element.
* It allow to have different behaviour on more complex containers
*/
virtual void forceOpen() { setActive(true); }
virtual void enableBlink(int /* numBlinks */ = 0) {}
virtual void disableBlink() {}
virtual bool getBlink() const { return false; }
// Options for views to be modulated by interface global color or not. Parsed with "global_color". Default: true
void setModulateGlobalColor(bool state) {_ModulateGlobalColor= state;}
bool getModulateGlobalColor() const {return _ModulateGlobalColor;}
void dummySet(sint32 value);
void dummySet(const std::string &value);
// lua methods
int luaUpdateCoords(CLuaState &ls);
int luaInvalidateCoords(CLuaState &ls);
int luaInvalidateContent(CLuaState &ls);
int luaCenter(CLuaState &ls);
int luaSetPosRef(CLuaState &ls);
int luaSetParentPos(CLuaState &ls);
// set sizeref as a string, like "wh", "wh5" ....
void setSizeRef(const std::string &sizeref);
std::string getSizeRefAsString() const;
// export some properties
REFLECT_EXPORT_START(CInterfaceElement, CReflectable)
REFLECT_BOOL ("active", getActive, setActive);
REFLECT_BOOL ("global_color", getModulateGlobalColor, setModulateGlobalColor);
REFLECT_SINT32 ("x", getX, setXAndInvalidateCoords);
REFLECT_SINT32 ("y", getY, setYAndInvalidateCoords);
REFLECT_SINT32 ("w", getW, setWAndInvalidateCoords);
REFLECT_SINT32 ("h", getH, setHAndInvalidateCoords);
REFLECT_SINT32 ("x_real", getXReal, dummySet);
REFLECT_SINT32 ("y_real", getYReal, dummySet);
REFLECT_SINT32 ("w_real", getWReal, dummySet);
REFLECT_SINT32 ("h_real", getHReal, dummySet);
REFLECT_STRING ("id", getIdByValue, dummySet);
REFLECT_STRING ("sizeref", getSizeRefAsString, setSizeRef);
REFLECT_LUA_METHOD("updateCoords", luaUpdateCoords);
REFLECT_LUA_METHOD("invalidateCoords", luaInvalidateCoords);
REFLECT_LUA_METHOD("invalidateContent", luaInvalidateContent);
REFLECT_LUA_METHOD("center", luaCenter);
REFLECT_LUA_METHOD("setPosRef", luaSetPosRef);
REFLECT_LUA_METHOD("setParentPos", luaSetParentPos);
REFLECT_EXPORT_END
/* invalidate coords. set numPass==1 if you are SURE that only XReal/YReal need to be updated
* Default 3 is needed for:
* 1: update _W/_H and _WReal/_HReal according to Sons
* 2: update XReal/YReal (eg: according to Scroll offset)
*/
void invalidateCoords(uint8 numPass= 2);
uint8 getInvalidCoords() const {return _InvalidCoords;}
/* Invalidates the content of the window. This method invalidate the content of the window (for CViewText or CGroupTable).
* It invalidates the content of the parent too.
*/
void invalidateContent();
/* This call back is called when the content or the content of a child is been invalidated.
*/
virtual void onInvalidateContent() {}
// called by interfaceManager for master window only
void resetInvalidCoords();
/// Update the elements coords convert x,y,w,h (parentpos coord) to xreal,yreal,wreal,hreal (BL coord)
virtual void updateCoords();
/// Called each frame before draw to possibly invalidateCoords().
virtual void checkCoords();
/// Test if this element is son of the given element
bool isSonOf(const CInterfaceElement *other) const;
/// Called after first frame initialised
virtual void launch () {}
void setRenderLayer(sint8 rl) {_RenderLayer= rl;}
sint8 getRenderLayer() const { return _RenderLayer; }
void copyOptionFrom(const CInterfaceElement &other);
// center the element in middle of screen
void center();
// for debug only: draw wired quad to see where groups and hotspots are
enum TRenderWired
{
RenderView,
RenderCtrl,
RenderGroup
};
// if uiFilter is not empty, draw a quad only if the element id match
virtual void renderWiredQuads(TRenderWired type, const std::string &uiFilter);
void drawHotSpot(THotSpot hs, NLMISC::CRGBA col);
// Returns 'true' if that element can be downcasted to a view
virtual bool isView() const { return false; }
// Returns 'true' if that element can be downcasted to a ctrl
virtual bool isCtrl() const { return false; }
// Returns 'true' if that element can be downcasted to an interface group
virtual bool isGroup() const { return false; }
/** This is called before the config loading begins. This is the place to restore default state for config info.
*/
virtual void onLoadConfig() {}
/** Tells whether that element wants to save info in a config stream. If this returns true, then serialConfig
* is called.
*/
virtual bool wantSerialConfig() const { return false; }
// Serial config info about that element. This is called only if wantSerialConfig() returns true
virtual void serialConfig(NLMISC::IStream &f);
// visit the node of the ui tree
virtual void visit(CInterfaceElementVisitor *visitor);
/** When user is quitting the interface, this is called. Then the interface config is saved
* This is where the element get the opportunity to do some cleanup.
*/
virtual void onQuit() {}
/// Whent an element is added to a CInterfaceGroup via addCtrl, addGroup or addView, this is called after the add.
virtual void onAddToGroup() {}
/** typically used only in conjunction with CGroupInScene. Such groups move every Frames. so
* this function is called on each children elements to move the XReal/Yreal only (with a delta)
*/
virtual void onFrameUpdateWindowPos(sint dx, sint dy);
/// if true, InterfaceGroup child resize won't take this element into account
bool avoidResizeParent() const {return _AvoidResizeParent;}
void setAvoidResizeParent(bool state) {_AvoidResizeParent= state;}
virtual std::string getClassName()
{
nlassert(0); // forgot to implement serial & to register the class ?
return "";
}
protected:
///the parent
CInterfaceGroup* _Parent;
///the id of the element
std::string _Id;
///is the element active?
bool _Active;
// if 0, don't need updateCoords(), else tell the number of pass needed
uint8 _InvalidCoords;
// Real display coords
sint32 _XReal, _YReal, _WReal, _HReal;
// Relative coords
sint32 _X;
sint32 _Y;
sint32 _W;
sint32 _H;
//sint32 _Snap;
// position references e.g. : _PosRef=BL, _ParentPosref=MM : the bottom left corner of the element
// will be placed on the center (middle middle) of the parent window
THotSpot _PosRef;
THotSpot _ParentPosRef;
NLMISC::CRefPtr _ParentPos; // RefPtr in case of group destroyed in a parent group with posref on it
sint32 _SizeRef; // none == 0, w == 1, h == 2, wh == 3
sint32 _SizeDivW, _SizeDivH;
NLMISC::CRefPtr _ParentSize; // RefPtr in case of group destroyed in a parent group with posref on it
// Friend Class
friend class CGroupList;
friend class CGroupParagraph;
// True if must modulate the global color with the view
bool _ModulateGlobalColor;
// Index of layer to render it.
sint8 _RenderLayer;
// Used for CInterfaceGroup ChildResize feature
bool _AvoidResizeParent;
virtual void serial(NLMISC::IStream &f);
void parseSizeRef(const char *sizeRef);
void parseSizeRef(const char *sizeRefStr, sint32 &sizeref, sint32 &sizeDivW, sint32 &sizeDivH);
private:
//void snapSize();
typedef NLMISC::CSmartPtr TLinkSmartPtr;
typedef std::vector TLinkVect;
TLinkVect *_Links; // links, or NULL if no link
};
extern NLMISC::CStringMapper *_UIStringMapper;
/**
* class to compress string usage in the interface
* \author Matthieu 'Trap' Besson
* \author Nevrax France
* \date October 2003
*/
class CStringShared
{
public:
CStringShared()
{
_Id = NLMISC::CStringMapper::emptyId();
}
const CStringShared& operator=(const std::string &str)
{
_Id = _UIStringMapper->localMap(str);
return *this;
}
const CStringShared& operator=(const CStringShared &str)
{
_Id = str._Id;
return *this;
}
const std::string &toString() const
{
return _UIStringMapper->localUnmap(_Id);
}
operator const std::string &() const
{
return _UIStringMapper->localUnmap(_Id);
}
bool empty() const
{
return _Id == NLMISC::CStringMapper::emptyId();
}
static CStringShared emptyString()
{
return CStringShared();
}
NLMISC::TStringId getStringId() const { return _Id; }
void serial(NLMISC::IStream &f)
{
std::string str;
if (f.isReading())
{
f.serial(str);
*this = str;
}
else
{
str = this->toString();
f.serial(str);
}
}
private:
NLMISC::TStringId _Id;
};
inline bool operator==(const CStringShared &lhs, const CStringShared &rhs) { return lhs.getStringId() == rhs.getStringId(); }
inline bool operator!=(const CStringShared &lhs, const CStringShared &rhs) { return !(lhs == rhs); }
#endif // NL_INTERFACE_ELEMENT_H
/* End of interface_element.h */