Need help with Drag and Drop for Wiki how-to

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

User avatar
Van
Just can't stay away
Just can't stay away
Posts: 225
Joined: Fri Jan 21, 2005 20:29
Contact:

Need help with Drag and Drop for Wiki how-to

Postby Van » Wed Sep 28, 2005 20:16

I have been studying the Drag and Drop and I am getting cross-eyed so I would like a little help..

1. I create a display window number 1.
2. I create a display window number 2.
3. I create a Drag Container.
4. I create a StaticImage window and disable it.
5. I add the StaticImage window to the Drag Container.
6. I add the Drag Container to display window 1 via addChild.

This all works fine. I can drag the item all over the place. But, what I am having a problem with is getting Window 2 to accept the Drag Container when I drag and drop the item to it.

I want to be able to verify that the target window can/will accept the Drag Container, if it can accept the Container then have the target window (window 2) accept the Drag Container and remove it from source window (window 1). Would someone please outline the steps. I will write it up, make sure it works and then create a Wiki how-to for others to use.

Thanks!

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

Re: Need help with Drag and Drop for Wiki how-to

Postby CrazyEddie » Thu Sep 29, 2005 09:11

This code was written against current CVS head, it should work with zero, or very minimal, changes for 0.4.0. The example code consists of three event handlers, a helper method to create target slots (to save code repetition), and the main creation and initialisation code. There is nothing earth-shattering going on, so should be simple to understand; even for newbies.

This is a handler function to do a little highlight for the current drag/drop target:

Code: Select all

bool handleDragEnter(const CEGUI::EventArgs& args)
{
    using namespace CEGUI;

    const DragDropEventArgs& ddea = static_cast<const DragDropEventArgs&>(args);
    ddea.window->setProperty("FrameColours", "tl:FF00FF00 tr:FF00FF00 bl:FF00FF00 br:FF00FF00");
    return true;
}


This is a handler function to reset highlight for the drag/drop target the item was moved out of:

Code: Select all

bool handleDragLeave(const CEGUI::EventArgs& args)
{
    using namespace CEGUI;

    const DragDropEventArgs& ddea = static_cast<const DragDropEventArgs&>(args);
    ddea.window->setProperty("FrameColours", "tl:FFFFFFFF tr:FFFFFFFF bl:FFFFFFFF br:FFFFFFFF");
    return true;
}


This is a handler function that transfers the item to the window where the item was dropped, it also resets the highlighting on that item:

Code: Select all

bool handleDragDropped(const CEGUI::EventArgs& args)
{
    using namespace CEGUI;

    const DragDropEventArgs& ddea = static_cast<const DragDropEventArgs&>(args);
    ddea.window->setProperty("FrameColours", "tl:FFFFFFFF tr:FFFFFFFF bl:FFFFFFFF br:FFFFFFFF");
    ddea.window->addChildWindow(ddea.dragDropItem);
    return true;
}


This is a helper to create an item 'slot'. This is just a normal StaticImage with some event handlers attached to do the real work.

Code: Select all

CEGUI::Window* createDragDropSlot(CEGUI::Window* parent, const CEGUI::UVector2& position)
{
    using namespace CEGUI;

    Window* slot = WindowManager::getSingleton().createWindow("TaharezLook/StaticImage");
    parent->addChildWindow(slot);
    slot->setWindowPosition(position);
    slot->setWindowSize(UVector2(cegui_reldim(0.2f), cegui_reldim(0.2f)));
    slot->subscribeEvent(Window::EventDragDropItemEnters, &handleDragEnter);
    slot->subscribeEvent(Window::EventDragDropItemLeaves, &handleDragLeave);
    slot->subscribeEvent(Window::EventDragDropItemDropped, &handleDragDropped);

    return slot;
}


This is the initialisation code. It basically creates two frame windows, then adds a number of 'slots' to each window - the slots will act as targets for drag/drop - they're just normal StaticText widgets with the relevant event handlers attached.

We then create a single DragContainer, and attach to that another StaticImage, this one is the 'item' that we'll be able to drag around the slots in the windows.

Code: Select all

// basic sheet initialisation
WindowManager& winMgr = WindowManager::getSingleton();
DefaultWindow* root = (DefaultWindow*)winMgr.createWindow("DefaultWindow", "Root");
System::getSingleton().setGUISheet(root);

// create main rucksack window
Window* rs = winMgr.createWindow("TaharezLook/FrameWindow");
root->addChildWindow(rs);
rs->setWindowPosition(UVector2(cegui_reldim(0.1f), cegui_reldim( 0.25f)));
rs->setWindowSize(UVector2(cegui_reldim(0.33f), cegui_reldim( 0.33f)));
rs->setWindowMaxSize(UVector2(cegui_reldim(1.0f), cegui_reldim( 1.0f)));
rs->setWindowMinSize(UVector2(cegui_reldim(0.1f), cegui_reldim( 0.1f)));
rs->setText("Rucksack");

