I hope everyone is doing well.
I'm trying to move the drop down arrow in a TitledPane to be laid out on the right, instead of the left like it is by default. I'm using JavaFX 8, and many of the resources I've found don't seem to work.
I have found that I am able to move the arrow a specific amount, like 20 pixels shown below
.accordion .title > .arrow-button .arrow
{
-fx-translate-x: 20;
}
But I want something responsive. Is there some way that I can get the width of the titled pane, and then subtract some pixels so that so that the arrow appears to be laid out on the right when resizing? Is there a better way to it? I added the element using SceneBuilder2 if that matters.
Thanks so much for your time.
Edit: The following was added for clarification
Primarily, I want the arrow to be right justified, like below
Instead of just "to the right" of the arrow. I really appreciate all the assistance.
This isn’t exactly the same, visually, but you can hide the arrow button and create a graphic that acts like an arrow button. TitledPane extends Labeled, so you have control over the placement of the graphic relative to the text, via the contentDisplay property.
First, hide the arrow button in the stylesheet:
In the code, you can create a Label to act as a fake button and set it as the TitledPane’s graphic. The entire title line is sensitive to the mouse, so an interactive control (like a Button) is not needed.
Unfortunately, there's no public API for moving the arrow to the right side of the
TitledPane
. This doesn't mean this can't be accomplished, however, we just have to translate the arrow dynamically, using bindings. In order for the rest of the title area to look correct we'll also have to translate the text, and graphic if present, to the left. The easiest way to do all this is by subclassingTitledPaneSkin
and accessing the internals of the "title region".Here's an example implementation. It lets you position the arrow on the left or right side via CSS. It's also responsive to resizing as well as alignment and graphic changes.
You can then apply this skin to all
TitledPane
s in your application from CSS, like so:Or you could target only certain
TitledPane
s by adding a style class and using said class instead of.titled-pane
.The above works with JavaFX 11 and likely JavaFX 10 and 9 as well. To get it to compile on JavaFX 8 you need to change some things:
Import
com.sun.javafx.scene.control.skin.TitledPaneSkin
instead.Remove the calls to
registerChangeListener(...)
andunregisterChangeListeners(...)
. I believe replacing them with the following is correct:Use
new SimpleStyleableObjectProperty<ArrowSide>(...) {...}
andnew CssMetaData<TitledPane, ArrowSide>(...) {...}
.Use
(StyleConverter<?, ArrowSide>) getEnumConverter(ArrowSide.class)
.getEnumConverter
that was fixed in a later version. Using the cast works around the problem. You may wish to@SuppressWarnings("unchecked")
the cast.Issue: Even with the above changes there's a problem in JavaFX 8—the arrow is only translated once the
TitledPane
is focused. This doesn't appear to be a problem with the above code as even changing thealignment
property does not cause theTitledPane
to update until it has focus (even when not using the above skin, but rather just the default skin). I've been unable to find a workaround to this problem (while using the custom skin) but maybe you or someone else can. I was using Java 1.8.0_202 when testing for JavaFX 8.If you don't want to use a custom skin, or you're on JavaFX 8 (this will cause the arrow to be translated without needing to focus the
TitledPane
first), you can extract the necessary code, with some modifications, into a utility method:Note: While this puts the arrow on the right without having to focus the
TitledPane
first, theTitledPane
still suffers from the issue noted above. For instance, changing thealignment
property doesn't update theTitledPane
until it's focused. I'm guessing this is simply a bug in JavaFX 8.This way of doing things is not as "easy" as the skin approach and requires two things:
TitledPane
must be using the defaultTitledPaneSkin
.The
TitledPane
must have been displayed in aWindow
(window was showing) before calling the utility method.NullPointerException
being thrown since thelookup
calls will returnnull
.If using FXML, note that the
initialize
method is called during a call toFXMLLoader.load
(any of the overloads). This means, under normal circumstances, it's not possible for the created nodes to be part of aScene
yet, let alone a showingWindow
. You must wait for theTitledPane
to be displayed first, then call the utility method.Waiting for the
TitledPane
to be displayed can be achieved by listening to theNode.scene
property, theScene.window
property, and theWindow.showing
property (or you could listen forWindowEvent.WINDOW_SHOWN
events). However, if you immediately put the loaded nodes into a showingWindow
, then you can forgo observing the properties; call the utility method inside aPlatform.runLater
call from insideinitialize
.When using the skin approach, the whole wait-for-showing-window hassle is avoided.
Usual Warning: This answer relies on the internal structure of
TitledPane
which may change in a future release. Be cautious when changing JavaFX versions. I only (somewhat) tested this on JavaFX 8u202 and JavaFX 11.0.2.