File APIs for Java Developers
Manipulate DOC, XLS, PPT, PDF and many others from your application.
The moose likes Swing / AWT / SWT and the fly likes Dragging and Dropping swing JPanels Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Java » Swing / AWT / SWT
Bookmark "Dragging and Dropping swing JPanels" Watch "Dragging and Dropping swing JPanels" New topic

Dragging and Dropping swing JPanels

Stevo Johnson

Joined: Jun 01, 2009
Posts: 12
Hello everyone!

I am writing a swing program that utilizes draggable JPanels. I would like to be able to drag a JPanel, drop it in another JPanel and have the dropped panel become a child of the panel it was dropped onto. This way if I then drag the parent panel around the new child will move with it. So far I have been using mouseEntered and mouseExited events to track which JPanel the mouse cursor is in. I would like to use that information to find out which component the mouse is over when a dragged component is released. The problem is that, because I'm moving the panel as I drag it, the mouse never leaves this component.

Is there a way that I can disable the "enter-ability" of the component I am dragging? So that mouseEntered and mouseExited events are generated as though the panel I am dragging did not exist?

Alternatively, could I do something like:
1) Make the panel I am dragging invisible.
2) Find the highest visible component under the MouseCursor
3) Make the panel I am dragging visible again.

Let me know if you guys have any suggestions!
Craig Wood
Ranch Hand

Joined: Jan 14, 2004
Posts: 1535
There always seems to be many ways to put something like this together.
I tried adding:
1 — a MouseAdapter to the parent container
2 — a separate MouseAdapter instance to each child component
3 — a single MouseAdapter instance to each child.
Since I generally use option 1 for this kind of drag_n_drop application I tried the others to explore what you might be doing.
I was able to get your idea of
1) Make the panel I am dragging invisible.
2) Find the highest visible component under the MouseCursor
3) Make the panel I am dragging visible again.

to work okay. The mouseEntered and mouseExited methods received events when the drag component was setVisible(false).

Using the mouseEntered/Exited methods to identify drag/drop components only made sense in option 3. I drew an outline of the draggingComponent in the parent containers paint method for some drag–action feedback and to be able to see what was under the cursor. One thing that I didn't like about this approach was that I had to position the cursor directly over the intended drop component to get/trigger the mouseEntered event (used to identify the dropTarget) instead of being able to drop with only the two components intersecting (cursor outside the dropTarget). But this (intersection–drop) could be handled well–enough in the mouseReleased method. While dragging, the mouseEntered event was sent by the component on top of any stacked components, ie, the one closest to the dragComponent.

In options 1 and 2 you could use the componentZOrder to identify the closest dropTarget among multiple/overlapping components under the dragComponent after mouseRelease.
Edit: re-word last sentence.
Stevo Johnson

Joined: Jun 01, 2009
Posts: 12
Thanks for the reply Craig!

I am not sure yet if I am going to try to draw the "ghost" component as I drag and use mouseEntered and mouseExited events to get the target component or if I am going to take this approach:

1) Make the dragged component invisible when the mouse is released.
2) Find the highest component under the mouse cursor and use that as the target component.
3) Make it visible again.
4) Move the dragged component to the new parent component.

Will post again when I get this working.
Stevo Johnson

Joined: Jun 01, 2009
Posts: 12
Alright so I went with the approach in my second reply and got this working beautifully. Each time I drag the component (mouseDragged event) I:

1) make the component being dragged invisible
2) get that components top level container and use the findComponentAt method with a converted mousePoint to find the highest visible component underneath the component I just made invisible
3) set that highest component as the target component
4) then make the dragged component visible again.

This allows me to drag and reposition JPanels on top of other JPanels!!!


Now I'm trying to expand my program to include JSplitPanes as well but I'm running into problems.

My previous solution worked like this:

1) The mouseDragged() method is given a mouseEvent as an argument. This mouseEvent contains a point which is calculated as the difference between the position of the mouse cursor and the top left corner of where the JPanel is DRAWN.
2) Next I moved the JPanel about by using the mouseEvent's point and translating the BOUNDS of the JPanel using setBounds().
3) The next time the JPanel is drawn, its BOUNDS are referenced and it is DRAWN accordingly.
4) Return to step 1.

This solution does not work when I am trying to drag a JPanel that is the child of a JSplitPane. In step 3, the bounds of the JPanel are ignored because the JPanel is restricted to reside within the JSplitPane. I don't mind that the JPanel is restricted there, the problem is that the bounds are used when the mousePoint is converted to the coordinate system of the topLevelContainer. If the bounds dot not correspond to where the component is drawn, it will not correctly determine the cursor's location.

While I was typing this problem up I poked around the API and managed to find a solution (below).

I fixed this by using getLocation() and setLocation() instead of getBounds() and setBounds(). I believe that bounds are a component preference (like size) and not a component rule. When I call setLocation() on a JPanel that is a child of a JSplitPane, neither the location nor the bounds of the JPanel change because the JSplitPane won't allow it.

Why it works:
Originally both the bounds of the JPanel and where it was being drawn were changing together, so the bounds correctly referenced the drawing location. With the JSplitPane the bounds were being changed but the drawing location was not, so there were errors. By using setLocation() we have ensured that the bounds will not change unless the drawing location does so again, they will correspond to each other and we get rid of our errors.

Another problem I found in my old code is that I was translating the JPanel's location before converting the mousePoint to the coordinate system of the top level container. This caused the mouse's movement to be counted twice.
I agree. Here's the link:
subject: Dragging and Dropping swing JPanels
It's not a secret anymore!