Need help with Drag and Drop for Wiki how-to
Moderators: CEGUI MVP, CEGUI Team
Need help with Drag and Drop for Wiki how-to
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!
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!
- 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
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:
This is a handler function to reset highlight for the drag/drop target the item was moved out of:
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:
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.
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.
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);
- jacmoe
- 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
Van wrote:
I will write it up, make sure it works and then create a Wiki how-to for others to use.
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?
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?
- CrazyEddie
- CEGUI Project Lead
- Posts: 6760
- Joined: Wed Jan 12, 2005 12:06
- Location: England
- Contact:
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
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
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
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
- CrazyEddie
- CEGUI Project Lead
- Posts: 6760
- Joined: Wed Jan 12, 2005 12:06
- Location: England
- Contact:
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.
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.
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:
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
- CrazyEddie
- CEGUI Project Lead
- Posts: 6760
- Joined: Wed Jan 12, 2005 12:06
- Location: England
- Contact:
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.
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.
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.
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.
- scriptkid
- Home away from home
- Posts: 1178
- Joined: Wed Jan 12, 2005 12:06
- Location: The Hague, The Netherlands
- Contact:
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
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
- CrazyEddie
- CEGUI Project Lead
- Posts: 6760
- Joined: Wed Jan 12, 2005 12:06
- Location: England
- Contact:
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 5 guests