Issue
I am trying to build a nonogram that looks something like this
src="https://i.stack.imgur.com/pZ1kA.png" alt="enter image description here" />
But currently my code displays the clues (the numbers at the top and left side) like this
The clues are stacked on top of each other, when I want them spaced out into rows.
This is my code so far using JavaFX:
public class PuzzleView implements FXComponent {
private Controller controller;
private Pane board;
private Pane entirePuzzle;
private VBox[] vertClues;
private HBox[] horzClues;
private Pane[][] tiles;
public PuzzleView(Controller controller) {
this.controller = controller;
}
@Override
public Parent render() {
entirePuzzle = new Pane(); // grid pane
initBoard();
initLabels();
entirePuzzle.getChildren().add(board);
return entirePuzzle;
}
private void initBoard() {
board = new Pane();
int width = controller.getClues().getWidth();
int height = controller.getClues().getHeight();
board.setLayoutX(100);
board.setLayoutY(100);
for (int i=0; i<2; i++) {
for (int j=0; j<2; j++) {
Rectangle clues = new Rectangle();
clues.setWidth(width);
clues.setHeight(height);
clues.setLayoutX(i*(width*50));
clues.setLayoutY(j*height*50);
clues.setStroke(Color.LIGHTBLUE);
clues.setStrokeWidth(2);
clues.setFill(Color.WHITE);
board.getChildren().add(clues);
}
}
tiles = new Pane[width][height];
for (int i=0; i<width; i++) {
for (int j=0; j<height; j++) {
tiles[i][j] = new Pane();
Rectangle rect = new Rectangle(50, 50);
rect.setStroke(Color.LIGHTGRAY);
rect.setStrokeWidth(1);
rect.setFill(Color.TRANSPARENT);
tiles[i][j].getChildren().add(rect);
tiles[i][j].setLayoutX(i*50);
tiles[i][j].setLayoutY(j*50);
board.getChildren().add(tiles[i][j]);
}
}
}
private void initLabels() {
int width = controller.getClues().getWidth();
int height = controller.getClues().getHeight();
vertClues = new VBox[height];
horzClues = new HBox[width];
Clues clue = controller.getClues();
for (int i = 0; i < width; i++) {
horzClues[i] = new HBox();
horzClues[i].setLayoutX(100 + i * width);
for (int j=0; j<controller.getClues().getColCluesLength(); j++) {
horzClues[i].setSpacing(width);
Label label = new Label(String.valueOf(clue.getColClues(i)[j]));
label.minHeight(25);
label.minWidth(25);
horzClues[i].getChildren().add(label);
}
entirePuzzle.getChildren().add(horzClues[i]);
}
for (int i = 0; i < height; i++) {
vertClues[i] = new VBox();
vertClues[i].setLayoutY(100 + i * height);
for (int j=0; j<controller.getClues().getRowCluesLength(); j++) {
Label label = new Label(String.valueOf(clue.getRowClues(i)[j]));
label.minHeight(25);
label.minWidth(25);
vertClues[i].getChildren().add(label);
}
entirePuzzle.getChildren().add(vertClues[i]);
}
}
}
FXComponent
is an interface I made that just has a render()
method that returns a component object that I can put together in a different class. controller
is another class I made that allows me to access the properties and other functions of the puzzle itself.
Solution
Not a complete answer, just a suggestion but hopefully it will help you progress.
I suggest using a TilePane for the board where each "tile" is a ToggleButton. Refer to Button color change in javafx for a way to change the colors of the ToggleButton
.
Set the "board" as the center Node
of a BorderPane and place the row hints as the left Node
and the column hints as the top Node
.
Note that the below code is based on your other question (that you deleted).
import javafx.application.Application;
import javafx.collections.ObservableList;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ToggleButton;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.TilePane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class Nonogram extends Application {
private static final int COLS = 5;
@Override
public void start(Stage primaryStage) throws Exception {
BorderPane root = new BorderPane();
root.setCenter(createBoard());
root.setLeft(createLeftPane());
root.setTop(createTopPane());
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
private TilePane createBoard() {
TilePane board = new TilePane(Orientation.VERTICAL);
board.setPrefRows(COLS);
ObservableList<Node> children = board.getChildren();
for (int row = 0; row < COLS; row++) {
for (int col = 0; col < COLS; col++) {
ToggleButton b = new ToggleButton();
children.add(b);
}
}
return board;
}
private Label createColumnLabel(String text) {
Label label = new Label(text);
label.setFont(Font.font("Monospaced"));
label.setWrapText(true);
label.setMinWidth(17);
label.setPrefWidth(17);
label.setMaxWidth(17);
return label;
}
private VBox createLeftPane() {
VBox leftPane = new VBox(createRowLabel(" 3"),
createRowLabel(" 2"),
createRowLabel(" 2"),
createRowLabel(" 1"),
createRowLabel("1 1"));
return leftPane;
}
private Label createRowLabel(String text) {
Label label = new Label(text);
label.setFont(Font.font("Monospaced"));
label.setMinHeight(25);
label.setPrefHeight(25);
label.setMaxHeight(25);
return label;
}
private HBox createTopPane() {
Rectangle rect = new Rectangle(22, 27);
rect.setFill(Color.AZURE);
HBox topPane = new HBox(rect,
createColumnLabel("1 1"),
createColumnLabel(" 2"),
createColumnLabel(" 4"),
createColumnLabel(" 2"),
createColumnLabel(" 3"));
return topPane;
}
public static void main(String[] args) {
launch(args);
}
}
There is probably a better way to determine the sizes and alignment of the "hints", but I was too lazy to look for it.
Here is a screen capture of the running app.
Answered By - Abra
Answer Checked By - Willingham (JavaFixing Volunteer)