Yeah my windows are pretty flexible and dynamic. I would say using builders instead of XML is primarily personal preference, but it also affords me a lot of flexibility in creating my Nifty Elements since, within the builders, I can use if statements, while/for loops, callbacks, etc…
Anyway, I’m not particularly familiar with creating layouts with XML in NiftyGUI so you might need to elicit the support of someone else in that area; however, I did have a thought that might help you out. Have you considered using XML to define custom controls rather than layouts. A custom control is a reusable collection of Nifty Elements that you can use in your layouts like you would a button or a panel.
Here’s an example of a quick and dirty custom control I made for my project, a number picker control. First we have the XML control definition file:
<?xml version="1.0" encoding="UTF-8"?>
<nifty-controls>
<controlDefinition name="number-picker" style="number-picker" controller="carpeDiem.UI.NiftyControls.NumberPickerControl">
<panel childLayout="horizontal">
<text id="#text" style="#text" text="$value" />
<panel style="#upButton" id="#upButton" controller="carpeDiem.UI.NiftyControls.NumberPickerButtonControl" inputMapping="de.lessvoid.nifty.input.mapping.MenuInputMapping">
<interact onClick="onClick()" />
</panel>
<panel style="#downButton" id="#downButton" controller="carpeDiem.UI.NiftyControls.NumberPickerButtonControl" inputMapping="de.lessvoid.nifty.input.mapping.MenuInputMapping">
<interact onClick="onClick()" />
</panel>
</panel>
</controlDefinition>
</nifty-controls>
Next we have the XML style definition file:
<?xml version="1.0" encoding="UTF-8"?>
<nifty-styles>
<style id="number-picker#text">
<attributes font="Interface/Fonts/SpaceAge_16.fnt" width="*" align="center" valign="center" textHAlign="center" textVAlign="center" visibleToMouse="false" color="#28ff9c"/>
</style>
<style id="number-picker#upButton">
<attributes backgroundImage="Interface/UI/Buttons/Button_ArrowUp.png" imageMode="sprite:18,18,0" childLayout="center" visibleToMouse="true" focusable="false" width="18px" height="18px" />
<effect>
<onHover name="imageOverlay" filename="Interface/UI/Buttons/Button_ArrowUp.png" imageMode="sprite:18,18,1" post="true" />
<onClick name="imageOverlay" filename="Interface/UI/Buttons/Button_ArrowUp.png" imageMode="sprite:18,18,2" post="true" />
</effect>
</style>
<style id="number-picker#downButton">
<attributes backgroundImage="Interface/UI/Buttons/Button_ArrowDown.png" imageMode="sprite:18,18,0" childLayout="center" visibleToMouse="true" focusable="false" width="18px" height="18px" />
<effect>
<onHover name="imageOverlay" filename="Interface/UI/Buttons/Button_ArrowDown.png" imageMode="sprite:18,18,1" post="true" />
<onClick name="imageOverlay" filename="Interface/UI/Buttons/Button_ArrowDown.png" imageMode="sprite:18,18,2" post="true" />
</effect>
</style>
</nifty-styles>
And finally we have two control classes, the first one for the Number Picker control:
package carpeDiem.UI.NiftyControls;
import de.lessvoid.nifty.Nifty;
import de.lessvoid.nifty.controls.AbstractController;
import de.lessvoid.nifty.controls.Parameters;
import de.lessvoid.nifty.elements.Element;
import de.lessvoid.nifty.elements.render.TextRenderer;
import de.lessvoid.nifty.input.NiftyInputEvent;
import de.lessvoid.nifty.screen.Screen;
/**
*
* @author Adam T. Ryder http://1337atr.weebly.com
*/
public class NumberPickerControl extends AbstractController {
private TextRenderer textRenderer;
private int value;
private int max;
private int min;
@Override
public void bind(final Nifty niftyParam, final Screen screenParam, final Element newElement, final Parameters parameter) {
super.bind(newElement);
textRenderer = getElement().findElementById("#text").getRenderer(TextRenderer.class);
max = Integer.parseInt(parameter.getProperty("max", "10"));
min = Integer.parseInt(parameter.getProperty("min", "0"));
value = Integer.parseInt(parameter.getProperty("value", Integer.toString(min)));
value = (value > max) ? max : (value < min) ? min : value;
textRenderer.setText(Integer.toString(value));
}
public int getValue() {
return value;
}
public void setValue(final int value) {
this.value = (value > max) ? max : (value < min) ? min : value;
textRenderer.setText(Integer.toString(this.value));
}
public void setMax(final int maxValue) {
max = maxValue;
min = (max < min) ? max : min;
if (value > max) { setValue(max); }
}
public int getMax() {
return max;
}
public void setMin(final int minValue) {
min = minValue;
max = (max < min) ? min : max;
if (value < min) { setValue(min); }
}
public int getMin() {
return min;
}
@Override
public void init(final Parameters parameter) {
}
@Override
public void onStartScreen() {
}
@Override
public boolean inputEvent(final NiftyInputEvent inputEvent) {
return false;
}
}
and the second one for the Number Picker buttons:
package carpeDiem.UI.NiftyControls;
import carpeDiem.UI.TooltipManager;
import de.lessvoid.nifty.Nifty;
import de.lessvoid.nifty.controls.AbstractController;
import de.lessvoid.nifty.controls.Parameters;
import de.lessvoid.nifty.elements.Element;
import de.lessvoid.nifty.input.NiftyInputEvent;
import de.lessvoid.nifty.input.NiftyStandardInputEvent;
import de.lessvoid.nifty.screen.Screen;
/**
*
* @author Adam T. Ryder http://1337atr.weebly.com
*/
public class NumberPickerButtonControl extends AbstractController {
private NumberPickerControl picker;
private boolean upButton;
@Override
public void bind(final Nifty niftyParam, final Screen screenParam, final Element newElement, final Parameters parameter) {
super.bind(newElement);
picker = getElement().getParent().getParent().getControl(NumberPickerControl.class);
upButton = getElement().getId().endsWith("#upButton");
}
@Override
public void init(final Parameters parameter) {
}
@Override
public void onStartScreen() {
}
@Override
public boolean inputEvent(final NiftyInputEvent inputEvent) {
if (inputEvent == NiftyStandardInputEvent.Activate) {
TooltipManager.clear();
if (upButton) {
if (picker.getValue() < picker.getMax()) {
picker.setValue(picker.getValue() + 1);
}
} else {
if (picker.getValue() > picker.getMin()) {
picker.setValue(picker.getValue() - 1);
}
}
return true;
}
return false;
}
public boolean onClick() {
TooltipManager.clear();
if (upButton) {
if (picker.getValue() < picker.getMax()) {
picker.setValue(picker.getValue() + 1);
}
} else {
if (picker.getValue() > picker.getMin()) {
picker.setValue(picker.getValue() - 1);
}
}
return true;
}
}
Now the above controls were created for use with NiftyGUI 1.3.3, but recently fudged a bit to work with NiftyGUI 1.4.1 so if you’re using NiftyGUI 1.3.3 there are some very minor differences so you’ll want to do a search on creating custom controls with NiftyGUI for additional information, also I would encourage you to download the NiftyGUI source code and take a look at some of the control definitions and controller classes for some of the default controls to get a better idea of how you want to create yours.
I don’t know exactly how you want to display your windows, but my thought was that you might have an XML layout for a window that’s basically a panel that includes your custom control which displays all the necessary information for the units in your game. When you instantiate your window you might do something like:
MyCustomControl unitDisplay = MyNewWindowElement.getControl(MyCustomControl.class);
unitDisplay.populateInfo(instanceOfMyUnit);
Then your populateInfo() method in the custom controller class would look at the information associated with the unit you pass in and populate whatever fields need to be populated with that information.