2009-01-27 20 views
6

En el pasado, cuando se hizo una JPopupMenu visible es el primer artículo conseguiría seleccionado por defecto: http://weblogs.java.net/blog/alexfromsun/archive/2008/02/jtrayicon_updat.html¿Cómo seleccionar el primer elemento en JPopupMenu?

Hoy en día el comportamiento por defecto es para que aparezca el menú sin ningún elemento seleccionado. Me gustaría crear un JPopupMenu con un solo elemento que aparecerá seleccionado y centrado bajo el puntero del mouse. He logrado que el elemento aparezca emergente centrado bajo el mouse pero I JMenuItem se niega a procesar como si estuviera seleccionado. Si retiro el mouse del elemento y lo vuelvo a seleccionar, lo seleccionará correctamente.

¿Alguna idea?

Aquí es mi caso de prueba:

import java.awt.Component; 
import java.awt.Point; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.event.MouseAdapter; 
import java.awt.event.MouseEvent; 
import javax.swing.JFrame; 
import javax.swing.JMenuItem; 
import javax.swing.JPopupMenu; 

public class Test extends JFrame 
{ 
    public static void main(String[] args) 
    { 
     JFrame frame = new JFrame(); 
     frame.setSize(800, 600); 
     frame.getContentPane().addMouseListener(new MouseAdapter() 
     { 
      @Override 
      public void mousePressed(MouseEvent e) 
      { 
       if (e.isPopupTrigger()) 
        popupTriggered(e); 
      } 

      @Override 
      public void mouseReleased(MouseEvent e) 
      { 
       if (e.isPopupTrigger()) 
        popupTriggered(e); 
      } 

      private void popupTriggered(MouseEvent e) 
      { 
       JPopupMenu menu = new JPopupMenu(); 
       final JMenuItem item = new JMenuItem("This is a JMenuItem"); 
       menu.add(item); 
       Point point = e.getPoint(); 
       int x = point.x - (item.getPreferredSize().width/2); 
       int y = point.y - (item.getPreferredSize().height/2); 
       menu.show((Component) e.getSource(), x, y); 
      } 
     }); 
     frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 
     frame.setVisible(true); 
    } 
} 
+0

Encontré un comportamiento incoherente también (ver mi respuesta actualizada). ¿Puedes confirmar esto? –

+0

Informé esto como un error a Sun. Te dejaré saber lo que escriben. – Gili

+0

Aquí está el informe de errores asociado: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6799989 – Gili

Respuesta

0

Hoy en día el comportamiento por defecto es para que aparezca el menú sin ningún elemento seleccionado.

En realidad, yo diría que este es el comportamiento correcto, al menos en Windows. Otras aplicaciones que no son Java también lo hacen. No creo que valga la pena romper esta convención, incluso si solo hay un elemento en el menú. Si opina lo contrario, puede establecer el índice de selección como en sean.bright's answer.


Así, finalmente me dieron la oportunidad de probarlo en Java 1.6.0_11, y encontré un comportamiento inconsistente: Si el menú emergente sobresale del marco padre, el elemento se selecciona automáticamente; si el menú emergente aparece por completo dentro del cuadro principal, no se selecciona nada. Suena como un error de Swing, que al menos garantiza un RFE para un comportamiento consistente.

+0

+1: El comportamiento correcto es lo que hace la plataforma particular para otros programas (que no sean Java). –

+0

Zach, estoy de acuerdo con su evaluación general, pero lamentablemente esto no responde mi pregunta. Yo * quiero * alejarme del comportamiento convencional pero me resulta imposible hacerlo. Estoy tratando de averiguar si esto es un error de Swing o de usuario. – Gili

+0

Archivé un informe de error con Sun: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6799989 – Gili

0

Esto es extraño.

he probado con Windows y con Java 1.5.0_08 e incluso 1.6.0_07 El primer elemento se selecciona de forma automática, como se esperaba que fuera.

así que lo intenté con 1.6.0_11, y no funciona más, el primer elemento no está seleccionado inicialmente. Seleccionar el elemento en selectionModel no parece ayudar.

Una solución (que no me enorgullece) es mover el mouse automáticamente después de mostrar el menú emergente, usando las coordenadas MouseEvent. Tal vez alguien tiene una mejor idea?

import java.awt.AWTException; 
import java.awt.Robot; 
import java.awt.event.MouseAdapter; 
import java.awt.event.MouseEvent; 

import javax.swing.JFrame; 
import javax.swing.JMenuItem; 
import javax.swing.JPopupMenu; 

public class SelectedPopupMenu extends JFrame { 

    public SelectedPopupMenu() { 
     addMouseListener(new MouseAdapter() { 
      public void mouseClicked(final MouseEvent e) { 
       JPopupMenu popupMenu = new JPopupMenu(); 
       popupMenu.add(new JMenuItem("Test-Item")); 
       popupMenu.add(new JMenuItem("Test-Item-2")); 
       // do not care to really hit the center of the popup 
       popupMenu.show(SelectedPopupMenu.this, e.getX() - 30, e.getY() - 10); 
       try { 
        // shake mouse, so that first element is selected even in Java 1.6.0_11 
        Robot robot = new Robot(); 
        robot.mouseMove(e.getX() + 1, e.getY()); 
        robot.mouseMove(e.getX(), e.getY()); 
       } catch (AWTException ex) { 
        ex.printStackTrace(); 
       } 
      } 
     }); 
    } 

