Merged branch 'develop' into f/groups

Contains several bug fixes
This commit is contained in:
DieGurke 2020-06-10 22:23:59 +02:00
commit 3439aee112
14 changed files with 160 additions and 112 deletions

View File

@ -148,12 +148,22 @@ public abstract class LocalDB {
* Searches for a message by ID. * Searches for a message by ID.
* *
* @param id the ID of the message to search for * @param id the ID of the message to search for
* @return the message with the corresponding ID, or {@code null} if no message * @return an optional containing the message
* has been found
* @since Envoy Client v0.1-beta * @since Envoy Client v0.1-beta
*/ */
public Message getMessage(long id) { public Optional<Message> getMessage(long id) {
return chats.stream().map(Chat::getMessages).flatMap(List::stream).filter(m -> m.getID() == id).findAny().orElse(null); return chats.stream().map(Chat::getMessages).flatMap(List::stream).filter(m -> m.getID() == id).findAny();
}
/**
* Searches for a chat by recipient ID.
*
* @param recipientID the ID of the chat's recipient
* @return an optional containing the chat
* @since Envoy Client v0.1-beta
*/
public Optional<Chat> getChat(long recipientID) {
return chats.stream().filter(c -> c.getRecipient().getID() == recipientID).findAny();
} }
/** /**

View File

@ -91,7 +91,7 @@ public class Client implements Closeable {
SerializationUtils.writeBytesWithLength(credentials, socket.getOutputStream()); SerializationUtils.writeBytesWithLength(credentials, socket.getOutputStream());
// Wait for a maximum of five seconds to acquire the sender object // Wait for a maximum of five seconds to acquire the sender object
long start = System.currentTimeMillis(); final long start = System.currentTimeMillis();
while (sender == null) { while (sender == null) {
// Quit immediately after handshake rejection // Quit immediately after handshake rejection
@ -141,7 +141,7 @@ public class Client implements Closeable {
receiver.registerProcessor(MessageStatusChangeEvent.class, new MessageStatusChangeEventProcessor()); receiver.registerProcessor(MessageStatusChangeEvent.class, new MessageStatusChangeEventProcessor());
// Process user status changes // Process user status changes
receiver.registerProcessor(UserStatusChangeEvent.class, new UserStatusChangeProcessor(localDB)); receiver.registerProcessor(UserStatusChangeEvent.class, eventBus::dispatch);
// Process message ID generation // Process message ID generation
receiver.registerProcessor(IDGenerator.class, localDB::setIDGenerator); receiver.registerProcessor(IDGenerator.class, localDB::setIDGenerator);
@ -162,7 +162,7 @@ public class Client implements Closeable {
eventBus.register(SendEvent.class, evt -> { eventBus.register(SendEvent.class, evt -> {
try { try {
sendEvent(evt.get()); sendEvent(evt.get());
} catch (IOException e) { } catch (final IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
}); });
@ -221,7 +221,7 @@ public class Client implements Closeable {
*/ */
public Map<String, Contact> getUsers() { public Map<String, Contact> getUsers() {
checkOnline(); checkOnline();
Map<String, Contact> users = new HashMap<>(); final Map<String, Contact> users = new HashMap<>();
contacts.forEach(u -> users.put(u.getName(), u)); contacts.forEach(u -> users.put(u.getName(), u));
users.put(sender.getName(), sender); users.put(sender.getName(), sender);
return users; return users;

View File

@ -1,33 +0,0 @@
package envoy.client.net;
import java.util.function.Consumer;
import envoy.client.data.LocalDB;
import envoy.data.User;
import envoy.event.EventBus;
import envoy.event.UserStatusChangeEvent;
/**
* Project: <strong>envoy-client</strong><br>
* File: <strong>UserStatusChangeProcessor.java</strong><br>
* Created: <strong>2 Feb 2020</strong><br>
*
* @author Leon Hofmeister
* @since Envoy Client v0.3-alpha
*/
public class UserStatusChangeProcessor implements Consumer<UserStatusChangeEvent> {
private final LocalDB localDB;
/**
* @param localDB the local database in which status updates will by applied
* @since Envoy Client v0.3-alpha
*/
public UserStatusChangeProcessor(LocalDB localDB) { this.localDB = localDB; }
@Override
public void accept(UserStatusChangeEvent evt) {
localDB.getUsers().values().stream().filter(u -> u.getID() == evt.getID()).map(User.class::cast).findFirst().get().setStatus(evt.get());
EventBus.getInstance().dispatch(evt);
}
}

View File

@ -49,7 +49,7 @@ public class WriteProxy {
client.sendMessage(msg); client.sendMessage(msg);
// Update message state to SENT in localDB // Update message state to SENT in localDB
localDB.getMessage(msg.getID()).nextStatus(); localDB.getMessage(msg.getID()).ifPresent(Message::nextStatus);
} catch (IOException e) { } catch (IOException e) {
logger.log(Level.SEVERE, "Could not send cached message", e); logger.log(Level.SEVERE, "Could not send cached message", e);
} }

View File

@ -3,8 +3,10 @@ package envoy.client.ui;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.ListCell; import javafx.scene.control.ListCell;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import envoy.data.Contact; import envoy.data.Contact;
import envoy.data.Group;
import envoy.data.User; import envoy.data.User;
/** /**
@ -20,15 +22,42 @@ public class ContactListCell extends ListCell<Contact> {
/** /**
* Displays the name of a contact. If the contact is a user, their online status * Displays the name of a contact. If the contact is a user, their online status
* is displayed as well. * is displayed as well.
* *
* @since Envoy Client v0.1-beta * @since Envoy Client v0.1-beta
*/ */
@Override @Override
protected void updateItem(Contact contact, boolean empty) { protected void updateItem(Contact contact, boolean empty) {
super.updateItem(contact, empty); super.updateItem(contact, empty);
if (!empty && contact != null) { if (empty || contact == null) {
final var name = new Label(contact.getName()); setText(null);
setGraphic(contact instanceof User ? new VBox(name, new Label(((User) contact).getStatus().toString())) : new VBox(name)); setGraphic(null);
} else {
// the infoLabel displays specific contact info, i.e. status of a user or amount
// of members in a group
Label infoLabel = null;
if (contact instanceof User) {
// user specific info
infoLabel = new Label(((User) contact).getStatus().toString());
Color textColor = null;
switch (((User) contact).getStatus()) {
case ONLINE:
textColor = Color.LIMEGREEN;
break;
case AWAY:
textColor = Color.ORANGERED;
break;
case BUSY:
textColor = Color.RED;
break;
case OFFLINE:
textColor = Color.GRAY;
break;
}
infoLabel.setTextFill(textColor);
} else
// group specific infos
infoLabel = new Label(String.valueOf(((Group) contact).getContacts().size()) + " members");
setGraphic(new VBox(new Label(contact.getName()), infoLabel));
} }
} }
} }

