Merge pull request #183 from informatik-ag-ngl/f/pictures
Add attachment creation support, display picture messages
This commit is contained in:
		@@ -2,7 +2,10 @@ package envoy.client.ui.controller;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import java.awt.Toolkit;
 | 
					import java.awt.Toolkit;
 | 
				
			||||||
import java.awt.datatransfer.StringSelection;
 | 
					import java.awt.datatransfer.StringSelection;
 | 
				
			||||||
 | 
					import java.io.ByteArrayInputStream;
 | 
				
			||||||
 | 
					import java.io.File;
 | 
				
			||||||
import java.io.IOException;
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.nio.file.Files;
 | 
				
			||||||
import java.util.logging.Level;
 | 
					import java.util.logging.Level;
 | 
				
			||||||
import java.util.logging.Logger;
 | 
					import java.util.logging.Logger;
 | 
				
			||||||
import java.util.stream.Collectors;
 | 
					import java.util.stream.Collectors;
 | 
				
			||||||
@@ -12,10 +15,12 @@ import javafx.collections.FXCollections;
 | 
				
			|||||||
import javafx.fxml.FXML;
 | 
					import javafx.fxml.FXML;
 | 
				
			||||||
import javafx.scene.control.*;
 | 
					import javafx.scene.control.*;
 | 
				
			||||||
import javafx.scene.control.Alert.AlertType;
 | 
					import javafx.scene.control.Alert.AlertType;
 | 
				
			||||||
 | 
					import javafx.scene.image.Image;
 | 
				
			||||||
import javafx.scene.image.ImageView;
 | 
					import javafx.scene.image.ImageView;
 | 
				
			||||||
import javafx.scene.input.KeyCode;
 | 
					import javafx.scene.input.KeyCode;
 | 
				
			||||||
import javafx.scene.input.KeyEvent;
 | 
					import javafx.scene.input.KeyEvent;
 | 
				
			||||||
import javafx.scene.paint.Color;
 | 
					import javafx.scene.paint.Color;
 | 
				
			||||||
 | 
					import javafx.stage.FileChooser;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import envoy.client.data.*;
 | 
					import envoy.client.data.*;
 | 
				
			||||||
import envoy.client.data.audio.AudioRecorder;
 | 
					import envoy.client.data.audio.AudioRecorder;
 | 
				
			||||||
