For my Chat GUI, I'm trying to 'show' the messages styled as Facebook's Messenger does, or at least, in that manner. If I'm the one sending them, they should be aligned to the right, if I'm getting a message from anyone else, they should be aligned to the left, and their background-color should be different.
I've made something similar, but it really looks ugly, and on top of this, I cannot style them individually, but in the even-odd way, so it looks something like this, even tho, a message has never been sent to 'create' them.
The message structure is really simple
public void sendMessageOnClick(){
sendButton.setOnAction((e) -> {
String message = textInput.getText();
chatHistory.getItems().add("Sorin: " + message + "\n");
textInput.clear();
sendButton.setDisable(true);
});
}
The trick is to use a cell factory returing custom ListCell
s:
@FXML
private ListView<ChatMessage> chatHistory;
private String user = "Sorin";
private static final PseudoClass USER_MESSAGE = PseudoClass.getPseudoClass("user-message");
@FXML
private void initialize() {
sendButton.disableProperty().bind(textInput.textProperty().isEmpty());
final ColumnConstraints ownUserConstraints = new ColumnConstraints();
ownUserConstraints.setHalignment(HPos.LEFT);
ownUserConstraints.setHgrow(Priority.ALWAYS);
final ColumnConstraints foreignUserConstraints = new ColumnConstraints();
foreignUserConstraints.setHalignment(HPos.RIGHT);
foreignUserConstraints.setHgrow(Priority.ALWAYS);
final ColumnConstraints userConstraints = new ColumnConstraints();
userConstraints.setHgrow(Priority.NEVER);
chatHistory.setCellFactory(lv -> new ListCell<ChatMessage>() {
private final Label message;
private final Label userName;
private final GridPane content;
{
message = new Label();
userName = new Label();
content = new GridPane();
content.setHgap(10);
content.addRow(0, userName, message);
content.getColumnConstraints().addAll(userConstraints, userConstraints);
}
@Override
protected void updateItem(ChatMessage item, boolean empty) {
super.updateItem(item, empty);
boolean userMessage;
if (empty || item == null) {
userMessage = false;
setGraphic(null);
} else {
userMessage = user.equals(item.getUserName());
userName.setText(item.getUserName() + ":");
message.setText(item.getMessage());
setGraphic(content);
content.getColumnConstraints().set(1, userMessage
? ownUserConstraints
: foreignUserConstraints);
}
pseudoClassStateChanged(USER_MESSAGE, userMessage);
}
});
}
public class ChatMessage {
private final String userName;
private final String message;
public ChatMessage(String userName, String message) {
this.userName = userName;
this.message = message;
}
public String getUserName() {
return userName;
}
public String getMessage() {
return message;
}
}
sendButton.setOnAction((e) -> {
...
chatHistory.getItems().add(new ChatMessage(user, message);
...
});
.list-cell {
-fx-background: lightblue;
}
.list-cell:user-message,
.list-cell:empty {
-fx-background: -fx-control-inner-background;
}
.list-view:focused .list-cell:filled:selected {
-fx-background: -fx-selection-bar;
}
BTW: For a alignment of the own user's message that does not overlap other usernames you need to set the with of userConstraints
to the largest text width for the user names...