Prevent user from clicking the same button? - java

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.

Related

Navigating out of a TextField in a GridPane using the arrow keys in JavaFX

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();
}
}
});
}
}

Why does this produce only one button on the grid?

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

Creating a clickable grid in Processing 3

I'm trying to make a grid of squares that change their fill (from black to white and vice-versa) when clicked. I'm able to turn the entire grid on or off currently, but I'm unable to figure out how to specify which particular square should be toggled when the mouse clicks within its borders. I've created buttons using mouseX and mouseY coordinates before, but they were for specific objects that I could adjust manually. I can't figure out how to do this using for loops and arrays.
I've been told to create a boolean array and pass the value of that array to the grid array, but again, I don't know how to specify which part of the array it needs to go to. For example, how do I change the fill value of square [6][3] upon mousePressed?
Here is my code so far:
int size = 100;
int cols = 8;
int rows = 5;
boolean light = false;
int a;
int b;
void setup() {
size (800, 600);
background (0);
}
void draw() {
}
void mousePressed() {
light = !light;
int[][] box = new int[cols][rows];
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
box[i][j] = i;
int a = i*100;
int b = j*100;
if (light == true) {
fill(255);
} else {
fill(0);
}
rect(a, b, 100, 100);
println(i, j);
}
}
}
First of all, you are currently recreating the entire board whenever the mouse is pressed. You must retain that info between mouse clicks, so make box a global array up there with the others. Further, it's sufficient to make it a boolean array if all you care about is the on/off state of each square:
boolean[][] isSquareLight = new boolean[cols][rows];
Instead of
if (light == true) {
you should then just check
if (isSquareLight[i][j] == true) {
(note that the == true is redundant).
Now, you've already written code that finds the coordinates for each box: You're passing it to rect!
rect(a, b, 100, 100);
All that is left to do is check whether the mouse is inside this rect, i.e. whether mouseX is between a and a+100 (and similar for mouseY) - if that's the case, then the user clicked in the box given by the current (i, j), so you can just negate isSquareLight[i][j] (before checking it like above) and it will work.
There are ways to calculate this without looping through the entire grid every time, but maybe the above helps you find the path yourself instead of just getting the code made for you.
PS: The int a; int b; at the top does nothing and can be removed. You are using the local variables a and b in your function, which is correct.

SWT Button Grid

It's a problem that it's annoying me for 3 days now.
I have to rewrite the UI of a little tictactoe(Gomoku of n x n) game.
the problem is that when i created the swing GUI , i made a new class that inherits JButton properties and added an int for rows and an int for columns. I cannot do that with SWT(no inheritance). is there a way for me to add the values of i and j to the button.
Here is the example in Swing:
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
final MyJButton button = new MyJButton(i, j);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
MoveResult move = game.move(button.getRow(), button.getCol());
switch (move) {
case ValidMove:
button.setBackground(game.getCurrentPlayer().getColor());
game.changePlayer();
break;
}
}
}
}
}
}
I give the i and j for the game class which give it to a table clas to check the move.
if (table.getElement(x, y) != PieceType.NONE) return MoveResult.InvalidMove;
private PieceType[][] table;
is there a way to do the same in SWT, any indication is welcomed .
this is what i made
buttonpanel = new Composite(shell, SWT.NONE);
buttonpanel.setLayout(new org.eclipse.swt.layout.GridLayout(cols, true));
buttonTable = new Button[rows][cols];
for (int i = 0; i < rows; ++i){
for (int j = 0; j < cols; ++j) {
gridData.heightHint = 45;
gridData.widthHint = 45;
Button button = new Button(buttonpanel, SWT.PUSH);
button.setLayoutData(gridData);
buttonTable[i][j] = button;
buttonTable[i][j].addSelectionListener(new buttSelectionListener());
// buttonpanel.pack();
}
}
I see two solutions :
use Button's setData method (defined in the Widget superclass) to associate an object containing your x and y (you'll found those data in the event object provided to your listener)
use different listeners for each button
In your case, the first solution seems the most natural one.
This means creating a class holding x and y (let's call it Cell), and doing
button.setData(new Cell(i, j));
and in you listener using
game.move(e.data.x, e.data.y);
Options include:
Subclass Button, and override its checkSubclass() method to indicate that you're taking responsibility for not subclassing harmfully.
Make each Button a Composite, which allows subclassing, and put a Button in the Composite.
Make a separate listener for each button.
In a single listener, search through the buttonTable for the button that calls the listener.

Show data from JTable in Jtextfield with 2 JFrames

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
}
}
}
}

Categories

Resources