diff --git a/Client/src/main/java/dev/wiing/gossip/client/Connection.java b/Client/src/main/java/dev/wiing/gossip/client/Connection.java index c316ac7..5c5b935 100644 --- a/Client/src/main/java/dev/wiing/gossip/client/Connection.java +++ b/Client/src/main/java/dev/wiing/gossip/client/Connection.java @@ -2,12 +2,15 @@ package dev.wiing.gossip.client; import dev.wiing.gossip.lib.PacketHandler; import dev.wiing.gossip.lib.PacketManager; +import dev.wiing.gossip.lib.PacketMetrics; import dev.wiing.gossip.lib.data.LongData; import dev.wiing.gossip.lib.models.SecretUser; import dev.wiing.gossip.lib.models.Topic; import dev.wiing.gossip.lib.models.User; import dev.wiing.gossip.lib.packets.*; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; import java.io.IOException; import java.net.Socket; import java.net.SocketException; @@ -18,6 +21,13 @@ public class Connection { private static final Connection instance = new Connection(); + private PropertyChangeSupport changeSupport; + + private boolean recordStats = false; + + private String compiledConnectionStats = "CONNECTION STATS\nNeed an update"; + private String compiledCacheStats = "CACHE STATS\nNeed an update"; + private final PacketManager packetManager = new PacketManager(); private final PacketHandler packetHandler = new PacketHandler(); @@ -32,6 +42,7 @@ public class Connection { private Connection() { packetManager.registerPackets(); + changeSupport = new PropertyChangeSupport(this); } public static Connection getInstance() { @@ -51,6 +62,8 @@ public class Connection { public PacketHandlerRunnable(Connection connection) { this.connection = connection; + + connection.packetHandler.addAllPacketsToMap(connection.packetManager.getPacketMap().values()); } @Override @@ -69,6 +82,8 @@ public class Connection { if (!connection.getPacketHandler().runPacket(packet)) { connection.queuedPackets.add(packet); } + + connection.updateConnectionStats(); } catch (SocketException e) { break; } catch (IOException e) { @@ -83,6 +98,91 @@ public class Connection { handlerThread.start(); } + public void updateConnectionStats() { + if (!recordStats) return; + + StringBuilder sb = new StringBuilder(); + + PacketMetrics m = packetManager.getPacketMetrics(); + + sb.append("OUTGOING CONNECTION\n"); + sb.append("Packets Sent: ").append(m.getTotalPacketsSentCount()).append("\n"); + sb.append("Bytes Sent: ").append(m.getTotalPacketsSentBytes()).append(" B\n"); + sb.append("Common Types:\n"); + m.getSendHistory().values().stream() + .filter(packets -> !packets.isEmpty()) + .sorted((o1, o2) -> Integer.compare(o2.size(), o1.size())) + .limit(4) + .forEachOrdered(packets -> { + sb .append(" ") + .append(packets.size()) + .append("x ") + .append(packets.get(0).getClass().getSimpleName()) + .append("\n"); + }); + + sb.append("\n"); + + sb.append("INCOMING CONNECTION\n"); + sb.append("Packets Received: ").append(m.getTotalPacketsReceivedCount()).append("\n"); + sb.append("Packets in Queue: ").append(getQueueSize()).append("\n"); + sb.append("Bytes Received: ").append(m.getTotalPacketsReceivedBytes()).append(" B\n"); + sb.append("Common Types:\n"); + m.getReceiveHistory().values().stream() + .filter(packets -> !packets.isEmpty()) + .sorted((o1, o2) -> Integer.compare(o2.size(), o1.size())) + .limit(4) + .forEachOrdered(packets -> { + sb .append(" ") + .append(packets.size()) + .append("x ") + .append(packets.get(0).getClass().getSimpleName()) + .append("\n"); + }); + + compiledConnectionStats = sb.toString(); + + changeSupport.firePropertyChange("connectionStatsChange", null, compiledConnectionStats); + } + + public void updateCacheStats() { + if (!recordStats) return; + + StringBuilder sb = new StringBuilder(); + + sb.append("DATA CACHE\n"); + sb.append("Users Cached: ").append(userCache.getSize()).append("\n"); + sb.append("Topics Cached: ").append(topicCache.getSize()).append("\n"); + + compiledCacheStats = sb.toString(); + + changeSupport.firePropertyChange("cacheStatsChange", null, compiledCacheStats); + } + + public boolean isRecordStats() { + return recordStats; + } + + public String getCompiledConnectionStats() { + return compiledConnectionStats; + } + + public String getCompiledCacheStats() { + return compiledCacheStats; + } + + public void setRecordStats(boolean recordStats) { + this.recordStats = recordStats; + } + + public void addStatListener(PropertyChangeListener listener) { + changeSupport.addPropertyChangeListener(listener); + } + + public void removeStatListener(PropertyChangeListener listener) { + changeSupport.removePropertyChangeListener(listener); + } + public Socket getSocket() { return socket; } @@ -134,6 +234,8 @@ public class Connection { continue; } + updateConnectionStats(); + return packet; } } @@ -192,6 +294,10 @@ public class Connection { } } + public int getQueueSize() { + return queuedPackets.size(); + } + public SecretUser getSelf() { return self; } @@ -230,7 +336,20 @@ public class Connection { }); public User getUser(long userID) { - return userCache.get(userID); + int prevSize = userCache.getSize(); + + User result = userCache.get(userID); + + if (prevSize != userCache.getSize()) + updateCacheStats(); + + return result; + } + + public void saveUser(User user) { + userCache.put(user.getUserID(), user); + + updateCacheStats(); } private final DataCache topicCache = new DataCache<>(id -> { @@ -263,6 +382,19 @@ public class Connection { }); public Topic getTopic(long topicID) { - return topicCache.get(topicID); + int prevSize = topicCache.getSize(); + + Topic result = topicCache.get(topicID); + + if (prevSize != topicCache.getSize()) + updateCacheStats(); + + return result; + } + + public void saveTopic(Topic topic) { + topicCache.put(topic.getId(), topic); + + updateCacheStats(); } } diff --git a/Client/src/main/java/dev/wiing/gossip/client/DataCache.java b/Client/src/main/java/dev/wiing/gossip/client/DataCache.java index 55fe150..299b0f7 100644 --- a/Client/src/main/java/dev/wiing/gossip/client/DataCache.java +++ b/Client/src/main/java/dev/wiing/gossip/client/DataCache.java @@ -30,13 +30,15 @@ public class DataCache { T result; - if ((result = cache.getOrDefault(id, null)) != null) { - return result; - } + synchronized (cache) { + if ((result = cache.getOrDefault(id, null)) != null) { + return result; + } - result = fetchFunction.fetch(id); - if (result != null) { - cache.put(id, result); + result = fetchFunction.fetch(id); + if (result != null) { + cache.put(id, result); + } } return result; @@ -45,4 +47,8 @@ public class DataCache { public void put(long id, T val) { cache.put(id, val); } + + public int getSize() { + return cache.size(); + } } diff --git a/Client/src/main/java/dev/wiing/gossip/client/controllers/AvatarController.java b/Client/src/main/java/dev/wiing/gossip/client/controllers/AvatarController.java new file mode 100644 index 0000000..2566e7f --- /dev/null +++ b/Client/src/main/java/dev/wiing/gossip/client/controllers/AvatarController.java @@ -0,0 +1,120 @@ +package dev.wiing.gossip.client.controllers; + +import dev.wiing.gossip.client.data.UserAvatar; +import dev.wiing.gossip.client.generic.Pair; +import dev.wiing.gossip.client.utils.Utils; +import dev.wiing.gossip.lib.models.User; +import javafx.event.ActionEvent; +import javafx.event.EventHandler; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.geometry.Pos; +import javafx.scene.Parent; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.effect.ColorAdjust; +import javafx.scene.effect.Effect; +import javafx.scene.layout.FlowPane; +import javafx.scene.layout.Pane; +import javafx.scene.layout.VBox; + +import java.net.URL; +import java.util.ResourceBundle; + +public class AvatarController implements Initializable { + + private VBox selectedAvatarPair; + + private UserAvatar selectedAvatar; + + @FXML + private FlowPane flowAvatars; + + @FXML + private Label lblSelectedDesc; + + @FXML + private Label lblSelectedName; + + @FXML + private Pane paneSelectedIcon; + + @FXML + private VBox vboxAvatarButton; + + @FXML + private Button btnSave; + + @FXML + private VBox vboxRoot; + + @Override + public void initialize(URL location, ResourceBundle resources) { + flowAvatars.getChildren().clear(); + + UserAvatar.MORNING.applyToRegionBackground(paneSelectedIcon, true); + + Effect effect = new ColorAdjust(0.0, -1.0, -0.4, 0.0); + + for (UserAvatar avatar : UserAvatar.getAvatars()) { + VBox vBox = new VBox(); + vBox.getStyleClass().add("clickable"); + vBox.setSpacing(16); + vBox.setAlignment(Pos.CENTER); + + Pane pane = new Pane(); + pane.setMinSize(64, 64); + pane.setMaxSize(64, 64); + avatar.applyToRegionBackground(pane, true); + + Label label = new Label(); + label.setText(avatar.getName()); + label.getStyleClass().addAll("axis", "accent"); + + vBox.getChildren().addAll(pane, label); + + vBox.setEffect(effect); + + vBox.setOnMouseClicked(event -> { + if (selectedAvatarPair != null) { + selectedAvatarPair.setEffect(effect); + } + + selectedAvatarPair = vBox; + + selectAvatar(avatar); + + vBox.setEffect(null); + }); + + flowAvatars.getChildren().add(vBox); + } + } + + public void selectAvatar(UserAvatar avatar) { + selectedAvatar = avatar; + + lblSelectedName.setText(avatar.getName()); + lblSelectedDesc.setText(avatar.getDescription()); + + avatar.applyToRegionBackground(paneSelectedIcon, false); + + vboxRoot.setStyle("-accent: " + selectedAvatar.getColor()); + } + + public UserAvatar getSelectedAvatar() { + return selectedAvatar; + } + + public void setOnSave(EventHandler eventHandler) { + btnSave.setOnAction(event -> { + btnSave.getScene().getWindow().hide(); + eventHandler.handle(event); + }); + } + + public static Pair createInstance() { + return Utils.createInstance("views/avatar-view.fxml"); + } + +} diff --git a/Client/src/main/java/dev/wiing/gossip/client/controllers/LoginController.java b/Client/src/main/java/dev/wiing/gossip/client/controllers/LoginController.java index 0134e0a..9160c5d 100644 --- a/Client/src/main/java/dev/wiing/gossip/client/controllers/LoginController.java +++ b/Client/src/main/java/dev/wiing/gossip/client/controllers/LoginController.java @@ -3,6 +3,7 @@ package dev.wiing.gossip.client.controllers; import dev.wiing.gossip.client.Connection; import dev.wiing.gossip.client.data.UserAvatar; +import dev.wiing.gossip.client.utils.Utils; import dev.wiing.gossip.lib.models.SecretUser; import dev.wiing.gossip.lib.packets.RegisterCredentialsPacket; import dev.wiing.gossip.lib.packets.Packet; @@ -35,6 +36,11 @@ public class LoginController implements Initializable { @FXML private TextField txtUsername; + @FXML + private VBox vboxRoot; + + private UserAvatar selectedAvatar = UserAvatar.MORNING; + @Override public void initialize(URL location, ResourceBundle resources) { UserAvatar.MORNING.applyToRegionBackground(paneIcon, false); @@ -42,17 +48,31 @@ public class LoginController implements Initializable { Circle circle = new Circle(paneIcon.getMinWidth() * 0.5); paneIcon.setShape(circle); hboxEditOverlay.setShape(circle); + + vboxRoot.setStyle("-accent: " + selectedAvatar.getColor()); } @FXML public void onChangeIcon(MouseEvent event) { + var pair = AvatarController.createInstance(); + pair.second().selectAvatar(selectedAvatar); + + Utils.openParentAsWindow(pair.first(), "Gossip -- Avatar Menu"); + + pair.second().setOnSave(event1 -> { + selectedAvatar = pair.second().getSelectedAvatar(); + + selectedAvatar.applyToRegionBackground(paneIcon, false); + + vboxRoot.setStyle("-accent: " + selectedAvatar.getColor()); + }); } @FXML public void onLogin(ActionEvent event) { RegisterRequestPacket packet = new RegisterRequestPacket(); - packet.setAvatarID((byte)0); + packet.setAvatarID((byte)selectedAvatar.getId()); packet.setUsername(txtUsername.getText()); Connection.getInstance().sendPacket(packet); diff --git a/Client/src/main/java/dev/wiing/gossip/client/controllers/MainChatController.java b/Client/src/main/java/dev/wiing/gossip/client/controllers/MainChatController.java index f62e5e7..a926edf 100644 --- a/Client/src/main/java/dev/wiing/gossip/client/controllers/MainChatController.java +++ b/Client/src/main/java/dev/wiing/gossip/client/controllers/MainChatController.java @@ -6,17 +6,22 @@ import dev.wiing.gossip.client.controllers.item.SystemMessageItemController; import dev.wiing.gossip.client.generic.Pair; import dev.wiing.gossip.client.utils.Utils; import dev.wiing.gossip.lib.models.SystemMessage; +import dev.wiing.gossip.lib.models.User; import dev.wiing.gossip.lib.models.UserMessage; import dev.wiing.gossip.lib.models.Topic; import dev.wiing.gossip.lib.packets.MessagePushPacket; import dev.wiing.gossip.lib.packets.TopicJoinPacket; +import dev.wiing.gossip.lib.packets.TypingPingPacket; import javafx.application.Platform; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.scene.Parent; import javafx.scene.control.Label; import javafx.scene.control.TextArea; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; import javafx.scene.input.MouseEvent; +import javafx.scene.layout.Region; import javafx.scene.layout.VBox; import java.beans.PropertyChangeEvent; @@ -64,6 +69,10 @@ public class MainChatController { @FXML private VBox vboxVisitPage; + private Thread typingWaitThread = null; + + private boolean composeFieldPreviouslyEmpty = true; + public void setTopic(Topic topic) { this.topic = topic; @@ -82,7 +91,7 @@ public class MainChatController { }); } - if (evt.getPropertyName().equals("messageAdd")) { + else if (evt.getPropertyName().equals("messageAdd")) { Platform.runLater(() -> { if (evt.getNewValue() instanceof UserMessage) { addMessage((UserMessage)evt.getNewValue()); @@ -93,7 +102,13 @@ public class MainChatController { } }); } + + else if (evt.getPropertyName().equals("typingAdd") || evt.getPropertyName().equals("typingRemove")) { + Platform.runLater(this::updateTypingMembers); + } }); + + updateTypingMembers(); } private void setTopicName(String name) { @@ -163,24 +178,111 @@ public class MainChatController { } } - @FXML - void onSend(MouseEvent event) { + private void sendMessage() { String messageContents = txtCompose.getText(); + if (messageContents.isBlank()) { + return; + } + + composeFieldPreviouslyEmpty = true; txtCompose.clear(); MessagePushPacket packet = new MessagePushPacket(); packet.setTopicID(topic.getId()); - packet.setMessage(messageContents); + packet.setMessage(messageContents.strip()); Connection.getInstance().sendPacketAuthenticated(packet); try { Connection.getInstance().findAck(MessagePushPacket.TYPE); - } catch (IOException e) { throw new RuntimeException(e); } + + TypingPingPacket pingPacket = new TypingPingPacket(); + pingPacket.setTyping(false); + pingPacket.setTopicID(topic.getId()); + + Connection.getInstance().sendPacketAuthenticated(pingPacket); + + typingWaitThread.interrupt(); + typingWaitThread = null; + } + + @FXML + void onSend(MouseEvent event) { + sendMessage(); + } + + @FXML + void onKeyTyped(KeyEvent event) { + boolean isBlankTextField = txtCompose.getText().isBlank(); + + if (!isBlankTextField && typingWaitThread != null && typingWaitThread.isAlive()) return; + + if (isBlankTextField && composeFieldPreviouslyEmpty) return; + + TypingPingPacket packet = new TypingPingPacket(); + packet.setTyping(!isBlankTextField); + packet.setTopicID(topic.getId()); + + Connection.getInstance().sendPacketAuthenticated(packet); + + composeFieldPreviouslyEmpty = isBlankTextField; + + if (typingWaitThread != null) typingWaitThread.interrupt(); + + if (isBlankTextField) return; + + typingWaitThread = new Thread(() -> { + try { + Thread.sleep(5000); + } catch (InterruptedException ignored) { + } + }); + typingWaitThread.start(); + } + + @FXML + void onKeyPressed(KeyEvent event) { + if (event.getCode() == KeyCode.ENTER) { + if (event.isShiftDown()) { + txtCompose.insertText(txtCompose.getLength(), "\n"); + } else { + sendMessage(); + } + } + } + + public void updateTypingMembers() { + if (topic.getTypingUsersCount() == 0) { + lblTyping.setText(""); + lblTyping.setMinHeight(0); + lblTyping.setMaxHeight(0); + return; + } + + lblTyping.setMinHeight(Region.USE_COMPUTED_SIZE); + lblTyping.setMaxHeight(Region.USE_COMPUTED_SIZE); + + if (topic.getTypingUsersCount() > 3) { + lblTyping.setText("Several babblers are writing..."); + return; + } + + StringBuilder sb = new StringBuilder(); + + for (User user : topic.getTypingUsersReadOnly()) { + if (!sb.isEmpty()) sb.append(", "); + sb.append(user.getUsername()); + } + + sb.append(topic.getTypingUsersCount() == 1 ? " is" : " are"); + + sb.append(" writing..."); + + lblTyping.setText(sb.toString()); } public static Pair createInstance() { @@ -188,3 +290,6 @@ public class MainChatController { } } + +// TODO: better mutliline input +// 25 + 17 per extra line \ No newline at end of file diff --git a/Client/src/main/java/dev/wiing/gossip/client/controllers/MainController.java b/Client/src/main/java/dev/wiing/gossip/client/controllers/MainController.java index 3c2659a..006d043 100644 --- a/Client/src/main/java/dev/wiing/gossip/client/controllers/MainController.java +++ b/Client/src/main/java/dev/wiing/gossip/client/controllers/MainController.java @@ -5,6 +5,7 @@ import dev.wiing.gossip.client.controllers.item.TopicItemController; import dev.wiing.gossip.client.data.UserAvatar; import dev.wiing.gossip.client.generic.Pair; import dev.wiing.gossip.client.utils.Utils; +import dev.wiing.gossip.lib.PacketHandler; import dev.wiing.gossip.lib.data.LongData; import dev.wiing.gossip.lib.models.*; import dev.wiing.gossip.lib.packets.*; @@ -14,15 +15,16 @@ import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.Parent; import javafx.scene.control.Label; +import javafx.scene.input.MouseEvent; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.Pane; import javafx.scene.layout.VBox; import java.net.SocketException; import java.net.URL; -import java.util.Map; -import java.util.ResourceBundle; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; public class MainController implements Initializable { @@ -48,6 +50,11 @@ public class MainController implements Initializable { @FXML private Label lblJoinMessage; + @FXML + private Label lblDebug; + + private boolean recordDebugStats = false; + @Override public void initialize(URL url, ResourceBundle resourceBundle) { User user = Connection.getInstance().getSelf(); @@ -74,7 +81,19 @@ public class MainController implements Initializable { throw new RuntimeException(e); } - Connection.getInstance().getPacketHandler().addListener(TopicCreatedPacket.class, packet -> { + setUpPacketHandler(Connection.getInstance().getPacketHandler()); + + Connection.getInstance().beginHandlingPackets(); + + vboxTopics.getChildren().clear(); + + Connection.getInstance().addStatListener(evt -> { + Platform.runLater(this::rebuildDebugStatText); + }); + } + + private void setUpPacketHandler(PacketHandler packetHandler) { + packetHandler.addListener(TopicCreatedPacket.class, packet -> { User host = Connection.getInstance().getUser(packet.getHostID()); Topic topic = new Topic( @@ -85,10 +104,12 @@ public class MainController implements Initializable { packet.getTopicColor() ); + Connection.getInstance().saveTopic(topic); + addTopic(topic); }); - Connection.getInstance().getPacketHandler().addListener(TopicUpdatePacket.class, packet -> { + packetHandler.addListener(TopicUpdatePacket.class, packet -> { if (!topicMap.containsKey(packet.getTopicID())) return; Topic topic = topicMap.get(packet.getTopicID()).second().getTopic(); @@ -111,7 +132,7 @@ public class MainController implements Initializable { } }); - Connection.getInstance().getPacketHandler().addListener(MessageCreatedPacket.class, packet -> { + packetHandler.addListener(MessageCreatedPacket.class, packet -> { if (!topicMap.containsKey(packet.getTopicID())) return; Topic topic = topicMap.get(packet.getTopicID()).second().getTopic(); @@ -123,7 +144,7 @@ public class MainController implements Initializable { topic.addMessage(userMessage); }); - Connection.getInstance().getPacketHandler().addListener(MessageSystemPacket.class, packet -> { + packetHandler.addListener(MessageSystemPacket.class, packet -> { if (!topicMap.containsKey(packet.getTopicID())) return; Topic topic = topicMap.get(packet.getTopicID()).second().getTopic(); @@ -138,9 +159,22 @@ public class MainController implements Initializable { topic.addMessage(systemMessage); }); - Connection.getInstance().beginHandlingPackets(); + packetHandler.addListener(TypingListUpdatePacket.class, packet -> { + if (!topicMap.containsKey(packet.getTopicID())) return; - vboxTopics.getChildren().clear(); + Topic topic = topicMap.get(packet.getTopicID()).second().getTopic(); + + Set unexplored = topic.getTypingUsersReadOnly().stream().map(User::getUserID).collect(Collectors.toSet()); + + for (LongData typingMember : packet.getTypingMembers()) { + topic.addTypingUser(Connection.getInstance().getUser(typingMember.getValue())); + unexplored.remove(typingMember.getValue()); + } + + for (Long userID : unexplored) { + topic.removeTypingUser(Connection.getInstance().getUser(userID)); + } + }); } private void fetchTopicMessage(Topic topic, long messageID) { @@ -265,6 +299,45 @@ public class MainController implements Initializable { Connection.getInstance().sendPacket(packet); } + @FXML + void onToggleDebug(MouseEvent event) { + recordDebugStats = !recordDebugStats; + + lblDebug.setVisible(recordDebugStats); + + Connection.getInstance().setRecordStats(recordDebugStats); + Connection.getInstance().updateConnectionStats(); + Connection.getInstance().updateCacheStats(); + } + + @FXML + void onDebugEnter(MouseEvent event) { + lblDebug.setOpacity(0.9); + } + + @FXML + void onDebugLeave(MouseEvent event) { + lblDebug.setOpacity(0.2); + } + + private void rebuildDebugStatText() { + StringBuilder sb = new StringBuilder(); + + sb.append(Connection.getInstance().getCompiledConnectionStats()).append("\n"); + sb.append(Connection.getInstance().getCompiledCacheStats()).append("\n"); + + sb.append("STATS\n"); + sb.append("Total Messages (Downloaded): ").append(topicMap.values().stream() + .map(Pair::second) + .map(MainChatController::getTopic) + .map(Topic::getMessagesReadOnly) + .map(List::size) + .reduce(Integer::sum) + .orElse(0)); + + lblDebug.setText(sb.toString()); + } + public static Pair createInstance() { return Utils.createInstance("views/main-view.fxml"); } diff --git a/Client/src/main/java/dev/wiing/gossip/client/data/UserAvatar.java b/Client/src/main/java/dev/wiing/gossip/client/data/UserAvatar.java index 6ff88e0..55b4f70 100644 --- a/Client/src/main/java/dev/wiing/gossip/client/data/UserAvatar.java +++ b/Client/src/main/java/dev/wiing/gossip/client/data/UserAvatar.java @@ -11,16 +11,16 @@ import java.util.Arrays; public class UserAvatar { - public static final UserAvatar MORNING = new UserAvatar("Morning", "Eggs and Bacon", 0, "avatars/avatar-0.png"); - public static final UserAvatar VINTAGE = new UserAvatar("Vintage", "Red Car", 1, "avatars/avatar-1.png"); - public static final UserAvatar ROUTINE = new UserAvatar("Routine", "Coffee Cup", 2, "avatars/avatar-2.png"); - public static final UserAvatar SILLY = new UserAvatar("Silly", "Ghost Toy", 3, "avatars/avatar-3.png"); - public static final UserAvatar ADVENTURE = new UserAvatar("Adventure", "Slide with a Plant", 4, "avatars/avatar-4.png"); - public static final UserAvatar REBELLION = new UserAvatar("Rebellion", "Potted Cactus", 5, "avatars/avatar-5.png"); - public static final UserAvatar DIGITAL = new UserAvatar("Digital", "Game Controller", 6, "avatars/avatar-6.png"); - public static final UserAvatar MYSTERY = new UserAvatar("Mystery", "Floating Sphere", 7, "avatars/avatar-7.png"); - public static final UserAvatar VACATION = new UserAvatar("Vacation", "Cat under an Umbrella", 8, "avatars/avatar-8.png"); - public static final UserAvatar SIMPLE = new UserAvatar("Simple", "Potted Succulent", 9, "avatars/avatar-9.png"); + public static final UserAvatar MORNING = new UserAvatar("Morning", "Eggs and Bacon", 0, "#facb01", "avatars/avatar-0.png"); + public static final UserAvatar VINTAGE = new UserAvatar("Vintage", "Red Car", 1, "#dd2046", "avatars/avatar-1.png"); + public static final UserAvatar ROUTINE = new UserAvatar("Routine", "Coffee Cup", 2, "#01b4da", "avatars/avatar-2.png"); + public static final UserAvatar SILLY = new UserAvatar("Silly", "Ghost Toy", 3, "#8f58d8", "avatars/avatar-3.png"); + public static final UserAvatar ADVENTURE = new UserAvatar("Adventure", "Slide with a Plant", 4, "#6bc7da", "avatars/avatar-4.png"); + public static final UserAvatar REBELLION = new UserAvatar("Rebellion", "Potted Cactus", 5, "#17b479", "avatars/avatar-5.png"); + public static final UserAvatar DIGITAL = new UserAvatar("Digital", "Game Controller", 6, "#c3c1cf", "avatars/avatar-6.png"); + public static final UserAvatar MYSTERY = new UserAvatar("Mystery", "Floating Sphere", 7, "#26badc", "avatars/avatar-7.png"); + public static final UserAvatar VACATION = new UserAvatar("Vacation", "Cat under an Umbrella", 8, "#f994fb", "avatars/avatar-8.png"); + public static final UserAvatar SIMPLE = new UserAvatar("Simple", "Potted Succulent", 9, "#ff7e29", "avatars/avatar-9.png"); public static UserAvatar getAvatar(int avatarID) { return Arrays.stream(getAvatars()) @@ -47,12 +47,14 @@ public class UserAvatar { private final String name; private final String description; private final int id; + private final String color; private final URL url; - private UserAvatar(String name, String description, int id, String path) { + private UserAvatar(String name, String description, int id, String color, String path) { this.name = name; this.description = description; this.id = id; + this.color = color; this.url = Program.class.getResource(path); } @@ -68,6 +70,10 @@ public class UserAvatar { return id; } + public String getColor() { + return color; + } + public URL getUrl() { return url; } diff --git a/Client/src/main/resources/dev/wiing/gossip/client/views/avatar-view.fxml b/Client/src/main/resources/dev/wiing/gossip/client/views/avatar-view.fxml index 136c6e7..7d8e23b 100644 --- a/Client/src/main/resources/dev/wiing/gossip/client/views/avatar-view.fxml +++ b/Client/src/main/resources/dev/wiing/gossip/client/views/avatar-view.fxml @@ -1,27 +1,83 @@ + + + + + - - + -