Skip to content

Instantly share code, notes, and snippets.

@jewelsea
Last active September 9, 2024 02:36
Show Gist options
  • Select an option

  • Save jewelsea/4344564 to your computer and use it in GitHub Desktop.

Select an option

Save jewelsea/4344564 to your computer and use it in GitHub Desktop.
A simple JavaFX calculator
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.*;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.*;
import javafx.stage.*;
import java.util.*;
// a simple JavaFX calculator.
public class Calc extends Application {
private static final String[][] template = {
{"7", "8", "9", "/"},
{"4", "5", "6", "*"},
{"1", "2", "3", "-"},
{"0", "c", "=", "+"}
};
private final Map<String, Button> accelerators = new HashMap<>();
private final DoubleProperty stackValue = new SimpleDoubleProperty();
private final DoubleProperty value = new SimpleDoubleProperty();
private enum Op {NOOP, ADD, SUBTRACT, MULTIPLY, DIVIDE}
private Op curOp = Op.NOOP;
private Op stackOp = Op.NOOP;
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) {
final TextField screen = createScreen();
final TilePane buttons = createButtons();
stage.setTitle("Calc");
stage.initStyle(StageStyle.UTILITY);
stage.setResizable(false);
stage.setScene(new Scene(createLayout(screen, buttons)));
stage.show();
}
private VBox createLayout(TextField screen, TilePane buttons) {
final VBox layout = new VBox(20);
layout.setAlignment(Pos.CENTER);
layout.setStyle("-fx-background-color: chocolate; -fx-padding: 20; -fx-font-size: 20;");
layout.getChildren().setAll(screen, buttons);
handleAccelerators(layout);
screen.prefWidthProperty().bind(buttons.widthProperty());
return layout;
}
private void handleAccelerators(VBox layout) {
layout.addEventFilter(KeyEvent.KEY_PRESSED, keyEvent -> {
Button activated = accelerators.get(keyEvent.getText());
if (activated != null) {
activated.fire();
}
});
}
private TextField createScreen() {
final TextField screen = new TextField();
screen.setStyle("-fx-background-color: aquamarine;");
screen.setAlignment(Pos.CENTER_RIGHT);
screen.setEditable(false);
screen.textProperty().bind(Bindings.format("%.0f", value));
return screen;
}
private TilePane createButtons() {
TilePane buttons = new TilePane();
buttons.setVgap(7);
buttons.setHgap(7);
buttons.setPrefColumns(template[0].length);
for (String[] r : template) {
for (String s : r) {
buttons.getChildren().add(createButton(s));
}
}
return buttons;
}
private Button createButton(final String s) {
Button button = makeStandardButton(s);
if (s.matches("[0-9]")) {
makeNumericButton(s, button);
} else {
final ObjectProperty<Op> triggerOp = determineOperand(s);
if (triggerOp.get() != Op.NOOP) {
makeOperandButton(button, triggerOp);
} else if ("c".equals(s)) {
makeClearButton(button);
} else if ("=".equals(s)) {
makeEqualsButton(button);
}
}
return button;
}
private ObjectProperty<Op> determineOperand(String s) {
final ObjectProperty<Op> triggerOp = new SimpleObjectProperty<>(Op.NOOP);
switch (s) {
case "+" -> triggerOp.set(Op.ADD);
case "-" -> triggerOp.set(Op.SUBTRACT);
case "*" -> triggerOp.set(Op.MULTIPLY);
case "/" -> triggerOp.set(Op.DIVIDE);
}
return triggerOp;
}
private void makeOperandButton(Button button, final ObjectProperty<Op> triggerOp) {
button.setStyle("-fx-base: lightgray;");
button.setOnAction(actionEvent -> curOp = triggerOp.get());
}
private Button makeStandardButton(String s) {
Button button = new Button(s);
button.setStyle("-fx-base: beige;");
accelerators.put(s, button);
button.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
return button;
}
private void makeNumericButton(final String s, Button button) {
button.setOnAction(actionEvent -> {
if (curOp == Op.NOOP) {
value.set(value.get() * 10 + Integer.parseInt(s));
} else {
stackValue.set(value.get());
value.set(Integer.parseInt(s));
stackOp = curOp;
curOp = Op.NOOP;
}
});
}
private void makeClearButton(Button button) {
button.setStyle("-fx-base: mistyrose;");
button.setOnAction(actionEvent -> value.set(0));
}
private void makeEqualsButton(Button button) {
button.setStyle("-fx-base: ghostwhite;");
button.setOnAction(actionEvent -> {
switch (stackOp) {
case ADD -> value.set(stackValue.get() + value.get());
case SUBTRACT -> value.set(stackValue.get() - value.get());
case MULTIPLY -> value.set(stackValue.get() * value.get());
case DIVIDE -> value.set(stackValue.get() / value.get());
}
});
}
}
@jewelsea
Copy link
Copy Markdown
Author

Oracle JavaFX forum thread: Simple calculator control. Anyone ?

@jewelsea
Copy link
Copy Markdown
Author

jewelsea commented Oct 1, 2014

Note, as the title says, this is a very simple calculator. It does not handle things like non-integral values and operator precedence, memory recall, bracketed expressions, etc. It's just basic add, subtract, multiply divide immediately applied on the latest displayed value.

@jewelsea
Copy link
Copy Markdown
Author

jewelsea commented Oct 1, 2014

This is coded as a single Java file - a more flexible design would be to have the layout in FXML, the styling in an external CSS stylesheet and the calculator logic less intertwined with the UI display.

@XiyunLiu
Copy link
Copy Markdown

XiyunLiu commented Mar 8, 2016

It doesn't work for sth like "1+2*3"

@jewelsea
Copy link
Copy Markdown
Author

jewelsea commented Apr 1, 2016

Yes xiyun, I already noted that "It does not handle things like non-integral values and operator precedence". Feel free to fork the code and add such functionality if you wish.

@azzamsa
Copy link
Copy Markdown

azzamsa commented Apr 24, 2017

thanks lot for this javafx introduction, Jewelsea

@hamilbrian
Copy link
Copy Markdown

Good stuff!

@jewelsea
Copy link
Copy Markdown
Author

For anybody who would like to improve the operation of this calculator, you could look at combining its operation with an implementation of the shunting-yard algorithm as a basis for providing the capability to handle operator precedence by converting the input to reverse polish notation and then evaluating it.

@Felixnkumbira
Copy link
Copy Markdown

it is giving me 9 errors

@Nicleonic
Copy link
Copy Markdown

thx bro

@Kapil-Jobanputra
Copy link
Copy Markdown

Can anybody tell us how can we make the sum of more than two numbers in above mentioned code?

@sedj601
Copy link
Copy Markdown

sedj601 commented Sep 13, 2023

Can anybody tell us how can we make the sum of more than two numbers in above mentioned code?

@Kapil-Joban did you read @jewelsea's comment dated Mar 9, 2018? The shunting-yard algorithm is proposed in that comment.

@sedj601
Copy link
Copy Markdown

sedj601 commented Sep 13, 2023

Can anybody tell us how can we make the sum of more than two numbers in above mentioned code?

@Kapil-Joban , You can also have a look at javax ScriptEngineManager. I guess it still works. I am not sure. https://github.com/sedj601/CalculatorFX2/blob/master/CalculatorController.java

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment