Discover Repeat collections and compatible Components in both hosts
This commit is contained in:
@@ -24,6 +24,9 @@ final class BindingEditor extends VBox {
|
||||
pane = field("pane"),
|
||||
component = field("component"),
|
||||
collection = field("collection");
|
||||
private final ComboBox<BindingMetadata.RepeatOption> repeatCollections = new ComboBox<>(),
|
||||
repeatComponents = new ComboBox<>(),
|
||||
repeatPanes = new ComboBox<>();
|
||||
private final ComboBox<BindingMetadata.Computed> methods = new ComboBox<>();
|
||||
private final BindingDocument.Kind kind;
|
||||
private final ComboBox<BindingMetadata.Converter> converters = new ComboBox<>();
|
||||
@@ -36,7 +39,7 @@ final class BindingEditor extends VBox {
|
||||
remove = new Button("Remove binding");
|
||||
private String source = "";
|
||||
private Integer selected;
|
||||
private boolean loading, writable, active, followingCanvas;
|
||||
private boolean loading, writable, active, followingCanvas, updatingChoices;
|
||||
private final Consumer<String> navigate;
|
||||
|
||||
BindingEditor(
|
||||
@@ -66,7 +69,7 @@ final class BindingEditor extends VBox {
|
||||
remove.setText("Remove Computed binding");
|
||||
}
|
||||
if (repeat) {
|
||||
inspect.setText("Validate Repeat bindings");
|
||||
inspect.setText("Discover / Validate Repeat");
|
||||
fresh.setText("New Repeat binding");
|
||||
apply.setText("Apply Repeat binding");
|
||||
remove.setText("Remove Repeat binding");
|
||||
@@ -141,8 +144,34 @@ final class BindingEditor extends VBox {
|
||||
field.textProperty().addListener((o, a, b) -> methods.getItems().clear());
|
||||
}
|
||||
if (repeat) {
|
||||
grid.addRow(0, new Label("Pane (fx:id)"), pane, new Label("Collection path"), collection);
|
||||
grid.addRow(1, new Label("Component class"), component);
|
||||
repeatCollections.setId("repeat-collections");
|
||||
repeatComponents.setId("repeat-components");
|
||||
repeatPanes.setId("repeat-panes");
|
||||
repeatCollections.setPrefWidth(420);
|
||||
repeatComponents.setPrefWidth(680);
|
||||
repeatPanes.setPrefWidth(420);
|
||||
repeatCollections.setOnAction(
|
||||
e -> {
|
||||
var v = repeatCollections.getValue();
|
||||
if (v != null && !updatingChoices) {
|
||||
collection.setText(v.name());
|
||||
discover.accept(values());
|
||||
}
|
||||
});
|
||||
repeatComponents.setOnAction(
|
||||
e -> {
|
||||
var v = repeatComponents.getValue();
|
||||
if (v != null && !updatingChoices) component.setText(v.name());
|
||||
});
|
||||
repeatPanes.setOnAction(
|
||||
e -> {
|
||||
var v = repeatPanes.getValue();
|
||||
if (v != null && !updatingChoices) pane.setText(v.name());
|
||||
});
|
||||
collection.textProperty().addListener((o, a, b) -> repeatComponents.getItems().clear());
|
||||
grid.addRow(0, new Label("Pane (fx:id)"), pane, repeatPanes);
|
||||
grid.addRow(1, new Label("Collection path"), collection, repeatCollections);
|
||||
grid.addRow(2, new Label("Component class"), component, repeatComponents);
|
||||
}
|
||||
getChildren()
|
||||
.addAll(
|
||||
@@ -209,6 +238,9 @@ final class BindingEditor extends VBox {
|
||||
if (source.equals(text) && writable == editable) return;
|
||||
converters.getItems().clear();
|
||||
methods.getItems().clear();
|
||||
repeatCollections.getItems().clear();
|
||||
repeatComponents.getItems().clear();
|
||||
repeatPanes.getItems().clear();
|
||||
source = text;
|
||||
writable = editable;
|
||||
loading = true;
|
||||
@@ -276,16 +308,25 @@ final class BindingEditor extends VBox {
|
||||
if (kind == BindingDocument.Kind.REPEAT) {
|
||||
try {
|
||||
var errors = new BindingDocument(source, kind).errors();
|
||||
status.setText(
|
||||
provenance
|
||||
+ "\n"
|
||||
+ (errors.isEmpty() ? "No structural binding errors." : String.join("\n", errors))
|
||||
+ "\n"
|
||||
+ "Collection and Component compatibility have not been checked. Render drafts to"
|
||||
+ " test them.");
|
||||
if (!errors.isEmpty()) status.setText(status.getText() + "\n" + String.join("\n", errors));
|
||||
} catch (Exception error) {
|
||||
status.setText("Validation unavailable: " + error.getMessage());
|
||||
}
|
||||
status.setText(
|
||||
status.getText()
|
||||
+ "\n"
|
||||
+ "Compatibility uses declarations; construction and runtime values remain preview"
|
||||
+ " checks.");
|
||||
if (requested != null && values().equals(requested)) {
|
||||
updatingChoices = true;
|
||||
try {
|
||||
repeatCollections.getItems().setAll(metadata.collections());
|
||||
repeatComponents.getItems().setAll(metadata.components());
|
||||
repeatPanes.getItems().setAll(metadata.panes());
|
||||
} finally {
|
||||
updatingChoices = false;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (requested == null || !values().equals(requested)) return;
|
||||
@@ -303,6 +344,17 @@ final class BindingEditor extends VBox {
|
||||
}
|
||||
}
|
||||
|
||||
void invalidateDiscovery() {
|
||||
updatingChoices = true;
|
||||
try {
|
||||
repeatCollections.getItems().clear();
|
||||
repeatComponents.getItems().clear();
|
||||
repeatPanes.getItems().clear();
|
||||
} finally {
|
||||
updatingChoices = false;
|
||||
}
|
||||
}
|
||||
|
||||
void error(String message) {
|
||||
status.setText(message);
|
||||
}
|
||||
|
||||
@@ -682,6 +682,7 @@ public final class DesignerPane extends BorderPane implements AutoCloseable {
|
||||
validationPreview = false;
|
||||
bindingWorkflow.cancel();
|
||||
bindingBrowser.invalidate(message);
|
||||
repeatEditor.invalidateDiscovery();
|
||||
}
|
||||
|
||||
private void editBinding(BindingEditor.Intent intent) {
|
||||
@@ -744,7 +745,11 @@ public final class DesignerPane extends BorderPane implements AutoCloseable {
|
||||
.bindingDetails(
|
||||
last,
|
||||
captured,
|
||||
values == null ? "" : values.path(),
|
||||
values == null
|
||||
? ""
|
||||
: values.pane().isEmpty() && values.collection().isEmpty()
|
||||
? values.path()
|
||||
: values.collection(),
|
||||
values == null ? "both" : values.direction(),
|
||||
values == null ? "" : values.control(),
|
||||
values == null ? "" : values.property())
|
||||
|
||||
@@ -727,14 +727,105 @@ public final class ProjectionHostChecks {
|
||||
java.nio.file.Path.of(document.snapshot().toCompletableFuture().get().document()))
|
||||
.equals(changed),
|
||||
"Repeat save changed source");
|
||||
click(pane, "Validate Repeat bindings");
|
||||
selectRepeat(pane, "otherVisits");
|
||||
click(pane, "Discover / Validate Repeat");
|
||||
await(
|
||||
() ->
|
||||
fx(
|
||||
() ->
|
||||
((Label) pane.lookup("#repeat-status"))
|
||||
.getText()
|
||||
.contains("Collection and Component compatibility")));
|
||||
.contains("Compatibility uses declarations")));
|
||||
await(() -> fx(() -> !((ComboBox<?>) pane.lookup("#repeat-components")).getItems().isEmpty()));
|
||||
fx(
|
||||
() -> {
|
||||
var choices =
|
||||
(ComboBox<de.neitzel.fxdesigner.protocol.BindingMetadata.RepeatOption>)
|
||||
pane.lookup("#repeat-components");
|
||||
check(
|
||||
choices.getItems().stream()
|
||||
.anyMatch(
|
||||
c ->
|
||||
c.name().equals("fixture.components.AddressForm")
|
||||
&& c.status().equals("COMPATIBLE")),
|
||||
"Compatible Component choice absent");
|
||||
check(
|
||||
choices.getItems().stream()
|
||||
.anyMatch(
|
||||
c ->
|
||||
c.name().equals("fixture.components.WrongRepeatCard")
|
||||
&& c.status().equals("INCOMPATIBLE")),
|
||||
"Incompatible Component not explained");
|
||||
var panes =
|
||||
(ComboBox<de.neitzel.fxdesigner.protocol.BindingMetadata.RepeatOption>)
|
||||
pane.lookup("#repeat-panes");
|
||||
check(
|
||||
panes.getItems().stream()
|
||||
.anyMatch(c -> c.name().equals("action") && c.status().equals("INCOMPATIBLE")),
|
||||
"Non-Pane target accepted");
|
||||
check(
|
||||
((TextField) pane.lookup("#repeat-component"))
|
||||
.getText()
|
||||
.equals("fixture.components.VisitRow"),
|
||||
"Discovery replaced explicit Component");
|
||||
var collections =
|
||||
(ComboBox<de.neitzel.fxdesigner.protocol.BindingMetadata.RepeatOption>)
|
||||
pane.lookup("#repeat-collections");
|
||||
collections.setValue(
|
||||
collections.getItems().stream()
|
||||
.filter(c -> c.name().equals("visits"))
|
||||
.findFirst()
|
||||
.orElseThrow());
|
||||
return null;
|
||||
});
|
||||
await(() -> fx(() -> !((ComboBox<?>) pane.lookup("#repeat-components")).getItems().isEmpty()));
|
||||
check(documentText(document).equals(changed), "Repeat discovery changed source");
|
||||
fx(
|
||||
() -> {
|
||||
var choices =
|
||||
(ComboBox<de.neitzel.fxdesigner.protocol.BindingMetadata.RepeatOption>)
|
||||
pane.lookup("#repeat-components");
|
||||
choices.setValue(
|
||||
choices.getItems().stream()
|
||||
.filter(c -> c.name().equals("fixture.components.WrongRepeatCard"))
|
||||
.findFirst()
|
||||
.orElseThrow());
|
||||
return null;
|
||||
});
|
||||
check(documentText(document).equals(changed), "Component choice applied without review");
|
||||
click(pane, "Apply Repeat binding");
|
||||
String mismatched =
|
||||
changed.replace(
|
||||
"component=\"fixture.components.VisitRow\"",
|
||||
"component=\"fixture.components.WrongRepeatCard\"");
|
||||
await(() -> documentText(document).equals(mismatched));
|
||||
await(() -> fx(() -> source(pane).getText().equals(mismatched)));
|
||||
document
|
||||
.save(document.snapshot().toCompletableFuture().get().revision())
|
||||
.toCompletableFuture()
|
||||
.get();
|
||||
click(pane, "Render drafts");
|
||||
await(
|
||||
() ->
|
||||
fx(
|
||||
() ->
|
||||
((Label) pane.lookup("#repeat-status"))
|
||||
.getText()
|
||||
.contains("incompatible with Component Model")));
|
||||
await(
|
||||
() ->
|
||||
fx(
|
||||
() ->
|
||||
pane.lookupAll(".label").stream()
|
||||
.anyMatch(
|
||||
n ->
|
||||
n instanceof Label l
|
||||
&& l.getText() != null
|
||||
&& l.getText().startsWith("Preview blocked:"))));
|
||||
repeatHistory(pane, document, changed, mismatched);
|
||||
click(pane, "Undo");
|
||||
await(() -> documentText(document).equals(changed));
|
||||
await(() -> fx(() -> source(pane).getText().equals(changed)));
|
||||
click(pane, "Render drafts");
|
||||
await(
|
||||
() ->
|
||||
@@ -773,6 +864,7 @@ public final class ProjectionHostChecks {
|
||||
click(pane, "Apply Repeat binding");
|
||||
String invalid = replaced.replace("collection=\"visits\"/>", "collection=\"invalid path\"/>");
|
||||
await(() -> documentText(document).equals(invalid));
|
||||
await(() -> fx(() -> source(pane).getText().equals(invalid)));
|
||||
document
|
||||
.save(document.snapshot().toCompletableFuture().get().revision())
|
||||
.toCompletableFuture()
|
||||
|
||||
Reference in New Issue
Block a user