JavaFX height resize issue - java

I am am trying to get both the Height and Width of a WebView in a tab to resize. The Width works the height doesn't. The System.out.println();'s show that the height is getting entered correctly, but you can only see a little bit at the top. How do I make it so the height will scale like the width does?
public class OuroborosViewer extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
stage.setTitle("Ouroboros");
Scene scene = new Scene(new VBox(), 1200, 800);
scene.setFill(Color.OLDLACE);
MenuBar menuBar = new MenuBar();
Menu menuFile = new Menu("File");
Menu menuEdit = new Menu("Edit");
Menu menuView = new Menu("View");
menuBar.getMenus().addAll(menuFile, menuEdit, menuView);
((VBox) scene.getRoot()).getChildren().addAll(menuBar);
BorderPane borderPane = createTab(scene);
((VBox) scene.getRoot()).getChildren().add(borderPane);
scene.widthProperty().addListener(new WidthListener());
scene.heightProperty().addListener(new HeightListener());
// stage.getIcons().add(new Image("Ouroboros.svg"));
stage.setScene(scene);
stage.show();
}
private BorderPane createTab(Scene scene) {
TabPane tabPane = new TabPane();
BorderPane borderPane = new BorderPane();
Tab tab = new Tab();
tab.setText("Google");
VBox hbox = new VBox();
hbox.setAlignment(Pos.CENTER);
tab.setContent(hbox);
tabPane.getTabs().add(tab);
tab.setContent(new OuroborosBrowser(scene));
// bind to take available space
borderPane.setCenter(tabPane);
return borderPane;
}
private static class WidthListener implements ChangeListener<Number> {
#Override
public void changed(ObservableValue<? extends Number> observableValue, Number oldSceneWidth, Number newSceneWidth) {
String no = String.valueOf(newSceneWidth);
double width = Double.parseDouble(no);
System.out.println("Width : " + width);
OuroborosBrowser.browsers[0].setPrefWidth(width);
}
}
private static class HeightListener implements ChangeListener<Number> {
#Override
public void changed(ObservableValue<? extends Number> observableValue, Number oldSceneHeight, Number newSceneHeight) {
String no = String.valueOf(newSceneHeight);
double height = Double.parseDouble(no);
System.out.println("Height : " + height);
OuroborosBrowser.browsers[0].setPrefHeight(height);
}
}
}
class OuroborosBrowser extends Region {
public static int browserCounter = 0;
public static WebView[] browsers = new WebView[25];
private static WebView browser = new WebView();
final WebEngine webEngine = browser.getEngine();
private static double browserWidth = 1200;
private static double browserHeight = 800;
public OuroborosBrowser(Scene scene) {
// getStyleClass().add("browser");
webEngine.load("http://www.google.com/index.html");
browser.setPrefSize(scene.getWidth(), scene.getHeight());
getChildren().add(browser);
browsers[browserCounter] = browser;
browserCounter++;
}
}

The width works because the Scene width is always equal to the Content width hence your problem, the height is different since you added the MenuBar(menuBar)
so in your HeightListener do this
System.out.println("Height : " + newSceneHeight);
OuroborosBrowser.browsers[0].setPrefHeight(newSceneHeight.doubleValue()
- OuroborosBrowser.browsers[0].getScene().getRoot().getChildrenUnmodifiable()
.get(0).prefHeight(-1));//all this nonsense to get the menuBar
also these
String no = String.valueOf(newSceneWidth); //no need
double width = Double.parseDouble(no); // no need use newSceneHeight.doubleValue()
System.out.println("Width : " + width); //why not use no rather ??, no need
OuroborosBrowser.browsers[0].setPrefWidth(width);//use newSceneHeight.doubleValue()
also change all these, its hard to understand, you can return only tab from here, BorderPane should be your root, where you can add the menuBar with setTop(node)
TabPane tabPane = new TabPane();
BorderPane borderPane = new BorderPane();
Tab tab = new Tab();
tab.setText("Google");
VBox hbox = new VBox();//you are not using this
hbox.setAlignment(Pos.CENTER);
tab.setContent(hbox);//you replaced this
tabPane.getTabs().add(tab);
tab.setContent(new OuroborosBrowser(scene));//you see?
// bind to take available space
borderPane.setCenter(tabPane); // why this? its not needed
return borderPane;

