Nifty GUI determine mouse button

Hello everyone,
I’m currentrly working on a rewrite of my inventory system.
For that, i need to know if the left or right mouse button was pressed. As I am writing my GUI with java builders,
I can’t seem to find a method for that.
Any help?
Or would it make more sense to rewrite the GUI into an XML file (altough it’s over 2000 lines ATM and I like the java structure)?

There are two ways offhand that I know of. The first is that you could subscribe to the mouse click events. You can also directly set the callbacks in java like this:

ElementInteraction ei = element.getElementInteraction();
ei.getPrimary().setOnClickMethod(new NiftyMethodInvoker(nifty, "onItemClick(" + element.getId() + ")" , ctrl));
ei.getSecondary().setOnClickMethod(new NiftyMethodInvoker(nifty, "onItemSecondaryClick(" + element.getId() + ")" , ctrl));

Where element is the element you want the callback on, ‘onItemClick’ and ‘onItemSecondaryClick’ are callbacks in the nifty control ‘ctrl’.

1 Like

Thank you for the answer
But is that the only way?

image(new ImageBuilder("InvHotbarSlot2")
{{
    filename("Gui/Game/Nothing.png");
    width("16.6%");
    height("16.6%");
    x("58.3%");
    y("83.3%");
    interactOnClick("clickInv(2, l)");
    interactOnMouseOver("showInvHint(2)");
}});    

That is the code for a slot, and I’d like it better if it were like interactOnClick().
Oh, and would it make sense to translate it into XML (because this can be achieved very easily there)?

No. If I remember correctly, the secondary mouse button on click isn’t exposed in either the builders or xml. You could write a custom builder to make it look cleaner, but the effect is still the same. In my game I have a method that adds the items and just calls those methods after the element is built.

The only other option is to directly subscribe to the mouse events on your controller for all of your inventory slots.

Thank you.
But the mouse buttons are infact there in XML: Cited from the manual:
“All events will use the primary mouse button. If necessary you can address the individual mouse buttons with additional events that work exactly as the standard ones but will only be executed when a specific mouse button is used.
First mouse button (usually the left mouse button)
onPrimaryClick
onPrimaryClickRepeat
onPrimaryRelease
onPrimaryClickMouseMove
Second mouse button (usually the right mouse button)
onSecondaryClick
onSecondaryClickRepeat
onSecondaryRelease
onSecondaryClickMouseMove”
I’ve got two possible ways now, I’ll think about it a bit.

Ahhhhh okay, I remember what happened now. I generate all of my inventory slots and items in java instead of in xml to avoid duplicating xml code and future extension. The secondary click wasn’t in the builder so I had to separate it out. I should have remembered that…

Ok, I got it to work (with the java part), but I ran into another problem that is very weird:

