Add label on drawing in Java


My experience in Java is very little but I like programming and everything I make I want it to be as good as possible. Having studied Java for a total time of at most 2 days, it is not so easy.

I've been asked to make a Logic Gate Simulator. I've done everything besides the GUI. It is not necessary as given by our teacher but it is for me as communication with the user in such a program is complicated. A GUI will make it much more clear.

I would like to create the gates on a canvas and then be able to move them around. I started by making an AND gate and got it to move around with the mouse when clicked.

I noticed, however, that now I have a canvas on top of everything. Every label,button, etc I add is behind the canvas. It seems like the canvas is necessary in order to move the gate as it is actually repainted when I move it.

The gate AND is made inside a class with paintComponent. Will I have to make every gate in this single class so they can be on the same canvas? How can I make every gate,label,button share the same canvas ?

Here is my code finally.Gates move with a double-click. It is long though.

Main:

                package Pack;
                import java.util.Scanner;
                import javax.swing.*;
                import java.awt.*;
                import java.awt.geom.*;

                public class Main {
                    public static JFrame f;
                    public static void main(String[] args) {

                    ShapeAnd sh=new ShapeAnd();
                    ShapeOr sh2=new ShapeOr();

                    f=new JFrame();
                    f.add(sh);
                    f.add(sh2);
                    f.setVisible(true);
                    f.setSize(700,600);
                    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                    f.setTitle("LGS");
                    f.getContentPane().setBackground(Color.RED);
                    }
                    }

OR:

            package Pack;
            import javax.swing.*;
            import java.awt.*;
            import java.awt.event.*;
            import java.awt.geom.*;

            public class ShapeOr extends JPanel implements MouseListener,MouseMotionListener{

                int preX,preY,preX2,preY2,difX,difY;
                Graphics g2;
                GeneralPath Or;
                int lim1x,lim2x,lim1y,lim2y;
                boolean check;
                public ShapeOr() {
                    preX=15;
                    preY=0;
                    addMouseMotionListener(this);
                    addMouseListener(this);
                  }
                public void paintComponent(Graphics g){
                    super.paintComponent(g);
                     Graphics2D g2 = (Graphics2D) g;
                     lim1x=preX;
                     lim2x=preX+80;
                     lim1y=preY;
                     lim2y=preY+60;
                             int x1Points[] = {preX,preX+50,preX+60,preX+70,preX+80,preX+70,preX+60,preX+50,preX,preX+10,preX+20,preX+30,preX+20,preX+10,preX};
                            int y1Points[] = {preY,preY,preY+5,preY+15,preY+30,preY+45,preY+55,preY+60,preY+60,preY+55,preY+45,preY+30,preY+15,preY+5,preY};
                            GeneralPath Or = new GeneralPath(GeneralPath.WIND_EVEN_ODD,
                                                    x1Points.length);

                            Or.moveTo(preX-15,preY+15);
                            Or.lineTo(preX+20,preY+15);
                            Or.moveTo(preX-15,preY+45);
                            Or.lineTo(preX+20,preY+45);
                            Or.moveTo(preX,preY);

                            for (int index = 1; index < x1Points.length; index++) {
                                    Or.lineTo(x1Points[index], y1Points[index]);
                            };
                            Or.closePath();
                            g2.draw(Or);
                            //check=false;
                }
                public void mousePressed(MouseEvent e) {
                    difX=preX-e.getX();
                    difY=preY-e.getY();

                  }
                public void updateLocation(MouseEvent e){
                    preX=e.getX()+difX;
                    preY=e.getY()+difY;
                    repaint();
                }

                      public void mouseReleased(MouseEvent e) {
                       check=false;
                      }

                      public void mouseMoved(MouseEvent e) {
                      }

                      public void mouseClicked(MouseEvent e) {
                          int mouseX=e.getX();
                          int mouseY=e.getY();
                            if(mouseX>lim1x && mouseX<lim2x && mouseY>lim1y && mouseY<lim2y){
                                check=true;
                            }
                      }

                      public void mouseExited(MouseEvent e) {
                      }

                      public void mouseEntered(MouseEvent e) {
                      }

                      public void mouseDragged(MouseEvent e) {
                          if(check==true){
                              updateLocation(e);
                          }
                      }

            }