Related

JavaFX: Multiple 'views', 'pages' or scenes - how to approach?

I am trying to build a dummy webshop with JavaFX. I deliberately do not use fxml, scenebuilder, maven or any other build tool. Just plain JavaFX, in order to really get to understand the basics.
However, I ran into a problem creating and navigating different 'pages'.
I have tried various creative solutions, like this one (is posting links allowed?), but none fully work for me, as I want every 'page', 'view' or scene in a seperate java class file, in order to keep everything structured and orderly.
I figured I'd make a Borderpane as a parent layout for every page
abstract class WindowBase extends BorderPane {
public abstract BorderPane render(App app);
public WindowBase() {
Label labelTop = new Label("Top box");
HBox topBox = new HBox();
topBox.setStyle("-fx-background-color: red;");
topBox.getChildren().addAll(labelTop);
Label labelLeft = new Label("Left box");
VBox leftBox = new VBox();
leftBox.setStyle("-fx-background-color: green;");
leftBox.getChildren().addAll(labelLeft);
Label labelRight = new Label("Right box");
VBox rightBox = new VBox();
rightBox.setStyle("-fx-background-color: blue;");
rightBox.getChildren().addAll(labelRight);
Label labelBottom = new Label("Bottom box");
HBox bottomBox = new HBox();
bottomBox.setStyle("-fx-background-color: yellow;");
bottomBox.getChildren().addAll(labelBottom);
this.setTop(topBox);
this.setLeft(leftBox);
this.setRight(rightBox);
this.setBottom(bottomBox);
}
}
and a child, the home page
public class Homepage extends WindowBase {
public BorderPane render(App app) {
Button button = new Button("Go to shopping cart");
button.setOnAction((event) -> app.toShoppingCart());
StackPane centerPane = new StackPane();
centerPane.getChildren().add(button);
this.setCenter(centerPane);
return this;
}
}
and lastly my App.java that runs everything
public class App extends Application{
private WindowBase view;
public void start(Stage stage) throws Exception {
view = new Homepage();
stage.setScene(new Scene(view.render(this)));
stage.setFullScreen(true);
stage.show();
}
public static void main(String[] args) throws Exception {
launch(args);
}
public void toHomepage() {
this.view = new Homepage();
}
public void toShoppingCart() {
this.view = new ShoppingCart();
}
}
I understand that I can't pass this (App) as an argument to view.render(), use the parameter within the method render and expect to be able to manipulate it, because it only creates a new instance of App as soon as it gets there. However, I see no other way either.
I tried placing the navigation buttons in the App class, in order to be able to manipulate view, but then I cannot call on the buttons from the subsequent views.
There must be a way to achieve what I want without writing the complete GUI in one file, right? Should I make my view static in stead, is that it?
Instead of BorderPanes I am of course also okay with using Scenes, whatever works.
I have figured out exactly the solution that I wanted. Posting it here for whoever encounters the same situation.
Class WindowBase
public class WindowBase {
public BorderPane getMainPane() {
Label labelTop = new Label("Top box");
HBox topBox = new HBox();
topBox.setStyle("-fx-background-color: red;");
topBox.getChildren().addAll(labelTop);
Label labelLeft = new Label("Left box");
VBox leftBox = new VBox();
leftBox.setStyle("-fx-background-color: green;");
leftBox.getChildren().addAll(labelLeft);
Label labelRight = new Label("Right box");
VBox rightBox = new VBox();
rightBox.setStyle("-fx-background-color: blue;");
rightBox.getChildren().addAll(labelRight);
Label labelBottom = new Label("Bottom box");
HBox bottomBox = new HBox();
bottomBox.setStyle("-fx-background-color: yellow;");
bottomBox.getChildren().addAll(labelBottom);
BorderPane borderPane = new BorderPane();
borderPane.setTop(topBox);
borderPane.setLeft(leftBox);
borderPane.setRight(rightBox);
borderPane.setBottom(bottomBox);
return borderPane;
}
}
Class Homepage
public class Homepage extends WindowBase {
public BorderPane render(Button toShoppingCart) {
Label label = new Label("This is the homepage.");
label.setStyle(
"-fx-font: normal bold 30px 'elephant'; -fx-text-fill: black; -fx-background-color: red;");
StackPane stackPane = new StackPane();
stackPane.getChildren().addAll(label);
BorderPane mainPane = getMainPane();
VBox leftBox = (VBox) mainPane.getLeft();
leftBox.getChildren().add(toShoppingCart);
//I do not understand why this works. I abstracted leftBox from mainPane, then added shoppingCart, but never added the abstracted
// leftBox back to the mainPane before returning it. This should not work, but it does. leftBox.getChildren().add(toShoppingCart)
// should have no effect.
//However, mainPane.getChildren().add(leftBox) throws an IllegalArgumentException about duplicate components, which is to be
// expected if the leftBox is already automatically added back to the mainPane.
mainPane.setCenter(stackPane);
return mainPane;
}
}
Class ShoppingCart
public class ShoppingCart extends WindowBase {
public ShoppingCart() {
super();
}
public BorderPane render(Button toHomepage) {
Label label = new Label("This is the shopping cart.");
label.setStyle(
"-fx-font: normal bold 30px 'elephant'; -fx-text-fill: black; -fx-background-color: red;");
StackPane centerPane = new StackPane();
centerPane.getChildren().add(label);
BorderPane mainPane = getMainPane();
VBox leftBox = (VBox) mainPane.getLeft();
leftBox.getChildren().add(toHomepage);
mainPane.setCenter(centerPane);
return mainPane;
}
}
Class App
public class App extends Application{
Scene scene;
#Override
public void start(Stage stage) throws Exception {
Homepage homePane = new Homepage();
Button toHomepage = new Button("Back to home page");
ShoppingCart shoppingPane = new ShoppingCart();
Button toShoppingCart = new Button("To shopping cart");
toHomepage.setOnAction(e -> scene.setRoot(homePane.render(toShoppingCart)));
toShoppingCart.setOnAction(e -> scene.setRoot(shoppingPane.render(toHomepage)));
scene = new Scene(homePane.render(toShoppingCart), 600, 400);
stage.setScene(scene);
stage.setFullScreen(true);
stage.show();
}
public static void main(String[] args) throws Exception {
launch(args);
}
}

