178
|
Chapter 5, Windows, Dialogs, and Frames
#34 Make a Draggable Window
HACK
However, this hack has one major flaw. Because the component events are
read-only, it is impossible to intercept the move and position the window
before it has been drawn on screen. Thus the window will flash as the user
moves it. Creating a custom event queue would seem to be the answer
because you could then modify the events before they are sent to the compo-
nents, but this won’t work for move events on windows (or any subclass like
JFrame). Windows are real structures provided by the operating system,
rather than purely Java objects. The window events are created by the OS
itself and passed into the JVM from the C level, meaning there is no way to
capture these before they take effect. Still, in many of your applications, the
flashing may be an acceptable trade-off for snapping.
H A C K
#34
Make a Draggable Window Hack #34
Drag a window by clicking on its background using a special event listener.
Most windows let you move them by dragging the titlebar. Some program
windows, however, don’t have titlebars. In the age of eye-candy interfaces
(see iTunes and WinAmp for prime examples) it is very common to have a
window—possibly non-rectangular—without any titlebar or window con-
trols at all. This makes for a pretty window, but how do you move it? Sim-
ply by dragging any available space on the window. Though not terribly
intuitive, such programs are commonplace, and this book wouldn’t be
called Swing Hacks without providing a Java implementation of draggable
windows, even when no titlebar is used.
The simplest approach to this problem is to create a listener that simply
catches all drags and moves the window:
public class MoveMouseListener implements MouseListener, MouseMotionListener
{
JComponent target;
JFrame frame;
public MoveMouseListener(JComponent target, JFrame frame) {
this.target = target;
this.frame = frame;
}
public void mouseClicked(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseMoved(MouseEvent e) {}
public void mouseDragged(MouseEvent e) {
frame.setLocation(new Point(e.getX(),e.getY( ));
}
}
Make a Draggable Window #34
Chapter 5, Windows, Dialogs, and Frames
|
179
HACK
This class implements MouseListener and MouseMotionListener with no-ops
for all methods except
mouseDragged( ), which moves the frame to the cur-
rent mouse location. However, this approach has two problems. First, the
mouse coordinates are going to be relative to the component, rather than the
screen. Thus, a click on a 50 × 50 button in the bottom right of the screen
might return (25, 25) when it should really be more like (1000, 700). The
other problem is that the code moves the origin of the frame to the mouse
cursor. This would look strange because the window would immediately
jump so that its upper-left corner is right under the cursor. The proper
behavior is for the window to stay in the same position relative to the cursor
as the cursor moves around.
The solution to the first problem (getting screen coordinates rather than
component coordinates) is to convert mouse coordinates to absolute screen
coordinates. The following method does just that (we’ll use this shortly in
mouseDragged( )):
Point getScreenLocation(MouseEvent e) {
Point cursor = e.getPoint( );
Point target_location = this.target.getLocationOnScreen( );
return new Point(
(int)(target_location.getX()+cursor.getX( )),
(int)(target_location.getY()+cursor.getY( )));
}
Solving the second issue (keeping the window static relative to the mouse)
requires saving an initial offset between window and cursor, and then main-
taining that offset throughout the drag. You should add a new
mousePressed( )
implementation that saves the current screen location of the mouse cursor
(
start_drag) and the current location of the window (start_loc). The dis-
tance between the two points can be used to form an offset:
Point start_drag;
Point start_loc;
public void mousePressed(MouseEvent e) {
this.start_drag = this.getScreenLocation(e);
this.start_loc = this.getFrame(this.target).getLocation( );
}
Next, the listener should maintain the offset difference throughout the drag
operation by calculating a new offset each time the mouse moves. Here is
the new
mouseDragged( ) method:
public void mouseDragged(MouseEvent e) {
Point current = this.getScreenLocation(e);
Point offset = new Point(
(int)current.getX()-(int)start_drag.getX( ),
(int)current.getY()-(int)start_drag.getY( ));
JFrame frame = this.getFrame(target);

Get Swing Hacks now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.