private void initInventoryClicks()
{
    //Normal inventory menu
    this.initInventoryClicksForSlot("InvMenu", "InvHotbarSlot0", "clickInv(0, l)", "clickInv(0, r)");
    this.initInventoryClicksForSlot("InvMenu", "InvHotbarSlot1", "clickInv(1, l)", "clickInv(1, r)");
    this.initInventoryClicksForSlot("InvMenu", "InvHotbarSlot2", "clickInv(2, l)", "clickInv(2, r)");
    this.initInventoryClicksForSlot("InvMenu", "Slot0", "clickInv(3, l)", "clickInv(3, r)");
    this.initInventoryClicksForSlot("InvMenu", "Slot1", "clickInv(4, l)", "clickInv(4, r)");
    this.initInventoryClicksForSlot("InvMenu", "Slot2", "clickInv(5, l)", "clickInv(5, r)");
    this.initInventoryClicksForSlot("InvMenu", "Slot3", "clickInv(6, l)", "clickInv(6, r)");
    this.initInventoryClicksForSlot("InvMenu", "Slot4", "clickInv(7, l)", "clickInv(7, r)");
    this.initInventoryClicksForSlot("InvMenu", "Slot5", "clickInv(8, l)", "clickInv(8, r)");
    this.initInventoryClicksForSlot("InvMenu", "Slot6", "clickInv(9, l)", "clickInv(9, r)");
    this.initInventoryClicksForSlot("InvMenu", "Slot7", "clickInv(10, l)", "clickInv(10, r)");
    this.initInventoryClicksForSlot("InvMenu", "Slot8", "clickInv(11, l)", "clickInv(11, r)");
    this.initInventoryClicksForSlot("InvMenu", "HatSlot", "clickInv(12, l)", "clickInv(12, r)");
    //Container menu
    this.initInventoryClicksForSlot("Container", "InvHotbarSlot0", "clickInvContainer(0, l)", "clickInvContainer(0, r)");
    this.initInventoryClicksForSlot("Container", "InvHotbarSlot1", "clickInvContainer(1, l)", "clickInvContainer(1, r)");
    this.initInventoryClicksForSlot("Container", "InvHotbarSlot2", "clickInvContainer(2, l)", "clickInvContainer(2, r)");
    this.initInventoryClicksForSlot("Container", "InvSlot0", "clickInvContainer(3, l)", "clickInvContainer(3, r)");
    this.initInventoryClicksForSlot("Container", "InvSlot1", "clickInvContainer(4, l)", "clickInvContainer(4, r)");
    this.initInventoryClicksForSlot("Container", "InvSlot2", "clickInvContainer(5, l)", "clickInvContainer(5, r)");
    this.initInventoryClicksForSlot("Container", "InvSlot3", "clickInvContainer(6, l)", "clickInvContainer(6, r)");
    this.initInventoryClicksForSlot("Container", "InvSlot4", "clickInvContainer(7, l)", "clickInvContainer(7, r)");
    this.initInventoryClicksForSlot("Container", "InvSlot5", "clickInvContainer(8, l)", "clickInvContainer(8, r)");
    this.initInventoryClicksForSlot("Container", "InvSlot6", "clickInvContainer(9, l)", "clickInvContainer(9, r)");
    this.initInventoryClicksForSlot("Container", "InvSlot7", "clickInvContainer(10, l)", "clickInvContainer(10, r)");
    this.initInventoryClicksForSlot("Container", "InvSlot8", "clickInvContainer(11, l)", "clickInvContainer(11, r)");
    this.initInventoryClicksForSlot("Container", "InvHatSlot", "clickInvContainer(12, l)", "clickInvContainer(12, r)");
    this.initInventoryClicksForSlot("Container", "ContainerSlot0", "clickInvContainer(13, l)", "clickInvContainer(13, r)");
    this.initInventoryClicksForSlot("Container", "ContainerSlot1", "clickInvContainer(14, l)", "clickInvContainer(14, r)");
    this.initInventoryClicksForSlot("Container", "ContainerSlot2", "clickInvContainer(15, l)", "clickInvContainer(15, r)");
    this.initInventoryClicksForSlot("Container", "ContainerSlot3", "clickInvContainer(16, l)", "clickInvContainer(16, r)");
    this.initInventoryClicksForSlot("Container", "ContainerSlot4", "clickInvContainer(17, l)", "clickInvContainer(17, r)");
    this.initInventoryClicksForSlot("Container", "ContainerSlot5", "clickInvContainer(18, l)", "clickInvContainer(18, r)");
    this.initInventoryClicksForSlot("Container", "ContainerSlot6", "clickInvContainer(19, l)", "clickInvContainer(19, r)");
    this.initInventoryClicksForSlot("Container", "ContainerSlot7", "clickInvContainer(20, l)", "clickInvContainer(20, r)");
    this.initInventoryClicksForSlot("Container", "ContainerSlot8", "clickInvContainer(21, l)", "clickInvContainer(21, r)");
    this.initInventoryClicksForSlot("Container", "ContainerSlot9", "clickInvContainer(22, l)", "clickInvContainer(22, r)");
    this.initInventoryClicksForSlot("Container", "ContainerSlot10", "clickInvContainer(23, l)", "clickInvContainer(23, r)");
    this.initInventoryClicksForSlot("Container", "ContainerSlot11", "clickInvContainer(24, l)", "clickInvContainer(24, r)");
    //Maker menu
    //TODO
}
    
private void initInventoryClicksForSlot(String screen, String elementId, String methodL, String methodR)
{
    Element e = nifty.getScreen(screen).findElementById(elementId);
    ElementInteraction ei = e.getElementInteraction();
    ei.getPrimary().setOnClickMethod(new NiftyMethodInvoker(nifty, methodL, gs));
    ei.getSecondary().setOnClickMethod(new NiftyMethodInvoker(nifty, methodR, gs));
}

The code works for the Inventory part, but not the container part. These are the two methods in the controller class (gs):

public void clickInv(String str, String button)
{
    if(button.equals("l"))
   {
        player.getInventory().clickItem(str, true);
    }
    else if(button.equals("r"))
    {
        player.getInventory().clickItem(str, false);
    }
}
    
//FIXME: Why the duck is this not getting called?
public void clickInvContainer(String str, String button)
{
    if(button.equals("l"))
    {
        player.getInventory().clickItemWithContainer(str, true, guis.currentContainer);
    }
    else if(button.equals("r"))
    {
        player.getInventory().clickItemWithContainer(str, false, guis.currentContainer);
    }
    guis.updateContainerScreenWithExistingContainer(player.getInventory());
}

