Need help optimizing my gui element.

If you are a new Irrlicht Engine user, and have a newbie-question, this is the forum for you. You may also post general programming questions here.
Post Reply
serengeor
Posts: 1712
Joined: Tue Jan 13, 2009 7:34 pm
Location: Lithuania

Need help optimizing my gui element.

Post by serengeor »

I need to optimize this gui element I wrote. Everything works ok, though not as fast as it could (I think). :roll:

So Here's the code first, so the ones who get blinded by the mess can leave without reading further :D

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. :oops:

PS. Here's how it might look like:
Image
It's a property editor using it.
Last edited by serengeor on Tue Jul 12, 2011 1:47 pm, edited 1 time in total.
Working on game: Marrbles (Currently stopped).
CuteAlien
Admin
Posts: 9734
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Need help optimizing my gui element.

Post by CuteAlien »

Looks nice. To make anything faster first step is always profiling. There's no point in optimizing before you know what is slow.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
bitplane
Admin
Posts: 3204
Joined: Mon Mar 28, 2005 3:45 am
Location: England
Contact:

Re: Need help optimizing my gui element.

Post by bitplane »

Why not use the one from the GUI editor? It's not the fastest thing in the world, but it's extensible and does almost the same thing (from what I can tell)
Submit bugs/patches to the tracker!
Need help right now? Visit the chat room
serengeor
Posts: 1712
Joined: Tue Jan 13, 2009 7:34 pm
Location: Lithuania

Re: Need help optimizing my gui element.

Post by serengeor »

bitplane wrote:Why not use the one from the GUI editor? It's not the fastest thing in the world, but it's extensible and does almost the same thing (from what I can tell)
I'll have a look at it as soon as I restore my pc.
Could you name the element to be more clear?

EDIT: well I've looked at the gui editor and I think you were talking about the editor it self (to be more specific attribute editor tab?) . I didn't actually find the code for that specific part, but I think that it wouldn't do what I need anyway.

The only thing I really wanted is too make my code a bit more clean looking.
Working on game: Marrbles (Currently stopped).
bitplane
Admin
Posts: 3204
Joined: Mon Mar 28, 2005 3:45 am
Location: England
Contact:

Re: Need help optimizing my gui element.

Post by bitplane »

Yeah I meant the attributes for the individual items and CGUIPanel for the scrollable area. If they don't do what you need then just ignore me :)
Submit bugs/patches to the tracker!
Need help right now? Visit the chat room
Post Reply