0

If a ContextMenu has lots of items, it fills the entire screen. It seems that ContextMenu.setMaxSize has no effect whatsoever.

Is there a way to restrict the size of a ContextMenu, in a way that it is still scrollable via mouse wheel & and the up and down buttons appear?

I guess I could roll my own control with VBox & Scrollpane, but I'd like to avoid this if possible.

user38725
  • 783
  • 6
  • 21

1 Answers1

1

Unfortunately, limiting the size of popup is not supported: the Region that's responsible for showing the MenuItems is ContextMenuContent and implements its computeMaxHeight to return the screenHeight. That container is created by ContextMenuSkin and stored into a private final field, so there's no way to replace it with a custom implementation with a more intelligent implementation.

What we can do, though, is to access that region and set its maxHeight to the same value as the ContextMenu. To remain off the evil illegal reflective access to the private field, we can register a handler for the Menu.ON_SHOWING event and update the size as needed [*].

Something like

public class MaxSizedContextMenu extends ContextMenu {

    public MaxSizedContextMenu() {
        addEventHandler(Menu.ON_SHOWING, e -> {
            Node content = getSkin().getNode();
            if (content instanceof Region) {
                ((Region) content).setMaxHeight(getMaxHeight());
            }
        });

    }
}

[*] update: to make this work, the ContextMenu must have a reasonable maxHeight (default is Double.MAX_VALUE), that is it must be set manually after instantiation. Furthermore, we have to use the ContextMenu's maxHeight in the eventHandler (vs. f.i. an arbitrary constant), otherwise vertical location of the popup is broken - the layout code still thinking, that it's filling the entire screen height.

ContextMenu menu = new MaxSizedContextMenu();
menu.setMaxHeight(200);
kleopatra
  • 49,346
  • 26
  • 88
  • 189
  • I haven't tested this, as I ended up doing my own component with VBox & Scrollpane. I'm sure it works though, and gave an upvote. – user38725 Jul 15 '18 at 19:19
  • Hi! Back to this issue... your solution does indeed work, but there is only one issue with it. If I show the ContextMenu _above_ a Node (with Side.TOP), the ContextMenu shows up at the very top of the screen. Any ideas how to fix this? – user38725 Jun 25 '19 at 15:20
  • @fluxi how are you calling the `show`? `contextMenu.show(anchor, Side.TOP, 0, 0)` works appropriately for me, but if you use offsets like `mouseEvent.getScreenX()` it could behave like that. Also make sure your anchor is the node being clicked and not the root node or something. – hinerm Jan 22 '20 at 16:57
  • @kleopatra your solution worked for me, except I had to use `Menu.ON_SHOWN` and `((Region) content).setMaxHeight(getHeight())` to capture the height I wanted at the right time. – hinerm Jan 22 '20 at 17:00
  • @hinerm That's exactly how I'm calling the show - contextMenu.show(anchor, Side.TOP, 0, 0). Unfortunately, I still haven't been able to resolve this problem, I just let it fill the screen in my app for now... – user38725 Jan 24 '20 at 05:29
  • @fluxi you might consider adding a [mcve] to your question - couldn't reproduce any difference in sizing for showing at Top/Bottom – kleopatra Jan 24 '20 at 11:06
  • @hinerm turned out that I forgot the useage note: it's crucial to set the maxHeight of the ContextMenu to make this work reasonably well. Thanks for the heads-up :) – kleopatra Jan 24 '20 at 11:23
  • https://pastebin.com/WRSTHKii - here's a simple example of my problem - how the vertical location of the popup is wrong after showing it. – user38725 Jan 25 '20 at 06:46
  • hmm ... see what you mean: the position calc of the popup differs in the two methods `show(.., x, y)` - contextMenu invoked by contextMenuHandler - and `show(.., Side, x,y)` - your example. – kleopatra Jan 25 '20 at 11:39