package bluej.stride.framedjava.frames;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import javafx.animation.ScaleTransition;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ObservableBooleanValue;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.util.Duration;
import bluej.stride.framedjava.ast.HighlightedBreakpoint;
import bluej.stride.generic.CanvasParent;
import bluej.stride.generic.Frame;
import bluej.stride.generic.FrameCanvas;
import bluej.stride.generic.FrameCursor;
import bluej.utility.javafx.FXPlatformSupplier;
import bluej.utility.javafx.JavaFXUtil;
import threadchecker.OnThread;
import threadchecker.Tag;
public class DebugInfo
{
private final IdentityHashMap<FrameCursor, Display> displays = new IdentityHashMap<>();
@OnThread(value = Tag.Any, requireSynchronized = true)
private Map<String,DebugVarInfo> prevState, state;
private final SimpleBooleanProperty showVars = new SimpleBooleanProperty(false);
private int stateIndex;
@OnThread(Tag.Any)
public DebugInfo()
{
}
@OnThread(Tag.Any)
public synchronized void addVarState(Map<String,DebugVarInfo> state, int index)
{
this.prevState = this.state;
this.state = state;
this.stateIndex = index;
}
@OnThread(Tag.FXPlatform)
public synchronized Display getInfoDisplay(FrameCursor f, Node frameNode, String stylePrefix, boolean isBeforeBreakpointFrame)
{
if (displays.containsKey(f))
{
displays.get(f).addState(prevState, state, stateIndex);
return displays.get(f);
}
else
{
Display d = new Display(prevState, state, stateIndex, frameNode, stylePrefix, isBeforeBreakpointFrame);
Frame frame = f.getFrameAfter();
while (frame != null)
{
frame = Optional.ofNullable(frame.getParentCanvas()).map(FrameCanvas::getParent).map(CanvasParent::getFrame).orElse(null);
if (frame != null && frame instanceof WhileFrame)
FrameCursor cursor = frame.getCursorBefore();
if (displays.containsKey(cursor))
displays.get(cursor).addChild(d);
}
}
displays.put(f, d);
return d;
}
}
public void removeAllDisplays(List<Node> disps)
{
Iterator<Entry<FrameCursor, Display>> it = displays.entrySet().iterator();
while (it.hasNext())
{
if (disps.contains(it.next().getValue()))
{
it.remove();
}
}
}
public void hideAllDisplays()
{
displays.forEach((cursor, display) -> {
cursor.getParentCanvas().getSpecialBefore(cursor).getChildren().remove(display);
});
displays.clear();
}
public void bindVarVisible(ObservableBooleanValue showVars)
{
this.showVars.bind(showVars);
}
public class Display
extends AnchorPane implements HighlightedBreakpoint
{
private final ObservableList<Pane> varDisplay = FXCollections.observableArrayList();
private final ArrayList<Integer> varIndexes = new ArrayList<>();
private final Node frameNode;
private final SimpleIntegerProperty curDisplay = new SimpleIntegerProperty(-1);
private final Label curCounter;
private final boolean isBreakpointFrame;
private final ObservableList<Display> children = FXCollections.observableArrayList();
private final BooleanBinding showControls;
private Display parent = null;
@OnThread(Tag.FXPlatform)
public Display(Map<String, DebugVarInfo> prevVars, Map<String, DebugVarInfo> vars, int varIndex, Node frameNode, String stylePrefix, boolean isBreakpointFrame)
{
this.isBreakpointFrame = isBreakpointFrame;
this.frameNode = frameNode;
HBox controls = new HBox();
curCounter = new Label("1/1");
curCounter.textProperty().bind(curDisplay.add(1).asString().concat("/").concat(Bindings.size(varDisplay).asString()));
Label leftArrow = new Label("<");
Label rightArrow = new Label(">");
JavaFXUtil.addStyleClass(curCounter, "debug-info-number");
JavaFXUtil.addStyleClass(leftArrow, "debug-info-arrow");
JavaFXUtil.addStyleClass(rightArrow, "debug-info-arrow");
leftArrow.setOnMouseClicked(e -> {left(); e.consume();
});
rightArrow.setOnMouseClicked(e -> {right(); e.consume();
});
controls.getChildren().addAll(leftArrow, curCounter, rightArrow);
AnchorPane.setTopAnchor(controls, 2.0);
AnchorPane.setRightAnchor(controls, 5.0);
showControls = Bindings.size(varDisplay).greaterThan(1).and(Bindings.isNotEmpty(children));
controls.managedProperty().bind(showControls);
controls.visibleProperty().bind(showControls);
getChildren().add(controls);
JavaFXUtil.addStyleClass(this, "debug-info-surround");
if (stylePrefix != null && !stylePrefix.isEmpty())
JavaFXUtil.setPseudoclass("bj-" + stylePrefix + "debug", true, this);
curDisplay.addListener((prop, prev, now) -> {
if (prev.intValue() >= 0 && prev.intValue() < varDisplay.size())
getChildren().remove(varDisplay.get(prev.intValue()));
if (now.intValue() >= 0 && now.intValue() < varDisplay.size())
getChildren().add(0, varDisplay.get(now.intValue()));
updateChildren();
});
varDisplay.addListener((ListChangeListener<? super Pane>)c -> {
if (parent == null && curDisplay.get() == varDisplay.size() - 1)
getChildren().add(0, varDisplay.get(curDisplay.get()));
});
addState(prevVars, vars, varIndex);
}
@OnThread(Tag.FXPlatform)
private Pane makeDisplay(Map<String, DebugVarInfo> prevVars,
Map<String, DebugVarInfo> vars) {
GridPane disp = new GridPane();
disp.setHgap(20);
JavaFXUtil.addStyleClass(disp, "debug-info-rows");
int index = 0;
for (Map.Entry<String, DebugVarInfo> var : vars.entrySet())
{
DebugVarInfo prev = prevVars == null ? null : prevVars.get(var.getKey());
HBox row = new HBox();
Label k = new Label(var.getKey() + ": ");
k.getStyleClass().add("debug-info-text");
Node v = var.getValue().getDisplay(prev);
v.getStyleClass().add("debug-info-text");
row.getChildren().addAll(k, v);
disp.add(row, index % 3, index / 3);
row.getStyleClass().add("debug-info");
row.managedProperty().bind(showVars);
row.visibleProperty().bind(showVars);
index += 1;
}
return disp;
}
@OnThread(Tag.FXPlatform)
public void addState(Map<String, DebugVarInfo> prevVars, Map<String, DebugVarInfo> vars, int varIndex)
{
Pane disp = makeDisplay(prevVars, vars);
varIndexes.add(varIndex);
varDisplay.add(disp);
if (parent == null)
curDisplay.set(varDisplay.size() - 1);
AnchorPane.setTopAnchor(disp, 1.0);
AnchorPane.setLeftAnchor(disp, 1.0);
AnchorPane.setBottomAnchor(disp, 1.0);
AnchorPane.setRightAnchor(disp, 1.0);
JavaFXUtil.setPseudoclass("bj-highlight", false, displays.values().toArray(new Node[0]));
JavaFXUtil.setPseudoclass("bj-highlight", true, this);
pulse();
}
private void left()
{
if (curDisplay.get() > 0)
{
curDisplay.set(curDisplay.get() - 1);
}
}
private void right()
{
if (curDisplay.get() < varDisplay.size() - 1)
{
curDisplay.set(curDisplay.get() + 1);
}
}
private void pulse()
{
|
|
|ScaleTransition st = new ScaleTransition(Duration.millis(200), this);
|
|st.setByX(0.3);
|
|st.setByY(0.3);
|
|st.setAutoReverse(true);
|
|st.setCycleCount(2);
|
|st.play();
}
@Override
public void removeHighlight()
{
}
@Override
public Node getNode()
{
return this;
}
@Override
public @OnThread(Tag.FXPlatform) double getYOffset()
{
return 5;
}
@Override
public @OnThread(Tag.FXPlatform) double getYOffsetOfTurnBack()
{
return frameNode.localToScene(frameNode.getBoundsInLocal()).getMaxY() - 5 - localToScene(getBoundsInLocal()).getMinY();
}
@Override
public @OnThread(Tag.FXPlatform) boolean isBreakpointFrame()
{
return isBreakpointFrame;
}
public void addChild(Display child)
{
if (!children.contains(child))
{
children.add(child);
child.parent = this;
child.varDisplay.addListener((ListChangeListener<? super Pane>)c -> updateChildren());
}
}
private void updateChildren()
{
for (Display child : children)
{
int lowerBound = curDisplay.get() >= 0 ? (isLatest() && curDisplay.get() >= 1 ? varIndexes.get(curDisplay.get() - 1) : varIndexes.get(curDisplay.get())) : -1;
int upperBound = curDisplay.get() + 1 < varIndexes.size() ? varIndexes.get(curDisplay.get() + 1) : Integer.MAX_VALUE;
child.curDisplay.set(child.varIndexes.indexOf(child.varIndexes.stream().filter(varIndex -> varIndex >= lowerBound && varIndex <= upperBound).findFirst().orElse(-1)));
}
}
@Override
public @OnThread(Tag.FXPlatform) boolean showExec(int index)
{
if (curDisplay.get() < 0)
return false;
if (curDisplay.get() < varDisplay.size())
{
if (!varIndexes.isEmpty() && varIndexes.get(0) == index && !children.isEmpty())
return true;
if (varIndexes.get(curDisplay.get()) == index && (children.isEmpty() || isLatest()))
return true;
if (curDisplay.get() + 1 < varIndexes.size() && varIndexes.get(curDisplay.get() + 1) == index && !children.isEmpty())
return true;
}
return false;
|
|
|return curDisplay.get() >= 0 &&
|
|((curDisplay.get() < varDisplay.size() &&
|
|(varIndexes.get(curDisplay.get()) == index ||
|
|(!children.isEmpty() && curDisplay.get() > 0 && varIndexes.get(curDisplay.get() - 1) == index)))
|
||| (parent != null && parent.curDisplay.get() > 0 && parent.curDisplay.get() < parent.varDisplay.size() && parent.varIndexes.get(parent.curDisplay.get() - 1) == index - 1));
}
| Is the latest index on this later than all children?
| @return
|
private boolean isLatest()
{
return varIndexes.get(curDisplay.get()) >= children.stream().mapToInt(c -> c.varIndexes.isEmpty() ? -1 : c.varIndexes.get(c.varIndexes.size() - 1)).max().orElse(-1);
}
}
}
top,
use,
map,
class DebugInfo
. DebugInfo
. addVarState
. getInfoDisplay
. removeAllDisplays
. hideAllDisplays
. bindVarVisible
top,
use,
map,
class Display
. Display
. makeDisplay
. addState
. left
. right
. pulse
. removeHighlight
. getNode
. addChild
. updateChildren
. isLatest
395 neLoCode
+ 15 LoComm