-2

As part of a larger project I'm trying to create a basic UI that will allow a user to click a JButton to select a desired color and then allow that user to click another JButton to designate a shape to be displayed, filled in according to the previously selected color. I understand that I must make use of action events.

Please note I am referencing this similar question:

GUI Application that allows the user to choose the shape and color of a drawing

My code thus far is:

import java.util.*;
import javax.swing.*;
import javax.swing.border.*;
import java.awt.*;
import java.awt.event.*;

public class GUI extends JFrame implements ActionListener, WindowListener
{
private final JButton circleButton, rectangleButton, redButton;
private final JButton greenButton, blueButton, exitButton;
private final JTextArea textArea;
private final JLabel label1;
private final JPanel colorPane;

private String shapeColor = "black";
private String actualShape = "rectangle";

private static final int ROWS = 2, COLS = 3;

public GUI (String title)
{
    super(title);
    //setResizable(false);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    colorPane = new JPanel();

    label1 = new JLabel("current date here");
    label1.setVerticalAlignment(SwingConstants.BOTTOM);
    label1.setHorizontalAlignment(SwingConstants.LEFT);
    label1.setPreferredSize(new Dimension(200,0));
    getContentPane().add(label1, BorderLayout.WEST);

    colorPane.setLayout(new GridLayout(ROWS,COLS));
    getContentPane().add(colorPane, BorderLayout.CENTER);

    redButton = makeButton("Red");
    colorPane.add(redButton);
    greenButton = makeButton("Green");
    colorPane.add(greenButton);
    blueButton = makeButton("Blue");
    colorPane.add(blueButton);
    rectangleButton = makeButton("Rectangle");
    colorPane.add(rectangleButton);
    circleButton = makeButton("Circle");
    colorPane.add(circleButton);
    exitButton = makeButton("Exit");
    colorPane.add(exitButton);

    textArea = new JTextArea(0,20);
    getContentPane().add(textArea, BorderLayout.EAST);

    pack();
}


public void paint(Graphics g, String color)
{
    if (shapeColor.equalsIgnoreCase("blue") && actualShape.equalsIgnoreCase("rectangle"))
    {   
        g.setColor(Color.BLUE);
        g.fillRect(50, 90, 100, 50);
    }
    else if (shapeColor.equalsIgnoreCase("green") && actualShape.equalsIgnoreCase("circle"))
    {   
        g.setColor(Color.GREEN);
        g.fillOval(50, 180, 55, 55);
    }
    else if (shapeColor.equalsIgnoreCase("red") && actualShape.equalsIgnoreCase("rectangle"))
    {
        g.setColor(Color.RED);
        g.fillRect(50, 90, 100, 50);
    }
    else if (shapeColor.equalsIgnoreCase("green") && actualShape.equalsIgnoreCase("rectangle"))
    {
        g.setColor(Color.GREEN);
        g.fillRect(50,90,100,50);
    }
    else if (shapeColor.equalsIgnoreCase("blue") && actualShape.equalsIgnoreCase("circle"))
    {
        g.setColor(Color.BLUE);
        g.fillOval(50, 180, 55, 55);
    }
    else if (shapeColor.equalsIgnoreCase("red") && actualShape.equalsIgnoreCase("circle"))
    {
        g.setColor(Color.RED);
        g.fillOval(50, 180, 55, 55);
    }
}




//method designed to create new JButtons while avoiding code duplication
private JButton makeButton(String text)
{
    JButton b = new JButton(text);
    b.setHorizontalAlignment(SwingConstants.LEFT);
    b.addActionListener(this);
    b.setPreferredSize(new Dimension(125,55));

    return b;
}

@Override
public void windowOpened(WindowEvent e) {
    // TODO Auto-generated method stub

}

@Override
public void windowClosing(WindowEvent e) 
{
    System.exit(0);

}

@Override
public void windowClosed(WindowEvent e) {
    // TODO Auto-generated method stub

}

@Override
public void windowIconified(WindowEvent e) {
    // TODO Auto-generated method stub

}

@Override
public void windowDeiconified(WindowEvent e) {
    // TODO Auto-generated method stub

}

@Override
public void windowActivated(WindowEvent e) {
    // TODO Auto-generated method stub

}

@Override
public void windowDeactivated(WindowEvent e) {
    // TODO Auto-generated method stub

}

@Override
public void actionPerformed(ActionEvent e) 
{
    System.out.println( ( (JButton)e.getSource() ).getText() + " button pressed ");

    if ( ( ((JButton) e.getSource()).getText().equalsIgnoreCase("Red")) )
    {   
        setShapeColor("Red");
        System.out.println("selected color is: " + shapeColor + " selected shape is: " + actualShape);
        //paint(this.getGraphics());
    }   
    else if ( ( ((JButton) e.getSource()).getText().equalsIgnoreCase("Blue")) )
    {   
        setShapeColor("Blue");
        System.out.println("selected color is: " + shapeColor + " selected shape is: " + actualShape);
        //paint(this.getGraphics());
    }

    else if ( ( ((JButton) e.getSource()).getText().equalsIgnoreCase("Green")) )
    {   
        setShapeColor("Green");
        System.out.println("selected color is: " + shapeColor + " selected shape is: " + actualShape);
        //paint(this.getGraphics());
    }   


    if ( ( ((JButton) e.getSource()).getText().equalsIgnoreCase("Rectangle")) )
    {
        setActualShape("rectangle");
        System.out.println("selected shape is: " + actualShape + " selected color is: " + shapeColor);
        paint(this.getGraphics(), shapeColor);


    }

    else if ( ( ((JButton) e.getSource()).getText().equalsIgnoreCase("Circle")) )
    {
        setActualShape("circle");
        System.out.println("selected shape is: " + actualShape + " selected color is: " + shapeColor);
        paint(this.getGraphics(), shapeColor);
    }

}

public String getShapeColor() {
    return shapeColor;
}

public void setShapeColor(String shapeColor) {
    this.shapeColor = shapeColor;
}

public String getActualShape() {
    return actualShape;
}

public void setActualShape(String actualShape) {
    this.actualShape = actualShape;
}

public static void main(String[] args)
{
    new GUI("My Gui").setVisible(true);
}

}

