Merge pull request #178 from informatik-ag-ngl/f/more_icons
Added multiple (recording) icons and change them on recording. Updated and simplified IconUtil API. Dismissed rethrowing of image loading error.
@ -25,7 +25,7 @@ import envoy.util.SerializationUtils;
|
||||
public class Settings {
|
||||
|
||||
// Actual settings accessible by the rest of the application
|
||||
private Map<String, SettingsItem<?>> items;
|
||||
private Map<String, SettingsItem<?>> items;
|
||||
|
||||
/**
|
||||
* Settings are stored in this file.
|
||||
@ -93,6 +93,15 @@ public class Settings {
|
||||
*/
|
||||
public void setCurrentTheme(String themeName) { ((SettingsItem<String>) items.get("currentTheme")).set(themeName); }
|
||||
|
||||
/**
|
||||
* @return true if the currently used theme is one of the default themes
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public boolean isUsingDefaultTheme() {
|
||||
final var theme = getCurrentTheme();
|
||||
return theme.equals("dark") || theme.equals("light");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@code true}, if pressing the {@code Enter} key suffices to send a
|
||||
* message. Otherwise it has to be pressed in conjunction with the
|
||||
|
@ -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.loadIconThemeSensitive("clear_button", size)));
|
||||
clearButton.setOnAction(e -> textField.clear());
|
||||
clearButton.setFocusTraversable(false);
|
||||
clearButton.getStyleClass().clear();
|
||||
|
@ -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.
|
||||
@ -21,37 +25,115 @@ public class IconUtil {
|
||||
private IconUtil() {}
|
||||
|
||||
/**
|
||||
* Loads an icon from the resource folder.
|
||||
* Loads an image from the resource folder.
|
||||
*
|
||||
* @param path the path to the icon inside the resource folder
|
||||
* @return the icon
|
||||
* @return the loaded image
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public static Image load(String path) { return new Image(IconUtil.class.getResource(path).toExternalForm()); }
|
||||
|
||||
/**
|
||||
* Loads an icon from the resource folder and scales it to a given size.
|
||||
*
|
||||
* @param path the path to the icon inside the resource folder
|
||||
* @param size the size to scale the icon to
|
||||
* @return the scaled icon
|
||||
* @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);
|
||||
public static Image load(String path) {
|
||||
Image image = null;
|
||||
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);
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads an image from the resource folder and scales it to the given size.
|
||||
*
|
||||
* Loads icons specified by an enum. The images have to be named like the
|
||||
* @param path the path to the icon inside the resource folder
|
||||
* @param size the size to scale the icon to
|
||||
* @return the scaled image
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
public static Image load(String path, int size) {
|
||||
Image image = null;
|
||||
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);
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a {@code .png} image from the sub-folder {@code /icons/} of the
|
||||
* resource folder.<br>
|
||||
* 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}.<br>
|
||||
* 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.<br>
|
||||
* 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.<br>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.<br>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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.<br>
|
||||
* 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 <T> the enum that specifies the icons to load
|
||||
* @param <T> the enum that specifies the images to load
|
||||
* @param enumClass the class of the enum
|
||||
* @param size the size to scale the icons to
|
||||
* @return a map containing the loaded icons with the corresponding enum
|
||||
* @param size the size to scale the images to
|
||||
* @return a map containing the loaded images with the corresponding enum
|
||||
* constants as keys
|
||||
* @since Envoy Client v0.1-beta
|
||||
*/
|
||||
@ -62,4 +144,17 @@ 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 image depends upon the
|
||||
* currently active theme.<br>
|
||||
* 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() + "/" : "";
|
||||
}
|
||||
}
|
||||
|
@ -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.loadIcon("envoy_logo"));
|
||||
|
||||
final var sceneContext = new SceneContext(stage);
|
||||
sceneContext.load(SceneInfo.LOGIN_SCENE);
|
||||
|
@ -110,7 +110,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 -> {
|
||||
@ -222,9 +223,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
|
||||
@ -262,16 +263,19 @@ 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");
|
||||
voiceButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("microphone", 20)));
|
||||
voiceButton.setText(null);
|
||||
checkPostConditions(false);
|
||||
attachmentView.setImage(IconUtil.load(settings.getCurrentTheme().equals("dark") ? "/icons/attachment_present_white.png"
|
||||
: "/icons/attachment_present_black.png", 20));
|
||||
attachmentView.setImage(IconUtil.loadIconThemeSensitive("attachment_present", 20));
|
||||
attachmentView.setVisible(true);
|
||||
});
|
||||
}
|
||||
@ -311,7 +315,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 +377,7 @@ public final class ChatScene implements Restorable {
|
||||
if (pendingAttachment != null) {
|
||||
builder.setAttachment(pendingAttachment);
|
||||
pendingAttachment = null;
|
||||
attachmentView.setVisible(false);
|
||||
}
|
||||
final var message = builder.build();
|
||||
|
||||
|
@ -117,7 +117,7 @@
|
||||
</GridPane.margin>
|
||||
<buttons>
|
||||
<Button fx:id="voiceButton" disable="true"
|
||||
onAction="#voiceButtonClicked" text="_Record Voice Message">
|
||||
onAction="#voiceButtonClicked">
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
</padding>
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
BIN
src/main/resources/icons/dark/microphone.png
Normal file
After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
BIN
src/main/resources/icons/light/forward.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
src/main/resources/icons/light/microphone.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
src/main/resources/icons/light/settings.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
src/main/resources/icons/microphone_recording.png
Normal file
After Width: | Height: | Size: 20 KiB |