I'm trying to build a javafx trimmer, I've done it, however when I added a setOnMouseClicked on ImageViews the trimmer I made hides these nodes, so the action is not handled.
I've have set Rectangle -50 in order to overlay the trimmer to the ImageView, unfortunately, this make event not working.
Rectangle rectangle = new Rectangle(0, -50, 80, 40);
the flowing controller builds is frame
package app.controller;
import java.io.File;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ScrollPane;
import javafx.scene.effect.DropShadow;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
public class Trimmer2 implements Initializable {
private Node selectedNode;
#FXML
private HBox hboxStream;
#FXML
private HBox hboxStreamTrim;
#FXML
private Label lblTime;
public void initialize(URL arg0, ResourceBundle arg1) {
buidTrimmer();
}
private void buidTrimmer() {
ImageView pic = null;
HBox hbox = new HBox();
DropShadow shadow = new DropShadow();
ClassLoader classLoader = getClass().getClassLoader();
File path = new File(classLoader.getResource("streams/sub_stream").getPath());
File [] files = path.listFiles();
Image[] images = new Image[files.length];
ImageView[] pics = new ImageView[files.length];
for (int i = 0; i < files.length; i++) {
final Image image = images[i] =
new Image(files[i].toURI().toString(), 80, 40, false, false);
pic = pics[i] =
new ImageView(image);
pic.setEffect(shadow);
pic.setOnMouseClicked((MouseEvent me) -> {
System.out.println("do action");
});
hbox.getChildren().add(pics[i]);
}
Rectangle rectangle = new Rectangle(0, -50, 80, 40);
rectangle.setFill(Color.rgb(33, 150, 243, 0.5));
Pane pane = new Pane( rectangle );
makeSelectable(rectangle, pane);
VBox vbox = new VBox();
vbox.setSpacing(10);
vbox.getChildren().addAll(hbox,pane);
ScrollPane scrollPane = new ScrollPane();
scrollPane.setContent(vbox);
scrollPane.setId("my_scrollPane");
hboxStream.getChildren().add(scrollPane);
}
private void makeSelectable(Node node, Pane root) {
node.setOnMouseClicked(event -> {
if (selectedNode != node) {
root.getChildren().removeIf(candidate -> candidate instanceof ResizingControl);
selectedNode = node;
node.toFront();
ResizingControl resizingControl = new ResizingControl(node);
root.getChildren().add(resizingControl);
}
System.out.println("here");
event.consume();
});
}
}
Try to add the Hbox and the rectangle inside your pane, by the way that pane must be empty. hope it helps :)
Rectangle rectangle = new Rectangle(0, 0, imgWidth, imgHeight);
rectangle.setFill(Color.rgb(33, 150, 243, 0.35));
Pane pane = new Pane();
VBox vbox = new VBox();
vbox.getChildren().addAll(pane);
pane.getChildren().addAll(hbox,rectangle);
Mouse events go to the topmost node that is not mouse transparent. Simply set the property to true and the mouse events will be handled at the usual node. One additional thing that can make your life easier is the fact that you can also place nodes inside a layout that are not affected by the layout algorithm of the parent and that do not contribute to the size calculations. Simply set managed to false. This allows you to implement the selection like this:
private Node selectedNode;
#Override
public void start(Stage primaryStage) throws Exception {
HBox container = new HBox();
File directory = new File("someDirectory");
Rectangle selection = new Rectangle(80, 40, Color.rgb(100, 100, 255, 0.5));
selection.setStrokeType(StrokeType.INSIDE);
selection.setStrokeWidth(4);
selection.setStroke(Color.WHITE);
selection.setVisible(false);
selection.setMouseTransparent(true);
selection.setManaged(false);
for (File file : directory.listFiles(f -> f.getName().endsWith(".jpg"))) {
Image image = new Image(file.toURI().toURL().toExternalForm(), 80, 40, false, false);
ImageView imageView = new ImageView(image);
container.getChildren().add(imageView);
imageView.setOnMouseClicked(evt -> {
if (selectedNode == imageView) {
selection.setVisible(false);
selectedNode = null;
} else {
selection.setVisible(true);
selection.setLayoutX(imageView.getLayoutX());
selectedNode = imageView;
}
});
}
container.getChildren().add(selection);
Scene scene = new Scene(new ScrollPane(container), 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
Note: I also removed the use of getResource and that is for a good reason. Resources are not guaranteed to be available as files. E.g. if you use a .jar file, you won't be able to access them via File. Either store outside of the classpath in a directory or make a list of the resources available as text resource to allow you multiple resources without hardcoding every single one of them. (Don't use File with the latter approach.)
Related
I want to implement some kind of notification system in my application but I have trouble with the calculation of the actual position of my notification. All notifications should appear in a separate stage and each notification should be aligned among themselves and each notification is a simple VBox with two labels (title and message).
I created a little standalone application with the issue I have.
As soon as you press the button on the main stage, a VBox will be created and added to a second notification stage. As soon as a seconds notification needs to be added, this second notification should be below the first notification and so on. Therefore I need to find the height of the first notification in order to position the second notification underneath.
I know I could use a VBox instead, but in my application the notification should make a smooth animation and push the other notifications further down. I removed the whole animation and removing part of notifications so the example stays as small as possible.
The problem is that all notification boxes have the same height - but they don't (if you modify the text and make it longer / smaller).
package whatever;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Screen;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class NotificationTest {
private Stage notificationStage;
private Pane contentPane;
private static final Integer NOTIFICATION_WIDTH = 250;
private Double notificationOffset = 0.0;
private static final Integer SPACING_BETWEEN_NOTIFICATIONS = 20;
public void start() {
Stage mainStage = new Stage();
TextField textField = new TextField("Some long text for testing purpose with even more letters in oder to create at least one linebreak...");
Button button = new Button("Add Notification");
button.setOnAction(actionEvent -> {
addNotification(textField.getText());
});
VBox vBox = new VBox(10);
vBox.getChildren().addAll(textField, button);
mainStage.setScene(new Scene(vBox, 300, 300));
mainStage.show();
}
private void addNotification(String text) {
if(notificationStage == null) {
notificationStage = new Stage();
notificationStage.setWidth(NOTIFICATION_WIDTH);
notificationStage.setHeight(Screen.getPrimary().getVisualBounds().getHeight() - 50);
notificationStage.setX(Screen.getPrimary().getVisualBounds().getWidth() - 260);
notificationStage.setY(50);
contentPane = new Pane();
contentPane.setStyle("-fx-background-color: transparent");
notificationStage.setScene(new Scene(contentPane));
notificationStage.initStyle(StageStyle.TRANSPARENT);
notificationStage.getScene().setFill(Color.TRANSPARENT);
notificationStage.show();
}
VBox notificationBox = new VBox(10);
notificationBox.setMaxWidth(NOTIFICATION_WIDTH);
notificationBox.setMinWidth(NOTIFICATION_WIDTH);
notificationBox.setStyle("-fx-background-radius: 10; -fx-background-color: red");
notificationBox.getChildren().add(new Label("Title of Notification"));
Label message = new Label(text);
message.setWrapText(true);
notificationBox.getChildren().add(message);
notificationBox.setLayoutY(notificationOffset);
contentPane.getChildren().add(notificationBox);
// Needs to be done - otherwise the height would be 0
contentPane.layout();
System.out.println(notificationBox.getHeight());
notificationOffset += notificationBox.getHeight() + SPACING_BETWEEN_NOTIFICATIONS;
}
}
I used the ScenicView Tool to verify the height and it says that the height is 79, but the System.out tells me the height is 10.4. The 79 value seems to be correct, but how can I get this value in my application?
The short answer is use applyCss():
contentPane.applyCss();
contentPane.layout();
From the documentation:
If required, apply styles to this Node and its children, if any. This
method does not normally need to be invoked directly but may be used
in conjunction with Parent.layout() to size a Node before the next
pulse
The long and better answer is to use a VBox or a ListView.
To add layout animation use LayoutAnimator.java. You can find more details here.
Edit: an mre of using LayoutAnimator to animate newly added notifications:
import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.*;
public class NotificationTest extends Application {
private Stage notificationStage;
private Pane contentPane;
private static final int NOTIFICATION_WIDTH = 250, SPACING_BETWEEN_NOTIFICATIONS = 20;
private static final String LONG_TEXT = "Some long text for testing purpose with even more letters in oder to create "
+ "at least one linebreak...";
private int counter = 0;
#Override
public void start(Stage mainStage) throws Exception {
mainStage = new Stage();
TextField textField = new TextField(LONG_TEXT);
Button button = new Button("Add Notification");
button.setOnAction(actionEvent -> {
addNotification(textField.getText());
});
VBox vBox = new VBox(10, textField, button);
mainStage.setScene(new Scene(vBox, 300, 300));
mainStage.show();
}
private void addNotification(String text) {
if(notificationStage == null) {
notificationStage = new Stage();
notificationStage.setWidth(NOTIFICATION_WIDTH);
notificationStage.setX(Screen.getPrimary().getVisualBounds().getWidth() - 260);
notificationStage.setY(50);
contentPane = new VBox(SPACING_BETWEEN_NOTIFICATIONS);
contentPane.setStyle("-fx-background-color: transparent");
notificationStage.setScene(new Scene(contentPane));
notificationStage.initStyle(StageStyle.TRANSPARENT);
//animate using LayoutAnimator https://gist.github.com/jewelsea/5683558
LayoutAnimator ly = new LayoutAnimator();
ly.observe(contentPane.getChildren());
notificationStage.show();
}
VBox notificationBox = new VBox(10);
notificationBox.setMaxWidth(NOTIFICATION_WIDTH);
notificationBox.setMinWidth(NOTIFICATION_WIDTH);
notificationBox.setStyle("-fx-border-color: black");
notificationBox.getChildren().add(new Label("Title of Notification"));
Label message = new Label(counter++ + ": " +text);
message.setWrapText(true);
notificationBox.getChildren().add(message);
contentPane.getChildren().add(0, notificationBox);
}
public static void main(String[] args) {
launch(null);
}
}
I'm using JavaFx 8.
From the JavaDoc of javafx.scene.Node.scaleYProperty():
[...] This scale factor is not included in layoutBounds by default, which makes it ideal for scaling the entire node after all effects and transforms have been taken into account. [...]
How can I include the scaling factor in layoutBounds, though?
Some context:
In the following example, when pressing the button I would like the GridPane to react also to the scaling of the HBox whithout having to hardcode the prefHeight of the RowConstraints.
Being able to include the scaling factor into the layoutBounds probably would do the trick, but other solutions are welcome as well.
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ToggleButton;
import javafx.scene.layout.*;
import javafx.stage.Stage;
public class ScalingStandAlone extends Application {
private VBox vBox = new VBox();
private GridPane gridPane = new GridPane();
private HBox hBox = new HBox();
private ToggleButton button = new ToggleButton("Click to scale");
private Label firstRowLabel = new Label("Some content in text form");
private Label secondRowLabel = new Label("Some content for scaling");
private Label thirdRowLabel = new Label("Some moving content");
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage stage) throws Exception {
AnchorPane root = new AnchorPane();
AnchorPane.setTopAnchor(vBox, 5.);
AnchorPane.setLeftAnchor(vBox, 5.);
AnchorPane.setRightAnchor(vBox, 5.);
root.autosize();
Scene scene = new Scene(root);
stage.setTitle("GridRow Scale Demo");
stage.setWidth(400);
stage.setHeight(300);
stage.setScene(scene);
stage.show();
root.getChildren().add(vBox);
vBox.setAlignment(Pos.CENTER);
vBox.getChildren().add(gridPane);
vBox.getChildren().add(button);
vBox.setStyle("-fx-spacing: 15;");
configureGridPane(root);
button.setOnAction(event -> {
hBox.setScaleY(button.isSelected() ? 2 : 1);
});
}
private void configureGridPane(Pane root) {
hBox.getChildren().add(secondRowLabel);
// Styling //
firstRowLabel.setStyle("-fx-padding: 5;");
hBox.setStyle("-fx-background-color: #800000; -fx-padding: 5;");
secondRowLabel.setStyle("-fx-text-fill: white; -fx-padding: 5;");
thirdRowLabel.setStyle("-fx-padding: 5;");
gridPane.add(firstRowLabel, 0, 0);
gridPane.add(hBox, 0, 1);
gridPane.add(thirdRowLabel, 0, 2);
gridPane.setGridLinesVisible(true);
gridPane.getColumnConstraints().add(new ColumnConstraints());
gridPane.getColumnConstraints().get(0).setPercentWidth(100);
}
}
From the Javadocs for Group:
Any transform, effect, or state applied to a Group will be applied to
all children of that group. Such transforms and effects will NOT be
included in this Group's layout bounds, however if transforms and
effects are set directly on children of this Group, those will be
included in this Group's layout bounds.
(my emphasis added).
Therefore, if you simply wrap your HBox in a Group, you will achieve the desired effect:
// gridPane.add(hBox, 0, 1);
gridPane.add(new Group(hBox), 0, 1);
I try to create an application that contains 2 panes in a stackpane.
One pane is the main pane and is centered, the second is smaller and docked to the bottom-left of the stage.
The thing is that I've tried using 'setAlignment' and it does'nt seems to work (although the button is aligned). The small pane is always centered.
What is the problem and how I fix this?
I guess that maybe I can't align a pane, so how can I overcome this?
Pane pane = new Pane();
for (SerialPoint sp : points) {
Circle circle = new Circle(sp.getX(), sp.getY(), 6, Color.GREEN);
pane.getChildren().add(circle);
}
Pane smallPane = new Pane();
smallPane.setScaleX(0.25);
smallPane.setScaleY(0.25);
smallPane.setStyle("-fx-border-color: black;");
for (SerialPoint sp : points) {
Circle circle = new Circle(sp.getX(), sp.getY(), 6, Color.RED);
smallPane.getChildren().add(circle);
}
Button startBtn = new Button("Start");
StackPane stackPane = new StackPane(pane, smallPane, startBtn);
StackPane.setAlignment(smallPane, Pos.BOTTOM_LEFT);
StackPane.setAlignment(startBtn, Pos.TOP_RIGHT);
StackPane.setMargin(startBtn, new Insets(5));
Scene scene = new Scene(stackPane);
(SerialPoint is my inner class)
This is what I get:
Place the scaled Pane inside a Group. For layout purposes, StackPane (and other layout panes) will ignore scale factors and other transforms on nodes, but a Group will not.
From the Group javadoc:
Any transform, effect, or state applied to a Group will be applied to all children of that group. Such transforms and effects will NOT be included in this Group's layout bounds, however if transforms and effects are set directly on children of this Group, those will be included in this Group's layout bounds.
Sample App
import javafx.application.Application;
import javafx.geometry.*;
import javafx.scene.*;
import javafx.scene.control.Button;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import java.util.Random;
public class AligningPains extends Application {
private static final int N_POINTS = 5;
private static final int MAX_POINT_POS = 100;
private static final int POINT_RADIUS = 6;
private static final int PREF_PANE_SIZE = 300;
private static final int BUTTON_INSETS = 5;
Point2D[] points = new Point2D[N_POINTS];
Random random = new Random(42);
#Override
public void start(Stage stage) {
initPoints();
Pane pane = new Pane();
pane.setPrefSize(PREF_PANE_SIZE, PREF_PANE_SIZE);
addCircles(pane, Color.GREEN);
Pane smallPane = new Pane();
smallPane.setStyle("-fx-border-color: black;");
smallPane.setPrefSize(PREF_PANE_SIZE, PREF_PANE_SIZE);
smallPane.setScaleX(0.25);
smallPane.setScaleY(0.25);
addCircles(smallPane, Color.RED);
Group smallGroup = new Group(smallPane);
Button startBtn = new Button("Start");
StackPane stackPane = new StackPane(pane, smallGroup, startBtn);
StackPane.setAlignment(smallGroup, Pos.BOTTOM_LEFT);
StackPane.setAlignment(startBtn, Pos.TOP_RIGHT);
StackPane.setMargin(startBtn, new Insets(BUTTON_INSETS));
stackPane.setPrefSize(PREF_PANE_SIZE, PREF_PANE_SIZE);
Scene scene = new Scene(stackPane);
stage.setScene(scene);
stage.show();
}
private void addCircles(Pane pane, Color color) {
for (Point2D sp : points) {
Circle circle = new Circle(sp.getX(), sp.getY(), POINT_RADIUS, color);
pane.getChildren().add(circle);
}
}
private void initPoints() {
for (int i = 0; i < points.length; i++) {
points[i] = new Point2D(random.nextInt(MAX_POINT_POS), random.nextInt(MAX_POINT_POS));
}
}
public static void main(String[] args) {
launch(args);
}
}
I have three VBoxes in a HBox. I want all of them to always take one third of the HBox and the full height. I've tried HBox.setHgrow(<every VBox>, Priority.ALWAYS) with <every VBox>.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE); and that worked fine, but when I added a component to one of the VBoxes, it resized itself and became larger than the other ones.
Any idea how to solve this properly?
Use a GridPane instead of the HBox. You can use a collection of column constraints, each with the percentWidth set to give each column equal width.
SSCCE:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.RowConstraints;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class VBoxInGridPane extends Application {
#Override
public void start(Stage primaryStage) {
VBox box1 = new VBox();
box1.setStyle("-fx-background-color: -fx-background; -fx-background: red ;");
box1.getChildren().add(new Label("Content"));
VBox box2 = new VBox();
box2.setStyle("-fx-background-color: green ;");
VBox box3 = new VBox();
box3.setStyle("-fx-background-color: blue ;");
GridPane root = new GridPane();
root.add(box1, 0, 0);
root.add(box2, 1, 0);
root.add(box3, 2, 0);
for (int i = 0 ; i < 3 ; i++) {
ColumnConstraints cc = new ColumnConstraints();
cc.setPercentWidth(100.0/3.0);
cc.setHgrow(Priority.ALWAYS);
root.getColumnConstraints().add(cc);
}
RowConstraints rc = new RowConstraints();
rc.setVgrow(Priority.ALWAYS);
root.getRowConstraints().add(rc);
Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
What you could do is to add your VBox to a StackPane and the StackPane to the HBox. Into the StackPane you also place a placeholder (I usually use a transparent Rectangular) and bind that to the binding maxVBoxWidth. This is a Binding that you have to define yourself:
DoubleBinding maxVBoxBinding = new DoubleBinding() {
{
super.bind(vbox1.widthProperty(),vbox2.widthProperty(), vbox3.widthProperty());
}
#Override
protected double computeValue() {
return Math.max(vbox1.getWidth(), Math.max(vbox2.getWidth(), vbox2.getWidth()));
}
}
Here is an example from Pro JavaFx 8:
package projavafx.reversi.examples;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.effect.DropShadow;
import javafx.scene.effect.InnerShadow;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.RadialGradient;
import javafx.scene.paint.Stop;
import javafx.scene.shape.Ellipse;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import projavafx.reversi.model.Owner;
import projavafx.reversi.model.ReversiModel;
/**
* #author Stephen Chin <steveonjava#gmail.com>
*/
public class BorderLayoutExample extends Application {
TilePane scoreTiles;
TilePane titleTiles;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
BorderPane borderPane = new BorderPane();
borderPane.setTop(createTitle());
borderPane.setCenter(createBackground());
borderPane.setBottom(createScoreBoxes());
Scene scene = new Scene(borderPane, 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
// scoreTiles.prefTileWidthProperty().bind(Bindings.selectDouble(scoreTiles.parentProperty(), "width").divide(2));
// titleTiles.prefTileWidthProperty().bind(Bindings.selectDouble(titleTiles.parentProperty(), "width").divide(2));
}
private Node createTitle() {
StackPane left = new StackPane();
left.setStyle("-fx-background-color: black");
Text text = new Text("JavaFX");
text.setFont(Font.font(null, FontWeight.BOLD, 18));
text.setFill(Color.WHITE);
StackPane.setAlignment(text, Pos.CENTER_RIGHT);
left.getChildren().add(text);
Text right = new Text("Reversi");
right.setFont(Font.font(null, FontWeight.BOLD, 18));
titleTiles = new TilePane();
titleTiles.setSnapToPixel(false);
TilePane.setAlignment(right, Pos.CENTER_LEFT);
titleTiles.getChildren().addAll(left, right);
titleTiles.setPrefTileHeight(40);
titleTiles.prefTileWidthProperty().bind(Bindings.selectDouble(titleTiles.parentProperty(), "width").divide(2));
return titleTiles;
}
private Node createBackground() {
Region answer = new Region();
RadialGradient rg = new RadialGradient(225, 0, 0, 0, 1, true, CycleMethod.NO_CYCLE,
new Stop(0.0, Color.WHITE),
new Stop(1.0, Color.GRAY)
);
answer.setBackground(new Background(new BackgroundFill(rg, null, null)));
// answer.setStyle("-fx-background-color: radial-gradient(radius 100%, white, gray)");
return answer;
}
private Node createScoreBoxes() {
scoreTiles = new TilePane(createScore(Owner.BLACK), createScore(Owner.WHITE));
scoreTiles.setSnapToPixel(false);
scoreTiles.setPrefColumns(2);
scoreTiles.prefTileWidthProperty().bind(Bindings.selectDouble(scoreTiles.parentProperty(), "width").divide(2));
return scoreTiles;
}
private Node createScore(Owner owner) {
Region background;
Ellipse piece = new Ellipse(32, 20);
piece.setFill(owner.getColor());
DropShadow pieceEffect = new DropShadow();
pieceEffect.setColor(Color.DODGERBLUE);
pieceEffect.setSpread(.2);
piece.setEffect(pieceEffect);
Text score = new Text();
score.setFont(Font.font(null, FontWeight.BOLD, 100));
score.setFill(owner.getColor());
Text remaining = new Text();
remaining.setFont(Font.font(null, FontWeight.BOLD, 12));
remaining.setFill(owner.getColor());
VBox remainingBox = new VBox(10, piece, remaining);
remainingBox.setAlignment(Pos.CENTER);
FlowPane flowPane = new FlowPane(20, 10, score, remainingBox);
flowPane.setAlignment(Pos.CENTER);
background = new Region();
background.setStyle("-fx-background-color: " + owner.opposite().getColorStyle());
ReversiModel model = ReversiModel.getInstance();
StackPane stack = new StackPane(background, flowPane);
InnerShadow innerShadow = new InnerShadow();
innerShadow.setColor(Color.DODGERBLUE);
innerShadow.setChoke(.5);
background.effectProperty().bind(Bindings.when(model.turn.isEqualTo(owner))
.then(innerShadow)
.otherwise((InnerShadow) null));
DropShadow dropShadow = new DropShadow();
dropShadow.setColor(Color.DODGERBLUE);
dropShadow.setSpread(.2);
piece.effectProperty().bind(Bindings.when(model.turn.isEqualTo(owner))
.then(dropShadow)
.otherwise((DropShadow) null));
score.textProperty().bind(model.getScore(owner).asString());
remaining.textProperty().bind(model.getTurnsRemaining(owner).asString().concat(" turns remaining"));
return stack;
}
}
A warning pops up in the console when running this app:
sept. 20, 2015 11:07:03 AM com.sun.javafx.binding.SelectBinding$SelectBindingHelper getObservableValue
WARNING: Exception while evaluating select-binding [width]
sept. 20, 2015 11:07:03 AM com.sun.javafx.binding.SelectBinding$SelectBindingHelper getObservableValue
WARNING: Exception while evaluating select-binding [width]
What went wrong here?
The problem is the following bit of code in the method createTitle():
titleTiles.prefTileWidthProperty().bind(
Bindings.selectDouble(
titleTiles.parentProperty(), "width").divide(2));
At this moment, the titleTiles have not yet been added to the borderPane, so the value of the parentProperty is null, hence the width property can not be found on it.
Same in createScoreBoxes().
Next time, though, it would be nice, if you cut down your sample code a bit, especially remove references to classes from your project (import projavafx.reversi.model.ReversiModel;), do that one can paste it into his IDE and run it right away.