Skip to content

Commit bbea07c

Browse files
authored
Merge pull request #659 from AustinShalit/osulacam-MinimizeSteps
minimize steps
2 parents 1e5fcd4 + 59f59f8 commit bbea07c

File tree

7 files changed

+184
-2
lines changed

7 files changed

+184
-2
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package edu.wpi.grip.ui.events;
2+
3+
/**
4+
* Toggles every steps' visibility.
5+
*/
6+
public class SetStepsExpandedEvent {
7+
8+
private final boolean expanded;
9+
10+
public SetStepsExpandedEvent(boolean expanded) {
11+
this.expanded = expanded;
12+
}
13+
14+
public boolean isExpanded() {
15+
return expanded;
16+
}
17+
}

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

Lines changed: 123 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,41 @@
44
import edu.wpi.grip.core.Step;
55
import edu.wpi.grip.core.sockets.InputSocket;
66
import edu.wpi.grip.core.sockets.OutputSocket;
7+
import edu.wpi.grip.core.sockets.SocketHint;
78
import edu.wpi.grip.ui.Controller;
89
import edu.wpi.grip.ui.annotations.ParametrizedController;
910
import edu.wpi.grip.ui.components.ExceptionWitnessResponderButton;
1011
import edu.wpi.grip.ui.dragging.StepDragService;
12+
import edu.wpi.grip.ui.events.SetStepsExpandedEvent;
1113
import edu.wpi.grip.ui.pipeline.input.InputSocketController;
1214
import edu.wpi.grip.ui.pipeline.input.InputSocketControllerFactory;
1315
import edu.wpi.grip.ui.util.ControllerMap;
1416
import edu.wpi.grip.ui.util.StyleClassNameUtility;
1517

18+
import com.google.common.eventbus.EventBus;
19+
import com.google.common.eventbus.Subscribe;
1620
import com.google.inject.assistedinject.Assisted;
1721

1822
import java.io.InputStream;
1923
import java.util.Collection;
20-
24+
import java.util.function.Predicate;
25+
import javafx.animation.KeyFrame;
26+
import javafx.animation.KeyValue;
27+
import javafx.animation.Timeline;
28+
import javafx.beans.property.BooleanProperty;
29+
import javafx.beans.property.DoubleProperty;
30+
import javafx.beans.property.SimpleBooleanProperty;
2131
import javafx.fxml.FXML;
2232
import javafx.scene.Node;
33+
import javafx.scene.control.Button;
2334
import javafx.scene.control.Labeled;
2435
import javafx.scene.image.Image;
2536
import javafx.scene.image.ImageView;
37+
import javafx.scene.input.MouseButton;
38+
import javafx.scene.input.MouseEvent;
2639
import javafx.scene.layout.HBox;
2740
import javafx.scene.layout.VBox;
28-
41+
import javafx.util.Duration;
2942
import javax.inject.Inject;
3043

