package bluej.stride.generic;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import bluej.stride.framedjava.ast.Parser;
import bluej.stride.framedjava.elements.CodeElement;
import bluej.stride.framedjava.frames.BlankFrame;

import bluej.utility.javafx.ScalableHeightLabel;
import javafx.beans.binding.DoubleExpression;
import javafx.beans.property.ReadOnlyDoubleWrapper;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.geometry.Bounds;
import javafx.scene.Node;
import javafx.scene.effect.PerspectiveTransform;
import javafx.scene.input.DragEvent;
import javafx.scene.input.MouseButton;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.scene.shape.Rectangle;

import bluej.stride.framedjava.frames.CodeFrame;
import bluej.stride.slots.HeaderItem;
import bluej.utility.javafx.JavaFXUtil;
import bluej.utility.javafx.SharedTransition;
import threadchecker.OnThread;
import threadchecker.Tag;


| A canvas: an area that contain several frames in a vertical array. | | Each canvas actually contains: | - N amounts of these triples: | - cursor | - "special" |* - frame * - One final cursor * * Thus, even when there are no subframes, there is always one cursor in the canvas. * * The "special" is a VBox to which you can add extra content between frames, * such as error messages or popup meta-information about a frame. * * @author Fraser McKay */ public class FrameCanvas implements FrameContentItem{ | |private final CanvasParent parentBlock; | |//protected SelectionScope selectionScope; | |protected final InteractionManager editorFrm; | |private final ObservableList<Frame> blockContents = FXCollections.observableArrayList(); | |private final List<VBox> specials = new ArrayList<VBox>(); | |private final List<FrameCursor> cursors = new ArrayList<FrameCursor>(); | |// When editorFrm != null, cursors should always be one larger than blockContents, and thus never empty | |private final CanvasVBox canvas = new CanvasVBox(200.0, blockContents); | |private final SimpleBooleanProperty showingProperty = new SimpleBooleanProperty(true); | |private boolean animateLeftMarginScale; | |private int childBlockIndex(int blockIndex) | |{ | |// block 0 is child 2 | |// block 1 is child 5 | |// etc | |return blockIndex * 3 + 2; | |} | |private int childCursorIndex(int cursorIndex) | |{ | |// cursor 0 is child 0 | |// cursor 1 is child 3 | |// etc | |return (cursorIndex * 3); | |} | |private void validate(FrameCursor cursor, int cursorIndex) | |{ | |validate(); | |if (cursors.get(cursorIndex) != cursor) | |{ | |throw new IllegalStateException("Stable cursor was moved, from: " + cursorIndex + " to: " + cursors.indexOf(cursor)); } } /** * Checks that the contents are valid */ private void validate() { if (cursors.size() > 0 && cursors.size() != blockContents.size() + 1) | |throw new IllegalStateException("Canvas: cursors and blocks out of length sync"); if (canvas.getChildren().size() != cursors.size() + blockContents.size() + specials.size()) throw new IllegalStateException("Canvas: children out of length sync"); for (int i = 0; i < cursors.size(); i++) { if (cursors.get(i) == null) throw new IllegalStateException("Canvas: cursor is null"); if (cursors.indexOf(cursors.get(i)) != i || cursors.lastIndexOf(cursors.get(i)) != i) throw new IllegalStateException("Canvas: cursor is duplicated"); if (canvas.getChildren().get(childCursorIndex(i)) != cursors.get(i).getNode()) throw new IllegalStateException("Canvas: cursors out of sync"); if (cursors.get(i).getParentCanvas() != this) throw new IllegalStateException("Canvas: cursor parent out of sync"); } for (int i = 0; i < blockContents.size(); i++) { if (blockContents.get(i) == null) throw new IllegalStateException("Canvas: block is null"); if (blockContents.indexOf(blockContents.get(i)) != i || blockContents.lastIndexOf(blockContents.get(i)) != i) throw new IllegalStateException("Canvas: block is duplicated"); if (canvas.getChildren().get(childBlockIndex(i)) != blockContents.get(i).getNode()) throw new IllegalStateException("Canvas: blocks out of sync"); if (blockContents.get(i).getParentCanvas() != this) throw new IllegalStateException("Canvas: block parent out of sync"); } for (int i = 0; i < blockContents.size(); i++) { if (specials.get(i) == null) throw new IllegalStateException("Canvas: special is null"); if (specials.indexOf(specials.get(i)) != i || specials.lastIndexOf(specials.get(i)) != i) throw new IllegalStateException("Canvas: special is duplicated"); if (canvas.getChildren().get(childBlockIndex(i) - 1) != specials.get(i)) throw new IllegalStateException("Canvas: specials out of sync"); } } /** * Find if this is embedded in, for example, a "container" construct block like a loop or an if. */ public CanvasParent getParent() { return parentBlock; } public VBox getSpecialBefore(FrameCursor cursor) { | |int index = 0; | |if (cursor == null) | |{ | |index = 0; | |} | |else | |{ | |index = cursors.indexOf(cursor); | |} | |if (index < 0) | |throw new IllegalArgumentException("insertSpecialBefore: canvas does not contain specified cursor"); return specials.get(index); } public VBox getSpecialBefore(Frame f) { return getSpecialAfter(getCursorBefore(f)); } public VBox getSpecialAfter(FrameCursor cursor) | |{ | |int index; | |if (cursor == null) | |{ | |index = cursors.size() - 1; | |} | |else | |{ | |index = cursors.indexOf(cursor); | |} | |if (index < 0) | |throw new IllegalArgumentException("insertSpecialBefore: canvas does not contain specified cursor"); return specials.get(index); } /** * Inserts block before the given cursor (which is guaranteed to end up after the inserted block) */ public void insertBlockBefore(Frame toAdd, FrameCursor cursor) | |{ | |if (toAdd == null) | |throw new IllegalArgumentException("Cannot add null block"); if (!acceptsType(toAdd)) throw new IllegalArgumentException("Block " + getClass() + " does not accept " + toAdd.getClass()); if (toAdd.getParentCanvas() != null) throw new IllegalArgumentException("Block already has parent"); int index = 0; if (cursor == null) { index = 0; cursor = cursors.get(index); } else { index = cursors.indexOf(cursor); | |} | |if (index < 0) | |throw new IllegalArgumentException("insertBlockBefore: canvas does not contain specified cursor"); int childIndex = childCursorIndex(index); FrameCursor newCursor = editorFrm.createCursor(this); VBox special = new VBox(); canvas.getChildren().add(childIndex, toAdd.getNode()); // Add block... | |canvas.getChildren().add(childIndex, special); // Then special before that.. | |canvas.getChildren().add(childIndex, newCursor.getNode()); // Then add new cursor before it | |cursors.add(index, newCursor); | |blockContents.add(index, toAdd); | |specials.add(index, special); | |toAdd.setParentCanvas(this); | |validate(cursor, index + 1); | |} | |/** | Inserts block after the given cursor (which is guaranteed to remain in the same place) | public void insertBlockAfter(Frame toAdd, FrameCursor cursor) { if (toAdd == null) throw new IllegalArgumentException("Cannot add null block"); if (!acceptsType(toAdd)) throw new IllegalArgumentException("Block " + getClass() + " does not accept " + toAdd.getClass()); if (toAdd.getParentCanvas() != null) throw new IllegalArgumentException("Block already has parent"); int index; if (cursor == null) { index = cursors.size() - 1; cursor = cursors.get(index); } else { index = cursors.indexOf(cursor); } if (index < 0) throw new IllegalArgumentException("insertBlockAfter: canvas does not contain specified cursor"); int childIndex = childCursorIndex(index); FrameCursor newCursor = editorFrm.createCursor(this); VBox special = new VBox(); canvas.getChildren().add(childIndex + 1, newCursor.getNode()); canvas.getChildren().add(childIndex + 1, toAdd.getNode()); canvas.getChildren().add(childIndex + 1, special); cursors.add(index + 1, newCursor); blockContents.add(index, toAdd); specials.add(index, special); toAdd.setParentCanvas(this); validate(cursor, index); }
| Remove a given block object from the list. | @param b block to remove | public void removeBlock(Frame b) { int index = blockContents.indexOf(b); if (index < 0) throw new IllegalArgumentException("removeBlock: canvas does not contain specified block"); canvas.getChildren().remove(blockContents.get(index).getNode()); canvas.getChildren().remove(specials.get(index)); canvas.getChildren().remove(cursors.get(index + 1).getNode()); blockContents.remove(index); cursors.remove(index + 1); specials.remove(index); b.cleanup(); b.setParentCanvas(null); validate(); }
| Replaces the block without altering any cursors | public void replaceBlock(Frame old, Frame replacement) { if (replacement == null) throw new IllegalArgumentException("Cannot add null block"); if (!acceptsType(replacement)) throw new IllegalArgumentException("Block " + getClass() + " does not accept " + replacement.getClass()); if (replacement.getParentCanvas() != null) throw new IllegalArgumentException("Block already has parent"); int index = blockContents.indexOf(old); if (index < 0) throw new IllegalArgumentException("replaceBlock: canvas does not contain specified block"); blockContents.set(index, replacement); canvas.getChildren().set(childBlockIndex(index), replacement.getNode()); replacement.setParentCanvas(this); old.cleanup(); old.setParentCanvas(null); validate(); replacement.focusWhenJustAdded(); }
| Gets the block before the cursor. Returns null if it's the first cursor. | public Frame getFrameBefore(FrameCursor cursor) { int index = cursors.indexOf(cursor); if (index < 0) throw new IllegalArgumentException("getBlockBefore: canvas does not contain specified cursor"); else if (index == 0) return null; else{ return blockContents.get(index - 1); } } public Stream getFramesBefore(Frame f) { if (f == null) return blockContents.stream(); int index = blockContents.indexOf(f); if (index == -1) throw new IllegalArgumentException("getFramesBefore: canvas does not contain specified frame"); return blockContents.stream().limit(index); }
| Gets the block after the cursor. Returns null if it's the last cursor. | public Frame getFrameAfter(FrameCursor cursor) { int index = cursors.indexOf(cursor); if (index < 0) throw new IllegalArgumentException("getBlockAfter: canvas does not contain specified cursor"); else if (index == blockContents.size()) return null; else{ return blockContents.get(index); } }
| Gets the blocks after the cursor. Null gets all blocks. | public Stream getFramesAfter(Frame frame) { if (frame == null) return blockContents.stream(); int index = blockContents.indexOf(frame); if (index < 0) throw new IllegalArgumentException("getFramesAfter: canvas does not contain specified frame"); else{ return blockContents.stream().skip(index + 1); } }
| Gets the cursor before the block. Never returns null. | public FrameCursor getCursorBefore(Frame block) { int index = blockContents.indexOf(block); if (index < 0) throw new IllegalArgumentException("getCursorBefore: canvas does not contain specified block"); else{ return cursors.get(index); } }
| Gets the cursor after the block. Never returns null. | public FrameCursor getCursorAfter(Frame block) { int index = blockContents.indexOf(block); if (index < 0) throw new IllegalArgumentException("getCursorAfter: canvas does not contain specified block"); else{ return cursors.get(index + 1); } } public boolean acceptsType(Frame blockOfType) { if (blockOfType != null) { return parentBlock.check(this).canPlace(blockOfType.getClass()); } return false; } public Node getNode() { return canvas; } public <T> List<T> getBlocksSubtype(Class<T> clazz) { List<T> r = new ArrayList<T>(); for (Frame b : blockContents) { if (clazz.isAssignableFrom(b.getClass())) { r.add(clazz.cast(b)); } } return r; } public FrameCursor getPrevCursor(FrameCursor orig, boolean canChangeLevel) { int index = cursors.indexOf(orig); if (index < 0) { throw new IllegalArgumentException("getPrevCursor: cursor not in this canvas"); } else if (index == 0) { if (canChangeLevel) return parentBlock.getCursorBefore(this); else{ return null; } } else { if (canChangeLevel) { FrameCursor c = blockContents.get(index - 1).getLastInternalCursor(); if (c != null) { return c; } } return cursors.get(index - 1); } } public FrameCursor getNextCursor(FrameCursor orig, boolean canChangeLevel) { int index = cursors.indexOf(orig); if (index < 0) { throw new IllegalArgumentException("getPrevCursor: cursor not in this canvas"); } else if (index == cursors.size() - 1) { if (canChangeLevel) { return parentBlock.getCursorAfter(this); } return null; } if (canChangeLevel) { FrameCursor c = blockContents.get(index).getFirstInternalCursor(); if (c != null) { return c; } } return cursors.get(index + 1); }
| Finds closest cursor to given point, even if out of bounds of this block | public FrameCursor findClosestCursor(double sceneX, double sceneY, List<Frame> exclude, boolean isDrag, boolean canDescend) { CursorLoop: for (int i = 0; i < cursors.size(); i++) { FrameCursor c = cursors.get(i); Bounds sceneBounds = c.getSceneBounds(); if (sceneY < sceneBounds.getMaxY()) { return c; } } if (i < blockContents.size()) { Frame b = blockContents.get(i); if (!canDescend || (exclude != null && exclude.contains(b))) { int validCursorAbove = i; while (validCursorAbove >= 1 && (exclude != null && exclude.contains(blockContents.get(validCursorAbove - 1)))) { validCursorAbove -= 1; } int validCursorBelow = i + 1; while (validCursorBelow < blockContents.size() && (exclude != null && exclude.contains(blockContents.get(validCursorBelow)))) { validCursorBelow += 1; } double distToAbove = sceneY - cursors.get(validCursorAbove).getSceneBounds().getMaxY(); double distToBelow = sceneY - cursors.get(validCursorBelow).getSceneBounds().getMinY(); if (distToBelow < 0) if (distToAbove <= -distToBelow) { return cursors.get(validCursorAbove); } else { return cursors.get(validCursorBelow); } } else { i = validCursorBelow - 1; continue CursorLoop; } } else { FrameCursor c = b.findCursor(sceneX, sceneY, cursors.get(i), cursors.get(i + 1), exclude, isDrag, true); if (sceneY < b.lowestCursorY()) { return c; } } } } return getLastCursor(); } public FrameCursor getFirstCursor() { return cursors.get(0); } public FrameCursor getLastCursor() { return cursors.get(cursors.size() - 1); } public Bounds getSceneBounds() { return canvas.localToScene(canvas.getBoundsInLocal()); } public double getHeight() { return canvas.getHeight(); } public int blockCount() { return blockContents.size(); }
| Do not modify the returned list! | | (I did try wrapping this with FXCollections.unmodifiableObservableList, but it | seemed to destroy the observable aspect for any listeners. | public ObservableList getBlockContents() { return blockContents; } public List getFocusableCursors() { return cursors; }
| Gets an ordered list of frames that lie between the two given cursors. | | Both cursors must be non-null and must be in this canvas. However, they can be passed | in either order; calling framesBetween(f, g) will give the same resuly as framesBetween(g, f) | and both are valid. If f == g, you will get an empty list back. | public List framesBetween(FrameCursor a, FrameCursor b) { int early, late; int ai = cursors.indexOf(a); int bi = cursors.indexOf(b); if (ai == -1 || bi == -1) throw new IllegalArgumentException("framesBetween called for a cursor not present in canvas"); if (ai < bi) { early = ai; late = bi; } else if (bi < ai) { early = bi; late = ai; } else { return Collections.emptyList(); } return Collections.unmodifiableList(blockContents.subList(early, late)); } public void shrinkUsing(DoubleExpression animate) { if (canvas.getChildren().size() >= 100) { Rectangle clipRect = new Rectangle(0.0, 0.0); canvas.setClip(clipRect); canvas.maxHeightProperty().set(0); canvas.prefHeightProperty().bind(canvas.maxHeightProperty()); } else { canvas.snapshot(null, null); Rectangle clipRect = new Rectangle(); clipRect.widthProperty().bind(canvas.widthProperty()); clipRect.heightProperty().bind(canvas.maxHeightProperty()); canvas.setClip(clipRect); canvas.maxHeightProperty().bind(animate.multiply(getHeight())); canvas.prefHeightProperty().bind(canvas.maxHeightProperty()); PerspectiveTransform pt = new PerspectiveTransform(); pt.setLlx(0.0); pt.setUlx(0.0); pt.setLrx(canvas.getWidth()); pt.setUrx(canvas.getWidth()); pt.setUly(0.0); pt.setUry(0.0); pt.llyProperty().bind(canvas.maxHeightProperty()); pt.lryProperty().bind(canvas.maxHeightProperty()); canvas.setEffect(pt); } } public void growUsing(DoubleExpression animate) { if (canvas.getChildren().size() < 100) { double calcHeight = blockContents.stream().mapToDouble(f -> f.getRegion().getHeight()).sum(); calcHeight += cursors.stream().mapToDouble(f -> f.getNode().getHeight()).sum(); calcHeight += Math.max(0, blockContents.size() - 1) * canvas.spacingProperty().get(); canvas.maxHeightProperty().bind(animate.multiply(calcHeight)); } animate.addListener((a, b, newVal) -> { if (newVal.doubleValue() >= 0.99) { canvas.maxHeightProperty().unbind(); canvas.prefHeightProperty().unbind(); canvas.setPrefHeight(Region.USE_COMPUTED_SIZE); canvas.setMaxHeight(Double.MAX_VALUE); canvas.setClip(null); canvas.setEffect(null); } }); } public DoubleExpression widthProperty() { return canvas.widthProperty(); }
| Constructor that specifies that this canvas is part of, for example, an "if" or "for" block's canvas area |*/ public FrameCanvas(InteractionManager editor, CanvasParent parent, String stylePrefix) { this.parentBlock = parent; //Default setup canvas.getStyleClass().addAll("frame-canvas", stylePrefix + "frame-canvas"); JavaFXUtil.setPseudoclass("bj-empty", true, canvas); blockContents.addListener((ListChangeListener<Frame>) c -> { boolean empty = blockContents.size() == 0; JavaFXUtil.setPseudoclass("bj-empty", empty, canvas); JavaFXUtil.setPseudoclass("bj-non-empty", !empty, canvas); //Notify parent: parent.modifiedCanvasContent(); }); //Drag canvas.setOnDragOver(new EventHandler <DragEvent>() { @Override | |public void handle(DragEvent event) { | |/* data is dragged over the target
| accept it only if it is not dragged from the same node | and if it has a string data | if (event.getGestureSource() != this && event.getDragboard().hasString()) {
| allow for both copying and moving, whatever user chooses | event.acceptTransferModes(TransferMode.COPY_OR_MOVE); } event.consume(); } }); canvas.setOnMouseClicked(e -> { if (e.getButton() == MouseButton.PRIMARY && e.isStillSincePress()) { editor.clickNearestCursor(e.getSceneX(), e.getSceneY(), e.isShiftDown()); e.consume(); } }); FrameCursor topCursor = editor.createCursor(this); cursors.add(topCursor); canvas.getChildren().add(0, topCursor.getNode()); VBox topSpecial = new VBox(); specials.add(topSpecial); canvas.getChildren().add(1, topSpecial); editorFrm = editor; }
| Empty the canvas of block contents, and move them all to the target canvas, starting at the specified index (in that canvas's children) | @param targetCanvas canvas to export contents to | @param index starting index in other canvas to insert at | public void emptyTo(FrameCanvas targetCanvas, Frame after) { List<Frame> allBlocks = getBlocksSubtype(Frame.class); for (int i = allBlocks.size() - 1; i >= 0; i--) { targetCanvas.insertBlockAfter(allBlocks.get(i), targetCanvas.getCursorAfter(after)); } } public void moveContentsTo(FrameCanvas targetCanvas) { getBlocksSubtype(Frame.class).forEach(b -> { removeBlock(b); targetCanvas.insertBlockAfter(b, targetCanvas.getLastCursor()); }); }
| Get the code inside this section, as text | @return code contained in this container | | | |@Override | |public String toCode(String prefix) | |{} String code = ""; for (CodeFrame b : getBlocksSubtype(CodeFrame.class)) {} //If it's code code += b.toCode(" " + prefix) + "\n"; } return code; } */ /* public void setSelectionScope(SelectionScope scope) {} selectionScope = scope; for (Block bl : getBlocksSubtype(Block.class)) {}bl.setSelectionScope(scope); | |} | |} | |public SelectionScope getSelectionScope() | |{} return selectionScope; | |} public void focusTopCursor() { FrameCursor firstCursor = getFirstCursor(); if (firstCursor == null) { firstCursor = getParent().getCursorAfter(this); } firstCursor.requestFocus(); } public boolean focusBottomCursor() { FrameCursor c = getLastCursor(); if (c != null) { c.requestFocus(); return true; } else{ return false; } }
| | |@Override | |public boolean acceptsType(Class<? extends Block> blockClass) {} //Don't accept blocks for the parameter-canvas | |if (blockClass.equals(MethodParameter.class)) | |{} return false; | |} | |//Don't put methods inside other things - unless it's a class or a commented-out section (you can't put a method in a loop) | |if (MethodBlock.class.isAssignableFrom(blockClass) && !allowsMethods()) | |{} return false; | |} | |//Only allow certain statements inside a class? | |if (isClassCanvas()) | |{}if (MethodBlock.class.isAssignableFrom(blockClass)) | |return true; | |if (blockClass.equals(CommentBlock.class)) | |return true; | |if (blockClass.equals(MultiCommentBlock.class)) | |return true; | |if (blockClass.equals(VarBlock.class)) | |return true; | |if (blockClass.equals(ObjectBlock.class)) | |return true; | |return false; | |} | |//Otherwise | |return true; | |} public void cleanup() { getBlockContents().forEach(f -> f.cleanup()); } public Stream getHeaderItems() { return getBlocksSubtype(Frame.class).stream().flatMap(Frame::getHeaderItems); } private ScalableHeightLabel previewOpeningCurly; private ScalableHeightLabel previewClosingCurly; public void setAnimateLeftMarginScale(boolean animateLeftMarginScale) { this.animateLeftMarginScale = animateLeftMarginScale; } public void setTopOutsideBorderBackgroundPadding(Optional<Double> height) { if (height.isPresent()) canvas.setStyle("-bj-border-insets: " + (-height.get()) + " 0 0 0;"); else{ canvas.setStyle(""); } } @OnThread(Tag.FXPlatform) public void previewCurly(boolean on, boolean affectOpen, boolean affectClose, double sceneX, DoubleExpression openingYAdjust, SharedTransition animate) { if (on) { canvas.addSpace(animate); ReadOnlyDoubleWrapper xOffset = new ReadOnlyDoubleWrapper(sceneX - canvas.localToScene(canvas.getBoundsInLocal()).getMinX()); if (affectOpen) { previewOpeningCurly = new ScalableHeightLabel("{", true); JavaFXUtil.addStyleClass(previewOpeningCurly, "preview-curly"); editorFrm.getCodeOverlayPane().addOverlay(previewOpeningCurly, canvas, xOffset, openingYAdjust); previewOpeningCurly.growToFullHeightWith(animate, true); } if (affectClose) { previewClosingCurly = new ScalableHeightLabel("}", true); JavaFXUtil.addStyleClass(previewClosingCurly, "preview-curly"); editorFrm.getCodeOverlayPane().addOverlay(previewClosingCurly, canvas, xOffset, canvas.heightProperty().subtract(18.0)); previewClosingCurly.growToFullHeightWith(animate, true); } } else { canvas.removeSpace(animate); if (affectOpen) { previewOpeningCurly.shrinkToNothingWith(animate, true); } if (affectClose) { previewClosingCurly.shrinkToNothingWith(animate, true); } animate.addOnStopped(() -> { if (affectOpen) { editorFrm.getCodeOverlayPane().removeOverlay(previewOpeningCurly); previewOpeningCurly = null; } if (affectClose) { editorFrm.getCodeOverlayPane().removeOverlay(previewClosingCurly); previewClosingCurly = null; } }); } } @OnThread(Tag.FXPlatform) public void previewCurly(boolean on, double sceneX, DoubleExpression openingYAdjust, SharedTransition animate) { previewCurly(on, true, true, sceneX, openingYAdjust, animate); } public List getCursors() { return cursors; } public void clear() { while (blockContents.size() > 0) removeBlock(blockContents.get(0)); } } public void setLastInMulti(boolean last) { JavaFXUtil.setPseudoclass("bj-last-canvas", last, canvas); } public SimpleBooleanProperty getShowingProperty() { return showingProperty; } public boolean isAlmostBlank() { return blockContents.stream().allMatch(f -> f instanceof BlankFrame); } public double getBottomMargin() { return canvas.getBottomMargin(); } public DoubleExpression leftMargin() { return canvas.leftMarginProperty(); } public DoubleExpression rightMargin() { return canvas.rightMarginProperty(); } public void setPseudoclass(String name, boolean on) { JavaFXUtil.setPseudoclass(name, on, canvas); }
| Gets the bounds, in scene coordinates, of the contents of the canvas. | | The bounds of getNode() includes the margins of the canvas. This utility method | excludes those margins and just gets the bounds of the actual visible canvas area | (which generally has the rounded rectangle around it) | public Bounds getContentSceneBounds() { return canvas.getContentSceneBounds(); } @Override public Optional getCanvas() { return Optional.of(this); } @Override public Stream getHeaderItemsDeep() { return getHeaderItems(); } @Override public Stream getHeaderItemsDirect() { return Stream.empty(); } @Override public boolean focusBottomEndFromNext() { return focusBottomCursor(); } @Override public boolean focusLeftEndFromPrev() { focusTopCursor(); return true; } @Override public boolean focusRightEndFromNext() { return focusBottomCursor(); } @Override public boolean focusTopEndFromPrev() { focusTopCursor(); return true; } @Override public void setView(Frame.View oldView, Frame.View newView, SharedTransition animation) { canvas.animateColorsToPseudoClass("bj-java-preview", newView == Frame.View.JAVA_PREVIEW, animation); if (animateLeftMarginScale) { if (newView == Frame.View.JAVA_PREVIEW) { canvas.leftMarginScaleProperty().bind(animation.getOppositeProgress()); animation.addOnStopped(canvas.leftMarginScaleProperty()::unbind); } else { canvas.leftMarginScaleProperty().bind(animation.getProgress()); animation.addOnStopped(canvas.leftMarginScaleProperty()::unbind); } } } public void restore(List<? extends CodeElement> elements, InteractionManager editor) { Map<String, List<Frame>> existingLookup = new HashMap<>(); List<String> existingList = new ArrayList<>(); for (CodeFrame f : getBlocksSubtype(CodeFrame.class)) { String xml = f.getCode().toXML().toXML(); existingLookup.merge(xml, new ArrayList<>(Arrays.asList((Frame) f)), (a, b) -> { a.addAll(b); return a; }); existingList.add(xml); } List<String> newContentXML = elements.stream().map(el -> el.toXML().toXML()).collect(Collectors.toList()); if (existingList.size() == newContentXML.size()) { int numDiff = 0; int lastDiff = -1; for (int i = 0; i < existingList.size(); i++) { if (existingList.get(i).equals(newContentXML.get(i)) == false) { numDiff += 1; lastDiff = i; } } if (numDiff == 0) { return; } else if (numDiff == 1) { if (blockContents.get(lastDiff).tryRestoreTo(elements.get(lastDiff))) { return; } } } List<Frame> newContents = new ArrayList<>(elements.size()); for (int i = 0; i < elements.size(); i++) { List<Frame> fs = existingLookup.get(newContentXML.get(i)); if (fs != null && fs.size() > 0) { newContents.add(fs.remove(0)); } else { newContents.add(elements.get(i).createFrame(editor)); } } while (blockContents.size() > 0) { removeBlock(blockContents.get(blockContents.size() - 1)); } for (Frame f : newContents) { insertBlockAfter(f, getLastCursor()); } } public double getCurlyBracketHeight() { return canvas.getCurlyBracketHeight(); } public Parser.JavaContext getContext() { switch (getParent().getChildKind(this)) { case IMPORTS: return Parser.JavaContext.TOP_LEVEL; case STATEMENTS: return Parser.JavaContext.STATEMENT; default: return Parser.JavaContext.CLASS_MEMBER; } } }

