From 65b8da1347592fe187a9a0260a1179d053fd7869 Mon Sep 17 00:00:00 2001 From: Marcin Chwedczuk <0xmarcin+gh@gmail.com> Date: Wed, 20 Nov 2024 11:40:30 +0100 Subject: [PATCH] add connection for digit buttons --- .../main/java/mscalc/engine/CCalcEngine.java | 32 ++- .../main/java/mscalc/engine/DegreeType.java | 5 + .../{NUM_WIDTH.java => NumberWidth.java} | 6 +- .../main/java/mscalc/engine/RadixType.java | 23 ++- .../mscalc/gui/viewmodel/MoreBindings.java | 18 ++ .../ScientificCalculatorViewModel.java | 191 ++++++++++++++---- .../gui/views/scientific/ScientificView.java | 64 +++++- 7 files changed, 270 insertions(+), 69 deletions(-) create mode 100644 engine/src/main/java/mscalc/engine/DegreeType.java rename engine/src/main/java/mscalc/engine/{NUM_WIDTH.java => NumberWidth.java} (86%) create mode 100644 gui/src/main/java/mscalc/gui/viewmodel/MoreBindings.java diff --git a/engine/src/main/java/mscalc/engine/CCalcEngine.java b/engine/src/main/java/mscalc/engine/CCalcEngine.java index 69251ee..bd38c70 100644 --- a/engine/src/main/java/mscalc/engine/CCalcEngine.java +++ b/engine/src/main/java/mscalc/engine/CCalcEngine.java @@ -12,9 +12,7 @@ import java.math.BigDecimal; import java.math.MathContext; -import java.math.RoundingMode; import java.util.*; -import java.util.concurrent.ThreadLocalRandom; import java.util.regex.Pattern; import static java.util.Map.entry; @@ -250,7 +248,7 @@ public static String OpCodeToUnaryString(int nOpCode, boolean fInv, AngleType an int m_precedenceOpCount; /* Current number of precedence ops in holding. */ int m_nLastCom; // Last command entered. AngleType m_angletype; // Current Angle type when in dec mode. one of deg, rad or grad - NUM_WIDTH m_numwidth; // one of qword, dword, word or byte mode. + NumberWidth m_numwidth; // one of qword, dword, word or byte mode. int m_dwWordBitWidth; // # of bits in currently selected word size Random m_randomGeneratorEngine = new Random(); @@ -298,7 +296,7 @@ public CCalcEngine(boolean fPrecedence, m_precedenceOpCount = (0); m_nLastCom = (0); m_angletype = (AngleType.Degrees); - m_numwidth = (NUM_WIDTH.QWORD_WIDTH); + m_numwidth = (NumberWidth.QWORD_WIDTH); m_HistoryCollector = new History(pCalcDisplay, pHistoryDisplay, DEFAULT_DEC_SEPARATOR); m_groupSeparator = (DEFAULT_GRP_SEPARATOR); @@ -954,7 +952,7 @@ void ProcessCommandWorker(int wParam) { case IDM_DEC: case IDM_OCT: case IDM_BIN: { - SetRadixTypeAndNumWidth(RadixType.fromCppValue(wParam - IDM_HEX), NUM_WIDTH.UNDEFINED); // TODO: cast -1 to enum; Add undef value to enum + SetRadixTypeAndNumWidth(RadixType.fromInt(wParam - IDM_HEX), NumberWidth.UNDEFINED); // TODO: cast -1 to enum; Add undef value to enum m_HistoryCollector.updateHistoryExpression(m_radix, m_precision); break; } @@ -969,7 +967,7 @@ void ProcessCommandWorker(int wParam) { } // Compat. mode BaseX: Qword, Dword, Word, Byte - SetRadixTypeAndNumWidth(RadixType.Unknown, NUM_WIDTH.fromInt(wParam - IDM_QWORD)); + SetRadixTypeAndNumWidth(RadixType.Unknown, NumberWidth.fromInt(wParam - IDM_QWORD)); break; case IDM_DEG: @@ -1277,7 +1275,7 @@ static class LASTDISP { int precision; uint radix; int nFE; - NUM_WIDTH numwidth; + NumberWidth numwidth; boolean fIntMath; boolean bRecord; boolean bUseSep; @@ -1287,7 +1285,7 @@ public LASTDISP( int precision, uint radix, int nFE, - NUM_WIDTH numwidth, + NumberWidth numwidth, boolean fIntMath, boolean bRecord, boolean bUseSep @@ -1304,7 +1302,7 @@ public LASTDISP( } } - static LASTDISP gldPrevious = new LASTDISP(Rational.of(0), -1, uint.ZERO, -1, NUM_WIDTH.UNDEFINED, false, false, false); + static LASTDISP gldPrevious = new LASTDISP(Rational.of(0), -1, uint.ZERO, -1, NumberWidth.UNDEFINED, false, false, false); // Truncates if too big, makes it a non negative - the number in rat. Doesn't do anything if not in INT mode Rational TruncateNumForIntMath(Rational rat) { @@ -2005,7 +2003,7 @@ Rational DoOperation(int operation, Rational lhs, Rational rhs) { // To be called when either the radix or num width changes. You can use -1 in either of these values to mean // dont change that. - void SetRadixTypeAndNumWidth(RadixType radixtype, NUM_WIDTH numwidth) { + void SetRadixTypeAndNumWidth(RadixType radixtype, NumberWidth numwidth) { // When in integer mode, the number is represented in 2's complement form. When a bit width is changing, we can // change the number representation back to sign, abs num form in ratpak. Soon when display sees this, it will // convert to 2's complement form, but this time all high bits will be propagated. Eg. -127, in byte mode is @@ -2023,13 +2021,13 @@ void SetRadixTypeAndNumWidth(RadixType radixtype, NUM_WIDTH numwidth) { } } - if (radixtype.cppValue() >= RadixType.Hex.cppValue() && radixtype.cppValue() <= RadixType.Binary.cppValue()) { + if (radixtype.toInt() >= RadixType.Hex.toInt() && radixtype.toInt() <= RadixType.Binary.toInt()) { m_radix = uint.of(NRadixFromRadixType(radixtype)); // radixtype is not even saved } // TODO: Better validation - if (numwidth.toInt() >= NUM_WIDTH.QWORD_WIDTH.toInt() && numwidth.toInt() <= NUM_WIDTH.BYTE_WIDTH.toInt()) { + if (numwidth.toInt() >= NumberWidth.QWORD_WIDTH.toInt() && numwidth.toInt() <= NumberWidth.BYTE_WIDTH.toInt()) { m_numwidth = numwidth; m_dwWordBitWidth = DwWordBitWidthFromNumWidth(numwidth); } @@ -2042,15 +2040,15 @@ void SetRadixTypeAndNumWidth(RadixType radixtype, NUM_WIDTH numwidth) { DisplayNum(); } - int DwWordBitWidthFromNumWidth(NUM_WIDTH numwidth) { + int DwWordBitWidthFromNumWidth(NumberWidth numwidth) { switch (numwidth) { - case NUM_WIDTH.DWORD_WIDTH: + case NumberWidth.DWORD_WIDTH: return 32; - case NUM_WIDTH.WORD_WIDTH: + case NumberWidth.WORD_WIDTH: return 16; - case NUM_WIDTH.BYTE_WIDTH: + case NumberWidth.BYTE_WIDTH: return 8; - case NUM_WIDTH.QWORD_WIDTH: + case NumberWidth.QWORD_WIDTH: default: return 64; } diff --git a/engine/src/main/java/mscalc/engine/DegreeType.java b/engine/src/main/java/mscalc/engine/DegreeType.java new file mode 100644 index 0000000..f1aded7 --- /dev/null +++ b/engine/src/main/java/mscalc/engine/DegreeType.java @@ -0,0 +1,5 @@ +package mscalc.engine; + +public enum DegreeType { + Radians, Gradians, Degrees +} diff --git a/engine/src/main/java/mscalc/engine/NUM_WIDTH.java b/engine/src/main/java/mscalc/engine/NumberWidth.java similarity index 86% rename from engine/src/main/java/mscalc/engine/NUM_WIDTH.java rename to engine/src/main/java/mscalc/engine/NumberWidth.java index fac0782..4a8bc9e 100644 --- a/engine/src/main/java/mscalc/engine/NUM_WIDTH.java +++ b/engine/src/main/java/mscalc/engine/NumberWidth.java @@ -1,7 +1,7 @@ package mscalc.engine; // This is expected to be in same order as IDM_QWORD, IDM_DWORD etc. -enum NUM_WIDTH +public enum NumberWidth { UNDEFINED(-1), QWORD_WIDTH(0), // Number width of 64 bits mode (default) @@ -11,7 +11,7 @@ enum NUM_WIDTH private final int value; - private NUM_WIDTH(int v) { + private NumberWidth(int v) { this.value = v; } @@ -19,7 +19,7 @@ public int toInt() { return value; } - public static NUM_WIDTH fromInt(int n) { + public static NumberWidth fromInt(int n) { return switch (n) { case 0 -> QWORD_WIDTH; case 1 -> DWORD_WIDTH; diff --git a/engine/src/main/java/mscalc/engine/RadixType.java b/engine/src/main/java/mscalc/engine/RadixType.java index 316ff96..77807d8 100644 --- a/engine/src/main/java/mscalc/engine/RadixType.java +++ b/engine/src/main/java/mscalc/engine/RadixType.java @@ -2,20 +2,33 @@ // This is expected to be in same order as IDM_HEX, IDM_DEC, IDM_OCT, IDM_BIN public enum RadixType { - Unknown(-1), Hex(0), Decimal(1), Octal(2), Binary(3); + Unknown(-1), + Hex(0), + Decimal(1), + Octal(2), + Binary(3); private final int value; - private RadixType(int value) { + RadixType(int value) { this.value = value; } - // TODO: Fix it later - public int cppValue() { + public boolean hasDigit(int digit) { + return switch (this) { + case Unknown -> false; + case Hex -> (0 <= digit && digit < 16); + case Decimal -> (0 <= digit && digit < 10); + case Octal -> (0 <= digit && digit < 8); + case Binary -> (0 <= digit && digit < 2); + }; + } + + public int toInt() { return this.value; } - public static RadixType fromCppValue(int value) { + public static RadixType fromInt(int value) { return switch (value) { case 0 -> Hex; case 1 -> Decimal; diff --git a/gui/src/main/java/mscalc/gui/viewmodel/MoreBindings.java b/gui/src/main/java/mscalc/gui/viewmodel/MoreBindings.java new file mode 100644 index 0000000..0826169 --- /dev/null +++ b/gui/src/main/java/mscalc/gui/viewmodel/MoreBindings.java @@ -0,0 +1,18 @@ +package mscalc.gui.viewmodel; + +import javafx.beans.binding.Bindings; +import javafx.beans.binding.BooleanBinding; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.ReadOnlyBooleanProperty; + +import java.util.function.Function; +import java.util.function.Predicate; + +public class MoreBindings { + public static BooleanBinding map(ObjectProperty objProperty, Predicate pred) { + return Bindings.createBooleanBinding(() -> { + T value = objProperty.get(); + return (value != null) && pred.test(value); + }, objProperty); + } +} diff --git a/gui/src/main/java/mscalc/gui/viewmodel/ScientificCalculatorViewModel.java b/gui/src/main/java/mscalc/gui/viewmodel/ScientificCalculatorViewModel.java index 1037eb2..6ccbe6a 100644 --- a/gui/src/main/java/mscalc/gui/viewmodel/ScientificCalculatorViewModel.java +++ b/gui/src/main/java/mscalc/gui/viewmodel/ScientificCalculatorViewModel.java @@ -1,33 +1,137 @@ package mscalc.gui.viewmodel; +import javafx.beans.binding.Bindings; +import javafx.beans.binding.BooleanExpression; import javafx.beans.property.*; -import javafx.scene.media.AudioClip; -import mscalc.engine.CalcDisplay; -import mscalc.engine.CalculatorManager; -import mscalc.engine.Pair; +import javafx.beans.value.ObservableValue; +import mscalc.engine.*; import mscalc.engine.commands.Command; import mscalc.engine.commands.IExpressionCommand; import mscalc.engine.resource.JavaBundleResourceProvider; +import java.util.ArrayList; import java.util.List; import java.util.Objects; -import java.util.function.Consumer; public class ScientificCalculatorViewModel { + private final List allInputs = new ArrayList<>(); + private final CalculatorManager calculatorManager = new CalculatorManager( new ThisViewModelCalculatorDisplay(), new JavaBundleResourceProvider()); - private final BooleanProperty invertedMode = new SimpleBooleanProperty(false); - private final BooleanProperty hyperbolicMode = new SimpleBooleanProperty(false); + public final BooleanProperty invertedModeProperty = new SimpleBooleanProperty(false); + public final BooleanProperty hyperbolicModeProperty = new SimpleBooleanProperty(false); + + public final BooleanProperty degreeGroupingProperty = new SimpleBooleanProperty(true); + + public final ObjectProperty radixProperty = new SimpleObjectProperty<>(RadixType.Decimal); + public final ObjectProperty integerNumberWidthProperty = new SimpleObjectProperty<>(NumberWidth.QWORD_WIDTH); + public final ObjectProperty degreeTypeProperty = new SimpleObjectProperty<>(DegreeType.Degrees); public final StringProperty displayProperty = new SimpleStringProperty(""); - public final InputViewModel digitOneButton = newInputViewModel() + // --- INPUT DIGITS & CONSTANTS ---- + public final InputViewModel digit0Button = newInputViewModel() + .withText("0") + .withCommand(Command.Command0) + .build(); + + public final InputViewModel digit1Button = newInputViewModel() .withText("1") .withCommand(Command.Command1) .build(); + public final InputViewModel digit2Button = newInputViewModel() + .withText("2") + .withCommand(Command.Command2) + .withEnabled(MoreBindings.map(radixProperty, r -> r.hasDigit(2))) + .build(); + + public final InputViewModel digit3Button = newInputViewModel() + .withText("3") + .withCommand(Command.Command3) + .withEnabled(MoreBindings.map(radixProperty, r -> r.hasDigit(3))) + .build(); + + public final InputViewModel digit4Button = newInputViewModel() + .withText("4") + .withCommand(Command.Command4) + .withEnabled(MoreBindings.map(radixProperty, r -> r.hasDigit(4))) + .build(); + + public final InputViewModel digit5Button = newInputViewModel() + .withText("5") + .withCommand(Command.Command5) + .withEnabled(MoreBindings.map(radixProperty, r -> r.hasDigit(5))) + .build(); + + public final InputViewModel digit6Button = newInputViewModel() + .withText("6") + .withCommand(Command.Command6) + .withEnabled(MoreBindings.map(radixProperty, r -> r.hasDigit(6))) + .build(); + + public final InputViewModel digit7Button = newInputViewModel() + .withText("7") + .withCommand(Command.Command7) + .withEnabled(MoreBindings.map(radixProperty, r -> r.hasDigit(7))) + .build(); + + public final InputViewModel digit8Button = newInputViewModel() + .withText("8") + .withCommand(Command.Command8) + .withEnabled(MoreBindings.map(radixProperty, r -> r.hasDigit(8))) + .build(); + + public final InputViewModel digit9Button = newInputViewModel() + .withText("9") + .withCommand(Command.Command9) + .withEnabled(MoreBindings.map(radixProperty, r -> r.hasDigit(9))) + .build(); + + public final InputViewModel digitAButton = newInputViewModel() + .withText("A") + .withCommand(Command.CommandA) + .withEnabled(MoreBindings.map(radixProperty, r -> r.hasDigit(0xA))) + .build(); + + public final InputViewModel digitBButton = newInputViewModel() + .withText("B") + .withCommand(Command.CommandB) + .withEnabled(MoreBindings.map(radixProperty, r -> r.hasDigit(0xB))) + .build(); + + public final InputViewModel digitCButton = newInputViewModel() + .withText("C") + .withCommand(Command.CommandC) + .withEnabled(MoreBindings.map(radixProperty, r -> r.hasDigit(0xC))) + .build(); + + public final InputViewModel digitDButton = newInputViewModel() + .withText("D") + .withCommand(Command.CommandD) + .withEnabled(MoreBindings.map(radixProperty, r -> r.hasDigit(0xD))) + .build(); + + public final InputViewModel digitEButton = newInputViewModel() + .withText("E") + .withCommand(Command.CommandE) + .withEnabled(MoreBindings.map(radixProperty, r -> r.hasDigit(0xE))) + .build(); + + public final InputViewModel digitFButton = newInputViewModel() + .withText("F") + .withCommand(Command.CommandF) + .withEnabled(MoreBindings.map(radixProperty, r -> r.hasDigit(0xF))) + .build(); + + public final InputViewModel piButton = newInputViewModel() + .withText("π") + .withCommand(Command.CommandPI) + .withEnabled(radixProperty.isEqualTo(RadixType.Decimal)) + .build(); + public final InputViewModel sineButton = newInputViewModel() .withText("sine") .withCommand(Command.CommandSIN) @@ -42,41 +146,10 @@ public InputViewModelBuilder newInputViewModel() { return new InputViewModelBuilder(); } - public class InputViewModel { - private final StringProperty textProperty; - private final ObjectProperty commandProperty; - private final BooleanProperty enabledProperty; - - public InputViewModel(StringProperty textProperty, - ObjectProperty commandProperty, - BooleanProperty enabledProperty) { - - this.textProperty = Objects.requireNonNull(textProperty); - this.commandProperty = Objects.requireNonNull(commandProperty); - this.enabledProperty = Objects.requireNonNull(enabledProperty); - } - - public void execute() { - calculatorManager.sendCommand(this.commandProperty.get()); - } - - public ReadOnlyStringProperty textProperty() { - return this.textProperty; - } - - public ReadOnlyObjectProperty commandProperty() { - return this.commandProperty; - } - - public ReadOnlyBooleanProperty enabledProperty() { - return this.enabledProperty; - } - } - public class InputViewModelBuilder { private StringProperty textProperty; private ObjectProperty commandProperty; - private BooleanProperty enabledProperty = new ReadOnlyBooleanWrapper(true); + private BooleanExpression enabledProperty = new ReadOnlyBooleanWrapper(true); public InputViewModelBuilder withText(String staticText) { return withText(new ReadOnlyStringWrapper(staticText)); @@ -96,14 +169,46 @@ public InputViewModelBuilder withCommand(ObjectProperty commandProperty return this; } - public InputViewModelBuilder withEnabled(BooleanProperty enabledProperty) { + public InputViewModelBuilder withEnabled(BooleanExpression enabledProperty) { this.enabledProperty = enabledProperty; return this; } public InputViewModel build() { - return new InputViewModel( - textProperty, commandProperty, enabledProperty); + var ivm = new InputViewModel(textProperty, commandProperty, enabledProperty); + allInputs.add(ivm); + return ivm; + } + } + + public class InputViewModel { + private final StringProperty textProperty; + private final ObjectProperty commandProperty; + private final BooleanExpression enabledProperty; + + public InputViewModel(StringProperty textProperty, + ObjectProperty commandProperty, + BooleanExpression enabledProperty) { + + this.textProperty = Objects.requireNonNull(textProperty); + this.commandProperty = Objects.requireNonNull(commandProperty); + this.enabledProperty = Objects.requireNonNull(enabledProperty); + } + + public void execute() { + calculatorManager.sendCommand(this.commandProperty.get()); + } + + public ReadOnlyStringProperty textProperty() { + return this.textProperty; + } + + public ReadOnlyObjectProperty commandProperty() { + return this.commandProperty; + } + + public BooleanExpression enabledProperty() { + return this.enabledProperty; } } diff --git a/gui/src/main/java/mscalc/gui/views/scientific/ScientificView.java b/gui/src/main/java/mscalc/gui/views/scientific/ScientificView.java index 3c62e5d..a3cde5c 100644 --- a/gui/src/main/java/mscalc/gui/views/scientific/ScientificView.java +++ b/gui/src/main/java/mscalc/gui/views/scientific/ScientificView.java @@ -38,13 +38,75 @@ public ScientificView() { @FXML private Button bDigit1; + @FXML + private Button bDigit2; + + @FXML + private Button bDigit3; + + @FXML + private Button bDigit4; + + @FXML + private Button bDigit5; + + @FXML + private Button bDigit6; + + @FXML + private Button bDigit7; + + @FXML + private Button bDigit8; + + @FXML + private Button bDigit9; + + @FXML + private Button bDigitA; + + @FXML + private Button bDigitB; + + @FXML + private Button bDigitC; + + @FXML + private Button bDigitD; + + @FXML + private Button bDigitE; + + @FXML + private Button bDigitF; + + @FXML + private Button bPiNumber; + @FXML private Button bSine; public void install(Scene scene) { display.textProperty().bind(viewModel.displayProperty); - bindButton(bDigit1, viewModel.digitOneButton); + bindButton(bDigit1, viewModel.digit1Button); + bindButton(bDigit2, viewModel.digit2Button); + bindButton(bDigit3, viewModel.digit3Button); + bindButton(bDigit4, viewModel.digit4Button); + bindButton(bDigit5, viewModel.digit5Button); + bindButton(bDigit6, viewModel.digit6Button); + bindButton(bDigit7, viewModel.digit7Button); + bindButton(bDigit8, viewModel.digit8Button); + bindButton(bDigit9, viewModel.digit9Button); + bindButton(bDigitA, viewModel.digitAButton); + bindButton(bDigitB, viewModel.digitBButton); + bindButton(bDigitC, viewModel.digitCButton); + bindButton(bDigitD, viewModel.digitDButton); + bindButton(bDigitE, viewModel.digitEButton); + bindButton(bDigitF, viewModel.digitFButton); + bindButton(bPiNumber, viewModel.piButton); + + bindButton(bSine, viewModel.sineButton); try {