    public static void main(String[] args) { 
     JFrame frame = new SelectedPopupMenu(); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.setSize(800, 600); 
     frame.setVisible(true); 
    } 
} 
6

secreto resulta ser MenuSelectionManager.defaultManager().setSelectedPath(new MenuElement[]{menu, ...});

import java.awt.event.MouseAdapter; 
import java.awt.event.MouseEvent; 
import javax.swing.JFrame; 
import javax.swing.JMenuItem; 
import javax.swing.JPopupMenu; 
import javax.swing.MenuElement; 
import javax.swing.MenuSelectionManager; 
import javax.swing.SwingUtilities; 

/** 
* Demonstrates programmatic {@link JMenuItem} selection; 
* specifically how to make the first item selected by default 
*/ 
public class TestPopup extends JFrame { 
    public static void main(String[] args) { 
    final JFrame frame = new JFrame("TestPopup"); 
    frame.setSize(640, 480); 
    frame.getContentPane().addMouseListener(new MouseAdapter() { 
     @Override 
     public void mousePressed(MouseEvent e) { 
     if (e.isPopupTrigger()) { 
      popupTriggered(e); 
     } 
     } 
     private void popupTriggered(MouseEvent e) { 
     final JPopupMenu menu = new JPopupMenu(); 
     final JMenuItem item0 = new JMenuItem("JMenuItem 0"); 
     final JMenuItem item1 = new JMenuItem("JMenuItem 1"); 
     menu.add(item0); 
     menu.add(item1); 
     menu.pack(); 
     // use invokeLater or just do this after the menu has been shown 
     SwingUtilities.invokeLater(new Runnable() { 
      public void run() { 
      MenuSelectionManager.defaultManager().setSelectedPath(new MenuElement[]{menu, item0}); 
      } 
     }); 
     int x = (int) ((int) (frame.getSize().width - (menu.getPreferredSize().width/2.))/2.); 
     int y = (int) ((int) (frame.getSize().height - (menu.getPreferredSize().height/2.))/2.); 
     menu.show(frame, x, y); 
     // doesn't work: 
     //item0.setSelected(true); 
     // doesn't work: 
     //menu.getSelectionModel().setSelectedIndex(0); 
     // bingo; see also MenuKeyListener/MenuKeyEvent 
//  MenuSelectionManager.defaultManager().setSelectedPath(new MenuElement[]{menu, item0}); 
     } 
    }); 
    frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 
    frame.setLocationRelativeTo(null); 
    frame.setVisible(true); 
    } 
} 
1

MenuSelectionManager.defaultManager() es de hecho una buena solución, pero no funcionará cuando se le intenta preseleccionar sub menús del JPopupMenu (ocultará el menú principal). Además, confunde otros comportamientos de navegación del teclado (no puede presionar hacia la izquierda para ocultar el submenú, etc.)

Desafortunadamente, no hay una buena solución para esta pregunta en Swing ... Mi solución es feo, pero lamentablemente hace el trabajo perfecto:

public static void setMenuSelectedIndex(final JPopupMenu popupMenu, final int index) { 
    SwingUtilities.invokeLater(new Runnable(){public void run() 
    { 
     for (int i=0; i < index+1; i++) { 
      popupMenu.dispatchEvent(new KeyEvent(popupMenu, KeyEvent.KEY_PRESSED, 0, 0, KeyEvent.VK_DOWN, '\0')); 
     } 
    }}); 
} 

Como se puede ver, estoy básicamente simulando 'abajo' del teclado pulsación de teclas en el popupmenu ...

una solución mejor podría no ser para simular de forma rígida VK_DOWN, pero leer el mapa de entrada de Popup y determinar qué código de clave significa "seleccionar el siguiente elemento de menú" - pero creo que la mayoría de nosotros se llevaría bien con este truco ...

Es posible que también desee para ver este método, que selecciona un elemento del menú una vez que se selecciona Utiliza el método anterior d

public static void setSelectedIndexWhenVisible(final JMenu menu, final int index) { 
    menu.getPopupMenu().addPopupMenuListener(new PopupMenuListener() { 
     @Override 
     public void popupMenuWillBecomeVisible(PopupMenuEvent e) { 
      PopupUtils.setMenuSelectedIndex(menu.getPopupMenu(), index); 
      menu.getPopupMenu().removePopupMenuListener(this); 
     } 

     @Override 
     public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { 
     } 

     @Override 
     public void popupMenuCanceled(PopupMenuEvent e) { 
     } 
    }); 
} 
+0

¿Qué es PopupUtils? No parece ser parte de mi JDK. –

+0

aparte de eso, la solución de simulación de la tecla de flecha hacia abajo funciona. –

Cuestiones relacionadas