When I run this, it creates only one button. I'm trying to create a gridPane of buttons, 10x10 and when a button is clicked it would send the row column(x,y) coordinates to another class which would handle its purpose (a battleship game)
Button button[][] = new Button[10][10];
public static int rows, columns, gridSize;
for (rows = 0; rows < 10; rows++) {
for (columns = 0; columns < 10; columns++) {
button[rows][columns] = new Button();
button[rows][columns].setStyle("-fx-background-color: red");
button[rows][columns].setPrefSize(50, 50);
button[rows][columns].setOnMouseClicked(new clickEvents(rows, columns));
//no setters, directly passed rows and cols to clickEvents Class
tileGrid.getChildren().add(button[rows][columns]); //adds buttons to the tile grid
}
}
container.getChildren().addAll(tileGrid);
ScrollPane scrollPane = new ScrollPane(container);
primaryStage.setScene(new Scene(scrollPane));
primaryStage.show();
You aren't telling the system where to put the button in the GridPane.
Use gridPane.add(child, colIndex, rowIndex):
tileGrid.add(button[row][column], column, row);
I fixed spelling and removed plurality (trailing 's') from the row, column variables.
Alternately, you could set constraints on the node in the GridPane:
tileGrid.getChildren().add(button[row][column]);
GridPane.setConstraints(button[row][column], column, row);
But it is a less verbose to use the add method which specifies the constraints in initial add parameters (as in the prior example).
Without any constraints on the nodes added as children to the grid, all the children will be located at the default 0,0 grid location (all stacked on top of each other).
This line is the one giving you issues
tileGrid.getChildren().add(button[rows][columns]);
This is due to the fact that you are incorrectly adding your buttons. If you try something like
tileGrid.add(
button[rows][columns], // Specific node in the array
columns, // Set the specific column
rows // Set the specific row
); //adds buttons to the tile grid
it should work here is the full example code
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
Button button[][] = new Button[10][10];
int rows, columns, gridSize;
GridPane tileGrid = new GridPane();
for (rows = 0; rows < 10; rows++) {
for (columns = 0; columns < 10; columns++) {
button[rows][columns] = new Button();
button[rows][columns].setStyle("-fx-background-color: red");
button[rows][columns].setPrefSize(50, 50);
//button[rows][columns].setOnMouseClicked(new clickEvents(rows, columns));
//no setters, directly passed rows and cols to clickEvents Class
tileGrid.add(
button[rows][columns], // Specific node in the array
columns, // Set the specific column
rows // Set the specific row
); //adds buttons to the tile grid
}
}
ScrollPane scrollPane = new ScrollPane(new VBox(tileGrid));
primaryStage.setScene(new Scene(scrollPane));
primaryStage.show();
}
}
EDIT: 2 Min too slow but I'm going to leave it here so he can see the example if he needs it
Related
I am making a sodoku solver program in Java with the JavaFX library. The program incorporates an interactive sodoku board consisting of a series of TextFields in a GridPane. The board looks like this:
Right now, the cursor is in the top left most TextField. If the field had text in it, the user would be able to move the cursor through the text by using the arrow keys. However, I want the user to be able to use the arrow keys to navigate to a different TextField. The issue is, the field is in "typing mode" (I don't know the official terminology) so the arrow keys only move the cursor to a different point in the text, but otherwise it stays in the same field.
This is what I mean:
Pretend that line I drew is the cursor. Right now, if I click the left arrow key, the cursor will move to the left of the 1, but I want it to move the the TextField on the left, instead. If I click the down arrow key, nothing happens because there is no text below the 1 for the cursor to navigate to, but I want it to move to the TextField below, instead.
The code for the GridPane is this:
TextField[][] squares = new TextField[9][9];
GridPane grid = new GridPane();
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
squares[i][j] = new TextField();
squares[i][j].setPrefHeight(8);
squares[i][j].setPrefWidth(25);
grid.add(squares[i][j], j, i);
}
}
grid.setAlignment(Pos.CENTER);
The squares array is for me to have access to individual TextFields in the GridPane.
Any suggestions for how I can fix this?
To avoid the focused TextField from handling arrow keys at all you need to intercept the KeyEvent before it reaches said TextField. This can be accomplished by adding an event filter to the GridPane and consuming the event as appropriate. If you're not sure why this works you can check out the JavaFX: Handling Events tutorial.
Then you can use Node#requestFocus() to programmatically change the focused node.
I also recommend setting the prefColumnCount of each TextField rather than trying to set the preferred dimensions manually. That way the preferred dimensions are computed based on the font size.
Here's an example:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.text.Font;
import javafx.stage.Stage;
public class App extends Application {
private TextField[][] fields;
#Override
public void start(Stage primaryStage) {
GridPane grid = new GridPane();
grid.setHgap(3);
grid.setVgap(3);
grid.setPadding(new Insets(5));
grid.setAlignment(Pos.CENTER);
grid.addEventFilter(KeyEvent.KEY_PRESSED, this::handleArrowNavigation);
fields = new TextField[9][9];
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
fields[i][j] = createTextField();
grid.add(fields[i][j], j, i);
}
}
primaryStage.setScene(new Scene(grid));
primaryStage.show();
}
private void handleArrowNavigation(KeyEvent event) {
Node source = (Node) event.getSource(); // the GridPane
Node focused = source.getScene().getFocusOwner();
if (event.getCode().isArrowKey() && focused.getParent() == source) {
int row = GridPane.getRowIndex(focused);
int col = GridPane.getColumnIndex(focused);
// Switch expressions were standardized in Java 14
switch (event.getCode()) {
case LEFT -> fields[row][Math.max(0, col - 1)].requestFocus();
case RIGHT -> fields[row][Math.min(8, col + 1)].requestFocus();
case UP -> fields[Math.max(0, row - 1)][col].requestFocus();
case DOWN -> fields[Math.min(8, row + 1)][col].requestFocus();
}
event.consume();
}
}
private TextField createTextField() {
TextField field = new TextField();
// Rather than setting the pref sizes manually this will
// compute the pref sizes based on the font size.
field.setPrefColumnCount(1);
field.setFont(Font.font(20));
field.setTextFormatter(
new TextFormatter<>(
change -> {
// Only allow the text to be empty or a single digit between 1-9
if (change.getControlNewText().matches("[1-9]?")) {
// Without this the text goes "off screen" to the left. This also
// seems to have the added benefit of selecting the just-entered
// text, which makes replacing it a simple matter of typing another
// digit.
change.setCaretPosition(0);
return change;
}
return null;
}));
return field;
}
}
The above also adds a TextFormatter to each TextField to show a way to limit the text to digits between 1 and 9. Note the arrow navigation does not "wrap around" when it reaches the end of a row or column. You can of course modify the code to implement this, if desired.
You may want to consider creating a model for the game. That way the business logic is not tied directly to JavaFX UI objects. When you update the model it would notify the view (possibly via a "view model", depending on the architecture) and the view will update itself accordingly.
You need to set the event handler to move on arrow key press as you can see below look at the setTextHandler function there is no error handling I just wrote this up to give you an idea of what you should be doing it is called from the loop when you create the TextFields from there it checks for an arrow key press and from there it will .requestFocus() from the next TextField
public class Main extends Application {
private TextField[][] squares;
#Override
public void start(Stage primaryStage) throws Exception {
squares = new TextField[9][9];
GridPane grid = new GridPane();
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
squares[i][j] = new TextField();
squares[i][j].setPrefHeight(8);
squares[i][j].setPrefWidth(25);
setTextHandler(squares[i][j], i, j);
grid.add(squares[i][j], j, i);
}
}
grid.setAlignment(Pos.CENTER);
Scene scene = new Scene(grid);
primaryStage.setScene(scene);
primaryStage.show();
}
private void setTextHandler(TextField textField, int i, int j){
textField.setOnKeyPressed(keyEvent -> {
System.out.println(keyEvent.getCode());
if(keyEvent.getCode().isArrowKey()) {
if (keyEvent.getCode() == KeyCode.UP) {
squares[i-1][j].requestFocus();
} else if (keyEvent.getCode() == KeyCode.DOWN) {
squares[i+1][j].requestFocus();
} else if (keyEvent.getCode() == KeyCode.LEFT) {
squares[i][j-1].requestFocus();
} else if (keyEvent.getCode() == KeyCode.RIGHT) {
squares[i][j+1].requestFocus();
}
}
});
}
}
I'm really new to java and i'm learning javafx while doing this project.The code sends rows and cols to another class when a button is clicked. My logic send the row and col accordingly to another class. But i want to prevent the user from clicking the same button. How would i go about doing that. I know there's a setDisable function but how would go implementing it.
for (row = 0; row < 10; row++) {
for (coloumn = 0; coloumn < 10; coloumn++) {
button[row][coloumn] = new Button();
// button[row][coloumn].setStyle("-fx-background-color: red");
button[row][coloumn].setPrefSize(50, 50);
button[row][coloumn].setOnAction(new clickEvents(row, coloumn));
//no setters, directly passed rows and cols to clickEvents Class
tileGrid.add(button[row][coloumn], row, coloumn); //adds buttons to the tile grid
//node , row position in grid, column position in grid
}
}
Revised
for (row = 0; row < 10; row++) {
for (coloumn = 0; coloumn < 10; coloumn++) {
button[row][coloumn] = new Button();
// button[row][coloumn].setStyle("-fx-background-color: red");
button[row][coloumn].setPrefSize(50, 50);
button[row][coloumn].setOnAction(new clickEvents(row, coloumn));
temprow= row;
tempcol=coloumn;
//if(row==temprow && coloumn == tempcol )
if(button[row][coloumn].isPressed()) {
button[row][coloumn].setDisable(true);
}
//no setters, directly passed rows and cols to clickEvents Class
tileGrid.add(button[row][coloumn], row, coloumn); //adds buttons to the tile grid
//node , row position in grid, column position in grid
}
Read the doc.
button.setDisable(true);
Works on any component that inherits from Node, by the way, so you can disable pretty much anything in your scene.
When you create the button, set a boolean called clicked, initialized to false. When the user clicks the button, the handler fires, and sets the boolean to true. Then you have a bit of logic that branches on clicked. If true, do nothing (since the button was already previously disabled), if false, disable the button.
im trying to add multiply VBox to a gridpane (called refPane in the following codesnippet) which is inside a scrollpane.
int columnIndex = 0;
int rowIndex = 0;
int boxWidth = windowWidth/ITEMS_PER_ROW;
int boxHeight = windowHeight/ITEMS_PER_COLUMN;
for(int i=0; i<items.size(); i++){
VBox vBox = new VBox();
vBox.setPrefWidth(boxWidth);
vBox.setPrefHeight(boxHeight);
Label label1 = new Label();
label1.setText("ImgPlaceholder");
label1.setPrefWidth(boxWidth);
label1.setPrefHeight(boxHeight / 100 * 70);
vBox.getChildren().add(label1);
Label label2 = new Label();
label2.setText("Description");
label2.setPrefWidth(boxWidth);
label2.setPrefHeight(boxHeight / 100 * 30);
label2.setPadding(new Insets(0,0,0, 10));
vBox.getChildren().add(label2);
refPane.add(vBox, columnIndex, rowIndex);
if(columnIndex != 0 && columnIndex % GAMES_PER_ROW == 0){
rowIndex++;
columnIndex = 0;
}else {
columnIndex++;
}
It adds no more then ITEMS_PER_ROW Vboxes in one row and continues in the next row. Also there should be no more rows then ITEMS_PER_COLUM visible.
The problem is, if I add more then ITEMS_PER_ROW * ITEMS_PER_COLUMN to the grid, instead ob beeing scrollable, the vboxes just get smaller in size.
Any Ideas? Thanks in advance.
Chances are javafx is prioritizing shrinking the VBox's over expanding your grid pane. Try setting the minHeight of each VBox, to be equal to its prefHeight to keep them from shrinking vertically.
I am looking for a way to add plus and minus buttons to table cells in LibGDX.
I'm trying to do something like this image:
I noticed that the uiskin files I'm using contain plus and minus buttons (right next to the checkbox) which will do just fine (uiskin.png).
Any tips on how I can add those to my table and make it so they increase/decrease the integer value in that table cell?
I've added a rudimentary sample code with an indication of what I want to do:
#Override
public void create() {
Stage stage = new Stage();
Skin skin = new Skin(Gdx.files.internal("skins/uiskin.json"));
Table table = new Table(skin);
table.setPosition(Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2);
for(int i = 0; i < 10; i++){
table.add(Integer.toString(i)); //Somehow add plus/minus buttons left
//and right that increase/decrease the integer value
table.row();
}
stage.addActor(table);
Gdx.input.setInputProcessor(stage);
}
#Override
public void render() {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.act(Gdx.graphics.getDeltaTime());
stage.draw();
}
This is just some example code, but if it works here I will certainly be able to figure it out.
Thanks
First, you need an array to store your number values. It's not good practice to use your View (table cells) to store your Model (number values), and besides, Label cells store a String, not an integer, so it would be inconvenient.
private int[] tableData = new int[10];
Now you need a button that can be associated with a row in your data. Here's one way Create a subclass of ImageButton that can take a row number and mode (subtract or add).
public class IncrementButton extends ImageButton {
public final int rowNumber;
public final boolean decrement;
public IncrementButton (Skin skin, String styleName, int rowNumber, boolean decrement){
super(skin, styleName);
this.rowNumber = rowNumber;
this.decrement = decrement;
}
}
The plus and minus images in the uiskin atlas you're using are called "tree-minus" and "tree-plus". You need an ImageButton style for each of these that uses one of these as the imageUp property. (imageUp is used as a default if you don't define images for other states such as imageDown.) You can add these to uiskin.json:
com.badlogic.gdx.scenes.scene2d.ui.ImageButton$ImageButtonStyle: {
plus: { down: default-round-down, up: default-round, imageUp: tree-plus },
minus: { down: default-round-down, up: default-round, imageUp: tree-minus }
},
Now you can create a ChangeListener for these buttons that will modify the numbers in the data and update the table accordingly. And then set it all up:
Stage stage = new Stage();
Skin skin = new Skin(Gdx.files.internal("skins/uiskin.json"));
final Table table = new Table(skin);
final Label[] labels = new Label[tableData.length]; //keep references to the labels for updating them.
final ChangeListener incrementListner = new ChangeListener() {
#Override
public void changed(ChangeEvent event, Actor actor) {
IncrementButton incrementButton = (IncrementButton)actor;
int row = incrementButton.rowNumber;
tableData[row] += incrementButton.decrement ? -1 : 1;
labels[row].setText(Integer.toString(tableData[row]));
}
};
table.setPosition(Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2);
for(int i = 0; i < tableData.length; i++){
IncrementButton decrementButton = new IncrementButton(skin, "minus", i, true);
decrementButton.addListener(incrementListner);
IncrementButton incrementButton = new IncrementButton(skin, "plus", i, false);
incrementButton.addListener(incrementListner);
table.add(decrementButton);
labels[i] = table.add(Integer.toString(i)).getActor();//Add number label and keep reference to it in labels array for the change listener to look up
table.add(incrementButton);
table.row();
}
stage.addActor(table);
Gdx.input.setInputProcessor(stage);
If you want to set min and max values for the data, then you need some arrays for the plus buttons and minus buttons (similar to the labels array) so the change listener can look up and disable/enable the buttons accordingly when a limit is hit.
I am making a program for school.
My program has two JFrame's
The first Jframe = Basisscherm
The second Jframe = Toetsenbord
On the Jframe basisscherm i've got a Jtable filled with data from MYSQL Database. This Table showing labels and with this labels are specific text so each label has his own text this is in the same data base
Now on the Jframe toetsenbord i've got a Jtextfield with the name: Tekst
Now my problem is i want to show the text in the jtextfield by selecting the label from the jtable and clicking on a ok button but i don't now where to start
Have a look at this. using which you can get the selected text in JTable.
JTable table = new JTable();
if (table.getColumnSelectionAllowed()
&& !table.getRowSelectionAllowed()) {
// Column selection is enabled
// Get the indices of the selected columns
int[] vColIndices = table.getSelectedColumns();
} else if (!table.getColumnSelectionAllowed()
&& table.getRowSelectionAllowed()) {
// Row selection is enabled
// Get the indices of the selected rows
int[] rowIndices = table.getSelectedRows();
} else if (table.getCellSelectionEnabled()) {
// Individual cell selection is enabled
// In SINGLE_SELECTION mode, the selected cell can be retrieved using
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
int rowIndex = table.getSelectedRow();
int colIndex = table.getSelectedColumn();
// In the other modes, the set of selected cells can be retrieved using
table.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
// Get the min and max ranges of selected cells
int rowIndexStart = table.getSelectedRow();
int rowIndexEnd = table.getSelectionModel().getMaxSelectionIndex();
int colIndexStart = table.getSelectedColumn();
int colIndexEnd = table.getColumnModel().getSelectionModel()
.getMaxSelectionIndex();
// Check each cell in the range
for (int r=rowIndexStart; r<=rowIndexEnd; r++) {
for (int c=colIndexStart; c<=colIndexEnd; c++) {
if (table.isCellSelected(r, c)) {
// cell is selected
}
}
}
}