JavaFX scaling does not resize the component in parent container - java

I have a container that needs to display two custom components scaled down by 25% aligning them vertically.
I'm using a VBox loaded from this FXML:
<fx:root type="VBox" fx:id="leaderDisplay" xmlns="https://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml" fx:controller="MyController"
prefHeight="720.0" prefWidth="200.0" alignment="CENTER">
</fx:root>
And the component is loaded from this FXML:
<fx:root stylesheets="#css/style.css" type="StackPane"
maxHeight="294.0" maxWidth="195.0"
fx:controller="MyOtherController"
xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1">
<AnchorPane fx:id="cardPane" styleClass="leader-card" prefHeight="294.0" prefWidth="195.0">
<FlowPane fx:id="lcRequirements" layoutX="13.0" layoutY="10.0" />
<Label fx:id="lcVictoryPoints" layoutX="87.0" layoutY="162.0" prefHeight="25.0" prefWidth="20.0" text="0" />
</AnchorPane>
<Pane fx:id="cardBack" styleClass="leader-card-back" maxHeight="294.0" maxWidth="195.0" visible="false" />
</fx:root>
I add the needed components programmatically with this method in the VBox controller:
private void addLeader(LeaderCard newLeader) {
Platform.runLater(() -> {
setStyle("-fx-background-color: grey;");
LeaderCardWidget newWidget = new LeaderCardWidget(newLeader);
System.out.println("Height before: " + newWidget.getMaxHeight());
newWidget.setStyle("-fx-border-color: black;" +
"-fx-border-width: 3");
newWidget.setScaleX(0.75);
newWidget.setScaleY(0.75);
System.out.println("Height after: " + newWidget.getMaxHeight());
leaderDisplay.getChildren().add(newWidget);
leadersAndWidgets.put(newLeader, newWidget);
});
}
The problem is that the components are being scaled down, but the vbox does not display them properly, leaving huge spacing around them (to the right is the same screenshot without scaling for reference):

From the "Visual Bounds versus Layout Bounds" section of the layout documentation:
Node provides the layoutBounds property to define the 'logical'
bounds of the node for layout and boundsInParent to define the visual
bounds once all effects, clipping, and transforms have been applied.
... if a ScaleTransition is used to pulse the size of a button,
that pulse animation will not disturb layout around that button. If an
application wishes to have the effect, clip, or transform factored
into the layout of a node, it should wrap that node in a Group.
In short, transforms, such as scaling, are not factored into the layout calculations of the parent. You can make that happen by wrapping your components in a group. I think you can achieve this in your code with
// leaderDisplay.getChildren().add(newWidget);
leaderDisplay.getChildren().add(new Group(newWidget));

Related

Problem Binding TableView prefWidthProperty to it's Parent Container's prefWidthProperty