What I've thus far achieved is an output that shows both the selected color as well as the selected shape to be rendered in the selected color.

Furthermore, I've been successful in outputting a shape whose position is hard-coded but whose type (either circle or square) and whose color (red, blue or green), is correctly output as a result of the user clicks.

The final phase, upon which I am struggling, is the implementation of the shape's output so that the user's sequence of clicks determines the location and dimensions of the shape output to the screen.

The goal is to achieve the functionality demonstrated here:

https://metrostate.learn.minnstate.edu/content/2020/4560296-20201000539/ICS372%20-%20Assignment%202%20Video.mp4?d2lSessionVal=ARifwbCHriCBrkgxBWpL9g8fL&ou=4560296

I'm relatively certain that the correct solution is similar to the following code:

import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

/**
 *  Note: Normally the ButtonPanel and DrawingArea would not be static 
classes.
 *  This was done for the convenience of posting the code in one class and 
to
 *  highlight the differences between the two approaches. All the 
differences
 *  are found in the DrawingArea class.
 */
public class DrawOnComponent
{
public static void main(String[] args)
{
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            createAndShowGUI();
        }
    });
}

private static void createAndShowGUI()
{
    DrawingArea drawingArea = new DrawingArea();
    ButtonPanel buttonPanel = new ButtonPanel( drawingArea );

    JFrame.setDefaultLookAndFeelDecorated(true);
    JFrame frame = new JFrame("Draw On Component");
    frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
    frame.getContentPane().add(drawingArea);
    frame.getContentPane().add(buttonPanel, BorderLayout.SOUTH);
    frame.setSize(400, 400);
    frame.setLocationRelativeTo( null );
    frame.setVisible(true);
}

static class ButtonPanel extends JPanel implements ActionListener
{
    private DrawingArea drawingArea;

    public ButtonPanel(DrawingArea drawingArea)
    {
        this.drawingArea = drawingArea;

        add( createButton(" ", Color.BLACK) );
        add( createButton(" ", Color.RED) );
        add( createButton(" ", Color.GREEN) );
        add( createButton(" ", Color.BLUE) );
        add( createButton(" ", Color.ORANGE) );
        add( createButton(" ", Color.YELLOW) );
        add( createButton("Clear Drawing", null) );
    }