Java FX component that appears from top and goes down

Does exists a Java FX component like one that appears from top and goes down, as in the gif below?
I was able to implement a demo based on transition of pane. It looks like:
The weakness of this approach is it doesn't work with a Window which has standard border, buttons, etc. A developer is responsible to build a nice window based on a Pane.
The other problem is resizing of the parent window will resize the sub pane as well. But if you have not resizable parent dialog it should not be a problem.
You are welcome to copy-paste this code and play with it:
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
final SubPane subPane = new SubPane();
final TranslateTransition dialogAnimator =
new TranslateTransition(new Duration(500), subPane);
final Button showSubPaneButton = new Button("Slide a sub pane");
showSubPaneButton.setOnAction(event -> {
dialogAnimator.setToY(10);
dialogAnimator.play();
});
subPane.setCloseAction(event -> {
dialogAnimator.setToY(-subPane.getHeight());
dialogAnimator.play();
});
final Pane rootPane = new StackPane(showSubPaneButton, subPane);
rootPane.setPrefWidth(400);
rootPane.setPrefHeight(300);
// By default hide the given pane by moving to the top.
Platform.runLater(() -> subPane.setTranslateY(-subPane.getHeight()));
primaryStage.setTitle("Drop slided dialog");
primaryStage.setScene(new Scene(rootPane));
primaryStage.show();
}
}
class SubPane extends BorderPane {
private final Button dialogButton = new Button("Close");
SubPane() {
final Pane centralPane = new StackPane(dialogButton);
centralPane.setStyle(
"-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.8), 10, 0, 0, 0);-fx-padding: 10;" +
"-fx-background-color: firebrick;-fx-background-radius: 10;");
setCenter(centralPane);
final Pane blankLeftPane = new StackPane();
blankLeftPane.setPrefWidth(100);
setLeft(blankLeftPane);
final Pane blankRightPane = new StackPane();
blankRightPane.setPrefWidth(100);
setRight(blankRightPane);
final Pane blankBottomPane = new StackPane();
blankBottomPane.setPrefHeight(200);
setBottom(blankBottomPane);
}
void setCloseAction(final EventHandler<ActionEvent> closeCallback) {
dialogButton.setOnAction(closeCallback);
}
}
If you're talking about the dialog window, ControlsFX have something similar NotificationPane.

