Morhaf Lababidi
Morhaf Lababidi

Reputation: 1

Clearing the screen for new graphics in Java (awt)

I have this code which is basically a home menu with two clickable rectangles.

Start Game works fine. Info is what is not really working. When pressed, the info screen will appear, but the home menu buttons will still be there though not visible (can be clicked).. it seems that when the info menu is appearing, the home menu buttons are not getting cleared. Also, any point on the info menu is clickable and will show the home menu again. (not what intended, only the back buttons should do that).

How can I fix those problems ?

package test;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.geom.Rectangle2D;
import java.awt.image.ImageObserver;
import java.awt.image.ImageProducer;



public class HomeMenu extends JComponent implements MouseListener, MouseMotionListener {

    private static final String GAME_TITLE = "BRICK DESTROY";
    private static final String START_TEXT = "START";
    private static final String INFO_TEXT = "INFO";
    private static final String howtoPlay = """
            1- Click Start\n
            2- Choose the mode\n
            3- Each mode has 3 levels\n
            4- To play/pause press space, use 'A' and 'D' to move\n
            5- To open pause menu press 'ESC'\n
            6- To open DebugPanel press 'ALT-SHIFT-F1'""";
    private static final String backText = "BACK";

    private static final Color BORDER_COLOR = new Color(200,8,21); //Venetian Red
    private static final Color DASH_BORDER_COLOR = new  Color(255, 216, 0);//school bus yellow
    private static final Color TEXT_COLOR = new Color(255, 255, 255);//white
    private static final Color CLICKED_BUTTON_COLOR = Color.ORANGE.darker();;
    private static final Color CLICKED_TEXT = Color.ORANGE.darker();
    private static final int BORDER_SIZE = 5;
    private static final float[] DASHES = {12,6};


    private Rectangle menuFace;
    private Rectangle infoFace;
    private Rectangle startButton;
    private Rectangle infoButton;
    private Rectangle backButton;


    private BasicStroke borderStoke;
    private BasicStroke borderStoke_noDashes;
    private Image img =  Toolkit.getDefaultToolkit().createImage("1.jpeg");
    private Font gameTitleFont;
    private Font infoFont;
    private Font buttonFont;
    private Font howtoPlayFont;

    private GameFrame owner;

    private boolean startClicked;
    private boolean infoClicked = false;
    private boolean backClicked = false;


    public HomeMenu(GameFrame owner,Dimension area){

        this.setFocusable(true);
        this.requestFocusInWindow();

        this.addMouseListener(this);
        this.addMouseMotionListener(this);

        this.owner = owner;


        menuFace = new Rectangle(new Point(0,0),area);
        infoFace = new Rectangle(new Point(0,0),area);
        this.setPreferredSize(area);

        Dimension btnDim = new Dimension(area.width / 3, area.height / 12);
        startButton = new Rectangle(btnDim);
        infoButton = new Rectangle(btnDim);
        backButton = new Rectangle(btnDim);

        borderStoke = new BasicStroke(BORDER_SIZE,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND,0,DASHES,0);
        borderStoke_noDashes = new BasicStroke(BORDER_SIZE,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND);


        gameTitleFont = new Font("Calibri",Font.BOLD,28);
        infoFont = new Font("Calibri",Font.BOLD,24);
        buttonFont = new Font("Calibri",Font.BOLD,startButton.height-2);
        howtoPlayFont = new Font("Calibri",Font.PLAIN,14);




    }


    public void paint(Graphics g){
        drawMenu((Graphics2D)g);

    }



    public void drawMenu(Graphics2D g2d){

        if(infoClicked) {
            drawInfoMenu(g2d);
            return;
        }else{
            drawContainer(g2d);

            Color prevColor = g2d.getColor();
            Font prevFont = g2d.getFont();

            double x = menuFace.getX();
            double y = menuFace.getY();


            g2d.translate(x,y);

            //methods calls
            drawText(g2d);
            drawButton(g2d);

            //end of methods calls

            g2d.translate(-x,-y);
            g2d.setFont(prevFont);
            g2d.setColor(prevColor);
        }


        Toolkit.getDefaultToolkit().sync();

    }



    private void drawContainer(Graphics2D g2d){
        Color prev = g2d.getColor();

        //g2d.setColor(BG_COLOR);
        g2d.drawImage(img,0,0,menuFace.width,menuFace.height,this);
        //g2d.fill(menuFace);

        Stroke tmp = g2d.getStroke();

        g2d.setStroke(borderStoke_noDashes);
        g2d.setColor(DASH_BORDER_COLOR);
        g2d.draw(menuFace);

        g2d.setStroke(borderStoke);
        g2d.setColor(BORDER_COLOR);
        g2d.draw(menuFace);

        g2d.setStroke(tmp);

        g2d.setColor(prev);
    }

