package bluej.stride.framedjava.errors;

import bluej.editor.EditorWatcher;
import bluej.editor.stride.CodeOverlayPane;
import bluej.editor.stride.CodeOverlayPane.WidthLimit;
import bluej.stride.generic.InteractionManager;
import bluej.utility.Utility;
import bluej.utility.javafx.FXPlatformRunnable;
import bluej.utility.javafx.JavaFXUtil;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.util.Duration;
import threadchecker.OnThread;
import threadchecker.Tag;

import java.util.ArrayList;
import java.util.List;

public class ErrorAndFixDisplay
{       
private static final Duration SHOW_DELAY = Duration.millis(400);
   
   
| Spacing between display and node it refers to, in pixels | private static final double SPACING = 5.0; private final InteractionManager editor; private final CodeError error; private final ErrorFixListener slot; private final VBox vbox = new VBox(); private final List<FixDisplay> fixes = new ArrayList<>(); private int highlighted = -1; private FXPlatformRunnable cancelShow; private boolean showing = false; public boolean isShowing() { return showing; } public static interface ErrorFixListener { @OnThread(Tag.FXPlatform) public void fixedError(CodeError err); } public ErrorAndFixDisplay(InteractionManager editor, CodeError err, ErrorFixListener slot) { this(editor, "", err, slot); } public ErrorAndFixDisplay(InteractionManager editor, String prefix, CodeError err, ErrorFixListener slot) { this.editor = editor; this.error = err; this.slot = slot; vbox.setOnMousePressed(MouseEvent::consume); Label errorLabel = new Label(prefix + err.getMessage()); JavaFXUtil.addStyleClass(errorLabel, "error-label"); vbox.getChildren().add(errorLabel); for (FixSuggestion fix : err.getFixSuggestions()) { FixDisplay l = new FixDisplay(" Fix: " + fix.getDescription()); l.onMouseClickedProperty().set(e -> { recordExecute(fixes.indexOf(l)); fix.execute(); ErrorAndFixDisplay.this.hide(); slot.fixedError(error); e.consume(); }); l.onMouseEnteredProperty().set(e -> setHighlighted(fixes.indexOf(l))); vbox.getChildren().add(l); fixes.add(l); } JavaFXUtil.addStyleClass(vbox, "error-fix-display"); vbox.setMinWidth(250.0); CodeOverlayPane.setDropShadow(vbox); } public CodeError getError() { return error; } @OnThread(Tag.FXPlatform) public void showAbove(final Region n, Duration delay) { if (cancelShow != null) { cancelShow.run(); cancelShow = null; } cancelShow = JavaFXUtil.runAfter(delay, () -> { editor.getCodeOverlayPane().addOverlay(vbox, n, null, vbox.heightProperty().negate().subtract(SPACING), WidthLimit.LIMIT_WIDTH_AND_SLIDE_LEFT); vbox.toBack(); error.focusedProperty().set(true); showing = true; recordShow(); }); } @OnThread(Tag.FXPlatform) private void recordShow() { final EditorWatcher watcher = editor.getFrameEditor().getWatcher(); List<String> fixDisplayText = Utility.mapList(fixes, FixDisplay::getDisplayText); watcher.recordShowErrorMessage(error.getIdentifier(), fixDisplayText); } @OnThread(Tag.FXPlatform) public void showAbove(final Region n) { showAbove(n, SHOW_DELAY); } @OnThread(Tag.FXPlatform) public void showBelow(final Region n) { showBelow(n, SHOW_DELAY); } @OnThread(Tag.FXPlatform) public void showBelow(final Region n, Duration delay) { if (cancelShow != null) { cancelShow.run(); cancelShow = null; } cancelShow = JavaFXUtil.runAfter(delay, () -> { editor.getCodeOverlayPane().addOverlay(vbox, n, null, n.heightProperty().add(SPACING), WidthLimit.LIMIT_WIDTH_AND_SLIDE_LEFT); vbox.toBack(); error.focusedProperty().set(true); showing = true; recordShow(); }); } @OnThread(Tag.FXPlatform) public void hide() { if (cancelShow != null) { cancelShow.run(); cancelShow = null; } editor.getCodeOverlayPane().removeOverlay(vbox); error.focusedProperty().set(false); showing = false; } public boolean hasFixes() { return !fixes.isEmpty(); }
| Called when down is pressed on the slot we are joined to. | Only call this if hasFixes() returns true, otherwise handle keypress differently. | public void down() { setHighlighted(Math.min(fixes.size() - 1, highlighted + 1)); }
| Called when up is pressed on the slot we are joined to. | Only call this if hasFixes() returns true, otherwise handle keypress differently. | public void up() { setHighlighted(Math.max(0, highlighted - 1)); } private void setHighlighted(int newHighlight) { if (highlighted != newHighlight) { if (highlighted != -1) fixes.get(highlighted).setHighlight(false); highlighted = newHighlight; if (highlighted != -1) fixes.get(highlighted).setHighlight(true); } } private static class FixDisplay extends HBox { private final Label enterHint = new Label("\u21B5"); private final String displayText; public FixDisplay(String display) { this.displayText = display; enterHint.setVisible(false); Label l = new Label(display); getChildren().addAll(l, enterHint); HBox.setHgrow(l, Priority.ALWAYS); l.setMaxWidth(9999); } private void setHighlight(boolean highlight) { if (highlight) JavaFXUtil.addStyleClass(this, "fix-highlight"); else{ JavaFXUtil.removeStyleClass(this, "fix-highlight"); } enterHint.setVisible(highlight); } @OnThread(Tag.Any) public String getDisplayText() { return displayText; } } @OnThread(Tag.FXPlatform) public void executeSelected() { if (highlighted != -1) { recordExecute(highlighted); error.getFixSuggestions().get(highlighted).execute(); ErrorAndFixDisplay.this.hide(); slot.fixedError(error); } } @OnThread(Tag.FXPlatform) private void recordExecute(int fixIndex) { final EditorWatcher watcher = editor.getFrameEditor().getWatcher(); watcher.recordFix(error.getIdentifier(), fixIndex); } }
top, use, map, class ErrorAndFixDisplay

.   isShowing

top, use, map, interface ErrorAndFixDisplay . ErrorFixListener

.   fixedError
.   ErrorAndFixDisplay
.   ErrorAndFixDisplay
.   getError
.   showAbove
.   recordShow
.   showAbove
.   showBelow
.   showBelow
.   hide
.   hasFixes
.   down
.   up
.   setHighlighted

top, use, map, class FixDisplay

.   FixDisplay
.   setHighlight
.   getDisplayText
.   executeSelected
.   recordExecute




308 neLoCode + 5 LoComm