Verified Owner
Top Description Inners Fields Constructors Methods
org.jabref.gui.preferences.forms

public Class PreferencesFormBuilder

extends Object
Class Inheritance
Annotations
@NullMarked
Imports
java.util.ArrayDeque, .ArrayList, .Deque, .List, java.util.function.Consumer, javafx.beans.binding.Bindings, javafx.beans.property.Property, .StringProperty, javafx.beans.value.ChangeListener, .ObservableValue, javafx.collections.ObservableList, javafx.geometry.HPos, .Insets, .Pos, javafx.scene.Node, .Parent, .Scene, javafx.scene.control.Button, .CheckBox, .ComboBox, .Control, .Hyperlink, .Label, .Labeled, .RadioButton, .TableView, .TextField, .ToggleButton, .ToggleGroup, .Tooltip, javafx.scene.layout.ColumnConstraints, .FlowPane, .GridPane, .HBox, .Pane, .Priority, .Region, .VBox, javafx.util.Callback, org.jabref.gui.DialogService, org.jabref.gui.actions.ActionFactory, .StandardActions, org.jabref.gui.help.HelpAction, org.jabref.gui.icon.IconTheme, .JabRefIcon, org.jabref.gui.preferences.GuiPreferences, .SearchableElement, org.jabref.gui.theme.StyleClasses, org.jabref.gui.util.IconValidationDecorator, .ViewModelListCellFactory, org.jabref.gui.util.component.HelpButton, org.jabref.logic.help.HelpFile, org.jabref.logic.l10n.Localization, org.jabref.logic.util.strings.StringUtil, com.dlsc.gemsfx.TagsField, de.saxsys.mvvmfx.utils.validation.ValidationStatus, de.saxsys.mvvmfx.utils.validation.visualization.ControlsFxVisualizer, org.controlsfx.control.SearchableComboBox, org.jspecify.annotations.NullMarked, .Nullable

Fluent, eager builder that assembles a preference tab's node tree and wires all bindings. It replaces the FXML + controller pair: each call creates a control, binds it to a view-model property and appends it to the current container.

One rule governs the shape of a form: the chain only ever adds elements; anything that configures an element happens inside that element's lambda. Nothing on this class configures anything, so no call can land on the wrong node — there is no "current element" to get wrong:

form() // A titled section. Its contents are the chain inside the lambda, so the grouping // is visible in the source; the trailing lambda addresses the section as a whole. .section("HTTP Server", httpServer -> httpServer // A checkbox with a value field beside it, enabled while the box is ticked. // Convenience sugar for checkbox(text, enabled, box -> box.attachField(...). // The lambda addresses that field, not the checkbox. .checkWithField("Enable HTTP Server on port", viewModel.enableHttpServerProperty(), viewModel.httpPortProperty(), port -> port.validate(viewModel.httpPortValidationStatus()) .help(HelpFile.REMOTE)) // A group of related elements, can be disabled as one. .group(expert -> expert .stringField("API base URL", viewModel.apiBaseUrlProperty()), expertGroup -> expertGroup.disableWhen(viewModel.disableExpertSettingsProperty())), // Documentation for the section as a whole: a help button beside its heading. httpServerSection -> httpServerSection.help("https://docs.jabref.org/advanced/remote")) .section("Another section", section -> section ...) .build();
form()
        // A titled section. Its contents are the chain inside the lambda, so the grouping
        // is visible in the source; the trailing lambda addresses the section as a whole.
        .section("HTTP Server", httpServer -> httpServer

                // A checkbox with a value field beside it, enabled while the box is ticked.
                // Convenience sugar for checkbox(text, enabled, box -> box.attachField(...).
                // The lambda addresses that field, not the checkbox.
                .checkWithField("Enable HTTP Server on port",
                        viewModel.enableHttpServerProperty(),
                        viewModel.httpPortProperty(),
                        port -> port.validate(viewModel.httpPortValidationStatus())
                                    .help(HelpFile.REMOTE))

                // A group of related elements, can be disabled as one.
                .group(expert -> expert
                        .stringField("API base URL", viewModel.apiBaseUrlProperty()),
                        expertGroup -> expertGroup.disableWhen(viewModel.disableExpertSettingsProperty())),

                // Documentation for the section as a whole: a help button beside its heading.
                httpServerSection -> httpServerSection.help("https://docs.jabref.org/advanced/remote"))
        .section("Another section", section -> section
                ...)
        .build();