    private JButton createButton(String text, Color background)
    {
        JButton button = new JButton( text );
        button.setBackground( background );
        button.addActionListener( this );

        return button;
    }

    public void actionPerformed(ActionEvent e)
    {
        JButton button = (JButton)e.getSource();

        if ("Clear Drawing".equals(e.getActionCommand()))
            drawingArea.clear();
        else
            drawingArea.setForeground( button.getBackground() );
    }
}

static class DrawingArea extends JPanel
{
    private final static int AREA_SIZE = 400;
    private ArrayList<ColoredRectangle> coloredRectangles = new ArrayList<ColoredRectangle>();
    private Rectangle shape;

    public DrawingArea()
    {
        setBackground(Color.WHITE);

        MyMouseListener ml = new MyMouseListener();
        addMouseListener(ml);
        addMouseMotionListener(ml);
    }

    @Override
    public Dimension getPreferredSize()
    {
        return isPreferredSizeSet() ?
            super.getPreferredSize() : new Dimension(AREA_SIZE, AREA_SIZE);
    }

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

        //  Custom code to paint all the Rectangles from the List

        Color foreground = g.getColor();

        g.setColor( Color.BLACK );
        g.drawString("Add a rectangle by doing mouse press, drag and release!", 40, 15);

        for (DrawingArea.ColoredRectangle cr : coloredRectangles)
        {
            g.setColor( cr.getForeground() );
            Rectangle r = cr.getRectangle();
            g.drawRect(r.x, r.y, r.width, r.height);
        }

        //  Paint the Rectangle as the mouse is being dragged

        if (shape != null)
        {
            Graphics2D g2d = (Graphics2D)g;
            g2d.setColor( foreground );
            g2d.draw( shape );
        }
    }

    public void addRectangle(Rectangle rectangle, Color color)
    {
        //  Add the Rectangle to the List so it can be repainted

        ColoredRectangle cr = new ColoredRectangle(color, rectangle);
        coloredRectangles.add( cr );
        repaint();
    }

    public void clear()
    {
        coloredRectangles.clear();
        repaint();
    }

    class MyMouseListener extends MouseInputAdapter
    {
        private Point startPoint;

        public void mousePressed(MouseEvent e)
        {
            startPoint = e.getPoint();
            shape = new Rectangle();
        }

        public void mouseDragged(MouseEvent e)
        {
            int x = Math.min(startPoint.x, e.getX());
            int y = Math.min(startPoint.y, e.getY());
            int width = Math.abs(startPoint.x - e.getX());
            int height = Math.abs(startPoint.y - e.getY());

            shape.setBounds(x, y, width, height);
            repaint();
        }

        public void mouseReleased(MouseEvent e)
        {
            if (shape.width != 0 || shape.height != 0)
            {
                addRectangle(shape, e.getComponent().getForeground());
            }

            shape = null;
        }
    }

    class ColoredRectangle
    {
        private Color foreground;
        private Rectangle rectangle;

        public ColoredRectangle(Color foreground, Rectangle rectangle)
        {
            this.foreground = foreground;
            this.rectangle = rectangle;
        }

        public Color getForeground()
        {
            return foreground;
        }

        public void setForeground(Color foreground)
        {
            this.foreground = foreground;
        }

        public Rectangle getRectangle()
        {
            return rectangle;
        }
    }
}
}

I know that I must override the 'paint' method and as a hard-coded exercise I have included the following in my code:

