Multiplication Table for JavaFX

The following source code creates a multiplication table for teaching purposes. It should run in the most recent Processing editor (v. 4.3.2) since all the JavaFX modules have now been added:

import javafx.scene.Scene;
import javafx.application.Platform;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.control.Label;
import javafx.scene.paint.Color;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.event.EventHandler;
import javafx.scene.input.MouseEvent;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javafx.scene.text.Font;
import javafx.scene.text.FontPosture;
import javafx.scene.text.FontWeight;

final int _wndW = 700;
final int _wndH = 480;

final int _numRows = 12;
final int _numCols = 12;

int colNum = 1;
int rowNum = 1;
int id = 0;
float correct = 0.0;
float incorrect = 0.0;
float score = 0.00;
int[] answer;

String strID = str(0);
String inputStr = "";
String correctStr = "";
String incorrectStr = "";
String scoreStr = "";

TextField[] txtFld;
Label instructionLabel;
Label correctLabel;
Label incorrectLabel;
Label scoreLabel;
Button resetBtn;
Button quitBtn;
Pane pane;

void resetAction() {
  for (int i = 0; i < txtFld.length-1; i++) {
    txtFld[i].setEditable(true);
    txtFld[i].setText("");
  }
  correct = 0.0;
  incorrect = 0.0;
  score = 0.00;

  correctStr = "Correct: " + int(correct);
  correctLabel.setText(correctStr);
  incorrectStr = "Incorrect: " + int(incorrect);
  incorrectLabel.setText(incorrectStr);
  scoreStr = "Score: " + nf(score, 2, 0);
  scoreLabel.setText(scoreStr);
}

void resetButton(int x, int y, int w, int h) {
  resetBtn = new Button("Reset");
  resetBtn.setLayoutX(x);
  resetBtn.setLayoutY(y);
  resetBtn.setMaxSize(w, h);
  resetBtn.setOnAction(event -> {
    resetAction();
  }
  );
  pane.getChildren().add(resetBtn);
}

void quitButton(int x, int y, int w, int h) {
  quitBtn = new Button("Quit");
  quitBtn.setLayoutX(x);
  quitBtn.setLayoutY(y);
  quitBtn.setMaxSize(w, h);
  quitBtn.setOnAction(event -> {
    System.exit(0);
  }
  );
  pane.getChildren().add(quitBtn);
}

void instructionLabel(int x, int y, int w, int h) {
  String instructionStr = "Hit return(enter) to register answer.";
  instructionLabel = new Label();
  Font font = Font.font("Menlo", FontWeight.BOLD, FontPosture.REGULAR, 15);
  instructionLabel.setFont(font);
  instructionLabel.setTextFill(Color.BLUE);
  instructionLabel.setLayoutX(x);
  instructionLabel.setLayoutY(y);
  instructionLabel.setPrefSize(w, h);
  instructionLabel.setText(instructionStr);
  pane.getChildren().add(instructionLabel);
}

void correctLabel(int x, int y, int w, int h) {
  correctLabel = new Label();
  Font font = Font.font("Menlo", FontWeight.BOLD, FontPosture.REGULAR, 15);
  correctLabel.setFont(font);
  correctLabel.setTextFill(Color.GREEN);
  correctLabel.setLayoutX(x);
  correctLabel.setLayoutY(y);
  correctLabel.setPrefSize(w, h);
  correctLabel.setText("Correct: " + int(correct));
  pane.getChildren().add(correctLabel);
}

void incorrectLabel(int x, int y, int w, int h) {
  incorrectLabel = new Label();
  Font font = Font.font("Menlo", FontWeight.BOLD, FontPosture.REGULAR, 15);
  incorrectLabel.setFont(font);
  incorrectLabel.setTextFill(Color.RED);
  incorrectLabel.setLayoutX(x);
  incorrectLabel.setLayoutY(y);
  incorrectLabel.setPrefSize(w, h);
  incorrectLabel.setText("Incorrect: " + int(incorrect));
  pane.getChildren().add(incorrectLabel);
}

void scoreLabel(int x, int y, int w, int h) {
  scoreLabel = new Label();
  Font font = Font.font("Menlo", FontWeight.BOLD, FontPosture.REGULAR, 15);
  scoreLabel.setFont(font);
  scoreLabel.setTextFill(Color.BLUE);
  scoreLabel.setLayoutX(x);
  scoreLabel.setLayoutY(y);
  scoreLabel.setPrefSize(w, h);
  scoreLabel.setText("Score: ");
  pane.getChildren().add(scoreLabel);
}

void horzLabelGrid(int left, int top, int w, int h, int vg) {
  for (int j = 0; j < _numCols; j++) {
    int x = left + j*(w+vg);
    int y = top;
    Label label = new Label(str(colNum));
    Font font = Font.font("Menlo", FontWeight.BOLD, FontPosture.REGULAR, 15);
    label.setFont(font);
    label.setTextFill(Color.BLUE);
    label.setLayoutX(x);
    label.setLayoutY(y);
    label.setPrefSize(w, h);
    pane.getChildren().add(label);
    colNum++;
  }
}

