Nifty-gui does not support multi-touch

I just noticed that on iOS multi-touch is not supported. It can be easily fixed in jmeAppDelegate.m:

Just iterate through the touches set, not just use one element (anyObject).

I’d love to submit my fix, but not before asking the 100th time:
Does nifty-gui support multi-touch??

I’m trying this with my image buttons:

interactOnClick("onHUDAction(Left, true)");
interactOnRelease("onHUDAction(Left, false)");
interactOnClick("onHUDAction(Jump, true)");
interactOnRelease("onHUDAction(Jump, false)");

But holding left, then hitting the jump button also triggers that the left button was released.

Currently I’m using a touch listener as a workaround, but I’d really like to remove it.

I have heard rumors that Lemur supports multi-touch :laughing:, but for now I’d like to fix my nifty-gui.

1 Like

It’s not rumors. It definitely does… and we spent quite some time making sure it works. Dragging multiple sliders at the same time is pretty neat.

1 Like

I know, I was just kidding :grinning:, I saw the postings, so I knew the most likely answer I would get was “use Lemur”, but I try not to add new libraries…

I have looked through the nifty code superficially, but I haven’t found it yet, so any help appreciated.

My conclusion (IMHO) is that nifty-gui does not support multi-touch.

If you want to verify that, here is my example:

Tap and hold LEFT button
stdout: onAction: Left = true
Tap and hold JUMP button
stdout: onAction: Left = false
no further events after releasing the buttons.

The left button receives a mouse focus lost event and triggers onRelease.
The jump button triggers no events at all. You have to touch it again.

Tested on iOS with my patch for multi-touch. (PR will follow.)

package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.input.RawInputListener;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.TouchListener;
import com.jme3.input.controls.TouchTrigger;
import com.jme3.input.event.JoyAxisEvent;
import com.jme3.input.event.JoyButtonEvent;
import com.jme3.input.event.KeyInputEvent;
import com.jme3.input.event.MouseButtonEvent;
import com.jme3.input.event.MouseMotionEvent;
import com.jme3.input.event.TouchEvent;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;
import de.lessvoid.nifty.screen.DefaultScreenController;

/**
 * Test to show that nifty-gui currently does not support multi-touch!
 */
public class Main extends SimpleApplication implements TouchListener {

    private NiftyMenu menu;
    
    public static void main(String[] args) {
        Main app = new Main();
        app.start();
    }

    @Override
    public void simpleInitApp() {
        Box b = new Box(1, 1, 1);
        Geometry geom = new Geometry("Box", b);

        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        mat.setColor("Color", ColorRGBA.Blue);
        geom.setMaterial(mat);

        rootNode.attachChild(geom);
        
        // NOTE: if flyCam is not disabled, nifty will not send interactOnClick events!
        flyCam.setEnabled(false);
        setDisplayFps(false);
        setDisplayStatView(false);
        
        // touch listener is not required to receive nifty-gui onClick/onRelease, but it is just good for debugging
        inputManager.addMapping("Touch", new TouchTrigger(0));
        inputManager.addListener(this, "Touch");
        
        menu = new NiftyMenu(this, new MenuController());
        menu.gotoScreen("HUDScreen_ID");
        //mouseInput.setCursorVisible(true);
        //inputManager.setSimulateMouse(false); // no effect        
    }

    @Override
    public void simpleUpdate(float tpf) {
    }

    @Override
    public void simpleRender(RenderManager rm) {
    }
    
    public class MenuController extends DefaultScreenController
    {
        public void onHUDAction(String binding, String value)
        {
            boolean b = value.equals("true");
            System.out.println("onHUDAction: " + binding + " = " + value);

            if (binding.equals("Left"))
            {
                
            }
            else if (binding.equals("Jump"))
            {
            }
            
        }
    }

    @Override
    public void onTouch(String name, TouchEvent e, float tpf) {
        float x = e.getX();
        float y = e.getY();
        int id = e.getPointerId();
        System.out.println(id + ": " + x + "," + y);
    }
    
    
} 
package mygame;

import com.jme3.app.SimpleApplication;
import com.jme3.niftygui.NiftyJmeDisplay;
import de.lessvoid.nifty.Nifty;
import de.lessvoid.nifty.builder.ImageBuilder;
import de.lessvoid.nifty.builder.LayerBuilder;
import de.lessvoid.nifty.builder.PanelBuilder;
import de.lessvoid.nifty.builder.ScreenBuilder;
import java.util.logging.Logger;
import mygame.Main.MenuController;

public class NiftyMenu
{
    private static final Logger log = Logger.getLogger(NiftyMenu.class.getName());
    
    private String iconSizePixel = "256px";
    
    private NiftyJmeDisplay niftyDisplay;
    private Nifty nifty;
    private MenuController screenController;

    private SimpleApplication app;

    public NiftyMenu(SimpleApplication app, MenuController screenController)
    {
        this.app = app;
        this.niftyDisplay = new NiftyJmeDisplay(app.getAssetManager(), app.getInputManager(),
                                                app.getAudioRenderer(), app.getGuiViewPort());
        this.nifty = niftyDisplay.getNifty();
        app.getGuiViewPort().addProcessor(niftyDisplay);
        this.screenController = screenController;
        
        //nifty.setDebugOptionPanelColors(true);
        //nifty.loadStyleFile("nifty-default-styles.xml");
        //nifty.loadControlFile("nifty-default-controls.xml");
        addHUDScreen();
           
        //nifty.setIgnoreMouseEvents(true); // that's too much!
    }
    
    public void gotoScreen(String id)
    {
        nifty.gotoScreen(id);
    }
    
    private void addHUDScreen()
    {
        nifty.addScreen("HUDScreen_ID", new ScreenBuilder("HUDScreen_ID")
        {
            {
                controller(screenController);
                
                layer(new LayerBuilder()
                {
                    {
                        childLayoutVertical();                        
                        
                        panel(new PanelBuilder()
                        {
                            {
                                childLayoutHorizontal();
     
                                image(new ImageBuilder("Left_ID")
                                {
                                    {
                                        alignCenter();
                                        filename("arrow-left.png"); // LEFT
                                        width(iconSizePixel);
                                        height(iconSizePixel);
                                        //visibleToMouse(true);
                                        interactOnClick("onHUDAction(Left, true)");
                                        interactOnRelease("onHUDAction(Left, false)");
                                        //focusable(false);
                                    }
                                });
                                
                                image(new ImageBuilder("Jump_ID")
                                {
                                    {
                                        alignCenter();
                                        filename("footprints.png"); // JUMP
                                        width(iconSizePixel);
                                        height(iconSizePixel);
                                        //visibleToMouse(true);
                                        interactOnClick("onHUDAction(Jump, true)");
                                        interactOnRelease("onHUDAction(Jump, false)");
                                        //focusable(false);
                                    }

                                });
                            }
                        });

                    }
                });
                // </layer>
            }
        }.build(nifty));
        // </screen>
    }

}