-4

How to create a component that can be dragged and resized in Java Swing?

Like "Text Tools" text box feature in MS Paint, highlighted by red border in image.

I only want drag and resize feature, not text formatting.

How can I implement this component using Java Swing?

Component is highlighted by red border

Akshat
  • 720
  • 9
  • 24
  • 1
    Since no one else has given you feedback for the downvotes, "suggestions" questions are frowned upon. You need to have a concrete problem. – avgvstvs Nov 12 '14 at 16:21
  • I do have a concrete problem. I searched the web already, did not able to find a component like this. – Akshat Nov 12 '14 at 16:27
  • most likely you'll have to do it yourself. At least standard swing cannot do this. Maybe some external librarly? – cello Nov 12 '14 at 16:31
  • These types of components are commonly used in image editor software. So anyone who created these components can share his knowledge here, I think. – Akshat Nov 12 '14 at 16:32
  • A "concrete problem" in Stackoverflow's context is a problem where you have some code to show with specific implementation problems. Your comment here has transformed your question into "is there a library for..." or "are there any suggestions to do something very general." See [this.](http://stackoverflow.com/help/on-topic) – avgvstvs Nov 13 '14 at 14:45
  • @avgvstvs I went through the page & I think my question falls in category-4 "a practical, answerable problem that is unique to software development". Also, I feel, it is fine to ask a problem that is not concrete but which is answerable & provide a concrete solution. I'd like you to see my answer. Thanks! – Akshat Jan 21 '15 at 18:46

1 Answers1

1

In my quest to solve this problem, I found Piccolo2d ZUI library.

That's exactly, what I was looking for! However, it might not be best approach just to implement a "Draggable & Resizable Text Box", but I got it working in under an hour using Piccolo2d. So, I am posting the code here, if someone can find it useful.

Here is the screenshot of sample application! Draggable & Resizable Box using Piccolo2d

I tried to make the code as describable as possible, so it's a bit long.

You'll require piccolo2d-core-XXX.jar & piccolo2d-extras-XXX.jar to run this code, that can be downloaded from Maven Central Repository. I used version 3.0 & didn't tested with any other version!

Custom Component Class

class PBox extends PNode {

    private PCanvas canvas;

    private Rectangle2D rectangle;

    private Cursor moveCursor;

    public PBox(PCanvas canvas) {           
        this(0, 0, 50, 50, canvas);         
    }

    public PBox(double x, double y, double width, double height, PCanvas canvas) {
        this.canvas = canvas;
        rectangle = new Rectangle2D.Double();
        moveCursor = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR);