    private void drawText(Graphics2D g2d){

        g2d.setColor(TEXT_COLOR);

        FontRenderContext frc = g2d.getFontRenderContext();

        Rectangle2D gameTitleRect = gameTitleFont.getStringBounds(GAME_TITLE,frc);

        int sX,sY;

        sY = (int)(menuFace.getHeight() / 4);

        sX = (int)(menuFace.getWidth() - gameTitleRect.getWidth()) / 2;
        sY += (int) gameTitleRect.getHeight() * 1.1;//add 10% of String height between the two strings

        g2d.setFont(gameTitleFont);
        g2d.drawString(GAME_TITLE,sX,sY);


    }

    private void drawButton(Graphics2D g2d){

        FontRenderContext frc = g2d.getFontRenderContext();

        Rectangle2D txtRect = buttonFont.getStringBounds(START_TEXT,frc);
        Rectangle2D mTxtRect = buttonFont.getStringBounds(INFO_TEXT,frc);

        g2d.setFont(buttonFont);

        int x = (menuFace.width - startButton.width) / 2;
        int y =(int) ((menuFace.height - startButton.height) * 0.5);

        startButton.setLocation(x,y);

        x = (int)(startButton.getWidth() - txtRect.getWidth()) / 2;
        y = (int)(startButton.getHeight() - txtRect.getHeight()) / 2;

        x += startButton.x;
        y += startButton.y + (startButton.height * 0.9);


        if(startClicked){
            Color tmp = g2d.getColor();
            g2d.setColor(CLICKED_BUTTON_COLOR);
            g2d.draw(startButton);
            g2d.setColor(CLICKED_TEXT);
            g2d.drawString(START_TEXT,x,y);
            g2d.setColor(tmp);
        }
        else{
            g2d.draw(startButton);
            g2d.drawString(START_TEXT,x,y);
        }

        x = startButton.x;
        y = startButton.y;

        y *= 1.3;

        infoButton.setLocation(x,y);


        x = (int)(infoButton.getWidth() - mTxtRect.getWidth()) / 2;
        y = (int)(infoButton.getHeight() - mTxtRect.getHeight()) / 2;

        x += infoButton.getX();
        y += infoButton.getY() + (startButton.height * 0.9);

        if(infoClicked){
            Color tmp = g2d.getColor();
            g2d.setColor(CLICKED_BUTTON_COLOR);
            g2d.draw(infoButton);
            g2d.setColor(CLICKED_TEXT);
            g2d.drawString(INFO_TEXT,x,y);
            g2d.setColor(tmp);
        }
        else{
            g2d.draw(infoButton);
            g2d.drawString(INFO_TEXT,x,y);
        }

    }


    private void drawInfoMenu(Graphics2D g2d){

        FontRenderContext frc = g2d.getFontRenderContext();

        Rectangle2D infoRec = infoFont.getStringBounds(INFO_TEXT,frc);

        Color prev = g2d.getColor();


        Stroke tmp = g2d.getStroke();


        g2d.setStroke(borderStoke_noDashes);
        g2d.setColor(DASH_BORDER_COLOR);
        g2d.draw(infoFace);

        g2d.setStroke(borderStoke);
        g2d.setColor(BORDER_COLOR);
        g2d.draw(infoFace);

        g2d.fillRect(0,0,infoFace.width,infoFace.height);

        g2d.setStroke(tmp);

        g2d.setColor(prev);

        g2d.setColor(TEXT_COLOR);


        int sX,sY;

        sY = (int)(infoFace.getHeight() / 15);

        sX = (int)(infoFace.getWidth() - infoRec.getWidth()) / 2;
        sY += (int) infoRec.getHeight() * 1.1;//add 10% of String height between the two strings

        g2d.setFont(infoFont);
        g2d.drawString(INFO_TEXT,sX,sY);

        TextLayout layout = new TextLayout(howtoPlay, howtoPlayFont, frc);
        String[] outputs = howtoPlay.split("\n");
        for(int i=0; i<outputs.length; i++) {
            g2d.setFont(howtoPlayFont);
            g2d.drawString(outputs[i], 40, (int) (80 + i * layout.getBounds().getHeight() + 0.5));
        }

        backButton.setLocation(getWidth()/3,getHeight()-50);


        int x = (int)(backButton.getWidth() - infoRec.getWidth()) / 2;
        int y = (int)(backButton.getHeight() - infoRec.getHeight()) / 2;

        x += backButton.x+11;
        y += backButton.y + (layout.getBounds().getHeight() * 1.35);

        backButton.setLocation(getWidth()/3,getHeight()-50);

        if(backClicked){
            Color tmp1 = g2d.getColor();
            g2d.setColor(CLICKED_BUTTON_COLOR);
            g2d.draw(backButton);
            g2d.setColor(CLICKED_TEXT);
            g2d.drawString(backText,x,y);
            g2d.setColor(tmp1);
            infoClicked = false;
            repaint();
        }
        else{
            g2d.draw(backButton);
            g2d.drawString(backText,x,y);
        }


    }

