Merge pull request #154 from informatik-ag-ngl/f/enhanced_UI

added:
    - alignment and coloring of messages according to sender
    - context menu for userList messageList and postButton (partially without function currently) -> implemented copying as well as "CopyAndSend" capability
    - hid horizontal scrollbars (they are still present, but cannot be seen)
    - displayed settings icon in ChatScene
This commit is contained in:
delvh 2020-06-26 20:20:37 +02:00 committed by GitHub
commit 961527e663
8 changed files with 293 additions and 99 deletions

View File

@ -35,11 +35,11 @@ public class ContactListCell extends ListCell<Contact> {
final var vbox = new VBox(new Label(contact.getName())); final var vbox = new VBox(new Label(contact.getName()));
if (contact instanceof User) { if (contact instanceof User) {
// Online status // Online status
final var user = (User) contact; final var user = (User) contact;
final var statusLabel = new Label(user.getStatus().toString()); final var statusLabel = new Label(user.getStatus().toString());
statusLabel.getStyleClass().add(user.getStatus().toString().toLowerCase()); statusLabel.getStyleClass().add(user.getStatus().toString().toLowerCase());
vbox.getChildren().add(statusLabel); vbox.getChildren().add(statusLabel);
} else { } else {
// Member count // Member count
vbox.getChildren().add(new Label(((Group) contact).getContacts().size() + " members")); vbox.getChildren().add(new Label(((Group) contact).getContacts().size() + " members"));
} }

View File

@ -5,15 +5,16 @@ import java.time.format.DateTimeFormatter;
import java.util.Map; import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
import javafx.geometry.Insets;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.ListCell; import javafx.scene.control.ListCell;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import envoy.data.Message; import envoy.data.Message;
import envoy.data.Message.MessageStatus; import envoy.data.Message.MessageStatus;
import envoy.data.User;
import envoy.util.EnvoyLog; import envoy.util.EnvoyLog;
/** /**
@ -30,10 +31,11 @@ public class MessageListCell extends ListCell<Message> {
private static final DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm"); private static final DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm");
private static Map<MessageStatus, Image> statusImages; private static Map<MessageStatus, Image> statusImages;
private static User client;
static { static {
try { try {
statusImages = IconUtil.loadByEnum(MessageStatus.class, 32); statusImages = IconUtil.loadByEnum(MessageStatus.class, 16);
} catch (final IOException e) { } catch (final IOException e) {
e.printStackTrace(); e.printStackTrace();
EnvoyLog.getLogger(MessageListCell.class).log(Level.WARNING, "could not load status icons: ", e); EnvoyLog.getLogger(MessageListCell.class).log(Level.WARNING, "could not load status icons: ", e);
@ -52,11 +54,19 @@ public class MessageListCell extends ListCell<Message> {
setText(null); setText(null);
setGraphic(null); setGraphic(null);
} else { } else {
setGraphic(new HBox( final var cell = new VBox(new Label(dateFormat.format(message.getCreationDate())), new Label(message.getText()));
new VBox( if (message.getRecipientID() == client.getID()) {
new Label(dateFormat.format(message.getCreationDate())), cell.getChildren().add(new Label("", new ImageView(statusImages.get(message.getStatus()))));
new Label(message.getText())), cell.getStyleClass().add("own-message");
new Label("", new ImageView(statusImages.get(message.getStatus()))))); } else cell.getStyleClass().add("received-message");
cell.paddingProperty().setValue(new Insets(5, 20, 5, 20));
setGraphic(cell);
} }
} }
/**
* @param client the user who chats with another person
* @since Envoy Client v0.1-beta
*/
public static void setUser(User client) { MessageListCell.client = client; }
} }

View File

