Skip to content

Commit

Permalink
Merge pull request #561 from JLLeitschuh/feat/dragAndDropOperations
Browse files Browse the repository at this point in the history
Feat/drag and drop operations
  • Loading branch information
JLLeitschuh committed Apr 11, 2016
2 parents 1ede713 + f02d15a commit a7d3256
Show file tree
Hide file tree
Showing 17 changed files with 490 additions and 58 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ install:
- '[ "${TRAVIS_PULL_REQUEST}" = "false" ] && ./gradlew :ui:assemble --stacktrace || ./gradlew --stacktrace '

script:
- ./gradlew check --stacktrace -PprintTestResults
- ./gradlew check --stacktrace -Pheadless=true

after_success:
- codecov
Expand Down
4 changes: 2 additions & 2 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ install:
- choco install -y InnoSetup

build_script:
- gradlew.bat :ui:assemble --stacktrace -PprintTestResults
- gradlew.bat :ui:assemble --stacktrace

# to run your custom scripts instead of automatic tests
test_script:
- gradlew.bat check --stacktrace -PprintTestResults
- gradlew.bat check --stacktrace -Pheadless=true

platform:
- x64
Expand Down
12 changes: 12 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ project(":ui") {
testCompile files(project(':core').sourceSets.test.output.resourcesDir)
testCompile group: 'org.testfx', name: 'testfx-core', version: '4.0.+'
testCompile group: 'org.testfx', name: 'testfx-junit', version: '4.0.+'
testRuntime group: 'org.testfx', name: 'openjfx-monocle', version: '1.8.0_20'
}

evaluationDependsOn(':core')
Expand All @@ -299,6 +300,17 @@ project(":ui") {
scopes.PROVIDED.plus += [configurations.ideProvider]
}

/*
* Allows you to run the UI tests in headless mode by calling gradle with the -Pheadless=true argument
*/
if (project.hasProperty('headless') && project.headless) {
println "Running UI Tests Headless"
test {
jvmArgs = ['-Djava.awt.headless=true', '-Dtestfx.robot=glass', '-Dtestfx.headless=true', '-Dprism.order=sw', '-Dprism.text=t2k']
}
}


