简体   繁体   English

使用箭头键控制连接到 TextField 的 JavaFX ContextMenu

[英]Controlling a JavaFX ContextMenu connected to a TextField with the arrow keys

I am trying to make an autocomplete style TextField that uses a ContextMenu to display the suggestions below the TextField.我正在尝试制作一个自动完成样式的TextField ,它使用ContextMenu来显示 TextField 下方的建议。 I would like for the ContextMenu to be displayed when a user presses the down key while focus is on the TextField.我希望当用户在焦点位于 TextField 时按下向下键时显示 ContextMenu。 This is my current solution:这是我目前的解决方案:

setOnKeyPressed(event -> {
        System.out.println("pressed " + event.getCode());
        switch (event.getCode()) {
            case DOWN:
                if(getText().length()>0) {
                    if (!suggestionMenu.isShowing()) {
                        suggestionMenu.show(AutoCompleteTextField.this, Side.BOTTOM, 0, 0);
                    }
                    suggestionMenu.getSkin().getNode().lookup(".menu-item").requestFocus();
                }
                break;
        }
    });

Source: ContextMenu and programmatically selecting an item来源: ContextMenu 并以编程方式选择一个项目

Using this code, the down arrow always "selects" (colours blue) the first item in the list.使用此代码,向下箭头始终“选择”(蓝色)列表中的第一项。 The problem is that sometimes (seems random to me), the second arrow key-press will not yield any response in the ContextMenu - the first item will stay selected.问题是有时(对我来说似乎是随机的),第二次按箭头键不会在 ContextMenu 中产生任何响应 - 第一个项目将保持选中状态。 After that press, it will always work fine.按下后,它会一直正常工作。

I would also prefer that pressing up while having the first element selected would hide the ContextMenu, and that space would not fire the onAction method of the MenuItem s, though I don't really understand how the focus/event listening for this menu works.我还希望在选择第一个元素时按下将隐藏 ContextMenu,并且该空间不会触发MenuItemonAction方法,尽管我并不真正了解监听此菜单的焦点/事件是如何工作的。 It seems like the keyboard has two focuses at once - up/down, spacebar and enter on the ContextMenu, while everything else goes to the TextField.键盘似乎同时有两个焦点 - 上/下、空格键和在 ContextMenu 上输入,而其他所有内容都转到 TextField。

Edit: Here is a complete example.编辑:这是一个完整的例子。 When showing the ContextMenu using the arrow down key, sometimes it will cause the problematic behaviour, other times not.使用向下箭头键显示 ContextMenu 时,有时会导致有问题的行为,有时则不会。

Main.java主程序

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class Main extends Application {
    @Override
    public void start(Stage stage) throws Exception {
        BorderPane pane = new BorderPane();
        AutoCompleteTextField actf = new AutoCompleteTextField();
        pane.setTop(actf);
        stage.setScene(new Scene(pane));
        stage.show();
    }
}

AutoCompleteTextField.java自动完成文本字段.java

import javafx.geometry.Side;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.CustomMenuItem;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;

public class AutoCompleteTextField extends TextField {
    private ContextMenu suggestionMenu;

    public AutoCompleteTextField(){
        super();
        suggestionMenu = new ContextMenu();
        for(int i = 0; i<5; i++) {
            CustomMenuItem item = new CustomMenuItem(new Label("Item "+i), true);
            item.setOnAction(event -> {
                setText("selected");
                positionCaret(getText().length());
                suggestionMenu.hide();
            });
            suggestionMenu.getItems().add(item);
        }

        textProperty().addListener((observable, oldValue, newValue) -> {
            if(getText().length()>0){
                if (!suggestionMenu.isShowing())
                    suggestionMenu.show(AutoCompleteTextField.this, Side.BOTTOM, 0, 0);
            } else {
                suggestionMenu.hide();
            }
        });

        setOnKeyPressed(event -> {
            System.out.println("pressed " + event.getCode());
            switch (event.getCode()) {
                case DOWN:
                    if(getText().length()>0) {
                        if (!suggestionMenu.isShowing()) {
                            suggestionMenu.show(AutoCompleteTextField.this, Side.BOTTOM, 0, 0);
                        }
                        suggestionMenu.getSkin().getNode().lookup(".menu-item").requestFocus();
                    }
                    break;
            }
        });


    }
}

I coincidentally had the same problem this week.我这周碰巧遇到了同样的问题。 After digging for a while I created a solution based on this and the ContextMenuContent.class itself.在挖掘了一段时间后,我创建了一个基于和 ContextMenuContent.class 本身的解决方案。

The ContextMenuContent, as kleopatra suggested, does most of the keybindings.正如 kleopatra 所建议的,ContextMenuContent 执行大部分键绑定。 Inside, there is a very convenient method called requestFocusOnIndex() which allowed me to get rid that weird behavior.在里面,有一个非常方便的方法叫做 requestFocusOnIndex() ,它允许我摆脱这种奇怪的行为。

So your first code would be:所以你的第一个代码是:

textField.addEventFilter(KeyEvent.KEY_PRESSED, event->{
    if(event.getCode() == KeyCode.DOWN) {
        if(!suggestionMenu.isShowing())
            suggestionMenu.show(textField, Side.BOTTOM, 0, 0);
        suggestionMenuKeyBindings = (ContextMenuContent) suggestionMenu.getSkin().getNode();
        suggestionMenuKeyBindings.requestFocusOnIndex(0);
    }
});

Also, you will need to put此外,你需要把

--add-exports javafx.controls/com.sun.javafx.scene.control=ALL-UNNAMED

When compiling AND running, otherwise you may get an IllegalAccessError编译和运行时,否则可能会得到 IllegalAccessError

I workaround the problem in Kotlin by opening a "fake" ContextMenu in the AutoCompleteTextField init-block and closing it after one millisecond in other coroutine.我通过在 AutoCompleteTextField init-block 中打开一个“假” ContextMenu 并在其他协程中在一毫秒后关闭它来解决 Kotlin 中的问题。

You can do that in Java as well with a new thread.您也可以在 Java 中使用新线程执行此操作。 Hope it helps!希望能帮助到你!

GlobalScope.launch {
    Platform.runLater {
        suggestionMenu.items.add(MenuItem("fake"))
        suggestionMenu.show(this@AutoCompleteTextField, Side.BOTTOM, 0.0, 0.0)
    }
    delay(1)
    Platform.runLater {
        suggestionMenu.hide()
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM