/******************************************************************************* * Copyright (c) 2013, Daniel Murphy All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted * provided that the following conditions are met: * Redistributions of source code must retain the * above copyright notice, this list of conditions and the following disclaimer. * Redistributions * in binary form must reproduce the above copyright notice, this list of conditions and the * following disclaimer in the documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ package org.jbox2d.testbed.framework.javafx; import java.util.HashMap; import java.util.Map; import javax.swing.ComboBoxModel; import javax.swing.DefaultComboBoxModel; import javax.swing.event.ListDataEvent; import javax.swing.event.ListDataListener; import org.jbox2d.testbed.framework.AbstractTestbedController; import org.jbox2d.testbed.framework.TestbedModel; import org.jbox2d.testbed.framework.TestbedModel.ListItem; import org.jbox2d.testbed.framework.TestbedSetting; import org.jbox2d.testbed.framework.TestbedSetting.SettingType; import org.jbox2d.testbed.framework.TestbedSettings; import org.jbox2d.testbed.framework.TestbedTest; import javafx.collections.ObservableList; import javafx.geometry.Pos; import javafx.scene.control.Button; import javafx.scene.control.CheckBox; import javafx.scene.control.ComboBox; import javafx.scene.control.Control; import javafx.scene.control.Label; import javafx.scene.control.Slider; import javafx.scene.control.cell.ComboBoxListCell; import javafx.scene.layout.BorderPane; import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Pane; import javafx.scene.layout.VBox; import javafx.util.StringConverter; /** * The testbed side panel. Facilitates test and setting changes. * * @author Daniel Murphy */ @SuppressWarnings("serial") public class TestbedSidePanel extends BorderPane { private static final String SETTING_TAG = "settings"; private static final String LABEL_TAG = "label"; final TestbedModel model; final AbstractTestbedController controller; public ComboBox<ListItem> tests; private Button pauseButton = new Button("Pause"); private Button stepButton = new Button("Step"); private Button resetButton = new Button("Reset"); private Button quitButton = new Button("Quit"); public Button saveButton = new Button("Save"); public Button loadButton = new Button("Load"); public TestbedSidePanel(TestbedModel argModel, AbstractTestbedController argController) { model = argModel; controller = argController; initComponents(); addListeners(); model.addTestChangeListener(new TestbedModel.TestChangedListener() { @Override public void testChanged(TestbedTest argTest, int argIndex) { tests.getSelectionModel().select(argIndex); saveButton.setDisable(!argTest.isSaveLoadEnabled()); loadButton.setDisable(!argTest.isSaveLoadEnabled()); } }); } private void updateTests(ComboBoxModel<ListItem> model) { ObservableList<ListItem> list = tests.itemsProperty().get(); list.clear(); for (int i = 0; i < model.getSize(); i++) { list.add((ListItem) model.getElementAt(i)); } } public void initComponents() { // setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); TestbedSettings settings = model.getSettings(); VBox top = new VBox(); // top.setLayout(new GridLayout(0, 1)); // top.setBorder(BorderFactory.createCompoundBorder(new EtchedBorder(EtchedBorder.LOWERED), // BorderFactory.createEmptyBorder(10, 10, 10, 10))); DefaultComboBoxModel testList = model.getComboModel(); testList.addListDataListener(new ListDataListener() { @Override public void intervalRemoved(ListDataEvent e) { updateTests((ComboBoxModel<ListItem>) e.getSource()); } @Override public void intervalAdded(ListDataEvent e) { updateTests((ComboBoxModel<ListItem>) e.getSource()); } @Override public void contentsChanged(ListDataEvent e) { updateTests((ComboBoxModel<ListItem>) e.getSource()); } }); tests = new ComboBox<ListItem>(); updateTests((ComboBoxModel<ListItem>) testList); tests.setOnAction((actionEvent) -> { testSelected(); }); tests.setCellFactory(ComboBoxListCell.<ListItem>forListView(new StringConverter<ListItem>() { @Override public String toString(ListItem listItem) { if (listItem == null) { return (""); } else if (listItem.isCategory()) { return (listItem.category); } else { return (listItem.test.getTestName()); } } @Override public ListItem fromString(String string) { return null; } }, tests.getItems())); top.getChildren().add(new Label("Choose a test:")); top.getChildren().add(tests); addSettings(top, settings, SettingType.DRAWING); setTop(top); VBox middle = new VBox(); // middle.setLayout(new GridLayout(0, 1)); // middle.setBorder(BorderFactory.createCompoundBorder(new EtchedBorder(EtchedBorder.LOWERED), // BorderFactory.createEmptyBorder(5, 10, 5, 10))); addSettings(middle, settings, SettingType.ENGINE); setCenter(middle); pauseButton.setAlignment(Pos.CENTER); stepButton.setAlignment(Pos.CENTER); resetButton.setAlignment(Pos.CENTER); saveButton.setAlignment(Pos.CENTER); loadButton.setAlignment(Pos.CENTER); quitButton.setAlignment(Pos.CENTER); HBox buttonGroups = new HBox(); VBox buttons1 = new VBox(); buttons1.getChildren().add(resetButton); VBox buttons2 = new VBox(); buttons2.getChildren().add(pauseButton); buttons2.getChildren().add(stepButton); VBox buttons3 = new VBox(); buttons3.getChildren().add(saveButton); buttons3.getChildren().add(loadButton); buttons3.getChildren().add(quitButton); buttonGroups.getChildren().add(buttons1); buttonGroups.getChildren().add(buttons2); buttonGroups.getChildren().add(buttons3); setBottom(buttonGroups); } protected void testSelected() { int testNum = tests.getSelectionModel().getSelectedIndex(); controller.playTest(testNum); } public void addListeners() { pauseButton.setOnAction((e) -> { if (model.getSettings().pause) { model.getSettings().pause = false; pauseButton.setText("Pause"); } else { model.getSettings().pause = true; pauseButton.setText("Resume"); } model.getPanel().grabFocus(); }); stepButton.setOnAction((e) -> { model.getSettings().singleStep = true; if (!model.getSettings().pause) { model.getSettings().pause = true; pauseButton.setText("Resume"); } model.getPanel().grabFocus(); }); resetButton.setOnAction((e) -> { controller.reset(); }); quitButton.setOnAction((e) -> { System.exit(0); }); saveButton.setOnAction((e) -> { controller.save(); }); loadButton.setOnAction((e) -> { controller.load(); }); } private void addSettings(Pane argPanel, TestbedSettings argSettings, SettingType argIgnore) { for (TestbedSetting setting : argSettings.getSettings()) { if (setting.settingsType == argIgnore) { continue; } switch (setting.constraintType) { case RANGE: Label text = new Label(setting.name + ": " + setting.value); Slider slider = new Slider(setting.min, setting.max, setting.value); // slider.setMaximumSize(new Dimension(200, 20)); slider.valueProperty().addListener((prop, oldValue, newValue) -> { stateChanged(slider); }); putClientProperty(slider, "name", setting.name); putClientProperty(slider, SETTING_TAG, setting); putClientProperty(slider, LABEL_TAG, text); argPanel.getChildren().add(text); argPanel.getChildren().add(slider); break; case BOOLEAN: CheckBox checkbox = new CheckBox(setting.name); checkbox.setSelected(setting.enabled); checkbox.selectedProperty().addListener((prop, oldValue, newValue) -> { stateChanged(checkbox); }); putClientProperty(checkbox, SETTING_TAG, setting); argPanel.getChildren().add(checkbox); break; } } } private <T> T getClientProperty(Control control, String tag) { Map<String, Object> map = (Map<String, Object>) control.getUserData(); return (map != null ? (T) map.get(tag) : null); } private void putClientProperty(Control control, String tag, Object o) { Map<String, Object> map = (Map<String, Object>) control.getUserData(); if (map == null) { map = new HashMap<>(); control.setUserData(map); } map.put(tag, o); } public void stateChanged(Control control) { TestbedSetting setting = getClientProperty(control, SETTING_TAG); switch (setting.constraintType) { case BOOLEAN: CheckBox box = (CheckBox) control; setting.enabled = box.isSelected(); break; case RANGE: Slider slider = (Slider) control; setting.value = (int) slider.getValue(); Label label = getClientProperty(slider, LABEL_TAG); label.setText(setting.name + ": " + setting.value); break; } model.getPanel().grabFocus(); } }