Menu Scroller
Posted by Darryl Burke on February 1, 2009
New: keepVisible(JMenuItem) and keepVisible(int) methods scroll the menu to the specified item or index.
A drawback of the JMenu API is that it does not provide a method to limit the number of items displayed at a time, something on the lines of the JComboBox method setMaximumRowCount. This means that adding a large number of items to a menu can and does result in a menu too tall for the screen. The usual workaround is to nest the items in submenus, which can make it tedious for the user to find and select the desired item, and lead to quite some extra code for the programmer, especially if the menu items are added dynamically at run time.
MenuScroller addresses this shortcoming by deterministically adding up and down arrows to the top and bottom of the menu. A number of items can optionally be frozen at the top and/or bottom of a scrolling subset of menu items.
There are two ways in which a MenuScroller can be attached to a JMenu/JPopupMenu. The recommended method, which provides better readability for your code, is via a call to the class’s static method
public static MenuScroller
setScrollerFor(JMenu|JPopupMenu menu[,
int rowCount[,
int interval[,
int topFixedCount, int bottomFixedCount]]]);
where the square brackets [ ] indicate optional parameters.
As a glance at the code will show, it is also sufficient to construct a MenuScroller with the JMenu/JPopupMenu as the first (or only) parameter.
This illustration shows the MenuScroller in action. The line of code to set this scroller was
MenuScroller.setScrollerFor(menu, 8, 125, 3, 1);
If a reference to the MenuScroller is retained, the number of items to scroll, the scrolling interval in milliseconds and the number of items to freeze at the top or bottom can be retrieved and set via the accessors and mutators provided. An item or an index into the menu can also be specified to be made visible whenever the menu is shown.
To restore the default, non-scrolling behavior of the JMenu, invoke the MenuScroller‘s dispose() method.
menuScroller.dispose();
Get The Code
Related Reading
The Java™ Tutorials:

