JavaFX HTML node elements - java

How do I add a new HTMLTableRowElement to the HTMLTableElement, and HTMLLIElement to a HTMLUListElement?
It seems to me that I can not just new something that I want and there does not seem to be a getInstance() method anywhere.
Thanks.

You can use the org.w3c.dom API (which is part of the core java libraries). To be honest, I find this API pretty ugly to use, but nevertheless it does the job.
Here's a minimal example:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
public class DynamicWebViewTest extends Application {
#Override
public void start(Stage primaryStage) {
WebView webView = new WebView();
webView.getEngine().loadContent("<html><body><table border='1' id='table'>"+
"<tr><th>Name</th><th>Value</th></tr></table></body></html>");
TextField nameTF = new TextField();
TextField valueTF = new TextField();
Button addRowButton = new Button("Add row");
addRowButton.setOnAction(event -> {
Document doc = webView.getEngine().getDocument();
Element table = doc.getElementById("table");
Element newRow = doc.createElement("tr");
Element nameCell = doc.createElement("td");
Element valueCell = doc.createElement("td");
nameCell.appendChild(doc.createTextNode(nameTF.getText()));
valueCell.appendChild(doc.createTextNode(valueTF.getText()));
newRow.appendChild(nameCell);
newRow.appendChild(valueCell);
table.appendChild(newRow);
nameTF.setText("");
valueTF.setText("");
});
HBox controls = new HBox(5, nameTF, valueTF, addRowButton);
controls.setAlignment(Pos.CENTER);
controls.setPadding(new Insets(5));
BorderPane root = new BorderPane(webView, null, null, controls, null);
Scene scene = new Scene(root, 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Supposing you are using the Java Fx Web View to add those elements , so i would recommend to build the whole page or just add the components you need via javascript calls , cause the web view gives you the js engine to perform js methods.
I am not sure if the scene builder supports customizing on the web view.

Related

JavaFX container in ScrollPane not showing

I have heard that, to add more controls/JavaFX Nodes to a ScrollPane, one must set the ScrollPane's content to a container, then add all Nodes to said container. However, when trying this, I only get a small, grey bar without any content. How can I fix this? (Trying to make something larger, but even this MWE doesn't work for me... - Working with OpenJDK OpenJFX 11 on Manjaro, if that helps)
Code:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(final Stage primaryStage) {
Label test = new Label();
test.setText("Testing JavaFX........");
VBox box = new VBox();
ScrollPane pane = new ScrollPane(box);
pane.setPrefSize(300.0, 300.0);
box.getChildren().add(test);
Scene main = new Scene(pane);
primaryStage.setScene(main);
primaryStage.show();
}
}
Result:

How to insert HTML text inside a JavaFX shape?

I want to create a tree-based algorithm visualization in JavaFx, and there are many sub scripts and super scripts in the notations. I want to add these notations to shapes like circles.
I have tried using WebView object for doing that, but it just covers up the entire screen.
public void start(Stage primaryStage) throws Exception{
primaryStage.setTitle("Shape Text");
Group circles = new Group();
Circle circle = new Circle(50, Color.web("white", 0.7));
circle.setCenterX(500.0f);
circle.setCenterY(200.0f);
circle.setStrokeType(StrokeType.OUTSIDE);
circle.setStroke(Color.web("white", 0.16));
circle.setStrokeWidth(4);
circles.getChildren().add(circle);
WebView webView = new WebView();
WebEngine webEngine = webView.getEngine();
webEngine.loadContent("<h1>B<sub>0</sub></h1>");
StackPane stack = new StackPane();
stack.getChildren().addAll(circles, webView);
Scene scene = new Scene(stack, 1000, 800, Color.BLACK);
primaryStage.setScene(scene);
primaryStage.show();
}
The above code replaces the entire view with HTML text. I also tried javafx.scene.text.Text class, but it does not support the HTML content.
Thank you in advance!
There are three things you might want to do:
Size the WebView to the HTML content (or the inner display region of the shape).
Make the background of the WebView pages transparent.
Center the HTML content in the WebView, with the WebView centered in the Shape.
Code below demonstrates some of these tricks:
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.StrokeType;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import java.lang.reflect.Field;
public class ShapedHTML extends Application {
#Override
public void start(Stage stage) throws Exception{
stage.setTitle("Shape Text");
Group circles = new Group();
Circle circle = new Circle(50, Color.web("white", 0.7));
circle.setCenterX(500.0f);
circle.setCenterY(200.0f);
circle.setStrokeType(StrokeType.OUTSIDE);
circle.setStroke(Color.web("white", 0.16));
circle.setStrokeWidth(4);
circles.getChildren().add(circle);
WebView webView = new WebView();
WebEngine webEngine = webView.getEngine();
webView.maxWidthProperty().bind(circle.radiusProperty().multiply(2));
webView.maxHeightProperty().bind(circle.radiusProperty().multiply(2));
webEngine.documentProperty().addListener(observable -> {
try {
// Use reflection to retrieve the WebEngine's private 'page' field.
Field f = webEngine.getClass().getDeclaredField("page");
f.setAccessible(true);
com.sun.webkit.WebPage page = (com.sun.webkit.WebPage) f.get(webEngine);
page.setBackgroundColor((new java.awt.Color(0, 0, 0, 0)).getRGB());
} catch (Exception e) {
System.out.println("Difficulty to make WebView background transparent");
e.printStackTrace();
}
});
webEngine.loadContent("<h1 id='root' style='background : rgba(0,0,0,0); margin: 0; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);'>B<sub>0</sub></h1>");
StackPane stack = new StackPane();
stack.getChildren().addAll(circles, webView);
Scene scene = new Scene(stack, 1000, 800, Color.BLACK);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Note on com.sun class usage
The above code uses com.sun classes (which is usually not recommended as it is not publicly supported API). But, it worked for me (on Java 8) and I don't know a better way to accomplish the transparency of the WebView background.
If you are using later versions of Java (e.g. Java 11+), then you will need to provide some VM arguments to allow usage of the relevant com.sun classes to work. See, for instance, the stack overflow question Cannot access JavaFX class "WebPage" in IntelliJ-IDEA for resolving accessibility issues for com.sun.webkit.WebPage in Java 11+. An answer to that question suggests using the following VM arguments (which I haven't tried):
--add-exports javafx.web/com.sun.webkit=projectname
where the last argument is your module name as declared in the module-info.java of your project.
Try setting the maxSize of the WebView.
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
public class JavaFXTestingGround extends Application
{
#Override
public void start(Stage primaryStage) throws Exception
{
Circle circle = new Circle(100, Color.web("white", 0.7));
WebView webView = new WebView();
WebEngine webEngine = webView.getEngine();
webEngine.loadContent("<h1>B<sub>0</sub></h1>");
webView.setMaxSize(50, 50);
StackPane stack = new StackPane();
stack.getChildren().addAll(circle, webView);
Scene scene = new Scene(stack, 1000, 800, Color.BLACK);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args)
{
launch(args);
}
}

JFXTabePane closing option is not working

I am trying to add close option in tabpane like a browser have it. Could you please tell me how to add closing feature in this tabpane? I tried this line but didn't solve my problem tabPane.setTabClosingPolicy(TabClosingPolicy.ALL_TABS);
I am using javafx jfoenix library for UI.
package tabsDemo;
import java.awt.Color;
import java.awt.Dimension;
import java.math.BigInteger;
import java.security.SecureRandom;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.SingleSelectionModel;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane.TabClosingPolicy;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXTabPane;
import com.jfoenix.controls.JFXTextField;
public class TabsDemo extends Application {
private String msg = "Tab 0";
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Tabs");
Group root = new Group();
Scene scene = new Scene(root, 800, 600);
JFXButton b1 = new JFXButton();
b1.setId("back");
b1.setGraphic(new ImageView(new Image("/tabsDemo/back.png")));
b1.setMinWidth(20);
b1.setMinHeight(20);
b1.setMaxWidth(20);
b1.setMaxHeight(20);
JFXButton b2 = new JFXButton();
b2.setId("farword");
b2.setGraphic(new ImageView(new Image("/tabsDemo/forward.png")));
b2.setMinWidth(20);
b2.setMinHeight(20);
b2.setMaxWidth(20);
b2.setMaxHeight(20);
JFXButton b3 = new JFXButton();
b3.setId("refresh");
b3.setGraphic(new ImageView(new Image("/tabsDemo/refresh.png")));
b3.setMinWidth(20);
b3.setMinHeight(20);
b3.setMaxWidth(20);
b3.setMaxHeight(20);
JFXTextField t1 = new JFXTextField();
t1.setMinWidth(100);
// t1.setPrefWidth(900);
// t1.setMaxWidth(1000);
t1.setMinHeight(30);
t1.setMaxHeight(30);
GridPane gridPane = new GridPane();
gridPane.setHgap(4);
gridPane.add(b1, 0, 0);
gridPane.add(b2, 1, 0);
gridPane.add(b3, 2, 0);
gridPane.add(t1, 3, 0);
gridPane.setHgrow(t1, Priority.ALWAYS);
BorderPane borderPane = new BorderPane();
borderPane.setTop(gridPane);
JFXTabPane tabPane = new JFXTabPane();
Tab tab1 = new Tab();
tab1.setText("Tab1");
tab1.setContent(borderPane);
VBox vbox = new VBox();
vbox.getChildren().addAll(new JFXButton("B1"), new JFXButton("B2"), new JFXButton("B3"), new JFXButton("B4"));
Tab tab2 = new Tab();
tab2.setText("Tab2");
tab2.setContent(vbox);
tabPane.getTabs().addAll(tab1, tab2);
tabPane.setPrefSize(800, 600);
// I add here the closing option for tab but it's not working
tabPane.setTabClosingPolicy(TabClosingPolicy.ALL_TABS);
SingleSelectionModel<Tab> selectionModel = tabPane.getSelectionModel();
selectionModel.select(1);
JFXButton button = new JFXButton("Add New Tab");
button.setOnMouseClicked((o) -> {
Tab temp = new Tab();
int count = tabPane.getTabs().size();
temp.setText(msg + count);
temp.setContent(new Label("Tab 0" + count));
tabPane.getTabs().add(temp);
});
borderPane.setRight(button);
tabPane.setMaxSize(800, 600);
/*
* HBox hbox = new HBox(); hbox.getChildren().addAll(button, tabPane);
* hbox.setSpacing(50); hbox.setAlignment(Pos.CENTER);
* hbox.setStyle("-fx-padding:20");
*/
BorderPane rootBorderpane = new BorderPane();
rootBorderpane.setCenter(tabPane);
root.getChildren().addAll(rootBorderpane);
scene.getStylesheets().add(TabsDemo.class.getResource("jfoenix-components.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
primaryStage.setTitle("JFX Tabs Demo");
}
private SecureRandom random = new SecureRandom();
public String nextSessionId() {
return new BigInteger(50, random).toString(16);
}
}
Thank you!
Your JFXTabPane is a custom component from the JFoenix library, so there's no guarantee that all standard JavaFX settings, such as TabClosingPolicy in your case, are fully implemented.
I checked their GitHub repository and looks like there's an unresolved issue regarding this missing feature. Someone forked the repository, apparently resolved the problem and submitted a pull request, but it's still has an open status. In other words, the changes are not included in the current build, so the bug still exists.
My suggestion is to stick with the standard JavaFX components, which should provide everything you need to create simple applications, including the closeable tabs, which are are working well as presented in the following example.
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("JFX Tabs Demo");
TabPane tabPane = new TabPane();
tabPane.setTabClosingPolicy(TabClosingPolicy.ALL_TABS);
tabPane.getTabs().add(new Tab("Test"));
Scene scene = new Scene(tabPane, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}

class is not public in my package and cannot be accessed from outside package

I am using the following ControlFX project. Hence, created a Dialogs.java class in my package and pasted the code from there.
Since I am not Inside the package org.controlsfx.dialog , I have to do the following:
import org.controlsfx.dialog.LightweightDialog;
And I am getting the following error as shown in the image below:
When I went inside the package org.controlsfx.dialog and opened, LightweightDialog.class,
I wasn't able to make the class public.
How should I overcome this situation? Please advise.
If the class is not public, it is not part of the public API, so it's not intended (or really possible) for you to use.
To use a lightweight dialog in ControlsFX, you can either use the Dialogs class API and call the lightweight() method as part of the creation of your dialog, or you can call one of the Dialog constructors which takes a flag for the lightweight property.
Here's a complete example using the Dialogs fluent API:
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import org.controlsfx.dialog.Dialogs;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
try {
BorderPane root = new BorderPane();
Scene scene = new Scene(root,600,400);
TabPane tabPane = new TabPane();
Tab tab1 = new Tab("Tab 1");
BorderPane tab1Root = new BorderPane();
Button showDialogButton = new Button("Enter message...");
VBox messages = new VBox(3);
HBox buttons = new HBox(5);
buttons.setAlignment(Pos.CENTER);
buttons.setPadding(new Insets(5));
buttons.getChildren().add(showDialogButton);
tab1Root.setBottom(buttons);
ScrollPane messageScroller = new ScrollPane();
messageScroller.setContent(messages);
tab1Root.setCenter(messageScroller);
tab1.setContent(tab1Root);
Tab tab2 = new Tab("Tab 2");
tab2.setContent(new TextField("This is tab 2"));
tabPane.getTabs().addAll(tab1, tab2);
showDialogButton.setOnAction(event -> {
String response = Dialogs.create()
.lightweight()
.owner(tab1)
.masthead("Enter a new message")
.message("Enter your new message:")
.showTextInput();
if (response != null) {
messages.getChildren().add(new Label(response));
}
});
root.setCenter(tabPane);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
Using the Dialog constructor you'd do something like this, though it's a lot more work:
// params are owner, title, lightweight:
Dialog dialog = new Dialog(someNode, "Dialog", true);
// lots of code here to configure dialog...
Action response = dialog.show();
The real beauty of ControlsFX is the very comprehensive documentation. Just check the Javadocs for Dialogs and for Dialog.

JavaFx :Default Message for Empty ListView

When there is no record in any table it shows a message 'No content in table', which is by default functionality of TableView in JavaFx.
So here my question is, does the same can be possible with ListView in JavaFx ? Like, if there is no item in any ListView then it will show a message same as TableView, instead of a blank/empty fields.
You have to try this:-
listView.setPlaceholder(new Label("No Content In List"));
its 100% working....
JavaFX8 has a setPlaceholder(...) method for ListView.
In earlier versions, you need to roll your own somehow. This is a bit of a hack: it wraps the ListView in a stack pane, with a white rectangle and the placeholder displayed over the top of the list view. The placeholder and rectangle have their visible property bound, so they are only visible if the list is empty.
There may be easier ways that I'm not seeing right away...
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class ListViewPlaceholderTest extends Application {
#Override
public void start(Stage primaryStage) {
final ListView<String> listView = new ListView<>();
final IntegerProperty counter = new SimpleIntegerProperty();
final Button addButton = new Button("Add item");
addButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
counter.set(counter.get()+1);
listView.getItems().add("Item "+counter.get());
}
});
final Button removeButton = new Button("Remove");
removeButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
listView.getItems().remove(listView.getSelectionModel().getSelectedIndex());
}
});
removeButton.disableProperty().bind(Bindings.equal(listView.getSelectionModel().selectedIndexProperty(), -1));
final HBox buttons = new HBox(5);
buttons.setPadding(new Insets(10));
buttons.getChildren().addAll(addButton, removeButton);
final BorderPane root = new BorderPane();
root.setCenter(createPlaceholderForListView(listView, new Label("No content in List")));
root.setBottom(buttons);
final Scene scene = new Scene(root, 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
private Node createPlaceholderForListView(ListView<?> listView, Node placeholder) {
final StackPane pane = new StackPane();
final Rectangle rect = new Rectangle(0, 0, Color.WHITE);
rect.widthProperty().bind(listView.widthProperty());
rect.heightProperty().bind(listView.heightProperty());
pane.getChildren().addAll(listView, rect, placeholder);
placeholder.visibleProperty().bind(Bindings.isEmpty(listView.getItems()));
rect.visibleProperty().bind(placeholder.visibleProperty());
rect.setMouseTransparent(true);
return pane ;
}
}
With fxml:
<ListView fx:id="foundContentList">
<placeholder>
<Label text="Nothing found" />
</placeholder>
</ListView>
Not entirely sure but I don't think there is a setPlaceholder method(to set the default message when no content in table) for ListView.
The workaround that I use is to create an Object in the list that indicate "No content" and show that on the listview and also disable it.
For example:
ObservableList noContent= FXCollections.observableArrayList("No content found");
ListView listView = new ListView(noContent);
listView.setDisable(true);

Categories