javafx {
profiles {
linux {
Expand Down
32 changes: 32 additions & 0 deletions core/src/main/java/edu/wpi/grip/core/Pipeline.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import edu.wpi.grip.core.sockets.OutputSocket;
import edu.wpi.grip.core.sockets.SocketHint;

import javax.annotation.Nullable;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
Expand Down Expand Up @@ -230,6 +231,37 @@ public void onSourceRemoved(SourceRemovedEvent event) {
}
}

/**
* Adds the step between two other steps.
* @param stepToAdd The step to add to the pipeline
* @param lower The step to be added above
* @param higher The step to be added below
*/
public void addStepBetween(Step stepToAdd, @Nullable Step lower, @Nullable Step higher) {
checkNotNull(stepToAdd, "The step to add cannot be null");
int index = readStepsSafely(steps -> {
// If not in the list these can return -1
int lowerIndex = steps.indexOf(lower);
int upperIndex = steps.indexOf(higher);
if (lowerIndex != -1 && upperIndex != -1) {
assert lowerIndex <= upperIndex : "Upper step was before lower index";
int difference = Math.abs(upperIndex - lowerIndex); // Just in case
// Place the step halfway between these two steps
return lowerIndex + 1 + difference / 2;
} else if (lowerIndex != -1) {
// Place it above the lower one
return lowerIndex + 1;
} else if (upperIndex != -1) {
// Place it below the upper one
return upperIndex;
} else {
// Otherwise if they are both null place it at the end of the list
return steps.size();
}
});
addStep(index, stepToAdd);
}

public void addStep(int index, Step step) {
checkNotNull(step, "The step can not be null");
checkArgument(!step.removed(), "The step must not have been disabled already");
Expand Down
60 changes: 60 additions & 0 deletions core/src/test/java/edu/wpi/grip/core/PipelineTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -275,4 +275,64 @@ public void testCannotConnectIncompatibleTypes() {

assertFalse("Should not be able to connect incompatible types", pipeline.canConnect((OutputSocket) b, (InputSocket) a));
}

@Test
public void testAddBetweenSteps() {
final Step
stepToAdd = new MockStep(),
lowerStep = new MockStep(),
upperStep = new MockStep();
pipeline.addStep(lowerStep);
pipeline.addStep(upperStep);

pipeline.addStepBetween(stepToAdd, lowerStep, upperStep);
assertEquals("The step was not added to the middle of the pipeline",
Arrays.asList(lowerStep, stepToAdd, upperStep), pipeline.getSteps());
}

@Test
public void testAddBetweenNullAndStep() {
final Step
stepToAdd = new MockStep(),
lowerStep = new MockStep(),
upperStep = new MockStep();
pipeline.addStep(lowerStep);
pipeline.addStep(upperStep);
pipeline.addStepBetween(stepToAdd, null, lowerStep);
assertEquals("The step was not added to the begining of the pipeline",
Arrays.asList(stepToAdd, lowerStep, upperStep), pipeline.getSteps());
}

@Test
public void testAddBetweenStepAndNull() {
final Step
stepToAdd = new MockStep(),
lowerStep = new MockStep(),
upperStep = new MockStep();
pipeline.addStep(lowerStep);
pipeline.addStep(upperStep);
pipeline.addStepBetween(stepToAdd, upperStep, null);
assertEquals("The step was not added to the end of the pipeline",
Arrays.asList(lowerStep, upperStep, stepToAdd), pipeline.getSteps());
}

@Test
public void testAddBetweenTwoNulls() {
final Step stepToAdd = new MockStep();
pipeline.addStepBetween(stepToAdd, null, null);
assertEquals("The step should have been added to the pipeline",
Collections.singletonList(stepToAdd), pipeline.getSteps());
}

@Test(expected = AssertionError.class)
public void testAddBetweenStepsOutOfOrder() {
final Step
stepToAdd = new MockStep(),
lowerStep = new MockStep(),
upperStep = new MockStep();
pipeline.addStep(lowerStep);
pipeline.addStep(upperStep);

pipeline.addStepBetween(stepToAdd, upperStep, lowerStep);
}
}
26 changes: 25 additions & 1 deletion ui/src/main/java/edu/wpi/grip/ui/OperationController.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,20 @@
import edu.wpi.grip.core.Pipeline;
import edu.wpi.grip.core.Step;
import edu.wpi.grip.ui.annotations.ParametrizedController;
import edu.wpi.grip.ui.dragging.OperationDragService;
import edu.wpi.grip.ui.util.StyleClassNameUtility;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.Tooltip;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.DataFormat;
import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.GridPane;

import java.util.Collections;

/**
* A JavaFX control that renders information about an {@link Operation}. This is used in the palette view to present
* the user with information on the various operations to choose from.
Expand All @@ -35,16 +41,18 @@ public class OperationController implements Controller {

private final Pipeline pipeline;
private final Step.Factory stepFactory;
private final OperationDragService operationDragService;
private final Operation operation;

public interface Factory {
OperationController create(Operation operation);
}

@Inject
OperationController(Pipeline pipeline, Step.Factory stepFactory, @Assisted Operation operation) {
OperationController(Pipeline pipeline, Step.Factory stepFactory, OperationDragService operationDragService, @Assisted Operation operation) {
this.pipeline = pipeline;
this.stepFactory = stepFactory;
this.operationDragService = operationDragService;
this.operation = operation;
}

Expand All @@ -65,6 +73,22 @@ public void initialize() {

// Ensures that when this element is hidden that it also removes its size calculations
root.managedProperty().bind(root.visibleProperty());


root.setOnDragDetected(mouseEvent -> {
// Create a snapshot to use as the cursor
final ImageView preview = new ImageView(root.snapshot(null, null));

final Dragboard db = root.startDragAndDrop(TransferMode.ANY);
db.setContent(Collections.singletonMap(DataFormat.PLAIN_TEXT, operation.getName()));
db.setDragView(preview.getImage());
// Begin the dragging
root.startFullDrag();
// Tell the drag service that this is the operation that will be received
operationDragService.beginDrag(operation);

mouseEvent.consume();
});
}

@FXML
Expand Down
56 changes: 56 additions & 0 deletions ui/src/main/java/edu/wpi/grip/ui/dragging/DragService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package edu.wpi.grip.ui.dragging;


import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.SimpleObjectProperty;

import java.util.Optional;

/**
* A service to provide data transfer capabilities between two controllers.
* Concrete versions of the service are usually {@link com.google.inject.Singleton Singletons}
* so that they can be injected into the two controllers.
*
* @param <T> The value that the object property holds.
*/
public abstract class DragService<T> {
private final ObjectProperty<T> dragProperty;

/**
* @param name The name for the {@link SimpleObjectProperty}
*/
public DragService(String name) {
this.dragProperty = new SimpleObjectProperty<>(this, name);
}

/**
* @return The read only version of this object property.
*/
public ReadOnlyObjectProperty<T> getDragProperty() {
return dragProperty;
}

/**
* @return The value stored in the object property.
*/
public Optional<T> getValue() {
return Optional.ofNullable(dragProperty.get());
}

/**
* Begins the drag action
*
* @param value The value to be transferred during the drag.
*/
public void beginDrag(T value) {
this.dragProperty.set(value);
}

/**
* This should be called when the drag action is complete.
*/
public void completeDrag() {
dragProperty.setValue(null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package edu.wpi.grip.ui.dragging;


import com.google.inject.Singleton;
import edu.wpi.grip.core.Operation;

/**
* Service for dragging an {@link Operation} from the {@link edu.wpi.grip.ui.pipeline.PipelineController}
* to the {@link edu.wpi.grip.ui.pipeline.PipelineController}.
*/
@Singleton
public class OperationDragService extends DragService<Operation> {

public OperationDragService() {
super("operation");
}
}
Loading

0 comments on commit a7d3256

Please sign in to comment.