+ * To allow Maven shading, the main method has to be separated from the + * {@link Startup} class which extends {@link Application}. + *
+ * Project: envoy-client
+ * The suffix {@code .png} is automatically appended.
+ *
+ * @param name the image name without the "black" or "white" suffix and without
+ * the .png suffix
+ * @return the loaded image
+ * @since Envoy Client v0.1-beta
+ * @apiNote let's take two sample images {@code /icons/dark/abc.png} and
+ * {@code /icons/light/abc.png}, and load one of them.
+ * The suffix {@code .png} is automatically appended.
+ *
+ * @param name the image name without the .png suffix
+ * @param size the size of the image to scale to
+ * @return the loaded image
+ * @since Envoy Client v0.1-beta
+ * @apiNote let's take two sample images {@code /icons/dark/abc.png} and
+ * {@code /icons/light/abc.png}, and load one of them in size 16.
+ * File: Main.java
+ * Created: 05.07.2020
+ *
+ * @author Kai S. K. Engelbart
+ * @since Envoy Client v0.1-beta
+ */
+public class Main {
+
+ /**
+ * Starts the application.
+ *
+ * @param args the command line arguments are processed by the
+ * client configuration
+ * @since Envoy Client v0.1-beta
+ */
+ public static void main(String[] args) { Application.launch(Startup.class, args); }
+}
diff --git a/src/main/java/envoy/client/data/Settings.java b/src/main/java/envoy/client/data/Settings.java
index 9d43bca..fc16c26 100644
--- a/src/main/java/envoy/client/data/Settings.java
+++ b/src/main/java/envoy/client/data/Settings.java
@@ -25,7 +25,7 @@ import envoy.util.SerializationUtils;
public class Settings {
// Actual settings accessible by the rest of the application
- private Map
+ * The suffix {@code .png} is automatically appended.
+ *
+ * @param name the image name without the .png suffix
+ * @return the loaded image
+ * @since Envoy Client v0.1-beta
+ * @apiNote let's load a sample image {@code /icons/abc.png}.
+ * To do that, we only have to call {@code IconUtil.loadIcon("abc")}
+ */
+ public static Image loadIcon(String name) { return load("/icons/" + name + ".png"); }
+
+ /**
+ * Loads a {@code .png} image from the sub-folder {@code /icons/} of the
+ * resource folder and scales it to the given size.
+ * The suffix {@code .png} is automatically appended.
+ *
+ * @param name the image name without the .png suffix
+ * @param size the size of the image to scale to
+ * @return the loaded image
+ * @since Envoy Client v0.1-beta
+ * @apiNote let's load a sample image {@code /icons/abc.png} in size 16.
+ * To do that, we only have to call
+ * {@code IconUtil.loadIcon("abc", 16)}
+ */
+ public static Image loadIcon(String name, int size) { return load("/icons/" + name + ".png", size); }
+
+ /**
+ * Loads a {@code .png} image whose design depends on the currently active theme
+ * from the sub-folder {@code /icons/dark/} or {@code /icons/light/} of the
+ * resource folder.
+ *
+ * To do that theme sensitive, we only have to call
+ * {@code IconUtil.loadIconThemeSensitive("abc")}
+ */
+ public static Image loadIconThemeSensitive(String name) { return loadIcon(themeSpecificSubFolder() + name); }
+
+ /**
+ * Loads a {@code .png} image whose design depends on the currently active theme
+ * from the sub-folder {@code /icons/dark/} or {@code /icons/light/} of the
+ * resource folder and scales it to the given size.
+ *
+ * To do that theme sensitive, we only have to call
+ * {@code IconUtil.loadIconThemeSensitive("abc", 16)}
+ */
+ public static Image loadIconThemeSensitive(String name, int size) { return loadIcon(themeSpecificSubFolder() + name, size); }
+
+ /**
+ *
+ * Loads images specified by an enum. The images have to be named like the
* lowercase enum constants with {@code .png} extension and be located inside a
* folder with the lowercase name of the enum, which must be contained inside
- * the {@code /icons} folder.
+ * the {@code /icons/} folder.
*
- * @param
+ * In case of a default theme, the string returned will be
+ * ({@code dark/} or {@code light/}), otherwise it will be empty.
+ *
+ * @return the theme specific folder
+ * @since Envoy Client v0.1-beta
+ */
+ public static String themeSpecificSubFolder() {
+ return Settings.getInstance().isUsingDefaultTheme() ? Settings.getInstance().getCurrentTheme() + "/" : "";
+ }
}
diff --git a/src/main/java/envoy/client/ui/Startup.java b/src/main/java/envoy/client/ui/Startup.java
index de3289f..54a72d8 100644
--- a/src/main/java/envoy/client/ui/Startup.java
+++ b/src/main/java/envoy/client/ui/Startup.java
@@ -108,7 +108,7 @@ public final class Startup extends Application {
groupMessageStatusCache = new Cache<>();
stage.setTitle("Envoy");
- stage.getIcons().add(IconUtil.load("/icons/envoy_logo.png"));
+ stage.getIcons().add(IconUtil.loadIcon("envoy_logo"));
final var sceneContext = new SceneContext(stage);
sceneContext.load(SceneInfo.LOGIN_SCENE);
@@ -135,13 +135,4 @@ public final class Startup extends Application {
logger.log(Level.SEVERE, "Unable to save local files: ", e);
}
}
-
- /**
- * Starts the application.
- *
- * @param args the command line arguments are processed by the
- * {@link ClientConfig}
- * @since Envoy Client v0.1-beta
- */
- public static void main(String[] args) { launch(args); }
}
diff --git a/src/main/java/envoy/client/ui/controller/ChatScene.java b/src/main/java/envoy/client/ui/controller/ChatScene.java
index d327039..ccc9380 100644
--- a/src/main/java/envoy/client/ui/controller/ChatScene.java
+++ b/src/main/java/envoy/client/ui/controller/ChatScene.java
@@ -75,6 +75,9 @@ public final class ChatScene implements Restorable {
@FXML
private MenuItem deleteContactMenuItem;
+ @FXML
+ private ImageView attachmentView;
+
private LocalDB localDB;
private Client client;
private WriteProxy writeProxy;
@@ -103,7 +106,8 @@ public final class ChatScene implements Restorable {
messageList.setCellFactory(MessageListCellFactory::new);
userList.setCellFactory(ContactListCellFactory::new);
- settingsButton.setGraphic(new ImageView(IconUtil.load("/icons/settings.png", 16)));
+ settingsButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("settings", 16)));
+ voiceButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("microphone", 20)));
// Listen to received messages
eventBus.register(MessageCreationEvent.class, e -> {
@@ -220,9 +224,9 @@ public final class ChatScene implements Restorable {
if (recorder.isRecording()) {
recorder.cancel();
recording = false;
- voiceButton.setText("Record Voice Message");
}
pendingAttachment = null;
+ attachmentView.setVisible(false);
remainingChars.setVisible(true);
remainingChars
@@ -260,14 +264,23 @@ public final class ChatScene implements Restorable {
try {
if (!recording) {
recording = true;
- Platform.runLater(() -> voiceButton.setText("Recording..."));
+ Platform.runLater(() -> {
+ voiceButton.setText("Recording");
+ voiceButton.setGraphic(new ImageView(IconUtil.loadIcon("microphone_recording", 24)));
+ });
recorder.start();
} else {
pendingAttachment = new Attachment(recorder.finish(), AttachmentType.VOICE);
recording = false;
- Platform.runLater(() -> { voiceButton.setText("Record Voice Message"); checkPostConditions(false); });
+ Platform.runLater(() -> {
+ voiceButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("microphone", 20)));
+ voiceButton.setText(null);
+ checkPostConditions(false);
+ attachmentView.setImage(IconUtil.loadIconThemeSensitive("attachment_present", 20));
+ attachmentView.setVisible(true);
+ });
}
- } catch (EnvoyException e) {
+ } catch (final EnvoyException e) {
logger.log(Level.SEVERE, "Could not record audio: ", e);
Platform.runLater(new Alert(AlertType.ERROR, "Could not record audio")::showAndWait);
}
@@ -303,7 +316,7 @@ public final class ChatScene implements Restorable {
private void checkPostConditions(boolean sendKeyPressed) {
if (!postingPermanentlyDisabled) {
if (!postButton.isDisabled() && sendKeyPressed) postMessage();
- postButton.setDisable((messageTextArea.getText().isBlank() && pendingAttachment == null) || currentChat == null);
+ postButton.setDisable(messageTextArea.getText().isBlank() && pendingAttachment == null || currentChat == null);
} else {
final var noMoreMessaging = "Go online to send messages";
if (!infoLabel.getText().equals(noMoreMessaging))
@@ -367,6 +380,7 @@ public final class ChatScene implements Restorable {
if (pendingAttachment != null) {
builder.setAttachment(pendingAttachment);
pendingAttachment = null;
+ attachmentView.setVisible(false);
}
// Building the final message
final var message = currentChat.getRecipient() instanceof Group ? builder.buildGroupMessage((Group) currentChat.getRecipient())
diff --git a/src/main/resources/css/dark.css b/src/main/resources/css/dark.css
index 967732b..eaad3a9 100644
--- a/src/main/resources/css/dark.css
+++ b/src/main/resources/css/dark.css
@@ -11,7 +11,7 @@
}
.button:pressed {
- -fx-background-color: darkgray;
+ -fx-background-color: darkviolet;
}
.button:disabled {
diff --git a/src/main/resources/fxml/ChatScene.fxml b/src/main/resources/fxml/ChatScene.fxml
index 3239307..0ecdeb2 100644
--- a/src/main/resources/fxml/ChatScene.fxml
+++ b/src/main/resources/fxml/ChatScene.fxml
@@ -1,6 +1,7 @@
+
@@ -9,6 +10,7 @@
+
@@ -23,6 +25,9 @@
prefWidth="160.0" />