summaryrefslogtreecommitdiff
path: root/src/main/java/sevenUnitsGUI/TabbedView.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/sevenUnitsGUI/TabbedView.java')
-rw-r--r--src/main/java/sevenUnitsGUI/TabbedView.java280
1 files changed, 234 insertions, 46 deletions
diff --git a/src/main/java/sevenUnitsGUI/TabbedView.java b/src/main/java/sevenUnitsGUI/TabbedView.java
index d0eb32f..3a951ef 100644
--- a/src/main/java/sevenUnitsGUI/TabbedView.java
+++ b/src/main/java/sevenUnitsGUI/TabbedView.java
@@ -20,16 +20,18 @@ import java.awt.BorderLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
+import java.awt.event.ItemEvent;
import java.awt.event.KeyEvent;
-import java.text.DecimalFormat;
-import java.text.NumberFormat;
import java.util.AbstractSet;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.NoSuchElementException;
+import java.util.Objects;
import java.util.Optional;
+import java.util.OptionalInt;
import java.util.Set;
+import java.util.function.Function;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
@@ -37,7 +39,6 @@ import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
-import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
@@ -58,6 +59,7 @@ import javax.swing.border.TitledBorder;
import sevenUnits.ProgramInfo;
import sevenUnits.unit.UnitType;
import sevenUnits.utils.NameSymbol;
+import sevenUnits.utils.UncertainDouble;
/**
* A View that separates its functions into multiple tabs
@@ -111,7 +113,99 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView {
}
- private static final NumberFormat NUMBER_FORMATTER = new DecimalFormat();
+ /**
+ * The standard types of rounding, corresponding to the options on the
+ * TabbedView's settings panel.
+ *
+ * @since 2022-04-18
+ */
+ private static enum StandardRoundingType {
+ /**
+ * Rounds to a fixed number of significant digits. Precision is used,
+ * representing the number of significant digits to round to.
+ */
+ SIGNIFICANT_DIGITS(true) {
+ @Override
+ public Function<UncertainDouble, String> getRuleFromPrecision(
+ int precision) {
+ return StandardDisplayRules.fixedPrecision(precision);
+ }
+ },
+ /**
+ * Rounds to a fixed number of decimal places. Precision is used,
+ * representing the number of decimal places to round to.
+ */
+ DECIMAL_PLACES(true) {
+ @Override
+ public Function<UncertainDouble, String> getRuleFromPrecision(
+ int precision) {
+ return StandardDisplayRules.fixedDecimals(precision);
+ }
+ },
+ /**
+ * Rounds according to UncertainDouble's toString method. The specified
+ * precision is ignored.
+ */
+ UNCERTAINTY(false) {
+ @Override
+ public Function<UncertainDouble, String> getRuleFromPrecision(
+ int precision) {
+ return StandardDisplayRules.uncertaintyBased();
+ }
+ };
+
+ /**
+ * If true, this type of rounding rule requires you to specify a
+ * precision.
+ */
+ private final boolean requiresPrecision;
+
+ /**
+ * @param canCustomizePrecision
+ * @since 2022-04-18
+ */
+ private StandardRoundingType(boolean requiresPrecision) {
+ this.requiresPrecision = requiresPrecision;
+ }
+
+ /**
+ * Gets a rounding rule of this type.
+ *
+ * @param precision the rounding type's precision. If
+ * {@link #requiresPrecision} is false, this field will
+ * be ignored.
+ * @return rounding rule
+ * @since 2022-04-18
+ */
+ public abstract Function<UncertainDouble, String> getRuleFromPrecision(
+ int precision);
+
+ /**
+ * Tries to get this rule without specifying precision.
+ *
+ * @throws UnsupportedOperationException if this rule requires specifying
+ * precision
+ * @since 2022-04-18
+ */
+ public final Function<UncertainDouble, String> getRuleWithoutPrecision() {
+ if (this.requiresPrecision())
+ throw new UnsupportedOperationException("Rounding type " + this
+ + " requires you to specify precision.");
+ else
+ // random number to mess with anyone who lies about whether or not
+ // precision is required
+ return this.getRuleFromPrecision(-623546735);
+ }
+
+ /**
+ * @return whether or not this rounding type requires you to specify an
+ * integer precision
+ * @since 2022-04-18
+ */
+ public boolean requiresPrecision() {
+ return this.requiresPrecision;
+ }
+ }
/**
* Creates a TabbedView.
@@ -137,7 +231,7 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView {
/** The combo box that selects dimensions */
private final JComboBox<String> dimensionSelector;
/** The panel for inputting values in the dimension-based converter */
- private final JFormattedTextField valueInput;
+ private final JTextField valueInput;
/** The panel for "From" in the dimension-based converter */
private final SearchBoxList<String> fromSearch;
/** The panel for "To" in the dimension-based converter */
@@ -163,6 +257,10 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView {
/** The text box for prefix data in the prefix viewer */
private final JTextArea prefixTextBox;
+ // SETTINGS STUFF
+ private StandardRoundingType roundingType;
+ private int precision;
+
/**
* Creates the view and makes it visible to the user
*
@@ -229,7 +327,7 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView {
final JLabel valuePrompt = new JLabel("Value to convert: ");
outputPanel.add(valuePrompt, BorderLayout.LINE_START);
- this.valueInput = new JFormattedTextField(NUMBER_FORMATTER);
+ this.valueInput = new JTextField();
outputPanel.add(this.valueInput, BorderLayout.CENTER);
// conversion button
@@ -352,61 +450,89 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView {
// rounding rule selection
final ButtonGroup roundingRuleButtons = new ButtonGroup();
+ this.roundingType = this.getPresenterRoundingType()
+ .orElseThrow(() -> new AssertionError(
+ "Presenter loaded non-standard rounding rule"));
+ this.precision = this.getPresenterPrecision().orElse(6);
final JLabel roundingRuleLabel = new JLabel("Rounding Rule:");
roundingPanel.add(roundingRuleLabel, new GridBagBuilder(0, 0)
.setAnchor(GridBagConstraints.LINE_START).build());
+ // sigDigSlider needs to be first so that the rounding-type buttons can
+ // show and hide it
+ final JLabel sliderLabel = new JLabel("Precision:");
+ sliderLabel.setVisible(
+ this.roundingType != StandardRoundingType.UNCERTAINTY);
+ roundingPanel.add(sliderLabel, new GridBagBuilder(0, 4)
+ .setAnchor(GridBagConstraints.LINE_START).build());
+
+ final JSlider sigDigSlider = new JSlider(0, 12);
+ roundingPanel.add(sigDigSlider, new GridBagBuilder(0, 5)
+ .setAnchor(GridBagConstraints.LINE_START).build());
+
+ sigDigSlider.setMajorTickSpacing(4);
+ sigDigSlider.setMinorTickSpacing(1);
+ sigDigSlider.setSnapToTicks(true);
+ sigDigSlider.setPaintTicks(true);
+ sigDigSlider.setPaintLabels(true);
+
+ sigDigSlider.setVisible(
+ this.roundingType != StandardRoundingType.UNCERTAINTY);
+ sigDigSlider.setValue(this.precision);
+
+ sigDigSlider.addChangeListener(e -> {
+ this.precision = sigDigSlider.getValue();
+ this.updatePresenterRoundingRule();
+ });
+
+ // significant digit rounding
final JRadioButton fixedPrecision = new JRadioButton(
"Fixed Precision");
-// if (this.presenter.roundingType == RoundingType.SIGNIFICANT_DIGITS) {
-// fixedPrecision.setSelected(true);
-// }
-// fixedPrecision.addActionListener(e -> this.presenter
-// .setRoundingType(RoundingType.SIGNIFICANT_DIGITS));
+ if (this.roundingType == StandardRoundingType.SIGNIFICANT_DIGITS) {
+ fixedPrecision.setSelected(true);
+ }
+ fixedPrecision.addActionListener(e -> {
+ this.roundingType = StandardRoundingType.SIGNIFICANT_DIGITS;
+ sliderLabel.setVisible(true);
+ sigDigSlider.setVisible(true);
+ this.updatePresenterRoundingRule();
+ });
roundingRuleButtons.add(fixedPrecision);
roundingPanel.add(fixedPrecision, new GridBagBuilder(0, 1)
.setAnchor(GridBagConstraints.LINE_START).build());
+ // decimal place rounding
final JRadioButton fixedDecimals = new JRadioButton(
"Fixed Decimal Places");
-// if (this.presenter.roundingType == RoundingType.DECIMAL_PLACES) {
-// fixedDecimals.setSelected(true);
-// }
-// fixedDecimals.addActionListener(e -> this.presenter
-// .setRoundingType(RoundingType.DECIMAL_PLACES));
+ if (this.roundingType == StandardRoundingType.DECIMAL_PLACES) {
+ fixedDecimals.setSelected(true);
+ }
+ fixedDecimals.addActionListener(e -> {
+ this.roundingType = StandardRoundingType.DECIMAL_PLACES;
+ sliderLabel.setVisible(true);
+ sigDigSlider.setVisible(true);
+ this.updatePresenterRoundingRule();
+ });
roundingRuleButtons.add(fixedDecimals);
roundingPanel.add(fixedDecimals, new GridBagBuilder(0, 2)
.setAnchor(GridBagConstraints.LINE_START).build());
+ // scientific rounding
final JRadioButton relativePrecision = new JRadioButton(
- "Scientific Precision");
-// if (this.presenter.roundingType == RoundingType.SCIENTIFIC) {
-// relativePrecision.setSelected(true);
-// }
-// relativePrecision.addActionListener(
-// e -> this.presenter.setRoundingType(RoundingType.SCIENTIFIC));
+ "Uncertainty-Based Rounding");
+ if (this.roundingType == StandardRoundingType.UNCERTAINTY) {
+ relativePrecision.setSelected(true);
+ }
+ relativePrecision.addActionListener(e -> {
+ this.roundingType = StandardRoundingType.UNCERTAINTY;
+ sliderLabel.setVisible(false);
+ sigDigSlider.setVisible(false);
+ this.updatePresenterRoundingRule();
+ });
roundingRuleButtons.add(relativePrecision);
roundingPanel.add(relativePrecision, new GridBagBuilder(0, 3)
.setAnchor(GridBagConstraints.LINE_START).build());
-
- final JLabel sliderLabel = new JLabel("Precision:");
- roundingPanel.add(sliderLabel, new GridBagBuilder(0, 4)
- .setAnchor(GridBagConstraints.LINE_START).build());
-
- final JSlider sigDigSlider = new JSlider(0, 12);
- roundingPanel.add(sigDigSlider, new GridBagBuilder(0, 5)
- .setAnchor(GridBagConstraints.LINE_START).build());
-
- sigDigSlider.setMajorTickSpacing(4);
- sigDigSlider.setMinorTickSpacing(1);
- sigDigSlider.setSnapToTicks(true);
- sigDigSlider.setPaintTicks(true);
- sigDigSlider.setPaintLabels(true);
-// sigDigSlider.setValue(this.presenter.precision);
-
-// sigDigSlider.addChangeListener(
-// e -> this.presenter.setPrecision(sigDigSlider.getValue()));
}
// ============ PREFIX REPETITION SETTINGS ============
@@ -501,17 +627,18 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView {
miscPanel.setLayout(new GridBagLayout());
final JCheckBox oneWay = new JCheckBox("Convert One Way Only");
-// oneWay.setSelected(this.presenter.oneWay);
-// oneWay.addItemListener(
-// e -> this.presenter.setOneWay(e.getStateChange() == 1));
+ oneWay.setSelected(this.presenter.oneWayConversionEnabled());
+ oneWay.addItemListener(e -> this.presenter.setOneWayConversionEnabled(
+ e.getStateChange() == ItemEvent.SELECTED));
miscPanel.add(oneWay, new GridBagBuilder(0, 0)
.setAnchor(GridBagConstraints.LINE_START).build());
final JCheckBox showAllVariations = new JCheckBox(
"Show Duplicates in \"Convert Units\"");
-// showAllVariations.setSelected(this.presenter.includeDuplicateUnits);
-// showAllVariations.addItemListener(e -> this.presenter
-// .setIncludeDuplicateUnits(e.getStateChange() == 1));
+ showAllVariations.setSelected(this.presenter.duplicateUnitsShown());
+ showAllVariations
+ .addItemListener(e -> this.presenter.setShowDuplicateUnits(
+ e.getStateChange() == ItemEvent.SELECTED));
miscPanel.add(showAllVariations, new GridBagBuilder(0, 1)
.setAnchor(GridBagConstraints.LINE_START).build());
@@ -552,6 +679,43 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView {
return this.valueInput.getText();
}
+ /**
+ * @return the precision of the presenter's rounding rule, if that is
+ * meaningful
+ * @since 2022-04-18
+ */
+ private OptionalInt getPresenterPrecision() {
+ final var presenterRule = this.presenter.getNumberDisplayRule();
+ if (presenterRule instanceof StandardDisplayRules.FixedDecimals)
+ return OptionalInt
+ .of(((StandardDisplayRules.FixedDecimals) presenterRule)
+ .decimalPlaces());
+ else if (presenterRule instanceof StandardDisplayRules.FixedPrecision)
+ return OptionalInt
+ .of(((StandardDisplayRules.FixedPrecision) presenterRule)
+ .significantFigures());
+ else
+ return OptionalInt.empty();
+ }
+
+ /**
+ * Determines which rounding type the presenter is currently using, if any.
+ *
+ * @since 2022-04-18
+ */
+ private Optional<StandardRoundingType> getPresenterRoundingType() {
+ final var presenterRule = this.presenter.getNumberDisplayRule();
+ if (Objects.equals(presenterRule,
+ StandardDisplayRules.uncertaintyBased()))
+ return Optional.of(StandardRoundingType.UNCERTAINTY);
+ else if (presenterRule instanceof StandardDisplayRules.FixedDecimals)
+ return Optional.of(StandardRoundingType.DECIMAL_PLACES);
+ else if (presenterRule instanceof StandardDisplayRules.FixedPrecision)
+ return Optional.of(StandardRoundingType.SIGNIFICANT_DIGITS);
+ else
+ return Optional.empty();
+ }
+
@Override
public Optional<String> getSelectedDimensionName() {
final String selectedItem = (String) this.dimensionSelector
@@ -644,4 +808,28 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView {
public void showUnitConversionOutput(UnitConversionRecord uc) {
this.unitOutput.setText(uc.toString());
}
+
+ /**
+ * Sets the presenter's rounding rule to the one specified by the current
+ * settings
+ *
+ * @since 2022-04-18
+ */
+ private void updatePresenterRoundingRule() {
+ final Function<UncertainDouble, String> roundingRule;
+ switch (this.roundingType) {
+ case DECIMAL_PLACES:
+ roundingRule = StandardDisplayRules.fixedDecimals(this.precision);
+ break;
+ case SIGNIFICANT_DIGITS:
+ roundingRule = StandardDisplayRules.fixedPrecision(this.precision);
+ break;
+ case UNCERTAINTY:
+ roundingRule = StandardDisplayRules.uncertaintyBased();
+ break;
+ default:
+ throw new AssertionError();
+ }
+ this.presenter.setNumberDisplayRule(roundingRule);
+ }
}