Merge branch 'develop' into f/groupMessages
This commit is contained in:
commit
483ab9e344
3
.github/PULL_REQUEST_TEMPLATE/bugfix.md
vendored
3
.github/PULL_REQUEST_TEMPLATE/bugfix.md
vendored
@ -5,6 +5,7 @@ labels: bug
|
||||
assignees: CyB3RC0nN0R, delvh, DieGurke
|
||||
reviewers: CyB3RC0nN0R, delvh
|
||||
projects: Envoy
|
||||
milestone: Envoy v0.3-alpha
|
||||
milestone: Envoy v0.1-beta
|
||||
|
||||
---
|
||||
Fixes #{issue}
|
||||
|
@ -1,9 +1,10 @@
|
||||
---
|
||||
name: Feature integration
|
||||
title: Added feature
|
||||
labels: enhancement
|
||||
labels: feature
|
||||
assignees: CyB3RC0nN0R, delvh, DieGurke
|
||||
reviewers: CyB3RC0nN0R, delvh
|
||||
projects: Envoy
|
||||
milestone: Envoy v0.3-alpha
|
||||
milestone: Envoy v0.1-beta
|
||||
|
||||
---
|
||||
|
@ -5,5 +5,6 @@ labels: documentation
|
||||
assignees: CyB3RC0nN0R, delvh
|
||||
reviewers: CyB3RC0nN0R, delvh
|
||||
projects: Envoy
|
||||
milestone: Envoy v0.3-alpha
|
||||
milestone: Envoy v0.1-beta
|
||||
|
||||
---
|
15
.github/workflows/maven.yml
vendored
15
.github/workflows/maven.yml
vendored
@ -4,14 +4,25 @@ on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 11
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 11
|
||||
- name: Cache Maven packages
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.m2
|
||||
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: ${{ runner.os }}-m2
|
||||
- name: Build with Maven
|
||||
run: mvn -B package --file pom.xml
|
||||
- name: Stage build artifacts
|
||||
run: mkdir staging && cp target/*.jar staging
|
||||
- uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: envoy-client-artifacts
|
||||
path: staging
|
||||
|
@ -114,6 +114,21 @@ public final class Chat implements Serializable {
|
||||
*/
|
||||
public boolean isUnread() { return !messages.isEmpty() && messages.get(messages.size() - 1).getStatus() != MessageStatus.READ; }
|
||||
|
||||
/**
|
||||
* Inserts a message at the correct place according to its creation date.
|
||||
*
|
||||
* @param message the message to insert
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public void insert(Message message) {
|
||||
for (int i = messages.size() - 1; i >= 0; --i)
|
||||
if (message.getCreationDate().isAfter(messages.get(i).getCreationDate())) {
|
||||
messages.add(i + 1, message);
|
||||
return;
|
||||
}
|
||||
messages.add(0, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return all messages in the current chat
|
||||
* @since Envoy Client v0.1-beta
|
||||
|
174
src/main/java/envoy/client/ui/ClearableTextField.java
Normal file
174
src/main/java/envoy/client/ui/ClearableTextField.java
Normal file
@ -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.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>ClearableTextField.java</strong><br>
|
||||
* Created: <strong>25.06.2020</strong><br>
|
||||
*
|
||||
* @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.
|
||||
* <p>
|
||||
* The default is
|
||||
* <b><code> e -> {clearableTextField.getTextField().clear();}</code></b>
|
||||
*
|
||||
* @param onClearButtonAction the action that should be performed
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public void setClearButtonListener(EventHandler<ActionEvent> 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<Tooltip> 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<ContextMenu> 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(); }
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
package envoy.client.ui;
|
||||
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ListCell;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
import envoy.data.Contact;
|
||||
import envoy.data.Group;
|
||||
import envoy.data.User;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>UserListCell.java</strong><br>
|
||||
* Created: <strong>28.03.2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public class ContactListCell extends ListCell<Contact> {
|
||||
|
||||
/**
|
||||
* Displays the name of a contact. If the contact is a user, their online status
|
||||
* is displayed as well.
|
||||
*
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
@Override
|
||||
protected void updateItem(Contact contact, boolean empty) {
|
||||
super.updateItem(contact, empty);
|
||||
if (empty || contact == null) {
|
||||
setText(null);
|
||||
setGraphic(null);
|
||||
} else {
|
||||
// Container with contact name
|
||||
final var vbox = new VBox(new Label(contact.getName()));
|
||||
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"));
|
||||
}
|
||||
setGraphic(vbox);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
package envoy.client.ui;
|
||||
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Map;
|
||||
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ListCell;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
import envoy.data.Message;
|
||||
import envoy.data.Message.MessageStatus;
|
||||
import envoy.data.User;
|
||||
|
||||
/**
|
||||
* Displays a single message inside the message list.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>MessageListCell.java</strong><br>
|
||||
* Created: <strong>28.03.2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public class MessageListCell extends ListCell<Message> {
|
||||
|
||||
private static User client;
|
||||
private static final DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm");
|
||||
private static final Map<MessageStatus, Image> statusImages = IconUtil.loadByEnum(MessageStatus.class, 16);
|
||||
|
||||
/**
|
||||
* 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 cell = new VBox(new Label(dateFormat.format(message.getCreationDate())), new Label(message.getText()));
|
||||
if (message.getRecipientID() == client.getID()) {
|
||||
cell.getChildren().add(new Label("", new ImageView(statusImages.get(message.getStatus()))));
|
||||
cell.getStyleClass().add("own-message");
|
||||
} else cell.getStyleClass().add("received-message");
|
||||
cell.paddingProperty().setValue(new Insets(5, 20, 5, 20));
|
||||
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; }
|
||||
}
|
25
src/main/java/envoy/client/ui/Restorable.java
Normal file
25
src/main/java/envoy/client/ui/Restorable.java
Normal file
@ -0,0 +1,25 @@
|
||||
package envoy.client.ui;
|
||||
|
||||
/**
|
||||
* This interface defines an action that should be performed when a scene gets
|
||||
* restored from the scene stack in {@link SceneContext}.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>Restorable.java</strong><br>
|
||||
* Created: <strong>03.07.2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Restorable {
|
||||
|
||||
/**
|
||||
* This method is getting called when a scene gets restored.<br>
|
||||
* Hence, it can contain anything that should be done when the underlying scene
|
||||
* gets restored.
|
||||
*
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
void onRestore();
|
||||
}
|
@ -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.
|
||||
@ -83,8 +90,9 @@ public final class SceneContext {
|
||||
}
|
||||
|
||||
private final Stage stage;
|
||||
private final FXMLLoader loader = new FXMLLoader();
|
||||
private final Stack<Scene> sceneStack = new Stack<>();
|
||||
private final FXMLLoader loader = new FXMLLoader();
|
||||
private final Stack<Scene> sceneStack = new Stack<>();
|
||||
private final Stack<Object> controllerStack = new Stack<>();
|
||||
|
||||
private static final Settings settings = Settings.getInstance();
|
||||
|
||||
@ -113,6 +121,7 @@ public final class SceneContext {
|
||||
try {
|
||||
final var rootNode = (Parent) loader.load(getClass().getResourceAsStream(sceneInfo.path));
|
||||
final var scene = new Scene(rootNode);
|
||||
controllerStack.push(loader.getController());
|
||||
|
||||
sceneStack.push(scene);
|
||||
stage.setScene(scene);
|
||||
@ -132,10 +141,16 @@ public final class SceneContext {
|
||||
*/
|
||||
public void pop() {
|
||||
sceneStack.pop();
|
||||
controllerStack.pop();
|
||||
if (!sceneStack.isEmpty()) {
|
||||
stage.setScene(sceneStack.peek());
|
||||
final var newScene = sceneStack.peek();
|
||||
stage.setScene(newScene);
|
||||
applyCSS();
|
||||
stage.sizeToScene();
|
||||
// If the controller implements the Restorable interface,
|
||||
// the actions to perform on restoration will be executed here
|
||||
final var controller = controllerStack.peek();
|
||||
if (controller instanceof Restorable) ((Restorable) controller).onRestore();
|
||||
}
|
||||
stage.show();
|
||||
}
|
||||
@ -154,7 +169,7 @@ public final class SceneContext {
|
||||
* @return the controller used by the current scene
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public <T> T getController() { return loader.getController(); }
|
||||
public <T> T getController() { return (T) controllerStack.peek(); }
|
||||
|
||||
/**
|
||||
* @return the stage in which the scenes are displayed
|
||||
|
@ -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,12 @@ 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.Restorable;
|
||||
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.*;
|
||||
import envoy.event.contact.ContactOperation;
|
||||
@ -38,7 +42,7 @@ import envoy.util.EnvoyLog;
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public final class ChatScene {
|
||||
public final class ChatScene implements Restorable {
|
||||
|
||||
@FXML
|
||||
private Label contactLabel;
|
||||
@ -90,26 +94,16 @@ public final class ChatScene {
|
||||
private void initialize() {
|
||||
|
||||
// Initialize message and user rendering
|
||||
messageList.setCellFactory(listView -> new MessageListCell());
|
||||
userList.setCellFactory(listView -> new ContactListCell());
|
||||
messageList.setCellFactory(MessageListCellFactory::new);
|
||||
userList.setCellFactory(ContactListCellFactory::new);
|
||||
|
||||
settingsButton.setGraphic(new ImageView(IconUtil.load("/icons/settings.png", 16)));
|
||||
|
||||
// Listen to received messages
|
||||
eventBus.register(MessageCreationEvent.class, e -> {
|
||||
final var message = e.get();
|
||||
if (message.getClass().equals(Message.class)) {
|
||||
localDB.getChat(message.getSenderID()).ifPresent(chat -> {
|
||||
chat.getMessages().add(message);
|
||||
|
||||
// Update UI if in current chat
|
||||
if (chat == currentChat)
|
||||
Platform.runLater(messageList::refresh);
|
||||
});
|
||||
} else {
|
||||
localDB.getChat(message.getRecipientID()).ifPresent(chat -> {
|
||||
chat.getMessages().add(message);
|
||||
|
||||
localDB.getChat(message instanceof GroupMessage ? message.getSenderID() : message.getRecipientID()).ifPresent(chat -> {
|
||||
chat.insert(message);
|
||||
if (chat.equals(currentChat)) {
|
||||
try {
|
||||
currentChat.read(writeProxy);
|
||||
@ -118,8 +112,7 @@ public final class ChatScene {
|
||||
}
|
||||
Platform.runLater(() -> { messageList.refresh(); scrollToMessageListEnd(); });
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Listen to message status changes
|
||||
@ -128,7 +121,7 @@ public final class ChatScene {
|
||||
// Update UI if in current chat
|
||||
if (currentChat != null && message.getSenderID() == currentChat.getRecipient().getID()) Platform.runLater(messageList::refresh);
|
||||
}));
|
||||
|
||||
|
||||
eventBus.register(GroupMessageStatusChange.class, e -> localDB.getMessage(e.getID()).ifPresent(groupMessage -> {
|
||||
((GroupMessage) groupMessage).getMemberStatuses().replace(e.getMemberID(), e.get());
|
||||
|
||||
@ -180,10 +173,13 @@ 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");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestore() { updateRemainingCharsLabel(); }
|
||||
|
||||
/**
|
||||
* Actions to perform when the list of contacts has been clicked.
|
||||
*
|
||||
@ -193,7 +189,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
|
||||
|
||||
@ -201,6 +196,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
|
||||
@ -271,7 +269,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");
|
||||
}
|
||||
}
|
||||
|
||||
@ -317,35 +315,24 @@ 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();
|
||||
if (text.isBlank()) throw new IllegalArgumentException("A message without visible text can not be sent.");
|
||||
try {
|
||||
if (currentChat.getRecipient().getClass().equals(Group.class)) {
|
||||
// Create and send groupMessage
|
||||
final var groupMessage = new MessageBuilder(localDB.getUser().getID(), currentChat.getRecipient().getID(), localDB.getIDGenerator())
|
||||
.setText(messageTextArea.getText().strip())
|
||||
.buildGroupMessage((Group) currentChat.getRecipient());
|
||||
// Create and send message
|
||||
final var builder = new MessageBuilder(localDB.getUser().getID(), currentChat.getRecipient().getID(), localDB.getIDGenerator())
|
||||
.setText(text);
|
||||
final var message = currentChat.getRecipient() instanceof Group ? builder.buildGroupMessage((Group) currentChat.getRecipient())
|
||||
: builder.build();
|
||||
|
||||
// Send groupMessage
|
||||
writeProxy.writeMessage(groupMessage);
|
||||
// Send message
|
||||
writeProxy.writeMessage(message);
|
||||
|
||||
// Add message to LocalDB and update UI
|
||||
messageList.getItems().add(groupMessage);
|
||||
} else {
|
||||
// Create and send message
|
||||
final var message = new MessageBuilder(localDB.getUser().getID(), currentChat.getRecipient().getID(), localDB.getIDGenerator())
|
||||
.setText(messageTextArea.getText().strip())
|
||||
.build();
|
||||
|
||||
// Send message
|
||||
writeProxy.writeMessage(message);
|
||||
|
||||
// Add message to LocalDB and update UI
|
||||
messageList.getItems().add(message);
|
||||
}
|
||||
// Add message to LocalDB and update UI
|
||||
currentChat.insert(message);
|
||||
messageList.refresh();
|
||||
scrollToMessageListEnd();
|
||||
|
||||
// Request a new ID generator if all IDs were used
|
||||
@ -372,13 +359,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);
|
||||
}
|
||||
|
||||
@ -414,4 +402,7 @@ public final class ChatScene {
|
||||
updateRemainingCharsLabel();
|
||||
postButton.setDisable(messageText.isBlank());
|
||||
}
|
||||
|
||||
@FXML
|
||||
private void loadMessageInfoScene() { try {} catch (final NullPointerException e) {} }
|
||||
}
|
||||
|
@ -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<Contact> contactList;
|
||||
@ -67,7 +58,8 @@ public class ContactSearchScene {
|
||||
|
||||
@FXML
|
||||
private void initialize() {
|
||||
contactList.setCellFactory(e -> new ContactListCell());
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -119,14 +101,17 @@ public class ContactSearchScene {
|
||||
final var alert = new Alert(AlertType.CONFIRMATION);
|
||||
alert.setTitle("Add Contact to Contact List");
|
||||
alert.setHeaderText("Add the user " + contact.getName() + " to your contact list?");
|
||||
alert.showAndWait().filter(btn -> btn == ButtonType.OK).ifPresent(btn -> {
|
||||
// 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
|
||||
// Platform.runLater is less error-prone than without it
|
||||
Platform.runLater(() -> alert.showAndWait().filter(btn -> btn == ButtonType.OK).ifPresent(btn -> {
|
||||
final var event = new ContactOperation(contact, ElementOperation.ADD);
|
||||
// Sends the event to the server
|
||||
eventBus.dispatch(new SendEvent(event));
|
||||
// Updates the UI
|
||||
eventBus.dispatch(event);
|
||||
logger.log(Level.INFO, "Added contact " + contact);
|
||||
});
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<Contact> contactList;
|
||||
@ -41,8 +42,9 @@ public class GroupCreationScene {
|
||||
|
||||
@FXML
|
||||
private void initialize() {
|
||||
contactList.setCellFactory(e -> new ContactListCell());
|
||||
contactList.setCellFactory(ContactListCellFactory::new);
|
||||
contactList.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
|
||||
groupNameField.setClearButtonListener(e -> { groupNameField.getTextField().clear(); createButton.setDisable(true); });
|
||||
}
|
||||
|
||||
/**
|
||||
@ -59,11 +61,22 @@ public class GroupCreationScene {
|
||||
|
||||
/**
|
||||
* Enables the {@code createButton} if at least one contact is selected.
|
||||
*
|
||||
*
|
||||
* @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()))));
|
||||
|
@ -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.*;
|
||||
@ -34,7 +35,7 @@ import envoy.util.EnvoyLog;
|
||||
public final class LoginScene {
|
||||
|
||||
@FXML
|
||||
private TextField userTextField;
|
||||
private ClearableTextField userTextField;
|
||||
|
||||
@FXML
|
||||
private PasswordField passwordField;
|
||||
@ -119,17 +120,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
|
||||
@ -162,8 +163,7 @@ public final class LoginScene {
|
||||
loadChatScene();
|
||||
}
|
||||
} catch (IOException | InterruptedException | TimeoutException e) {
|
||||
logger.log(Level.WARNING, "Could not connect to server: ", e);
|
||||
logger.log(Level.FINER, "Attempting offline mode...");
|
||||
logger.log(Level.INFO, "Could not connect to server. Entering offline mode...");
|
||||
attemptOfflineMode(credentials);
|
||||
}
|
||||
}
|
||||
|
40
src/main/java/envoy/client/ui/listcell/ContactControl.java
Normal file
40
src/main/java/envoy/client/ui/listcell/ContactControl.java
Normal file
@ -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.
|
||||
* <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"));
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package envoy.client.ui.listcell;
|
||||
|
||||
import javafx.scene.control.ListCell;
|
||||
import javafx.scene.control.ListView;
|
||||
|
||||
import envoy.data.Contact;
|
||||
|
||||
/**
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>UserListCell.java</strong><br>
|
||||
* Created: <strong>28.03.2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public class ContactListCellFactory extends ListCell<Contact> {
|
||||
|
||||
private final ListView<Contact> listView;
|
||||
|
||||
/**
|
||||
* @param listView the list view inside which this cell is contained
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public ContactListCellFactory(ListView<Contact> listView) { this.listView = listView; }
|
||||
|
||||
/**
|
||||
* Displays the name of a contact. If the contact is a user, their online status
|
||||
* is displayed as well.
|
||||
*
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
@Override
|
||||
protected void updateItem(Contact contact, boolean empty) {
|
||||
super.updateItem(contact, empty);
|
||||
if (empty || contact == null) {
|
||||
setText(null);
|
||||
setGraphic(null);
|
||||
} else {
|
||||
final var control = new ContactControl(contact);
|
||||
prefWidthProperty().bind(listView.widthProperty().subtract(40));
|
||||
setGraphic(control);
|
||||
}
|
||||
}
|
||||
}
|
60
src/main/java/envoy/client/ui/listcell/MessageControl.java
Normal file
60
src/main/java/envoy/client/ui/listcell/MessageControl.java
Normal file
@ -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.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>MessageControl.java</strong><br>
|
||||
* Created: <strong>01.07.2020</strong><br>
|
||||
*
|
||||
* @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<MessageStatus, Image> 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; }
|
||||
}
|
@ -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.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>MessageListCellFactory.java</strong><br>
|
||||
* Created: <strong>28.03.2020</strong><br>
|
||||
*
|
||||
* @author Kai S. K. Engelbart
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public class MessageListCellFactory extends ListCell<Message> {
|
||||
|
||||
private final ListView<Message> listView;
|
||||
|
||||
/**
|
||||
* @param listView the list view inside which this cell is contained
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public MessageListCellFactory(ListView<Message> 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);
|
||||
}
|
||||
}
|
||||
}
|
12
src/main/java/envoy/client/ui/listcell/package-info.java
Normal file
12
src/main/java/envoy/client/ui/listcell/package-info.java
Normal file
@ -0,0 +1,12 @@
|
||||
/**
|
||||
* This package contains custom list cells that are used to display certain
|
||||
* things.
|
||||
* <p>
|
||||
* Project: <strong>envoy-client</strong><br>
|
||||
* File: <strong>package-info.java</strong><br>
|
||||
* Created: <strong>30.06.2020</strong><br>
|
||||
*
|
||||
* @author Leon Hofmeister
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
package envoy.client.ui.listcell;
|
BIN
src/main/other/CustomComponents.jar
Normal file
BIN
src/main/other/CustomComponents.jar
Normal file
Binary file not shown.
@ -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;
|
||||
}
|
||||
|
@ -12,9 +12,9 @@
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.scene.layout.RowConstraints?>
|
||||
|
||||
<GridPane maxHeight="-Infinity" maxWidth="-Infinity"
|
||||
<GridPane hgap="5.0" maxHeight="-Infinity" maxWidth="-Infinity"
|
||||
minHeight="400.0" minWidth="350.0" prefHeight="400.0" prefWidth="600.0"
|
||||
xmlns="http://javafx.com/javafx/11.0.1"
|
||||
vgap="2.0" xmlns="http://javafx.com/javafx/11.0.1"
|
||||
xmlns:fx="http://javafx.com/fxml/1"
|
||||
fx:controller="envoy.client.ui.controller.ChatScene">
|
||||
<columnConstraints>
|
||||
@ -47,7 +47,7 @@
|
||||
prefHeight="211.0" prefWidth="300.0" GridPane.rowIndex="1"
|
||||
GridPane.rowSpan="2147483647">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="10.0" left="10.0" right="5.0" top="5.0" />
|
||||
<Insets bottom="10.0" left="10.0" />
|
||||
</GridPane.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
@ -64,7 +64,7 @@
|
||||
<Label fx:id="contactLabel" prefHeight="16.0" prefWidth="250.0"
|
||||
text="Select a contact to chat with" GridPane.columnSpan="2">
|
||||
<GridPane.margin>
|
||||
<Insets left="15.0" right="5.0" top="5.0" />
|
||||
<Insets left="10.0" />
|
||||
</GridPane.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
@ -75,17 +75,18 @@
|
||||
GridPane.columnIndex="1" GridPane.columnSpan="2"
|
||||
GridPane.halignment="RIGHT" GridPane.valignment="CENTER">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="10.0" left="5.0" right="10.0" top="10.0" />
|
||||
<Insets bottom="10.0" right="10.0" top="10.0" />
|
||||
</GridPane.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
</Button>
|
||||
<ListView fx:id="messageList" prefHeight="257.0"
|
||||
prefWidth="465.0" GridPane.columnIndex="1" GridPane.columnSpan="2"
|
||||
GridPane.rowIndex="1" GridPane.rowSpan="2">
|
||||
prefWidth="465.0" GridPane.columnIndex="1"
|
||||
GridPane.columnSpan="2147483647" GridPane.rowIndex="1"
|
||||
GridPane.rowSpan="2">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="10.0" top="5.0" />
|
||||
<Insets left="5.0" right="10.0" />
|
||||
</GridPane.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
@ -101,6 +102,8 @@
|
||||
onAction="#forwardMessage" text="Forward" />
|
||||
<MenuItem mnemonicParsing="false"
|
||||
onAction="#quoteMessage" text="Quote" />
|
||||
<MenuItem mnemonicParsing="false"
|
||||
onAction="#loadMessageInfoScene" text="Info" />
|
||||
</items>
|
||||
</ContextMenu>
|
||||
</contextMenu>
|
||||
@ -111,11 +114,8 @@
|
||||
GridPane.halignment="CENTER" GridPane.rowIndex="4"
|
||||
GridPane.valignment="BOTTOM">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="10.0" left="5.0" right="10.0" />
|
||||
<Insets bottom="10.0" right="10.0" />
|
||||
</GridPane.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
<tooltip>
|
||||
<Tooltip anchorLocation="WINDOW_TOP_LEFT" autoHide="true"
|
||||
maxWidth="350.0"
|
||||
@ -130,6 +130,9 @@
|
||||
</items>
|
||||
</ContextMenu>
|
||||
</contextMenu>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
</Button>
|
||||
<TextArea fx:id="messageTextArea" disable="true"
|
||||
onInputMethodTextChanged="#messageTextUpdated"
|
||||
@ -137,7 +140,7 @@
|
||||
prefHeight="200.0" prefWidth="200.0" wrapText="true"
|
||||
GridPane.columnIndex="1" GridPane.rowIndex="4">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="10.0" left="5.0" right="5.0" top="5.0" />
|
||||
<Insets bottom="10.0" left="5.0" top="3.0" />
|
||||
</GridPane.margin>
|
||||
<opaqueInsets>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
@ -163,7 +166,7 @@
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</GridPane.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
<Insets bottom="5.0" top="5.0" />
|
||||
</padding>
|
||||
<opaqueInsets>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
@ -175,8 +178,8 @@
|
||||
</tooltip>
|
||||
</Label>
|
||||
<Label fx:id="infoLabel" text="Something happened"
|
||||
textFill="#faa007" visible="false" GridPane.columnIndex="1"
|
||||
GridPane.rowIndex="1">
|
||||
wrapText="true" textFill="#faa007" visible="false"
|
||||
GridPane.columnIndex="1" GridPane.rowIndex="1">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</GridPane.margin>
|
||||
|
@ -1,77 +1,74 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import envoy.client.ui.ClearableTextField?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.ListView?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.control.Tooltip?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
|
||||
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="envoy.client.ui.controller.ContactSearchScene">
|
||||
<children>
|
||||
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0">
|
||||
<children>
|
||||
<TextField fx:id="searchBar" onInputMethodTextChanged="#checkClearButton" onKeyTyped="#checkClearButton" prefColumnCount="22" promptText="Enter username to search for">
|
||||
<HBox.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</HBox.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
<tooltip>
|
||||
<Tooltip text="Enter a name. If an account by that name exists, it will be displayed below." wrapText="true" />
|
||||
</tooltip>
|
||||
</TextField>
|
||||
<Button fx:id="clearButton" disable="true" mnemonicParsing="true" onAction="#clear" prefHeight="26.0" prefWidth="110.0" text="Clea_r">
|
||||
<tooltip>
|
||||
<Tooltip autoHide="true" text="Clears the text to the left and the elements below" wrapText="true" />
|
||||
</tooltip>
|
||||
<HBox.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="10.0" top="5.0" />
|
||||
</HBox.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
</Button>
|
||||
<Button fx:id="searchButton" defaultButton="true" disable="true" mnemonicParsing="true" onAction="#suggestContacts" prefHeight="26.0" prefWidth="124.0" text="_Search" textOverrun="LEADING_WORD_ELLIPSIS">
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
<HBox.margin>
|
||||
<Insets bottom="5.0" right="20.0" top="5.0" />
|
||||
</HBox.margin>
|
||||
<tooltip>
|
||||
<Tooltip autoHide="true" text="Search for accounts with the name entered to the left" wrapText="true" />
|
||||
</tooltip>
|
||||
</Button>
|
||||
<Button mnemonicParsing="false" onAction="#newGroupButtonClicked" prefHeight="26.0" prefWidth="139.0" text="New Group">
|
||||
<HBox.margin>
|
||||
<Insets bottom="5.0" left="20.0" right="5.0" top="5.0" />
|
||||
</HBox.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
</Button>
|
||||
</children>
|
||||
</HBox>
|
||||
<ListView fx:id="contactList" onMouseClicked="#contactListClicked" prefHeight="314.0" prefWidth="600.0">
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
<VBox.margin>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</VBox.margin></ListView>
|
||||
<Button fx:id="backButton" cancelButton="true" mnemonicParsing="true" onAction="#backButtonClicked" text="_Back">
|
||||
<VBox.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</VBox.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
<tooltip>
|
||||
<Tooltip autoHide="true" text="Takes you back to the screen where you can chat with others" wrapText="true" />
|
||||
</tooltip>
|
||||
</Button>
|
||||
</children>
|
||||
<VBox maxHeight="-Infinity" maxWidth="-Infinity"
|
||||
minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0"
|
||||
prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1"
|
||||
xmlns:fx="http://javafx.com/fxml/1"
|
||||
fx:controller="envoy.client.ui.controller.ContactSearchScene">
|
||||
<children>
|
||||
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0">
|
||||
<children>
|
||||
<ClearableTextField fx:id="searchBar"
|
||||
prefWidth="310.0">
|
||||
<textField onInputMethodTextChanged="#sendRequest"
|
||||
onKeyTyped="#sendRequest" prefColumnCount="22"
|
||||
promptText="Enter username to search for">
|
||||
</textField>
|
||||
<HBox.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="15.0" />
|
||||
</HBox.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
<tooltip>
|
||||
<Tooltip
|
||||
text="Enter a name. If an account by that name exists, it will be displayed below."
|
||||
wrapText="true" />
|
||||
</tooltip>
|
||||
</ClearableTextField>
|
||||
<Button mnemonicParsing="false"
|
||||
onAction="#newGroupButtonClicked" prefHeight="26.0"
|
||||
prefWidth="139.0" text="New Group">
|
||||
<HBox.margin>
|
||||
<Insets bottom="5.0" left="30.0" right="5.0" top="5.0" />
|
||||
</HBox.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
</Button>
|
||||
</children>
|
||||
</HBox>
|
||||
<ListView fx:id="contactList"
|
||||
onMouseClicked="#contactListClicked" prefHeight="314.0"
|
||||
prefWidth="600.0">
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
<VBox.margin>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</VBox.margin>
|
||||
</ListView>
|
||||
<Button cancelButton="true" mnemonicParsing="true"
|
||||
onAction="#backButtonClicked" text="_Back">
|
||||
<VBox.margin>
|
||||
<Insets bottom="10.0" left="10.0" />
|
||||
</VBox.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
<tooltip>
|
||||
<Tooltip autoHide="true"
|
||||
text="Takes you back to the screen where you can chat with others"
|
||||
wrapText="true" />
|
||||
</tooltip>
|
||||
</Button>
|
||||
</children>
|
||||
</VBox>
|
||||
|
@ -1,67 +1,91 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import envoy.client.ui.ClearableTextField?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.ListView?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.control.Tooltip?>
|
||||
<?import javafx.scene.layout.BorderPane?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.text.Font?>
|
||||
|
||||
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="envoy.client.ui.controller.GroupCreationScene">
|
||||
<children>
|
||||
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0">
|
||||
<children>
|
||||
<TextField fx:id="groupNameField" prefColumnCount="22" promptText="Enter Group Name">
|
||||
<HBox.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</HBox.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
<tooltip>
|
||||
<Tooltip text="Enter a name. If an account by that name exists, it will be displayed below." wrapText="true" />
|
||||
</tooltip>
|
||||
</TextField>
|
||||
</children>
|
||||
</HBox>
|
||||
<Label text="Choose Members:">
|
||||
<font>
|
||||
<Font size="16.0" />
|
||||
</font>
|
||||
<VBox.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</VBox.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
</Label>
|
||||
<ListView fx:id="contactList" onMouseClicked="#contactListClicked" prefHeight="314.0" prefWidth="600.0">
|
||||
<VBox.margin>
|
||||
<Insets bottom="5.0" left="10.0" right="10.0" top="5.0" />
|
||||
</VBox.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding></ListView>
|
||||
<Button fx:id="createButton" defaultButton="true" disable="true" mnemonicParsing="false" onAction="#createButtonClicked" text="Create">
|
||||
<VBox.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</VBox.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding></Button>
|
||||
<Button cancelButton="true" mnemonicParsing="true" onAction="#backButtonClicked" text="_Back">
|
||||
<VBox.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</VBox.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
<tooltip>
|
||||
<Tooltip autoHide="true" text="Takes you back to the screen where you can chat with others" wrapText="true" />
|
||||
</tooltip>
|
||||
</Button>
|
||||
</children>
|
||||
<VBox maxHeight="-Infinity" maxWidth="-Infinity"
|
||||
minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0"
|
||||
prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1"
|
||||
xmlns:fx="http://javafx.com/fxml/1"
|
||||
fx:controller="envoy.client.ui.controller.GroupCreationScene">
|
||||
<children>
|
||||
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0">
|
||||
<children>
|
||||
<ClearableTextField fx:id="groupNameField">
|
||||
<textField prefColumnCount="22"
|
||||
promptText="Enter Group Name"
|
||||
onInputMethodTextChanged="#textUpdated" onKeyTyped="#textUpdated" />
|
||||
<HBox.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</HBox.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
<tooltip>
|
||||
<Tooltip
|
||||
text="Enter something. A group with this name will be created."
|
||||
wrapText="true" />
|
||||
</tooltip>
|
||||
</ClearableTextField>
|
||||
</children>
|
||||
</HBox>
|
||||
<Label text="Choose Members:">
|
||||
<font>
|
||||
<Font size="16.0" />
|
||||
</font>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
</Label>
|
||||
<ListView fx:id="contactList"
|
||||
onMouseClicked="#contactListClicked" prefHeight="314.0"
|
||||
prefWidth="600.0">
|
||||
<VBox.margin>
|
||||
<Insets bottom="5.0" left="10.0" right="10.0" top="5.0" />
|
||||
</VBox.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
</ListView>
|
||||
<BorderPane prefHeight="50.0">
|
||||
<left>
|
||||
<Button cancelButton="true" mnemonicParsing="true"
|
||||
onAction="#backButtonClicked" text="_Back"
|
||||
BorderPane.alignment="CENTER">
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
<tooltip>
|
||||
<Tooltip autoHide="true"
|
||||
text="Takes you back to the screen where you can chat with others"
|
||||
wrapText="true" />
|
||||
</tooltip>
|
||||
<BorderPane.margin>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</BorderPane.margin>
|
||||
</Button>
|
||||
</left>
|
||||
<right>
|
||||
<Button fx:id="createButton" alignment="CENTER_RIGHT"
|
||||
defaultButton="true" disable="true" mnemonicParsing="false"
|
||||
onAction="#createButtonClicked" text="Create"
|
||||
BorderPane.alignment="CENTER">
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
<BorderPane.margin>
|
||||
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
|
||||
</BorderPane.margin>
|
||||
</Button>
|
||||
</right>
|
||||
</BorderPane>
|
||||
</children>
|
||||
</VBox>
|
||||
|
@ -1,104 +1,149 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import envoy.client.ui.ClearableTextField?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.ButtonBar?>
|
||||
<?import javafx.scene.control.CheckBox?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.PasswordField?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.layout.BorderPane?>
|
||||
<?import javafx.scene.layout.ColumnConstraints?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.scene.layout.RowConstraints?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.text.Font?>
|
||||
|
||||
<VBox prefHeight="206.0" prefWidth="440.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="envoy.client.ui.controller.LoginScene">
|
||||
<children>
|
||||
<Label text="User Login">
|
||||
<font>
|
||||
<Font size="26.0" />
|
||||
</font>
|
||||
<VBox.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</VBox.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
<VBox prefHeight="206.0" prefWidth="440.0"
|
||||
xmlns="http://javafx.com/javafx/11.0.1"
|
||||
xmlns:fx="http://javafx.com/fxml/1"
|
||||
fx:controller="envoy.client.ui.controller.LoginScene">
|
||||
<children>
|
||||
<Label text="User Login">
|
||||
<font>
|
||||
<Font size="26.0" />
|
||||
</font>
|
||||
<VBox.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</VBox.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
</Label>
|
||||
<GridPane hgap="5.0" vgap="10.0">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES"
|
||||
minWidth="10.0" percentWidth="40.0" prefWidth="100.0" />
|
||||
<ColumnConstraints hgrow="SOMETIMES"
|
||||
minWidth="10.0" prefWidth="100.0" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0"
|
||||
vgrow="SOMETIMES" />
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0"
|
||||
vgrow="SOMETIMES" />
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0"
|
||||
vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
<children>
|
||||
<Label text="User Name:">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</GridPane.margin>
|
||||
<padding>
|
||||
<Insets bottom="2.0" left="2.0" right="2.0" top="2.0" />
|
||||
</padding>
|
||||
</Label>
|
||||
<GridPane>
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" percentWidth="40.0" prefWidth="100.0" />
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
<children>
|
||||
<Label text="User Name:">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</GridPane.margin>
|
||||
<padding>
|
||||
<Insets bottom="2.0" left="2.0" right="2.0" top="2.0" />
|
||||
</padding></Label>
|
||||
<Label text="Password" GridPane.rowIndex="1">
|
||||
<padding>
|
||||
<Insets bottom="2.0" left="2.0" right="2.0" top="2.0" />
|
||||
</padding>
|
||||
<GridPane.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</GridPane.margin></Label>
|
||||
<Label fx:id="repeatPasswordLabel" text="Repeat Password:" visible="false" GridPane.rowIndex="2">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</GridPane.margin>
|
||||
<padding>
|
||||
<Insets bottom="2.0" left="2.0" right="2.0" top="2.0" />
|
||||
</padding></Label>
|
||||
<TextField fx:id="userTextField" GridPane.columnIndex="1">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="10.0" left="5.0" right="5.0" top="5.0" />
|
||||
</GridPane.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding></TextField>
|
||||
<PasswordField fx:id="passwordField" GridPane.columnIndex="1" GridPane.rowIndex="1">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="10.0" left="5.0" right="5.0" top="10.0" />
|
||||
</GridPane.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding></PasswordField>
|
||||
<PasswordField fx:id="repeatPasswordField" visible="false" GridPane.columnIndex="1" GridPane.rowIndex="2">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="10.0" />
|
||||
</GridPane.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding></PasswordField>
|
||||
</children>
|
||||
</GridPane>
|
||||
<CheckBox fx:id="registerCheckBox" mnemonicParsing="true" onAction="#registerCheckboxChanged" prefHeight="17.0" prefWidth="181.0" text="_Register">
|
||||
<padding>
|
||||
<Insets bottom="2.0" left="2.0" right="2.0" top="2.0" />
|
||||
</padding>
|
||||
<VBox.margin>
|
||||
<Insets bottom="3.0" left="3.0" right="3.0" top="3.0" />
|
||||
</VBox.margin></CheckBox>
|
||||
<Label fx:id="connectionLabel" />
|
||||
<ButtonBar prefHeight="40.0" prefWidth="200.0">
|
||||
<buttons>
|
||||
<Button mnemonicParsing="false" onAction="#abortLogin" text="Close">
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
</Button>
|
||||
<Button mnemonicParsing="false" onAction="#offlineModeButtonPressed" text="Offline mode" />
|
||||
<Button defaultButton="true" mnemonicParsing="false" onAction="#loginButtonPressed" text="Login" />
|
||||
</buttons>
|
||||
</ButtonBar>
|
||||
</children>
|
||||
<Label text="Password:" GridPane.rowIndex="1">
|
||||
<padding>
|
||||
<Insets bottom="2.0" left="2.0" right="2.0" top="2.0" />
|
||||
</padding>
|
||||
<GridPane.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</GridPane.margin>
|
||||
</Label>
|
||||
<Label fx:id="repeatPasswordLabel" text="Repeat Password:"
|
||||
visible="false" GridPane.rowIndex="2">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</GridPane.margin>
|
||||
<padding>
|
||||
<Insets bottom="2.0" left="2.0" right="2.0" top="2.0" />
|
||||
</padding>
|
||||
</Label>
|
||||
<ClearableTextField fx:id="userTextField"
|
||||
GridPane.columnIndex="1">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="10.0" left="5.0" right="5.0" top="5.0" />
|
||||
</GridPane.margin>
|
||||
</ClearableTextField>
|
||||
<PasswordField fx:id="passwordField"
|
||||
GridPane.columnIndex="1" GridPane.rowIndex="1">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="10.0" left="5.0" right="5.0" top="10.0" />
|
||||
</GridPane.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
</PasswordField>
|
||||
<PasswordField fx:id="repeatPasswordField"
|
||||
visible="false" GridPane.columnIndex="1" GridPane.rowIndex="2">
|
||||
<GridPane.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="10.0" />
|
||||
</GridPane.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
</PasswordField>
|
||||
</children>
|
||||
<VBox.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</VBox.margin>
|
||||
</GridPane>
|
||||
<CheckBox fx:id="registerCheckBox" mnemonicParsing="true"
|
||||
onAction="#registerCheckboxChanged" prefHeight="17.0"
|
||||
prefWidth="181.0" text="_Register">
|
||||
<VBox.margin>
|
||||
<Insets left="5.0" right="3.0" />
|
||||
</VBox.margin>
|
||||
</CheckBox>
|
||||
<Label fx:id="connectionLabel">
|
||||
<VBox.margin>
|
||||
<Insets left="5.0" />
|
||||
</VBox.margin>
|
||||
</Label>
|
||||
<BorderPane prefWidth="200.0">
|
||||
<left>
|
||||
<Button cancelButton="true" mnemonicParsing="false"
|
||||
onAction="#abortLogin" text="Close" BorderPane.alignment="CENTER">
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
<BorderPane.margin>
|
||||
<Insets />
|
||||
</BorderPane.margin>
|
||||
</Button>
|
||||
</left>
|
||||
<center>
|
||||
<Button mnemonicParsing="false"
|
||||
onAction="#offlineModeButtonPressed" text="Offline mode"
|
||||
BorderPane.alignment="CENTER">
|
||||
<BorderPane.margin>
|
||||
<Insets />
|
||||
</BorderPane.margin>
|
||||
</Button>
|
||||
</center>
|
||||
<right>
|
||||
<Button defaultButton="true" mnemonicParsing="false"
|
||||
onAction="#loginButtonPressed" text="Login"
|
||||
BorderPane.alignment="CENTER">
|
||||
<BorderPane.margin>
|
||||
<Insets />
|
||||
</BorderPane.margin>
|
||||
</Button>
|
||||
</right>
|
||||
<VBox.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" />
|
||||
</VBox.margin>
|
||||
</BorderPane>
|
||||
</children>
|
||||
</VBox>
|
||||
|
@ -7,39 +7,50 @@
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
|
||||
<VBox alignment="TOP_RIGHT" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="envoy.client.ui.controller.SettingsScene">
|
||||
<VBox alignment="TOP_RIGHT" maxHeight="-Infinity"
|
||||
maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity"
|
||||
prefHeight="400.0" prefWidth="600.0"
|
||||
xmlns="http://javafx.com/javafx/11.0.1"
|
||||
xmlns:fx="http://javafx.com/fxml/1"
|
||||
fx:controller="envoy.client.ui.controller.SettingsScene">
|
||||
<children>
|
||||
<HBox prefHeight="389.0" prefWidth="600.0">
|
||||
<children>
|
||||
<ListView fx:id="settingsList" onMouseClicked="#settingsListClicked" prefHeight="200.0" prefWidth="200.0">
|
||||
<opaqueInsets>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</opaqueInsets>
|
||||
<HBox.margin>
|
||||
<Insets bottom="10.0" left="10.0" right="5.0" top="10.0" />
|
||||
</HBox.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding></ListView>
|
||||
<TitledPane fx:id="titledPane" collapsible="false" prefHeight="325.0" prefWidth="300.0">
|
||||
<HBox.margin>
|
||||
<Insets bottom="10.0" left="5.0" right="10.0" top="10.0" />
|
||||
</HBox.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding></TitledPane>
|
||||
</children>
|
||||
<children>
|
||||
<ListView fx:id="settingsList"
|
||||
onMouseClicked="#settingsListClicked" prefHeight="200.0"
|
||||
prefWidth="200.0">
|
||||
<opaqueInsets>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</opaqueInsets>
|
||||
<HBox.margin>
|
||||
<Insets bottom="10.0" left="10.0" right="5.0" top="10.0" />
|
||||
</HBox.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
</ListView>
|
||||
<TitledPane fx:id="titledPane" collapsible="false"
|
||||
prefHeight="325.0" prefWidth="300.0">
|
||||
<HBox.margin>
|
||||
<Insets bottom="10.0" left="5.0" right="10.0" top="10.0" />
|
||||
</HBox.margin>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
</TitledPane>
|
||||
</children>
|
||||
</HBox>
|
||||
<Button defaultButton="true" mnemonicParsing="true" onMouseClicked="#backButtonClicked" text="_Back">
|
||||
<opaqueInsets>
|
||||
<Insets />
|
||||
</opaqueInsets>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
<VBox.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</VBox.margin>
|
||||
</Button>
|
||||
<Button defaultButton="true" mnemonicParsing="true"
|
||||
onMouseClicked="#backButtonClicked" text="_Back">
|
||||
<opaqueInsets>
|
||||
<Insets />
|
||||
</opaqueInsets>
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
<VBox.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</VBox.margin>
|
||||
</Button>
|
||||
</children>
|
||||
</VBox>
|
||||
|
BIN
src/main/resources/icons/clear_button_black.png
Normal file
BIN
src/main/resources/icons/clear_button_black.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
BIN
src/main/resources/icons/clear_button_white.png
Normal file
BIN
src/main/resources/icons/clear_button_white.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
Reference in New Issue
Block a user