I do not appear to have permissions to create new pages, so here is the tutorial I am proposing to put up. Can someone with privileges make a page for it? Also, please give your feedback.
In this tutorial, you will see how you can change colors of a node using Nifty’s list box.
There are 3 entities interacting in this demo: the app, the controller and nifty GUI.
- HelloNiftySelect.java - app wires everything up, displays the box
- HelloNiftySelectController.java - controller receives events from the GUI, and passes them on to the app
- hello-nifty-select-gui.xml - GUI contains the selection screen layout
[java]
package jme3test.helloworld;
import com.jme3.app.SimpleApplication;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.niftygui.NiftyJmeDisplay;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;
import de.lessvoid.nifty.Nifty;
import java.util.HashMap;
import java.util.Map;
public class HelloNiftySelect extends SimpleApplication {
private Nifty nifty;
Material selectedColor;
Map<String, ColorRGBA> colorSelections = new HashMap<String, ColorRGBA>();
public static void main(String[] args) {
HelloNiftySelect app = new HelloNiftySelect();
app.start();
}
public void simpleInitApp() {
colorSelections.put("Red", ColorRGBA.Red);
colorSelections.put("Blue", ColorRGBA.Blue);
createBox();
NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(assetManager,
inputManager,
audioRenderer,
guiViewPort, 2048, 2048);
nifty = niftyDisplay.getNifty();
HelloNiftySelectController controller = new HelloNiftySelectController(this);
nifty.fromXml("Interface/hello-nifty-select-gui.xml", "select", controller);
guiViewPort.addProcessor(niftyDisplay);
flyCam.setEnabled(false);
inputManager.setCursorVisible(true);
}
public void colorSelected(String color) {
selectedColor.setColor(“Color”, colorSelections.get(color));
}
public void doneSelecting() {
nifty.gotoScreen("start");
flyCam.setEnabled(true);
inputManager.setCursorVisible(false);
}
private void createBox() {
Box box = new Box(1,1,1);
Geometry geometry = new Geometry("Box", box);
selectedColor = new Material(assetManager,
"Common/MatDefs/Misc/Unshaded.j3md");
selectedColor.setColor("Color", colorSelections.values().iterator().next());
geometry.setMaterial(selectedColor);
rootNode.attachChild(geometry);
}
}
[/java]
The important parts to pay attention to in the simpleInitApp method are around the controller creation. In the constructor, we pass reference to the app to the controller, so it can notify the app when GUI events are happening. We then pass this controller to nifty, and we tell nifty to start with “select” screen. We also tell nifty to initialize GUI screen layout based on hello-nifty-select-gui.xml. When select screen is displayed, user will need a mouse pointer to interact with the selections, so we disable the flycam and display the mouse.
Now take a look at the two notification methods that the controller will use. First one, colorSelected, tells the box to change colors. New color will be displayed on the screen during the next app update method. Second notification method is called when the done button is clicked. We want to hide the selection screen and return to the game. To hide the selection screen, we tell nifty to display a special empty screen called “start.” We also re-enable the flycam and hide mouse pointer. Now time to look at the controller.
[java]
package jme3test.helloworld;
import de.lessvoid.nifty.Nifty;
import de.lessvoid.nifty.NiftyEventSubscriber;
import de.lessvoid.nifty.controls.ButtonClickedEvent;
import de.lessvoid.nifty.controls.ListBox;
import de.lessvoid.nifty.controls.ListBoxSelectionChangedEvent;
import de.lessvoid.nifty.screen.Screen;
import de.lessvoid.nifty.screen.ScreenController;
import java.util.List;
public class HelloNiftySelectController implements ScreenController {
private final HelloNiftySelect app;
public HelloNiftySelectController(HelloNiftySelect app) {
this.app = app;
}
public void bind(Nifty nifty, Screen screen) {
ListBox theBox = screen.findNiftyControl("colorSelectionBox", ListBox.class);
for (String color : app.colorSelections.keySet()) {
theBox.addItem(color);
}
}
@NiftyEventSubscriber(id = "colorSelectionBox")
public void onMyListBoxSelectionChanged(final String id, final ListBoxSelectionChangedEvent<String> event) {
List<String> selection = event.getSelection();
app.colorSelected(selection.get(0));
}
@NiftyEventSubscriber(id = "doneButton")
public void onDoneButtonClicked(final String id, final ButtonClickedEvent event) {
app.doneSelecting();
}
public void onStartScreen() {
}
public void onEndScreen() {
}
}
[/java]
Bind method gets called by nifty when it initializes the screen. Nifty knows which controller class to bind to which screen, because it is specified in the screen layout xml file (see below). It also knows to bind the specific instance of the controller, because we passed it in the fromXml method in the app. Next, controller declares that it would like to be notified when list box “colorSelectionBox” selection changes, and when the done button is clicked.
Finally, here is how the screen is laid out in the hello-nifty-select-gui.xml file:
[xml]
<?xml version=“1.0” encoding=“UTF-8”?>
<nifty xmlns=“http://nifty-gui.sourceforge.net/nifty-1.3.xsd” xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance” xsi:schemaLocation=“http://nifty-gui.sourceforge.net/nifty-1.3.xsd http://nifty-gui.sourceforge.net/nifty-1.3.xsd”>
<useStyles filename=“nifty-default-styles.xml” />
<useControls filename=“nifty-default-controls.xml” />
<screen id="select" controller="jme3test.helloworld.HelloNiftySelectController">
<layer childLayout="center">
<panel id="panel" height="20%" width="30%" align="right" valign="top" childLayout="center" visibleToMouse="true">
<control name="label" text="Select cube color" align="left" valign="top"/>
<control id="colorSelectionBox" width="60%" height="90%" align="left"
valign="center" name="listBox" vertical="optional" horizontal="optional"
displayItems="3" selectionMode="Single"
forceSelection = "true" />
<control id="doneButton" name="button" label="Done" align = "left" valign="bottom" />
</panel>
</layer>
</screen>
<screen id="start">
<layer id="baseLayer" childLayout="center">
</layer>
</screen>
</nifty>
[/xml]
We tell nifty that we will have two screens: one for selecting, and one for hiding. “Start” is a special screen that is required by the nifty framework, and here we use it to put nifty in idle state while we return main interaction to the game. While hiding nifty screens could also be accomplished by using nifty’s exit method, with the current version of the framework, you will save yourself from problems if you just follow the pattern in this tutorial.
Next, note that the select screen needs to specify the fully qualified name of the controller class. When fromXml method is called, nifty will pair up screens to controllers based on this assignments. If no instances of controllers are passed in, nifty will construct them automatically. As controllers are paired to screens, each controller is registered for event notifications and then bind method is called.
Another thing to point out is that the containing panel has “visibleToMouse” property set. If this property is not set, the contained controls will not respond to mouse events. Finally, note the control ids: doneButton and colorSelectionBox. These are strings that nifty uses to connect controller to the screen elements, the button and the listbox.
As you see, even a simple GUI interaction is a fairly complex matter to wire up. As interaction complexity grows, you will wonder how to split up responsibilities between classes and where to put certain logic. Most GUI developers follow a Model-View-Controller pattern to help them make these decisions.
References:
Nifty controls page
Nifty API
Nifty controls API
Model View Controller pattern