I created a Lemur element that displays a JFX panel

I would like to display a JFX screen inside a Lemur rollup panel. I am using Lemur and jmejfx2.0.0 at the same time in my game. I was wondering if anyone had created a JFX custom component as a panel in Lemur? If not, I will have to create one from scratch. If I do, is anyone interested in it?

EDIT:

I got tired of waiting so I created a Lemur Element that acts like a Lemur Panel but displays the contents of a .FXML file. If you have a window resize listener in your application just call updateShape() so that the translation and size get refreshed based on the new window size. Enjoy!

IMPORTANT!!! MAKE SURE THE Z ORDER OF YOUR JMEJFX NODE IS IN FRONT OF THE LEMUR COMPONENTS IN THE GUI NODE. I SET MINE TO 100!

import java.io.IOException;
import java.net.URL;

import org.lwjgl.opengl.Display;

import com.jfoenix.concurrency.JFXUtilities;
import com.jme3.app.Application;
import com.jme3.math.Vector3f;
import com.simsilica.lemur.GuiGlobals;
import com.simsilica.lemur.Panel;
import com.simsilica.lemur.component.AbstractGuiComponent;
import com.simsilica.lemur.core.GuiControl;
import com.simsilica.lemur.core.GuiUpdateListener;
import com.simsilica.lemur.core.VersionedReference;

import javafx.fxml.FXMLLoader;
import javafx.scene.Group;
import javafx.scene.layout.AnchorPane;

public class JFXAnchorPane<ControllerClass> extends Panel
{

	private  class JFXAnchorPaneComponent<T extends Object> extends AbstractGuiComponent
	{
		public T getController()
		{
			return controller;
		}

		private AnchorPane anchorPane;

		private T controller;

		private Group jfxGroup;

		@SuppressWarnings("unchecked")
		public JFXAnchorPaneComponent(URL fxmlLocation, Group jfxGroup)
		{
			FXMLLoader loader = new FXMLLoader(fxmlLocation);
			try
			{
				anchorPane = loader.load();
				controller = (T) loader.getController();
			} catch (IOException e)
			{
				e.printStackTrace();
			}

			this.jfxGroup = jfxGroup;

		}

		@Override
		public void calculatePreferredSize(Vector3f size)
		{
			size.setX((float) anchorPane.getPrefWidth());
			size.setY((float) anchorPane.getPrefHeight());

		}

		public void privateReshape()
		{
			JFXUtilities.runInFXAndWait(new Runnable()
			{

				@Override
				public void run()
				{
					Vector3f screenPos = getNode().getWorldTranslation();
					Vector3f size = getSize();
					anchorPane.setLayoutX(screenPos.getX());
					anchorPane.setLayoutY(Display.getHeight() - screenPos.getY());
					anchorPane.setPrefWidth(size.getX());
					anchorPane.setPrefHeight(size.getY());
				}
			});

		}

		@Override
		public void detach(GuiControl parent)
		{
			super.detach(parent);
			JFXUtilities.runInFX(() -> {
				jfxGroup.getChildren().remove(anchorPane);
			});
		}

		@Override
		public void attach(GuiControl parent)
		{
			// TODO Auto-generated method stub
			super.attach(parent);
			JFXUtilities.runInFX(() -> {
				jfxGroup.getChildren().add(anchorPane);
			});

		}

		@Override
		public void reshape(Vector3f pos, Vector3f size)
		{
		}

	}

	public ControllerClass getController()
	{
		return component.getController();
	}

	private JFXAnchorPaneComponent<ControllerClass> component;

	public JFXAnchorPane(URL fxmlLocation, Group jfxGroup)
	{
		super();
		component = new JFXAnchorPaneComponent<ControllerClass>(fxmlLocation, jfxGroup);
		setBackground(component);

	}

	/**
	 * This will update the translation and size of the JFX component based on
	 * the current layout and window size of the {@link Application}.
	 */
	public void updateShape()
	{
		component.privateReshape();
	}

	private Vector3f oldTranslation = new Vector3f();

	private Vector3f oldSize = new Vector3f();

	@Override
	public void updateLogicalState(float tpf)
	{
		super.updateLogicalState(tpf);
		if(getWorldTranslation().equals(oldTranslation) == false || getSize().equals(oldSize) == false)
		{

			updateShape();
			oldTranslation.set(getWorldTranslation());
			oldSize.set(getSize());
		}
	}

	

}
2 Likes

I’m interested. I imagine it’s not too difficult.

1 Like

I was impatient and created it in 20 minutes. There is a small refresh bug on window resizing but it’s functional. Once I fix the bug I will post the code.

Here is a screenshot of a test of a JFX gague component in a Lemur rollup panel.

image

1 Like

I finished coding and testing the element and posted the code up in the original post. Enjoy!

1 Like

I think If you override updateGeometricState() or whatever it’s called and call super and then your resize fix method it should solve your issue.

1 Like

reshaping the element every time is inefficient. It’s easier to setup a listener on the GUI viewport and call the updateShape() method on the JFXAnchorPane when the window is resized.