View File

@ -45,10 +45,15 @@ public class MessageListCell extends ListCell<Message> {
@Override @Override
protected void updateItem(Message message, boolean empty) { protected void updateItem(Message message, boolean empty) {
super.updateItem(message, empty); super.updateItem(message, empty);
setGraphic(!empty && message != null ? new HBox( if(empty || message == null) {
setText(null);
setGraphic(null);
} else {
setGraphic(new HBox(
new VBox( new VBox(
new Label(dateFormat.format(message.getCreationDate())), new Label(dateFormat.format(message.getCreationDate())),
new Label(message.getText())), new Label(message.getText())),
new Label("", new ImageView(statusImages.get(message.getStatus())))) : null); new Label("", new ImageView(statusImages.get(message.getStatus())))));
}
} }
} }

View File

@ -115,6 +115,7 @@ public final class SceneContext {
sceneStack.push(scene); sceneStack.push(scene);
stage.setScene(scene); stage.setScene(scene);
applyCSS(); applyCSS();
stage.sizeToScene();
stage.show(); stage.show();
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
@ -131,6 +132,7 @@ public final class SceneContext {
if (!sceneStack.isEmpty()) { if (!sceneStack.isEmpty()) {
stage.setScene(sceneStack.peek()); stage.setScene(sceneStack.peek());
applyCSS(); applyCSS();
stage.sizeToScene();
} }
stage.show(); stage.show();
} }