@ -35,7 +35,7 @@ public final class Startup extends Application {
/** /**
* The version of this client. Used to verify compatibility with the server. * The version of this client. Used to verify compatibility with the server.
* *
* @since Envoy Client v0.1-beta * @since Envoy Client v0.1-beta
*/ */
public static final String VERSION = "0.1-beta"; public static final String VERSION = "0.1-beta";

View File

@ -1,5 +1,7 @@
package envoy.client.ui.controller; package envoy.client.ui.controller;
import java.awt.Toolkit;
import java.awt.datatransfer.StringSelection;
import java.io.IOException; import java.io.IOException;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -10,6 +12,7 @@ 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.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;
@ -20,9 +23,7 @@ import envoy.client.data.Settings;
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;
import envoy.client.ui.ContactListCell; import envoy.client.ui.*;
import envoy.client.ui.MessageListCell;
import envoy.client.ui.SceneContext;
import envoy.data.*; import envoy.data.*;
import envoy.event.EventBus; import envoy.event.EventBus;
import envoy.event.MessageStatusChange; import envoy.event.MessageStatusChange;
@ -61,6 +62,9 @@ public final class ChatScene {
@FXML @FXML
private Label remainingChars; private Label remainingChars;
@FXML
private MenuItem deleteContactMenuItem;
private LocalDB localDB; private LocalDB localDB;
private Client client; private Client client;
private WriteProxy writeProxy; private WriteProxy writeProxy;
@ -85,6 +89,12 @@ public final class ChatScene {
messageList.setCellFactory(listView -> new MessageListCell()); messageList.setCellFactory(listView -> new MessageListCell());
userList.setCellFactory(listView -> new ContactListCell()); userList.setCellFactory(listView -> new ContactListCell());
try {
settingsButton.setGraphic(new ImageView(IconUtil.load("/icons/settings.png", 16)));
} catch (final IOException e2) {
logger.log(Level.WARNING, "Could not load settings icon", e2);
}
// Listen to received messages // Listen to received messages
eventBus.register(MessageCreationEvent.class, e -> { eventBus.register(MessageCreationEvent.class, e -> {
final var message = e.get(); final var message = e.get();
@ -97,7 +107,7 @@ public final class ChatScene {
} catch (final IOException e1) { } catch (final IOException e1) {
logger.log(Level.WARNING, "Could not read current chat: ", e1); logger.log(Level.WARNING, "Could not read current chat: ", e1);
} }
Platform.runLater(messageList::refresh); Platform.runLater(() -> { messageList.refresh(); scrollToMessageListEnd(); });
} }
}); });
}); });
@ -153,6 +163,8 @@ public final class ChatScene {
this.writeProxy = writeProxy; this.writeProxy = writeProxy;
userList.setItems(FXCollections.observableList(localDB.getChats().stream().map(Chat::getRecipient).collect(Collectors.toList()))); userList.setItems(FXCollections.observableList(localDB.getChats().stream().map(Chat::getRecipient).collect(Collectors.toList())));
contactLabel.setText(localDB.getUser().getName());
MessageListCell.setUser(localDB.getUser());
} }
/** /**
@ -165,7 +177,6 @@ public final class ChatScene {
final Contact user = userList.getSelectionModel().getSelectedItem(); final Contact user = userList.getSelectionModel().getSelectedItem();
if (user != null && (currentChat == null || !user.equals(currentChat.getRecipient()))) { if (user != null && (currentChat == null || !user.equals(currentChat.getRecipient()))) {
logger.log(Level.FINEST, "Loading chat with " + user); logger.log(Level.FINEST, "Loading chat with " + user);
contactLabel.setText(user.getName());
// LEON: JFC <===> JAVA FRIED CHICKEN <=/=> Java Foundation Classes // LEON: JFC <===> JAVA FRIED CHICKEN <=/=> Java Foundation Classes
@ -173,6 +184,7 @@ public final class ChatScene {
currentChat = localDB.getChat(user.getID()).get(); currentChat = localDB.getChat(user.getID()).get();
messageList.setItems(FXCollections.observableList(currentChat.getMessages())); messageList.setItems(FXCollections.observableList(currentChat.getMessages()));
deleteContactMenuItem.setText("Delete " + user.getName());
// Read the current chat // Read the current chat
try { try {
@ -287,6 +299,7 @@ public final class ChatScene {
// Add message to LocalDB and update UI // Add message to LocalDB and update UI
messageList.getItems().add(message); messageList.getItems().add(message);
scrollToMessageListEnd();
// Request a new ID generator if all IDs were used // Request a new ID generator if all IDs were used
if (!localDB.getIDGenerator().hasNext() && client.isOnline()) client.requestIdGenerator(); if (!localDB.getIDGenerator().hasNext() && client.isOnline()) client.requestIdGenerator();
@ -301,4 +314,44 @@ public final class ChatScene {
postButton.setDisable(true); postButton.setDisable(true);
updateRemainingCharsLabel(); updateRemainingCharsLabel();
} }
/**
* Scrolls to the bottom of the {@code messageList}.
*
* @since Envoy Client v0.1-beta
*/
private void scrollToMessageListEnd() { messageList.scrollTo(messageList.getItems().size() - 1); }
// Context menu actions
@FXML
private void copyMessage() {
try {
Toolkit.getDefaultToolkit()
.getSystemClipboard()
.setContents(new StringSelection(messageList.getSelectionModel().getSelectedItem().getText()), null);
} catch (final NullPointerException e) {}
}
@FXML
private void deleteMessage() { try {} catch (final NullPointerException e) {} }
@FXML
private void forwardMessage() { try {} catch (final NullPointerException e) {} }
@FXML
private void quoteMessage() { try {} catch (final NullPointerException e) {} }
@FXML
private void deleteContact() { try {} catch (final NullPointerException e) {} }
@FXML
private void copyAndPostMessage() {
final var messageText = messageTextArea.getText();
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(messageText), null);
postMessage();
messageTextArea.setText(messageText);
updateRemainingCharsLabel();
postButton.setDisable(messageText.isBlank());
}
} }