How to force ScrollPane fill parent vbox

I have ListView inside ScrollPane and ScrollPane inside VBox and all is in SplitPane. How to force ScrollPane to fit and fill VBox. It should by resistant for resizing scene and split pane. I tried do this by adding listener on scene resized and setup pref width and height of scroll pane (taken from parent VBox), but it's not working always.
I do everything in pure java and i want to avoid to use fxml.
Here is sample code
Somewhere in main class (extends Application):
private void setupLayout()
{
BorderPane lvBorderPane = new BorderPane();
String lvSplitPaneStyle =
"-fx-background-color: #FFFFFF;-fx-padding: 10px";
SplitPane lvMainContainer = new SplitPane();
lvMainContainer.setOrientation(Orientation.VERTICAL);
lvMainContainer.setStyle(lvSplitPaneStyle);
VBox mLeftContainer = new VBox();
mLeftContainer.setStyle("-fx-background-color: #000000");
mLeftContainer.setMinWidth(140);
mLeftContainer.setPrefWidth(220);
VBox lvCenterContainer = new VBox();
lvCenterContainer.setStyle("-fx-background-color: #000000;");
VBox lvRightContainer = new FooBarList(mScene);
HBox lvBottomContainer = new HBox();
lvBottomContainer.setStyle("-fx-background-color: #000000;");
lvBottomContainer.setMinHeight(40);
lvBottomContainer.setMaxHeight(40);
SplitPane lvTopPane = new SplitPane();
lvTopPane.setOrientation(Orientation.HORIZONTAL);
lvMainContainer.getItems().addAll(lvTopPane, lvBottomContainer);
lvTopPane.getItems().addAll(mLeftContainer, lvCenterContainer, lvRightContainer);
lvBorderPane.setCenter(lvMainContainer);
mRoot.getChildren().add(lvBorderPane);
}
FooBarList class:
public class FooBarList extends VBox
{
public FooBarList(Scene pmScene)
{
this.mWindow = pmScene;
setupLayout();
}
private void setupLayout()
{
this.setStyle("-fx-background-color: #000000;");
this.setMinWidth(240);
this.setPrefWidth(240);
mContainer = new ListView<String>();
ObservableList<String> lvItems = FXCollections.observableArrayList("");
this.mContainer.setItems(lvItems);
this.mScrollPane = new ScrollPane(mContainer);
this.getChildren().add(mScrollPane);
this.mScrollPane.setFitToHeight(true);
this.mScrollPane.setFitToWidth(true);
final VBox lvParent = this;
mWindow.widthProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observable,
Number oldValue, Number newValue) {
mScrollPane.setPrefWidth(lvParent.getWidth());
}
});
mWindow.heightProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observable,
Number oldValue, Number newValue) {
mScrollPane.setPrefHeight(lvParent.getHeight());
}
});
}
private ScrollPane mScrollPane;
private ListView<String> mContainer;
private Scene mWindow;
}

JavaFX - BorderPane/StackPane not resizing after children changed

