What am i trying is to align HBox Buttons to center in the Bottom in dialog box.
I want to do this in fxml.However,BorderPane alignment is working in label.
Here is code from my side.I think BorderPane.alignment="BOTTOM_CENTER" must work even if the tag is Bottom.
Class file:
package application;
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class HBoxDialog extends Application {
#Override
public void start(Stage primaryStage) {
try {
Parent root = FXMLLoader.load(getClass().getResource("HBoxDialog.fxml"));
primaryStage.setScene(new Scene(root, 500, 100));
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
FXML file:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.text.Font?>
<?import javafx.scene.layout.BorderPane?>
<BorderPane xmlns:fx="http://javafx.com/fxml/1">
<top>
<Label text="this is dialogbox" BorderPane.alignment="TOP_CENTER"/>
<font>
<Font size="35"/>
</font>
</top>
<bottom>
<HBox spacing="10">
<Button text="Okay" prefWidth="90" BorderPane.alignment="BOTTOM_CENTER"/>
<Button text="Cancel" prefWidth="90" BorderPane.alignment="BOTTOM_CENTER"/>
<Button text="Help" prefWidth="90" BorderPane.alignment="BASELINE_RIGHT"/>
</HBox>
</bottom>
</BorderPane>
The BorderPane.alignment static property only makes sense for nodes whose parent is a BorderPane. The Buttons defined in your FXML file have an HBox as a parent, so setting the BorderPane.alignment property on the buttons will have no effect.
You can achieve the desired effect by centering the buttons within the HBox, simply by using the alignment property of the HBox (which positions the HBox's child nodes within its bounds):
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.text.Font?>
<?import javafx.scene.layout.BorderPane?>
<BorderPane xmlns:fx="http://javafx.com/fxml/1">
<top>
<Label text="this is dialogbox"
BorderPane.alignment="TOP_CENTER" />
<font>
<Font size="35" />
</font>
</top>
<bottom>
<HBox spacing="10" alignment="CENTER">
<Button text="Okay" prefWidth="90" />
<Button text="Cancel" prefWidth="90" />
<Button text="Help" prefWidth="90" />
</HBox>
</bottom>
</BorderPane>
This gives
The reason that the Label needs BorderPane.alignment="CENTER" but the HBox needs alignment="CENTER" is because they have different resizable ranges, and in particular their max widths are different. The max width of a label, by default, is its preferred width, whereas the max width of an HBox is infinite. You can see this by setting background colors on them both:
<Label text="this is dialogbox"
BorderPane.alignment="TOP_CENTER"
style="-fx-background-color: aquamarine;"/>
<!-- ... -->
<HBox spacing="10" alignment="CENTER"
style="-fx-background-color: lightskyblue;">
The alignment property positions the content of a node within its bounds. Since the label has no extra space within its bounds for the text to be positioned, with the default settings the alignment property will have no effect. On the other hand, the label is less wide than the top region of the border pane, so there is room to position it within that region. The BorderPane.alignment="CENTER" attribute centers the entire label within the top region of the border pane.
By contrast, the HBox itself already fills the entire width of the bottom region of the border pane. So there is no additional space to align it within that region, and so for the HBox, setting BorderPane.alignment="CENTER" will have no effect. On the other hand, there is more space in the HBox itself than is needed for the buttons, so the buttons (the content of the HBox) can be aligned within the HBox itself using the alignment="CENTER" property of the HBox.
If you want, you can change the max widths to achieve the same effect. For example:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.Double?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.text.Font?>
<?import javafx.scene.layout.BorderPane?>
<BorderPane xmlns:fx="http://javafx.com/fxml/1">
<top>
<Label text="this is dialogbox"
alignment="CENTER"
style="-fx-background-color: aquamarine;">
<maxWidth>
<Double fx:constant="MAX_VALUE"/>
</maxWidth>
</Label>
<font>
<Font size="35" />
</font>
</top>
<bottom>
<HBox spacing="10" alignment="CENTER"
style="-fx-background-color: lightskyblue;">
<Button text="Okay" prefWidth="90" />
<Button text="Cancel" prefWidth="90" />
<Button text="Help" prefWidth="90" />
</HBox>
</bottom>
</BorderPane>
allows the label to grow (like the default for the HBox), so now its alignment property has the desired effect:
or
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.text.Font?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.Region?>
<BorderPane xmlns:fx="http://javafx.com/fxml/1">
<top>
<Label text="this is dialogbox"
BorderPane.alignment="CENTER"
style="-fx-background-color: aquamarine;" />
<font>
<Font size="35" />
</font>
</top>
<bottom>
<HBox spacing="10" BorderPane.alignment="CENTER"
style="-fx-background-color: lightskyblue;">
<maxWidth>
<Region fx:constant="USE_PREF_SIZE" />
</maxWidth>
<Button text="Okay" prefWidth="90" />
<Button text="Cancel" prefWidth="90" />
<Button text="Help" prefWidth="90" />
</HBox>
</bottom>
</BorderPane>
makes the HBox behave like the button, so now its BorderPane.alignment gives the desired effect:
Related
I have a simple JavaFX application, a BorderLayout with an AnchorPane and inside two panes, one contains a HBox and a VBox with a TextArea and a ListView. I would like them to increase in width, as I increase the window size. I have tried Vgrow/Hgrow = 'ALWAYS' and Max Width/Max Height = 'MAX_VALUE' on the the controls but nothing works.- (I use Scene Builder to create the fxml)
The FXML is the following:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.VBox?>
<BorderPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" xmlns="http://javafx.com/javafx/18" xmlns:fx="http://javafx.com/fxml/1">
<center>
<AnchorPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="200.0" BorderPane.alignment="CENTER">
<children>
<Pane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="200.0" AnchorPane.leftAnchor="0.0" AnchorPane.topAnchor="0.0" />
<Pane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="200.0" AnchorPane.leftAnchor="0.0" AnchorPane.topAnchor="200.0">
<children>
<HBox maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="200.0">
<children>
<VBox maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="200.0" HBox.hgrow="ALWAYS">
<children>
<TextArea maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="200.0" VBox.vgrow="ALWAYS" />
<ListView maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="200.0" VBox.vgrow="ALWAYS" />
</children>
</VBox>
</children>
</HBox>
</children>
</Pane>
</children>
</AnchorPane>
</center>
</BorderPane>
My java application class (HelloApplication):
package com.example.demo;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
public class HelloApplication extends Application {
#Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
Scene scene = new Scene(fxmlLoader.load());
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
UPDATE: I removed the unnecessary AnchorPane and other Panes, kept only a VBox and placed it in another BorderLayout, take a look, now seems perfect :)
Don't hard-code sizes, and don't use layout panes such as Pane and AnchorPane that rely on hard-coded sizes except in very rare circumstances.
(You may sometimes need to set the max size to Double.MAX_VALUE to allow a control to grow beyond its preferred size, but even that is not necessary in this example, as far as I can tell.)
Your FXML has an empty Pane and at least two redundant wrapper containers which only wrap one node (the other Pane and the AnchorPane). Remove all of these and the hard-coded values and it will work:
<?import javafx.scene.control.ListView?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.layout.*?>
<BorderPane xmlns="http://javafx.com/javafx/18" xmlns:fx="http://javafx.com/fxml/1">
<center>
<HBox>
<children>
<VBox HBox.hgrow="ALWAYS">
<children>
<TextArea VBox.vgrow="ALWAYS" />
<ListView VBox.vgrow="ALWAYS" />
</children>
</VBox>
</children>
</HBox>
</center>
</BorderPane>
Depending on your real use-case, you may not need the HBox either:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.layout.*?>
<BorderPane xmlns="http://javafx.com/javafx/18" xmlns:fx="http://javafx.com/fxml/1">
<center>
<VBox>
<children>
<TextArea VBox.vgrow="ALWAYS" />
<ListView VBox.vgrow="ALWAYS" />
</children>
</VBox>
</center>
</BorderPane>
I have a few UI elements that I want stacked vertically, in the center of the panel, all aligned on the left edge of the center. I've tried to do this with a VBox, and it was working until I added an item that had text that was too long; it always truncates the text with ellipsis's and I can't get it to wrap the text down to the next line. I set the wrapText param to true on the Checkbox, but it doesn't seem to respect it. I've tried setting perfWidth and maxWidth on the checkbox and the vbox it is inside of but nothing seems to work. Can anyone tell me what I am doing wrong?
Screenshot:
textwrap.fxml
<?import java.net.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.ToggleGroup?>
<VBox xmlns:fx="http://javafx.com/fxml" maxWidth="100"
prefHeight="200" prefWidth="300" alignment="BASELINE_CENTER" spacing="30">
<VBox alignment="CENTER" maxWidth="100" prefWidth="100">
<VBox spacing="10" style=" -fx-border-color:black; -fx-border-width: 1; -fx-border-style: solid;" alignment="BASELINE_LEFT"
>
<padding>
<Insets top="0" right="0" bottom="10" left="5"/>
</padding>
<RadioButton text="Option 1">
<toggleGroup>
<ToggleGroup fx:id="group"/>
</toggleGroup>
<selected>true</selected>
</RadioButton>
<RadioButton text="Option 2">
<toggleGroup>
<fx:reference source="group"/>
</toggleGroup>
</RadioButton>
</VBox>
<CheckBox text="My Long Textbox Text" selected="true" wrapText="true">
</CheckBox>
</VBox>
</VBox>
TextWrap.java
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class TextWrap extends Application
{
#Override
public void start(Stage stage) throws Exception
{
Parent root = FXMLLoader.load(getClass().getResource("textwrap.fxml"));
Scene scene = new Scene(root, 300, 275);
stage.setTitle("Text Wrap");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args)
{
launch();
}
}
EDIT - as #jewelsea pointed out below, the issues was BASELINE_CENTER in the root VBox; changing that to center removed the truncation issue. This had the unintended side effect of making the outside around the radio boxes extend farther than I liked:
by adding
<maxWidth><Control fx:constant="USE_PREF_SIZE" /></maxWidth>
to the inner VBOX I was able to correct this:
I also found out, that if for some reason I was wed to BASELINE_CENTER, I could still fix the issue with the checkbox text by adding
<minWidth><Control fx:constant="USE_PREF_SIZE" /></minWidth>
to the checkbox to get the same effect.
This is what I came up with, try it and see if it is what you want.
You may need to change some things to reach your final desired layout, but hopefully this addresses your immediate wrapping issue.
I think the BASELINE_CENTER alignment on the outer VBox was confusing things, but I changed a couple of other things, so it may have been something else.
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.RadioButton?>
<?import javafx.scene.control.ToggleGroup?>
<?import javafx.scene.layout.VBox?>
<VBox alignment="CENTER" prefHeight="200.0" prefWidth="300.0" xmlns="http://javafx.com/javafx/17"
xmlns:fx="http://javafx.com/fxml/1">
<children>
<VBox maxWidth="100.0" prefWidth="100.0">
<VBox alignment="BASELINE_LEFT" spacing="10"
style=" -fx-border-color:black; -fx-border-width: 1; -fx-border-style: solid;">
<padding>
<Insets bottom="10" left="5" right="0" top="0"/>
</padding>
<RadioButton text="Option 1">
<toggleGroup>
<ToggleGroup fx:id="group"/>
</toggleGroup>
<selected>true</selected>
</RadioButton>
<RadioButton text="Option 2">
<toggleGroup>
<fx:reference source="group"/>
</toggleGroup>
</RadioButton>
</VBox>
<CheckBox selected="true" text="My Long Textbox Text" wrapText="true">
</CheckBox>
</VBox>
</children>
</VBox>
I run a JavaFX program in IDEA on Ubuntu. The window, first, locates on the left-top of the screen, and then I see a jump of the window to a center. How can I handle this?
I've tried different methods of Stage, but it didn't help.
start method
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("View/MainMenuView.fxml"));
primaryStage.setTitle("Flash Cards");
primaryStage.setScene(new Scene(root, 800, 600));
primaryStage.setResizable(false);
primaryStage.show();
}
MainMenuView.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.Cursor?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.text.Font?>
<?import javafx.scene.text.Text?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controllers.MainMenuController">
<children>
<Pane fx:id="mainMenu" prefHeight="600.0" prefWidth="800.0">
<children>
<Text layoutX="233.0" layoutY="116.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Flash Cards v1" textAlignment="CENTER" wrappingWidth="333.419921875">
<font>
<Font size="37.0" />
</font>
</Text>
<Button fx:id="trainingButton" layoutX="314.0" layoutY="225.0" mnemonicParsing="false" prefHeight="75.0" prefWidth="172.0" style="-fx-background-color: #fff; -fx-border-color: #000; -fx-border-width: 2;" text="Training">
<cursor>
<Cursor fx:constant="OPEN_HAND" />
</cursor>
</Button>
<Button fx:id="addNewWordButton" layoutX="314.0" layoutY="325.0" mnemonicParsing="false" prefHeight="75.0" prefWidth="172.0" style="-fx-background-color: #fff; -fx-border-color: #000; -fx-border-width: 2;" text="Add a new word">
<cursor>
<Cursor fx:constant="OPEN_HAND" />
</cursor>
</Button>
</children>
</Pane>
</children>
</AnchorPane>
I just want the window be in a center immediately when my program starts.
This is a bug in JavaFX itself. A workaround for until it is fixed is to do this:
primaryStage.show();
primaryStage.centerOnScreen();
You need to call centerOnScreen() immediately after show() to prevent it from showing up in the corner.
I am creating a basic game launcher in JavaFX with SceneBuilder. Since SceneBuilder works in FXML, my launcher layout is in FXML. I have a method in my main class that I want to call on a button click. I read that you could use
#methodName
In the button's
onAction
property, but this does not work.
My main Java class:
#FXML
private void launchGame(ActionEvent e) {
System.out.println("Launching...");
}
#Override
public void start(Stage primaryStage) throws IOException {
Parent root = FXMLLoader.load(Main.class.getResource("launcher.fxml"));
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.setTitle("First Week Login");
primaryStage.setResizable(false);
primaryStage.sizeToScene();
primaryStage.show();
}
My FXML file:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.text.Text?>
<?import javafx.scene.web.WebView?>
<AnchorPane xmlns:fx="http://javafx.com/fxml/1"
xmlns="http://javafx.com/javafx/8.0.102">
<children>
<BorderPane prefHeight="493.0" prefWidth="664.0" styleClass="background"
stylesheets="#launcher.css">
<bottom>
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0"
BorderPane.alignment="CENTER">
<children>
<Button alignment="CENTER" mnemonicParsing="false"
text="Launch Game" onAction="#launchGame" />
</children>
</HBox>
</bottom>
<top>
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0"
BorderPane.alignment="CENTER">
<children>
<Text strokeType="OUTSIDE" strokeWidth="0.0"
styleClass="title" text="First Week" />
</children>
</HBox>
</top>
<center>
<WebView prefHeight="200.0" prefWidth="200.0"
BorderPane.alignment="CENTER" />
</center>
</BorderPane>
</children>
</AnchorPane>
You need to create a separate Controller class and specify it in top AnchorPane tag with fx:controller="packageName.Classname"
Like this:
<AnchorPane xmlns:fx="http://javafx.com/fxml/1"
xmlns="http://javafx.com/javafx/8.0.102"
fx:controller="com.packageName.Controller">
The called method should be inside the specified Controller class.
com.packageName is just an example, you should use the name of the package where you put the Controller class or no package name if it's not in any package.
I made a TextField in FXML. I want to add Button in this TextField like clear button. How can I implement this?
You could use an AnchorPane to wrap the TextField and the Button and set the anchors to have the TextField to fill the entire AnchorPane and the Button to be anchored to the right-side.
Example:
<AnchorPane prefHeight="24.0" prefWidth="322.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<TextField prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
<Button graphicTextGap="0.0" minHeight="10.0" minWidth="13.0" mnemonicParsing="false" prefHeight="20.0" prefWidth="22.0" style="-fx-background-color: #7085FF;
-fx-background-radius: 30;
-fx-background-insets: 0;" text="x" textFill="WHITE" AnchorPane.bottomAnchor="4.0" AnchorPane.rightAnchor="4.0" AnchorPane.topAnchor="4.0">
<font>
<Font size="9.0" />
</font>
</Button>
</children>
</AnchorPane>
The output is like:
If you need it more than once (and most probably you will), it is a good idea to have it separately in another FXML file with the corresponding controller class that also handles the action of the Button.
Example with separated FXML:
ButtonedTextField.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.*?>
<?import javafx.scene.text.Font?>
<AnchorPane xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="application.ButtonedTextField">
<children>
<TextField fx:id="textField" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
<Button fx:id="button" graphicTextGap="0.0" minHeight="10.0" minWidth="13.0" mnemonicParsing="false" prefHeight="20.0" prefWidth="22.0" style="-fx-background-color: #7085FF;
-fx-background-radius: 30;
-fx-background-insets: 0;" text="x" textFill="WHITE" AnchorPane.bottomAnchor="4.0" AnchorPane.rightAnchor="4.0" AnchorPane.topAnchor="4.0">
<font>
<Font size="9.0" />
</font>
</Button>
</children>
</AnchorPane>
ButtonedTextField.java (controller)
public class ButtonedTextField implements Initializable {
public #FXML TextField textField;
private #FXML Button button;
#Override
public void initialize(URL location, ResourceBundle resources) {
button.setOnAction(e -> textField.setText(""));
}
}
And then you can include this control to another FXML:
ButtonedTextFieldTest.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.layout.*?>
<TabPane xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="application.ButtonedTextFieldTest">
<tabs>
<Tab text="Untitled Tab">
<content>
<AnchorPane id="Content" minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">
<children>
<fx:include fx:id="buttonedTextField" source="ButtonedTextField.fxml" prefWidth="200.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="2.0" />
</children>
</AnchorPane>
</content>
</Tab>
</tabs>
</TabPane>
ButtonedTextFieldTest.java (controller of including FXML)
public class ButtonedTextFieldTest implements Initializable {
private #FXML AnchorPane buttonedTextField;
private #FXML ButtonedTextField buttonedTextFieldController;
#Override
public void initialize(URL location, ResourceBundle resources) {
buttonedTextFieldController.textField.textProperty().addListener((ov, oldVal, newVal) -> {
System.out.println(newVal);
});
}
}
Notes:
You can customize the Button as you wish (maybe it is the easier to set the graphic)
The placement and the styling can be further customized.
As you can see in the last class, the control itself and its controller can be also injected! The convention is to have the controller injected is to use the "Controller" postfix after the ID of the inected control.
I have used the CustomTextField class from the ControlsFX project for this kind of functionality before. ControlsFX lacks good documentation, but the CustomTextField class is quite easy to use:
CustomTextField textfield = new CustomTextField
textfield.setRight(new Button());