        addAnchors(x, y, width, height);
        setBounds(x, y, width, height);
        setInputEventListener();
    }

    private void addAnchors(double x, double y, double width, double height) {
        addChild(new Anchor(SwingConstants.NORTH));
        addChild(new Anchor(SwingConstants.NORTH_EAST));
        addChild(new Anchor(SwingConstants.EAST));
        addChild(new Anchor(SwingConstants.SOUTH_EAST));
        addChild(new Anchor(SwingConstants.SOUTH));
        addChild(new Anchor(SwingConstants.SOUTH_WEST));
        addChild(new Anchor(SwingConstants.WEST));
        addChild(new Anchor(SwingConstants.NORTH_WEST));
    }

    private void setInputEventListener() {
        addInputEventListener(new PBasicInputEventHandler() {

            @Override
            public void mouseEntered(PInputEvent event) {
                canvas.setCursor(moveCursor);
            }

            @Override
            public void mouseExited(PInputEvent event) {
                canvas.setCursor(Cursor.getDefaultCursor());
            }

            @Override
            public void mouseDragged(PInputEvent event) {
                PDimension delta = event.getDeltaRelativeTo(PBox.this);
                translate(delta.width, delta.height);
                event.setHandled(true);
            }

        });
    }

    @Override
    protected void layoutChildren() {
        Iterator iterator = getChildrenIterator();
        int position = SwingConstants.NORTH;
        while (iterator.hasNext()) {
            PNode anchor = (PNode) iterator.next();
            anchor.setBounds(getAnchorBounds(position));
            ++position;
        }
    }

    private Rectangle2D getAnchorBounds(int position) {             
        double x = 0, y = 0;
        Rectangle2D b = getBounds();

        switch (position) {                 
        case SwingConstants.NORTH:
            x = b.getX()+b.getWidth()/2;
            y = b.getY();
            break;

        case SwingConstants.NORTH_EAST:
            x = b.getX()+b.getWidth();
            y = b.getY();
            break;

        case SwingConstants.EAST:
            x = b.getX()+b.getWidth();
            y = b.getY()+b.getHeight()/2;
            break;

        case SwingConstants.SOUTH_EAST:
            x = b.getX()+b.getWidth();
            y = b.getY()+b.getHeight();
            break;

        case SwingConstants.SOUTH:
            x = b.getX()+b.getWidth()/2;
            y = b.getY()+b.getHeight();
            break;

        case SwingConstants.SOUTH_WEST:
            x = b.getX();
            y = b.getY()+b.getHeight();
            break;  

        case SwingConstants.WEST:
            x = b.getX();
            y = b.getY()+b.getHeight()/2;
            break;              

        case SwingConstants.NORTH_WEST:
            x = b.getX();
            y = b.getY();
            break;
        }

        return new Rectangle2D.Double(x-2, y-2, 4, 4);
    }

    @Override
    public boolean setBounds(double x, double y, double width, double height) {
        if (super.setBounds(x, y, width, height)) {
            rectangle.setFrame(x, y, width, height);
            return true;
        }
        return false;
    }

    @Override
    public boolean intersects(Rectangle2D localBounds) {
        return rectangle.intersects(localBounds);
    }

    @Override
    protected void paint(PPaintContext paintContext) {
        Graphics2D g2 = paintContext.getGraphics();
        g2.setPaint(Color.BLACK);
        g2.setStroke(new BasicStroke(1.0f,
                BasicStroke.CAP_BUTT,
                BasicStroke.JOIN_MITER,
                1.0f, new float[]{4.0f}, 0));
        g2.draw(rectangle);
    }

    class Anchor extends PNode {

        private Rectangle2D point;

        private Cursor resizeCursor;

        Anchor(int position) {
            point = new Rectangle2D.Double();
            setCursor(position);
            setInputEventListener(position);
        }

        private void setCursor(int position) {
            switch (position) {                 
            case SwingConstants.NORTH:
                resizeCursor = Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR);
                break;

            case SwingConstants.NORTH_EAST:
                resizeCursor = Cursor.getPredefinedCursor(Cursor.NE_RESIZE_CURSOR);
                break;

            case SwingConstants.EAST:
                resizeCursor = Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR);
                break;

            case SwingConstants.SOUTH_EAST:
                resizeCursor = Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR);
                break;

            case SwingConstants.SOUTH:
                resizeCursor = Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR);
                break;

            case SwingConstants.SOUTH_WEST:
                resizeCursor = Cursor.getPredefinedCursor(Cursor.SW_RESIZE_CURSOR);
                break;  

            case SwingConstants.WEST:
                resizeCursor = Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR);
                break;              

            case SwingConstants.NORTH_WEST:
                resizeCursor = Cursor.getPredefinedCursor(Cursor.NW_RESIZE_CURSOR);
                break;

            default:
                resizeCursor = Cursor.getDefaultCursor();
            }
        }

        private void setInputEventListener(final int position) {
            addInputEventListener(new PBasicInputEventHandler() {

                @Override
                public void mouseEntered(PInputEvent event) {
                    canvas.setCursor(resizeCursor);
                    event.setHandled(true);
                }

                @Override
                public void mouseExited(PInputEvent event) {
                    canvas.setCursor(Cursor.getDefaultCursor());
                    event.setHandled(true);
                }

                @Override
                public void mouseDragged(PInputEvent event) {
                    PDimension delta = event.getDeltaRelativeTo(Anchor.this);
                    PNode parent = getParent();

                    if (position == SwingConstants.EAST
                            || position == SwingConstants.NORTH_EAST
                            || position == SwingConstants.SOUTH_EAST) {
                        parent.setWidth(parent.getWidth() + delta.width);
                    } else if (position == SwingConstants.WEST
                            || position == SwingConstants.NORTH_WEST
                            || position == SwingConstants.SOUTH_WEST) {
                        parent.setX(parent.getX() + delta.width);
                        parent.setWidth(parent.getWidth() - delta.width);
                    }

                    if (position == SwingConstants.SOUTH
                            || position == SwingConstants.SOUTH_EAST
                            || position == SwingConstants.SOUTH_WEST) {
                        parent.setHeight(parent.getHeight() + delta.height);
                    } else if (position == SwingConstants.NORTH
                            || position == SwingConstants.NORTH_EAST
                            || position == SwingConstants.NORTH_WEST) {
                        parent.setY(parent.getY() + delta.height);
                        parent.setHeight(parent.getHeight() - delta.height);
                    }

                    event.setHandled(true);
                }

            });
        }

        @Override
        public boolean setBounds(double x, double y, double width, double height) {
            if (super.setBounds(x, y, width, height)) {
                point.setFrame(x, y, width, height);
                return true;
            }
            return false;
        }

        @Override
        public boolean intersects(Rectangle2D localBounds) {
            return point.intersects(localBounds);
        }

        @Override
        protected void paint(PPaintContext paintContext) {
            Graphics2D g2 = paintContext.getGraphics();
            g2.setColor(Color.WHITE);
            g2.fill(point);
            g2.setStroke(new BasicStroke(1.0f));
            g2.setColor(Color.BLACK);
            g2.draw(point);
        }

    }

}

And, the main class

public class Piccolo2DExample extends PFrame {

    @Override
    public void initialize() {
        PCanvas canvas = getCanvas();

        PNode text = new PText("Draggable & Resizable Box using Piccolo2d");
        text.scale(2.0);
        canvas.getCamera().addChild(text);

        PLayer layer = canvas.getLayer();

        PNode aLayerNode = new PText("A_Layer_Node");
        aLayerNode.setOffset(10, 50);
        layer.addChild(aLayerNode);

        // Adding the component to layer
        layer.addChild(new PBox(50, 100, 250, 50, canvas));
    }

    public static void main(String[] args) {
        PFrame frame = new Piccolo2DExample();
        frame.setSize(800, 640);
    }

}
Akshat
  • 720
  • 9
  • 24