views:

1686

answers:

8

Once I've called DragManager.acceptDrag is there any way to "unaccept" the drag? Say that I have a view which can accept drag and drop, but only in certain areas. Once the user drags over one of these areas I call DragManager.acceptDrag(this) (from a DragEvent.DRAG_OVER handler), but if the user then moves out of this area I'd like to change the status of the drag to not accepted and show the DragManager.NONE feedback. However, neither calling DragManager.acceptDrag(null) nor DragManager.showFeedback(DragManager.NONE) seems to have any effect. Once I've accepted the drag an set the feedback type I can't seem to change it.

Just to make it clear: the areas where the user should be able to drop are not components or even display objects, in fact they are just ranges in the text of a text field (like the selection). Had they been components of their own I could have solved it by making each of them accept drag events individually. I guess I could create proxy components that float over the text to emulate it, but I'd rather not if it isn't necessary.

+2  A: 

Are you using only the dragEnter method? If you are trying to reject the drag while still dragging over the same component you need to use both the dragEnter and dragOver methods.

Check out this example:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
    <mx:Script>
     <![CDATA[
      import mx.core.DragSource;
      import mx.managers.DragManager;
      import mx.events.DragEvent;

      private function onDragEnter(e:DragEvent):void {
       if ( e.target == lbl ) {

        if ( e.localX < lbl.width/2 ) {
         trace("accept");
         DragManager.acceptDragDrop(this);
        }
        else {
         DragManager.acceptDragDrop(null);
        }
       }
      }

      private function doStartDrag(e:MouseEvent):void {
       if ( e.buttonDown ) {
        var ds:DragSource = new DragSource();
        ds.addData("test", "text");

        DragManager.doDrag(btn, ds, e);
       }
      }
     ]]>
    </mx:Script>
    <mx:Label id="lbl" text="hello world!" left="10" top="10" dragEnter="onDragEnter(event)" dragOver="onDragEnter(event)" />
    <mx:Button id="btn" x="47" y="255" label="Button" mouseMove="doStartDrag(event)"/>
</mx:Application>
maclema
+1  A: 

@maclema Accepting the drag in the DRAG_ENTER handler just makes things worse. I still can't reject the drag (calling DragManager.acceptDragDrop(null) has no effect, at least no visible effect), and accepting the drag on enter removes the little control I had. Now the + below the pointer is shown as soon as the mouse enters the component, instead of starting to show when the user dragged over the special areas.

The weird thing is that it seems like once set the feedback (DragManager.showFeedback) can't be changed. Whatever I do it only shows a + below the pointer, NONE shows a +, LINK shows a +, MOVE shows a + and COPY shows a plus.

I should add that I'm testing this on a Mac in AIR.

Theo
A: 

ok, I see the problem now. Rather than null, try setting it to the dragInitiator.

Check this out.

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
    <mx:Script>
     <![CDATA[
      import mx.controls.Alert;
      import mx.events.DragEvent;
      import mx.managers.DragManager;
      import mx.core.DragSource;

      private function doStartDrag(e:MouseEvent):void {
       if ( e.buttonDown && !DragManager.isDragging ) {
       var ds:DragSource = new DragSource();
       ds.addData("test", "test");

       DragManager.doDrag(btn, ds, e);
       }
      }

      private function handleDragOver(e:DragEvent):void {
       if ( e.localX < cvs.width/2 ) {
        //since null does nothing, lets just set to accept the drag
        //operation, but accept it to the dragInitiator
        DragManager.acceptDragDrop(e.dragInitiator);
       } 
       else {
        //accept drag
        DragManager.acceptDragDrop(cvs);
        DragManager.showFeedback( DragManager.COPY );
       }
      }

      private function handleDragDrop(e:DragEvent):void {
       if ( e.dragSource.hasFormat("test") ) {
        Alert.show("Got a drag drop!");
       }
      }
     ]]>
    </mx:Script>
    <mx:Canvas x="265" y="66" width="321" height="245" backgroundColor="#FF0000" id="cvs" dragOver="handleDragOver(event)" dragDrop="handleDragDrop(event)">
    </mx:Canvas>
    <mx:Button id="btn" x="82" y="140" label="Drag Me" mouseDown="doStartDrag(event)"/>
</mx:WindowedApplication>
maclema
A: 

@maclema, well, that would make the initiator a target too, showing the + when the user drags over that component (which it definitely shouldn't since it doesn't accept drops). So that doesn't work either.

Theo
+1  A: 

I've tested some more and what @maclema works, but not if you run in AIR. It seems like drag and drop in AIR is different. It's really, really weird because not only is the feedback not showing correctly, and it's not possible to unaccept, but the coordinates are also completely off. I just tried my application in a browser instead of AIR and dragging and dropping is completely broken.

Also, skipping the dragEnter handler works fine in AIR, but breaks everything when running in a browser.

Theo
A: 

Yes, drag and drop is different in AIR. I HATE that! It takes a lot of playing around to figure out how to get things to work the same as custom dnd that was built in flex.

As for the coordinates, maybe play around with localToContent, and localToGlobal methods. They may help in translating the coordinates to something useful.

Good luck. I will let you know if I think of anything else.

maclema
+1  A: 

I've managed to get it working in both AIR and the browser now, but only by putting proxy components on top of the ranges of text where you should be able to drop things. That way I get the right feedback and drops are automatically unaccepted on drag exit.

This is the oddest thing about D&D in AIR:

DragManager.doDrag(initiator, source, event, dragImage, offsetX, offsetY);

In browser-based Flex, offsetX and offsetY should be negative (so says the documentation, and it works fine). However, when running exactly the same code in AIR you have to make the offsets positive. The same numbers, but positive. That is very, very weird.

Theo
A: 

If you don't need native drag and drop in AIR, you can get the Flex drag and drop behavior by subclassing WindowedApplication and setting the DragManager. See this post on the Adobe Jira for more info: https://bugs.adobe.com/jira/browse/SDK-13983

Christophe Herreman