Skip to content

Commit 58635d3

Browse files
AustinShalitJLLeitschuh
authored andcommitted
Change source selector to a dropdown. (#631)
* Change source selector to a dropdown. * Rename AddSourceView -> AddSourceButton
1 parent cc2a889 commit 58635d3

File tree

5 files changed

+66
-67
lines changed

5 files changed

+66
-67
lines changed

ui/src/main/java/edu/wpi/grip/ui/pipeline/AddSourceView.java renamed to ui/src/main/java/edu/wpi/grip/ui/pipeline/AddSourceButton.java

Lines changed: 34 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -24,25 +24,23 @@
2424
import java.util.Optional;
2525
import java.util.function.Consumer;
2626
import java.util.function.Predicate;
27-
2827
import javafx.application.Platform;
28+
import javafx.event.ActionEvent;
2929
import javafx.event.EventHandler;
3030
import javafx.scene.Parent;
3131
import javafx.scene.control.Button;
3232
import javafx.scene.control.ButtonBar;
3333
import javafx.scene.control.ButtonType;
34-
import javafx.scene.control.ContentDisplay;
3534
import javafx.scene.control.Control;
3635
import javafx.scene.control.Dialog;
36+
import javafx.scene.control.MenuButton;
37+
import javafx.scene.control.MenuItem;
3738
import javafx.scene.control.Spinner;
3839
import javafx.scene.control.TextField;
3940
import javafx.scene.image.ImageView;
40-
import javafx.scene.input.MouseEvent;
4141
import javafx.scene.layout.GridPane;
42-
import javafx.scene.layout.HBox;
4342
import javafx.scene.layout.Priority;
4443
import javafx.scene.text.Text;
45-
import javafx.scene.text.TextAlignment;
4644
import javafx.stage.FileChooser;
4745
import javafx.stage.FileChooser.ExtensionFilter;
4846

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

5755
@VisibleForTesting
5856
static final String SOURCE_DIALOG_STYLE_CLASS = "source-dialog";
5957
private final EventBus eventBus;
6058

61-
private final Button webcamButton;
62-
private final Button ipcamButton;
59+
private final MenuItem webcamButton;
60+
private final MenuItem ipcamButton;
61+
private final MenuItem httpButton;
6362
private Optional<Dialog> activeDialog = Optional.empty();
6463

6564
@Inject
66-
AddSourceView(EventBus eventBus,
65+
AddSourceButton(EventBus eventBus,
6766
MultiImageFileSource.Factory multiImageSourceFactory,
6867
ImageFileSource.Factory imageSourceFactory,
6968
CameraSource.Factory cameraSourceFactory,
7069
HttpSource.Factory httpSourceFactory) {
70+
super("Add Source");
7171
this.eventBus = eventBus;
72-
73-
this.setFillHeight(true);
74-
75-
addButton("Add\nImage(s)",
76-
getClass().getResource("/edu/wpi/grip/ui/icons/add-image.png"),
77-
mouseEvent -> {
72+
73+
addMenuItem("Image(s)",
74+
getClass().getResource("/edu/wpi/grip/ui/icons/add-image.png"), mouseEvent -> {
7875
// Show a file picker so the user can open one or more images from disk
7976
final FileChooser fileChooser = new FileChooser();
8077
fileChooser.setTitle("Open an image");
@@ -120,14 +117,12 @@ public class AddSourceView extends HBox {
120117
}
121118
});
122119

123-
webcamButton = addButton(
124-
"Add\nWebcam",
125-
getClass().getResource("/edu/wpi/grip/ui/icons/add-webcam.png"),
126-
mouseEvent -> {
120+
webcamButton = addMenuItem("Webcam",
121+
getClass().getResource("/edu/wpi/grip/ui/icons/add-webcam.png"), mouseEvent -> {
127122
final Parent root = this.getScene().getRoot();
128123

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

133128
dialog.setTitle("Add Webcam");
@@ -144,15 +139,11 @@ public class AddSourceView extends HBox {
144139
cameraSource.initialize();
145140
return cameraSource;
146141
},
147-
e -> {
148-
dialog.errorText.setText(e.getMessage());
149-
});
142+
e -> dialog.errorText.setText(e.getMessage()));
150143
});
151144

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

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

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

261-
final Button button = new Button(text, graphic);
262-
button.setTextAlignment(TextAlignment.CENTER);
263-
button.setContentDisplay(ContentDisplay.TOP);
264-
button.setOnMouseClicked(onMouseClicked);
252+
final MenuItem menuItem = new MenuItem(" " + text, graphic);
253+
menuItem.setOnAction(onActionEvent);
265254

266-
this.getChildren().add(button);
267-
return button;
255+
getItems().add(menuItem);
256+
return menuItem;
268257
}
269258

270259
@VisibleForTesting
271-
Button getWebcamButton() {
260+
MenuItem getWebcamButton() {
272261
return webcamButton;
273262
}
274263

275264
@VisibleForTesting
276-
Button getIpcamButton() {
265+
MenuItem getIpcamButton() {
277266
return ipcamButton;
278267
}
279268

269+
@VisibleForTesting
270+
MenuItem getHttpButton() {
271+
return httpButton;
272+
}
273+
280274
@VisibleForTesting
281275
void closeDialogs() {
282276
activeDialog.ifPresent(dialog -> {
@@ -291,7 +285,7 @@ void closeDialogs() {
291285
}
292286

293287
public interface Factory {
294-
AddSourceView create();
288+
AddSourceButton create();
295289
}
296290

297291
private static class SourceDialog extends Dialog<ButtonType> {

ui/src/main/java/edu/wpi/grip/ui/pipeline/PipelineController.java

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
import java.util.NavigableMap;
3232
import java.util.TreeMap;
3333
import java.util.stream.Collectors;
34-
3534
import javafx.beans.InvalidationListener;
3635
import javafx.beans.property.ReadOnlyObjectProperty;
3736
import javafx.collections.ObservableList;
@@ -43,9 +42,7 @@
4342
import javafx.scene.Parent;
4443
import javafx.scene.input.TransferMode;
4544
import javafx.scene.layout.HBox;
46-
import javafx.scene.layout.Pane;
4745
import javafx.scene.layout.VBox;
48-
4946
import javax.annotation.Nullable;
5047
import javax.inject.Inject;
5148

@@ -61,7 +58,7 @@ public final class PipelineController {
6158
@FXML
6259
private VBox sourcesBox;
6360
@FXML
64-
private Pane addSourcePane;
61+
private VBox addSourceBox;
6562
@FXML
6663
private HBox stepBox;
6764
@FXML
@@ -78,7 +75,7 @@ public final class PipelineController {
7875
@Inject
7976
private StepController.Factory stepControllerFactory;
8077
@Inject
81-
private AddSourceView addSourceView;
78+
private AddSourceButton addSourceButton;
8279
@Inject
8380
private OperationDragService operationDragService;
8481
@Inject
@@ -135,7 +132,7 @@ public void initialize() throws Exception {
135132
});
136133
});
137134

138-
addSourcePane.getChildren().add(addSourceView);
135+
addSourceBox.getChildren().add(addSourceButton);
139136
}
140137

141138
/**

ui/src/main/resources/edu/wpi/grip/ui/GRIP.css

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,15 @@ Label.operation-description {
7272
-fx-padding: 0.5em;
7373
}
7474

75+
.addSource {
76+
-fx-padding: 0.5em;
77+
-fx-spacing: 1em;
78+
}
79+
80+
.addSource * {
81+
-fx-max-width: Infinity;
82+
}
83+
7584
.sources {
7685
-fx-padding: 0.5em;
7786
-fx-spacing: 1em;

ui/src/main/resources/edu/wpi/grip/ui/pipeline/Pipeline.fxml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
<?import javafx.scene.control.Separator?>
66
<?import javafx.scene.Group?>
77
<?import javafx.scene.layout.HBox?>
8-
<?import javafx.scene.layout.Pane?>
98
<?import javafx.scene.layout.StackPane?>
109
<?import javafx.scene.layout.VBox?>
1110
<?import javafx.scene.shape.Rectangle?>
@@ -21,7 +20,7 @@
2120
</maxWidth>
2221
<Label maxWidth="Infinity" styleClass="pane-title" text="Sources" />
2322
<Separator orientation="HORIZONTAL" />
24-
<Pane fx:id="addSourcePane" />
23+
<VBox fx:id="addSourceBox" styleClass="addSource" />
2524
<VBox fx:id="sourcesBox" styleClass="sources" />
2625
</VBox>
2726
<Separator orientation="VERTICAL" />

ui/src/test/java/edu/wpi/grip/ui/pipeline/AddSourceViewTest.java renamed to ui/src/test/java/edu/wpi/grip/ui/pipeline/AddSourceButtonTest.java

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import java.io.IOException;
1818
import java.util.Optional;
1919
import java.util.Properties;
20-
20+
import javafx.application.Platform;
2121
import javafx.scene.Scene;
2222
import javafx.stage.Stage;
2323

@@ -26,23 +26,23 @@
2626
import static org.testfx.api.FxAssert.verifyThat;
2727

2828
@RunWith(Enclosed.class)
29-
public class AddSourceViewTest {
29+
public class AddSourceButtonTest {
3030

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

3636
private EventBus eventBus;
37-
private AddSourceView addSourceView;
37+
private AddSourceButton addSourceView;
3838
private MockCameraSourceFactory mockCameraSourceFactory;
3939

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

45-
addSourceView = new AddSourceView(eventBus, null, null, mockCameraSourceFactory, null);
45+
addSourceView = new AddSourceButton(eventBus, null, null, mockCameraSourceFactory, null);
4646

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

5252
@After
5353
public void after() {
54-
// Ensuer that all of the dialogs that were created get closed afterward.
54+
// Ensure that all of the dialogs that were created get closed afterward.
5555
addSourceView.closeDialogs();
5656
}
5757

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

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

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

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

9191
class MockCameraSourceFactory implements CameraSource.Factory {
@@ -123,15 +123,15 @@ private MockCameraSource assignLastCreated(MockCameraSource source) {
123123
*/
124124
public static class AddSourceViewWithExceptionsTest extends ApplicationTest {
125125
private EventBus eventBus;
126-
private AddSourceView addSourceView;
126+
private AddSourceButton addSourceView;
127127
private MockCameraSourceFactory mockCameraSourceFactory;
128128

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

134-
addSourceView = new AddSourceView(eventBus, null, null, mockCameraSourceFactory, null);
134+
addSourceView = new AddSourceButton(eventBus, null, null, mockCameraSourceFactory, null);
135135

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

141141
@After
142142
public void after() {
143-
// Ensuer that all of the dialogs that were created get closed afterward.
143+
// Ensure that all of the dialogs that were created get closed afterward.
144144
addSourceView.closeDialogs();
145145
}
146146

147147
@Test
148148
@SuppressWarnings("PMD.JUnitTestsShouldIncludeAssert")
149149
public void testWhenStartFailsDialogStillCloses() throws Exception {
150150
// When
151-
clickOn(addSourceView.getWebcamButton());
151+
Platform.runLater(() -> addSourceView.getWebcamButton().fire());
152152
WaitForAsyncUtils.waitForFxEvents();
153153

154154
clickOn("OK");
155155

156156
WaitForAsyncUtils.waitForFxEvents();
157157

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

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

169169
clickOn("OK");
170170
WaitForAsyncUtils.waitForFxEvents();

0 commit comments

Comments
 (0)