    @Override
    public void mouseClicked(MouseEvent mouseEvent) {
        Point p = mouseEvent.getPoint();
        if(startButton.contains(p)){
           owner.enableGameBoard();

        }
        else if(infoButton.contains(p)){
            infoClicked = true;
        }
        else if(backButton.contains(p)){
            infoClicked = false;

        }
        repaint();

    }

    @Override
    public void mousePressed(MouseEvent mouseEvent) {
        Point p = mouseEvent.getPoint();
        if(startButton.contains(p)){
            startClicked = true;
            repaint(startButton.x,startButton.y,startButton.width+1,startButton.height+1);

        }
        else if(infoButton.contains(p)){
            infoClicked = true;

        }
        else if(backButton.contains(p)){
            infoClicked = false;
        }
        repaint();

    }

    @Override
    public void mouseReleased(MouseEvent mouseEvent) {
        if(startClicked){
            startClicked = false;
            repaint(startButton.x,startButton.y,startButton.width+1,startButton.height+1);
        }
        else if(infoClicked){
            infoClicked = false;
        }
        else if(backClicked){
            infoClicked = true;
        }
        repaint();
    }

    @Override
    public void mouseEntered(MouseEvent mouseEvent) {

    }

    @Override
    public void mouseExited(MouseEvent mouseEvent) {

    }


    @Override
    public void mouseDragged(MouseEvent mouseEvent) {

    }

    @Override
    public void mouseMoved(MouseEvent mouseEvent) {
        Point p = mouseEvent.getPoint();
        if(startButton.contains(p) || infoButton.contains(p) || backButton.contains(p)) {
            this.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
        }
        else {
            this.setCursor(Cursor.getDefaultCursor());
        }
    }

}

Here are the images of both windows main menu

info menu, pressing anywhere = back to home menu, pressing roughly in the middle = start game or back to main menu too

Upvotes: 0

Views: 98

Answers (1)

MadProgrammer
MadProgrammer

Reputation: 347194

First read, Performing Custom Painting and Painting in AWT and Swing to get a better understanding how painting in Swing works and how you're suppose to work with it.

But I already have ...

public void paint(Graphics g){
    drawMenu((Graphics2D)g);

}

would suggest otherwise. Seriously, go read those links so you understand all the issues that the above decision is going to create for you.

You're operating in a OO language, you need to take advantage of that and decouple your code and focus on the "single responsibility" principle.

I'm kind of tired of talking about it, so you can do some reading:

These are basic concepts you really need to understand as they will make your live SOOO much easier and can be applied to just about any language.

As an example, from your code...

