Here’s a minimal example:
import com.jme3.app.SimpleApplication;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Command;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.GuiGlobals;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.component.IconComponent;
import com.simsilica.lemur.component.SpringGridLayout;
import com.simsilica.lemur.style.BaseStyles;
public class Test extends SimpleApplication {
public static void main( String... args ) {
Test main = new Test();
main.start();
}
protected String styleLocation = "Interface/Style/base.groovy";
protected String defaultStyleName = "base";
protected String title = "Title";
protected String startButtonText = "Start";
protected String settingsButtonText = "Settings";
protected String quitButtonText = "Quit";
protected String backgroundImageLocation = "Interface/Background/Spacebackground.png";
protected Vector2f backgroundImageMargin = new Vector2f(0,0);
protected float backgroundImageScale = 1f;
protected Container rootNode;
protected IconComponent backgroundImage;
protected Label titleLabel;
protected Container buttonContainer;
protected Button startButton;
protected Button settingsButton;
protected Button quitButton;
@Override
public void simpleInitApp() {
GuiGlobals.initialize(this);
BaseStyles.loadStyleResources(styleLocation);
GuiGlobals.getInstance().getStyles().setDefaultStyle(defaultStyleName);
Vector3f cameraDimensions = new Vector3f(getCamera().getWidth(), getCamera().getHeight(),0);
Vector3f cameraCenter = cameraDimensions.mult(0.5f);
backgroundImage = new IconComponent( backgroundImageLocation, backgroundImageScale, backgroundImageMargin.x, backgroundImageMargin.y, 1f, false );
//Top sets background image here from code
// rootNode = new Container(new SpringGridLayout());
//Uncomment above and comment below to disable background image and see menu
// rootNode.setBackground(backgroundImage);
//!Top
//Bottom sets background image from style
rootNode = new Container(new SpringGridLayout(),"backgroundImage");
//!Bottom
Vector3f pco = rootNode.getPreferredSize().mult(0.5f);
Vector3f trans = new Vector3f(cameraCenter.x - pco.x, cameraCenter.y + pco.y ,0);
rootNode.setLocalTranslation(trans);
rootNode.setPreferredSize(new Vector3f(getCamera().getWidth(), getCamera().getHeight(), 1));
titleLabel = new Label(title,"title");
rootNode.addChild(titleLabel);
buttonContainer = new Container(new SpringGridLayout());
startButton = new Button(startButtonText);
buttonContainer.addChild(startButton);
settingsButton = new Button(settingsButtonText);
buttonContainer.addChild(settingsButton);
quitButton = new Button(quitButtonText);
quitButton.addClickCommands((Command<Button>) (Button source) -> {
stop();
});
buttonContainer.addChild(quitButton);
rootNode.addChild(buttonContainer);
getGuiNode().attachChild(rootNode);
}
}
And here’s the style (glass-style.groovy but “glass” was replaced by “base” and a couple selectors added):
import com.simsilica.lemur.*;
import com.simsilica.lemur.Button.ButtonAction;
import com.simsilica.lemur.component.*;
def gradient = TbtQuadBackgroundComponent.create(
texture( name:"/com/simsilica/lemur/icons/bordered-gradient.png",
generateMips:false ),
1, 1, 1, 126, 126,
1f, false );
def bevel = TbtQuadBackgroundComponent.create(
texture( name:"/com/simsilica/lemur/icons/bevel-quad.png",
generateMips:false ),
0.125f, 8, 8, 119, 119,
1f, false );
def border = TbtQuadBackgroundComponent.create(
texture( name:"/com/simsilica/lemur/icons/border.png",
generateMips:false ),
1, 1, 1, 6, 6,
1f, false );
def border2 = TbtQuadBackgroundComponent.create(
texture( name:"/com/simsilica/lemur/icons/border.png",
generateMips:false ),
1, 2, 2, 6, 6,
1f, false );
def doubleGradient = new QuadBackgroundComponent( color(0.5, 0.75, 0.85, 0.5) );
doubleGradient.texture = texture( name:"/com/simsilica/lemur/icons/double-gradient-128.png",
generateMips:false )
def spaceBackground = new IconComponent( "Interface/Background/Spacebackground.png", 1f, 0, 0, -1f, false );
selector( "base" ) {
font = font("Interface/Fonts/menu.fnt");
fontSize = 20
}
selector("backgroundImage") {
background = spaceBackground
}
selector("title") {
font = font("Interface/Fonts/menu-large.fnt");
fontSize = 50
}
selector( "label", "base" ) {
insets = new Insets3f( 2, 2, 0, 2 );
color = color(0.5, 0.75, 0.75, 0.85)
}
selector( "container", "base" ) {
background = gradient.clone()
background.setColor(color(0.25, 0.5, 0.5, 0.5))
}
selector( "slider", "base" ) {
background = gradient.clone()
background.setColor(color(0.25, 0.5, 0.5, 0.5))
}
def pressedCommand = new Command<Button>() {
public void execute( Button source ) {
if( source.isPressed() ) {
source.move(1, -1, 0);
} else {
source.move(-1, 1, 0);
}
}
};
def repeatCommand = new Command<Button>() {
private long startTime;
private long lastClick;
public void execute( Button source ) {
// Only do the repeating click while the mouse is
// over the button (and pressed of course)
if( source.isPressed() && source.isHighlightOn() ) {
long elapsedTime = System.currentTimeMillis() - startTime;
// After half a second pause, click 8 times a second
if( elapsedTime > 500 ) {
if( elapsedTime - lastClick > 125 ) {
source.click();
// Try to quantize the last click time to prevent drift
lastClick = ((elapsedTime - 500) / 125) * 125 + 500;
}
}
} else {
startTime = System.currentTimeMillis();
lastClick = 0;
}
}
};
def stdButtonCommands = [
(ButtonAction.Down):[pressedCommand],
(ButtonAction.Up):[pressedCommand]
];
def sliderButtonCommands = [
(ButtonAction.Hover):[repeatCommand]
];
selector( "title", "base" ) {
color = color(0.8, 0.9, 1, 0.85f)
highlightColor = color(1, 0.8, 1, 0.85f)
shadowColor = color(0, 0, 0, 0.75f)
shadowOffset = new com.jme3.math.Vector3f(2, -2, -1);
background = new QuadBackgroundComponent( color(0.5, 0.75, 0.85, 0.5) );
background.texture = texture( name:"/com/simsilica/lemur/icons/double-gradient-128.png",
generateMips:false )
insets = new Insets3f( 2, 2, 2, 2 );
buttonCommands = stdButtonCommands;
}
selector( "button", "base" ) {
background = gradient.clone()
color = color(0.8, 0.9, 1, 0.85f)
background.setColor(color(0, 0.75, 0.75, 0.5))
insets = new Insets3f( 2, 2, 2, 2 );
buttonCommands = stdButtonCommands;
}
selector( "slider", "base" ) {
insets = new Insets3f( 1, 3, 1, 2 );
}
selector( "slider", "button", "base" ) {
background = doubleGradient.clone()
background.setColor(color(0.5, 0.75, 0.75, 0.5))
insets = new Insets3f( 0, 0, 0, 0 );
}
selector( "slider.thumb.button", "base" ) {
text = "[]"
color = color(0.6, 0.8, 0.8, 0.85)
}
selector( "slider.left.button", "base" ) {
text = "-"
background = doubleGradient.clone()
background.setColor(color(0.5, 0.75, 0.75, 0.5))
background.setMargin(5, 0);
color = color(0.6, 0.8, 0.8, 0.85)
buttonCommands = sliderButtonCommands;
}
selector( "slider.right.button", "base" ) {
text = "+"
background = doubleGradient.clone()
background.setColor(color(0.5, 0.75, 0.75, 0.5))
background.setMargin(4, 0);
color = color(0.6, 0.8, 0.8, 0.85)
buttonCommands = sliderButtonCommands;
}
selector( "slider.up.button", "base" ) {
buttonCommands = sliderButtonCommands;
}
selector( "slider.down.button", "base" ) {
buttonCommands = sliderButtonCommands;
}
selector( "checkbox", "base" ) {
def on = new IconComponent( "/com/simsilica/lemur/icons/Glass-check-on.png", 1f,
0, 0, 1f, false );
on.setColor(color(0.5, 0.9, 0.9, 0.9))
on.setMargin(5, 0);
def off = new IconComponent( "/com/simsilica/lemur/icons/Glass-check-off.png", 1f,
0, 0, 1f, false );
off.setColor(color(0.6, 0.8, 0.8, 0.8))
off.setMargin(5, 0);
onView = on;
offView = off;
color = color(0.8, 0.9, 1, 0.85f)
}
selector( "rollup", "base" ) {
background = gradient.clone()
background.setColor(color(0.25, 0.5, 0.5, 0.5))
}
selector( "tabbedPanel", "base" ) {
activationColor = color(0.8, 0.9, 1, 0.85f)
}
selector( "tabbedPanel.container", "base" ) {
background = null
}
selector( "tab.button", "base" ) {
background = gradient.clone()
background.setColor(color(0.25, 0.5, 0.5, 0.5))
color = color(0.4, 0.45, 0.5, 0.85f)
insets = new Insets3f( 4, 2, 0, 2 );
buttonCommands = stdButtonCommands;
}
Interestingly enough, inverting the Z order didn’t change anything, but when refactoring all this to post, the debug display is now properly displaying over the background, but the menu is still hidden.
I also made a way in the code to easily switch between setting the background from style and setting it in code, both with the same results.
The background image in question is just a 4000x2000 image in blender with some random “stars” and “nebulae”, made much bigger than any expected screen so it fills any screen without special adjustments, so any big image should replace it fine, but really any image should be fine.
The font is Mongolian Baiti at image size 512, font size 40, and letter spacing 2, from the sdk font generator