package bluej.stride.generic;

import java.util.Collections;
import java.util.HashSet;
import java.util.List;

import bluej.utility.javafx.BetterVBox;

import javafx.beans.binding.DoubleBinding;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.value.ChangeListener;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.css.CssMetaData;
import javafx.css.SimpleStyleableDoubleProperty;
import javafx.css.SimpleStyleableObjectProperty;
import javafx.css.Styleable;
import javafx.geometry.BoundingBox;
import javafx.geometry.Bounds;
import javafx.geometry.Insets;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.Border;
import javafx.scene.layout.BorderStroke;
import javafx.scene.layout.BorderStrokeStyle;
import javafx.scene.layout.BorderWidths;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.Pane;
import javafx.scene.Node;
import javafx.scene.paint.Color;
import javafx.scene.shape.StrokeLineCap;
import javafx.scene.shape.StrokeLineJoin;
import javafx.scene.shape.StrokeType;

import bluej.utility.javafx.JavaFXUtil;
import bluej.utility.javafx.SharedTransition;



class CanvasVBox
extends BetterVBox{    
   private final SimpleStyleableDoubleProperty leftMarginProperty = new SimpleStyleableDoubleProperty(LEFT_MARGIN_META_DATA);
   
   private final SimpleStyleableDoubleProperty bottomMarginProperty = new SimpleStyleableDoubleProperty(BOTTOM_MARGIN_META_DATA);
   
   private final SimpleStyleableDoubleProperty topMarginProperty = new SimpleStyleableDoubleProperty(TOP_MARGIN_META_DATA);
   
   private final SimpleStyleableDoubleProperty rightMarginProperty = new SimpleStyleableDoubleProperty(RIGHT_MARGIN_META_DATA);
   
   private final SimpleStyleableDoubleProperty cssMinHeightProperty = new SimpleStyleableDoubleProperty(MIN_HEIGHT_META_DATA);
   
   private final SimpleStyleableDoubleProperty frameMarginTopProperty = new SimpleStyleableDoubleProperty(TOP_FRAME_MARGIN_META_DATA);
   
   private final SimpleStyleableDoubleProperty frameMarginBottomProperty = new SimpleStyleableDoubleProperty(BOTTOM_FRAME_MARGIN_META_DATA);

   
   private final SimpleStyleableDoubleProperty curlyBracketHeight = new SimpleStyleableDoubleProperty(CURLY_BRACKET_HEIGHT_META_DATA);

   
   
| | To explain these properties: we want to be able to animate the insets for the border and background | as part of the transition to Java preview (primarily, to add space for brackets above and below the canvas, | without having to add another node). We are setting custom insets anyway, because we use custom properties | for the margins, above. | | If any of the border or background properties are set in CSS, then when CSS is reapplied, any customisations | (e.g. during animation) to *any* border or background aspects are clobbered, because border and background | are each set as one complete object. To avoid this, we don't set the -fx-{}order,background}-* properties | in CSS, but instead set -bj-{}order,background}-* equivalents. We then manually merge these with the custom | margins to form the real border and background. | private final SimpleStyleableObjectProperty<Color> borderColorProperty = new SimpleStyleableObjectProperty<Color>(BORDER_COLOR_META_DATA); private final SimpleStyleableObjectProperty<Insets> borderInsetsProperty = new SimpleStyleableObjectProperty<>(BORDER_INSETS_META_DATA); private final SimpleStyleableObjectProperty<Color> backgroundColorProperty = new SimpleStyleableObjectProperty<Color>(BACKGROUND_COLOR_META_DATA); private final SimpleStyleableObjectProperty<Insets> backgroundInsetsProperty = new SimpleStyleableObjectProperty<>(BACKGROUND_INSETS_META_DATA); private final SimpleStyleableObjectProperty<Insets> borderWidthProperty = new SimpleStyleableObjectProperty<>(BORDER_WIDTH_META_DATA); private final SimpleStyleableObjectProperty<Insets> borderRadiusProperty = new SimpleStyleableObjectProperty<>(BORDER_RADIUS_META_DATA); private final SimpleStyleableObjectProperty<Insets> backgroundRadiusProperty = new SimpleStyleableObjectProperty<>(BACKGROUND_RADIUS_META_DATA); private static final CssMetaData<CanvasVBox, Number> LEFT_MARGIN_META_DATA = JavaFXUtil.cssSize("-bj-left-margin", v -> v.leftMarginProperty); private static final CssMetaData<CanvasVBox, Number> BOTTOM_MARGIN_META_DATA = JavaFXUtil.cssSize("-bj-bottom-margin", v -> v.bottomMarginProperty); private static final CssMetaData<CanvasVBox, Number> TOP_MARGIN_META_DATA = JavaFXUtil.cssSize("-bj-top-margin", v -> v.topMarginProperty); private static final CssMetaData<CanvasVBox, Number> RIGHT_MARGIN_META_DATA = JavaFXUtil.cssSize("-bj-right-margin", v -> v.rightMarginProperty); private static final CssMetaData<CanvasVBox, Number> MIN_HEIGHT_META_DATA = JavaFXUtil.cssSize("-bj-min-height", v -> v.cssMinHeightProperty); private static final CssMetaData<CanvasVBox, Number> CURLY_BRACKET_HEIGHT_META_DATA = JavaFXUtil.cssSize("-bj-curly-bracket-height", v -> v.curlyBracketHeight); private static final CssMetaData<CanvasVBox, Number> TOP_FRAME_MARGIN_META_DATA = JavaFXUtil.cssSize("-bj-frame-margin-top", v -> v.frameMarginTopProperty); private static final CssMetaData<CanvasVBox, Number> BOTTOM_FRAME_MARGIN_META_DATA = JavaFXUtil.cssSize("-bj-frame-margin-bottom", v -> v.frameMarginBottomProperty); private static final CssMetaData<CanvasVBox, Color> BORDER_COLOR_META_DATA = JavaFXUtil.cssColor("-bj-border-color", v -> v.borderColorProperty); private static final CssMetaData<CanvasVBox, Color> BACKGROUND_COLOR_META_DATA = JavaFXUtil.cssColor("-bj-background-color", v -> v.backgroundColorProperty); private static final CssMetaData<CanvasVBox, Insets> BORDER_INSETS_META_DATA = JavaFXUtil.cssInsets("-bj-border-insets", v -> v.borderInsetsProperty); private static final CssMetaData<CanvasVBox, Insets> BACKGROUND_INSETS_META_DATA = JavaFXUtil.cssInsets("-bj-background-insets", v -> v.backgroundInsetsProperty); private static final CssMetaData<CanvasVBox, Insets> BORDER_RADIUS_META_DATA = JavaFXUtil.cssInsets("-bj-border-radius", v -> v.borderRadiusProperty); private static final CssMetaData<CanvasVBox, Insets> BORDER_WIDTH_META_DATA = JavaFXUtil.cssInsets("-bj-border-width", v -> v.borderWidthProperty); private static final CssMetaData<CanvasVBox, Insets> BACKGROUND_RADIUS_META_DATA = JavaFXUtil.cssInsets("-bj-background-radius", v -> v.backgroundRadiusProperty); private static final List <CssMetaData <? extends Styleable, ? > > cssMetaDataList = JavaFXUtil.extendCss(Pane.getClassCssMetaData()) .add(LEFT_MARGIN_META_DATA) .add(BOTTOM_MARGIN_META_DATA) .add(TOP_MARGIN_META_DATA) .add(RIGHT_MARGIN_META_DATA) .add(MIN_HEIGHT_META_DATA) .add(TOP_FRAME_MARGIN_META_DATA) .add(BOTTOM_FRAME_MARGIN_META_DATA) .add(CURLY_BRACKET_HEIGHT_META_DATA) .add(BORDER_COLOR_META_DATA) .add(BACKGROUND_COLOR_META_DATA) .add(BORDER_INSETS_META_DATA) .add(BORDER_RADIUS_META_DATA) .add(BORDER_WIDTH_META_DATA) .add(BACKGROUND_RADIUS_META_DATA) .build(); public static List > getClassCssMetaData() { return cssMetaDataList; } @Override public List> getCssMetaData() { return getClassCssMetaData(); } private DoubleProperty animateExtraSpace = new SimpleDoubleProperty(0.0); private final DoubleProperty leftMarginScale = new SimpleDoubleProperty(1.0); private final HashSet<Node> frameNodes = new HashSet<>(); public CanvasVBox(double minWidth, ObservableList<Frame> frames) { super(minWidth); frames.addListener((ListChangeListener<? super Frame>) change -> { frameNodes.clear(); for (Frame f : frames) frameNodes.add(f.getNode()); }); minHeightProperty().bind(cssMinHeightProperty); ChangeListener l = (a, b, c) -> { Insets insets = calculateMargins(Insets.EMPTY); setPadding(insets); updateBackground(); updateBorder(); }; leftMarginProperty.addListener(l); leftMarginScale.addListener(l); rightMarginProperty.addListener(l); topMarginProperty.addListener(l); bottomMarginProperty.addListener(l); animateExtraSpace.addListener(l); ChangeListener border = (a, b, c) -> updateBorder(); borderWidthProperty.addListener(border); borderColorProperty.addListener(border); borderRadiusProperty.addListener(border); borderInsetsProperty.addListener(border); ChangeListener bk = (a, b, c) -> updateBackground(); backgroundColorProperty.addListener(bk); backgroundRadiusProperty.addListener(bk); } private void updateBackground() { setBackground(new Background(new BackgroundFill(backgroundColorProperty.get(), makeCornerRadii(backgroundRadiusProperty.get()), calculateMargins(backgroundInsetsProperty.get())))); } private static CornerRadii makeCornerRadii(Insets xs) { if (xs == null) return CornerRadii.EMPTY; else{ return new CornerRadii(xs.getTop(), xs.getRight(), xs.getBottom(), xs.getLeft(), false); } } private static BorderWidths makeBorderWidths(Insets xs) { if (xs == null) return BorderWidths.EMPTY; else{ return new BorderWidths(xs.getTop(), xs.getRight(), xs.getBottom(), xs.getLeft()); } } private void updateBorder() { final BorderStrokeStyle style = new BorderStrokeStyle(StrokeType.OUTSIDE, StrokeLineJoin.ROUND, StrokeLineCap.SQUARE, 10.0f, 0.0f, Collections.emptyList()); setBorder(new Border(new BorderStroke(borderColorProperty.get(), style, makeCornerRadii(borderRadiusProperty.get()), makeBorderWidths(borderWidthProperty.get()), calculateMargins(borderInsetsProperty.get())))); } private Insets calculateMargins(Insets including) { if (including == null) including = Insets.EMPTY; final int top = (int) topMarginProperty.add(animateExtraSpace.multiply(curlyBracketHeight)).get() + (int) including.getTop(); final int right = (int) rightMarginProperty.get() + (int)including.getRight(); final int bottom = (int) bottomMarginProperty.add(animateExtraSpace.multiply(curlyBracketHeight)).get() + (int) including.getBottom(); final int left = (int) leftMarginProperty.multiply(leftMarginScale).get() + (int)including.getLeft(); return new Insets(top, right, bottom, left); } public void addSpace(SharedTransition animate) { animateExtraSpace.bind(animate.getProgress()); animate.addOnStopped(animateExtraSpace::unbind); } public void removeSpace(SharedTransition animate) { animateExtraSpace.bind(animate.getProgress().negate().add(1.0)); animate.addOnStopped(animateExtraSpace::unbind); } public double getBottomMargin() { return (int)bottomMarginProperty.get(); } public DoubleBinding leftMarginProperty() { return new DoubleBinding() { { super.bind(leftMarginProperty); super.bind(leftMarginScale); } @Override protected double computeValue() { return (int)(leftMarginProperty.get() * leftMarginScale.get()); } }; } public DoubleBinding rightMarginProperty() { return new DoubleBinding() { { super.bind(rightMarginProperty); } @Override protected double computeValue() { return (int)rightMarginProperty.get(); } }; } @Override public double getTopMarginFor(Node n) { if (frameNodes.contains(n)) return frameMarginTopProperty.get(); return super.getTopMarginFor(n); } @Override public double getBottomMarginFor(Node n) { if (frameNodes.contains(n)) return frameMarginBottomProperty.get(); return super.getBottomMarginFor(n); }
| 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() { Bounds b = localToScene(getBoundsInLocal()); return new BoundingBox( b.getMinX() + leftMarginProperty.get(), b.getMinY() + topMarginProperty.get(), b.getWidth() - leftMarginProperty.get() - rightMarginProperty.get(), b.getHeight() - topMarginProperty.get() - bottomMarginProperty.get() ); } public void animateColorsToPseudoClass(String pseudo, boolean on, SharedTransition animation) { final Color startBackground = backgroundColorProperty.get(); final Color startBorder = borderColorProperty.get(); JavaFXUtil.setPseudoclass(pseudo, on, this); applyCss(); final Color endBackground = backgroundColorProperty.get(); final Color endBorder = borderColorProperty.get(); if (!startBackground.equals(endBackground) || !startBorder.equals(endBorder)) { JavaFXUtil.addChangeListener(animation.getProgress(), t -> { Color background = startBackground.interpolate(endBackground, t.doubleValue()); Color border = startBorder.interpolate(endBorder, t.doubleValue()); backgroundColorProperty.set(background); borderColorProperty.set(border); updateBackground(); updateBorder(); }); } } public DoubleProperty leftMarginScaleProperty() { return leftMarginScale; } public double getCurlyBracketHeight() { return curlyBracketHeight.get(); } }

.   - CanvasVBox
.   getClassCssMetaData
.   getCssMetaData
.   CanvasVBox
.   updateBackground
.   makeCornerRadii
.   makeBorderWidths
.   updateBorder
.   calculateMargins
.   addSpace
.   removeSpace
.   getBottomMargin
.   leftMarginProperty
.   computeValue
.   rightMarginProperty
.   computeValue
.   getTopMarginFor
.   getBottomMarginFor
.   getContentSceneBounds
.   animateColorsToPseudoClass
.   leftMarginScaleProperty
.   getCurlyBracketHeight




378 neLoCode + 14 LoComm