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.java605
1 files changed, 605 insertions, 0 deletions
diff --git a/src/main/java/sevenUnitsGUI/TabbedView.java b/src/main/java/sevenUnitsGUI/TabbedView.java
new file mode 100644
index 0000000..e92b661
--- /dev/null
+++ b/src/main/java/sevenUnitsGUI/TabbedView.java
@@ -0,0 +1,605 @@
+/**
+ * Copyright (C) 2022 Adrien Hopkins
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+package sevenUnitsGUI;
+
+import java.awt.BorderLayout;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.GridLayout;
+import java.awt.event.KeyEvent;
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.util.AbstractSet;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Optional;
+import java.util.Set;
+
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+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;
+import javax.swing.JPanel;
+import javax.swing.JRadioButton;
+import javax.swing.JScrollPane;
+import javax.swing.JSlider;
+import javax.swing.JTabbedPane;
+import javax.swing.JTextArea;
+import javax.swing.JTextField;
+import javax.swing.SwingConstants;
+import javax.swing.UIManager;
+import javax.swing.UnsupportedLookAndFeelException;
+import javax.swing.WindowConstants;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.TitledBorder;
+
+import sevenUnits.ProgramInfo;
+import sevenUnits.unit.BaseDimension;
+import sevenUnits.unit.Unit;
+import sevenUnits.unit.UnitPrefix;
+import sevenUnits.utils.NamedObjectProduct;
+import sevenUnits.utils.ObjectProduct;
+
+/**
+ * A View that separates its functions into multiple tabs
+ *
+ * @since 2022-02-19
+ */
+final class TabbedView implements ExpressionConversionView, UnitConversionView {
+ /**
+ * A List-like view of a JComboBox's items
+ *
+ * @param <E> type of item in list
+ *
+ * @since 2022-02-19
+ */
+ private static final class JComboBoxItemSet<E> extends AbstractSet<E> {
+ private final JComboBox<E> comboBox;
+
+ /**
+ * @param comboBox combo box to get items from
+ * @since 2022-02-19
+ */
+ public JComboBoxItemSet(JComboBox<E> comboBox) {
+ this.comboBox = comboBox;
+ }
+
+ @Override
+ public Iterator<E> iterator() {
+ return new Iterator<>() {
+ private int index = 0;
+
+ @Override
+ public boolean hasNext() {
+ return this.index < JComboBoxItemSet.this.size();
+ }
+
+ @Override
+ public E next() {
+ if (this.hasNext())
+ return JComboBoxItemSet.this.comboBox.getItemAt(this.index++);
+ else
+ throw new NoSuchElementException(
+ "Iterator has finished iteration");
+ }
+ };
+ }
+
+ @Override
+ public int size() {
+ return this.comboBox.getItemCount();
+ }
+
+ }
+
+ private static final NumberFormat NUMBER_FORMATTER = new DecimalFormat();
+
+ /**
+ * Creates a TabbedView.
+ *
+ * @param args command line arguments
+ * @since 2022-02-19
+ */
+ public static void main(String[] args) {
+ // This view doesn't need to do anything, the side effects of creating it
+ // are enough to start the program
+ @SuppressWarnings("unused")
+ final View view = new TabbedView();
+ }
+
+ /** The Presenter that handles this View */
+ final Presenter presenter;
+ /** The frame that this view lives on */
+ final JFrame frame;
+ /** The tabbed pane that contains all of the components */
+ final JTabbedPane masterPane;
+
+ // DIMENSION-BASED CONVERTER
+ /** The combo box that selects dimensions */
+ private final JComboBox<NamedObjectProduct<BaseDimension>> dimensionSelector;
+ /** The panel for inputting values in the dimension-based converter */
+ private final JTextField valueInput;
+ /** The panel for "From" in the dimension-based converter */
+ private final SearchBoxList<Unit> fromSearch;
+ /** The panel for "To" in the dimension-based converter */
+ private final SearchBoxList<Unit> toSearch;
+ /** The output area in the dimension-based converter */
+ private final JTextArea unitOutput;
+
+ // EXPRESSION-BASED CONVERTER
+ /** The "From" entry in the conversion panel */
+ private final JTextField fromEntry;
+ /** The "To" entry in the conversion panel */
+ private final JTextField toEntry;
+ /** The output area in the conversion panel */
+ private final JTextArea expressionOutput;
+
+ // UNIT AND PREFIX VIEWERS
+ /** The searchable list of unit names in the unit viewer */
+ private final SearchBoxList<Unit> unitNameList;
+ /** The searchable list of prefix names in the prefix viewer */
+ private final SearchBoxList<UnitPrefix> prefixNameList;
+ /** The text box for unit data in the unit viewer */
+ private final JTextArea unitTextBox;
+ /** The text box for prefix data in the prefix viewer */
+ private final JTextArea prefixTextBox;
+
+ /**
+ * Creates the view and makes it visible to the user
+ *
+ * @since 2022-02-19
+ */
+ public TabbedView() {
+ // enable system look and feel
+ try {
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ } catch (ClassNotFoundException | InstantiationException
+ | IllegalAccessException | UnsupportedLookAndFeelException e) {
+ // oh well, just use default theme
+ System.err.println("Failed to enable system look-and-feel.");
+ e.printStackTrace();
+ }
+
+ // initialize important components
+ this.presenter = new Presenter(this);
+ this.frame = new JFrame("7Units " + ProgramInfo.VERSION);
+ this.frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
+
+ // master components (those that contain everything else within them)
+ this.masterPane = new JTabbedPane();
+ this.frame.add(this.masterPane);
+
+ // ============ UNIT CONVERSION TAB ============
+ final JPanel convertUnitPanel = new JPanel();
+ this.masterPane.addTab("Convert Units", convertUnitPanel);
+ this.masterPane.setMnemonicAt(0, KeyEvent.VK_U);
+ convertUnitPanel.setLayout(new BorderLayout());
+
+ { // panel for input part
+ final JPanel inputPanel = new JPanel();
+ convertUnitPanel.add(inputPanel, BorderLayout.CENTER);
+ inputPanel.setLayout(new GridLayout(1, 3));
+ inputPanel.setBorder(new EmptyBorder(6, 6, 3, 6));
+
+ this.fromSearch = new SearchBoxList<>();
+ inputPanel.add(this.fromSearch);
+
+ final JPanel inBetweenPanel = new JPanel();
+ inputPanel.add(inBetweenPanel);
+ inBetweenPanel.setLayout(new BorderLayout());
+
+ this.dimensionSelector = new JComboBox<>();
+ inBetweenPanel.add(this.dimensionSelector, BorderLayout.PAGE_START);
+ this.dimensionSelector
+ .addItemListener(e -> this.presenter.applyDimensionFilter());
+
+ final JLabel arrowLabel = new JLabel("-->");
+ inBetweenPanel.add(arrowLabel, BorderLayout.CENTER);
+ arrowLabel.setHorizontalAlignment(SwingConstants.CENTER);
+
+ this.toSearch = new SearchBoxList<>();
+ inputPanel.add(this.toSearch);
+ }
+
+ { // panel for submit and output, and also value entry
+ final JPanel outputPanel = new JPanel();
+ convertUnitPanel.add(outputPanel, BorderLayout.PAGE_END);
+ outputPanel.setLayout(new BorderLayout());
+ outputPanel.setBorder(new EmptyBorder(3, 6, 6, 6));
+
+ final JLabel valuePrompt = new JLabel("Value to convert: ");
+ outputPanel.add(valuePrompt, BorderLayout.LINE_START);
+
+ this.valueInput = new JFormattedTextField(NUMBER_FORMATTER);
+ outputPanel.add(this.valueInput, BorderLayout.CENTER);
+
+ // conversion button
+ final JButton convertButton = new JButton("Convert");
+ outputPanel.add(convertButton, BorderLayout.LINE_END);
+ convertButton.addActionListener(e -> this.presenter.convertUnits());
+ convertButton.setMnemonic(KeyEvent.VK_ENTER);
+
+ // conversion output
+ this.unitOutput = new JTextArea(2, 32);
+ outputPanel.add(this.unitOutput, BorderLayout.PAGE_END);
+ this.unitOutput.setEditable(false);
+ }
+
+ // ============ EXPRESSION CONVERSION TAB ============
+ final JPanel convertExpressionPanel = new JPanel();
+ this.masterPane.addTab("Convert Unit Expressions",
+ convertExpressionPanel);
+ this.masterPane.setMnemonicAt(1, KeyEvent.VK_E);
+ convertExpressionPanel.setLayout(new GridLayout(4, 1));
+
+ // from and to expressions
+ this.fromEntry = new JTextField();
+ convertExpressionPanel.add(this.fromEntry);
+ this.fromEntry.setBorder(BorderFactory.createTitledBorder("From"));
+
+ this.toEntry = new JTextField();
+ convertExpressionPanel.add(this.toEntry);
+ this.toEntry.setBorder(BorderFactory.createTitledBorder("To"));
+
+ // button to convert
+ final JButton convertButton = new JButton("Convert");
+ convertExpressionPanel.add(convertButton);
+
+ convertButton.addActionListener(e -> this.presenter.convertExpressions());
+ convertButton.setMnemonic(KeyEvent.VK_ENTER);
+
+ // output of conversion
+ this.expressionOutput = new JTextArea(2, 32);
+ convertExpressionPanel.add(this.expressionOutput);
+ this.expressionOutput
+ .setBorder(BorderFactory.createTitledBorder("Output"));
+ this.expressionOutput.setEditable(false);
+
+ // =========== UNIT VIEWER ===========
+ final JPanel unitLookupPanel = new JPanel();
+ this.masterPane.addTab("Unit Viewer", unitLookupPanel);
+ this.masterPane.setMnemonicAt(2, KeyEvent.VK_V);
+ unitLookupPanel.setLayout(new GridLayout());
+
+ this.unitNameList = new SearchBoxList<>();
+ unitLookupPanel.add(this.unitNameList);
+ this.unitNameList.getSearchList()
+ .addListSelectionListener(e -> this.presenter.unitNameSelected());
+
+ // the text box for unit's toString
+ this.unitTextBox = new JTextArea();
+ unitLookupPanel.add(this.unitTextBox);
+ this.unitTextBox.setEditable(false);
+ this.unitTextBox.setLineWrap(true);
+
+ // ============ PREFIX VIEWER =============
+ final JPanel prefixLookupPanel = new JPanel();
+ this.masterPane.addTab("Prefix Viewer", prefixLookupPanel);
+ this.masterPane.setMnemonicAt(3, KeyEvent.VK_P);
+ prefixLookupPanel.setLayout(new GridLayout(1, 2));
+
+ this.prefixNameList = new SearchBoxList<>();
+ prefixLookupPanel.add(this.prefixNameList);
+ this.prefixNameList.getSearchList()
+ .addListSelectionListener(e -> this.presenter.prefixSelected());
+
+ // the text box for prefix's toString
+ this.prefixTextBox = new JTextArea();
+ prefixLookupPanel.add(this.prefixTextBox);
+ this.prefixTextBox.setEditable(false);
+ this.prefixTextBox.setLineWrap(true);
+
+ final JPanel infoPanel = new JPanel();
+ this.masterPane.addTab("\uD83D\uDEC8", // info (i) character
+ new JScrollPane(infoPanel));
+
+ final JTextArea infoTextArea = new JTextArea();
+ infoTextArea.setEditable(false);
+ infoTextArea.setOpaque(false);
+ infoPanel.add(infoTextArea);
+ infoTextArea.setText(Presenter.getAboutText());
+
+ // ============ SETTINGS PANEL ============
+ this.masterPane.addTab("\u2699",
+ new JScrollPane(this.createSettingsPanel()));
+ this.masterPane.setMnemonicAt(5, KeyEvent.VK_S);
+
+ // ============ FINALIZE CREATION OF VIEW ============
+ this.frame.pack();
+ this.frame.setVisible(true);
+
+ }
+
+ /**
+ * Creates and returns the settings panel (in its own function to make this
+ * code more organized, as this function is massive!)
+ *
+ * @since 2022-02-19
+ */
+ private JPanel createSettingsPanel() {
+ final JPanel settingsPanel = new JPanel();
+
+ settingsPanel
+ .setLayout(new BoxLayout(settingsPanel, BoxLayout.PAGE_AXIS));
+
+ // ============ ROUNDING SETTINGS ============
+ {
+ final JPanel roundingPanel = new JPanel();
+ settingsPanel.add(roundingPanel);
+ roundingPanel.setBorder(new TitledBorder("Rounding Settings"));
+ roundingPanel.setLayout(new GridBagLayout());
+
+ // rounding rule selection
+ final ButtonGroup roundingRuleButtons = new ButtonGroup();
+
+ final JLabel roundingRuleLabel = new JLabel("Rounding Rule:");
+ roundingPanel.add(roundingRuleLabel, new GridBagBuilder(0, 0)
+ .setAnchor(GridBagConstraints.LINE_START).build());
+
+ 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));
+ roundingRuleButtons.add(fixedPrecision);
+ roundingPanel.add(fixedPrecision, new GridBagBuilder(0, 1)
+ .setAnchor(GridBagConstraints.LINE_START).build());
+
+ 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));
+ roundingRuleButtons.add(fixedDecimals);
+ roundingPanel.add(fixedDecimals, new GridBagBuilder(0, 2)
+ .setAnchor(GridBagConstraints.LINE_START).build());
+
+ final JRadioButton relativePrecision = new JRadioButton(
+ "Scientific Precision");
+// if (this.presenter.roundingType == RoundingType.SCIENTIFIC) {
+// relativePrecision.setSelected(true);
+// }
+// relativePrecision.addActionListener(
+// e -> this.presenter.setRoundingType(RoundingType.SCIENTIFIC));
+ 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 ============
+ {
+ final JPanel prefixRepetitionPanel = new JPanel();
+ settingsPanel.add(prefixRepetitionPanel);
+ prefixRepetitionPanel
+ .setBorder(new TitledBorder("Prefix Repetition Settings"));
+ prefixRepetitionPanel.setLayout(new GridBagLayout());
+
+ // prefix rules
+ final ButtonGroup prefixRuleButtons = new ButtonGroup();
+
+ final JRadioButton noRepetition = new JRadioButton("No Repetition");
+// if (this.presenter.prefixRule == DefaultPrefixRepetitionRule.NO_REPETITION) {
+// noRepetition.setSelected(true);
+// }
+// noRepetition
+// .addActionListener(e -> this.presenter.setPrefixRepetitionRule(
+// DefaultPrefixRepetitionRule.NO_REPETITION));
+ prefixRuleButtons.add(noRepetition);
+ prefixRepetitionPanel.add(noRepetition, new GridBagBuilder(0, 0)
+ .setAnchor(GridBagConstraints.LINE_START).build());
+
+ final JRadioButton noRestriction = new JRadioButton("No Restriction");
+// if (this.presenter.prefixRule == DefaultPrefixRepetitionRule.NO_RESTRICTION) {
+// noRestriction.setSelected(true);
+// }
+// noRestriction
+// .addActionListener(e -> this.presenter.setPrefixRepetitionRule(
+// DefaultPrefixRepetitionRule.NO_RESTRICTION));
+ prefixRuleButtons.add(noRestriction);
+ prefixRepetitionPanel.add(noRestriction, new GridBagBuilder(0, 1)
+ .setAnchor(GridBagConstraints.LINE_START).build());
+
+ final JRadioButton customRepetition = new JRadioButton(
+ "Complex Repetition");
+// if (this.presenter.prefixRule == DefaultPrefixRepetitionRule.COMPLEX_REPETITION) {
+// customRepetition.setSelected(true);
+// }
+// customRepetition
+// .addActionListener(e -> this.presenter.setPrefixRepetitionRule(
+// DefaultPrefixRepetitionRule.COMPLEX_REPETITION));
+ prefixRuleButtons.add(customRepetition);
+ prefixRepetitionPanel.add(customRepetition, new GridBagBuilder(0, 2)
+ .setAnchor(GridBagConstraints.LINE_START).build());
+ }
+
+ // ============ SEARCH SETTINGS ============
+ {
+ final JPanel searchingPanel = new JPanel();
+ settingsPanel.add(searchingPanel);
+ searchingPanel.setBorder(new TitledBorder("Search Settings"));
+ searchingPanel.setLayout(new GridBagLayout());
+
+ // searching rules
+ final ButtonGroup searchRuleButtons = new ButtonGroup();
+
+ final JRadioButton noPrefixes = new JRadioButton(
+ "Never Include Prefixed Units");
+ noPrefixes.setEnabled(false);
+ searchRuleButtons.add(noPrefixes);
+ searchingPanel.add(noPrefixes, new GridBagBuilder(0, 0)
+ .setAnchor(GridBagConstraints.LINE_START).build());
+
+ final JRadioButton fixedPrefixes = new JRadioButton(
+ "Include Some Prefixes");
+ fixedPrefixes.setEnabled(false);
+ searchRuleButtons.add(fixedPrefixes);
+ searchingPanel.add(fixedPrefixes, new GridBagBuilder(0, 1)
+ .setAnchor(GridBagConstraints.LINE_START).build());
+
+ final JRadioButton explicitPrefixes = new JRadioButton(
+ "Include Explicit Prefixes");
+ explicitPrefixes.setEnabled(false);
+ searchRuleButtons.add(explicitPrefixes);
+ searchingPanel.add(explicitPrefixes, new GridBagBuilder(0, 2)
+ .setAnchor(GridBagConstraints.LINE_START).build());
+
+ final JRadioButton alwaysInclude = new JRadioButton(
+ "Include All Single Prefixes");
+ alwaysInclude.setEnabled(false);
+ searchRuleButtons.add(alwaysInclude);
+ searchingPanel.add(alwaysInclude, new GridBagBuilder(0, 3)
+ .setAnchor(GridBagConstraints.LINE_START).build());
+ }
+
+ // ============ OTHER SETTINGS ============
+ {
+ final JPanel miscPanel = new JPanel();
+ settingsPanel.add(miscPanel);
+ 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));
+ 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));
+ miscPanel.add(showAllVariations, new GridBagBuilder(0, 1)
+ .setAnchor(GridBagConstraints.LINE_START).build());
+
+ final JButton unitFileButton = new JButton("Manage Unit Data Files");
+ unitFileButton.setEnabled(false);
+ miscPanel.add(unitFileButton, new GridBagBuilder(0, 2)
+ .setAnchor(GridBagConstraints.LINE_START).build());
+ }
+
+ return settingsPanel;
+ }
+
+ @Override
+ public Set<NamedObjectProduct<BaseDimension>> getDimensions() {
+ return Collections
+ .unmodifiableSet(new JComboBoxItemSet<>(this.dimensionSelector));
+ }
+
+ @Override
+ public String getFromExpression() {
+ return this.fromEntry.getText();
+ }
+
+ @Override
+ public Optional<Unit> getFromSelection() {
+ return this.fromSearch.getSelectedValue();
+ }
+
+ @Override
+ public String getInputValue() {
+ return this.valueInput.getText();
+ }
+
+ @Override
+ public Optional<? extends ObjectProduct<BaseDimension>> getSelectedDimension() {
+ // this must work because this function can only return items that are in
+ // the selector, which are all of type ObjectProduct<BaseDimension>
+ @SuppressWarnings("unchecked")
+ final ObjectProduct<BaseDimension> selectedItem = (ObjectProduct<BaseDimension>) this.dimensionSelector
+ .getSelectedItem();
+ return Optional.ofNullable(selectedItem);
+ }
+
+ @Override
+ public String getToExpression() {
+ return this.toEntry.getText();
+ }
+
+ @Override
+ public Optional<Unit> getToSelection() {
+ return this.toSearch.getSelectedValue();
+ }
+
+ @Override
+ public void setDimensions(
+ Set<NamedObjectProduct<BaseDimension>> dimensions) {
+ this.dimensionSelector.removeAllItems();
+ for (final NamedObjectProduct<BaseDimension> d : dimensions) {
+ this.dimensionSelector.addItem(d);
+ }
+ }
+
+ @Override
+ public void setFromUnits(Set<? extends Unit> units) {
+ this.fromSearch.setItems(units);
+ }
+
+ @Override
+ public void setToUnits(Set<? extends Unit> units) {
+ this.toSearch.setItems(units);
+ }
+
+ @Override
+ public void showErrorMessage(String title, String message) {
+ JOptionPane.showMessageDialog(this.frame, message, title,
+ JOptionPane.ERROR_MESSAGE);
+ }
+
+ @Override
+ public void showExpressionConversionOutput(String fromExpression,
+ String toExpression, double value) {
+ this.expressionOutput.setText(
+ String.format("%s = %s %s", fromExpression, value, toExpression));
+ }
+
+ @Override
+ public void showUnitConversionOutput(String outputString) {
+ this.unitOutput.setText(outputString);
+ }
+
+}