public void paint(Graphics g, String color)
{
    if (shapeColor.equalsIgnoreCase("blue") && actualShape.equalsIgnoreCase("rectangle"))
    {   
        g.setColor(Color.BLUE);
        g.fillRect(50, 90, 100, 50);
    }
    else if (shapeColor.equalsIgnoreCase("green") && actualShape.equalsIgnoreCase("circle"))
    {   
        g.setColor(Color.GREEN);
        g.fillOval(50, 180, 55, 55);
    }
    else if (shapeColor.equalsIgnoreCase("red") && actualShape.equalsIgnoreCase("rectangle"))
    {
        g.setColor(Color.RED);
        g.fillRect(50, 90, 100, 50);
    }
    else if (shapeColor.equalsIgnoreCase("green") && actualShape.equalsIgnoreCase("rectangle"))
    {
        g.setColor(Color.GREEN);
        g.fillRect(50,90,100,50);
    }
    else if (shapeColor.equalsIgnoreCase("blue") && actualShape.equalsIgnoreCase("circle"))
    {
        g.setColor(Color.BLUE);
        g.fillOval(50, 180, 55, 55);
    }
    else if (shapeColor.equalsIgnoreCase("red") && actualShape.equalsIgnoreCase("circle"))
    {
        g.setColor(Color.RED);
        g.fillOval(50, 180, 55, 55);
    }
}
}

I am unsure how to record coordinates of a user's button click and to then pass those coordinates into the constructor of the desired shape.

Trixie the Cat
  • 317
  • 3
  • 18
  • `I know that I must override the 'paint' method ` - no you should not! Did you even execute the code you posted in your question? That code does NOT override paint(). Learn from the example. `I am unsure how to record coordinates of a user's button click and to then pass those coordinates into the constructor of the desired shape` - again did you not try the example. The starting point is handled in the mousePressed event and the ending point is handled in the mouseReleased event. The point is for you to modify the code to do what you need to be done. – camickr May 31 '19 at 00:42
  • (1-) Why do you have all those if/else statements? Again, did you not see how the working example sets the Color? The main difference is the handling of the shapes. The example only draws Rectangle and you need to draw different Shapes. So first make modifications to the working code to handle your specific requirements by only drawing the Rectangle. Once you get that working you can make further modifications I do not see how your posted code attempts to follow any of the suggestion in the working code. – camickr May 31 '19 at 00:47
  • @camickr thank you for your suggestions, I am trying to utilize the working example, I realize that the example had functionality for rectangles and I have attempted to add circle functionality as well. I'm sorry about all the 'if/else' statements, I am simply trying to learn how to do event handling and am trying out different things as I learn how to implement this project. Can you suggest an alternative method with fewer if else statements? Perhaps I need to handle multiple events instead of placing all the code within a single action event? – Trixie the Cat May 31 '19 at 15:05
  • 1
    @TrixietheCat I actually showed you a better alternative using an `enum` for the shapes and how to set the `Color` directly using a ternary operator and thus reduced the code significantly. Btw you can only tag one person at a time, I didn't get notified of your reply on c0der's answer, he gets notified as it's on his answer anyway, so you should have tagged me first without a space between `@` and my name. For the Serialization include it in your model. I didn't do the same GUI as you here because your question history shows that you've been asking how to do every single step of the program – Frakcool May 31 '19 at 18:11
  • 1
    ... Try to do the serialization part first then come and ask what's not working – Frakcool May 31 '19 at 18:11
  • @Frakcool I'm sorry to annoy you. I know my history shows a lot of requests for help, I'm just not quite at the skill level to do nice clean GUI and I am trying to learn as much as I can. Thank you for your help, my previous posts were downvoted aggressively so I deleted them and subsequently lost track of some of the earlier suggestions that you and others gave me, I apologize for this disorganization. I will attempt to complete the rest of the program on my own and will only as for help in the event of a failure of something that I tried. Thank you for your time and patience – Trixie the Cat May 31 '19 at 18:46
  • 1
    I'm not saying you're annoying me or others, the reason those post might have been downvoted are possibly: 1) A lack of a proper [mcve] (Like the one I posted, it's short, it's complete, you can verify the same result on your computer) that's what we're asking you. Even if the UI is different from the one in your "big" program, you can take the knowledge from each different little questions / answers, isolate them, that's how you learn. Divide and conquer. My first questions were horrible as well and were dowvoted, I learnt a lot while writing answers to these kind of questions. – Frakcool May 31 '19 at 18:54
  • 1
    If you get stuck, you still can come back and post questions, but be sure to explain what you want to achieve and show at least some effort into trying to do it yourself first. For example, write a simple program with Serialization (console application), read & write to / from it... – Frakcool May 31 '19 at 18:57
  • 1
    ... Then write a simple program that has a single `JTextArea` and a `JButton` that writes to the file whatever you've wrote there and reads it and displays it on the `JTextArea` then you can take that knowledge and apply it on this program that is the one that you want to complete, yes it takes more time, but in the long run, you understand everything better. – Frakcool May 31 '19 at 18:57

