Discover Table compatibility and declared Row alignment

This commit is contained in:
2026-11-13 15:33:40 +01:00
parent 3417788194
commit 5462f480a4
26 changed files with 1086 additions and 76 deletions
@@ -213,7 +213,9 @@ public final class DesignerPane extends BorderPane implements AutoCloseable {
if (target != null) kit.editor().getSelection().select(target);
},
values -> validateBindings(values, false));
tableEditor = new TableEditor(this::editTable, this::navigateComponent);
tableEditor =
new TableEditor(
this::editTable, this::navigateComponent, values -> validateBindings(values, false));
bindingBrowser =
new BindingBrowser(
this::discoverBindings,
@@ -691,6 +693,7 @@ public final class DesignerPane extends BorderPane implements AutoCloseable {
bindingWorkflow.cancel();
bindingBrowser.invalidate(message);
repeatEditor.invalidateDiscovery();
tableEditor.invalidateDiscovery();
}
private void editTable(TableEditor.Intent intent) {
@@ -817,6 +820,14 @@ public final class DesignerPane extends BorderPane implements AutoCloseable {
: "Current successful build")
+ " · draft revision "
+ captured.revision());
tableEditor.metadata(
metadata,
values,
(last
? "Explicit retained build (outdated)"
: "Current successful build")
+ " · draft revision "
+ captured.revision());
status.setText(
"Binding validation complete: "
+ metadata.problems().size()
@@ -1,6 +1,7 @@
package de.neitzel.fxdesigner.kit;
import de.neitzel.fxdesigner.contracts.EditBatch.Replacement;
import de.neitzel.fxdesigner.protocol.BindingMetadata;
import java.util.List;
import java.util.function.Consumer;
import javafx.scene.control.*;
@@ -21,12 +22,47 @@ final class TableEditor extends VBox {
private final Consumer<Intent> edit;
private String source = "";
private TableDocument document;
private boolean loading;
private boolean loading, updatingChoices;
private final ComboBox<BindingMetadata.RepeatOption> collectionChoices = new ComboBox<>(),
rowChoices = new ComboBox<>(),
columnChoices = new ComboBox<>();
private final Consumer<BindingDocument.Values> discover;
private BindingDocument.Values values() {
return BindingDocument.Values.repeat("@table:" + table, row.getText(), collection.getText());
}
private Integer table, column;
TableEditor(Consumer<Intent> edit, Consumer<String> navigate) {
TableEditor(
Consumer<Intent> edit, Consumer<String> navigate, Consumer<BindingDocument.Values> discover) {
super(5);
this.edit = edit;
this.discover = discover;
collectionChoices.setId("table-collections");
rowChoices.setId("table-rows");
columnChoices.setId("table-attributes");
for (var c : List.of(collectionChoices, rowChoices, columnChoices)) c.setPrefWidth(360);
collectionChoices.setOnAction(
e -> {
var v = collectionChoices.getValue();
if (v != null && !updatingChoices) {
collection.setText(v.name());
discover.accept(values());
}
});
rowChoices.setOnAction(
e -> {
var v = rowChoices.getValue();
if (v != null && !updatingChoices) row.setText(v.name());
});
columnChoices.setOnAction(
e -> {
var v = columnChoices.getValue();
if (v != null && !updatingChoices) attribute.setText(v.name());
});
collection.textProperty().addListener((o, a, b) -> invalidateDiscovery());
setId("table-editor");
tables.setId("table-bindings");
columns.setId("table-columns");
@@ -68,6 +104,11 @@ final class TableEditor extends VBox {
.addAll(
tables,
tableFields,
new HBox(
6,
button("Discover / Validate Table", () -> discover.accept(values())),
collectionChoices,
rowChoices),
new HBox(
6,
button(
@@ -100,6 +141,7 @@ final class TableEditor extends VBox {
})),
columns,
columnFields,
columnChoices,
new HBox(
6,
button(
@@ -203,6 +245,7 @@ final class TableEditor extends VBox {
setDisable(!writable);
if (source.equals(text)) return;
source = text;
invalidateDiscovery();
loading = true;
try {
document = new TableDocument(text);
@@ -243,6 +286,49 @@ final class TableEditor extends VBox {
}
}
void invalidateDiscovery() {
updatingChoices = true;
try {
collectionChoices.getItems().clear();
rowChoices.getItems().clear();
columnChoices.getItems().clear();
} finally {
updatingChoices = false;
}
status.setText(
"Document/build or collection changed; Discover / Validate Table again. Row alignment"
+ " unverified.");
}
void metadata(BindingMetadata metadata, BindingDocument.Values requested, String provenance) {
var messages = new java.util.ArrayList<String>();
if (document != null) messages.addAll(document.errors());
for (var p : metadata.problems())
if (java.util.Set.of("collection", "row", "columns", "alignment", "Table")
.contains(p.property()))
messages.add(p.severity() + " " + p.control() + ": " + p.message());
status.setText(
provenance
+ "\n"
+ (messages.isEmpty()
? "No Table compatibility errors found."
: String.join("\n", messages))
+ "\n"
+ "Diagnostics describe the current document. Choices compare the requested collection."
+ " Open Row Component to resolve alignment warnings; its layout is never changed"
+ " automatically.");
if (requested != null && requested.equals(values())) {
updatingChoices = true;
try {
collectionChoices.getItems().setAll(metadata.tableCollections());
rowChoices.getItems().setAll(metadata.tableRows());
columnChoices.getItems().setAll(metadata.tableColumns());
} finally {
updatingChoices = false;
}
}
}
void error(String message) {
status.setText(message);
}
@@ -691,6 +691,118 @@ public final class ProjectionHostChecks {
});
}
private static void tableDiscovery(DesignerPane pane, DocumentSession document, String full)
throws Exception {
bindingTab(pane, "Table / Columns");
tableSelect(pane, "bindings", 0);
click(pane, "Discover / Validate Table");
await(() -> fx(() -> !((ComboBox<?>) pane.lookup("#table-attributes")).getItems().isEmpty()));
check(
fx(
() ->
((Label) pane.lookup("#table-status"))
.getText()
.contains("matches direct bindings")),
"Declared Row alignment missing");
fx(
() -> {
var rows =
(ComboBox<de.neitzel.fxdesigner.protocol.BindingMetadata.RepeatOption>)
pane.lookup("#table-rows");
check(
rows.getItems().stream()
.anyMatch(
c ->
c.name().equals("fixture.components.VisitRow")
&& c.status().equals("COMPATIBLE")),
"Compatible Row absent");
check(
rows.getItems().stream()
.anyMatch(
c ->
c.name().equals("fixture.components.WrongRepeatCard")
&& c.status().equals("INCOMPATIBLE")),
"Wrong Model Row accepted");
check(
((TextField) pane.lookup("#table-row"))
.getText()
.equals("fixture.components.VisitRow"),
"Discovery replaced explicit Row");
var attrs =
(ComboBox<de.neitzel.fxdesigner.protocol.BindingMetadata.RepeatOption>)
pane.lookup("#table-attributes");
attrs
.getSelectionModel()
.select(
attrs.getItems().stream()
.filter(c -> c.name().equals("street"))
.findFirst()
.orElseThrow());
return null;
});
check(documentText(document).equals(full), "Table discovery changed source");
// The Row draft must participate in discovery even though the parent source is unchanged.
var rowFile =
java.nio.file.Path.of(document.snapshot().toCompletableFuture().get().document())
.resolveSibling("VisitRow.fxml");
String disk = java.nio.file.Files.readString(rowFile);
try (var row =
pane.kit().host().documents().open(rowFile.toUri()).toCompletableFuture().get()) {
String original = documentText(row);
set(row, original.replace("path=\"street\"", "path=\"different\""));
await(() -> fx(() -> ((ComboBox<?>) pane.lookup("#table-rows")).getItems().isEmpty()));
click(pane, "Discover / Validate Table");
await(
() ->
fx(
() ->
((Label) pane.lookup("#table-status"))
.getText()
.contains("mismatch at Column")));
check(
java.nio.file.Files.readString(rowFile).equals(disk),
"Row discovery saved or rewrote Row source");
set(row, original);
click(pane, "Discover / Validate Table");
await(
() ->
fx(
() ->
((Label) pane.lookup("#table-status"))
.getText()
.contains("matches direct bindings")));
}
tableSelect(pane, "columns", 0);
tableField(pane, "attribute", "missing");
click(pane, "Apply Column");
await(() -> !documentText(document).equals(full));
String invalid = documentText(document);
await(() -> fx(() -> source(pane).getText().equals(invalid)));
document
.save(document.snapshot().toCompletableFuture().get().revision())
.toCompletableFuture()
.get();
click(pane, "Render drafts");
await(
() ->
fx(
() ->
pane.lookupAll(".label").stream()
.anyMatch(
n ->
n instanceof Label l
&& l.getText() != null
&& l.getText().startsWith("Preview blocked:")
&& l.getText().contains("simple read/write Attribute"))));
repeatHistory(pane, document, full, invalid);
click(pane, "Undo");
await(() -> documentText(document).equals(full));
await(() -> fx(() -> source(pane).getText().equals(full)));
System.out.println(
"PASS Table discovery: choices, explicit selection, Row draft overlays/alignment, type"
+ " errors, preview gating and undo/redo");
}
private static void tableEdits(DesignerPane pane, DocumentSession document, String full)
throws Exception {
set(document, full);
@@ -1413,6 +1525,7 @@ public final class ProjectionHostChecks {
directBindingEdits(pane, document, full);
computedBindingEdits(pane, document, full);
repeatBindingEdits(pane, document, full);
tableDiscovery(pane, document, full);
tableEdits(pane, document, full);
document
.save(document.snapshot().toCompletableFuture().get().revision())