java swing jpopupmenu

java - ¿Agregar desplazamiento vertical a un JPopupMenu?



swing (7)

Además del JScrollPopupMenu anterior, también necesitaba una barra de desplazamiento en un submenú (también conocido como "Pull Right Menu"). Este parece ser un caso más común. Así que adapté un JMenu para usar el JScrollPopupMenu llamado JScrollMenu:

import javax.swing.Action; import javax.swing.JButton; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.MenuElement; import javax.swing.UIManager; import javax.swing.plaf.MenuItemUI; import javax.swing.plaf.PopupMenuUI; import java.awt.Component; import java.awt.ComponentOrientation; public class JScrollMenu extends JMenu { // Covers the one in the JMenu because the method that creates it in JMenu is private /** The popup menu portion of the menu.*/ private JPopupMenu popupMenu; /** * Constructs a new <code>JMenu</code> with no text. */ public JScrollMenu() { this(""); } /** * Constructs a new <code>JMenu</code> with the supplied string as its text. * * @param s the text for the menu label */ public JScrollMenu(String s) { super(s); } /** * Constructs a menu whose properties are taken from the <code>Action</code> supplied. * * @param a an <code>Action</code> */ public JScrollMenu(Action a) { this(); setAction(a); } /** * Lazily creates the popup menu. This method will create the popup using the <code>JScrollPopupMenu</code> class. */ protected void ensurePopupMenuCreated() { if(popupMenu == null) { this.popupMenu = new JScrollPopupMenu(); popupMenu.setInvoker(this); popupListener = createWinListener(popupMenu); } } ////////////////////////////// //// All of these methods are necessary because ensurePopupMenuCreated() is private in JMenu ////////////////////////////// @Override public void updateUI() { setUI((MenuItemUI) UIManager.getUI(this)); if(popupMenu != null) { popupMenu.setUI((PopupMenuUI) UIManager.getUI(popupMenu)); } } @Override public boolean isPopupMenuVisible() { ensurePopupMenuCreated(); return popupMenu.isVisible(); } @Override public void setMenuLocation(int x, int y) { super.setMenuLocation(x, y); if(popupMenu != null) { popupMenu.setLocation(x, y); } } @Override public JMenuItem add(JMenuItem menuItem) { ensurePopupMenuCreated(); return popupMenu.add(menuItem); } @Override public Component add(Component c) { ensurePopupMenuCreated(); popupMenu.add(c); return c; } @Override public Component add(Component c, int index) { ensurePopupMenuCreated(); popupMenu.add(c, index); return c; } @Override public void addSeparator() { ensurePopupMenuCreated(); popupMenu.addSeparator(); } @Override public void insert(String s, int pos) { if(pos < 0) { throw new IllegalArgumentException("index less than zero."); } ensurePopupMenuCreated(); popupMenu.insert(new JMenuItem(s), pos); } @Override public JMenuItem insert(JMenuItem mi, int pos) { if(pos < 0) { throw new IllegalArgumentException("index less than zero."); } ensurePopupMenuCreated(); popupMenu.insert(mi, pos); return mi; } @Override public JMenuItem insert(Action a, int pos) { if(pos < 0) { throw new IllegalArgumentException("index less than zero."); } ensurePopupMenuCreated(); JMenuItem mi = new JMenuItem(a); mi.setHorizontalTextPosition(JButton.TRAILING); mi.setVerticalTextPosition(JButton.CENTER); popupMenu.insert(mi, pos); return mi; } @Override public void insertSeparator(int index) { if(index < 0) { throw new IllegalArgumentException("index less than zero."); } ensurePopupMenuCreated(); popupMenu.insert(new JPopupMenu.Separator(), index); } @Override public void remove(JMenuItem item) { if(popupMenu != null){ popupMenu.remove(item); } } @Override public void remove(int pos) { if(pos < 0) { throw new IllegalArgumentException("index less than zero."); } if(pos > getItemCount()) { throw new IllegalArgumentException("index greater than the number of items."); } if(popupMenu != null){ popupMenu.remove(pos); } } @Override public void remove(Component c) { if(popupMenu != null){ popupMenu.remove(c); } } @Override public void removeAll() { if(popupMenu != null){ popupMenu.removeAll(); } } @Override public int getMenuComponentCount() { return (popupMenu == null) ? 0 : popupMenu.getComponentCount(); } @Override public Component getMenuComponent(int n) { return (popupMenu == null) ? null : popupMenu.getComponent(n); } @Override public Component[] getMenuComponents() { return (popupMenu == null) ? new Component[0] : popupMenu.getComponents(); } @Override public JPopupMenu getPopupMenu() { ensurePopupMenuCreated(); return popupMenu; } @Override public MenuElement[] getSubElements() { return popupMenu == null ? new MenuElement[0] : new MenuElement[]{popupMenu}; } @Override public void applyComponentOrientation(ComponentOrientation o) { super.applyComponentOrientation(o); if(popupMenu != null) { int ncomponents = getMenuComponentCount(); for(int i = 0; i < ncomponents; ++i) { getMenuComponent(i).applyComponentOrientation(o); } popupMenu.setComponentOrientation(o); } } @Override public void setComponentOrientation(ComponentOrientation o) { super.setComponentOrientation(o); if(popupMenu != null) { popupMenu.setComponentOrientation(o); } } }

Me gustaría agregar una forma de desplazarse por los elementos del menú en un JPopupMenu , de forma muy similar a desplazarse por una lista de elementos en un JComboBox .

Digamos que tengo 10 elementos de menú. Me gustaría mostrar solo 5 elementos de menú a la vez, y usaría un botón de desplazamiento vertical en la parte inferior o superior del JPopupMenu para mostrar los elementos del menú que no están en la lista y ocultar los que acabo de ver.

¿Es posible? Estoy usando JideSplitButton de JIDE Software, que muestra un JPopupMenu cuando se hace clic. JideSplitButton mantener el aspecto y la sensación de la barra de comandos en la que coloqué el JideSplitButton , por lo que no quiero reemplazarlo con un JComboBox menos que realmente tenga que hacerlo.




Aquí hay una versión que creé usando una barra de desplazamiento. Es solo un ejemplo simple, así que adaptate como mejor te parezca:

import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Insets; import java.awt.LayoutManager; import java.awt.event.AdjustmentEvent; import java.awt.event.AdjustmentListener; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; import javax.swing.JPopupMenu; import javax.swing.JScrollBar; public class JScrollPopupMenu extends JPopupMenu { protected int maximumVisibleRows = 10; public JScrollPopupMenu() { this(null); } public JScrollPopupMenu(String label) { super(label); setLayout(new ScrollPopupMenuLayout()); super.add(getScrollBar()); addMouseWheelListener(new MouseWheelListener() { @Override public void mouseWheelMoved(MouseWheelEvent event) { JScrollBar scrollBar = getScrollBar(); int amount = (event.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) ? event.getUnitsToScroll() * scrollBar.getUnitIncrement() : (event.getWheelRotation() < 0 ? -1 : 1) * scrollBar.getBlockIncrement(); scrollBar.setValue(scrollBar.getValue() + amount); event.consume(); } }); } private JScrollBar popupScrollBar; protected JScrollBar getScrollBar() { if(popupScrollBar == null) { popupScrollBar = new JScrollBar(JScrollBar.VERTICAL); popupScrollBar.addAdjustmentListener(new AdjustmentListener() { @Override public void adjustmentValueChanged(AdjustmentEvent e) { doLayout(); repaint(); } }); popupScrollBar.setVisible(false); } return popupScrollBar; } public int getMaximumVisibleRows() { return maximumVisibleRows; } public void setMaximumVisibleRows(int maximumVisibleRows) { this.maximumVisibleRows = maximumVisibleRows; } public void paintChildren(Graphics g){ Insets insets = getInsets(); g.clipRect(insets.left, insets.top, getWidth(), getHeight() - insets.top - insets.bottom); super.paintChildren(g); } protected void addImpl(Component comp, Object constraints, int index) { super.addImpl(comp, constraints, index); if(maximumVisibleRows < getComponentCount()-1) { getScrollBar().setVisible(true); } } public void remove(int index) { // can''t remove the scrollbar ++index; super.remove(index); if(maximumVisibleRows >= getComponentCount()-1) { getScrollBar().setVisible(false); } } public void show(Component invoker, int x, int y){ JScrollBar scrollBar = getScrollBar(); if(scrollBar.isVisible()){ int extent = 0; int max = 0; int i = 0; int unit = -1; int width = 0; for(Component comp : getComponents()) { if(!(comp instanceof JScrollBar)) { Dimension preferredSize = comp.getPreferredSize(); width = Math.max(width, preferredSize.width); if(unit < 0){ unit = preferredSize.height; } if(i++ < maximumVisibleRows) { extent += preferredSize.height; } max += preferredSize.height; } } Insets insets = getInsets(); int widthMargin = insets.left + insets.right; int heightMargin = insets.top + insets.bottom; scrollBar.setUnitIncrement(unit); scrollBar.setBlockIncrement(extent); scrollBar.setValues(0, heightMargin + extent, 0, heightMargin + max); width += scrollBar.getPreferredSize().width + widthMargin; int height = heightMargin + extent; setPopupSize(new Dimension(width, height)); } super.show(invoker, x, y); } protected static class ScrollPopupMenuLayout implements LayoutManager{ @Override public void addLayoutComponent(String name, Component comp) {} @Override public void removeLayoutComponent(Component comp) {} @Override public Dimension preferredLayoutSize(Container parent) { int visibleAmount = Integer.MAX_VALUE; Dimension dim = new Dimension(); for(Component comp :parent.getComponents()){ if(comp.isVisible()) { if(comp instanceof JScrollBar){ JScrollBar scrollBar = (JScrollBar) comp; visibleAmount = scrollBar.getVisibleAmount(); } else { Dimension pref = comp.getPreferredSize(); dim.width = Math.max(dim.width, pref.width); dim.height += pref.height; } } } Insets insets = parent.getInsets(); dim.height = Math.min(dim.height + insets.top + insets.bottom, visibleAmount); return dim; } @Override public Dimension minimumLayoutSize(Container parent) { int visibleAmount = Integer.MAX_VALUE; Dimension dim = new Dimension(); for(Component comp : parent.getComponents()) { if(comp.isVisible()){ if(comp instanceof JScrollBar) { JScrollBar scrollBar = (JScrollBar) comp; visibleAmount = scrollBar.getVisibleAmount(); } else { Dimension min = comp.getMinimumSize(); dim.width = Math.max(dim.width, min.width); dim.height += min.height; } } } Insets insets = parent.getInsets(); dim.height = Math.min(dim.height + insets.top + insets.bottom, visibleAmount); return dim; } @Override public void layoutContainer(Container parent) { Insets insets = parent.getInsets(); int width = parent.getWidth() - insets.left - insets.right; int height = parent.getHeight() - insets.top - insets.bottom; int x = insets.left; int y = insets.top; int position = 0; for(Component comp : parent.getComponents()) { if((comp instanceof JScrollBar) && comp.isVisible()) { JScrollBar scrollBar = (JScrollBar) comp; Dimension dim = scrollBar.getPreferredSize(); scrollBar.setBounds(x + width-dim.width, y, dim.width, height); width -= dim.width; position = scrollBar.getValue(); } } y -= position; for(Component comp : parent.getComponents()) { if(!(comp instanceof JScrollBar) && comp.isVisible()) { Dimension pref = comp.getPreferredSize(); comp.setBounds(x, y, width, pref.height); y += pref.height; } } } } }


Básicamente puede agregar cualquier JComponents al JPopupMenu, puede agregar JScrollpane al JPopup anidando JPanel / JList con otros JComponents,

Aviso pero hay una regla de que la GUI de oscilación no permite dos ventanas emergentes de peso ligero al mismo tiempo, el mejor ejemplo es el error común en Swing sobre JComboBox en el JPopup

tienes que mirar JWindow, crear una vez y volver a usar eso para otra Acción, nada mejor para comprobar cómo funciona JWindow realmente emergente para JCalendar por Kai Toedter


Como necesitaba el menú emergente con la barra de desplazamiento, simplemente reutilicé el menú emergente de JComboBox. El truco fue poner JComboBox en un JViewport, de modo que solo el botón de flecha fuera visible. Puede hacer que solo un píxel sea pequeño o incluso menor y usar eventos de JideSplitButton para abrir ventanas emergentes.

Puede encontrar el código en github .