feat: add javafx chat ui and styling

This commit is contained in:
2026-06-24 12:56:42 +03:30
parent 079145b4e2
commit 8454be1cec
3 changed files with 466 additions and 0 deletions
@@ -0,0 +1,337 @@
package com.university.chat.Client.UI;
import com.university.chat.Client.Net.chatClient;
import com.university.chat.Client.TransferProgress;
import com.university.chat.Common.ChatMessage;
import com.university.chat.Common.FileMessage;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.geometry.Insets;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.*;
public class ChatController {
@FXML
private ListView<String> usersList;
@FXML
private TextArea publicChatArea;
@FXML
private AnchorPane chatAnchor;
@FXML
private TextField messageField;
@FXML
private BorderPane titleBar;
private double xOffset = 0;
private double yOffset = 0;
private chatClient client;
private String selectedUser = null;
private final Map<String, BorderPane> privateChatViews = new HashMap<>();
private final Map<String, TextArea> privateTextAreas = new HashMap<>();
private final Map<String, TextField> privateInputFields = new HashMap<>();
@FXML
private VBox fileTransferBox;
@FXML
private Label fileTransferLabel;
@FXML
private ProgressBar fileTransferProgress;
@FXML
private void initialize() {
titleBar.setOnMousePressed(event -> {
xOffset = event.getSceneX();
yOffset = event.getSceneY();
});
titleBar.setOnMouseDragged(event -> {
Stage stage = (Stage) titleBar.getScene().getWindow();
stage.setX(event.getScreenX() - xOffset);
stage.setY(event.getScreenY() - yOffset);
});
usersList.getSelectionModel().selectedItemProperty().addListener((obs, oldVal, newVal) -> {
if (newVal == null || newVal.isBlank()) {
selectedUser = null;
chatAnchor.getChildren().clear();
return;
}
String clickedUser = newVal.trim();
if (client != null && clickedUser.equals(client.getUsername())) {
selectedUser = null;
chatAnchor.getChildren().clear();
return;
}
selectedUser = clickedUser;
openPrivateChat(clickedUser);
});
}
public void setClient(chatClient client) {
this.client = client;
if (client.getServerListener() != null) {
client.getServerListener().setOnMessageReceived(this::handleIncomingMessage);
}
try {
client.requestUserList();
} catch (IOException e) {
e.printStackTrace();
}
}
public void handleIncomingMessage(Object obj) {
if (obj instanceof ChatMessage msg) {
switch (msg.getType()) {
case USER_LIST -> {
String content = msg.getContent();
if (content != null) {
List<String> users = Arrays.stream(content.split(","))
.map(String::trim)
.filter(u -> !u.isEmpty())
.toList();
updateUsersList(users);
}
}
case PUBLIC_MESSAGE -> Platform.runLater(() ->
publicChatArea.appendText(msg.getSender() + ": " + msg.getContent() + "\n"));
case PRIVATE_MESSAGE -> Platform.runLater(() ->
appendPrivateMessage(msg.getSender(),
msg.getSender() + ": " + msg.getContent()));
}
}
else if (obj instanceof FileMessage msg) {
Platform.runLater(() -> {
FileChooser chooser = new FileChooser();
chooser.setInitialFileName(msg.getFilename());
File file = chooser.showSaveDialog(usersList.getScene().getWindow());
if(file != null){
try {
Files.write(file.toPath(), msg.getData());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
});
}
}
public void updateUsersList(List<String> users) {
Platform.runLater(() ->
usersList.getItems().setAll(users));
}
@FXML
public void sendPublicMessage() {
String text = messageField.getText();
if (text == null || text.trim().isEmpty()) return;
text = text.trim();
try {
client.sendPublicMessage(text);
publicChatArea.appendText("Me: " + text + "\n");
messageField.clear();
} catch (IOException e) {
e.printStackTrace();
}
}
private void sendPrivateMessage(String receiver) {
TextField inputField = privateInputFields.get(receiver);
TextArea chatArea = privateTextAreas.get(receiver);
if (inputField == null || chatArea == null) return;
String text = inputField.getText();
if (text == null || text.trim().isEmpty()) return;
text = text.trim();
try {
client.sendPrivateMessage(receiver, text);
chatArea.appendText("Me: " + text + "\n");
inputField.clear();
} catch (IOException e) {
e.printStackTrace();
}
}
@FXML
private void sendFile() {
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Select file to send");
Stage stage = (Stage) usersList.getScene().getWindow();
File file = fileChooser.showOpenDialog(stage);
if (file == null) return;
try {
updateFileTransfer(file.length());
client.sendFile(selectedUser,file);
publicChatArea.appendText("Me sent file: " + file.getName() + "\n");
}
catch (Exception e){
e.printStackTrace();
}
}
private void openPrivateChat(String user) {
BorderPane chatView = privateChatViews.get(user);
if (chatView == null) {
chatView = createPrivateChatView(user);
privateChatViews.put(user, chatView);
}
chatAnchor.getChildren().setAll(chatView);
AnchorPane.setTopAnchor(chatView, 0.0);
AnchorPane.setBottomAnchor(chatView, 0.0);
AnchorPane.setLeftAnchor(chatView, 0.0);
AnchorPane.setRightAnchor(chatView, 0.0);
}
private BorderPane createPrivateChatView(String user) {
TextArea chatArea = new TextArea();
chatArea.setEditable(false);
chatArea.setWrapText(true);
TextField inputField = new TextField();
inputField.setPromptText("Message to " + user);
Button sendButton = new Button("Send");
sendButton.setOnAction(e -> sendPrivateMessage(user));
inputField.setOnAction(e -> sendPrivateMessage(user));
HBox bottom = new HBox(8, inputField, sendButton);
bottom.setPadding(new Insets(8));
inputField.setPrefWidth(300);
BorderPane root = new BorderPane();
root.setCenter(chatArea);
root.setBottom(bottom);
privateTextAreas.put(user, chatArea);
privateInputFields.put(user, inputField);
return root;
}
private void appendPrivateMessage(String user, String line) {
BorderPane view = privateChatViews.get(user);
if (view == null) {
view = createPrivateChatView(user);
privateChatViews.put(user, view);
}
TextArea area = privateTextAreas.get(user);
if (area != null) {
area.appendText(line + "\n");
}
}
public void showFileTransfer(String fileName) {
Platform.runLater(() -> {
fileTransferBox.setVisible(true);
fileTransferBox.setManaged(true);
fileTransferProgress.setProgress(0);
fileTransferLabel.setText("Sending " + fileName + "...");
});
}
public void updateFileTransfer(long length) {
Platform.runLater(() -> {
fileTransferProgress.setProgress(length);
fileTransferLabel.setText(String.format(
"Sending... %d",
(int) (length * 100)
));
});
}
public void finishFileTransfer(String fileName) {
Platform.runLater(() -> {
fileTransferProgress.setProgress(1.0);
fileTransferLabel.setText(fileName + " sent successfully");
});
}
@FXML
private void closeApp() {
System.exit(0);
}
@FXML
private void minimize() {
Stage stage = (Stage) usersList.getScene().getWindow();
stage.setIconified(true);
}
@FXML
private void maximize() {
Stage stage = (Stage) usersList.getScene().getWindow();
stage.setMaximized(!stage.isMaximized());
}
}