I've been trying to get a GridPane's content to behave regarding auto-size but I'm relatively new to JavaFX, so I've not had any success. Basically, each column is what appears to me to be a completely random size, and I have no idea how to make them grow correctly. If I try to set column constraints, the content sometimes disappears completely, sometimes it's just sized completely arbitrarily. Trying to bind preferred width to anything else also fails to work. I'm sure the error is mine, though. Here's my code (the contentHolder is a ScrollPane with vGrow set to ALWAYS):
int row = 1;
GridPane gridPane = new GridPane();
for (List<IndexObject> stepList : search) {
Label itemLabel = new Label();
itemLabel.setText(String.valueOf(row));
TreeItem<String> treeRoot = new TreeItem<>(stepList.get(0).getPath());
for (int line = 1; line < stepList.size(); line++) {
treeRoot.getChildren().add(new TreeItem<>(stepList.get(line).getPath()));
}
TreeView treeView = new TreeView();
treeView.setShowRoot(true);
treeView.setRoot(treeRoot);
treeRoot.setExpanded(true);
IndexObject lastObject = stepList.get(stepList.size() - 1);
TextArea output = new TextArea();
output.setText(lastObject.prettyPrint());
gridPane.addRow(row, itemLabel, treeView, output);
row++;
}
contentHolder.setContent(gridPane);
Everything up to the contentHolder is defined in fxml. Here's what it looks like. No entry has anywhere near the height that is automatically assigned, but all the widths are too small. What am I missing?
The answer is that the scroll pane does not automatically resize until you make it:
<ScrollPane VBox.vgrow="ALWAYS" fitToWidth="true">
at which point everything works as expected.
Related
I need to make a chat-simulating JavaFX application for my assignment (no web functionalities, just two text fields for input and two text areas for output). I need to have a “Send” button next to my text field. I cannot make the text field to fill up the width of my window without “squishing” the button at startup, similarly to how swing’s boxLayout does that?
I bound the field’s width to the parent pane’s width, subtracting the button’s width and the pane’sspacing and it works after I start resizing the window, but when the application first starts up, the button’s text is not fully visible.
public void start(Stage stage_main) throws Exception {
//Pane creation and nesting:
HBox pane_main = new HBox();
Scene scene_main = new Scene(pane_main, 480, 360);
BorderPane pane_left_parent = new BorderPane();
BorderPane pane_right_parent = new BorderPane();
HBox pane_left_bottom = new HBox();
HBox pane_right_bottom = new HBox();
pane_main.getChildren().addAll(pane_left_parent, pane_right_parent); //Focusing only on the left pane for now for testing.
pane_left_parent.setBottom(pane_left_bottom);
//Contents creation and nesting:
TextArea textA_left = new TextArea("Testing...");
Button button_left = new Button("Send");
TextField textF_left = new TextField("Test input...");
textF_left.prefWidthProperty().bind(pane_left_bottom.widthProperty().subtract(button_left.widthProperty()).subtract(pane_left_bottom.spacingProperty()));
//Placing contents in panes:
pane_left_parent.setCenter(textA_left);
pane_left_bottom.setSpacing(3);
pane_left_bottom.getChildren().addAll(textF_left, button_left);
//Finishing up:
stage_main.setScene(scene_main);
stage_main.show();
}
Is there any way to have the button have it’s “best” size already at startup without manually setting any widths in pixels, just like in swing?
Don't bind the prefWidth to the parent. If you want a child of an HBox to grow horizontally you can set an hgrow constraint on it:
HBox.setHgrow(theChild, Priority.ALWAYS);
Then let the HBox handle sizing and positioning the child node. And as you noted, in order to stop the Button from shrinking as the HBox changes size you need to set its minWidth. However, you should use:
button.setMinWidth(Region.USE_PREF_SIZE);
If you use getWidth() you might accidentally call it before the Button actually has a non-zero width. Plus, using USE_PREF_SIZE means the minWidth will stay up-to-date with the prefWidth (if you change it for whatever reason).
Some links:
HBox#.setHgrow(Node,Priority)
Priority
Region#USE_PREF_SIZE
I am builing an expanding Window/layout that shall expand/shrink on supplied data, possibly expanding to fill the browser window.
Edit: The scroll bars shall be on the work-area, not the window.
So far i have only managed to get it working vertically with a simple list of labels (see simple_list.png).
Vertical scrollbar appear when browser window filled vertically Horizontal scrollbar always present and width of window/layout seems directed by length of panel title and window icons at top-right.
Edit: Scroll bars OK on Panel. No horizontal expansion.
It fails completeley for a GridLayout (see gridlayout.png).
Edit: I.E no scrollbars on work-area (Panel)
The final layout is supposed to have a central work area and header, footer, left/right areas. The work area to expand according to data.
Barked up the wrong tree? CSSLayout? Not for Vaadin?
Any help/guidance appreciated.
A simplified test case below (Vaadin 7, subclassing Window).
public TestWindow() {
super.setSizeUndefined();
VerticalLayout vL = new VerticalLayout();
vL.setSizeFull();
super.setContent(vL);
//vL.addComponent(buildHeader());
Panel workArea = new Panel();
workArea.setSizeFull();
workArea.setCaption("View");
vL.addComponent(workArea);
vL.setExpandRatio(workArea, 1f);
//vL.addComponent(buildFooter());
//-----------------------------
VerticalLayout view = new VerticalLayout();
view.setSizeUndefined();
workArea.setContent(view);
for (int i=0; i < 100; i++) {
String s = "view line "+i;
for (int j=0; j < 10; j++) {
s += "____________________"+j;
}
Label line = new Label(s);
line.setSizeUndefined();
view.addComponent(line);
}
//=============================
/*
VerticalLayout view = new VerticalLayout();
view.setSizeUndefined();
workArea.setContent(view);
GridLayout gl = new GridLayout();
gl.setSizeUndefined();
view.addComponent(gl);
view.setExpandRatio(gl, 1);
gl.setColumns(2);
for (int i=0; i < 100; i++) {
Label line = new Label("view line "+i);
line.setSizeUndefined();
gl.addComponent(line);
}
*/
//------------------------------
center();
setModal(true);
setDraggable(true);
UI.getCurrent().addWindow(this);
}
simple_list.png
gridlayout.png
Vaadin documentation says:
If a sub-window has a fixed or percentual size and its content becomes too big to fit in the content area, a scroll bar will appear for the particular direction. On the other hand, if the sub-window has undefined size in the direction, it will fit the size of the content and never get a scroll bar.
Source: https://vaadin.com/docs/v7/framework/layout/layout-sub-window.html
I have some legacy code to fix and I'm struggling on the following:
I have 2 nested gridpanes. Inside the inner grid, text has to be added.
The column widths of both inner and outer grids are calculated relative to the screen size using following function:
private GridPane createGridPane( int []colsPercent, int []rowsPercent ) {
GridPane gridPane = new GridPane()
// setup columns
ColumnConstraints []colConst = new ColumnConstraints[colsPercent.length];
for( int i = 0 ; i < colsPercent.length ; i++ ) {
colConst[i] = new ColumnConstraints();
colConst[i].setPercentWidth(colsPercent[i]);
colConst[i].setFillWidth(true);
}
gridPane.getColumnConstraints().addAll(colConst);
// setup rows
RowConstraints []rowConst = new RowConstraints[rowsPercent.length];
for( int i = 0 ; i < rowsPercent.length ; i++ ) {
rowConst[i] = new RowConstraints();
rowConst[i].setPercentHeight(rowsPercent[i]);
rowConst[i].setFillHeight(true);
}
gridPane.getRowConstraints().addAll(rowConst);
return gridPane;
}
The grids and their contents are added by following code:
gridPane = createGridPane(colConstFeedbackScreenPercent,
rowConstFeedbackScreenPercent);
gridPane.setGridLinesVisible(true);
GridPane.setHgrow(gridPane, Priority.NEVER);
GridPane innerGridPane = createGridPane(colConstFeedbackInnerPercent,
rowConstFeedbackInnerPercent);
GridPane.setHgrow(innerGridPane, Priority.NEVER);
innerGridPane.setGridLinesVisible(true);
gridPane.add(innerGridPane, 1, 1);
If I add text to the inner gridPane, like so:
VBox vBox = new VBox();
vBox.setSpacing(25);
{
Text text = new Text(IRAPfx.getData().getOptionString("Localised text 6"));
text.setFont(Font.font("Arial",FontPosture.ITALIC,FONT_SIZE_INSTRUCTIONS));
text.setFill(Color.WHITE);
GridPane.setHgrow(text, Priority.NEVER);
vBox.getChildren().add(text);
}
...
GridPane.setHgrow(vBox, Priority.NEVER);
innerGridPane.add(vBox, 1, 1 + resultsCell );
The inner grid, and together with this inner grid the outer grid, will grow when the text string is too large to fit in its cell, resulting in the whole grid (inner and outer) being "stretched" to fit this long contents, and running over the right edge of the screen.
What I would like to archieve is that the long string runs over the outer grid, or even out of the screen if it is really long, but not stretching the whole grid.
As you can see in the code, I tried setting Hgrow to NEVER to no avail. Any suggestions how to fix it (I can't find examples or tips on the www..) would be very welcome.
Thanks in advance,
Joris
I found a partial solution to it:
You can clip or wrap the text to the available width of the cell by using 'label' in stead of 'text'. See below:
For wrapping:
{
Label label = new Label(IRAPfx.getData().getOptionString("Localised text 6"));
label.setFont(Font.font("Arial",FontPosture.ITALIC,FONT_SIZE_INSTRUCTIONS));
label.setWrapText(true);
label.setTextFill(Color.WHITE);
vBox.getChildren().add(label);
}
For clipping:
{
Label label = new Label(IRAPfx.getData().getOptionString("Localised text 6"));
label.setFont(Font.font("Arial",FontPosture.ITALIC,FONT
label.setTextOverrun(OverrunStyle.CLIP);
label.setTextFill(Color.WHITE);
vBox.getChildren().add(label);
}
This more or less solves my problem, however I still did not manage to let the text run over into the next columns (the ones to the right of the cell where the text/label is put).
Any help on that still welcome!
Have you tried the setWrappingWidth method on Text object, you can define a predefined with of this way, 200 in this case:
text.setWrappingWidth(200);
It isn't really clear to me why you have the Text elements inside a VBox before putting them into the GridPane. But you can do something like this:
vbox.maxWidthProperty().bind(innerGridPane.widthProperty().multiply(innerGridPane.getColumnConstraints().get(1).percentWidthProperty());
text.wrappingWidthProperty().bind(vbox.maxWidthProperty());
Unfortunately, you can't just get a GridPane column and get it's width property to bind it, so you have to recalculate it using the ColumnConstraints properties.
Any case where you need to have items dynamically change behaviours base on window sizes and so on, you'll need to bind the relevant properties together.
To inspect this unexpected behavior, I simply put a TextArea directly into the Scene contained by the PrimaryStage: On app start, the TextArea exactly fits the window (as expected).
But the size of the TextArea does not change if I move the window's borders, which is the problem I am trying to solve.
Please see my Screenshot
This is my ScalaFX code (which I expect to act exactly like its JavaFX equivalent):
object MyApp extends JFXApp {
stage = new PrimaryStage {
title = "My App"
resizable = true // has no effect
maxWidth = Double.MaxValue // has no effect
maxHeight = Double.MaxValue // has no effect
val outputDisplay = new TextArea {
resizable = true // has no effect
maxWidth = Double.MaxValue // has no effect
maxHeight = Double.MaxValue // has no effect
hgrow = Priority.Always // has no effect
vgrow = Priority.Always // has no effect
}
scene = new Scene {
resizable = true // has no effect
maxWidth = Double.MaxValue // has no effect
maxHeight = Double.MaxValue // has no effect
fill = LightGreen
// add outputDisplay as content
content = outputDisplay
}
}
}
For TextArea to resize properly you need a layout. For example BorderPane should be used as scene's content.
More about layouts can found at http://docs.oracle.com/javase/8/javafx/layout-tutorial/builtin_layouts.htm#JFXLY102
UPDATE
After looking at the ScalaFX source code,I realized that root should be used instead of content on the scene
When I was append some text dynamically to the TextArea it was wrapped correctly as follow:
But due to requirement changed I have to add some image as bullet for (in front of the) each text. Then I used GridPane to add those text with images as follow:
Code used to add components to GridPane:
// Set the GridPane empty
gridPane.getChildren().removeAll();
// Add image with each text
int index = 0;
for(String des : descriptionsList) {
HBox btnHb = new HBox();
ImageView passed = new ImageView();
passed.setImage(new Image(getClass().getResourceAsStream(GuiConstant.Image.IMAGE_PASSED)));
btnHb.getChildren().add(passed);
Text text = new Text(des);
btnHb.getChildren().add(text);
gridPane.addRow(index, btnHb);
index++;
}
Now (with GridPane) added Texts are not wrapped properly. How can I fix this issue. Thanks.
You need to set the wrappingWidth on the Text instances to be relative to the width of the GridPane. You may want to do that indirectly via the HBox instances:
btnHub.prefWidthProperty().bind(gridPane.widthProperty();
text.wrappingWidthProperty().bind(btnHub.widthProperty().subtract(passed.widthProperty().subtract(10));
I didn't actually try these changes, but they (or something very similar) should do the trick.