Mastodon

JavaFX Series - Hover-Panes

This article is part of my JavaFX Series. Get the preceding articles here.

After setting up the basic world in which the simulation takes place, I decided to tweak the user interface a little bit. As you can see below, there are context menus at the resource spawners that allow for manipulation of the radius in which new resources are being spawned. The new objects now grow into existence by using a FadeTransition and a ScaleTransition.

SimFX Hover Panes

Get the executable jar here.

Using JavaFX

After using the framework for a couple of hours, I’m positively suprised. JavaFX has a well designed API that allows for intuitive programming. Often I find myself thinking “He, I want to do X. There should be a method at this object here - great, there it is!”. Also ,the use of varargs minimizes boiler plate code. For example, to add some more objects to the root group of objects, I could either do this:

Node n1 = getN1();
Node n2 = getN2();
Node n3 = getN3();

rootGroup().getChildren().add(n1);
rootGroup().getChildren().add(n2);
rootGroup().getChildren().add(n3);

… or I simply use the varargs-method:

Node n1 = getN1();
Node n2 = getN2();
Node n3 = getN3();

rootGroup().getChildren().addAll(n1, n2, n3);

Of course, diving into a new framework without having read a single tutorial has its downsides. I wanted to add a listener to the resource spawners to listen for a mouse over event. I found the known method addEventHandler and a new method addEventFilter According to my namesake Steven Yang, the difference between them is that filters are executed before handlers. A small, but important detail.

Context Menus

The two resource spawners now have a context menu that allows for setting the radius in which new resources are being spawned. You can see one context menu at the picture above. Adding them is very similar to adding listeners for Swing components:

this.addEventFilter(MouseEvent.MOUSE_ENTERED, new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
   // this is just the red circle marking the radius for this spawner
   JavaFxApplication.getInstance().getRootGroup().getChildren().add(radiusCircle);

        // mouseOverPane is an instance variable
        if (mouseOverPane == null)
            mouseOverPane = new MouseOverPane(thisResourceSpawnerFX);
 
      	// Possible that the pane is already added because the
        // mouse hovered over this spawner and the pane didn't
        // get removed after the mouse left.
        if(!JavaFxApplication.getInstance().getRootGroup().getChildren().contains(mouseOverPane)) {
            JavaFxApplication.getInstance().getRootGroup().getChildren().add(mouseOverPane);
        }
 
        // after instanciating the pane, there shall be a nice animation opening it
        mouseOverPane.getAnimationAppear().playFromStart();
    }
});

The pane itself is a subclass of TitledPane and provides two ScaleTransitions for the appearance and the disappearance of the pane. The first is called when opening the pane, see code above, the second is called from within the pane itself:

addEventFilter(MouseEvent.MOUSE_EXITED, new EventHandler<MouseEvent>() {

    @Override
    public void handle(MouseEvent event) {
        // first let the animation finish
        animationDisappear.setOnFinished(new EventHandler<ActionEvent>() {
 
	    @Override
	    public void handle(ActionEvent event) {
                // remove the pane from the root group of nodes
                JavaFxApplication.getInstance().getRootGroup().getChildren().remove(createdPane);
	    }
	});
 
	JavaFxApplication.getInstance().getRootGroup().getChildren().remove(resourceSpawnerFX.getRadiusCircle());
 
	animationDisappear.play();
    }
});

Transitions

Not only the context menu has a nice animation, also the resources now fade into the world and grow. This is done by a FadeTransition and a ScaleTransition:

public class ResourceFX extends Polygon {

	private Resource representedResource;
 
	public ResourceFX(final Resource representedRessource) {
 
		// these numnbers represent the polygon that shapes the resource
		super(new double[] { 0, 0, 10, 10, 20, 0, 20, 10, 30, 20, 20, 20, 20,
				30, 10, 30, 0, 20, 10, 20 });
 
		this.representedResource = representedRessource;
 
		this.setFill(Color.CORAL);
		setLayoutX(representedRessource.getPosition().getX());
		setLayoutY(representedRessource.getPosition().getY());
 
		FadeTransition ft = new FadeTransition(Duration.millis(3000), this);
		ft.setFromValue(0.1);
		ft.setToValue(1.0);
		ft.play();
 
		ScaleTransition st = new ScaleTransition(Duration.millis(3000), this);
		st.setFromX(0);
		st.setToX(1);
		st.setFromY(0);
		st.setToY(1);
		st.play();
	}
}

Once again, I find JavaFX code quite intuitive.

Get the Code

You can get the code in my repository on github. The codebase for this article is tagged as Codebase_HoverPanes.

Next Steps

Having had first contact with forms, I wonder if larger panes with more form components handle as well as the small ones did. Because most of the enterprise applications are heavily form-based, handling them in a good way is essential for becoming widly accepted. On the other hand, transitions are also easy to handle which makes me think if I should go on introducing the bases / cities / settlements of the culture that harvests the resources. They could send little harvesters to get the resources. Or should they beam them into their base? What do you think?