View File

@ -1,5 +1,15 @@
.button { .button, .list-cell {
-fx-background-radius: 5em; -fx-background-radius: 5.0em;
}
.context-menu, .context-menu > * {
-fx-background-radius: 15px;
/*TODO: solution below does not work */
-fx-background-color: transparent;
}
.menu-item {
-fx-background-radius: 15.0px;
} }
.button:hover { .button:hover {
@ -11,10 +21,9 @@
-fx-background-color: transparent; -fx-background-color: transparent;
} }
#remainingCharsLabel { .scroll-bar:horizontal, .scroll-bar:horizontal *, .scroll-bar:horizontal > *{
-fx-text-fill: #00FF00;
-fx-opacity: 1;
-fx-background-color: transparent; -fx-background-color: transparent;
-fx-text-fill: transparent;
} }
.online { .online {
@ -32,3 +41,20 @@
.offline { .offline {
-fx-text-fill: gray; -fx-text-fill: gray;
} }
.received-message {
-fx-alignment: center-left;
-fx-background-radius: 4.0em;
-fx-text-alignment: right;
}
.own-message {
-fx-alignment: center-right;
-fx-background-radius: 4.0em;
-fx-text-alignment: left;
}
#remainingCharsLabel {
-fx-text-fill: #00FF00;
-fx-background-color: transparent;
}

View File

@ -18,12 +18,20 @@
-fx-background-color: lightgray; -fx-background-color: lightgray;
} }
.list-view, .list-cell, .text-area .content, .text-field, .password-field, .tooltip, .pane, .pane .content, .vbox, .titled-pane > .title, .titled-pane > *.content { .list-view, .list-cell, .text-area .content, .text-field, .password-field, .tooltip, .pane, .pane .content, .vbox, .titled-pane > .title, .titled-pane > *.content, .context-menu, .menu-item {
-fx-background-color: dimgray; -fx-background-color: dimgray;
} }
.list-cell:selected { .list-cell:selected, .list-cell:selected > *, .menu-item:hover {
-fx-background-color:rgb(105.0,0.0,153.0) ; -fx-background-color: rgb(105.0,0.0,153.0);
}
.received-message {
-fx-background-color: gray;
}
.own-message {
-fx-background-color: #8fa88f;
} }
.alert.information.dialog-pane, .alert.warning.dialog-pane, .alert.error.dialog-pane { .alert.information.dialog-pane, .alert.warning.dialog-pane, .alert.error.dialog-pane {

View File

@ -1,3 +1,16 @@
.button, .list-cell:selected{ .button{
-fx-background-color: orangered; -fx-background-color: orangered;
} }
.list-cell:selected, .list-cell:selected > * {
-fx-background-color: orangered;
-fx-text-fill: black;
}
.received-message, .menu-item {
-fx-background-color: lightgray;
}
.own-message {
-fx-background-color: lightgreen;
}

View File

@ -2,93 +2,177 @@
<?import javafx.geometry.Insets?> <?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?> <?import javafx.scene.control.Button?>
<?import javafx.scene.control.ContextMenu?>
<?import javafx.scene.control.Label?> <?import javafx.scene.control.Label?>
<?import javafx.scene.control.ListView?> <?import javafx.scene.control.ListView?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.control.TextArea?> <?import javafx.scene.control.TextArea?>
<?import javafx.scene.control.Tooltip?> <?import javafx.scene.control.Tooltip?>
<?import javafx.scene.layout.ColumnConstraints?> <?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?> <?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?> <?import javafx.scene.layout.RowConstraints?>
<GridPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="400.0" minWidth="350.0" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="envoy.client.ui.controller.ChatScene"> <GridPane maxHeight="-Infinity" maxWidth="-Infinity"
minHeight="400.0" minWidth="350.0" prefHeight="400.0" prefWidth="600.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="SOMETIMES" maxWidth="1.7976931348623157E308" minWidth="10.0" percentWidth="20.0" prefWidth="161.0" /> <ColumnConstraints hgrow="SOMETIMES"
<ColumnConstraints hgrow="SOMETIMES" maxWidth="1.7976931348623157E308" minWidth="10.0" percentWidth="65.0" prefWidth="357.0" /> maxWidth="1.7976931348623157E308" minWidth="10.0" percentWidth="20.0"
<ColumnConstraints hgrow="SOMETIMES" maxWidth="1.7976931348623157E308" minWidth="6.285736083984375" percentWidth="15.0" prefWidth="48.000030517578125" /> prefWidth="161.0" />
<ColumnConstraints hgrow="SOMETIMES"
maxWidth="1.7976931348623157E308" minWidth="10.0" percentWidth="65.0"
prefWidth="357.0" />
<ColumnConstraints hgrow="SOMETIMES"
maxWidth="1.7976931348623157E308" minWidth="6.285736083984375"
percentWidth="15.0" prefWidth="48.000030517578125" />
</columnConstraints> </columnConstraints>
<rowConstraints> <rowConstraints>
<RowConstraints maxHeight="1.7976931348623157E308" minHeight="10.0" percentHeight="10.0" prefHeight="70.0" vgrow="SOMETIMES" /> <RowConstraints maxHeight="1.7976931348623157E308"
<RowConstraints maxHeight="1.7976931348623157E308" minHeight="10.0" percentHeight="70.0" prefHeight="326.2857404436384" vgrow="SOMETIMES" /> minHeight="10.0" percentHeight="10.0" prefHeight="70.0"
<RowConstraints maxHeight="1.7976931348623157E308" minHeight="10.0" percentHeight="5.0" prefHeight="50.0" vgrow="SOMETIMES" /> vgrow="SOMETIMES" />
<RowConstraints maxHeight="1.7976931348623157E308" minHeight="10.0" percentHeight="15.0" prefHeight="100.0" vgrow="SOMETIMES" /> <RowConstraints maxHeight="1.7976931348623157E308"
minHeight="10.0" percentHeight="70.0" prefHeight="326.2857404436384"
vgrow="SOMETIMES" />
<RowConstraints maxHeight="1.7976931348623157E308"
minHeight="10.0" percentHeight="5.0" prefHeight="50.0"
vgrow="SOMETIMES" />
<RowConstraints maxHeight="1.7976931348623157E308"
minHeight="10.0" percentHeight="15.0" prefHeight="100.0"
vgrow="SOMETIMES" />
</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"
<GridPane.margin> prefHeight="211.0" prefWidth="300.0" GridPane.rowIndex="1"
<Insets bottom="10.0" left="10.0" right="5.0" top="5.0" /> GridPane.rowSpan="2147483647">
</GridPane.margin> <GridPane.margin>
<padding> <Insets bottom="10.0" left="10.0" right="5.0" top="5.0" />
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" /> </GridPane.margin>
</padding></ListView> <padding>
<Label fx:id="contactLabel" prefHeight="16.0" prefWidth="250.0" text="Select a contact to chat with" GridPane.columnSpan="2"> <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
<GridPane.margin> </padding>
<Insets bottom="5.0" left="10.0" right="5.0" top="10.0" /> <contextMenu>
</GridPane.margin> <ContextMenu anchorLocation="CONTENT_TOP_LEFT">
<padding> <items>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" /> <MenuItem fx:id="deleteContactMenuItem"
</padding></Label> mnemonicParsing="false" text="Delete" onAction="#deleteContact" />
<Button fx:id="settingsButton" mnemonicParsing="true" onAction="#settingsButtonClicked" text="_Settings" GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.valignment="CENTER"> </items>
<GridPane.margin> </ContextMenu>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" /> </contextMenu>
</GridPane.margin> </ListView>
<padding> <Label fx:id="contactLabel" prefHeight="16.0" prefWidth="250.0"
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" /> text="Select a contact to chat with" GridPane.columnSpan="2">
</padding></Button> <GridPane.margin>
<ListView fx:id="messageList" prefHeight="257.0" prefWidth="155.0" GridPane.columnIndex="1" GridPane.columnSpan="2" GridPane.rowIndex="1" GridPane.rowSpan="2"> <Insets left="15.0" right="5.0" top="5.0" />
<GridPane.margin> </GridPane.margin>
<Insets bottom="5.0" left="5.0" right="10.0" top="5.0" /> <padding>
</GridPane.margin> <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
<padding> </padding>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" /> </Label>
</padding></ListView> <Button fx:id="settingsButton" mnemonicParsing="true"
<Button fx:id="postButton" defaultButton="true" disable="true" mnemonicParsing="true" onAction="#postMessage" prefHeight="10.0" prefWidth="65.0" text="_Post" GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.rowIndex="3" GridPane.valignment="CENTER"> onAction="#settingsButtonClicked" text="_Settings"
<GridPane.margin> GridPane.columnIndex="1" GridPane.columnSpan="2" GridPane.halignment="RIGHT"
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" /> GridPane.valignment="CENTER">
</GridPane.margin> <GridPane.margin>
<padding> <Insets bottom="10.0" left="5.0" right="10.0" top="10.0" />
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" /> </GridPane.margin>
</padding> <padding>
<tooltip> <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
<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 &quot;Alt&quot; + &quot;P&quot;." wrapText="true" /> </padding>
</tooltip></Button> </Button>
<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="3"> <ListView fx:id="messageList" prefHeight="257.0"
<GridPane.margin> prefWidth="155.0" GridPane.columnIndex="1" GridPane.columnSpan="2"
<Insets bottom="10.0" left="5.0" right="5.0" top="5.0" /> GridPane.rowIndex="1" GridPane.rowSpan="2">
</GridPane.margin> <GridPane.margin>
<opaqueInsets> <Insets bottom="5.0" left="5.0" right="10.0" top="5.0" />
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" /> </GridPane.margin>
</opaqueInsets></TextArea> <padding>
<Button mnemonicParsing="true" onAction="#addContactButtonClicked" text="_Add Contacts" GridPane.halignment="CENTER" GridPane.rowIndex="3" GridPane.valignment="CENTER"> <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
<padding> </padding>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" /> <contextMenu>
</padding> <ContextMenu anchorLocation="CONTENT_TOP_LEFT">
<GridPane.margin> <items>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" /> <MenuItem mnemonicParsing="false" text="Copy"
</GridPane.margin> onAction="#copyMessage" />
</Button> <MenuItem mnemonicParsing="false" text="Delete"
<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="2"> onAction="#deleteMessage" />
<GridPane.margin> <MenuItem mnemonicParsing="false" text="Forward"
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" /> onAction="#forwardMessage" />
</GridPane.margin> <MenuItem mnemonicParsing="false" text="Quote"
<padding> onAction="#quoteMessage" />
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" /> </items>
</padding> </ContextMenu>
<opaqueInsets> </contextMenu>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" /> </ListView>
</opaqueInsets> <Button fx:id="postButton" defaultButton="true" disable="true"
<tooltip> mnemonicParsing="true" onAction="#postMessage" prefHeight="10.0"
<Tooltip text="Shows how many chars you can still enter in this message" wrapText="true" /> prefWidth="65.0" text="_Post" GridPane.columnIndex="2"
</tooltip> GridPane.halignment="CENTER" GridPane.rowIndex="3"
</Label> GridPane.valignment="CENTER">
<GridPane.margin>
<Insets bottom="5.0" left="5.0" right="5.0"/>
</GridPane.margin>
<padding>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
</padding>
<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 &quot;Alt&quot; + &quot;P&quot;."
wrapText="true" />
</tooltip>
<contextMenu>
<ContextMenu anchorLocation="CONTENT_TOP_LEFT">
<items>
<MenuItem mnemonicParsing="false" text="Copy and Send"
onAction="#copyAndPostMessage" />
</items>
</ContextMenu>
</contextMenu>
</Button>
<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="3">
<GridPane.margin>
<Insets bottom="10.0" left="5.0" right="5.0" top="5.0" />
</GridPane.margin>
<opaqueInsets>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
</opaqueInsets>
</TextArea>
<Button mnemonicParsing="true"
onAction="#addContactButtonClicked" text="_Add Contacts"
GridPane.halignment="CENTER" GridPane.rowIndex="3"
GridPane.valignment="CENTER">
<padding>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
</padding>
<GridPane.margin>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
</GridPane.margin>
</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="2">
<GridPane.margin>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
</GridPane.margin>
<padding>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
</padding>
<opaqueInsets>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
</opaqueInsets>
<tooltip>
<Tooltip
text="Shows how many chars you can still enter in this message"
wrapText="true" />
</tooltip>
</Label>
</children> </children>
</GridPane> </GridPane>