import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.util.HashMap;
import java.util.Map;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.border.EtchedBorder;
import javax.swing.event.MouseInputAdapter;
public class ResizeTest
{
/**
* Main with a JFrame and 2 sample components.
*
* @param args
*/
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(null);
ResizableAndMoveableComponent component = new ResizableAndMoveableComponent(3);
frame.getContentPane().add(component);
component.setBounds(20, 20, 100, 50);
component.getContentPane().setLayout(new GridLayout(1, 1, 0, 0));
component.getContentPane().add(new JButton("Test"));
ResizableAndMoveableComponent component2 = new ResizableAndMoveableComponent(3);
frame.getContentPane().add(component2);
component2.setBounds(140, 20, 100, 50);
component2.getContentPane().setLayout(new FlowLayout());
component2.getContentPane().add(new JComboBox(new String[]
{ "eins", "zwo", "drei" }));
frame.setSize(200, 200);
frame.setVisible(true);
}
/**
* Component which manages the resize. Do not add or remove components to it but use getContentPane().add(..)
*/
public static class ResizableAndMoveableComponent extends JComponent
{
private static final long serialVersionUID = 1L;
private int insets;
// current mode of resize or move
private Mode mode = Mode.NONE;
// offsets of the start of the current resize or move mode
private int offsetLeft = 0;
private int offsetRight = 0;
private int offsetTop = 0;
private int offsetBottom = 0;
// init a map with cached cursors
private Map<Mode, Cursor> cursors = new HashMap<Mode, Cursor>();
{
cursors.put(Mode.NONE, null);
cursors.put(Mode.MOVE, Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
cursors.put(Mode.RESIZE_S, Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR));
cursors.put(Mode.RESIZE_N, Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR));
cursors.put(Mode.RESIZE_E, Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
cursors.put(Mode.RESIZE_W, Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR));
cursors.put(Mode.RESIZE_SW, Cursor.getPredefinedCursor(Cursor.SW_RESIZE_CURSOR));
cursors.put(Mode.RESIZE_SE, Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR));
cursors.put(Mode.RESIZE_NW, Cursor.getPredefinedCursor(Cursor.NW_RESIZE_CURSOR));
cursors.put(Mode.RESIZE_NE, Cursor.getPredefinedCursor(Cursor.NE_RESIZE_CURSOR));
}
public ResizableAndMoveableComponent(int insets)
{
this.insets = insets;
add(new JPanel());
setBorder(BorderFactory.createEtchedBorder(EtchedBorder.RAISED));
setToolTipText("Resize with left button, move with right button");
MouseHandler handler = new MouseHandler();
addMouseMotionListener(handler);
addMouseListener(handler);
}
public JPanel getContentPane()
{
return (JPanel) getComponent(0);
}
@Override
public Dimension getPreferredSize()
{
return translatePanelDimension(getContentPane().getPreferredSize());
}
@Override
public Dimension getMaximumSize()
{
return translatePanelDimension(getContentPane().getMaximumSize());
}
@Override
public Dimension getMinimumSize()
{
return translatePanelDimension(getContentPane().getMinimumSize());
}
private Dimension translatePanelDimension(Dimension d)
{
d.height += 2 * insets;
d.width += 2 * insets;
// avoid int overflow
if (d.height < 0)
d.height = Integer.MAX_VALUE;
if (d.width < 0)
d.width = Integer.MAX_VALUE;
return d;
}
@Override
public void setBounds(int x, int y, int width, int height)
{
super.setBounds(x, y, width, height);
// delegate the bounds to our content pane
getContentPane().setBounds(insets, insets, width - insets - insets, height - insets - insets);
getContentPane().doLayout();
}
// TODO overwrite all add(...) and remove(...) methods to delegate them to ContentPane
/**
* update the cursor with the given mode
*/
private void updateCursor(Mode mode)
{
setCursor(cursors.get(mode));
}
/**
* update the cursor with the mode acording to the given point
*/
private void updateCursor(Point p)
{
updateCursor(getModeForPoint(p));
}
private Mode getModeForPoint(Point p)
{
Rectangle bounds = getBounds();
if (p == null || p.y < 0 || p.y >= bounds.y + bounds.height || p.x < 0 || p.x >= bounds.x + bounds.width)
{
return mode.NONE;
}
// if upper border
else if (p.y <= insets)
{
// if left edge
if (p.x <= insets)
{
return Mode.RESIZE_NW;
}
// if right edge
else if (p.x >= bounds.width - insets)
{
return Mode.RESIZE_NE;
}
else
{
return Mode.RESIZE_N;
}
}
// if lower border
else if (p.y >= bounds.height - insets)
{
// if left edge
if (p.x <= insets)
{
return Mode.RESIZE_SW;
}
// if right edge
else if (p.x >= bounds.width - insets)
{
return Mode.RESIZE_SE;
}
else
{
return Mode.RESIZE_S;
}
}
// if left border
else if (p.x <= insets)
{
return Mode.RESIZE_W;
}
// if right border
else if (p.x >= bounds.width - insets)
{
return Mode.RESIZE_E;
}
else
{
return Mode.NONE;
}
}
private class MouseHandler extends MouseInputAdapter
{
public void mouseMoved(MouseEvent e)
{
if (mode == Mode.NONE)
{
updateCursor(e.getPoint());
}
}
@Override
public void mouseEntered(MouseEvent e)
{
if (mode == Mode.NONE)
{
updateCursor(e.getPoint());
}
}
@Override
public void mouseExited(MouseEvent e)
{
if (mode == Mode.NONE)
{
updateCursor(Mode.NONE);
}
}
@Override
public void mouseDragged(MouseEvent e)
{
if (mode != Mode.NONE)
{
updateAction(e.getPoint());
}
}
@Override
public void mousePressed(MouseEvent e)
{
if (mode == Mode.NONE)
{
if (SwingUtilities.isLeftMouseButton(e))
{
mode = getModeForPoint(e.getPoint());
}
else if (SwingUtilities.isRightMouseButton(e))
{
mode = Mode.MOVE;
}
Rectangle bounds = getBounds();
Point p = e.getPoint();
offsetLeft = p.x;
offsetRight = p.x - bounds.width;
offsetTop = p.y;
offsetBottom = p.y - bounds.height;
updateCursor(mode);
}
}
@Override
public void mouseReleased(MouseEvent e)
{
if (mode != Mode.NONE)
{
updateAction(e.getPoint());
offsetLeft = 0;
offsetRight = 0;
offsetTop = 0;
offsetBottom = 0;
mode = Mode.NONE;
updateCursor(e.getPoint());
}
}
}
private void updateAction(Point p)
{
if (mode == Mode.MOVE)
{
Rectangle newBounds = new Rectangle(getBounds());
newBounds.x += p.x - offsetLeft;
newBounds.y += p.y - offsetTop;
newBounds.x = Math.max(0, newBounds.x);
newBounds.y = Math.max(0, newBounds.y);
setBounds(newBounds);
}
else
{
Rectangle originalBounds = getBounds();
Rectangle newBounds = new Rectangle(originalBounds);
Dimension minSize = getMinimumSize();
Dimension maxSize = getMaximumSize();
if (mode == Mode.RESIZE_N || mode == Mode.RESIZE_NE || mode == Mode.RESIZE_NW)
{
newBounds.y += (p.y - offsetTop);
newBounds.y = Math.min(newBounds.y, originalBounds.y + originalBounds.height - minSize.height);
newBounds.y = Math.max(newBounds.y, originalBounds.y + originalBounds.height - maxSize.height);
newBounds.height = originalBounds.y + originalBounds.height - newBounds.y;
}
else if (mode == Mode.RESIZE_S || mode == Mode.RESIZE_SE || mode == Mode.RESIZE_SW)
{
newBounds.height = p.y - offsetBottom;
newBounds.height = Math.max(minSize.height, newBounds.height);
newBounds.height = Math.min(maxSize.height, newBounds.height);
}
if (mode == Mode.RESIZE_W || mode == Mode.RESIZE_NW || mode == Mode.RESIZE_SW)
{
newBounds.x += (p.x - offsetLeft);
newBounds.x = Math.min(newBounds.x, originalBounds.x + originalBounds.width - minSize.width);
newBounds.x = Math.max(newBounds.x, originalBounds.x + originalBounds.width - maxSize.width);
newBounds.width = originalBounds.x + originalBounds.width - newBounds.x;
}
else if (mode == Mode.RESIZE_E || mode == Mode.RESIZE_NE || mode == Mode.RESIZE_SE)
{
newBounds.width = p.x - offsetRight;
newBounds.width = Math.max(minSize.width, newBounds.width);
newBounds.width = Math.min(maxSize.width, newBounds.width);
}
setBounds(newBounds);
}
}
private enum Mode
{
NONE,
MOVE,
RESIZE_E,
RESIZE_W,
RESIZE_S,
RESIZE_N,
RESIZE_SE,
RESIZE_NE,
RESIZE_NW,
RESIZE_SW,
}
}
}