Creating a Scene-Context Menu using nifty

Hello Community!



I want to a “what you see is what you get”-scene-editor which enables to place and delete simple jme3 built-in shapes and custom meshes on a solid floor.



Placing the objects is no problem (using MouseButtonTrigger-handling, raycasting and just creating a new Shape at that position)



But… Only click into the scene and letting the shape appear… this is not what I want: When I click into the scene I want that a context menu appears because I want to

  • display context information about the scenepoint that I have hit with my mouseclick
  • to have more possibilitys for scene manipulation: I want to have Menu-Items like… “add sphere”, “add cube”, “delete obstacle”, “change texture” and so on. It would also be nice if the menu could be nested.



    Is that possible using nifty?



    As a first try I try to modify nifty examples. This is the xml I use:

    [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”>

    <!-- get the standard styles and controls in -->

    <useStyles filename=“nifty-default-styles.xml” />

    <useControls filename=“nifty-default-controls.xml” />



    <!-- just a simple screen that opens a popup by secondary click -->

    <screen id=“start” controller=“de.upb.ddi.nextsim.se.gui.ContextMenuController”>

    <layer id=“layer” backgroundColor="#003f" childLayout=“center”>

    <interact onSecondaryClick=“showMenu()”/>

    <panel align=“center” valign=“center” childLayout=“vertical” width=“50%”>

    <control id=“textOut” name=“label” backgroundColor="#8005" width=“100%”/>

    </panel>

    </layer>

    </screen>

    </nifty>



    [/xml]



    This is the way I initialize the nifty gui (once called in my “simpleInitApp()”):



    [java] public void Init_Nifty(){

    NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(assetManager,

    inputManager,

    audioRenderer,

    guiViewPort);

    nifty = niftyDisplay.getNifty();



    ContextMenuController itemconrol = new ContextMenuController();

    assetManager.registerLocator(“assets/”, FileLocator.class);

    nifty.fromXml(“Interface/UI_PopMenuItem.xml” ,“start”,itemconrol);

    guiViewPort.addProcessor(niftyDisplay);



    } [/java]



    The problem with this code is that the nifty screen (or the panel (or whatever)) occludes my scene. In addition, my mouse-cursor is immobile. What can I do?

For context menus I use nifty builders to build the menu dynamically. I create a panel and place it in a suitable position on the screen then place my buttons/whatever within that.

The nifty screen will always be above the rest of the scene unless you display it on some geometry/quad or something. For the rest thats surely possible, maybe you’ll need to do some custom Nifty Controllers for some things, check the Nifty Bible.



…but you can edit scenes visually in the SDK and easily add items to its add menus.



Heres an example of a fully functional plugin for the SDK that allows adding a box using the SceneComposer/SceneExplorer, including automatic undo support, it will appear in the “New Spatial” submenu:

[java]@org.openide.util.lookup.ServiceProvider(service = NewSpatialAction.class)

public class NewBoxAction extends AbstractNewSpatialAction {



public NewBoxAction() {

name = “Add Box”;

}



@Override

protected Spatial doCreateSpatial(Node parent) {

Spatial spatial=new Geometry(“Box”, new Box(Vector3f.ZERO, 1, 1, 1));

return spatial;

}

}[/java]



Yes, its only that one class :wink:

@normen said:
The nifty screen will always be above the rest of the scene unless you display it on some geometry/quad or something. For the rest thats surely possible, maybe you'll need to do some custom Nifty Controllers for some things, check the Nifty Bible.

..but you can edit scenes visually in the SDK and easily add items to its add menus.

Heres an example of a fully functional plugin for the SDK that allows adding a box using the SceneComposer/SceneExplorer, including automatic undo support, it will appear in the "New Spatial" submenu:
[java]@org.openide.util.lookup.ServiceProvider(service = NewSpatialAction.class)
public class NewBoxAction extends AbstractNewSpatialAction {

public NewBoxAction() {
name = "Add Box";
}

@Override
protected Spatial doCreateSpatial(Node parent) {
Spatial spatial=new Geometry("Box", new Box(Vector3f.ZERO, 1, 1, 1));
return spatial;
}
}[/java]

Yes, its only that one class ;)

