gluonhq / scenebuilder

Scene Builder is a visual, drag 'n' drop, layout tool for designing JavaFX application user interfaces.
https://gluonhq.com/products/scene-builder/
Other
736 stars 219 forks source link

Dynamic scene size breaks scene builder #327

Open delvh opened 3 years ago

delvh commented 3 years ago

If a project uses dynamic scene sizes by using the current screen, i.e.

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.stage.Screen?>
<GridPane maxHeight="-Infinity" maxWidth="-Infinity"
    minHeight="400.0" minWidth="500.0"
    prefWidth="${screen.visualBounds.width}"
    prefHeight="${screen.visualBounds.height}"
    xmlns="http://javafx.com/javafx/11.0.1"
    xmlns:fx="http://javafx.com/fxml/1">
    <fx:define>
        <Screen fx:factory="getPrimary" fx:id="screen" />
    </fx:define>
</GridPane>

scene builder will refuse to load the fxml file, or even crash when called from for example the e(fx)clipse plugin for Eclipse.

JavaFX however will accept the fxml file and run as intended.

Additionally, on some platforms it appears that after such a file has been encountered, scene builder is unable to terminate when closed. Only after manually killing SceneBuilder (i.e. with SIGKILL), can you reopen it. This is bad because it seems that only one instance of SceneBuilder can be open, resulting in effectively a dead program.

Expected Behavior

Scene builder should be able to load the file, at best with the size as intended.

Current Behavior

Scene builder does not load the file when opened using internal Open button, or does not start at all when called externally, i.e. through the e(fx)clipse plugin for Eclipse.

Steps to Reproduce

Extract the code given above into an fxml file and attempt opening it in SceneBuilder: Scene builder will not open the given file. If you try running it with pure JavaFX, it should work.

Your Environment

Has been observed on many Linux platforms, including Ubuntu 18.04, Kubuntu 20.04, Gentoo stable, Arch 1.4.

Screenshots

Not relevant, as simply nothing happens.

Oliver-Loeffler commented 2 years ago

This is related to #402, with PR #403 an error message will be shown to the user. There is the following stack trace then:

java.lang.IllegalStateException: Bug in FXOMRefresher: FXML dumped in /var/folders/kd/22d474z97yx97rgj2ttf_c3r0000gn/T/DTL-5996-6816594968554613127.fxml
    at com.oracle.javafx.scenebuilder.kit.fxom.FXOMRefresher.refresh(FXOMRefresher.java:96)
    at com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument.refreshSceneGraph(FXOMDocument.java:329)
    at com.oracle.javafx.scenebuilder.kit.fxom.FXOMNormalizer.normalize(FXOMNormalizer.java:110)
    at com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument.<init>(FXOMDocument.java:94)
    at com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument.<init>(FXOMDocument.java:108)
    at com.oracle.javafx.scenebuilder.kit.editor.EditorController.updateFxomDocument(EditorController.java:2560)
    at com.oracle.javafx.scenebuilder.kit.editor.EditorController.setFxmlTextAndLocation(EditorController.java:763)
    at com.oracle.javafx.scenebuilder.app.DocumentWindowController.loadFromFile(DocumentWindowController.java:389)
    at com.oracle.javafx.scenebuilder.app.SceneBuilderApp.performOpenFiles(SceneBuilderApp.java:668)
    at com.oracle.javafx.scenebuilder.app.SceneBuilderApp.performOpenFile(SceneBuilderApp.java:604)
    at com.oracle.javafx.scenebuilder.app.SceneBuilderApp.performControlAction(SceneBuilderApp.java:206)
    at com.oracle.javafx.scenebuilder.app.menubar.MenuBarController$ApplicationControlActionController.perform(MenuBarController.java:1778)
    at com.oracle.javafx.scenebuilder.app.menubar.MenuBarController.handleOnActionMenu(MenuBarController.java:1222)
    at com.oracle.javafx.scenebuilder.app.menubar.MenuBarController.lambda$new$4(MenuBarController.java:1216)
    at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
    at javafx.base/javafx.event.Event.fireEvent(Event.java:198)
    at javafx.controls/javafx.scene.control.MenuItem.fire(MenuItem.java:459)
    at javafx.controls/com.sun.javafx.scene.control.GlobalMenuAdapter.lambda$bindMenuItemProperties$2(GlobalMenuAdapter.java:153)
    at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
    at javafx.base/javafx.event.Event.fireEvent(Event.java:198)
    at javafx.controls/javafx.scene.control.MenuItem.fire(MenuItem.java:459)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassSystemMenu$1.action(GlassSystemMenu.java:234)