Kleopatra said
Hi Darryl,
that got me rolling (or should I say scrolling :-) Especially since there aren’t any – at least no free, open, don’t know about commercial – reliably working scrolling menu. We tried over at SwingX (Karl Schaefer’s incubator section has some code), but were not overly successful.
Unfortunately, your implementation seems to be easily confused as well: click on any of the scroll arrows then the next time it opens behaves erratically. And couldn’t find a way to control it by keyboard only (probably overlooking the obvious :-)
Another question – again probably due to my ignorance :-) – is how to apply that to a JPopupMenu?
Cheers
Jeanette
Darryl Burke said
Support for JPopupMenu has been added to MenuScroller.
Darryl
Darryl Burke said
Jeanette, thank you for testing this so thoroughly!
I’ve cured the erratic behavior by the simple expedient of removing the original MouseListener(s) while retaining a reference to them, cross referenced to the menu item from which they are removed. I then forward only mouseEntered and mouseExited events from my own listener. No mouseClicked, no problem :D
I’ve also taken care of the keyboard control with a ChangeListener that tests isArmed().
The new code is available now, but a further update will be uploaded in a day or two*, as the up/down menu items now have so much added functionality that I feel it’s time for a new inner class extending JMenuItem.
I hadn’t thought about using this for a JPopupMenu, I’ll have to study it.
And if you claim ignorance, then I haven’t yet emerged from the primordial ooze ;-)
regards, Darryl
* edit That took less time than I thought it would. The update is uploaded now.
Ben Leers said
Darryl, what you’ve said about removing the original mouselisteners from the menuitems “up/down” isn’t implemented in the sourcecode provided. You just keep the mouselisteners in a seperate variable, and that’s it.
other than that, nice work! worked straight out of the box for me :)
Darryl Burke said
Thanks Ben for noticing that. You’re right, of course, and while it was an oversight that I hadn’t removed the MouseListeners as planned, it’s now apparent to me that using a ChangeListener to start/stop the scroll timer instead of my earlier approach using the mouseEntered/Exited methods of a MouseListener provided the real fix for the erratic behavior Jeanette noted. So … it’s neither required to remove the MouseListeners nor to add a custom MouseListener.
I’ve shortened the code appropriately and as soon as the new version is available I’ll post a comment on the Recent Updates page.
I’m happy you found this utility useful.
cheers, Darryl
Shaun Kalley said
Hello, Darryl,
This is a neat little controller class, one which I’ve been looking for for a while and which I’ve already incorporated into a test branch of one of my projects. I’ve been working with your code a bit and would like to offer a couple of suggestions if I may.
My first suggestion is to have the MenuScroller just manage a JPopupMenu rather than both a JPopupMenu and a JMenu. Since JMenu instances really just delegate their functionality to an internal JPopupMenu which you can access using getPopupMenu(), you can simply manage that JPopupMenu instead of the JMenu itself. This will help to clean up the MenuScroller code and make it easier to test and maintain.
Second, assuming the previous suggestion, you need to add a PropertyChangeListener to the JPopupMenu to listen to the “visible” property and repaint the JPopupMenu the new value is True. This will solve a painting issue when managing the JPopupMenus of JMenus which themselves are children of JPopupMenus. I’ll include a quick-and-dirty test program to visualize the three use cases I’ve tested for so far below.
Cheers,
Shaun
public class MenuScrollerTest { public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { // initialize menu-bar-test components JMenuBar menuBar = new JMenuBar(); for (int i = 1; i <= 3; i++) { JMenu menu1 = new JMenu("Menu Bar Test (" + i + ")"); new MenuScroller(menu1, 15, 125, 5, 5); for (int j = 1; j <= 100; j++) { menu1.add(new JMenuItem("Item " + j + " (" + i + ")")); } menuBar.add(menu1); } // initialize popup-menu-test components final JPopupMenu popupMenu1 = new JPopupMenu(); new MenuScroller(popupMenu1, 15, 125, 5, 5); for (int i = 1; i <= 100; i++) { popupMenu1.add(new JMenuItem("Item " + i)); } JPanel panel1 = new JPanel(null); panel1.setBorder(BorderFactory.createTitledBorder("Popup Menu Test")); panel1.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (SwingUtilities.isRightMouseButton(e)) { popupMenu1.show(e.getComponent(), e.getX(), e.getY()); } } }); // initialize popup-sub-menu-test components final JPopupMenu popupMenu2 = new JPopupMenu(); for (int i = 1; i <= 3; i++) { JMenu menu2 = new JMenu("Menu Bar Test " + i); new MenuScroller(menu2, 15, 125, 5, 5); for (int j = 1; j <= 100; j++) { menu2.add(new JMenuItem("Item " + j + " (" + i + ")")); } popupMenu2.add(menu2); } JPanel panel2 = new JPanel(null); panel2.setBorder(BorderFactory.createTitledBorder("Popup Sub-Menu Test")); panel2.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (SwingUtilities.isRightMouseButton(e)) { popupMenu2.show(e.getComponent(), e.getX(), e.getY()); } } }); JPanel contentPane = new JPanel(new GridLayout(1, 2, 20, 20)); contentPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); contentPane.add(panel1); contentPane.add(panel2); JFrame frame = new JFrame("MenuScroller Test"); frame.setJMenuBar(menuBar); frame.setContentPane(contentPane); frame.setSize(800, 600); frame.setLocationRelativeTo(null); frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); frame.setVisible(true); } }); } }Darryl Burke said
Thank you Shaun for your suggestions. I’ll definitely look into them.
Darryl
Axel Dörfler said
Thanks for sharing this! However, I just tested using JDK 6 under Windows Vista (with “native” L&F), and I see two problems:
1. the scroll items often move during scrolling.
2. the popup window often (especially with a full screen window) opens at the wrong position (ie. the one it would have needed to open with the original number of items).
Note that I only used the MenuScroller on sub-menus of JPopupMenus so far, though. Unfortunately, these problems make it unusable at least in this case.
Jeremy said
Regarding item #2: we often saw the popup menu defaulting to the wrong position. Specifically: the first time the popup is shown, it may show several hundred hundred pixels above where it should. But if you exit and reenter that popup: subsequent showings are in the correct location.
I was able to work around this by adding this to the bottom of the MenuScroller constructor:
//trigger this once to resolve a bug:
//sometimes the context menu appears vertically several hundred
//pixels above where it should.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
menuListener.popupMenuWillBecomeVisible(null);
}
});
Kalyan said
Hi Jeremy, did you find any fix for the 2.) issue? This utility works like charm except for this issue and I am stuck because of it.
Jeremy said
If you copy the code above and paste it into the bottom of the MenuScroller constructor: that may resolve the offset problem. Have you tried that?
Kalyan said
Yes I did that but it did not solve the problem, can you share the reason why you this code could fix the problem?
Jeremy said
Hmm. Maybe the difference is when the scrollers are constructed? If you have a concise stand-alone sample, you can try emailing me (click through the link in my name to get my email) and I can look into it?
Jeremy said
I made an alternative work-around here: https://java.net/projects/javagraphics/sources/svn/content/trunk/src/com/bric/swing/ScrollingMenu.java
It’s not as elegant in design as the original MenuScroller, but it is easier to control the structure/placement of it. (My original work-around started failing when I tried to populate the JPopupMenu just before showing it.)
Chris Yates said
I found this to be just what I needed, but my users requested mouse scroll wheel ability. It was easy enough to add, but I’ve pasted it just to help out:
Darryl Burke said
Thank you, Chris.
Darryl
Simon said
That sounds interesting! To which object do you add this listener?
Thanks in advance!
Simon
April said
This was just what I was looking for. Thanks you for putting this out there.
Shylux said
Thanks for your great work! :D
Beirti said
Sweet as a nut! Cheers for that
Oncu Altuntas said
Thanks very good work, but there is a problem with Menus width. Menus with long labels not viewed properly. For example if the first menu label smaller than the 20th one than 20th is croped according to first one.
Darryl Burke said
Thanks for pointing that out. If I develop a solution, I’ll update the code.
Darryl
Anonymous said
Thanks for this utility. Saved me a bit of work.
About the crop problem, you can change the last 3 lines of code from refreshMenu() with
menu.pack();
It solves the cropping problem.
Anonymous said
Thanks..a good Utility class for JPopupMenu.
had one question, is it possible to have similar functionality on Mouse click event instead of mouse over scroll event.
Darryl Burke said
You would do that by taking the code in the MenuScrollTimer’s actionPerformed and putting it in an ActionListener added to the MenuScrollItem. Feel free to modify the code to suit your requirements.
Anonymous said
Darryl: Our group would like to use your MenuScroller code in our SIFT application. SIFT is a tsunami forecast system being built for the US Tsunami Warning Centers. Would you grant us permission to do so? If so, would you like to be acknowledged in our “About SIFT” window. Thanks in advance. I can be contacted at [email protected].
Darryl Burke said
Like all code published on the blog, MenuScroller is free to use, at your own risk . A link to the blog page in the doc comments or elsewhere would be nice, but is by no means mandatory.
Please do read the earlier comments for some limitations in the usage of this class. Please also feel free to modify the code (including doc comments) in any way that suits your application.
Darryl
Steve said
Hi Darryl. First let me thank you VERY much for creating this utility! I am on a tight schedule and the lack of a scrolling popup menu is the last item stopping us from shipping. To say the least I was very pleased to find your website.
I do have an issue however to report. In my application the popup menu items vary significantly in the number of characters per menu item. When the popup first appears (after a user clicking a control) the popup menu’s width is wide enough to accommodate the displayed number of menu items. As the user scrolls down however some menu items are longer than the popup’s width and are truncated.
So I guess my question is: is there someway the popup menu width can be autosized to accommodate the longest menu item width?
Again thanks for your efforts! I look forward to your reply.
Steve
Darryl Burke said
Thank you for the kind words, Steve. The size problem had already been reported by Oncu Altuntas at No. 10 above, but I never did get round to finding a workaround.
The only way I’m aware of to force a popup menu to resize to accommodate new or changed content is to hide and redisplay it, and the way to do that is to save the selected menu path, then clear and restore it. The code would be something like
final MenuElement[] path = manager.getSelectedPath(); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { manager.clearSelectedPath(); manager.setSelectedPath(path); } });This will cause a noticeable flicker when the menu extends beyond the bounds of its parent window, though.
Darryl
Steve said
Darryl – thanks for the quick response. In the meantime I found a workaround. As I add items to the menu I track the number of characters in each menu item. Right before displaying the menu I call “menu.setPopupSize(numChars * 10, menu.getHeight());”. Assuming that 10 pixels is the width of the largest character is admittedly a hack but works for my needs.
Simon said
The following patch should tackle the issue concerning the menu width.
--- MenuScroller.java.orig 2011-09-18 18:57:22.519438516 +0200 +++ MenuScroller.java 2011-09-18 19:01:44.051355009 +0200 @@ -456,20 +460,26 @@ } menu.add(downItem); if (bottomFixedCount > 0) { menu.add(new JSeparator()); } for (int i = menuItems.length - bottomFixedCount; i < menuItems.length; i++) { menu.add(menuItems[i]); } + int preferredWidth = 0; + for (Component item : menuItems) { + preferredWidth = Math.max(preferredWidth, item.getPreferredSize().width); + } + menu.setPreferredSize(new Dimension(preferredWidth, menu.getPreferredSize().height)); + JComponent parent = (JComponent) upItem.getParent(); parent.revalidate(); parent.repaint(); } } private class MenuScrollListener implements PopupMenuListener { @Override public void popupMenuWillBecomeVisible(PopupMenuEvent e) {Anonymous said
Thanks Darryl, that is just what I want now.
Huw said
Great post!
Couple of comments…
1. I found that to get the scroll wheel support in response 6 I had to include a event.consume(); in the handler. The listener should be added to the (JPopupMenu) menu.
2. When looking at an app like Chrome, its scrolling menus fill the available vertical space. I have achieved something like this with the following static helper that computes a scrollCount…
/** * Calculates the number for scrollCount such that the menu fills the available * vertical space from the point (mouse press) to the bottom of the screen. * * @param c The component on which the point parameter is based * @param pt The point at which the top of the menu will appear (in component coordinate space) * @param item A menuitem of prototypical height off of which the average height is determined * @param bottomFixedCount Needed to offset the returned scrollCount * @return the scrollCount */ public static int scrollCountForScreen(Component c, Point pt, JMenuItem item, int bottomFixedCount) { Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); Point ptScreen = new Point(pt); SwingUtilities.convertPointToScreen(ptScreen, c); int height = screenSize.height - ptScreen.y; int miHeight = item.getPreferredSize().height; int scrollCount = (height / miHeight) - bottomFixedCount - 2; // 2 just takes the menu up a bit from the bottom which looks nicer return scrollCount; }Paul said
Thanks Darryl :-)
Is it ok to use your code in a project which is distributed by the GNU GPU license?
I’d like to integrate this into Arduino, which is an open source program. It has a bug where a dynamically built JMenu becomes too tall if the user adds too many files. This looks like a perfect solution. I didn’t see any licensing info, so it seems safest to ask first. Thanks!
Darryl Burke said
Like I said in my reply to #12, all code published on the blog is free to use, at your own risk :) Do first read the comments about its limitations and the improvements suggested by some of the responders.
Darryl
Dave Benson said
Hey Darryl,
Thanks so much for writing this — it’s been a huge time saver for me.
BTW, my IDE (Eclipse) complained about a dozen or so override annotations toward the bottom of the file. To make it compile I had to comment them out. I’m not sure whether that due to a problem with your code, or my IDE’s finnickyness. I just thought I’d mention it for the benefit of future users.
Cheers,
D
Darryl Burke said
I’m glad the class was useful to you. The @Override annotation on interface method implementations is valid from Java 6 aka 1.6. Your IDE or project settings (compiler compliance level? I don’t use Eclipse) may be for an earlier version.
There is no problem with the @Override annotations in the code.
Darryl
David Mail said
Simply works right out of the box!
JPopupMenu popupMenu = new JPopupMenu(); // existed, showing a very long context menu with languages selection
MenuScroller.setScrollerFor( popupMenu); // one new line added
Great work, thank you Darryl
Anonymous said
I’m happy you found it useful.
Darryl
Anonymous said
Oops, I seem to have lost my login just around the time I posted that. But yes, it’s me :)
Darryl
Darryl Burke said
Something went wrong…
GoldenGnu said
I don’t know if you fixed this already or not, but I’ll post it just in case…
IMHO In the refreshMenu() method you should use:
menu.addSeparator();
OR
menu.add(new JPopupMenu.Separator());
And NOT:
menu.add(new JSeparator());
JSeparator will not have the default menu look and feel…
Thank you for this epic piece of code!
On a side note the link:
http://www.camick.com/java/source/MenuScroller.java
Is blocked by OpenDNS >_<
Darryl Burke said
Thank you for your suggestion. I don’t find the link to the source file blocked in any way.
Darryl
Milos Kleint said
any idea if this works on macosx and native main menu?
what is the license for the code?
Darryl Burke said
I don’t have a Mac nor access to one. Perhaps you could try and let us know here?
There’s no license, nor any guarantee. All code on the blog is offered on a ‘take it, use it, change it — just don’t cry about it!’ basis. If used in commercial software, we do request a credit in the doc or other code comments, but that is by no means mandatory.
Darryl
GrassEh said
Thank you very much, exactly what I was looking for. However, have you ever been working on a simple implementation of a scrollable JPopupMenu using JScrollPane ? I’ve been trying to be it but it’s not as easy as I was thinking. I’ve found some works on the web, but they are a bit complicated, usin JPanel or JWindow.
I’m looking for an easy and clean implementation of it, by starting to implement the Scrollable interface from the JPopupMenu for example.
Darryl Burke said
> have you ever been working on a simple implementation of a scrollable JPopupMenu using JScrollPane ?
Nope.
Darryl
Steve Cohen said
I’d like to see a main() for the demo that I can launch from here. Where is that?
Will said
Great work this is very helpful
Helmut Kleber said
Great work!
Brief Summary of enhancements.
1. Adapt the with of the items (comment 14, Simon):
Search for this string:
“JComponent parent = (JComponent) upItem.getParent();”
Insert above the following lines:
int preferredWidth = 0;
for (Component item : menuItems) {
preferredWidth = Math.max(preferredWidth, item.getPreferredSize().width);
}
menu.setPreferredSize(new Dimension(preferredWidth, menu.getPreferredSize().height));
2. Mouse Wheeling:
Add the following class to the MenuScroller class (Comment 6, Chris Yates):
private class MouseScrollListener implements MouseWheelListener {
public void mouseWheelMoved(MouseWheelEvent mwe){
firstIndex += mwe.getWheelRotation();
refreshMenu();
mwe.consume(); // (Comment 16, Huw)
}
}
Search for the line:
“menu.addPopupMenuListener(menuListener);”
Insert the following line below or above:
menu.addMouseWheelListener(new MouseScrollListener());
Simple to usage:
JMenu myMenu= new JMenu(“Menu”);
new MenuScroller(
myMenu, // The JMenu instance
20, // visible number of sub items
30, // delay at scrolling with the top/down buttons
0, // visible number of the first visible items out of the scrolling box
0 // visible number of the last visible items out of the scrolling box
);
Thats all, just one line!
Vincent said
It is also important to unregister the MouseWheelListener in dispose(), as follows:
private final MouseWheelListener mouseWheelListener = new MouseScrollListener();
…
public void dispose() {
if (menu != null) {
menu.removePopupMenuListener(menuListener);
menu.removeMouseWheelListener(mouseWheelListener);
menu = null;
}
}
John Mercier said
Wow this works really well. Are there any issues with using this in an open source project on google code?
Darryl Burke said
Thanks for the appreciation. As for your question, see my reply to comment #12 and #21.
Darryl
Seth said
Hi,
Thank you so much for this class. It really fills a needed gap in Swing for us.
I just wanted to mention that in refreshMenu(), you call revalidate() on the parent. I haven’t looked to deeply into why, but with JRE7, this is not sufficient anymore. The positions of the menu items do not get recalculated during scrolling, so it becomes a mess. My quickfix involves instead calling validate() instead, which fires the necessary doLayout() call.
Thanks,
Seth
Darryl Burke said
Thank you Seth. I’m presently in the throes of moving house, and down to a laptop with older versions of everything, but once I unpack and set up my computer I’ll see if I can identify where the relevant code changes are between JRE6 and 7.
Thank you for sharing your quick fix here.
Darryl
RedTulips said
Thanks a lot for this, it’s been really helpful!!!
I am having an issue where the scrollable menu is not laying out its components properly. The menu contains items with different heights. As I scroll down the menu, the height of the popup is not getting adjusted properly.
I tried the following in the MenuScroller.refreshMenu() method, but it did not work:
final MenuElement[] path = manager.getSelectedPath();
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
manager.clearSelectedPath();
manager.setSelectedPath(path);
}
});
Any other ideas?
RedTulips said
Ooops sorry my mistake, I meant to say I tried the following, but with no luck:
int preferredWidth = 0;
int preferredTotalHeight = 0;
for (Component item : mPopupMenu.getComponents()) {
preferredWidth = Math.max(preferredWidth, item.getPreferredSize().width);
preferredTotalHeight += item.getPreferredSize().height;
}
mPopupMenu.setPreferredSize(new Dimension(preferredWidth, preferredTotalHeight));
mPopupMenu.validate();
mPopupMenu.repaint();
Darryl Burke said
Sorry, I’ve never had to deal with menu items of differing heights, so your guess is as good as (or, more probably, better than!) mine.
Darryl
Helmut Kleber said
I am happy that I could contribute something to this code, in spite of that it took several hours. Im not good in java voodoo.
– Fix
Call in the following method “refreshOldStyleMenu();” in the else branch.
private void setMenuItems() {
………..
if (menuItems.size() > topFixedCount + scrollCount + bottomFixedCount) {
refreshMenu();
} else {
refreshOldStyleMenu();
}
}
Copy the following method in the “MenuScroller” class:
private void refreshOldStyleMenu() {
menu.removeAll();
int preferredWidth = 0;
int preferredTotalHeight = 0;
for (Component item : menuItems) {
menu.add(item);
preferredWidth = Math.max(preferredWidth, item.getPreferredSize().width);
preferredTotalHeight += item.getPreferredSize().height;
}
menu.setPreferredSize(new Dimension(preferredWidth, preferredTotalHeight));
}
– Optional
My first idea was the same, in “refreshMenu()”, it is not necessary, but it makes the code safer for later changes:
int preferredWidth = 0;
int preferredTotalHeight = 0;
for (Component item : menuItems) {
preferredWidth = Math.max(preferredWidth, item.getPreferredSize().width);
preferredTotalHeight += item.getPreferredSize().height;
}
menu.setPreferredSize(new Dimension(preferredWidth, preferredTotalHeight));
Dirk Steinkamp said
Hey Darryl,
thanks for your great blog! It’s a great resource. I tried the Menu Scroller and it worked fine for mouse usage.
The reason I was looking for it brought up another challenge which I couldn’t solve. I’m in the process of adapting an existing swing-application to be usable with tablet/touch-screen-devices.
And here’s the problem: with a touch-screen you can’t hover over a MenuItem – you just can click. But if clicking on the arrows the menu disappears :-(.
I tried to combine it with your suggestion on keeping menus open, but that doesn’t work – https://tips4java.wordpress.com/2010/09/12/keeping-menus-open/
Any ideas how to solve this?
Thanks
Dirk
Steve Cohen said
Very nice work. However, my requirement is basically to pretend the mouse doesn’t exist and just use the keyboard (up-arrow and down-arrow). I’d rather disable the whole timer business and make it work like page down. Is there anything in here for that or is that an enhancement (dehancement?) I should roll on my own.
Darryl Burke said
You should be able to do that with Key Bindings for Up, Down, PageUp and Page Down.
See the scroll wheel enabling by Chris at #6 for inspiration.
Darryl
Steve Cohen said
Key Bindings or a Key Listener? I went with the latter approach. I also wound up stripping out most of the logic associated with top and bottom fixed items. I also got rid of the timer and the scroll items. The Key Listener handles the up and down arrows and correctly arms the element it should. All I’ve winded up keeping is the logic of repopulating the menu when I scroll. That part works quite nicely. But there is one big problem – the Enter key doesn’t function. My key listener is not consuming the event, but nothing happens when the user presses Enter. I can listen for Enter in the KeyPressed event and perform the action associated with the armed menu item, but then the menu doesn’t disappear! I can make the menu disappear, but it has parents that also should disappear. All this is handled by normal menus, but I can’t figure out to do it here. Aargh!
Darryl Burke said
Swing is designed to work with Key Bindings. As for the other problems, if you are unable to solve them yourself I suggest you find a reasonably active Java forum and ask in their GUI / Swing section.
Steve Cohen said
I’m reasonably familiar with Key Bindings. I looked at the KeyBindings app looking for what actions the Enter or Escape keys might be bound to, I didn’t find anything I could bind to. And the comment in #6 you referred to also used a listener, which is why I went down the KeyListener path.
So I suppose you might have been suggesting binding the Up and Down Arrow keys to actions I would have to write and avoiding listeners (which I tend to dislike in general). Is that right?
Darryl Burke said
Well, the Swing team didn’t see fit to design for Mouse Bindings, so yes, the comment in #6 uses a listener.
The Actions you would use for the Up and Down keys would be on the lines of the anonymous ActionListener of the MenuScrollTimer.
Steve Cohen said
Got this working with a big assist from your associate Rob Camick over at Stack Overflow (thanks, Rob!). The key was to call MenuSelectionManager.defaultManager().clearSelectedPath(); on the Enter keypress after performing the action.
So I now have a nice little stripped down version of your system that is perfect for my needs. There is no scrolling with the mouse, which probably is a show-stopper for most situations but not for mine. Do you suppose anyone would be interested in it? It does sort of illustrate your basic concept (the refreshMenu() logic), which I have retained. If so, I guess I could post it.
Gigel said
Awesome, thanks!
Jim Nik said
Thank you very much for this work. I have a problem with it though:
I have a jMenu that gets populated several times during a session, with different jMenuItems each time (i.e. menu empties with jMenu.removeAll() and its jMenuItem content is regenerated).
The first time my jMenu gets populated, the MenuScroller works perfectly!
However, for every subsequent re-population of the same menu, the MenuScroller works completely erratically: for example double arrows may appear on both top and bottom, response is bad, scrolling arrows appear and disappear when trying to scroll one direction, and most often the menu content will get stuck at some point with no arrows to click on.
I have no idea why this happens in my case, any thoughts? Thanks again.
Jim Nik said
Just in case it is important to the problem, I am setting the scroller with the following parameters:
MenuScroller.setScrollerFor(myMenu, 30, 200, 0, 4);
Works great the first time myMenu gets populated. Becomes bugged, as described above, with each subsequent myMenu repopulation.
Darryl Burke said
You could try retaining a reference to the MenuScroller, then calling dispose() before removeAll() and then set a new MenuScroller after repopulating the menu.
Darryl
Jim Nik said
Darryl thank you very much for your response. It helped me realize that I was actually re-adding a MenuScroller with each repopulation of the menu.
Problem solved by adding the MenuScroller only once, upon the initialization of the empty jMenu. I tried it out with 4-5 repopulations: no problems at all.
Thank you once more for the excellent scroller and your prompt response.
Jim
PS. Previously, while experimenting with your code, I removed completely this part:
@Override
public void finalize() throws Throwable {
dispose();
}
And it still works fine. I left it out ever since. I really don’t know much about finalize() usage, I just read that it isn’t very good practice in general, although I couldn’t figure all the details.
Is it ok to leave this out or do you absolutely recommend including it?
Darryl Burke said
You can leave out the finalize() override if you want.
Darryl
김소현 said
Hi
I’m the beginner of JAVA who have taught my self with your code.
I want to ask you how can I use this Menu Scroller on JPanel.
It is not component so I can’t find the way to add it on JPanel.
Sorry to ask you simple question but I really really want to know…
Please answer my question…
Dirk Steinkamp said
For use with a JPanel you might consider JScrollPane instead. Or do you lack a special function there that you see better realized with Menu Scroller?
김소현 said
I solve that problem!!!
But I don’t know how I can resize the JPopupMenu that is applied Menu Scroller.
(I want to resize width)
Sorry for being bad at English …
I hope you can understand my question Darryl
김소현 said
(+Add) How could I resize MenuScroller?
Darryl Burke said
MenuScroller is designed for scrolling a menu. Not for use on a panel nor for managing the width of a popup.
Darryl
Anony-mouse said
this is very nice…have wanted this for some years now…
I detect a problem still. If you use the arrow keys (this is java 7 on OSX) the selection loops around at top or bottom. I’ve spent zero effort trying to figure it out.
I took all the remarks made about improvements and merged them into my copy of the code, it all runs great.
ponty said
Hi Darryl,I have used your code.In my code i set the scroller on a JMenu that is supposed to popup when i put the mouse over a jpopupMenu item but my problem is that sometimes the Jmenu pops in wrong vertical position with rispect to the JpopupMenu and the other times it shows up in the correct position.I am unable to figure out the cause of this behaviour.please help
Darryl Burke said
It’s simply not possible to help without seeing your code, and this blog isn’t the right place for that. I suggest you find an active Java forum like CodeRanch or Java-Forums.org and post an SSCCE (google that of you don’t know what it is).
Darryl
kaicertyrant said
Hi Darryl! is 2019 and we still using your awesome code! thx so much!
im having this issue i cant fix, we are using java.swing Java SE 1.8, the popup menu working fine! its awesome! but when i scroll down the items on the option i see a grey line under all the options and it something times moves, its on the height, the items acomodate good but under all theres that line, ill show you a screenshot o that, thx for your help!
screenshot: https://www.dropbox.com/s/ja7vtverazvz4fb/greyline.png?dl=0
Kaicer
Darryl Burke said
I just ran my test rig from 2009 under Java 8 and couldn’t reproduce the line seen on your screenshot.
I suggest you create an SSCCE and ask on a Java forum.
Darryl