View File

@ -9,6 +9,7 @@ import javafx.application.Platform;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.control.*; import javafx.scene.control.*;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent; import javafx.scene.input.KeyEvent;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
@ -22,9 +23,7 @@ import envoy.client.net.WriteProxy;
import envoy.client.ui.ContactListCell; import envoy.client.ui.ContactListCell;
import envoy.client.ui.MessageListCell; import envoy.client.ui.MessageListCell;
import envoy.client.ui.SceneContext; import envoy.client.ui.SceneContext;
import envoy.data.Contact; import envoy.data.*;
import envoy.data.Message;
import envoy.data.MessageBuilder;
import envoy.event.EventBus; import envoy.event.EventBus;
import envoy.event.MessageStatusChangeEvent; import envoy.event.MessageStatusChangeEvent;
import envoy.event.UserStatusChangeEvent; import envoy.event.UserStatusChangeEvent;
@ -74,6 +73,11 @@ public final class ChatScene {
private static final Logger logger = EnvoyLog.getLogger(ChatScene.class); private static final Logger logger = EnvoyLog.getLogger(ChatScene.class);
private static final int MAX_MESSAGE_LENGTH = 255; private static final int MAX_MESSAGE_LENGTH = 255;
/**
* Initializes the appearance of certain visual components.
*
* @since Envoy Client v0.1-beta
*/
@FXML @FXML
private void initialize() { private void initialize() {
@ -83,24 +87,36 @@ public final class ChatScene {
// Listen to received messages // Listen to received messages
eventBus.register(MessageCreationEvent.class, e -> { eventBus.register(MessageCreationEvent.class, e -> {
final var message = e.get(); final var message = e.get();
final var chat = localDB.getChats().stream().filter(c -> c.getRecipient().getID() == message.getSenderID()).findAny().get(); localDB.getChat(message.getSenderID()).ifPresent(chat -> {
chat.getMessages().add(message);
// Update UI if in current chat // Update UI if in current chat
if (chat == currentChat) Platform.runLater(() -> messageList.getItems().add(message)); if (chat == currentChat)
Platform.runLater(messageList::refresh);
});
}); });
// Listen to message status changes // Listen to message status changes
eventBus.register(MessageStatusChangeEvent.class, e -> { eventBus.register(MessageStatusChangeEvent.class, e ->
final var message = localDB.getMessage(e.getID()); localDB.getMessage(e.getID()).ifPresent(message -> {
message.setStatus(e.get()); message.setStatus(e.get());
// Update UI if in current chat // Update UI if in current chat
if (currentChat != null && message.getSenderID() == currentChat.getRecipient().getID()) Platform.runLater(messageList::refresh); if (currentChat != null && message.getSenderID() == currentChat.getRecipient().getID()) Platform.runLater(messageList::refresh);
}); }));
// Listen to user status changes // Listen to user status changes
eventBus.register(UserStatusChangeEvent.class, e -> Platform.runLater(userList::refresh)); eventBus.register(UserStatusChangeEvent.class, e ->
userList.getItems()
.stream()
.filter(c -> c.getID() == e.getID())
.findAny()
.ifPresent(u -> {
((User) u).setStatus(e.get());
Platform.runLater(userList::refresh);
})
);
// Listen to contacts changes // Listen to contacts changes
eventBus.register(ContactOperationEvent.class, e -> { eventBus.register(ContactOperationEvent.class, e -> {
@ -121,6 +137,8 @@ public final class ChatScene {
} }
/** /**
* Initializes all necessary data via dependency injection-
*
* @param sceneContext the scene context used to load other scenes * @param sceneContext the scene context used to load other scenes
* @param localDB the local database form which chats and users are loaded * @param localDB the local database form which chats and users are loaded
* @param client the client used to request ID generators * @param client the client used to request ID generators
@ -137,6 +155,11 @@ public final class ChatScene {
userList.setItems(FXCollections.observableList(localDB.getChats().stream().map(Chat::getRecipient).collect(Collectors.toList()))); userList.setItems(FXCollections.observableList(localDB.getChats().stream().map(Chat::getRecipient).collect(Collectors.toList())));
} }
/**
* Actions to perform when the list of contacts has been clicked.
*
* @since Envoy Client v0.1-beta
*/
@FXML @FXML
private void userListClicked() { private void userListClicked() {
final Contact user = userList.getSelectionModel().getSelectedItem(); final Contact user = userList.getSelectionModel().getSelectedItem();
@ -146,10 +169,8 @@ public final class ChatScene {
// LEON: JFC <===> JAVA FRIED CHICKEN <=/=> Java Foundation Classes // LEON: JFC <===> JAVA FRIED CHICKEN <=/=> Java Foundation Classes
// Load the chat or create a new one and add it to the LocalDB // Load the chat or create a new one and add it to the LocalDB
currentChat = localDB.getChats() currentChat = localDB
.stream() .getChat(user.getID())
.filter(c -> c.getRecipient().getID() == user.getID())
.findAny()
.orElseGet(() -> { final var chat = new Chat(user); localDB.getChats().add(chat); return chat; }); .orElseGet(() -> { final var chat = new Chat(user); localDB.getChats().add(chat); return chat; });
messageList.setItems(FXCollections.observableList(currentChat.getMessages())); messageList.setItems(FXCollections.observableList(currentChat.getMessages()));
@ -161,32 +182,35 @@ public final class ChatScene {
messageTextArea.setDisable(currentChat == null); messageTextArea.setDisable(currentChat == null);
} }
@FXML /**
private void postButtonClicked() { postMessage(); } * Actions to perform when the Settings Button has been clicked.
*
* @since Envoy Client v0.1-beta
*/
@FXML @FXML
private void settingsButtonClicked() { private void settingsButtonClicked() {
sceneContext.load(SceneContext.SceneInfo.SETTINGS_SCENE); sceneContext.load(SceneContext.SceneInfo.SETTINGS_SCENE);
sceneContext.<SettingsScene>getController().initializeData(sceneContext); sceneContext.<SettingsScene>getController().initializeData(sceneContext);
} }
/**
* Actions to perform when the "Add Contact" - Button has been clicked.
*
* @since Envoy Client v0.1-beta
*/
@FXML @FXML
private void addContactButtonClicked() { private void addContactButtonClicked() {
sceneContext.load(SceneContext.SceneInfo.CONTACT_SEARCH_SCENE); sceneContext.load(SceneContext.SceneInfo.CONTACT_SEARCH_SCENE);
sceneContext.<ContactSearchScene>getController().initializeData(sceneContext, localDB); sceneContext.<ContactSearchScene>getController().initializeData(sceneContext, localDB);
} }
/**
* Actions to perform when the text was updated in the messageTextArea.
*
* @since Envoy Client v0.1-beta
*/
@FXML @FXML
private void messageTextUpdated(KeyEvent e) { private void messageTextUpdated() {
// TODO: Letting go of ctrl automatically posts a message. Needs to be fixed
// soon.
// Automatic sending of messages via (ctrl +) enter
if (!postButton.isDisabled() && settings.isEnterToSend() && e.getCode() == KeyCode.ENTER
|| !settings.isEnterToSend() && e.getCode() == KeyCode.CONTROL)
postMessage();
else postButton.setDisable(messageTextArea.getText().isBlank() || currentChat == null);
// Truncating messages that are too long and staying at the same position // Truncating messages that are too long and staying at the same position
if (messageTextArea.getText().length() >= MAX_MESSAGE_LENGTH) { if (messageTextArea.getText().length() >= MAX_MESSAGE_LENGTH) {
messageTextArea.setText(messageTextArea.getText().substring(0, MAX_MESSAGE_LENGTH)); messageTextArea.setText(messageTextArea.getText().substring(0, MAX_MESSAGE_LENGTH));
@ -201,33 +225,34 @@ public final class ChatScene {
remainingChars.setTextFill(Color.rgb(currentLength, remainingLength, 0, 1)); remainingChars.setTextFill(Color.rgb(currentLength, remainingLength, 0, 1));
} }
/**
* Actions to perform when a key has been entered.
*
* @param e the Keys that have been entered
* @since Envoy Client v0.1-beta
*/
@FXML
private void checkKeyCombination(KeyEvent e) {
// Automatic sending of messages via (ctrl +) enter
if (!postButton.isDisabled() && settings.isEnterToSend() && e.getCode() == KeyCode.ENTER
|| !settings.isEnterToSend() && e.getCode() == KeyCode.ENTER && e.isControlDown())
postMessage();
postButton.setDisable(messageTextArea.getText().isBlank() || currentChat == null);
}
/** /**
* Sends a new message to the server based on the text entered in the * Sends a new message to the server based on the text entered in the
* messageTextArea. * messageTextArea.
* *
* @since Envoy Client v0.1-beta * @since Envoy Client v0.1-beta
*/ */
@FXML
private void postMessage() { private void postMessage() {
// Create and send message
sendMessage(new MessageBuilder(localDB.getUser().getID(), currentChat.getRecipient().getID(), localDB.getIDGenerator())
.setText(messageTextArea.getText().strip())
.build());
// Clear text field and disable post button
messageTextArea.setText("");
postButton.setDisable(true);
}
/**
* Sends a message to the server and appends it to the current chat. If all
* message IDs have been used, a new ID generator is requested.
*
* @param message the message to send
* @since Envoy Client v0.1-beta
*/
private void sendMessage(Message message) {
try { try {
// Create and send message
final var message = new MessageBuilder(localDB.getUser().getID(), currentChat.getRecipient().getID(), localDB.getIDGenerator())
.setText(messageTextArea.getText().strip())
.build();
// Send message // Send message
writeProxy.writeMessage(message); writeProxy.writeMessage(message);
@ -240,6 +265,11 @@ public final class ChatScene {
} catch (final IOException e) { } catch (final IOException e) {
logger.log(Level.SEVERE, "Error sending message", e); logger.log(Level.SEVERE, "Error sending message", e);
new Alert(AlertType.ERROR, "An error occured while sending the message!").showAndWait();
} }
// Clear text field and disable post button
messageTextArea.setText("");
postButton.setDisable(true);
} }
} }

View File

@ -28,7 +28,7 @@ import envoy.util.EnvoyLog;
* Project: <strong>envoy-client</strong><br> * Project: <strong>envoy-client</strong><br>
* File: <strong>LoginDialog.java</strong><br> * File: <strong>LoginDialog.java</strong><br>
* Created: <strong>03.04.2020</strong><br> * Created: <strong>03.04.2020</strong><br>
* *
* @author Kai S. K. Engelbart * @author Kai S. K. Engelbart
* @since Envoy Client v0.1-beta * @since Envoy Client v0.1-beta
*/ */
@ -72,7 +72,7 @@ public final class LoginScene {
/** /**
* Loads the login dialog using the FXML file {@code LoginDialog.fxml}. * Loads the login dialog using the FXML file {@code LoginDialog.fxml}.
* *
* @param client the client used to perform the handshake * @param client the client used to perform the handshake
* @param localDB the local database used for offline login * @param localDB the local database used for offline login
* @param receivedMessageCache the cache storing messages received during * @param receivedMessageCache the cache storing messages received during
@ -104,9 +104,7 @@ public final class LoginScene {
if (registerCheckBox.isSelected() && !passwordField.getText().equals(repeatPasswordField.getText())) { if (registerCheckBox.isSelected() && !passwordField.getText().equals(repeatPasswordField.getText())) {
clearPasswordFields(); clearPasswordFields();
new Alert(AlertType.ERROR, "The entered password is unequal to the repeated one").showAndWait(); new Alert(AlertType.ERROR, "The entered password is unequal to the repeated one").showAndWait();
} else { } else performHandshake(new LoginCredentials(userTextField.getText(), passwordField.getText().toCharArray(), registerCheckBox.isSelected()));
performHandshake(new LoginCredentials(userTextField.getText(), passwordField.getText().toCharArray(), registerCheckBox.isSelected()));
}
} }
@FXML @FXML
@ -120,7 +118,6 @@ public final class LoginScene {
// Make repeat password field and label visible / invisible // Make repeat password field and label visible / invisible
repeatPasswordField.setVisible(registerCheckBox.isSelected()); repeatPasswordField.setVisible(registerCheckBox.isSelected());
repeatPasswordLabel.setVisible(registerCheckBox.isSelected()); repeatPasswordLabel.setVisible(registerCheckBox.isSelected());
clearPasswordFields(); clearPasswordFields();
} }
@ -148,12 +145,12 @@ public final class LoginScene {
try { try {
// Try entering offline mode // Try entering offline mode
localDB.loadUsers(); localDB.loadUsers();
User clientUser = (User) localDB.getUsers().get(credentials.getIdentifier()); final User clientUser = (User) localDB.getUsers().get(credentials.getIdentifier());
if (clientUser == null) throw new EnvoyException("Could not enter offline mode: user name unknown"); if (clientUser == null) throw new EnvoyException("Could not enter offline mode: user name unknown");
client.setSender(clientUser); client.setSender(clientUser);
new Alert(AlertType.WARNING, "A connection to the server could not be established. Starting in offline mode.").showAndWait(); new Alert(AlertType.WARNING, "A connection to the server could not be established. Starting in offline mode.").showAndWait();
loadChatScene(); loadChatScene();
} catch (Exception e) { } catch (final Exception e) {
new Alert(AlertType.ERROR, "Client error: " + e).showAndWait(); new Alert(AlertType.ERROR, "Client error: " + e).showAndWait();
System.exit(1); System.exit(1);
} }

View File

@ -4,8 +4,8 @@
* Project: <strong>envoy-client</strong><br> * Project: <strong>envoy-client</strong><br>
* File: <strong>package-info.java</strong><br> * File: <strong>package-info.java</strong><br>
* Created: <strong>08.06.2020</strong><br> * Created: <strong>08.06.2020</strong><br>
* *
* @author Kai S. K. Engelbart * @author Kai S. K. Engelbart
* @since Envoy Client v0.1-beta * @since Envoy Client v0.1-beta
*/ */
package envoy.client.ui.controller; package envoy.client.ui.controller;

View File

@ -8,13 +8,14 @@ import javafx.scene.layout.VBox;
import envoy.client.data.Settings; import envoy.client.data.Settings;
import envoy.client.data.SettingsItem; import envoy.client.data.SettingsItem;
import envoy.client.event.ThemeChangeEvent; import envoy.client.event.ThemeChangeEvent;
import envoy.data.User.UserStatus;
import envoy.event.EventBus; import envoy.event.EventBus;
/** /**
* Project: <strong>envoy-client</strong><br> * Project: <strong>envoy-client</strong><br>
* File: <strong>GeneralSettingsPane.java</strong><br> * File: <strong>GeneralSettingsPane.java</strong><br>
* Created: <strong>18.04.2020</strong><br> * Created: <strong>18.04.2020</strong><br>
* *
* @author Kai S. K. Engelbart * @author Kai S. K. Engelbart
* @since Envoy Client v0.1-beta * @since Envoy Client v0.1-beta
*/ */
@ -27,7 +28,7 @@ public class GeneralSettingsPane extends SettingsPane {
*/ */
public GeneralSettingsPane() { public GeneralSettingsPane() {
super("General"); super("General");
var vbox = new VBox(); final var vbox = new VBox();
// TODO: Support other value types // TODO: Support other value types
List.of("onCloseMode", "enterToSend") List.of("onCloseMode", "enterToSend")
@ -36,7 +37,7 @@ public class GeneralSettingsPane extends SettingsPane {
.map(i -> new SettingsCheckbox((SettingsItem<Boolean>) i)) .map(i -> new SettingsCheckbox((SettingsItem<Boolean>) i))
.forEach(vbox.getChildren()::add); .forEach(vbox.getChildren()::add);
var combobox = new ComboBox<String>(); final var combobox = new ComboBox<String>();
combobox.getItems().add("dark"); combobox.getItems().add("dark");
combobox.getItems().add("light"); combobox.getItems().add("light");
combobox.setValue(settings.getCurrentTheme()); combobox.setValue(settings.getCurrentTheme());
@ -44,6 +45,13 @@ public class GeneralSettingsPane extends SettingsPane {
e -> { settings.setCurrentTheme(combobox.getValue()); EventBus.getInstance().dispatch(new ThemeChangeEvent(combobox.getValue())); }); e -> { settings.setCurrentTheme(combobox.getValue()); EventBus.getInstance().dispatch(new ThemeChangeEvent(combobox.getValue())); });
vbox.getChildren().add(combobox); vbox.getChildren().add(combobox);
final var statusComboBox = new ComboBox<UserStatus>();
statusComboBox.getItems().setAll(UserStatus.values());
statusComboBox.setValue(UserStatus.ONLINE);
// TODO add action when value is changed
statusComboBox.setOnAction(e -> {});
vbox.getChildren().add(statusComboBox);
getChildren().add(vbox); getChildren().add(vbox);
} }
} }

View File

@ -1,4 +1,4 @@
.button{ .button{
-fx-background-color: rgb(105,0,153); -fx-background-color: rgb(105,0,153);
-fx-text-fill: white; -fx-text-fill: white;
} }

View File

@ -1,3 +1,3 @@
.button{ .button{
-fx-background-color: snow; -fx-background-color: snow;
} }

View File

@ -26,8 +26,8 @@
<Label fx:id="contactLabel" prefHeight="16.0" prefWidth="250.0" text="Select a contact to chat with" GridPane.columnSpan="2" /> <Label fx:id="contactLabel" prefHeight="16.0" prefWidth="250.0" text="Select a contact to chat with" GridPane.columnSpan="2" />
<Button fx:id="settingsButton" mnemonicParsing="true" onAction="#settingsButtonClicked" text="_Settings" GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.valignment="CENTER" /> <Button fx:id="settingsButton" mnemonicParsing="true" onAction="#settingsButtonClicked" text="_Settings" GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.valignment="CENTER" />
<ListView fx:id="messageList" prefHeight="257.0" prefWidth="155.0" GridPane.columnIndex="1" GridPane.columnSpan="2" GridPane.rowIndex="1" GridPane.rowSpan="2" /> <ListView fx:id="messageList" prefHeight="257.0" prefWidth="155.0" GridPane.columnIndex="1" GridPane.columnSpan="2" GridPane.rowIndex="1" GridPane.rowSpan="2" />
<Button fx:id="postButton" defaultButton="true" disable="true" mnemonicParsing="true" onAction="#postButtonClicked" prefHeight="10.0" prefWidth="65.0" text="_Post" GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.rowIndex="3" GridPane.valignment="CENTER" /> <Button fx:id="postButton" defaultButton="true" disable="true" mnemonicParsing="true" onAction="#postMessage" prefHeight="10.0" prefWidth="65.0" text="_Post" GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.rowIndex="3" GridPane.valignment="CENTER" />
<TextArea fx:id="messageTextArea" disable="true" onInputMethodTextChanged="#messageTextUpdated" onKeyPressed="#messageTextUpdated" onKeyTyped="#messageTextUpdated" prefHeight="200.0" prefWidth="200.0" wrapText="true" GridPane.columnIndex="1" GridPane.rowIndex="3" /> <TextArea fx:id="messageTextArea" disable="true" onInputMethodTextChanged="#messageTextUpdated" onKeyPressed="#checkKeyCombination" onKeyTyped="#messageTextUpdated" prefHeight="200.0" prefWidth="200.0" wrapText="true" GridPane.columnIndex="1" GridPane.rowIndex="3" />
<Button mnemonicParsing="true" onAction="#addContactButtonClicked" text="_Add Contacts" GridPane.halignment="CENTER" GridPane.rowIndex="3" GridPane.valignment="CENTER"> <Button mnemonicParsing="true" onAction="#addContactButtonClicked" text="_Add Contacts" GridPane.halignment="CENTER" GridPane.rowIndex="3" GridPane.valignment="CENTER">
<padding> <padding>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" /> <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />