Removing event inside the event handler

For help with anything that CEGUI doesn't offer straight out-of-the-box, e.g.:
- Implementation of new features, such as new Core classes, widgets, WindowRenderers, etc. ...
- Modification of any existing features for specific purposes
- Integration of CEGUI in new engines or frameworks and writing of new plugins (Renderer, Parser, ...) or modules

Moderators: CEGUI MVP, CEGUI Team

pw242
Just popping in
Just popping in
Posts: 6
Joined: Fri Apr 29, 2011 12:10

Removing event inside the event handler

Postby pw242 » Sun May 22, 2011 09:49

I am trying to remove event handlers from an object inside an event handler for that object and I get a crash:

Debug assertion failed! include\xtree line 256 expression map/set iterator not incrementable

I am trying to modify the TabControl widget in order to support dragging tabs out into their own separate windows and vice versa. The programme is:
1. Mouse down event on the TabButton widget sets drag_tab to point to the widget, and registers a mouse move event on it
2. If the mouse moves outside the drag threshold we start the drag:
3. This involves creating a new frame window, detaching the Content of the TabButton and attaching it to the window
4. We now want to drag the window, not the tab. So I try to release the mouse events from the TabButton and register them to the Window
5. I would then remove the TabButton and let the drag finish with the Window

I get a crash in step 4 as above. If I don't bother releasing the events and don't delete the TabButton the idea seems to work

Code: Select all

class DragTab : public TabControl
{
/* stuff */
public:
   static const String WidgetTypeName;             //!< Window factory name
   DragTab(const String &type, const String &name) : TabControl(type,name)
/* stuff */
};


It's set up in an initialisation in the main program like this:

Code: Select all

const String DragTab::WidgetTypeName("CEGUI/DragTab");

CEGUI::WindowFactoryManager::addFactory<CEGUI::TplWindowFactory<CEGUI::DragTab>>();
   CEGUI::WindowFactoryManager::getSingleton().addFalagardWindowMapping("TaharezLook/DragTab","CEGUI/DragTab","TaharezLook/TabControl","Falagard/TabControl");
   CEGUI::DragTab *tc = static_cast<CEGUI::DragTab *>(winMgr.createWindow("TaharezLook/DragTab","Overview/TabControl"));


The crash seems to be caused by releasing events, although I can't get my debugger to look into the CEGUI internals so I'm going by commenting out sections:

Code: Select all

bool DragTab::hdl_drag(const CEGUI::EventArgs &args) // registered to EventMouseMove on the tab button
{
/* stuff */
if ((m_args.position.d_x - mouse_clicked_at_x) > drag_threshold
         || (m_args.position.d_x - mouse_clicked_at_x) < -drag_threshold
         || (m_args.position.d_y - mouse_clicked_at_y) > drag_threshold
         || (m_args.position.d_y - mouse_clicked_at_y) < -drag_threshold)
      {
         drag_started=true;
         // need to convert the tab to a freestanding window
         Window *content=drag_tab->getTargetWindow(); // drag_tab is the TabButton we are dragging
         DragTab *tc = static_cast<DragTab*>(WindowManager::getSingleton().getWindow("Overview/TabControl"));
         drag_conn->disconnect();
         drag_tab->releaseInput();

         // release events
         drag_tab->removeAllEvents(); // commenting out this line prevents a crash

         float xpos=CEGUI::CoordConverter::windowToScreenX(*drag_tab,drag_tab->getPosition().d_x);
         float ypos=CEGUI::CoordConverter::windowToScreenY(*drag_tab,drag_tab->getPosition().d_y);
         //tc->removeTab(content->getName());
         //drag_tab=0;

         CEGUI::WindowManager &winMgr = WindowManager::getSingleton();
         drag_window = winMgr.createWindow("TaharezLook/FrameWindow","Overview2");
         winMgr.getWindow("root")->addChildWindow("Overview2");

/* set up window properties here */

         xpos += m_args.position.d_x - mouse_clicked_at_x;
         ypos += m_args.position.d_y - mouse_clicked_at_y;
         mouse_clicked_at_x = m_args.position.d_x;
         mouse_clicked_at_y = m_args.position.d_y;
         drag_window->setPosition(CEGUI::UVector2(CEGUI::UDim(0,xpos),CEGUI::UDim(0,ypos)));

         drag_window->addChildWindow(content);
         drag_conn=drag_window->subscribeEvent(CEGUI::Window::EventMouseMove , CEGUI::Event::Subscriber(&DragTab::hdl_drag, this));
         drag_window->subscribeEvent(CEGUI::Window::EventMouseButtonUp, CEGUI::Event::Subscriber(&DragTab::hdl_mup, this));
         drag_window->captureInput();
}


The workaround I have in mind is to use my game event manager to schedule an event for next tick to deregister and remove the tab so it doesn't happen in a CEGUI event handler. But that seems very messy.
Is there a way to fix this? Maybe I'm doing something fundamentally wrong since this is my first attempt at extending CEGUI in this way?
Alternatively, is there a generally better way to make a drag-out tab?

User avatar
CrazyEddie
CEGUI Project Lead
Posts: 6760
Joined: Wed Jan 12, 2005 12:06
Location: England
Contact:

Re: Removing event inside the event handler

Postby CrazyEddie » Sun May 22, 2011 10:13

The call of removeAllEvents is almost never correct because it deletes all of the Event objects themselves and not just the subscribers, doing this from within the handler of an event that (used to be) in the EventSet is a definite recipe for disaster. Even using disconnect - which is the correct way to detach from an Event - is fraught with danger because it potentially modifies a collection while it is being iterated internally by CEGUI.

The solution is as you suggest - set some flag somewhere and handle it outside of the event handler - admittedly, this is not all that elegant, if you want to request an enhancement of some kind, feel free to do so :)

CE


Return to “Modifications / Integrations / Customisations”

Who is online

Users browsing this forum: No registered users and 12 guests