Improve UI doc and fix minor UI bugs
This commit is contained in:
parent
a3873394be
commit
6d3971be40
20 changed files with 541 additions and 185 deletions
|
@ -43,6 +43,7 @@ public class MainApp extends Application {
|
|||
|
||||
@Override
|
||||
public void stop() throws Exception {
|
||||
// Stop all threads and active connections before exiting
|
||||
userList.destroy();
|
||||
super.stop();
|
||||
}
|
||||
|
|
|
@ -15,13 +15,14 @@ public class ChatHistory {
|
|||
private final DatabaseController db;
|
||||
private final PeerUser user;
|
||||
private final ObservableList<Message> history;
|
||||
private final ArrayList<HistoryLoadedCallback> historyListener;
|
||||
private HistoryLoadedCallback historyListener;
|
||||
private boolean historyLoaded;
|
||||
|
||||
public ChatHistory(PeerUser user) {
|
||||
this.user = user;
|
||||
db = new DatabaseController();
|
||||
history = FXCollections.observableArrayList();
|
||||
historyListener = new ArrayList<>();
|
||||
historyLoaded = false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -29,34 +30,29 @@ public class ChatHistory {
|
|||
*
|
||||
* @param listener The listener to add
|
||||
*/
|
||||
public void addHistoryLoadedListener(HistoryLoadedCallback listener) {
|
||||
historyListener.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a listener for history loaded event
|
||||
*
|
||||
* @param listener The listener to remove
|
||||
*/
|
||||
public void removeHistoryLoadedListener(HistoryLoadedCallback listener) {
|
||||
historyListener.remove(listener);
|
||||
public void setHistoryLoadedListener(HistoryLoadedCallback listener) {
|
||||
historyListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies all listeners of a history loaded event
|
||||
*/
|
||||
private void notifyHistoryLoaded() {
|
||||
historyListener.forEach(l -> l.onHistoryLoaded(user));
|
||||
historyListener.onHistoryLoaded(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads history from database
|
||||
* Loads history from database only if it has not previously been loaded
|
||||
*/
|
||||
public void load() {
|
||||
final Date from = new Date();
|
||||
// Load whole history
|
||||
from.setTime(0);
|
||||
db.getChatHistory(new UserInformation(user), from, new Date(), this::onLoaded);
|
||||
if (!historyLoaded) {
|
||||
final Date from = new Date();
|
||||
// Load whole history
|
||||
from.setTime(0);
|
||||
db.getChatHistory(new UserInformation(user), from, new Date(), this::onLoaded);
|
||||
} else {
|
||||
notifyHistoryLoaded();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -65,6 +61,7 @@ public class ChatHistory {
|
|||
* @param newHistory The fetched history
|
||||
*/
|
||||
private void onLoaded(ArrayList<Message> newHistory) {
|
||||
historyLoaded = true;
|
||||
history.addAll(newHistory);
|
||||
history.sort((message, t1) -> (int) (message.getDate().getTime() - t1.getDate().getTime()));
|
||||
Log.v(getClass().getSimpleName(), "Message history loaded");
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package fr.insa.clavardator.ui;
|
||||
|
||||
/**
|
||||
* Interface used to create callbacks for button press events
|
||||
*/
|
||||
public interface ButtonPressEvent {
|
||||
public void onPress();
|
||||
}
|
||||
|
|
|
@ -7,18 +7,31 @@ import javafx.scene.layout.VBox;
|
|||
import java.net.URL;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
/**
|
||||
* Controller for the error screen
|
||||
*/
|
||||
public class ErrorScreenController implements Initializable {
|
||||
|
||||
@FXML
|
||||
private VBox container;
|
||||
|
||||
/**
|
||||
* Instantly shows the error screen
|
||||
*/
|
||||
public void show() {
|
||||
container.setVisible(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantly hides the error screen
|
||||
*/
|
||||
public void hide() {
|
||||
container.setVisible(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exits the app on button press
|
||||
*/
|
||||
@FXML
|
||||
private void onPress() {
|
||||
System.exit(0);
|
||||
|
|
|
@ -6,13 +6,16 @@ import javafx.fxml.Initializable;
|
|||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
/**
|
||||
* Controller for the loading screen
|
||||
*/
|
||||
public class LoadingScreenController implements Initializable {
|
||||
|
||||
|
||||
@FXML
|
||||
public VBox indicatorOnlyContainer;
|
||||
@FXML
|
||||
|
@ -22,27 +25,45 @@ public class LoadingScreenController implements Initializable {
|
|||
@FXML
|
||||
public StackPane container;
|
||||
|
||||
/**
|
||||
* Instantly shows the loading screen
|
||||
*/
|
||||
public void show() {
|
||||
container.setVisible(true);
|
||||
setLabel(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantly shows the loading screen
|
||||
* @param label The text to display bellow the loading indicator
|
||||
*/
|
||||
public void show(String label) {
|
||||
container.setVisible(true);
|
||||
setLabel(label);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantly hides the loading screen
|
||||
*/
|
||||
public void hide() {
|
||||
container.setVisible(false);
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
/**
|
||||
* Sets the text to display bellow the loading indicator
|
||||
* @param label The text to use
|
||||
*/
|
||||
public void setLabel(@Nullable String label) {
|
||||
loadingLabel.setText(label);
|
||||
final boolean showLabel = label != null && !label.isEmpty();
|
||||
labeledContainer.setVisible(showLabel);
|
||||
indicatorOnlyContainer.setVisible(!showLabel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the main container styles to apply custom styling
|
||||
* @return The style list
|
||||
*/
|
||||
public ObservableList<String> getRootStyle() {
|
||||
return container.getStyleClass();
|
||||
}
|
||||
|
|
|
@ -48,9 +48,11 @@ public class MainController implements Initializable {
|
|||
private JFXSnackbar snackbar;
|
||||
private UserList userList;
|
||||
private boolean historyLoaded;
|
||||
private boolean online;
|
||||
|
||||
public MainController() {
|
||||
historyLoaded = false;
|
||||
online = false;
|
||||
currentUser = CurrentUser.getInstance();
|
||||
currentUser.addObserver(propertyChangeEvent -> {
|
||||
if (propertyChangeEvent.getPropertyName().equals("state")) {
|
||||
|
@ -60,6 +62,14 @@ public class MainController implements Initializable {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* If the current user becomes valid, start the chat.
|
||||
* If it is invalid or not set, show the login screen.
|
||||
* <p>
|
||||
* If the history is not yet loaded or the user is not initialized, do nothing.
|
||||
*
|
||||
* @param newState The new user state
|
||||
*/
|
||||
private void onCurrentUserStateChange(CurrentUser.State newState) {
|
||||
// Only do an action if the history is loaded
|
||||
if (historyLoaded) {
|
||||
|
@ -75,15 +85,32 @@ public class MainController implements Initializable {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the history loaded after the current user, fire the state change event again
|
||||
*/
|
||||
public void onHistoryLoaded() {
|
||||
historyLoaded = true;
|
||||
final CurrentUser.State userState = CurrentUser.getInstance().getState();
|
||||
// If the history loaded after the current user, fire the state change event again
|
||||
if (userState != CurrentUser.State.UNINITIALIZED) {
|
||||
onCurrentUserStateChange(userState);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a dialog allowing the user to set its username.
|
||||
* Depending on the mode, the behavior is different.
|
||||
* <ul>
|
||||
* <li>
|
||||
* On initial mode, canceling the dialog exists the app.
|
||||
* Any other mode shows the chat.
|
||||
* </li>
|
||||
* <li>
|
||||
* On edit mode, confirming the dialog shows a snackbar.
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* @param mode
|
||||
*/
|
||||
private void openEditUsernameDialog(EditUsernameDialogController.Mode mode) {
|
||||
final boolean initialMode = mode == EditUsernameDialogController.Mode.INITIAL;
|
||||
editUserDialogController.setOnCancelListener(() -> {
|
||||
|
@ -101,6 +128,12 @@ public class MainController implements Initializable {
|
|||
Platform.runLater(() -> editUserDialogController.show(root, mode));
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a snackbar
|
||||
*
|
||||
* @param text The text to show
|
||||
* @param mode The mode to use
|
||||
*/
|
||||
private void showSnackbarEvent(String text, SnackbarController.Mode mode) {
|
||||
try {
|
||||
final FXMLLoader loader = new FXMLLoader(getClass().getResource("dialogs/snackbar.fxml"));
|
||||
|
@ -115,12 +148,19 @@ public class MainController implements Initializable {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the about dialog
|
||||
*/
|
||||
private void openAboutDialog() {
|
||||
aboutDialogController.show(root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a broadcast over the network to discover active users.
|
||||
* If any error happens, disconnect the user from chat and ask for reconnection.
|
||||
*/
|
||||
private void discoverActiveUsers() {
|
||||
if (userList != null) {
|
||||
if (userList != null && online) {
|
||||
userList.discoverActiveUsers((e) -> {
|
||||
Log.e(this.getClass().getSimpleName(), "Error discovering users", e);
|
||||
CurrentUser.getInstance().setState(CurrentUser.State.INVALID);
|
||||
|
@ -128,6 +168,10 @@ public class MainController implements Initializable {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start listening to broadcast and chat requests.
|
||||
* If any error happens, disconnect the user from chat and ask for reconnection.
|
||||
*/
|
||||
private void startListening() {
|
||||
if (userList != null) {
|
||||
userList.startDiscoveryListening();
|
||||
|
@ -138,30 +182,46 @@ public class MainController implements Initializable {
|
|||
}
|
||||
}
|
||||
|
||||
private void stopListening() {
|
||||
if (userList != null) {
|
||||
userList.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts network related functions to allow for chat functionality.
|
||||
*/
|
||||
private void startChat() {
|
||||
online = true;
|
||||
listController.setRefreshButtonEnabled(true);
|
||||
Log.v(this.getClass().getSimpleName(), "Chat started");
|
||||
discoverActiveUsers();
|
||||
startListening();
|
||||
Platform.runLater(this::showChat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops any network related functions to disable chat functionality.
|
||||
*/
|
||||
private void endChat() {
|
||||
online = false;
|
||||
listController.setRefreshButtonEnabled(false);
|
||||
Log.v(this.getClass().getSimpleName(), "Chat ended");
|
||||
stopListening();
|
||||
if (userList != null) {
|
||||
userList.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simply shows the chat.
|
||||
* This does not start any network related functions.
|
||||
* Use this for offline browsing.
|
||||
*/
|
||||
private void showChat() {
|
||||
Log.v(this.getClass().getSimpleName(), "Chat shown");
|
||||
loadingController.hide();
|
||||
mainContainer.setVisible(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a screen allowing the user to set its username.
|
||||
*
|
||||
* @param isError True is the screen is shown because of an error, false otherwise
|
||||
*/
|
||||
private void showLogin(boolean isError) {
|
||||
Log.v(this.getClass().getSimpleName(), "Login shown");
|
||||
mainContainer.setVisible(false);
|
||||
|
@ -180,6 +240,9 @@ public class MainController implements Initializable {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows an error screen telling the user the app failed to start
|
||||
*/
|
||||
private void showError() {
|
||||
Log.v(this.getClass().getSimpleName(), "Error shown");
|
||||
mainContainer.setVisible(false);
|
||||
|
@ -193,12 +256,15 @@ public class MainController implements Initializable {
|
|||
snackbar = new JFXSnackbar(root);
|
||||
|
||||
listController.setUserSelectedListener((user) -> chatController.setRemoteUser(user));
|
||||
chatController.addAttachmentListener(() -> System.out.println("attach event"));
|
||||
chatController.addSendErrorListener((e) -> showSnackbarEvent("Erreur: Message non envoyé", SnackbarController.Mode.ERROR));
|
||||
toolbarController.addEditListener(() -> openEditUsernameDialog(EditUsernameDialogController.Mode.EDIT));
|
||||
toolbarController.addAboutListener(this::openAboutDialog);
|
||||
chatController.setAttachmentListener(() -> System.out.println("attach event"));
|
||||
chatController.setSendErrorListener((e) -> showSnackbarEvent("Erreur: Message non envoyé", SnackbarController.Mode.ERROR));
|
||||
toolbarController.setEditListener(() -> openEditUsernameDialog(EditUsernameDialogController.Mode.EDIT));
|
||||
toolbarController.setAboutListener(this::openAboutDialog);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates database if needed, then init current user, and finally load user list
|
||||
*/
|
||||
private void initDb() {
|
||||
final DatabaseController db = new DatabaseController();
|
||||
db.init(() -> {
|
||||
|
@ -212,6 +278,12 @@ public class MainController implements Initializable {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user list to use.
|
||||
* We must set it from the MainApp to allow destroying it on exit.
|
||||
*
|
||||
* @param userList The user list to use
|
||||
*/
|
||||
public void setUserList(UserList userList) {
|
||||
this.userList = userList;
|
||||
listController.setUserList(userList);
|
||||
|
|
|
@ -4,6 +4,11 @@ import javafx.collections.FXCollections;
|
|||
import javafx.collections.ObservableList;
|
||||
import javafx.scene.control.MultipleSelectionModel;
|
||||
|
||||
/**
|
||||
* Model used to disable list selection
|
||||
*
|
||||
* @param <T>
|
||||
*/
|
||||
public class NoSelectionModel<T> extends MultipleSelectionModel<T> {
|
||||
@Override
|
||||
public ObservableList<Integer> getSelectedIndices() {
|
||||
|
|
|
@ -11,29 +11,40 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
/**
|
||||
* Controller for the main screen toolbar
|
||||
*/
|
||||
public class ToolbarController implements Initializable {
|
||||
|
||||
@FXML
|
||||
private Label currentUsernameLabel;
|
||||
|
||||
private List<ButtonPressEvent> editListeners;
|
||||
private List<ButtonPressEvent> aboutListeners;
|
||||
private ButtonPressEvent editListeners;
|
||||
private ButtonPressEvent aboutListeners;
|
||||
|
||||
public void addEditListener(ButtonPressEvent listener) {
|
||||
editListeners.add(listener);
|
||||
public void setEditListener(ButtonPressEvent listener) {
|
||||
editListeners = listener;
|
||||
}
|
||||
public void addAboutListener(ButtonPressEvent listener) {
|
||||
aboutListeners.add(listener);
|
||||
public void setAboutListener(ButtonPressEvent listener) {
|
||||
aboutListeners = listener;
|
||||
}
|
||||
|
||||
public void onEditPress() {
|
||||
editListeners.forEach(ButtonPressEvent::onPress);
|
||||
if (editListeners != null) {
|
||||
editListeners.onPress();
|
||||
}
|
||||
}
|
||||
|
||||
public void onAboutPress() {
|
||||
aboutListeners.forEach(ButtonPressEvent::onPress);
|
||||
if (aboutListeners != null) {
|
||||
aboutListeners.onPress();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the text on current username change
|
||||
*
|
||||
* @param propertyChangeEvent The change event
|
||||
*/
|
||||
private void onUsernameChange(PropertyChangeEvent propertyChangeEvent) {
|
||||
if (propertyChangeEvent.getPropertyName().equals("username")) {
|
||||
final String newUsername = (String) propertyChangeEvent.getNewValue();
|
||||
|
@ -44,7 +55,5 @@ public class ToolbarController implements Initializable {
|
|||
@Override
|
||||
public void initialize(URL location, ResourceBundle resources) {
|
||||
CurrentUser.getInstance().addObserver(this::onUsernameChange);
|
||||
editListeners = new ArrayList<>();
|
||||
aboutListeners = new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,9 @@ import javafx.scene.layout.VBox;
|
|||
import java.net.URL;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
/**
|
||||
* Controller for the chat window
|
||||
*/
|
||||
public class ChatController implements Initializable {
|
||||
|
||||
@FXML
|
||||
|
@ -32,40 +35,50 @@ public class ChatController implements Initializable {
|
|||
@FXML
|
||||
private VBox emptyContainer;
|
||||
private PeerUser remoteUser;
|
||||
private final ChatHistory.HistoryLoadedCallback onHistoryLoaded = (PeerUser user) -> {
|
||||
|
||||
public void setAttachmentListener(ButtonPressEvent listener) {
|
||||
chatFooterController.setAttachmentListener(listener);
|
||||
}
|
||||
public void setSendErrorListener(ErrorCallback listener) {
|
||||
chatFooterController.setSendErrorListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the user that finished loading is the right one then set the chat state to done
|
||||
* @param user The user that finished loading
|
||||
*/
|
||||
private void onHistoryLoaded (PeerUser user) {
|
||||
if (user.equals(remoteUser)) {
|
||||
setState(State.DONE);
|
||||
scrollToEnd();
|
||||
}
|
||||
};
|
||||
|
||||
public void addAttachmentListener(ButtonPressEvent listener) {
|
||||
chatFooterController.addAttachmentListener(listener);
|
||||
}
|
||||
|
||||
public void addSendErrorListener(ErrorCallback listener) {
|
||||
chatFooterController.addSendErrorListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the remote user to chat with and subscribe to its messages
|
||||
*
|
||||
* @param remoteUser The user to chat with
|
||||
*/
|
||||
public void setRemoteUser(PeerUser remoteUser) {
|
||||
this.chatFooterController.setRemoteUser(remoteUser);
|
||||
this.chatHeaderController.setRemoteUser(remoteUser);
|
||||
setState(State.LOADING);
|
||||
|
||||
if (this.remoteUser != null) {
|
||||
this.remoteUser.getHistory().removeHistoryLoadedListener(onHistoryLoaded);
|
||||
}
|
||||
this.remoteUser = remoteUser;
|
||||
|
||||
final ChatHistory history = remoteUser.getHistory();
|
||||
history.addHistoryLoadedListener(onHistoryLoaded);
|
||||
history.setHistoryLoadedListener(this::onHistoryLoaded);
|
||||
messageList.setItems(history.getHistory());
|
||||
messageList.getItems().addListener((ListChangeListener<? super Message>) (c) -> {
|
||||
c.next();
|
||||
// Make sure we always have the latest item on screen
|
||||
scrollToEnd();
|
||||
});
|
||||
history.load();
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll to the end of the message list
|
||||
*/
|
||||
private void scrollToEnd() {
|
||||
final int size = messageList.getItems().size();
|
||||
if (size > 0) {
|
||||
|
@ -73,11 +86,26 @@ public class ChatController implements Initializable {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the chat state.
|
||||
* <ul>
|
||||
* <li>
|
||||
* On Initial state, show an empty screen
|
||||
* </li>
|
||||
* <li>
|
||||
* on Loading state, show the loading screen
|
||||
* </li>
|
||||
* <li>
|
||||
* on Done state, show the chat
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* @param state The new state to set
|
||||
*/
|
||||
private void setState(State state) {
|
||||
switch (state) {
|
||||
case INITIAL:
|
||||
emptyContainer.setVisible(true);
|
||||
chatFooterController.setEnabled(false);
|
||||
chatContainer.setVisible(false);
|
||||
loadingController.hide();
|
||||
messageList.setItems(null);
|
||||
|
@ -85,11 +113,9 @@ public class ChatController implements Initializable {
|
|||
case LOADING:
|
||||
chatContainer.setVisible(true);
|
||||
loadingController.show();
|
||||
chatFooterController.setEnabled(false);
|
||||
emptyContainer.setVisible(false);
|
||||
break;
|
||||
case DONE:
|
||||
chatFooterController.setEnabled(true);
|
||||
chatContainer.setVisible(true);
|
||||
emptyContainer.setVisible(false);
|
||||
loadingController.hide();
|
||||
|
|
|
@ -6,18 +6,21 @@ import fr.insa.clavardator.ui.ButtonPressEvent;
|
|||
import fr.insa.clavardator.users.PeerUser;
|
||||
import fr.insa.clavardator.util.ErrorCallback;
|
||||
import fr.insa.clavardator.util.Log;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.layout.HBox;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
/**
|
||||
* Controller for the chat input field and associated buttons
|
||||
*/
|
||||
public class ChatFooterController implements Initializable {
|
||||
|
||||
@FXML
|
||||
|
@ -27,25 +30,38 @@ public class ChatFooterController implements Initializable {
|
|||
@FXML
|
||||
private JFXButton sendButton;
|
||||
|
||||
private List<ButtonPressEvent> attachmentListeners;
|
||||
private List<ErrorCallback> sendErrorListeners;
|
||||
private ButtonPressEvent attachmentListeners;
|
||||
private ErrorCallback sendErrorListeners;
|
||||
|
||||
private PeerUser remoteUser;
|
||||
private HashMap<PeerUser, String> savedText;
|
||||
|
||||
|
||||
public void addAttachmentListener(ButtonPressEvent listener) {
|
||||
attachmentListeners.add(listener);
|
||||
public void setAttachmentListener(ButtonPressEvent listener) {
|
||||
attachmentListeners = listener;
|
||||
}
|
||||
public void addSendErrorListener(ErrorCallback listener) {
|
||||
sendErrorListeners.add(listener);
|
||||
|
||||
public void setSendErrorListener(ErrorCallback listener) {
|
||||
sendErrorListeners = listener;
|
||||
}
|
||||
|
||||
public void onAttachmentPress() {
|
||||
attachmentListeners.forEach(ButtonPressEvent::onPress);
|
||||
if (attachmentListeners != null) {
|
||||
attachmentListeners.onPress();
|
||||
}
|
||||
}
|
||||
|
||||
public void onSendError(Exception e) {
|
||||
Log.e(this.getClass().getSimpleName(), "Error: Could not send message", e);
|
||||
if (sendErrorListeners != null) {
|
||||
sendErrorListeners.onError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the input text is not empty and the remote user set, send the message
|
||||
*/
|
||||
public void onSend() {
|
||||
if(!textField.getText().isEmpty()) {
|
||||
if (!isTextFieldEmpty()) {
|
||||
if (remoteUser != null) {
|
||||
remoteUser.sendTextMessage(textField.getText(), this::onSendError);
|
||||
} else {
|
||||
|
@ -55,15 +71,20 @@ public class ChatFooterController implements Initializable {
|
|||
}
|
||||
}
|
||||
|
||||
public void onSendError(Exception e) {
|
||||
Log.e(this.getClass().getSimpleName(), "Error: Could not send message", e);
|
||||
sendErrorListeners.forEach((l) -> l.onError(e));
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
/**
|
||||
* Enables or disables the chat controls
|
||||
*
|
||||
* @param enabled True to enable, false otherwise
|
||||
*/
|
||||
private void setEnabled(boolean enabled) {
|
||||
container.setDisable(!enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find saved input for the current user
|
||||
*
|
||||
* @return The saved text, or an empty string if none found
|
||||
*/
|
||||
private String findSavedText() {
|
||||
String text = null;
|
||||
if (remoteUser != null) {
|
||||
|
@ -72,22 +93,74 @@ public class ChatFooterController implements Initializable {
|
|||
return text != null ? text : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the give text for the current user.
|
||||
* This is used to save input when jumping between users
|
||||
*
|
||||
* @param text The text to save
|
||||
*/
|
||||
private void saveText(String text) {
|
||||
if (remoteUser != null) {
|
||||
savedText.put(remoteUser, text);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves text and checks if we should disable the send button on input change.
|
||||
*
|
||||
* @param observable The observed string
|
||||
* @param oldText The previous text
|
||||
* @param newText The new text
|
||||
*/
|
||||
public void onTextChange(ObservableValue<? extends String> observable, String oldText, String newText) {
|
||||
saveText(newText);
|
||||
sendButton.setDisable(isTextFieldEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the text field is empty
|
||||
*
|
||||
* @return True if empty, false otherwise
|
||||
*/
|
||||
private boolean isTextFieldEmpty() {
|
||||
return textField.getText().isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates shown username and item color when user changes
|
||||
*
|
||||
* @param propertyChangeEvent The change event
|
||||
*/
|
||||
private void onUserStateChange(PropertyChangeEvent propertyChangeEvent) {
|
||||
final String propName = propertyChangeEvent.getPropertyName();
|
||||
if (propName.equals("state")) {
|
||||
final PeerUser.State newState = (PeerUser.State) propertyChangeEvent.getNewValue();
|
||||
Platform.runLater(() -> setEnabled(newState == PeerUser.State.CONNECTED));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the remote user to send messages to, and tries to find saved input.
|
||||
*
|
||||
* @param user The remote user to set
|
||||
*/
|
||||
public void setRemoteUser(PeerUser user) {
|
||||
if (this.remoteUser == null || !this.remoteUser.equals(user)) {
|
||||
if (this.remoteUser != null) {
|
||||
// remove old observer before setting new user
|
||||
this.remoteUser.removeObserver(this::onUserStateChange);
|
||||
}
|
||||
this.remoteUser = user;
|
||||
user.addObserver(this::onUserStateChange);
|
||||
textField.setText(findSavedText());
|
||||
sendButton.setDisable(isTextFieldEmpty());
|
||||
setEnabled(user.isActive());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(URL location, ResourceBundle resources) {
|
||||
savedText = new HashMap<>();
|
||||
attachmentListeners = new ArrayList<>();
|
||||
sendErrorListeners = new ArrayList<>();
|
||||
textField.textProperty().addListener(this::onTextChange);
|
||||
textField.setOnKeyPressed(event -> {
|
||||
if (event.getCode() == KeyCode.ENTER) {
|
||||
|
@ -96,14 +169,4 @@ public class ChatFooterController implements Initializable {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isTextFieldEmpty() {
|
||||
return textField.getText().equals("");
|
||||
}
|
||||
|
||||
public void setRemoteUser(PeerUser remoteUser) {
|
||||
this.remoteUser = remoteUser;
|
||||
textField.setText(findSavedText());
|
||||
sendButton.setDisable(isTextFieldEmpty());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,9 @@ import java.net.URL;
|
|||
import java.util.ArrayList;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
/**
|
||||
* Controller for the chat title
|
||||
*/
|
||||
public class ChatHeaderController implements Initializable {
|
||||
|
||||
@FXML
|
||||
|
@ -20,10 +23,11 @@ public class ChatHeaderController implements Initializable {
|
|||
private UserActiveIndicatorController indicatorController;
|
||||
private PeerUser user;
|
||||
|
||||
@Override
|
||||
public void initialize(URL location, ResourceBundle resources) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates username on remote user change
|
||||
*
|
||||
* @param propertyChangeEvent THe change event
|
||||
*/
|
||||
private void onUsernameChange(PropertyChangeEvent propertyChangeEvent) {
|
||||
if (propertyChangeEvent.getPropertyName().equals("username")) {
|
||||
final String newUsername = (String) propertyChangeEvent.getNewValue();
|
||||
|
@ -31,6 +35,11 @@ public class ChatHeaderController implements Initializable {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the remote user we are currently chatting to
|
||||
*
|
||||
* @param remoteUser The remote user to set
|
||||
*/
|
||||
public void setRemoteUser(PeerUser remoteUser) {
|
||||
if (this.user != null) {
|
||||
// remove old observer before setting new user
|
||||
|
@ -42,4 +51,7 @@ public class ChatHeaderController implements Initializable {
|
|||
indicatorController.setUser(remoteUser);
|
||||
indicatorController.setSize(10.0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(URL location, ResourceBundle resources) {}
|
||||
}
|
||||
|
|
|
@ -24,25 +24,35 @@ public class MessageListItemController implements Initializable {
|
|||
@FXML
|
||||
private Label timestamp;
|
||||
|
||||
@Override
|
||||
public void initialize(URL url, ResourceBundle rb) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the message to display
|
||||
*
|
||||
* @param message The message to display
|
||||
*/
|
||||
public void setMessage(Message message) {
|
||||
if (!message.equals(currentMessage)) {
|
||||
currentMessage = message;
|
||||
button.setText(message.getText());
|
||||
timestamp.setText(DateFormat.getTimeInstance().format(message.getDate()));
|
||||
clearBackground();
|
||||
if (CurrentUser.getInstance().isLocalId(message.getSender().id)) {
|
||||
container.setAlignment(Pos.CENTER_RIGHT);
|
||||
button.getStyleClass().remove("message-other");
|
||||
button.getStyleClass().add("message-self");
|
||||
} else {
|
||||
container.setAlignment(Pos.CENTER_LEFT);
|
||||
button.getStyleClass().remove("message-self");
|
||||
button.getStyleClass().add("message-other");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes any style applied to the background
|
||||
*/
|
||||
private void clearBackground() {
|
||||
button.getStyleClass().remove("message-self");
|
||||
button.getStyleClass().remove("message-other");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(URL url, ResourceBundle rb) {}
|
||||
}
|
|
@ -1,22 +1,16 @@
|
|||
package fr.insa.clavardator.ui.dialogs;
|
||||
|
||||
import com.jfoenix.controls.JFXButton;
|
||||
import com.jfoenix.controls.JFXDialog;
|
||||
import com.jfoenix.controls.JFXTextField;
|
||||
import com.jfoenix.validation.base.ValidatorBase;
|
||||
import fr.insa.clavardator.ui.ButtonPressEvent;
|
||||
import javafx.beans.property.ReadOnlyBooleanWrapper;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.StackPane;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
/**
|
||||
* Controller for the about dialog
|
||||
*/
|
||||
public class AboutDialogController implements Initializable {
|
||||
|
||||
@FXML
|
||||
|
|
|
@ -18,14 +18,11 @@ import javafx.scene.layout.StackPane;
|
|||
import java.net.URL;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
/**
|
||||
* Controller for the username edit dialog
|
||||
*/
|
||||
public class EditUsernameDialogController implements Initializable {
|
||||
|
||||
enum State {
|
||||
VALID,
|
||||
INVALID,
|
||||
NETWORK
|
||||
}
|
||||
|
||||
@FXML
|
||||
private Label titleLabel;
|
||||
@FXML
|
||||
|
@ -45,8 +42,6 @@ public class EditUsernameDialogController implements Initializable {
|
|||
private ButtonPressEvent cancelListener;
|
||||
private UserList userList;
|
||||
|
||||
private final int MAX_LENGTH = 16;
|
||||
|
||||
@FXML
|
||||
private void onConfirm() {
|
||||
setLocked(true);
|
||||
|
@ -76,6 +71,16 @@ public class EditUsernameDialogController implements Initializable {
|
|||
this.cancelListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays the dialog show animation
|
||||
*
|
||||
* @implNote WARNING: Do not try to open the dialog instantly after closing it.
|
||||
* Due to JFoenix animations, the dialog will not reopen.
|
||||
* If you must, wait at least 500ms before reopening.
|
||||
*
|
||||
* @param root The dialog's root component
|
||||
* @param mode The dialog display mode
|
||||
*/
|
||||
public void show(StackPane root, Mode mode) {
|
||||
setLocked(false);
|
||||
setFieldError(State.VALID);
|
||||
|
@ -84,10 +89,22 @@ public class EditUsernameDialogController implements Initializable {
|
|||
dialog.show(root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the dialog.
|
||||
*
|
||||
* @implNote WARNING: Do not try to open the dialog instantly after closing it.
|
||||
* Due to JFoenix animations, the dialog will not reopen.
|
||||
* If you must, wait at least 500ms before reopening.
|
||||
*/
|
||||
public void hide() {
|
||||
dialog.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks the dialog on screen or frees it.
|
||||
*
|
||||
* @param state True to lock, false to unlock
|
||||
*/
|
||||
private void setLocked(boolean state) {
|
||||
confirmButton.setDisable(state || textField.getText().isEmpty());
|
||||
cancelButton.setDisable(state);
|
||||
|
@ -95,20 +112,38 @@ public class EditUsernameDialogController implements Initializable {
|
|||
textField.setDisable(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the input error
|
||||
*
|
||||
* @param state The input state to set
|
||||
*/
|
||||
private void setFieldError(State state) {
|
||||
currentState = state;
|
||||
textField.validate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables save button if username is not empty, and stop input at max length.
|
||||
*
|
||||
* @param observable The observed string
|
||||
* @param oldText The previous text
|
||||
* @param newText The new text
|
||||
*/
|
||||
public void onUsernameChange(ObservableValue<? extends String> observable, String oldText, String newText) {
|
||||
setFieldError(State.VALID);
|
||||
confirmButton.setDisable(newText.isEmpty());
|
||||
final int MAX_LENGTH = 16;
|
||||
if (textField.getText().length() > MAX_LENGTH) {
|
||||
String s = textField.getText().substring(0, MAX_LENGTH);
|
||||
textField.setText(s);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the dialog display mode
|
||||
*
|
||||
* @param mode The mode to set
|
||||
*/
|
||||
public void setMode(Mode mode) {
|
||||
switch (mode) {
|
||||
case INITIAL:
|
||||
|
@ -132,6 +167,32 @@ public class EditUsernameDialogController implements Initializable {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Propagates the username change to active users and unlocks the dialog.
|
||||
*/
|
||||
private void onSuccess() {
|
||||
setFieldError(State.VALID);
|
||||
setLocked(false);
|
||||
hide();
|
||||
final String newName = textField.getText();
|
||||
CurrentUser.getInstance().setUsername(newName);
|
||||
if (successListener != null) {
|
||||
successListener.onPress();
|
||||
}
|
||||
if (userList != null) {
|
||||
userList.propagateUsernameChange();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the field error and unlocks the dialog
|
||||
*/
|
||||
private void onError() {
|
||||
validator.setMessage("Nom d'utilisateur déjà utilisé");
|
||||
setFieldError(State.INVALID);
|
||||
setLocked(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(URL location, ResourceBundle resources) {
|
||||
validator = new Validator();
|
||||
|
@ -149,33 +210,17 @@ public class EditUsernameDialogController implements Initializable {
|
|||
});
|
||||
}
|
||||
|
||||
private void onSuccess() {
|
||||
setFieldError(State.VALID);
|
||||
setLocked(false);
|
||||
hide();
|
||||
final String newName = textField.getText();
|
||||
CurrentUser.getInstance().setUsername(newName);
|
||||
if (successListener != null) {
|
||||
successListener.onPress();
|
||||
}
|
||||
if (userList != null) {
|
||||
userList.propagateUsernameChange();
|
||||
}
|
||||
}
|
||||
|
||||
private void onError() {
|
||||
validator.setMessage("Nom d'utilisateur invalide");
|
||||
setFieldError(State.INVALID);
|
||||
setLocked(false);
|
||||
}
|
||||
|
||||
|
||||
public enum Mode {
|
||||
INITIAL,
|
||||
ERROR,
|
||||
EDIT
|
||||
}
|
||||
|
||||
enum State {
|
||||
VALID,
|
||||
INVALID
|
||||
}
|
||||
|
||||
class Validator extends ValidatorBase {
|
||||
@Override
|
||||
protected void eval() {
|
||||
|
|
|
@ -12,6 +12,9 @@ import java.net.URL;
|
|||
import java.util.Arrays;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
/**
|
||||
* Controller for the notification snackbar
|
||||
*/
|
||||
public class SnackbarController implements Initializable {
|
||||
|
||||
@FXML
|
||||
|
@ -21,10 +24,20 @@ public class SnackbarController implements Initializable {
|
|||
@FXML
|
||||
public HBox container;
|
||||
|
||||
/**
|
||||
* Sets the snackbar text
|
||||
*
|
||||
* @param text The text to display
|
||||
*/
|
||||
public void setText(String text) {
|
||||
label.setText(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the snackbar display mode
|
||||
*
|
||||
* @param mode The mode to set
|
||||
*/
|
||||
public void setMode(Mode mode) {
|
||||
ObservableList<String> styleClasses = container.getStyleClass();
|
||||
styleClasses.clear();
|
||||
|
@ -52,6 +65,7 @@ public class SnackbarController implements Initializable {
|
|||
@Override
|
||||
public void initialize(URL location, ResourceBundle resources) {
|
||||
setMode(Mode.INFO);
|
||||
// Set a small shadow bellow the snackbar
|
||||
JFXDepthManager.setDepth(container, 1);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package fr.insa.clavardator.ui.users;
|
||||
|
||||
import fr.insa.clavardator.users.PeerUser;
|
||||
import fr.insa.clavardator.util.Log;
|
||||
import javafx.application.Platform;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
|
@ -11,17 +10,20 @@ import java.beans.PropertyChangeEvent;
|
|||
import java.net.URL;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
/**
|
||||
* Controller for the user indicator, showing the connection status
|
||||
*/
|
||||
public class UserActiveIndicatorController implements Initializable {
|
||||
|
||||
@FXML
|
||||
private Circle circle;
|
||||
private PeerUser user;
|
||||
|
||||
@Override
|
||||
public void initialize(URL location, ResourceBundle resources) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the new circle color
|
||||
*
|
||||
* @param isActive True for active color, false for inactive
|
||||
*/
|
||||
private void updateState(boolean isActive) {
|
||||
circle.getStyleClass().clear();
|
||||
if (isActive) {
|
||||
|
@ -31,6 +33,12 @@ public class UserActiveIndicatorController implements Initializable {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the indicator color on user state change.
|
||||
* The indicator becomes active only if the user state goes to connected.
|
||||
*
|
||||
* @param propertyChangeEvent The change event
|
||||
*/
|
||||
private void onStateChange(PropertyChangeEvent propertyChangeEvent) {
|
||||
if (propertyChangeEvent.getPropertyName().equals("state")) {
|
||||
final PeerUser.State newState = (PeerUser.State) propertyChangeEvent.getNewValue();
|
||||
|
@ -38,6 +46,11 @@ public class UserActiveIndicatorController implements Initializable {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user associated to this indicator
|
||||
*
|
||||
* @param user The user to set
|
||||
*/
|
||||
public void setUser(PeerUser user) {
|
||||
if (this.user != null) {
|
||||
// remove old observer before setting new user
|
||||
|
@ -48,8 +61,16 @@ public class UserActiveIndicatorController implements Initializable {
|
|||
updateState(user.isActive());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the indicator's radius
|
||||
*
|
||||
* @param value The radius in pixels
|
||||
*/
|
||||
public void setSize(double value) {
|
||||
circle.setRadius(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(URL location, ResourceBundle resources) {}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package fr.insa.clavardator.ui.users;
|
||||
|
||||
import com.jfoenix.controls.JFXButton;
|
||||
import fr.insa.clavardator.ui.ButtonPressEvent;
|
||||
import fr.insa.clavardator.ui.UserSelectedEvent;
|
||||
import fr.insa.clavardator.users.PeerUser;
|
||||
|
@ -15,26 +16,34 @@ import java.net.URL;
|
|||
import java.util.ResourceBundle;
|
||||
|
||||
public class UserListController implements Initializable {
|
||||
@FXML
|
||||
private ListView<PeerUser> peerUserListView;
|
||||
@FXML
|
||||
private JFXButton refreshButton;
|
||||
|
||||
private ButtonPressEvent refreshUserListener;
|
||||
private UserSelectedEvent userSelectedListener;
|
||||
@FXML
|
||||
private ListView<PeerUser> peerUserListView;
|
||||
private UserList userList;
|
||||
|
||||
public UserListController() {
|
||||
}
|
||||
public UserListController() {}
|
||||
|
||||
public void setRefreshUserListener(ButtonPressEvent listener) {
|
||||
refreshUserListener = listener;
|
||||
}
|
||||
|
||||
public void setUserSelectedListener(UserSelectedEvent listener) {
|
||||
userSelectedListener = listener;
|
||||
}
|
||||
|
||||
public void onRefreshUserListPress() {
|
||||
refreshUserListener.onPress();
|
||||
if (refreshUserListener != null) {
|
||||
refreshUserListener.onPress();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables the refresh button
|
||||
* @param enabled True to enable, false otherwise
|
||||
*/
|
||||
public void setRefreshButtonEnabled(boolean enabled) {
|
||||
refreshButton.setDisable(!enabled);
|
||||
}
|
||||
|
||||
private void onUserSelected(@org.jetbrains.annotations.NotNull PeerUser user) {
|
||||
|
@ -54,6 +63,10 @@ public class UserListController implements Initializable {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add user to UI if not already present
|
||||
* @param user The new user to display
|
||||
*/
|
||||
private void onUserConnected(PeerUser user) {
|
||||
Platform.runLater(() -> {
|
||||
if (!peerUserListView.getItems().contains(user)) {
|
||||
|
@ -65,8 +78,11 @@ public class UserListController implements Initializable {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user list to subscribe to
|
||||
* @param userList The user list to use
|
||||
*/
|
||||
public void setUserList(UserList userList) {
|
||||
this.userList = userList;
|
||||
userList.addNewUserObserver(this::onUserConnected);
|
||||
userList.setNewUserObserver(this::onUserConnected);
|
||||
}
|
||||
}
|
|
@ -19,8 +19,8 @@ public class UserListItemController implements Initializable {
|
|||
private UserActiveIndicatorController indicatorController;
|
||||
|
||||
private ButtonPressEvent listener;
|
||||
|
||||
private PeerUser user;
|
||||
private boolean selected;
|
||||
|
||||
public void setOnPressListener(ButtonPressEvent listener) {
|
||||
this.listener = listener;
|
||||
|
@ -32,51 +32,83 @@ public class UserListItemController implements Initializable {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current item selection state
|
||||
*
|
||||
* @param selected True to select the item, false otherwise
|
||||
*/
|
||||
public void setSelected(boolean selected) {
|
||||
this.selected = selected;
|
||||
if (selected)
|
||||
setBackgroundSelected();
|
||||
else
|
||||
resetBackground();
|
||||
resetBackground(this.user.isActive());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(URL location, ResourceBundle resources) {
|
||||
resetBackground();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates shown username and item color when user changes
|
||||
*
|
||||
* @param propertyChangeEvent The change event
|
||||
*/
|
||||
private void onUsernameChange(PropertyChangeEvent propertyChangeEvent) {
|
||||
if (propertyChangeEvent.getPropertyName().equals("username")) {
|
||||
final String propName = propertyChangeEvent.getPropertyName();
|
||||
if (propName.equals("username")) {
|
||||
final String newUsername = (String) propertyChangeEvent.getNewValue();
|
||||
Platform.runLater(() -> button.setText(newUsername));
|
||||
} else if (propName.equals("state") && !selected) {
|
||||
final PeerUser.State newState = (PeerUser.State) propertyChangeEvent.getNewValue();
|
||||
Platform.runLater(() -> resetBackground(newState == PeerUser.State.CONNECTED));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user to subscribe to for this item.
|
||||
*
|
||||
* @param user The user to subscribe to
|
||||
*/
|
||||
public void setUser(PeerUser user) {
|
||||
if (this.user != null) {
|
||||
// remove old observer before setting new user
|
||||
this.user.removeObserver(this::onUsernameChange);
|
||||
if (this.user == null || !this.user.equals(user)) {
|
||||
if (this.user != null) {
|
||||
// remove old observer before setting new user
|
||||
this.user.removeObserver(this::onUsernameChange);
|
||||
}
|
||||
this.user = user;
|
||||
user.addObserver(this::onUsernameChange);
|
||||
indicatorController.setUser(user);
|
||||
button.setText(user.getUsername());
|
||||
setSelected(false);
|
||||
}
|
||||
this.user = user;
|
||||
user.addObserver(this::onUsernameChange);
|
||||
indicatorController.setUser(user);
|
||||
button.setText(user.getUsername());
|
||||
resetBackground();
|
||||
}
|
||||
|
||||
private void resetBackground() {
|
||||
if (user != null && user.isActive()) {
|
||||
button.getStyleClass().remove("inactive-user-item");
|
||||
/**
|
||||
* Sets the background color to active or inactive
|
||||
*/
|
||||
private void resetBackground(boolean isActive) {
|
||||
clearBackground();
|
||||
if (isActive) {
|
||||
button.getStyleClass().add("active-user-item");
|
||||
} else {
|
||||
button.getStyleClass().remove("active-user-item");
|
||||
button.getStyleClass().add("inactive-user-item");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the background color in selected mode
|
||||
*/
|
||||
private void setBackgroundSelected() {
|
||||
button.getStyleClass().remove("inactive-user-item");
|
||||
button.getStyleClass().remove("active-user-item");
|
||||
clearBackground();
|
||||
button.getStyleClass().add("selected-user-item");
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the background of any color class
|
||||
*/
|
||||
private void clearBackground() {
|
||||
button.getStyleClass().remove("inactive-user-item");
|
||||
button.getStyleClass().remove("active-user-item");
|
||||
button.getStyleClass().remove("selected-user-item");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(URL location, ResourceBundle resources) {}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ public class UserList {
|
|||
private final Map<Integer, PeerUser> inactiveUsers = new HashMap<>();
|
||||
private final Map<Integer, PeerUser> activeUsers = new HashMap<>();
|
||||
|
||||
private final ArrayList<UserConnectionCallback> newUsersObservers = new ArrayList<>();
|
||||
private UserConnectionCallback newUsersObservers = null;
|
||||
|
||||
private final DatabaseController db = new DatabaseController();
|
||||
private final NetDiscoverer netDiscoverer = new NetDiscoverer();
|
||||
|
@ -33,8 +33,8 @@ public class UserList {
|
|||
*
|
||||
* @param connectionCallback The function to add as listener
|
||||
*/
|
||||
public void addNewUserObserver(UserConnectionCallback connectionCallback) {
|
||||
newUsersObservers.add(connectionCallback);
|
||||
public void setNewUserObserver(UserConnectionCallback connectionCallback) {
|
||||
newUsersObservers = connectionCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -43,7 +43,9 @@ public class UserList {
|
|||
* @param user The newly connected user
|
||||
*/
|
||||
private void notifyNewUserObservers(PeerUser user) {
|
||||
newUsersObservers.forEach(callback -> callback.onUserConnected(user));
|
||||
if (newUsersObservers != null) {
|
||||
newUsersObservers.onUserConnected(user);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -52,7 +54,7 @@ public class UserList {
|
|||
* Observers are notified for each new successful connection.
|
||||
*
|
||||
* @param errorCallback The function to call on error
|
||||
* @see UserList#addNewUserObserver(UserConnectionCallback)
|
||||
* @see UserList#setNewUserObserver(UserConnectionCallback)
|
||||
*/
|
||||
public void discoverActiveUsers(ErrorCallback errorCallback) {
|
||||
netDiscoverer.discoverActiveUsers("CLAVARDATOR_BROADCAST", (ipAddr, data) -> {
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<VBox AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0"
|
||||
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||
<HBox alignment="CENTER" prefHeight="64.0" spacing="10.0">
|
||||
<JFXButton mnemonicParsing="false" text="Utilisateurs" onMouseClicked="#onRefreshUserListPress">
|
||||
<JFXButton mnemonicParsing="false" text="Utilisateurs" onMouseClicked="#onRefreshUserListPress" fx:id="refreshButton">
|
||||
<graphic>
|
||||
<FontIcon iconLiteral="fas-sync-alt" iconSize="24"/>
|
||||
</graphic>
|
||||
|
|
Loading…
Reference in a new issue