@@ -60,6 +65,9 @@ public final class ChatScene implements Restorable {
 | 
				
			|||||||
	@FXML
 | 
						@FXML
 | 
				
			||||||
	private Button voiceButton;
 | 
						private Button voiceButton;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@FXML
 | 
				
			||||||
 | 
						private Button attachmentButton;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@FXML
 | 
						@FXML
 | 
				
			||||||
	private Button settingsButton;
 | 
						private Button settingsButton;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -87,12 +95,15 @@ public final class ChatScene implements Restorable {
 | 
				
			|||||||
	private AudioRecorder	recorder;
 | 
						private AudioRecorder	recorder;
 | 
				
			||||||
	private boolean			recording;
 | 
						private boolean			recording;
 | 
				
			||||||
	private Attachment		pendingAttachment;
 | 
						private Attachment		pendingAttachment;
 | 
				
			||||||
	private boolean			postingPermanentlyDisabled	= false;
 | 
						private boolean			postingPermanentlyDisabled;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private static final Settings	settings			= Settings.getInstance();
 | 
						private static final Settings	settings	= Settings.getInstance();
 | 
				
			||||||
	private static final EventBus	eventBus			= EventBus.getInstance();
 | 
						private static final EventBus	eventBus	= EventBus.getInstance();
 | 
				
			||||||
	private static final Logger		logger				= EnvoyLog.getLogger(ChatScene.class);
 | 
						private static final Logger		logger		= EnvoyLog.getLogger(ChatScene.class);
 | 
				
			||||||
	private static final int		MAX_MESSAGE_LENGTH	= 255;
 | 
					
 | 
				
			||||||
 | 
						private static final Image	DEFAULT_ATTACHMENT_VIEW_IMAGE	= IconUtil.loadIconThemeSensitive("attachment_present", 20);
 | 
				
			||||||
 | 
						private static final int	MAX_MESSAGE_LENGTH				= 255;
 | 
				
			||||||
 | 
						private static final int	DEFAULT_ICON_SIZE				= 16;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Initializes the appearance of certain visual components.
 | 
						 * Initializes the appearance of certain visual components.
 | 
				
			||||||
@@ -106,8 +117,10 @@ public final class ChatScene implements Restorable {
 | 
				
			|||||||
		messageList.setCellFactory(MessageListCellFactory::new);
 | 
							messageList.setCellFactory(MessageListCellFactory::new);
 | 
				
			||||||
		userList.setCellFactory(ContactListCellFactory::new);
 | 
							userList.setCellFactory(ContactListCellFactory::new);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		settingsButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("settings", 16)));
 | 
							settingsButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("settings", DEFAULT_ICON_SIZE)));
 | 
				
			||||||
		voiceButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("microphone", 20)));
 | 
							voiceButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("microphone", DEFAULT_ICON_SIZE)));
 | 
				
			||||||
 | 
							attachmentButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("attachment", DEFAULT_ICON_SIZE)));
 | 
				
			||||||
 | 
							attachmentView.setImage(DEFAULT_ATTACHMENT_VIEW_IMAGE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Listen to received messages
 | 
							// Listen to received messages
 | 
				
			||||||
		eventBus.register(MessageCreationEvent.class, e -> {
 | 
							eventBus.register(MessageCreationEvent.class, e -> {
 | 
				
			||||||
@@ -226,7 +239,7 @@ public final class ChatScene implements Restorable {
 | 
				
			|||||||
				recording = false;
 | 
									recording = false;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			pendingAttachment = null;
 | 
								pendingAttachment = null;
 | 
				
			||||||
			attachmentView.setVisible(false);
 | 
								updateAttachmentView(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			remainingChars.setVisible(true);
 | 
								remainingChars.setVisible(true);
 | 
				
			||||||
			remainingChars
 | 
								remainingChars
 | 
				
			||||||
@@ -234,6 +247,7 @@ public final class ChatScene implements Restorable {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		messageTextArea.setDisable(currentChat == null || postingPermanentlyDisabled);
 | 
							messageTextArea.setDisable(currentChat == null || postingPermanentlyDisabled);
 | 
				
			||||||
		voiceButton.setDisable(!recorder.isSupported());
 | 
							voiceButton.setDisable(!recorder.isSupported());
 | 
				
			||||||
 | 
							attachmentButton.setDisable(false);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
@@ -266,18 +280,17 @@ public final class ChatScene implements Restorable {
 | 
				
			|||||||
					recording = true;
 | 
										recording = true;
 | 
				
			||||||
					Platform.runLater(() -> {
 | 
										Platform.runLater(() -> {
 | 
				
			||||||
						voiceButton.setText("Recording");
 | 
											voiceButton.setText("Recording");
 | 
				
			||||||
						voiceButton.setGraphic(new ImageView(IconUtil.loadIcon("microphone_recording", 24)));
 | 
											voiceButton.setGraphic(new ImageView(IconUtil.loadIcon("microphone_recording", DEFAULT_ICON_SIZE)));
 | 
				
			||||||
					});
 | 
										});
 | 
				
			||||||
					recorder.start();
 | 
										recorder.start();
 | 
				
			||||||
				} else {
 | 
									} else {
 | 
				
			||||||
					pendingAttachment	= new Attachment(recorder.finish(), AttachmentType.VOICE);
 | 
										pendingAttachment	= new Attachment(recorder.finish(), AttachmentType.VOICE);
 | 
				
			||||||
					recording			= false;
 | 
										recording			= false;
 | 
				
			||||||
					Platform.runLater(() -> {
 | 
										Platform.runLater(() -> {
 | 
				
			||||||
						voiceButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("microphone", 20)));
 | 
											voiceButton.setGraphic(new ImageView(IconUtil.loadIconThemeSensitive("microphone", DEFAULT_ICON_SIZE)));
 | 
				
			||||||
						voiceButton.setText(null);
 | 
											voiceButton.setText(null);
 | 
				
			||||||
						checkPostConditions(false);
 | 
											checkPostConditions(false);
 | 
				
			||||||
						attachmentView.setImage(IconUtil.loadIconThemeSensitive("attachment_present", 20));
 | 
											updateAttachmentView(true);
 | 
				
			||||||
						attachmentView.setVisible(true);
 | 
					 | 
				
			||||||
					});
 | 
										});
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			} catch (final EnvoyException e) {
 | 
								} catch (final EnvoyException e) {
 | 
				
			||||||
@@ -287,6 +300,52 @@ public final class ChatScene implements Restorable {
 | 
				
			|||||||
		}).start();
 | 
							}).start();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@FXML
 | 
				
			||||||
 | 
						private void attachmentButtonClicked() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Display file chooser
 | 
				
			||||||
 | 
							final var fileChooser = new FileChooser();
 | 
				
			||||||
 | 
							fileChooser.setTitle("Add Attachment");
 | 
				
			||||||
 | 
							fileChooser.setInitialDirectory(new File(System.getProperty("user.home")));
 | 
				
			||||||
 | 
							fileChooser.getExtensionFilters()
 | 
				
			||||||
 | 
								.addAll(new FileChooser.ExtensionFilter("Pictures", "*.png", "*.jpg", "*.bmp", "*.gif"),
 | 
				
			||||||
 | 
										new FileChooser.ExtensionFilter("Videos", "*.mp4"),
 | 
				
			||||||
 | 
										new FileChooser.ExtensionFilter("All Files", "*.*"));
 | 
				
			||||||
 | 
							final var file = fileChooser.showOpenDialog(sceneContext.getStage());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (file != null) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Check max file size
 | 
				
			||||||
 | 
								if (file.length() > 16E6) {
 | 
				
			||||||
 | 
									new Alert(AlertType.WARNING, "The selected file exceeds the size limit of 16MB!").showAndWait();
 | 
				
			||||||
 | 
									return;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Get attachment type (default is document)
 | 
				
			||||||
 | 
								AttachmentType type = AttachmentType.DOCUMENT;
 | 
				
			||||||
 | 
								switch (fileChooser.getSelectedExtensionFilter().getDescription()) {
 | 
				
			||||||
 | 
									case "Pictures":
 | 
				
			||||||
 | 
										type = AttachmentType.PICTURE;
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
									case "Videos":
 | 
				
			||||||
 | 
										type = AttachmentType.VIDEO;
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Create the pending attachment
 | 
				
			||||||
 | 
								try {
 | 
				
			||||||
 | 
									final var fileBytes = Files.readAllBytes(file.toPath());
 | 
				
			||||||
 | 
									pendingAttachment = new Attachment(fileBytes, type);
 | 
				
			||||||
 | 
									// Setting the preview image as image of the attachmentView
 | 
				
			||||||
 | 
									if (type == AttachmentType.PICTURE)
 | 
				
			||||||
 | 
										attachmentView.setImage(new Image(new ByteArrayInputStream(fileBytes), DEFAULT_ICON_SIZE, DEFAULT_ICON_SIZE, true, true));
 | 
				
			||||||
 | 
									attachmentView.setVisible(true);
 | 
				
			||||||
 | 
								} catch (final IOException e) {
 | 
				
			||||||
 | 
									new Alert(AlertType.ERROR, "The selected file could not be loaded!").showAndWait();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Checks the text length of the {@code messageTextArea}, adjusts the
 | 
						 * Checks the text length of the {@code messageTextArea}, adjusts the
 | 
				
			||||||
	 * {@code remainingChars} label and checks whether to send the message
 | 
						 * {@code remainingChars} label and checks whether to send the message
 | 
				
			||||||
@@ -373,16 +432,16 @@ public final class ChatScene implements Restorable {
 | 
				
			|||||||
		final var text = messageTextArea.getText().strip();
 | 
							final var text = messageTextArea.getText().strip();
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
			// Creating the message and its metadata
 | 
								// Creating the message and its metadata
 | 
				
			||||||
			final var	builder	= new MessageBuilder(localDB.getUser().getID(), currentChat.getRecipient().getID(), localDB.getIDGenerator())
 | 
								final var builder = new MessageBuilder(localDB.getUser().getID(), currentChat.getRecipient().getID(), localDB.getIDGenerator())
 | 
				
			||||||
				.setText(text);
 | 
									.setText(text);
 | 
				
			||||||
      // Setting an attachment, if present
 | 
								// Setting an attachment, if present
 | 
				
			||||||
      if (pendingAttachment != null) {
 | 
								if (pendingAttachment != null) {
 | 
				
			||||||
				builder.setAttachment(pendingAttachment);
 | 
									builder.setAttachment(pendingAttachment);
 | 
				
			||||||
				pendingAttachment = null;
 | 
									pendingAttachment = null;
 | 
				
			||||||
				attachmentView.setVisible(false);
 | 
									updateAttachmentView(false);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
      // Building the final message
 | 
								// Building the final message
 | 
				
			||||||
			final var	message	= currentChat.getRecipient() instanceof Group ? builder.buildGroupMessage((Group) currentChat.getRecipient())
 | 
								final var message = currentChat.getRecipient() instanceof Group ? builder.buildGroupMessage((Group) currentChat.getRecipient())
 | 
				
			||||||
					: builder.build();
 | 
										: builder.build();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Send message
 | 
								// Send message
 | 
				
			||||||
@@ -428,8 +487,22 @@ public final class ChatScene implements Restorable {
 | 
				
			|||||||
		infoLabel.setVisible(true);
 | 
							infoLabel.setVisible(true);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Updates the {@code attachmentView} in terms of visibility.<br>
 | 
				
			||||||
 | 
						 * Additionally resets the shown image to
 | 
				
			||||||
 | 
						 * {@code DEFAULT_ATTACHMENT_VIEW_IMAGE} if another image is currently
 | 
				
			||||||
 | 
						 * present.
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * @param visible whether the {@code attachmentView} should be displayed
 | 
				
			||||||
 | 
						 * @since Envoy Client v0.1-beta
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						private void updateAttachmentView(boolean visible) {
 | 
				
			||||||
 | 
							if (!attachmentView.getImage().equals(DEFAULT_ATTACHMENT_VIEW_IMAGE)) attachmentView.setImage(DEFAULT_ATTACHMENT_VIEW_IMAGE);
 | 
				
			||||||
 | 
							attachmentView.setVisible(visible);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Context menu actions
 | 
						// Context menu actions
 | 
				
			||||||
	
 | 
					
 | 
				
			||||||
	@FXML
 | 
						@FXML
 | 
				
			||||||
	private void deleteContact() { try {} catch (final NullPointerException e) {} }
 | 
						private void deleteContact() { try {} catch (final NullPointerException e) {} }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@ package envoy.client.ui.listcell;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import java.awt.Toolkit;
 | 
					import java.awt.Toolkit;
 | 
				
			||||||
import java.awt.datatransfer.StringSelection;
 | 
					import java.awt.datatransfer.StringSelection;
 | 
				
			||||||
 | 
					import java.io.ByteArrayInputStream;
 | 
				
			||||||
import java.time.format.DateTimeFormatter;
 | 
					import java.time.format.DateTimeFormatter;
 | 
				
			||||||
import java.util.Map;
 | 
					import java.util.Map;
 | 
				
			||||||
import java.util.logging.Level;
 | 
					import java.util.logging.Level;
 | 
				
			||||||
@@ -67,6 +68,7 @@ public class MessageControl extends Label {
 | 
				
			|||||||
		if (message.hasAttachment()) {
 | 
							if (message.hasAttachment()) {
 | 
				
			||||||
			switch (message.getAttachment().getType()) {
 | 
								switch (message.getAttachment().getType()) {
 | 
				
			||||||
				case PICTURE:
 | 
									case PICTURE:
 | 
				
			||||||
 | 
										vbox.getChildren().add(new ImageView(new Image(new ByteArrayInputStream(message.getAttachment().getData()), 256, 256, true, true)));
 | 
				
			||||||
					break;
 | 
										break;
 | 
				
			||||||
				case VIDEO:
 | 
									case VIDEO:
 | 
				
			||||||
					break;
 | 
										break;
 | 
				
			||||||
@@ -90,9 +92,7 @@ public class MessageControl extends Label {
 | 
				
			|||||||
			statusIcon.setPreserveRatio(true);
 | 
								statusIcon.setPreserveRatio(true);
 | 
				
			||||||
			vbox.getChildren().add(statusIcon);
 | 
								vbox.getChildren().add(statusIcon);
 | 
				
			||||||
			getStyleClass().add("own-message");
 | 
								getStyleClass().add("own-message");
 | 
				
			||||||
		} else {
 | 
							} else getStyleClass().add("received-message");
 | 
				
			||||||
			getStyleClass().add("received-message");
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		// Adjusting height and weight of the cell to the corresponding ListView
 | 
							// Adjusting height and weight of the cell to the corresponding ListView
 | 
				
			||||||
		paddingProperty().setValue(new Insets(5, 20, 5, 20));
 | 
							paddingProperty().setValue(new Insets(5, 20, 5, 20));
 | 
				
			||||||
		setContextMenu(contextMenu);
 | 
							setContextMenu(contextMenu);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -51,7 +51,7 @@
 | 
				
			|||||||
				<Insets bottom="5.0" left="10.0" />
 | 
									<Insets bottom="5.0" left="10.0" />
 | 
				
			||||||
			</GridPane.margin>
 | 
								</GridPane.margin>
 | 
				
			||||||
			<padding>
 | 
								<padding>
 | 
				
			||||||
				<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
 | 
									<Insets bottom="5.0" left="5.0" right="2.0" top="5.0" />
 | 
				
			||||||
			</padding>
 | 
								</padding>
 | 
				
			||||||
			<contextMenu>
 | 
								<contextMenu>
 | 
				
			||||||
				<ContextMenu anchorLocation="CONTENT_TOP_LEFT">
 | 
									<ContextMenu anchorLocation="CONTENT_TOP_LEFT">
 | 
				
			||||||
@@ -82,24 +82,29 @@
 | 
				
			|||||||
				<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
 | 
									<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
 | 
				
			||||||
			</padding>
 | 
								</padding>
 | 
				
			||||||
		</Button>
 | 
							</Button>
 | 
				
			||||||
		<ListView fx:id="messageList" prefHeight="257.0"
 | 
							<ListView fx:id="messageList" GridPane.columnIndex="1"
 | 
				
			||||||
			prefWidth="465.0" GridPane.columnIndex="1"
 | 
					 | 
				
			||||||
			GridPane.columnSpan="2147483647" GridPane.rowIndex="1"
 | 
								GridPane.columnSpan="2147483647" GridPane.rowIndex="1"
 | 
				
			||||||
			GridPane.rowSpan="2">
 | 
								GridPane.rowSpan="2">
 | 
				
			||||||
			<GridPane.margin>
 | 
								<GridPane.margin>
 | 
				
			||||||
				<Insets left="5.0" right="10.0" />
 | 
									<Insets left="5.0" right="10.0" />
 | 
				
			||||||
			</GridPane.margin>
 | 
								</GridPane.margin>
 | 
				
			||||||
			<padding>
 | 
								<padding>
 | 
				
			||||||
				<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
 | 
									<Insets bottom="5.0" left="5.0" right="2.0" top="5.0" />
 | 
				
			||||||
			</padding>
 | 
								</padding>
 | 
				
			||||||
		</ListView>
 | 
							</ListView>
 | 
				
			||||||
		<ButtonBar prefWidth="436.0" GridPane.columnIndex="1"
 | 
							<ButtonBar buttonMinWidth="40.0" GridPane.columnIndex="1"
 | 
				
			||||||
			GridPane.columnSpan="2147483647" GridPane.halignment="CENTER"
 | 
								GridPane.columnSpan="2147483647" GridPane.halignment="CENTER"
 | 
				
			||||||
			GridPane.rowIndex="5" GridPane.valignment="BOTTOM">
 | 
								GridPane.rowIndex="5" GridPane.valignment="BOTTOM">
 | 
				
			||||||
			<GridPane.margin>
 | 
								<GridPane.margin>
 | 
				
			||||||
				<Insets right="10.0" />
 | 
									<Insets right="10.0" />
 | 
				
			||||||
			</GridPane.margin>
 | 
								</GridPane.margin>
 | 
				
			||||||
			<buttons>
 | 
								<buttons>
 | 
				
			||||||
 | 
									<Button fx:id="attachmentButton" disable="true"
 | 
				
			||||||
 | 
										mnemonicParsing="false" onAction="#attachmentButtonClicked">
 | 
				
			||||||
 | 
										<padding>
 | 
				
			||||||
 | 
											<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
 | 
				
			||||||
 | 
										</padding>
 | 
				
			||||||
 | 
									</Button>
 | 
				
			||||||
				<Button fx:id="voiceButton" disable="true"
 | 
									<Button fx:id="voiceButton" disable="true"
 | 
				
			||||||
					onAction="#voiceButtonClicked">
 | 
										onAction="#voiceButtonClicked">
 | 
				
			||||||
					<padding>
 | 
										<padding>
 | 
				
			||||||
@@ -175,7 +180,7 @@
 | 
				
			|||||||
		</Label>
 | 
							</Label>
 | 
				
			||||||
		<Label fx:id="infoLabel" text="Something happened"
 | 
							<Label fx:id="infoLabel" text="Something happened"
 | 
				
			||||||
			textFill="#faa007" visible="false" wrapText="true"
 | 
								textFill="#faa007" visible="false" wrapText="true"
 | 
				
			||||||
			GridPane.columnIndex="1" GridPane.rowIndex="1">
 | 
								GridPane.columnIndex="1">
 | 
				
			||||||
			<GridPane.margin>
 | 
								<GridPane.margin>
 | 
				
			||||||
				<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
 | 
									<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
 | 
				
			||||||
			</GridPane.margin>
 | 
								</GridPane.margin>
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										
											BIN
										
									
								
								src/main/resources/icons/dark/search.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/main/resources/icons/dark/search.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 10 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/main/resources/icons/light/search.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/main/resources/icons/light/search.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 8.3 KiB  | 
		Reference in New Issue
	
	Block a user