AND:

        package Pack;
        import javax.swing.*;
        import java.awt.*;
        import java.awt.event.*;
        import java.awt.geom.*;

        public class ShapeAnd extends JPanel implements MouseListener,MouseMotionListener{

            int preX,preY,preX2,preY2,difX,difY;
            Graphics g2;
            GeneralPath And;
            int lim1x,lim2x,lim1y,lim2y;
            boolean check;
            public ShapeAnd() {
                addMouseMotionListener(this);
                addMouseListener(this);
              }
            public void paintComponent(Graphics g){
                super.paintComponent(g);
                 Graphics2D g2 = (Graphics2D) g;
                 lim1x=preX+15;
                 lim2x=preX+95;
                 lim1y=preY;
                 lim2y=preY+75;
                         int x1Points[] = {preX,preX+ 50, preX+60,preX +70,preX+80,preX+70,preX+60,preX+50,preX+0};
                        int y1Points[] = {preY+0,preY+ 0,preY+5,preY+15,preY+30,preY+45,preY+55,preY+60,preY+60};
                        GeneralPath And = new GeneralPath(GeneralPath.WIND_EVEN_ODD,
                                                x1Points.length);

                        And.moveTo(preX,preY+15);
                        And.lineTo(preX+15,preY+15);
                        And.moveTo(preX,preY+45);
                        And.lineTo(preX+15,preY+45);
                        And.moveTo(preX+15,y1Points[0]);

                        for (int index = 1; index < x1Points.length; index++) {
                                And.lineTo(x1Points[index]+15, y1Points[index]);
                        };
                        And.closePath();
                        g2.draw(And);
                        //check=false;
            }
            public void mousePressed(MouseEvent e) {
                difX=preX-e.getX();
                difY=preY-e.getY();

              }
            public void updateLocation(MouseEvent e){
                preX=e.getX()+difX;
                preY=e.getY()+difY;
                repaint();
            }

                  public void mouseReleased(MouseEvent e) {
                   check=false;
                  }

                  public void mouseMoved(MouseEvent e) {
                  }

                  public void mouseClicked(MouseEvent e) {
                      int mouseX=e.getX();
                      int mouseY=e.getY();
                        if(mouseX>lim1x && mouseX<lim2x && mouseY>lim1y && mouseY<lim2y){
                            check=true;
                        }
                  }

                  public void mouseExited(MouseEvent e) {
                  }

                  public void mouseEntered(MouseEvent e) {
                  }

                  public void mouseDragged(MouseEvent e) {
                      if(check==true){
                          updateLocation(e);
                      }
                  }

        }

PS:Needs a better title, I know.


Answers:


Thank you for your code post as that helps clarify things quite a bit. My assumption was correct -- you're making your gates extend a GUI component, adding a lot of unnecessary "weight" to them, and making them hard to move around as you'd like, or place multiple ones of them on your GUI.

Suggestions for a solution include:

  • Make your gates much more light-weight by not having them extend JPanel or any GUI component.
  • Instead make them "logical" (non-GUI components) that can be drawn by a single drawing component via a public void draw(Graphics2d g2) method. I usually use the paintComponent(Graphics g) method of a single JPanel to do this drawing.
  • Give this drawing JPanel an ArrayList of your gate objects, and then draw the gates by iterating through the list in the JPanel's single paintComponent method.
  • Add to the same JPanel a MouseAdapter as both mouse listener and motion listener, and allow this listener to change the state of any of your gate shapes that have been clicked or dragged on.
  • Allow the gates to draw themselves by giving them a draw(...) method that a drawing component can call,
  • Give your gate objects a public boolean contains(Point p) method that allows you to tell if the mouse clicks on them
  • And give them getter and setter methods for their positions, so that this can be checked and changed.
  • Utilize the functionality classes that derive from the Shape interface (by composition) to help give your own shape the ability to draw itself and move. I've used Path2D objects for this as they can be easily moved by using AffineTransforms.

Example code forthcoming....

All Gate objects can share the same interface,...

interface MyGate {
    void draw(Graphics2D g2);
    void setPoint(Point p);
    Point getPoint();
    boolean contains(Point p);  
}

an example gate class that implements the above interface

class OrGate implements MyGate {
    private Path2D path; 
    private Point point = new Point(0, 0); // initial Point

