I want to create a refresh button for my webView that will be on top of the view (even if it hides part of the view), when I place the button on the Grid Pane it pushes the webView down or to the side (depends where I place the button)
How can I place my "refresh" button on top of the webView and not move it aside?
import java.util.List;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.VPos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
public class webviewbrowser extends Application {
#Override public void start(Stage primaryStage) throws Exception {
Pane root = new WebViewPane();
primaryStage.setScene(new Scene(root, 1024, 768));
primaryStage.setFullScreen(true);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
/**
* Create a resizable WebView pane
*/
public class WebViewPane extends Pane {
public WebViewPane() {
VBox.setVgrow(this, Priority.ALWAYS);
setMaxWidth(Double.MAX_VALUE);
setMaxHeight(Double.MAX_VALUE);
WebView view = new WebView();
view.setMinSize(500, 400);
view.setPrefSize(500, 400);
final WebEngine eng = view.getEngine();
eng.load("http://google.com");
//final TextField locationField = new TextField("http://www.google.com");
//locationField.setMaxHeight(Double.MAX_VALUE);
Button goButton = new Button("Refresh");
goButton.setDefaultButton(true);
EventHandler<ActionEvent> goAction = new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
eng.reload();
}
};
goButton.setOnAction(goAction);
GridPane grid = new GridPane();
grid.setVgap(0);
grid.setHgap(0);
GridPane.setConstraints(goButton,2,0,2,1, HPos.RIGHT, VPos.BOTTOM, Priority.ALWAYS, Priority.ALWAYS);
GridPane.setConstraints(view, 0, 0, 2, 1, HPos.CENTER, VPos.CENTER, Priority.SOMETIMES, Priority.SOMETIMES);
grid.getColumnConstraints().addAll(
new ColumnConstraints(100, 100, Double.MAX_VALUE, Priority.ALWAYS, HPos.CENTER, true),
new ColumnConstraints(40, 40, 40, Priority.NEVER, HPos.CENTER, true)
);
grid.getChildren().addAll(goButton, view);
getChildren().add(grid);
}
#Override protected void layoutChildren() {
List<Node> managed = getManagedChildren();
double width = getWidth();
double height = getHeight();
double top = getInsets().getTop();
double right = getInsets().getRight();
double left = getInsets().getLeft();
double bottom = getInsets().getBottom();
for (int i = 0; i < managed.size(); i++) {
Node child = managed.get(i);
layoutInArea(child, left, top,
width - left - right, height - top - bottom,
0, Insets.EMPTY, true, true, HPos.CENTER, VPos.CENTER);
}
}
}
}
If you want to stack one component on top of another, don't use a GridPane for layout, instead use a parent that allows layout components to be placed on top of one another. For example, a standard Pane, a StackPane, Group or Region. In these stacked style layouts, the components are rendered in order of the child component's position in the parent's child list.
In your sample code you are already extending Pane, so get rid of all of the grid code and just do:
getChildren().addAll(view, goButton);
instead of:
grid.getChildren().addAll(goButton, view);
Modify the layout properties of your goButton to position it within a parent which does not manage the layout position of it's children, e.g. you can call goButton.relocate(xPos, yPos).
You have some custom stuff in the layoutChildren method you override that may mess up the default Pane layout processing logic. Overriding layoutChildren is more of an advanced layout topic and I wouldn't advise it for beginners.
Here is an updated sample you could look at which uses some of the concepts mentioned in this answer.
import javafx.application.Application;
import javafx.event.*;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
import javafx.scene.web.*;
import javafx.stage.Stage;
public class WebViewBrowser extends Application {
#Override public void start(Stage stage) throws Exception {
stage.setScene(new Scene(new WebViewPane("http://google.com")));
stage.setFullScreen(true);
stage.show();
}
public static void main(String[] args) { launch(args); }
}
class WebViewPane extends Pane {
final WebView view = new WebView();
final Button goButton = createGoButton(view.getEngine());
public WebViewPane(String initURL) {
view.getEngine().load(initURL);
getChildren().addAll(
view,
goButton
);
initLayout();
}
private Button createGoButton(final WebEngine eng) {
Button go = new Button("Refresh");
go.setDefaultButton(true);
go.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent event) {
eng.reload();
}
});
return go;
}
private void initLayout() {
setMinSize(500, 400);
setPrefSize(1024, 768);
view.prefWidthProperty().bind(widthProperty());
view.prefHeightProperty().bind(heightProperty());
goButton.setLayoutX(10);
goButton.layoutYProperty().bind(
heightProperty().subtract(20).subtract(goButton.heightProperty())
);
}
}
Related
I found a little bug in JavaFX TabPane, and am looking for a workaround. I am running JavaFX 13.0.1.
How it happens:
The TabPane's DragPolicy must be set to TabPane.TabDragPolicy.REORDER.
You can navigate between tabs via keyboard shortcuts CTRL + TAB & CTRL + SHIFT + TAB.
However, if I drag, say, the last tab to the very left and release it back to the position it was in (so that nothing should change), these keyboard shortcuts get messed up - no longer pointing to proper next/previous tabs.
You should be able to reproduce it simply with the following code:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Test extends Application {
#Override
public void start(Stage primaryStage) {
TabPane tabPane = new TabPane();
tabPane.setTabDragPolicy(TabPane.TabDragPolicy.REORDER);
tabPane.getTabs().add(new Tab("First"));
tabPane.getTabs().add(new Tab("Second"));
tabPane.getTabs().add(new Tab("Third"));
tabPane.getTabs().add(new Tab("Fourth"));
tabPane.getTabs().add(new Tab("Fifth"));
StackPane root = new StackPane();
root.getChildren().add(tabPane);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("TabPane bug");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
An interesting bug. It appears TabPane.getTabs() returns a List which may or may not reflect the visual ordering of the tabs. But the navigation keys always rely on the getTabs() order, not the visual order.
One workaround is to use a Label as a graphic for each Tab, while leaving the Tab’s text as null. You can then keep the Tabs sorted properly yourself, by checking the visual position of each such Label.
import java.util.List;
import java.util.ArrayList;
import javafx.beans.Observable;
import javafx.geometry.Bounds;
import javafx.geometry.NodeOrientation;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class TabDragTest2 extends Application {
private static Tab createTab(String title) {
Tab tab = new Tab();
tab.setGraphic(new Label(title));
return tab;
}
#Override
public void start(Stage primaryStage) {
TabPane tabPane = new TabPane();
tabPane.setTabDragPolicy(TabPane.TabDragPolicy.REORDER);
tabPane.getTabs().add(createTab("First"));
tabPane.getTabs().add(createTab("Second"));
tabPane.getTabs().add(createTab("Third"));
tabPane.getTabs().add(createTab("Fourth"));
tabPane.getTabs().add(createTab("Fifth"));
tabPane.getTabs().addListener((Observable o) -> {
List<Tab> tabs = new ArrayList<>(tabPane.getTabs());
NodeOrientation orientation = tabPane.getEffectiveNodeOrientation();
boolean ltr = orientation == NodeOrientation.LEFT_TO_RIGHT;
tabs.sort((t1, t2) -> {
Node title1 = t1.getGraphic();
Node title2 = t2.getGraphic();
Bounds title1Bounds =
title1.localToScene(title1.getLayoutBounds());
Bounds title2Bounds =
title2.localToScene(title2.getLayoutBounds());
if (tabPane.getSide().isHorizontal()) {
if (ltr) {
return Double.compare(
title1Bounds.getMinX(), title2Bounds.getMinX());
} else {
return Double.compare(
title2Bounds.getMaxX(), title1Bounds.getMaxX());
}
} else {
return Double.compare(
title1Bounds.getMinY(), title2Bounds.getMinY());
}
});
if (!tabPane.getTabs().equals(tabs)) {
Platform.runLater(() -> tabPane.getTabs().setAll(tabs));
}
});
StackPane root = new StackPane();
root.getChildren().add(tabPane);
Scene scene = new Scene(root, 300, 250);
primaryStage.setTitle("TabPane bug");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I have a Label and a ProgressBar in a GridPane.
I've registered an onMouseClicked-event handler on the GridPane.
If I click on the Label the handler gets triggered.
If I click on the ProgressBar the handler doesn't get triggered.
Why? How can I fix it?
package test;
import javafx.application.Application;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.RowConstraints;
import javafx.stage.Stage;
public class ProgressBarTestApplication extends Application {
#Override
public void start(Stage primaryStage) {
GridPane gridPane = new GridPane();
ColumnConstraints columnConstraints0 = new ColumnConstraints();
columnConstraints0.setHgrow(Priority.SOMETIMES);
ColumnConstraints columnConstraints1 = new ColumnConstraints();
columnConstraints1.setHgrow(Priority.NEVER);
gridPane.getColumnConstraints().addAll(columnConstraints0, columnConstraints1);
RowConstraints rowConstraints0 = new RowConstraints();
rowConstraints0.setVgrow(Priority.SOMETIMES);
gridPane.getRowConstraints().add(rowConstraints0);
Label someLabel = new Label("Some Label:");
ProgressBar progressBar = new ProgressBar();
progressBar.setPrefWidth(250.0d);
someLabel.setLabelFor(progressBar);
gridPane.add(someLabel, 0, 0);
gridPane.add(progressBar, 1, 0);
gridPane.setCursor(Cursor.HAND);
gridPane.setHgap(5.0d);
gridPane.setOnMouseClicked(event -> System.out.println("Clicked!"));
Scene scene = new Scene(gridPane, 350, 150);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
It would seem that the MouseEvent is being consumed. I had a quick look through the ProgressBar class and it would seem that the ProgressIndicatorSkin's BehaviourBase may be causing the event to be consumed.
A quick and dirty solution would be to set the mouse clicked EventHandler for the ProgressBar to re-fire the event to the GridPane:
progressBar.setOnMouseClicked(e -> gridPane.fireEvent(e));
or to work with any Parent node:
progressBar.setOnMouseClicked(e -> progressBar.getParent().fireEvent(e));
or subclass ProgressBar to automatically apply this behavior:
public class NoConsumeProgressBar extends ProgressBar {
public NoConsumeProgressBar() {
setOnMouseClicked(e -> {
Parent parent = getParent();
if (parent != null) {
parent.fireEvent(e);
}
});
}
}
I want to extend a Class from Pane and then use the setContent() method from a tab from TabPane to display the Pane inside this Tab. It worked in Swing when I extended from JPanel but if I try something similar in JavaFX it only displays the tab itself and stays empty below.
I want to handle the content of the tabs in separate classes, am I doing something completely wrong here?
Swing version:
import java.awt.*;
import javax.swing.*;
import java.util.*;
public class Home extends JFrame{
private JTabbedPane jTabbedPane1 = new JTabbedPane();
private Example ex = new Example();
public static void main(String[] args) {
Home h1 = new Home();
h1.ex= new Example();
h1.jTabbedPane1.add("test",h1.ex);
} // end of main
public Home() {
// Frame-Initialisierung
super();
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
setSize(200,200);
Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
int x = (d.width - getSize().width) / 2;
int y = (d.height - getSize().height) / 2;
setLocation(x, y);
setResizable(false);
Container cp = getContentPane();
cp.setLayout(null);
// Anfang Komponenten
jTabbedPane1.setBounds(0, 0, 100, 100);
cp.add(jTabbedPane1);
setVisible(true);
} // end of public home
} // end of class Home
import java.awt.*;
import javax.swing.*;
import java.util.*;
public class Example extends JPanel {
private JList jList1 = new JList();
private DefaultListModel jList1Model = new DefaultListModel();
private JScrollPane jList1ScrollPane = new JScrollPane(jList1);
public Example(){
super();
setLayout(null);
jList1.setModel(jList1Model);
jList1ScrollPane.setBounds(0, 0, 100, 100);
add(jList1ScrollPane);
}
} // end of class Example
Not working in JavaFX version:
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.scene.Scene;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TabPane.TabClosingPolicy;
import javafx.scene.layout.Pane;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Home extends Application {
private TabPane tabpane = new TabPane();
private Example ex;
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("TEST");
Pane layout = new Pane();
tabpane.setLayoutX(-8);
tabpane.setLayoutY(24);
tabpane.setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE);
Tab tab = new Tab();
tab.setText("new tab");
tab.setContent(new Rectangle(200,200));
this.ex = new Example();
tab.setContent(ex);
tabpane.getTabs().add(tab);
layout.getChildren().add(tabpane);
Scene scene = new Scene(layout, 500, 500);
scene.getStylesheets().clear();
scene.getStylesheets().add(Home.class.getResource("style.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.setResizable(false);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
import java.util.ArrayList;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.ListView;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class Example extends Pane{
ListView<String> list = new ListView<String>();
public void start(Stage primaryStage) {
ArrayList<String> arraytest = new ArrayList<String>();
arraytest.add("test1");
arraytest.add("test2");
ObservableList<String> test = FXCollections.observableArrayList(arraytest);
list.setItems(test);
list.setLayoutX(10);
list.setLayoutY(10);
list.setPrefWidth(270);
list.setPrefHeight(270);
getChildren().add(list);
}
}
Your Swing example is incorrectly synchronized and suffers from an inflexible layout having absolute positioning. The example should be discarded except to observe that Pane "may be used directly in cases where absolute positioning of children is required." In this case, your JavaFX example does not.
In the JavaFX variation below, Example constructs the desired ListView and makes it available via the getContent() method. Home then uses that content for the tab. Resize the enclosing stage to see the effect.
tab.setContent(example.getContent());
As an aside, the private static class Example is semantically equivalent to a class having package-private access, making it easy to test in situ and later refactor into a separate class.
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TabPane.TabClosingPolicy;
import javafx.stage.Stage;
/** #see https://stackoverflow.com/a/44102580/230513 */
public class Home extends Application {
private TabPane tabpane = new TabPane();
private Example example = new Example();
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Test");
tabpane.setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE);
Tab tab = new Tab();
tab.setText("Tab One");
tab.setContent(example.getContent());
tabpane.getTabs().add(tab);
Scene scene = new Scene(tabpane);
primaryStage.setScene(scene);
primaryStage.show();
}
private static class Example {
ListView<String> view = new ListView<>();
public Example() {
ObservableList<String> list = FXCollections.observableArrayList(
"Test 1", "Test 2", "Test 3");
view.setItems(list);
}
public ListView getContent() {
return view;
}
}
public static void main(String[] args) {
launch(args);
}
}
I am adding some drag and drop behavior to my application where the user can DnD stuff onto a canvas with custom logic and rendering code. I need to get the mouse's position relative to the canvas (i.e. 0,0 being top-left) but the DragEvent does not let me do this, and mouse events do not get delivered to the canvas during the DnD.
Is it possible to allow the mouse events to be delivered while a drag-and-drop is in progress?
You can do this in a dragOver handler, which is invoked any time the mouse moves on the node on which the handler is registered during the drag.
Simple example:
import java.util.stream.Stream;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Label;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class DragToCanvas extends Application {
private Color draggingColor ;
#Override
public void start(Stage primaryStage) {
Pane dragFromPane = new Pane();
Canvas canvas = new Canvas(600, 600);
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setFill(Color.ANTIQUEWHITE);
gc.fillRect(0, 0, 600, 600);
Label coordinateLabel = new Label("[ ]");
coordinateLabel.setStyle("-fx-font-size: 24;");
Color[] colors = {Color.RED, Color.GREEN, Color.BLUE, Color.BLACK} ;
for (int i = 0; i < colors.length; i++) {
drawCircle(colors[i], dragFromPane, i);
}
canvas.setOnDragOver(e -> {
if (draggingColor != null
&& e.getDragboard().hasString()
&& e.getDragboard().getString().equals("circle")) {
e.acceptTransferModes(TransferMode.COPY);
}
coordinateLabel.setText(String.format("[%.1f, %.1f]", e.getX(), e.getY()));
});
canvas.setOnDragDropped(e -> {
if (e.getDragboard().hasString()
&& e.getDragboard().getString().equals("circle")) {
gc.setFill(draggingColor);
gc.fillOval(e.getX()-25, e.getY()-25, 50, 50);
draggingColor = null ;
e.setDropCompleted(true);
}
});
BorderPane root = new BorderPane();
root.setCenter(canvas);
root.setLeft(dragFromPane);
root.setBottom(coordinateLabel);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
private void drawCircle(Color c, Pane dragFromPane, int index) {
Circle circle = new Circle(60, 60*(index+1), 25);
circle.setFill(c);
dragFromPane.getChildren().add(circle);
circle.setOnDragDetected(e -> {
Dragboard db = circle.startDragAndDrop(TransferMode.COPY);
db.setDragView(circle.snapshot(null, null));
ClipboardContent cc = new ClipboardContent();
cc.putString("circle");
db.setContent(cc);
draggingColor = c ;
});
}
public static void main(String[] args) {
launch(args);
}
}
I'm just working on a little project and started to get in JavaFX.
I have a problem and I don't know any solutions..
So I managed it to bring a Scrollbar(incl Listener) into the root.
But the listener doesn't work.
I want to add many comboboxes one below the other and when I reached the scenesize(LayoutX) it should be possible to scrolldown.
How can I solve my problem?
package application;
import javafx.application.Application;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Orientation;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.ScrollEvent;
import javafx.scene.layout.Pane;
import javafx.scene.text.*;
import javafx.stage.Stage;
import application.XMLActions;;
public class Main extends Application {
/**
* Globale Variablen
*/
int abstandszaehler = 0;
private Pane root = new Pane();
private ScrollBar sB = new ScrollBar();
private XMLActions xA = new XMLActions();
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("XML-Zeilenersteller");
primaryStage.setResizable(false);
/**
* Elemente für die Root
*/
//Buttons
Button newXMLLine = new Button();
newXMLLine.setText("Einfügen neuer XML-Zeile");
newXMLLine.setLayoutX(735);
newXMLLine.setLayoutY(80);
newXMLLine.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
setComboBox();
}
});
Button newXMLDocument = new Button();
newXMLDocument.setText("Erstelle XML-Dokument");
newXMLDocument.setLayoutX(735);
newXMLDocument.setLayoutY(550);
newXMLDocument.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("Test2");
}
});
//Überschrift
Text header = new Text(105, 50, "XML Lines Creator");
header.setFont(new Font(30)); header.setStyle("-fx-underline: true;");
//Scrollbar
sB.setLayoutX(715);
sB.setLayoutY(80);
sB.setMin(0);
sB.setOrientation(Orientation.VERTICAL);
sB.setPrefHeight(500);
sB.setMax(360);
sB.setUnitIncrement(30);
sB.setBlockIncrement(35);
sB.valueProperty().addListener((ObservableValue<? extends Number> ov,
Number old_val, Number new_val) -> {
System.out.println(-new_val.doubleValue());
});
/**
* Hauptseite als Root
* Rootbearbeitungen
*/
root.setStyle("-fx-background-color: lightsteelblue");
root.getChildren().addAll(sB,newXMLDocument,header,newXMLLine );
//Scene setzen
Scene mainScene = new Scene(root, 900, 600);
primaryStage.setScene(mainScene);
primaryStage.show();
}
public void setComboBox(){
ComboBox cB = new ComboBox(xA.getList());
root.getChildren().add(cB);
cB.setLayoutX(80);
cB.setLayoutY(80 + abstandszaehler);
abstandszaehler = abstandszaehler + 30;
}
}
EDIT 1:
I got a little progress with that code in the listener:
root.setLayoutY(-new_val.doubleValue());
Replace the Pane with a ScrollPane. On the ScrollPane you can define the policy for your scrollbar.
If you define it like this, it will behave like a Pane:
ScrollPane sp = new ScrollPane();
sp.setHbarPolicy(ScrollBarPolicy.NEVER);
sp.setVbarPolicy(ScrollBarPolicy.NEVER);
Take a look at this article by Oracle.
First, if you wish to layout controls vertically consider using VBox. This VBox should then be enclosed by a ScrollPane.
If you then set the VBox's prefHeight to Control.USE_COMPUTED_SIZE and maxHeight to Double.POSITIVE_INFINITY the VBox should resize to fit it's content without limit, and the enclosing ScrollPane should show and hide scrollbars as necessary.