The latter is not getting called somehow… any idea why this happens?

Just at a first glance it looks like all of the elements you are assigning ‘clickInvContainer’ to are all for a different screen which is probably why its not working. I’m not sure how you are calling your setup method, but my guess is that something is reset when you switch screens that’s potentially removing the callbacks. You could try setting up the container callbacks when you switch to your container screen. You could also try setting breakpoints in the nifty code for where NiftyMethodInvoker is used. If it doesn’t even hit that code that would be a pretty clear indication that they are being reset. Are there any errors in the logs? Usually if the invoke fails nifty prints something in the logs even if it doesn’t error out. Otherwise you could try stepping through ‘performInvoke’ on the nifty method invoker.

Thanks for the answer.
In fact, it’s supposed to be two different screens, but neither is active when the MethodInvoker is used, and the game goes to the screens and back to another one when needed, so that can’t be it. Also, I check with simple System.out.println()'s if it found the elements. So the method is getting called for every element.

If you change ‘clickInvContainer’ for one of container elements to your other method does it work?

Nope…

What about the opposite? Change one of your InvMenu components to ‘clickInvContainer’.

That works… That’s so strange, the setup of the screens seems to be identical in every way but that the container has more slots.

Well that narrows it down at least. It’s got to be something to do with the container setup somehow then. Are the screens created in xml or java?

They’re done in java, as I find that a lot cleaner.
Oh, and thank you that you’re still trying to help me… :smiley:

No problem, I know nifty can be a challenge to work with sometimes. Could you post the java code that you use to build those containers? It might give some clues as to what is wrong.

Ok, here’s a massively shortened version of the container code (leaving out the other slots (they’re the same), and the background) as well as a comparison version of the inventory code:

    nifty.addScreen("Container", new ScreenBuilder("Container")
    {{
        controller(gs);
        layer(new LayerBuilder("Inv_Layer")
        {{
            childLayoutCenter();
            panel(new PanelBuilder("Main_Panel")
            {{
                    childLayoutCenter();
                    this.valignCenter();
                    this.alignCenter();
                    width("15%");
                    height("45%");

                    panel(new PanelBuilder("Container_Main_Panel")
                    {{
                        this.valignTop();
                        this.alignCenter();
                        this.childLayoutAbsoluteInside();
                        width("100%");
                        height("100%");
                            
                        image(new ImageBuilder("ContainerSlot0")
                        {{
                            filename("Gui/Game/Nothing.png");
                            width("16.6%");
                            height("9.8%");
                            x("16.6%");
                            y("9.8%");
                            childLayoutCenter();
                            image(new ImageBuilder("ContainerSlot0StackNum")
                            {{
                                filename("Gui/Game/Nothing.png");
                                width("100%");
                                height("100%");
                            }});
                        }});
    //SNIP

Inventory code for comparison:
nifty.addScreen("InvMenu", new ScreenBuilder("InvMenu")
{{
    controller(gs);
    layer(new LayerBuilder("Inv_Layer")
    {{
        childLayoutCenter();
        panel(new PanelBuilder("Inv_Main_Panel")
        {{
            childLayoutCenter();
            this.valignCenter();
            this.alignCenter();
            width("15%");
            height("26%");
            panel(new PanelBuilder("Inv_Panel")
            {{
                this.valignCenter();
                this.alignCenter();
                this.childLayoutAbsoluteInside();
                image(new ImageBuilder("Slot0")
                {{
                    filename("Gui/Game/Nothing.png");
                    width("16.6%");
                    height("16.6%");
                    x("25%");
                    y("25%");
                    interactOnMouseOver("showInvHint(3)");
                    childLayoutCenter();
                    image(new ImageBuilder("Slot0StackNum")
                    {{
                        filename("Gui/Game/Nothing.png");
                        width("100%");
                        height("100%");
                    }});
                }});
//SNIP

EDIT 9001: Darn forum code formatting… I’ll just leave it like this.

The only thing that looks different is the added call for ‘interactOnMouseOver(“showInvHint(3)”);’. No container elements are running the callback correct? Try setting visible to mouse on your container elements and see if the callbacks are called then.

1 Like

That actually fixed it, I can’t believe it. Have to squish some bugs with the item moving related to that now, but at least it isn’t some nifty problem. I can work with System.out.println()'s there again. :smile:

Awesome, glad it worked! I’ve found that when there is a problem related to nifty it’s usually something obscure I’ve forgot to check. Half the time I forget that flag too and don’t realize it for a couple of hours :slight_smile: