From da3a675a009248a4112457da964c436fdf4c5b96 Mon Sep 17 00:00:00 2001 From: cody-malcolm Date: Wed, 14 Apr 2021 20:19:18 -0400 Subject: [PATCH 01/13] Switch to gridpane --- .../org/amc/ataxx/client/GameController.java | 6 +- .../java/org/amc/ataxx/client/GameView.java | 10 +-- .../resources/org/amc/ataxx/client/game.css | 2 +- .../resources/org/amc/ataxx/client/game.fxml | 73 +++++++++---------- 4 files changed, 44 insertions(+), 47 deletions(-) diff --git a/src/main/java/org/amc/ataxx/client/GameController.java b/src/main/java/org/amc/ataxx/client/GameController.java index 6f893d6..8186dcf 100644 --- a/src/main/java/org/amc/ataxx/client/GameController.java +++ b/src/main/java/org/amc/ataxx/client/GameController.java @@ -8,6 +8,8 @@ import javafx.scene.control.ScrollPane; import javafx.scene.control.TextField; import javafx.scene.layout.BorderPane; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import org.amc.ataxx.GameLogic; import org.javatuples.Pair; @@ -17,7 +19,7 @@ public class GameController extends Controller { @FXML - private BorderPane borderPane; + private HBox canvasContainer; @FXML private VBox messagesContainer; @FXML @@ -97,7 +99,7 @@ private void initialize() { // initialize the Canvas view = GameView.getInstance(); view.setGameController(this); - view.createCanvas(borderPane); + view.createCanvas(canvasContainer); this.buffer.setText("\r\n"); } diff --git a/src/main/java/org/amc/ataxx/client/GameView.java b/src/main/java/org/amc/ataxx/client/GameView.java index 9f0c635..912a215 100644 --- a/src/main/java/org/amc/ataxx/client/GameView.java +++ b/src/main/java/org/amc/ataxx/client/GameView.java @@ -17,7 +17,7 @@ import javafx.scene.control.Label; import javafx.scene.control.ScrollPane; import javafx.scene.input.MouseEvent; -import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import javafx.scene.paint.Color; import javafx.util.Duration; @@ -31,7 +31,7 @@ public class GameView { private static GameView instance = null; /** The size of one square of the board, in pixels */ - final private static int SIZE = 70; + final private static int SIZE = 75; /** The true size of a drawn square of the board, in pixels */ final private static int actualSIZE = SIZE-2; /** The size of the canvas, in pixels */ @@ -66,16 +66,16 @@ public synchronized static GameView getInstance(){ /** * Creates the Canvas that board will be rendered on * - * @param borderPane the BorderPane to attach the Canvas to + * @param container the BorderPane to attach the Canvas to */ - public void createCanvas(BorderPane borderPane) { + public void createCanvas(HBox container) { Canvas canvas = new Canvas(); canvas.setHeight(canvasSIZE); canvas.setWidth(canvasSIZE); canvas.addEventHandler(MouseEvent.MOUSE_PRESSED, instance.handleMouseClick()); gc = canvas.getGraphicsContext2D(); - borderPane.setCenter(canvas); + container.getChildren().add(canvas); } /** diff --git a/src/main/resources/org/amc/ataxx/client/game.css b/src/main/resources/org/amc/ataxx/client/game.css index 85c0277..323bbf1 100644 --- a/src/main/resources/org/amc/ataxx/client/game.css +++ b/src/main/resources/org/amc/ataxx/client/game.css @@ -50,4 +50,4 @@ .gameIDlabel { -fx-font-size: 16px; -} \ No newline at end of file +} diff --git a/src/main/resources/org/amc/ataxx/client/game.fxml b/src/main/resources/org/amc/ataxx/client/game.fxml index 4754c0d..5a94304 100644 --- a/src/main/resources/org/amc/ataxx/client/game.fxml +++ b/src/main/resources/org/amc/ataxx/client/game.fxml @@ -7,9 +7,8 @@ - - + @@ -22,39 +21,35 @@ - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + From c8767e25669874adf9660cf32a5219142e105ea8 Mon Sep 17 00:00:00 2001 From: cody-malcolm Date: Wed, 14 Apr 2021 20:44:04 -0400 Subject: [PATCH 02/13] More spacing work --- .../java/org/amc/ataxx/client/GameView.java | 2 +- .../resources/org/amc/ataxx/client/game.fxml | 25 ++++++++++++------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/amc/ataxx/client/GameView.java b/src/main/java/org/amc/ataxx/client/GameView.java index 912a215..bc5e095 100644 --- a/src/main/java/org/amc/ataxx/client/GameView.java +++ b/src/main/java/org/amc/ataxx/client/GameView.java @@ -445,7 +445,7 @@ private void insertMessage(String message, VBox container, ScrollPane pane, Stri } public void displayGameId(String gameId, Label gameIDlabel) { - gameIDlabel.setText("Game ID:" + gameId); + gameIDlabel.setText("Game ID: " + gameId); } public void displayCounts(Pair counts, Label blueLabel, Label redLabel) { diff --git a/src/main/resources/org/amc/ataxx/client/game.fxml b/src/main/resources/org/amc/ataxx/client/game.fxml index 5a94304..9bd772b 100644 --- a/src/main/resources/org/amc/ataxx/client/game.fxml +++ b/src/main/resources/org/amc/ataxx/client/game.fxml @@ -30,7 +30,7 @@ + styleClass="leftContainer" minWidth="315" maxWidth="315" minHeight="634" maxHeight="634"> @@ -43,13 +43,20 @@ - - From 243b71f6fc070e409d9143ba15493fb8be35c1ff Mon Sep 17 00:00:00 2001 From: mariya0900 Date: Wed, 14 Apr 2021 20:51:53 -0400 Subject: [PATCH 03/13] README and some more comments --- README.md | 18 +++++++ demos/findGame.png | Bin 16906 -> 9787 bytes demos/gameInPlay.png | Bin 0 -> 30607 bytes demos/username.png | Bin 0 -> 7235 bytes demos/welcome_screen.png | Bin 13306 -> 5510 bytes src/main/java/org/amc/ataxx/client/Main.java | 17 ++++++ src/main/java/org/amc/ataxx/client/User.java | 52 +++++++++++++++++-- src/main/java/org/amc/ataxx/server/Main.java | 1 - 8 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 demos/gameInPlay.png create mode 100644 demos/username.png diff --git a/README.md b/README.md index 2cf52ff..ff18952 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,25 @@ If application has been started correctly, the player will see the screen below. Hence, if the client is running on the same machine as the server, host IP address can be left empty. ![Alt text](./demos/welcome_screen.png?raw=true "Welcome screen"). + +## Functionality +* The server supports multiple clients. To start the game, both clients should be connected to the same IP address. +By clicking 'Find the game' button, the player will either be automatically added to the existing game, or he/she will + start a new one, waiting for a second player to connect. + ![Alt text](./demos/findGame.png?raw=true "Finding the game"). + +* The server is the sole source of authority of the game state. It's robust against a modified request from the client. + +* Each game is assigned to a unique gameID, allowing the client to spectate said game. + +* Username validation happens on the first welcome screen (can't be "", "-" or contain ' \ ' due to the Board string representation) +![Alt text](./demos/username.png?raw=true "Invalid username"). + +* During the game, the GameID and the number of squares occupied by each player is shown + + + ## Credit For the layout of the README we took inspiration from Cody's assignment2: https://github.com/cody-malcolm/FileSharer diff --git a/demos/findGame.png b/demos/findGame.png index 48f4486033076931fbe3c6595db3e6a92dfa8c92..7e48bdd0cee05322a7d936bf744eab80a0a983a0 100644 GIT binary patch literal 9787 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 R-t7Y1Ffh?aTy=i(e*k$yWitQ( literal 16906 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
  • QsVrHeP&6q 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 diff --git a/demos/gameInPlay.png b/demos/gameInPlay.png new file mode 100644 index 0000000000000000000000000000000000000000..20a5477b3a5c5f657f5c5e216a68cf043fbc9522 GIT binary patch literal 30607 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 literal 13306 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 diff --git a/src/main/java/org/amc/ataxx/client/Main.java b/src/main/java/org/amc/ataxx/client/Main.java index 7e8e01a..c9f75b0 100644 --- a/src/main/java/org/amc/ataxx/client/Main.java +++ b/src/main/java/org/amc/ataxx/client/Main.java @@ -4,21 +4,38 @@ import javafx.stage.Stage; public class Main extends Application { + /** Listens for and manages Server responses and user mouse clicks */ private static ClientListener listener; + /** + * Main method. Starts the user interface for the client + * + * @param args None + */ public static void main(String[] args) { launch(args); } + /** + * Static method setting the clientListener + * @param listener clientListener for server responses + */ public static void setListener(ClientListener listener) { Main.listener = listener; } + /** Method starting the application + * @param primaryStage primary stage of the application + */ @Override public void start(Stage primaryStage) { new SplashController(primaryStage).showStage(); } + /** + * Method closing the client window and closing the ClientListener for server responses + * @throws Exception + */ @Override public void stop() throws Exception { super.stop(); diff --git a/src/main/java/org/amc/ataxx/client/User.java b/src/main/java/org/amc/ataxx/client/User.java index 029dda3..1476846 100644 --- a/src/main/java/org/amc/ataxx/client/User.java +++ b/src/main/java/org/amc/ataxx/client/User.java @@ -26,15 +26,27 @@ public class User { private String[] displayNames = new String[2]; private boolean gameActive = false; + /** + * Constructor for a User + * @param username client's username + * @param key '1' or '2' for players, '3' for spectators + */ public User(String username, char key) { this.username = username; this.key = key; } + /** + * Getter for User's key + * @return key of type char, provided by the server + */ public char getKey() { return this.key; } - + /** + * Returns true if it's User's turn + * @return boolean representing if it's player's turn + */ public boolean usersTurn() { return this.key == this.activePlayer; } @@ -143,28 +155,52 @@ public String getBoard() { return this.board; } + /** + * Method setting player's usernames to display + * + * @param playerOneUsername first player's username + * @param playerTwoUsername second player's username + */ public void setDisplayNames(String playerOneUsername, String playerTwoUsername) { this.displayNames[0] = playerOneUsername; this.displayNames[1] = playerTwoUsername; } + /** + * Getter for displayNames (players' usernames) + * @return array of Strings + */ public String[] getDisplayNames() { return this.displayNames; } - + /** + * Setter for GameID. + * + * @param gameId of type String, randomly assigned + */ public void setGameId(String gameId) { this.gameId = gameId; } - public String getOpponentUsername() { + /*public String getOpponentUsername() { return this.opponentUsername; - } + }*/ + /** + * Getter for GameID. + * + * @return the game's gameID + */ public String getGameId() { return this.gameId; } + /** + * Gets a username of one of the players. + * @param key the "key" of the player, '1' or '2' + * @return active player's username, if key is this.key; opponent's username otherwise + */ public String getName(char key) { if (key == this.key) { return this.username; @@ -173,10 +209,18 @@ public String getName(char key) { } } + /** + * Setter for this.gameActive + * @param gameActive boolean representing if the game is active + */ public void setGameActive(boolean gameActive) { this.gameActive = gameActive; } + /** + * Getter for this.gameActive + * @return true if the game is active, false otherwise + */ public boolean getGameActive() { return this.gameActive; } diff --git a/src/main/java/org/amc/ataxx/server/Main.java b/src/main/java/org/amc/ataxx/server/Main.java index 8ce81b2..95cc156 100644 --- a/src/main/java/org/amc/ataxx/server/Main.java +++ b/src/main/java/org/amc/ataxx/server/Main.java @@ -34,7 +34,6 @@ private static void processCommands() { // initialize the Scanner Scanner scanner = new Scanner(System.in); - // TODO add and implement command to force close all connections // initialize the exit flag boolean exit = false; From fac2056710267cb6504aba34adef9faedd32aa78 Mon Sep 17 00:00:00 2001 From: cody-malcolm Date: Wed, 14 Apr 2021 21:00:34 -0400 Subject: [PATCH 04/13] Explicit turn indicators --- .../java/org/amc/ataxx/client/GameController.java | 14 +++++++++++--- src/main/java/org/amc/ataxx/client/GameView.java | 6 ++++++ src/main/resources/org/amc/ataxx/client/game.css | 5 +++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/amc/ataxx/client/GameController.java b/src/main/java/org/amc/ataxx/client/GameController.java index 8186dcf..4ecce1d 100644 --- a/src/main/java/org/amc/ataxx/client/GameController.java +++ b/src/main/java/org/amc/ataxx/client/GameController.java @@ -7,8 +7,6 @@ import javafx.scene.control.Label; import javafx.scene.control.ScrollPane; import javafx.scene.control.TextField; -import javafx.scene.layout.BorderPane; -import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import org.amc.ataxx.GameLogic; @@ -117,10 +115,19 @@ public void refreshBoard(String board, char activePlayer, char key, String[] dis view.renderBoard(board); user.setKey(key); user.setActivePlayer(activePlayer); + displayActivePlayer(); view.displayTurn(activePlayer, key, blueNameLabel, redNameLabel, displayNames); highlightSquares(board); // TODO 1 99% sure this can be deleted } + private void displayActivePlayer() { + if (user.usersTurn()) { + view.feedback("It's your turn!", feedbackLabel); + } else { + view.feedback("Opponent's turn...", feedbackLabel); + } + } + /** * Given a square that was clicked on, gives the information to the User. * @@ -170,6 +177,7 @@ public void handleMove(String oldBoard, String move, String newBoard, char activ char key, String[] displayNames) { user.setKey(key); user.setActivePlayer(activePlayer); + displayActivePlayer(); view.displayCounts(GameLogic.getCounts(newBoard), blueScoreLabel, redScoreLabel); view.displayTurn(activePlayer, key, blueNameLabel, redNameLabel, displayNames); view.animateMove(oldBoard, newBoard, move, activePlayer); @@ -191,7 +199,7 @@ public void updateBoard(String board) { */ public void winnerDetermined(char key) { String username = user.getName(key); - view.displayWinner(username); + view.feedback(username + " has won the game!", feedbackLabel); } public void processMessage(String message, char style) { diff --git a/src/main/java/org/amc/ataxx/client/GameView.java b/src/main/java/org/amc/ataxx/client/GameView.java index ffbbaf5..3bf739f 100644 --- a/src/main/java/org/amc/ataxx/client/GameView.java +++ b/src/main/java/org/amc/ataxx/client/GameView.java @@ -510,4 +510,10 @@ public void displayCounts(Pair counts, Label blueLabel, Label redLabel.setText(counts.getValue0().toString()); }); } + + public void feedback(String message, Label feedbackLabel) { + Platform.runLater(()-> { + feedbackLabel.setText(message); + }); + } } diff --git a/src/main/resources/org/amc/ataxx/client/game.css b/src/main/resources/org/amc/ataxx/client/game.css index 323bbf1..95b9ea5 100644 --- a/src/main/resources/org/amc/ataxx/client/game.css +++ b/src/main/resources/org/amc/ataxx/client/game.css @@ -51,3 +51,8 @@ .gameIDlabel { -fx-font-size: 16px; } + +.feedbackLabel { + -fx-alignment: center; + -fx-font-size: 16px; +} From d43ed1208d14210e9a4756ebe79575aa2545de8d Mon Sep 17 00:00:00 2001 From: cody-malcolm Date: Wed, 14 Apr 2021 21:36:14 -0400 Subject: [PATCH 05/13] Handling game over state on client side, bugfixes --- .../org/amc/ataxx/client/ClientListener.java | 2 ++ .../org/amc/ataxx/client/GameController.java | 10 ++-------- src/main/java/org/amc/ataxx/client/GameView.java | 7 +++++++ src/main/java/org/amc/ataxx/client/User.java | 10 ++++++++++ src/main/java/org/amc/ataxx/server/Game.java | 16 ++++++++++++++-- src/main/java/org/amc/ataxx/server/Player.java | 15 +++++++++------ .../resources/org/amc/ataxx/client/game.fxml | 10 +++++----- .../resources/org/amc/ataxx/client/splash.fxml | 4 +--- 8 files changed, 50 insertions(+), 24 deletions(-) diff --git a/src/main/java/org/amc/ataxx/client/ClientListener.java b/src/main/java/org/amc/ataxx/client/ClientListener.java index c740649..bd48eb7 100644 --- a/src/main/java/org/amc/ataxx/client/ClientListener.java +++ b/src/main/java/org/amc/ataxx/client/ClientListener.java @@ -166,7 +166,9 @@ private void handleGameResponse(String response) { char key = args[5].charAt(0); this.user.setBoard(args[3]); boolean gameActive = args[7].equals("true"); + boolean gameFinished = args[8].equals("true"); this.user.setGameActive(gameActive); + this.user.setGameFinished(gameFinished); if (!gameActive) { splashController.giveFeedback("Waiting for opponent..."); diff --git a/src/main/java/org/amc/ataxx/client/GameController.java b/src/main/java/org/amc/ataxx/client/GameController.java index 4ecce1d..bae52ee 100644 --- a/src/main/java/org/amc/ataxx/client/GameController.java +++ b/src/main/java/org/amc/ataxx/client/GameController.java @@ -200,6 +200,8 @@ public void updateBoard(String board) { public void winnerDetermined(char key) { String username = user.getName(key); view.feedback(username + " has won the game!", feedbackLabel); + view.handleGameOver(replayButton, newGameButton, gameIDlabel); + resignButton.setDisable(true); } public void processMessage(String message, char style) { @@ -211,14 +213,6 @@ public void processMessage(String message, char style) { } else if (style == 'b') { this.view.addError(message, this.messagesContainer, this.messagesScrollpane); } -// Platform.runLater(()-> { -// if (messages.getText().equals("")) { -// messages.setText(message); -// } else { -// messages.setText(messages.getText() + "\r\n" + message); -// } -// }); - } public void chat() { diff --git a/src/main/java/org/amc/ataxx/client/GameView.java b/src/main/java/org/amc/ataxx/client/GameView.java index 3bf739f..aad4f85 100644 --- a/src/main/java/org/amc/ataxx/client/GameView.java +++ b/src/main/java/org/amc/ataxx/client/GameView.java @@ -14,6 +14,7 @@ import javafx.scene.Node; import javafx.scene.canvas.Canvas; import javafx.scene.canvas.GraphicsContext; +import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.ScrollPane; import javafx.scene.input.MouseEvent; @@ -516,4 +517,10 @@ public void feedback(String message, Label feedbackLabel) { feedbackLabel.setText(message); }); } + + public void handleGameOver(Button replayButton, Button newGameButton, Label gameIDlabel) { + gameIDlabel.setVisible(false); + replayButton.setVisible(true); + newGameButton.setVisible(true); + } } diff --git a/src/main/java/org/amc/ataxx/client/User.java b/src/main/java/org/amc/ataxx/client/User.java index 1476846..e420581 100644 --- a/src/main/java/org/amc/ataxx/client/User.java +++ b/src/main/java/org/amc/ataxx/client/User.java @@ -25,6 +25,7 @@ public class User { private String gameId; private String[] displayNames = new String[2]; private boolean gameActive = false; + private boolean gameFinished = false; /** * Constructor for a User @@ -164,6 +165,11 @@ public String getBoard() { public void setDisplayNames(String playerOneUsername, String playerTwoUsername) { this.displayNames[0] = playerOneUsername; this.displayNames[1] = playerTwoUsername; + if (this.username.equals(playerOneUsername)) { + this.opponentUsername = playerTwoUsername; + } else { + this.opponentUsername = playerOneUsername; + } } /** @@ -224,4 +230,8 @@ public void setGameActive(boolean gameActive) { public boolean getGameActive() { return this.gameActive; } + + public void setGameFinished(boolean gameFinished) { + this.gameFinished = gameFinished; + } } diff --git a/src/main/java/org/amc/ataxx/server/Game.java b/src/main/java/org/amc/ataxx/server/Game.java index 27faff8..f6780ca 100644 --- a/src/main/java/org/amc/ataxx/server/Game.java +++ b/src/main/java/org/amc/ataxx/server/Game.java @@ -13,6 +13,7 @@ public class Game { private ArrayList spectators; private Board board; private char winner = '-'; // may be unnecessary + private boolean finished = false; public Game(String id) { // set up the board and any other internal stuff you need to do @@ -111,10 +112,10 @@ public void applyMove(String move, char key) { } /** - * Sets the current game inactive and allows for its players & spectators to start/observe a new game + * Sets the current game finished and allows for its players & spectators to start/observe a new game */ private void handleGameOver() { - this.active = false; + this.finished = false; for (Player player : this.players) { if (null != player) { player.finishedGame(); @@ -227,6 +228,17 @@ public ArrayList getSpectators() { /** * Returns true if this game is still active, false otherwise + * + * @return true if the game is active, false otherwise */ public boolean getActive() { return this.active; } + + /** + * Returns true if the game is finished, false otherwise + * + * @return true if the game is finished, false otherwise + */ + public boolean getFinished() { + return this.finished; + } } diff --git a/src/main/java/org/amc/ataxx/server/Player.java b/src/main/java/org/amc/ataxx/server/Player.java index e941b47..e80d888 100644 --- a/src/main/java/org/amc/ataxx/server/Player.java +++ b/src/main/java/org/amc/ataxx/server/Player.java @@ -262,7 +262,7 @@ private void handleDisconnection() { game.handleResignation(this.key); } game.sendToAll(this.username + " has disconnected"); - updateClients(game.getBoard(), "resn", game); + updateClients(game.getBoard(), "none", game); } } } @@ -353,17 +353,18 @@ private void updateClients(String oldBoard, String move, Game game) { Player opponent = game.getPlayer(this.key == '1' ? 1 : 0); char winner = game.getWinner(); boolean active = game.getActive(); + boolean finished = game.getFinished(); // guard against null opponent (during Game setup) if (null != opponent) { - opponent.sendGameState(oldBoard, move, newBoard, activePlayer, winner, active); + opponent.sendGameState(oldBoard, move, newBoard, activePlayer, winner, active, finished); } - this.sendGameState(oldBoard, move, newBoard, activePlayer, winner, active); + this.sendGameState(oldBoard, move, newBoard, activePlayer, winner, active, finished); ArrayList spectators = game.getSpectators(); for (Player spectator : spectators) { - spectator.sendGameState(oldBoard, move, newBoard, activePlayer, winner, active); + spectator.sendGameState(oldBoard, move, newBoard, activePlayer, winner, active, finished); } } } @@ -377,7 +378,8 @@ private void updateClients(String oldBoard, String move, Game game) { * @param activePlayer the key of the player to make the next move * @param winner the Username of the player who won, or '-' if the game is ongoing */ - private void sendGameState(String oldBoard, String move, String newBoard, char activePlayer, char winner, boolean active) { + private void sendGameState(String oldBoard, String move, String newBoard, char activePlayer, + char winner, boolean active, boolean finished) { StringBuilder response = new StringBuilder(); response.append("GAME\\") .append(oldBoard).append("\\") @@ -386,7 +388,8 @@ private void sendGameState(String oldBoard, String move, String newBoard, char a .append(activePlayer).append("\\") .append(this.key).append("\\") .append(winner).append("\\") - .append(active); + .append(active).append("\\") + .append(finished); log("Sending response '" + response.toString() + " to " + this.username + this.clientIP); responseOutput.println(response.toString()); diff --git a/src/main/resources/org/amc/ataxx/client/game.fxml b/src/main/resources/org/amc/ataxx/client/game.fxml index 9bd772b..a12de1d 100644 --- a/src/main/resources/org/amc/ataxx/client/game.fxml +++ b/src/main/resources/org/amc/ataxx/client/game.fxml @@ -8,7 +8,7 @@ + minHeight="700.0" maxHeight="700" minWidth="1100.0" maxWidth="1100" hgap="10" vgap="10"> @@ -22,7 +22,7 @@ - +