public HomeMenu(GameFrame owner,Dimension area){
    //...
    this.setPreferredSize(area);

There is no good reason (other than laziness (IMHO)) that any caller should be telling a component what size it should be, that's not their responsibility. It's the responsibility of the component to tell the parent container how big it would like to be and for the parent component to figure out how it's going to achieve that (or ignore it as the case may be).

The "basic" problem you're having is a simple one. Your "God" class is simply trying to do too much (ie it's taken on too much responsibility). Now we "could" add a dozen or more flags into the code to compensate for this, which is just going to increase the coupling and complexity, making it harder to understand and maintain, or we can take a step back, break it down into individual areas of responsibility and build the solution around those, for example...

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Stroke;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new HomePane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class HomePane extends JPanel {

        public HomePane() {
            setLayout(new BorderLayout());
            navigateToMenu();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 400);
        }

        protected void navigateToMenu() {
            removeAll();
            HomeMenuPane pane = new HomeMenuPane(new HomeMenuPane.NavigationListener() {
                @Override
                public void navigateToInfo(HomeMenuPane source) {
                    HomePane.this.navigateToInfo();
                }

                @Override
                public void navigateToStartGame(HomeMenuPane source) {
                    startGame();
                }
            });
            add(pane);
            revalidate();
            repaint();
        }

        protected void navigateToInfo() {
            removeAll();
            HowToPlayPane pane = new HowToPlayPane(new HowToPlayPane.NavigationListener() {
                @Override
                public void navigateBack(HowToPlayPane source) {
                    navigateToMenu();
                }
            });
            add(pane);
            revalidate();
            repaint();
        }

        protected void startGame() {
            removeAll();
            add(new JLabel("This is pretty awesome, isn't it!", JLabel.CENTER));
            revalidate();
            repaint();
        }

    }

    public abstract class AbstractBaseMenuPane extends JPanel {

        protected static final Color BORDER_COLOR = new Color(200, 8, 21); //Venetian Red
        protected static final Color DASH_BORDER_COLOR = new Color(255, 216, 0);//school bus yellow
        protected static final Color TEXT_COLOR = new Color(255, 255, 255);//white
        protected static final Color CLICKED_BUTTON_COLOR = Color.ORANGE.darker();
        protected static final Color CLICKED_TEXT = Color.ORANGE.darker();
        protected static final int BORDER_SIZE = 5;
        protected static final float[] DASHES = {12, 6};

        private Rectangle border;

        private BasicStroke borderStoke;
        private BasicStroke borderStoke_noDashes;

        private BufferedImage backgroundImage;

        public AbstractBaseMenuPane() {
            borderStoke = new BasicStroke(BORDER_SIZE, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 0, DASHES, 0);
            borderStoke_noDashes = new BasicStroke(BORDER_SIZE, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
            border = new Rectangle(new Point(0, 0), getPreferredSize());
            // You are now responsible for filling the background
            setOpaque(false);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 400);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            BufferedImage backgroundImage = getBackgroundImage();
            if (backgroundImage != null) {
                g2d.drawImage(backgroundImage, 0, 0, getWidth(), getHeight(), this);
            }

            Color prev = g2d.getColor();
            Stroke tmp = g2d.getStroke();

            g2d.setStroke(borderStoke_noDashes);
            g2d.setColor(DASH_BORDER_COLOR);
            g2d.draw(border);

            g2d.setStroke(borderStoke);
            g2d.setColor(BORDER_COLOR);
            g2d.draw(border);

            g2d.dispose();
        }

        public void setBackgroundImage(BufferedImage backgroundImage) {
            this.backgroundImage = backgroundImage;
            repaint();
        }

        public BufferedImage getBackgroundImage() {
            return backgroundImage;
        }

    }

    public class HomeMenuPane extends AbstractBaseMenuPane {

        public static interface NavigationListener {

            public void navigateToInfo(HomeMenuPane source);

            public void navigateToStartGame(HomeMenuPane source);
        }

        private static final String GAME_TITLE = "BRICK DESTROY";
        private static final String START_TEXT = "START";
        private static final String INFO_TEXT = "INFO";

        private Rectangle startButton;
        private Rectangle infoButton;

        private Font gameTitleFont;
        private Font buttonFont;

        // Don't do this, this just sucks (for so many reasons)
        // Use ImageIO.read instead and save yourself a load of frustration
        //private Image img =  Toolkit.getDefaultToolkit().createImage("1.jpeg");

        private Point lastClickPoint;

        private NavigationListener navigationListener;

        public HomeMenuPane(NavigationListener navigationListener) {
            this.navigationListener = navigationListener;

            try {
                setBackgroundImage(ImageIO.read(getClass().getResource("/images/BrickWall.jpg")));
            } catch (IOException ex) {
                Logger.getLogger(Test.class.getName()).log(Level.SEVERE, null, ex);
            }

            this.addMouseListener(new MouseAdapter() {

                @Override
                public void mousePressed(MouseEvent mouseEvent) {
                    Point p = mouseEvent.getPoint();
                    lastClickPoint = p;
                    if (startButton.contains(p)) {
                        peformStartGameAction();
                    } else if (infoButton.contains(p)) {
                        performInfoAction();
                    }
                    repaint();
                }

                @Override
                public void mouseReleased(MouseEvent mouseEvent) {
                    lastClickPoint = null;
                    repaint();
                }
            });
            this.addMouseMotionListener(new MouseAdapter() {
                @Override
                public void mouseMoved(MouseEvent mouseEvent) {
                    Point p = mouseEvent.getPoint();
                    if (startButton.contains(p) || infoButton.contains(p)) {
                        setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
                    } else {
                        setCursor(Cursor.getDefaultCursor());
                    }
                }
            });

            Dimension area = getPreferredSize();

            Dimension btnDim = new Dimension(area.width / 3, area.height / 12);
            startButton = new Rectangle(btnDim);
            infoButton = new Rectangle(btnDim);

            gameTitleFont = new Font("Calibri", Font.BOLD, 28);
            buttonFont = new Font("Calibri", Font.BOLD, startButton.height - 2);
        }

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

            Color prevColor = g2d.getColor();
            Font prevFont = g2d.getFont();

            //methods calls
            drawText(g2d);
            drawButton(g2d);

            //end of methods calls
            g2d.setFont(prevFont);
            g2d.setColor(prevColor);
            g2d.dispose();
        }

        private void drawText(Graphics2D g2d) {
            g2d.setColor(TEXT_COLOR);

            FontRenderContext frc = g2d.getFontRenderContext();

            Rectangle2D gameTitleRect = gameTitleFont.getStringBounds(GAME_TITLE, frc);

            int sX, sY;

            sY = (int) (getHeight() / 4);

            sX = (int) (getWidth() - gameTitleRect.getWidth()) / 2;
            sY += (int) gameTitleRect.getHeight() * 1.1;//add 10% of String height between the two strings

            g2d.setFont(gameTitleFont);
            g2d.drawString(GAME_TITLE, sX, sY);

        }

        private void drawButton(Graphics2D g2d) {

            FontRenderContext frc = g2d.getFontRenderContext();

            Rectangle2D txtRect = buttonFont.getStringBounds(START_TEXT, frc);
            Rectangle2D mTxtRect = buttonFont.getStringBounds(INFO_TEXT, frc);

            g2d.setFont(buttonFont);

            int x = (getWidth() - startButton.width) / 2;
            int y = (int) ((getHeight() - startButton.height) * 0.5);

            startButton.setLocation(x, y);

            x = (int) (startButton.getWidth() - txtRect.getWidth()) / 2;
            y = (int) (startButton.getHeight() - txtRect.getHeight()) / 2;

            x += startButton.x;
            y += startButton.y + (startButton.height * 0.9);

            if (lastClickPoint != null && startButton.contains(lastClickPoint)) {
                Color tmp = g2d.getColor();
                g2d.setColor(CLICKED_BUTTON_COLOR);
                g2d.draw(startButton);
                g2d.setColor(CLICKED_TEXT);
                g2d.drawString(START_TEXT, x, y);
                g2d.setColor(tmp);
            } else {
                g2d.draw(startButton);
                g2d.drawString(START_TEXT, x, y);
            }

            x = startButton.x;
            y = startButton.y;

            y *= 1.3;

            infoButton.setLocation(x, y);

            x = (int) (infoButton.getWidth() - mTxtRect.getWidth()) / 2;
            y = (int) (infoButton.getHeight() - mTxtRect.getHeight()) / 2;

            x += infoButton.getX();
            y += infoButton.getY() + (startButton.height * 0.9);

            if (lastClickPoint != null && infoButton.contains(lastClickPoint)) {
                Color tmp = g2d.getColor();
                g2d.setColor(CLICKED_BUTTON_COLOR);
                g2d.draw(infoButton);
                g2d.setColor(CLICKED_TEXT);
                g2d.drawString(INFO_TEXT, x, y);
                g2d.setColor(tmp);
            } else {
                g2d.draw(infoButton);
                g2d.drawString(INFO_TEXT, x, y);
            }

        }

        protected void peformStartGameAction() {
            navigationListener.navigateToStartGame(this);
        }

        protected void performInfoAction() {
            navigationListener.navigateToInfo(this);
        }
    }

    public class HowToPlayPane extends AbstractBaseMenuPane {

        public static interface NavigationListener {

            public void navigateBack(HowToPlayPane source);
        }

        private static final String HOW_TO_PLAY_TEXT = """
            1- Click Start\n
            2- Choose the mode\n
            3- Each mode has 3 levels\n
            4- To play/pause press space, use 'A' and 'D' to move\n
            5- To open pause menu press 'ESC'\n
            6- To open DebugPanel press 'ALT-SHIFT-F1'""";
        private static final String BACK_TEXT = "BACK";
        private static final String INFO_TEXT = "INFO";

        private Rectangle backButton;
        private boolean backClicked = false;

        private Font infoFont;
        private Font howtoPlayFont;

        private NavigationListener navigationListener;

        public HowToPlayPane(NavigationListener navigationListener) {
            this.navigationListener = navigationListener;
            this.addMouseListener(new MouseAdapter() {
                @Override
                public void mousePressed(MouseEvent mouseEvent) {
                    Point p = mouseEvent.getPoint();
                    if (backButton.contains(p)) {
                        backClicked = true;
                        repaint();
                        performBackAction();
                    }
                }

                @Override
                public void mouseReleased(MouseEvent e) {
                    backClicked = false;
                }
            });

            this.addMouseMotionListener(new MouseAdapter() {
                @Override
                public void mouseMoved(MouseEvent mouseEvent) {
                    Point p = mouseEvent.getPoint();
                    if (backButton.contains(p)) {
                        setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
                    } else {
                        setCursor(Cursor.getDefaultCursor());
                    }
                }
            });

            Dimension btnDim = new Dimension(getPreferredSize().width / 3, getPreferredSize().height / 12);
            backButton = new Rectangle(btnDim);

            infoFont = new Font("Calibri", Font.BOLD, 24);
            howtoPlayFont = new Font("Calibri", Font.PLAIN, 14);
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 400);
        }

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

            g2d.setColor(BORDER_COLOR);
            g2d.fillRect(0, 0, getWidth(), getHeight());

            FontRenderContext frc = g2d.getFontRenderContext();
            Rectangle2D infoRec = infoFont.getStringBounds(INFO_TEXT, frc);
