4
4
import edu .wpi .grip .core .Step ;
5
5
import edu .wpi .grip .core .sockets .InputSocket ;
6
6
import edu .wpi .grip .core .sockets .OutputSocket ;
7
+ import edu .wpi .grip .core .sockets .SocketHint ;
7
8
import edu .wpi .grip .ui .Controller ;
8
9
import edu .wpi .grip .ui .annotations .ParametrizedController ;
9
10
import edu .wpi .grip .ui .components .ExceptionWitnessResponderButton ;
10
11
import edu .wpi .grip .ui .dragging .StepDragService ;
12
+ import edu .wpi .grip .ui .events .SetStepsExpandedEvent ;
11
13
import edu .wpi .grip .ui .pipeline .input .InputSocketController ;
12
14
import edu .wpi .grip .ui .pipeline .input .InputSocketControllerFactory ;
13
15
import edu .wpi .grip .ui .util .ControllerMap ;
14
16
import edu .wpi .grip .ui .util .StyleClassNameUtility ;
15
17
18
+ import com .google .common .eventbus .EventBus ;
19
+ import com .google .common .eventbus .Subscribe ;
16
20
import com .google .inject .assistedinject .Assisted ;
17
21
18
22
import java .io .InputStream ;
19
23
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 ;
21
31
import javafx .fxml .FXML ;
22
32
import javafx .scene .Node ;
33
+ import javafx .scene .control .Button ;
23
34
import javafx .scene .control .Labeled ;
24
35
import javafx .scene .image .Image ;
25
36
import javafx .scene .image .ImageView ;
37
+ import javafx .scene .input .MouseButton ;
38
+ import javafx .scene .input .MouseEvent ;
26
39
import javafx .scene .layout .HBox ;
27
40
import javafx .scene .layout .VBox ;
28
-
41
+ import javafx . util . Duration ;
29
42
import javax .inject .Inject ;
30
43
31
44
/**
@@ -40,7 +53,9 @@ public class StepController implements Controller {
40
53
private final OutputSocketController .Factory outputSocketControllerFactory ;
41
54
private final ExceptionWitnessResponderButton .Factory exceptionWitnessResponderButtonFactory ;
42
55
private final StepDragService stepDragService ;
56
+ private final EventBus eventBus ;
43
57
private final Step step ;
58
+ private final BooleanProperty expanded = new SimpleBooleanProperty (true );
44
59
@ FXML
45
60
private VBox root ;
46
61
@ FXML
@@ -53,21 +68,32 @@ public class StepController implements Controller {
53
68
private VBox inputs ;
54
69
@ FXML
55
70
private VBox outputs ;
71
+ @ FXML
72
+ private ImageView expandIcon ;
73
+ @ FXML
74
+ private Button expand ;
56
75
private ControllerMap <InputSocketController , Node > inputSocketMapManager ;
57
76
private ControllerMap <OutputSocketController , Node > outputSocketMapManager ;
58
77
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
+
59
83
@ Inject
60
84
StepController (Pipeline pipeline ,
61
85
InputSocketControllerFactory inputSocketControllerFactory ,
62
86
OutputSocketController .Factory outputSocketControllerFactory ,
63
87
ExceptionWitnessResponderButton .Factory exceptionWitnessResponderButtonFactory ,
64
88
StepDragService stepDragService ,
89
+ EventBus eventBus ,
65
90
@ Assisted Step step ) {
66
91
this .pipeline = pipeline ;
67
92
this .inputSocketControllerFactory = inputSocketControllerFactory ;
68
93
this .outputSocketControllerFactory = outputSocketControllerFactory ;
69
94
this .exceptionWitnessResponderButtonFactory = exceptionWitnessResponderButtonFactory ;
70
95
this .stepDragService = stepDragService ;
96
+ this .eventBus = eventBus ;
71
97
this .step = step ;
72
98
}
73
99
@@ -82,6 +108,30 @@ private void initialize() {
82
108
new Image (InputStream .class .cast (icon ))));
83
109
buttons .getChildren ().add (0 , exceptionWitnessResponderButtonFactory .create (step , "Step Error" ));
84
110
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
+
85
135
// Add a SocketControlView for each input socket and output socket
86
136
for (InputSocket <?> inputSocket : step .getInputSockets ()) {
87
137
inputSocketMapManager .add (inputSocketControllerFactory .create (inputSocket ));
@@ -142,6 +192,77 @@ private void moveStepRight() {
142
192
pipeline .moveStep (step , +1 );
143
193
}
144
194
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
+
145
266
/**
146
267
* Used for assisted injects. Guice will automatically create an instance of this interface so we
147
268
* can create step controllers. This lets us use injection with StepController even though it
0 commit comments