2 Answers2

3

There are a couple of errors to change in your code:

  1. Don't extend JFrame, see Extends JFrame vs. creating it inside the program, instead create an instance of it inside your class. If you need to extend from a JComponent let it be a more flexible one such as JPanel.

  2. Don't override paint(...) method, you need to override JPanel's paintComponent(...) method and don't forget to call super.paintComponent(g) as the first line in it, otherwise you might break the paint chain and have funny / weird behaviors. Neither pass the getGraphics() object, see the Tutorial on Custom Painting

  3. Use the Shape API rather than drawing directly on the JPanel as it provides more functionality. See this post: Create the square, rectangle, triangle of java in jframe

  4. Don't call setPreferredSize, override the getPreferredSize, see: Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?

  5. Place your program on the EDT, see point #7 in the linked post in point #3 in this same answer.

So, here's an example that follows the above recommendations:

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

public class PaintExample {
    private JFrame frame;
    private JPanel pane;
    private JPanel buttonsPane;
    private CustomShape customShape;
    private JButton squareButton;
    private JButton circleButton;
    private JButton purpleButton;
    private JButton blueButton;

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

    private void createAndShowGUI() {
        frame = new JFrame(getClass().getSimpleName()); //Create a new JFrame with a title = this class name
        pane = new JPanel();
        buttonsPane = new JPanel();

        buttonsPane.setLayout(new GridLayout(2, 2, 5, 5)); // We generate a grid layout of 2 x 2 for our JButtons

        squareButton = new JButton("Square");
        circleButton = new JButton("Circle");
        purpleButton = new JButton("Purple");
        blueButton = new JButton("Blue");

        squareButton.addActionListener(listener);
        circleButton.addActionListener(listener);
        purpleButton.addActionListener(listener);
        blueButton.addActionListener(listener);

        buttonsPane.add(squareButton);
        buttonsPane.add(circleButton);
        buttonsPane.add(purpleButton);
        buttonsPane.add(blueButton);

        customShape = new CustomShape(); // We create an instance of our custom JPanel class

        pane.setLayout(new BorderLayout());
        pane.add(customShape);
        pane.add(buttonsPane, BorderLayout.SOUTH);

        frame.add(pane);
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }

    ActionListener listener = e -> { //Java 8+, for Java 7- add the actionPerformed method instead of the lambda expression
        // We check which button was clicked and set the shape / color for our custom class
        if (e.getSource().equals(squareButton)) {
            customShape.setShape(ShapeToDraw.SQUARE);
        } else if (e.getSource().equals(circleButton)) {
            customShape.setShape(ShapeToDraw.CIRCLE);
        } else if (e.getSource().equals(purpleButton)) {
            customShape.setColor(Color.MAGENTA);
        } else if (e.getSource().equals(blueButton)) {
            customShape.setColor(Color.BLUE);
        } 
    };

    enum ShapeToDraw {
        SQUARE, CIRCLE // You can define here other properties for each enum option
    }

    class CustomShape extends JPanel {
        private Color color;
        private ShapeToDraw shape;

        public CustomShape() {

        }

        public Color getColor() {
            return color;
        }

        public void setColor(Color color) {
            this.color = color;
            this.repaint(); // Everytime we set the color we ask the component to repaint itself
        }

        public ShapeToDraw getShape() {
            return shape;
        }

        public void setShape(ShapeToDraw shape) {
            this.shape = shape;
            this.repaint(); // Everytime we set the shape we ask the component to repaint itself
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200); // We define the panel's size
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;
            g2d.setColor(color != null ? color : Color.BLACK); //If we haven't set the Color yet, we default it to black, otherwise we set the color to the one chosen by the user.
            if (shape == ShapeToDraw.SQUARE) { //If the shape is a square, we draw a square
                g2d.draw(new Rectangle2D.Double(50, 50, 100, 100)); // Change the coordinates that you get by user click using the MouseListener
            } else if (shape == ShapeToDraw.CIRCLE) { // Or we draw a circle
                g2d.draw(new Ellipse2D.Double(50, 50, 100, 100));
            } 
        }
    }
}

