Drag and drop MenuBar item and position them

Ok. When you want to reposition the menubar items dynamically, how would you do it? Say, you have a menubar that provides the horizontal menu to users and users want to reposition the menubar items as per their conveniece like the frequently used menubar items in the first place and least used ones at the last. In this case, if you provide the functionality of repositioning menubar items by dragging and dropping, the above requirement is closed. Check below sample. Drag "Menu 1" and place it on "Menu 2", Menu 2 will move to first place and Menu 1 to second place.


Lets see step by step how to get this functionality.

Step 1: To get this functionality, you need to create your custom class that extends MenuBar. In this custom class add the logic to drag and drop the MenuBarItem. To drag and drop the MenuBarItem, you need add the listeners for multiple events as below and listen to those events. Lets see what each event does.



 public class DraggableMenuBar extends MenuBar {

   public function DraggableMenuBar(){

      super();
      this.addEventListener(DragEvent.DRAG_ENTER, onDragEnter);
      this.addEventListener(DragEvent.DRAG_DROP, onDragDrop);
      this.addEventListener(MouseEvent.MOUSE_DOWN, tryDrag);
      this.addEventListener(MouseEvent.MOUSE_UP, removeDrag);
   }

 }

Step 2: Generally how you drag? First you will hold Mouse (Mouse Down), then drag the selected component to the specific location and then release the Mouse (Mouse Up). For this to happen, you need to listen to all these events. Since the first action is Mouse Down, listen to that event and add an eventhandler to move the move because after you press mouse (Mouse Down), you will drag it. Thats what is done in the below function "tryDrag()". When you are moving mouse after holding it, you will add the component you are dragging to DragSource and drag it.



public class DraggableMenuBar extends MenuBar {

   public function DraggableMenuBar(){

      super();
      
	  this.addEventListener(MouseEvent.MOUSE_DOWN, tryDrag);
      ...
   }

   private function tryDrag(e:MouseEvent):void{
      e.target.addEventListener(MouseEvent.MOUSE_MOVE, doDrag);
   }

   private function doDrag(event:MouseEvent):void{
      var ds:DragSource = new DragSource();
      ds.addData(event.currentTarget,'tabDrag');
      if(event.target is IUIComponent){
         DragManager.doDrag(IUIComponent(event.target),ds,event);
      }
   }

}

Step 3: After you enter the target area which is MenuBar itself, you will accept the component for dropping. What I mean to say here, the target area to drop the component is MenuBar and so if you try to drop the component in any other area, other than MenuBar like any empty space in Panel, the component will move beck to its previous place because that is not the target place and hence the component is not accepted. If you dont mention "DragManager.acceptDragDrop(IUIComponent(event.target))", you cannot drop the component on the MenuBar.



public class DraggableMenuBar extends MenuBar {

   public function DraggableMenuBar(){

      super();
      
      ...
	  this.addEventListener(DragEvent.DRAG_ENTER, onDragEnter);
      ...
   }

   private function onDragEnter(event:DragEvent):void{
      if(event.dragSource.hasFormat('tabDrag')){
         DragManager.acceptDragDrop(IUIComponent(event.target));
      }
   }

}


Step 4: After you drop the component (MenuBarItem) on the MenuBar, you need to reposition it by changing the position of the object in the ArrayCollection (MenuBarItems collection that is set through dataProvider).



public class DraggableMenuBar extends MenuBar {

   public function DraggableMenuBar(){

      super();
      
      ...
      this.addEventListener(DragEvent.DRAG_DROP, onDragDrop);
      ...
   }

   private function onDragDrop(event:DragEvent):void{
			
      var oldPosition:int = ArrayCollection(this.dataProvider)
         .getItemIndex(MenuBarItem(event.dragInitiator).data);

      var childRef:DisplayObject;
      var compPositionAndWidth:Number;

      var newPosition:int = ArrayCollection(this.dataProvider).length - 1;

      //Iterate over the menu bar items
      for(var r:Number = 1; r<numChildren; r++){
         childRef = getChildAt(r); //Get the menubar item

         //Get the component (menu bar item) position and width to identify if the dragged 
         //item is falling in this position and if yes, set the new position of the
         //dragged item
         compPositionAndWidth = (Number(childRef.x) + Number(childRef.width));

         if(event.dragInitiator.mouseX >= childRef.x 
                && event.dragInitiator.mouseX <= compPositionAndWidth){

            newPosition = r;
         }

      }
      newPosition -= 1;
      ArrayCollection(this.dataProvider).removeItemAt(oldPosition);
      ArrayCollection(this.dataProvider).addItemAt(MenuBarItem(event.dragInitiator).data, 
            newPosition<oldPosition?newPosition-1:newPosition);

   }
	
 }


Step 5: Finally once you finish dragging and dropping, when you release mouse (Mouse Up), stop listening to MOUSE_MOVE event.



public class DraggableMenuBar extends MenuBar {

   public function DraggableMenuBar(){

      super();
      
      ...
      this.addEventListener(MouseEvent.MOUSE_UP, removeDrag);
      ...
   }

   private function removeDrag(e:MouseEvent):void{
      e.target.removeEventListener(MouseEvent.MOUSE_MOVE, doDrag);
   }

}


Step 6: Now use this custom class instead of MenuBar class.



<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
   xmlns:mb="com.jusfortechies.controls.*"
   ...
   initialize="initCollections();">

      ...

   <mb:DraggableMenuBar dataProvider="{menuBarCollection}"/>

</mx:Application>



You can download the example source code here.

Download the example code.





blog comments powered by Disqus