// create main equipped items window.
Window* eq = winMgr.createWindow("TaharezLook/FrameWindow");
root->addChildWindow(eq);
eq->setWindowPosition(UVector2(cegui_reldim(0.57f), cegui_reldim( 0.25f)));
eq->setWindowSize(UVector2(cegui_reldim(0.33f), cegui_reldim( 0.33f)));
eq->setWindowMaxSize(UVector2(cegui_reldim(1.0f), cegui_reldim( 1.0f)));
eq->setWindowMinSize(UVector2(cegui_reldim(0.1f), cegui_reldim( 0.1f)));
eq->setText("Equipped Items");

// add slots to rucksack
Window* startSlot =
createDragDropSlot(rs, UVector2(cegui_reldim(0.05f), cegui_reldim(0.2f)));
createDragDropSlot(rs, UVector2(cegui_reldim(0.3f), cegui_reldim(0.2f)));
createDragDropSlot(rs, UVector2(cegui_reldim(0.55f), cegui_reldim(0.2f)));
createDragDropSlot(rs, UVector2(cegui_reldim(0.05f), cegui_reldim(0.5f)));
createDragDropSlot(rs, UVector2(cegui_reldim(0.3f), cegui_reldim(0.5f)));
createDragDropSlot(rs, UVector2(cegui_reldim(0.55f), cegui_reldim(0.5f)));

// add slots to equipment window
createDragDropSlot(eq, UVector2(cegui_reldim(0.05f), cegui_reldim(0.2f)));
createDragDropSlot(eq, UVector2(cegui_reldim(0.3f), cegui_reldim(0.2f)));
createDragDropSlot(eq, UVector2(cegui_reldim(0.55f), cegui_reldim(0.2f)));
createDragDropSlot(eq, UVector2(cegui_reldim(0.05f), cegui_reldim(0.5f)));
createDragDropSlot(eq, UVector2(cegui_reldim(0.3f), cegui_reldim(0.5f)));
createDragDropSlot(eq, UVector2(cegui_reldim(0.55f), cegui_reldim(0.5f)));

// create a drag/drop item
DragContainer* item = static_cast<DragContainer*>(
    winMgr.createWindow("DragContainer", "theItem"));
item->setWindowPosition(UVector2(cegui_reldim(0.05f), cegui_reldim(0.05f)));
item->setWindowSize(UVector2(cegui_reldim(0.9f), cegui_reldim(0.9f)));

// set a static image as drag container's contents
Window* itemIcon = winMgr.createWindow("TaharezLook/StaticImage");
item->addChildWindow(itemIcon);
itemIcon->setWindowPosition(UVector2(cegui_reldim(0), cegui_reldim(0)));
itemIcon->setWindowSize(UVector2(cegui_reldim(1), cegui_reldim(1)));
itemIcon->setProperty("Image", "set:TaharezLook image:CloseButtonNormal");
// disable to allow inputs to pass through.
itemIcon->disable();

// set starting slot for the item.
startSlot ->addChildWindow(item);

User avatar
jacmoe
Just can't stay away
Just can't stay away
Posts: 136
Joined: Sun Apr 03, 2005 14:18
Location: Holbaek, Denmark
Contact:

Re: Need help with Drag and Drop for Wiki how-to

Postby jacmoe » Thu Sep 29, 2005 16:24

Van wrote:
I will write it up, make sure it works and then create a Wiki how-to for others to use.
:hammer:

Rackle
CEGUI Team (Retired)
Posts: 534
Joined: Mon Jan 16, 2006 11:59
Location: Montréal

Postby Rackle » Wed Nov 08, 2006 19:44

I'm working on the wiki (couldn't wait for Van anymore) for an enhanced version of this topic. The main enhancement being the ability to add parameters to determine whether a drag item can be dropped into a particular drag target. One case is giving me some difficulty.

When the drag targets are empty then the events are easy to manage: EventDragDropItemEnters is received when the drag item hovers over the drag target and EventDragDropItemLeaves is received when the drag item is moved out of the drag target's window. However when I drop an item into a drag target then these messages are also sent when a different drag item moves over the "slotted" drag item. What would be the best solution to deal with this case? I want the drag target to receive the messages even if it is slotted with a drag item.

A second enhancement would be to support the pick-up and drop of drag items; click once to pick up a drag item, move it around with the mouse, and then click again to drop it. I'm thinking that the solution may revolve around overriding the mouse events. Any suggestions?

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

