javajavafxgluon-mobile

Lookup fails the first time a View is shown


Minimal classes to reproduce the issue:

import static com.gluonhq.charm.glisten.application.AppManager.*;

import javafx.application.Application;
import javafx.stage.Stage;

import com.gluonhq.charm.glisten.application.AppManager;
import com.gpsdemo.view.View1;
import com.gpsdemo.view.View2;

public class MyApplication extends Application {

    AppManager appManager = AppManager.initialize();

    public static final String VIEW1 = HOME_VIEW;
    public static final String VIEW2 = "View2";

    @Override
    public void init() {
        appManager.addViewFactory(VIEW1, View1::get);
        appManager.addViewFactory(VIEW2, View2::get);
    }

    @Override
    public void start(Stage stage) throws Exception {
        appManager.start(stage);
        if (com.gluonhq.attach.util.Platform.isDesktop()) {
            stage.setHeight(600);
            stage.setWidth(360);
            stage.centerOnScreen();
        }
    }

    public static void main(String args[]) {
        launch(args);
    }
}
import javafx.scene.control.Label;

import com.gluonhq.charm.glisten.control.AppBar;
import com.gluonhq.charm.glisten.mvc.View;
import com.gluonhq.charm.glisten.visual.MaterialDesignIcon;
import com.gpsdemo.MyApplication;

public class View1 extends View {

    private static View1 INSTANCE;

    public static View1 get() {
        return INSTANCE != null ? INSTANCE : (INSTANCE = new View1());
    }

    private View1() {
        setCenter(new Label("Nothing to see here"));
    }

    @Override
    protected void updateAppBar(AppBar appBar) {
        appBar.setTitleText("View1");

        var optionsButton = MaterialDesignIcon.MENU.button(e -> getAppManager().switchView(MyApplication.VIEW2));
        appBar.getActionItems().add(optionsButton);
    }
}
import java.util.Set;

import javafx.beans.property.SimpleDoubleProperty;
import javafx.scene.Node;

import com.gluonhq.charm.glisten.control.AppBar;
import com.gluonhq.charm.glisten.control.SettingsPane;
import com.gluonhq.charm.glisten.control.settings.DefaultOption;
import com.gluonhq.charm.glisten.mvc.View;
import com.gluonhq.charm.glisten.visual.MaterialDesignIcon;

public class View2 extends View {

    private static View2 INSTANCE;

    public static View2 get() {
        return INSTANCE != null ? INSTANCE : (INSTANCE = new View2());
    }

    private View2() {
        var settingsPane = new SettingsPane();
        var option = new DefaultOption<>("Title", "Description", "Category", new SimpleDoubleProperty(), true);
        settingsPane.getOptions().add(option);
        setCenter(settingsPane);

        setOnShown(e -> {
            System.out.println("On shown");
            Set<Node> lookup = settingsPane.lookupAll(".secondary-graphic");
            System.out.println(lookup);
        });
    }

    @Override
    protected void updateAppBar(AppBar appBar) {
        appBar.setTitleText("View2");

        var backButton = MaterialDesignIcon.ARROW_BACK.button(e -> getAppManager().switchToPreviousView().get());
        appBar.setNavIcon(backButton);
    }
}

Launch the application normally, View1 will show.

Click on the button to show View2. The first time View2 is loaded the output is

On shown
[]

So the lookup fails in the onShown event.

Click on the back button and then show View2 again. The output is

On shown
[HBox@2c8d8a10[styleClass=secondary-graphic]]

which is correct.

If View2 is set as HOME_VIEW, the lookup will find the nodes correctly on the first onShown event. This looks like a bug to me. Regardless, I would like the lookup to succeed on the first time so I can configure the view correctly.

Using:

<javafx-maven-plugin-version>0.0.8</javafx-maven-plugin-version>
<gluonfx-maven-plugin-version>1.0.14</gluonfx-maven-plugin-version>

<java-version>17</java-version>
<javafx-version>18.0.1</javafx-version>
<charm-version>6.1.0</charm-version>

Solution

  • As mentioned in the comments, the issue is that the SHOWN event does not happen after a css pass is applied, therefore the lookup fails. A fix can be to manually do a css pass before the lookup, inside the SHOWN handler:

    setOnShown(e -> {
        System.out.println("On shown");
        settingsPane.applyCss();
        Set<Node> lookup = settingsPane.lookupAll(".secondary-graphic");
        System.out.println(lookup);
    });
    

    This is confusing because the SHOWN event does not actually happen after the view is fully shown (in which case a css pass has happened). The event also behaves differently than JavaFX's dialog DIALOG_SHOWN event, that does happen after the dialog is fully shown (including a css pass).