diff --git a/src/main/java/envoy/client/ui/ClearableTextField.java b/src/main/java/envoy/client/ui/ClearableTextField.java new file mode 100644 index 0000000..f19155b --- /dev/null +++ b/src/main/java/envoy/client/ui/ClearableTextField.java @@ -0,0 +1,174 @@ +package envoy.client.ui; + +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.StringProperty; +import javafx.event.ActionEvent; +import javafx.event.EventHandler; +import javafx.scene.control.*; +import javafx.scene.image.ImageView; +import javafx.scene.layout.Background; +import javafx.scene.layout.ColumnConstraints; +import javafx.scene.layout.GridPane; + +import envoy.client.data.Settings; + +/** + * This class offers a text field that is automatically equipped with a clear + * button. + *

+ * Project: envoy-client
+ * File: ClearableTextField.java
+ * Created: 25.06.2020
+ * + * @author Leon Hofmeister + * @since Envoy Client v0.1-beta + */ +public class ClearableTextField extends GridPane { + + private final TextField textField; + + private final Button clearButton; + + /** + * Constructs a new {@code ClearableTextField} with no initial text and icon + * size 16. + * + * @since Envoy Client v0.1-beta + */ + public ClearableTextField() { this("", 16); } + + /** + * Constructs a new {@code ClearableTextField} with initial text and a + * predetermined icon size. + * + * @param text the text that should be displayed by default + * @param size the size of the icon + * @since Envoy Client v0.1-beta + */ + public ClearableTextField(String text, int size) { + // initializing the textField and the button + textField = new TextField(text); + clearButton = new Button("", + new ImageView(IconUtil.load( + Settings.getInstance().getCurrentTheme().equals("dark") ? "/icons/clear_button_white.png" : "/icons/clear_button_black.png", + size))); + clearButton.setOnAction(e -> textField.clear()); + clearButton.setFocusTraversable(false); + clearButton.getStyleClass().clear(); + clearButton.setBackground(Background.EMPTY); + // Adding the two elements to the GridPane + add(textField, 0, 0, 2, 1); + add(clearButton, 1, 0, 1, 1); + // Setting the percent - widths of the two columns. + // Used to locate the button on the right. + final var columnConstraints = new ColumnConstraints(); + columnConstraints.setPercentWidth(90); + getColumnConstraints().add(columnConstraints); + final var columnConstraints2 = new ColumnConstraints(); + columnConstraints2.setPercentWidth(10); + getColumnConstraints().add(columnConstraints2); + } + + /** + * @return the underlying {@code textField} + * @since Envoy Client v0.1-beta + */ + public TextField getTextField() { return textField; } + + /** + * This method offers the freedom to perform custom actions when the + * {@code clearButton} has been pressed. + *

+ * The default is + * e -> {clearableTextField.getTextField().clear();} + * + * @param onClearButtonAction the action that should be performed + * @since Envoy Client v0.1-beta + */ + public void setClearButtonListener(EventHandler onClearButtonAction) { clearButton.setOnAction(onClearButtonAction); } + + /** + * @return the current property of the prompt text + * @see javafx.scene.control.TextInputControl#promptTextProperty() + * @since Envoy Client v0.1-beta + */ + public final StringProperty promptTextProperty() { return textField.promptTextProperty(); } + + /** + * @return the current prompt text + * @see javafx.scene.control.TextInputControl#getPromptText() + * @since Envoy Client v0.1-beta + */ + public final String getPromptText() { return textField.getPromptText(); } + + /** + * @param value the prompt text to display + * @see javafx.scene.control.TextInputControl#setPromptText(java.lang.String) + * @since Envoy Client v0.1-beta + */ + public final void setPromptText(String value) { textField.setPromptText(value); } + + /** + * @return the current property of the tooltip + * @see javafx.scene.control.Control#tooltipProperty() + * @since Envoy Client v0.1-beta + */ + public final ObjectProperty tooltipProperty() { return textField.tooltipProperty(); } + + /** + * @param value the new tooltip + * @see javafx.scene.control.Control#setTooltip(javafx.scene.control.Tooltip) + * @since Envoy Client v0.1-beta + */ + public final void setTooltip(Tooltip value) { textField.setTooltip(value); } + + /** + * @return the current tooltip + * @see javafx.scene.control.Control#getTooltip() + * @since Envoy Client v0.1-beta + */ + public final Tooltip getTooltip() { return textField.getTooltip(); } + + /** + * @return the current property of the context menu + * @see javafx.scene.control.Control#contextMenuProperty() + * @since Envoy Client v0.1-beta + */ + public final ObjectProperty contextMenuProperty() { return textField.contextMenuProperty(); } + + /** + * @param value the new context menu + * @see javafx.scene.control.Control#setContextMenu(javafx.scene.control.ContextMenu) + * @since Envoy Client v0.1-beta + */ + public final void setContextMenu(ContextMenu value) { textField.setContextMenu(value); } + + /** + * @return the current context menu + * @see javafx.scene.control.Control#getContextMenu() + * @since Envoy Client v0.1-beta + */ + public final ContextMenu getContextMenu() { return textField.getContextMenu(); } + + /** + * @param value whether this ClearableTextField should be editable + * @see javafx.scene.control.TextInputControl#setEditable(boolean) + * @since Envoy Client v0.1-beta + */ + public final void setEditable(boolean value) { textField.setEditable(value); } + + /** + * @return the current property whether this ClearableTextField is editable + * @see javafx.scene.control.TextInputControl#editableProperty() + * @since Envoy Client v0.1-beta + */ + public final BooleanProperty editableProperty() { return textField.editableProperty(); } + + /** + * @return whether this {@code ClearableTextField} is editable + * @see javafx.scene.control.TextInputControl#isEditable() + * @since Envoy Client v0.1-beta + */ + public final boolean isEditable() { return textField.isEditable(); } +} diff --git a/src/main/java/envoy/client/ui/MessageListCell.java b/src/main/java/envoy/client/ui/MessageListCell.java deleted file mode 100644 index e33c502..0000000 --- a/src/main/java/envoy/client/ui/MessageListCell.java +++ /dev/null @@ -1,82 +0,0 @@ -package envoy.client.ui; - -import java.time.format.DateTimeFormatter; -import java.util.Map; - -import javafx.geometry.Insets; -import javafx.scene.control.*; -import javafx.scene.image.Image; -import javafx.scene.image.ImageView; -import javafx.scene.layout.VBox; -import javafx.stage.PopupWindow.AnchorLocation; - -import envoy.data.Message; -import envoy.data.Message.MessageStatus; -import envoy.data.User; - -/** - * Displays a single message inside the message list. - *

- * Project: envoy-client
- * File: MessageListCell.java
- * Created: 28.03.2020
- * - * @author Kai S. K. Engelbart - * @since Envoy Client v0.1-beta - */ -public class MessageListCell extends ListCell { - - private final ListView listView; - - private static User client; - private static final DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm"); - private static final Map statusImages = IconUtil.loadByEnum(MessageStatus.class, 16); - - /** - * @param listView the list view inside which this cell is contained - * @since Envoy Client v0.1-beta - */ - public MessageListCell(ListView listView) { this.listView = listView; } - - /** - * Displays the text, the data of creation and the status of a message. - * - * @since Envoy v0.1-beta - */ - @Override - protected void updateItem(Message message, boolean empty) { - super.updateItem(message, empty); - if (empty || message == null) { - setText(null); - setGraphic(null); - } else { - // Creating the underlying VBox, the dateLabel and the textLabel - final var cell = new VBox(new Label(dateFormat.format(message.getCreationDate()))); - final var textLabel = new Label(message.getText()); - textLabel.setWrapText(true); - cell.getChildren().add(textLabel); - // Setting the message status icon and background color - if (message.getRecipientID() != client.getID()) { - final var statusIcon = new Label("", new ImageView(statusImages.get(message.getStatus()))); - statusIcon.setPadding(new Insets(1, 0, 5, 5)); - cell.getChildren().add(statusIcon); - cell.getStyleClass().add("own-message"); - } else cell.getStyleClass().add("received-message"); - // Adjusting height and weight of the cell to the corresponding ListView - cell.paddingProperty().setValue(new Insets(5, 20, 5, 20)); - cell.prefWidthProperty().bind(listView.widthProperty().subtract(40)); - // Creating the Tooltip to deselect a message - final var tooltip = new Tooltip("You can select a message by clicking on it \nand deselect it by pressing \"ctrl\" and clicking on it"); - tooltip.setWrapText(true); - tooltip.setAnchorLocation(AnchorLocation.WINDOW_TOP_LEFT); - setTooltip(tooltip); - setGraphic(cell); - } - } - - /** - * @param client the user who chats with another person - * @since Envoy Client v0.1-beta - */ - public static void setUser(User client) { MessageListCell.client = client; } -} diff --git a/src/main/java/envoy/client/ui/SceneContext.java b/src/main/java/envoy/client/ui/SceneContext.java index 52c847b..6b6757e 100644 --- a/src/main/java/envoy/client/ui/SceneContext.java +++ b/src/main/java/envoy/client/ui/SceneContext.java @@ -40,21 +40,21 @@ public final class SceneContext { public enum SceneInfo { /** - * The main scene in which chats are displayed. + * The main scene in which the chat screen is displayed. * * @since Envoy Client v0.1-beta */ CHAT_SCENE("/fxml/ChatScene.fxml"), /** - * The scene in which settings are displayed. + * The scene in which the settings screen is displayed. * * @since Envoy Client v0.1-beta */ SETTINGS_SCENE("/fxml/SettingsScene.fxml"), /** - * The scene in which the contact search is displayed. + * The scene in which the contact search screen is displayed. * * @since Envoy Client v0.1-beta */ @@ -72,7 +72,14 @@ public final class SceneContext { * * @since Envoy Client v0.1-beta */ - LOGIN_SCENE("/fxml/LoginScene.fxml"); + LOGIN_SCENE("/fxml/LoginScene.fxml"), + + /** + * The scene in which the info screen is displayed. + * + * @since Envoy Client v0.1-beta + */ + MESSAGE_INFO_SCENE("/fxml/MessageInfoScene.fxml"); /** * The path to the FXML resource. diff --git a/src/main/java/envoy/client/ui/controller/ChatScene.java b/src/main/java/envoy/client/ui/controller/ChatScene.java index 38f3aa1..ed35cda 100644 --- a/src/main/java/envoy/client/ui/controller/ChatScene.java +++ b/src/main/java/envoy/client/ui/controller/ChatScene.java @@ -16,7 +16,6 @@ import javafx.scene.image.ImageView; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; import javafx.scene.paint.Color; -import javafx.scene.paint.Paint; import envoy.client.data.Chat; import envoy.client.data.LocalDB; @@ -24,7 +23,11 @@ import envoy.client.data.Settings; import envoy.client.event.MessageCreationEvent; import envoy.client.net.Client; import envoy.client.net.WriteProxy; -import envoy.client.ui.*; +import envoy.client.ui.IconUtil; +import envoy.client.ui.SceneContext; +import envoy.client.ui.listcell.ContactListCellFactory; +import envoy.client.ui.listcell.MessageControl; +import envoy.client.ui.listcell.MessageListCellFactory; import envoy.data.*; import envoy.event.EventBus; import envoy.event.MessageStatusChange; @@ -92,8 +95,8 @@ public final class ChatScene { private void initialize() { // Initialize message and user rendering - messageList.setCellFactory(MessageListCell::new); - userList.setCellFactory(ContactListCell::new); + messageList.setCellFactory(MessageListCellFactory::new); + userList.setCellFactory(ContactListCellFactory::new); settingsButton.setGraphic(new ImageView(IconUtil.load("/icons/settings.png", 16))); @@ -166,8 +169,8 @@ public final class ChatScene { userList.setItems(FXCollections.observableList(localDB.getChats().stream().map(Chat::getRecipient).collect(Collectors.toList()))); contactLabel.setText(localDB.getUser().getName()); - MessageListCell.setUser(localDB.getUser()); - if (!client.isOnline()) updateInfoLabel("You are offline", Color.YELLOW); + MessageControl.setUser(localDB.getUser()); + if (!client.isOnline()) updateInfoLabel("You are offline", "infoLabel-info"); } /** @@ -179,7 +182,6 @@ public final class ChatScene { private void userListClicked() { final Contact user = userList.getSelectionModel().getSelectedItem(); if (user != null && (currentChat == null || !user.equals(currentChat.getRecipient()))) { - logger.log(Level.FINEST, "Loading chat with " + user); // LEON: JFC <===> JAVA FRIED CHICKEN <=/=> Java Foundation Classes @@ -187,6 +189,9 @@ public final class ChatScene { currentChat = localDB.getChat(user.getID()).get(); messageList.setItems(FXCollections.observableList(currentChat.getMessages())); + final var scrollIndex = messageList.getItems().size() - 1; + messageList.scrollTo(scrollIndex); + logger.log(Level.FINEST, "Loading chat with " + user + " at index " + scrollIndex); deleteContactMenuItem.setText("Delete " + user.getName()); // Read the current chat @@ -257,7 +262,7 @@ public final class ChatScene { if (!infoLabel.getText().equals(noMoreMessaging)) // Informing the user that he is a f*cking moron and should use Envoy online // because he ran out of messageIDs to use - updateInfoLabel(noMoreMessaging, Color.RED); + updateInfoLabel(noMoreMessaging, "infoLabel-error"); } } @@ -302,7 +307,7 @@ public final class ChatScene { postButton.setDisable(true); messageTextArea.setDisable(true); messageTextArea.clear(); - updateInfoLabel("You need to go online to send more messages", Color.RED); + updateInfoLabel("You need to go online to send more messages", "infoLabel-error"); return; } final var text = messageTextArea.getText().strip(); @@ -345,13 +350,14 @@ public final class ChatScene { /** * Updates the {@code infoLabel}. * - * @param text the text to use - * @param textfill the color in which to display information + * @param text the text to use + * @param infoLabelID the id the the {@code infoLabel} should have so that it + * can be styled accordingly in CSS * @since Envoy Client v0.1-beta */ - private void updateInfoLabel(String text, Paint textfill) { + private void updateInfoLabel(String text, String infoLabelID) { infoLabel.setText(text); - infoLabel.setTextFill(textfill); + infoLabel.setId(infoLabelID); infoLabel.setVisible(true); } @@ -387,4 +393,7 @@ public final class ChatScene { updateRemainingCharsLabel(); postButton.setDisable(messageText.isBlank()); } + + @FXML + private void loadMessageInfoScene() { try {} catch (final NullPointerException e) {} } } diff --git a/src/main/java/envoy/client/ui/controller/ContactSearchScene.java b/src/main/java/envoy/client/ui/controller/ContactSearchScene.java index 7e70a80..957a52b 100644 --- a/src/main/java/envoy/client/ui/controller/ContactSearchScene.java +++ b/src/main/java/envoy/client/ui/controller/ContactSearchScene.java @@ -5,13 +5,16 @@ import java.util.logging.Logger; import javafx.application.Platform; import javafx.fxml.FXML; -import javafx.scene.control.*; +import javafx.scene.control.Alert; import javafx.scene.control.Alert.AlertType; +import javafx.scene.control.ButtonType; +import javafx.scene.control.ListView; import envoy.client.data.LocalDB; import envoy.client.event.SendEvent; -import envoy.client.ui.ContactListCell; +import envoy.client.ui.ClearableTextField; import envoy.client.ui.SceneContext; +import envoy.client.ui.listcell.ContactListCellFactory; import envoy.data.Contact; import envoy.event.ElementOperation; import envoy.event.EventBus; @@ -31,19 +34,7 @@ import envoy.util.EnvoyLog; public class ContactSearchScene { @FXML - private Button backButton; - - @FXML - private Button clearButton; - - @FXML - private Button searchButton; - - @FXML - private Button newGroupButton; - - @FXML - private TextField searchBar; + private ClearableTextField searchBar; @FXML private ListView contactList; @@ -67,7 +58,8 @@ public class ContactSearchScene { @FXML private void initialize() { - contactList.setCellFactory(ContactListCell::new); + contactList.setCellFactory(ContactListCellFactory::new); + searchBar.setClearButtonListener(e -> { searchBar.getTextField().clear(); contactList.getItems().clear(); }); eventBus.register(ContactSearchResult.class, response -> Platform.runLater(() -> { contactList.getItems().clear(); contactList.getItems().addAll(response.get()); })); } @@ -78,20 +70,12 @@ public class ContactSearchScene { * @since Envoy Client v0.1-beta */ @FXML - private void checkClearButton() { - final var containsContent = searchBar.getText().strip().isEmpty(); - clearButton.setDisable(containsContent); - searchButton.setDisable(containsContent); + private void sendRequest() { + final var text = searchBar.getTextField().getText().strip(); + if (!text.isBlank()) eventBus.dispatch(new SendEvent(new ContactSearchRequest(text))); + else contactList.getItems().clear(); } - /** - * Sends a {@link ContactSearchRequest} to the server. - * - * @since Envoy Client v0.1-beta - */ - @FXML - private void suggestContacts() { eventBus.dispatch(new SendEvent(new ContactSearchRequest(searchBar.getText()))); } - /** * Clears the text in the search bar and the items shown in the list. * Additionally disables both clear and search button. @@ -100,10 +84,8 @@ public class ContactSearchScene { */ @FXML private void clear() { - searchBar.setText(null); + searchBar.getTextField().setText(null); contactList.getItems().clear(); - clearButton.setDisable(true); - searchButton.setDisable(true); } /** diff --git a/src/main/java/envoy/client/ui/controller/GroupCreationScene.java b/src/main/java/envoy/client/ui/controller/GroupCreationScene.java index 9234fa2..76573b8 100644 --- a/src/main/java/envoy/client/ui/controller/GroupCreationScene.java +++ b/src/main/java/envoy/client/ui/controller/GroupCreationScene.java @@ -9,8 +9,9 @@ import javafx.scene.control.Alert.AlertType; import envoy.client.data.LocalDB; import envoy.client.event.SendEvent; -import envoy.client.ui.ContactListCell; +import envoy.client.ui.ClearableTextField; import envoy.client.ui.SceneContext; +import envoy.client.ui.listcell.ContactListCellFactory; import envoy.data.Contact; import envoy.event.EventBus; import envoy.event.GroupCreation; @@ -30,7 +31,7 @@ public class GroupCreationScene { private Button createButton; @FXML - private TextField groupNameField; + private ClearableTextField groupNameField; @FXML private ListView contactList; @@ -41,8 +42,9 @@ public class GroupCreationScene { @FXML private void initialize() { - contactList.setCellFactory(ContactListCell::new); + contactList.setCellFactory(ContactListCellFactory::new); contactList.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); + groupNameField.setClearButtonListener(e -> { groupNameField.getTextField().clear(); createButton.setDisable(true); }); } /** @@ -63,7 +65,18 @@ public class GroupCreationScene { * @since Envoy Client v0.1-beta */ @FXML - private void contactListClicked() { createButton.setDisable(contactList.getSelectionModel().isEmpty()); } + private void contactListClicked() { + createButton.setDisable(contactList.getSelectionModel().isEmpty() || groupNameField.getTextField().getText().isBlank()); + } + + /** + * Checks, whether the {@code createButton} can be enabled because text is + * present in the textfield. + * + * @since Envoy Client v0.1-beta + */ + @FXML + private void textUpdated() { createButton.setDisable(groupNameField.getTextField().getText().isBlank()); } /** * Sends a {@link GroupCreation} to the server and closes this scene. @@ -74,10 +87,10 @@ public class GroupCreationScene { */ @FXML private void createButtonClicked() { - final var name = groupNameField.getText(); + final var name = groupNameField.getTextField().getText(); if (!Bounds.isValidContactName(name)) { new Alert(AlertType.ERROR, "The entered group name is not valid (" + Bounds.CONTACT_NAME_PATTERN + ")").showAndWait(); - groupNameField.clear(); + groupNameField.getTextField().clear(); } else { eventBus.dispatch(new SendEvent(new GroupCreation(name, contactList.getSelectionModel().getSelectedItems().stream().map(Contact::getID).collect(Collectors.toSet())))); diff --git a/src/main/java/envoy/client/ui/controller/LoginScene.java b/src/main/java/envoy/client/ui/controller/LoginScene.java index 3ec2338..5933665 100644 --- a/src/main/java/envoy/client/ui/controller/LoginScene.java +++ b/src/main/java/envoy/client/ui/controller/LoginScene.java @@ -13,6 +13,7 @@ import javafx.scene.control.Alert.AlertType; import envoy.client.data.*; import envoy.client.net.Client; +import envoy.client.ui.ClearableTextField; import envoy.client.ui.SceneContext; import envoy.client.ui.Startup; import envoy.data.LoginCredentials; @@ -38,7 +39,7 @@ import envoy.util.EnvoyLog; public final class LoginScene { @FXML - private TextField userTextField; + private ClearableTextField userTextField; @FXML private PasswordField passwordField; @@ -116,17 +117,17 @@ public final class LoginScene { if (registerCheckBox.isSelected() && !passwordField.getText().equals(repeatPasswordField.getText())) { new Alert(AlertType.ERROR, "The entered password is unequal to the repeated one").showAndWait(); repeatPasswordField.clear(); - } else if (!Bounds.isValidContactName(userTextField.getText())) { + } else if (!Bounds.isValidContactName(userTextField.getTextField().getText())) { new Alert(AlertType.ERROR, "The entered user name is not valid (" + Bounds.CONTACT_NAME_PATTERN + ")").showAndWait(); - userTextField.clear(); - } else - performHandshake( - new LoginCredentials(userTextField.getText(), passwordField.getText().toCharArray(), registerCheckBox.isSelected(), Startup.VERSION)); + userTextField.getTextField().clear(); + } else performHandshake(new LoginCredentials(userTextField.getTextField().getText(), passwordField.getText().toCharArray(), + registerCheckBox.isSelected(), Startup.VERSION)); } @FXML private void offlineModeButtonPressed() { - attemptOfflineMode(new LoginCredentials(userTextField.getText(), passwordField.getText().toCharArray(), false, Startup.VERSION)); + attemptOfflineMode( + new LoginCredentials(userTextField.getTextField().getText(), passwordField.getText().toCharArray(), false, Startup.VERSION)); } @FXML diff --git a/src/main/java/envoy/client/ui/listcell/ContactControl.java b/src/main/java/envoy/client/ui/listcell/ContactControl.java new file mode 100644 index 0000000..2a802fc --- /dev/null +++ b/src/main/java/envoy/client/ui/listcell/ContactControl.java @@ -0,0 +1,40 @@ +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. + *

+ * Project: envoy-client
+ * File: ContactControl.java
+ * Created: 01.07.2020
+ * + * @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")); + } +} diff --git a/src/main/java/envoy/client/ui/ContactListCell.java b/src/main/java/envoy/client/ui/listcell/ContactListCellFactory.java similarity index 51% rename from src/main/java/envoy/client/ui/ContactListCell.java rename to src/main/java/envoy/client/ui/listcell/ContactListCellFactory.java index b13543b..8f8d3f0 100644 --- a/src/main/java/envoy/client/ui/ContactListCell.java +++ b/src/main/java/envoy/client/ui/listcell/ContactListCellFactory.java @@ -1,13 +1,9 @@ -package envoy.client.ui; +package envoy.client.ui.listcell; -import javafx.scene.control.Label; import javafx.scene.control.ListCell; import javafx.scene.control.ListView; -import javafx.scene.layout.VBox; import envoy.data.Contact; -import envoy.data.Group; -import envoy.data.User; /** * Project: envoy-client
@@ -17,7 +13,7 @@ import envoy.data.User; * @author Kai S. K. Engelbart * @since Envoy Client v0.1-beta */ -public class ContactListCell extends ListCell { +public class ContactListCellFactory extends ListCell { private final ListView listView; @@ -25,7 +21,7 @@ public class ContactListCell extends ListCell { * @param listView the list view inside which this cell is contained * @since Envoy Client v0.1-beta */ - public ContactListCell(ListView listView) { this.listView = listView; } + public ContactListCellFactory(ListView listView) { this.listView = listView; } /** * Displays the name of a contact. If the contact is a user, their online status @@ -40,20 +36,9 @@ public class ContactListCell extends ListCell { setText(null); setGraphic(null); } else { - // Container with contact name - final var nameLabel = new Label(contact.getName()); - nameLabel.setWrapText(true); - final var vbox = new VBox(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()); - vbox.getChildren().add(statusLabel); - } else // Member count - vbox.getChildren().add(new Label(((Group) contact).getContacts().size() + " members")); + final var control = new ContactControl(contact); prefWidthProperty().bind(listView.widthProperty().subtract(40)); - setGraphic(vbox); + setGraphic(control); } } } diff --git a/src/main/java/envoy/client/ui/listcell/MessageControl.java b/src/main/java/envoy/client/ui/listcell/MessageControl.java new file mode 100644 index 0000000..d68acdd --- /dev/null +++ b/src/main/java/envoy/client/ui/listcell/MessageControl.java @@ -0,0 +1,60 @@ +package envoy.client.ui.listcell; + +import java.time.format.DateTimeFormatter; +import java.util.Map; + +import javafx.geometry.Insets; +import javafx.scene.control.Label; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.VBox; + +import envoy.client.ui.IconUtil; +import envoy.data.Message; +import envoy.data.Message.MessageStatus; +import envoy.data.User; + +/** + * This class formats a single {@link Message} into a UI component. + *

+ * Project: envoy-client
+ * File: MessageControl.java
+ * Created: 01.07.2020
+ * + * @author Leon Hofmeister + * @since Envoy Client v0.1-beta + */ +public class MessageControl extends VBox { + + private static User client; + private static final DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm"); + private static final Map statusImages = IconUtil.loadByEnum(MessageStatus.class, 16); + + /** + * + * @param message the message that should be formatted + * @since Envoy Client v0.1-beta + */ + public MessageControl(Message message) { + // Creating the underlying VBox, the dateLabel and the textLabel + super(new Label(dateFormat.format(message.getCreationDate()))); + final var textLabel = new Label(message.getText()); + textLabel.setWrapText(true); + getChildren().add(textLabel); + // Setting the message status icon and background color + if (message.getRecipientID() != client.getID()) { + final var statusIcon = new ImageView(statusImages.get(message.getStatus())); + statusIcon.setPreserveRatio(true); + getChildren().add(statusIcon); + getStyleClass().add("own-message"); + } else getStyleClass().add("received-message"); + // Adjusting height and weight of the cell to the corresponding ListView + paddingProperty().setValue(new Insets(5, 20, 5, 20)); + } + + /** + * @param client the user who has logged in + * @since Envoy Client v0.1-beta + */ + public static void setUser(User client) { MessageControl.client = client; } +} diff --git a/src/main/java/envoy/client/ui/listcell/MessageListCellFactory.java b/src/main/java/envoy/client/ui/listcell/MessageListCellFactory.java new file mode 100644 index 0000000..f8e4fa2 --- /dev/null +++ b/src/main/java/envoy/client/ui/listcell/MessageListCellFactory.java @@ -0,0 +1,52 @@ +package envoy.client.ui.listcell; + +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.control.Tooltip; +import javafx.stage.PopupWindow.AnchorLocation; + +import envoy.data.Message; + +/** + * Displays a single message inside the message list. + *

+ * Project: envoy-client
+ * File: MessageListCellFactory.java
+ * Created: 28.03.2020
+ * + * @author Kai S. K. Engelbart + * @since Envoy Client v0.1-beta + */ +public class MessageListCellFactory extends ListCell { + + private final ListView listView; + + /** + * @param listView the list view inside which this cell is contained + * @since Envoy Client v0.1-beta + */ + public MessageListCellFactory(ListView listView) { this.listView = listView; } + + /** + * Displays the text, the data of creation and the status of a message. + * + * @since Envoy v0.1-beta + */ + @Override + protected void updateItem(Message message, boolean empty) { + super.updateItem(message, empty); + if (empty || message == null) { + setText(null); + setGraphic(null); + } else { + final var control = new MessageControl(message); + control.prefWidthProperty().bind(listView.widthProperty().subtract(40)); + // Creating the Tooltip to deselect a message + final var tooltip = new Tooltip("You can select a message by clicking on it \nand deselect it by pressing \"ctrl\" and clicking on it"); + tooltip.setWrapText(true); + tooltip.setAnchorLocation(AnchorLocation.WINDOW_TOP_LEFT); + setTooltip(tooltip); + setGraphic(control); + } + } +} diff --git a/src/main/java/envoy/client/ui/listcell/package-info.java b/src/main/java/envoy/client/ui/listcell/package-info.java new file mode 100644 index 0000000..ab0788e --- /dev/null +++ b/src/main/java/envoy/client/ui/listcell/package-info.java @@ -0,0 +1,12 @@ +/** + * This package contains custom list cells that are used to display certain + * things. + *

+ * Project: envoy-client
+ * File: package-info.java
+ * Created: 30.06.2020
+ * + * @author Leon Hofmeister + * @since Envoy Client v0.1-beta + */ +package envoy.client.ui.listcell; diff --git a/src/main/other/CustomComponents.jar b/src/main/other/CustomComponents.jar new file mode 100644 index 0000000..083f070 Binary files /dev/null and b/src/main/other/CustomComponents.jar differ diff --git a/src/main/resources/css/base.css b/src/main/resources/css/base.css index f9f1ce2..9230b0f 100644 --- a/src/main/resources/css/base.css +++ b/src/main/resources/css/base.css @@ -3,7 +3,7 @@ } .context-menu, .context-menu > * { - -fx-background-radius: 15px; + -fx-background-radius: 15.0px; /*TODO: solution below does not work */ -fx-background-color: transparent; } @@ -58,3 +58,19 @@ -fx-text-fill: #00FF00; -fx-background-color: transparent; } + +#infoLabel-success { + -fx-text-fill: #00FF00; +} + +#infoLabel-info { + -fx-text-fill: yellow; +} + +#infoLabel-warning { + -fx-text-fill: orange; +} + +#infoLabel-error { + -fx-text-fill: red; +} diff --git a/src/main/resources/fxml/ChatScene.fxml b/src/main/resources/fxml/ChatScene.fxml index fa1a4ef..8ed1185 100644 --- a/src/main/resources/fxml/ChatScene.fxml +++ b/src/main/resources/fxml/ChatScene.fxml @@ -12,9 +12,9 @@ - @@ -47,7 +47,7 @@ prefHeight="211.0" prefWidth="300.0" GridPane.rowIndex="1" GridPane.rowSpan="2147483647"> - + @@ -64,7 +64,7 @@