package qualification; import com.intellij.openapi.application.*; import com.intellij.openapi.fileEditor.*; import com.intellij.openapi.project.Project; import com.intellij.openapi.startup.StartupActivity; import com.intellij.openapi.vfs.*; import com.oracle.javafx.scenebuilder.kit.fxom.*; import de.neitzel.fxdesigner.kit.*; import java.nio.file.*; import java.util.concurrent.*; import javafx.application.Platform; import javafx.embed.swing.JFXPanel; import javafx.scene.control.Button; /** Disposable companion plugin, never included in the delivered designer plugin. */ public final class WorkflowProbe implements StartupActivity.DumbAware { static final Path output = Path.of(System.getProperty("designer.check.output")); static DesignerPane active; static final java.util.concurrent.atomic.AtomicBoolean started = new java.util.concurrent.atomic.AtomicBoolean(); static T edt(Callable action) throws Exception { var f = new CompletableFuture(); ApplicationManager.getApplication() .invokeLater( () -> WriteIntentReadAction.run( () -> { try { f.complete(action.call()); } catch (Throwable t) { f.completeExceptionally(t); } }), ModalityState.nonModal()); return f.get(20, TimeUnit.SECONDS); } static T fx(Callable action) throws Exception { var f = new CompletableFuture(); Platform.runLater( () -> { try { f.complete(action.call()); } catch (Throwable t) { f.completeExceptionally(t); } }); return f.get(20, TimeUnit.SECONDS); } static JFXPanel bridge(java.awt.Component c) { if (c instanceof JFXPanel p) return p; if (c instanceof java.awt.Container parent) for (var child : parent.getComponents()) { var p = bridge(child); if (p != null) return p; } return null; } static void await(Callable condition) throws Exception { long until = System.nanoTime() + TimeUnit.SECONDS.toNanos(120); while (System.nanoTime() < until) { if (condition.call()) return; Thread.sleep(100); } throw new AssertionError("IDE workflow timed out"); } static void click(DesignerPane pane, String text) throws Exception { fx( () -> { ((Button) pane.lookupAll(".button").stream() .filter(n -> n instanceof Button b && b.getText().equals(text)) .findFirst() .orElseThrow()) .fire(); return null; }); } public void runActivity(Project project) { if (!started.compareAndSet(false, true)) return; ApplicationManager.getApplication() .executeOnPooledThread( () -> { try { Path path = Path.of( project.getBasePath(), "app/src/main/resources/fixture/components/Person.fxml"); VirtualFile file = edt(() -> LocalFileSystem.getInstance().refreshAndFindFileByNioFile(path)); FileEditor editor = edt( () -> { var manager = FileEditorManager.getInstance(project); var all = manager.openFile(file, true); manager.setSelectedEditor(file, "neitzel-fx-designer"); return java.util.Arrays.stream(all) .filter(e -> e.getName().equals("Neitzel Designer")) .findFirst() .orElseThrow(); }); await(() -> edt(() -> bridge(editor.getComponent()) != null)); JFXPanel bridge = edt(() -> bridge(editor.getComponent())); await( () -> fx( () -> bridge.getScene() != null && bridge.getScene().getRoot() instanceof DesignerPane)); DesignerPane pane = fx(() -> (DesignerPane) bridge.getScene().getRoot()); active = pane; await(() -> fx(() -> pane.kit().editor().getFxomDocument() != null)); click(pane, "Build and Refresh"); await( () -> fx( () -> java.util.List.of("card", "second").stream() .allMatch( id -> { var item = new FXOMFxIdIndex( pane.kit().editor().getFxomDocument()) .lookup(id); return item instanceof FXOMInstance instance && instance.getSceneGraphObject() instanceof SnapshotRegion region && region.image() != null && region.statusText().equals("Target runtime"); }))); fx( () -> { var kit = pane.kit().editor(); var card = (FXOMInstance) new FXOMFxIdIndex(kit.getFxomDocument()).lookup("card"); kit.getSelection().select(card); var property = (FXOMPropertyT) card.getProperties() .get( new com.oracle.javafx.scenebuilder.kit.metadata.util .PropertyName("caption")); String old = property.getValue(); kit.getJobManager() .push( new com.oracle.javafx.scenebuilder.kit.editor.job.Job(kit) { public boolean isExecutable() { return true; } public String getDescription() { return "IDE caption check"; } public void execute() { set("IDE canvas draft"); } public void undo() { set(old); } public void redo() { execute(); } void set(String text) { var doc = kit.getFxomDocument(); doc.beginUpdate(); try { property.setValue(text); } finally { doc.endUpdate(); } } }); return null; }); await( () -> edt( () -> FileDocumentManager.getInstance() .getDocument(file) .getText() .contains("IDE canvas draft"))); click(pane, "Undo"); await( () -> edt( () -> FileDocumentManager.getInstance() .getDocument(file) .getText() .contains("FXML caption"))); click(pane, "Redo"); await( () -> edt( () -> FileDocumentManager.getInstance() .getDocument(file) .getText() .contains("IDE canvas draft"))); click(pane, "Render drafts"); await( () -> fx( () -> java.util.List.of("card", "second").stream() .allMatch( id -> { var item = new FXOMFxIdIndex( pane.kit().editor().getFxomDocument()) .lookup(id); return item instanceof FXOMInstance instance && instance.getSceneGraphObject() instanceof SnapshotRegion region && region.image() != null && region.statusText().equals("Target runtime"); }))); fx( () -> { javax.imageio.ImageIO.write( javafx.embed.swing.SwingFXUtils.fromFXImage( pane.snapshot(null, null), null), "png", output.resolve("intellij.png").toFile()); return null; }); String version = fx(() -> System.getProperty("javafx.runtime.version")); if (!version.startsWith("26")) throw new AssertionError("Wrong host JavaFX: " + version); Files.writeString( output.resolve("SUCCESS.txt"), "PASS registered IntelliJ editor, shared Kit, real Build and Refresh, target" + " pixels, native document edits, undo/redo, draft render; JavaFX " + version + "\n"); } catch (Throwable e) { try { if (active != null) fx( () -> { Files.writeString( output.resolve("ui-state.txt"), active.lookupAll(".label").stream() .filter(n -> n instanceof javafx.scene.control.Label) .map( n -> String.valueOf( ((javafx.scene.control.Label) n).getText())) .toList() .toString()); javax.imageio.ImageIO.write( javafx.embed.swing.SwingFXUtils.fromFXImage( active.snapshot(null, null), null), "png", output.resolve("failed.png").toFile()); return null; }); var w = new java.io.StringWriter(); e.printStackTrace(new java.io.PrintWriter(w)); Files.writeString(output.resolve("FAILURE.txt"), w.toString()); } catch (Exception ignored) { } } }); } }