如何让JavaFX MenuItem响应TAB KeyPress?

时间:2014-11-13 18:47:19

标签: javafx

JavaFX MenuItem可以通过设置ActionEvent EventHandler来响应大多数KeyPress事件。但是,虽然事件处理程序确实捕获了KeyCode.ENTER的KeyPress,但它捕获KeyCode.TAB KeyPress事件。显然,像TAB这样的一些关键事件是在更深层次上处理的。例如,箭头键可以遍历菜单。

我的ContextMenu是用户已开始在TextField中输入的电子邮件地址字符串的完成列表。用户想要按箭头键选择所需的项目,然后按TAB键执行完成。

我可以将事件处理程序附加到ContextMenu本身并捕获TAB按键。但事件的Source是ContextMenu,我在ContextMenu中找不到任何变量,指示在按下TAB键时哪个MenuItem突出显示。 MenuItem允许css样式控制焦点中菜单项的外观,但它没有任何属性来判断它是否处于焦点。

我通过MenuItem buildEventDispatchChain()尝试使用EventDispatchChain,但无济于事。在按下TAB键时,似乎没有办法拦截TAB KeyPress或以其他方式确定哪个菜单项处于焦点。

有什么建议吗?

2 个答案:

答案 0 :(得分:0)

如果我说得对,你想要覆盖默认的按键式监听器来添加你自己的响应,所以我们必须找到它应用的位置。

为了实现这一目标,我们必须亲自动手使用私有API ...

ContextMenu skin(ContextMenuSkin)使用ContextMenuContent对象作为包含所有项目的容器。这些项目中的每一项都位于ContextMenuContent.MenuItemContainer容器中。

我们可以覆盖父容器上的keypressed侦听器,同时我们可以将itemsProperty侦听器添加到items容器上的项目。

使用此私有API

import com.sun.javafx.scene.control.skin.ContextMenuContent;

这对我有用:

private ContextMenuContent.MenuItemContainer itemSelected=null;

@Override
public void start(Stage primaryStage) {

    MenuItem cmItem1 = new MenuItem("Item 1");
    cmItem1.setOnAction(e->System.out.println("Item 1"));
    MenuItem cmItem2 = new MenuItem("Item 2");
    cmItem2.setOnAction(e->System.out.println("Item 2"));

    final ContextMenu cm = new ContextMenu(cmItem1,cmItem2);

    Scene scene = new Scene(new StackPane(), 300, 250);
    scene.setOnMouseClicked(t -> {
        if(t.getButton()==MouseButton.SECONDARY || t.isControlDown()){
            cm.show(scene.getWindow(),t.getScreenX(),t.getScreenY());

            ContextMenuContent cmc= (ContextMenuContent)cm.getSkin().getNode();

            cmc.setOnKeyPressed(ke->{
                switch (ke.getCode()) {
                    case UP:    break;
                    case DOWN:  break;
                    case TAB:   ke.consume();
                                if(itemSelected!=null){
                                    itemSelected.getItem().fire();
                                }
                                cm.hide();
                                break;
                    default: break;
                }
            });
            VBox itemsContainer = cmc.getItemsContainer();
            itemsContainer.getChildren().forEach(n->{
                ContextMenuContent.MenuItemContainer item=(ContextMenuContent.MenuItemContainer)n;
                item.focusedProperty().addListener((obs,b,b1)->{
                    if(b1){
                        itemSelected=item;
                    }
                });
            });
        }
    });

    primaryStage.setScene(scene);
    primaryStage.show();
}

答案 1 :(得分:0)

出色!谢谢@jose!我最后编写了一些不同的代码但是 关键是使用com.sun.javafx.scene.control.skin.ContextMenuContent,它提供了 访问包含MenuItems的ContextMenuContent.MenuItemContainer对象。

为了不破坏现有的UP / DOWN键行为,我添加了一个新的处理程序 到ContextMenuContent对象;这个处理程序只使用TAB KeyPress和 everthing else传递给他们的正常处理程序。

查看ContextMenuContent类,我借用了他们现有的方法 找到焦点项,所以没有必要添加focusedProperty听众。

另外,我使用的是Java 1.7并且没有lambda,我使用的是非常基本的编程风格。

public class MenuItemHandler_CMC <T extends Event> implements EventHandler {
    public ContextMenuContent m_cmc;

    public AddressCompletionMenuItemHandler_CMC(ContextMenuContent cmc){
        m_cmc = cmc;
    }

    @Override
    public void handle(Event event){
        KeyEvent ke = (KeyEvent)event;
        switch(ke.getCode()){
        case TAB: 
            ke.consume();
            MenuItem focused_menu_item = findFocusedMenuItem();
            if(focused_menu_item != null){
                focused_menu_item.fire();
            }
            break;
        default: break;
        }
    }

    public MenuItem findFocusedMenuItem() {
        VBox items_container = m_cmc.getItemsContainer();
        for (int i = 0; i < items_container.getChildren().size(); i++) {
            Node n = items_container.getChildren().get(i);
            if (n.isFocused()) {
                ContextMenuContent.MenuItemContainer menu_item_container = (ContextMenuContent.MenuItemContainer)n;
                MenuItem menu_item = menu_item_container.getItem();
                return menu_item;
            }
        }
        return null;
    }
}

...附加额外的处理程序

    if(m_context_menu.getSkin() != null){
        ContextMenuContent cmc = (ContextMenuContent)m_context_menu.getSkin().getNode();
        MenuItemHandler_CMC menu_item_handler_cmc = new MenuItemHandler_CMC(cmc);
        cmc.addEventHandler(KeyEvent.KEY_PRESSED, menu_item_handler_cmc);
    }