package nl.utwente.viskell.ui;
import java.util.List;
import javafx.animation.ScaleTransition;
import javafx.scene.control.Button;
import javafx.scene.layout.TilePane;
import javafx.util.Duration;
import nl.utwente.viskell.haskell.type.*;
import nl.utwente.viskell.ui.components.*;
/** A menu for attaching something to an open wire. */
public class WireMenu extends TilePane {
/** The toplevel pane this menu is on. */
private final ToplevelPane toplevel;
/** The draw wire belonging to this menu */
private DrawWire attachedWire;
public WireMenu(DrawWire wire, boolean byMouse) {
this.toplevel = wire.getAnchor().getPane();
this.attachedWire = wire;
this.setMouseTransparent(true);
this.getStyleClass().add("menu");
this.setPrefColumns(1);
this.setVgap(5);
Button cancelButton = new Button("Cancel");
cancelButton.getStyleClass().add("escape");
cancelButton.setOnAction(event -> this.attachedWire.remove());
// FIXME: silly workaround, somehow the buttons in this menu don't work on touch click while those in FunctionMenu do...
cancelButton.setOnTouchPressed(event -> this.attachedWire.remove());
Type type = wire.getAnchor().getFreshType();
int tArity = 2;
if (type instanceof TypeApp) {
List<Type> appChain = ((TypeApp)type).asFlattenedAppChain();
if (appChain.get(0) instanceof TupleTypeCon) {
tArity = appChain.size()-1;
}
}
final int tupleArity = tArity; // why do I need such silly workaround when using lambda in Java...
if (wire.getAnchor() instanceof InputAnchor) {
Button lambdaBlockButton = new Button("Lambda");
lambdaBlockButton.setOnAction(event -> addBlockWithOutput(new LambdaBlock(this.toplevel, type.countArguments())));
lambdaBlockButton.setOnTouchPressed(event -> addBlockWithOutput(new LambdaBlock(this.toplevel, type.countArguments())));
Button arbitraryBlockButton = new Button("Arbitrary");
arbitraryBlockButton.setOnAction(event -> addBlockWithOutput(new ArbitraryBlock(this.toplevel)));
arbitraryBlockButton.setOnTouchPressed(event -> addBlockWithOutput(new ArbitraryBlock(this.toplevel)));
Button rationalBlockButton = new Button("Rational");
rationalBlockButton.setOnAction(event -> addBlockWithOutput(new SliderBlock(this.toplevel, false)));
rationalBlockButton.setOnTouchPressed(event -> addBlockWithOutput(new SliderBlock(this.toplevel, false)));
Button IntegerBlockButton = new Button("Integer");
IntegerBlockButton.setOnAction(event -> addBlockWithOutput(new SliderBlock(this.toplevel, true)));
IntegerBlockButton.setOnTouchPressed(event -> addBlockWithOutput(new SliderBlock(this.toplevel, true)));
Button joinBlockButton = new Button("Joiner");
joinBlockButton.setOnAction(event -> addBlockWithOutput(new JoinerBlock(this.toplevel, tupleArity)));
joinBlockButton.setOnTouchPressed(event -> addBlockWithOutput(new JoinerBlock(this.toplevel, tupleArity)));
Button appendBlockButton = new Button("Append");
appendBlockButton.setOnAction(event -> addBlockWithOutput(new AppendBlock(this.toplevel, 2)));
appendBlockButton.setOnTouchPressed(event -> addBlockWithOutput(new AppendBlock(this.toplevel, 2)));
this.getChildren().addAll(cancelButton, arbitraryBlockButton, rationalBlockButton, IntegerBlockButton , joinBlockButton, appendBlockButton);
if (type.countArguments() > 0) {
this.getChildren().add(lambdaBlockButton);
}
} else {
Button applyBlockButton = new Button("Apply");
applyBlockButton.setOnAction(event -> addBlockWithInput(new FunApplyBlock(this.toplevel, new ApplyAnchor(type.countArguments()))));
applyBlockButton.setOnTouchPressed(event -> addBlockWithInput(new FunApplyBlock(this.toplevel, new ApplyAnchor(type.countArguments()))));
Button disBlockButton = new Button("Observe");
disBlockButton.setOnAction(event -> addBlockWithInput(new DisplayBlock(this.toplevel)));
disBlockButton.setOnTouchPressed(event -> addBlockWithInput(new DisplayBlock(this.toplevel)));
Button graphBlockButton = new Button("Graph");
graphBlockButton.setOnAction(event -> addBlockWithInput(new GraphBlock(this.toplevel)));
graphBlockButton.setOnTouchPressed(event -> addBlockWithInput(new GraphBlock(this.toplevel)));
Button splitBlockButton = new Button("Splitter");
splitBlockButton.setOnAction(event -> addBlockWithInput(new SplitterBlock(this.toplevel, tupleArity)));
splitBlockButton.setOnTouchPressed(event -> addBlockWithInput(new SplitterBlock(this.toplevel, tupleArity)));
this.getChildren().addAll(cancelButton, disBlockButton, graphBlockButton, splitBlockButton);
if (type.countArguments() > 0) {
this.getChildren().add(applyBlockButton);
}
}
// opening animation
this.setScaleX(0.1);
this.setScaleY(0.1);
ScaleTransition opening = new ScaleTransition(byMouse ? Duration.ONE : Duration.millis(250), this);
opening.setToX(1);
opening.setToY(1);
opening.setOnFinished(e -> this.setMouseTransparent(false));
opening.play();
}
private void addBlockWithInput(Block block) {
this.toplevel.addBlock(block);
block.relocate(this.attachedWire.getEndX(), this.attachedWire.getEndY());
this.close();
if (! block.belongsOnBottom()) {
block.refreshContainer();
}
block.initiateConnectionChanges();
InputAnchor input = block.getAllInputs().get(0);
Connection connection = this.attachedWire.buildConnectionTo(input);
if (connection != null) {
connection.getStartAnchor().initiateConnectionChanges();
}
this.attachedWire.remove();
}
private void addBlockWithOutput(Block block) {
this.toplevel.addBlock(block);
block.relocate(this.attachedWire.getStartX(), this.attachedWire.getStartY() - block.prefHeight(-1));
this.close();
if (! block.belongsOnBottom()) {
block.refreshContainer();
}
block.initiateConnectionChanges();
OutputAnchor output = block.getAllOutputs().get(0);
Connection connection = this.attachedWire.buildConnectionTo(output);
if (connection != null) {
connection.getStartAnchor().initiateConnectionChanges();
}
this.attachedWire.remove();
}
/** Closes this menu by removing it from it's parent. */
public void close() {
this.toplevel.removeMenu(this);
}
}