Cross-Platform Swing GUI App Example

Swing is an old GUI framework that is still useful and competitive by 2024 standards. It comes with Java so no extra installation is needed. Downside is that you can only target desktop platforms, with no web or mobile support, unlike JavaFX. It has a lot of widgets with capabilities to create your own custom ones.

Enough intro. Here is a simple, cross-platform Swing GUI app example that does not look horribly outdated (ie. not Metal). It uses Nimbus look & feel (which is easily customizable) and works with Java 11+. Screenshots (scaled to same resolution):

Java 17, MacOS

Java 11, Manjaro

The results are respectably similar, with some differences in line height. You can compile the below source with:

javac -g:none Chat.java
jar cvfe Chat.jar Chat Chat*.class
rm Chat*.class

Double-clicking the jar file should open the app, and it is less than 3200 bytes on either platform. Chat.java file:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.plaf.FontUIResource;
import javax.swing.plaf.nimbus.NimbusLookAndFeel;

class Chat {
    public static void changeFontSize(int delta) {
        var def = UIManager.getLookAndFeelDefaults();
        for (var key : def.keySet()) {
            try {
                var fr = (FontUIResource) def.get(key);
                var nf = new FontUIResource(fr.getName(),
                    fr.getStyle(), fr.getSize() + delta);
                def.put(key, nf);
            } catch (ClassCastException ex) {}
        }
    }

    public static void main(String args[]) {
        try {
            UIManager.setLookAndFeel(new NimbusLookAndFeel());
        } catch (UnsupportedLookAndFeelException ex) {
            System.err.println(ex.toString());
            return;
        }
        changeFontSize(3);

        // Creating the Frame
        var frame = new JFrame("Chat App");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setMinimumSize(new Dimension(650, 400));
        frame.setLocationRelativeTo(null); // screen center

        // Creating the MenuBar and adding menus
        var menus = new JMenuBar();
        var file = new JMenu("File");
        var open = new JMenuItem("Open");
        var save = new JMenuItem("Save");
        file.add(open);
        file.add(save);
        var help = new JMenu("Help");
        var about = new JMenuItem("About");
        help.add(about);
        menus.add(file);
        menus.add(help);

        about.addActionListener(ae -> {
            JOptionPane.showMessageDialog(frame, "Chat v0.1",
                "About", JOptionPane.INFORMATION_MESSAGE);
        });

        // Creating the panel at bottom and adding widgets
        var panel = new JPanel(false); // single-buffered, not visible
        var label = new JLabel("Enter Text");
        var msgBox = new JTextField(20);
        var send = new JButton("Send");
        var reset = new JButton("Reset");
        panel.add(label); // Components Added using Flow Layout
        panel.add(msgBox);
        panel.add(send);
        panel.add(reset);

        // Text Area at the center
        var text = new JTextArea();
        var scroll = new JScrollPane(text);

        msgBox.addKeyListener(new KeyAdapter() {
            @Override
            public void keyTyped(KeyEvent ke) {
                if (ke.getKeyChar() == '\n') { // send when pressed enter
                    send.doClick();
                }
            }
        });
        send.addActionListener(ae -> {
            var msg = msgBox.getText();
            if (msg != null && msg.length() > 0) {
                text.append(msg + "\n");
                msgBox.setText(null);
            }
        });
        reset.addActionListener(ae -> {
            text.setText(null);
        });

        // Adding Components to the frame.
        frame.add(scroll, BorderLayout.CENTER);
        frame.add(panel, BorderLayout.SOUTH);
        frame.add(menus, BorderLayout.NORTH);
        frame.setVisible(true);
    }
}