3144
/**
@@ -40,7 +53,9 @@ public class StepController implements Controller {
4053
private final OutputSocketController.Factory outputSocketControllerFactory;
4154
private final ExceptionWitnessResponderButton.Factory exceptionWitnessResponderButtonFactory;
4255
private final StepDragService stepDragService;
56+
private final EventBus eventBus;
4357
private final Step step;
58+
private final BooleanProperty expanded = new SimpleBooleanProperty(true);
4459
@FXML
4560
private VBox root;
4661
@FXML
@@ -53,21 +68,32 @@ public class StepController implements Controller {
5368
private VBox inputs;
5469
@FXML
5570
private VBox outputs;
71+
@FXML
72+
private ImageView expandIcon;
73+
@FXML
74+
private Button expand;
5675
private ControllerMap<InputSocketController, Node> inputSocketMapManager;
5776
private ControllerMap<OutputSocketController, Node> outputSocketMapManager;
5877

78+
private static final Image UP_ARROW = new Image("/edu/wpi/grip/ui/icons/up.png");
79+
private static final Image DOWN_ARROW = new Image("/edu/wpi/grip/ui/icons/down.png");
80+
private static final Predicate<InputSocketController> interactiveInputSocketFilter
81+
= i -> !i.getSocket().getSocketHint().getView().equals(SocketHint.View.NONE);
82+
5983
@Inject
6084
StepController(Pipeline pipeline,
6185
InputSocketControllerFactory inputSocketControllerFactory,
6286
OutputSocketController.Factory outputSocketControllerFactory,
6387
ExceptionWitnessResponderButton.Factory exceptionWitnessResponderButtonFactory,
6488
StepDragService stepDragService,
89+
EventBus eventBus,
6590
@Assisted Step step) {
6691
this.pipeline = pipeline;
6792
this.inputSocketControllerFactory = inputSocketControllerFactory;
6893
this.outputSocketControllerFactory = outputSocketControllerFactory;
6994
this.exceptionWitnessResponderButtonFactory = exceptionWitnessResponderButtonFactory;
7095
this.stepDragService = stepDragService;
96+
this.eventBus = eventBus;
7197
this.step = step;
7298
}
7399

@@ -82,6 +108,30 @@ private void initialize() {
82108
new Image(InputStream.class.cast(icon))));
83109
buttons.getChildren().add(0, exceptionWitnessResponderButtonFactory.create(step, "Step Error"));
84110

111+
if (step.getInputSockets().stream()
112+
.allMatch(inputSocket -> inputSocket.getSocketHint().getView()
113+
.equals(SocketHint.View.NONE))) {
114+
expand.setManaged(false);
115+
} else {
116+
expandIcon.setImage(UP_ARROW);
117+
expanded.addListener(((observable, oldValue, newValue) -> {
118+
if (newValue) {
119+
inputSocketMapManager.keySet().stream()
120+
.filter(interactiveInputSocketFilter)
121+
.forEach(this::fadeIn);
122+
reopen();
123+
expandIcon.setImage(UP_ARROW);
124+
} else {
125+
inputSocketMapManager.keySet().stream()
126+
.filter(interactiveInputSocketFilter)
127+
.filter(i -> i.getSocket().getConnections().isEmpty())
128+
.forEach(this::fadeOut);
129+
closeUp();
130+
expandIcon.setImage(DOWN_ARROW);
131+
}
132+
}));
133+
}
134+
85135
// Add a SocketControlView for each input socket and output socket
86136
for (InputSocket<?> inputSocket : step.getInputSockets()) {
87137
inputSocketMapManager.add(inputSocketControllerFactory.create(inputSocket));
@@ -142,6 +192,77 @@ private void moveStepRight() {
142192
pipeline.moveStep(step, +1);
143193
}
144194

195+
/**
196+
* Clicking the arrow at the top of the step will cause the step to either expand or retract.
197+
* Secondary clicking the arrow at the top of the step will cause all steps to either expand or
198+
* retract.
199+
*/
200+
@FXML
201+
private void toggleExpand(MouseEvent event) {
202+
if (event.getButton().equals(MouseButton.PRIMARY)) {
203+
expanded.set(!expanded.get());
204+
} else if (event.getButton().equals(MouseButton.SECONDARY)) {
205+
eventBus.post(new SetStepsExpandedEvent(!expanded.get()));
206+
}
207+
}
208+
209+
@Subscribe
210+
public void setExpanded(SetStepsExpandedEvent event) {
211+
expanded.set(event.isExpanded());
212+
}
213+
214+
/**
215+
* Makes an animation to make an input socket fade out over 0.1 seconds.
216+
*
217+
* @param input the input socket controller that will be faded out.
218+
*/
219+
private void fadeOut(InputSocketController input) {
220+
DoubleProperty opacity = input.getRoot().opacityProperty();
221+
Timeline fadeOut = new Timeline(
222+
new KeyFrame(Duration.ZERO, new KeyValue(opacity, 1.0)),
223+
new KeyFrame(new Duration(100), new KeyValue(opacity, 0.0)));
224+
fadeOut.setOnFinished(event -> {
225+
input.getRoot().setVisible(false);
226+
input.getRoot().setManaged(false);
227+
});
228+
fadeOut.play();
229+
}
230+
231+
/**
232+
* Makes an animation to make an input socket fade in over 0.1 seconds.
233+
*
234+
* @param input the input socket controller that will be faded out.
235+
*/
236+
private void fadeIn(InputSocketController input) {
237+
input.getRoot().setVisible(true);
238+
DoubleProperty opacity = input.getRoot().opacityProperty();
239+
Timeline fadeIn = new Timeline(
240+
new KeyFrame(new Duration(100), new KeyValue(opacity, 1.0)));
241+
fadeIn.setOnFinished(
242+
event -> inputSocketMapManager.keySet().forEach(i -> input.getRoot().setManaged(true)));
243+
fadeIn.play();
244+
}
245+
246+
/**
247+
* Makes an animation to make the input vbox slide closed over .25 seconds
248+
*/
249+
private void closeUp() {
250+
Timeline animation = new Timeline(
251+
new KeyFrame(Duration.seconds(0.25),
252+
new KeyValue(inputs.prefHeightProperty(), 0)));
253+
animation.play();
254+
}
255+
256+
/**
257+
* Makes an animation to make the input vbox slide open over .1 seconds
258+
*/
259+
private void reopen() {
260+
Timeline animation = new Timeline(
261+
new KeyFrame(Duration.seconds(0.1),
262+
new KeyValue(inputs.prefHeightProperty(), inputs.getMaxHeight())));
263+
animation.play();
264+
}
265+
145266
/**
146267
* Used for assisted injects. Guice will automatically create an instance of this interface so we
147268
* can create step controllers. This lets us use injection with StepController even though it

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@ Button.delete, Button.move-left, Button.move-right, .start-stoppable-button, .ex
122122
-fx-pref-width: 2em;
123123
}
124124

125+
.expand {
126+
-fx-background-color: transparent;
127+
}
128+
125129
VBox.sockets {
126130
-fx-spacing: 0.5em;
127131
}
237 Bytes
Loading
231 Bytes
Loading

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,19 @@
7979
</graphic>
8080
</Label>
8181
<Separator orientation="HORIZONTAL" />
82+
<Button fx:id="expand" onMouseClicked="#toggleExpand" styleClass="expand"
83+
maxWidth="Infinity" prefHeight="0" HBox.hgrow="ALWAYS">
84+
<graphic>
85+
<ImageView fx:id="expandIcon">
86+
<fitHeight>
87+
<DPIUtility fx:constant="SMALL_ICON_SIZE" />
88+
</fitHeight>
89+
</ImageView>
90+
</graphic>
91+
<tooltip>
92+
<Tooltip text="Minimizes/Maximizes the inputs in the step"/>
93+
</tooltip>
94+
</Button>
8295
<VBox fx:id="inputs" styleClass="sockets" />
8396
<Separator orientation="HORIZONTAL" />
8497
<VBox fx:id="outputs" styleClass="sockets" />

ui/src/test/java/edu/wpi/grip/ui/pipeline/PipelineUITest.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import edu.wpi.grip.core.Pipeline;
88
import edu.wpi.grip.core.Step;
99
import edu.wpi.grip.core.SubtractionOperation;
10+
import edu.wpi.grip.core.operations.composite.BlurOperation;
11+
import edu.wpi.grip.core.operations.composite.DesaturateOperation;
1012
import edu.wpi.grip.core.sockets.InputSocket;
1113
import edu.wpi.grip.core.sockets.OutputSocket;
1214
import edu.wpi.grip.core.util.MockExceptionWitness;
@@ -39,6 +41,7 @@
3941
import javafx.stage.Stage;
4042

4143
import static junit.framework.Assert.assertEquals;
44+
import static junit.framework.TestCase.assertTrue;
4245
import static junit.framework.TestCase.fail;
4346
import static org.testfx.api.FxAssert.verifyThat;
4447
import static org.testfx.api.FxAssert.verifyThatIter;
@@ -49,6 +52,8 @@ public class PipelineUITest extends ApplicationTest {
4952
private EventBus eventBus;
5053
private OperationMetaData additionOperation;
5154
private OperationMetaData subtractionOperation;
55+
private OperationMetaData blurOperation;
56+
private OperationMetaData desaturateOperation;
5257
private PipelineController pipelineController;
5358
private Pipeline pipeline;
5459

@@ -66,6 +71,10 @@ public void start(Stage stage) {
6671
AdditionOperation(isf, osf));
6772
subtractionOperation = new OperationMetaData(SubtractionOperation.DESCRIPTION, () -> new
6873
SubtractionOperation(isf, osf));
74+
blurOperation = new OperationMetaData(BlurOperation.DESCRIPTION, () -> new
75+
BlurOperation(isf, osf));
76+
desaturateOperation = new OperationMetaData(DesaturateOperation.DESCRIPTION, () -> new
77+
DesaturateOperation(isf, osf));
6978
pipelineController = injector.getInstance(PipelineController.class);
7079
final Scene scene = new Scene(TestAnnotationFXMLLoader.load(pipelineController), 800, 600);
7180
stage.setScene(scene);
@@ -102,6 +111,24 @@ public void testConnectingTwoOperations() {
102111

103112
}
104113

114+
@Test
115+
public void testMinimizeButton() {
116+
Step desaturateStep = addOperation(1, desaturateOperation);
117+
Step blurStep = addOperation(1, blurOperation);
118+
assertTrue("blur input socket size is:" + blurStep.getInputSockets().size(),
119+
blurStep.getInputSockets().size() > 0);
120+
121+
drag(StyleClassNameUtility.cssSelectorForOutputSocketHandleOn(desaturateStep), MouseButton
122+
.PRIMARY).dropTo(StyleClassNameUtility.cssSelectorForInputSocketHandleOn(blurStep));
123+
124+
clickOn(".pipeline .blur-step .expand", MouseButton.PRIMARY);
125+
clickOn(".pipeline .blur-step .expand", MouseButton.PRIMARY);
126+
127+
assertTrue("blur input socket size is:" + blurStep.getInputSockets().size(),
128+
blurStep.getInputSockets().size() > 0);
129+
130+
}
131+
105132
@Test
106133
public void testMoveOperation() {
107134
final Step step1 = MockStep.createMockStepWithOperation();

0 commit comments

Comments
 (0)