Stopping JPopupMenu stealing the focus

前端 未结 3 1566
感动是毒
感动是毒 2021-02-05 21:18

I have a JTextField for which I\'m hoping to suggest results to match the user\'s input. I\'m displaying these suggestions in a JList contained within

3条回答
  •  野的像风
    2021-02-05 21:42

    The technical answer is to set the popup's focusable property to false:

    popup.setFocusable(false);
    

    The implication is that the textField has to take over all keyboard and mouse-triggered actions that are normally handled by the list itself, sosmething like:

    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");
    

    As your context is very similar to a JComboBox, you might consider having a look into the sources of BasicComboBoxUI and BasicComboPopup.

    Edit

    Just for fun, the following is not answering the focus question :-) Instead, it demonstrates how to use a sortable/filterable JXList to show only the options in the dropdown which correspond to the typed text (here with a starts-with rule)

    // 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 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);
    

提交回复
热议问题