javapiccolo

Extending PLayer in Piccolo2d


I have next problem. In my project I need to delegate message to the third-party library when nodes are added or removed from layer. In order to achieve this I have extended PLayer in next way:

public class DelegateLayer extends PLayer {
    private Delegate delegate = null;
    private boolean delegationNeeded = false;
    public DelegateLayer() {
        super();
    }
    @Override
    public void removeChildren(final Collection children) {
        for (Object child : children) {
            removeChild((PNode)child);
        }
    }
    @Override
    public void addChildren(final Collection children) {
        for (Object child : children) {
            addChild((PNode) child);
        }
    }

    @Override
    public void addChild(final PNode child) {
        if (delegationNeeded) {
            Preconditions.checkNotNull(delegate, "DelegateLayer: Delegate is not initialized");
            delegate.delegateNodeAdded((CloudNode)child);
        }
        super.addChild(child);
    }

    @Override
    public PNode removeChild(final PNode child) {
        if (delegationNeeded) {
            Preconditions.checkNotNull(delegate, "DelegateLayer: Delegate is not initialized");
            delegate.delegateNodeRemoved((CloudNode)child);
        }
        return super.removeChild(child);
    }

    public void setDelegationNeeded(boolean needed) {
        this.delegationNeeded = needed;
    }

    public void setDelegate(ClusterUpdateDelegate delegate) {
        this.delegate = delegate;
    }
}


I have also added this node layer to the canvas's camera:

DelegateLayer nodeLayer = new DelegateLayer();
camera.addLayer(0, nodeLayer);

However, after I place nodes to the layer and apply transformation (centering nodes on point) nothing happens. But as soon as I swith to PLayer that I get using camera.getLayer(0) everything works fine.

So, could anyone, please, explain what is wrong?


Solution

  • You may be missing a call to add a newly created layer to a PRoot. Here is a snapshot of the runtime structure that is described in in Piccolo2D Patterns:

    enter image description here

    In this brief demo, when you comment out canvas.getCamera().getRoot().addChild(layer); the animation stops working:

    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.geom.Point2D;
    import javax.swing.JFrame;
    import javax.swing.SwingUtilities;
    
    import edu.umd.cs.piccolo.PCanvas;
    import edu.umd.cs.piccolo.PLayer;
    import edu.umd.cs.piccolo.PNode;
    import edu.umd.cs.piccolo.event.PBasicInputEventHandler;
    import edu.umd.cs.piccolo.event.PInputEvent;
    import edu.umd.cs.piccolo.nodes.PPath;
    import edu.umd.cs.piccolo.util.PBounds;
    
    public class Test {
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable() {   
                public void run() {   
                    JFrame frame = new JFrame("Test");
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    frame.setLocationByPlatform(true);
    
                    final PCanvas canvas = new PCanvas() {
                        @Override
                        public Dimension getPreferredSize() {
                            return new Dimension(600, 400);
                        }
                    };
    
                    PLayer layer = new PLayer() {
                        @Override
                        public void addChild(final PNode child) {
                            System.out.println("notify: addChild");
                            super.addChild(child);
                        }
                    };
    
                    canvas.getCamera().addLayer(0, layer);
                    canvas.getCamera().getRoot().addChild(layer);
    
                    final PPath node = PPath.createRectangle(0, 0, 100, 100);
                    node.setPaint(Color.RED);
                    canvas.getLayer().addChild(node);
    
                    canvas.addInputEventListener(new PBasicInputEventHandler() {
                        @Override
                        public void mouseClicked(PInputEvent event) {
                            Point2D p = event.getCamera().localToView(
                                    event.getCanvasPosition());
                            PBounds bounds = node.getBounds();
                            final double dx = p.getX() - bounds.getCenterX();
                            final double dy = p.getY() - bounds.getCenterY();
                            node.animateToBounds(node.getBounds().x + dx, bounds.y
                                    + dy, bounds.width, bounds.height, 300);
                        }
                    });
    
                    frame.add(canvas);            
                    frame.pack();
                    frame.setVisible(true);
                }
            });
        }    
    }
    

    Also, as an alternative, you can listener to PNode.PROPERTY_CHILDREN property without adding a custom layer:

    canvas.getLayer().addPropertyChangeListener(PNode.PROPERTY_CHILDREN, new PropertyChangeListener() {
        @Override
        public void propertyChange(PropertyChangeEvent e) {
            System.out.println("notify");
        }
    });
    

    Although, in this approach you have no information which child was added/removed.