Implementing Border ComponentResizer Offset

225 views Asked by At

In using the WebLookAndFeel UI for my window frame I looked up a way of resizing undecorated frames. Luckily, that solution was found: Drag and Resize undecorated JFrame

However, in using the Component Resizer code I encountered a problem with border detection. The resizing was detecting a little beyond the visual border of my application. Using Windows's snipping tool I confirmed my suspicions by setting the mode to Window Snip mode, which you can see here. The actual border of my frame is extended past the visual portion that a typical user would see because there's soft shadows being rendered for the frame.

How can I implement offsets into the Component Resizer code so the cursor is detected on the visual border and not the soft shadow border of the look and feel? I've tried playing around with the insets and changing internal values, but no dice. Any help is appreciated.

1

There are 1 answers

0
danthonywalker On BEST ANSWER

After recreating the class myself so I could understand how the code worked I implemented the offset in there. Note, that I've only tested with a negative offset, but it seems to work fine. I also changed up how you install the listeners and variables to control, like zone, which all it does is check half out and half in the line of offset before deciding on the cursor to use. So a zone of 7 will have the cursor check 3 outside and 3 inside.

public class CustomResizer extends MouseAdapter
{
    protected final static Map<Component, CustomResizer> instance = new HashMap<Component, CustomResizer>();
    protected final static Map<Integer, Integer> cursors = new HashMap<Integer, Integer>();
    {
        cursors.put(1, Cursor.N_RESIZE_CURSOR);
        cursors.put(2, Cursor.W_RESIZE_CURSOR);
        cursors.put(4, Cursor.S_RESIZE_CURSOR);
        cursors.put(8, Cursor.E_RESIZE_CURSOR);
        cursors.put(3, Cursor.NW_RESIZE_CURSOR);
        cursors.put(9, Cursor.NE_RESIZE_CURSOR);
        cursors.put(6, Cursor.SW_RESIZE_CURSOR);
        cursors.put(12, Cursor.SE_RESIZE_CURSOR);
    }

    public static CustomResizer install(Component component)
    {
        if(instance.containsKey(component))
        {
            CustomResizer.uninstall(component);
        }
        CustomResizer crInstance = new CustomResizer();
        component.addMouseMotionListener(crInstance);
        component.addMouseListener(crInstance);
        instance.put(component, crInstance);
        return crInstance;
    }

    public static void uninstall(Component component)
    {
        CustomResizer crInstance = instance.get(component);
        instance.remove(component);
        component.removeMouseListener(crInstance);
        component.removeMouseMotionListener(crInstance);
    }

    public static CustomResizer getInstance(Component component)
    {
        return instance.get(component);
    }

    protected final static int NORTH = 1;
    protected final static int WEST = 2;
    protected final static int SOUTH = 4;
    protected final static int EAST = 8;

    private int zone = 7 / 2;
    private int offset = 0;

    private Point pressed;
    private int direction;
    private Rectangle bounds;
    private boolean resizing;
    private boolean autoscrolls;
    private Cursor originalCursor;

    @Override
    public void mouseEntered(MouseEvent e)
    {
        originalCursor = !(resizing) ? e.getComponent().getCursor() : originalCursor;
    }

    @Override
    public void mouseExited(MouseEvent e)
    {
        Component source = e.getComponent();
        source.setCursor(!(resizing) ? originalCursor : source.getCursor());
    }

    @Override
    public void mousePressed(MouseEvent e)
    {
        if(direction != 0)
        {
            resizing = true;

            pressed = e.getPoint();
            Component source = e.getComponent();
            SwingUtilities.convertPointToScreen(pressed, source);
            bounds = source.getBounds();

            if(source instanceof JComponent)
            {
                JComponent jc = (JComponent) source;
                autoscrolls = jc.getAutoscrolls();
                jc.setAutoscrolls(false);
            }
        }
    }

    @Override
    public void mouseDragged(MouseEvent e)
    {
        if(resizing)
        {
            Component source = e.getComponent();
            Point dragged = e.getPoint();
            SwingUtilities.convertPointToScreen(dragged, source);

            int x = bounds.x;
            int y = bounds.y;
            int width = bounds.width;
            int height = bounds.height;
            Dimension maximumSize = source.getMaximumSize();
            Dimension minimumSize = source.getMinimumSize();

            if(WEST == (direction & WEST))
            {
                int drag = getDragDistance(pressed.x, dragged.x);
                drag = getDragBounded(drag, width, minimumSize.width, Math.min(width + x - offset, maximumSize.width));

                x -= drag;
                width += drag;
            }
            if(NORTH == (direction & NORTH))
            {
                int drag = getDragDistance(pressed.y, dragged.y);
                drag = getDragBounded(drag, height, minimumSize.height, Math.min(height + y - offset, maximumSize.height));

                y -= drag;
                height += drag;
            }
            if(EAST == (direction & EAST))
            {
                int drag = getDragDistance(dragged.x, pressed.x);
                drag = getDragBounded(drag, width, minimumSize.width, Math.min(getBoundingSize(source).width - x, maximumSize.width));

                width += drag;
            }
            if(SOUTH == (direction & SOUTH))
            {
                int drag = getDragDistance(dragged.y, pressed.y);
                drag = getDragBounded(drag, height, minimumSize.height, Math.min(getBoundingSize(source).height - y, maximumSize.height));

                height += drag;
            }

            source.setBounds(x, y, width, height);
        }
    }

    @Override
    public void mouseMoved(MouseEvent e)
    {
        Component source = e.getComponent();
        direction = 0;

        Point location = e.getPoint();
        int x = location.x;
        int y = location.y;
        int widthOffset = source.getWidth() + offset;
        int heightOffset = source.getHeight() + offset;

        if(x < -offset + zone && x > -offset - zone) direction += WEST;
        if(x > widthOffset - zone && x < widthOffset + zone) direction += EAST;
        if(y < -offset + zone && y > -offset - zone) direction += NORTH;
        if(y > heightOffset - zone && y < heightOffset + zone) direction += SOUTH;

        source.setCursor(direction == 0 ? originalCursor : Cursor.getPredefinedCursor(cursors.get(direction)));
    }

    @Override
    public void mouseReleased(MouseEvent e)
    {
        resizing = false;

        Component source = e.getComponent();
        source.setCursor(originalCursor);

        if(source instanceof JComponent)
        {
            ((JComponent) source).setAutoscrolls(autoscrolls);
        }
    }

    private int getDragDistance(int larger, int smaller)
    {
        int drag = larger - smaller;
        drag += (drag < 0) ? -1 : 1;
        return drag;
    }

    private int getDragBounded(int drag, int dimension, int minimum, int maximum)
    {
        while(dimension + drag < minimum) drag += 1;
        while(dimension + drag > maximum) drag -= 1;
        return drag;
    }

    private Dimension getBoundingSize(Component source)
    {
        if(source instanceof Window)
        {
            Rectangle bounds = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds();
            return new Dimension(bounds.width - offset, bounds.height - offset);
        }
        else
        {
            Dimension size = source.getParent().getSize();
            return new Dimension(size.width - offset, size.height - offset);
        }
    }

    public void setOffset(int offset)
    {
        this.offset = offset;
    }

    public int getOffset()
    {
        return offset;
    }

    public void setZone(int zone)
    {
        this.zone = zone / 2;
    }

    public int getZone()
    {
        return zone;
    }
}