package bluej.editor.stride;

import java.util.List;

import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ReadOnlyIntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableBooleanValue;
import javafx.beans.value.ObservableValue;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.shape.Rectangle;

import bluej.Config;
import bluej.stride.generic.InteractionManager;
import bluej.utility.javafx.FXPlatformRunnable;
import bluej.utility.javafx.FXRunnable;
import bluej.utility.javafx.JavaFXUtil;


| The bar down the right-hand side of the code that shows small red rectangles where the errors are. | public class ErrorOverviewBar extends VBox{
| The overall state of the code. EDITING means there are fresh frames and thus there | could be errors, but we're not showing them yet if there are. | NO_ERRORS and ERRORS are the other two self-explanatory states when there are no fresh frames. | public static enum ErrorState { NO_ERRORS, ERRORS, EDITING }
| The small icon at the top of the error bad, indicating the ErrorState | private final Label status;
| The rest of the bar, that houses the red rectangles | private final ErrorsBar bar;
| Link to the editor | private final InteractionManager editor;
| The code pane, used to map heights of errors into heights within the scroll pane | private final Pane codeContainer; private final IntegerProperty showingCount = new SimpleIntegerProperty(0);
| | @param editor | @param codeContainer | @param nextError The code to execute in order to move focus to the next error | public ErrorOverviewBar(InteractionManager editor, Pane codeContainer, FXRunnable nextError) { this.editor = editor; this.codeContainer = codeContainer; bar = new ErrorsBar(); status = new Label(); JavaFXUtil.addStyleClass(status, "error-overview-bar-status"); status.setOnMouseClicked(e -> {nextError.run(); e.consume(); }); JavaFXUtil.addStyleClass(this, "error-overview-bar-pane"); getChildren().addAll(status, bar); VBox.setVgrow(bar, Priority.ALWAYS); }
| Information about an error in the code | public static class ErrorInfo { private final String message;
| The Node within the code that corresponds to this error. Used for working out the vertical | position of the error rectangle on the error bar. | private final Node node;
| Callback to give focus to this error (e.g. when the error rectangle is clicked) | private final FXPlatformRunnable giveFocus; private final ObservableBooleanValue visible; private final ObservableBooleanValue focused; ErrorInfo(String message, Node node, ObservableBooleanValue visible, ObservableBooleanValue focused, FXPlatformRunnable giveFocus) { this.message = message; this.node = node; this.visible = visible; this.focused = focused; this.giveFocus = giveFocus; } public boolean isVisible() { return visible.get(); } } public void update(List<ErrorInfo> errors, ErrorState state) { bar.clear(); errors.forEach(bar::add); updateShowingCount(); setState(state); } private void setState(ErrorState state) { switch (state) { case ERRORS: status.setText("\u2717"); status.setTooltip(new Tooltip("" + showingCount.get() + " " + Config.getString(showingCount.get() == 1 ? "frame.error.overview.bar.error.single" : "frame.error.overview.bar.error.plural"))); break; case NO_ERRORS: status.setText("\u2713"); status.setTooltip(new Tooltip(Config.getString("frame.error.overview.bar.error.none"))); break; case EDITING: status.setText("\u270e"); status.setTooltip(new Tooltip(Config.getString("frame.error.overview.bar.error.editing"))); break; } JavaFXUtil.selectPseudoClass(status, state.ordinal(), "bj-success", "bj-failure", "bj-editing"); } private void updateShowingCount() { showingCount.set(bar.calculateShowing()); if (showingCount.get() > 0) { setState(ErrorState.ERRORS); } } public ReadOnlyIntegerProperty showingCount() { return showingCount; } private class ErrorsBar extends Pane { public void clear() { getChildren().forEach(x -> ((Error)x).cleanup()); getChildren().clear(); } public void add(ErrorInfo info) { double posTop = codeContainer.sceneToLocal(info.node.localToScene(0, 0)).getY() / codeContainer.getHeight(); Error e = new Error(info.message, info.focused, info.visible, info.giveFocus); e.visibleProperty().bind(info.visible); e.setManaged(false); e.setX(1.0); e.setHeight(8.0); e.setY(posTop * getHeight()); e.setWidth(getWidth() - 2.0); getChildren().add(e); } public int calculateShowing() { return (int)getChildren().stream().filter(x -> x.visibleProperty().get()).count(); } } private class Error extends Rectangle implements ChangeListener<Boolean> { private final ObservableBooleanValue focused; private final ObservableBooleanValue visible; public Error(String message, ObservableBooleanValue focused, ObservableBooleanValue visible, FXPlatformRunnable onClick) { JavaFXUtil.addStyleClass(this, "error-overview-error"); setOnMouseClicked(e -> {onClick.run(); e.consume(); }); this.focused = focused; this.visible = visible; this.focused.addListener(this); this.visible.addListener(this); Tooltip.install(this, new Tooltip(message)); } public void cleanup() { focused.removeListener(this); visible.removeListener(this); } @Override public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) { if (observable == focused) JavaFXUtil.setPseudoclass("bj-showing", newValue, this); else if (observable == visible) updateShowingCount(); } } }
top, use, map, class ErrorOverviewBar

.   ErrorOverviewBar

top, use, map, class ErrorInfo

.   isVisible
.   update
.   setState
.   updateShowingCount
.   showingCount

top, use, map, class ErrorsBar

.   clear
.   add
.   calculateShowing
.   Error
.   cleanup
.   changed




246 neLoCode + 15 LoComm