Sounds simple. What I want is, that the functionality is in my software. Your code is an extention for the SceneComposer/SceneExplorer (which is external software, isnt it?) menus. If I got it right, these menus work inside the SceneComposer/SceneExplorer only(?) Is it easy to embedd in custom SimpleAplications? (If yes.. how?)
@zarch said:
For context menus I use nifty builders to build the menu dynamically. I create a panel and place it in a suitable position on the screen then place my buttons/whatever within that.

I just checked out this tutorial about creating nifty things without xml:
https://wiki.jmonkeyengine.org/legacy/doku.php/jme3:advanced:nifty_gui_java_layout?s[]=nifty&s[]=java

In that tutorial it looks like that you always have to define a screen. (To me it looks that this is always required, even if you want to have a context menu with dynamic position.) Do you have a solution without a screen or (at least) where the nifty screen is not occluding your scene? Can you post a code example please?

Just create the nifty screen, add a layer into it that’s transparent (probably absolute layout) then add the popup menu as a smaller panel within that.



Nifty layers always fill the full screen, but they default to transparent…

Thanks! Finally it works now. I have a non-nested context menu now. And experiences how I can build a nested one?



Adding menus as menuitem does not work (createPopup() ):

[java]import org.bushe.swing.event.EventTopicSubscriber;



import de.lessvoid.nifty.EndNotify;

import de.lessvoid.nifty.Nifty;

import de.lessvoid.nifty.controls.Label;

import de.lessvoid.nifty.controls.Menu;

import de.lessvoid.nifty.controls.MenuItemActivatedEvent;

import de.lessvoid.nifty.elements.Element;

import de.lessvoid.nifty.screen.Screen;

import de.lessvoid.nifty.screen.ScreenController;

import de.lessvoid.nifty.tools.SizeValue;



/**

  • ScreenController for Hello World Example.
  • @author void

    */

    public class ContextMenuController implements ScreenController {

    private Nifty nifty;

    private Screen screen;

    private Element popup;



    public void bind(final Nifty nifty, final Screen screen) {

    this.nifty = nifty;

    this.screen = screen;

    createPopup();

    }



    @Override

    public void onStartScreen() {

    }



    @Override

    public void onEndScreen() {

    }



    private void createPopup() {

    this.popup = nifty.createPopup("niftyPopupMenu");



    Menu<Menu<String>> popupMenu = popup.findNiftyControl("#menu", Menu.class);



    Menu<String> submenu1 = popup.findNiftyControl("#menu", Menu.class);

    submenu1.setWidth(new SizeValue("250px"));

    submenu1.addMenuItem("Item11", "Item11");

    submenu1.addMenuItem("Item12", "Item12");

    submenu1.addMenuItem("Item13", "Item13");

    Menu<String> submenu2 = popup.findNiftyControl("#menu", Menu.class);

    submenu2.setWidth(new SizeValue("250px"));

    submenu2.addMenuItem("Item21", "Item21");

    submenu2.addMenuItem("Item22", "Item22");

    submenu2.addMenuItem("Item23", "Item23");

    Menu<String> submenu3 = popup.findNiftyControl("#menu", Menu.class);

    submenu3.setWidth(new SizeValue("250px"));

    submenu3.addMenuItem("Item31", "Item31");

    submenu3.addMenuItem("Item32", "Item32");

    submenu3.addMenuItem("Item33", "Item33");





    popupMenu.setWidth(new SizeValue("250px"));

    popupMenu.addMenuItem("Item1", submenu1);

    popupMenu.addMenuItem("Item2", submenu2);

    popupMenu.addMenuItem("Item3", submenu3);

    }



    public void showMenu() {

    nifty.showPopup(screen, popup.getId(), null);

    nifty.subscribe(screen, popup.findNiftyControl("#menu", Menu.class).getId(), MenuItemActivatedEvent.class, new MenuItemActivatedEventSubscriber());

    }



    private class MenuItemActivatedEventSubscriber implements EventTopicSubscriber<MenuItemActivatedEvent> {

    @Override

    public void onEvent(final String id, final MenuItemActivatedEvent event) {

    System.out.println("halloooooooooo");

    }};



    }

    [/java]

In theory you can just recurs - add another panel for each layer of nesting.



I don’t think there is anything already built to do that for you though.