In e4 the main user interface is constructed (rendered) based on the e4 application model. If you want to change the way that a specific model element is represented in the ui, you can plug in a custom renderer for this type. This approach is very powerful, but can be a technical challenge to get working the way you want it to. If you only want to make minor adjustments to the main window, like adding a search field to the menu bar, there is now an easier way with the e(fx)clipse 2.2.0 default window renderer using FXML.
Contents
Starting Point
The starting point for this introduction will be a small demo app I developed to show case this feature. The application model only has one part but uses every possible window trim location to place some buttons.
Yes, it’s ugly … but don’t worry … that will get even worse! 😉
The full demo application is available as part of the demos in the e(fx)clipse Git repository. To run it you need to check out all the org.eclipse.fx.demo.customWindow.*
projects.
Customize the locations of window ui elements
Setup of the FXML
If we want be able to control the layout of the window, we need to tell the renderer where to put the children of the window. This is done using specific IDs that the renderer will look for after loading (checking the property id, not fx:id).
ID | UI Elements | Default Location |
---|---|---|
client-area | children (sashes, stacks, part, …) | center of the main BorderPane |
menu-bar-area | main menu bar | top of the main BorderPane |
top-trim-area | top window trim bar (toolbar, tool control) | top of the client area |
bottom-trim-area | bottom window trim bar (toolbar, tool control) | bottom of the client area |
left-trim-area | left window trim bar (toolbar, tool control) | left of the client area |
right-trim-area | right window trim bar (toolbar, tool control) | right of the client area |
The root element of the FXML has to be a JavaFX BorderPane
or a org.eclipse.fx.ui.controls.stage.TrimmedWindow
. In most cases using the BorderPane
will be what you want to do. You only need to use a TrimmedWindow
if you also need control over the window decorations (like the min/max/close buttons on the top).
Hook the FXML to the window
It is very easy to hook an FXML to a window in the application model. All you need to to is set the property efx.window.root.fxml
in the persisted state of the window. The value is an URL pointing to the FXML you want to use.
Examples
This FXML will insert a TextField
in the top row next to the main menu bar. The menu bar will be placed inside the HBox
with id="menu-bar-area"
. The rest of the windows elements (trim bars, client area) will be placed by default. In this example I left out the properties maxHeight
and maxWidth
which I usually set to Double.MAX_VALUE for all containers, so you can focus on the structure of the window.
<BorderPane fx:controller="org.eclipse.fx.demo.customWindow.app.controller.MainFrameController"> <top> <VBox> <children> <HBox> <children> <HBox id="menu-bar-area" /> <TextField fx:id="searchField" promptText="Search..." /> </children> </HBox> </children> </VBox> </top> </BorderPane>
As you can see in this screen shot, we now have a text input field next to the menu bar:
To use the new search field in my application I can inject it into the controller I defined with fx:controller
in the FXML. Here I use the e(fx)clipse EventBus
to forward a search request, but there are lots of ways on how to proceed from here. Notice that it is possible to use dependency injection to get hold of all the e4 and OSGi services in the controller!
public class MainFrameController implements Initializable { @Inject private EventBus eventBus; @FXML TextField searchField; @Override public void initialize(URL location, ResourceBundle resources) { searchField.textProperty().addListener((observable, oldValue, newValue) -> { eventBus.publish(new Topic<String>("app/event/search"), newValue, true); }); } }
Go crazy
In the full demo I placed every trim bar on the outside of the window so I can insert a “cool” separation bar between the client area and the trim bars (yes … the blue bars with the light blue circles … I warned you it would get ugly!). I also thought it would be totally awesome to place the tool bar on top of the menu bar just to … well … show thats it’s possible, I guess. 😉
Please have a look at the complete FXML. I also put the “client area” in there, although I could have skipped this, since it would be placed in the center of the main BorderPane
by default anyway.
Using SceneBuilder
You might notice that I sometimes used minWidth
and minHeight
at some places in the FXML. I also gave the root BorderPane
a prefWidth
and prefHeight
. I did this, so that the pane visually resembles the final outcome if you open it with SceneBuilder. This is not really necessary for the correct rendering and can look broken, if you plan to remove trims at runtime, since the container will then leave an empty space where the trim can be. If you omit the minimums the container will shrink to 0 and will therefore become invisible when the trim is removed.
Pingback: JavaFX links of the week, November 30 // JavaFX News, Demos and Insight // FX Experience
Pingback: Java desktop links of the week, November 30 « Jonathan Giles
Pingback: Java desktop links of the week, November 30 – Jonathan Giles