Postby CrazyEddie » Thu Nov 09, 2006 09:29

Hi Rackle,

The first point is something that I had not considered and is, I can see, quite an obstacle.

For the library devs, the answer is obvious - to implement a setting that enables or disables a Window's ability to receive those events - and allowing the event to propagate up to parents. I've made a note and will add this as a feature request.

For people out in in 'user-land' that solution is not so ideal, and neither is the interim solution, but here goes! What you need to do is subscribe those events on the 'item' with a handler which will fire the event on the parent. You can do that by using the following members of Window:
notifyDragDropItemEnters
notifyDragDropItemLeaves
notifyDragDropItemDropped

Normally you do not use such functions yourself, bun in this case it's the best solution for the time being.

For the other point, this is a good idea, and another one which I'm interested in putting in as a feature. To do it at the moment, you're right that you would need to sub-class and override the mouse click or mouse up & down event handlers - best way would be to respond to those can set / reset the dragging state using 'initialiseDragging' method and the onCaptureLost hander (or just use the code in those as a starting point. Obviously with sub-classing you need to create a factory and register that with the system also.

Anyway, good-luck, let us know if there is anything I can clarify more, and I look forward to seeing a wiki article ;)

CE.

PS. I personally will probably not be back on here until Monday, so if there is no further reply, that's why :)

Rackle
CEGUI Team (Retired)
Posts: 534
Joined: Mon Jan 16, 2006 11:59
Location: Montréal

Postby Rackle » Thu Nov 09, 2006 18:53

It worked; I can handle drag 'n' dropping over a drop target that already contains a drag item. Now I just need to take care of all the little details. One such detail is that I'm now needlessly generating multiple enters & leaves events when hovering over the drop target's children.

Rackle
CEGUI Team (Retired)
Posts: 534
Joined: Mon Jan 16, 2006 11:59
Location: Montréal

Postby Rackle » Thu Nov 09, 2006 19:24

A different approach could be to override the onDragDropTargetChanged event within the drag item such that it would climb up the parent hierarchy of the new target until the drop target window is found.

For this to work there needs to be a list of drop target windows, which is not a problem in my implementation since I created a class to contain the conditions/requirements to allow an item to be dropped within each drop target.

This approach would solve my problem of multiple enter & leave events being generated.

P.S. I'm abusing the message board and using it as my personal notepad :wink:

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

Postby CrazyEddie » Mon Nov 13, 2006 09:44

Yeah, subclassing and overriding those members is the 'better' way; I was not sure if you wanted to get into that side of things ;)

Feel free to abuse the board :lol:

CE.

Rackle
CEGUI Team (Retired)
Posts: 534
Joined: Mon Jan 16, 2006 11:59
Location: Montréal

Postby Rackle » Mon Nov 13, 2006 13:49

I've tested a few things and here's how my final implementation is shaping out.

The DropTarget window is no longer subscribing to any drag and drop events. Instead it's being driven by the DragItem. The DragItem is subscribing to DragContainer::EventDragDropTargetChanged and DragContainer::EventDragEnded. Also none of its children are subscribing to drag and drop events. Finally I've created an internal list of DropTarget windows: static std::map<CEGUI::String, DragDropTarget*> mDropTargets;

DragContainer::EventDragDropTargetChanged is used to determine whether the drag item is hovering over a new DropTarget. To do this I perform two tests. 1) is the ddea.window's parent within my list of DropTarget windows (when a DragItem is hovering over an empty DropTarget) and 2) is one of the parent's of ddea.window a DragItem, in which case I can then find its parent and handle the event appropriately. This let's me generate a single "enters" and "leaves" event to the DropTarget.

DragContainer::EventDragEnded is used to intercept the "drops" event. The DragContainer::EventDragDropTargetChanged event updates the current DropTarget variable, which DragContainer::EventDragEnded can then use to dispatch the event to.

The DropTarget has functions to handle calls from the DragItem: onEnter(DragItem*), onLeave(DragItem*), and onDrop(DragItem*). Passing the DragItem allows the DropTarget to test whether that DragItem is valid.

What remains to be delt with is the case where a DragItem-1 is dropped within a DropTarget that already contains a DragItem-2. I think the proper behavior would be to for DragItem-1 to replace DragItem-2 within DropTarget's slot and for DragItem-2 to latch onto the cursor and activate the pickup-and-drop mode mentioned in a previous post.

So far everything is contained within two classes, outside of the main Cegui code. Maybe the CEGUI::DragContainer needs to be reworked/rethinked; I think there may be a need for a CEGUI::DropContainer class to be added. More later, as I finish testing.

Rackle
CEGUI Team (Retired)
Posts: 534
Joined: Mon Jan 16, 2006 11:59
Location: Montréal

