I have a TreeItem<String> object.
I would like to put a button in it.
Button button = new Button("Hello!");
root.setGraphic(button); // root is the TreeItem object
Not bad, but I would like to freely position this button. I would like to the right of "Root".
Changing the button's layout X doesn't seem to do anything. How can I do this then?
You can create custom class extending HBox class and containing constructor declarations with Label and Button initialization. Next, you can use this class to specify TreeView<T> and TreeItem<T> generic types. Sample of such simple HBox custom class can look like this:
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
public class CustomItem extends HBox {
private Label boxText;
private Button boxButton;
CustomItem(Label txt) {
super();
this.boxText = txt;
this.getChildren().add(boxText);
this.setAlignment(Pos.CENTER_LEFT);
}
CustomItem(Label txt, Button bt) {
super(5);
this.boxText = txt;
this.boxButton = bt;
this.getChildren().addAll(boxText, boxButton);
this.setAlignment(Pos.CENTER_LEFT);
}
}
And the use of it in practice can look like shown below:
import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class TreeViewDemo extends Application {
public Parent createContent() {
/* layout */
BorderPane layout = new BorderPane();
/* layout -> center */
/* initialize TreeView<CustomItem> object */
TreeView<CustomItem> tree = new TreeView<CustomItem>();
/* initialize tree root item */
TreeItem<CustomItem> root = new TreeItem<CustomItem>(new CustomItem(new Label("Root")));
/* initialize TreeItem<CustomItem> as container for CustomItem object */
TreeItem<CustomItem> node = new TreeItem<CustomItem>(new CustomItem(new Label("Node 1"), new Button("Button 1")));
/* add node to root */
root.getChildren().add(node);
/* set tree root */
tree.setRoot(root);
/* add items to the layout */
layout.setCenter(tree);
return layout;
}
#Override
public void start(Stage stage) throws Exception {
stage.setScene(new Scene(createContent()));
stage.setWidth(200);
stage.setHeight(200);
stage.show();
}
public static void main(String args[]) {
launch(args);
}
}
Effect will look like this:
The quick way will be not to set the valueProperty of tree item and add the desired value as text (or label) to the graphicProperty.
root.setValue("");
Text text = new Text("Root");
Button button = new Button("Hello!");
HBox hbox = new HBox(5);
hbox.getChildren().addAll(text, button);
root.setGraphic(vbox);
The other long way, will be looking up the sub node of tree item and change its properties accordingly.
Edit note: Well it would more appropriate to use HBox instead of VBox. I was in hurry :)
Related
(My) expected behaviour:
When you call setFocusTraversable(false) on a Node with children (i.e. the Node has a method getChildren() that returns a List of Nodes) the focusTraversable properties of that node and all its children get the value false.
Actual behaviour:
However, when I call setFocusTraversable(false) on for example a TextFlow, its children are still being able to receive focus. This is illustrated in the code below.
Why does the setFocusTraversable(boolean) method work this way, and how should one work around this limitation?
// File: App.java
package nl.aronhoogeveen.bugreports;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Hyperlink;
import javafx.scene.layout.BorderPane;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import javafx.stage.Stage;
/**
* JavaFX App
*/
public class App extends Application {
#Override
public void start(Stage stage) {
// TextFlow 1
Text text1 = new Text("This is text1");
Hyperlink hyperlink1 = new Hyperlink("This is hyperlink1");
Text text2 = new Text("This is text2");
Hyperlink hyperlink2 = new Hyperlink("This is hyperlink2");
TextFlow textFlow1 = new TextFlow(text1, hyperlink1, text2, hyperlink2);
textFlow1.setFocusTraversable(false);
// TextFlow 2 (CustomTextFlow)
Text text3 = new Text("This is text3");
Hyperlink hyperlink3 = new Hyperlink("This is hyperlink3");
Text text4 = new Text("This is text4");
Hyperlink hyperlink4 = new Hyperlink("This is SPARTAAAAA");
CustomTextFlow textFlow2 = new CustomTextFlow(text3, hyperlink3, text4, hyperlink4);
textFlow2.customSetFocusTraversable(false);
BorderPane rootBorderPane = new BorderPane();
rootBorderPane.setTop(textFlow1);
rootBorderPane.setBottom(textFlow2);
stage.setScene(new Scene(rootBorderPane));
stage.sizeToScene();
stage.show();
}
public static void main(String[] args) {
launch();
}
}
// File: CustomTextFlow.java
package nl.aronhoogeveen.bugreports;
import javafx.scene.Node;
import javafx.scene.text.TextFlow;
public class CustomTextFlow extends TextFlow {
public CustomTextFlow(Node... children) {
super(children);
}
/**
* Sets the property focusTraversable of all children's children to {#code b}.
*
* #param b the value to set for the properties
*/
public void customSetFocusTraversable(boolean b) {
for (Node child : getChildren()) {
// Assume for simplicity that the children are not Parents or other types that have children
child.setFocusTraversable(b);
}
}
}
I filed a bug on the ControlsFX component HyperlinkLabel regarding this behaviour, and after investigating I discovered that this behaviour was already present in the JavaFX TextFlow component that is used by the HyperlinkLabel and therefore I am posting this question.
I have a JavaFX TreeView which has TreeItems with two basic colors , red and white . When they are selected they both have -fx-underline:true but i have a problem with the line being Red or White i want the underline to be always white , is that possible with css in JavaFX ?
I have tried the below but it doesn't work .
.tree-cell-white:selected {
-fx-font-weight: bold ;
-fx-text-fill:white;
-fx-underline: true;
-fx-underline-fill: orange;
-fx-underline-color: blue;
}
.tree-cell-red:selected {
-fx-font-weight: bold ;
-fx-text-fill:firebrick;
-fx-underline: true;
}
Unfortunately I could not find how to do it by JavaFX CSS, but I found another solution. That is I created MyTrack class that derives from HBox and added an ImageView as an music/mp3 icon and Label with text.
Here is a little trick I underlined label by setting its bottom border color and set MyTrack as a graphic for TreeItem.
package underlined.treeview;
import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.stage.Stage;
import java.util.Arrays;
import java.util.List;
public class TreeViewApp extends Application {
public static void main(String[] args) {
launch(args);
}
private Parent createContent() {
TreeItem<String> treeRoot = new TreeItem<>("Root node");
List<TreeItem<String>> tracks = Arrays.asList(
new TreeItem<>(""),
new TreeItem<>(""),
new TreeItem<>(""));
for (TreeItem<String> track : tracks)
track.setGraphic(new MyTrack("Track"));
treeRoot.getChildren().addAll(tracks);
TreeView treeView = new TreeView();
treeView.setShowRoot(true);
treeView.setRoot(treeRoot);
treeRoot.setExpanded(true);
return treeView;
}
#Override
public void start(Stage primaryStage) throws Exception {
Scene scene = new Scene(createContent());
primaryStage.setScene(scene);
primaryStage.show();
}
}
and MyTrack class:
package underlined.treeview;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
public class MyTrack extends HBox {
public MyTrack(String text) {
Label label = new Label(text);
label.setStyle("-fx-border-color: aqua;" +
"-fx-border-width: 0 0 1 0;");
Image image = new Image(getClass().getResource("/img/icon/mp3.png")
.toExternalForm());
ImageView imageView = new ImageView(image);
imageView.setFitWidth(16);
imageView.setFitHeight(16);
getChildren().addAll(imageView, label);
}
}
And Voila!!!
In my example bottom border color is aqua here. Note that TreeItem should be instantiated with empty String. It was just an example you need to optimize it yourself.
I would like to apply two or three different styles to text in a single cell in a TableView.
For example, I'd like the single cell to have text formatted like this:
Edge of the Sun [EP] (Disc 2)
I'd really like to do it with colors, as well.
I know how to apply styling to the entire cell, but I don't even know where to start for applying style to part of the text.
Putting the data in different columns isn't a viable option.
Below's a quick example that
uses TextFlow to style parts of a text
implements a custom TableCell that has a TextFlow as its graphics and updates the text parts as appropriate
Note that there is a slight visual glitch: the prefHeight of the flow seems to return the accumulated height of the lines as if they were wrapped even if they aren't, thus making the row height oversized. As a quick hack, the computePrefHeight is overridden to force a single line - with the drawback that the other line/s simply disappear if the column width is decreased. Pretty sure there's something better but too lazy to further dig ;)
import java.util.Locale;
import java.util.logging.Logger;
import javafx.application.Application;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.scene.text.TextFlow;
import javafx.stage.Stage;
/**
*
*/
public class TableFormattedCell extends Application {
public static class MyCell extends TableCell<Locale, Locale> {
private TextFlow flow;
private Label displayName;
private Label displayLanguage;
public MyCell() {
displayName = new Label();
displayName.setStyle("-fx-font-weight: bold");
displayLanguage = new Label();
displayLanguage.setStyle("-fx-font-style: italic; -fx-text-fill: darkviolet");
flow = new TextFlow(displayName, displayLanguage) {
#Override
protected double computePrefHeight(double width) {
// quick hack to force into single line ...
// there must be something better ..
return super.computePrefHeight(-1);
}
};
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
setGraphic(flow);
}
#Override
protected void updateItem(Locale item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
displayName.setText("");
displayLanguage.setText("");
} else {
displayName.setText(item.getDisplayName() + " ");
displayLanguage.setText(item.getDisplayLanguage());
}
}
}
private Parent getContent() {
TableView<Locale> table = new TableView<>(FXCollections.observableArrayList(
Locale.getAvailableLocales()));
TableColumn<Locale, String> countryCode = new TableColumn<>("CountryCode");
countryCode.setCellValueFactory(new PropertyValueFactory<>("country"));
TableColumn<Locale, String> language = new TableColumn<>("Language");
language.setCellValueFactory(new PropertyValueFactory<>("language"));
table.getColumns().addAll(countryCode, language);
TableColumn<Locale, Locale> local = new TableColumn<>("Locale");
local.setCellValueFactory(c -> new SimpleObjectProperty<>(c.getValue()));
local.setCellFactory(e -> new MyCell());
table.getColumns().addAll(local);
BorderPane pane = new BorderPane(table);
return pane;
}
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setScene(new Scene(getContent(), 800, 400));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
#SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(TableFormattedCell.class.getName());
}
I'm creating a simple lottery game where I can chose numbers from 1 to 42. I would like to mark the chosen number with a red "X" (instead of using an effect) but the user should still see the chosen number beneath the "X". When the user clicks the marked number again, the "X" should disappear.
The GUI looks like this:
enter image description here
How can I display a second text on a button in the way that the first text is still visible?
Thanks for your help
kind regards
Joel
You can set the Graphic of a button to a StackPane and then add an element to the StackPane. Since the children of a StackPane are all displayed on top of each other, this yields the effect that you want. My example just shows the concept, I would probably write a new class that extends Button in your use scenario :)
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.Pane;
public class NewFXMain extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Pane pane = new Pane();
StackPane buttonPane = new StackPane();
Label label = new Label("42");
buttonPane.getChildren().add(label);
Button button = new Button();
button.setGraphic(buttonPane);
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
if(buttonPane.getChildren().size() == 1){
Label labelX = new Label("X");
labelX.setStyle("-fx-text-fill: red;");
buttonPane.getChildren().add(labelX);
}
else
buttonPane.getChildren().remove(1);
}
});
pane.getChildren().addAll(button);
primaryStage.setScene(new Scene(pane, 300, 275));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I want to set up a model for my project so my controllers can communicate with each other. I want it to have a setter and getter, to allow easy access to styling certain nodes from either class.
My question: is it possible to bind a style property (ex. "-fx-background-color: blue") to a node?
From my research, I see that this is definitely possible with text values for labels (explained by James_D here: JavaFX - How to use a method in a controller from another controller?), but I am having a hard time figuring out what the syntax for doing a similar thing with "setStyle" would be.
The model I have so far:
public class Model {
private final StringProperty shadow = new SimpleStringProperty("-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.24), 10,0,0,0)");
public StringProperty shadowProperty() {
return shadow;
}
public final String getShadow() {
return shadowProperty().get();
}
public final void setShadow(String shadow) {
shadowProperty().set(shadow);
}
}
I understand how I would set the "shadow" value from a controller, but what I don't understand is how I can bind a node from another controller to listen to that change.
Let's say the node is something like:
#FXML AnchorPane appBar
I want "appBar" to take on any changes made to "shadow" in the model. What would that look like?
You need to add a listener to the shadowProperty to listen to its changes.
something.shadowProperty() .addListener( (observable, oldValue, newValue) -> {
//do something with appBar
}) ;
I'm not entirely sure what you want to achieve, but this should answer your question about how to listen to property changes.
PS: im on mobile, so no guarantees regarding typos
Edit: you can also bind the property of one object to the property of another. Use bind() for that.
EDIT: Here is an example:
import javafx.application.Application;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.scene.Scene;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class Main extends Application {
Property<Background> backgroundProperty;
StringProperty styleProperty;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
VBox root = new VBox(10);
backgroundProperty = new SimpleObjectProperty<>();
styleProperty = new SimpleStringProperty();
// Pane that changes background by listener
Pane pane1 = new Pane();
pane1.setMinHeight(40);
backgroundProperty.addListener( (observable, oldValue, newValue) -> {
pane1.setBackground(backgroundProperty.getValue());
});
// Pane that changes background by property binding
Pane pane2 = new Pane();
pane2.setMinHeight(40);
pane2.backgroundProperty().bind(backgroundProperty);
// Pane that binds the style property
Pane pane3 = new Pane();
pane3.setMinHeight(40);
pane3.styleProperty().bind(styleProperty);
backgroundProperty.setValue(new Background(new BackgroundFill(Color.RED, null, null)));
styleProperty.setValue("-fx-background-color: black");
root.getChildren().add(pane1);
root.getChildren().add(pane2);
root.getChildren().add(pane3);
Scene scene = new Scene(root, 200, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
}