Прекращение кражи JPopupMenu фокуса

У меня есть JTextField, для которого я надеюсь предложить результаты, соответствующие вводу пользователя. Я показываю эти предложения в JList, содержащемся в JPopupMenu.

Однако при программном открытии всплывающего меню через show(Component invoker, int x, int y), фокус переносится с JTextField.

Как ни странно, если вместо этого я вызову setVisible(true), фокус не украден; но тогда JPopupMenu не привязан ни к одной панели, и при сворачивании приложения, когда окно открыто, оно остается нарисованным в окне.

Я также пытался сбросить фокус на JTextField с помощью requestFocus(), но затем мне приходится восстанавливать положение курсора с помощью SwingUtilities.invokeLater(), а сторона вызвать позже дает пользователю небольшое поле для беспорядка с существующим содержимым / перезаписать его / или сделать другие непредсказуемые вещи.

Код, который у меня есть, эффективно:

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());
    }
});

Может ли кто-нибудь предложить лучший способ спуститься, чтобы показать JPopupMenu программно, сохраняя при этом фокус на JTextField?


person Isaac    schedule 05.04.2012    source источник


Ответы (3)


Технический ответ заключается в том, чтобы установить для свойства focusable всплывающего окна значение false:

popup.setFocusable(false);

Подразумевается, что textField должен взять на себя все действия, запускаемые клавиатурой и мышью, которые обычно обрабатываются самим списком, например:

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

Поскольку ваш контекст очень похож на JComboBox, вы можете рассмотреть исходные коды BasicComboBoxUI и BasicComboPopup.

Изменить

Просто для удовольствия, следующее не отвечает на основной вопрос :-) Вместо этого оно демонстрирует, как использовать сортируемый/фильтруемый JXList для отображения только тех параметров в раскрывающемся списке, которые соответствуют введенному тексту (здесь с правилом «начинается с»)

// 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);
person kleopatra    schedule 06.04.2012
comment
После долгих поисков этот ответ мне очень помог - person Saikiran Gosikonda; 01.12.2015

Он смотрит прямо на меня. Добавьте следующее

field.requestFocus();

после

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

Конечно, вам придется кодировать, когда скрывать всплывающее окно и т. д., в зависимости от того, что происходит с файлом JTextField.

i.e;

 menu.show(field, field.getX(), field.getY()+field.getHeight());
 menu.setVisible(true);
 field.requestFocus();
person ring bearer    schedule 05.04.2012
comment
Спасибо за Ваш ответ; к сожалению, я уже пробовал этот подход. Это усложняется, так как затем мне приходится восстанавливать позицию курсора с помощью SwingUtilities.invokeLater(), а сторона вызвать позже дает пользователю небольшой запас, чтобы возиться с существующим содержимым / перезаписывать его / или делать другие непредсказуемые вещи. - person Isaac; 06.04.2012

Вы можете взглянуть на JXSearchField, который является частью xswingx.

person michelemarcon    schedule 10.08.2012