/*******************************************************************************
* Copyright (c) 2015, 2016 itemis AG and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Matthias Wienand (itemis AG) - initial API and implementation
*
*******************************************************************************/
package org.eclipse.gef.fx.examples.snippets;
import org.eclipse.gef.fx.examples.AbstractFxExample;
import org.eclipse.gef.geometry.convert.fx.FX2Geometry;
import org.eclipse.gef.geometry.planar.AffineTransform;
import javafx.beans.binding.DoubleBinding;
import javafx.beans.property.DoubleProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Bounds;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.TitledPane;
import javafx.scene.effect.DropShadow;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.StrokeType;
import javafx.scene.transform.Affine;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Scale;
import javafx.scene.transform.Shear;
import javafx.scene.transform.Transform;
public class LayoutSnippet extends AbstractFxExample {
public static void main(String[] args) {
launch();
}
private Point2D startPoint;
private Point2D endPoint;
private double initialTx;
private double initialTy;
private double initialWidth;
private double initialHeight;
private Affine affine;
public LayoutSnippet() {
super("LayoutSnippet");
}
private void applyTransform(Affine dst, Transform transform) {
AffineTransform affineTransform = FX2Geometry
.toAffineTransform(dst);
AffineTransform result = affineTransform
.concatenate(FX2Geometry.toAffineTransform(transform));
setAffine(dst, result);
}
@Override
public Scene createScene() {
final BorderPane root = new BorderPane();
root.setStyle("-fx-background-color: black;");
final Pane contentPane = new Pane();
root.setCenter(contentPane);
final Scene scene = new Scene(root, 600, 400);
// create rect
final Rectangle rect = new Rectangle(0, 0, 0, 0);
rect.setStroke(Color.WHITE);
rect.setFill(Color.PINK);
rect.setStrokeWidth(2.5);
rect.setEffect(new DropShadow(10, 0, 0, Color.LIGHTBLUE));
rect.boundsInParentProperty().addListener(new ChangeListener<Bounds>() {
@Override
public void changed(ObservableValue<? extends Bounds> observable,
Bounds oldValue, Bounds newValue) {
// JavaFX Bug
}
});
affine = new Affine();
rect.getTransforms().add(affine);
// visualize rect's origin
final Circle origin = new Circle(1.5, new Color(1, 0, 0, 0.5));
origin.setStroke(Color.BLACK);
origin.setStrokeWidth(1.5);
origin.setEffect(new DropShadow(5, 0, 0, Color.RED));
origin.layoutXProperty().bind(rect.layoutXProperty());
origin.layoutYProperty().bind(rect.layoutYProperty());
// create handle at top right corner
final Rectangle handle = new Rectangle(0, 0, 10, 10);
handle.setTranslateX(-5);
handle.setTranslateY(-5);
handle.setStroke(Color.CYAN);
handle.setFill(Color.BLUE);
handle.setStrokeWidth(1.5);
handle.setStrokeType(StrokeType.INSIDE);
handle.layoutXProperty().bind(new DoubleBinding() {
{
bind(rect.boundsInParentProperty());
}
@Override
protected double computeValue() {
Bounds bounds = rect.getLayoutBounds();
return rect.localToParent(bounds.getMaxX(), bounds.getMinY())
.getX();
}
});
handle.layoutYProperty().bind(new DoubleBinding() {
{
bind(rect.boundsInParentProperty());
}
@Override
protected double computeValue() {
Bounds bounds = rect.getLayoutBounds();
return rect.localToParent(bounds.getMaxX(), bounds.getMinY())
.getY();
}
});
handle.setOnMousePressed(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
startPoint = new Point2D(event.getSceneX(), event.getSceneY());
initialTx = affine.getTx();
initialTy = affine.getTy();
initialWidth = rect.getWidth();
initialHeight = rect.getHeight();
}
});
handle.setOnMouseDragged(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
endPoint = new Point2D(event.getSceneX(), event.getSceneY());
Point2D start = rect.sceneToLocal(startPoint);
Point2D end = rect.sceneToLocal(endPoint);
double dx = end.getX() - start.getX();
double dy = end.getY() - start.getY();
Point2D layout = rect.parentToLocal(initialTx, initialTy);
Point2D layoutParent = rect.localToParent(layout.getX(),
layout.getY() + dy);
rect.setWidth(initialWidth + dx);
rect.setHeight(initialHeight - dy);
affine.setTx(layoutParent.getX());
affine.setTy(layoutParent.getY());
}
});
// add nodes to the scene
contentPane.getChildren().addAll(rect, origin, handle);
// options
VBox vbox = new VBox();
TitledPane options = new TitledPane("Options:", vbox);
root.setRight(options);
HBox rowX = new HBox();
rowX.getChildren().addAll(new Label("Layout-X: "),
text(rect.layoutXProperty()));
HBox rowY = new HBox();
rowY.getChildren().addAll(new Label("Layout-Y: "),
text(rect.layoutYProperty()));
HBox rowW = new HBox();
rowW.getChildren().addAll(new Label("Width: "),
text(rect.widthProperty()));
HBox rowH = new HBox();
rowH.getChildren().addAll(new Label("Height: "),
text(rect.heightProperty()));
HBox rowR = new HBox();
Button rotLeftButton = new Button("Rot Left");
Button rotRightButton = new Button("Rot Right");
rowR.getChildren().addAll(rotLeftButton, rotRightButton);
HBox rowS = new HBox();
Button scaleUpButton = new Button("Scale Up");
Button scaleDownButton = new Button("Scale Down");
rowS.getChildren().addAll(scaleUpButton, scaleDownButton);
HBox rowShear = new HBox();
Button shearRightButton = new Button("Shear Right");
Button shearLeftButton = new Button("Shear Left");
rowShear.getChildren().addAll(shearRightButton, shearLeftButton);
HBox rowReset = new HBox();
Button resetTxButton = new Button("Reset Transforms");
rowReset.getChildren().addAll(resetTxButton);
vbox.getChildren().addAll(rowX, rowY, rowW, rowH, rowR, rowS, rowShear,
rowReset);
rotLeftButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
Bounds bounds = rect.getLayoutBounds();
Rotate rotate = Transform.rotate(-30,
bounds.getMinX() + bounds.getWidth() / 2,
bounds.getMinY() + bounds.getHeight() / 2);
applyTransform(affine, rotate);
}
});
rotRightButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
Bounds bounds = rect.getLayoutBounds();
Rotate rotate = Transform.rotate(30,
bounds.getMinX() + bounds.getWidth() / 2,
bounds.getMinY() + bounds.getHeight() / 2);
applyTransform(affine, rotate);
}
});
scaleUpButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
Bounds bounds = rect.getLayoutBounds();
Scale scale = Transform.scale(1.25, 1.25,
bounds.getMinX() + bounds.getWidth() / 2,
bounds.getMinY() + bounds.getHeight() / 2);
applyTransform(affine, scale);
}
});
scaleDownButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
Bounds bounds = rect.getLayoutBounds();
Scale scale = Transform.scale(0.8, 0.8,
bounds.getMinX() + bounds.getWidth() / 2,
bounds.getMinY() + bounds.getHeight() / 2);
applyTransform(affine, scale);
}
});
shearRightButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
Bounds bounds = rect.getLayoutBounds();
Shear shear = Transform.shear(-0.25, 0,
bounds.getMinX() + bounds.getWidth() / 2,
bounds.getMinY() + bounds.getHeight() / 2);
applyTransform(affine, shear);
}
});
shearLeftButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
Bounds bounds = rect.getLayoutBounds();
Shear shear = Transform.shear(0.25, 0,
bounds.getMinX() + bounds.getWidth() / 2,
bounds.getMinY() + bounds.getHeight() / 2);
applyTransform(affine, shear);
}
});
resetTxButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
setAffine(affine, new AffineTransform());
}
});
// initial move
rect.setLayoutX(100);
rect.setLayoutY(100);
// initial resize
rect.setWidth(80);
rect.setHeight(40);
return scene;
}
private void setAffine(Affine dst, AffineTransform src) {
dst.setMxx(src.getM00());
dst.setMxy(src.getM01());
dst.setMyx(src.getM10());
dst.setMyy(src.getM11());
dst.setTx(src.getTranslateX());
dst.setTy(src.getTranslateY());
}
private Node text(final DoubleProperty property) {
final TextField text = new TextField();
text.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> observable,
String oldValue, String newValue) {
try {
double d = Double.parseDouble(newValue);
property.set(d);
} catch (Exception x) {
}
}
});
property.addListener(new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> observable,
Number oldValue, Number newValue) {
text.setText(newValue.toString());
}
});
return text;
}
}