Caused by: java.io.IOException: javafx.fxml.LoadException: Invalid attribute.
/Volumes/Daten/Temp/broken.fxml:7

    at com.oracle.javafx.scenebuilder.kit.fxom.FXOMLoader.handleUnknownAndMissingCauses(FXOMLoader.java:111)
    at com.oracle.javafx.scenebuilder.kit.fxom.FXOMLoader.handleFxmlLoadingError(FXOMLoader.java:106)
    at com.oracle.javafx.scenebuilder.kit.fxom.FXOMLoader.load(FXOMLoader.java:98)
    at com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument.<init>(FXOMDocument.java:91)
    at com.oracle.javafx.scenebuilder.kit.fxom.FXOMRefresher.refresh(FXOMRefresher.java:67)
    ... 33 more
Caused by: javafx.fxml.LoadException: Invalid attribute.
/Volumes/Daten/Temp/broken.fxml:7

    at javafx.fxml/javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2703)
    at javafx.fxml/javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:948)
    at javafx.fxml/javafx.fxml.FXMLLoader$Element.processStartElement(FXMLLoader.java:230)
    at javafx.fxml/javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:755)
    at javafx.fxml/javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2808)
    at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2634)
    at javafx.fxml/javafx.fxml.FXMLLoader.load(FXMLLoader.java:2532)
    at com.oracle.javafx.scenebuilder.kit.fxom.FXOMLoader.load(FXOMLoader.java:96)
    ... 35 more
Oliver-Loeffler commented 2 years ago

It seems that the error occurs when the FXOMNormalizer is applied to the FXOMDocument. Immediately after loading, the FXML text generated by FXOMDocument looks as follows:

After loading the FXML:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.GridPane?>

<GridPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="400.0" minWidth="500.0" prefHeight="${screen.visualBounds.height}" prefWidth="${screen.visualBounds.width}" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1">
    <fx:define>
        <Screen fx:id="screen" fx:factory="getPrimary" />
    </fx:define>
</GridPane>

FXML with normalization applied:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.GridPane?>

<GridPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="400.0" minWidth="500.0" prefHeight="${screen.visualBounds.height}" prefWidth="${screen.visualBounds.width}" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1">
    <fx:define>
        <Screen fx:id="screen" fx:factory="getPrimary" />
    </fx:define>
</GridPane>

Interestingly the normalization process does not change the FXML text. However, the refresh step causes the exception.

public void normalize() {
        changeCount = 0;
        normalizeExpandedPaneProperties();
        normalizeGridPanes();
        if (changeCount >= 1) {
            // the exception is caused here, this task is delegated to the FXOMRefresher
            fxomDocument.refreshSceneGraph();
        }
    }

The FXOMRefresher basically creates a new FXOMDocument. Here, the FXMLLoader raises the exception. I think, that is is caused by the actually missing import declaration <?import javafx.stage.Screen?> in the source FXML text. The import is basically gone after opening the original FXML and saving it.

So the cause of the trouble seems to be the FXMLDocument loading process which removes essentially the required imports. Without the import, the FXMLLoader tries to resolve what is defined in <fx:define> and will fail there, as the referenced classes will not be available.

<fx:define>
    <Screen fx:factory="getPrimary" fx:id="screen" />
</fx:define>

In a very similar form, this is described in other issues such as:

I have no clear idea yet, how this can be solved but in my opinion, but at least the user should get an appropriate notification. Solving this would potentially help to close a number of issues, guess there are more than just those 4 here.