//
//            Color prev = g2d.getColor();
//
//            Stroke tmp = g2d.getStroke();
//
//            g2d.setStroke(borderStoke_noDashes);
//            g2d.setColor(DASH_BORDER_COLOR);
//            g2d.draw(infoFace);
//
//            g2d.setStroke(borderStoke);
//            g2d.setColor(BORDER_COLOR);
//            g2d.draw(infoFace);
//
//            g2d.fillRect(0, 0, infoFace.width, infoFace.height);
//
//            g2d.setStroke(tmp);
//
//            g2d.setColor(prev);
//
            g2d.setColor(TEXT_COLOR);

            int sX, sY;

            sY = (int) (getHeight() / 15);

            sX = (int) (getWidth() - infoRec.getWidth()) / 2;
            sY += (int) infoRec.getHeight() * 1.1;//add 10% of String height between the two strings

            g2d.setFont(infoFont);
            g2d.drawString(INFO_TEXT, sX, sY);

            TextLayout layout = new TextLayout(HOW_TO_PLAY_TEXT, howtoPlayFont, frc);
            String[] outputs = HOW_TO_PLAY_TEXT.split("\n");
            for (int i = 0; i < outputs.length; i++) {
                g2d.setFont(howtoPlayFont);
                g2d.drawString(outputs[i], 40, (int) (80 + i * layout.getBounds().getHeight() + 0.5));
            }

            backButton.setLocation(getWidth() / 3, getHeight() - 50);

            int x = (int) (backButton.getWidth() - infoRec.getWidth()) / 2;
            int y = (int) (backButton.getHeight() - infoRec.getHeight()) / 2;

            x += backButton.x + 11;
            y += backButton.y + (layout.getBounds().getHeight() * 1.35);

            backButton.setLocation(getWidth() / 3, getHeight() - 50);

            if (backClicked) {
                Color tmp1 = g2d.getColor();
                g2d.setColor(CLICKED_BUTTON_COLOR);
                g2d.draw(backButton);
                g2d.setColor(CLICKED_TEXT);
                g2d.drawString(BACK_TEXT, x, y);
                g2d.setColor(tmp1);
                repaint();
            } else {
                g2d.draw(backButton);
                g2d.drawString(BACK_TEXT, x, y);
            }
            g2d.dispose();
        }

        protected void performBackAction() {
            navigationListener.navigateBack(this);
        }
    }

}

Now, this example makes use of components to present different views (it even has a nice abstract implementation to allow for code re-use 😱), but it occurs to me, that, if you "really" wanted to, you could have a series of "painter" classes, which could be used to delegate the painting of the current state to, and mouse clicks/movements could be delegated to, meaning you could have a single component, which would simple delegate the painting (via the paintComponent method) to which ever painter is active.

And, wouldn't you know it, they have a design principle for that to, the Delegation Pattern

The above example also makes use of the observer pattern, so you might want to have a look into that as well

Upvotes: 1

Related Questions