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>
Related
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));
How do I make changes to generated items using FXML?
When creating a JavaFX ToolBar, a nested HBox (or VBox) is generated automatically. For example, I would like to set the spacing of this generated HBox to 0.
<ToolBar fx:id="welcomeToolBar" prefHeight="50">
<items>
<Button fx:id="closeButton" prefHeight="50" prefWidth="100" onAction="#closeWindow" text="Close" />
<Button text="New Image" prefHeight="50" prefWidth="100" onAction="#newImage" styleClass="button-accent"/>
<Pane HBox.hgrow="ALWAYS"/>
<CheckBox text="Hide this window on application start"
fx:id="hideOnLoadCheckBox" onAction="#setVisibilityOnApplicationLoad"/>
</items>
</ToolBar>
Use the CSS property -fx-spacing (uses the <size> type). You can do this in a separate CSS file and link the file or do this directly in the FXML file using the style attribute.
Example FXML:
<ToolBar fx:id="welcomeToolBar" prefHeight="50" style="-fx-spacing: 0px;">
<!-- items -->
</ToolBar>
Example CSS:
.tool-bar {
-fx-spacing: 0px;
}
The reason this works is that the default skin of the ToolBar class exposes a StyleableProperty for spacing1. I found it in the "CSS Analyzer" of Scene Builder and the JavaFX 9 (and 8)2 source code (for some reason can't get Scenic View to run). I couldn't find any documentation on this property, however; even in the JavaFX CSS Reference Guide.
1. Another undocumented styleable property exposed by the default skin (at least in JavaFX 13) is -fx-alignment which accepts a javafx.geometry.Pos. These properties are applied to the HBox or VBox depending on if the toolbar's orientation is horizontal or vertical, respectively.
2. Still present in the JavaFX 13 source code.
I've got a Java project I'm working on that uses JavaFX to generate a UI. I'm not much of a Java programmer (not much of a programmer in general - I'm a CS student), so it's a bit of a learning experience, but generally I'm answering my own questions as I go. I'm now hitting a sticky spot with a TableView, though, and getting a bit lost - specifically, I can't seem to set any kind of action in response to interacting with the TableView.
The current layout of my project is Main.java, Controller.java and UI.fxml. In Main the pane/scene is loaded from UI.fxml as the program starts. (Technically the project contains several other bits of code, but I'm trying to only post what's relevant.) I have a TableView defined in UI.fxml as so:
<TableView fx:id="queueTable">
<columns>
<TableColumn text="Title">
<cellValueFactory>
<PropertyValueFactory property="tableTitle"/>
</cellValueFactory>
</TableColumn>
<TableColumn text="Artist">
<cellValueFactory>
<PropertyValueFactory property="tableArtist"/>
</cellValueFactory>
</TableColumn>
<TableColumn text="Album">
<cellValueFactory>
<PropertyValueFactory property="tableAlbum"/>
</cellValueFactory>
</TableColumn>
</columns>
</TableView>
Similarly I have several buttons defined in UI.fxml with onAction as so:
<Button fx:id="buttonQueueAdd" text="Add" onAction="#addMediaToQueue"/>
In my previous code I was able to define an event for when an item in the TableView was selected, in the method where my TableView was being generated. Now, however, all the UI creation is done via the FXML file. The FXML file has a controller class it refers to (Controller.java), where all the actions are defined for the various buttons, but I can't seem to get it to accept any attribute like onAction, setOnMousePressed, etc. in the FXML file for the TableView like it accepts an 'onAction' attribute for the Button.
It seems like I can get a workaround going by defining the action in my Main class (or creating a method in my Controller class that's called in the Main class, when the scene is being created), but that won't work unless I make queueTable static in my Controller class... which then breaks all the functions that manipulate items in the TableView - for example, I can no longer add items to the TableView/the UI does not refresh to show items are added.
It seems like the best solution is to not have the TableView created in FXML, to instead have the TableView created and all its onMousePressed events and etc. set up outside of the FXML file and have them added to the scene in my Main class's start method. This is how it was done previously, and it worked acceptably then. However, that seems messy, and leads to my UI elements being strewn across multiple classes and/or the FXML file - this is a project I'm working on with several other students, and I'm trying to keep everything as streamlined/organized as possible. Do I have to bite the bullet and do it the "messy" way, or is there a better option?
You can still generate the TableView using the fxml, but add a initialize method to the controller. This method is invoked by the FXMLLoader after it's done creating the object stucture and can be used to do modifications to the object structure that you cannot do from fxml:
#FXML
private void initialize() {
queueTable.getSelectionModel().selectedIndexProperty().addListener((observable, oldValue, newValue) -> {
...
});
}
In case you need to add this listener from Main, you could add a method to the controller and get the controller from the FXMLLoader instance you use to create the fxml.
Controller
public void addSelectedIndexListener(ChangeListener<? super Number> listener) {
queueTable.getSelectionModel().selectedIndexProperty().addListener(listener);
}
Main
FXMLLoader loader = new FXMLLoader(fxmlUrl);
myNode = loader.load();
Controller controller = loader.getController();
controller.addSelectedIndexListener((observable, oldValue, newValue) -> {
...
});
Note that is an property property should depend on a property of the selection model, you may be able to use expression binding to achieve the desired effect.
<BorderPane xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1">
<center>
<TableView fx:id="table" BorderPane.alignment="CENTER">
<columns>
<TableColumn text="value">
<cellValueFactory>
<PropertyValueFactory property="value"/>
</cellValueFactory>
</TableColumn>
</columns>
<items>
<FXCollections fx:factory="observableArrayList">
<Item value="Hello World"/>
<Item value="42"/>
</FXCollections>
</items>
</TableView>
</center>
<left>
<Button mnemonicParsing="false" text="Remove" BorderPane.alignment="CENTER" disable="${table.selectionModel.selectedIndex+0<0}" />
</left>
</BorderPane>
Note that the table.selectionModel.selectedIndex+0<0 part is a workaround used, since JavaFX currently doesn't use null-safe equality checks (table.selectionModel.selectedItem==null doesn't work) and cannot get the type of the 0 constant right for comparison (table.selectionModel.selectedIndex<0), so this currently doesn't seem like a good option.
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 make a BoxPane (for example vertically) where one of the components within the BoxPane fills the avaible space?
For example here I would like the ScrollPane to take all avaible space which is left after the Label. BXML:
<BoxPane orientation="vertical" styles="{fill:true}">
<Label text="Triggers:" />
<ScrollPane preferredWidth="80" preferredHeight="110"
horizontalScrollBarPolicy="fill"
verticalScrollBarPolicy="fill_to_capacity"
>
<ListView bxml:id="listTriggers" selectMode="single"
listData="['TRNIF_Trigger1'],['TRNIF_Trigger2'],['TRNIF_Trigger3']"
/>
</ScrollPane>
</BoxPane>
It looks like BoxPane in Pivot is designed to take only minimal needed space. You have to use TablePane. This looks a bit unfortune to me because your BXML blows up when using a large frontend which should adapt to available space. For example within WinForms I can say to a component "Stick to your right border with 5px distance and resize if needed to do so".
Nevertheless, here is the BXML for above question / example:
<TablePane styles="{padding:8, horizontalSpacing:6, verticalSpacing:6}">
<columns>
<TablePane.Column width="1*" />
</columns>
<TablePane.Row height="-1">
<Label text="Triggers:" />
</TablePane.Row>
<TablePane.Row height="1*">
<ScrollPane
horizontalScrollBarPolicy="fill"
verticalScrollBarPolicy="fill_to_capacity"
>
<ListView bxml:id="listTriggers" selectMode="single"
listData="['TRNIF_Trigger1'],['TRNIF_Trigger2'],['TRNIF_Trigger3']"
/>
</ScrollPane>
</TablePane.Row>
</TablePane>