Why the frontend isn't the way I want?

90 Views Asked by At

I want to achieve this look and trying to set the JPanel components accordingly but when I run the app, I see a different placement. This is the look I am looking for:my image goal

But this is what I have rn:My App

First of all, the order isn't correct and secondly, there are no spaces between the items so, in general, this is not what I want. How can I fix it to look similar to the image I first added? My code:

package Lab5;

import java.awt.*;
import javax.swing.*;
import javax.swing.border.Border;

public class FontDesigner extends JFrame {
    public FontDesigner() {
        // JFrame
        setSize(400, 400);
        setTitle("Font Changer");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // String
        JLabel text = new JLabel("Change Me");
        text.setFont(new Font("Arial", 1, 28));

        // Radio Button
        JRadioButton button1 = new JRadioButton("Small");
        JRadioButton button2 = new JRadioButton("Medium");
        JRadioButton button3 = new JRadioButton("Large");
        ButtonGroup radioGroup = new ButtonGroup();
        radioGroup.add(button1);
        radioGroup.add(button2);
        radioGroup.add(button3);

        /*
         * if (button1.isSelected()) {
         * text.setSize(1);
         * }
         */

        // Combo Box
        JComboBox combo = new JComboBox<>();
        combo.addItem("Serif");
        combo.addItem("Sans-Serif");
        // String selectedFont = (String) combo.getSelectedItem();

        // Check Box
        JCheckBox checkBox1 = new JCheckBox("Italic");
        JCheckBox checkBox2 = new JCheckBox("Bold");
        ButtonGroup checkBoxGroup = new ButtonGroup();
        checkBoxGroup.add(checkBox1);
        checkBoxGroup.add(checkBox2);

        // JPanel
        JPanel panel1 = new JPanel();
        JPanel panel2 = new JPanel();
        JPanel panel3 = new JPanel();
        JPanel panel4 = new JPanel();
        panel1.setLayout(new BorderLayout());
        panel2.setLayout(new BorderLayout());
        panel3.setLayout(new BorderLayout());
        panel4.setLayout(new BorderLayout());
        panel1.add(text, BorderLayout.CENTER);
        panel2.add(combo, BorderLayout.CENTER);
        panel3.add(button1, BorderLayout.WEST);
        panel3.add(button2, BorderLayout.CENTER);
        panel3.add(button3, BorderLayout.EAST);
        panel4.add(checkBox1);
        panel4.add(checkBox2);

        // Frame Configuration
        // setLayout(new BorderLayout());
        setLayout(new FlowLayout());
        add(panel1);
        add(panel2);
        add(panel3);
        add(panel4);

        // Set Visibility
        setVisible(true);

    }

    public static void main(String[] args) {
        FontDesigner newFontDesigner = new FontDesigner();
    }
}
1

There are 1 best solutions below

0
Gilbert Le Blanc On BEST ANSWER

Introduction

I rearranged your code to create the following GUI.

GUI

Explanation

Oracle has a helpful tutorial, Creating a GUI With Swing. Skip the Learning Swing with the NetBeans IDE section.

All Swing applications must start with a call to the SwingUtilities invokeLater method. This method ensures that the Swing components are created and executed on the Event Dispatch Thread.

The first thing I noticed was that your GUI could be divided into four separate JPanels. One to hold the text, one to hold the font selection, one to hold the style, and one to hold the size.

So, I rearranged your code into four methods, with each method producing one of the JPanels.

Then, I decided the text JPanel probably should be in the center of the JFrame's BorderLayout. So, I created a control JPanel to display the bottom three JPanels using another BorderLayout.

Creating each JPanel in a separate method allows me to try different Swing layout managers and see which ones I like the best. It also makes the code much easier for other people to read and understand.

I modified the JComboBox to hold an AppFont instance. This way, I can display the font family name in the combo box. When the user selects one of the options, you have a Font instance to move to the currentFont field.

Code