void vertLabelGrid(int left, int top, int w, int h, int gap) {
  for (int k = 0; k < _numRows; k++) {
    int x = left;
    int y = top + k*(h+gap);
    Label label = new Label(str(rowNum));
    Font font = Font.font("Menlo", FontWeight.BOLD, FontPosture.REGULAR, 15);
    label.setFont(font);
    label.setTextFill(Color.BLUE);
    label.setLayoutX(x);
    label.setLayoutY(y);
    label.setPrefSize(w, h);
    pane.getChildren().add(label);
    rowNum++;
  }
}

void txtFldGrid(int left, int top, int w, int h, int vg, int hg) {
  for (int k = 0; k < _numRows; k++) {
    for (int j = 0; j < _numCols; j++) {
      int product = (j+1)*(k+1);
      answer[id] = product;
      int x = left + j*(w+vg);
      int y = top + k*(h+hg);
      txtFld[id] = new TextField("");
      txtFld[id].setFont(Font.font("Verdana", FontWeight.BOLD, FontPosture.REGULAR, 13));
      txtFld[id].setLayoutX(x);
      txtFld[id].setLayoutY(y);
      txtFld[id].setMaxSize((double) w, (double) h);
      txtFld[id].setId(str(id));
      txtFld[id].addEventFilter(MouseEvent.MOUSE_CLICKED, eventHandler);
      txtFld[id].setOnAction(event -> {
        getInput();
      }
      );
      pane.getChildren().add(txtFld[id]);
      id++;
    }
  }
}

EventHandler<MouseEvent> eventHandler = new EventHandler<MouseEvent>() {
  @Override
    public void handle(MouseEvent e) {
    strID = ((TextField)e.getSource()).getId();
  }
};

void getInput() {
  int i = Integer.parseInt(strID);
  inputStr = txtFld[i].getText();
  if (inputStr.equals(str(answer[i]))) {
    correct++;
    txtFld[i].setStyle("-fx-text-fill: green;");
    txtFld[i].setEditable(false);
    correctStr = "Correct: " + int(correct);
    correctLabel.setText(correctStr);
  } else {
    txtFld[i].setStyle("-fx-text-fill: red;");
    txtFld[i].setEditable(false);
    incorrect++;
    incorrectStr = "Incorrect: " + int(incorrect);
    incorrectLabel.setText(incorrectStr);
  }
  if (correct+incorrect > 0) {
    float total = correct + incorrect;
    score = correct/total*100;
    scoreStr = "Score: " + nf(score, 2, 0);
    scoreLabel.setText(scoreStr);
  }
}

void setup() {
  size(1, 1, FX2D);
  Stage stage = new Stage();
  stage.setTitle("Multiply rows x cols");
  pane = new Pane();
  txtFld = new TextField[_numRows*_numCols+1];
  answer = new int[_numRows*_numCols+1];
  instructionLabel(45, 10, _wndW - 45, 30);
  horzLabelGrid(90, 35, 49, 30, 0);
  vertLabelGrid(45, 58, 40, 30, 0);
  txtFldGrid(70, 60, 45, 30, 5, 0);
  correctLabel(45, _wndH - 50, 120, 30);
  incorrectLabel(170, _wndH - 50, 150, 30);
  scoreLabel(330, _wndH - 50, 140, 30);
  resetButton(500, _wndH - 50, 100, 30);
  quitButton(_wndW - 80, _wndH - 50, 100, 30);
  Scene scene = new Scene(pane, _wndW, _wndH);
  stage.setScene(scene);
  stage.show();
}

void draw() {
  // Does nothing in JavaFX!
}

Output:

1 Like

There is an issue running this demo on Windows and Linux; it will not run without drag 'n dropping all seven JavaFX module jars onto the open sketch window. See this thread: Processing and JavaFX

I have the same problem on Windows 11 and Linux even though I installed the JavaFX library and a javafx folder is in the libraries folder where it’s supposed to be. I drag 'n dropped the seven jar files onto the open sketch window in the editor and that fixed it; copy/paste of the jar files into the sketch folder did not work. Processing adds the modules to a folder named ‘code’ after a drag 'n drop. The demo above runs ok on MacOS so it appears to be a Windows and Linux issue. Before the pull request to add all seven JavaFX modules to the editor source code was approved, one of the reviewers tested it on Windows and reported no problem so I’m not sure where the problem lies. If others have trouble I guess we will need to file a ‘bug’ report; we shouldn’t have to drag 'n drop the jar files every time we want to run a JavaFX demo in Windows or Linux.

Please provide a reference to this.
Without any details this is meaningless.

You already have the reference; read the whole discussion.

Tested on windows, does indeed fix the issue you're describing, so happy to merge after the requested changes.

You did not post the complete context:

If you can find a user friendly way, e.g. not just adding the JavaFX jars to the code folder, please let me know. I would like to make that possible.

My interpretation of this is they are still needed and that seems to be the case with Processing 4.3.2 and Windows 10 for me. It may not be clear in the post but testing clarified this for me.

Absolute clarity and rigorous testing is needed to iterate down to working solutions and reporting issues.

I am going to end my participation in this topic.

Have fun!

:)

In retrospect, the reviewer may have installed the module jars in a ‘code’ folder in the sketch folder to get it to work. How he tested it was not described. At any rate, that procedure is no longer necessary on my Mac, but that does not appear to be the case for either Windows or Linux. The challenge is to figure out why it works on one operating system but not the other two.