diff options
Diffstat (limited to 'src/jcgp/gui/population/GUIOutput.java')
-rw-r--r-- | src/jcgp/gui/population/GUIOutput.java | 343 |
1 files changed, 294 insertions, 49 deletions
diff --git a/src/jcgp/gui/population/GUIOutput.java b/src/jcgp/gui/population/GUIOutput.java index f023d00..d715138 100644 --- a/src/jcgp/gui/population/GUIOutput.java +++ b/src/jcgp/gui/population/GUIOutput.java @@ -1,79 +1,324 @@ package jcgp.gui.population; +import javafx.event.EventHandler; +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 jcgp.backend.population.Gene; +import jcgp.backend.population.Connection; +import jcgp.backend.population.Input; +import jcgp.backend.population.Node; import jcgp.backend.population.Output; +import jcgp.gui.GUI; import jcgp.gui.constants.Constants; -import jcgp.gui.constants.Position; -import jcgp.gui.handlers.OutputHandlers; -/** - * The GUI counterpart of {@link jcgp.backend.population.Output}. This is a - * subclass of {@code GUIGene} which represents a chromosome output. - * - * @author Eduardo Pedroni - */ -public class GUIOutput extends GUIGene implements GUIMutable { +public class GUIOutput extends GUIGene { + private Line sourceLine; private Output output; - private Line line; - - /** - * Instantiate {@code GUIOutput} given an {@code Output} and the line needed - * to show its connection. - * - * @param output the associated backend output. - * @param line the line used to display connection. - */ - public GUIOutput(final Output output, Line line) { + + public GUIOutput(ChromosomePane parentRef, final Output output, Line line, GUI gui) { super(); - // store references, associate with backend object + + this.parent = parentRef; this.output = output; - this.line = line; - output.setGUIObject(this); + this.sourceLine = line; - // create input socket - Circle socket = new Circle(-Constants.NODE_RADIUS, 0, Constants.SOCKET_RADIUS, Constants.SOCKET_PAINT); - socket.setStroke(Paint.valueOf("black")); + relocate(((gui.getExperiment().getResources().columns() + 1) * (2 * Constants.NODE_RADIUS + Constants.SPACING)) + Constants.NODE_RADIUS, + (output.getIndex() * (2 * Constants.NODE_RADIUS + Constants.SPACING)) + Constants.NODE_RADIUS); + + // set the line ends correctly + updateLines(); + updateText(); + + Circle socket = new Circle(-Constants.NODE_RADIUS, 0, Constants.SOCKET_RADIUS, Paint.valueOf("white")); socket.setId(String.valueOf(0)); - Position.connect(line, (GUIGene) ((Gene) output.getSource()).getGUIObject()); - getChildren().add(socket); + socket.setStroke(Paint.valueOf("black")); + + final Label 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(MouseEvent.DRAG_DETECTED, new EventHandler<MouseEvent>() { + @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<MouseEvent>() { + @Override + public void handle(MouseEvent event) { + // user is hovering over connection socket + connectionLabel.setVisible(true); + } + }); + + socket.addEventFilter(MouseEvent.MOUSE_EXITED, new EventHandler<MouseEvent>() { + @Override + public void handle(MouseEvent event) { + // user exits the connection socket + connectionLabel.setVisible(false); + } + }); + + socket.addEventFilter(MouseEvent.MOUSE_PRESSED, new EventHandler<MouseEvent>() { + @Override + public void handle(MouseEvent event) { + // mouse was pressed on the socket + setState(GUIGeneState.SOURCE); + } + }); + + socket.addEventFilter(MouseEvent.MOUSE_DRAGGED, new EventHandler<MouseEvent>() { + @Override + public void handle(MouseEvent event) { + if (!parent.isTarget()) { + sourceLine.setEndX(event.getX() + ((Circle) event.getSource()).getParent().getLayoutX()); + sourceLine.setEndY(event.getY() + ((Circle) event.getSource()).getParent().getLayoutY()); + } + + } + }); + + socket.addEventFilter(MouseEvent.MOUSE_RELEASED, new EventHandler<MouseEvent>() { + @Override + public void handle(MouseEvent event) { + if (event.isStillSincePress()) { + // mouse was released before dragging out of the socket + updateLines(); + setState(GUIGeneState.HOVER); + } else if (getState() == GUIGeneState.SOURCE) { + // no connection has been made, fallback + resetState(); + updateLines(); + } + + } + }); + - // relocate output, add handlers - Position.place(this); - OutputHandlers.addHandlers(this); + /* + * Mouse event handlers on whole gene + */ + addEventFilter(MouseDragEvent.MOUSE_DRAG_ENTERED, new EventHandler<MouseDragEvent>() { + @Override + public void handle(MouseDragEvent event) { + // the drag has entered this node, react appropriately + setState(GUIGeneState.INVALID_TARGET); + } + }); + + addEventFilter(MouseDragEvent.MOUSE_DRAG_EXITED, new EventHandler<MouseDragEvent>() { + @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()) { + setState(GUIGeneState.SOURCE); + } else { + setState(isLocked() ? GUIGeneState.HOVER : GUIGeneState.NEUTRAL); + } + } + } + }); + + addEventFilter(MouseDragEvent.MOUSE_DRAG_RELEASED, new EventHandler<MouseDragEvent>() { + @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.setConnectionStates(GUIGeneState.HOVER); + } else { + source.setState(GUIGeneState.NEUTRAL); + source.setConnectionStates(GUIGeneState.NEUTRAL); + } + + source.updateLines(); + setState(GUIGeneState.HOVER); + } + }); + + + addEventFilter(MouseEvent.MOUSE_ENTERED, new EventHandler<MouseEvent>() { + @Override + public void handle(MouseEvent event) { + // cursor has entered this node without dragging, or it is dragging and this is the source + if (getState() == GUIGeneState.NEUTRAL) { + setState(GUIGeneState.HOVER); + } + } + }); + + addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() { + @Override + public void handle(MouseEvent event) { + setLocked(!isLocked()); + } + }); + + addEventFilter(MouseEvent.MOUSE_EXITED, new EventHandler<MouseEvent>() { + @Override + public void handle(MouseEvent event) { + // cursor has left this node without dragging, or it is dragging and this is the source + if (getState() == GUIGeneState.HOVER && !isLocked()) { + setState(GUIGeneState.NEUTRAL); + setConnectionStates(GUIGeneState.NEUTRAL); + } + } + }); + + + getChildren().addAll(mainCircle, text, socket, connectionLabel); + } - /** - * @return the {@code Output} instance associated with this object. - */ - public Output getOutput() { - return output; + @Override + public void setState(GUIGeneState newState) { + super.setState(newState); + + switch (newState) { + case ACTIVE_HOVER: + break; + case INVALID_TARGET: + mainCircle.setFill(Paint.valueOf(Constants.BAD_SELECTION_COLOUR)); + break; + case HOVER: + mainCircle.setFill(Paint.valueOf(Constants.MEDIUM_HIGHLIGHT_COLOUR)); + sourceLine.setVisible(true); + if (!isLocked()) { + setConnectionStates(GUIGeneState.ACTIVE_HOVER); + } + break; + case INDIRECT_HOVER: + mainCircle.setFill(Paint.valueOf(Constants.SOFT_HIGHLIGHT_COLOUR)); + break; + case NEUTRAL: + mainCircle.setFill(Paint.valueOf(Constants.NEUTRAL_COLOUR)); + sourceLine.setVisible(false); + break; + case NO_CHANGE_TARGET: + mainCircle.setFill(Paint.valueOf(Constants.NEUTRAL_SELECTION_COLOUR)); + break; + case SOURCE: + mainCircle.setFill(Paint.valueOf(Constants.HARD_HIGHLIGHT_COLOUR)); + setConnectionStates(GUIGeneState.NEUTRAL); + setConnectionStates(GUIGeneState.INDIRECT_HOVER); + break; + case VALID_TARGET: + mainCircle.setFill(Paint.valueOf(Constants.GOOD_SELECTION_COLOUR)); + break; + default: + break; + } } - /** - * Associates this instance with a new output. - * - * @param output the new output. - */ - void setOutput(Output output) { - this.output = output; + @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 * Constants.NODE_RADIUS + Constants.SPACING)) + 2 * Constants.NODE_RADIUS); + sourceLine.setEndY((row * (2 * Constants.NODE_RADIUS + Constants.SPACING)) + Constants.NODE_RADIUS); + } else if (output.getSource() instanceof Input) { + int inputIndex = ((Input) output.getSource()).getIndex(); + sourceLine.setEndX(2 * Constants.NODE_RADIUS); + sourceLine.setEndY(inputIndex * (2 * Constants.NODE_RADIUS + Constants.SPACING) + Constants.NODE_RADIUS); + } + } + + @Override + public void setConnectionStates(GUIGeneState newState) { + parent.getGuiGene(output.getSource()).setState(newState); } @Override - public Line[] getLines() { - return new Line[] {line}; + public void resetState() { + if (locked > 0) { + setState(GUIGeneState.HOVER); + setConnectionStates(GUIGeneState.HOVER); + } else { + setState(GUIGeneState.NEUTRAL); + setConnectionStates(GUIGeneState.NEUTRAL); + } } @Override - protected void setLinesVisible(boolean value) { - line.setVisible(value); + protected void setLocked(boolean value) { + locked += value ? 1 : -1; + setConnectionStates(value ? GUIGeneState.HOVER : GUIGeneState.ACTIVE_HOVER); + + parent.getGuiGene(output.getSource()).setLocked(value); + } @Override - public GUIConnection[] getConnections() { - return new GUIConnection[] {(GUIConnection) output.getGUIObject()}; + public void setChangingConnection(Connection newConnection) { + output.setSource(newConnection); + updateText(); } + + @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() + Constants.NODE_RADIUS); + sourceLine.setEndY(gene.getLayoutY()); + } + + public void unlock() { + if (isLocked()) { + setLocked(false); + setState(GUIGeneState.NEUTRAL); + setConnectionStates(GUIGeneState.NEUTRAL); + } + } + + public void lock() { + if (!isLocked()) { + setState(GUIGeneState.HOVER); + setLocked(true); + } + } + + @Override + public void updateText() { + if (parent.isEvaluating()) { + text.setText("O: " + output.getIndex() + "\n" + output.getSource().getValue().toString()); + } else { + text.setText("O: " + output.getIndex()); + } + + } + + public void setOutput(Output newOutput) { + output = newOutput; + } + } |