Skip to content

Commit

Permalink
Change source selector to a dropdown. (#631)
Browse files Browse the repository at this point in the history
* Change source selector to a dropdown.

* Rename AddSourceView -> AddSourceButton
  • Loading branch information
AustinShalit authored and JLLeitschuh committed Jul 28, 2016
1 parent cc2a889 commit 58635d3
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,23 @@
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Predicate;

import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBar;
import javafx.scene.control.ButtonType;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Control;
import javafx.scene.control.Dialog;
import javafx.scene.control.MenuButton;
import javafx.scene.control.MenuItem;
import javafx.scene.control.Spinner;
import javafx.scene.control.TextField;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment;
import javafx.stage.FileChooser;
import javafx.stage.FileChooser.ExtensionFilter;

Expand All @@ -52,29 +50,28 @@
* construct that source. As an example, the image file source results in a file picker that the
* user can use to browse for an image.
*/
public class AddSourceView extends HBox {
public class AddSourceButton extends MenuButton {

@VisibleForTesting
static final String SOURCE_DIALOG_STYLE_CLASS = "source-dialog";
private final EventBus eventBus;

private final Button webcamButton;
private final Button ipcamButton;
private final MenuItem webcamButton;
private final MenuItem ipcamButton;
private final MenuItem httpButton;
private Optional<Dialog> activeDialog = Optional.empty();

@Inject
AddSourceView(EventBus eventBus,
AddSourceButton(EventBus eventBus,
MultiImageFileSource.Factory multiImageSourceFactory,
ImageFileSource.Factory imageSourceFactory,
CameraSource.Factory cameraSourceFactory,
HttpSource.Factory httpSourceFactory) {
super("Add Source");
this.eventBus = eventBus;

this.setFillHeight(true);

addButton("Add\nImage(s)",
getClass().getResource("/edu/wpi/grip/ui/icons/add-image.png"),
mouseEvent -> {

addMenuItem("Image(s)",
getClass().getResource("/edu/wpi/grip/ui/icons/add-image.png"), mouseEvent -> {
// Show a file picker so the user can open one or more images from disk
final FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Open an image");
Expand Down Expand Up @@ -120,14 +117,12 @@ public class AddSourceView extends HBox {
}
});

webcamButton = addButton(
"Add\nWebcam",
getClass().getResource("/edu/wpi/grip/ui/icons/add-webcam.png"),
mouseEvent -> {
webcamButton = addMenuItem("Webcam",
getClass().getResource("/edu/wpi/grip/ui/icons/add-webcam.png"), mouseEvent -> {
final Parent root = this.getScene().getRoot();

// Show a dialog for the user to pick a camera index
final Spinner<Integer> cameraIndex = new Spinner<Integer>(0, Integer.MAX_VALUE, 0);
final Spinner<Integer> cameraIndex = new Spinner<>(0, Integer.MAX_VALUE, 0);
final SourceDialog dialog = new SourceDialog(root, cameraIndex);

dialog.setTitle("Add Webcam");
Expand All @@ -144,15 +139,11 @@ public class AddSourceView extends HBox {
cameraSource.initialize();
return cameraSource;
},
e -> {
dialog.errorText.setText(e.getMessage());
});
e -> dialog.errorText.setText(e.getMessage()));
});

ipcamButton = addButton(
"Add IP\nCamera",
getClass().getResource("/edu/wpi/grip/ui/icons/add-webcam.png"),
mouseEvent -> {
ipcamButton = addMenuItem("IP Camera",
getClass().getResource("/edu/wpi/grip/ui/icons/add-webcam.png"), mouseEvent -> {
final Parent root = this.getScene().getRoot();

// Show a dialog for the user to pick a camera URL
Expand Down Expand Up @@ -197,8 +188,8 @@ public class AddSourceView extends HBox {
e -> dialog.errorText.setText(e.getMessage()));
});

addButton("Add\nHTTP source", getClass().getResource("/edu/wpi/grip/ui/icons/publish.png"),
mouseEvent -> {
httpButton = addMenuItem("HTTP",
getClass().getResource("/edu/wpi/grip/ui/icons/publish.png"), mouseEvent -> {
final Parent root = this.getScene().getRoot();
// Show a dialog to pick the server path images will be uploaded on
final String imageRoot = GripServer.IMAGE_UPLOAD_PATH + "/";
Expand Down Expand Up @@ -252,31 +243,34 @@ private void loadCamera(Dialog<ButtonType> dialog, SupplierWithIO<CameraSource>
/**
* Add a new button for adding a source. This method takes care of setting the event handler.
*/
private Button addButton(String text, URL graphicURL, EventHandler<? super MouseEvent>
onMouseClicked) {
private MenuItem addMenuItem(String text, URL graphicURL, EventHandler<ActionEvent>
onActionEvent) {
final ImageView graphic = new ImageView(graphicURL.toString());
graphic.setFitWidth(DPIUtility.SMALL_ICON_SIZE);
graphic.setFitHeight(DPIUtility.SMALL_ICON_SIZE);

final Button button = new Button(text, graphic);
button.setTextAlignment(TextAlignment.CENTER);
button.setContentDisplay(ContentDisplay.TOP);
button.setOnMouseClicked(onMouseClicked);
final MenuItem menuItem = new MenuItem(" " + text, graphic);
menuItem.setOnAction(onActionEvent);

this.getChildren().add(button);
return button;
getItems().add(menuItem);
return menuItem;
}

@VisibleForTesting
Button getWebcamButton() {
MenuItem getWebcamButton() {
return webcamButton;
}

@VisibleForTesting
Button getIpcamButton() {
MenuItem getIpcamButton() {
return ipcamButton;
}

@VisibleForTesting
MenuItem getHttpButton() {
return httpButton;
}

@VisibleForTesting
void closeDialogs() {
activeDialog.ifPresent(dialog -> {
Expand All @@ -291,7 +285,7 @@ void closeDialogs() {
}

public interface Factory {
AddSourceView create();
AddSourceButton create();
}

private static class SourceDialog extends Dialog<ButtonType> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
import java.util.NavigableMap;
import java.util.TreeMap;
import java.util.stream.Collectors;

import javafx.beans.InvalidationListener;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.collections.ObservableList;
Expand All @@ -43,9 +42,7 @@
import javafx.scene.Parent;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;

import javax.annotation.Nullable;
import javax.inject.Inject;

Expand All @@ -61,7 +58,7 @@ public final class PipelineController {
@FXML
private VBox sourcesBox;
@FXML
private Pane addSourcePane;
private VBox addSourceBox;
@FXML
private HBox stepBox;
@FXML
Expand All @@ -78,7 +75,7 @@ public final class PipelineController {
@Inject
private StepController.Factory stepControllerFactory;
@Inject
private AddSourceView addSourceView;
private AddSourceButton addSourceButton;
@Inject
private OperationDragService operationDragService;
@Inject
Expand Down Expand Up @@ -135,7 +132,7 @@ public void initialize() throws Exception {
});
});

addSourcePane.getChildren().add(addSourceView);
addSourceBox.getChildren().add(addSourceButton);
}

/**
Expand Down
9 changes: 9 additions & 0 deletions ui/src/main/resources/edu/wpi/grip/ui/GRIP.css
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,15 @@ Label.operation-description {
-fx-padding: 0.5em;
}

.addSource {
-fx-padding: 0.5em;
-fx-spacing: 1em;
}

.addSource * {
-fx-max-width: Infinity;
}

.sources {
-fx-padding: 0.5em;
-fx-spacing: 1em;
Expand Down
3 changes: 1 addition & 2 deletions ui/src/main/resources/edu/wpi/grip/ui/pipeline/Pipeline.fxml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
<?import javafx.scene.control.Separator?>
<?import javafx.scene.Group?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.shape.Rectangle?>
Expand All @@ -21,7 +20,7 @@
</maxWidth>
<Label maxWidth="Infinity" styleClass="pane-title" text="Sources" />
<Separator orientation="HORIZONTAL" />
<Pane fx:id="addSourcePane" />
<VBox fx:id="addSourceBox" styleClass="addSource" />
<VBox fx:id="sourcesBox" styleClass="sources" />
</VBox>
<Separator orientation="VERTICAL" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import java.io.IOException;
import java.util.Optional;
import java.util.Properties;

import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.stage.Stage;

Expand All @@ -26,23 +26,23 @@
import static org.testfx.api.FxAssert.verifyThat;

@RunWith(Enclosed.class)
public class AddSourceViewTest {
public class AddSourceButtonTest {

/**
* Tests what happens when a source is created and started successfully
*/
public static class AddSourceViewNoExceptionsTest extends ApplicationTest {

private EventBus eventBus;
private AddSourceView addSourceView;
private AddSourceButton addSourceView;
private MockCameraSourceFactory mockCameraSourceFactory;

@Override
public void start(Stage stage) {
this.eventBus = new EventBus("Test Event Bus");
this.mockCameraSourceFactory = new MockCameraSourceFactory(eventBus);

addSourceView = new AddSourceView(eventBus, null, null, mockCameraSourceFactory, null);
addSourceView = new AddSourceButton(eventBus, null, null, mockCameraSourceFactory, null);

final Scene scene = new Scene(addSourceView, 800, 600);
stage.setScene(scene);
Expand All @@ -51,32 +51,32 @@ public void start(Stage stage) {

@After
public void after() {
// Ensuer that all of the dialogs that were created get closed afterward.
// Ensure that all of the dialogs that were created get closed afterward.
addSourceView.closeDialogs();
}

@Test
@SuppressWarnings("PMD.JUnitTestsShouldIncludeAssert")
public void testClickOnCreateWebCameraOpensDialog() throws Exception {
clickOn(addSourceView.getWebcamButton());
Platform.runLater(() -> addSourceView.getWebcamButton().fire());
WaitForAsyncUtils.waitForFxEvents();
verifyThat("." + AddSourceView.SOURCE_DIALOG_STYLE_CLASS, NodeMatchers.isVisible());
verifyThat('.' + AddSourceButton.SOURCE_DIALOG_STYLE_CLASS, NodeMatchers.isVisible());
}

@Test
@SuppressWarnings("PMD.JUnitTestsShouldIncludeAssert")
public void testClickOnCreateIPCameraOpensDialog() throws Exception {
clickOn(addSourceView.getIpcamButton());
Platform.runLater(() -> addSourceView.getIpcamButton().fire());
WaitForAsyncUtils.waitForFxEvents();
verifyThat("." + AddSourceView.SOURCE_DIALOG_STYLE_CLASS, NodeMatchers.isVisible());
verifyThat("." + AddSourceButton.SOURCE_DIALOG_STYLE_CLASS, NodeMatchers.isVisible());
}

@Test
public void testCreatesSourceStarted() throws Exception {
// When
clickOn(addSourceView.getWebcamButton());
Platform.runLater(() -> addSourceView.getWebcamButton().fire());
WaitForAsyncUtils.waitForFxEvents();
verifyThat("." + AddSourceView.SOURCE_DIALOG_STYLE_CLASS, NodeMatchers.isVisible());
verifyThat("." + AddSourceButton.SOURCE_DIALOG_STYLE_CLASS, NodeMatchers.isVisible());

clickOn("OK");
WaitForAsyncUtils.waitForFxEvents();
Expand All @@ -85,7 +85,7 @@ public void testCreatesSourceStarted() throws Exception {
Optional<CameraSource> cameraSource = mockCameraSourceFactory.lastSourceCreated;
assertTrue("A source was not constructed", cameraSource.isPresent());
assertTrue("A source was not created started", cameraSource.get().isRunning());
verifyThat("." + AddSourceView.SOURCE_DIALOG_STYLE_CLASS, NodeMatchers.isNull());
verifyThat("." + AddSourceButton.SOURCE_DIALOG_STYLE_CLASS, NodeMatchers.isNull());
}

class MockCameraSourceFactory implements CameraSource.Factory {
Expand Down Expand Up @@ -123,15 +123,15 @@ private MockCameraSource assignLastCreated(MockCameraSource source) {
*/
public static class AddSourceViewWithExceptionsTest extends ApplicationTest {
private EventBus eventBus;
private AddSourceView addSourceView;
private AddSourceButton addSourceView;
private MockCameraSourceFactory mockCameraSourceFactory;

@Override
public void start(Stage stage) {
this.eventBus = new EventBus("Test Event Bus");
this.mockCameraSourceFactory = new MockCameraSourceFactory(eventBus);

addSourceView = new AddSourceView(eventBus, null, null, mockCameraSourceFactory, null);
addSourceView = new AddSourceButton(eventBus, null, null, mockCameraSourceFactory, null);

final Scene scene = new Scene(addSourceView, 800, 600);
stage.setScene(scene);
Expand All @@ -140,31 +140,31 @@ public void start(Stage stage) {

@After
public void after() {
// Ensuer that all of the dialogs that were created get closed afterward.
// Ensure that all of the dialogs that were created get closed afterward.
addSourceView.closeDialogs();
}

@Test
@SuppressWarnings("PMD.JUnitTestsShouldIncludeAssert")
public void testWhenStartFailsDialogStillCloses() throws Exception {
// When
clickOn(addSourceView.getWebcamButton());
Platform.runLater(() -> addSourceView.getWebcamButton().fire());
WaitForAsyncUtils.waitForFxEvents();

clickOn("OK");

WaitForAsyncUtils.waitForFxEvents();

// The dialog should not have closed because the source wasn't started
verifyThat("." + AddSourceView.SOURCE_DIALOG_STYLE_CLASS, NodeMatchers.isNull());
verifyThat("." + AddSourceButton.SOURCE_DIALOG_STYLE_CLASS, NodeMatchers.isNull());
}

@Test
public void testCreatesSourceStartedFails() throws Exception {
// When
clickOn(addSourceView.getWebcamButton());
Platform.runLater(() -> addSourceView.getWebcamButton().fire());
WaitForAsyncUtils.waitForFxEvents();
verifyThat("." + AddSourceView.SOURCE_DIALOG_STYLE_CLASS, NodeMatchers.isVisible());
verifyThat("." + AddSourceButton.SOURCE_DIALOG_STYLE_CLASS, NodeMatchers.isVisible());

clickOn("OK");
WaitForAsyncUtils.waitForFxEvents();
Expand Down

0 comments on commit 58635d3

Please sign in to comment.