Replace the internal event bus with Event Bus 0.0.3

The Event class has been retrofitted to implement IEvent, so that no
event implementations had to be changed.
This commit is contained in:
Kai S. K. Engelbart 2020-09-08 20:41:01 +02:00
parent 69ea737361
commit 465ed20efa
Signed by: kske
GPG Key ID: 8BEB13EC5DF7EF13
21 changed files with 253 additions and 299 deletions

View File

@ -10,16 +10,7 @@ import envoy.event.Event;
* @author Kai S. K. Engelbart
* @since Envoy Client v0.2-alpha
*/
public final class ThemeChangeEvent extends Event<String> {
public final class ThemeChangeEvent extends Event.Valueless {
private static final long serialVersionUID = 0L;
/**
* Initializes a {@link ThemeChangeEvent} conveying information about the change
* of the theme currently in use.
*
* @param theme the name of the new theme
* @since Envoy Client v0.2-alpha
*/
public ThemeChangeEvent(String theme) { super(theme); }
}

View File

@ -1,20 +1,19 @@
package envoy.client.net;
import java.io.Closeable;
import java.io.IOException;
import java.io.*;
import java.net.Socket;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.*;
import envoy.client.data.*;
import envoy.client.event.SendEvent;
import envoy.data.*;
import envoy.event.*;
import envoy.event.contact.ContactOperation;
import envoy.event.contact.UserSearchResult;
import envoy.util.EnvoyLog;
import envoy.util.SerializationUtils;
import envoy.event.Event;
import envoy.event.contact.*;
import envoy.util.*;
import dev.kske.eventbus.*;
/**
* Establishes a connection to the server, performs a handshake and delivers
@ -29,7 +28,7 @@ import envoy.util.SerializationUtils;
* @author Leon Hofmeister
* @since Envoy Client v0.1-alpha
*/
public final class Client implements Closeable {
public final class Client implements EventListener, Closeable {
// Connection handling
private Socket socket;
@ -45,6 +44,15 @@ public final class Client implements Closeable {
private static final Logger logger = EnvoyLog.getLogger(Client.class);
private static final EventBus eventBus = EventBus.getInstance();
/**
* Constructs a client and registers it as an event listener.
*
* @since Envoy Client v0.2-beta
*/
public Client() {
eventBus.registerListener(this);
}
/**
* Enters the online mode by acquiring a user ID from the server. As a
* connection has to be established and a handshake has to be made, this method
@ -164,22 +172,14 @@ public final class Client implements Closeable {
// Process ProfilePicChanges
receiver.registerProcessor(ProfilePicChange.class, eventBus::dispatch);
// Process requests to not send any more attachments as they will not be shown to
// Process requests to not send any more attachments as they will not be shown
// to
// other users
receiver.registerProcessor(NoAttachments.class, eventBus::dispatch);
// Process group creation results - they might have been disabled on the server
receiver.registerProcessor(GroupCreationResult.class, eventBus::dispatch);
// Send event
eventBus.register(SendEvent.class, evt -> {
try {
sendEvent(evt.get());
} catch (final IOException e) {
logger.log(Level.WARNING, "An error occurred when trying to send " + evt, e);
}
});
// Request a generator if none is present or the existing one is consumed
if (!localDB.hasIDGenerator() || !localDB.getIDGenerator().hasNext()) requestIdGenerator();
@ -219,6 +219,21 @@ public final class Client implements Closeable {
writeObject(new IDGeneratorRequest());
}
/**
* Sends the value of a send event to the server.
*
* @param evt the send event to extract the value from
* @since Envoy Client v0.2-beta
*/
@dev.kske.eventbus.Event
private void onSendEvent(SendEvent evt) {
try {
sendEvent(evt.get());
} catch (final IOException e) {
logger.log(Level.WARNING, "An error occurred when trying to send " + evt, e);
}
}
@Override
public void close() throws IOException { if (online) socket.close(); }

View File

@ -4,10 +4,11 @@ import java.util.function.Consumer;
import java.util.logging.Logger;
import envoy.data.Message.MessageStatus;
import envoy.event.EventBus;
import envoy.event.GroupMessageStatusChange;
import envoy.util.EnvoyLog;
import dev.kske.eventbus.EventBus;
/**
* Project: <strong>envoy-client</strong><br>
* File: <strong>GroupMessageStatusChangePocessor.java</strong><br>
@ -25,5 +26,4 @@ public final class GroupMessageStatusChangeProcessor implements Consumer<GroupMe
if (evt.get().ordinal() < MessageStatus.RECEIVED.ordinal()) logger.warning("Received invalid group message status change " + evt);
else EventBus.getInstance().dispatch(evt);
}
}

View File

@ -4,10 +4,11 @@ import java.util.function.Consumer;
import java.util.logging.Logger;
import envoy.data.Message.MessageStatus;
import envoy.event.EventBus;
import envoy.event.MessageStatusChange;
import envoy.util.EnvoyLog;
import dev.kske.eventbus.EventBus;
/**
* Project: <strong>envoy-client</strong><br>
* File: <strong>MessageStatusChangeProcessor.java</strong><br>

View File

@ -6,9 +6,10 @@ import java.util.logging.Logger;
import envoy.client.event.MessageCreationEvent;
import envoy.data.GroupMessage;
import envoy.data.Message.MessageStatus;
import envoy.event.EventBus;
import envoy.util.EnvoyLog;
import dev.kske.eventbus.EventBus;
/**
* Project: <strong>envoy-client</strong><br>
* File: <strong>ReceivedGroupMessageProcessor.java</strong><br>

View File

@ -5,7 +5,8 @@ import java.util.function.Consumer;
import envoy.client.event.MessageCreationEvent;
import envoy.data.Message;
import envoy.data.Message.MessageStatus;
import envoy.event.EventBus;
import dev.kske.eventbus.EventBus;
/**
* Project: <strong>envoy-client</strong><br>

View File

@ -6,15 +6,15 @@ import java.util.logging.Level;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.*;
import javafx.stage.Stage;
import envoy.client.data.Settings;
import envoy.client.event.ThemeChangeEvent;
import envoy.event.EventBus;
import envoy.util.EnvoyLog;
import dev.kske.eventbus.*;
/**
* Manages a stack of scenes. The most recently added scene is displayed inside
* a stage. When a scene is removed from the stack, its predecessor is
@ -30,7 +30,7 @@ import envoy.util.EnvoyLog;
* @author Kai S. K. Engelbart
* @since Envoy Client v0.1-beta
*/
public final class SceneContext {
public final class SceneContext implements EventListener {
/**
* Contains information about different scenes and their FXML resource files.
@ -84,7 +84,7 @@ public final class SceneContext {
*/
public SceneContext(Stage stage) {
this.stage = stage;
EventBus.getInstance().register(ThemeChangeEvent.class, theme -> applyCSS());
EventBus.getInstance().registerListener(this);
}
/**
@ -152,6 +152,9 @@ public final class SceneContext {
}
}
@Event(priority = 150, eventType = ThemeChangeEvent.class)
private void onThemeChange() { applyCSS(); }
/**
* @param <T> the type of the controller
* @return the controller used by the current scene

View File

@ -8,7 +8,9 @@ import javafx.stage.Stage;
import envoy.client.event.MessageCreationEvent;
import envoy.data.Message;
import envoy.event.EventBus;
import dev.kske.eventbus.*;
import dev.kske.eventbus.Event;
/**
* Project: <strong>envoy-client</strong><br>
@ -18,7 +20,7 @@ import envoy.event.EventBus;
* @author Kai S. K. Engelbart
* @since Envoy Client v0.2-alpha
*/
public final class StatusTrayIcon {
public final class StatusTrayIcon implements EventListener {
/**
* The {@link TrayIcon} provided by the System Tray API for controlling the
@ -67,14 +69,7 @@ public final class StatusTrayIcon {
trayIcon.addActionListener(evt -> Platform.runLater(() -> { stage.setIconified(false); stage.toFront(); stage.requestFocus(); }));
// Start processing message events
EventBus.getInstance().register(MessageCreationEvent.class, evt -> {
if (displayMessages) trayIcon
.displayMessage(
evt.get().hasAttachment() ? "New " + evt.get().getAttachment().getType().toString().toLowerCase() + " message received"
: "New message received",
evt.get().getText(),
MessageType.INFO);
});
EventBus.getInstance().registerListener(this);
}
/**
@ -94,4 +89,11 @@ public final class StatusTrayIcon {
* @since Envoy Client v0.2-beta
*/
public void hide() { SystemTray.getSystemTray().remove(trayIcon); }
@Event
private void onMessageCreation(MessageCreationEvent evt) {
if (displayMessages) trayIcon
.displayMessage(evt.get().hasAttachment() ? "New " + evt.get().getAttachment().getType().toString().toLowerCase() + " message received"
: "New message received", evt.get().getText(), MessageType.INFO);
}
}

View File

@ -1,7 +1,5 @@
package envoy.client.ui.controller;
import static envoy.data.Message.MessageStatus.RECEIVED;
import java.awt.Toolkit;
import java.awt.datatransfer.StringSelection;
import java.io.*;
@ -36,11 +34,15 @@ import envoy.client.ui.listcell.*;
import envoy.client.util.ReflectionUtil;
import envoy.data.*;
import envoy.data.Attachment.AttachmentType;
import envoy.data.Message.MessageStatus;
import envoy.event.*;
import envoy.event.contact.ContactOperation;
import envoy.exception.EnvoyException;
import envoy.util.EnvoyLog;
import dev.kske.eventbus.*;
import dev.kske.eventbus.Event;
/**
* Project: <strong>envoy-client</strong><br>
* File: <strong>ChatSceneController.java</strong><br>
@ -49,7 +51,7 @@ import envoy.util.EnvoyLog;
* @author Kai S. K. Engelbart
* @since Envoy Client v0.1-beta
*/
public final class ChatScene implements Restorable {
public final class ChatScene implements EventListener, Restorable {
@FXML
private GridPane scene;
@ -160,6 +162,8 @@ public final class ChatScene implements Restorable {
*/
@FXML
private void initialize() {
eventBus.registerListener(this);
// Initialize message and user rendering
messageList.setCellFactory(MessageListCell::new);
chatList.setCellFactory(new ListCellFactory<>(ChatControl::new));
@ -201,110 +205,119 @@ public final class ChatScene implements Restorable {
updateInfoLabel("You are offline", "info-label-warning");
}
});
}
// Listen to backEvents
eventBus.register(BackEvent.class, e -> tabPane.getSelectionModel().select(Tabs.CONTACT_LIST.ordinal()));
@Event(eventType = BackEvent.class)
private void onBackEvent() { tabPane.getSelectionModel().select(Tabs.CONTACT_LIST.ordinal()); }
// Listen to received messages
eventBus.register(MessageCreationEvent.class, e -> {
final var message = e.get();
@Event
private void onMessageCreation(MessageCreationEvent evt) {
final var message = evt.get();
// The sender of the message is the recipient of the chat
// Exceptions: this user is the sender (sync) or group message (group is
// recipient)
final var recipientID = message instanceof GroupMessage || message.getSenderID() == localDB.getUser().getID() ? message.getRecipientID()
: message.getSenderID();
localDB.getChat(recipientID).ifPresent(chat -> {
chat.insert(message);
if (chat.equals(currentChat)) {
try {
currentChat.read(writeProxy);
} catch (final IOException e1) {
logger.log(Level.WARNING, "Could not read current chat: ", e1);
}
Platform.runLater(() -> { ListViewRefresh.deepRefresh(messageList); scrollToMessageListEnd(); });
// TODO: Increment unread counter for group messages with status < RECEIVED
} else if (message.getSenderID() != localDB.getUser().getID() && message.getStatus() == RECEIVED) chat.incrementUnreadAmount();
// The sender of the message is the recipient of the chat
// Exceptions: this user is the sender (sync) or group message (group is
// recipient)
final var recipientID = message instanceof GroupMessage || message.getSenderID() == localDB.getUser().getID() ? message.getRecipientID()
: message.getSenderID();
localDB.getChat(recipientID).ifPresent(chat -> {
chat.insert(message);
if (chat.equals(currentChat)) {
try {
currentChat.read(writeProxy);
} catch (final IOException e1) {
logger.log(Level.WARNING, "Could not read current chat: ", e1);
}
Platform.runLater(() -> { ListViewRefresh.deepRefresh(messageList); scrollToMessageListEnd(); });
// TODO: Increment unread counter for group messages with status < RECEIVED
} else
if (message.getSenderID() != localDB.getUser().getID() && message.getStatus() == MessageStatus.RECEIVED) chat.incrementUnreadAmount();
// Move chat with most recent unread messages to the top
Platform.runLater(() -> {
chats.getSource().remove(chat);
((ObservableList<Chat>) chats.getSource()).add(0, chat);
if (chat.equals(currentChat)) chatList.getSelectionModel().select(0);
});
});
});
// Listen to message status changes
eventBus.register(MessageStatusChange.class, e -> localDB.getMessage(e.getID()).ifPresent(message -> {
message.setStatus(e.get());
// Update UI if in current chat and the current user was the sender of the
// message
if (currentChat != null && message.getSenderID() == client.getSender().getID()) Platform.runLater(messageList::refresh);
}));
eventBus.register(GroupMessageStatusChange.class, e -> localDB.getMessage(e.getID()).ifPresent(groupMessage -> {
((GroupMessage) groupMessage).getMemberStatuses().replace(e.getMemberID(), e.get());
// Update UI if in current chat
if (currentChat != null && groupMessage.getRecipientID() == currentChat.getRecipient().getID()) Platform.runLater(messageList::refresh);
}));
// Listen to user status changes
eventBus.register(UserStatusChange.class,
e -> chats.getSource()
.stream()
.filter(c -> c.getRecipient().getID() == e.getID())
.findAny()
.map(Chat::getRecipient)
.ifPresent(u -> { ((User) u).setStatus(e.get()); Platform.runLater(() -> ListViewRefresh.deepRefresh(chatList)); }));
// Listen to contacts changes
eventBus.register(ContactOperation.class, e -> {
final var contact = e.get();
switch (e.getOperationType()) {
case ADD:
if (contact instanceof User) localDB.getUsers().put(contact.getName(), (User) contact);
final var chat = contact instanceof User ? new Chat(contact) : new GroupChat(client.getSender(), contact);
Platform.runLater(() -> ((ObservableList<Chat>) chats.getSource()).add(0, chat));
break;
case REMOVE:
Platform.runLater(() -> chats.getSource().removeIf(c -> c.getRecipient().equals(contact)));
break;
}
});
// Disable attachment button if server says attachments will be filtered out
eventBus.register(NoAttachments.class, e -> {
// Move chat with most recent unread messages to the top
Platform.runLater(() -> {
attachmentButton.setDisable(true);
voiceButton.setDisable(true);
final var alert = new Alert(AlertType.ERROR);
alert.setTitle("No attachments possible");
alert.setHeaderText("Your current server does not support attachments.");
alert.setContentText("If this is unplanned, please contact your server administrator.");
alert.showAndWait();
chats.getSource().remove(chat);
((ObservableList<Chat>) chats.getSource()).add(0, chat);
if (chat.equals(currentChat)) chatList.getSelectionModel().select(0);
});
});
}
eventBus.register(GroupCreationResult.class, e -> Platform.runLater(() -> { newGroupButton.setDisable(!e.get()); }));
eventBus.register(ThemeChangeEvent.class, e -> {
settingsButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("settings", DEFAULT_ICON_SIZE)));
voiceButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("microphone", DEFAULT_ICON_SIZE)));
attachmentButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("attachment", DEFAULT_ICON_SIZE)));
DEFAULT_ATTACHMENT_VIEW_IMAGE = IconUtil.loadIconThemeSensitive("attachment_present", 20);
attachmentView.setImage(isCustomAttachmentImage ? attachmentView.getImage() : DEFAULT_ATTACHMENT_VIEW_IMAGE);
messageSearchButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("search", DEFAULT_ICON_SIZE)));
clientProfilePic.setImage(IconUtil.loadIconThemeSensitive("user_icon", 43));
chatList.setCellFactory(new ListCellFactory<>(ChatControl::new));
messageList.setCellFactory(MessageListCell::new);
if (currentChat.getRecipient() instanceof User) recipientProfilePic.setImage(IconUtil.loadIconThemeSensitive("user_icon", 43));
else recipientProfilePic.setImage(IconUtil.loadIconThemeSensitive("group_icon", 43));
@Event
private void onMessageStatusChange(MessageStatusChange evt) {
localDB.getMessage(evt.getID()).ifPresent(message -> {
message.setStatus(evt.get());
// Update UI if in current chat and the current user was the sender of the
// message
if (currentChat != null && message.getSenderID() == client.getSender().getID()) Platform.runLater(messageList::refresh);
});
}
@Event
private void onGroupMessageStatusChange(GroupMessageStatusChange evt) {
localDB.getMessage(evt.getID()).ifPresent(groupMessage -> {
((GroupMessage) groupMessage).getMemberStatuses().replace(evt.getMemberID(), evt.get());
// Update UI if in current chat
if (currentChat != null && groupMessage.getRecipientID() == currentChat.getRecipient().getID()) Platform.runLater(messageList::refresh);
});
}
@Event
private void onUserStatusChange(UserStatusChange evt) {
chats.getSource()
.stream()
.filter(c -> c.getRecipient().getID() == evt.getID())
.findAny()
.map(Chat::getRecipient)
.ifPresent(u -> { ((User) u).setStatus(evt.get()); Platform.runLater(() -> ListViewRefresh.deepRefresh(chatList)); });
}
@Event
private void onContactOperation(ContactOperation operation) {
final var contact = operation.get();
switch (operation.getOperationType()) {
case ADD:
if (contact instanceof User) localDB.getUsers().put(contact.getName(), (User) contact);
final var chat = contact instanceof User ? new Chat(contact) : new GroupChat(client.getSender(), contact);
Platform.runLater(() -> ((ObservableList<Chat>) chats.getSource()).add(0, chat));
break;
case REMOVE:
Platform.runLater(() -> chats.getSource().removeIf(c -> c.getRecipient().equals(contact)));
break;
}
}
@Event(eventType = NoAttachments.class)
private void onNoAttachments() {
Platform.runLater(() -> {
attachmentButton.setDisable(true);
voiceButton.setDisable(true);
final var alert = new Alert(AlertType.ERROR);
alert.setTitle("No attachments possible");
alert.setHeaderText("Your current server does not support attachments.");
alert.setContentText("If this is unplanned, please contact your server administrator.");
alert.showAndWait();
});
}
@Event
private void onGroupCreationResult(GroupCreationResult result) { Platform.runLater(() -> newGroupButton.setDisable(!result.get())); }
@Event(eventType = ThemeChangeEvent.class)
private void onThemeChange() {
settingsButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("settings", DEFAULT_ICON_SIZE)));
voiceButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("microphone", DEFAULT_ICON_SIZE)));
attachmentButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("attachment", DEFAULT_ICON_SIZE)));
DEFAULT_ATTACHMENT_VIEW_IMAGE = IconUtil.loadIconThemeSensitive("attachment_present", 20);
attachmentView.setImage(isCustomAttachmentImage ? attachmentView.getImage() : DEFAULT_ATTACHMENT_VIEW_IMAGE);
messageSearchButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("search", DEFAULT_ICON_SIZE)));
clientProfilePic.setImage(IconUtil.loadIconThemeSensitive("user_icon", 43));
chatList.setCellFactory(new ListCellFactory<>(ChatControl::new));
messageList.setCellFactory(MessageListCell::new);
if (currentChat.getRecipient() instanceof User) recipientProfilePic.setImage(IconUtil.loadIconThemeSensitive("user_icon", 43));
else recipientProfilePic.setImage(IconUtil.loadIconThemeSensitive("group_icon", 43));
}
/**
* Initializes all {@code SystemCommands} used in {@code ChatScene}.
*

View File

@ -1,26 +1,21 @@
package envoy.client.ui.controller;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.*;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.control.Alert.AlertType;
import envoy.client.event.BackEvent;
import envoy.client.event.SendEvent;
import envoy.client.ui.listcell.ContactControl;
import envoy.client.ui.listcell.ListCellFactory;
import envoy.client.event.*;
import envoy.client.ui.listcell.*;
import envoy.data.User;
import envoy.event.ElementOperation;
import envoy.event.EventBus;
import envoy.event.contact.ContactOperation;
import envoy.event.contact.UserSearchRequest;
import envoy.event.contact.UserSearchResult;
import envoy.event.contact.*;
import envoy.util.EnvoyLog;
import dev.kske.eventbus.*;
/**
* Provides a search bar in which a user name (substring) can be entered. The
* users with a matching name are then displayed inside a list view. A
@ -39,7 +34,7 @@ import envoy.util.EnvoyLog;
* @author Maximilian K&auml;fer
* @since Envoy Client v0.1-beta
*/
public class ContactSearchTab {
public class ContactSearchTab implements EventListener {
@FXML
private TextArea searchBar;
@ -48,26 +43,29 @@ public class ContactSearchTab {
private ListView<User> userList;
private Alert alert = new Alert(AlertType.CONFIRMATION);
private User currentlySelectedUser;
private final Consumer<ContactOperation> handler = e -> {
final var contact = e.get();
if (e.getOperationType() == ElementOperation.ADD) Platform.runLater(() -> {
userList.getItems().remove(contact);
if (currentlySelectedUser != null && currentlySelectedUser.equals(contact) && alert.isShowing()) alert.close();
});
};
private static final EventBus eventBus = EventBus.getInstance();
private static final Logger logger = EnvoyLog.getLogger(ChatScene.class);
@FXML
private void initialize() {
eventBus.registerListener(this);
userList.setCellFactory(new ListCellFactory<>(ContactControl::new));
eventBus.register(UserSearchResult.class,
response -> Platform.runLater(() -> { userList.getItems().clear(); userList.getItems().addAll(response.get()); }));
eventBus.register(ContactOperation.class, handler);
}
@Event
private void onUserSearchResult(UserSearchResult result) {
Platform.runLater(() -> { userList.getItems().clear(); userList.getItems().addAll(result.get()); });
}
@Event
private void onContactOperation(ContactOperation operation) {
final var contact = operation.get();
if (operation.getOperationType() == ElementOperation.ADD) Platform.runLater(() -> {
userList.getItems().remove(contact);
if (currentlySelectedUser != null && currentlySelectedUser.equals(contact) && alert.isShowing()) alert.close();
});
}
/**

View File

@ -8,20 +8,15 @@ import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.layout.HBox;
import envoy.client.data.Chat;
import envoy.client.data.Context;
import envoy.client.data.LocalDB;
import envoy.client.event.BackEvent;
import envoy.client.event.SendEvent;
import envoy.client.ui.listcell.ContactControl;
import envoy.client.ui.listcell.ListCellFactory;
import envoy.data.Contact;
import envoy.data.Group;
import envoy.data.User;
import envoy.event.EventBus;
import envoy.client.data.*;
import envoy.client.event.*;
import envoy.client.ui.listcell.*;
import envoy.data.*;
import envoy.event.GroupCreation;
import envoy.util.Bounds;
import dev.kske.eventbus.*;
/**
* Provides a group creation interface. A group name can be entered in the text
* field at the top. Available users (local chat recipients) are displayed
@ -38,7 +33,7 @@ import envoy.util.Bounds;
* @author Maximilian K&auml;fer
* @since Envoy Client v0.1-beta
*/
public class GroupCreationTab {
public class GroupCreationTab implements EventListener {
@FXML
private Button createButton;

View File

@ -1,9 +1,7 @@
package envoy.client.ui.controller;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.*;
import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.geometry.Insets;
import javafx.scene.control.*;
@ -11,13 +9,12 @@ import javafx.scene.control.Alert.AlertType;
import javafx.scene.image.ImageView;
import envoy.client.data.ClientConfig;
import envoy.client.ui.IconUtil;
import envoy.client.ui.Startup;
import envoy.client.ui.*;
import envoy.data.LoginCredentials;
import envoy.event.EventBus;
import envoy.event.HandshakeRejection;
import envoy.util.Bounds;
import envoy.util.EnvoyLog;
import envoy.util.*;
import dev.kske.eventbus.*;
/**
* Project: <strong>envoy-client</strong><br>
@ -28,7 +25,7 @@ import envoy.util.EnvoyLog;
* @author Maximilian K&auml;fer
* @since Envoy Client v0.1-beta
*/
public final class LoginScene {
public final class LoginScene implements EventListener {
@FXML
private TextField userTextField;
@ -60,7 +57,6 @@ public final class LoginScene {
private boolean registration = false;
private static final Logger logger = EnvoyLog.getLogger(LoginScene.class);
private static final EventBus eventBus = EventBus.getInstance();
private static final ClientConfig config = ClientConfig.getInstance();
@FXML
@ -68,7 +64,7 @@ public final class LoginScene {
connectionLabel.setText("Server: " + config.getServer() + ":" + config.getPort());
// Show an alert after an unsuccessful handshake
eventBus.register(HandshakeRejection.class, e -> Platform.runLater(() -> { new Alert(AlertType.ERROR, e.get()).showAndWait(); }));
EventBus.getInstance().registerListener(this);
logo.setImage(IconUtil.loadIcon("envoy_logo"));
@ -119,4 +115,7 @@ public final class LoginScene {
logger.log(Level.INFO, "The login process has been cancelled. Exiting...");
System.exit(0);
}
@Event
private void onHandshakeRejection(HandshakeRejection evt) { new Alert(AlertType.ERROR, evt.get()).showAndWait(); }
}

View File

@ -7,9 +7,10 @@ import javafx.scene.input.InputEvent;
import envoy.client.event.SendEvent;
import envoy.client.util.IssueUtil;
import envoy.data.User;
import envoy.event.EventBus;
import envoy.event.IssueProposal;
import dev.kske.eventbus.EventBus;
/**
* This class offers the option for users to submit a bug report. Only the title
* of a bug is needed to be sent.

View File

@ -1,12 +1,12 @@
package envoy.client.ui.settings;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Tooltip;
import javafx.scene.control.*;
import envoy.client.data.SettingsItem;
import envoy.client.event.ThemeChangeEvent;
import envoy.data.User.UserStatus;
import envoy.event.EventBus;
import dev.kske.eventbus.EventBus;
/**
* Project: <strong>envoy-client</strong><br>
@ -44,7 +44,7 @@ public final class GeneralSettingsPane extends SettingsPane {
combobox.setTooltip(new Tooltip("Determines the current theme Envoy will be displayed in."));
combobox.setValue(settings.getCurrentTheme());
combobox.setOnAction(
e -> { settings.setCurrentTheme(combobox.getValue()); EventBus.getInstance().dispatch(new ThemeChangeEvent(combobox.getValue())); });
e -> { settings.setCurrentTheme(combobox.getValue()); EventBus.getInstance().dispatch(new ThemeChangeEvent()); });
getChildren().add(combobox);
final var statusComboBox = new ComboBox<UserStatus>();

View File

@ -1,31 +1,27 @@
package envoy.client.ui.settings;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.*;
import java.nio.file.Files;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.*;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Cursor;
import javafx.scene.control.*;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.*;
import javafx.scene.input.InputEvent;
import javafx.scene.layout.HBox;
import javafx.stage.FileChooser;
import envoy.client.event.SendEvent;
import envoy.client.ui.IconUtil;
import envoy.client.ui.SceneContext;
import envoy.client.ui.*;
import envoy.client.ui.custom.ProfilePicImageView;
import envoy.data.User;
import envoy.event.*;
import envoy.util.Bounds;
import envoy.util.EnvoyLog;
import envoy.util.*;
import dev.kske.eventbus.EventBus;
/**
* Project: <strong>envoy-client</strong><br>

View File

@ -7,19 +7,20 @@
* @author Maximilian K&auml;fer
* @since Envoy Client v0.1-beta
*/
module envoy {
module envoy.client {
requires transitive envoy.common;
requires transitive java.desktop;
requires transitive java.logging;
requires transitive java.prefs;
requires envoy.common;
requires java.desktop;
requires java.logging;
requires java.prefs;
requires javafx.controls;
requires javafx.fxml;
requires javafx.base;
requires javafx.graphics;
opens envoy.client.ui to javafx.graphics, javafx.fxml;
opens envoy.client.ui.controller to javafx.graphics, javafx.fxml, envoy.client.util;
opens envoy.client.ui to javafx.graphics, javafx.fxml, dev.kske.eventbus;
opens envoy.client.ui.controller to javafx.graphics, javafx.fxml, envoy.client.util, dev.kske.eventbus;
opens envoy.client.ui.custom to javafx.graphics, javafx.fxml;
opens envoy.client.ui.settings to envoy.client.util;
opens envoy.client.net to dev.kske.eventbus;
}

View File

@ -12,11 +12,24 @@
<version>0.1-beta</version>
</parent>
<repositories>
<repository>
<id>kske-repo</id>
<url>https://kske.dev/maven-repo</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>dev.kske</groupId>
<artifactId>event-bus</artifactId>
<version>0.0.3</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.5.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>

View File

@ -2,7 +2,13 @@ package envoy.event;
import java.io.Serializable;
import dev.kske.eventbus.IEvent;
/**
* This class serves as a convenience base class for all events. It implements
* the {@link IEvent} interface and provides a generic value. For events without
* a value there also is {@link envoy.event.Event.Valueless}.
* <p>
* Project: <strong>envoy-common</strong><br>
* File: <strong>Event.java</strong><br>
* Created: <strong>04.12.2019</strong><br>
@ -11,7 +17,7 @@ import java.io.Serializable;
* @param <T> the type of the Event
* @since Envoy v0.2-alpha
*/
public abstract class Event<T> implements Serializable {
public abstract class Event<T> implements IEvent, Serializable {
protected final T value;

View File

@ -1,82 +0,0 @@
package envoy.event;
import java.util.*;
import java.util.function.Consumer;
/**
* This class handles events by allowing event handlers to register themselves
* and then be notified about certain events dispatched by the event bus.<br>
* <br>
* The event bus is a singleton and can be used across the entire application to
* guarantee the propagation of events.<br>
* <br>
* Project: <strong>envoy-common</strong><br>
* File: <strong>EventBus.java</strong><br>
* Created: <strong>04.12.2019</strong><br>
*
* @author Kai S. K. Engelbart
* @since Envoy v0.2-alpha
*/
public final class EventBus {
/**
* Contains all event handler instances registered at this event bus as values
* mapped to by their supported event classes.
*/
private Map<Class<? extends Event<?>>, List<Consumer<Event<?>>>> handlers = new HashMap<>();
/**
* The singleton instance of this event bus that is used across the
* entire application.
*/
private static EventBus eventBus = new EventBus();
/**
* This constructor is not accessible from outside this class because a
* singleton instance of it is provided by the {@link EventBus#getInstance()}
* method.
*/
private EventBus() {}
/**
* @return the singleton instance of the event bus
* @since Envoy v0.2-alpha
*/
public static EventBus getInstance() { return eventBus; }
/**
* Registers an event handler to be notified when an
* event of a certain type is dispatched.
*
* @param <T> the type of event values to notify the handler about
* @param eventClass the class which the event handler is subscribing to
* @param handler the event handler to register
* @since Envoy v0.2-alpha
*/
public <T extends Event<?>> void register(Class<T> eventClass, Consumer<T> handler) {
if (!handlers.containsKey(eventClass)) handlers.put(eventClass, new ArrayList<>());
handlers.get(eventClass).add((Consumer<Event<?>>) handler);
}
/**
* Dispatches an event to every event handler subscribed to it.
*
* @param event the {@link Event} to dispatch
* @since Envoy v0.2-alpha
*/
public void dispatch(Event<?> event) {
handlers.keySet()
.stream()
.filter(event.getClass()::equals)
.map(handlers::get)
.flatMap(List::stream)
.forEach(h -> h.accept(event));
}
/**
* @return a map of all event handler instances currently registered at this
* event bus with the event classes they are subscribed to as keys
* @since Envoy v0.2-alpha
*/
public Map<Class<? extends Event<?>>, List<Consumer<Event<?>>>> getHandlers() { return handlers; }
}

View File

@ -16,4 +16,5 @@ module envoy.common {
exports envoy.event.contact;
requires transitive java.logging;
requires transitive dev.kske.eventbus;
}

View File

@ -16,5 +16,4 @@ module envoy.server {
requires transitive java.persistence;
requires transitive java.sql;
requires transitive org.hibernate.orm.core;
}