44import edu .wpi .grip .core .Step ;
55import edu .wpi .grip .core .sockets .InputSocket ;
66import edu .wpi .grip .core .sockets .OutputSocket ;
7+ import edu .wpi .grip .core .sockets .SocketHint ;
78import edu .wpi .grip .ui .Controller ;
89import edu .wpi .grip .ui .annotations .ParametrizedController ;
910import edu .wpi .grip .ui .components .ExceptionWitnessResponderButton ;
1011import edu .wpi .grip .ui .dragging .StepDragService ;
12+ import edu .wpi .grip .ui .events .SetStepsExpandedEvent ;
1113import edu .wpi .grip .ui .pipeline .input .InputSocketController ;
1214import edu .wpi .grip .ui .pipeline .input .InputSocketControllerFactory ;
1315import edu .wpi .grip .ui .util .ControllerMap ;
1416import edu .wpi .grip .ui .util .StyleClassNameUtility ;
1517
18+ import com .google .common .eventbus .EventBus ;
19+ import com .google .common .eventbus .Subscribe ;
1620import com .google .inject .assistedinject .Assisted ;
1721
1822import java .io .InputStream ;
1923import 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 ;
2131import javafx .fxml .FXML ;
2232import javafx .scene .Node ;
33+ import javafx .scene .control .Button ;
2334import javafx .scene .control .Labeled ;
2435import javafx .scene .image .Image ;
2536import javafx .scene .image .ImageView ;
37+ import javafx .scene .input .MouseButton ;
38+ import javafx .scene .input .MouseEvent ;
2639import javafx .scene .layout .HBox ;
2740import javafx .scene .layout .VBox ;
28-
41+ import javafx . util . Duration ;
2942import 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
0 commit comments