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 @@