0

I'm trying to make a ContextMenu similar to this one in the image below:

enter image description here

I want to display a list of Strings in one of the groups.

This is possible by using ActionGroup, from controlsFX library. In order to reproduce the example in the picture, use that code:

    public class HelloActionGroup{
            Label label = new Label("hello world, I'm a label");
            label.setContextMenu(ActionUtils.createContextMenu(action));
                    private Collection<? extends Action> action = Arrays.asList(
                            new ActionGroup("Group 1", new DummyAction("Action 1")),
                                                       new DummyAction("Action 2"),
                                                       new DummyAction("Action 3"))
                    );
                
                private static class DummyAction extends Action{
                        public DummyAction(String name){
                            super(name);
                        }
                    }
}

In order to make that code run in your code, simply place that label in your root Parent:

yourPaneName.getChildren().add(label);

As you can see, I have to hard code all the items being shown inside "Group 1", which are "Action1", "Action2", and "Action3". How can I place a list of Strings inside "Group 1"?

Something like:

List<String> list = new ArrayList<>();
list.add("Action1");
list.add("Action2");
list.add("Action3");
private Collection<? extends Action> action = Arrays.asList(
                                new ActionGroup("Group 1", new DummyAction(list)));

UPDATE: Managed to get really close using a loop for inserting the list's items in the ContextMenu:

List<String> list = new ArrayList<>();
        list.add("string1");
        list.add("string2");
        for (String str: list) {
            action2.add(new ActionGroup("action1",new DummyAction(str)));
        }

Result: enter image description here string2 is showed up below the selected (action1)

I know it's creating multiple ActionGroup because of the statement "new ActionGroup", now I just have to find a way to create only one ActionGroup and add multiple DummyAction(strings from list) to it

FARS
  • 313
  • 6
  • 20
  • the reason for using a List aims for using multiple items inserted in it. Why would I select the first index of it? In short, I'm not sure how your suggestion would help me. Thanks for giving it a shot btw – FARS Sep 16 '21 at 01:42

2 Answers2

3

Menus are just a kind of MenuItem, so wherever you can put a MenuItem, you can put a Menu, which can, in turn, have child menu items.

As far as ControlsFX controls go, I don't know about them because I don't use them. But for the problem, as you described it, I can't see that they would be needed. If in your application, you did actually need to use ControlsFX for additional functionality, perhaps you could adapt the info from this solution to do so.

subcontext

Example

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;

import java.util.stream.IntStream;

public class SubContext extends Application {
    private static final int N_ITEMS_PER_MENU = 5;

    @Override
    public void start(Stage stage) {
        MenuGenerator menuGenerator = new MenuGenerator();

        Menu[] contextTopicMenus = menuGenerator.generateMenus(
                N_ITEMS_PER_MENU
        );

        for (Menu menu: contextTopicMenus) {
            menu.getItems().setAll(
                    menuGenerator.generateMenuItems(
                            N_ITEMS_PER_MENU
                    )
            );
        }

        ContextMenu contextMenu = new ContextMenu(contextTopicMenus);

        Label label = new Label("Click for context");
        label.setContextMenu(contextMenu);

        Pane layout = new VBox(label);
        layout.setPadding(new Insets(10));

        stage.setScene(new Scene(layout));
        stage.show();
    }

    private static final class MenuGenerator {
        private final Lorem lorem = new Lorem();
        
        public Menu generateMenu() {
            return new Menu(lorem.nextString());
        }

        public MenuItem generateMenuItem() {
            return new MenuItem(lorem.nextString());
        }

        public Menu[] generateMenus(int n) {
            return IntStream.range(0, n)
                    .mapToObj(i -> generateMenu())
                    .toArray(Menu[]::new);
        }

        public MenuItem[] generateMenuItems(int n) {
            return IntStream.range(0, n)
                    .mapToObj(i -> generateMenuItem())
                    .toArray(MenuItem[]::new);
        }
    }
    
    private static final class Lorem {
        private static final String[] lorem = "lorem ipsum dolor sit amet consectetur adipiscing elit sed do eiusmod tempor incididunt ut labore et dolore magna aliqua".split(" ");
        private int idx = 0;

        public String nextString() {
            return lorem[getAndIncrementIdx()];
        }

        private int getAndIncrementIdx() {
            int retVal = idx;

            idx = (idx + 1) % lorem.length;

            return retVal;
        }
    }

    public static void main(String[] args) {
        Application.launch(args);
    }
}

The fancy menu generator and lorem in the example is just for ease of data generation. For your actual application, you wouldn't need it, instead, you would create the applicable menus, menu items, and their appropriate action functions and use those rather than the generated ones.

jewelsea
  • 150,031
  • 14
  • 366
  • 406
  • Greate contribution, @jewelsea, Thanks. Although, I'm not sure if that will fit in my scenario. The environment behind my given examples is much more complex. I don't have a list in my real application. Instead, I've got a jpa method findAll() (instanceVariableFromMyClassType.findAll();), that list returns a couple of Strings from my database which I want to insert in my ContextMenu as a sub-menu. Note that I add Images to every row in my ContextMenu, I'm not sure whether that's possible with your suggestion (even though I certainly will git it a shot). Again, thanks for contributing! – FARS Sep 16 '21 at 01:41
  • managed to solve it myself, although I appreciate your suggestion. I've tested it and it works. The only reason for not accepting your answer is the fact that I had to use controlsFX (it's much easier to set logic and graphics to it), even though it's sometimes painful, but rewarding once it's done (not only that it has an awesome UI, but the performance and ease of implementation are outstanding! -excluding when you have no resources about how to do stuff.. even in the official project sample/documentation- kek) – FARS Sep 16 '21 at 20:36
0

First, create a collection of ActionGroup, then create a separate instance from the same type of the collection (ActionGroup). After that, run a loop in order to insert every item from a list into the ActionGroup instance. Here's how it looks like:

Collection<ActionGroup> action = new LinkedList<>();
        ActionGroup actionGroup = new ActionGroup("My list items:");
        List<String> list = new ArrayList<>();
        list.add("string 1");
        list.add("string 2");
        list.add("string 3");
        for (String str : list) {
            actionGroup.getActions().add(new DummyAction(str));
        }
        action.add(actionGroup);
        node.setContextMenu(ActionUtils.createContextMenu(action));

result: enter image description here

FARS
  • 313
  • 6
  • 20