Feature Request: Window Event Parent

Discuss about anything related to the Irrlicht Engine, or read announcements about any significant features or usage changes.
Post Reply
chronologicaldot
Competition winner
Posts: 684
Joined: Mon Sep 10, 2012 8:51 am

Feature Request: Window Event Parent

Post by chronologicaldot »

Windows are currently handled by their parent GUI element. While in many cases, this is fine, there comes a time when GUI events from in a window must be handled by something that isn't an ancestor. MyGrampaWheels encountered this problem here: http://irrlicht.sourceforge.net/forum/v ... =3&t=51031
Or, for example, I have a material panel that creates windows (not inside and thus not clipped by the material panel), and these windows allow the user to edit color or do blah blah blah, which ultimately effect things in the material panel. The solution, and in short...

tl;dr - Please give CGUIWindow an event handler.

By this, I mean a pointer to an IEventReceiver which is defaulted to 0 but can be set by the programmer to handle events from the window, including window close events.
I've named by IEventReceiver as EventParent (following our ISO), and with that assumption, very little changes about CGUIWindow. Aside from the pointer member, it needs only a setter function ( setEventParent(IEventReceiver* pEventReceiver) ) and a slight modification of OnEvent(), a well-tested working version is below:

Code: Select all

 
bool GUIWindow::OnEvent(const SEvent& event)
{
    if (isEnabled())
    {
        switch(event.EventType)
        {
        case EET_GUI_EVENT:
            /* Note that this only handles GUI events. Mouse events for child elements
            are not handled because focus is determined by the environment. */
            if ( event.GUIEvent.Caller != this )
                if ( EventParent )
                    if ( EventParent->OnEvent(event) )
                        return true;
 
            if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST)
            {
                Dragging = false;
                IsActive = false;
 
                if ( EventParent )
                    EventParent->OnEvent(event);
            }
            else
            if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUSED)
            {
                if (Parent && ((event.GUIEvent.Caller == this) || isMyChild(event.GUIEvent.Caller)))
                {
                    Parent->bringToFront(this);
                    IsActive = true;
 
                    if ( EventParent )
                        EventParent->OnEvent(event);
                }
                else
                {
                    IsActive = false;
                }
            }
            else
            if (event.GUIEvent.EventType == EGET_BUTTON_CLICKED)
            {
                if (event.GUIEvent.Caller == CloseButton)
                {
                    if ( EventParent )
                    {
                        SEvent e;
                        e.EventType = EET_GUI_EVENT;
                        e.GUIEvent.Caller = this;
                        e.GUIEvent.Element = 0;
                        e.GUIEvent.EventType = EGET_ELEMENT_CLOSED;
 
                        // event parent may catch this event
                        if ( EventParent->OnEvent(e) )
                            return true;
                    }
 
                    if (Parent)
                    {
                        // send close event to parent
                        SEvent e;
                        e.EventType = EET_GUI_EVENT;
                        e.GUIEvent.Caller = this;
                        e.GUIEvent.Element = 0;
                        e.GUIEvent.EventType = EGET_ELEMENT_CLOSED;
 
                        // if the event was not absorbed
                        if (!Parent->OnEvent(e))
                            remove();
 
                        return true;
 
                    }
                    else
                    {
                        remove();
                        return true;
                    }
                }
            }
            break;
        case EET_MOUSE_INPUT_EVENT:
            switch(event.MouseInput.Event)
            {
            case EMIE_LMOUSE_PRESSED_DOWN:
                DragStart.X = event.MouseInput.X;
                DragStart.Y = event.MouseInput.Y;
                Dragging = IsDraggable;
                if (Parent)
                    Parent->bringToFront(this);
                return true;
            case EMIE_LMOUSE_LEFT_UP:
                Dragging = false;
                return true;
            case 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(core::position2d<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 ( EventParent )
        if ( EventParent->OnEvent(event) )
            return true;
 
    return IGUIElement::OnEvent(event);
}
 
For the lazy, here's the setter function...

Code: Select all

 
void GUIWindow::setEventParent( IEventReceiver* pEventParent )
{
    EventParent = pEventParent;
}
 
CuteAlien
Admin
Posts: 9628
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: Feature Request: Window Event Parent

Post by CuteAlien »

I remember I added that once to IGUIContextMenu. It might make sense in general, thought it's already possible to work around it (for example with dummy-elements as parent, or with passing events on from your user-event-receiver). The questions is also - just CGUIWindow... or IGUIElement which should have this.
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
chronologicaldot
Competition winner
Posts: 684
Joined: Mon Sep 10, 2012 8:51 am

Re: Feature Request: Window Event Parent

Post by chronologicaldot »

Good point. The reason I found it was needed was for custom GUI elements independent of my user receiver (GUI classes I wanted to use for other things). One might think that a work-around is creating a custom wrapper element with a GUI event parent (even though that's one more thing in the heap) that is embedded inside the window so as not to clip it, but the biggest problem is detecting changes in the window. For example, if the window is closed, that close-event might be needed to register changes (as was the case for me). Same could be said for minimize, etc.

Adding an event parent to IGUIElement sounds interesting. I hadn't considered that. I'm inclined to think that it matters most for, what we might call, "detached" GUI elements, such as those of IGUIWindow and IGUIContextMenu (and anything else that can't be clipped). Other things would work with the work-around I mentioned above. But that said, I suppose it could be useful beyond that.

hm... Must think about this more...
Post Reply