Greatly Overhaul GUI and get server in a usable state

This commit is contained in:
Xircon 2026-02-21 00:20:13 -05:00
parent 9a1e6e925a
commit 61edfb42af
8 changed files with 219 additions and 47 deletions

View File

@ -2,21 +2,25 @@ package net.xircon.xenon.client;
import com.formdev.flatlaf.FlatDarkLaf;
import net.xircon.xenon.client.io.CustomOutputStream;
import net.xircon.xenon.client.io.Log;
import net.xircon.xenon.client.io.Util;
import net.xircon.xenon.client.networking.Networking;
import net.xircon.xenon.client.networking.textclient.TextClient;
import org.jspecify.annotations.NonNull;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Objects;
public class Xenon implements Runnable {
private Thread mainThread;
public static void main(String[] args) {new Xenon().start();}
private static Socket mainSocket;
public Color buttonColour = new Color(55, 90, 129);
public Image xenonIcon;
static final String propertiesFile = "client.properties";
// Properties
@ -28,12 +32,39 @@ public class Xenon implements Runnable {
private static String nickname = "Test User";
public void start() {
mainThread = new Thread(this, "mainThread");
Thread mainThread = new Thread(this, "mainThread");
mainThread.start();
}
@Override
public void run() {
// Load Icon
xenonIcon = Util.loadIcon("xenon.png");
// Set Swing LaF before all Swing Stuff gets init
try {
UIManager.setLookAndFeel( new FlatDarkLaf());
} catch (UnsupportedLookAndFeelException e) {
Log.err("Java Swing GUI Look and Feel Did Not Correctly Apply! Falling Back to Default!");
}
// Start Swing Logger ASAP
// Disable this If the entire thing dies :)
JFrame logFrame = new JFrame("Xenon Log");
logFrame.setIconImage(xenonIcon);
// logFrame.setLayout(new BorderLayout());
logFrame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
logFrame.setSize(640,480);
JTextArea logText = new JTextArea();
logText.setAutoscrolls(true);
logText.setLineWrap(true);
logText.setEditable(false);
JScrollPane logScrollPane = new JScrollPane(logText);
logFrame.add(logScrollPane);
PrintStream systemOutput = new PrintStream(new CustomOutputStream(logText));
System.setOut(systemOutput);
System.setErr(systemOutput);
Log.info("Starting Xenon Client!");
// Get Property Values
PortVoIP = Objects.requireNonNull(Util.GetProperties(propertiesFile, "PortVoIP", Util.PROPERTY_TYPE_INT)).intValue;
@ -43,21 +74,17 @@ public class Xenon implements Runnable {
// Start Java Swing GUI
JFrame mainFrame = new JFrame("Xenon Client");
mainFrame.setIconImage(xenonIcon);
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainFrame.setSize(Width, Height);
try {
UIManager.setLookAndFeel( new FlatDarkLaf());
} catch (UnsupportedLookAndFeelException e) {
Log.err("Java Swing GUI Look and Feel Did Not Correctly Apply! Falling Back to Default!");
}
// Creating the MenuBar and adding components
JMenuBar mb = new JMenuBar();
JMenuBar menubar = new JMenuBar();
// Menu 1
JMenu m1 = new JMenu("File");
JMenuItem m11 = new JMenuItem("Open");
JMenuItem m12 = new JMenuItem("Save as");
mb.add(m1);
menubar.add(m1);
m1.add(m11);
m1.add(m12);
// Menu 2
@ -66,8 +93,7 @@ public class Xenon implements Runnable {
@Override
public void actionPerformed(ActionEvent e) {
String IPAddress = JOptionPane.showInputDialog(null, "IP Address Of Server", "127.0.0.1");
if (IPAddress == null) {return;}
else {
if (IPAddress != null) {
mainSocket = Networking.connectToServer(IPAddress, PortText);
}
}
@ -82,27 +108,53 @@ public class Xenon implements Runnable {
@Override
public void actionPerformed(ActionEvent e) {
String Nick = JOptionPane.showInputDialog(null, "New Nickname", nickname);
if (Nick == null) {return;}
else {
if (Nick != null) {
nickname = Nick;
}
}
});
mb.add(m2);
menubar.add(m2);
m2.add(m21);
m2.add(m22);
m2.add(m23);
// Menu 3
JMenu m3 = new JMenu("Help");
JMenuItem m31 = new JMenuItem(new AbstractAction("About") {
JMenu m3 = new JMenu("Edit");
// Preferences Menu
JFrame preferences = preferencesJFrame();
JMenuItem m31 = new JMenuItem(new AbstractAction("Preferences") {
@Override
public void actionPerformed(ActionEvent e) {
preferences.setVisible(true);
}
});
menubar.add(m3);
m3.add(m31);
//Menu 4
JMenu m4 = new JMenu("View");
JMenuItem m41 = new JMenuItem(new AbstractAction("Log") {
@Override
public void actionPerformed(ActionEvent e) {
logFrame.setVisible(true);
}
});
menubar.add(m4);
m4.add(m41);
// Menu 5
JMenu m5 = new JMenu("Help");
JMenuItem m51 = new JMenuItem(new AbstractAction("About") {
@Override
public void actionPerformed(ActionEvent actionEvent) {
JOptionPane.showMessageDialog(null, "Xenon Created By Xircon\nLicenced under the GNU GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.en.html)",
"About Xenon", JOptionPane.INFORMATION_MESSAGE);
}
});
mb.add(m3);
m3.add(m31);
menubar.add(m5);
m5.add(m51);
// Creating the panel at bottom and adding components
@ -112,7 +164,9 @@ public class Xenon implements Runnable {
sendField.setToolTipText("Enter Message");
sendField.setPreferredSize(new Dimension(200 ,20 ));
JButton send = new JButton("Send");
send.setBackground(buttonColour);
JButton upload = new JButton("Upload");
upload.setBackground(buttonColour);
panel.add(sendField, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new BorderLayout());
@ -125,19 +179,98 @@ public class Xenon implements Runnable {
if (mainSocket == null) {JOptionPane.showMessageDialog(null, "Ensure you are Connected to a Server!", "Error", JOptionPane.ERROR_MESSAGE);}
else {
Log.info("Sending " + sendField.getText());
Networking.sendMessage(sendField.getText(), nickname, mainSocket);
Thread textClient = new Thread(new TextClient(mainSocket, nickname, sendField.getText()));
textClient.start();
SwingUtilities.invokeLater(() -> sendField.setText(""));
}
});
// Text Area at the Center
JTextArea ta = new JTextArea();
// Central Panel With Text Stuffs
JPanel centrePanel = new JPanel();
JTextArea textarea = new JTextArea();
textarea.setAutoscrolls(true);
textarea.setLineWrap(true);
textarea.setEditable(false);
textarea.setBackground(new Color(70,73,75));
centrePanel.setLayout(new BorderLayout());
centrePanel.add(panel, BorderLayout.SOUTH);
centrePanel.add(textarea, BorderLayout.CENTER);
// Side Panel
JPanel sidePanel = sideJPanel();
//Adding Components to the Frame.
mainFrame.getContentPane().add(BorderLayout.SOUTH, panel);
mainFrame.getContentPane().add(BorderLayout.NORTH, mb);
mainFrame.getContentPane().add(BorderLayout.CENTER, ta);
mainFrame.getContentPane().add(BorderLayout.NORTH, menubar);
mainFrame.getContentPane().add(BorderLayout.CENTER, centrePanel);
mainFrame.getContentPane().add(BorderLayout.WEST, sidePanel);
// Set GUI To Visible
mainFrame.setVisible(true);
}
private @NonNull JPanel sideJPanel() {
JPanel sidePanel = new JPanel(new BorderLayout());
sidePanel.setBackground(new Color(60,63,65));
JPanel bottomSidePanel = new JPanel(new BorderLayout());
// Mute Button
JToggleButton muteButton = muteJToggleButton();
// Deaf Button
JToggleButton deafButton = deafJToggleButton();
bottomSidePanel.add(muteButton, BorderLayout.WEST);
bottomSidePanel.add(deafButton, BorderLayout.CENTER);
sidePanel.add(bottomSidePanel, BorderLayout.SOUTH);
return sidePanel;
}
private @NonNull JToggleButton deafJToggleButton() {
JToggleButton deafButton = new JToggleButton("Deafen");
deafButton.setBackground(buttonColour);
deafButton.setForeground(Color.WHITE);
deafButton.setFocusPainted(false);
deafButton.setBorderPainted(false);
deafButton.addActionListener(e -> {
if (deafButton.isSelected()) {
deafButton.setBackground(Color.RED); // Change to RED
} else {
deafButton.setBackground(buttonColour);
}
});
return deafButton;
}
private @NonNull JToggleButton muteJToggleButton() {
JToggleButton muteButton = new JToggleButton("Mute");
muteButton.setBackground(buttonColour);
muteButton.setForeground(Color.WHITE);
muteButton.setFocusPainted(false);
muteButton.setBorderPainted(false);
muteButton.addActionListener(e -> {
if (muteButton.isSelected()) {
muteButton.setBackground(Color.RED); // Change to RED
} else {
muteButton.setBackground(buttonColour);
}
});
return muteButton;
}
private @NonNull JFrame preferencesJFrame() {
JFrame preferences = new JFrame("Preferences");
preferences.setSize(800,600);
preferences.setIconImage(xenonIcon);
preferences.setLayout(new BorderLayout());
preferences.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
JPanel preferencesSidePanel = new JPanel(new BorderLayout());
JButton preferencesGeneralButton = new JButton(new AbstractAction("General") {
@Override
public void actionPerformed(ActionEvent e) {
}
});
preferencesSidePanel.add(preferencesGeneralButton);
preferences.add(preferencesSidePanel, BorderLayout.WEST);
return preferences;
}
}

View File

@ -0,0 +1,23 @@
package net.xircon.xenon.client.io;
import javax.swing.*;
import java.io.IOException;
import java.io.OutputStream;
public class CustomOutputStream extends OutputStream {
private JTextArea textArea;
public CustomOutputStream(JTextArea textArea) {
this.textArea = textArea;
}
@Override
public void write(int b) throws IOException {
// redirects data to the text area
textArea.append(String.valueOf((char)b));
// scrolls the text area to the end of data
textArea.setCaretPosition(textArea.getDocument().getLength());
// keeps the textArea up to date
textArea.update(textArea.getGraphics());
}
}

View File

@ -2,6 +2,7 @@ package net.xircon.xenon.client.io;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.Appender;
import javax.swing.*;
@ -23,3 +24,4 @@ public class Log {
logger.info(message);
}
}

View File

@ -1,11 +1,11 @@
package net.xircon.xenon.client.io;
import javax.annotation.Nullable;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URL;
import java.util.Objects;
import java.util.Properties;
public class Util {
@ -47,6 +47,7 @@ public class Util {
Log.warn("[Property Loader] Unable to Find Property/Invalid Type!");
return null;
}
public static String GetExternalIP() throws IOException {
URL whatismyip = new URL("http://checkip.amazonaws.com");
BufferedReader in = new BufferedReader(new InputStreamReader(
@ -55,4 +56,13 @@ public class Util {
// Return IP As String
return in.readLine();
}
@Nullable
public static BufferedImage loadIcon(String path) {
try {
return ImageIO.read(Objects.requireNonNull(Util.class.getClassLoader().getResource(path)));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -13,7 +13,7 @@ import java.net.SocketAddress;
import java.net.SocketTimeoutException;
public class Networking {
private static final String nameTerminator = "\uffff";
@Nullable
public static Socket connectToServer(String Address, int Port) {
@ -44,18 +44,4 @@ public class Networking {
Log.warn("Ignoring Disconnect Request, Null Socket!");
}
}
public static String sendMessage(String message, String nickname, Socket socket) {
try {
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter output = new PrintWriter(socket.getOutputStream(), true);
output.println(nickname + nameTerminator + message);
String messageFromServer = input.readLine();
Log.info("Server Message: " + messageFromServer);
return messageFromServer;
} catch (IOException e) {
Log.warn("Could not Read/Send Message!");
return null;
}
}
}

View File

@ -1,15 +1,33 @@
package net.xircon.xenon.client.networking.textclient;
import net.xircon.xenon.client.io.Log;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class TextClient implements Runnable {
private final String nameTerminator = "\uffff";
private String nickname;
private String message;
private Socket socket;
public TextClient(Socket serverSocket) {
public TextClient(Socket serverSocket, String nickname, String message) {
this.socket = serverSocket; this.message = message; this.nickname = nickname;
}
@Override
public void run() {
try {
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter output = new PrintWriter(socket.getOutputStream(), true);
output.println(nickname + nameTerminator + message);
String messageFromServer = input.readLine();
Log.info("Server Message: " + messageFromServer);
} catch (IOException e) {
Log.warn("Could not Read/Send Message!");
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB