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;
|
package src;
|
||||||
|
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
public class Application extends Base {
|
public class Application extends Base {
|
||||||
public Application() {
|
private String piBuffer = "";
|
||||||
super("Hello FXML", 675, 600);
|
|
||||||
}
|
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private void sayHello() {
|
private Label label;
|
||||||
System.out.println("Hello from FXML!");
|
private String formula = "";
|
||||||
}
|
private boolean lastInputWasError = false;
|
||||||
|
|
||||||
protected void on_button_click(String label) {
|
public Application() {
|
||||||
System.out.println("Button clicked: " + label);
|
super("Calci UwU", 675, 600);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initUi() {
|
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 int height;
|
||||||
private final String title;
|
private final String title;
|
||||||
|
|
||||||
|
// 🔹 keep a reference so getById works
|
||||||
|
protected Scene scene;
|
||||||
|
|
||||||
public Base(String title, int width, int height) {
|
public Base(String title, int width, int height) {
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.width = width;
|
this.width = width;
|
||||||
@ -39,8 +42,8 @@ public abstract class Base {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public abstract void initUi();
|
public abstract void initUi();
|
||||||
|
|
||||||
protected abstract void on_button_click(String label);
|
protected abstract void on_button_click(String label);
|
||||||
|
protected abstract void on_key_press(javafx.scene.input.KeyEvent event);
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
protected void button_click(MouseEvent e) {
|
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(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
try {
|
try {
|
||||||
String resolvedPath;
|
String resolvedPath;
|
||||||
@ -66,17 +77,37 @@ public abstract class Base {
|
|||||||
throw new IllegalStateException("FXML not found: " + resolvedPath);
|
throw new IllegalStateException("FXML not found: " + resolvedPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
FXMLLoader loader = new FXMLLoader(url);
|
|
||||||
loader.setController(this);
|
|
||||||
Parent root = loader.load();
|
|
||||||
|
|
||||||
javafx.application.Platform.runLater(() -> {
|
javafx.application.Platform.runLater(() -> {
|
||||||
Scene scene = new Scene(root, width, height);
|
try {
|
||||||
fxPanel.setScene(scene);
|
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();
|
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