Postby Rackle » Mon Nov 13, 2006 18:50

Grrr, another feature/gotcha/concept that I had not thought of is that sometimes we do not want to move the drag item but instead copy/clone it. If I have a toolbar of actions then yes, I want to move the DragItem from one slot to another. However if I have a spellbook and want to drag a spell to a toolbar then I want to copy/clone the DragItem; I don't want to remove it from the spellbook.

Hopefully I've now covered every possible behavior for the DragItem and DropTarget widgets:
  • clone the DragItem from a DropTarget to a new DropTarget (spellbook to toolbar)
  • move the DragItem from a DropTarget to a new DropTarget (toolbar to toolbar)
  • highlight the DropTarget when a DragItem hovers it
  • remove the highlight when the DragItem moves away from the DropTarget
  • DropTarget validates (accept/refuse) a DragItem. Can be implemented as a DropTarget highlight (red for no and green for yes) or by changing the cursor image (large red X when no)
  • pickup (new drag'n'drop mode where the DragItem is anchored to the cursor and dropped with a single click) a DragItem when dropping a second DragItem into an already slotted DropTarget

Pompei2
Home away from home
Home away from home
Posts: 489
Joined: Tue May 23, 2006 16:31

Postby Pompei2 » Mon Nov 13, 2006 23:37

Cool, i'm looking forward to read that tutorial ;)

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

Postby CrazyEddie » Tue Nov 14, 2006 09:39

Yep, sounds very cool. Hopefully we'll be able to pick a few ideas and fix shortcomings in the system.

A CEGUI::DropContainer is certainly possible, though I do like the idea of being able to have any window as the receiver, so any specific receiver class would be an addition rather than removing what we currently have.

Anyway, I'm looking forwards to seeing the finished article :)

CE.

Rackle
CEGUI Team (Retired)
Posts: 534
Joined: Mon Jan 16, 2006 11:59
Location: Montréal

Postby Rackle » Tue Nov 14, 2006 18:28

It's becoming complex but will hopefully not become complicated; those two terms are similar but do not have the same meaning. These may be the last points involving drag and drop (sorry for the incomplete sentences):

Drag a DragItem from spellbook DropTarget to spellbook DropTarget:
1) Exchange DragItem within the two DropTarget

Drag a DragItem from toolbar DropTarget to toolbar DropTarget:
1) Exchange DragItem within the two DropTarget
2) If target is slotted then pickup the slotted DragItem
3) If target is slotted then replace the slotted DragItem

Drag a DragItem from spellbook DropTarget to toolbar DropTarget:
* The Source DropTarget clones the DragItem (it is not removed)
2) If target is slotted then pickup the slotted DragItem
3) If target is slotted then replace the slotted DragItem

Note: the numbers represent possible choices.


A distinction must be made between a library that provides low level events, and an applications which builds upon that library. I'm working through the various features I'll need for my personal project and, as a way of "paying" for Cegui, provide these features as Wikied articles/snippits. This drag and drop thing is evolving more toward a set of classes to provide a higher level framework than a tutorial. Hopefully it'll remain clear enough to be understood by others, as well as covering every possible use.

On the library side I would argue that the various EventDragDrop should only be exchanged between the DragItem and the DropTarget. The only real problem right now is that moving a DragItem over the children of a DropTarget generates multiple "enters" and "leaves" events, when in fact the DragItem remains within the same DropTarget.

User avatar
scriptkid
Home away from home
Home away from home
Posts: 1178
Joined: Wed Jan 12, 2005 12:06
Location: The Hague, The Netherlands
Contact:

Postby scriptkid » Thu Nov 16, 2006 08:56

Hi Rackle,

thanks in advance for your efforts! :-)

Just one FYI: as you might know we are working on a cegui manual, which will wrap up many of the current wiki pages and of course new stuff too. I have no idea how a wiki page would be written differently from a manual page, but i just want to mention that new wiki pages might also end up in the manual in one form or the other.

Of course with credits to those who have written the original texts! :-)

Sorry if this post doesn't make any sense at all ;-)

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

Postby CrazyEddie » Thu Nov 16, 2006 09:12

Rackle wrote:On the library side I would argue that the various EventDragDrop should only be exchanged between the DragItem and the DropTarget. The only real problem right now is that moving a DragItem over the children of a DropTarget generates multiple "enters" and "leaves" events, when in fact the DragItem remains within the same DropTarget.


Yeah, I largely agree. This should be achieved via the new setting that I'll be putting in to allow a window to 'opt out' of getting those events.

CE.


Return to “Modifications / Integrations / Customisations”

Who is online

Users browsing this forum: No registered users and 2 guests