I found this code in a thread and basically I want to change it so when I click on the treeView node 'High' I want the right pane to display a tabPane (instead of label and textfield) and inside the tabPane I want to display a TableView. I've attached a pic of what the GUI does. Any help would be appreciated, thanks.
Attached what the GUI does: to get to 'High' right click component A and select 'Add Parameter'
GUI example
Main.java
import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.TextFieldTreeCell;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import java.util.HashMap;
import java.util.Map;
public class Main extends Application {
private static int rootNr = 0;
private static int coordinateNr = 0;
public static void main(String[] args) {
launch(args);
}
static final Map<TreeItem<String>, BorderPane> map = new HashMap<>();
TabPane tabPane;
#Override
public void start(Stage primaryStage) {
BorderPane root = new BorderPane();
TreeItem<String> tree = new TreeItem<>("Project");
TreeItem<String> item1 = new TreeItem<>("Component A");
TreeView<String> treeView = new TreeView<>(tree);
treeView.setOnMouseClicked((event) -> {
TreeItem<String> treeItem = treeView.getSelectionModel().getSelectedItem();
if (treeItem.getChildren().stream().anyMatch(child -> child.getValue().startsWith("C"))) {
root.setCenter(getRootsPanel(treeItem.getValue()));
} else {
root.setCenter(map.get(treeItem));
}
});
treeView.setCellFactory(p -> new AddMenuTreeCell());
tree.setExpanded(true);
root.setLeft(treeView);
tree.getChildren().add(item1);
Scene scene = new Scene(root, 700, 500);
primaryStage.setTitle("Tree View");
primaryStage.setScene(scene);
primaryStage.show();
}
private static class AddMenuTreeCell extends TextFieldTreeCell<String> {
private ContextMenu menu = new ContextMenu();
public AddMenuTreeCell() {
MenuItem newitem1 = new MenuItem("Add Component");
MenuItem newitem2 = new MenuItem("Add Parameter");
menu.getItems().addAll(newitem1, newitem2);
newitem1.setOnAction(arg0 -> {
TreeItem<String> item3 = new TreeItem<>("Component X");
getTreeItem().getChildren().add(item3);
});
newitem2.setOnAction(arg0 -> {
TreeItem<String> newLeaf = new TreeItem<>("Parameter");
TreeItem<String> uxItem1 = new TreeItem<>("High");
map.put(uxItem1, getrightPane1());
TreeItem<String> uyItem1 = new TreeItem<>("Low");
map.put(uyItem1, getrightPane1());
newLeaf.getChildren().add(uxItem1);
newLeaf.getChildren().add(uyItem1);
getTreeItem().getChildren().add(newLeaf);
});
}
#Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
if (!isEditing()) {
setText(item);
setGraphic(getTreeItem().getGraphic());
if (!(getTreeItem().isLeaf() && getTreeItem().getParent() == null)) {
setContextMenu(menu);
}
}
}
}
}
private static BorderPane getrightPane1() {
TextField textf1 = new TextField();
TextField textf2 = new TextField();
BorderPane root1 = new BorderPane();
VBox vbox = new VBox(20);
vbox.setPadding(new Insets(10));
HBox h1 = new HBox(7);
HBox h2 = new HBox(7);
textf1.setPrefWidth(100);
textf1.setPromptText("Enter Height");
textf1.setOnKeyReleased(event -> {
if (textf1.getText().length() > 0 && textf2.getText().length() > 0) {
Rectangle rect1 = new Rectangle();
rect1.setHeight(Double.parseDouble(textf1.getText()));
rect1.setWidth(Double.parseDouble(textf2.getText()));
rect1.setFill(null);
rect1.setStroke(Color.RED);
root1.setCenter(rect1);
}
});
textf2.setPrefWidth(100);
textf2.setPromptText("Enter Width");
textf2.setOnKeyReleased(event -> {
if (textf1.getText().length() > 0 && textf2.getText().length() > 0) {
Rectangle rect2 = new Rectangle();
rect2.setHeight(Double.parseDouble(textf1.getText()));
rect2.setWidth(Double.parseDouble(textf2.getText()));
rect2.setFill(null);
rect2.setStroke(Color.RED);
root1.setCenter(rect2);
}
});
if (textf1.getText().length() > 0 && textf2.getText().length() > 0 && root1.getCenter() == null) {
Rectangle rect = new Rectangle();
rect.setHeight(Double.parseDouble(textf1.getText()));
rect.setWidth(Double.parseDouble(textf2.getText()));
rect.setFill(null);
rect.setStroke(Color.RED);
root1.setCenter(rect);
}
h1.getChildren().addAll(new Label("Y1:"), textf1);
h2.getChildren().addAll(new Label("X1:"), textf2);
vbox.getChildren().addAll(h1, h2);
root1.setLeft(vbox);
return root1;
}
private static BorderPane getRootsPanel(String root) {
BorderPane root2 = new BorderPane();
HBox hbox = new HBox(10);
hbox.setPadding(new Insets(40));
hbox.setAlignment(Pos.TOP_CENTER);
for (Map.Entry<TreeItem<String>, BorderPane> entry : map.entrySet()) {
if (entry.getKey().getParent().getParent().getValue().equals(root)) {
Rectangle rect1 = (Rectangle) entry.getValue().getCenter();
if (rect1 != null) {
Rectangle rect2 = new Rectangle();
rect2.setWidth(rect1.getWidth());
rect2.setHeight(rect1.getHeight());
rect2.setFill(rect1.getFill());
rect2.setStroke(rect1.getStroke());
Platform.runLater(() -> hbox.getChildren().add(rect2));
}
}
}
Platform.runLater(() -> root2.setLeft(hbox));
return root2;
}
}
Related
This question already has answers here:
JavaFX launch another application
(3 answers)
Closed 4 years ago.
I've got an app made in javafx and another class with menu in this project. In this menu I've got two buttons and one works (exit buuton) and I want buttonStart to open my Main class. How to launch it?
Button buttonStart = new Button("START GAME");
Button buttonExit = new Button("EXIT");
buttonExit.setOnMouseClicked(event -> System.exit(0));
My menu:
package pl.main;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class Menu extends Application {
private BorderPane layout;
private Scene scene;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage window) throws Exception {
layout = new BorderPane();
scene = new Scene(layout, 720, 480);
HBox hbox = new HBox();
hbox.setPadding(new Insets(15, 12, 15, 12));
hbox.setSpacing(10);
hbox.setStyle("-fx-background-color: #F9A825;");
Button buttonStart = new Button("START GAME");
buttonStart.setPrefSize(100, 20);
buttonStart.setStyle("-fx-background-color: #E65100;");
Button buttonExit = new Button("EXIT");
buttonExit.setPrefSize(100, 20);
buttonExit.setStyle("-fx-background-color: #E65100;");
buttonExit.setOnMouseClicked(event -> System.exit(0));
hbox.getChildren().addAll(buttonStart, buttonExit);
layout.setCenter(hbox);
window.setScene(scene);
window.show();
}
}
Class which I wanna launch:
package pl.main;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import java.util.ArrayList;
import java.util.HashMap;
public class Main extends Application {
private HashMap<KeyCode, Boolean> keys = new HashMap<KeyCode, Boolean>();
private ArrayList<Node> blocks = new ArrayList<Node>();
private Pane appRoot = new Pane();
private Pane gameRoot = new Pane();
private Pane uiRoot = new Pane();
private Node player;
private Point2D playerGoDown = new Point2D(0, 0);
private Point2D playerGoRight = new Point2D(0, 0);
private boolean canJump = true;
private int levelWidth;
private void initContent() {
Rectangle background = new Rectangle(720, 480);
// BackgroundFill(Color.WHITE);
levelWidth = LevelData.LEVEL1[0].length();
for (int i = 0; i < LevelData.LEVEL1.length; i++) {
String map = LevelData.LEVEL1[i] + LevelData.LEVEL2[i]+ LevelData.LEVEL1[i]+ LevelData.LEVEL2[i]+ LevelData.LEVEL1[i]+ LevelData.LEVEL2[i];
String line = map;
for (int j = 0; j < line.length(); j++) {
switch (line.charAt(j)) {
case '0':
break;
case '1':
Node block = createEntity(j * 30, i * 30, 30, 30, Color.ORANGE);
blocks.add(block);
break;
}
}
}
player = createEntity(0, 350, 40, 40, Color.YELLOW);
// a ????????????
player.translateXProperty().addListener((a, old, newValue) -> {
int offset = newValue.intValue();
//if (offset > 360 && offset < levelWidth - 360) {
gameRoot.setLayoutX(-(offset - 360));
//}
});
appRoot.getChildren().addAll(background, gameRoot, uiRoot);
}
private void update() {
if (isPressed(KeyCode.W) && player.getTranslateY() >= 0) {
jumpPlayer();
}
if (playerGoDown.getY() < 10) {
playerGoDown = playerGoDown.add(0, 1);
}
movePlayerY((int) playerGoDown.getY());
if (player.getTranslateX() <= levelWidth - 5) {
// movePlayerX(5);
movePlayerRight();
}
if (playerGoRight.getX() < 0) {
playerGoRight = playerGoRight.add(0, 1);
}
movePlayerX((int) playerGoRight.getX());
}
private void movePlayerX(int value) {
boolean movingRight = value > 0;
for (int i = 0; i < Math.abs(value); i++) {
for (Node block : blocks) {
if (player.getBoundsInParent().intersects(block.getBoundsInParent())) {
if (movingRight) {
if (player.getTranslateX() + 40 == block.getTranslateX()) {
return;
}
} else {
if (player.getTranslateX() == block.getTranslateX() + 60) {
return;
}
}
}
}
player.setTranslateX(player.getTranslateX() + (movingRight ? 1 : -1));
}
}
private void movePlayerY(int value) {
boolean movingDown = value > 0;
for (int i = 0; i < Math.abs(value); i++) {
for (Node block : blocks) {
if (player.getBoundsInParent().intersects(block.getBoundsInParent())) {
if (movingDown) {
if (player.getTranslateY() + 40 == block.getTranslateY()) {
canJump = true;
return;
}
} else {
if (player.getTranslateY() == block.getTranslateY() + 60) {
return;
}
}
}
}
player.setTranslateY(player.getTranslateY() + (movingDown ? 1 : -1));
}
}
private void jumpPlayer() {
if (canJump) {
playerGoDown = playerGoDown.add(0, -10);
canJump = false;
}
}
private void movePlayerRight() {
playerGoRight = playerGoRight.add(10, 0);
}
private Node createEntity(int x, int y, int w, int h, Color color) {
Rectangle entity = new Rectangle(w, h);
entity.setTranslateX(x);
entity.setTranslateY(y);
entity.setFill(color);
gameRoot.getChildren().add(entity);
return entity;
}
private boolean isPressed(KeyCode key) {
return keys.getOrDefault(key, false);
}
#Override
public void start(Stage primaryStage) throws Exception {
initContent();
Scene scene = new Scene(appRoot);
scene.setOnKeyPressed(event -> keys.put(event.getCode(), true));
scene.setOnKeyReleased(event -> keys.put(event.getCode(), false));
primaryStage.setTitle("Jetpack gameplay");
primaryStage.setScene(scene);
primaryStage.show();
primaryStage.setResizable(false);
AnimationTimer timer = new AnimationTimer() {
#Override
public void handle(long now) {
update();
}
};
timer.start();
}
public static void main(String[] args) {
launch(args);
}
}
In future I wanna change current Main.java into ordinary class because now I have two classes which works independently.
If you want to go on another page, you have to load the game scene.
Look at Scene class in JavaDoc.
Once you have your scene, you just have to add an ActionEvent to your button that will display your page.
I am tasked with creating a DVD Collection application that displays Three columns and five row of stuff. I have written the code for this to work but keep on getting compilation errors( CANNOT FIND SYMBOL ==> class DVDColletionApp ) that I am having a hard time debugging.
Below is the code in question. Assistance would be greatly appreciated.
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
public class DVDCollectionApp extends Application {
private DVDCollection model;
private ListView<String> tList;
private ListView<Integer> yList, lList;
public DVDCollectionApp() {
model = DVDCollection.example1();
}
public void start(Stage primaryStage) {
BorderPane borderPane = new BorderPane();
// Create the labels
HBox labelPane = new HBox();
labelPane.setPadding(new Insets(0,0,0,10));
labelPane.setSpacing(10);
Label label1 = new Label("Title");
label1.setMinSize(300,30);
label1.setPrefSize(2000,30);
Label label2 = new Label("Year");
label2.setMinSize(60,30);
label2.setPrefSize(60,30);
Label label3 = new Label("Length");
label3.setMinSize(60,30);
label3.setPrefSize(60,30);
labelPane.getChildren().addAll(label1, label2, label3);
borderPane.setTop(labelPane);
// Create the lists
GridPane listPane = new GridPane();
listPane.setPadding(new Insets(10));
listPane.setHgap(10);
tList = new ListView<String>();
listPane.add(tList, 0, 0);
tList.setMinSize(300,60);
tList.setPrefSize(2000,2000);
yList = new ListView<Integer>();
listPane.add(yList, 1, 0);
yList.setMinSize(60,60);
yList.setPrefSize(60,500);
lList = new ListView<Integer>();
listPane.add(lList, 2, 0);
lList.setMinSize(60,60);
lList.setPrefSize(60,500);
borderPane.setCenter(listPane);
// Create the button pane
HBox buttonPane = new HBox();
buttonPane.setPadding(new Insets(10));
buttonPane.setSpacing(10);
Button addButton = new Button("Add");
addButton.setStyle("-fx-font: 12 arial; -fx-base: rgb(0,100,0); -fx-text-fill: rgb(255,255,255);");
addButton.setPrefSize(90,30);
Button deleteButton = new Button("Delete");
deleteButton.setStyle("-fx-font: 12 arial; -fx-base: rgb(200,0,0); -fx-text-fill: rgb(255,255,255);");
deleteButton.setPrefSize(90,30);
Button statsButton = new Button("Stats");
statsButton.setStyle("-fx-font: 12 arial;");
statsButton.setPrefSize(90,30);
buttonPane.getChildren().addAll(addButton, deleteButton, statsButton);
borderPane.setBottom(buttonPane);
addButton.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent actionEvent) {
String title = javax.swing.JOptionPane.showInputDialog("Please enter the DVD Title: ");
String year = javax.swing.JOptionPane.showInputDialog("Please enter the DVD Year: ");
String length = javax.swing.JOptionPane.showInputDialog("Please enter the DVD Duration: ");
if ((title != null) && (year != null) && (length != null) && (title.length() > 0) && (year.length() > 0) && (length.length() > 0)) {
DVD d = new DVD(title, Integer.parseInt(year), Integer.parseInt(length));
model.add(d);
update(model, -1);
}
}
});
deleteButton.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent actionEvent) {
if (tList.getSelectionModel().getSelectedItem() != null) {
model.remove(tList.getSelectionModel().getSelectedItem());
update(model, -1);
}
}
});
tList.setOnMousePressed(new EventHandler<MouseEvent>() {
public void handle(MouseEvent mouseEvent) {
model.setSelectedDVD(tList.getSelectionModel().getSelectedIndex());
update(model, tList.getSelectionModel().getSelectedIndex());
}
});
yList.setOnMousePressed(new EventHandler<MouseEvent>() {
public void handle(MouseEvent mouseEvent) {
model.setSelectedDVD(yList.getSelectionModel().getSelectedIndex());
update(model, yList.getSelectionModel().getSelectedIndex());
}
});
lList.setOnMousePressed(new EventHandler<MouseEvent>() {
public void handle(MouseEvent mouseEvent) {
model.setSelectedDVD(lList.getSelectionModel().getSelectedIndex());
update(model, lList.getSelectionModel().getSelectedIndex());
}
});
// Populate the lists
DVD[] theList = model.getDVDList();
String[] titles = new String[theList.length];
Integer[] years = new Integer[theList.length];
Integer[] lengths = new Integer[theList.length];
for (int i=0; i<theList.length; i++) {
titles[i] = theList[i].getTitle();
years[i] = theList[i].getYear();
lengths[i] = theList[i].getDuration();
}
tList.setItems(FXCollections.observableArrayList(titles));
yList.setItems(FXCollections.observableArrayList(years));
lList.setItems(FXCollections.observableArrayList(lengths));
primaryStage.setTitle("My DVD Collection");
primaryStage.setScene(new Scene(borderPane, 600, 300));
primaryStage.show();
}
// Update the view to reflect the model
public void update(DVDCollection model, int selectedDVD) {
DVD[] theList = model.getDVDList();
String[] titles = new String[theList.length];
Integer[] years = new Integer[theList.length];
Integer[] lengths = new Integer[theList.length];
for (int i=0; i<theList.length; i++) {
titles[i] = theList[i].getTitle();
years[i] = theList[i].getYear();
lengths[i] = theList[i].getDuration();
}
tList.setItems(FXCollections.observableArrayList(titles));
yList.setItems(FXCollections.observableArrayList(years));
lList.setItems(FXCollections.observableArrayList(lengths));
tList.getSelectionModel().select(selectedDVD);
yList.getSelectionModel().select(selectedDVD);
lList.getSelectionModel().select(selectedDVD);
}
public static void main(String[] args) {
launch(args);
}
}
Have you checked that java files are being compiled correctly?
The class DVDCollection doesn't seem to be imported, the DVDCollectionApp class must be in a .java of the same name and if you're using an IDE be sure to do a clean&build.
Pre-story:
Ok so i had another problem with a custom pagination control and thought i had figure out why it didn't work as expected. However it still didn't work. So i created a min example to find out what was happening and if something else in my code was messing it up.
Problem
When setting the page count, the page factory gets called again, but the page that is being returned is not displayed. And I just can't figure out why.
Additional information for the code example:
If you comment out the setPageCount(4) the "initial page" will load. However when you do not comment it out, everything will only work if you switch pages or resize.
Edit: I was also wondering why the page factory is called at all if my current page is lower then the new page count. seems so weird - shouldn't it only call the page factory, if my new page count is lower then my current page index in addition to changing the current page index to 0?
Code - Minimal Working Example
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Pagination;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.TilePane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class Main extends Application{
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
StackPane root = new StackPane();
Pagination pagination = new Pagination();
pagination.setPageFactory( index -> {
TilePane tilePane = new TilePane();
System.out.println("Trace: start: createTilePane");
System.out.println("TilePane: " + tilePane);
tilePane.widthProperty().addListener( observable -> {
System.out.println("WidthProperty-Listener");
System.out.println("tilePane: w: " + tilePane.getWidth() + "; h: " + tilePane.getHeight());
});
tilePane.heightProperty().addListener( observable -> {
System.out.println("HeightProperty-Listener");
System.out.println("tilePane: w: " + tilePane.getWidth() + "; h: " + tilePane.getHeight());
pagination.setPageCount(4);
});
tilePane.setOnMouseClicked( event -> {
System.out.println("Trace: onMouseClicked()");
System.out.println("tilePane: " + tilePane);
});
tilePane.setBackground(new Background(new BackgroundFill(Color.BEIGE, null, null)));
System.out.println("Trace: end: createTilePane");
return tilePane;
});
root.getChildren().add(pagination);
Scene scene = new Scene(root, 500, 500);
primaryStage.setScene(scene);
primaryStage.show();
}
}
You can do it like this. I'm using spinners to be more flexible in the demonstration:
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.Pagination;
import javafx.scene.control.Spinner;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Callback;
public class TilesOnAGrid extends Application {
Pagination pagination = new Pagination();
Spinner<Integer> totalSpinner = new Spinner<Integer>(0, 30, 12);
Spinner<Integer> rowSpinner = new Spinner<Integer>(1, 5, 2);
Spinner<Integer> colSpinner = new Spinner<Integer>(1, 5, 2);
#Override
public void start(Stage primaryStage) throws Exception {
BorderPane root = new BorderPane();
pagination.setPageFactory(new Callback<Integer, Node>() {
#Override
public Node call(Integer index) {
StackPane page = new StackPane();
GridPane grid = new GridPane();
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(0, 10, 0, 10));
int total = totalSpinner.getValue();
int rows = rowSpinner.getValue();
int cols = colSpinner.getValue();
int offset = rows * cols * index;
for (int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++) {
offset++;
if (offset > total)
break;
StackPane container = new StackPane();
container.setStyle("-fx-background-color:lightgrey");
Label label = new Label("Item Nr. " + offset);
container.getChildren().add(label);
GridPane.setRowIndex(container, row);
GridPane.setColumnIndex(container, col);
GridPane.setHgrow(container, Priority.ALWAYS);
GridPane.setVgrow(container, Priority.ALWAYS);
grid.getChildren().add(container);
}
}
page.getChildren().add(grid);
return page;
}
});
totalSpinner.valueProperty().addListener((ChangeListener<Number>) (observable, oldValue, newValue) -> layoutItems());
rowSpinner.valueProperty().addListener((ChangeListener<Number>) (observable, oldValue, newValue) -> layoutItems());
colSpinner.valueProperty().addListener((ChangeListener<Number>) (observable, oldValue, newValue) -> layoutItems());
HBox toolbar = new HBox();
toolbar.setPadding(new Insets(5,5,5,5));
toolbar.setSpacing(5);
toolbar.getChildren().addAll(new Label("Total"), totalSpinner, new Label("Columns"), colSpinner, new Label("Rows"), rowSpinner);
root.setTop(toolbar);
root.setCenter(pagination);
Scene scene = new Scene(root, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
layoutItems();
}
private void layoutItems() {
int total = totalSpinner.getValue();
int rows = rowSpinner.getValue();
int cols = colSpinner.getValue();
int pages = (int) Math.ceil(total / (double) (rows * cols));
pagination.setPageCount(pages);
}
public static void main(String[] args) {
launch(args);
}
}
Regarding your code, you shouldn't change the page count in the factory. And if you need the changes depending on width / height, you can add a listener to root
root.heightProperty().addListener((ChangeListener<Number>) (observable, oldValue, newValue) -> layoutItems());
root.widthProperty().addListener((ChangeListener<Number>) (observable, oldValue, newValue) -> layoutItems());
or whatever your pagination container is and calculate the rows / columns accordingly.
Edit
Here's an example which considers cell width and height when you click the "Use Resizing" checkbox, i. e. controls the pagination while you resize the parent. Changing rows/columns should work now as well. I'm sending a custom event to trigger an update.
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.Pagination;
import javafx.scene.control.Spinner;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Callback;
public class TilesOnAGrid extends Application {
StackPane currentPageContainer = null;
Pagination pagination = new Pagination();
double minColWidth = 300;
double minRowHeight = 300;
Spinner<Integer> totalSpinner = new Spinner<Integer>(0, 30, 12);
Spinner<Integer> rowSpinner = new Spinner<Integer>(1, 5, 2);
Spinner<Integer> colSpinner = new Spinner<Integer>(1, 5, 2);
BorderPane root;
CheckBox useResizeCheckBox;
#Override
public void start(Stage primaryStage) throws Exception {
root = new BorderPane();
pagination.setPageFactory(new Callback<Integer, Node>() {
#Override
public Node call(Integer index) {
currentPageContainer = createPage(index);
currentPageContainer.addEventHandler(MyEvent.UPDATE, new MyEventHandler());
return currentPageContainer;
}
});
root.heightProperty().addListener((ChangeListener<Number>) (observable, oldValue, newValue) -> layoutItems());
root.widthProperty().addListener((ChangeListener<Number>) (observable, oldValue, newValue) -> layoutItems());
pagination.currentPageIndexProperty().addListener((ChangeListener<Number>) (observable, oldValue, newValue) -> layoutItems());
pagination.pageCountProperty().addListener((ChangeListener<Number>) (observable, oldValue, newValue) -> layoutItems());
totalSpinner.valueProperty().addListener((ChangeListener<Number>) (observable, oldValue, newValue) -> layoutItems());
rowSpinner.valueProperty().addListener((ChangeListener<Number>) (observable, oldValue, newValue) -> layoutItems());
colSpinner.valueProperty().addListener((ChangeListener<Number>) (observable, oldValue, newValue) -> layoutItems());
Label totalLabel = new Label("Total");
Label rowsLabel = new Label("Rows");
Label columnsLabel = new Label("Columns");
useResizeCheckBox = new CheckBox("Use Resizing");
useResizeCheckBox.selectedProperty().addListener((ChangeListener<Boolean>) (observable, oldValue, newValue) -> {
rowSpinner.setVisible(!newValue);
colSpinner.setVisible(!newValue);
rowsLabel.setVisible(!newValue);
columnsLabel.setVisible(!newValue);
layoutItems();
});
HBox toolbar = new HBox();
toolbar.setPadding(new Insets(5, 5, 5, 5));
toolbar.setSpacing(5);
toolbar.getChildren().addAll(useResizeCheckBox, totalLabel, totalSpinner, columnsLabel, colSpinner, rowsLabel, rowSpinner);
root.setTop(toolbar);
root.setCenter(pagination);
Scene scene = new Scene(root, 800, 800);
primaryStage.setScene(scene);
primaryStage.show();
layoutItems();
}
public int getRows() {
if (useResizeCheckBox.isSelected()) {
return (int) Math.ceil(root.getBoundsInParent().getHeight() / minRowHeight);
} else {
return rowSpinner.getValue();
}
}
public int getCols() {
if (useResizeCheckBox.isSelected()) {
return (int) Math.ceil(root.getBoundsInParent().getWidth() / minColWidth);
} else {
return colSpinner.getValue();
}
}
private void layoutItems() {
int total = totalSpinner.getValue();
int rows = getRows();
int cols = getCols();
int pages = (int) Math.ceil(total / (double) (rows * cols));
pagination.setPageCount(pages);
if (currentPageContainer != null) {
MyEvent myEvent = new MyEvent(pagination.getCurrentPageIndex());
currentPageContainer.fireEvent(myEvent);
}
}
public StackPane createPage(Integer index) {
StackPane page = new StackPane();
page.getChildren().setAll(getGrid(index));
return page;
}
public GridPane getGrid(Integer index) {
GridPane grid = new GridPane();
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(0, 10, 0, 10));
int total = totalSpinner.getValue();
int rows = getRows();
int cols = getCols();
int offset = rows * cols * index;
for (int row = 0; row < rows; row++) {
for (int col = 0; col < cols; col++) {
offset++;
StackPane container = new StackPane();
Label label;
if (offset <= total) {
container.setStyle("-fx-background-color:lightgrey");
label = new Label("Item Nr. " + offset);
} else {
label = new Label("");
}
container.getChildren().add(label);
GridPane.setRowIndex(container, row);
GridPane.setColumnIndex(container, col);
GridPane.setHgrow(container, Priority.ALWAYS);
GridPane.setVgrow(container, Priority.ALWAYS);
grid.getChildren().add(container);
}
}
return grid;
}
static class MyEvent extends Event {
public static final EventType<MyEvent> UPDATE = new EventType<>(Event.ANY, "UPDATE");
int index = 1;
public MyEvent(int index) {
super(UPDATE);
this.index = index;
}
public int getIndex() {
return index;
}
}
private class MyEventHandler implements EventHandler<MyEvent> {
#Override
public void handle(MyEvent event) {
currentPageContainer.getChildren().setAll(getGrid(event.getIndex()));
}
}
public static void main(String[] args) {
launch(args);
}
}
I "just" had to use Platform.runlater to set the pagination and rethink the flow of the whole thing. There is still some optimisation to be done, but the core problem of the question is answered.
This is the solution:
import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.Pagination;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class Main extends Application {
private static final double CELL_HEIGHT = 60;
private static final double CELL_WIDTH = 100;
private static final double CELL_H_GAP = 10;
private static final double CELL_V_GAP = 10;
private static final int ITEM_COUNT = 100;
private Pagination pagination;
private int itemCount; // Count of items that fit on a page
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
StackPane root = new StackPane();
pagination = new Pagination();
pagination.setPageFactory(this::pageFactory);
root.getChildren().add(pagination);
Scene scene = new Scene(root, 500, 500);
primaryStage.setScene(scene);
primaryStage.show();
}
private StackPane pageFactory( int pageIndex ) {
TilePane page = new TilePane();
StackPane pageContainer = new StackPane(page);
pageContainer.setAlignment(Pos.CENTER);
initPage(page);
addListeners(pageContainer, page);
fillPage(page, pageIndex);
return pageContainer;
}
private void initPage( TilePane page ) {
page.setHgap(CELL_H_GAP);
page.setVgap(CELL_V_GAP);
page.setBackground(new Background(new BackgroundFill(Color.BEIGE, null, null)));
}
private void addListeners ( StackPane pageContainer, TilePane page) {
pageContainer.heightProperty().addListener( observable -> {
if (pageContainer.getWidth() != 0 && pageContainer.getHeight() != 0) {
onResize(pageContainer, page);
}
});
pageContainer.widthProperty().addListener( observable -> {
if (pageContainer.getWidth() != 0 && pageContainer.getHeight() != 0) {
onResize(pageContainer, page);
}
});
}
private void onResize ( StackPane pageContainer, TilePane page ) {
int itemCountHorizontal = 1;
int itemCountVertical = 1;
int itemCount;
int pageCount;
itemCountHorizontal += ( pageContainer.getWidth() - CELL_WIDTH - 1 ) / ( CELL_WIDTH + CELL_H_GAP );
itemCountVertical += (pageContainer.getHeight() - CELL_HEIGHT - 1 ) / ( CELL_HEIGHT + CELL_V_GAP );
itemCount = itemCountHorizontal * itemCountVertical;
itemCount = ( itemCount == 0 ) ? 1 : itemCount;
pageCount = ITEM_COUNT / itemCount + 1;
page.setMinWidth( (CELL_WIDTH * itemCountHorizontal) + (CELL_H_GAP * (itemCountHorizontal-1)) );
page.setMaxWidth( (CELL_WIDTH * itemCountHorizontal) + (CELL_H_GAP * (itemCountHorizontal-1)) );
page.setMinHeight( (CELL_HEIGHT * itemCountVertical) + (CELL_V_GAP * (itemCountVertical-1)) );
page.setMaxHeight( (CELL_HEIGHT * itemCountVertical) + (CELL_V_GAP * (itemCountVertical-1)) );
if ( pagination.getPageCount() != pageCount ) {
Platform.runLater( () -> {
pagination.setPageCount(pageCount);
});
} else if ( this.itemCount != itemCount ){
// If item count changed, rebuild the page
this.itemCount = itemCount;
page.getChildren().clear();
fillPage(page, pagination.getCurrentPageIndex());
}
}
private void fillPage ( TilePane page, int pageIndex ) {
int startIndex = pageIndex * itemCount;
int endIndex = Math.min( ITEM_COUNT, startIndex+itemCount );
for ( int i = startIndex; i < endIndex; i++ ) {
page.getChildren().add(cellFactory("C" + (i+1)));
}
}
private Pane cellFactory( String cellText ) {
StackPane cell = new StackPane();
cell.setMaxHeight(CELL_HEIGHT);
cell.setMinHeight(CELL_HEIGHT);
cell.setMaxWidth(CELL_WIDTH);
cell.setMinWidth(CELL_WIDTH);
cell.setAlignment(Pos.CENTER);
cell.setBackground(new Background(new BackgroundFill(Color.LIGHTGRAY, null, null)));
cell.getChildren().add(new Label(cellText));
return cell;
}
}
I want to change the text under Progress Indicator. By default, when the ProgressIndicator has completed its Progress the text is Done, I want to be able to edit this text with any user-defined text or text depending on the locale.
When I run the program output shows that text has been changed, but on the GUI it doesn't change. Please look at the following pictures :
MCVE
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Main extends Application {
Task copyWorker;
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Background Processes");
Group root = new Group();
Scene scene = new Scene(root, 330, 120, Color.WHITE);
BorderPane mainPane = new BorderPane();
root.getChildren().add(mainPane);
final Label label = new Label("Files Transfer:");
final ProgressIndicator progressIndicator = new ProgressIndicator(0);
progressIndicator.progressProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> ov, Number t, Number newValue) {
progressIndicator .applyCss();
// If progress is 100% then show Text
if (newValue.doubleValue() >= 1.0) {
// Apply CSS so you can lookup the text
Text text = (Text) progressIndicator .lookup(".percentage");//also I checked .lookup(.text.percentage) version
System.out.println(text.getText());
// This text replaces "Done"
text.setText("some text");
//for testing
Text x= (Text) progressIndicator .lookup(".percentage");
System.out.println(x.getText());//output shows that the text under progress indicator is changed
}
}});
final HBox hb = new HBox();
hb.setSpacing(5);
hb.setAlignment(Pos.CENTER);
hb.getChildren().addAll(label, progressIndicator);
mainPane.setTop(hb);
final Button startButton = new Button("Start");
final Button cancelButton = new Button("Cancel");
final HBox hb2 = new HBox();
hb2.setSpacing(5);
hb2.setAlignment(Pos.CENTER);
hb2.getChildren().addAll(startButton, cancelButton);
mainPane.setBottom(hb2);
startButton.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
startButton.setDisable(true);
progressIndicator.setProgress(0);
cancelButton.setDisable(false);
copyWorker = createWorker();
progressIndicator.progressProperty().unbind();
progressIndicator.progressProperty().bind(copyWorker.progressProperty());
copyWorker.messageProperty().addListener(new ChangeListener<String>() {
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
System.out.println(newValue);
}
});
new Thread(copyWorker).start();
}
});
cancelButton.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
startButton.setDisable(false);
cancelButton.setDisable(true);
copyWorker.cancel(true);
progressIndicator.progressProperty().unbind();
progressIndicator.setProgress(0);
System.out.println("cancelled.");
}
});
primaryStage.setScene(scene);
primaryStage.show();
}
public Task createWorker() {
return new Task() {
#Override
protected Object call() throws Exception {
for (int i = 0; i < 10; i++) {
Thread.sleep(100);
updateMessage("100 milliseconds");
updateProgress(i + 1, 10);
}
return true;
}
};
}
}
You need to change the text of the ProgressIndicator as well as set the width of the ProgressIndicator to the new width of the Text.
progressIndicator.progressProperty().addListener((ov, oldValue, newValue) -> {
Text text = (Text) progressIndicator.lookup(".percentage");
if(text!=null && text.getText().equals("Done")){
text.setText("New Text");
progressIndicator.setPrefWidth(text.getLayoutBounds().getWidth());
}
});
Complete Code
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Main extends Application {
Task copyWorker;
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Background Processes");
Group root = new Group();
Scene scene = new Scene(root, 330, 120, Color.WHITE);
BorderPane mainPane = new BorderPane();
root.getChildren().add(mainPane);
final Label label = new Label("Files Transfer:");
final ProgressIndicator progressIndicator = new ProgressIndicator(0);
final HBox hb = new HBox();
hb.setSpacing(5);
hb.setAlignment(Pos.CENTER);
hb.getChildren().addAll(label, progressIndicator);
mainPane.setTop(hb);
final Button startButton = new Button("Start");
final Button cancelButton = new Button("Cancel");
final HBox hb2 = new HBox();
hb2.setSpacing(5);
hb2.setAlignment(Pos.CENTER);
hb2.getChildren().addAll(startButton, cancelButton);
mainPane.setBottom(hb2);
startButton.setOnAction(event -> {
startButton.setDisable(true);
progressIndicator.setProgress(0);
cancelButton.setDisable(false);
copyWorker = createWorker();
progressIndicator.progressProperty().unbind();
progressIndicator.progressProperty().bind(copyWorker.progressProperty());
new Thread(copyWorker).start();
});
cancelButton.setOnAction(event -> {
startButton.setDisable(false);
cancelButton.setDisable(true);
copyWorker.cancel(true);
progressIndicator.progressProperty().unbind();
progressIndicator.setProgress(0);
});
primaryStage.setScene(scene);
primaryStage.show();
progressIndicator.progressProperty().addListener((observable, oldValue, newValue) -> {
Text text = (Text) progressIndicator.lookup(".percentage");
if (text != null && text.getText().equals("Done")) {
text.setText("New Text");
progressIndicator.setPrefWidth(text.getLayoutBounds().getWidth());
}
});
}
public Task createWorker() {
return new Task() {
#Override
protected Object call() throws Exception {
for (int i = 0; i < 10; i++) {
Thread.sleep(500);
updateMessage("2000 milliseconds");
updateProgress(i + 1, 10);
}
return true;
}
};
}
public static void main(String[] args) {
Application.launch(args);
}
}
It's posiible to change ProgressIndicator label (and all other default labels, like TextField context menu labels) by overwrite controls bundle file. In this file you have field:
ProgressIndicator.doneString=YourTextHere
How to overwrite ResourceBundle file you'll find here:
https://stackoverflow.com/a/48773353/7746751
I wrote a thread that checks constantly if the mouse is over a ListView, because I want to show a Popup containing info about the cell I point with the mouse.
So no problem to check if the mouse is over the ListView.
But how do I check if the mouse is over a certain cell since I cannot use ListCell.localToScreen(ListCell.getBoundsInLocal()); to get the cell coordinates on screen?
I prefer not to use ListCell event such as onMouseEntered.
Either register handlers for mouseEntered and mouseExited events on each ListCell, or observe the ListCell's hoverProperty. Here's an example using the second method:
import java.util.stream.IntStream;
import javafx.animation.FadeTransition;
import javafx.application.Application;
import javafx.geometry.Bounds;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.stage.Popup;
import javafx.stage.Stage;
import javafx.util.Duration;
public class PopupOnListCellHover extends Application {
private Popup popup ;
private Node popupContent ;
private Label titleLabel ;
private Label detailsLabel ;
private FadeTransition fadeOut ;
#Override
public void start(Stage primaryStage) {
ListView<Item> listView = new ListView<>();
popup = new Popup();
titleLabel = new Label();
titleLabel.setStyle("-fx-font-size: 1.5em ; -fx-font-weight: bold;");
detailsLabel = new Label();
popupContent = new VBox(10, titleLabel, detailsLabel);
popupContent.setStyle("-fx-background-color: -fx-background; "+
"-fx-background: lightskyblue; -fx-padding:12px;");
popup.getContent().add(popupContent);
fadeOut = new FadeTransition(Duration.millis(500), popupContent);
fadeOut.setFromValue(1.0);
fadeOut.setToValue(0.0);
fadeOut.setOnFinished(e -> popup.hide());
listView.setCellFactory(lv -> {
ListCell<Item> cell = new ListCell<Item>() {
#Override
public void updateItem(Item item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
} else {
setText(item.getName());
}
}
};
cell.hoverProperty().addListener((obs, wasHovered, isNowHovered) -> {
if (isNowHovered && ! cell.isEmpty()) {
showPopup(cell);
} else {
hidePopup();
}
});
return cell ;
});
IntStream.rangeClosed(1, 100).mapToObj(i -> new Item("Item "+i, i))
.forEach(listView.getItems()::add);
BorderPane root = new BorderPane(listView);
Scene scene = new Scene(root, 250, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
private void showPopup(ListCell<Item> cell) {
fadeOut.stop();
popupContent.setOpacity(1.0);
Bounds bounds = cell.localToScreen(cell.getBoundsInLocal());
popup.show(cell, bounds.getMaxX(), bounds.getMinY());
Item item = cell.getItem() ;
titleLabel.setText(item.getName());
detailsLabel.setText(String.format("This is %s.%nIt has value %d.",
item.getName(), item.getValue()));
}
private void hidePopup() {
fadeOut.playFromStart();
}
public static class Item {
private final int value ;
private final String name ;
public Item(String name, int value) {
this.name = name ;
this.value = value ;
}
public int getValue() {
return value ;
}
public String getName() {
return name ;
}
}
public static void main(String[] args) {
launch(args);
}
}
To use handlers for mouseEntered and mouseExited, replace
cell.hoverProperty().addListener((obs, wasHovered, isNowHovered) -> {
if (isNowHovered && ! cell.isEmpty()) {
showPopup(cell);
} else {
hidePopup();
}
});
with
cell.setOnMouseEntered(e -> showPopup(cell));
cell.setOnMouseExited(e -> hidePopup());