Commit cef833e1 authored by Axel Howind's avatar Axel Howind

renamed classes / InputDialogPane.init() / defer creation of UI

parent a948038c
Pipeline #50687987 passed with stages
in 18 minutes and 48 seconds
......@@ -22,13 +22,13 @@ import javafx.scene.control.Alert.AlertType;
* Provides a fluent interface to create Alerts.
*/
public class AlertPaneBuilder
extends AbstractPaneBuilder<TypedDialogPane<Void>, AlertPaneBuilder, Void> {
extends AbstractPaneBuilder<InputDialogPane<Void>, AlertPaneBuilder, Void> {
public AlertPaneBuilder(AlertType type) {
super(() -> createPane(type));
}
public static TypedDialogPane<Void> createPane(AlertType type) {
return new TypedDialogPane<Void>() {
public static InputDialogPane<Void> createPane(AlertType type) {
return new InputDialogPane<Void>() {
@Override
public Void get() {
return null;
......@@ -41,8 +41,8 @@ extends AbstractPaneBuilder<TypedDialogPane<Void>, AlertPaneBuilder, Void> {
* @return Alert instance
*/
@Override
public TypedDialogPane<Void> build() {
TypedDialogPane<Void> dlg = super.build();
public InputDialogPane<Void> build() {
InputDialogPane<Void> dlg = super.build();
return dlg;
}
......
......@@ -31,7 +31,7 @@ public class InputDialog extends Dialog<Map<String, Object>> {
if (btn != ButtonType.OK) {
return null;
}
return ((InputDialogPane) getDialogPane()).get();
return ((InputPane) getDialogPane()).get();
});
}
......
package com.dua3.fx.util.controls;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.logging.Logger;
import java.util.function.Supplier;
import com.dua3.fx.util.FxUtil;
import javafx.scene.control.DialogPane;
import javafx.event.ActionEvent;
import javafx.geometry.Dimension2D;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Label;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.GridPane;
public abstract class InputDialogPane<R> extends DialogPane implements Supplier<R> {
public class InputDialogPane extends TypedDialogPane<Map<String,Object>> {
/** Logger */
protected static final Logger LOG = Logger.getLogger(InputDialogPane.class.getSimpleName());
private static final String MARKER_INITIAL = "";
private static final String MARKER_ERROR = "\u26A0";
private static final String MARKER_OK = "";
/**
* Meta data for a single input field consisting of ID, label text, default value etc.
*
* @param <T>
* the input's value type
*/
static class Meta<T> {
final String id;
final Class<T> cls;
final T dflt;
final InputControl<T> control;
Label label = new Label();
Label marker = new Label();
Meta(String id, String label, Class<T> cls, T dflt, InputControl<T> control) {
this.id = id;
this.label.setText(label);
this.cls = cls;
this.dflt = dflt;
this.control = control;
Dimension2D dimMarker = new Dimension2D(0,0);
dimMarker = FxUtil.growToFit(dimMarker, marker.getBoundsInLocal());
marker.setMinSize(dimMarker.getWidth(), dimMarker.getHeight());
this.marker.setText(MARKER_INITIAL);
}
void reset() {
control.set(dflt);
}
Optional<String> validate() {
return control.validate();
}
}
private Collection<Meta<?>> data = null;
@Override
public Map<String, Object> get() {
// Collecors.toMap() does not support null values!
Map<String,Object> result = new HashMap<>();
data.stream().forEach(e -> result.put(e.id, e.control.get()));
return result;
}
public InputDialogPane() {
}
private void addToGrid(GridPane grid, Node child, int c, int r, Insets insets) {
grid.add(child, c, r);
GridPane.setMargin(child, insets);
}
void setContent(Collection<Meta<?>> data, int columns) {
this.data = Objects.requireNonNull(data);
// create grid with input controls
GridPane grid = new GridPane();
Insets insets = new Insets(2);
Insets markerInsets = new Insets(0);
int r = 0, c = 0;
for (var entry : data) {
// add label and control
addToGrid(grid, entry.label, 3 * c, r, insets);
addToGrid(grid, entry.control.node(), 3 * c + 1, r, insets);
addToGrid(grid, entry.marker, 3 * c + 2, r, markerInsets);
// move to next position in grid
c = (c + 1) % columns;
if (c == 0) {
r++;
}
}
setContent(grid);
// buttons
getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL);
final Button okButton = (Button) lookupButton(ButtonType.OK);
okButton.addEventFilter(ActionEvent.ACTION, ae -> {
if (!validate()) {
ae.consume(); //not valid
}
});
}
private boolean validate() {
// validate all input fields. validation succeeds if no validation returns an error message.
// do not use allMatches() because it might not process all items
return data.stream()
.map(item -> validateAndMark(item))
.filter(Optional::isPresent)
.count() == 0;
}
private Optional<String> validateAndMark(Meta<?> item) {
Optional<String> result = item.validate();
boolean ok = result.isEmpty();
if (ok) {
item.marker.setText(MARKER_OK);
item.marker.setTooltip(null);
} else {
item.marker.setText(MARKER_ERROR);
item.marker.setTooltip(new Tooltip(result.get()));
}
return result;
public void init() {
}
}
......@@ -24,7 +24,7 @@ import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.function.Supplier;
import com.dua3.fx.util.controls.InputDialogPane.Meta;
import com.dua3.fx.util.controls.InputPane.Meta;
import com.dua3.utility.lang.LangUtil;
import com.dua3.utility.options.OptionSet;
import com.dua3.utility.options.OptionValues;
......@@ -41,16 +41,16 @@ import javafx.scene.control.TextField;
* Provides a fluent interface to create Alerts.
*/
public class InputDialogPaneBuilder
extends AbstractPaneBuilder<InputDialogPane, InputDialogPaneBuilder, Map<String, Object>>
extends AbstractPaneBuilder<InputPane, InputDialogPaneBuilder, Map<String, Object>>
implements InputBuilder<InputDialogPaneBuilder> {
public InputDialogPaneBuilder() {
super(InputDialogPane::new);
super(InputPane::new);
}
private int columns = 1;
private LinkedHashMap<String, InputDialogPane.Meta<?>> data = new LinkedHashMap<>();
private LinkedHashMap<String, InputPane.Meta<?>> data = new LinkedHashMap<>();
/* (non-Javadoc)
* @see com.dua3.fx.util.controls.InputBuilder#add(java.lang.String, java.lang.String, java.lang.Class, T, com.dua3.fx.util.controls.InputDialogPane.InputControl)
......@@ -273,8 +273,8 @@ implements InputBuilder<InputDialogPaneBuilder> {
* @see com.dua3.fx.util.controls.InputBuilder#build()
*/
@Override
public InputDialogPane build() {
InputDialogPane pane = super.build();
public InputPane build() {
InputPane pane = super.build();
pane.setContent(data.values(), columns);
......
package com.dua3.fx.util.controls;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.logging.Logger;
import com.dua3.fx.util.FxUtil;
import javafx.event.ActionEvent;
import javafx.geometry.Dimension2D;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Label;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.GridPane;
public class InputPane extends InputDialogPane<Map<String,Object>> {
/** Logger */
protected static final Logger LOG = Logger.getLogger(InputPane.class.getSimpleName());
private static final String MARKER_INITIAL = "";
private static final String MARKER_ERROR = "\u26A0";
private static final String MARKER_OK = "";
/**
* Meta data for a single input field consisting of ID, label text, default value etc.
*
* @param <T>
* the input's value type
*/
static class Meta<T> {
final String id;
final Class<T> cls;
final T dflt;
final InputControl<T> control;
Label label = new Label();
Label marker = new Label();
Meta(String id, String label, Class<T> cls, T dflt, InputControl<T> control) {
this.id = id;
this.label.setText(label);
this.cls = cls;
this.dflt = dflt;
this.control = control;
Dimension2D dimMarker = new Dimension2D(0,0);
dimMarker = FxUtil.growToFit(dimMarker, marker.getBoundsInLocal());
marker.setMinSize(dimMarker.getWidth(), dimMarker.getHeight());
this.marker.setText(MARKER_INITIAL);
}
void reset() {
control.set(dflt);
}
Optional<String> validate() {
return control.validate();
}
}
private Collection<Meta<?>> data = null;
@Override
public Map<String, Object> get() {
// Collecors.toMap() does not support null values!
Map<String,Object> result = new HashMap<>();
data.stream().forEach(e -> result.put(e.id, e.control.get()));
return result;
}
public InputPane() {
}
private void addToGrid(GridPane grid, Node child, int c, int r, Insets insets) {
grid.add(child, c, r);
GridPane.setMargin(child, insets);
}
void setContent(Collection<Meta<?>> data, int columns) {
this.data = Objects.requireNonNull(data);
// create grid with input controls
GridPane grid = new GridPane();
Insets insets = new Insets(2);
Insets markerInsets = new Insets(0);
int r = 0, c = 0;
for (var entry : data) {
// add label and control
addToGrid(grid, entry.label, 3 * c, r, insets);
addToGrid(grid, entry.control.node(), 3 * c + 1, r, insets);
addToGrid(grid, entry.marker, 3 * c + 2, r, markerInsets);
// move to next position in grid
c = (c + 1) % columns;
if (c == 0) {
r++;
}
}
setContent(grid);
// buttons
getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL);
final Button okButton = (Button) lookupButton(ButtonType.OK);
okButton.addEventFilter(ActionEvent.ACTION, ae -> {
if (!validate()) {
ae.consume(); //not valid
}
});
}
private boolean validate() {
// validate all input fields. validation succeeds if no validation returns an error message.
// do not use allMatches() because it might not process all items
return data.stream()
.map(item -> validateAndMark(item))
.filter(Optional::isPresent)
.count() == 0;
}
private Optional<String> validateAndMark(Meta<?> item) {
Optional<String> result = item.validate();
boolean ok = result.isEmpty();
if (ok) {
item.marker.setText(MARKER_OK);
item.marker.setTooltip(null);
} else {
item.marker.setText(MARKER_ERROR);
item.marker.setTooltip(new Tooltip(result.get()));
}
return result;
}
}
......@@ -47,20 +47,21 @@ public class OptionsPane extends GridPane implements InputControl<OptionValues>{
public OptionsPane(Supplier<OptionSet> options, Supplier<OptionValues> dflt) {
this.options = options;
this.currentValues=dflt;
init(options.get(), dflt.get());
}
public void init(OptionSet options, OptionValues currentValues) {
public void init() {
getChildren().clear();
OptionSet optionSet = options.get();
OptionValues values = currentValues.get();
int row = 0;
for (Option<?> option: options) {
for (Option<?> option: optionSet) {
Label label = new Label(option.getName());
Property<?> property;
Control control;
Value<?> value = currentValues.get(option);
Value<?> value = values.get(option);
if (option instanceof StringOption) {
TextField c = new TextField();
c.setText(String.valueOf(value));
......
package com.dua3.fx.util.controls;
import java.util.function.Supplier;
import javafx.scene.control.DialogPane;
public abstract class TypedDialogPane<R> extends DialogPane implements Supplier<R> {
}
......@@ -74,7 +74,7 @@ public class WizardDialog extends Dialog<Map<String,Object>> {
/**
* Wizard page information class.
*/
static class Page<D extends TypedDialogPane<R>,R> {
static class Page<D extends InputDialogPane<R>,R> {
private D pane;
private String next;
private R result;
......@@ -167,9 +167,10 @@ public class WizardDialog extends Dialog<Map<String,Object>> {
private void setPage(String pageName) {
this.current = Pair.of(pageName, pages.get(pageName));
DialogPane pane = current.second.pane;
InputDialogPane<?> pane = current.second.pane;
setDialogPane(pane);
pane.init();
pane.layout();
pane.getScene().getWindow().sizeToScene();
......
......@@ -19,7 +19,7 @@ public class WizardDialogBuilder {
LinkedHashMap<String, Page<?,?>> pages = new LinkedHashMap<>();
public <D extends TypedDialogPane<R>,B extends AbstractPaneBuilder<D,B,R>,R> WizardDialogBuilder page(String name, B builder) {
public <D extends InputDialogPane<R>,B extends AbstractPaneBuilder<D,B,R>,R> WizardDialogBuilder page(String name, B builder) {
Page<D,R> page = new Page<>();
page.setNext(builder.next);
D pane = builder.build();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment