org.controlsfx.samples.HelloSnapshotView Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of controlsfx-samples Show documentation
Show all versions of controlsfx-samples Show documentation
High quality UI controls and other tools to complement the core JavaFX distribution
/**
* Copyright (c) 2014, ControlsFX
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of ControlsFX, any associated website, nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL CONTROLSFX BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.controlsfx.samples;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.util.Random;
import javafx.animation.Animation;
import javafx.animation.AnimationTimer;
import javafx.animation.RotateTransition;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.geometry.Insets;
import javafx.geometry.Rectangle2D;
import javafx.scene.Node;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.ColorPicker;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.SelectionModel;
import javafx.scene.control.Slider;
import javafx.scene.control.TextField;
import javafx.scene.control.TitledPane;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
import javafx.util.StringConverter;
import org.controlsfx.ControlsFXSample;
import org.controlsfx.control.SnapshotView;
import org.controlsfx.control.SnapshotView.Boundary;
/**
* Demonstrates the {@link SnapshotView}.
*/
@SuppressWarnings("nls")
public class HelloSnapshotView extends ControlsFXSample {
/* ************************************************************************
* *
* Attributes & Properties *
* *
**************************************************************************/
// STATIC
/**
* The format used to display all numbers in the text fields.
*/
private static final DecimalFormat zeroDpFormat = new DecimalFormat("0");
private static final DecimalFormat twoDpFormat = new DecimalFormat("0.00");
// INSTANCE
/**
* The displayed nodes.
*/
private final Node[] nodes;
/**
* The names of the displayed nodes.
*/
private final String[] nodeNames = new String[] {
"ImageView",
"Fitted ImageView",
"Transformed ImageView",
"Rotating Node",
"Null Node",
};
/**
* The images displayed by the image views.
*/
private final Image[] images;
/**
* The names of the displayed nodes.
*/
private final String[] imageNames = new String[] {
"ControlsFX",
"Java's Duke",
"Null Image",
};
private final IntegerProperty selectedImageIndex = new SimpleIntegerProperty();
/**
* The demonstrated view.
*/
private final SnapshotView snapshotView = new SnapshotView();
/* ************************************************************************
* *
* Construction *
* *
**************************************************************************/
public HelloSnapshotView() {
images = loadImages();
nodes = createNodes();
}
/* ************************************************************************
* *
* Displayed Controls *
* *
**************************************************************************/
@Override
public Node getPanel(Stage stage) {
snapshotView.setNode(nodes[0]);
return snapshotView;
}
/**
* Loads the displayed images.
*
* @return an array of {@link Image image}s
*/
private static Image[] loadImages() {
Image controlsFX = new Image(HelloSnapshotView.class.getResource("ControlsFX.png").toExternalForm());
Image duke = new Image(HelloSnapshotView.class.getResource("duke_wave.png").toExternalForm());
return new Image[] { controlsFX, duke, null };
}
/**
* Creates the nodes used by the snapshot view.
*
* @return an array of {@link Node node}s
*/
private Node[] createNodes() {
// regular image view
ImageView imageView = new ImageView(images[0]);
imageView.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> increaseImageIndex());
// fitted image view
ImageView fittedImageView = new ImageView(images[0]);
fittedImageView.setPreserveRatio(true);
fittedImageView.fitWidthProperty().bind(snapshotView.widthProperty());
fittedImageView.fitHeightProperty().bind(snapshotView.heightProperty());
fittedImageView.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> increaseImageIndex());
// transformed image view
ImageView transformedImageView = new ImageView(images[0]);
transformedImageView.setScaleX(0.5);
transformedImageView.setRotate(45);
transformedImageView.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> increaseImageIndex());
// rotating rectangle
Rectangle rotatingRect = new Rectangle(200, 300, Color.GREEN);
rotatingRect.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> {
Random r = new Random();
Color newColor = new Color(r.nextDouble(), r.nextDouble(), r.nextDouble(), r.nextDouble());
rotatingRect.setFill(newColor);
});
RotateTransition rotator = new RotateTransition(Duration.seconds(3), rotatingRect);
rotator.setAutoReverse(true);
rotator.setByAngle(360);
rotator.setCycleCount(Animation.INDEFINITE);
rotator.play();
return new Node[] {
new Pane(imageView), new Pane(fittedImageView), new Pane(transformedImageView),
new Pane(rotatingRect), null
};
}
private void increaseImageIndex() {
int currentImageIndex = selectedImageIndex.get();
// set the next index but leave out the null image (which is assumed to be last in the array)
int nextImageIndex = (currentImageIndex + 1) % (images.length - 1);
selectedImageIndex.set(nextImageIndex);
}
@Override
public Node getControlPanel() {
return new VBox(10,
createNodeControl(), createSettingsControl(),
createVisualizationControl(), createSelectionControl(), createSnapshotImageView());
}
/**
* @return a control for all the node related properties
*/
private Node createNodeControl() {
GridPane grid = new GridPane();
grid.setVgap(5);
grid.setHgap(5);
grid.setPadding(new Insets(5));
int row = 0;
// --- node
final Label nodeTypeLabel = new Label("Node type: ");
nodeTypeLabel.getStyleClass().add("property");
grid.add(nodeTypeLabel, 0, row);
final ChoiceBox nodeOptions = new ChoiceBox<>(FXCollections.observableArrayList(nodeNames));
final SelectionModel nodeSelectionModel = nodeOptions.getSelectionModel();
nodeSelectionModel.selectedIndexProperty().addListener(
(o, oldNodeIndex, newNodeIndex) -> snapshotView.setNode(nodes[newNodeIndex.intValue()]));
nodeSelectionModel.select(0);
nodeOptions.setMaxWidth(Double.MAX_VALUE);
GridPane.setHgrow(nodeOptions, Priority.ALWAYS);
grid.add(nodeOptions, 1, row++);
// --- image
final Label imageLabel = new Label("Image: ");
imageLabel.getStyleClass().add("property");
grid.add(imageLabel, 0, row);
final ChoiceBox imageOptions = new ChoiceBox<>(FXCollections.observableArrayList(imageNames));
// disable the box if no image view is shown
imageOptions.disableProperty().bind(Bindings.equal(3, nodeSelectionModel.selectedIndexProperty()));
// bind 'selectedImageIndex' and the box' selection model together
final SelectionModel imageSelectionModel = imageOptions.getSelectionModel();
imageSelectionModel.selectedIndexProperty().addListener(
(o, oldIndex, newIndex) -> selectedImageIndex.set(newIndex.intValue()));
selectedImageIndex.addListener((o, oldIndex, newIndex) -> {
imageSelectionModel.clearAndSelect(newIndex.intValue());
setImageForAllViews(newIndex.intValue());
});
imageSelectionModel.select(0);
imageOptions.setMaxWidth(Double.MAX_VALUE);
GridPane.setHgrow(imageOptions, Priority.ALWAYS);
grid.add(imageOptions, 1, row++);
return new TitledPane("Node", grid);
}
private void setImageForAllViews(int index) {
Image image = images[index];
for (int i = 0; i < 3; i++) {
setImageForView(i, image);
}
}
private void setImageForView(int imageViewIndex, Image image) {
Pane containingPane = (Pane) nodes[imageViewIndex];
ImageView view = (ImageView) containingPane.getChildren().get(0);
view.setImage(image);
}
/**
* @return a control for all the view related properties
*/
private Node createSettingsControl() {
GridPane grid = new GridPane();
grid.setVgap(5);
grid.setHgap(5);
grid.setPadding(new Insets(5));
int row = 0;
// selection active
CheckBox selectionActive = new CheckBox();
selectionActive.selectedProperty().bindBidirectional(snapshotView.selectionActiveProperty());
selectionActive.disableProperty().bind(snapshotView.selectionActivityManagedProperty());
grid.addRow(row++, new Label("Active:"), selectionActive);
// selection managed
CheckBox selectionActivityManaged = new CheckBox();
selectionActivityManaged.selectedProperty().bindBidirectional(snapshotView.selectionActivityManagedProperty());
grid.addRow(row++, new Label("Activity Managed:"), selectionActivityManaged);
// selection mouse transparent
CheckBox selectionMouseTransparent = new CheckBox();
selectionMouseTransparent.selectedProperty().bindBidirectional(
snapshotView.selectionMouseTransparentProperty());
grid.addRow(row++, new Label("Mouse Transparent:"), selectionMouseTransparent);
// --- fixed ratio
Label fixedRatioLabel = new Label("Fixed selection ratio: ");
fixedRatioLabel.getStyleClass().add("property");
grid.add(fixedRatioLabel, 0, row);
CheckBox ratioFixed = new CheckBox();
ratioFixed.selectedProperty().bindBidirectional(snapshotView.selectionRatioFixedProperty());
grid.add(ratioFixed, 1, row++);
// --- ratio
Label ratioLabel = new Label("Fixed ratio: ");
ratioLabel.getStyleClass().add("property");
grid.add(ratioLabel, 0, row);
TextField ratioTextField = new TextField();
ratioTextField.textProperty().bindBidirectional(snapshotView.fixedSelectionRatioProperty(),
new StringConverter() {
@Override
public Number fromString(String value) {
try {
return twoDpFormat.parse(value);
} catch (ParseException e) {
return 1;
}
}
@Override
public String toString(Number value) {
return twoDpFormat.format(value);
}
});
grid.add(ratioTextField, 1, row++);
// --- selection area boundary
final Label selectionBoundaryLabel = new Label("Selection Area Boundary: ");
selectionBoundaryLabel.getStyleClass().add("property");
grid.add(selectionBoundaryLabel, 0, row);
final ChoiceBox selectionBoundaryOptions = new ChoiceBox<>(
FXCollections.observableArrayList(Boundary.CONTROL, Boundary.NODE));
selectionBoundaryOptions.getSelectionModel().select(Boundary.CONTROL);
snapshotView.selectionAreaBoundaryProperty().bind(
selectionBoundaryOptions.getSelectionModel().selectedItemProperty());
selectionBoundaryOptions.setMaxWidth(Double.MAX_VALUE);
GridPane.setHgrow(selectionBoundaryOptions, Priority.ALWAYS);
grid.add(selectionBoundaryOptions, 1, row++);
// --- unselected area boundary
final Label unselectedBoundaryLabel = new Label("Unselected Area Boundary: ");
unselectedBoundaryLabel.getStyleClass().add("property");
grid.add(unselectedBoundaryLabel, 0, row);
final ChoiceBox unselectedBoundaryOptions = new ChoiceBox<>(
FXCollections.observableArrayList(Boundary.CONTROL, Boundary.NODE));
unselectedBoundaryOptions.getSelectionModel().select(Boundary.CONTROL);
snapshotView.unselectedAreaBoundaryProperty().bind(
unselectedBoundaryOptions.getSelectionModel().selectedItemProperty());
unselectedBoundaryOptions.setMaxWidth(Double.MAX_VALUE);
GridPane.setHgrow(unselectedBoundaryOptions, Priority.ALWAYS);
grid.add(unselectedBoundaryOptions, 1, row++);
return new TitledPane("Selection Settings", grid);
}
/**
* @return a control for all the visualization related properties
*/
private Node createVisualizationControl() {
GridPane grid = new GridPane();
grid.setVgap(5);
grid.setHgap(5);
grid.setPadding(new Insets(5));
int row = 0;
// selection fill color
ColorPicker selectionFillPicker = new ColorPicker((Color) snapshotView.getSelectionAreaFill());
snapshotView.selectionAreaFillProperty().bind(selectionFillPicker.valueProperty());
grid.addRow(row++, new Label("Fill Color:"), selectionFillPicker);
// selection border color
ColorPicker selectionBorderPaintPicker = new ColorPicker((Color) snapshotView.getSelectionBorderPaint());
snapshotView.selectionBorderPaintProperty().bind(selectionBorderPaintPicker.valueProperty());
grid.addRow(row++, new Label("Stroke Color:"), selectionBorderPaintPicker);
// selection border width
Slider selectionStrokeWidth = new Slider(0, 25, snapshotView.getSelectionBorderWidth());
snapshotView.selectionBorderWidthProperty().bindBidirectional(selectionStrokeWidth.valueProperty());
grid.addRow(row++, new Label("Stroke Width:"), selectionStrokeWidth);
// unselected area fill color
ColorPicker unselectedAreaFillPicker = new ColorPicker((Color) snapshotView.getUnselectedAreaFill());
snapshotView.unselectedAreaFillProperty().bind(unselectedAreaFillPicker.valueProperty());
grid.addRow(row++, new Label("Outer Color:"), unselectedAreaFillPicker);
return new TitledPane("Visualization Settings", grid);
}
/**
* @return a control for all the selection related properties
*/
private Node createSelectionControl() {
// upper left
TextField upperLeftX = new TextField();
upperLeftX.setPrefColumnCount(3);
upperLeftX.setEditable(false);
TextField upperLeftY = new TextField();
upperLeftY.setPrefColumnCount(3);
upperLeftY.setEditable(false);
// lower right
TextField lowerRightX = new TextField();
lowerRightX.setPrefColumnCount(3);
lowerRightX.setEditable(false);
TextField lowerRightY = new TextField();
lowerRightY.setPrefColumnCount(3);
lowerRightY.setEditable(false);
// size
TextField width = new TextField();
width.setPrefColumnCount(3);
width.setEditable(false);
TextField height = new TextField();
height.setPrefColumnCount(3);
height.setEditable(false);
TextField ratio = new TextField();
ratio.setPrefColumnCount(3);
// set up the binding
snapshotView.selectionProperty().addListener(new ChangeListener() {
@Override
public void changed(
ObservableValue extends Rectangle2D> observable, Rectangle2D oldValue, Rectangle2D newValue) {
if (newValue == null) {
upperLeftX.setText("");
upperLeftY.setText("");
lowerRightX.setText("");
lowerRightY.setText("");
width.setText("");
height.setText("");
ratio.setText("");
} else {
upperLeftX.setText(zeroDpFormat.format(newValue.getMinX()));
upperLeftY.setText(zeroDpFormat.format(newValue.getMinY()));
lowerRightX.setText(zeroDpFormat.format(newValue.getMaxX()));
lowerRightY.setText(zeroDpFormat.format(newValue.getMaxY()));
width.setText(zeroDpFormat.format(newValue.getWidth()));
height.setText(zeroDpFormat.format(newValue.getHeight()));
ratio.setText(twoDpFormat.format(newValue.getWidth() / newValue.getHeight()));
}
}
});
// put it all together
GridPane grid = new GridPane();
grid.setVgap(5);
grid.setHgap(5);
grid.setPadding(new Insets(5));
int row = 0;
grid.addRow(row++, new Label("Upper Left Corner:"), upperLeftX, new Label("/"), upperLeftY);
grid.addRow(row++, new Label("Lower Right Corner:"), lowerRightX, new Label("/"), lowerRightY);
grid.addRow(row++, new Label("Size (Ratio):"), width, new Label("x"), height, new Label(" ("), ratio,
new Label(")"));
// selection changing
CheckBox selectionChanging = new CheckBox();
selectionChanging.setDisable(true);
selectionChanging.selectedProperty().bind(snapshotView.selectionChangingProperty());
grid.addRow(row++, new Label("Selection Changing:"), selectionChanging);
return new TitledPane("Selection Stats", grid);
}
/**
* @return a control which displays the current snapshot
*/
private Node createSnapshotImageView() {
final ImageView snapshotImageView = new ImageView();
// display snapshots which are constantly taken
AnimationTimer timer = new AnimationTimer() {
@Override
public void handle(long timestamp) {
Image snapshot = null;
if (snapshotView.getNode() != null && snapshotView.hasSelection()) {
snapshot = snapshotView.createSnapshot();
}
snapshotImageView.setImage(snapshot);
}
};
timer.start();
ScrollPane scrollPane = new ScrollPane();
scrollPane.setContent(snapshotImageView);
return new TitledPane("Snapshot", scrollPane);
}
/* ************************************************************************
* *
* Boilerplate *
* *
**************************************************************************/
public static void main(String[] args) {
Application.launch(args);
}
@Override
public String getSampleName() {
return "SnapshotView";
}
@Override
public String getJavaDocURL() {
return Utils.JAVADOC_BASE
+ "org/controlsfx/control/SnapshotView.html";
}
@Override
public String getControlStylesheetURL() {
return "/org/controlsfx/control/snapshot-view.css";
}
@Override
public String getSampleDescription() {
return "A control which allows the user to select a rectangular area of the displayed node. " +
"The selection's ratio can be fixed so that the user can only make selections with that ratio. " +
"The method 'createSnapshot()' returns an Image of the selected area. " +
"The displayed node can be interacted with if the selection is set to be mouse transparent. ";
}
}