diff --git a/src/main/java/envoy/client/ui/ClearableTextField.java b/src/main/java/envoy/client/ui/ClearableTextField.java
index f19155b..b8b3a34 100644
--- a/src/main/java/envoy/client/ui/ClearableTextField.java
+++ b/src/main/java/envoy/client/ui/ClearableTextField.java
@@ -11,8 +11,6 @@ import javafx.scene.layout.Background;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
-import envoy.client.data.Settings;
-
/**
* This class offers a text field that is automatically equipped with a clear
* button.
@@ -49,10 +47,7 @@ public class ClearableTextField extends GridPane {
public ClearableTextField(String text, int size) {
// initializing the textField and the button
textField = new TextField(text);
- clearButton = new Button("",
- new ImageView(IconUtil.load(
- Settings.getInstance().getCurrentTheme().equals("dark") ? "/icons/clear_button_white.png" : "/icons/clear_button_black.png",
- size)));
+ clearButton = new Button("", new ImageView(IconUtil.loadDefaultThemeSensitive("clear_button_", size)));
clearButton.setOnAction(e -> textField.clear());
clearButton.setFocusTraversable(false);
clearButton.getStyleClass().clear();
diff --git a/src/main/java/envoy/client/ui/IconUtil.java b/src/main/java/envoy/client/ui/IconUtil.java
index b477e53..337227e 100644
--- a/src/main/java/envoy/client/ui/IconUtil.java
+++ b/src/main/java/envoy/client/ui/IconUtil.java
@@ -2,9 +2,13 @@ package envoy.client.ui;
import java.util.EnumMap;
import java.util.EnumSet;
+import java.util.logging.Level;
import javafx.scene.image.Image;
+import envoy.client.data.Settings;
+import envoy.util.EnvoyLog;
+
/**
* Provides static utility methods for loading icons from the resource
* folder.
@@ -27,7 +31,16 @@ public class IconUtil {
* @return the icon
* @since Envoy Client v0.1-beta
*/
- public static Image load(String path) { return new Image(IconUtil.class.getResource(path).toExternalForm()); }
+ public static Image load(String path) {
+ Image image;
+ try {
+ image = new Image(IconUtil.class.getResource(path).toExternalForm());
+ } catch (final NullPointerException e) {
+ EnvoyLog.getLogger(IconUtil.class).log(Level.WARNING, String.format("Could not load image at path %s: ", path), e);
+ throw e;
+ }
+ return image;
+ }
/**
* Loads an icon from the resource folder and scales it to a given size.
@@ -38,9 +51,93 @@ public class IconUtil {
* @since Envoy Client v0.1-beta
*/
public static Image load(String path, int size) {
- return new Image(IconUtil.class.getResource(path).toExternalForm(), size, size, true, true);
+ Image image;
+ try {
+ image = new Image(IconUtil.class.getResource(path).toExternalForm(), size, size, true, true);
+ } catch (final NullPointerException e) {
+ EnvoyLog.getLogger(IconUtil.class).log(Level.WARNING, String.format("Could not load image at path %s: ", path), e);
+ throw e;
+ }
+ return image;
}
+ /**
+ * Loads a {@code .png} icon from the sub-folder {@code /icons/} of the resource
+ * folder and scales it to 16px.
+ * 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 take a sample image {@code abc.png} in the folder
+ * {@code /icons/}.
+ *
+ * To do that, we only have to call
+ * {@code IconUtil.loadDefault("abc")}
+ */
+ public static Image loadDefault(String name) { return load("/icons/" + name + ".png"); }
+
+ /**
+ * Loads a {@code .png} icon from the sub-folder {@code /icons/} of the resource
+ * folder and scales it to a 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 take a sample image {@code abc.png} in the folder
+ * {@code /icons/} and load it in size 16.
+ *
+ * To do that, we only have to call
+ * {@code IconUtil.loadDefault("abc", 16)}
+ */
+ public static Image loadDefault(String name, int size) { return load("/icons/" + name + ".png", size); }
+
+ /**
+ * Loads a {@code .png} icon whose design depends on the currently active theme
+ * from the sub-folder {@code /icons/} of the resource folder.
+ *
+ * In case of the dark theme, the suffix {@code "white"} will be appended, else + * the suffix {@code "black"} will be appended. + *
+ * 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 abc_black.png} and
+ * {@code abc_white.png} in the folder {@code /icons/} and load them.
+ *
+ * To do that theme sensitve, we only have to call
+ * {@code IconUtil.loadDefaultThemeSensitive("abc_")}
+ */
+ public static Image loadDefaultThemeSensitive(String name) { return loadDefault(name + themeSpecificSuffix()); }
+
+ /**
+ * Loads a {@code .png} icon whose design depends on the currently active theme
+ * from the sub-folder {@code /icons/} of the resource
+ * folder and scales it to the given size.
+ *
+ * In case of the dark theme, the suffix {@code "white"} will be appended, else + * the suffix {@code "black"} will be appended. + *
+ * 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 abc_black.png} and
+ * {@code abc_white.png} in the folder {@code /icons/} and load it in
+ * size 16.
+ *
+ * To do that theme sensitve, we only have to call
+ * {@code IconUtil.loadDefaultThemeSensitive("abc_", 16)}
+ */
+ public static Image loadDefaultThemeSensitive(String name, int size) { return loadDefault(name + themeSpecificSuffix(), size); }
+
/**
*
* Loads icons specified by an enum. The images have to be named like the
@@ -62,4 +159,15 @@ public class IconUtil {
icons.put(e, load(path + e.toString().toLowerCase() + ".png", size));
return icons;
}
+
+ /**
+ * This method should be called if the display of an icon depends upon the
+ * currently active theme.
+ * In case of the dark theme, the suffix {@code "white"} will be appended, else
+ * the suffix {@code "black"} will be appended.
+ *
+ * @return the theme specific suffix
+ * @since Envoy Client v0.1-beta
+ */
+ public static String themeSpecificSuffix() { return Settings.getInstance().getCurrentTheme().equals("dark") ? "white" : "black"; }
}
diff --git a/src/main/java/envoy/client/ui/Startup.java b/src/main/java/envoy/client/ui/Startup.java
index c505f33..a28963a 100644
--- a/src/main/java/envoy/client/ui/Startup.java
+++ b/src/main/java/envoy/client/ui/Startup.java
@@ -102,7 +102,7 @@ public final class Startup extends Application {
messageStatusCache = new Cache<>();
stage.setTitle("Envoy");
- stage.getIcons().add(IconUtil.load("/icons/envoy_logo.png"));
+ stage.getIcons().add(IconUtil.loadDefault("envoy_logo"));
final var sceneContext = new SceneContext(stage);
sceneContext.load(SceneInfo.LOGIN_SCENE);
diff --git a/src/main/java/envoy/client/ui/controller/ChatScene.java b/src/main/java/envoy/client/ui/controller/ChatScene.java
index bb457d4..f515952 100644
--- a/src/main/java/envoy/client/ui/controller/ChatScene.java
+++ b/src/main/java/envoy/client/ui/controller/ChatScene.java
@@ -110,7 +110,7 @@ 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.loadDefault("settings", 16)));
// Listen to received messages
eventBus.register(MessageCreationEvent.class, e -> {
@@ -222,9 +222,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
@@ -270,8 +270,7 @@ public final class ChatScene implements Restorable {
Platform.runLater(() -> {
voiceButton.setText("Record Voice Message");
checkPostConditions(false);
- attachmentView.setImage(IconUtil.load(settings.getCurrentTheme().equals("dark") ? "/icons/attachment_present_white.png"
- : "/icons/attachment_present_black.png", 20));
+ attachmentView.setImage(IconUtil.loadDefaultThemeSensitive("attachment_present_", 20));
attachmentView.setVisible(true);
});
}
@@ -311,7 +310,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))
@@ -373,6 +372,7 @@ public final class ChatScene implements Restorable {
if (pendingAttachment != null) {
builder.setAttachment(pendingAttachment);
pendingAttachment = null;
+ attachmentView.setVisible(false);
}
final var message = builder.build();
diff --git a/src/main/resources/fxml/ChatScene.fxml b/src/main/resources/fxml/ChatScene.fxml
index 76f77a0..0ecdeb2 100644
--- a/src/main/resources/fxml/ChatScene.fxml
+++ b/src/main/resources/fxml/ChatScene.fxml
@@ -117,7 +117,7 @@