refactor: enhance Application and Base classes for improved UI interaction and key handling
This commit is contained in:
parent
04034d1d3a
commit
2f80195a7d
@ -1,25 +1,249 @@
|
||||
package src;
|
||||
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Label;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Application extends Base {
|
||||
public Application() {
|
||||
super("Hello FXML", 675, 600);
|
||||
}
|
||||
|
||||
private String piBuffer = "";
|
||||
@FXML
|
||||
private void sayHello() {
|
||||
System.out.println("Hello from FXML!");
|
||||
}
|
||||
private Label label;
|
||||
private String formula = "";
|
||||
private boolean lastInputWasError = false;
|
||||
|
||||
protected void on_button_click(String label) {
|
||||
System.out.println("Button clicked: " + label);
|
||||
public Application() {
|
||||
super("Calci UwU", 675, 600);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void initUi() {
|
||||
loadUi("./hello.fxml");
|
||||
loadUi("./hello.fxml", () -> {
|
||||
label = getById("label");
|
||||
label.setText("");
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void on_key_press(javafx.scene.input.KeyEvent event) {
|
||||
String key = event.getText();
|
||||
javafx.scene.input.KeyCode code = event.getCode();
|
||||
|
||||
if (key.equalsIgnoreCase("p")) {
|
||||
piBuffer = "p";
|
||||
return;
|
||||
} else if (piBuffer.equals("p") && key.equalsIgnoreCase("i")) {
|
||||
on_button_click("PI");
|
||||
piBuffer = "";
|
||||
return;
|
||||
} else {
|
||||
piBuffer = "";
|
||||
}
|
||||
|
||||
if (code == javafx.scene.input.KeyCode.ENTER) key = "=";
|
||||
else if (code == javafx.scene.input.KeyCode.ESCAPE) key = "CLR";
|
||||
else if (code == javafx.scene.input.KeyCode.ADD) key = "+";
|
||||
else if (code == javafx.scene.input.KeyCode.SUBTRACT) key = "-";
|
||||
else if (code == javafx.scene.input.KeyCode.MULTIPLY) key = "*";
|
||||
else if (code == javafx.scene.input.KeyCode.DIVIDE) key = "/";
|
||||
else if (code == javafx.scene.input.KeyCode.DECIMAL) key = ".";
|
||||
else if (code.isKeypadKey()) {
|
||||
switch (code) {
|
||||
case NUMPAD0: key = "0"; break;
|
||||
case NUMPAD1: key = "1"; break;
|
||||
case NUMPAD2: key = "2"; break;
|
||||
case NUMPAD3: key = "3"; break;
|
||||
case NUMPAD4: key = "4"; break;
|
||||
case NUMPAD5: key = "5"; break;
|
||||
case NUMPAD6: key = "6"; break;
|
||||
case NUMPAD7: key = "7"; break;
|
||||
case NUMPAD8: key = "8"; break;
|
||||
case NUMPAD9: key = "9"; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
if (code == javafx.scene.input.KeyCode.BACK_SPACE && event.isControlDown()) {
|
||||
this.formula = "";
|
||||
this.label.setText(formula);
|
||||
return;
|
||||
}
|
||||
|
||||
if (code == javafx.scene.input.KeyCode.BACK_SPACE || code == javafx.scene.input.KeyCode.DELETE) {
|
||||
if (code == javafx.scene.input.KeyCode.DELETE) {
|
||||
this.formula = "";
|
||||
this.label.setText(formula);
|
||||
return;
|
||||
}
|
||||
String trimmed = this.formula.trim();
|
||||
if (trimmed.isEmpty()) {
|
||||
this.label.setText(formula);
|
||||
return;
|
||||
}
|
||||
|
||||
String[] tokens = trimmed.split(" ");
|
||||
if (tokens.length > 0) {
|
||||
// remove last token
|
||||
tokens = Arrays.copyOf(tokens, tokens.length - 1);
|
||||
this.formula = String.join(" ", tokens);
|
||||
} else {
|
||||
this.formula = "";
|
||||
}
|
||||
|
||||
this.label.setText(formula);
|
||||
return;
|
||||
}
|
||||
|
||||
if (key == null || key.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!key.matches("[0-9\\+\\-\\*/=\\.]") && !"CLR".equals(key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
on_button_click(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void on_button_click(String label) {
|
||||
if ("PI".equals(label)) {
|
||||
this.formula += " PI ";
|
||||
this.label.setText(formula);
|
||||
return;
|
||||
}
|
||||
|
||||
if (lastInputWasError) {
|
||||
this.formula = "";
|
||||
lastInputWasError = false;
|
||||
}
|
||||
|
||||
String trimmed = this.formula.trim();
|
||||
|
||||
if ("+/-".equals(label)) {
|
||||
String[] tokens = trimmed.split(" ");
|
||||
if (tokens.length == 0) return;
|
||||
String last = tokens[tokens.length - 1];
|
||||
if (last.matches("-?\\d+(\\.\\d+)?")) {
|
||||
if (last.startsWith("-")) {
|
||||
tokens[tokens.length - 1] = last.substring(1);
|
||||
} else {
|
||||
tokens[tokens.length - 1] = "-" + last;
|
||||
}
|
||||
this.formula = String.join(" ", tokens);
|
||||
this.label.setText(formula);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ("+-*/".contains(label)) {
|
||||
if (label.equals("-") && (trimmed.isEmpty() || trimmed.matches(".*[\\+\\-\\*/]$"))) {
|
||||
this.formula += "-";
|
||||
this.label.setText(formula);
|
||||
return;
|
||||
}
|
||||
if (trimmed.isEmpty()) return;
|
||||
if (trimmed.matches(".*[\\+\\-\\*/]$")) return;
|
||||
this.formula += " " + label + " ";
|
||||
} else if ("=".equals(label)) {
|
||||
if (trimmed.isEmpty() || trimmed.matches(".*[\\+\\-\\*/]$")) return;
|
||||
solve();
|
||||
} else if ("CLR".equals(label)) {
|
||||
this.formula = "";
|
||||
} else if (".".equals(label)) {
|
||||
String[] tokens = trimmed.split(" ");
|
||||
String last = tokens.length > 0 ? tokens[tokens.length - 1] : "";
|
||||
if (!last.contains(".") && last.matches("\\d+")) {
|
||||
this.formula += ".";
|
||||
}
|
||||
} else {
|
||||
this.formula += label;
|
||||
}
|
||||
|
||||
this.label.setText(formula);
|
||||
}
|
||||
|
||||
private void solve() {
|
||||
String expr = this.formula.trim();
|
||||
if (expr.isEmpty()) {
|
||||
this.formula = "";
|
||||
return;
|
||||
}
|
||||
try {
|
||||
double result = eval(expr);
|
||||
if (result == (long) result) {
|
||||
this.formula = String.valueOf((long) result);
|
||||
} else {
|
||||
this.formula = String.valueOf(result);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
this.formula = "ERR";
|
||||
lastInputWasError = true;
|
||||
}
|
||||
}
|
||||
|
||||
private double eval(String expr) {
|
||||
String[] rawTokens = expr.split("\\s+");
|
||||
List<String> tokens = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < rawTokens.length; i++) {
|
||||
String t = rawTokens[i];
|
||||
if (t.equals("-")) {
|
||||
if (i == 0 || rawTokens[i - 1].matches("[\\+\\-\\*/]")) {
|
||||
if (i + 1 < rawTokens.length && rawTokens[i + 1].matches("[0-9.]+")) {
|
||||
tokens.add("-" + rawTokens[i + 1]);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (t.equals("PI")) {
|
||||
tokens.add(String.valueOf(Math.PI));
|
||||
continue;
|
||||
}
|
||||
tokens.add(t);
|
||||
}
|
||||
|
||||
Queue<String> output = new LinkedList<>();
|
||||
Stack<String> ops = new Stack<>();
|
||||
|
||||
Map<String, Integer> prec = Map.of(
|
||||
"+", 1,
|
||||
"-", 1,
|
||||
"*", 2,
|
||||
"/", 2
|
||||
);
|
||||
|
||||
for (String t : tokens) {
|
||||
if (t.matches("-?[0-9.]+")) {
|
||||
output.add(t);
|
||||
} else if (prec.containsKey(t)) {
|
||||
while (!ops.isEmpty() && prec.containsKey(ops.peek())
|
||||
&& prec.get(ops.peek()) >= prec.get(t)) {
|
||||
output.add(ops.pop());
|
||||
}
|
||||
ops.push(t);
|
||||
}
|
||||
}
|
||||
while (!ops.isEmpty()) {
|
||||
output.add(ops.pop());
|
||||
}
|
||||
|
||||
Stack<Double> stack = new Stack<>();
|
||||
for (String t : output) {
|
||||
if (t.matches("-?[0-9.]+")) {
|
||||
stack.push(Double.parseDouble(t));
|
||||
} else {
|
||||
double b = stack.pop();
|
||||
double a = stack.pop();
|
||||
switch (t) {
|
||||
case "+": stack.push(a + b); break;
|
||||
case "-": stack.push(a - b); break;
|
||||
case "*": stack.push(a * b); break;
|
||||
case "/": stack.push(a / b); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return stack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -20,6 +20,9 @@ public abstract class Base {
|
||||
private final int height;
|
||||
private final String title;
|
||||
|
||||
// 🔹 keep a reference so getById works
|
||||
protected Scene scene;
|
||||
|
||||
public Base(String title, int width, int height) {
|
||||
this.title = title;
|
||||
this.width = width;
|
||||
@ -39,8 +42,8 @@ public abstract class Base {
|
||||
}
|
||||
|
||||
public abstract void initUi();
|
||||
|
||||
protected abstract void on_button_click(String label);
|
||||
protected abstract void on_key_press(javafx.scene.input.KeyEvent event);
|
||||
|
||||
@FXML
|
||||
protected void button_click(MouseEvent e) {
|
||||
@ -49,7 +52,15 @@ public abstract class Base {
|
||||
}
|
||||
}
|
||||
|
||||
protected void loadUi(String fxmlPath) {
|
||||
protected void addKeyListener() {
|
||||
if (scene == null) {
|
||||
throw new IllegalStateException("Scene not loaded yet");
|
||||
}
|
||||
scene.setOnKeyPressed(this::on_key_press);
|
||||
}
|
||||
|
||||
|
||||
protected void loadUi(String fxmlPath, Runnable onLoaded) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
try {
|
||||
String resolvedPath;
|
||||
@ -66,17 +77,37 @@ public abstract class Base {
|
||||
throw new IllegalStateException("FXML not found: " + resolvedPath);
|
||||
}
|
||||
|
||||
FXMLLoader loader = new FXMLLoader(url);
|
||||
loader.setController(this);
|
||||
Parent root = loader.load();
|
||||
|
||||
javafx.application.Platform.runLater(() -> {
|
||||
Scene scene = new Scene(root, width, height);
|
||||
fxPanel.setScene(scene);
|
||||
try {
|
||||
FXMLLoader loader = new FXMLLoader(url);
|
||||
loader.setController(this);
|
||||
Parent root = loader.load();
|
||||
|
||||
this.scene = new Scene(root, width, height);
|
||||
fxPanel.setScene(this.scene);
|
||||
frame.setResizable(false);
|
||||
if (onLoaded != null) onLoaded.run();
|
||||
addKeyListener();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
} catch (IOException e) {
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T extends javafx.scene.Node> T getById(String id) {
|
||||
if (this.scene == null) {
|
||||
throw new IllegalStateException("Scene not loaded yet");
|
||||
}
|
||||
T node = (T) this.scene.lookup("#" + id);
|
||||
if (node == null) {
|
||||
throw new IllegalArgumentException("No node with id '" + id + "' found");
|
||||
}
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user