停止JPopupMenu窃取焦点 [英] Stopping JPopupMenu stealing the focus

查看:175
本文介绍了停止JPopupMenu窃取焦点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 JTextField ,我希望能够建议匹配用户输入的结果。我将这些建议显示在 JPopupMenu 中的 JList 中。



不过,当通过 show(Component invoker,int x,int y) ,重点是从 JTextField



奇怪的是,如果我调用 setVisible(true)而不是焦点,但是 JPopupMenu 没有附加到任何面板,并且在打开该框时最小化应用程序时,它将保持在窗口上。



我也尝试使用 requestFocus()将焦点重置到 JTextField 但是之后我必须使用 SwingUtilities.invokeLater()来恢复插入符的位置,然后调用稍后的弄乱现有的内容/覆盖它/或做其他不可预知的事情。

我得到的代码是有效的:

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

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

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

任何人都可以建议最好的方法来显示 JPopupMenu 以编程方式,同时保留焦点在 JTextField

解决方案

<技术答案是将弹出窗口的focusable属性设置为false:
$ b $ pre $ popup.setFocusable(false);

含义是textField必须接管通常处理的所有键盘和鼠标触发的操作通过列表本身,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){
$ b $ @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来显示下拉菜单中对应于输入文本的选项(这里带有开始规则) p>

  //实例化一个可排序的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);

//实例化一个PatternModel来映射文本 - > pattern
final PatternModel model = new PatternModel();
model.setMatchRule(PatternModel.MATCH_RULE_STARTSWITH);
// listener哪个更新列表的RowFilter对模型的模式属性的更改
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更新模型的rawtext属性对字段的更改
DocumentListener documentListener = new DocumentListener(){

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


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

$ b 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);


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 a JPopupMenu.

However, when opening the popup menu programmatically via show(Component invoker, int x, int y), the focus is getting taken from the JTextField.

Strangely enough, if I call setVisible(true) instead, the focus is not stolen; but then the JPopupMenu is not attached to any panel, and when minimizing the application whilst the box is open, it stays painted on the window.

I've also tried to reset the focus to the JTextField using requestFocus(), but then I have to restore the caret position using SwingUtilities.invokeLater(), and the invoke later side of things is giving the user a slight margin to mess around with the existing contents / overwrite it / or do other unpredictable things.

The code I've got is effectively:

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

Can anyone suggest the best avenue to go down to show the JPopupMenu programmatically whilst preserving the focus on the JTextField?

解决方案

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

这篇关于停止JPopupMenu窃取焦点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