I am having a resize issue when content is added and removed from a JavaFX BorderPane. The BorderPane content is not being resized until the window is manually resized. I have written a small test app to model this behavior. The application builds a BorderPane that contains a rectangle embedded within a StackPane at the center of the BorderPane. Along the bottom of the BorderPane there is a VBox and HBox that contain text and a separator. There is a menu item that removes the content from the bottom of the BorderPane (MoveText -> Right) and adds similar content to the right position of the BorderPane.
When the text is added to the right position of the BorderPane, the rectangle overlaps the text. In otherwords the content of the BorderPane center is overlapping the content in the BorderPane right.
I saw the following link -
https://stackoverflow.com/questions/5302197/javafx-bug-is-there-a-way-to-force-repaint-this-is-not-a-single-threading-pr
Calling requestLayout does not seem to help. I have also tried calling impl_updatePG and impl_transformsChanged on various nodes in the graph. I got this idea from this thread - https://forums.oracle.com/forums/thread.jspa?threadID=2242083
public class BorderPaneExample extends Application
{
private BorderPane root;
private StackPane centerPane;
#Override
public void start(Stage primaryStage) throws Exception
{
root = new BorderPane();
root.setTop(getMenu());
root.setBottom(getBottomVBox());
centerPane = getCenterPane();
root.setCenter(centerPane);
Scene scene = new Scene(root, 900, 500);
primaryStage.setTitle("BorderPane Example");
primaryStage.setScene(scene);
primaryStage.show();
}
private MenuBar getMenu()
{
MenuBar menuBar = new MenuBar();
MenuItem rightMenuItem = new MenuItem("Right");
rightMenuItem.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
root.setRight(getRightHBox());
root.setBottom(null);
root.requestLayout();
}
});
MenuItem bottomMenuItem = new MenuItem("Bottom");
bottomMenuItem.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
root.setRight(null);
root.setBottom(getBottomVBox());
}
});
Menu menu = new Menu("Move text");
menu.getItems().add(rightMenuItem);
menu.getItems().add(bottomMenuItem);
menuBar.getMenus().addAll(menu);
return menuBar;
}
private HBox getRightHBox()
{
HBox hbox = new HBox();
VBox vbox = new VBox(50);
vbox.setPadding(new Insets(0, 20, 0, 20));
vbox.setAlignment(Pos.CENTER);
vbox.getChildren().addAll(new Text("Additional Info 1"),
new Text("Additional Info 2"), new Text("Additional Info 3"));
hbox.getChildren().addAll(new Separator(Orientation.VERTICAL), vbox);
return hbox;
}
private VBox getBottomVBox()
{
VBox vbox = new VBox();
HBox hbox = new HBox(20);
hbox.setPadding(new Insets(5));
hbox.setAlignment(Pos.CENTER);
hbox.getChildren().addAll(new Text("Footer Item 1")
, new Text("Footer Item 2"), new Text("Footer Item 3"));
vbox.getChildren().addAll(new Separator(), hbox);
return vbox;
}
private StackPane getCenterPane()
{
StackPane stackPane = new StackPane();
stackPane.setAlignment(Pos.CENTER);
final Rectangle rec = new Rectangle(200, 200);
rec.setFill(Color.DODGERBLUE);
rec.widthProperty().bind(stackPane.widthProperty().subtract(50));
rec.heightProperty().bind(stackPane.heightProperty().subtract(50));
stackPane.getChildren().addAll(rec);
return stackPane;
}
public static void main(String[] args)
{
Application.launch(args);
}
}
Any suggestions would be appreciated.
Thanks
For the sake of having an answer:
The StackPane needs to have its minimum size set in order to do clipping. So if you explicitly add stackPane.setMinSize(0, 0); to the getCenterPane() method, it should fix your problem.
So your getCenterPane() method would now look like this:
private StackPane getCenterPane()
{
StackPane stackPane = new StackPane();
stackPane.setMinSize(0, 0);
stackPane.setAlignment(Pos.CENTER);
final Rectangle rec = new Rectangle(200, 200);
rec.setFill(Color.DODGERBLUE);
rec.widthProperty().bind(stackPane.widthProperty().subtract(50));
rec.heightProperty().bind(stackPane.heightProperty().subtract(50));
stackPane.getChildren().addAll(rec);
return stackPane;
}

How to resize an Rectangle accordingly with a TextField?

I want to resize an Rectangle shape from JavaFX accordingly with a TextField which automatically resize on Window resize using Anchors.
I tried add anchors to the rectangle also but it won't resize from the width I set from SceneBuilder.
So short story: when my TextField resizes because my Window resize the Rectangle should resize to the same width as the TextField. Thanks
You can bind the rectangle's width to the text field's:
myRectangle.widthProperty().bind(myTextField.widthProperty());
Example:
public class Demo extends Application {
#Override
public void start(Stage stage) {
Group root = new Group();
Scene scene = new Scene(root);
stage.setScene(scene);
TextField myTextField = new TextField("default");
Rectangle myRectangle = new Rectangle();
myRectangle.setHeight(30);
myRectangle.setFill(Color.AQUA);
myRectangle.widthProperty().bind(myTextField.widthProperty());
final VBox hb = new VBox(10);
hb.setPadding(new Insets(5));
hb.getChildren().addAll(myTextField, myRectangle);
scene.setRoot(hb);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Categories