Merge pull request #186 from informatik-ag-ngl/f/displayUnreadMessages

Displaying unread messages amount
This commit is contained in:
Kai S. K. Engelbart 2020-07-13 09:02:44 +00:00 committed by GitHub
commit 540dad79eb
12 changed files with 158 additions and 89 deletions

View File

@ -29,6 +29,8 @@ public class Chat implements Serializable {
protected final Contact recipient; protected final Contact recipient;
protected final List<Message> messages = new ArrayList<>(); protected final List<Message> messages = new ArrayList<>();
protected int unreadAmount;
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
@ -87,6 +89,7 @@ public class Chat implements Serializable {
writeProxy.writeMessageStatusChange(new MessageStatusChange(m)); writeProxy.writeMessageStatusChange(new MessageStatusChange(m));
} }
} }
unreadAmount = 0;
} }
/** /**
@ -111,6 +114,19 @@ public class Chat implements Serializable {
messages.add(0, message); messages.add(0, message);
} }
/**
* Increments the amount of unread messages.
*
* @since Envoy Client v0.1-beta
*/
public void incrementUnreadAmount() { unreadAmount++; }
/**
* @return the amount of unread mesages in this chat
* @since Envoy Client v0.1-beta
*/
public int getUnreadAmount() { return unreadAmount; }
/** /**
* @return all messages in the current chat * @return all messages in the current chat
* @since Envoy Client v0.1-beta * @since Envoy Client v0.1-beta

View File

@ -50,5 +50,6 @@ public class GroupChat extends Chat {
} }
} }
} }
unreadAmount = 0;
} }
} }

View File

@ -9,7 +9,6 @@ import java.nio.file.Files;
import java.util.Random; import java.util.Random;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.stream.Collectors;
import javafx.animation.RotateTransition; import javafx.animation.RotateTransition;
import javafx.application.Platform; import javafx.application.Platform;
@ -65,7 +64,7 @@ public final class ChatScene implements Restorable {
private ListView<Message> messageList; private ListView<Message> messageList;
@FXML @FXML
private ListView<Contact> userList; private ListView<Chat> chatList;
@FXML @FXML
private Button postButton; private Button postButton;
@ -126,7 +125,7 @@ public final class ChatScene implements Restorable {
// Initialize message and user rendering // Initialize message and user rendering
messageList.setCellFactory(MessageListCellFactory::new); messageList.setCellFactory(MessageListCellFactory::new);
userList.setCellFactory(ContactListCellFactory::new); chatList.setCellFactory(ContactListCellFactory::new);
settingsButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("settings", DEFAULT_ICON_SIZE))); settingsButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("settings", DEFAULT_ICON_SIZE)));
voiceButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("microphone", DEFAULT_ICON_SIZE))); voiceButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("microphone", DEFAULT_ICON_SIZE)));
@ -146,7 +145,15 @@ public final class ChatScene implements Restorable {
logger.log(Level.WARNING, "Could not read current chat: ", e1); logger.log(Level.WARNING, "Could not read current chat: ", e1);
} }
Platform.runLater(() -> { messageList.refresh(); scrollToMessageListEnd(); }); Platform.runLater(() -> { messageList.refresh(); scrollToMessageListEnd(); });
} } else chat.incrementUnreadAmount();
// Moving chat with most recent unreadMessages to the top
Platform.runLater(() -> {
chatList.getItems().remove(chat);
chatList.getItems().add(0, chat);
if (chat.equals(currentChat)) chatList.getSelectionModel().select(0);
localDB.getChats().remove(chat);
localDB.getChats().add(0, chat);
});
}); });
}); });
@ -166,11 +173,12 @@ public final class ChatScene implements Restorable {
// Listen to user status changes // Listen to user status changes
eventBus.register(UserStatusChange.class, eventBus.register(UserStatusChange.class,
e -> userList.getItems() e -> chatList.getItems()
.stream() .stream()
.filter(c -> c.getID() == e.getID()) .filter(c -> c.getRecipient().getID() == e.getID())
.findAny() .findAny()
.ifPresent(u -> { ((User) u).setStatus(e.get()); Platform.runLater(userList::refresh); })); .map(Chat::getRecipient)
.ifPresent(u -> { ((User) u).setStatus(e.get()); Platform.runLater(chatList::refresh); }));
// Listen to contacts changes // Listen to contacts changes
eventBus.register(ContactOperation.class, e -> { eventBus.register(ContactOperation.class, e -> {
@ -178,13 +186,14 @@ public final class ChatScene implements Restorable {
switch (e.getOperationType()) { switch (e.getOperationType()) {
case ADD: case ADD:
localDB.getUsers().put(contact.getName(), contact); localDB.getUsers().put(contact.getName(), contact);
localDB.getChats().add(contact instanceof User ? new Chat(contact) : new GroupChat(client.getSender(), contact)); Chat chat = contact instanceof User ? new Chat(contact) : new GroupChat(client.getSender(), contact);
Platform.runLater(() -> userList.getItems().add(contact)); localDB.getChats().add(chat);
Platform.runLater(() -> chatList.getItems().add(chat));
break; break;
case REMOVE: case REMOVE:
localDB.getUsers().remove(contact.getName()); localDB.getUsers().remove(contact.getName());
localDB.getChats().removeIf(c -> c.getRecipient().getID() == contact.getID()); localDB.getChats().removeIf(c -> c.getRecipient().getID() == contact.getID());
Platform.runLater(() -> userList.getItems().removeIf(c -> c.getID() == contact.getID())); Platform.runLater(() -> chatList.getItems().removeIf(c -> c.getRecipient().getID() == contact.getID()));
break; break;
} }
}); });
@ -206,7 +215,7 @@ public final class ChatScene implements Restorable {
this.client = client; this.client = client;
this.writeProxy = writeProxy; this.writeProxy = writeProxy;
userList.setItems(FXCollections.observableList(localDB.getChats().stream().map(Chat::getRecipient).collect(Collectors.toList()))); chatList.setItems(FXCollections.observableList(localDB.getChats()));
contactLabel.setText(localDB.getUser().getName()); contactLabel.setText(localDB.getUser().getName());
MessageControl.setUser(localDB.getUser()); MessageControl.setUser(localDB.getUser());
if (!client.isOnline()) updateInfoLabel("You are offline", "infoLabel-info"); if (!client.isOnline()) updateInfoLabel("You are offline", "infoLabel-info");
@ -223,8 +232,8 @@ public final class ChatScene implements Restorable {
* @since Envoy Client v0.1-beta * @since Envoy Client v0.1-beta
*/ */
@FXML @FXML
private void userListClicked() { private void chatListClicked() {
final Contact user = userList.getSelectionModel().getSelectedItem(); final Contact user = chatList.getSelectionModel().getSelectedItem().getRecipient();
if (user != null && (currentChat == null || !user.equals(currentChat.getRecipient()))) { if (user != null && (currentChat == null || !user.equals(currentChat.getRecipient()))) {
// LEON: JFC <===> JAVA FRIED CHICKEN <=/=> Java Foundation Classes // LEON: JFC <===> JAVA FRIED CHICKEN <=/=> Java Foundation Classes
@ -233,7 +242,7 @@ public final class ChatScene implements Restorable {
currentChat = localDB.getChat(user.getID()).get(); currentChat = localDB.getChat(user.getID()).get();
messageList.setItems(FXCollections.observableList(currentChat.getMessages())); messageList.setItems(FXCollections.observableList(currentChat.getMessages()));
final var scrollIndex = messageList.getItems().size() - 1; final var scrollIndex = messageList.getItems().size() - currentChat.getUnreadAmount() - 1;
messageList.scrollTo(scrollIndex); messageList.scrollTo(scrollIndex);
logger.log(Level.FINEST, "Loading chat with " + user + " at index " + scrollIndex); logger.log(Level.FINEST, "Loading chat with " + user + " at index " + scrollIndex);
deleteContactMenuItem.setText("Delete " + user.getName()); deleteContactMenuItem.setText("Delete " + user.getName());
@ -260,6 +269,7 @@ public final class ChatScene implements Restorable {
messageTextArea.setDisable(currentChat == null || postingPermanentlyDisabled); messageTextArea.setDisable(currentChat == null || postingPermanentlyDisabled);
voiceButton.setDisable(!recorder.isSupported()); voiceButton.setDisable(!recorder.isSupported());
attachmentButton.setDisable(false); attachmentButton.setDisable(false);
chatList.refresh();
} }
/** /**
@ -483,6 +493,14 @@ public final class ChatScene implements Restorable {
// Add message to LocalDB and update UI // Add message to LocalDB and update UI
currentChat.insert(message); currentChat.insert(message);
// Moving currentChat to the top
Platform.runLater(() -> {
chatList.getItems().remove(currentChat);
chatList.getItems().add(0, currentChat);
chatList.getSelectionModel().select(0);
localDB.getChats().remove(currentChat);
localDB.getChats().add(0, currentChat);
});
messageList.refresh(); messageList.refresh();
scrollToMessageListEnd(); scrollToMessageListEnd();

View File

@ -2,6 +2,7 @@ package envoy.client.ui.controller;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import java.util.stream.Collectors;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.fxml.FXML; import javafx.fxml.FXML;
@ -10,12 +11,12 @@ import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.ButtonType; import javafx.scene.control.ButtonType;
import javafx.scene.control.ListView; import javafx.scene.control.ListView;
import envoy.client.data.Chat;
import envoy.client.data.LocalDB; import envoy.client.data.LocalDB;
import envoy.client.event.SendEvent; import envoy.client.event.SendEvent;
import envoy.client.ui.ClearableTextField; import envoy.client.ui.ClearableTextField;
import envoy.client.ui.SceneContext; import envoy.client.ui.SceneContext;
import envoy.client.ui.listcell.ContactListCellFactory; import envoy.client.ui.listcell.ContactListCellFactory;
import envoy.data.Contact;
import envoy.event.ElementOperation; import envoy.event.ElementOperation;
import envoy.event.EventBus; import envoy.event.EventBus;
import envoy.event.contact.ContactOperation; import envoy.event.contact.ContactOperation;
@ -37,7 +38,7 @@ public class ContactSearchScene {
private ClearableTextField searchBar; private ClearableTextField searchBar;
@FXML @FXML
private ListView<Contact> contactList; private ListView<Chat> chatList;
private SceneContext sceneContext; private SceneContext sceneContext;
@ -58,10 +59,13 @@ public class ContactSearchScene {
@FXML @FXML
private void initialize() { private void initialize() {
contactList.setCellFactory(ContactListCellFactory::new); chatList.setCellFactory(ContactListCellFactory::new);
searchBar.setClearButtonListener(e -> { searchBar.getTextField().clear(); contactList.getItems().clear(); }); searchBar.setClearButtonListener(e -> { searchBar.getTextField().clear(); chatList.getItems().clear(); });
eventBus.register(ContactSearchResult.class, eventBus.register(ContactSearchResult.class,
response -> Platform.runLater(() -> { contactList.getItems().clear(); contactList.getItems().addAll(response.get()); })); response -> Platform.runLater(() -> {
chatList.getItems().clear();
chatList.getItems().addAll(response.get().stream().map(Chat::new).collect(Collectors.toList()));
}));
} }
/** /**
@ -73,7 +77,7 @@ public class ContactSearchScene {
private void sendRequest() { private void sendRequest() {
final var text = searchBar.getTextField().getText().strip(); final var text = searchBar.getTextField().getText().strip();
if (!text.isBlank()) eventBus.dispatch(new SendEvent(new ContactSearchRequest(text))); if (!text.isBlank()) eventBus.dispatch(new SendEvent(new ContactSearchRequest(text)));
else contactList.getItems().clear(); else chatList.getItems().clear();
} }
/** /**
@ -85,7 +89,7 @@ public class ContactSearchScene {
@FXML @FXML
private void clear() { private void clear() {
searchBar.getTextField().setText(null); searchBar.getTextField().setText(null);
contactList.getItems().clear(); chatList.getItems().clear();
} }
/** /**
@ -95,22 +99,22 @@ public class ContactSearchScene {
* @since Envoy Client v0.1-beta * @since Envoy Client v0.1-beta
*/ */
@FXML @FXML
private void contactListClicked() { private void chatListClicked() {
final var contact = contactList.getSelectionModel().getSelectedItem(); final var chat = chatList.getSelectionModel().getSelectedItem();
if (contact != null) { if (chat != null) {
final var alert = new Alert(AlertType.CONFIRMATION); final var alert = new Alert(AlertType.CONFIRMATION);
alert.setTitle("Add Contact to Contact List"); alert.setTitle("Add Contact to Contact List");
alert.setHeaderText("Add the user " + contact.getName() + " to your contact list?"); alert.setHeaderText("Add the user " + chat.getRecipient().getName() + " to your contact list?");
// Normally, this would be total BS (we are already on the FX Thread), however // Normally, this would be total BS (we are already on the FX Thread), however
// it could be proven that the creation of this dialog wrapped in // it could be proven that the creation of this dialog wrapped in
// Platform.runLater is less error-prone than without it // Platform.runLater is less error-prone than without it
Platform.runLater(() -> alert.showAndWait().filter(btn -> btn == ButtonType.OK).ifPresent(btn -> { Platform.runLater(() -> alert.showAndWait().filter(btn -> btn == ButtonType.OK).ifPresent(btn -> {
final var event = new ContactOperation(contact, ElementOperation.ADD); final var event = new ContactOperation(chat.getRecipient(), ElementOperation.ADD);
// Sends the event to the server // Sends the event to the server
eventBus.dispatch(new SendEvent(event)); eventBus.dispatch(new SendEvent(event));
// Updates the UI // Updates the UI
eventBus.dispatch(event); eventBus.dispatch(event);
logger.log(Level.INFO, "Added contact " + contact); logger.log(Level.INFO, "Added contact " + chat.getRecipient());
})); }));
} }
} }

View File

@ -7,12 +7,13 @@ import javafx.fxml.FXML;
import javafx.scene.control.*; import javafx.scene.control.*;
import javafx.scene.control.Alert.AlertType; import javafx.scene.control.Alert.AlertType;
import envoy.client.data.Chat;
import envoy.client.data.LocalDB; import envoy.client.data.LocalDB;
import envoy.client.event.SendEvent; import envoy.client.event.SendEvent;
import envoy.client.ui.ClearableTextField; import envoy.client.ui.ClearableTextField;
import envoy.client.ui.SceneContext; import envoy.client.ui.SceneContext;
import envoy.client.ui.listcell.ContactListCellFactory; import envoy.client.ui.listcell.ContactListCellFactory;
import envoy.data.Contact; import envoy.data.Group;
import envoy.event.EventBus; import envoy.event.EventBus;
import envoy.event.GroupCreation; import envoy.event.GroupCreation;
import envoy.util.Bounds; import envoy.util.Bounds;
@ -34,7 +35,7 @@ public class GroupCreationScene {
private ClearableTextField groupNameField; private ClearableTextField groupNameField;
@FXML @FXML
private ListView<Contact> contactList; private ListView<Chat> chatList;
private SceneContext sceneContext; private SceneContext sceneContext;
@ -42,8 +43,8 @@ public class GroupCreationScene {
@FXML @FXML
private void initialize() { private void initialize() {
contactList.setCellFactory(ContactListCellFactory::new); chatList.setCellFactory(ContactListCellFactory::new);
contactList.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); chatList.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
groupNameField.setClearButtonListener(e -> { groupNameField.getTextField().clear(); createButton.setDisable(true); }); groupNameField.setClearButtonListener(e -> { groupNameField.getTextField().clear(); createButton.setDisable(true); });
} }
@ -55,8 +56,12 @@ public class GroupCreationScene {
*/ */
public void initializeData(SceneContext sceneContext, LocalDB localDB) { public void initializeData(SceneContext sceneContext, LocalDB localDB) {
this.sceneContext = sceneContext; this.sceneContext = sceneContext;
Platform.runLater(() -> contactList.getItems() Platform.runLater(() -> chatList.getItems()
.addAll(localDB.getUsers().values().stream().filter(c -> c.getID() != localDB.getUser().getID()).collect(Collectors.toList()))); .addAll(localDB.getChats()
.stream()
.filter(c -> !(c.getRecipient() instanceof Group))
.filter(c -> c.getRecipient().getID() != localDB.getUser().getID())
.collect(Collectors.toList())));
} }
/** /**
@ -65,8 +70,8 @@ public class GroupCreationScene {
* @since Envoy Client v0.1-beta * @since Envoy Client v0.1-beta
*/ */
@FXML @FXML
private void contactListClicked() { private void chatListClicked() {
createButton.setDisable(contactList.getSelectionModel().isEmpty() || groupNameField.getTextField().getText().isBlank()); createButton.setDisable(chatList.getSelectionModel().isEmpty() || groupNameField.getTextField().getText().isBlank());
} }
/** /**
@ -93,7 +98,7 @@ public class GroupCreationScene {
groupNameField.getTextField().clear(); groupNameField.getTextField().clear();
} else { } else {
eventBus.dispatch(new SendEvent(new GroupCreation(name, eventBus.dispatch(new SendEvent(new GroupCreation(name,
contactList.getSelectionModel().getSelectedItems().stream().map(Contact::getID).collect(Collectors.toSet())))); chatList.getSelectionModel().getSelectedItems().stream().map(c -> c.getRecipient().getID()).collect(Collectors.toSet()))));
new Alert(AlertType.INFORMATION, String.format("Group '%s' successfully created.", name)).showAndWait(); new Alert(AlertType.INFORMATION, String.format("Group '%s' successfully created.", name)).showAndWait();
sceneContext.pop(); sceneContext.pop();
} }

View File

@ -0,0 +1,58 @@
package envoy.client.ui.listcell;
import javafx.geometry.Pos;
import javafx.scene.control.Label;
import javafx.scene.layout.*;
import envoy.client.data.Chat;
import envoy.data.Contact;
import envoy.data.Group;
import envoy.data.User;
/**
* This class formats a single {@link Contact} into a UI component.
* <p>
* Project: <strong>envoy-client</strong><br>
* File: <strong>ContactControl.java</strong><br>
* Created: <strong>01.07.2020</strong><br>
*
* @author Leon Hofmeister
* @since Envoy Client v0.1-beta
*/
public class ChatControl extends HBox {
/**
* @param chat the chat to display
* @since Envoy Client v0.1-beta
*/
public ChatControl(Chat chat) {
// Container with contact name
final var vBox = new VBox();
final var nameLabel = new Label(chat.getRecipient().getName());
nameLabel.setWrapText(true);
vBox.getChildren().add(nameLabel);
if (chat.getRecipient() instanceof User) {
// Online status
final var user = (User) chat.getRecipient();
final var statusLabel = new Label(user.getStatus().toString());
statusLabel.getStyleClass().add(user.getStatus().toString().toLowerCase());
vBox.getChildren().add(statusLabel);
} else // Member count
vBox.getChildren().add(new Label(((Group) chat.getRecipient()).getContacts().size() + " members"));
getChildren().add(vBox);
if (chat.getUnreadAmount() != 0) {
Region spacing = new Region();
setHgrow(spacing, Priority.ALWAYS);
getChildren().add(spacing);
final var unreadMessagesLabel = new Label(Integer.toString(chat.getUnreadAmount()));
unreadMessagesLabel.setMinSize(15, 15);
var vBox2 = new VBox();
vBox2.setAlignment(Pos.CENTER_RIGHT);
unreadMessagesLabel.setAlignment(Pos.CENTER);
unreadMessagesLabel.getStyleClass().add("unreadMessagesAmount");
vBox2.getChildren().add(unreadMessagesLabel);
getChildren().add(vBox2);
}
}
}

View File

@ -1,40 +0,0 @@
package envoy.client.ui.listcell;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import envoy.data.Contact;
import envoy.data.Group;
import envoy.data.User;
/**
* This class formats a single {@link Contact} into a UI component.
* <p>
* Project: <strong>envoy-client</strong><br>
* File: <strong>ContactControl.java</strong><br>
* Created: <strong>01.07.2020</strong><br>
*
* @author Leon Hofmeister
* @since Envoy Client v0.1-beta
*/
public class ContactControl extends VBox {
/**
* @param contact the contact that should be formatted
* @since Envoy Client v0.1-beta
*/
public ContactControl(Contact contact) {
// Container with contact name
final var nameLabel = new Label(contact.getName());
nameLabel.setWrapText(true);
getChildren().add(nameLabel);
if (contact instanceof User) {
// Online status
final var user = (User) contact;
final var statusLabel = new Label(user.getStatus().toString());
statusLabel.getStyleClass().add(user.getStatus().toString().toLowerCase());
getChildren().add(statusLabel);
} else // Member count
getChildren().add(new Label(((Group) contact).getContacts().size() + " members"));
}
}

View File

@ -3,7 +3,7 @@ package envoy.client.ui.listcell;
import javafx.scene.control.ListCell; import javafx.scene.control.ListCell;
import javafx.scene.control.ListView; import javafx.scene.control.ListView;
import envoy.data.Contact; import envoy.client.data.Chat;
/** /**
* Project: <strong>envoy-client</strong><br> * Project: <strong>envoy-client</strong><br>
@ -13,15 +13,15 @@ import envoy.data.Contact;
* @author Kai S. K. Engelbart * @author Kai S. K. Engelbart
* @since Envoy Client v0.1-beta * @since Envoy Client v0.1-beta
*/ */
public class ContactListCellFactory extends ListCell<Contact> { public class ContactListCellFactory extends ListCell<Chat> {
private final ListView<Contact> listView; private final ListView<Chat> listView;
/** /**
* @param listView the list view inside which this cell is contained * @param listView the list view inside which this cell is contained
* @since Envoy Client v0.1-beta * @since Envoy Client v0.1-beta
*/ */
public ContactListCellFactory(ListView<Contact> listView) { this.listView = listView; } public ContactListCellFactory(ListView<Chat> listView) { this.listView = listView; }
/** /**
* 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
@ -30,13 +30,13 @@ public class ContactListCellFactory extends ListCell<Contact> {
* @since Envoy Client v0.1-beta * @since Envoy Client v0.1-beta
*/ */
@Override @Override
protected void updateItem(Contact contact, boolean empty) { protected void updateItem(Chat chat, boolean empty) {
super.updateItem(contact, empty); super.updateItem(chat, empty);
if (empty || contact == null) { if (empty || chat.getRecipient() == null) {
setText(null); setText(null);
setGraphic(null); setGraphic(null);
} else { } else {
final var control = new ContactControl(contact); final var control = new ChatControl(chat);
prefWidthProperty().bind(listView.widthProperty().subtract(40)); prefWidthProperty().bind(listView.widthProperty().subtract(40));
setGraphic(control); setGraphic(control);
} }

View File

@ -58,6 +58,13 @@
-fx-text-alignment: left; -fx-text-alignment: left;
} }
.unreadMessagesAmount {
-fx-alignment: center;
-fx-background-color: orange;
-fx-background-radius: 4.0em;
-fx-text-alignment: center;
}
#remainingCharsLabel { #remainingCharsLabel {
-fx-text-fill: #00FF00; -fx-text-fill: #00FF00;
-fx-background-color: transparent; -fx-background-color: transparent;

View File

@ -45,7 +45,7 @@
minHeight="-Infinity" prefHeight="40.0" vgrow="NEVER" /> minHeight="-Infinity" prefHeight="40.0" vgrow="NEVER" />
</rowConstraints> </rowConstraints>
<children> <children>
<ListView fx:id="userList" onMouseClicked="#userListClicked" <ListView fx:id="chatList" onMouseClicked="#chatListClicked"
prefHeight="211.0" prefWidth="300.0" GridPane.rowIndex="1" prefHeight="211.0" prefWidth="300.0" GridPane.rowIndex="1"
GridPane.rowSpan="2147483647"> GridPane.rowSpan="2147483647">
<GridPane.margin> <GridPane.margin>

View File

@ -46,8 +46,8 @@
</Button> </Button>
</children> </children>
</HBox> </HBox>
<ListView fx:id="contactList" <ListView fx:id="chatList"
onMouseClicked="#contactListClicked" prefHeight="314.0" onMouseClicked="#chatListClicked" prefHeight="314.0"
prefWidth="600.0"> prefWidth="600.0">
<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" />

View File

@ -45,8 +45,8 @@
<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" />
</padding> </padding>
</Label> </Label>
<ListView fx:id="contactList" <ListView fx:id="chatList"
onMouseClicked="#contactListClicked" prefHeight="314.0" onMouseClicked="#chatListClicked" prefHeight="314.0"
prefWidth="600.0"> prefWidth="600.0">
<VBox.margin> <VBox.margin>
<Insets bottom="5.0" left="10.0" right="10.0" top="5.0" /> <Insets bottom="5.0" left="10.0" right="10.0" top="5.0" />