Skip to content

Commit

Permalink
bind angle & word with select to view model
Browse files Browse the repository at this point in the history
  • Loading branch information
marcin-chwedczuk committed Nov 21, 2024
1 parent 500cf5e commit 2aecf9b
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,57 @@ public class ScientificCalculatorViewModel {
.withKeyboardShortcut(KeyCode.F8)
.build();

// -- ANGLE TYPE SELECT --
public final InputViewModel angleDegreesButton = newInputViewModel()
.withText("Degrees")
.withCommand(Command.CommandDEG)
.withKeyboardShortcut(KeyCode.F2)
.onlyActiveWhen(radixProperty.isEqualTo(RadixType.Decimal))
.build();

public final InputViewModel angleRadiansButton = newInputViewModel()
.withText("Radians")
.withCommand(Command.CommandRAD)
.withKeyboardShortcut(KeyCode.F3)
.onlyActiveWhen(radixProperty.isEqualTo(RadixType.Decimal))
.build();

public final InputViewModel angleGradiansButton = newInputViewModel()
.withText("Gradians")
.withCommand(Command.CommandGRAD)
.withKeyboardShortcut(KeyCode.F4)
.onlyActiveWhen(radixProperty.isEqualTo(RadixType.Decimal))
.build();

// --- WORD WIDTH SELECT ---
public final InputViewModel radixWordWidthQWord = newInputViewModel()
.withText("QWORD")
.withCommand(Command.CommandQword)
.withKeyboardShortcut(KeyCode.F12)
.onlyActiveWhen(radixProperty.isNotEqualTo(RadixType.Decimal))
.build();

public final InputViewModel radixWordWidthDWord = newInputViewModel()
.withText("DWORD")
.withCommand(Command.CommandDword)
.withKeyboardShortcut(KeyCode.F2)
.onlyActiveWhen(radixProperty.isNotEqualTo(RadixType.Decimal))
.build();

public final InputViewModel radixWordWidthWord = newInputViewModel()
.withText("WORD")
.withCommand(Command.CommandWord)
.withKeyboardShortcut(KeyCode.F3)
.onlyActiveWhen(radixProperty.isNotEqualTo(RadixType.Decimal))
.build();

public final InputViewModel radixWordWidthByte = newInputViewModel()
.withText("BYTE")
.withCommand(Command.CommandByte)
.withKeyboardShortcut(KeyCode.F4)
.onlyActiveWhen(radixProperty.isNotEqualTo(RadixType.Decimal))
.build();

// -- INVERT & HYP BUTTONS ---
public final InputViewModel invertButton = newInputViewModel()
.withText("Inverse")
Expand Down Expand Up @@ -433,7 +484,9 @@ public class ScientificCalculatorViewModel {
public ScientificCalculatorViewModel() {
// Initialize Scientific mode
calculatorManager.sendCommand(Command.ModeScientific);
// Select radians
// Setup some initial configuration
calculatorManager.sendCommand(Command.CommandQword);
calculatorManager.sendCommand(Command.CommandDec);
calculatorManager.sendCommand(Command.CommandRAD);
}

Expand Down Expand Up @@ -476,12 +529,18 @@ public String toShortcutDescription() {
}

public class InputViewModelBuilder {
private BooleanExpression activeProperty = new ReadOnlyBooleanWrapper(true);
private StringExpression textProperty;
private StringExpression tooltipProperty = new ReadOnlyStringWrapper(null);
private ObjectExpression<Command> commandProperty;
private BooleanExpression enabledProperty = new ReadOnlyBooleanWrapper(true);
private KeyboardCode keyboardShortcut;

public InputViewModelBuilder onlyActiveWhen(BooleanExpression condition) {
this.activeProperty = condition;
return this;
}

/*
* Set text for normal, inverted, hyperbolic and hyperbolic-inverted modes.
* You may specify only one, two or four names.
Expand Down Expand Up @@ -568,32 +627,43 @@ public InputViewModel build() {
keyboardShortcut.toShortcutDescription());
}

return new InputViewModel(textProperty, tooltipProperty,
return new InputViewModel(activeProperty, textProperty, tooltipProperty,
commandProperty, enabledProperty, keyboardShortcut);
}
}

public class InputViewModel {
private final BooleanExpression activeProperty;
private final StringExpression textProperty;
private final StringExpression tooltipProperty;
private final ObjectExpression<Command> commandProperty;
private final BooleanExpression enabledProperty;
private final KeyboardCode keyboardShortcut;

public InputViewModel(StringExpression textProperty,
public InputViewModel(BooleanExpression activeProperty,
StringExpression textProperty,
StringExpression tooltipProperty,
ObjectExpression<Command> commandProperty,
BooleanExpression enabledProperty,
@Nullable KeyboardCode keyboardShortcut) {

this.activeProperty = Objects.requireNonNull(activeProperty);
this.textProperty = Objects.requireNonNull(textProperty);
this.tooltipProperty = Objects.requireNonNull(tooltipProperty);
this.commandProperty = Objects.requireNonNull(commandProperty);
this.enabledProperty = Objects.requireNonNull(enabledProperty);
this.keyboardShortcut = keyboardShortcut;
}

public boolean isActive() {
return activeProperty.get();
}

public void execute() {
if (!activeProperty.get()) {
return;
}

switch (this.commandProperty.get()) {
case CommandINV -> {
invertedModeProperty.set(!invertedModeProperty.get());
Expand Down
19 changes: 15 additions & 4 deletions gui/src/main/java/mscalc/gui/views/scientific/ScientificView.fxml
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,25 @@
<RadioButton mnemonicParsing="false" toggleGroup="${radixToggleGroup}" text="Bin" fx:id="radioRadixBin" />
</children>
</HBox>
<HBox styleClass="selectAngleTypeArea">
<HBox fx:id="selectAngleTypeArea" styleClass="selectAngleTypeArea">
<children>
<fx:define>
<ToggleGroup fx:id="angleTypeToggleGroup" />
</fx:define>
<RadioButton mnemonicParsing="false" toggleGroup="${angleTypeToggleGroup}" text="Degrees" />
<RadioButton mnemonicParsing="false" toggleGroup="${angleTypeToggleGroup}" text="Radians" />
<RadioButton mnemonicParsing="false" toggleGroup="${angleTypeToggleGroup}" text="Gradians" />
<RadioButton mnemonicParsing="false" toggleGroup="${angleTypeToggleGroup}" text="Degrees" fx:id="radioAngleDegrees" />
<RadioButton mnemonicParsing="false" toggleGroup="${angleTypeToggleGroup}" text="Radians" fx:id="radioAngleRadians" />
<RadioButton mnemonicParsing="false" toggleGroup="${angleTypeToggleGroup}" text="Gradians" fx:id="radioAngleGradians" />
</children>
</HBox>
<HBox fx:id="selectWordWidthArea" styleClass="selectWordWidthArea">
<children>
<fx:define>
<ToggleGroup fx:id="wordWidthToggleGroup" />
</fx:define>
<RadioButton mnemonicParsing="false" toggleGroup="${wordWidthToggleGroup}" text="QWORD" fx:id="radioWidthQWord" />
<RadioButton mnemonicParsing="false" toggleGroup="${wordWidthToggleGroup}" text="DWORD" fx:id="radioWidthDWord" />
<RadioButton mnemonicParsing="false" toggleGroup="${wordWidthToggleGroup}" text="WORD" fx:id="radioWidthWord" />
<RadioButton mnemonicParsing="false" toggleGroup="${wordWidthToggleGroup}" text="BYTE" fx:id="radioWidthByte" />
</children>
</HBox>
</children>
Expand Down
95 changes: 63 additions & 32 deletions gui/src/main/java/mscalc/gui/views/scientific/ScientificView.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,28 @@
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import mscalc.engine.RadixType;
import mscalc.gui.viewmodel.ScientificCalculatorViewModel;
import mscalc.gui.viewmodel.ScientificCalculatorViewModel.InputViewModel;
import mscalc.gui.viewmodel.ScientificCalculatorViewModel.KeyboardCode;
import mscalc.gui.views.CalculatorView;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class ScientificView extends VBox implements CalculatorView {
private static final Logger logger = LogManager.getLogger(ScientificView.class);

private final ScientificCalculatorViewModel viewModel = new ScientificCalculatorViewModel();
private final HashMap<KeyboardCode, ButtonBase> keyboardMapping = new HashMap<>();

// Some buttons share same shortcuts but are not active at the same time
private final HashMap<KeyboardCode, List<ButtonBase>> keyboardMapping = new HashMap<>();

public ScientificView() {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("ScientificView.fxml"));
Expand All @@ -42,6 +47,7 @@ public ScientificView() {
@FXML
private TextField display;

// Radix selection
@FXML
private ToggleGroup radixToggleGroup;
@FXML
Expand All @@ -53,6 +59,33 @@ public ScientificView() {
@FXML
private RadioButton radioRadixBin;

// Angle type selection
@FXML
private ToggleGroup angleTypeToggleGroup;
@FXML
private RadioButton radioAngleDegrees;
@FXML
private RadioButton radioAngleRadians;
@FXML
private RadioButton radioAngleGradians;
@FXML
private HBox selectAngleTypeArea;

// Word width selection
@FXML
private ToggleGroup wordWidthToggleGroup;
@FXML
private RadioButton radioWidthQWord;
@FXML
private RadioButton radioWidthDWord;
@FXML
private RadioButton radioWidthWord;
@FXML
private RadioButton radioWidthByte;
@FXML
private HBox selectWordWidthArea;

// Invert & Hyperbolic
@FXML
private CheckBox cbInvert;

Expand Down Expand Up @@ -237,27 +270,22 @@ public void install(Scene scene) {
bindButton(radioRadixDec, viewModel.radixDecButton);
bindButton(radioRadixOct, viewModel.radixOctButton);
bindButton(radioRadixBin, viewModel.radixBinButton);
/*
viewModel.radixProperty.addListener((observable, oldValue, newValue) -> {
logger.info("Selected radix from ViewModel: {}", newValue);
radixToggleGroup.selectToggle(switch (newValue) {
case Hex -> radioRadixHex;
case Decimal -> radioRadixDec;
case Octal -> radioRadixOct;
case Binary -> radioRadixBin;
default -> null;
});
});
radixToggleGroup.selectedToggleProperty().addListener((observableValue, oldValue, newValue) -> {
RadixType radix =
(newValue == radioRadixHex) ? RadixType.Hex :
(newValue == radioRadixDec) ? RadixType.Decimal :
(newValue == radioRadixOct) ? RadixType.Octal :
(newValue == radioRadixBin) ? RadixType.Binary :
null;
logger.info("Selected radix from UI: {}", radix);
viewModel.radixProperty.set(radix);
});*/

selectAngleTypeArea.visibleProperty().bind(viewModel.radixProperty.isEqualTo(RadixType.Decimal));
selectAngleTypeArea.managedProperty().bind(selectAngleTypeArea.visibleProperty());
selectWordWidthArea.visibleProperty().bind(viewModel.radixProperty.isNotEqualTo(RadixType.Decimal));
selectWordWidthArea.managedProperty().bind(selectWordWidthArea.visibleProperty());

angleTypeToggleGroup.selectToggle(radioAngleRadians);
bindButton(radioAngleDegrees, viewModel.angleDegreesButton);
bindButton(radioAngleRadians, viewModel.angleRadiansButton);
bindButton(radioAngleGradians, viewModel.angleGradiansButton);

wordWidthToggleGroup.selectToggle(radioWidthQWord);
bindButton(radioWidthQWord, viewModel.radixWordWidthQWord);
bindButton(radioWidthDWord, viewModel.radixWordWidthDWord);
bindButton(radioWidthWord, viewModel.radixWordWidthWord);
bindButton(radioWidthByte, viewModel.radixWordWidthByte);

bindButton(bClear, viewModel.clearButton);
bindButton(bClearEntry, viewModel.clearEntryButton);
Expand Down Expand Up @@ -335,7 +363,7 @@ public void uninstall() {
throw new RuntimeException("TODO");
}

private void bindButton(ButtonBase button, ScientificCalculatorViewModel.InputViewModel operation) {
private void bindButton(ButtonBase button, InputViewModel operation) {
button.textProperty().bind(operation.textProperty());

// Pre-create the tooltip object
Expand All @@ -356,18 +384,18 @@ private void bindButton(ButtonBase button, ScientificCalculatorViewModel.InputVi
button.setUserData(operation);

operation.keyboardShortcut().ifPresent(ks -> {
var conflict = keyboardMapping.put(ks, button);
if (conflict != null) {
logger.error("Conflicting keyboard mapping: {}", ks);
}
keyboardMapping.putIfAbsent(ks, new ArrayList<>());
keyboardMapping.get(ks).add(button);
});
}

private void onKeyPressed(KeyEvent e) {
KeyboardCode kc = new KeyboardCode(e.getCode(), e.isControlDown(), e.isShiftDown());

if (keyboardMapping.containsKey(kc)) {
keyboardMapping.get(kc).arm();
keyboardMapping.get(kc).stream()
.filter(input -> ((InputViewModel)input.getUserData()).isActive())
.forEach(ButtonBase::arm);
e.consume();
}
}
Expand All @@ -376,9 +404,12 @@ private void onKeyReleased(KeyEvent e) {
KeyboardCode kc = new KeyboardCode(e.getCode(), e.isControlDown(), e.isShiftDown());

if (keyboardMapping.containsKey(kc)) {
var button = keyboardMapping.get(kc);
button.disarm();
button.fire();
keyboardMapping.get(kc).stream()
.filter(input -> ((InputViewModel)input.getUserData()).isActive())
.forEach(button -> {
button.disarm();
button.fire();
});
e.consume();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@
@include selectArea();
}

.selectWordWidthArea {
@include selectArea();
}

.functionSelectArea {
@include selectArea();
}
Expand Down

0 comments on commit 2aecf9b

Please sign in to comment.