2014-11-13 2 views
1

JavaFX MenuItem может реагировать на большинство событий KeyPress, устанавливая ActionEvent EventHandler. Однако, в то время как обработчик события захватывает KeyPress KeyCode.ENTER, он делает не catch KeyCode.TAB событие KeyPress. По-видимому, некоторые ключевые события, такие как TAB, обрабатываются на более глубоком уровне. Например, клавиши со стрелками позволяют обходить меню.Как получить JavaFX MenuItem для ответа на TAB KeyPress?

My ContextMenu - это список пополнений строки адреса электронной почты, на которой пользователь начал вводить текст в TextField. Пользователи хотят нажимать клавиши со стрелками, чтобы выбрать нужный элемент, и клавишу TAB для выполнения завершения.

Я могу подключить обработчик событий к самому ContextMenu и захватить нажатие клавиши TAB. Но источником события является ContextMenu, и я не могу найти никаких переменных в ContextMenu, указывающих, какой MenuItem был выделен при нажатии клавиши TAB. MenuItem позволяет css-стилю контролировать внешний вид элемента меню в фокусе, но у него нет никаких свойств, указывающих, находится ли он в фокусе или нет.

Я попытался futzing с EventDispatchChain через MenuItem buildEventDispatchChain() безрезультатно. Кажется, что нет способа перехватить TAB KeyPress или иначе определить, какой пункт меню находился в фокусе, когда была нажата клавиша TAB.

Любые предложения?

+0

Если вы получили это далеко, чтобы перехватить нажатие клавиши табуляции на уровне меню, которые помешали вам хранить последнюю сфокусирована MenuItem и огнь действия для этого один (если он не является нулевым)? Например. приложите прослушиватель фокуса к каждому элементу MenuItem, если элемент будет сфокусирован, чтобы вы знали, что нужно сделать позже .. – eckig

+0

Я не смог перехватить нажатие клавиши TAB на уровне меню. Я не вижу возможности подключить прослушиватель фокуса к MenuItems. MenuItems не являются узлами. –

ответ

0

Если я получу это правильно, вы хотите переопределить прослушиваемый по умолчанию прослушиватель, чтобы добавить свой собственный ответ, поэтому для этого мы должны найти, где он применяется.

Чтобы получить эту работу, мы должны получить в свои руки грязные с собственным API ...

ContextMenu кожи (ContextMenuSkin) использует ContextMenuContent объект, как контейнер со всеми деталями. Каждый из этих предметов также находится в контейнере ContextMenuContent.MenuItemContainer.

Мы можем переопределить прослушиваемый нажатием прослушиватель в родительском контейнере, в то время как мы можем добавить слушателя focusProperty к элементам в контейнере элементов.

Используя этот частный 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(); 
} 
0

Отлично! Благодарю вас @jose! Я закончил писать несколько иной код, но ключ использует com.sun.javafx.scene.control.skin.ContextMenuContent, который предоставляет доступ к объектам ContextMenuContent.MenuItemContainer, которые хранят MenuItems.

Чтобы не нарушить существующее поведение клавиш UP/DOWN, я добавил новый обработчик в объект ContextMenuContent; этот обработчик потребляет только TAB KeyPress и все остальное переходит к своим обычным обработчикам.

Глядя на класс ContextMenuContent, я позаимствовал их существующий метод для , нахожу сфокусированный предмет, поэтому не нужно было добавлять читателей focusProperty.

Кроме того, я на Java 1.7 и не имею lambdas, и я использую очень простой стиль программирования.

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); 
    } 
+0

Спасибо @EricSaund за отправку вашего решения. Я не использовал событие с помощью кнопок вверх/вниз, поэтому он не был лишним. Я включил его в коммутатор, если вы хотите реализовать какое-то пользовательское поведение. –

Смежные вопросы