Here's the complete runnable code. I made the additional class an inner class so I could post the code as one block.

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Font;

import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.SwingUtilities;

public class FontDesigner implements Runnable {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new FontDesigner());
    }

    private Font currentFont;

    private JComboBox<AppFont> fontComboBox;

    private JLabel textLabel;

    public FontDesigner() {
        this.currentFont = new Font("Arial", Font.BOLD, 28);
    }

    @Override
    public void run() {
        JFrame frame = new JFrame("Font Changer");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.add(createDisplayPanel(), BorderLayout.CENTER);
        frame.add(createControlPanel(), BorderLayout.SOUTH);

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

    private JPanel createDisplayPanel() {
        JPanel panel = new JPanel(new FlowLayout());
        panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));

        textLabel = new JLabel("Change Me");
        textLabel.setFont(currentFont);
        panel.add(textLabel);

        return panel;
    }

    private JPanel createControlPanel() {
        JPanel panel = new JPanel(new BorderLayout());
        panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));

        panel.add(createFontSelectionPanel(), BorderLayout.NORTH);
        panel.add(createStylePanel(), BorderLayout.CENTER);
        panel.add(createSizePanel(), BorderLayout.SOUTH);

        return panel;
    }

    private JPanel createFontSelectionPanel() {
        JPanel panel = new JPanel(new FlowLayout());

        fontComboBox = new JComboBox<>();
        fontComboBox.addItem(new AppFont(new Font("Arial", Font.BOLD, 28)));
        fontComboBox.addItem(new AppFont(new Font("Dialog", Font.BOLD, 28)));
        fontComboBox.addItem(new AppFont(new Font("Serif", Font.BOLD, 28)));
        panel.add(fontComboBox);

        return panel;
    }

    private JPanel createStylePanel() {
        JPanel panel = new JPanel(new FlowLayout());
        panel.setBorder(BorderFactory.createTitledBorder("Style"));

        JCheckBox checkBox2 = new JCheckBox("Bold");
        panel.add(checkBox2);

        JCheckBox checkBox1 = new JCheckBox("Italic");
        panel.add(checkBox1);

        return panel;
    }

    private JPanel createSizePanel() {
        JPanel panel = new JPanel(new FlowLayout());
        panel.setBorder(BorderFactory.createTitledBorder("Size"));

        ButtonGroup radioGroup = new ButtonGroup();

        JRadioButton button1 = new JRadioButton("Small");
        radioGroup.add(button1);
        panel.add(button1);

        JRadioButton button2 = new JRadioButton("Medium");
        radioGroup.add(button2);
        panel.add(button2);

        JRadioButton button3 = new JRadioButton("Large");
        radioGroup.add(button3);
        panel.add(button3);

        return panel;
    }

    public class AppFont {

        private final Font font;

        private final String fontName;

        public AppFont(Font font) {
            this.font = font;
            this.fontName = font.getFamily();
        }

        public Font getFont() {
            return font;
        }

        @Override
        public String toString() {
            return fontName;
        }
    }

}

Edited to add 1:

I went ahead and finished the GUI by adding an application model and all of the ActionListeners. I made all the ActionListeners lambda expressions, as I wasn't planning to provide the complete answer.

To the OP, go ahead and finish the GUI on your own.

For everyone else:

I scanned the GraphicsEnvironment for all the possible font families that would correctly display "Change Me". My computer has 218 font families that qualify. Your computer will probably have a different set of font families. The Dialog font should be present on most computers, so I made that the default.

I modified the GUI somewhat. Here's the revised GUI.

Updated GUI

Code 1

Here's the complete runnable code. I made the additional classes inner classes so I could post the code as one block.

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GraphicsEnvironment;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.ButtonGroup;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.SwingUtilities;

