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