Add Audio Playback Capability
* Add envoy.client.data.audio package * Move AudioRecorder to the audio package * Add AudioPlayer class * Add AudioControl class that acts as a small media player * Display the audio control in message controls that contain voice messages
This commit is contained in:
parent
9883a5ddd3
commit
a90f58afe0
64
src/main/java/envoy/client/data/audio/AudioPlayer.java
Normal file
64
src/main/java/envoy/client/data/audio/AudioPlayer.java
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
package envoy.client.data.audio;
|
||||||
|
|
||||||
|
import javax.sound.sampled.*;
|
||||||
|
|
||||||
|
import envoy.exception.EnvoyException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plays back audio from a byte array.
|
||||||
|
* <p>
|
||||||
|
* Project: <strong>envoy-client</strong><br>
|
||||||
|
* File: <strong>AudioPlayer.java</strong><br>
|
||||||
|
* Created: <strong>05.07.2020</strong><br>
|
||||||
|
*
|
||||||
|
* @author Kai S. K. Engelbart
|
||||||
|
* @since Envoy Client v0.1-beta
|
||||||
|
*/
|
||||||
|
public final class AudioPlayer {
|
||||||
|
|
||||||
|
private final AudioFormat format;
|
||||||
|
private final DataLine.Info info;
|
||||||
|
|
||||||
|
private Clip clip;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the player with the default audio format.
|
||||||
|
*
|
||||||
|
* @since Envoy Client v0.1-beta
|
||||||
|
*/
|
||||||
|
public AudioPlayer() { this(AudioRecorder.DEFAULT_AUDIO_FORMAT); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the player with a given audio format.
|
||||||
|
*
|
||||||
|
* @param format the audio format to use
|
||||||
|
* @since Envoy Client v0.1-beta
|
||||||
|
*/
|
||||||
|
public AudioPlayer(AudioFormat format) {
|
||||||
|
this.format = format;
|
||||||
|
info = new DataLine.Info(Clip.class, format);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {@code true} if audio play back is supported
|
||||||
|
* @since Envoy Client v0.1-beta
|
||||||
|
*/
|
||||||
|
public boolean isSupported() { return AudioSystem.isLineSupported(info); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plays back an audio clip.
|
||||||
|
*
|
||||||
|
* @param data the data of the clip
|
||||||
|
* @throws EnvoyException if the play back failed
|
||||||
|
* @since Envoy Client v0.1-beta
|
||||||
|
*/
|
||||||
|
public void play(byte[] data) throws EnvoyException {
|
||||||
|
try {
|
||||||
|
clip = (Clip) AudioSystem.getLine(info);
|
||||||
|
clip.open(format, data, 0, data.length);
|
||||||
|
clip.start();
|
||||||
|
} catch (LineUnavailableException e) {
|
||||||
|
throw new EnvoyException("Cannot play back audio", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package envoy.client.data;
|
package envoy.client.data.audio;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
@ -20,21 +20,49 @@ import envoy.exception.EnvoyException;
|
|||||||
*/
|
*/
|
||||||
public final class AudioRecorder {
|
public final class AudioRecorder {
|
||||||
|
|
||||||
private final AudioFormat format;
|
/**
|
||||||
|
* The default audio format used for recording and play back.
|
||||||
|
*
|
||||||
|
* @since Envoy Client v0.1-beta
|
||||||
|
*/
|
||||||
|
public static final AudioFormat DEFAULT_AUDIO_FORMAT = new AudioFormat(16000, 16, 1, true, false);
|
||||||
|
|
||||||
|
private final AudioFormat format;
|
||||||
|
private final DataLine.Info info;
|
||||||
|
|
||||||
private DataLine.Info info;
|
|
||||||
private TargetDataLine line;
|
private TargetDataLine line;
|
||||||
private Path tempFile;
|
private Path tempFile;
|
||||||
|
|
||||||
public AudioRecorder() { this(new AudioFormat(16000, 16, 1, true, false)); }
|
/**
|
||||||
|
* Initializes the recorder with the default audio format.
|
||||||
|
*
|
||||||
|
* @since Envoy Client v0.1-beta
|
||||||
|
*/
|
||||||
|
public AudioRecorder() { this(DEFAULT_AUDIO_FORMAT); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the recorder with a given audio format.
|
||||||
|
*
|
||||||
|
* @param format the audio format to use
|
||||||
|
* @since Envoy Client v0.1-beta
|
||||||
|
*/
|
||||||
public AudioRecorder(AudioFormat format) {
|
public AudioRecorder(AudioFormat format) {
|
||||||
this.format = format;
|
this.format = format;
|
||||||
info = new DataLine.Info(TargetDataLine.class, format);
|
info = new DataLine.Info(TargetDataLine.class, format);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {@code true} if audio recording is supported
|
||||||
|
* @since Envoy Client v0.1-beta
|
||||||
|
*/
|
||||||
public boolean isSupported() { return AudioSystem.isLineSupported(info); }
|
public boolean isSupported() { return AudioSystem.isLineSupported(info); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the audio recording.
|
||||||
|
*
|
||||||
|
* @throws EnvoyException if starting the recording failed
|
||||||
|
* @since Envoy Client v0.1-beta
|
||||||
|
*/
|
||||||
public void start() throws EnvoyException {
|
public void start() throws EnvoyException {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
@ -54,6 +82,13 @@ public final class AudioRecorder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops the recording.
|
||||||
|
*
|
||||||
|
* @return the finished recording
|
||||||
|
* @throws EnvoyException if finishing the recording failed
|
||||||
|
* @since Envoy Client v0.1-beta
|
||||||
|
*/
|
||||||
public byte[] finish() throws EnvoyException {
|
public byte[] finish() throws EnvoyException {
|
||||||
try {
|
try {
|
||||||
line.stop();
|
line.stop();
|
11
src/main/java/envoy/client/data/audio/package-info.java
Normal file
11
src/main/java/envoy/client/data/audio/package-info.java
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* Contains classes related to recording and playing back audio clips.
|
||||||
|
* <p>
|
||||||
|
* Project: <strong>envoy-client</strong><br>
|
||||||
|
* File: <strong>package-info.java</strong><br>
|
||||||
|
* Created: <strong>05.07.2020</strong><br>
|
||||||
|
*
|
||||||
|
* @author Kai S. K. Engelbart
|
||||||
|
* @since Envoy Client v0.1-beta
|
||||||
|
*/
|
||||||
|
package envoy.client.data.audio;
|
40
src/main/java/envoy/client/ui/AudioControl.java
Normal file
40
src/main/java/envoy/client/ui/AudioControl.java
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package envoy.client.ui;
|
||||||
|
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
|
||||||
|
import envoy.client.data.audio.AudioPlayer;
|
||||||
|
import envoy.exception.EnvoyException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables the play back of audio clips through a button.
|
||||||
|
* <p>
|
||||||
|
* Project: <strong>envoy-client</strong><br>
|
||||||
|
* File: <strong>AudioControl.java</strong><br>
|
||||||
|
* Created: <strong>05.07.2020</strong><br>
|
||||||
|
*
|
||||||
|
* @author Kai S. K. Engelbart
|
||||||
|
* @since Envoy Client v0.1-beta
|
||||||
|
*/
|
||||||
|
public final class AudioControl extends HBox {
|
||||||
|
|
||||||
|
private AudioPlayer player = new AudioPlayer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the audio control.
|
||||||
|
*
|
||||||
|
* @param audioData the audio data to play.
|
||||||
|
* @since Envoy Client v0.1-beta
|
||||||
|
*/
|
||||||
|
public AudioControl(byte[] audioData) {
|
||||||
|
var button = new Button("Play");
|
||||||
|
button.setOnAction(e -> {
|
||||||
|
try {
|
||||||
|
player.play(audioData);
|
||||||
|
} catch (EnvoyException ex) {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
getChildren().add(button);
|
||||||
|
}
|
||||||
|
}
|
@ -18,6 +18,7 @@ import javafx.scene.input.KeyEvent;
|
|||||||
import javafx.scene.paint.Color;
|
import javafx.scene.paint.Color;
|
||||||
|
|
||||||
import envoy.client.data.*;
|
import envoy.client.data.*;
|
||||||
|
import envoy.client.data.audio.AudioRecorder;
|
||||||
import envoy.client.event.MessageCreationEvent;
|
import envoy.client.event.MessageCreationEvent;
|
||||||
import envoy.client.net.Client;
|
import envoy.client.net.Client;
|
||||||
import envoy.client.net.WriteProxy;
|
import envoy.client.net.WriteProxy;
|
||||||
|
@ -9,7 +9,9 @@ import javafx.scene.image.Image;
|
|||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
|
|
||||||
|
import envoy.client.ui.AudioControl;
|
||||||
import envoy.client.ui.IconUtil;
|
import envoy.client.ui.IconUtil;
|
||||||
|
import envoy.data.Attachment.AttachmentType;
|
||||||
import envoy.data.Message;
|
import envoy.data.Message;
|
||||||
import envoy.data.Message.MessageStatus;
|
import envoy.data.Message.MessageStatus;
|
||||||
import envoy.data.User;
|
import envoy.data.User;
|
||||||
@ -38,6 +40,11 @@ public class MessageControl extends VBox {
|
|||||||
public MessageControl(Message message) {
|
public MessageControl(Message message) {
|
||||||
// Creating the underlying VBox, the dateLabel and the textLabel
|
// Creating the underlying VBox, the dateLabel and the textLabel
|
||||||
super(new Label(dateFormat.format(message.getCreationDate())));
|
super(new Label(dateFormat.format(message.getCreationDate())));
|
||||||
|
|
||||||
|
// Voice attachment
|
||||||
|
if (message.hasAttachment() && message.getAttachment().getType() == AttachmentType.VOICE)
|
||||||
|
getChildren().add(new AudioControl(message.getAttachment().getData()));
|
||||||
|
|
||||||
final var textLabel = new Label(message.getText());
|
final var textLabel = new Label(message.getText());
|
||||||
textLabel.setWrapText(true);
|
textLabel.setWrapText(true);
|
||||||
getChildren().add(textLabel);
|
getChildren().add(textLabel);
|
||||||
|
@ -13,21 +13,35 @@
|
|||||||
<?import javafx.scene.layout.GridPane?>
|
<?import javafx.scene.layout.GridPane?>
|
||||||
<?import javafx.scene.layout.RowConstraints?>
|
<?import javafx.scene.layout.RowConstraints?>
|
||||||
|
|
||||||
<GridPane hgap="5.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="400.0" minWidth="350.0" prefHeight="400.0" prefWidth="600.0" vgap="2.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="envoy.client.ui.controller.ChatScene">
|
<GridPane hgap="5.0" maxHeight="-Infinity" maxWidth="-Infinity"
|
||||||
|
minHeight="400.0" minWidth="350.0" prefHeight="400.0" prefWidth="600.0"
|
||||||
|
vgap="2.0" xmlns="http://javafx.com/javafx/11.0.1"
|
||||||
|
xmlns:fx="http://javafx.com/fxml/1"
|
||||||
|
fx:controller="envoy.client.ui.controller.ChatScene">
|
||||||
<columnConstraints>
|
<columnConstraints>
|
||||||
<ColumnConstraints hgrow="NEVER" minWidth="60.0" prefWidth="160.0" />
|
<ColumnConstraints hgrow="NEVER" minWidth="60.0"
|
||||||
<ColumnConstraints hgrow="ALWAYS" maxWidth="1.7976931348623157E308" minWidth="10.0" prefWidth="357.0" />
|
prefWidth="160.0" />
|
||||||
|
<ColumnConstraints hgrow="ALWAYS"
|
||||||
|
maxWidth="1.7976931348623157E308" minWidth="10.0" prefWidth="357.0" />
|
||||||
</columnConstraints>
|
</columnConstraints>
|
||||||
<rowConstraints>
|
<rowConstraints>
|
||||||
<RowConstraints maxHeight="-Infinity" minHeight="-Infinity" prefHeight="50.0" vgrow="NEVER" />
|
<RowConstraints maxHeight="-Infinity"
|
||||||
<RowConstraints maxHeight="-Infinity" minHeight="-Infinity" prefHeight="20.0" vgrow="NEVER" />
|
minHeight="-Infinity" prefHeight="50.0" vgrow="NEVER" />
|
||||||
<RowConstraints maxHeight="1.7976931348623157E308" minHeight="50.0" prefHeight="155.14286150251115" vgrow="ALWAYS" />
|
<RowConstraints maxHeight="-Infinity"
|
||||||
<RowConstraints maxHeight="-Infinity" minHeight="-Infinity" prefHeight="20.0" vgrow="NEVER" />
|
minHeight="-Infinity" prefHeight="20.0" vgrow="NEVER" />
|
||||||
<RowConstraints maxHeight="120.0" minHeight="40.0" prefHeight="60.0" vgrow="NEVER" />
|
<RowConstraints maxHeight="1.7976931348623157E308"
|
||||||
<RowConstraints maxHeight="-Infinity" minHeight="-Infinity" prefHeight="40.0" vgrow="NEVER" />
|
minHeight="50.0" prefHeight="155.14286150251115" vgrow="ALWAYS" />
|
||||||
|
<RowConstraints maxHeight="-Infinity"
|
||||||
|
minHeight="-Infinity" prefHeight="20.0" vgrow="NEVER" />
|
||||||
|
<RowConstraints maxHeight="120.0" minHeight="40.0"
|
||||||
|
prefHeight="60.0" vgrow="NEVER" />
|
||||||
|
<RowConstraints maxHeight="-Infinity"
|
||||||
|
minHeight="-Infinity" prefHeight="40.0" vgrow="NEVER" />
|
||||||
</rowConstraints>
|
</rowConstraints>
|
||||||
<children>
|
<children>
|
||||||
<ListView fx:id="userList" onMouseClicked="#userListClicked" prefHeight="211.0" prefWidth="300.0" GridPane.rowIndex="1" GridPane.rowSpan="2147483647">
|
<ListView fx:id="userList" onMouseClicked="#userListClicked"
|
||||||
|
prefHeight="211.0" prefWidth="300.0" GridPane.rowIndex="1"
|
||||||
|
GridPane.rowSpan="2147483647">
|
||||||
<GridPane.margin>
|
<GridPane.margin>
|
||||||
<Insets bottom="10.0" left="10.0" />
|
<Insets bottom="10.0" left="10.0" />
|
||||||
</GridPane.margin>
|
</GridPane.margin>
|
||||||
@ -37,12 +51,14 @@
|
|||||||
<contextMenu>
|
<contextMenu>
|
||||||
<ContextMenu anchorLocation="CONTENT_TOP_LEFT">
|
<ContextMenu anchorLocation="CONTENT_TOP_LEFT">
|
||||||
<items>
|
<items>
|
||||||
<MenuItem fx:id="deleteContactMenuItem" mnemonicParsing="false" onAction="#deleteContact" text="Delete" />
|
<MenuItem fx:id="deleteContactMenuItem"
|
||||||
|
mnemonicParsing="false" onAction="#deleteContact" text="Delete" />
|
||||||
</items>
|
</items>
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
</contextMenu>
|
</contextMenu>
|
||||||
</ListView>
|
</ListView>
|
||||||
<Label fx:id="contactLabel" prefHeight="27.0" prefWidth="134.0" GridPane.columnSpan="2">
|
<Label fx:id="contactLabel" prefHeight="27.0" prefWidth="134.0"
|
||||||
|
GridPane.columnSpan="2">
|
||||||
<GridPane.margin>
|
<GridPane.margin>
|
||||||
<Insets left="10.0" />
|
<Insets left="10.0" />
|
||||||
</GridPane.margin>
|
</GridPane.margin>
|
||||||
@ -50,7 +66,10 @@
|
|||||||
<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>
|
||||||
</Label>
|
</Label>
|
||||||
<Button fx:id="settingsButton" mnemonicParsing="true" onAction="#settingsButtonClicked" text="_Settings" GridPane.columnIndex="1" GridPane.halignment="RIGHT" GridPane.valignment="CENTER">
|
<Button fx:id="settingsButton" mnemonicParsing="true"
|
||||||
|
onAction="#settingsButtonClicked" text="_Settings"
|
||||||
|
GridPane.columnIndex="1" GridPane.halignment="RIGHT"
|
||||||
|
GridPane.valignment="CENTER">
|
||||||
<GridPane.margin>
|
<GridPane.margin>
|
||||||
<Insets bottom="10.0" right="10.0" top="10.0" />
|
<Insets bottom="10.0" right="10.0" top="10.0" />
|
||||||
</GridPane.margin>
|
</GridPane.margin>
|
||||||
@ -58,7 +77,10 @@
|
|||||||
<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" prefWidth="465.0" GridPane.columnIndex="1" GridPane.columnSpan="2147483647" GridPane.rowIndex="1" GridPane.rowSpan="2">
|
<ListView fx:id="messageList" prefHeight="257.0"
|
||||||
|
prefWidth="465.0" GridPane.columnIndex="1"
|
||||||
|
GridPane.columnSpan="2147483647" GridPane.rowIndex="1"
|
||||||
|
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>
|
||||||
@ -68,29 +90,43 @@
|
|||||||
<contextMenu>
|
<contextMenu>
|
||||||
<ContextMenu anchorLocation="CONTENT_TOP_LEFT">
|
<ContextMenu anchorLocation="CONTENT_TOP_LEFT">
|
||||||
<items>
|
<items>
|
||||||
<MenuItem mnemonicParsing="false" onAction="#copyMessage" text="Copy" />
|
<MenuItem mnemonicParsing="false" onAction="#copyMessage"
|
||||||
<MenuItem mnemonicParsing="false" onAction="#deleteMessage" text="Delete" />
|
text="Copy" />
|
||||||
<MenuItem mnemonicParsing="false" onAction="#forwardMessage" text="Forward" />
|
<MenuItem mnemonicParsing="false"
|
||||||
<MenuItem mnemonicParsing="false" onAction="#quoteMessage" text="Quote" />
|
onAction="#deleteMessage" text="Delete" />
|
||||||
<MenuItem mnemonicParsing="false" onAction="#loadMessageInfoScene" text="Info" />
|
<MenuItem mnemonicParsing="false"
|
||||||
|
onAction="#forwardMessage" text="Forward" />
|
||||||
|
<MenuItem mnemonicParsing="false"
|
||||||
|
onAction="#quoteMessage" text="Quote" />
|
||||||
|
<MenuItem mnemonicParsing="false"
|
||||||
|
onAction="#loadMessageInfoScene" text="Info" />
|
||||||
</items>
|
</items>
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
</contextMenu>
|
</contextMenu>
|
||||||
</ListView>
|
</ListView>
|
||||||
<ButtonBar prefWidth="436.0" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="5" GridPane.valignment="BOTTOM">
|
<ButtonBar prefWidth="436.0" GridPane.columnIndex="1"
|
||||||
|
GridPane.halignment="CENTER" GridPane.rowIndex="5"
|
||||||
|
GridPane.valignment="BOTTOM">
|
||||||
<GridPane.margin>
|
<GridPane.margin>
|
||||||
<Insets right="10.0" top="5.0" />
|
<Insets right="10.0" top="5.0" />
|
||||||
</GridPane.margin>
|
</GridPane.margin>
|
||||||
<buttons>
|
<buttons>
|
||||||
<Button fx:id="voiceButton" onAction="#voiceButtonClicked" text="_Record Voice Message" />
|
<Button fx:id="voiceButton" onAction="#voiceButtonClicked"
|
||||||
<Button fx:id="postButton" defaultButton="true" disable="true" mnemonicParsing="true" onAction="#postMessage" prefHeight="10.0" prefWidth="75.0" text="_Post">
|
text="_Record Voice Message" />
|
||||||
|
<Button fx:id="postButton" defaultButton="true"
|
||||||
|
disable="true" mnemonicParsing="true" onAction="#postMessage"
|
||||||
|
prefHeight="10.0" prefWidth="75.0" text="_Post">
|
||||||
<tooltip>
|
<tooltip>
|
||||||
<Tooltip anchorLocation="WINDOW_TOP_LEFT" autoHide="true" maxWidth="350.0" text="Click this button to send the message. If it is disabled, you first have to select a contact to send it to. A message may automatically be sent when you press (Ctrl + ) Enter, according to your preferences. Additionally sends a message when pressing "Alt" + "P"." wrapText="true" />
|
<Tooltip anchorLocation="WINDOW_TOP_LEFT" autoHide="true"
|
||||||
|
maxWidth="350.0"
|
||||||
|
text="Click this button to send the message. If it is disabled, you first have to select a contact to send it to. A message may automatically be sent when you press (Ctrl + ) Enter, according to your preferences. Additionally sends a message when pressing "Alt" + "P"."
|
||||||
|
wrapText="true" />
|
||||||
</tooltip>
|
</tooltip>
|
||||||
<contextMenu>
|
<contextMenu>
|
||||||
<ContextMenu anchorLocation="CONTENT_TOP_LEFT">
|
<ContextMenu anchorLocation="CONTENT_TOP_LEFT">
|
||||||
<items>
|
<items>
|
||||||
<MenuItem mnemonicParsing="false" onAction="#copyAndPostMessage" text="Copy and Send" />
|
<MenuItem mnemonicParsing="false"
|
||||||
|
onAction="#copyAndPostMessage" text="Copy and Send" />
|
||||||
</items>
|
</items>
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
</contextMenu>
|
</contextMenu>
|
||||||
@ -100,7 +136,11 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</buttons>
|
</buttons>
|
||||||
</ButtonBar>
|
</ButtonBar>
|
||||||
<TextArea fx:id="messageTextArea" disable="true" onInputMethodTextChanged="#messageTextUpdated" onKeyPressed="#checkPostConditions" onKeyTyped="#checkKeyCombination" prefHeight="200.0" prefWidth="200.0" wrapText="true" GridPane.columnIndex="1" GridPane.rowIndex="4">
|
<TextArea fx:id="messageTextArea" disable="true"
|
||||||
|
onInputMethodTextChanged="#messageTextUpdated"
|
||||||
|
onKeyPressed="#checkPostConditions" onKeyTyped="#checkKeyCombination"
|
||||||
|
prefHeight="200.0" prefWidth="200.0" wrapText="true"
|
||||||
|
GridPane.columnIndex="1" GridPane.rowIndex="4">
|
||||||
<GridPane.margin>
|
<GridPane.margin>
|
||||||
<Insets bottom="10.0" left="5.0" right="10.0" top="3.0" />
|
<Insets bottom="10.0" left="5.0" right="10.0" top="3.0" />
|
||||||
</GridPane.margin>
|
</GridPane.margin>
|
||||||
@ -108,7 +148,10 @@
|
|||||||
<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" />
|
||||||
</opaqueInsets>
|
</opaqueInsets>
|
||||||
</TextArea>
|
</TextArea>
|
||||||
<Button mnemonicParsing="true" onAction="#addContactButtonClicked" text="_Add Contacts" GridPane.halignment="CENTER" GridPane.rowIndex="5" GridPane.valignment="CENTER">
|
<Button mnemonicParsing="true"
|
||||||
|
onAction="#addContactButtonClicked" text="_Add Contacts"
|
||||||
|
GridPane.halignment="CENTER" GridPane.rowIndex="5"
|
||||||
|
GridPane.valignment="CENTER">
|
||||||
<padding>
|
<padding>
|
||||||
<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>
|
||||||
@ -116,7 +159,11 @@
|
|||||||
<Insets bottom="10.0" left="10.0" right="5.0" top="5.0" />
|
<Insets bottom="10.0" left="10.0" right="5.0" top="5.0" />
|
||||||
</GridPane.margin>
|
</GridPane.margin>
|
||||||
</Button>
|
</Button>
|
||||||
<Label id="remainingCharsLabel" fx:id="remainingChars" ellipsisString="" maxHeight="30.0" maxWidth="180.0" prefHeight="30.0" prefWidth="180.0" text="remaining chars: 0/x" textFill="LIME" textOverrun="LEADING_WORD_ELLIPSIS" visible="false" GridPane.columnIndex="1" GridPane.rowIndex="3">
|
<Label id="remainingCharsLabel" fx:id="remainingChars"
|
||||||
|
ellipsisString="" maxHeight="30.0" maxWidth="180.0" prefHeight="30.0"
|
||||||
|
prefWidth="180.0" text="remaining chars: 0/x" textFill="LIME"
|
||||||
|
textOverrun="LEADING_WORD_ELLIPSIS" visible="false"
|
||||||
|
GridPane.columnIndex="1" GridPane.rowIndex="3">
|
||||||
<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>
|
||||||
@ -127,10 +174,14 @@
|
|||||||
<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" />
|
||||||
</opaqueInsets>
|
</opaqueInsets>
|
||||||
<tooltip>
|
<tooltip>
|
||||||
<Tooltip text="Shows how many chars you can still enter in this message" wrapText="true" />
|
<Tooltip
|
||||||
|
text="Shows how many chars you can still enter in this message"
|
||||||
|
wrapText="true" />
|
||||||
</tooltip>
|
</tooltip>
|
||||||
</Label>
|
</Label>
|
||||||
<Label fx:id="infoLabel" text="Something happened" textFill="#faa007" visible="false" wrapText="true" GridPane.columnIndex="1" GridPane.rowIndex="1">
|
<Label fx:id="infoLabel" text="Something happened"
|
||||||
|
textFill="#faa007" visible="false" wrapText="true"
|
||||||
|
GridPane.columnIndex="1" GridPane.rowIndex="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>
|
||||||
|
Reference in New Issue
Block a user