Get triggers linked to an input mapping name

Hi monkeys!

If I’m not mistaken, there is no way for me to ask jme for what triggers are linked to what mapping.
Two reasons:

  • there is no method to get useful data out of the jme input system
  • the input system holds triggers as hashed integers

Did I miss something? Or do I have to duplicate such data like I had to with appStates?
Mind you, in this case, it’s not only about duplication, it’s also about dependencies between libs… I’m hoping to add support for custom triggers to TGG so it can support joystick and so on without depending on special classes to feed it data.

You might want to look at Lemur’s InputMapper. It can be used without ever touching any of the rest of Lemur but it provides a LOT of useful stuff on top of regular InputManager and support a logical delineation between what triggers a function and the function being triggered in a more general way.

From there if it is still missing methods then we can talk about it… as one of its main jobs is to support the kind of thing you want to do.

Lol 1 min :D.

To the Lemur cave Robin! I’ll be back :D.

Thx pspeed!

Unfortunately, I haven’t gotten to the detailed docs yet…

heheh.

But there are some tutorials.

And the javadoc has a little overview:
http://jmonkeyengine-contributions.github.io/Lemur/javadoc/Lemur/com/simsilica/lemur/input/InputMapper.html:

1 Like

Well, InputMapper is beautiful!

Sadly, it’s a very different paradigm and would force me to change half my application and change half of TGG’s main class.
Yes… it does make me very self-conscious :.

Basically, TGG kinda takes care out of the box of registering/listening and on my side, I just react to events on the TGG controls.
Each of my 2D screens are made of 3 classes:

  • the screen (the view)
  • the GUI (fetches data + reacts to activations (and ddlb selection changes etc))
  • the listener which checks the name of the control activated and calls the linked reaction method from the badly named GUI class. NB: I enriched the controls so they provide their name when they call their listener.

… and mostly due to my track editor, I have dozens of such screens. It is very convenient though and supported everything I wanted to do out of the box, up to crud generic screens built from adapters.

TGG itself has hard coded what triggers it listens to.
It also supports home/end/shift/etc and such inside text inputs which I think would be hard to move to inputMapper.

I was hoping to add a refreshTriggers() or something method on TGG’s main class that would have it interrogate the inputManager and from there discover what triggers it should listen to instead of checking for constants. I would have to add the joystick part.

The idea is to make jme core the core data handler and have libraries interrogate it.

I really like what you did there (I’m in awe about your addMapping method), but on this project, it seems a hard task moving to it due to how TGG and my stuff works. We are the culprits :D.

This is what I’m currently thinking… not saying it’s right or anything (how would I know lol). I’m open to debate/discuss this if you want to.

So currently, I’m thinking of making an InputManagerWithRead class composed of the app.inputManager, that would store mappings/triggers and offer methods to get that data. That would create a dependency and data duplication though.

What you think?

So I wrote the probably soon infamous InputManagerWithReading class:

import com.jme3.input.InputManager;
import com.jme3.input.controls.InputListener;
import com.jme3.input.controls.Trigger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;


/**
 *
 * @author jo... yah, you copy pasted most of it :p
 */
public class InputManagerWithRead {
    private static final Logger logger = Logger.getLogger(InputManager.class.getName());
    private final InputManager inputManager;
    
    private final HashMap<String, Mapping> mappings;
    
    public static class Mapping {
        public final String name;
        public final ArrayList<Trigger> triggers;
        public final ArrayList<InputListener> listeners;

        public Mapping(String name) {
            this.name = name;
            triggers = new ArrayList<Trigger>();
            listeners = new ArrayList<InputListener>();
        }
    }
    
    public InputManagerWithRead(InputManager inputManager){
        this.inputManager = inputManager;
        
        mappings = new HashMap<String, Mapping>();
    }
    
    public Mapping getMapping(String mappingName){
        return mappings.get(mappingName);
    }
    
    public HashMap<String, Mapping> getMappings(){
        return mappings;
    }
            
    public void addListener(InputListener listener, String... mappingNames) {
        for (String mappingName : mappingNames) {
            Mapping mapping = mappings.get(mappingName);
            if (mapping == null) {
                mapping = new Mapping(mappingName);
                mappings.put(mappingName, mapping);
            }
            if (!mapping.listeners.contains(listener)) {
                mapping.listeners.add(listener);
            }
        }
        
        inputManager.addListener(listener, mappingNames);
    }
    
    public void removeListener(InputListener listener) {
        for (Mapping mapping : mappings.values()) {
            mapping.listeners.remove(listener);
        }
        
        inputManager.removeListener(listener);
    }
    
    public void addMapping(String mappingName, Trigger... triggers) {
        Mapping mapping = mappings.get(mappingName);
        if (mapping == null) {
            mapping = new Mapping(mappingName);
            mappings.put(mappingName, mapping);
        }
        
        boolean alreadyThere = false;
        
        for (Trigger trigger : triggers) {
            String name = trigger.getName();
            for(Trigger trigger_ : mapping.triggers){
                if(trigger_.getName().equals(name)){ alreadyThere = true; break; }
            }
            
            if(alreadyThere){
                logger.log(Level.WARNING, "Attempted to add mapping \"{0}\" twice to trigger.", mappingName);
                alreadyThere = false;
            }
            else {
                mapping.triggers.add(trigger);
            }
        }
        
        inputManager.addMapping(mappingName, triggers);
    }
    
    public boolean hasMapping(String mappingName) {
        return mappings.containsKey(mappingName);
    }
    
    public void deleteMapping(String mappingName) {
        Mapping mapping = mappings.remove(mappingName);
        if (mapping == null) {
            logger.log(Level.WARNING, "Cannot find mapping to be removed, skipping: {0}", mappingName);
        } else {
            inputManager.deleteMapping(mappingName);
        }
    }
    
    public void deleteTrigger(String mappingName, Trigger trigger) {
        Mapping mapping = mappings.get(mappingName);
        if (mapping == null) {
            throw new IllegalArgumentException("Cannot find mapping: " + mappingName);
        }

        mapping.triggers.remove(trigger);
        
        inputManager.deleteTrigger(mappingName, trigger);
    }

    /**
     * Clears all the input mappings from this InputManager.
     * Consequently, also clears all of the
     * InputListeners as well.
     */
    public void clearMappings() {
        mappings.clear();
        
        inputManager.clearMappings();
    }

Should I punish myself for giving birth to such a monstrosity?