The texts are written out above for readability; in a tab they are Localization.lang(...) calls.

The handle a lambda receives states what it is: an InputElement is a Control, so it can carry a tooltip, validation and attachments — a help button, a browse button, an inline value field appended right after it; a NodeElement offers none of that because it is not a Control. Asking for the wrong one does not compile.

Documentation is linked with help(...), which appends a help icon button that stays clickable while the element it belongs to is disabled. It sits on the two handles that own a row of their own: SectionRegion#help puts it beside the section heading, InputElement#help beside a single control. Both take either a HelpFile — a page of the JabRef user documentation — or a URL for anything outside it.

Consecutive labelled fields share an aligned two-column GridPane. Validation decoration is applied per control once that control reaches a scene, which is when ControlsFX can position it.

Nested and Inner Type Summary

Modifier and TypeClass and Description
public abstract static class
PreferencesFormBuilder.ElementBase<S extends PreferencesFormBuilder.ElementBase<S, N>, N extends Node>

Base of the element handles: what can be done to any node the builder just added.

public static class
PreferencesFormBuilder.FormRegion<T extends Pane>

A plain region: a group, columns or flow block, which has contents but no heading of its own.

public static class
PreferencesFormBuilder.InputElement<N extends Control>

A Control the builder placed.

public static class
PreferencesFormBuilder.NodeElement<N extends Node>

A node that is not a Control: a hand-assembled row, a table, a custom region.

public abstract static class
PreferencesFormBuilder.RegionBase<S extends PreferencesFormBuilder.RegionBase<S, T>, T extends Pane>

Base of the region handles, handed to a region's configuration lambda once the builder has closed it.

public static class
PreferencesFormBuilder.SectionRegion

The region of a section: unlike a plain region it has a header, which is therefore the only kind of region that can take a help button.

Field Summary

Modifier and TypeField and Description
private boolean
private final Deque<Pane>
private @Nullable GridPane
currentGrid

Element grid spanning multiple input elements to ensure correct alignment.

private @Nullable ToggleGroup
currentToggleGroup

The toggle group radios join inside radioGroup.

private final DialogService
private final GuiPreferences
private final VBox
private final List<SearchableElement>
searchableElements

Every visible text handed to the builder, paired with the node it captions.

private final de.saxsys.mvvmfx.utils.validation.visualization.ControlsFxVisualizer

Constructor Summary

AccessConstructor and Description
public

Method Summary

Modifier and TypeMethod and Description
private <X> PreferencesFormBuilder
addCombo(String label, ComboBox<X> combo, Property<X> value, Callback<X, String> display, Consumer<PreferencesFormBuilder.InputElement<ComboBox<X>>> config)

Shared wiring for combo variants.

private void
addField(String label, Node control)

private void
addNode(Node node)

private void
addToContainer(Node node)

Appends to the open container.

private void
attachTo(Node primary, Node attachment)

Places attachment directly after primary in its row: the same append addToContainer does for top-level nodes, only targeting the primary's row rather than the open container.

public VBox
public PreferencesFormBuilder
button(String text, Runnable action)

public PreferencesFormBuilder
public PreferencesFormBuilder
button(String text, JabRefIcon icon, Runnable action)

public PreferencesFormBuilder
public PreferencesFormBuilder
buttonRow(Button... buttons)

The row of actions under a table, right-aligned as the table's actions always are.

public PreferencesFormBuilder
public PreferencesFormBuilder
public PreferencesFormBuilder
checkWithField(String text, Property<Boolean> enabled, StringProperty fieldValue)