public class FontChanger implements Runnable {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new FontChanger());
    }

    private final FontChangerModel model;

    private JComboBox<AppFont> fontComboBox;

    private JLabel chooseLabel, textLabel;

    public FontChanger() {
        this.model = new FontChangerModel();
    }

    @Override
    public void run() {
        JFrame frame = new JFrame("Font Changer");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.add(createDisplayPanel(), BorderLayout.CENTER);
        frame.add(createControlPanel(), BorderLayout.SOUTH);

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

    private JPanel createDisplayPanel() {
        JPanel panel = new JPanel(new FlowLayout());
        panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));

        textLabel = new JLabel(model.getDisplayText());
        updateDisplayPanel();
        panel.add(textLabel);

        Dimension d = panel.getPreferredSize();
        panel.setPreferredSize(new Dimension(d.width + 150, d.height));
        return panel;
    }

    private void updateTextDisplay() {
        Font font = model.getCurrentFont();
        String fontFamily = font.getFamily();
        int fontSizeIndex = model.getCurrentFontSizeIndex();
        model.updateComboBoxModel(fontFamily, model.getCurrentFontStyle(),
                fontSizeIndex);
        updateFontSelectionPanel();
        updateDisplayPanel();
    }

    private JPanel createControlPanel() {
        JPanel panel = new JPanel(new BorderLayout());
        panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));

        panel.add(createFontSelectionPanel(), BorderLayout.NORTH);
        panel.add(createStylePanel(), BorderLayout.CENTER);
        panel.add(createSizePanel(), BorderLayout.SOUTH);

        return panel;
    }

    private JPanel createFontSelectionPanel() {
        JPanel panel = new JPanel(new BorderLayout());
        panel.setBorder(BorderFactory.createEmptyBorder(0, 0, 20, 0));

        chooseLabel = new JLabel(" ");
        panel.add(chooseLabel, BorderLayout.NORTH);

        panel.add(Box.createVerticalStrut(5), BorderLayout.CENTER);

        fontComboBox = new JComboBox<>(model.getComboBoxModel());
        fontComboBox.addActionListener(event -> {
            AppFont appFont = (AppFont) fontComboBox.getSelectedItem();
            Font font = model.getCurrentFont();
            if (appFont != null) {
                font = appFont.getFont();
            }

            String fontFamily = font.getFamily();
            int fontSizeIndex = model.getCurrentFontSizeIndex();
            model.setCurrentFont(font);
            model.updateComboBoxModel(fontFamily, model.getCurrentFontStyle(),
                    fontSizeIndex);
            updateFontSelectionPanel();
            updateDisplayPanel();
        });
        panel.add(fontComboBox, BorderLayout.SOUTH);

        updateFontSelectionPanel();

        return panel;
    }

    private JPanel createStylePanel() {
        JPanel panel = new JPanel(new FlowLayout());
        panel.setBorder(BorderFactory.createTitledBorder("Style"));

        JCheckBox checkBox2 = new JCheckBox("Bold");
        checkBox2.addActionListener(event -> {
            if (checkBox2.isSelected()) {
                model.setBold(true);
            } else {
                model.setBold(false);
            }
            updateTextDisplay();
        });
        panel.add(checkBox2);

        JCheckBox checkBox1 = new JCheckBox("Italic");
        checkBox1.addActionListener(event -> {
            if (checkBox1.isSelected()) {
                model.setItalic(true);
            } else {
                model.setItalic(false);
            }
            updateTextDisplay();
        });
        panel.add(checkBox1);

        return panel;
    }

    private JPanel createSizePanel() {
        JPanel panel = new JPanel(new FlowLayout());
        panel.setBorder(BorderFactory.createTitledBorder("Size"));

        ButtonGroup radioGroup = new ButtonGroup();

        JRadioButton button1 = new JRadioButton("Small");
        button1.addActionListener(event -> {
            updateSizePanel(0);
        });
        radioGroup.add(button1);
        panel.add(button1);

        JRadioButton button2 = new JRadioButton("Medium");
        button2.addActionListener(event -> {
            updateSizePanel(1);
        });
        radioGroup.add(button2);
        panel.add(button2);

        JRadioButton button3 = new JRadioButton("Large");
        button3.addActionListener(event -> {
            updateSizePanel(2);
        });
        button3.setSelected(true);
        radioGroup.add(button3);
        panel.add(button3);

        return panel;
    }

    private void updateSizePanel(int fontSizeIndex) {
        Font font = model.getCurrentFont();
        String fontFamily = font.getFamily();
        model.setCurrentFontSizeIndex(fontSizeIndex);
        model.updateComboBoxModel(fontFamily, model.getCurrentFontStyle(),
                fontSizeIndex);
        updateFontSelectionPanel();
        updateDisplayPanel();
    }

    private void updateFontSelectionPanel() {
        DefaultComboBoxModel<AppFont> comboBoxModel = model.getComboBoxModel();
        int size = comboBoxModel.getSize();
        String s = "Choose one of the " + size + " fonts:";
        chooseLabel.setText(s);
        fontComboBox.setSelectedIndex(model.getCurrentFontIndex());
    }

    private void updateDisplayPanel() {
        textLabel.setFont(model.getCurrentFont());
    }

    public class FontChangerModel {

        private boolean isBold, isItalic;

        private final int[] pointSizes;

        private int currentFontIndex, currentFontSizeIndex;

        private final DefaultComboBoxModel<AppFont> comboBoxModel;

        private Font currentFont;

        private final String displayText;

        public FontChangerModel() {
            this.displayText = "Change Me";
            this.currentFontSizeIndex = 2;
            this.isBold = false;
            this.isItalic = false;
            this.pointSizes = new int[] { 16, 32, 48 };
            this.comboBoxModel = new DefaultComboBoxModel<>();
            updateComboBoxModel("Dialog", getCurrentFontStyle(),
                    currentFontSizeIndex);
        }

        public void updateComboBoxModel(String fontFamily, int fontStyle,
                int size) {
            comboBoxModel.removeAllElements();
            GraphicsEnvironment ge = GraphicsEnvironment
                    .getLocalGraphicsEnvironment();
            String[] allFonts = ge.getAvailableFontFamilyNames();

            int fontIndex = 0;
            for (int index = 0; index < allFonts.length; index++) {
                Font font = new Font(allFonts[index], fontStyle,
                        pointSizes[size]);
                if (font.canDisplayUpTo(displayText) < 0) {
                    comboBoxModel.addElement(new AppFont(font));
                    if (allFonts[index].equals(fontFamily)) {
                        currentFont = font;
                        currentFontIndex = fontIndex;
                    }
                    fontIndex++;
                }
            }
        }

        public Font getCurrentFont() {
            return currentFont;
        }

        public void setCurrentFont(Font currentFont) {
            this.currentFont = currentFont;
        }

        public int getCurrentFontIndex() {
            return currentFontIndex;
        }

        public int getCurrentFontSizeIndex() {
            return currentFontSizeIndex;
        }

        public void setCurrentFontSizeIndex(int currentFontSizeIndex) {
            this.currentFontSizeIndex = currentFontSizeIndex;
        }

        public int getCurrentFontStyle() {
            int currentFontStyle = 0;
            if (isBold) {
                currentFontStyle |= 1;
            }
            if (isItalic) {
                currentFontStyle |= 2;
            }

            return currentFontStyle;
        }

        public boolean isBold() {
            return isBold;
        }

        public void setBold(boolean isBold) {
            this.isBold = isBold;
        }

        public boolean isItalic() {
            return isItalic;
        }

        public void setItalic(boolean isItalic) {
            this.isItalic = isItalic;
        }

        public DefaultComboBoxModel<AppFont> getComboBoxModel() {
            return comboBoxModel;
        }

        public String getDisplayText() {
            return displayText;
        }

    }

    public class AppFont {

        private final Font font;

        public AppFont(Font font) {
            this.font = font;
        }

        public Font getFont() {
            return font;
        }

        @Override
        public String toString() {
            return font.getFamily();
        }

    }

}