.   insertBlockAfter
.   removeBlock
.   replaceBlock
.   getFrameBefore
.   getFramesBefore
.   getFrameAfter
.   getFramesAfter
.   getCursorBefore
.   getCursorAfter
.   acceptsType
.   getNode
.   getPrevCursor
.   getNextCursor
.   findClosestCursor
.   getFirstCursor
.   getLastCursor
.   getSceneBounds
.   getHeight
.   blockCount
.   getBlockContents
.   getFocusableCursors
.   framesBetween
.   shrinkUsing
.   growUsing
.   widthProperty
.   emptyTo
.   moveContentsTo
.   focusTopCursor
.   focusBottomCursor
.   cleanup
.   getHeaderItems
.   setAnimateLeftMarginScale
.   setTopOutsideBorderBackgroundPadding
.   previewCurly
.   previewCurly
.   getCursors
.   clear
.   setLastInMulti
.   getShowingProperty
.   isAlmostBlank
.   getBottomMargin
.   leftMargin
.   rightMargin
.   setPseudoclass
.   getContentSceneBounds
.   getCanvas
.   getHeaderItemsDeep
.   getHeaderItemsDirect
.   focusBottomEndFromNext
.   focusLeftEndFromPrev
.   focusRightEndFromNext
.   focusTopEndFromPrev
.   setView
.   restore
.   getCurlyBracketHeight
.   getContext




1117 neLoCode + 142 LoComm