2012-04-05 15 views
10

Tengo un JTextField para el que estoy esperando sugerir que los resultados coincidan con las entradas del usuario. Estoy mostrando estas sugerencias en un JList contenido dentro de un JPopupMenu.Detener JPopupMenu robando el foco

Sin embargo, al abrir el menú emergente de programación a través de show(Component invoker, int x, int y), el foco está siendo tomado de la JTextField.

Por extraño que parezca, si llamo a setVisible(true) en su lugar, el enfoque no es robado; pero entonces el JPopupMenu no está conectado a ningún panel, y cuando se minimiza la aplicación mientras el cuadro está abierto, permanece pintado en la ventana.

También he intentado restablecer el foco a la JTextField usando requestFocus(), pero luego tengo que restaurar la posición de intercalación utilizando SwingUtilities.invokeLater(), y la invocación de tarde lado de las cosas es dar al usuario un ligero margen para perder el tiempo con los contenidos existentes/sobrescribirlo/o hacer otras cosas impredecibles.

El código lo que tengo es efectiva:

JTextField field = new JTextField(); 
JPopupMenu menu = new JPopupMenu(); 

field.addKeyListener(new KeyAdapter() { 
    public void keyTyped(KeyEvent e) { 
     JList list = getAListOfResults(); 

     menu.add(list); 
     menu.show(field, 0, field.getHeight()); 
    } 
}); 

¿Puede alguien sugerir el mejor camino para ir hacia abajo para mostrar la programación JPopupMenu preservando al mismo tiempo el foco en el JTextField?

Respuesta

14

La respuesta técnica es establecer la propiedad enfocable de la ventana emergente para falsa:

popup.setFocusable(false); 

La implicación es que el campo de texto tiene que hacerse cargo de todas las acciones de teclado y ratón activados por que normalmente son manejados por el lista en sí, como sosmething:

final JList list = new JList(Locale.getAvailableLocales()); 
final JPopupMenu popup = new JPopupMenu(); 
popup.add(new JScrollPane(list)); 
popup.setFocusable(false); 
final JTextField field = new JTextField(20); 
Action down = new AbstractAction("nextElement") { 

    @Override 
    public void actionPerformed(ActionEvent e) { 
     int next = Math.min(list.getSelectedIndex() + 1, 
       list.getModel().getSize() - 1); 
     list.setSelectedIndex(next); 
     list.ensureIndexIsVisible(next); 
    } 
}; 
field.getActionMap().put("nextElement", down); 
field.getInputMap().put(
     KeyStroke.getKeyStroke("DOWN"), "nextElement"); 

medida que su contexto es muy similar a un JComboBox, es posible considerar la posibilidad de una mirada hacia las fuentes de BasicComboBoxUI y Basi cComboPopup.

Editar

Sólo por diversión, lo siguiente es no responder a la pregunta de enfoque :-) En su lugar, se muestra cómo utilizar un JXList sortable/filtrar para mostrar sólo las opciones en el menú desplegable que se corresponden al texto mecanografiado (aquí con un starts-con la regla)

// instantiate a sortable JXList 
final JXList list = new JXList(Locale.getAvailableLocales(), true); 
list.setSortOrder(SortOrder.ASCENDING); 

final JPopupMenu popup = new JPopupMenu(); 
popup.add(new JScrollPane(list)); 
popup.setFocusable(false); 
final JTextField field = new JTextField(20); 

// instantiate a PatternModel to map text --> pattern 
final PatternModel model = new PatternModel(); 
model.setMatchRule(PatternModel.MATCH_RULE_STARTSWITH); 
// listener which to update the list's RowFilter on changes to the model's pattern property 
PropertyChangeListener modelListener = new PropertyChangeListener() { 

    @Override 
    public void propertyChange(PropertyChangeEvent evt) { 
     if ("pattern".equals(evt.getPropertyName())) { 
      updateFilter((Pattern) evt.getNewValue()); 
     } 
    } 

    private void updateFilter(Pattern newValue) { 
     RowFilter<Object, Integer> filter = null; 
     if (newValue != null) { 
      filter = RowFilters.regexFilter(newValue); 
     } 
     list.setRowFilter(filter); 
    } 
}; 
model.addPropertyChangeListener(modelListener); 

// DocumentListener to update the model's rawtext property on changes to the field 
DocumentListener documentListener = new DocumentListener() { 

    @Override 
    public void removeUpdate(DocumentEvent e) { 
     updateAfterDocumentChange(); 
    } 

    @Override 
    public void insertUpdate(DocumentEvent e) { 
     updateAfterDocumentChange(); 
    } 

    private void updateAfterDocumentChange() { 
     if (!popup.isVisible()) { 
      popup.show(field, 0, field.getHeight()); 
     } 
     model.setRawText(field.getText()); 
    } 

    @Override 
    public void changedUpdate(DocumentEvent e) { 
    } 
}; 
field.getDocument().addDocumentListener(documentListener); 
+0

La gran respuesta ... funciona perfectamente ... gracias;) – Isaac

+0

Esto me ayudó mucho, ¡gracias! – Epaga

+0

Después de mucho googlear, esta respuesta me ayudó mucho –

2

Parece sencillo para mí. Agregue el siguiente

field.requestFocus();

después

menu.add(list); 
menu.show(field, 0, field.getHeight()); 

Por supuesto, tendrá que codificar cuando para ocultar el etc emergente en base a lo que está pasando con el JTextField.

i.e;

menu.show(field, field.getX(), field.getY()+field.getHeight()); 
menu.setVisible(true); 
field.requestFocus(); 
+0

Gracias por su respuesta; desafortunadamente, ya he probado este enfoque. Se vuelve más complicado ya que luego tengo que restaurar la posición de intercalación usando 'SwingUtilities.invokeLater()', y el lado * invoke later * le da al usuario un pequeño margen para jugar con los contenidos existentes/sobrescribirlo/o hacer otras cosas impredecibles. – Isaac

Cuestiones relacionadas