Here is how I am doing for my Gui app states, I hope you find the idea useful. 
It’s very simple, I have a “ControlPanelState” which I use to register my Gui app states into it. Everything is extended from JME’s “BaseAppState”.
Here is the entry point into the control panel, a button on the top center of the screen 
The control panel open with bunch of registered Gui app states:
at some point, I will switch it to use a multi-column ListBox to display plugins.
Here is how the code looks like:
/**
* Main entry point to all GUI app states. AppStates can register themselves
* so their enabling/disabling can be controlled from within control panel.
*
* @author Ali-RS
*/
public class ControlPanelState extends BaseAppState {
// Keep track of registered app state id's to be displayed in settings panel
private final SafeArrayList<String> ids = new SafeArrayList<>(String.class);
// Contains registered Guis
private Container controlPanel;
// A closable/minimizable window for displaying contents of enabled Gui
private Container mainWindow;
// Contains widgets on right side of the screen
private Container widgetContainer;
// Current active Gui, only one at a time
private AppState enabledState;
private Panel contents;
private HAlignment hAlignment;
private VAlignment vAlignment;
private boolean minimized = false;
public ControlPanelState() {
super("Control Panel");
}
@Override
protected void initialize(Application app) {
// FIXME: Use a proper icon for control panel button
Button controlPanelToggleBtn = new Button(" ");
controlPanelToggleBtn.addClickCommands(src -> {
if (minimized) {
setMinimised(false);
} else if (enabledState == null) {
toggleControlPanel();
}
});
getState(SceneGraphState.class).getGuiNode().attachChild(controlPanelToggleBtn);
alignPanel(controlPanelToggleBtn, HAlignment.Center, VAlignment.Top);
controlPanel = new Container();
mainWindow = new Container(new BorderLayout());
widgetContainer = new Container(new BoxLayout());
Container buttons = new Container(new BoxLayout(Axis.X, FillMode.Even));
Button minimizeBtn = buttons.addChild(new Button("_"));
minimizeBtn.setTextHAlignment(HAlignment.Center);
minimizeBtn.addClickCommands(src -> {
setMinimised(true);
});
Button closeBtn = buttons.addChild(new Button("x"));
closeBtn.setTextHAlignment(HAlignment.Center);
closeBtn.addClickCommands(src -> {
enabledState.setEnabled(false);
enabledState = null;
});
mainWindow.addChild(buttons, BorderLayout.Position.North);
refresh();
}
@Override
protected void cleanup(Application app) {
}
@Override
protected void onEnable() {
getState(SceneGraphState.class).getGuiNode().attachChild(widgetContainer);
}
@Override
protected void onDisable() {
widgetContainer.removeFromParent();
}
@Override
public void update(float tpf) {
if (controlPanel.getParent() != null) {
alignPanel(controlPanel, HAlignment.Center, VAlignment.Center);
}
if (mainWindow.getParent() != null) {
alignPanel(mainWindow, hAlignment, vAlignment);
}
alignPanel(widgetContainer, HAlignment.Right, VAlignment.Center);
if (enabledState != null && !enabledState.isEnabled()) {
enabledState = null;
}
}
public float getStandardScale() {
return getApplication().getCamera().getHeight() / 720f;
}
/**
* Apply standard scale and align panel
*/
public void alignPanel(Panel panel, HAlignment ha, VAlignment va) {
Camera cam = getApplication().getCamera();
// Apply standard scale
float scale = getStandardScale() * 1.2f;
panel.setLocalScale(scale);
int width = cam.getWidth();
int height = cam.getHeight();
Vector3f prefSize = new Vector3f(panel.getPreferredSize());
prefSize.multLocal(scale);
float x = 0;
float y = 0;
// Align panel
switch (ha) {
case Center:
x = (width - prefSize.x) / 2;
break;
case Left:
x = 0;
break;
case Right:
x = (width - prefSize.x);
break;
}
switch (va) {
case Center:
y = (height + prefSize.y) / 2;
break;
case Bottom:
y = prefSize.y;
break;
case Top:
y = height;
break;
}
Vector3f translation = panel.getLocalTranslation();
if (translation.x != x || translation.y != y) {
panel.setLocalTranslation(x, y, translation.z);
}
}
/**
* Registers an app state into the control panel. The state id will be used for panel name.
*
* @throws IllegalArgumentException if state has no id specified to it.
*/
public <T extends AppState> void register(T state) {
if (state.getId() == null) {
throw new IllegalArgumentException("AppState has no id.");
}
ids.add(state.getId());
refresh();
}
public boolean remove(AppState state) {
if (state.getId() == null) {
throw new IllegalArgumentException("AppState has no id.");
}
boolean removed = ids.remove(state.getId());
if (removed) {
refresh();
}
return removed;
}
public void show(Panel window, HAlignment hAlignment, VAlignment vAlignment, Panel... widgets) {
showWindow(window, hAlignment, vAlignment);
showWidgets(widgets);
}
public void close(Panel window, Panel... widgets) {
closeWindow(window);
closeWidgets(widgets);
}
public void showWindow(Panel contents, HAlignment hAlignment, VAlignment vAlignment) {
if (this.contents != null) {
closeWindow(this.contents);
}
if (contents != null) {
this.contents = contents;
this.hAlignment = hAlignment;
this.vAlignment = vAlignment;
mainWindow.addChild(contents, BorderLayout.Position.Center);
getState(SceneGraphState.class).getGuiNode().attachChild(mainWindow);
contents.runEffect(Panel.EFFECT_OPEN);
GuiGlobals.getInstance().requestFocus(mainWindow);
}
}
public void closeWindow(Panel contents) {
if (Objects.equals(this.contents, contents)) {
this.contents.runEffect(Panel.EFFECT_CLOSE);
mainWindow.removeFromParent();
mainWindow.removeChild(contents);
GuiGlobals.getInstance().releaseFocus(mainWindow);
this.contents = null;
hAlignment = null;
vAlignment = null;
}
}
public void showWidgets(Panel... widgets) {
for (Panel widget : widgets) {
widgetContainer.addChild(widget);
}
}
public void closeWidgets(Panel... widgets) {
for (Panel widget : widgets) {
widgetContainer.removeChild(widget);
}
}
private void toggleControlPanel() {
if (controlPanel.getParent() == null) {
getState(SceneGraphState.class).getGuiNode().attachChild(controlPanel);
} else {
controlPanel.removeFromParent();
}
}
private void setMinimised(boolean minimized) {
this.minimized = minimized;
if (minimized) {
mainWindow.removeFromParent();
contents.runEffect(Panel.EFFECT_CLOSE);
} else {
getState(SceneGraphState.class).getGuiNode().attachChild(mainWindow);
contents.runEffect(Panel.EFFECT_OPEN);
}
}
private void refresh() {
if (isInitialized()) {
controlPanel.clearChildren();
int index = 0;
for (String id : ids) {
// FIXME: No hardcoded values
int x = index / 3;
int y = index % 3;
Button toggleBtn = controlPanel.addChild(new Button(id), x, y);
toggleBtn.setTextHAlignment(HAlignment.Center);
toggleBtn.addClickCommands(source -> {
// Close ControlPanel main window
toggleControlPanel();
// Enable gui app state
enabledState = getStateManager().stateForId(id, AppState.class);
enabledState.setEnabled(true);
});
index++;
}
}
}
}
So basically it gives me two panels for displaying stuff. A main window with close/minimize action and panel for adding widgets.
Only one window can be enabled at a time but there can be multiple widgets.
Let me show it in action. For example here is how my “Scene Editor” looks:
You can see the main window on the left side (it can be aligned by providing a HAlignment and a VAlignment) and widgets on the right side.
For the main window, I mostly use Lemur “TabbedPanel” and for widgets, I use Lemur “RollupPanel”.
So for a Gui app state to be listed on control panel I need to register it into the control panel. I do this from the initialize() method of the Gui app state:
@Override
protected void initialize(Application app) {
...
getState(ControlPanelState.class).register(this);
}
@Override
protected void cleanup(Application app) {
getState(ControlPanelState.class).remove(this);
}
Now it will be listed on the control panel. When toggled, control panel is going to call it’s enable/disable method.
now on enable, a Gui app state can call the show method from “ControlPanelState” to display it’s stuff:
protected void onEnable() {
getState(ControlPanelState.class).show(tabs, HAlignment.Left, VAlignment.Center, widgets);
...
}
@Override
protected void onDisable() {
getState(ControlPanelState.class).close(tabs, widgets);
...
}
now say I also want to use an external widget, for example I want to use “TransformWidget” from my Scene Editor, I just need to call this from the Scene Editor:
getState(TransformState.class).setEnabled(true);
now when the “TransformState” gets enable it can add it’s widgets into the control panel:
@Override
protected void onEnable() {
....
getState(ControlPanelState.class).showWidgets(transformWidget);
}
@Override
protected void onDisable() {
...
getState(ControlPanelState.class).closeWidgets(transformWidget);
}
This is how it looks like in action:
Sorry that it got abit long, hope you find it useful 