Jaquarh
Jaquarh

Reputation: 6673

How to drag and drop a JLabel into a JPanel

After researching how to use Label DnD I come across with using this solution:

 public class LayerItem extends JLabel {
    int x = 0, y = 0;

    public LayerItem(String text) {
        this.setText(text);
        this.addMouseMotionListener(new MouseAdapter(){
            @Override
            public void mouseDragged(MouseEvent evt){
                lblMouseDragged(evt);
            }
        });
    }

    protected void lblMouseDragged(MouseEvent evt){
        this.x = evt.getX();
        this.y = evt.getY();
    }
}

As the user clicks, and holds, the JLabel the X and Y are recorded as the mouse moves. However, I am stuck on how to know when the click is stopped (ie, user reaches his targeted JPanel) to then move the text into it.

The only bit of reference to allowing JPanels to receive a drop action is by doing something like this:

new JPanel().setDropTarget(getDropTarget());

However, I cannot again find any references on passing through the JLabel as the drop target with the Coords (Absolute layout).

Upvotes: 1

Views: 1067

Answers (1)

Jaquarh
Jaquarh

Reputation: 6673

After looking at a few examples posted by @MadProgrammer I came up with a solution that extends both the JPanel and JLabel. Here is the JLabel class:

public class LayerItem extends JLabel {

    public LayerItem(String text) {

        this.setText(text);

        this.setTransferHandler(new ValueExportTransferHandler(text));

        this.addMouseMotionListener(new MouseAdapter() {
            @Override
            public void mouseDragged(MouseEvent e) {
                JLabel lbl = (JLabel) e.getSource();
                TransferHandler handle = lbl.getTransferHandler();
                handle.exportAsDrag(lbl, e, TransferHandler.COPY);
            }
        });

    }

    protected static class ValueExportTransferHandler extends TransferHandler {

        public static final DataFlavor SUPPORTED_DATE_FLAVOR = DataFlavor.stringFlavor;
        private String value;

        public ValueExportTransferHandler(String value) {
            this.value = value;
        }

        public String getValue() {
            return value;
        }

        @Override
        public int getSourceActions(JComponent c) {
            return DnDConstants.ACTION_COPY_OR_MOVE;
        }

        @Override
        protected Transferable createTransferable(JComponent c) {
            Transferable t = new StringSelection(getValue());
            return t;
        }

        @Override
        protected void exportDone(JComponent source, Transferable data, int action) {
            super.exportDone(source, data, action);
            // Clean up and remove the LayerItem that was moved
            ((LayerItem) source).setVisible(false);
            ((LayerItem) source).getParent().remove((LayerItem) source);
        }

    }
}

Here is the class for the JPanel:

public class LayerContainer extends JPanel {

    public LayerContainer() {
        this.setTransferHandler(new ValueImportTransferHandler());
        this.setLayout(new GridBagLayout()); // Optional layout
        this.setBorder(new CompoundBorder(new LineBorder(Color.DARK_GRAY), new EmptyBorder(20, 20, 20, 20))); // Optional border
    }

    protected static class ValueImportTransferHandler extends TransferHandler {

        public static final DataFlavor SUPPORTED_DATE_FLAVOR = DataFlavor.stringFlavor;

        public ValueImportTransferHandler() {
        }

        @Override
        public boolean canImport(TransferHandler.TransferSupport support) {
            return support.isDataFlavorSupported(SUPPORTED_DATE_FLAVOR);
        }

        @Override
        public boolean importData(TransferHandler.TransferSupport support) {
            boolean accept = false;
            if (canImport(support)) {
                try {
                    Transferable t = support.getTransferable();
                    Object value = t.getTransferData(SUPPORTED_DATE_FLAVOR);
                    if (value instanceof String) { // Ensure no errors
                        // TODO: here you can create your own handler
                        // ie: ((LayerContainer) component).getHandler()....
                        Component component = support.getComponent();
                        LayerItem j = new LayerItem((String) value);
                        ((LayerContainer) component).add(j); // Add a new drag JLabel
                        accept = true;
                    }
                } catch (Exception exp) {
                    exp.printStackTrace();
                }
            }
            return accept;
        }
    }

}

Here is an example of how you could use this (drag from one JPanel to another and back again):

    JPanel left_panel = new LayerContainer();
    panel_1.setBounds(28, 47, 129, 97);
    add(panel_1);

    LayerContainer right_panel = new LayerContainer();
    layerContainer.setBounds(203, 47, 129, 97);
    add(layerContainer);

    JLabel lblToDrag = new LayerItem("Drag Me");
    GridBagConstraints gbc_lblToDrag = new GridBagConstraints();
    gbc_lblDragMe.gridx = 0;
    gbc_lblDragMe.gridy = 0;
    panel_right.add(lblToDrag, gbc_lblToDrag);

For future use, I'll create a onTransfer() method and create my own LayerContainerHandler() which overrites a run() method so each time a Label is moved to different Containers, it execute seperate actions.

Upvotes: 4

Related Questions