NiftyGUI tries to instantiate my Controller even though I've only passed it an instance!

For some reason, NIftyGUI seems to be trying to instantiate my (Nifty) Controller even though I passed an instance of the controller to the only Element that it should be controlling. I think this may have something to do with the fact that I’m trying to generate a Popup. Below is the code for the constructor that generates the problem. AbilityMeny implements de.lessvoid.nifty.controls.Controller. If anyone can explain to me why it’s trying to generate another instance of my Controller, I would greatly appreciate it.

The error is:

Oct 13, 2016 8:21:08 PM de.lessvoid.xml.tools.ClassHelper getInstance
WARNING: class [AbilityMenu] could not be instantiated (java.lang.InstantiationException: AbilityMenu)
private AbilityMenu(ArrayList<ActivatedAbility> abilities, int x, int y) {
	Nifty nifty = playingLevelUI.app.nifty;
	Screen screen = playingLevelUI.getScreen();
	layer = nifty.findPopupByName("popup_menu_layer");
	if (layer == null) {
		PopupBuilder lb = new PopupBuilder("popup_menu_layer");
		lb.childLayoutAbsolute();
		lb.name("popup_menu_layer");
		lb.registerPopup(nifty);
		nifty.createPopupWithId("popup_menu_layer", "popup_menu_layer");
		layer = nifty.findPopupByName("popup_menu_layer");
	}
			
	for (Element e : layer.getChildren()) {
		e.markForRemoval();
	}
	PanelBuilder pb = new PanelBuilder("ability_menu");
	pb.childLayoutVertical();
	pb.controller(this);
	pb.width("10%");
	pb.backgroundColor(new Color(.6f,.6f,.5f,.3f));
	pb.interactOnClick("otherClick()");
	pb.x(new SizeValue(x, SizeValueType.Pixel));
	pb.y(new SizeValue(y, SizeValueType.Pixel));
	this.abilities = abilities;
	for (int i = 0; i < abilities.size(); i++) {
		TextBuilder tb = new TextBuilder();
		tb.alignCenter();
		tb.width("*");
		tb.textHAlignLeft();
		HoverEffectBuilder eb = new HoverEffectBuilder("colorBar");
		eb.effectParameter("color", "#E0E0FF40");
		tb.onHoverEffect(eb);
		HoverEffectBuilder eb2 = new HoverEffectBuilder("hint");
		eb2.effectParameter("hintDelay", "1000");
		eb2.effectParameter("hintText", abilities.get(i).on(activeActor).getDescription());
		tb.onHoverEffect(eb2);
		tb.text(abilities.get(i).getName());
		tb.text(tb);
		tb.interactOnRelease("click(" + Integer.toString(i) + ")");
	}
				
	//layer.addChild(pb.build(nifty, screen, layer));
	pb.build(nifty, screen, layer);
	layer.resetLayout();
}

You aren’t showing the relevant code where you register the controller as well as where / how you load your Ability Menu.

I register the Controller right in this constructor. The line is easy to miss, though. I load the ability menu by calling this constructor.

Okay, missed that line. That method call actually just takes the class name from the instance you pass in so when you attempt to build the control it will try to create a new instance of the class ability menu.

If I was writing this type of component using nifty I would create a separate popup. Then I would rewrite the AbilityMenu controller so it has methods to add new skills to the list. Then, whenever you need to display the AbilityMenu you would get the controller from the popup element, set the skills, and then show the popup. That is the clean way to do it.

Also, it doesn’t look like you are adding your TextBuilder elements to your panel in the existing code.

1 Like

Just out of interest:
Why do you use the controller in a (single) panel in your popup and not on the popup itself or make it a NiftyControl (like label, ListBox, etc.)?

Besides, Nifty works differently than jME or any other practical library. Nifty instantiates most of it’s controllers itself, so you gotta expect a short lifetime for those. You could, however, create a more general ScreenController with whom the other, small controllers communicate (very messy code - I don’t recommend it).

Wow. That’s horrible. If it wants a class it should have a Class<? extends Controller> parameter, not a Controller parameter. This was the missing bit of information I needed.

Based on my understanding of the documentation, the Popup itself acts like (or is) a Layer. If I have other popups in the future, I want to be able to use the same layer.

The builders are kind of a syntactical glue to make creating elements in Java easier. Creating elements in java / xml for nifty are functionally equivalent and you would be passing a string from xml not a class. In reality you are right… it would probably be more correct to remove that method entirely since there is a controller method that takes a string. I’m not sure why that method exists given the confusion it can cause if you’ve only used java to build elements.

1 Like

This answers my next question, which would have been, “Am I then forced to instantiate an instance of my Controller, which is then never used, just so I can pass it to Nifty.?”

Is there a reason for that? You are making it more complicated on yourself when you don’t packing things as discreet components.

If it makes things more complicated, I can easily change the code. For now, since the Controller only responds to what happens in the Panel, it makes sense to make it a direct controller of the Panel.