This is how the program looks like:

enter image description here enter image description here

I am unsure how to record coordinates of a user's button click and to then pass those coordinates into the constructor of the desired shape.

To get the coordinates relative to your window see: How to get location of a mouse click relative to a swing window

Frakcool
  • 10,915
  • 9
  • 50
  • 89
2

The following is a suggested implementation, also incorporating the good guidance you got from Frakcool:

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.util.HashMap;
import java.util.Map;
import javax.swing.ButtonGroup;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JToggleButton;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;

public class GUI
{
    private final ButtonGroup colorGroup; //group  buttons
    private final ButtonGroup shapeGroup; //so only one can be selected at any given time
    private final Map<String, Color> colors; //map colors to it names.
    private Color color; // color for painting
    private Shape shape; //shape to paint
    private JFrame frame;
    private JPanel buttonsPane;
    private JTextArea textArea;

    private static final int ROWS = 2, COLS = 3;
    private static final String[] BUTTONS_LABELS = {"Rectangle", "Circle", "Exit"};

    public GUI()
    {
        shapeGroup = new ButtonGroup(); colorGroup = new ButtonGroup();
        colors = new HashMap<>();
        colors.put("Red", Color.RED); colors.put("Green", Color.GREEN); colors.put("Blue", Color.BLUE);
    }

    private void createAndShowGUI(String title) {
        frame = new JFrame(title);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //use a GridLayout for the buttons pane
        buttonsPane = new JPanel();
        buttonsPane.setLayout(new GridLayout(ROWS, COLS));
        frame.getContentPane().add(buttonsPane, BorderLayout.CENTER);//each BorderLayout position can hold ONE component

        for(String colorName : colors.keySet()){
            JToggleButton button = makeButton(colorName);
            buttonsPane.add(button);
            colorGroup.add(button);
            button.addActionListener(changeColorAction());
        }

        for(String text : BUTTONS_LABELS){
            JToggleButton button = makeButton(text);
            buttonsPane.add(button);
            shapeGroup.add(button);
            button.addActionListener(changeShapeAction());
        }

        setDefaults();

        frame.getContentPane().add(new Draw(), BorderLayout.WEST);
        textArea = new JTextArea(0,20);
        frame.getContentPane().add(textArea, BorderLayout.EAST);

        frame.pack();
        frame.setVisible(true);
    }

    private JToggleButton makeButton(String text) {
        JToggleButton b = new JToggleButton(text); //use toggle buttons
        b.setHorizontalAlignment(SwingConstants.LEFT);
        b.setPreferredSize(new Dimension(100, 80)); //set preferred and let Layout manager do its work
        return b;
    }

    private ActionListener changeColorAction() {
        return e->{
            color = colors.get(((JToggleButton)e.getSource()).getText());
            frame.repaint();
        };
    }

    private ActionListener changeShapeAction() {
        return e->{
            switch (((JToggleButton)e.getSource()).getText()){

                case "Circle":
                    shape = Shape.CIRCLE;
                    break;
                case "Rectangle":
                    shape = Shape.RECTANGLE;
                    break;
                default: System.exit(0);
            }

            frame.repaint();
        };
    }

    private void setDefaults() {
        colorGroup.getElements().nextElement().setSelected(true);
        color = colors.get(colorGroup.getElements().nextElement().getText());
        shapeGroup.getElements().nextElement().setSelected(true);
        shape = Shape.RECTANGLE;
    }

      public static void main(String[] args) {
            SwingUtilities.invokeLater(() -> new GUI().createAndShowGUI("My Gui"));
        }

    class Draw extends JPanel{

        private final Point[] points; // an array to hold clicked points
        private int mouseClicks = 0;
        private static final int POINT_SIZE = 2;

