Skip to content

Commit d1877d4

Browse files
committed
Adds Dragging and Dropping Steps in the Pipeline
1 parent 880cc48 commit d1877d4

File tree

8 files changed

+191
-67
lines changed

8 files changed

+191
-67
lines changed

core/src/main/java/edu/wpi/grip/core/Pipeline.java

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -232,14 +232,13 @@ public void onSourceRemoved(SourceRemovedEvent event) {
232232
}
233233

234234
/**
235-
* Adds the step between two other steps.
236-
* @param stepToAdd The step to add to the pipeline
237-
* @param lower The step to be added above
238-
* @param higher The step to be added below
235+
* Finds the index between the two steps
236+
* @param lower The lower step
237+
* @param higher The higher step
238+
* @return The index that is in between the two of these steps
239239
*/
240-
public void addStepBetween(Step stepToAdd, @Nullable Step lower, @Nullable Step higher) {
241-
checkNotNull(stepToAdd, "The step to add cannot be null");
242-
int index = readStepsSafely(steps -> {
240+
private int indexBetween(@Nullable Step lower, @Nullable Step higher) {
241+
return readStepsSafely(steps -> {
243242
// If not in the list these can return -1
244243
int lowerIndex = steps.indexOf(lower);
245244
int upperIndex = steps.indexOf(higher);
@@ -259,9 +258,33 @@ public void addStepBetween(Step stepToAdd, @Nullable Step lower, @Nullable Step
259258
return steps.size();
260259
}
261260
});
261+
}
262+
263+
/**
264+
* Adds the step between two other steps.
265+
* @param stepToAdd The step to add to the pipeline
266+
* @param lower The step to be added above
267+
* @param higher The step to be added below
268+
*/
269+
public void addStepBetween(Step stepToAdd, @Nullable Step lower, @Nullable Step higher) {
270+
checkNotNull(stepToAdd, "The step to add cannot be null");
271+
final int index = indexBetween(lower, higher);
262272
addStep(index, stepToAdd);
263273
}
264274

275+
/**
276+
*
277+
* @param toMove The step to move
278+
* @param lower The lower step
279+
* @param higher The upper step
280+
*/
281+
public void moveStepBetween(Step toMove, @Nullable Step lower, @Nullable Step higher) {
282+
checkNotNull(toMove, "The step to move cannot be null");
283+
final int index = indexBetween(lower, higher);
284+
final int currentIndex = readStepsSafely(steps -> steps.indexOf(toMove));
285+
moveStep(toMove, index > currentIndex ? index - (currentIndex + 1 ) : index - currentIndex);
286+
}
287+
265288
public void addStep(int index, Step step) {
266289
checkNotNull(step, "The step can not be null");
267290
checkArgument(!step.removed(), "The step must not have been disabled already");

core/src/test/java/edu/wpi/grip/core/PipelineTest.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,4 +335,34 @@ public void testAddBetweenStepsOutOfOrder() {
335335

336336
pipeline.addStepBetween(stepToAdd, upperStep, lowerStep);
337337
}
338+
339+
@Test
340+
public void testMoveStepToLeft() {
341+
final Step
342+
stepToMove = new MockStep(),
343+
lowerStep = new MockStep(),
344+
upperStep = new MockStep();
345+
pipeline.addStep(lowerStep);
346+
pipeline.addStep(upperStep);
347+
pipeline.addStep(stepToMove);
348+
pipeline.moveStepBetween(stepToMove, lowerStep, upperStep);
349+
350+
assertEquals("The step should have been moved within the pipeline",
351+
Arrays.asList(lowerStep, stepToMove, upperStep), pipeline.getSteps());
352+
}
353+
354+
@Test
355+
public void testMoveStepToRight() {
356+
final Step
357+
stepToMove = new MockStep(),
358+
lowerStep = new MockStep(),
359+
upperStep = new MockStep();
360+
pipeline.addStep(stepToMove);
361+
pipeline.addStep(lowerStep);
362+
pipeline.addStep(upperStep);
363+
pipeline.moveStepBetween(stepToMove, lowerStep, upperStep);
364+
365+
assertEquals("The step should have been moved within the pipeline",
366+
Arrays.asList(lowerStep, stepToMove, upperStep), pipeline.getSteps());
367+
}
338368
}

ui/src/main/java/edu/wpi/grip/ui/OperationController.java

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,8 @@
1313
import javafx.scene.control.Tooltip;
1414
import javafx.scene.image.Image;
1515
import javafx.scene.image.ImageView;
16-
import javafx.scene.input.DataFormat;
17-
import javafx.scene.input.Dragboard;
18-
import javafx.scene.input.TransferMode;
1916
import javafx.scene.layout.GridPane;
2017

21-
import java.util.Collections;
22-
2318
/**
2419
* A JavaFX control that renders information about an {@link Operation}. This is used in the palette view to present
2520
* the user with information on the various operations to choose from.
@@ -76,19 +71,15 @@ public void initialize() {
7671

7772

7873
root.setOnDragDetected(mouseEvent -> {
79-
// Create a snapshot to use as the cursor
80-
final ImageView preview = new ImageView(root.snapshot(null, null));
81-
82-
final Dragboard db = root.startDragAndDrop(TransferMode.ANY);
83-
db.setContent(Collections.singletonMap(DataFormat.PLAIN_TEXT, operation.getName()));
84-
db.setDragView(preview.getImage());
85-
// Begin the dragging
86-
root.startFullDrag();
8774
// Tell the drag service that this is the operation that will be received
88-
operationDragService.beginDrag(operation);
75+
operationDragService.beginDrag(operation, root, operation.getName());
8976

9077
mouseEvent.consume();
9178
});
79+
80+
root.setOnDragDone(mouseEvent -> {
81+
operationDragService.completeDrag();
82+
});
9283
}
9384

9485
@FXML

ui/src/main/java/edu/wpi/grip/ui/dragging/DragService.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@
44
import javafx.beans.property.ObjectProperty;
55
import javafx.beans.property.ReadOnlyObjectProperty;
66
import javafx.beans.property.SimpleObjectProperty;
7+
import javafx.scene.Node;
8+
import javafx.scene.image.ImageView;
9+
import javafx.scene.input.DataFormat;
10+
import javafx.scene.input.Dragboard;
11+
import javafx.scene.input.TransferMode;
712

13+
import java.util.Collections;
814
import java.util.Optional;
915

1016
/**
@@ -39,11 +45,22 @@ public Optional<T> getValue() {
3945
}
4046

4147
/**
42-
* Begins the drag action
48+
* Begins the drag action.
49+
* Creates the dragboard on the root node and adds a
50+
* snapshot of the root node as the view.
4351
*
4452
* @param value The value to be transferred during the drag.
53+
* @param root The root node to drag
54+
* @param name The name to set as the content of the dragboard
4555
*/
46-
public void beginDrag(T value) {
56+
public void beginDrag(T value, Node root, String name) {
57+
// Create a snapshot to use as the cursor
58+
final ImageView preview = new ImageView(root.snapshot(null, null));
59+
60+
final Dragboard db = root.startDragAndDrop(TransferMode.ANY);
61+
db.setContent(Collections.singletonMap(DataFormat.PLAIN_TEXT, name));
62+
db.setDragView(preview.getImage());
63+
4764
this.dragProperty.set(value);
4865
}
4966

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package edu.wpi.grip.ui.dragging;
2+
3+
import com.google.inject.Singleton;
4+
import edu.wpi.grip.core.Step;
5+
6+
/**
7+
* Service for dragging and dropping a step
8+
*/
9+
@Singleton
10+
public class StepDragService extends DragService<Step> {
11+
12+
public StepDragService() {
13+
super("step");
14+
}
15+
}

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

Lines changed: 75 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import edu.wpi.grip.core.sockets.OutputSocket;
1414
import edu.wpi.grip.ui.annotations.ParametrizedController;
1515
import edu.wpi.grip.ui.dragging.OperationDragService;
16+
import edu.wpi.grip.ui.dragging.StepDragService;
1617
import edu.wpi.grip.ui.pipeline.input.InputSocketController;
1718
import edu.wpi.grip.ui.pipeline.source.SourceController;
1819
import edu.wpi.grip.ui.pipeline.source.SourceControllerFactory;
@@ -31,6 +32,7 @@
3132
import javafx.scene.layout.Pane;
3233
import javafx.scene.layout.VBox;
3334

35+
import javax.annotation.Nullable;
3436
import javax.inject.Inject;
3537
import java.util.Collection;
3638
import java.util.Map;
@@ -70,6 +72,8 @@ public final class PipelineController {
7072
private AddSourceView addSourceView;
7173
@Inject
7274
private OperationDragService operationDragService;
75+
@Inject
76+
private StepDragService stepDragService;
7377

7478
private ControllerMap<StepController, Node> stepsMapManager;
7579
private ControllerMap<SourceController, Node> sourceMapManager;
@@ -98,55 +102,90 @@ public void initialize() throws Exception {
98102
dragEvent.acceptTransferModes(TransferMode.ANY);
99103
});
100104

105+
stepDragService.getValue().ifPresent(step -> {
106+
dragEvent.acceptTransferModes(TransferMode.ANY);
107+
});
108+
101109
});
102110

103111
stepBox.setOnDragDropped(mouseEvent -> {
104112
// If this is an operation being dropped
105113
operationDragService.getValue().ifPresent(operation -> {
106-
// Then we need to figure out where to put it in the pipeline
107-
// First create a map of every node in the steps list to its x position
108-
final Map<Double, Node> positionMapping = stepsMapManager
109-
.entrySet()
110-
.stream()
111-
.collect(
112-
Collectors
113-
.toMap(e -> calculateMiddleXPosOfNodeInParent(e.getValue()),
114-
Map.Entry::getValue));
115-
116-
// A tree map is an easy way to sort the values
117-
final NavigableMap<Double, Node> sortedPositionMapping
118-
= new TreeMap<>(positionMapping);
119-
120-
// Now we find the sockets that are to the immediate left and
121-
// immediate right of the drop point
122-
123-
// These can be null
124-
final Map.Entry<Double, Node>
125-
lowerEntry = sortedPositionMapping.floorEntry(mouseEvent.getX()),
126-
higherEntry = sortedPositionMapping.ceilingEntry(mouseEvent.getX());
127-
// These can be null
128-
final StepController
129-
lowerStepController =
130-
lowerEntry == null ?
131-
null : stepsMapManager.getWithNode(lowerEntry.getValue());
132-
final StepController
133-
higherStepController =
134-
higherEntry == null ?
135-
null : stepsMapManager.getWithNode(higherEntry.getValue());
136-
final Step
137-
lowerStep = lowerStepController == null ? null : lowerStepController.getStep(),
138-
higherStep = higherStepController == null ? null : higherStepController.getStep();
139-
140-
141114
operationDragService.completeDrag();
115+
final StepPair pair = lowerAndHigherStep(mouseEvent.getX());
142116
// Add the new step to the pipeline between these two steps
143-
pipeline.addStepBetween(stepFactory.create(operation), lowerStep, higherStep);
117+
pipeline.addStepBetween(stepFactory.create(operation), pair.lower, pair.higher);
118+
});
119+
120+
// If this is a step being dropped
121+
stepDragService.getValue().ifPresent(step -> {
122+
stepDragService.completeDrag();
123+
final StepPair pair = lowerAndHigherStep(mouseEvent.getX());
124+
// Move the new step to the pipeline between these two steps
125+
pipeline.moveStepBetween(step, pair.lower, pair.higher);
144126
});
145127
});
146128

147129
addSourcePane.getChildren().add(addSourceView);
148130
}
149131

132+
/**
133+
* Simple class for returning two steps
134+
*/
135+
private static final class StepPair {
136+
final Step lower;
137+
final Step higher;
138+
139+
StepPair(@Nullable Step lower, @Nullable Step higher) {
140+
this.lower = lower;
141+
this.higher = higher;
142+
}
143+
}
144+
145+
/**
146+
* Determines the steps (via the {@link StepController} that are above and below the given
147+
* {@code x} value in the list of steps.
148+
*
149+
* @param x The x value to find what steps this is between
150+
* @return The pair of steps that are above and below this x position.
151+
*/
152+
private StepPair lowerAndHigherStep(double x) {
153+
// Then we need to figure out where to put it in the pipeline
154+
// First create a map of every node in the steps list to its x position
155+
final Map<Double, Node> positionMapping = stepsMapManager
156+
.entrySet()
157+
.stream()
158+
.collect(
159+
Collectors
160+
.toMap(e -> calculateMiddleXPosOfNodeInParent(e.getValue()),
161+
Map.Entry::getValue));
162+
163+
// A tree map is an easy way to sort the values
164+
final NavigableMap<Double, Node> sortedPositionMapping
165+
= new TreeMap<>(positionMapping);
166+
167+
// Now we find the sockets that are to the immediate left and
168+
// immediate right of the drop point
169+
170+
// These can be null
171+
final Map.Entry<Double, Node>
172+
lowerEntry = sortedPositionMapping.floorEntry(x),
173+
higherEntry = sortedPositionMapping.ceilingEntry(x);
174+
// These can be null
175+
final StepController
176+
lowerStepController =
177+
lowerEntry == null ?
178+
null : stepsMapManager.getWithNode(lowerEntry.getValue());
179+
final StepController
180+
higherStepController =
181+
higherEntry == null ?
182+
null : stepsMapManager.getWithNode(higherEntry.getValue());
183+
return new StepPair(
184+
lowerStepController == null ? null : lowerStepController.getStep(),
185+
higherStepController == null ? null : higherStepController.getStep()
186+
);
187+
}
188+
150189
private double calculateMiddleXPosOfNodeInParent(Node node) {
151190
return node.getLayoutX() + (node.getBoundsInParent().getWidth() / 2.);
152191
}

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

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,8 @@
1919
import javafx.css.PseudoClass;
2020
import javafx.scene.control.Button;
2121
import javafx.scene.control.Tooltip;
22-
import javafx.scene.input.DataFormat;
23-
import javafx.scene.input.Dragboard;
2422
import javafx.scene.input.TransferMode;
2523

26-
import java.util.Collections;
2724
import java.util.Optional;
2825
import java.util.Set;
2926
import java.util.stream.Collectors;
@@ -95,12 +92,9 @@ public interface Factory {
9592
// When the user starts dragging a socket handle, starting forming a connection. This involves keeping a
9693
// reference to the SocketView that the drag started at.
9794
this.setOnDragDetected(mouseEvent -> {
98-
Dragboard db = this.startDragAndDrop(TransferMode.ANY);
99-
db.setContent(Collections.singletonMap(DataFormat.PLAIN_TEXT, "socket"));
100-
mouseEvent.consume();
101-
10295
this.connectingProperty.set(true);
103-
socketDragService.beginDrag(this.socket);
96+
socketDragService.beginDrag(this.socket, this, "socket");
97+
mouseEvent.consume();
10498
});
10599

106100
// Remove the "connecting" property (which changes the appearance of the handle) when the user moves the cursor

0 commit comments

Comments
 (0)