package bluej.stride.generic;
import java.util.ArrayList;
import java.util.List;
import javafx.beans.binding.DoubleExpression;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.effect.BlendMode;
import javafx.scene.paint.Color;
import bluej.stride.framedjava.frames.StrideDictionary;
import bluej.utility.javafx.FXConsumer;
import bluej.utility.javafx.JavaFXUtil;
import bluej.utility.javafx.SharedTransition;
import threadchecker.OnThread;
import threadchecker.Tag;
| A statement block with a canvas underneath for embedding additional blocks - used for if statements, loops, etc.
| @author Fraser McKay
|
public abstract class SingleCanvasFrame
extends Frame implements CanvasParent{
protected FrameCanvas canvas;
private Sidebar sidebar;
private boolean collapsible = false;
private boolean collapsed = false;
private PlusMinus collapsedIndicator;
| @param caption
| @param stylePrefix
|
protected SingleCanvasFrame(final InteractionManager editor, String caption, String stylePrefix)
{
super(editor, caption, stylePrefix);
canvas = createCanvas(editor, stylePrefix);
sidebar = Sidebar.addSidebar(editor, getSidebarContainer(), getNode().layoutBoundsProperty(), stylePrefix);
contents.setAll(getHeaderRow(), canvas);
}
private static class PlusMinus
extends Canvas
{
private final static double DIAMETER = 15;
private final static double BAR_WIDTH = 2;
public PlusMinus()
{
setWidth(DIAMETER + 5);
setHeight(DIAMETER);
drawPlus(false);
}
public void drawPlus(boolean light)
{
drawMinus(light);
GraphicsContext gc = getGraphicsContext2D();
gc.clearRect(DIAMETER / 2 - BAR_WIDTH / 2, 0, BAR_WIDTH, DIAMETER);
}
public void drawMinus(boolean light)
{
GraphicsContext gc = getGraphicsContext2D();
gc.clearRect(0, 0, getWidth(), getHeight());
gc.setGlobalBlendMode(BlendMode.SRC_OVER);
gc.setFill(Color.rgb(0, 0, 0, light ? 0.1 : 0.2));
gc.fillOval(0, 0, DIAMETER, DIAMETER);
gc.clearRect(0, DIAMETER / 2 - BAR_WIDTH / 2, DIAMETER, BAR_WIDTH);
}
}
protected void setCollapsible(boolean collapsible)
{
if (collapsible != this.collapsible)
{
if (collapsible)
{
collapsedIndicator = new PlusMinus();
collapsedIndicator.setCursor(Cursor.HAND);
collapsedIndicator.setOnMouseClicked(e ->
{
setCollapsed(!collapsed);
e.consume();
});
collapsedIndicator.setOnMouseEntered(e ->
{
if (collapsed)
collapsedIndicator.drawPlus(true);
else{ collapsedIndicator.drawMinus(true);
}
});
collapsedIndicator.setOnMouseExited(e ->
{
if (collapsed)
collapsedIndicator.drawPlus(false);
else{ collapsedIndicator.drawMinus(false);
}
});
addTopRight(collapsedIndicator);
}
else
{
}
this.collapsible = collapsible;
}
}
@Override
public boolean isCollapsible()
{
return collapsible;
}
@Override
public void setCollapsed(boolean collapse)
{
if (collapse && !collapsible)
return;
if (collapse != this.collapsed)
{
if (collapse) {
collapsedIndicator.drawPlus(false);
}
else {
collapsedIndicator.drawMinus(false);
}
this.collapsed = collapse;
}
}
| Dissolves/deletes the "edges" of the container, so that contents remain intact, on the container's own parent
|* @see expandContents()
*/
public void dissolve()
{
this.expandContents();
getParentCanvas().removeBlock(this);
|
|}
|
|/**
| Remove all children and dump them in this container's parent, expanding contents out of container
|
public void expandContents()
{
canvas.emptyTo(p, this);
}
| Get this block's internal canvas that contains its inner body (for a loop, etc.).
| @return This container's body canvas
|
public FrameCanvas getCanvas()
{
return canvas;
}
| Gets an update function for the sidebar.
| (Curried refers to functional programming term, meaning a function that
| takes one argument and returns a function which consumes the next)
|
protected FXConsumer updateSidebarCurried(String prefix)
{
return content -> sidebar.textProperty().set(prefix + "(" + content + ")");
}
protected void setSidebar(String content)
{
sidebar.setText(content);
}
@Override
public double lowestCursorY()
{
return canvas.getSceneBounds().getMaxY();
}
|
|
|@Override
|
|protected void setPreviewMode(PreviewMode view)
|
|{}if (view == PreviewMode.EXPAND_OUT)
|
|{} canvas.getNode().setEffect(new DropShadow(10.0, Color.BLACK));
|
|Timeline t = new Timeline();
|
|KeyValue kv1 = new KeyValue(canvas.getNode().translateXProperty(), -40);
|
|KeyValue kv2 = new KeyValue(canvas.getNode().translateYProperty(), 20);
|
|KeyValue kv3 = new KeyValue(getRegion().maxHeightProperty(), 0);
|
|BorderPane.setMargin(getRegion(), new Insets(0, 0, canvas.getHeight() - 20, 0));
|
|KeyFrame kf1 = new KeyFrame(Duration.millis(500), kv1);
|
|KeyFrame kf2 = new KeyFrame(Duration.millis(500), kv2);
|
|KeyFrame kf3 = new KeyFrame(Duration.millis(500), kv3);
|
|t.getKeyFrames().add(kf1);
|
|t.getKeyFrames().add(kf2);
|
|t.getKeyFrames().add(kf3);
|
|t.play();
|
|}
|
|else
|
|{}if (view == PreviewMode.NONE)
|
|{}canvas.getNode().setTranslateX(0);
|
|canvas.getNode().setTranslateY(0);
|
|canvas.getNode().setEffect(null);
|
|getRegion().setMaxHeight(Region.USE_COMPUTED_SIZE);
|
|BorderPane.setMargin(getRegion(), new Insets(0));
|
|}
|
|super.setPreviewMode(view);
|
|}
|
|}
protected FrameCanvas createCanvas(InteractionManager editor, String stylePrefix)
{
return new FrameCanvas(editor, this, stylePrefix);
}
@Override
public Frame getFrame()
{
return this;
}
@Override
@OnThread(Tag.FXPlatform)
public void setView(View oldView, View newView, SharedTransition animateProgress)
{
super.setView(oldView, newView, animateProgress);
JavaFXUtil.setPseudoclass("bj-java-preview", newView == View.JAVA_PREVIEW, sidebar.getStyleable());
canvas.getCursors().forEach(c -> c.setView(newView, animateProgress));
canvas.setView(oldView, newView, animateProgress);
}
protected DoubleExpression tweakOpeningCurlyY()
{
return null;
}
protected double tweakCurlyX()
{
return 0;
}
@Override
protected List extends Node> calculateContents(List<Node> normalContent)
{
ArrayList<Node> content = new ArrayList<>(super.calculateContents(normalContent));
if (sidebar != null)
content.add(0, sidebar.getNode());
return content;
}
@Override
public FrameTypeCheck check(FrameCanvas child)
{
if (child == this.canvas)
{
return StrideDictionary.checkStatement();
}
else
{
throw new IllegalStateException("Asking about unknown child of SingleCanvasFrame");
}
}
}
top,
use,
map,
abstract class SingleCanvasFrame
. SingleCanvasFrame
top,
use,
map,
class SingleCanvasFrame . PlusMinus
. PlusMinus
. drawPlus
. drawMinus
. setCollapsible
. isCollapsible
. setCollapsed
. expandContents
. getCanvas
. updateSidebarCurried
. setSidebar
. lowestCursorY
. createCanvas
. getFrame
. setView
. tweakOpeningCurlyY
. tweakCurlyX
. calculateContents
. check
300 neLoCode
+ 42 LoComm