#ifndef __I_GUI_ELEMENT_H_INCLUDED__
#define __I_GUI_ELEMENT_H_INCLUDED__
#include "IAttributeExchangingObject.h"
#include "irrList.h"
#include "rect.h"
#include "irrString.h"
#include "IEventReceiver.h"
#include "EGUIElementTypes.h"
#include "EGUIAlignment.h"
#include "IAttributes.h"
namespace irr
{
namespace gui
{
class IGUIEnvironment;
class IGUIElement : public virtual io::IAttributeExchangingObject, public IEventReceiver
{
public:
IGUIElement(EGUI_ELEMENT_TYPE type, IGUIEnvironment* environment, IGUIElement* parent,
s32 id, const core::rect<s32>& rectangle)
: Parent(0), RelativeRect(rectangle), AbsoluteRect(rectangle),
AbsoluteClippingRect(rectangle), DesiredRect(rectangle),
MaxSize(0,0), MinSize(1,1), IsVisible(true), IsEnabled(true),
IsSubElement(false), NoClip(false), ID(id), IsTabStop(false), TabOrder(-1), IsTabGroup(false),
AlignLeft(EGUIA_UPPERLEFT), AlignRight(EGUIA_UPPERLEFT), AlignTop(EGUIA_UPPERLEFT), AlignBottom(EGUIA_UPPERLEFT),
Environment(environment), Type(type)
{
#ifdef _DEBUG
setDebugName("IGUIElement");
#endif
if (parent)
{
parent->addChildToEnd(this);
recalculateAbsolutePosition(true);
}
}
virtual ~IGUIElement()
{
core::list<IGUIElement*>::Iterator it = Children.begin();
for (; it != Children.end(); ++it)
{
(*it)->Parent = 0;
(*it)->drop();
}
}
IGUIElement* getParent() const
{
return Parent;
}
core::rect<s32> getRelativePosition() const
{
return RelativeRect;
}
void setRelativePosition(const core::rect<s32>& r)
{
if (Parent)
{
const core::rect<s32>& r2 = Parent->getAbsolutePosition();
core::dimension2df d((f32)(r2.getSize().Width), (f32)(r2.getSize().Height));
if (AlignLeft == EGUIA_SCALE)
ScaleRect.UpperLeftCorner.X = (f32)r.UpperLeftCorner.X / d.Width;
if (AlignRight == EGUIA_SCALE)
ScaleRect.LowerRightCorner.X = (f32)r.LowerRightCorner.X / d.Width;
if (AlignTop == EGUIA_SCALE)
ScaleRect.UpperLeftCorner.Y = (f32)r.UpperLeftCorner.Y / d.Height;
if (AlignBottom == EGUIA_SCALE)
ScaleRect.LowerRightCorner.Y = (f32)r.LowerRightCorner.Y / d.Height;
}
DesiredRect = r;
updateAbsolutePosition();
}
void setRelativePosition(const core::position2di & position)
{
const core::dimension2di mySize = RelativeRect.getSize();
const core::rect<s32> rectangle(position.X, position.Y,
position.X + mySize.Width, position.Y + mySize.Height);
setRelativePosition(rectangle);
}
void setRelativePositionProportional(const core::rect<f32>& r)
{
if (!Parent)
return;
const core::dimension2di& d = Parent->getAbsolutePosition().getSize();
DesiredRect = core::rect<s32>(
core::floor32((f32)d.Width * r.UpperLeftCorner.X),
core::floor32((f32)d.Height * r.UpperLeftCorner.Y),
core::floor32((f32)d.Width * r.LowerRightCorner.X),
core::floor32((f32)d.Height * r.LowerRightCorner.Y));
ScaleRect = r;
updateAbsolutePosition();
}
core::rect<s32> getAbsolutePosition() const
{
return AbsoluteRect;
}
core::rect<s32> getAbsoluteClippingRect() const
{
return AbsoluteClippingRect;
}
void setNotClipped(bool noClip)
{
NoClip = noClip;
updateAbsolutePosition();
}
bool isNotClipped() const
{
return NoClip;
}
void setMaxSize(core::dimension2du size)
{
MaxSize = size;
updateAbsolutePosition();
}
void setMinSize(core::dimension2du size)
{
MinSize = size;
if (MinSize.Width < 1)
MinSize.Width = 1;
if (MinSize.Height < 1)
MinSize.Height = 1;
updateAbsolutePosition();
}
void setAlignment(EGUI_ALIGNMENT left, EGUI_ALIGNMENT right, EGUI_ALIGNMENT top, EGUI_ALIGNMENT bottom)
{
AlignLeft = left;
AlignRight = right;
AlignTop = top;
AlignBottom = bottom;
if (Parent)
{
core::rect<s32> r(Parent->getAbsolutePosition());
core::dimension2df d((f32)r.getSize().Width, (f32)r.getSize().Height);
if (AlignLeft == EGUIA_SCALE)
ScaleRect.UpperLeftCorner.X = (f32)DesiredRect.UpperLeftCorner.X / d.Width;
if (AlignRight == EGUIA_SCALE)
ScaleRect.LowerRightCorner.X = (f32)DesiredRect.LowerRightCorner.X / d.Width;
if (AlignTop == EGUIA_SCALE)
ScaleRect.UpperLeftCorner.Y = (f32)DesiredRect.UpperLeftCorner.Y / d.Height;
if (AlignBottom == EGUIA_SCALE)
ScaleRect.LowerRightCorner.Y = (f32)DesiredRect.LowerRightCorner.Y / d.Height;
}
}
virtual void updateAbsolutePosition()
{
recalculateAbsolutePosition(false);
core::list<IGUIElement*>::Iterator it = Children.begin();
for (; it != Children.end(); ++it)
{
(*it)->updateAbsolutePosition();
}
}
IGUIElement* getElementFromPoint(const core::position2d<s32>& point)
{
IGUIElement* target = 0;
core::list<IGUIElement*>::Iterator it = Children.getLast();
if (isVisible())
{
while(it != Children.end())
{
target = (*it)->getElementFromPoint(point);
if (target)
return target;
--it;
}
}
if (isVisible() && isPointInside(point))
target = this;
return target;
}
virtual bool isPointInside(const core::position2d<s32>& point) const
{
return AbsoluteClippingRect.isPointInside(point);
}
virtual void addChild(IGUIElement* child)
{
addChildToEnd(child);
if (child)
{
child->updateAbsolutePosition();
}
}
virtual void removeChild(IGUIElement* child)
{
core::list<IGUIElement*>::Iterator it = Children.begin();
for (; it != Children.end(); ++it)
if ((*it) == child)
{
(*it)->Parent = 0;
(*it)->drop();
Children.erase(it);
return;
}
}
virtual void remove()
{
if (Parent)
Parent->removeChild(this);
}
virtual void draw()
{
if ( isVisible() )
{
core::list<IGUIElement*>::Iterator it = Children.begin();
for (; it != Children.end(); ++it)
(*it)->draw();
}
}
virtual void OnPostRender(u32 timeMs)
{
if ( isVisible() )
{
core::list<IGUIElement*>::Iterator it = Children.begin();
for (; it != Children.end(); ++it)
(*it)->OnPostRender( timeMs );
}
}
virtual void move(core::position2d<s32> absoluteMovement)
{
setRelativePosition(DesiredRect + absoluteMovement);
}
virtual bool isVisible() const
{
_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
return IsVisible;
}
virtual void setVisible(bool visible)
{
IsVisible = visible;
}
virtual bool isSubElement() const
{
_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
return IsSubElement;
}
virtual void setSubElement(bool subElement)
{
IsSubElement = subElement;
}
void setTabStop(bool enable)
{
IsTabStop = enable;
}
bool isTabStop() const
{
_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
return IsTabStop;
}
void setTabOrder(s32 index)
{
if (index < 0)
{
TabOrder = 0;
IGUIElement *el = getTabGroup();
while (IsTabGroup && el && el->Parent)
el = el->Parent;
IGUIElement *first=0, *closest=0;
if (el)
{
el->getNextElement(-1, true, IsTabGroup, first, closest, true);
if (first)
{
TabOrder = first->getTabOrder() + 1;
}
}
}
else
TabOrder = index;
}
s32 getTabOrder() const
{
return TabOrder;
}
void setTabGroup(bool isGroup)
{
IsTabGroup = isGroup;
}
bool isTabGroup() const
{
_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
return IsTabGroup;
}
IGUIElement* getTabGroup()
{
IGUIElement *ret=this;
while (ret && !ret->isTabGroup())
ret = ret->getParent();
return ret;
}
virtual bool isEnabled() const
{
if ( isSubElement() && IsEnabled && getParent() )
return getParent()->isEnabled();
_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
return IsEnabled;
}
virtual void setEnabled(bool enabled)
{
IsEnabled = enabled;
}
virtual void setText(const wchar_t* text)
{
Text = text;
}
virtual const wchar_t* getText() const
{
return Text.c_str();
}
virtual void setToolTipText(const wchar_t* text)
{
ToolTipText = text;
}
virtual const core::stringw& getToolTipText() const
{
return ToolTipText;
}
virtual s32 getID() const
{
return ID;
}
virtual void setID(s32 id)
{
ID = id;
}
virtual bool OnEvent(const SEvent& event)
{
return Parent ? Parent->OnEvent(event) : false;
}
virtual bool bringToFront(IGUIElement* element)
{
core::list<IGUIElement*>::Iterator it = Children.begin();
for (; it != Children.end(); ++it)
{
if (element == (*it))
{
Children.erase(it);
Children.push_back(element);
return true;
}
}
_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
return false;
}
virtual bool sendToBack(IGUIElement* child)
{
core::list<IGUIElement*>::Iterator it = Children.begin();
if (child == (*it))
return true;
for (; it != Children.end(); ++it)
{
if (child == (*it))
{
Children.erase(it);
Children.push_front(child);
return true;
}
}
_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
return false;
}
virtual const core::list<IGUIElement*>& getChildren() const
{
return Children;
}
virtual IGUIElement* getElementFromId(s32 id, bool searchchildren=false) const
{
IGUIElement* e = 0;
core::list<IGUIElement*>::ConstIterator it = Children.begin();
for (; it != Children.end(); ++it)
{
if ((*it)->getID() == id)
return (*it);
if (searchchildren)
e = (*it)->getElementFromId(id, true);
if (e)
return e;
}
return e;
}
bool isMyChild(IGUIElement* child) const
{
if (!child)
return false;
do
{
if (child->Parent)
child = child->Parent;
} while (child->Parent && child != this);
_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
return child == this;
}
bool getNextElement(s32 startOrder, bool reverse, bool group,
IGUIElement*& first, IGUIElement*& closest, bool includeInvisible=false) const
{
s32 wanted = startOrder + ( reverse ? -1 : 1 );
if (wanted==-2)
wanted = 1073741824;
core::list<IGUIElement*>::ConstIterator it = Children.begin();
s32 closestOrder, currentOrder;
while(it != Children.end())
{
if ( ( (*it)->isVisible() || includeInvisible ) &&
(group == true || (*it)->isTabGroup() == false) )
{
if ((*it)->isTabStop() && ((*it)->isTabGroup() == group))
{
currentOrder = (*it)->getTabOrder();
if (currentOrder == wanted)
{
closest = *it;
return true;
}
if (closest)
{
closestOrder = closest->getTabOrder();
if ( ( reverse && currentOrder > closestOrder && currentOrder < startOrder)
||(!reverse && currentOrder < closestOrder && currentOrder > startOrder))
{
closest = *it;
}
}
else
if ( (reverse && currentOrder < startOrder) || (!reverse && currentOrder > startOrder) )
{
closest = *it;
}
if (first)
{
closestOrder = first->getTabOrder();
if ( (reverse && closestOrder < currentOrder) || (!reverse && closestOrder > currentOrder) )
{
first = *it;
}
}
else
{
first = *it;
}
}
if ((*it)->getNextElement(startOrder, reverse, group, first, closest))
{
_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
return true;
}
}
++it;
}
_IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
return false;
}
EGUI_ELEMENT_TYPE getType() const
{
return Type;
}
virtual bool hasType(EGUI_ELEMENT_TYPE type) const
{
return type == Type;
}
virtual const c8* getTypeName() const
{
return GUIElementTypeNames[Type];
}
virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const
{
out->addInt("Id", ID );
out->addString("Caption", getText());
out->addRect("Rect", DesiredRect);
out->addPosition2d("MinSize", core::position2di(MinSize.Width, MinSize.Height));
out->addPosition2d("MaxSize", core::position2di(MaxSize.Width, MaxSize.Height));
out->addEnum("LeftAlign", AlignLeft, GUIAlignmentNames);
out->addEnum("RightAlign", AlignRight, GUIAlignmentNames);
out->addEnum("TopAlign", AlignTop, GUIAlignmentNames);
out->addEnum("BottomAlign", AlignBottom, GUIAlignmentNames);
out->addBool("Visible", IsVisible);
out->addBool("Enabled", IsEnabled);
out->addBool("TabStop", IsTabStop);
out->addBool("TabGroup", IsTabGroup);
out->addInt("TabOrder", TabOrder);
out->addBool("NoClip", NoClip);
}
virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0)
{
setID(in->getAttributeAsInt("Id"));
setText(in->getAttributeAsStringW("Caption").c_str());
setVisible(in->getAttributeAsBool("Visible"));
setEnabled(in->getAttributeAsBool("Enabled"));
IsTabStop = in->getAttributeAsBool("TabStop");
IsTabGroup = in->getAttributeAsBool("TabGroup");
TabOrder = in->getAttributeAsInt("TabOrder");
core::position2di p = in->getAttributeAsPosition2d("MaxSize");
setMaxSize(core::dimension2du(p.X,p.Y));
p = in->getAttributeAsPosition2d("MinSize");
setMinSize(core::dimension2du(p.X,p.Y));
setAlignment((EGUI_ALIGNMENT) in->getAttributeAsEnumeration("LeftAlign", GUIAlignmentNames),
(EGUI_ALIGNMENT)in->getAttributeAsEnumeration("RightAlign", GUIAlignmentNames),
(EGUI_ALIGNMENT)in->getAttributeAsEnumeration("TopAlign", GUIAlignmentNames),
(EGUI_ALIGNMENT)in->getAttributeAsEnumeration("BottomAlign", GUIAlignmentNames));
setRelativePosition(in->getAttributeAsRect("Rect"));
setNotClipped(in->getAttributeAsBool("NoClip"));
}
protected:
void addChildToEnd(IGUIElement* child)
{
if (child)
{
child->grab();
child->remove();
child->LastParentRect = getAbsolutePosition();
child->Parent = this;
Children.push_back(child);
}
}
void recalculateAbsolutePosition(bool recursive)
{
core::rect<s32> parentAbsolute(0,0,0,0);
core::rect<s32> parentAbsoluteClip;
f32 fw=0.f, fh=0.f;
if (Parent)
{
parentAbsolute = Parent->AbsoluteRect;
if (NoClip)
{
IGUIElement* p=this;
while (p && p->Parent)
p = p->Parent;
parentAbsoluteClip = p->AbsoluteClippingRect;
}
else
parentAbsoluteClip = Parent->AbsoluteClippingRect;
}
const s32 diffx = parentAbsolute.getWidth() - LastParentRect.getWidth();
const s32 diffy = parentAbsolute.getHeight() - LastParentRect.getHeight();
if (AlignLeft == EGUIA_SCALE || AlignRight == EGUIA_SCALE)
fw = (f32)parentAbsolute.getWidth();
if (AlignTop == EGUIA_SCALE || AlignBottom == EGUIA_SCALE)
fh = (f32)parentAbsolute.getHeight();
switch (AlignLeft)
{
case EGUIA_UPPERLEFT:
break;
case EGUIA_LOWERRIGHT:
DesiredRect.UpperLeftCorner.X += diffx;
break;
case EGUIA_CENTER:
DesiredRect.UpperLeftCorner.X += diffx/2;
break;
case EGUIA_SCALE:
DesiredRect.UpperLeftCorner.X = core::round32(ScaleRect.UpperLeftCorner.X * fw);
break;
}
switch (AlignRight)
{
case EGUIA_UPPERLEFT:
break;
case EGUIA_LOWERRIGHT:
DesiredRect.LowerRightCorner.X += diffx;
break;
case EGUIA_CENTER:
DesiredRect.LowerRightCorner.X += diffx/2;
break;
case EGUIA_SCALE:
DesiredRect.LowerRightCorner.X = core::round32(ScaleRect.LowerRightCorner.X * fw);
break;
}
switch (AlignTop)
{
case EGUIA_UPPERLEFT:
break;
case EGUIA_LOWERRIGHT:
DesiredRect.UpperLeftCorner.Y += diffy;
break;
case EGUIA_CENTER:
DesiredRect.UpperLeftCorner.Y += diffy/2;
break;
case EGUIA_SCALE:
DesiredRect.UpperLeftCorner.Y = core::round32(ScaleRect.UpperLeftCorner.Y * fh);
break;
}
switch (AlignBottom)
{
case EGUIA_UPPERLEFT:
break;
case EGUIA_LOWERRIGHT:
DesiredRect.LowerRightCorner.Y += diffy;
break;
case EGUIA_CENTER:
DesiredRect.LowerRightCorner.Y += diffy/2;
break;
case EGUIA_SCALE:
DesiredRect.LowerRightCorner.Y = core::round32(ScaleRect.LowerRightCorner.Y * fh);
break;
}
RelativeRect = DesiredRect;
const s32 w = RelativeRect.getWidth();
const s32 h = RelativeRect.getHeight();
if (w < (s32)MinSize.Width)
RelativeRect.LowerRightCorner.X = RelativeRect.UpperLeftCorner.X + MinSize.Width;
if (h < (s32)MinSize.Height)
RelativeRect.LowerRightCorner.Y = RelativeRect.UpperLeftCorner.Y + MinSize.Height;
if (MaxSize.Width && w > (s32)MaxSize.Width)
RelativeRect.LowerRightCorner.X = RelativeRect.UpperLeftCorner.X + MaxSize.Width;
if (MaxSize.Height && h > (s32)MaxSize.Height)
RelativeRect.LowerRightCorner.Y = RelativeRect.UpperLeftCorner.Y + MaxSize.Height;
RelativeRect.repair();
AbsoluteRect = RelativeRect + parentAbsolute.UpperLeftCorner;
if (!Parent)
parentAbsoluteClip = AbsoluteRect;
AbsoluteClippingRect = AbsoluteRect;
AbsoluteClippingRect.clipAgainst(parentAbsoluteClip);
LastParentRect = parentAbsolute;
if ( recursive )
{
core::list<IGUIElement*>::Iterator it = Children.begin();
for (; it != Children.end(); ++it)
{
(*it)->recalculateAbsolutePosition(recursive);
}
}
}
protected:
core::list<IGUIElement*> Children;
IGUIElement* Parent;
core::rect<s32> RelativeRect;
core::rect<s32> AbsoluteRect;
core::rect<s32> AbsoluteClippingRect;
core::rect<s32> DesiredRect;
core::rect<s32> LastParentRect;
core::rect<f32> ScaleRect;
core::dimension2du MaxSize, MinSize;
bool IsVisible;
bool IsEnabled;
bool IsSubElement;
bool NoClip;
core::stringw Text;
core::stringw ToolTipText;
s32 ID;
bool IsTabStop;
s32 TabOrder;
bool IsTabGroup;
EGUI_ALIGNMENT AlignLeft, AlignRight, AlignTop, AlignBottom;
IGUIEnvironment* Environment;
EGUI_ELEMENT_TYPE Type;
};
}
}
#endif