Relevant FXML :
<ScrollPane fx:id="scrollpane_var" hbarPolicy="NEVER" prefHeight="200.0" prefWidth="200.0" GridPane.columnSpan="3" GridPane.rowIndex="2" GridPane.rowSpan="5">
<content>
<TableView fx:id="tableview_var" prefHeight="169.0" prefWidth="292.0">
<columns>
<TableColumn prefWidth="69.0" text="Quantité" />
<TableColumn prefWidth="161.0" text="Nom" />
<TableColumn prefWidth="56.0" text="Prix" />
</columns>
</TableView>
</content>
</ScrollPane>
Controller class Code :
public class Lancer {
#FXML // fx:id="scrollpane_var
private ScrollPane scrollpane_var; // Value injected by FXMLLoader
#FXML // fx:id="tableview_var"
private TableView<String> tableview_var; // Value injected by FXMLLoaderte void initialize()
public void initialize() {
tableview_var.prefWidthProperty().bind(scrollpane_var.prefWidthProperty());
}
Visual Representation :
Click to see Scenebuilder screenshot
as the title says i'm trying to bind a TableView's width to it's Parent container and the documentation is a bit confusing as i am very new to GUI Applications .
This line of code is the furthest i've gottent and i dont know why it's not working :
public void initialize() {
tableview_var.prefWidthProperty().bind(scrollpane_var.prefWidthProperty());
}
A TableView manages its own scrolling, so you should not be putting it inside a ScrollPane at all. Just omit the scroll pane from the layout entirely.
In general, if you want the content of a scroll pane to fit the available width of the scroll pane, you would use the fitToWidth property, instead of trying to use a binding. In general, binding the pref width like this is a bad idea; if the layout or container you are using doesn't provide the functionality you need, and it almost always will, you should subclass Pane and override layoutChildren() and other methods. Here, the TableView already provides scrolling, and in more general cases, the ScrollPane allows you to specify the content should fit the width of the scroll pane.
<ScrollPane ... fitToWidth="true>
<!-- ... -->
</ScrollPane>
But again, here the scroll pane is redundant.

How do I make a javafx node adapt to it´s parent?

Example:
<AnchorPane xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="HomeController">
<children>
<VBox fx:id="vBoxParent" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity">
<children>
<MenuBar fx:id="menubar" prefHeight="30.0" prefWidth="800.0">
...
With this code, the stage opens fullsize, but the menubar isn´t covering all the lenght. What do I have to add?
If you want the MenuBar to take the full size of VBox, you can replace VBox with MenuBar, placing MenuBar directly in AnchorPane, and giving Anchor Pane Constraints to the MenuBar (to be responsive).
If you want to keep VBox for some reason, you can change the MenuBar properties:
set Vgrow to: allways (allways increase the vertical length)
set Max Width to: MAX_VALUE
I recommend you to use SceneBuilder, to easily find functions and preview changes.

JavaFX Autosizing two or more VBoxes side-by-side in an HBox [duplicate]

I am looking for a way to say set a maxWidth size to 80% in FXML.
Much like in web development.
<VBox fx:id="testVB" prefWidth="600">
But this does not:
<VBox fx:id="testVB" prefWidth="80%">
I know that in Straight JavaFX2 non-fxml you can create insets? What is the best way to do this outside of code in FMXL?
Thanks!
Riley
I'm not sure you can. You need to use the GridPane layout component. In this component, you can specify rows and columns constraints, and in these constraints you can specify a width as a percentage. For example:
<GridPane>
<children>
<TitledPane text="testGridPane" GridPane.columnIndex="0" GridPane.rowIndex="0" />
</children>
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" percentWidth="80.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" percentWidth="20.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
</GridPane>
This code defines a GridPane with a first column with a width of 80%. The TitledPane is set in the first cell of the first column of this GridPane, and can (because you need to be sure that the width constraints of the TitledPane match your needs) occupy 80% of the width of the GridPane.
Please note that I removed all information not relevant to your question. By the way, Oracle's Scene Builder tool is very useful to define complex FXML layout.
It seems like many answers have already been provided and they should work. However, there is a way to set percentages:
<fx:define>
<Screen fx:factory="getPrimary" fx:id="screen" />
</fx:define>
This would help you detect the dimensions of the current screen, the application is being displayed on. Now that we have the display dimensions, we can play with it in FXML as follows:
<HBox fx:id="hroot" prefHeight="${screen.visualBounds.height}" prefWidth="${screen.visualBounds.width}"> Your FXML elements inside the root... </HBox>
Note that I use visualBounds, since this would get me the available space on the screen, since I don't want an overlap with the taskbar in Windows for example. For fullscreen applications, you would just use 'bounds'.
Now, to come to your point of using percentages, you can actually play with the value of the prefheight and prefWidth. You can put calculations inside the ${}.
Optionally:
If you want to have all your elements use relative sizes, just refer to them, using their ID and width or height property, and make your calculation.
<VBox fx:id="VBSidebar" prefWidth="${hroot.width*0.15}" prefHeight="${hroot.height}"> more elements.. </VBox>
Hope this helps!
You can simulate it - basic example that simulates 50% for two cols in an HBox. You can add dummy panes to get thirds, etc.
HBox {
VBox {
static hgrow : "ALWAYS",
Label {
text : "Privacy",
alignment : "CENTER",
styleClass : ["h2", "heading"]
}
},
VBox {
static hgrow : "ALWAYS",
Label {
text : "Messages",
alignment : "CENTER",
styleClass : ["h2", "heading"]
},
Label {text:""}
}
}

A method to algn JavaFX checkbox label text with the text on a line

I have a plain CheckBox in an FXML file on the same line as some other controls and labels in a HBox.
The checkBox label text-base is about 6px units lower than ALL the other text and labels on the same line (HBox).
I can manually line things up in SceneBuilder by specifying a padding-bottom value of: 6. I wanted to put that into the CSS so all checkbox labels would be "lined-up", but everthing I've tried is ignored and doesn't show in the CSS Analyzer (too).
I looked through the Checbox default styling as pointed out here:
Styling a checkbox and also:
Checkbox in the UI controls
I had similar issues with ListBox where the control is constructed from a number of components. You have to know which 'thing' is relevant. However, looking through: com/sun/javafx/scene/control/skin/
caspian/caspian.css
I can't pick the component that makes the text label lower than other text on the same line/row. Add to that, the fact that specifying the padding in the SceneBuilder designer layout, will fix the issue on a one-by-one (manual) basis, it just seems strange that it won't work for:
.check-box {
padding-bottom: 6px; /* or just 6 */
}
Does not work on the following FXML mark-up.
<HBox alignment="CENTER_LEFT" >
<children>
<CheckBox fx:id="acknowledged" alignment="TOP_LEFT" styleClass="normal" text="00">
<padding>
<Insets bottom="6.0" left="4.0" right="8.0" />
</padding>
</CheckBox>
<Button fx:id="detailButton" text="%alarm.detail.label" />
<Label fx:id="alarmType" styleClass="normal" text="%alarm.type.value">
<padding>
<Insets left="8.0" right="8.0" />
</padding>
</Label>
</children>
</HBox>
The objective is to define the padding-bottom via CSS rather than have to do it manually in the FXML:
<padding>
<Insets bottom="6.0" left="4.0" right="8.0" />
</padding>
Any ideas?
To be clear, the visual result for this row is that the checkbox itself has a base-line smaller/lower than the other elements (button, label). The CheckBox label is also subsequently "below" the other elements. If we can pad using CSS, then we don't need to manually maintain the layouts.
As a general rule, alignment problems should be solved by the layout (vs. tweaking paddings or such). So first stop to a solution could be the doc of the parent pane, here HBox:
The alignment of the content is controlled by the alignment property,
which defaults to Pos.TOP_LEFT.
That might be consistent with what you are be seeing (can't be 100% certain, though, as you forgot to include a runnable example ;-) If all other components on the line are accidentally being same height or filling the box with the checkbox smaller, it will positioned at the top of the pane.
Assumed solution is to change the pane's alignment to BASELINE_XX, quick check in code works fine for me:
private Parent getContent() {
HBox box = new HBox(new TextField("something"),
new CheckBox("soso"), new Button("hello"));
box.setAlignment(Pos.BASELINE_CENTER);
return box;
}

Can I Layer JavaFX Controls?

I just had a problem with an invisible, disabled list and a text field(visible) underneath it. I wasn't able to access the text field because I was still clicking on the list. Is there any way to have an invisible control and still be able to use the control underneath it?
Yes, use the StackPane control. This is easiest to do using the JavaFX scenebuilder.
This webpage has a topic on StackPanes.
I am assuming that you just have to fiddle with the StackPane.alignment to change which controls are usable etc. Hope this helps;
FXML:
<StackPane id="StackPane" HBox.hgrow="ALWAYS">
<children>
<ProgressBar fx:id="" disable="false" prefWidth="294.0" progress="0.0" StackPane.alignment="CENTER" />
<Slider fx:id="" prefWidth="294.0" style="" StackPane.alignment="CENTER" />
</children>
</StackPane>

Categories

Resources