Java Tips Weblog

Moving Windows

Posted by Rob Camick on June 14, 2009

Over the years I’ve seen many questions asking about borderless windows. The common suggestion is to use a JWindow or an undecorated JFrame, depending on the requirements. Often you will then find a follow up question asking how to move the window now that there is no title bar. The common answer is that you need to add your own listeners to handle the dragging of the window.

Well, that answer is not necessarily as simple as it seems. Sure the basics are straight forward, you would add a MouseListener to handle the mousePressed event to track the original location of the window. Then you would add a MouseMotionListener to handle the mouseDragged event so the window can be moved with each drag event. I noticed a couple of gotchas that fooled me the first time I attempted to do this.

My first attempt resulted in flickering movement as the window would move to the new location and then back to its original location and finally back to the new location. I now better understand that the mouseDragged events are generated relative to the window. I wasn’t properly considering the fact that the window was changing location at the same time as the mouse was being dragged.

The second problem was a little more subtle. Normally you would add the listeners to the window. However, as we all know each window actually contains a content pane where you add components. So when you click on the window you are actually clicking on the content pane. This is generally not a problem as MouseEvents are passed from ancestor to ancestor to find a component that handles the event, which will usually end up being the window itself. The problem occurs when a MouseListener is added to a child component as the events no longer reach the window. This can happen innocently, for example, by adding a tooltip to a component contained in the window.

My solution for the above gotchas is the ComponentMover class. First it implements a proper algorithm for moving components without flicker. More importantly it gives you flexibility for controlling which component is responsible for moving a Window. You can registor one, or more components with the ComponentMover. This component could be the window itself, or it might be a custom “title bar” that you add to the window. Either way, any drag events generated on these components will affect the window.

Although the above discussion deals directly with moving windows, the actual implementation of the ComponentMover is more generic. Any component can be moved. It could be a top level container like a JFrame or JDialog. Or it could be a container like a JPanel or JInternalFrame. Finally it can be an individual component like a JTextField or JLabel. It depends on how the ComponentMover is constructed. First you need to specify a Component to be moved. Secondly you need to register components to listen for mouseDragged events. This is done using one of the following constructors:

  • ComponentMover() – the empty constructor indicates that each registered component will handle the mouseDragged events. Therefore each individual component can be moved.
  • ComponentMover(Class destinationClass, Component… components) – indicates that each registered component should apply the mouseDragged events to a parent component. In this case the parent component is the first ancestor that is of the specified Class type.
  • ComponentMover(Component destinationComponent, Component… components) – indicates that each registered component should apply the mouseDragged events to the specified component.

Simple examples using each of these constructors would be:

  • to move individual components:
     
    JPanel panel = new JPanel();
    panel.setLayout( null );
    panel.add(c1);
    panel.add(c2);
    panel.add(c3);
    ComponentMover cm = new ComponentMover();
    cm.registerComponent(c1, c2, c3)

     
  • to move a JWindow by dragging a custom title bar:
     
    JWindow window = new JWindow();
    window.add(titleBar, BorderLayout.NORTH);
    ComponentMover cm = new ComponentMover(JWindow.class, titleBar);

     
  • to move a specific Window by dragging a custom title bar:
     
    JWindow window = new JWindow();
    window.add(titleBar, BorderLayout.NORTH);
    ComponentMover cm = new ComponentMover(window, titleBar);

     

There may be situations when you require the ability to both move and resize a component. In this case the mouseDragged event should only be handled by either the move or the resize, but not both. To handle this situation you can use the setDragInsets(…) method. The insets, which typicaly would be the Border insets, specify the area in which drag events will be ignored for the purposes of moving the component. Therefore when you click in this area you will not see the move cursor, but instead you will see the resizing cursor.

The ComponentMover also supports a “snapping” feature. That is you can specify a snap distance and the component can only be moved in increments of that value. Once the mouse is dragged half the snap distance the component will move the entire distance. This will allow the user to more easily align components in a grid.

Hopefully this class will solve your simple dragging issues.

Note, I’d like to thank Maxideon from the Sun forums for solving the “jumping” component problem I noticed when attempting to drag a text field. It turns out that this problem is noticable on any component that use the setAutoScrolls(true) method. When a component is dragged rapidly it appears that the mouse temporarily leaves the bounds of the component which in turn is generating a “synthetic mouseDragged event” which somehow affects the moving of the component. The simple solution was to turn off auto scrolls during the dragging process.

Get The Code

ComponentMover.java

See Also

Resizing Components

3 Responses to “Moving Windows”

  1. Allen Bagwell said

    I’ve tried your code. I’m adding a JPanel inside an undecorated JFrame then using your class code constructor per above:


    new ComponentMover(myFrame, myPanel);

    or


    new ComponentMover(JFrame.class, myPanel);

    The JPanel is simply painted black, no other components — so it just looks like a black rectangle when started. However, the gui still flickers just as badly when dragging it around the screen. I’m running this on a RedHat ENT4 box with Java 6 update 14. Is there something I’m missing here?

    • Rob Camick said

      Both approaches work fine for me. I use JDK6u7 on XP. Create a SSCCE (search the web if you don’t know what a SSCCE is) and send it to me using the “Contact Us” page and I’ll do a quick test to see if it is a code or version problem.

    • Rob Camick said

      Thanks to Allen for looking at the problem on Linux. The solution was to change the code to use:

      MouseEvent.getLocationOnScreen();

      instead of:

      SwingUtilities.convertPointToScreen(...);

      Not sure why the SwingUtilities method occasionally fails on Linux.

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <pre> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>