package jcgp.gui.population; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.event.EventHandler; import javafx.geometry.VPos; import javafx.scene.control.Label; import javafx.scene.input.MouseDragEvent; import javafx.scene.input.MouseEvent; import javafx.scene.paint.Paint; import javafx.scene.shape.Circle; import javafx.scene.shape.Line; import javafx.scene.text.Font; import javafx.scene.text.Text; import javafx.scene.text.TextAlignment; import jcgp.GUI; import jcgp.population.Connection; import jcgp.population.Input; import jcgp.population.Node; import jcgp.population.Output; public class GUIOutput extends GUIGene { private Circle socket; private Label connectionLabel; private Line sourceLine; private Output output; public GUIOutput(ChromosomePane parentRef, final Output output, Line line) { this.parent = parentRef; this.output = output; this.sourceLine = line; relocate((((int) GUI.resources.get("columns") + 1) * (2 * NODE_RADIUS + SPACING)) + NODE_RADIUS, (output.getIndex() * (2 * NODE_RADIUS + SPACING)) + NODE_RADIUS); // set the line ends correctly updateLines(); mainCircle = new Circle(NODE_RADIUS, Paint.valueOf("white")); mainCircle.setStroke(Paint.valueOf("black")); text = new Text("O: " + output.getIndex()); text.setFont(Font.font("Arial", 12)); text.setTextOrigin(VPos.CENTER); text.setTextAlignment(TextAlignment.CENTER); text.setWrappingWidth(NODE_RADIUS * 2); text.setX(-NODE_RADIUS); text.setVisible(true); socket = new Circle(-NODE_RADIUS, 0, SOCKET_RADIUS, Paint.valueOf("white")); socket.setId(String.valueOf(0)); socket.setStroke(Paint.valueOf("black")); connectionLabel = new Label("S"); connectionLabel.setStyle("-fx-background-color:rgb(255, 255, 255); -fx-border-color:rgba(0, 0, 0, 0.5); "); connectionLabel.relocate(socket.getCenterX() + 5, socket.getCenterY() - 10); connectionLabel.setVisible(false); /* * Mouse event handlers on sockets * */ socket.addEventFilter(MouseDragEvent.DRAG_DETECTED, new EventHandler() { @Override public void handle(MouseEvent event) { // the mouse has been dragged out of the socket, this means a full drag is in progress startFullDrag(); } }); socket.addEventFilter(MouseEvent.MOUSE_ENTERED, new EventHandler() { @Override public void handle(MouseEvent event) { // user is hovering over connection socket connectionLabel.setVisible(true); } }); socket.addEventFilter(MouseEvent.MOUSE_EXITED, new EventHandler() { @Override public void handle(MouseEvent event) { // user exits the connection socket connectionLabel.setVisible(false); } }); socket.addEventFilter(MouseDragEvent.MOUSE_PRESSED, new EventHandler() { @Override public void handle(MouseEvent event) { // mouse was pressed on the socket stateProperty.set(GUIGeneState.SOURCE); } }); socket.addEventFilter(MouseDragEvent.MOUSE_DRAGGED, new EventHandler() { @Override public void handle(MouseEvent event) { sourceLine.setEndX(event.getX() + ((Circle) event.getSource()).getParent().getLayoutX()); sourceLine.setEndY(event.getY() + ((Circle) event.getSource()).getParent().getLayoutY()); } }); socket.addEventFilter(MouseDragEvent.MOUSE_RELEASED, new EventHandler() { @Override public void handle(MouseEvent event) { if (event.isDragDetect()) { // mouse was released before dragging out of the socket updateLines(); stateProperty.set(GUIGeneState.HOVER); } } }); /* * Mouse event handlers on whole gene */ addEventFilter(MouseDragEvent.MOUSE_DRAG_ENTERED, new EventHandler() { @Override public void handle(MouseDragEvent event) { // the drag has entered this node, react appropriately stateProperty.set(GUIGeneState.FORBIDDEN_TARGET); } }); addEventFilter(MouseDragEvent.MOUSE_DRAG_EXITED, new EventHandler() { @Override public void handle(MouseDragEvent event) { // the drag has exited this node, react appropriately // this happens even if we are the source of the drag if (event.isPrimaryButtonDown()) { if (event.getGestureSource() == event.getSource()) { stateProperty.set(GUIGeneState.SOURCE); } else { stateProperty.set(isLocked() ? GUIGeneState.HOVER : GUIGeneState.NEUTRAL); } } } }); addEventFilter(MouseDragEvent.MOUSE_DRAG_RELEASED, new EventHandler() { @Override public void handle(MouseDragEvent event) { // making a connection to an output is illegal // set states to reflect the new situation GUIGene source = ((GUIGene) event.getGestureSource()); if (source.isLocked()) { source.setState(GUIGeneState.HOVER); source.setConnections(GUIGeneState.HOVER); } else { source.setState(GUIGeneState.NEUTRAL); source.setConnections(GUIGeneState.NEUTRAL); } source.updateLines(); stateProperty.set(GUIGeneState.HOVER); } }); addEventFilter(MouseEvent.MOUSE_ENTERED, new EventHandler() { @Override public void handle(MouseEvent event) { // cursor has entered this node without dragging, or it is dragging and this is the source if (stateProperty.get() == GUIGeneState.NEUTRAL) { stateProperty.set(GUIGeneState.HOVER); } } }); addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler() { @Override public void handle(MouseEvent event) { setLocked(!isLocked()); } }); addEventFilter(MouseEvent.MOUSE_EXITED, new EventHandler() { @Override public void handle(MouseEvent event) { // cursor has left this node without dragging, or it is dragging and this is the source if (stateProperty.get() == GUIGeneState.HOVER) { if (isLocked()) { } else { stateProperty.set(GUIGeneState.NEUTRAL); setConnections(GUIGeneState.NEUTRAL); } } } }); getChildren().addAll(mainCircle, text, socket, connectionLabel); stateProperty.addListener(new ChangeListener() { @Override public void changed(ObservableValue observable, GUIGeneState oldValue, GUIGeneState newValue) { switch (newValue) { case ACTIVE_HOVER: break; case FORBIDDEN_TARGET: mainCircle.setFill(Paint.valueOf(GUI.BAD_SELECTION_COLOUR)); break; case HOVER: mainCircle.setFill(Paint.valueOf(GUI.MEDIUM_HIGHLIGHT_COLOUR)); sourceLine.setVisible(true); if (!isLocked()) { setConnections(GUIGeneState.ACTIVE_HOVER); } break; case INDIRECT_HOVER: mainCircle.setFill(Paint.valueOf(GUI.SOFT_HIGHLIGHT_COLOUR)); break; case NEUTRAL: mainCircle.setFill(Paint.valueOf(GUI.NEUTRAL_COLOUR)); sourceLine.setVisible(false); break; case NO_CHANGE_TARGET: mainCircle.setFill(Paint.valueOf(GUI.NEUTRAL_SELECTION_COLOUR)); break; case SOURCE: mainCircle.setFill(Paint.valueOf(GUI.HARD_HIGHLIGHT_COLOUR)); setConnections(GUIGeneState.NEUTRAL); setConnections(GUIGeneState.INDIRECT_HOVER); break; case TARGET: mainCircle.setFill(Paint.valueOf(GUI.GOOD_SELECTION_COLOUR)); break; default: break; } } }); output.sourceProperty().addListener(new ChangeListener() { @Override public void changed(ObservableValue observable, Connection oldValue, Connection newValue) { updateLines(); } }); } @Override public void updateLines() { if (output.getSource() instanceof Node) { int row = ((Node) output.getSource()).getRow(), column = ((Node) output.getSource()).getColumn(); sourceLine.setEndX(((column + 1) * (2 * NODE_RADIUS + SPACING)) + 2 * NODE_RADIUS); sourceLine.setEndY((row * (2 * NODE_RADIUS + SPACING)) + NODE_RADIUS); } else if (output.getSource() instanceof Input) { int inputIndex = ((Input) output.getSource()).getIndex(); sourceLine.setEndX(2 * NODE_RADIUS); sourceLine.setEndY(inputIndex * (2 * NODE_RADIUS + SPACING) + NODE_RADIUS); } } @Override public Output getGene() { return output; } @Override public void setConnections(GUIGeneState newState) { parent.getGuiGene(output.getSource()).setState(newState); } @Override public void resetState() { stateProperty.set(GUIGeneState.NEUTRAL); setConnections(GUIGeneState.NEUTRAL); } @Override public void setLocked(boolean value) { locked += value ? 1 : -1; setConnections(value ? GUIGeneState.HOVER : GUIGeneState.ACTIVE_HOVER); parent.getGuiGene(output.getSource()).setLocked(value); } @Override public void setChangingConnection(Connection newConnection) { output.setConnection(0, newConnection); } @Override public Connection getChangingConnection() { return output.getSource(); } @Override public void addLocks(int value) { locked += value; } @Override public void removeLocks(int value) { locked -= value; } @Override public void setConnectionLine(GUIGene gene) { sourceLine.setEndX(gene.getLayoutX() + NODE_RADIUS); sourceLine.setEndY(gene.getLayoutY()); } }