A checkbox with an inline value field that is enabled only while the box is ticked (the recurring "Enable ...

public PreferencesFormBuilder
private void
collectSearchable(Node current)

Registers the text of every Labeled inside current, each highlighting the labeled itself.

public PreferencesFormBuilder
columns(Consumer<PreferencesFormBuilder> content)

A side-by-side region: every element inside becomes an equally growing column.

public PreferencesFormBuilder
public <X> PreferencesFormBuilder
combo(String label, ObservableValue<? extends ObservableList<X>> items, Property<X> value, Callback<X, String> display)

public <X> PreferencesFormBuilder
combo(String label, ObservableValue<? extends ObservableList<X>> items, Property<X> value, Callback<X, String> display, Consumer<PreferencesFormBuilder.InputElement<ComboBox<X>>> config)

private <E> PreferencesFormBuilder
configured(E element, Consumer<E> config)

private Pane
container()

The open container: root until a region pushes one, and never empty, since the stack is seeded in the constructor and every push is popped in a finally.

public <T extends Node> PreferencesFormBuilder
custom(T node)

Adds a fully custom node spanning the form.

public <T extends Node> PreferencesFormBuilder
private void
decorate(de.saxsys.mvvmfx.utils.validation.ValidationStatus status, Control control)

Applies a validation decoration once control actually sits in a scene — ControlsFX positions the decoration against the parent, so doing this earlier is a no-op at best.

private static ObservableValue<Boolean>
either(ObservableValue<? extends Boolean> first, ObservableValue<? extends Boolean> second)

Logical OR of two conditions, used to combine disable conditions: an element is disabled while any of the conditions installed on it holds (see ElementBase#disableWhen).

private GridPane
ensureGrid()

The two-column grid consecutive labeled fields share, created on first use.

public <T extends Control> PreferencesFormBuilder
field(String label, T control)

public <T extends Control> PreferencesFormBuilder
public PreferencesFormBuilder
field(String label, Node node)

A labeled row whose value is not a control, e.g. an image; nothing to validate or focus.

public PreferencesFormBuilder
flow(Consumer<PreferencesFormBuilder> content)

A wrapping region: elements flow left to right and wrap onto the next line as the dialog narrows.

public PreferencesFormBuilder
private void
public List<SearchableElement>
getSearchableElements()

The visible texts of this form with the nodes they caption; see SearchableElement.

public PreferencesFormBuilder
public PreferencesFormBuilder
private Button
public PreferencesFormBuilder
info(String text)

An indented, italic explanatory line below the control it comments on.

public PreferencesFormBuilder
public PreferencesFormBuilder
label(String text)

A plain, unstyled caption line (for text that introduces the following controls).

public PreferencesFormBuilder
private static <E> Consumer<E>
noConfig()

The empty configuration every no-config overload delegates with.

public PreferencesFormBuilder
radio(String text, Property<Boolean> selected)

public PreferencesFormBuilder
public PreferencesFormBuilder
radioGroup(Consumer<PreferencesFormBuilder> content)

A mutually-exclusive radio group: radios added inside share one ToggleGroup, while each stays bound to its own boolean property (matching the existing view models).

private <T extends Pane> T
region(T region, Consumer<PreferencesFormBuilder> content)

Everything added inside becomes a sub-region of the form.

private static HBox
row(Node content)

The row an element lives in, and the guarantee attachTo reads back out: an element placed through one of these can always take attachments, because there is an HBox to append them to.

private void
searchable(String text, Node node)

public <X> PreferencesFormBuilder
searchableCombo(String label, ObservableValue<? extends ObservableList<X>> items, Property<X> value, Callback<X, String> display)

public <X> PreferencesFormBuilder
public PreferencesFormBuilder
section(String title, Consumer<PreferencesFormBuilder> content)

A titled section.

public PreferencesFormBuilder
section(String title, Consumer<PreferencesFormBuilder> content, Consumer<PreferencesFormBuilder.SectionRegion> config)

The configuration lambda addresses the section as a whole, including its header (e.g. for help buttons).

public <T extends Control> PreferencesFormBuilder
stackedField(String label, T control)

A field with the label above.

public <T extends Control> PreferencesFormBuilder
public PreferencesFormBuilder
public PreferencesFormBuilder
public <X> PreferencesFormBuilder
table(TableView<X> table)

An editable list, taking the form's remaining height.

public <X> PreferencesFormBuilder
public <X> PreferencesFormBuilder
tagsField(String label, TagsField<X> tagsField)

A pre-built, pre-bound TagsField (see TagsFieldEditor).

public <X> PreferencesFormBuilder
Inherited from java.lang.Object:
cloneequalsfinalizegetClasshashCodenotifynotifyAlltoStringwaitwaitwait