        Draw(){
            setLayout(new BorderLayout());
            setBackground(Color.WHITE);
            setPreferredSize(new Dimension(200, 200));

            JLabel help = new JLabel("Click 2 points to draw");
            help.setHorizontalAlignment(SwingConstants.CENTER);
            add(help, BorderLayout.PAGE_START);

            JLabel timeLabel = new JLabel("current time here");
            timeLabel.setHorizontalAlignment(SwingConstants.LEFT);
            add(timeLabel, BorderLayout.PAGE_END);

            points = new Point[2];
            addMouseListener(new MouseAdapter(){
                @Override
                public void mouseClicked(MouseEvent e) {
                    addPoint(e.getX(), +e.getY() );
                }
            });
        }

        @Override
        public void paintComponent(Graphics g){
            super.paintComponent(g);
            g.setColor(color);
            for(Point point : points)
                if(point != null){
                    g.drawOval(point.x, point.y, POINT_SIZE, POINT_SIZE);
                }

            drawShape((Graphics2D)g);
        }

        private void addPoint(int x, int y) {
            if(mouseClicks ==2){
                mouseClicks = 0;
                points[1] = null;
            }

            points[mouseClicks++] = new Point(x, y);
            repaint();
        }

        private void drawShape(Graphics2D g2d) {

            if(points[0] == null ||  points[1] == null) return;
            if(shape == Shape.RECTANGLE) {
                drawRectangle(g2d);
            }else{
                drawCircle(g2d);
            }
        }

        private void drawRectangle(Graphics2D g2D) {

            int minX = Math.min(points[0].x, points[1].x);
            int minY = Math.min(points[0].y, points[1].y);
            int maxX = Math.max(points[0].x, points[1].x);
            int maxY = Math.max(points[0].y, points[1].y);
            int width  = maxX - minX;
            int height = maxY - minY;
            Rectangle2D.Double rectangle = new Rectangle2D.Double(minX, minY, width, height);
            g2D.draw(rectangle);
        }

        private void drawCircle(Graphics2D g2D) {

            int minX = Math.min(points[0].x, points[1].x);
            int minY = Math.min(points[0].y, points[1].y);
            int maxX = Math.max(points[0].x, points[1].x);
            int maxY = Math.max(points[0].y, points[1].y);
            double dx  = maxX - minX;
            double dy = maxY - minY;
            double radius =  Math.sqrt(dx*dx + dy*dy)/2;
            double centerX = minX + dx/2;
            double centerY = minY + dy/2;

            g2D.draw(new Ellipse2D.Double(centerX - radius , centerY - radius, 2* radius, 2* radius));
        }
    }

    enum Shape {
        RECTANGLE, CIRCLE
    }
}

enter image description here

c0der
  • 18,467
  • 6
  • 33
  • 65
  • 1
    I would only argue with the `System.exit(0)` call, I would use `frame.dispose()` instead. – Frakcool May 31 '19 at 14:30
  • 1
    @Frakcool thanks for your comment. Why would you use `frame.dispose()` ? I guess it depends in what you want to achieve. – c0der May 31 '19 at 14:33
  • 1
    It's safer overall, `System.exit(0)` forces the app to quit, `frame.dispose()` exits the application in a safer way. As the `JFrame` has being told to `EXIT_ON_CLOSE`, once you dispose it it will terminate itself. Here is some more information on the matter: https://stackoverflow.com/questions/13360430/jframe-dispose-vs-system-exit Specially the answer from Sri Harsha is the one I like the most there and the comment from Tomas Zato in the accepted answer. – Frakcool May 31 '19 at 14:36
  • 1
    I actually meant "close all windows and terminate the application" including threads. For closing all windows, I agree that `JFrame.dispose()` is more appropriate. – c0der May 31 '19 at 14:40
  • @c0der, and @ Frakcool, thank you both. One additional question: how would serialization fit into this? Part of the specification for this project is: "When the exit button is clicked, serialize the figures and the content of the text area (haven't implemented yet but should have coordinates of chosen shapes) to disk in a file named “figures”. When the GUI is started, check for the existence of a file named “figures” and if it exists, open it to populate the figures and the corresponding list in the text area. You must not assume any specific directory structure – Trixie the Cat May 31 '19 at 15:31
  • 1
    The policy in SO is one question in a post. For more questions please have a new post. – c0der May 31 '19 at 17:22
  • @c0der okay, sorry, thanks, I will attempt to complete the project on my own at this point as I feel that I'm asking too many questions – Trixie the Cat May 31 '19 at 20:32