So Here's the code first, so the ones who get blinded by the mess can leave without reading further
Code: Select all
#ifndef CSCROLLABLEELEMENTWINDOW_H
#define CSCROLLABLEELEMENTWINDOW_H
#include "irrlicht.h"
#include <vector>
#include <assert.h>
template <class userID, int nElements=1>
class CScrollableElementWindow: public irr::gui::IGUIElement
{
public:
CScrollableElementWindow(irr::gui::IGUIEnvironment * env,
const irr::core::position2di & pos,
const int & rows, const int & cols, const irr::core::recti sizes[nElements],
const irr::core::vector2di & colrowSpacing = irr::core::vector2di(10,20),
const irr::core::vector2di & leftTopPadding= irr::core::vector2di(10,20), const irr::core::vector2di & rightBottomPadding=irr::core::vector2di(30,30),
const int & horizontalElementMargin=10);
virtual ~CScrollableElementWindow() {};
void addElements(userID val, irr::gui::IGUIElement* elements[nElements]);
void clearElements();
userID getUserIDByElement(irr::gui::IGUIElement * elem);
void precalculatePositions();
void removeElementByUserID(const userID & val);
void removeElementByID(const int & ID);
void setReceiver(irr::IEventReceiver * rcv)
{
m_otherReceiver=rcv;
}
bool OnEvent(const irr::SEvent &event);
void draw();
protected:
struct data
{
data(userID v, irr::gui::IGUIElement* e[nElements]):userVal(v)
{
for(int i=0; i< nElements; i++)
{
elements[i]=e[i];
}
}
userID userVal;
irr::gui::IGUIElement* elements[nElements];
};
irr::gui::IGUIScrollBar * m_scrollBar;
irr::gui::IGUIEnvironment * m_env;
int m_rows,m_cols;
irr::core::vector2di m_colrowSpacing;
irr::core::vector2di m_leftTopPadding;
irr::core::vector2di m_rightBottomPadding;
int m_horizontalElementMargin;
irr::core::recti m_sizes[nElements];
irr::core::recti m_largestY;
int m_ID;
irr::IEventReceiver * m_otherReceiver;
std::vector<data> m_elements;
irr::core::position2d<irr::s32> DragStart;
int currentScrollBarPosition, lastScrollBarPosition;
bool Dragging, IsDraggable, IsActive;
};
template <class userID, int nElements>
CScrollableElementWindow<userID, nElements>::CScrollableElementWindow(irr::gui::IGUIEnvironment * env,
const irr::core::position2di & pos,
const int & rows, const int & cols, const irr::core::recti sizes[nElements],
const irr::core::vector2di & colrowSpacing,
const irr::core::vector2di & leftTopPadding, const irr::core::vector2di & rightBottomPadding,
const int & horizontalElementMargin)
:
irr::gui::IGUIElement((irr::gui::EGUI_ELEMENT_TYPE)(irr::gui::EGET_COUNT+1),env,env->getRootGUIElement(),123,irr::core::recti(0,0,0,0)),
m_env(env),
m_rows(rows), m_cols(cols),
m_colrowSpacing(colrowSpacing),
m_leftTopPadding(leftTopPadding), m_rightBottomPadding(rightBottomPadding),
m_horizontalElementMargin(horizontalElementMargin),
m_ID(0),
m_otherReceiver(0),
Dragging(false), IsDraggable(true), IsActive(false)
{
assert(nElements>0);
for(int i=0; i < nElements; i++)
m_sizes[i]=sizes[i];
m_largestY=m_sizes[0];
for(int i=1; i < nElements; i++)
{
if(m_largestY.getHeight()<m_sizes[i].getHeight())
{
m_largestY=m_sizes[i];
}
}
///Create properly sized window
int x=0;
int y=0;
///X AXIS
x+=((nElements-1)*m_horizontalElementMargin*m_cols);
x+=(m_colrowSpacing.X*(m_cols-1));
x+=(m_rightBottomPadding.X+m_leftTopPadding.X);
int sizeSum=0;
for(int i=0; i<nElements; i++)
{
sizeSum+=m_sizes[i].getWidth();
}
sizeSum*=m_cols;
x+=sizeSum;
///Y AXIS
y+=(m_rows*m_largestY.getHeight());
y+=((m_colrowSpacing.Y)*(m_rows-1));
y+=(m_leftTopPadding.Y + m_rightBottomPadding.Y);
this->setRelativePosition(irr::core::recti(0,0,x,y));
this->move(pos);
this->updateAbsolutePosition();
m_scrollBar=m_env->addScrollBar(false,irr::core::recti(5+x-m_rightBottomPadding.X,m_leftTopPadding.Y+5, x-5,5+y-m_rightBottomPadding.Y),this);
m_scrollBar->setMax((m_rows)*m_largestY.getHeight()+((m_rows-1)*m_colrowSpacing.Y));
currentScrollBarPosition=lastScrollBarPosition=m_scrollBar->getPos();
///modify skin
this->m_env->getSkin()->setColor(irr::gui::EGDC_WINDOW,irr::video::SColor(255,255,255,255));
}
template <class userID, int nElements>
void CScrollableElementWindow<userID, nElements>::addElements(userID val, irr::gui::IGUIElement* elements[nElements])
{
for(int i=0; i<nElements; i++)
{
elements[i]->setRelativePosition(m_sizes[i]);
elements[i]->setID(m_ID);
}
m_ID++;
if(m_elements.size()==0)
{
m_elements.push_back(data(val,elements));
}
else
{
m_elements.push_back(data(val,elements));
}
for(int i=0; i<nElements; i++)
{
this->addChild(elements[i]);
}
}
template <class userID, int nElements>
void CScrollableElementWindow<userID, nElements>::clearElements()
{
for(typename std::vector<data>::iterator it=m_elements.begin(); it != m_elements.end(); it++)
{
for (int j = 0; j<nElements; j++)
{
if ((*it).elements[j])
(*it).elements[j]->remove();
}
}
m_elements.clear();
}
template <class userID, int nElements>
userID CScrollableElementWindow<userID, nElements>::getUserIDByElement(irr::gui::IGUIElement * elem)
{
for(typename std::vector<data>::iterator it=m_elements.begin(); it != m_elements.end(); it++)
{
for (int j = 0; j<nElements; j++)
{
if((*it).elements[j]->getID()==elem->getID()&&(*it).elements[j]==elem)
{
return(*it).userVal;
}
}
}
assert(false);/// maybe use exceptions?
}
template <class userID, int nElements>
void CScrollableElementWindow<userID, nElements>::removeElementByUserID(const userID & val)
{
for(typename std::vector<data>::iterator it=m_elements.begin(); it != m_elements.end(); it++)
{
if((*it).userVal==val)
{
for (int j = 0; j<nElements; j++)
{
(*it).elements[j]->remove();
(*it).elements[j]=NULL;
}
m_elements.erase(it);
precalculatePositions();
return;
}
}
}
template <class userID, int nElements>
void CScrollableElementWindow<userID, nElements>::removeElementByID(const int & val)
{
for(typename std::vector<data>::iterator it=m_elements.begin(); it != m_elements.end(); it++)
{
if((*it).elements[0]->getID()==val)
{
for (int j = 0; j<nElements; j++)
{
(*it).elements[j]->remove();
(*it).elements[j]=NULL;
}
m_elements.erase(it);
precalculatePositions();
return;
}
}
}
template <class userID, int nElements>
void CScrollableElementWindow<userID, nElements>::precalculatePositions()
{
int x,y;
x=m_leftTopPadding.X;
y=m_leftTopPadding.Y;
int row,col,lrow,lcol;
row=col=lrow=lcol=0;
for(int i=0; i < (int)m_elements.size(); i++)
{
lrow=row;
lcol=col;
col=i%m_cols;
row=i/m_cols;
if(row!=lrow&&row!=0)
{
x=m_leftTopPadding.X;
y+=m_largestY.getHeight()+m_colrowSpacing.Y;
}
if(col!=lcol&&col!=0)
x+=m_colrowSpacing.X;
for(int j = 0; j<nElements; j++)
{
if(j==0&&col!=0)
x+=m_sizes[nElements-1].getWidth();
else if(j!=0)
x+=m_sizes[j-1].getWidth()+m_horizontalElementMargin;
m_elements[i].elements[j]->setRelativePosition
(
irr::core::position2di(x,y + (m_largestY.getHeight()/2) - (m_sizes[j].getHeight()/2))
);
if(m_elements[i].elements[j]->getRelativePosition().UpperLeftCorner.Y>this->getRelativePosition().getHeight()
||m_elements[i].elements[j]->getRelativePosition().LowerRightCorner.Y<=0)
{
m_elements[i].elements[j]->setVisible(false);
}
else
m_elements[i].elements[j]->setVisible(true);
}
}
m_scrollBar->setMax((m_largestY.getHeight()*(((m_elements.size()/m_cols)+(m_elements.size()%m_cols>0))-m_rows))+((((m_elements.size()/m_cols)+(m_elements.size()%m_cols>0))-m_rows)*m_colrowSpacing.Y));
currentScrollBarPosition=lastScrollBarPosition=m_scrollBar->getPos();
}
template <class userID, int nElements>
bool CScrollableElementWindow<userID, nElements>::OnEvent(const irr::SEvent &event)
{
switch(event.EventType)
{
case irr::EET_GUI_EVENT:
{
switch(event.GUIEvent.EventType)
{
case irr::gui::EGET_ELEMENT_FOCUS_LOST:
{
Dragging = false;
IsActive = false;
break;
}
case irr::gui::EGET_ELEMENT_FOCUSED:
{
if (Parent && ((event.GUIEvent.Caller == this) || isMyChild(event.GUIEvent.Caller)))
{
Parent->bringToFront(this);
IsActive = true;
}
else
{
IsActive = false;
}
}
case irr::gui::EGET_SCROLL_BAR_CHANGED:
{
if(event.GUIEvent.Caller==m_scrollBar)
{
lastScrollBarPosition=currentScrollBarPosition;
currentScrollBarPosition=m_scrollBar->getPos();
for(int i=0; i<(int)m_elements.size(); i++)
{
for(int j=0; j<nElements; j++)
{
m_elements[i].elements[j]->setRelativePosition(m_elements[i].elements[j]->getRelativePosition()+irr::core::vector2di(0,lastScrollBarPosition-currentScrollBarPosition));
if(m_elements[i].elements[j]->getRelativePosition().UpperLeftCorner.Y>this->getRelativePosition().getHeight()||
m_elements[i].elements[j]->getRelativePosition().LowerRightCorner.Y<=0
)
{
m_elements[i].elements[j]->setVisible(false);
}
else
m_elements[i].elements[j]->setVisible(true);
}
}
}
break;
}
default:
break;
}
break;
}
case irr::EET_MOUSE_INPUT_EVENT:
switch(event.MouseInput.Event)
{
case irr::EMIE_LMOUSE_PRESSED_DOWN:
DragStart.X = event.MouseInput.X;
DragStart.Y = event.MouseInput.Y;
Dragging = IsDraggable;
if (Parent)
Parent->bringToFront(this);
return true;
case irr::EMIE_LMOUSE_LEFT_UP:
Dragging = false;
return true;
case irr::EMIE_MOUSE_MOVED:
if (!event.MouseInput.isLeftPressed())
Dragging = false;
if (Dragging)
{
// gui window should not be dragged outside its parent
if (Parent &&
(event.MouseInput.X < Parent->getAbsolutePosition().UpperLeftCorner.X +1 ||
event.MouseInput.Y < Parent->getAbsolutePosition().UpperLeftCorner.Y +1 ||
event.MouseInput.X > Parent->getAbsolutePosition().LowerRightCorner.X -1 ||
event.MouseInput.Y > Parent->getAbsolutePosition().LowerRightCorner.Y -1))
return true;
move(irr::core::position2d<irr::s32>(event.MouseInput.X - DragStart.X, event.MouseInput.Y - DragStart.Y));
DragStart.X = event.MouseInput.X;
DragStart.Y = event.MouseInput.Y;
return true;
}
break;
default:
break;
}
default:
break;
}
if(m_otherReceiver)
m_otherReceiver->OnEvent(event);
return irr::gui::IGUIElement::OnEvent(event);
}
template <class userID, int nElements>
void CScrollableElementWindow<userID, nElements>::draw()
{
if (IsVisible)
{
irr::gui::IGUISkin* skin = m_env->getSkin();
skin->draw3DWindowBackground(this,true,skin->getColor(irr::gui::EGDC_WINDOW),this->getRelativePosition());
IGUIElement::draw();
}
}
#endif // CSCROLLABLEELEMENTWINDOW_H
Now id like to make it a bit faster and maybe more easy to use.
What could be done:
-Use other types for some of the stuff (like the use of vector2di maybe could be changed to other type that would make more sense).
-Better variable names (also the class name).
-More const correctness.
-Nicer indentations in some places.
-Whatever else you can think of!
Also I might have not implemented some parts of the gui element, that I should have. But its my first element so I don't really know what should be re-implemented and what not.
PS. Here's how it might look like:
It's a property editor using it.