Commit both corner-resize dimensions in one host transaction
This commit is contained in:
@@ -109,7 +109,8 @@ public final class DesignerPane extends BorderPane implements AutoCloseable {
|
||||
private DocumentSnapshot draft;
|
||||
private FxmlProjection projection;
|
||||
private DesignerWorkflow workflow;
|
||||
private boolean loading, closed, pending, last;
|
||||
private boolean loading, closed, pending, last, canvasCommitQueued;
|
||||
private long canvasGeneration;
|
||||
private final java.util.concurrent.atomic.AtomicLong renderSequence =
|
||||
new java.util.concurrent.atomic.AtomicLong();
|
||||
private AutoCloseable changes;
|
||||
@@ -205,6 +206,7 @@ public final class DesignerPane extends BorderPane implements AutoCloseable {
|
||||
.addListener(
|
||||
(o, a, b) -> {
|
||||
if (!loading) {
|
||||
flushCanvas();
|
||||
if (pending && submittedSource == null) submittedSource = pendingBase;
|
||||
commitSource();
|
||||
workflow.cancel();
|
||||
@@ -338,6 +340,8 @@ public final class DesignerPane extends BorderPane implements AutoCloseable {
|
||||
private void load(DocumentSnapshot value) {
|
||||
if (closed) return;
|
||||
loading = true;
|
||||
canvasCommitQueued = false;
|
||||
canvasGeneration++;
|
||||
renderSequence.incrementAndGet();
|
||||
workflow.cancel();
|
||||
try {
|
||||
@@ -441,6 +445,28 @@ public final class DesignerPane extends BorderPane implements AutoCloseable {
|
||||
}
|
||||
|
||||
private void canvasChanged() {
|
||||
if (loading || pending || closed || canvasCommitQueued) return;
|
||||
// Kit's corner-resize gesture pushes one job per dimension on the same FX event turn.
|
||||
// Capture the completed gesture, not the intermediate width-only document.
|
||||
canvasCommitQueued = true;
|
||||
long generation = ++canvasGeneration;
|
||||
var projectedDocument = kit.editor().getFxomDocument();
|
||||
var expectedDraft = draft;
|
||||
Platform.runLater(
|
||||
() -> {
|
||||
if (generation != canvasGeneration || closed) return;
|
||||
if (projectedDocument != kit.editor().getFxomDocument() || draft != expectedDraft) {
|
||||
canvasCommitQueued = false;
|
||||
return;
|
||||
}
|
||||
flushCanvas();
|
||||
});
|
||||
}
|
||||
|
||||
private void flushCanvas() {
|
||||
if (!canvasCommitQueued) return;
|
||||
canvasCommitQueued = false;
|
||||
canvasGeneration++;
|
||||
if (loading || pending || closed) return;
|
||||
try {
|
||||
submit(
|
||||
@@ -454,6 +480,7 @@ public final class DesignerPane extends BorderPane implements AutoCloseable {
|
||||
|
||||
private void withDraft(Runnable action) {
|
||||
if (document == null) return;
|
||||
flushCanvas();
|
||||
if (pending || !source.getText().equals(draft.text())) {
|
||||
commitSource();
|
||||
status.setText("Finishing source edit; repeat the action when synchronized");
|
||||
@@ -689,6 +716,7 @@ public final class DesignerPane extends BorderPane implements AutoCloseable {
|
||||
}
|
||||
|
||||
private void flushInto(CompletableFuture<Void> result, long deadline) {
|
||||
flushCanvas();
|
||||
if (System.nanoTime() > deadline) {
|
||||
result.completeExceptionally(
|
||||
new IllegalStateException("Source edit could not be synchronized"));
|
||||
|
||||
@@ -118,6 +118,95 @@ public final class ProjectionHostChecks {
|
||||
new javafx.scene.input.PickResult(node, p.getX() + dx, p.getY() + dy)));
|
||||
}
|
||||
|
||||
public static void resizeCorner(DesignerPane pane, DocumentSession document) throws Exception {
|
||||
String before = document.snapshot().toCompletableFuture().get().text();
|
||||
fx(
|
||||
() -> {
|
||||
pane.kit().editor().getSelection().select(object(pane, "second"));
|
||||
pane.applyCss();
|
||||
pane.layout();
|
||||
return null;
|
||||
});
|
||||
Thread.sleep(150);
|
||||
double initialWidth =
|
||||
fx(() -> ((SnapshotRegion) object(pane, "second").getSceneGraphObject()).getWidth());
|
||||
double initialHeight =
|
||||
fx(() -> ((SnapshotRegion) object(pane, "second").getSceneGraphObject()).getHeight());
|
||||
fx(
|
||||
() -> {
|
||||
var handles = pane.content().lookupHandles(object(pane, "second"));
|
||||
check(
|
||||
handles
|
||||
instanceof
|
||||
com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.handles
|
||||
.AbstractGenericHandles<
|
||||
?>,
|
||||
"Native resize handles missing");
|
||||
var handle =
|
||||
((com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.handles
|
||||
.AbstractGenericHandles<
|
||||
?>)
|
||||
handles)
|
||||
.getHandleNode(
|
||||
com.oracle.javafx.scenebuilder.kit.editor.panel.content.util.CardinalPoint
|
||||
.SE);
|
||||
mouse(handle, javafx.scene.input.MouseEvent.MOUSE_PRESSED, 0, 0, true);
|
||||
mouse(handle, javafx.scene.input.MouseEvent.DRAG_DETECTED, 20, 15, true);
|
||||
mouse(handle, javafx.scene.input.MouseEvent.MOUSE_DRAGGED, 20, 15, true);
|
||||
mouse(handle, javafx.scene.input.MouseEvent.MOUSE_RELEASED, 20, 15, false);
|
||||
return null;
|
||||
});
|
||||
await(() -> !document.snapshot().toCompletableFuture().get().text().equals(before));
|
||||
String resized = document.snapshot().toCompletableFuture().get().text();
|
||||
check(
|
||||
resized.contains("prefHeight=") && resized.contains("Card"),
|
||||
"Resize must preserve real Component declaration");
|
||||
var parser = javax.xml.parsers.DocumentBuilderFactory.newInstance();
|
||||
parser.setNamespaceAware(true);
|
||||
parser.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
|
||||
var resizedXml =
|
||||
parser
|
||||
.newDocumentBuilder()
|
||||
.parse(new org.xml.sax.InputSource(new java.io.StringReader(resized)));
|
||||
var resizedNodes = resizedXml.getElementsByTagName("*");
|
||||
org.w3c.dom.Element resizedCard = null;
|
||||
for (int i = 0; i < resizedNodes.getLength(); i++) {
|
||||
var candidate = (org.w3c.dom.Element) resizedNodes.item(i);
|
||||
if (candidate.getAttributeNS("http://javafx.com/fxml/1", "id").equals("second"))
|
||||
resizedCard = candidate;
|
||||
}
|
||||
String storedHeight = resizedCard.getAttribute("prefHeight");
|
||||
check(
|
||||
!storedHeight.isBlank() && Double.parseDouble(storedHeight) > initialHeight,
|
||||
"Corner resize lost height: before="
|
||||
+ initialHeight
|
||||
+ " stored="
|
||||
+ storedHeight
|
||||
+ " source="
|
||||
+ resized);
|
||||
String storedWidth = resizedCard.getAttribute("prefWidth");
|
||||
check(
|
||||
!storedWidth.isBlank() && Double.parseDouble(storedWidth) > initialWidth,
|
||||
"Corner resize lost width: before=" + initialWidth + " stored=" + storedWidth);
|
||||
await(
|
||||
() ->
|
||||
fx(
|
||||
() ->
|
||||
source(pane).getText().equals(resized)
|
||||
&& ((SnapshotRegion) object(pane, "second").getSceneGraphObject())
|
||||
.getHeight()
|
||||
> initialHeight));
|
||||
click(pane, "Undo");
|
||||
await(() -> document.snapshot().toCompletableFuture().get().text().equals(before));
|
||||
await(() -> fx(() -> source(pane).getText().equals(before)));
|
||||
click(pane, "Redo");
|
||||
await(() -> document.snapshot().toCompletableFuture().get().text().equals(resized));
|
||||
await(() -> fx(() -> source(pane).getText().equals(resized)));
|
||||
click(pane, "Undo");
|
||||
await(() -> document.snapshot().toCompletableFuture().get().text().equals(before));
|
||||
await(() -> fx(() -> source(pane).getText().equals(before)));
|
||||
}
|
||||
|
||||
public static void run(DesignerPane pane, DocumentSession document) throws Exception {
|
||||
String original = document.snapshot().toCompletableFuture().get().text();
|
||||
// The same real application, now with nested, fully-qualified Component declarations.
|
||||
@@ -185,39 +274,7 @@ public final class ProjectionHostChecks {
|
||||
pane.layout();
|
||||
return null;
|
||||
});
|
||||
Thread.sleep(150);
|
||||
fx(
|
||||
() -> {
|
||||
var handles = pane.content().lookupHandles(object(pane, "second"));
|
||||
check(
|
||||
handles
|
||||
instanceof
|
||||
com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.handles
|
||||
.AbstractGenericHandles<
|
||||
?>,
|
||||
"Native resize handles missing");
|
||||
var handle =
|
||||
((com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.handles
|
||||
.AbstractGenericHandles<
|
||||
?>)
|
||||
handles)
|
||||
.getHandleNode(
|
||||
com.oracle.javafx.scenebuilder.kit.editor.panel.content.util.CardinalPoint
|
||||
.SE);
|
||||
mouse(handle, javafx.scene.input.MouseEvent.MOUSE_PRESSED, 0, 0, true);
|
||||
mouse(handle, javafx.scene.input.MouseEvent.DRAG_DETECTED, 20, 15, true);
|
||||
mouse(handle, javafx.scene.input.MouseEvent.MOUSE_DRAGGED, 20, 15, true);
|
||||
mouse(handle, javafx.scene.input.MouseEvent.MOUSE_RELEASED, 20, 15, false);
|
||||
return null;
|
||||
});
|
||||
await(() -> !document.snapshot().toCompletableFuture().get().text().equals(changed));
|
||||
String resized = document.snapshot().toCompletableFuture().get().text();
|
||||
check(
|
||||
resized.contains("prefHeight=") && resized.contains("fixture.components.Card"),
|
||||
"Resize must preserve real Component declaration");
|
||||
click(pane, "Undo");
|
||||
await(() -> document.snapshot().toCompletableFuture().get().text().equals(changed));
|
||||
await(() -> fx(() -> source(pane).getText().equals(changed)));
|
||||
resizeCorner(pane, document);
|
||||
// Exercise the exact Kit relocation command used after a canvas move. Native OS
|
||||
// dragboard delivery is a separate manual/platform qualification, not synthesized here.
|
||||
fx(
|
||||
@@ -243,7 +300,11 @@ public final class ProjectionHostChecks {
|
||||
document.save(snapshot.revision()).toCompletableFuture().get();
|
||||
check(
|
||||
java.nio.file.Files.readString(java.nio.file.Path.of(snapshot.document())).equals(changed),
|
||||
"Save reformatted source");
|
||||
"Save reformatted source: expected=["
|
||||
+ changed
|
||||
+ "] actual=["
|
||||
+ java.nio.file.Files.readString(java.nio.file.Path.of(snapshot.document()))
|
||||
+ "]");
|
||||
try (var reopened =
|
||||
pane.kit().host().documents().open(snapshot.document()).toCompletableFuture().get()) {
|
||||
check(
|
||||
|
||||
Reference in New Issue
Block a user