= Intro =
I want a screen with a starup menu for “play / continue” “story” “reset” “quit”
If the player clicks on “reset” or “quit” I want a confirmation to pop up.
I have implemeted these as layers that are hidden until they are needed - only one layer is visible at a time.
= Problem =
When I switch which layer is visible - the button focus stuff
doesn’t get activated … or something.
Here’s a video
http://www.youtube.com/watch?v=6TSA2OzAWNw
When the screen with the blue background comes up the buttons don’t highlight
as the mouse goes over them
Here’s the code from my controller:
[java]
private String _lastLayer = null;
public void changeLayer(final String newLayerId) {
final Screen currentScreen = (_niftyDisplay).getNifty().getCurrentScreen();
// hide the last layer
if ( _lastLayer != null ) {
final Element layer = currentScreen.findElementByName(_lastLayer);
layer.hide();
}
// end now if we’re not really doing anything
if ( newLayerId == null || newLayerId.equals("<null>") ) {
_lastLayer = null;
return;
}
// set the new layer visible and record it as the last layer
_lastLayer = newLayerId;
final Element layer = currentScreen.findElementByName(_lastLayer);
layer.show();
}
[/java]
Here are my layers:
[xml]
<layer visible=‘false’ id=‘start.confirmQuit’ backgroundColor="#00000000" childLayout=“center”>
<panel id=‘start.confirmQuit.panel0’ align=“center” valign=“center”
backgroundColor="#5599ffff" childLayout=“vertical” height=“40%”
width=“40%”>
<text font=“console.fnt” text=“Do you really want to quit?” align=“center”
valign=“center” />
<panel id=‘start.confirmQuit.panel0.panelYes’ align=“left” valign=“bottom”
backgroundColor="#ffff00ff" childLayout=“vertical” height=“40%”
width=“40%”>
<control name=“button” label=“Yes, quit” align=“center”>
<interact onClick=“raiseEvent(gui.quit)” />
</control>
</panel>
<panel id=‘start.confirmQuit.panel0.panelNo’ align=“right” valign=“bottom”
backgroundColor="#ff0000ff" childLayout=“vertical” height=“40%”
width=“40%”>
<control name=“button” label=“No, do not quit” align=“right”>
<interact onClick=“changeLayer(start.gui)” />
</control>
</panel>
</panel>
</layer>
<layer visible=‘false’ id=“start.gui” backgroundColor="#00000000" childLayout=“center”>
<panel id=‘start.gui.panel0’ align=“center” valign=“bottom”
backgroundColor="#ffffff99" childLayout=“vertical” height=“80%”
width=“55%”>
<text font=“console.fnt” text=“Hello World!” align=“center”
valign=“center” />
<control name=“button” label=“Play” align=“center”>
<interact onClick=“raiseEvent(gui.play)” />
</control>
<control name=“button” label=“Story” align=“center”>
<interact onClick=“raiseEvent(gui.story)” />
</control>
<control name=“button” label=“Reset” align=“center”>
<interact onClick=“changeLayer(start.confirmReset)” />
</control>
<control name=“button” label=“Quit” align=“center”>
<interact onClick=“changeLayer(start.confirmQuit)” />
</control>
</panel>
</layer>
<layer visible=‘false’ id=‘start.confirmReset’ backgroundColor="#00000000" childLayout=“center”>
<panel id=‘start.confirmReset.panel0’ align=“center” valign=“center”
backgroundColor="#5599ffff" childLayout=“vertical” height=“40%”
width=“40%”>
</panel>
</layer>
[/xml]
I have a similar problem.
What I understood is :
You can’t hide a panel wich contains the current focus otherwise Nifty loose where the focus is and you can’t click on buttons anymore.
This is bothersome. I don’t remember if I saw a post on the tracker for that.
There is a work around :
- First add some identifer to your buttons :
[java]
<layer visible=‘false’ id=“start.gui” backgroundColor="#00000000" childLayout=“center”>
<panel id=‘start.gui.panel0’ align=“center” valign=“bottom”
backgroundColor="#ffffff99" childLayout=“vertical” height=“80%”
width=“55%”>
<text font=“console.fnt” text=“Hello World!” align=“center”
valign=“center” />
<control id=“start.gui.play” name=“button” label=“Play” align=“center”>
<interact onClick=“raiseEvent(gui.play)” />
</control>
<control id=“start.gui.story” name=“button” label=“Story” align=“center”>
<interact onClick=“raiseEvent(gui.story)” />
</control>
<control id=“start.gui.confirmReset” name=“button” label=“Reset” align=“center”>
<interact onClick=“changeLayer(start.confirmReset)” />
</control>
<control id=“start.gui.confirmQuit” name=“button” label=“Quit” align=“center”>
<interact onClick=“changeLayer(start.confirmQuit)” />
</control>
</panel>
</layer>
[/java]
- Then in your screencontroller, fill the bind(…) method :
[java]
@Override
public final void bind( Nifty nifty, Screen screen )
{
screen.findElementByName(“start.gui.play”).setFocusable( false );
screen.findElementByName(“start.gui.story”).setFocusable( false );
screen.findElementByName(“start.gui.confirmReset”).setFocusable( false );
screen.findElementByName(“start.gui.confirmQuit”).setFocusable( false );
}
[/java]
You won’t have focus problems anymore but you won’t be able to use the keybords key to select the buttons.
Yuck … w00+ w00+ it works!
I’m using this:
EDIT this may be really, broken - it seems to be swapping the resetConfim interaction for the quit confirm one … but I’m out of time
[java]
// use an XPath statement to automate this and complain for all controls that lack an ID value
try {
final DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
builderFactory.setNamespaceAware(true);
final Document parsedDocument = builderFactory.newDocumentBuilder().parse(getClass().getResourceAsStream("/"+_file));
final XPath newXPath = XPathFactory.newInstance().newXPath();
// TODO : this does not work right
if ( ((NodeList)newXPath.compile("//control[not(@id)]").evaluate(parsedDocument, XPathConstants.NODESET)).getLength() > 0 ) {
throw new IllegalArgumentException(“All control tags must have ids”);
}
final NodeList nodeList = (NodeList) newXPath.compile("//control/@id")
.evaluate(parsedDocument, XPathConstants.NODESET);
for (int i = 0; i < nodeList.getLength(); i++) {
screen.findElementByName(nodeList.item(i).getNodeValue()).setFocusable(false);
}
} catch (XPathExpressionException e) {
// TODO Auto-generated catch block
throw new RuntimeException(“TODO Auto-generated catch block”, e);
} catch (SAXException e) {
// TODO Auto-generated catch block
throw new RuntimeException(“TODO Auto-generated catch block”, e);
} catch (IOException e) {
// TODO Auto-generated catch block
throw new RuntimeException(“TODO Auto-generated catch block”, e);
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
throw new RuntimeException(“TODO Auto-generated catch block”, e);
}
[/java]
Personally I created a MessagePanel control for just this. This control creates a pop-up on the screen in the form of a panel, not a layer. At the moment this panel needs to be defined on the screen itself, but I am going to change this to use the new builder patter so I can add and remove it on the fly. This would make for a much cleaner solution since it won’t require you to add anything to your screen XML to be able to use it.
control xml
implementation in a page:
[xml]
<panel id=“error” childLayout=“horizontal” align=“center” width=“250px” height=“100px”>
<panel childLayout=“horizontal” height=“25px” />
<control id=“errorPanel” name=“messagePanel” />
</panel>
[/xml]
controller code
Implementation of the controller:
[java]
Element errorPanel = nifty.getCurrentScreen().findElementByName(“error”);
MessageController errorController = errorPanel.findElementByName(“errorPanel”).getControl(MessageController.class);
errorController.showMessage(“error:” + eMsg.getErrorMessage(), “resources/error.jpg”);
[/java]
This works like a charm for me, although you might need to tweak the controller and the xml a bit since I’m using Nifty 1.3. If you want I should have the Nifty 1.2 version in my svn somewhere as well.
hidden elements will correctly lose focus in Nifty 1.3…
Whoa … this thread seems like months ago
JME’s still using 1.2-SNAPSHOT, how hard would it be to update it to 1.3?
It will happen. Soon.