    public OrGate() {
        // initialize the Path2D and give it a winding rule
        path = new Path2D.Double(Path2D.WIND_EVEN_ODD);

        // lots of "magic" numbers below, a code design "smell"
        // better to not do this. Perhaps have a data file to hold
        // this information, and have it read on program startup
        int preX = 15;
        int preY = 0;
        int x1Points[] = { preX, preX + 50, preX + 60, preX + 70, preX + 80, preX + 70, preX + 60,
                preX + 50, preX, preX + 10, preX + 20, preX + 30, preX + 20, preX + 10, preX };
        int y1Points[] = { preY, preY, preY + 5, preY + 15, preY + 30, preY + 45, preY + 55,
                preY + 60, preY + 60, preY + 55, preY + 45, preY + 30, preY + 15, preY + 5, preY };
        path.moveTo(preX - 15, preY + 15);
        path.lineTo(preX + 20, preY + 15);
        path.moveTo(preX - 15, preY + 45);
        path.lineTo(preX + 20, preY + 45);
        path.moveTo(preX, preY);

        for (int index = 1; index < x1Points.length; index++) {
            path.lineTo(x1Points[index], y1Points[index]);
        }
        path.closePath();
    }

    @Override
    public void draw(Graphics2D g2) {
        // simple method that leverages the Path2D path object
        g2.draw(path);
    }

    @Override
    public boolean contains(Point p) {
        // simple method that leverages the Path2D path object
        return path.contains(p);
    }

    @Override
    public Point getPoint() {
        return point;
    }

    @Override
    public void setPoint(Point p) {
        Point pOld = this.point;
        Point pNew = p;
        this.point = p;

        // create a transform that helps us move our Path2D
        int tx = pNew.x - pOld.x;
        int ty = pNew.y - pOld.y;       
        AffineTransform at = AffineTransform.getTranslateInstance(tx, ty);
        path.transform(at);  // and then move it
    }    

}

Main JPanel that shows use of the MouseAdapter and drawing/dragging of shapes:

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;

@SuppressWarnings("serial")
public class MainGates2 extends JPanel {
    private static final int PREF_W = 700;
    private static final int PREF_H = 600;
    private List<MyGate> gates = new ArrayList<>();

    public MainGates2() {
        // create a few Gates
        MyGate gate1 = new OrGate();
        gate1.setPoint(new Point(200, 300)); // move this guy
        MyGate gate2 = new OrGate();

        // add them to the gates ArrayList
        gates.add(gate1);
        gates.add(gate2);

        // create our mouse listener / adapter and add to JPanel
        MyMouse myMouse = new MyMouse();
        addMouseListener(myMouse);
        addMouseMotionListener(myMouse);        
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;

        // rendering hints to smooth graphics
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        // iterate through collection and draw
        for (MyGate myGate : gates) {
            myGate.draw(g2);
        }

    }

    @Override
    public Dimension getPreferredSize() {
        if (isPreferredSizeSet()) {
            return super.getPreferredSize();
        }
        // give our JPanel some size
        return new Dimension(PREF_W, PREF_H);
    }

    private class MyMouse extends MouseAdapter {
        private MyGate selectedGate = null;
        private Point p0; // initial Gate location
        private Point p1; // first mouse press location

        @Override
        public void mousePressed(MouseEvent e) {
            if (e.getButton() != MouseEvent.BUTTON1) {
                return;
            }
            p1 = e.getPoint();
            for (int i = gates.size() - 1; i >= 0; i--) {
                if (gates.get(i).contains(e.getPoint())) {
                    selectedGate = gates.get(i);
                    p0 = selectedGate.getPoint();
                    return;
                }
            }
            p1 = null;
        }

        @Override
        public void mouseReleased(MouseEvent e) {
            if (selectedGate != null) {
                dragShape(e);

                // de-select the gate
                selectedGate = null;
                p0 = null;
                p1 = null;
            }
        }

        public void mouseDragged(MouseEvent e) {
            if (selectedGate != null) {
                dragShape(e);
            }
        }

        private void dragShape(MouseEvent e) {
            Point p2 = e.getPoint(); // current mouse location
            int x = p0.x + p2.x - p1.x;
            int y = p0.y + p2.y - p1.y;
            Point p = new Point(x, y);
            selectedGate.setPoint(p);
            repaint();
        };
    }

    private static void createAndShowGui() {
        // main JPanel
        MainGates2 mainPanel = new MainGates2();

        JFrame frame = new JFrame("Main Gates2");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(mainPanel);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}